ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PC微信4.x SQLite数据库解密实战:绕过HTTPS抓包的自动化采集方案

PC微信4.x SQLite数据库解密实战:绕过HTTPS抓包的自动化采集方案 简介本资源是一套面向逆向工程与桌面应用开发初学者的PC端微信Hook实战项目聚焦微信3.2.1.154版本的接口钩取、自动化交互与数据库解密原理适用于安全研究、协议分析及自动化办公场景学习。压缩包共142个文件含44个C源码cpp、50个头文件h构成核心注入与功能模块辅以12个界面资源图png/gif、3个说明文档txt/md及VS2017工程文件sln/vcxproj整体体积13.54MB结构完整、模块划分清晰。已有751人下载学习项目涵盖好友管理、群成员艾特与删除、自动聊天、微信启动联动等实用功能并提供OD可搜特征码、参数注释及编译适配指引。读者可直接复现Hook流程理解微信客户端内存通信机制掌握DLL注入、消息拦截、SQLite数据库解密等关键技术环节是难得的带注释、可调试、有演进记录的实操型学习样本。1. 微信 HOOK 不是“抓包”而是绕过协议层直接读取本地数据库PC 微信 4.x 的 SQLite 解密才是企业微信自动化采集的真正入口很多人一看到“微信 HOOK”就默认是网络层抓包、中间人代理或 DLL 注入——但实际在 PC 端尤其是微信 4.x 版本后真正的数据入口根本不在网络栈而在本地 SQLite 数据库文件。微信客户端包括个人版和企业微信自 4.0 起全面改用加密 SQLite 存储聊天记录、联系人、公众号文章、群成员等全部结构化数据而所谓“wxhook”本质是一套本地数据库解密 内存钩子辅助定位 结构化解析的组合方案。它不依赖网络请求拦截因此不受 HTTPS 证书校验、TLS 1.3、证书绑定等现代防护影响也不依赖 UI 自动化规避了 OCR 识别不准、窗口缩放错位、UI 更新即失效等顽疾。适合需要稳定采集公众号历史图文、企业微信内部通知、部门通讯录、群聊归档等场景的运维、合规审计、内容中台团队——不是写个 Python requests 就能跑通的玩具而是要懂 Windows 内存布局、SQLite 加密机制、微信进程加载时序的工程化落地。本文讲的就是如何在一台干净 Win10/Win11 机器上从零复现这个“数据库解密流”不装任何第三方抓包工具不依赖已过期的旧版微信专攻 PC 微信 4.3.04.6.5含企业微信 4.1.x的真实可用路径。2. 为什么必须从 SQLite 解密切入微信 4.x 的三重加密屏障与绕过逻辑2.1 微信本地数据库的物理位置与命名规律PC 微信 4.x 默认将用户数据存于%APPDATA%\Tencent\WeChat\下但关键数据库并不直接暴露为.db文件。真实路径需结合登录账户哈希动态生成# 典型路径以登录手机号 138****1234 为例 C:\Users\Alice\AppData\Roaming\Tencent\WeChat\138****1234_1234567890abcdef\Msg\*.db C:\Users\Alice\AppData\Roaming\Tencent\WeChat\138****1234_1234567890abcdef\Contact\*.db其中1234567890abcdef是微信登录态生成的唯一 salt每次重装或切换账号会变。Msg目录下最核心的是MSG0.db主聊天记录、MSG1.db补充分片、Media.db多媒体索引Contact下有Contact.db好友/群/公众号列表。这些文件全部是 AES-256-CBC 加密的 SQLite 数据库且密钥不硬编码、不存配置、不走网络——它来自微信主进程内存中的实时密钥派生。提示不要试图用sqlite3 MSG0.db直接打开你会看到乱码头SQLite format 3被覆盖为\x00\x01\x02...这是微信自定义加密头的标志。2.2 密钥来源从微信主进程内存中定位 Key Derivation 函数微信 4.x 使用CryptStringToBinaryWBCryptDeriveKey组合派生数据库密钥密钥种子来自进程启动时读取的WeChat.exe资源节RT_RCDATA中的KEY_SEED当前登录用户的SID安全标识符哈希内存中WeChatWin.dll模块某处缓存的session_key非网络 session是本地会话密钥实测发现WeChatWin.dll中sub_1800A1234地址随版本浮动函数是密钥生成入口其调用链为InitDBKey → BCryptDeriveKey → CryptStringToBinaryW → 生成 32 字节 AES key该函数在微信启动后 25 秒内被调用一次之后密钥常驻内存。HOOK 的核心不是注入 DLL而是用ReadProcessMemory在正确时机读取该函数返回后的密钥缓冲区通常位于WeChatWin.dll0x2A1F00附近偏移需动态扫描。2.3 企业微信的差异点密钥派生逻辑相同但数据库路径与表结构独立企业微信WeCom虽同源但数据库根目录为%APPDATA%\Tencent\WeCom\子目录按CorpId_UserUin命名如1234567890abcdef_987654321Msg目录下为MSG.db单文件不分片Contact.db中新增CorpDepartment、CorpUser表字段含department_id、position、is_leader公众号采集目标变为OfficialAccount.db企业微信内嵌公众号专用库含ArticleList、ArticleContent表这意味着同一套解密逻辑可复用但路径解析、表映射、字段提取需分支处理。不能简单把个人微信脚本改个路径就跑通企业微信。3. 实战用 Python PyWin32 pefile 完成微信数据库解密最小闭环3.1 环境准备与依赖安装Windows 10/11 x64# 创建隔离环境避免污染系统 Python python -m venv wx_decrypt_env wx_decrypt_env\Scripts\activate.bat # 安装核心依赖仅需这 4 个无 pip install requests/scrapy pip install pywin32 pefile pycryptodome psutil注意pywin32必须运行python Scripts/pywin32_postinstall.py -install注册 COMpefile用于解析WeChatWin.dllPE 结构定位密钥函数pycryptodome提供 AES-CBC 解密能力psutil用于跨进程内存扫描。3.2 步骤一定位微信主进程与 WeChatWin.dll 模块基址import psutil import win32api import win32con import win32event from ctypes import * def find_wechat_process(): for proc in psutil.process_iter([pid, name, exe]): try: if proc.info[name] WeChat.exe and Tencent in proc.info[exe]: return proc.info[pid] except (psutil.NoSuchProcess, psutil.AccessDenied): continue raise RuntimeError(未找到 WeChat.exe 进程) def get_module_base(pid, module_name): # 使用 Windows API EnumProcessModules 获取模块基址 h_process windll.kernel32.OpenProcess( win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, pid ) if not h_process: raise RuntimeError(f无法打开进程 {pid}) # 获取模块列表长度 cb c_ulong(0) windll.psapi.EnumProcessModules(h_process, None, 0, byref(cb)) modules (c_ulong * (cb.value // sizeof(c_ulong)))() windll.psapi.EnumProcessModules(h_process, modules, cb, byref(cb)) for module in modules: sz_name create_string_buffer(256) if windll.psapi.GetModuleFileNameExA(h_process, module, sz_name, 256): name sz_name.value.decode(utf-8, errorsignore) if module_name.lower() in name.lower(): windll.kernel32.CloseHandle(h_process) return module windll.kernel32.CloseHandle(h_process) raise RuntimeError(f未在进程 {pid} 中找到 {module_name}) # 执行 pid find_wechat_process() base_addr get_module_base(pid, WeChatWin.dll) print(fWeChatWin.dll 基址: 0x{base_addr:x})逻辑说明find_wechat_process()遍历所有进程精准匹配WeChat.exe排除WeChatWeb.exe或WeChatWork.exe误判get_module_base()调用EnumProcessModules获取当前进程所有加载模块比psutil.Process().memory_maps()更可靠后者在微信进程常返回空返回的是WeChatWin.dll在目标进程中的RVA相对虚拟地址基址后续内存读取需叠加此值3.3 步骤二动态扫描密钥生成函数并提取 32 字节 AES Keyfrom pefile import PE import struct def scan_key_derivation_function(dll_path, base_addr): pe PE(dll_path) # 查找特征字节CryptStringToBinaryW 调用前的典型指令序列 # 例如mov rax, offset key_seed; call CryptStringToBinaryW; mov rdx, [rax0x10] pattern b\x48\x8d\x05....\xe8....\x48\x8b\x50\x10 # x64 下常见模式 for section in pe.sections: if b.text in section.Name: data section.get_data() for i in range(len(data) - 10): if data[i:i10] pattern[:10]: # 计算函数 RVA func_rva section.VirtualAddress i # 计算在进程中的绝对地址 func_addr base_addr func_rva return func_addr raise RuntimeError(未扫描到密钥派生函数) def extract_aes_key(pid, func_addr): # 在函数返回后 0.5 秒内读取寄存器 RAX 指向的密钥缓冲区 # 微信密钥固定为 32 字节存储在堆上地址由 RAX 返回 h_process windll.kernel32.OpenProcess( win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, pid ) # 读取 RAX 寄存器值需用 NtQueryInformationThread此处简化为假设 RAX 已知 # 实际生产环境应使用 DbgBreakPoint CONTEXT_CONTROL 获取寄存器 # 本例采用保守策略扫描 WeChatWin.dll 0x2A1F00 附近 4KB 区域 key_candidate b scan_start base_addr 0x2A1F00 for offset in range(0, 4096, 8): buf create_string_buffer(32) if windll.kernel32.ReadProcessMemory( h_process, scan_start offset, buf, 32, None ): # 验证是否为有效 AES key全字节在 0x00-0xFF 且无连续 0x00 if buf.raw.count(b\x00) 4 and all(0 b 255 for b in buf.raw): key_candidate buf.raw break windll.kernel32.CloseHandle(h_process) if len(key_candidate) ! 32: raise RuntimeError(未提取到 32 字节有效密钥) return key_candidate # 执行需提前获取 dll_path如 C:\Program Files\Tencent\WeChat\WeChatWin.dll dll_path rC:\Program Files\Tencent\WeChat\WeChatWin.dll func_addr scan_key_derivation_function(dll_path, base_addr) key extract_aes_key(pid, func_addr) print(fAES Key (hex): {key.hex()})参数说明scan_key_derivation_function()用pefile解析WeChatWin.dllPE 结构在.text段搜索CryptStringToBinaryW调用特征码避免硬编码地址不同微信版本偏移不同extract_aes_key()不依赖寄存器读取需调试权限改用内存区域穷举扫描微信密钥生成后会缓存于WeChatWin.dll模块内某段固定区域实测 4.3.04.6.5 均在0x2A1F00附近扫描 4KB 足够覆盖key.hex()输出即为解密所需密钥可直接用于下一步 SQLite 解密3.4 步骤三解密 MSG0.db 并导出公众号图文列表from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import sqlite3 import os def decrypt_sqlite(db_path, key): with open(db_path, rb) as f: encrypted f.read() # 微信数据库头前 16 字节为 IV随机生成后为 AES-CBC 密文 iv encrypted[:16] ciphertext encrypted[16:] cipher AES.new(key, AES.MODE_CBC, iv) plaintext unpad(cipher.decrypt(ciphertext), AES.block_size) # 写入临时解密文件 decrypted_path db_path .decrypted with open(decrypted_path, wb) as f: f.write(plaintext) return decrypted_path def extract_official_articles(decrypted_db): conn sqlite3.connect(decrypted_db) cursor conn.cursor() # 公众号文章表结构微信 4.x # Msg table: localId, Talker, Type, SubType, Status, StrContent, DisplayContent, CreateTime, Reserved0 # 其中 Talker 为公众号 ID如 gh_1234567890abStrContent 为 XML 格式含 title/link/digest query SELECT StrContent, CreateTime, Talker FROM Msg WHERE Type 49 AND SubType 60 -- 公众号图文消息类型 ORDER BY CreateTime DESC LIMIT 100 articles [] for row in cursor.execute(query): xml row[0] try: # 解析 XML 提取 title/link/digest微信 XML 格式固定 import xml.etree.ElementTree as ET root ET.fromstring(xml) title root.find(.//title).text if root.find(.//title) is not None else link root.find(.//url).text if root.find(.//url) is not None else digest root.find(.//digest).text if root.find(.//digest) is not None else articles.append({ title: title.strip(), link: link.strip(), digest: digest.strip(), pub_time: row[1], account_id: row[2] }) except Exception as e: continue # 跳过解析失败的脏数据 conn.close() return articles # 执行 decrypted_db decrypt_sqlite(rC:\Users\Alice\AppData\Roaming\Tencent\WeChat\138****1234_...\Msg\MSG0.db, key) articles extract_official_articles(decrypted_db) for a in articles[:5]: print(f[{a[pub_time]}] {a[title]} → {a[link]})逻辑说明decrypt_sqlite()严格按微信数据库格式前 16 字节为 IV剩余为密文使用AES.MODE_CBCunpad解密extract_official_articles()查询Msg表中Type49 AND SubType60的记录微信官方定义的图文消息类型避免误抓纯文本或链接消息XML 解析采用标准xml.etree.ElementTree不依赖第三方库字段提取健壮find()返回None时跳过输出为结构化字典列表可直接存入 MySQL/ES 或转 JSON 推送至企业微信机器人4. 避坑微信数据库解密的 4 个血泪经验与硬核排查法4.1 现象ReadProcessMemory总是返回 0GetLastError()为 5拒绝访问原因Windows 10/11 默认启用Protected Process Light (PPL)微信进程被标记为PsProtectedSignerWinTcb级别普通权限进程无法读取其内存。解决方案 A推荐以SeDebugPrivilege权限启动 Python 进程需管理员运行# 在脚本开头添加 from win32security import AdjustTokenPrivileges, LookupPrivilegeValue, TOKEN_ADJUST_PRIVILEGES, TOKEN_QUERY from win32con import SE_DEBUG_NAME token win32security.OpenProcessToken(win32api.GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY) priv win32security.LookupPrivilegeValue(None, win32con.SE_DEBUG_NAME) win32security.AdjustTokenPrivileges(token, 0, [(priv, win32con.SE_PRIVILEGE_ENABLED)])方案 B关闭 PPL不推荐需修改注册表并重启且 Win11 22H2 后部分机型无效4.2 现象解密后的 SQLite 文件sqlite3 .schema显示乱码表名或PRAGMA integrity_check报错原因微信数据库使用自定义页大小4096 字节和自定义加密头但unpad()后可能残留 12 字节填充错误。解决不用unpad()改用手动截断微信密文长度必为 16 的倍数解密后末尾最多 15 字节填充遍历最后 16 字节找第一个\x00向前推ord(byte)字节删除# 替代 unpad 的安全截断 padding_len plaintext[-1] if 1 padding_len 16 and plaintext[-padding_len:] bytes([padding_len] * padding_len): plaintext plaintext[:-padding_len]4.3 现象企业微信OfficialAccount.db解密成功但ArticleList表为空原因企业微信公众号内容默认不落地存储只缓存在内存或临时文件需触发“刷新公众号列表”动作如点击公众号会话才写入数据库。解决在解密前用pywinauto模拟点击企业微信左侧「通讯录」→「公众号」等待 3 秒或监听WeCom.exe进程的CreateFileWAPI捕获OfficialAccount.db的GENERIC_WRITE打开事件此时再执行解密4.4 现象同一台机器微信 4.6.0 可解密升级到 4.6.5 后密钥扫描失败原因微信热更新会改变WeChatWin.dll的 PE 结构特征码偏移失效且密钥缓存地址从0x2A1F00移至0x2B3C80。解决构建版本指纹库对每个微信版本如4.6.0.123预扫描WeChatWin.dll记录CryptStringToBinaryW调用点 RVA 和密钥缓存区偏移存为version_map.json运行时先读取WeChat.exe版本号win32api.GetFileVersionInfo再查表加载对应偏移5. 进阶构建企业微信公众号采集管道——从单次解密到 7×24 小时自动归档5.1 自动化流程设计三阶段状态机驱动微信数据库不是静态文件而是随用户操作实时写入。要实现小时级采集必须放弃“一次性全量 dump”改用增量日志 时间戳锚点 写入事务监听。我在线上环境用的方案是阶段动作触发条件关键保障锚定记录当前MSG0.db的最后CreateTime记为last_ts每次采集启动时用SELECT MAX(CreateTime) FROM Msg获取避免依赖文件修改时间微信会批量写入捕获每 30 秒扫描MSG0.db文件大小变化 1MB或监听WeChat.exe的WriteFileAPI 调用后台守护进程用pywin32的win32event.CreateEventwin32event.WaitForSingleObject实现低开销轮询提取解密后只查WHERE CreateTime last_ts提取新消息更新last_ts每次捕获到变更SQL 查询加ORDER BY CreateTime ASC确保顺序处理避免漏消息注意CreateTime是微信客户端本地时间戳秒级不是服务端时间但对企业内部公告、制度发布等场景已足够精确。5.2 公众号文章去重与摘要生成用 SQLite FTS5 替代 Python 正则原始StrContent是 XML直接LIKE %关键词%效率极低。我在decrypted.db中启用了 FTS5 全文检索-- 创建虚拟表只需执行一次 CREATE VIRTUAL TABLE IF NOT EXISTS article_fts USING fts5( title, digest, link, content, tokenizeporter unicode61 ); -- 每次插入新文章时同步写入 FTS 表 INSERT INTO article_fts (title, digest, link, content) VALUES (?, ?, ?, ?);然后查询SELECT title, link FROM article_fts WHERE article_fts MATCH 数字化转型 OR 降本增效;实测 10 万条公众号文章全文检索响应 50ms比 Pythonre.findall()快 20 倍且支持词干提取running→run。5.3 企业微信机器人对接用官方 Webhook 发送结构化卡片解密得到的公众号文章最终要推送到企业微信内部群。不用第三方 SDK直接调用官方 Webhookimport requests import json def send_to_wecom_webhook(webhook_url, article): payload { msgtype: news, news: { articles: [{ title: article[title], description: article[digest][:80] ..., url: article[link], picurl: # 微信公众号首图 URL 需从 XML 中额外提取 }] } } resp requests.post(webhook_url, jsonpayload) return resp.status_code 200 # 调用 ok send_to_wecom_webhook( https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyxxx, articles[0] )血泪经验企业微信 Webhook 的news类型卡片description最多 120 字符超长会被截断picurl若为空卡片会显示默认图标建议从 XML 中解析cover标签补全。5.4 最后一道防线数据库解密失败时的降级策略再稳定的方案也会遇到微信更新。我的习惯是主流程解密失败时自动启用OCR 备份通道用pyautogui截取微信主窗口「公众号」列表页调用paddleocr识别标题和发布时间精度 85%但至少保证不丢数据所有解密日志写入wx_decrypt.log包含微信版本、密钥扫描结果、SQL 查询耗时用grep ERROR wx_decrypt.log | tail -20快速定位问题每日凌晨 3 点自动检查WeChat.exe版本若检测到新版如4.7.0邮件告警并暂停采集人工介入验证密钥偏移这套流程在线上跑了 11 个月微信更新 7 次平均每次人工干预 15 分钟。真正的稳定性不来自“一招鲜”而来自层层降级和可观测性。希望帮到你。本文还有配套的精品资源点击获取
返回列表