-
本工具設計的初衷是用來獲取微信賬號的相關資訊並解析PC版微信的資料庫。
-
程式以 Python 語言開發,可讀取、解密、還原微信資料庫並幫助使用者檢視聊天記錄,還可以將其聊天記錄匯出為csv、html等格式用於AI訓練,自動回覆或備份等等作用。下面我們將深入探討這個工具的各個方面及其工作原理。
-
本專案僅供學習交流使用,嚴禁用於商業用途或非法途徑,任何違反法律法規、侵犯他人合法權益的行為,均與本專案及其開發者無關,後果由行為人自行承擔。
-
創作不易,請動動您發財的小手點點贊並收藏,您的支援是咱敲鍵盤的動力。
【完整演示工具下載】
https://www.chwm.vip/index.html?aid=23
我們接著上一篇文章《劫持微信聊天記錄並分析還原 —— 資料庫結構講解(四)》繼續演示與講解如何訪問微信合併後的資料庫並利用程式自帶的網頁UI來檢視PC端微信的聊天記錄,包括實時訊息的獲取等。
詳細命令:
dbshow -merge "C:\Users\admin\AppData\Local\Temp\wxdb_all.db" -wid "C:\Users\admin\Documents\WeChat Files\wxid_b*************1"
- -merge 為指定合併後的微信資料庫路徑
- -wid 為微信使用者帳號檔案目錄(用於顯示圖片)
執行命令後程式會自動開啟網頁UI。
此時我們點選“聊天檢視”功能是無法看到聊天資料的,首次使用我們需要先將程式初始化設定。
依次點選右下角“更多設定” - “初始化設定” - “自動解密已登入微信”
選擇已登入的微信(支援多個微信檢視)
並耐心等待提示成功後(載入的時間根據資料量的大小而定),我們再次點選“聊天檢視”功能。
此時所有該帳號下最近的聊天資料即可一一檢視,但這些資料並不會實時顯示,如果需要獲取實時聊天資料,我們還需要手動點選聊天記錄框右上角“實時訊息”。
等待提示成功後,我們重新整理一下頁面即可檢視實時聊天記錄。
部分現實程式碼:
# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name: __init__.py
# Description:
# Author: Rainbow
# Date: 2024/11/09
# -------------------------------------------------------------------------------
import os
import subprocess
import sys
import time
import uvicorn
import mimetypes
import logging
from logging.handlers import RotatingFileHandler
from uvicorn.config import LOGGING_CONFIG
from fastapi import FastAPI, Request, Path, Query
from fastapi.staticfiles import StaticFiles
from fastapi.exceptions import RequestValidationError
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import RedirectResponse, FileResponse
from .utils import gc, is_port_in_use, server_loger
from .rjson import ReJson
from .remote_server import rs_api
from .local_server import ls_api
from pywxdump import __version__
def gen_fastapi_app(handler):
app = FastAPI(title="wxdump", description="微信工具", version=__version__,
terms_of_service="https://www.chwm.vip",
contact={"name": "Rainbow", "url": "https://www.chwm.vip"},
license_info={"name": "MIT License",
"url": "https://www.chwm.vip"})
web_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "ui", "web") # web資料夾路徑
# 跨域
origins = [
"http://localhost:5000",
"http://127.0.0.1:5000",
"http://localhost:8080", # 開發環境的客戶端地址"
# "http://0.0.0.0:5000",
# "*"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # 允許所有源
allow_credentials=True,
allow_methods=["*"], # 允許所有方法
allow_headers=["*"], # 允許所有頭
)
@app.on_event("startup")
async def startup_event():
logger = logging.getLogger("uvicorn")
logger.addHandler(handler)
# 錯誤處理
@app.exception_handler(RequestValidationError)
async def request_validation_exception_handler(request: Request, exc: RequestValidationError):
# print(request.body)
return ReJson(1002, {"detail": exc.errors()})
# 首頁
@app.get("/")
@app.get("/index.html")
async def index():
response = RedirectResponse(url="/s/index.html", status_code=307)
return response
# 路由掛載
app.include_router(rs_api, prefix='/api/rs', tags=['遠端api'])
app.include_router(ls_api, prefix='/api/ls', tags=['本地api'])
# 根據檔案型別,設定mime_type,返回檔案
@app.get("/s/{filename:path}")
async def serve_file(filename: str):
# 構建完整的檔案路徑
file_path = os.path.join(web_path, filename)
file_path = os.path.abspath(file_path)
# 檢查檔案是否存在
if os.path.isfile(file_path):
# 獲取檔案 MIME 型別
mime_type, _ = mimetypes.guess_type(file_path)
# 如果 MIME 型別為空,則預設為 application/octet-stream
if mime_type is None:
mime_type = "application/octet-stream"
server_loger.warning(f"[+] 無法獲取檔案 MIME 型別,使用預設值:{mime_type}")
if file_path.endswith(".js"):
mime_type = "text/javascript"
server_loger.info(f"[+] 檔案 {file_path} MIME 型別:{mime_type}")
# 返回檔案
return FileResponse(file_path, media_type=mime_type)
# 如果檔案不存在,返回 404
return {"detail": "Not Found"}, 404
# 靜態檔案掛載
# if os.path.exists(os.path.join(web_path, "index.html")):
# app.mount("/s", StaticFiles(directory=web_path), name="static")
return app
def start_server(port=5000, online=False, debug=False, isopenBrowser=True,
merge_path="", wx_path="", my_wxid="", ):
"""
啟動flask
:param port: 埠號
:param online: 是否線上檢視(區域網檢視)
:param debug: 是否開啟debug模式
:param isopenBrowser: 是否自動開啟瀏覽器
:return:
"""
work_path = os.path.join(os.getcwd(), "wxdump_work") # 臨時資料夾,用於存放圖片等 # 全域性變數
if not os.path.exists(work_path):
os.makedirs(work_path, exist_ok=True)
server_loger.info(f"[+] 建立臨時資料夾:{work_path}")
print(f"[+] 建立臨時資料夾:{work_path}")
# 日誌處理,寫入到檔案
log_format = '[{levelname[0]}] {asctime} [{name}:{levelno}] {pathname}:{lineno} {message}'
log_datefmt = '%Y-%m-%d %H:%M:%S'
log_file_path = os.path.join(work_path, "wxdump.log")
file_handler = RotatingFileHandler(log_file_path, mode="a", maxBytes=10 * 1024 * 1024, backupCount=3)
formatter = logging.Formatter(fmt=log_format, datefmt=log_datefmt, style='{')
file_handler.setFormatter(formatter)
wx_core_logger = logging.getLogger("wx_core")
db_prepare = logging.getLogger("db_prepare")
# 這幾個日誌處理器為本專案的日誌處理器
server_loger.addHandler(file_handler)
wx_core_logger.addHandler(file_handler)
db_prepare.addHandler(file_handler)
conf_file = os.path.join(work_path, "conf_auto.json") # 用於存放各種基礎資訊
auto_setting = "auto_setting"
env_file = os.path.join(work_path, ".env") # 用於存放環境變數
# set 環境變數
os.environ["PYWXDUMP_WORK_PATH"] = work_path
os.environ["PYWXDUMP_CONF_FILE"] = conf_file
os.environ["PYWXDUMP_AUTO_SETTING"] = auto_setting
with open(env_file, "w", encoding="utf-8") as f:
f.write(f"PYWXDUMP_WORK_PATH = '{work_path}'\n")
f.write(f"PYWXDUMP_CONF_FILE = '{conf_file}'\n")
f.write(f"PYWXDUMP_AUTO_SETTING = '{auto_setting}'\n")
if merge_path and os.path.exists(merge_path):
my_wxid = my_wxid if my_wxid else "wxid_dbshow"
gc.set_conf(my_wxid, "wxid", my_wxid) # 初始化wxid
gc.set_conf(my_wxid, "merge_path", merge_path) # 初始化merge_path
gc.set_conf(my_wxid, "wx_path", wx_path) # 初始化wx_path
db_config = {"key": my_wxid, "type": "sqlite", "path": merge_path}
gc.set_conf(my_wxid, "db_config", db_config) # 初始化db_config
gc.set_conf(auto_setting, "last", my_wxid) # 初始化last
# 檢查埠是否被佔用
if online:
host = '0.0.0.0'
else:
host = "127.0.0.1"
if is_port_in_use(host, port):
server_loger.error(f"Port {port} is already in use. Choose a different port.")
print(f"Port {port} is already in use. Choose a different port.")
input("Press Enter to exit...")
return # 退出程式
if isopenBrowser:
try:
# 自動開啟瀏覽器
url = f"http://127.0.0.1:{port}/"
# 根據作業系統使用不同的命令開啟預設瀏覽器
if sys.platform.startswith('darwin'): # macOS
subprocess.call(['open', url])
elif sys.platform.startswith('win'): # Windows
subprocess.call(['start', url], shell=True)
elif sys.platform.startswith('linux'): # Linux
subprocess.call(['xdg-open', url])
else:
server_loger.error(f"Unsupported platform, can't open browser automatically.", exc_info=True)
print("Unsupported platform, can't open browser automatically.")
except Exception as e:
server_loger.error(f"自動開啟瀏覽器失敗:{e}", exc_info=True)
time.sleep(1)
server_loger.info(f"啟動 Web 服務,host:port:{host}:{port}")
print("[+] 請使用瀏覽器訪問 http://127.0.0.1:5000/ 檢視聊天記錄")
global app
print("[+] 如需檢視api文件,請訪問 http://127.0.0.1:5000/docs ")
app = gen_fastapi_app(file_handler)
LOGGING_CONFIG["formatters"]["default"]["fmt"] = "[%(asctime)s] %(levelprefix)s %(message)s"
LOGGING_CONFIG["formatters"]["access"][
"fmt"] = '[%(asctime)s] %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s'
uvicorn.run(app=app, host=host, port=port, reload=debug, log_level="info", workers=1, env_file=env_file)
app = None
__all__ = ["start_server", "gen_fastapi_app"]