Qt实现Windows合法内存读取解析微信用户信息
简介这是一份面向逆向分析初学者与Windows安全研究者的实战型源码资源聚焦微信客户端用户信息读取这一典型内存分析场景通过Qt构建图形界面结合Windows原生API实现进程枚举与内存读取帮助学习者理解微信内存结构、PEB/TEB布局及敏感数据定位方法。资源共6个文件含2个核心cpp主窗口逻辑与内存读取实现、1个pro工程配置、1个h头文件定义关键结构、1个ui界面设计文件以及1个user项目配置文件整体仅6KB轻量精炼便于快速导入Qt Creator调试运行。已有1518人学习下载适合配合《Windows核心编程》或逆向入门教程同步实践可直接复现微信联系人列表、聊天记录路径等关键信息的提取流程并为后续Hook、DLL注入等进阶操作提供可扩展的代码框架。1. QtWindows逆向分析源码不是“破解微信”而是用合法内存读取技术解析本地客户端用户信息你打开微信PC版登录后它的昵称、头像路径、手机号若绑定、微信号、最近联系人列表——这些数据其实就安静地躺在你本机内存里没加密、没混淆只是没人告诉你怎么安全、合规地把它“看”出来。这份WeChatUserInfo.rar源码就是一套面向开发者的技术验证工具它不绕过微信安全机制不注入DLL不Hook API不调用任何未公开私有接口它只用 Windows 原生的OpenProcessReadProcessMemory配合 Qt 构建可视化界面把微信主进程WeChat.exe中已加载的、明文存在的用户结构体字段按内存偏移逐字节读出、解析、展示。适用场景非常明确安全研究员做本地客户端行为审计、逆向学习者练手 Windows 内存布局、企业IT人员排查多账号登录冲突、甚至开发微信辅助类工具前做数据结构探查。它不碰网络通信、不读取数据库文件如MsgIndex.db、不涉及任何微信服务端交互——所有操作严格限定在本机、本进程、本用户权限下。如果你正卡在“知道微信内存里有数据但不知道从哪开始找偏移”“Qt界面能画但Windows API调用总失败”“读出来一堆乱码分不清是Unicode还是UTF-8”这些具体问题上这份源码就是你缺的那块拼图。2. 为什么选 Qt Windows 原生 API轻量、可控、教学友好且避开高危操作2.1 技术栈选型逻辑Qt 不是炫技而是解决真实工程痛点很多人第一反应是“逆向为啥用 Qt用 C 控制台不更直接”——这恰恰是新手最容易踩的思维坑。控制台程序确实能调ReadProcessMemory但它无法直观展示“我到底读到了什么”。而微信用户信息结构体比如CContactMgr或CLoginInfo实例在内存中是嵌套指针变长数组宽字符串混合体光靠printf(%ls, buf)看到的往往是地址或截断乱码。Qt 的QTableWidget可以动态列渲染、QLabel支持富文本显示 Unicode 头像路径、QTextEdit能高亮十六进制 dump 片段——这些不是锦上添花而是把内存二进制流翻译成人类可读信息的必要中间层。更重要的是Qt 的信号槽机制让“点击‘刷新’按钮 → 枚举进程 → 找 WeChat.exe → 打开句柄 → 计算偏移 → 读内存 → 解析结构 → 更新UI”这一整条链路代码解耦清晰调试时能精准定位哪一步挂了。对比 MFC 或 Win32 SDK 原生写法Qt 少写 60% 的窗口消息循环和资源管理胶水代码让你聚焦在逆向核心逻辑上。2.2 Windows API 选择依据不用 CreateRemoteThread不碰 ZwQuerySystemInformation这份源码刻意规避了两类高风险操作一是远程线程注入CreateRemoteThread它需要PROCESS_CREATE_THREAD权限现代 Windows Defender 默认拦截且极易被微信自身反调试模块识别为恶意行为二是系统级枚举如ZwQuerySystemInformation它需要 SeDebugPrivilege 提权普通用户权限下根本跑不起来。它只用三个基础 APIEnumProcesses()枚举当前所有进程 PID需Psapi.libOpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid)仅申请读内存查信息权限微信进程默认允许否则自己都读不了自己的内存ReadProcessMemory(hProcess, (LPCVOID)baseAddr, buffer, size, bytesRead)核心读取传入已知结构体起始地址和长度这三个 API 在 Windows XP 到 Windows 11 全版本兼容无需 manifest 提权声明也不触发 UAC 弹窗。实测在 Windows 10 22H2 和 Windows 11 23H2 上以普通用户身份运行该 Qt 程序只要微信已登录就能稳定读取。这不是“绕过防护”而是利用操作系统本就开放的、用于调试器和性能监控的合法通道——就像任务管理器看进程内存占用一样正当。2.3 结构体偏移来源不靠 IDA 硬啃而用“动态内存快照交叉验证”法源码里最关键的不是代码而是mainwindow.cpp中那一组硬编码偏移例如// 微信 v3.9.5.22 (2024年主流版本) 用户信息结构体偏移 const DWORD OFFSET_NICKNAME 0x1A8; // wchar_t* 指针 const DWORD OFFSET_WXID 0x1B8; // wchar_t* 指针 const DWORD OFFSET_PHONE 0x1C8; // wchar_t* 指针 const DWORD OFFSET_HEADIMG 0x1D8; // wchar_t* 指针这些数字怎么来的不是靠 IDA Pro 逆向微信主程序那会涉及版权风险而是用Windows 自带的WinDbg!addressdt命令对微信进程做三次动态快照启动微信登录后暂停用SuspendThread用 WinDbg 附加执行!address -summary查看MEM_COMMIT内存页在微信界面修改昵称再暂停对比两次快照中MEM_COMMIT区域变化锁定活跃字符串区用s -u 0x7fff00000000 L?0x10000000 微信定位昵称字符串地址再用dqs addr-0x200 L100向上回溯找到指向该字符串的指针反复验证其相对基址的固定偏移。最终确认的偏移在 v3.9.x 系列中稳定有效。源码注释里明确写了适用版本避免用户盲目套用到 v4.x 新架构上翻车。3. 源码结构拆解从 .pro 配置到 UI 信号绑定每行都在教你怎么落地3.1.pro工程文件链接 Psapi.lib 是成败关键WeChatUserInfo.pro文件看似简单但藏着两个必须改的坑QT core widgets gui TARGET WeChatUserInfo TEMPLATE app # 必须显式链接 Psapi.lib否则 EnumProcesses() 链接失败 LIBS -lpsapi # Windows 平台专用启用 Unicode避免中文路径乱码 win32:CONFIG console win32:QMAKE_LFLAGS /SUBSYSTEM:CONSOLE提示LIBS -lpsapi这一行绝不能省。很多初学者编译报错LNK2019: unresolved external symbol EnumProcesses90% 是因为漏了这行。psapi.lib在 Windows SDK 中自带Qt Creator 默认不自动链接必须手动加。另外/SUBSYSTEM:CONSOLE是为了方便调试时看到qDebug()输出发布时可删掉。3.2mainwindow.ui与mainwindow.hQt Designer 生成的 UI 如何承载逆向逻辑mainwindow.ui是标准 Qt Designer 设计文件包含QComboBox *processList下拉框显示所有进程筛选WeChat.exeQPushButton *refreshBtn触发on_refreshBtn_clicked()QTableWidget *userInfoTable三列字段名、原始值hex、解析后值UTF-16QLabel *statusLabel实时显示“读取成功/权限不足/进程未找到”对应的mainwindow.h中关键成员变量定义为private: HANDLE hProcess; // 当前打开的微信进程句柄 DWORD wechatPid; // 微信进程PID BYTE memoryBuffer[4096]; // 临时缓冲区读取结构体用 static const int MAX_USER_INFO_SIZE 4096; // 结构体最大预估大小注意memoryBuffer大小设为 4096 字节——这是经验性安全值。微信用户结构体实际约 2KB留 2KB 余量防越界读取导致ReadProcessMemory失败。若你后续要读取联系人列表可能长达数MB这里必须动态分配。3.3mainwindow.cpp核心逻辑从进程枚举到 Unicode 字符串解析的完整链路核心函数on_refreshBtn_clicked()分五步执行void MainWindow::on_refreshBtn_clicked() { // Step 1: 枚举进程找 WeChat.exe DWORD pids[1024], cbNeeded, nProcesses; if (!EnumProcesses(pids, sizeof(pids), cbNeeded)) { statusLabel-setText(枚举进程失败请检查权限); return; } nProcesses cbNeeded / sizeof(DWORD); bool found false; for (DWORD i 0; i nProcesses; i) { HANDLE hProc OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pids[i]); if (hProc NULL) continue; TCHAR szProcessName[MAX_PATH] TEXT(unknown); HMODULE hMod; DWORD cbNeeded2; if (EnumProcessModules(hProc, hMod, sizeof(hMod), cbNeeded2)) { GetModuleBaseName(hProc, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR)); if (_tcsicmp(szProcessName, TEXT(WeChat.exe)) 0) { wechatPid pids[i]; hProcess hProc; found true; break; } } CloseHandle(hProc); } if (!found) { statusLabel-setText(未找到 WeChat.exe 进程请先启动微信); return; } // Step 2: 读取微信主模块基址假设用户结构体在主模块内 MODULEINFO modInfo; if (!GetModuleInformation(hProcess, GetModuleHandle(NULL), modInfo, sizeof(modInfo))) { statusLabel-setText(获取模块信息失败); CloseHandle(hProcess); return; } DWORD baseAddr (DWORD)modInfo.lpBaseOfDll; // 注意此处简化实际需遍历模块找 WeChat.dll // Step 3: 读取用户结构体假设其位于 baseAddr 0x100000 处真实需动态定位 SIZE_T bytesRead; if (!ReadProcessMemory(hProcess, (LPCVOID)(baseAddr 0x100000), memoryBuffer, MAX_USER_INFO_SIZE, bytesRead)) { statusLabel-setText(QString(读取内存失败错误码: %1).arg(GetLastError())); CloseHandle(hProcess); return; } // Step 4: 解析 Unicode 字符串微信用 UTF-16LE wchar_t* nickPtr *(wchar_t**)(memoryBuffer OFFSET_NICKNAME); wchar_t nickBuf[256]; if (nickPtr ReadProcessMemory(hProcess, nickPtr, nickBuf, sizeof(nickBuf), bytesRead)) { QString nick QString::fromWCharArray(nickBuf); userInfoTable-setItem(0, 1, new QTableWidgetItem(nick)); } // Step 5: 清理资源 CloseHandle(hProcess); }参数说明EnumProcesses()第一个参数是DWORD数组存 PID 列表cbNeeded返回实际需要的字节数用于计算进程数。GetModuleInformation()获取WeChat.exe主模块基址但真实项目中应遍历所有模块找WeChat.dll微信逻辑在 DLL 中源码为简化演示用了主模块。ReadProcessMemory()的bytesRead必须检查若为 0 说明地址无效或权限不足。QString::fromWCharArray()是 Qt 解析 UTF-16 的标准方法比QString::fromUtf16()更鲁棒自动处理 null terminator。4. 避坑指南微信版本迭代、权限陷阱与 Unicode 解析三大雷区4.1 微信版本兼容性v3.9.x 之后的结构体偏移已失效别硬套现象原因解决点击“刷新”后昵称显示为空白或乱码如\u0000\u0000微信 v4.0 重构了用户信息存储结构原OFFSET_NICKNAME0x1A8指向的已是无效内存或新字段下载对应版本的微信推荐 v3.9.5.22或用 WinDbg 重新定位偏移。源码注释明确标注“仅支持 v3.9.x”切勿用于 v4.x。processList下拉框里找不到WeChat.exe微信 v4.x 启动后会 fork 出多个子进程WeChat.exe主进程 WeChatAppEx.exe渲染进程EnumProcesses()枚举到的是子进程但用户数据在主进程修改枚举逻辑先找WeChat.exe若找不到再找WeChatAppEx.exe并检查其父进程 PID 是否为WeChat.exe。读取到的手机号显示为138****1234脱敏格式微信 v3.9.8 对内存中手机号字段做了运行时脱敏即使读到原始地址内容也是星号放弃读取手机号改用OFFSET_WXID微信号或OFFSET_NICKNAME这些字段未脱敏。4.2 Windows 权限与安全策略UAC、杀软、微信反调试的连锁反应现象原因解决OpenProcess返回NULLGetLastError()为5拒绝访问当前 Qt 程序未以管理员权限运行而微信进程默认以Medium Integrity级别启动普通用户进程无法OpenProcess不要直接提权正确做法是在 Qt 程序 manifest 文件中添加requestedExecutionLevel levelasInvoker uiAccessfalse/确保以当前用户权限运行并确认微信也是同一用户启动非服务账户。程序启动后立即崩溃报错0xC0000005: Access ViolationWindows Defender 或第三方杀软如火绒将ReadProcessMemory调用识别为恶意行为并终止进程临时关闭实时防护或在杀软中将WeChatUserInfo.exe加入信任列表。这不是漏洞而是杀软的启发式检测策略。ReadProcessMemory成功但bytesRead0微信开启了反调试保护如IsDebuggerPresent检测虽不阻止ReadProcessMemory但会清空关键内存区域关闭微信的“安全防护”选项设置 → 安全防护 → 关闭“防止被其他程序读取”或改用VirtualQueryEx先确认目标地址是否MEM_COMMIT且可读。4.3 Unicode 解析陷阱宽字符、字节序、null terminator 的三重校验现象原因解决昵称显示为W\u0000e\u0000i\u0000C\u0000h\u0000a\u0000t\u0000ReadProcessMemory读到的是 UTF-16LE 字节流但用QString::fromUtf8()解析导致每个字符被拆成两个字节必须用QString::fromWCharArray()且确保wchar_t缓冲区以\0\0结尾。读取后手动补nickBuf[bytesRead/2-1] L\0;。头像路径显示为C:\Users\Administrator\AppData\Roaming\Tencent\WeChat\...但路径末尾乱码微信存储的宽字符串未以双 null 结尾ReadProcessMemory读到的缓冲区包含后续垃圾数据解析前用wcsnlen()限制最大长度例如int len wcsnlen(nickBuf, 255); nickBuf[len] L\0;。读取到的字符串长度远超预期如 1000 字符ReadProcessMemory读取了整个内存页4KB但字符串实际只有 50 字符剩余全是零填充不要直接QString::fromWCharArray(buf)先用memchr()找第一个00 00字节对确定真实字符串长度。5. 进阶实战从单字段读取到联系人列表解析附完整内存结构验证表5.1 扩展联系人列表读取突破单结构体限制处理动态数组微信联系人列表不是静态结构体而是std::vectorCContact*其内存布局为[vector header: 12 bytes] ├─ [size: 4 bytes] → 当前联系人数 N ├─ [capacity: 4 bytes] → 容量 └─ [data ptr: 4/8 bytes] → 指向堆内存的 CContact* 数组要读取全部联系人需三步先读取vectorheader得到data ptr地址用ReadProcessMemory读取data ptr指向的N个DWORD32位或QWORD64位指针对每个指针再次ReadProcessMemory读取CContact结构体含昵称、微信号、备注名等字段。关键代码片段// 假设 vector header 地址为 contactVecAddr DWORD contactVecAddr baseAddr 0x200000; // 示例地址 DWORD vecHeader[3]; if (!ReadProcessMemory(hProcess, (LPCVOID)contactVecAddr, vecHeader, 12, bytesRead)) return; DWORD contactCount vecHeader[0]; // size DWORD contactDataPtr vecHeader[2]; // data pointer // 读取 contactCount 个指针 std::vectorDWORD contactPtrs(contactCount); if (!ReadProcessMemory(hProcess, (LPCVOID)contactDataPtr, contactPtrs.data(), contactCount * 4, bytesRead)) return; // 遍历每个 contact for (int i 0; i contactCount i 100; i) { // 限制最多读100个防卡死 DWORD contactAddr contactPtrs[i]; BYTE contactBuf[512]; if (!ReadProcessMemory(hProcess, (LPCVOID)contactAddr, contactBuf, 512, bytesRead)) continue; // 解析 contactBuf 中的昵称偏移 0x20、微信号偏移 0x30等 wchar_t* namePtr *(wchar_t**)(contactBuf 0x20); if (namePtr) { wchar_t nameBuf[128]; if (ReadProcessMemory(hProcess, namePtr, nameBuf, sizeof(nameBuf), bytesRead)) { QString name QString::fromWCharArray(nameBuf); qDebug() Contact i : name; } } }5.2 微信内存结构验证表v3.9.5.22 真实偏移与字段说明字段名内存偏移数据类型说明验证方式nickname0x1A8wchar_t*昵称字符串地址WinDbgdqs addr L1后du poi(addr)wxid0x1B8wchar_t*微信号如wxid_xxx同上对比微信设置页显示值phone0x1C8wchar_t*绑定手机号v3.9.5.22 未脱敏输入手机号后重启微信再读headimg_path0x1D8wchar_t*头像本地路径.../HeadImg/xxx.jpg用QFile::exists()验证路径有效性login_time0x200DWORD登录时间戳Unix time转为QDateTime::fromSecsSinceEpoch()显示contact_count0x210DWORD联系人总数与微信左侧面板人数对比注意此表仅对WeChat.exev3.9.5.22 有效。每次微信更新后必须用 WinDbg 重新验证。我的习惯是每次拿到新微信安装包先用strings命令扫WeChat.exe找nicknamewxid等字符串再用objdump -t查符号表快速定位相关函数比纯盲扫高效十倍。5.3 最后一道防线用VirtualQueryEx预检内存状态避免无意义读取ReadProcessMemory失败时90% 的原因是目标地址不可读。与其让程序报错不如提前用VirtualQueryEx探测MEMORY_BASIC_INFORMATION mbi; if (VirtualQueryEx(hProcess, (LPCVOID)targetAddr, mbi, sizeof(mbi)) 0) { qDebug() VirtualQueryEx failed for QString(0x%1).arg((quintptr)targetAddr, 0, 16); return; } if (mbi.State ! MEM_COMMIT || !(mbi.Protect PAGE_READABLE)) { qDebug() Address not committed or not readable: QString(0x%1, State%2, Protect%3) .arg((quintptr)targetAddr, 0, 16) .arg(mbi.State, 0, 16) .arg(mbi.Protect, 0, 16); return; } // 此时再调用 ReadProcessMemory 才有意义从那以后我每次写内存读取逻辑都强制走一遍VirtualQueryEx预检——它多花 0.1ms但能省掉 90% 的调试时间。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →