WeTool逆向:借用别人的成果 打造自己的程序
收藏

本文作者:鬼手56(信安之路病毒分析小组成员 & 信安之路 2019 年度优秀作者)

成员招募:信安之路病毒分析小组寻找志同道合的朋友

什么是 WeTool

百度直接搜索 WeTools 就可直接找到 WeTools 官方网站

这是一款专业的微信粉丝和社群的管理工具,是目前市面上微信 HOOK 做的非常不错的一款产品。不管是从产品设计角度还是用户体验角度或者是产品安全角度来看都无可挑剔。具体功能各位可以去官网看相关介绍,这里就不多做说明。

目前官网已经不提供免费版本下载了,我会在文末放出免费版本的安装包。

前置知识&基于 WM_COPYDATA 的消息模型

进程通讯相关知识

WeTool 的客户端和服务端之间的采用 WM_COPYDATA 的方式进行进程通讯。要想逆向 WeTool,就必须理解 WM_COPYDATA 消息模型。

在 Windows 程序中,各个进程之间常常需要交换数据,进行数据通讯。常用的方法有

  • 使用内存映射文件

  • 通过共享内存

  • 使用 SendMessage 向另一进程发送 WM_COPYDATA 消息

比起前两种的复杂实现来,WM_COPYDATA 消息无疑是一种经济实惠的一种方法

WM_COPYDATA 的相关知识

我们可以用下面的函数发送 WM_COPYDATA 消息

SendMessage(hwnd,WM_COPYDATA,wParam,lParam);

其中,WM_COPYDATA 对应的十六进制数为 0x4A,wParam 设置为包含数据的窗口的句柄。lParam 指向一个 COPYDATASTRUCT 的结构:

typedef struct tagCOPYDATASTRUCT{  DWORD dwData;//用户定义数据  DWORD cbData;//数据大小  PVOID lpData;//指向数据的指针}COPYDATASTRUCT;

其中 dwData 既可以是数组,也可以是结构体。能发送这两种数据类型,其实就已经满足大部分的进程通讯需求了。

WM_COPYDATA 的缺点

这种通讯方式有一个缺点:接收方必须是窗口程序。所以要想实现 exe 和 dll 之间的通信,就必须在 dll 里注册一个窗口类,并将其显示方式设置为隐藏。

示例代码

发送端:

COPYDATASTRUCT show_qrpic;show_qrpic.dwData = WM_ShowQrPicture;show_qrpic.cbData = 0;show_qrpic.lpData = NULL;//发送消息::SendMessage(hWeChatHelp, WM_COPYDATA, (WPARAM)hWeChatHelp, (LPARAM)&show_qrpic);

接收端:

LRESULT CALLBACK WndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam){  if (Message == WM_COPYDATA)  {    COPYDATASTRUCT *pCopyData = (COPYDATASTRUCT*)lParam;    switch (pCopyData->dwData)    {      //显示二维码    case WM_ShowQrPicture:    {      GotoQrCode();      HookQrCode(QrCodeOffset);    }    break;      }}

逆向WeTool

定位 WeTool 核心模块

我们打开 WeTool,并登陆微信。WeTool 分为两个部分,一个是客户端,就是现在大家所看到的这个,负责显示 UI 和数据,还有一个是服务端,负责从微信进程获取数据,并将数据传到客户端。WeTool 的核心功能都在服务端里面。所以我们要做的就是找到 WeTool 的核心模块。

打开 PCHunter,选中微信进程,右键->查看进程模块,并按照模块路径排序

接着在这里我们看到了一个特殊的东西,之所以特殊是因为这个模块的路径既不属于微信,也不属于系统 dll

接着右键->定位到模块文件,找到这模块所在的路径。这个文件夹的名字 WeToolCore 其实已经暴露了这个 dll 的功能了。没错,这个就是我们要找的 WeTool 的核心模块。

这个文件的原始路径是在 WeTool 文件夹下的 WeChatVersion 这个子文件夹下。WeTools 启动后,会先检测当前微信版本,并根据不同的微信版本注入不同的 dll。

这里我们选择分析 2.6.5.52 这个版本的 WeChatHelp。

分析 WeChatHelp

接下来我们用 IDA 载入 WeChatHelp

在左侧的函数窗口,能直接看到一堆带有名字的函数。之所以能看到这些带有名字的函数,是因为 WeTools 将这些函数作为接口导出了

我们可以使用 LoadPE 查看其导出函数。

分析 SendMsg 函数

接下来,为了和微信逆向的过程进行对比,我们选择分析 SendMsg 这个函数,双击 SendMsg,并按 F5 查看伪代码。

这里将一个全局变量赋给了 lParam,并且调用 SendMessage,参数为 lParam 和 0x4A。根据之前我们学习过的 WM_COPYDATA 通信模型,很容易就能看出这里并不是真正的 SendMsg 函数,而是和客户端进行通信的函数。

那么怎么找到真正的 SendMsg 函数呢

选中赋值给 lParam 的这个常量,右键,跳转到交叉引用

选择第二个调用

这里实际上是在判断 lParam 的消息类型是否是发送消息,如果是则调用 sub_100050F0 发送文本消息,如果要翻译成源码,就是这个样子

接下来进入 sub_100050F0 这个函数

重点关注和数字相关的地方,比如 0x2EB4E0。接着用 OD 附加微信(记得退掉 WeTool)

用 RVA 的方式跳转到 WeChatWin 模块 0x2EB4E0 的地方,下断点

然后随便发送一条消息,程序断下

接着通过堆栈,找到上一层返回地址,下断点,让程序再次断下,分析函数参数

此时 edx 指向消息接收者的微信 ID

ebx 指向消息内容

那么要想确定这个是不是发送消息的 call,只需要修改发送的消息内容。接着 F9 运行

此时消息内容已经被修改,我们就已经找到了发送消息的 call。

这里为了节省篇幅我只分析了 SendMsg 函数内的一个地址,事实上,只要是函数内有的偏移,都需要逐个排查一遍,一个多多个其实没什么差别,只不过多花点时间而已。

WeTool 逆向和微信逆向的对比

微信逆向

请看我上一篇文章,《原创 PC微信逆向:发送与接收消息的分析与代码实现

这里我们找到发送消息的接口,总共需要以下几步:

  1. 寻找切入点:消息内容

  2. 给消息内容下内存访问断点,栈回溯分析

  3. 验证是否是发送消息的 call

WeTool 逆向

而 WeTool 逆向则完全不同,只需要两步

  1. 找到函数内的所有偏移

  2. 逐个排查偏移,并最终确定 call

明显直接逆向 WeTool 会比逆向微信要省很多时间。特别是在一些不可控的情况,比如找不到切入点,无法确定是不是关键 call 等等。

方法扩展

只要掌握了上面的方法,就能搞定 WeChatHelp 里导出的所有函数。这些你都可以直接拿过来,变成自己的程序。事实上我的微信机器人,其中百分之三十的功能就是来自于 WeChatHelp。

最后 附上 WeTool 安装包和微信机器人的成品地址(点击阅读原文直达):

链接:https://pan.baidu.com/s/1GHjFhkdlUCxjIRfwyo89sA 提取码:d5v6

https://github.com/TonyChen56/WeChatRobot

官方公众号