ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Qt与Windows API实战:逆向读取微信用户信息源码解析

Qt与Windows API实战:逆向读取微信用户信息源码解析 简介这份资源是一套面向Windows平台逆向分析入门与进阶学习者的实战源码聚焦进程内存读取与用户信息解析场景界面层采用Qt搭建底层进程操作与内存访问则使用Windows原生方法实现适合具备一定C基础、希望理解反编译与内存结构分析流程的开发者参考。压缩包共6个文件以cpp源码与h头文件为核心配合pro工程文件、ui界面描述文件及user配置整体仅6KB结构精简便于快速导入Qt环境阅读与调试。目前已有1518人学习下载说明该方向具备一定关注度。通过阅读源码读者可以了解如何借助Windows接口定位目标进程、读取指定内存区域并结合Qt界面完成数据展示从而掌握逆向分析中进程枚举、内存读写与结构解析的基本思路为后续深入学习反编译与内存结构分析打下实践基础。1. 从一份 Qt 源码说起Windows 下读取微信用户信息的逆向路径很多人第一次接触 Windows 逆向都是从「读一个进程的内存」开始的。这份 WeChatUserInfo 源码包做的事情很具体用 Qt 搭一个界面通过 Windows 原生 API 去枚举进程、打开目标进程、读取内存把微信进程里跟当前登录用户相关的信息捞出来显示在窗口上。它不是一个完整的商业级工具而是一份能跑通「进程枚举 → 句柄获取 → 内存读取 → 数据解析」这条链路的教学型源码适合想入门 Windows 逆向分析、又不想一上来就啃汇编和调试器的开发者。源码结构很干净mainwindow.cpp负责界面逻辑和按钮响应mainwindow.h声明槽函数和成员变量main.cpp是 Qt 应用入口mainwindow.ui是 Qt Designer 拖出来的界面文件.pro是 qmake 工程配置。换句话说它把「界面」和「底层读取」这两件事拆得很清楚你改界面不影响读取逻辑改读取逻辑也不动 UI。接下来我会按「这份源码怎么用 → 关键 API 怎么调 → 参数怎么设 → 哪里容易翻车」的顺序把它拆成能照着复现的步骤。2. 环境搭建与工程结构把 Qt 和 Windows API 接起来2.1 为什么选 Qt 而不是 MFC 或纯 Win32这份源码用 Qt 做界面不是因为它比 MFC 高级而是因为 Qt 的信号槽机制让「点击按钮 → 触发读取 → 刷新界面」这条链路写起来更直观。mainwindow.ui里拖一个按钮和一个文本显示区在mainwindow.cpp里用connect把按钮的clicked信号绑到自定义槽函数上槽函数里再去调 Windows API。整个过程不需要手动处理消息循环也不用管窗口重绘。另一个现实原因是跨版本兼容。Qt 的 qmake 工程在不同 Windows 版本上迁移成本低你换一台机器只要 Qt 版本对得上.pro文件基本不用改。而 MFC 工程一旦换了 VS 版本经常要重新配包含目录和库目录对新手不友好。常见做法是装 Qt 5.12 或 Qt 5.15 的 MinGW 版本因为这两个版本在 Windows 上编译这类调用 Win32 API 的工程最稳。如果你装的是 Qt 6QApplication的头文件路径变了.pro里要加greaterThan(QT_MAJOR_VERSION, 4): QT widgets否则编译会报找不到QApplication。2.2 工程文件逐个拆每个文件负责什么先把源码包解压你会看到这几个文件文件名作用改动频率WeChatUserInfo.proqmake 工程配置声明模块和源文件低main.cpp程序入口创建QApplication和主窗口极低mainwindow.h主窗口类声明含槽函数和成员变量中mainwindow.cpp界面逻辑 Windows API 调用核心高mainwindow.uiQt Designer 界面布局中WeChatUserInfo.pro.userQt Creator 本地配置不要提交不碰.pro文件里最关键的是QT core gui和greaterThan(QT_MAJOR_VERSION, 4): QT widgets以及LIBS -lpsapi或-luser32这类链接选项。读取进程内存要用到OpenProcess、ReadProcessMemory这些在kernel32.dll里Qt 的 MinGW 默认能链到但如果你用 MSVC 编译器可能要在.pro里显式加LIBS -lkernel32。mainwindow.h里一般会声明一个槽函数比如void onReadButtonClicked();以及一个QTextEdit *outputArea;用来显示结果。mainwindow.cpp里则是connect(ui-readButton, QPushButton::clicked, this, MainWindow::onReadButtonClicked);这样的绑定。2.3 编译前必须确认的三件事第一Qt 版本和编译器要匹配。如果你下载的是 MinGW 版 Qt就用 Qt Creator 里对应的 MinGW 套件如果用的是 MSVC 版 Qt就要装对应版本的 Visual Studio Build Tools。混用会报fatal: cannot mix incompatible qt library这类错误本质是 Qt 库和编译器 ABI 不一致。第二.pro.user文件不要跨机器拷贝。它记录了本机的 Qt 路径和构建套件换机器后 Qt Creator 会提示重新配置直接删掉让它重新生成即可。第三确认目标进程的位数。如果你的 Qt 编译出来是 32 位程序去读 64 位微信进程的内存OpenProcess会失败GetLastError返回 5拒绝访问。解决办法是编译 64 位 Qt 程序或者用 64 位 Qt 套件重新构建。# 在 Qt Creator 的构建目录下可以用命令行确认生成的 exe 位数 dumpbin /headers WeChatUserInfo.exe | findstr machine # 32 位输出 x8664 位输出 x64这段命令用来验证你编译出来的程序是 32 位还是 64 位。dumpbin是 Visual Studio 自带的工具如果你没装 VS可以用file命令在 Git Bash 里看或者直接在任务管理器里看进程有没有带(32 位)标记。位数不匹配是新手最容易翻车的地方现象是OpenProcess返回 NULL但代码逻辑看起来完全正确。3. 进程枚举与内存读取Windows API 的调用顺序和参数含义3.1 枚举进程从 CreateToolhelp32Snapshot 到 Process32Next读取微信用户信息的第一步是找到微信进程。Windows 下枚举进程最常用的方式是CreateToolhelp32Snapshot配合Process32First和Process32Next。这套 API 在tlhelp32.h里属于kernel32.dll。#include windows.h #include tlhelp32.h #include QString #include QDebug DWORD FindWeChatProcessId() { DWORD pid 0; // 创建系统进程快照TH32CS_SNAPPROCESS 表示只枚举进程 HANDLE snapshot CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot INVALID_HANDLE_VALUE) { qDebug() CreateToolhelp32Snapshot failed: GetLastError(); return 0; } PROCESSENTRY32 pe32; pe32.dwSize sizeof(PROCESSENTRY32); // 必须设置结构体大小否则 Process32First 失败 if (Process32First(snapshot, pe32)) { do { // 微信进程名通常是 WeChat.exe旧版可能是 WeChatApp.exe if (_wcsicmp(pe32.szExeFile, LWeChat.exe) 0) { pid pe32.th32ProcessID; break; } } while (Process32Next(snapshot, pe32)); } CloseHandle(snapshot); // 快照句柄必须关闭否则句柄泄漏 return pid; }这段代码的逻辑是先创建快照拿到一个包含所有进程信息的句柄然后用Process32First取第一条Process32Next往后遍历每次比较szExeFile是否等于WeChat.exe。找到就记录th32ProcessID并跳出循环。参数上要注意pe32.dwSize sizeof(PROCESSENTRY32)这行不能省。Process32First内部会检查这个字段如果大小不对直接返回 FALSEGetLastError返回 24ERROR_BAD_LENGTH。这是血泪经验很多人抄代码时漏了这行排查半天以为是权限问题。_wcsicmp是宽字符不区分大小写比较因为szExeFile是WCHAR数组。如果你用strcmp去比编译能过但逻辑永远不相等因为宽字符和窄字符的内存布局不同。3.2 打开进程OpenProcess 的权限参数怎么选拿到 PID 之后下一步是OpenProcess获取进程句柄。读取内存需要PROCESS_VM_READ权限如果还要查询模块信息再加PROCESS_QUERY_INFORMATION。HANDLE OpenTargetProcess(DWORD pid) { // PROCESS_VM_READ 允许读取进程内存 // PROCESS_QUERY_INFORMATION 允许查询进程信息如模块列表 HANDLE hProcess OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid); if (hProcess NULL) { DWORD err GetLastError(); qDebug() OpenProcess failed, error code: err; // 5 拒绝访问通常是权限不足或位数不匹配 // 87 参数错误通常是 PID 无效 } return hProcess; }OpenProcess的第二个参数bInheritHandle设为 FALSE表示子进程不继承这个句柄。第三个参数就是 PID。最常见的失败是错误码 5。原因通常有两个一是你的程序没有足够权限微信以更高权限运行二是位数不匹配32 位程序打不开 64 位进程。解决办法是以管理员身份运行你的 Qt 程序或者用 64 位套件重新编译。另一个坑是PROCESS_QUERY_INFORMATION在部分 Windows 版本上需要SeDebugPrivilege权限。如果你只是读内存可以只请求PROCESS_VM_READ减少失败概率。但如果你后续要遍历模块找基址就必须加PROCESS_QUERY_INFORMATION。3.3 读取内存ReadProcessMemory 的地址和缓冲区ReadProcessMemory是核心中的核心。它的原型是BOOL ReadProcessMemory( HANDLE hProcess, // OpenProcess 返回的句柄 LPCVOID lpBaseAddress, // 目标进程中的起始地址 LPVOID lpBuffer, // 你自己的缓冲区 SIZE_T nSize, // 要读多少字节 SIZE_T *lpNumberOfBytesRead // 实际读了多少 );在微信用户信息这个场景里你通常不是直接读一个固定地址而是先通过模块基址加偏移的方式定位。比如微信的WeChatWin.dll加载到某个基址用户信息结构体在基址加某个偏移的位置。bool ReadMemory(HANDLE hProcess, uintptr_t address, void *buffer, size_t size) { SIZE_T bytesRead 0; BOOL ok ReadProcessMemory(hProcess, (LPCVOID)address, buffer, size, bytesRead); if (!ok) { qDebug() ReadProcessMemory failed at address: Qt::hex address error: GetLastError(); return false; } if (bytesRead ! size) { qDebug() Partial read, expected: size got: bytesRead; return false; } return true; }参数说明address是目标进程虚拟地址空间里的地址不是物理地址buffer是你自己进程里的缓冲区大小至少size字节bytesRead返回实际读取的字节数正常情况下等于size如果目标地址跨了不可读页可能小于size。常见错误码 299ERROR_PARTIAL_COPY通常表示地址无效或权限不够。如果你读的是一个动态分配的字符串指针要先读指针值再拿指针值去读字符串内容不能一步到位。3.4 从内存字节到用户信息解析偏移和字符串读出来的是一段原始字节要变成可读的用户信息需要按结构体布局解析。微信的用户信息通常包含昵称、微信号、手机号等字段这些字段在内存里可能是std::string或std::wstring的布局。struct UserInfo { char nickname[64]; char wxid[64]; char phone[32]; }; // 假设已经通过基址偏移拿到了 userInfoAddr UserInfo info; if (ReadMemory(hProcess, userInfoAddr, info, sizeof(UserInfo))) { QString nickname QString::fromLocal8Bit(info.nickname); QString wxid QString::fromLocal8Bit(info.wxid); ui-outputArea-append(昵称: nickname); ui-outputArea-append(微信号: wxid); }这里用fromLocal8Bit是因为微信在中文 Windows 上存的可能是 GBK 编码的窄字符串。如果实际是 UTF-8就要用fromUtf8如果是宽字符wchar_t用fromWCharArray。编码判断错了显示出来就是乱码这是第二个高频翻车点。偏移的获取方式常见做法是用 Cheat Engine 或 x64dbg 附加到微信进程搜索已知的微信号字符串找到地址后反推基址和偏移。这个过程需要反复验证因为微信版本更新后偏移会变。源码里如果写死了偏移换一个微信版本就可能读不到。4. 避坑与排查读不到数据时先查这五件事4.1 现象OpenProcess 返回 NULL错误码 5原因权限不足或位数不匹配。微信可能以管理员权限运行而你的 Qt 程序是普通权限或者你的程序是 32 位微信是 64 位。解决右键 Qt Creator 或编译出的 exe选择「以管理员身份运行」。如果还不行检查 Qt 套件是不是 64 位用dumpbin /headers确认 exe 位数和微信进程位数对齐。4.2 现象ReadProcessMemory 返回 FALSE错误码 299原因目标地址无效或者地址所在的内存页不可读。常见于偏移写错、基址没算对、或者目标模块还没加载。解决先用VirtualQueryEx查询目标地址的内存属性确认Protect字段包含PAGE_READABLE相关标志。如果地址是动态分配的确认指针链每一级都读对了。可以在每一步读完后打印地址和值逐级比对。4.3 现象读出来的字符串是乱码原因编码判断错误。微信可能存 GBK、UTF-8 或 UTF-16不同版本不一样。解决先读原始字节用十六进制打印出来看特征。GBK 中文两个字节高位都在 0x80 以上UTF-8 中文通常是三个字节E4-BF-A1这种模式UTF-16 会有明显的00间隔。根据特征选fromLocal8Bit、fromUtf8或fromWCharArray。4.4 现象编译报 fatal: cannot mix incompatible qt library原因Qt 库版本和编译器 ABI 不匹配或者系统里有多套 Qt 环境变量冲突。解决在 Qt Creator 里检查「构建套件」是否选对了 Qt 版本和编译器。如果命令行编译确认PATH里只有一套 Qt 的 bin 目录。删掉构建目录重新 qmake 一次。4.5 现象程序能跑但读不到微信进程原因微信进程名不是WeChat.exe。新版微信可能是WeChatApp.exe或者有多个微信进程。解决在FindWeChatProcessId里把进程名打印出来看看实际叫什么。也可以枚举所有进程用qDebug()输出szExeFile确认目标名称后再改比较逻辑。5. 进阶技巧用模块基址加偏移替代硬编码地址硬编码地址在微信每次更新后都会失效这是这份源码最大的局限。更稳的做法是动态获取模块基址再加固定偏移。GetModuleInformation配合EnumProcessModules可以拿到WeChatWin.dll的基址。#include psapi.h uintptr_t GetModuleBase(DWORD pid, const wchar_t *moduleName) { HANDLE hProcess OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); if (!hProcess) return 0; HMODULE modules[1024]; DWORD needed 0; if (!EnumProcessModules(hProcess, modules, sizeof(modules), needed)) { CloseHandle(hProcess); return 0; } uintptr_t base 0; int count needed / sizeof(HMODULE); for (int i 0; i count; i) { wchar_t name[MAX_PATH] {0}; if (GetModuleBaseNameW(hProcess, modules[i], name, MAX_PATH)) { if (_wcsicmp(name, moduleName) 0) { base (uintptr_t)modules[i]; break; } } } CloseHandle(hProcess); return base; }拿到基址后用户信息地址就是base offset。偏移的获取需要用调试器附加微信搜索已知字符串找到地址后减去基址。这个过程我一般会做三次验证重启微信、切换账号、重启电脑确认偏移在多次运行中稳定再写进代码。另一个技巧是用VirtualQueryEx遍历内存区域找可读且类型为MEM_PRIVATE的块在里面搜索特征字符串。这种方式比固定偏移更抗版本更新但性能差一些适合做辅助定位。验证读取结果是否正确最直接的办法是拿你已知的微信号去比对。如果你当前登录的微信号是test_wxid_123读出来的wxid字段应该完全一致。如果不一致先检查偏移再检查编码最后检查是不是读到了缓存或旧数据。从那以后我每次调这类内存读取都会先写一个最小验证只读一个已知字符串打印十六进制和解析结果确认链路通了再往上叠业务逻辑。希望帮到你。本文还有配套的精品资源点击获取
返回列表