微信PC逆向:接收消息(一)

这篇文章主要分析一下微信消息接收与发送的一些相关内容。我会首先把微信消息的发送和接收的技术要点和一些概念罗列一下,好让大家有个大致的概念。最后会再絮叨一下淘宝联盟。

0x01微信自动化的3种方式

目前为止自动化处理微信消息的方式主要有三种方式。

  1. 利用web协议
  2. 利用微信iPad协议
  3. 客户端逆向处理

首先说一下web协议,相比大家都了解或使用过python的itchat库。A complete and graceful API for Wechat. 微信个人号接口、微信机器人及命令行微信,三十行即可自定义个人号机器人,这是这个github库的介绍。这个库主要是利用了网页版本的微信提供的api接口。但是随着微信官方的逐步封禁,目前只有很少一部分人可以正常使用网页版微信。大家可以自行登录https://wx.qq.com/ 查看自己是否还能登录网页版微信。
如果登录后显示 1203 错误代码,或者出现下面这个提示信息,都是网页版本不能使用的:

为了你的帐号安全,此微信号不能登录网页微信。你可以使用Windows微信或Mac微信在电脑端登录。Windows微信下载地址:https://pc.weixin.qq.com Mac微信下载地址:https://mac.weixin.qq.com

查询了一下,网上关于 微信网页版的猜测主要是 第一步新注册用户不可以登录网页版微信,之后逐步封禁17年之前注册的,但是超过一段时间没有使用过网页版微信的用户。所以现在想要通过itchat制作一个微信机器人的想法就破灭了,只能寻求其他的方案。

再来说一下第2种方式,利用iPad协议来实现微信消息的自动收发,pass

最终选择了第3种方案,即利用逆向工程来搞定它。这个方案说难也不难,说稳定也不稳定。

为什么说微信逆向说难也难,说稳定也不稳定呢。这个可能也是是逆向工程的特点,因为是对内存地址进行直接操作,因此如果编译之前的源代码的数据结构或代码结构发生改变,那么汇编代码的结构就会相应的发生改变,同时导致一些函数地址不确定。但是一旦思路确定,所做的工作就是不断的调试分析。下面就主要说说微信消息的接收和发送。

0x02微信的一些基本概念

  1. 微信ID
    刚注册的一个微信,有系统配置的一个wx_id号,大家应该都记得,一旦修改了微信号,那么显示的微信号就变成了你自己设置的那个微信号,但是在微信系统内部,依然保留着这个wx_id,这个wx_id和你的微信号保持着一一对应的关系。这个做一个简单的了解即可。

  2. 微信消息
    微信消息分为3种消息——个人消息、群消息以及公众号(企业号)消息。这3种消息数据在内存中的位置稍有差别,我们这里先只关注个人消息。不知道大家有没有注意过PC版本的微信收到消息时,会将最新的消息置顶(不包括认为置顶消息),即未读消息会按照到达时间进行排序,这将成为我们进行消息接收的利用点。因为最新的消息都在最上面,那么我们就只需要找到这个位置的消息即可实现消息的接收。

  3. 个人微信消息的类型
    微信消息的类型有多种,如文本、语音、图片、视频、红包等等,这个消息的类型还是很复杂的,本文也只关注文本消息。在查找到的资料中,得到了微信消息类型的代码如下,该代码位于 [[esp]]+0x30 的位置:

    [01文字] [03图片] [31转账XML信息] [22语音消息] [02B视频信息]

0x03微信消息的接收

借鉴了大佬的思路,通过CE和OllDbg来寻找微信消息等信息的地址。主要思路为:

  1. 首先利用CE搜寻消息的地址,使用OD附加WeChat.exe进程,然后在消息存储的地方下内存写入断点
  2. 按F9运行OD,再使用另一个微信2给处于调试中的微信1发送一条消息,这个时候程序会在断点处停下,即进入准备处理消息的状态,此时堆栈返回地址中必定会有一个函数是用来接收消息的;
  3. 点击 K 查看调用堆栈,在堆栈中通过栈回溯方法寻找该函数的地址,在该函数地址处下断点,按F9运行;
  4. 这个时候查看esp的值,并点击follow in dump,追踪[[esp]]前后的数据;
  5. 通过查看[[esp]]前后的数据,可以看到我们发送的消息以及发送者的微信wx_id

下面将详细说明上面每一条思路的具体操作

1、利用CE搜索消息地址

CE可以根据字符串来寻找此时字符串所在的内存地址,在CE中字符串类型选择字串。使用另一个微信给该微信发送一个字符串,在CE中进行搜索将得到许多包含该字符串的地址;再重复一两次该操作,最终包含该字符串的地址将被缩减到两三个,然后选中放到下面区域,全选之后,右键【更改记录->类型】,长度改为30或40均可,就是让这个地址的字符串显示
的多一些,然后可以看到另外两个为空,或者有一些乱码(多重复几次),只有一个地址的字符串始终包含着《 msg source》。

CE寻找字符串
记下那个包含《msg source》的地址,那个地址就是未被处理过的字符串所在的地址,接下来微信消息处理函数将处理这个地方的字符串。

2、 在OD中寻找接收函数的地址

关闭CE,使用OD附加WeChat.exe进程,然后Ctrl+G跳转到刚才的那个地址,设内存写入断点,F9运行,使用另一个微信给该微信发送一条消息,微信将停止在该消息的地方,然后点击【K】,然后查看堆栈情况,这个时候堆栈中肯定会有一个函数的返回地址是消息的接收处理函数的地址。

消息处理函数的地址

3、在接收函数附近,寻找消息存放的地址

上面已经找到了该处理函数,既然我们得到了该函数要处理的地址,那么假如我们将断点设在处理函数这里,微信在处理函数的时候,就会将需要的参数压入到栈中,我们就可以利用esp或者ebp来找到这个时候的消息地址,从而找到偏移地址,因为上面找到的地址全是绝对地址,每一次微信加载到内存中的基址都会发生改变,因此重点是找到消息地址于基址的偏移地址。

微信消息的偏移地址

在上一步找到的函数的地址下断点,再发送一条消息,然后这个时候查看栈顶的值,然后选择数据追踪,在数据窗口翻一翻,就可以看到这个微信号刚才发送的消息以及这个微信号的微信wx_id。我们观察一下这个消息所在的地址,可以看到这个地址始终是在 [[esp]]+0x40的位置,除此之外,消息的地址在[[esp]]+0x68的位置。多重复几次上面几步操作,我们得到的结果都是一样,说明我们成功找到了该消息的偏移地址。

之后我们在进行dll注入的时候,hook该函数,然后计算[[esp]]+0x40和[[esp]]+0x68处的内容即可,这样我们就可以成功得到给我们发送消息的微信id以及消息的内容。

4、DLL注入代码

dll注入的方式有好几种,根据需要这里是利用线程注入的方式把我们的dll注入到微信的进程中。我们看一下下图,下图中显示了WeChat加载到内存中加载的WeChat自有的DLL模块。其中有一个dll模块我们可以关注一下,“WeChatWin.dll”,这个模块其实就包含着微信进行通信的一些核心功能,包括发送消息,接收消息、发送接收红包等功能。我们要hook某个函数,就需要找到这个函数的偏移地址。

其中HOOK 公式=WeChatWin.dll的基址+功能函数的偏移地址。基址在每次加载的时候都会改变,但是偏移地址一般不会改变。现在就可以利用WeChatWin.dll的基址和上面我们已经找到了接收消息函数的地址来计算接收消息函数的偏移地址。

通过CE或者OD都可以查看此时WeChatWin.dll,我们现在已经用OD附加了WeChat.exe进程,点击[L]即可查看加载的dll信息,这里我们看到WeChatWin.dll加载的地址是 0x6B430000,函数的地址为:0x6B735758,那么计算得到的功能函数的偏移地址为:0x6B735758-0x6B430000=0x305758。
WeChatWin.dll的地址

现在我们已经确定了函数的偏移地址,每次登陆微信,微信加载dll的地址都会变化,如果使用静态地址显然是不行的,利用Windows提供的CreateToolhelp32Snapshot和Module32First函数可以得到dll的基址信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32;
hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
me32.dwSize = sizeof( MODULEENTRY32 );
if( !Module32First( hModuleSnap, &me32 ) ){
printError( "Module32First" ); // Show cause of failure
CloseHandle( hModuleSnap ); // Must clean up the
return( FALSE );
}
do {
printf( "\n\n MODULE NAME: %s", me32.szModule );
printf( "\n base address = 0x%08X", (DWORD) me32.modBaseAddr );
} while( Module32Next( hModuleSnap, &me32 ) );
CloseHandle( hModuleSnap );

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__declspec(naked) VOID RecieveMsgHook()
{
__asm
{
mov ecx, 0x10CDCFE8
//提取esp寄存器内容,放在一个变量中
mov r_esp, esp
pushad
pushf
}
RecieveMsg();

__asm
{
popf
popad
jmp jumBackAddress
}
}

最后的话,给大家推荐一个开源库,其实微信整个内容的逆向还是很麻烦复杂的,搞懂了大概的原理和基本操作就行了,除非以这个为饭碗,我这个微信机器人通过逆向做了一部分也做不下去了,最后还是选择了其他人的方案。WeChatSDK提供方便操作PC端微信的超级接口,提供包括多开、防撤销、语音备份、消息发送、加好友等接口。第三方可以直接使用WeChatSDK来开发自己的应用,不用再在分析微信功能、协议上耗费精力,WeChatSDK替你完成所有这些事情。


题外话:关于淘宝联盟

最后,给大家普及一下淘宝联盟这个平台。淘宝联盟发展了十余年,主要目的是为了推广淘宝卖家的商品。淘宝联盟提供一些商家设置的优惠券(不显示在淘宝中,也就是一些平台所说的内部优惠券)和佣金(这个佣金就是商家会把你付款买东西的钱拿出一定比例作为你推广的资金),也就出现了所谓的购物返利模式。大家可以下载一个淘宝联盟这个APP,你会觉得自己发现了新大陆。我之所以做了一个机器人,一开始肯定也想通过这个赚点钱,但是人很少,又不屑于到处拉人推广,又觉得这是个实实在在的优惠,所以现在已经打算把这个机器人做成一个非盈利的账号

为什么会做这么一个吃力不讨好的东西呢?主要还是接触到了这个东西,最初用了一个熊猫省钱类似的返利机器人,它们说的是返利80%,于是我就找人开了一个高级账号验证了一下,发现他们实际返利比例不足50%,所以我就做了一个这个东西。

END。。。