微信PC端技术研究(3)-如何找到消息发送接口

微信PC端技术研究-如何找到消息发送接口
by anhkgg(公众号:汉客儿)
2019年2月18日

0x0. 前言

准备工具:Cheat Engine,OllyDbg,IDA。

前一篇(微信PC端技术研究(2)-保存聊天语音)已经说过CE是什么,也应用CE研究了如何保存微信语音,这篇继续使用CE和OD来研究一下微信的消息发送接口。

思路大概是这样:在消息框中输入内容之后,通过CE找到内容地址,然后通过内存断点来找到发送该数据的相关代码,从而找到消息发送接口。

0x2. 分析过程

查找关键数据地址

在输入框输入一个比较特别的文字内容(避免搜索时太多内存选项)后,使用CE搜索该内容地址。

由于已经知道确切的消息内容,很容易就能通过CE的Exact value->String来找到内容地址,修改内容多次筛选,最后留下两个结果(详细操作见上一篇文章)。

1.png

通过CE修改一下内存的内容,微信输入框中内容同步改变,说明这个内存地址就是输入框中内容地址,最终确认地址是2A1E1A8

接着在点击发送按钮之后,输入框内容会被清空,所以第一想法就是对内存地址下内存写入断点,可以找到发送过程中清空内容的代码。

打开OD,挂载到WeChat.exe进程,在右下角数据窗口Ctrl+G输入2A1E1A8,然后右键选择断点->内存写入断点。

2.png

F9让OD跑起来,然后点击微信发送按钮,没想到意外发生了,输入框内容清空了,但是断点却没有触发。

怎么回事?断点弄错?地址找错?暂时没有答案。

用CE多次重复前面的操作,地址依然是这个地址,断点就是不触发。

通过OD查看到,在输入框清空后,2A1E1A8的内容确实没有变化,和发送前一样,并且在重新输入新的内容之后,该内存内容同步更新。

所以结论就是输入框内容地址确实是2A1E1A8,但是清空输入框并不是清空该内存内容,猜测编辑框可能通过控制字符串长短来控制显示的,清空输入框内容就是设置字符串长度为0。

找到输入框类

清空输入框没有进展了,那怎么办呢?

尝试去找了其他数据,比如发送按钮的发送(S),发送按钮的提示内容不能发送空白内容等等,数据地址也可以很快找到,但是和我们的分析目标偏的太远了。

几番折腾后,作罢。

转念一想,清空不行,发送总的读取输入框中内容吧,那换成内存访问断点尝试一下。

依然是在右下角数据窗口Ctrl+G输入2A1E1A8,然后右键选择断点->内存访问断点。

3.png

完成后回到微信界面,没想到直接断下了,我还没点发送按钮呢。根据以前的经验,下意识就觉得是界面刷新显示文字触发了断点,这可能会影响分析,根本没办法通过发送按钮来触发内存访问断点。

4.png

一般解决方法有:

  1. 条件断点。也就是屏蔽掉刷新界面触发的断点,但是好像内存断点不支持条件断点啊,要么通过脚本来完成,好麻烦。
  2. 找其他切入点。废话,清空那边的路都断了,死心吧。
  3. 其他我不知道的...

放弃了一般的解决方法,我决定看看本次断点究竟干嘛了。

注意到断点的位置不是微信模块WeChatWin.dll中,而是在msftedit.dll,很少见的一个模块。根据目录可以看到是微软系统的一个模块,名字中的edit也可以看出这应该是一个编辑框相关的模块。

可执行模块, 条目 20
 基址=6F050000
 大小=00094000 (606208.)
 入口=6F05D53D msftedit.<ModuleEntryPoint>
 名称=msftedit (系统)
 文件版本=5.41.21.2510
 路径=C:\Windows\System32\msftedit.dll

好像和我们的分析目标很贴近嘛,在OD中数据窗口右键断点->删除内存断点,然后按下Alt+F9回到用户模块领空,也就是跳过系统模块的代码,直接回到微信的模块代码中,省过对系统代码的分析。

看到返回到6E20CCC2这个地址,上一行代码就是调用msftedit.dll的函数,我们对其下一个断点,鼠标点击到6E20CCBF这行代码,按下F2下一个int 3断点,然后F9跳过本次分析。

5.png

OD继续断下,此次直接断在了6E20CCBF这个位置,可以看到call调用了msftedit.6F05AD69,这是个什么函数呢?

6.png

既然msftedit.dll是微软模块,那么肯定是有符号的嘛,嘿嘿。

这里可以直接在OD中加载符号来分析,使用方法是:

1.在WingDbg目录下拷贝dbgeng.dll,dbghelp.dll,srcsrv.dll,symbolcheck.dll,symsrv.dll,symsrv.yes,一共6个文件至OD目录下。

2.打开OD,设置符号路径。调试--->选择符号路径。

3.设置StrongOD的插件选项。选择加载符号。

原文:https://blog.csdn.net/sr0ad/article/details/8253311 

但是只支持本地符号,也就是得自己下载了模块对应符号到本地,OD设置符号文件路径后,才能正常使用,有点麻烦。

我这时候一般就会使用IDA了,因为它会自己在线下载模块对应的符号,很方便。

用IDA打开msftedit.dll,等待些许时间,IDA下载符号,解析等等完成后,我们去找到msftedit.6F05AD69对应的函数究竟是个什么东西。

但是这里msftedit.6F05AD69的模块基址是6F050000,而IDA解析使用的是默认基址0x6FCD0000,要么修改IDA解析基址为6F050000,等待IDA重新解析,要么通过偏移计算对应地址。

再解析等太久,直接计算吧,所以要安利我写的一个小工具(偏移计算工具),能够快速计算地址,具体使用见相关文章。

7.png

再IDA中按下g,输入6fcdad69,找到msftedit.6F05AD69对应函数为CTxtEdit::OnTxInPlaceActivate

8.png

很明显通过名字OnTxInPlaceActivate可以看出是编辑框中文字激活状态(显示)下就会触发该函数,这不是重点。

重点看CTxtEdit,不言而喻,这就是msftedit.dll中实现的编辑框的类。

如果写过MFC相关代码,应该很快就能想到CTxtEdit肯定还有其他读内容、写内容的函数,叫做GetXXX或者SetXXX

在IDA的函数列表中翻看一下,果然很快就找到了CTxtEdit::GetTextExCTxtEdit::SetText

9.png

但到底这两个函数是不是编辑框读写内容的函数呢,我们对这两个函数下断点试试,通过工具算到在ID调试中这两个函数的相应地址为6f0684376f056d37

10.png

在OD的底部命令窗口输入bp 6f068437bp 6f056d37,删掉之前CTxtEdit::OnTxInPlaceActivate的断点,然后F9跑起来。

11.png

回到微信界面,这次能够正常显示了,点击发送按钮。OD触发断点,断在了6f068437也就是CTxtEdit::GetTextEx上,很明显这是发送函数在读取输入框内容。

12.png

回溯找到发送函数

此时的调用堆栈是这样的:

调用堆栈 
地址       堆栈       函数过程 / 参数                       调用来自                      结构
0026E280   6F06842D   msftedit.6F068437                     msftedit.6F068428             0026E3FC //CTxtEdit::GetTextEx
0026E400   6E20D239   包含msftedit.6F06842D                   WeChatWi.6E20D233             0026E3FC
0026E43C   6DBD38EB   包含WeChatWi.6E20D239                   WeChatWi.6DBD38E8             0026E438 //TxtEdit_GetText
0026E5AC   6DC15B65   ? WeChatWi.6DBD3860                   WeChatWi.6DC15B60             0026E5A8 //sendBtn_GetText
0026E60C   6DC15DEE   WeChatWi.6DC15B10                     WeChatWi.6DC15DE9             0026E608 //sendbtn_click
0026E618   6E20BFB8   WeChatWi.6E20BEF4                     WeChatWi.6E20BFB3             0026E614
0026E62C   6E20362E   WeChatWi.6E20BF90                     WeChatWi.6E203629             0026E628
0026E6CC   6E203589   WeChatWi.6E2035A7                     WeChatWi.6E203584             0026E6C8
0026E820   6DC53695   ? WeChatWi.6E20352E                   WeChatWi.6DC53690             0026E81C
13.png

在OD中回溯调用堆栈跟踪返回到WeChatWi.6E20D239,看到右侧堆栈窗口已经获取到输入框中内容,证明前面的分析没有问题。

14.png

再次回溯两层到WeChatWi.6DC15B60,可以看到堆栈中的参数依然是获取到的输入框内容。

[0026E5E4] = 0828C070
[0828C070 + 4] = 0828CAF0 => a12bcAAAAA
15.png

此时函数首地址是WeChatWi.6DC15B10,进入到IDA中对应函数100d5b10(你要问我为什么此时进入IDA查看?我只好说其实这个步骤花费了很多时间,一边OD调试,一边IDA辅助确认等等,过程并没有这么顺利,篇幅原因省略),然后按下x回到上层函数,看到如下代码:

16.png

看到click很明显可以看出这就是发送按钮的响应函数了(相关知识可以了解duilib编程,微信界面是duilib实现的)。

到目前找到了发送消息的函数,但还并不是消息发送接口,这还只是界面的操作函数,具体发送消息接口应该在该函数内部被调用。

有技巧找到发送接口

先粗略地在OD中跟一遍WeChatWi.6DC15B10的代码逻辑,函数很多,没法很快确认哪个函数是消息发送接口。

截取部分代码感受一下,大概11个函数。根据OD跟的逻辑大概是sendBtn_GetText_10093860->sub_100DD340->sub_100C50C0->sub_10094100->sub_100DD9D0->sub_100C4450->sub_10323DF0->sub_100DE120

if ( sendBtn_GetText_10093860(a1->unk_560, (int)&savedregs, a2, a3, msg) <= 0 )// 这里是获取msg
  {                                             // x
    //省略一大段逻辑
  }
  if ( msg[0] != msg[1] )
  {                                             // x
    //省略一大段逻辑
  }
  if ( sub_100DD340() )
  {                                             // x
    //省略一大段逻辑
    sub_1047C070(&v34, v23);
    sub_100DB8C0((int)a1_, v34, v35, (int)v36, v37, (int)v38, v39, v40, (int)v41, msg_);
  }
  if ( sub_100C50C0((_DWORD *)(a1_->unk_558 + 2528), (int)msg, (int)v43) )
  {
    sub_10094100((_DWORD *)a1_->unk_560);//
    sub_100DD9D0(msg);                          
    sub_100C4450((_DWORD *)(a1_->unk_558 + 2528), (_msg *)msg);// 
    v31 = sub_10323DF0();
    sub_100DE120(v31, (int)a1_, (int)sub_100D6C40, 0, v40, (int)v41, msg_);// retn 18
    v12 = 1;
  }
  else
  {
    //省略一大段逻辑
    sub_10108D60(v30, *(&a1_->unk_558 + 1), v33, (int)v34, v35, v36, (int)v37, v38, v39, v40, v41);
  }

通常通过调试每个函数的参数、返回结果等基本可以猜测到函数功能,然后来找到消息发送接口。

但这里我偷懒了,因为参数结构复杂,一时半会没法找到关键点,有点晕了。

所以我通过排除法来一一筛选函数,最多11次左右就能找到消息发送接口。举个例子,如果sub_100DD340是消息发送接口,在我手工屏蔽其功能之后,消息肯定发不出去了,那么我就可以通过看到的结果(是否发送成功)来确认sub_100DD340是不是要找到的消息发送接口。

具体屏蔽方法:

  1. 通过IDA或OD进入sub_100DD340函数内部,找到函数结尾,找到retn xx类似代码
  2. 用OD在sub_100DD340函数开始修改汇编代码为retn xx,双击输入retn xx即可

这样sub_100DD340函数直接在入口就返回了,功能没有了,也保证了函数调用时的栈平衡。

17.png

在确认sub_100DD340并没有影响消息发送之后,通过右键撤销选择处修改恢复修改的内容。

如此重复筛选其他的函数,最终确认sub_100C4450为发送消息函数。代码接口如下:

sub_100C4450((_DWORD *)(a1_->unk_558 + 2528), (_msg *)msg);// 

msg是发送内容,a1_->unk_558 + 2528)是当前聊天窗口的好友信息,包括wxid和名字之类的信息。

18.png

但作为接口依然不够简洁,需要构造好友信息,比较复杂,所以继续深入sub_100C4450内部,看看是否能够找到最简单的接口,比如:

sendmsg(wxid, msg); //传入发给谁,发什么即可

sub_100C4450内部依然很复杂,使用和前面同样的方式,先大致跟一遍执行流程,然后通过排除法逐个筛选。

if ( !sub_100C43D0(msg_.buf, msg_.len, msg_.maxlen, wxid_) )// 是不是全是特殊字符\r\n\t等,是返回1,不是返回0
{
sub_1007D390();
msg_packet = sub_102DA4A0((int)wxid, (int)&v67, msg__, &unk, 1);// 数据打包,发送
sub_100494E0(msg_packet_, (size_t)msg_packet);//
sub_1004B550(&v67);              //
v11 = sub_102478D0();
v12 = sub_10402C10((int)v11);
v89 = (void **)v13;
if ( sub_10402C10((int)msg_packet_) != v12 || v14 != v89 )
{
    if ( sub_100C6770(this_) )        // 
    {
    sub_1004BBF0((int *)&msgpacket);//
    sub_10056940((int *)&msgpacket, (size_t)msg_packet_);// 
    sub_100C56D0(this___, (size_t)&msgpacket, 1);
    sub_10081210((LPVOID *)&msgpacket);
    v16 = sub_100C0EC0();
    sub_10247250((int **)v16, (int)path);
    }
}
}
if ( (signed int)(msg->msgend - (unsigned int)msg->msg) / 0x24 != 1 )
v9 = sub_10323DF0();
sub_10324E70(v9, msg_.len, msg_.maxlen, (int)wxid_, (int)path);
sub_100ADA10(&msg___);

这一次筛选屏蔽的方法换一种,直接在某个函数执行完成之后,通过jmp跳到sub_100C4450结尾,如果某次消息发送成功,最后执行的函数就是我们要的接口。

19.png

很幸运,这次在第三个函数就找到了消息发送函数sub_102DA4A0,看看它的参数:

sub_102DA4A0((int)wxid, (int)&v67, msg__, &unk, 1);
sub_102DA4A0@<eax>(int wxid@<edx>, int a2@<ecx>, wxstring *msg, _DWORD *a4, int a5)

下图是调试中看到的数据,确认接口没有问题。至于其他两个参数,经过分析是用于接收输出的,没有实际作用,在此不赘述。

20.png

如此分析消息发送接口的工作完成,找到了和预期基本一致的接口函数。

0x3. 总结

篇幅好像有点长了,最后做一下此次分析的总结:

  1. ce找到编辑框中的内容内存
  2. 发送后,编辑框内容删除,写断点无效,神奇,猜测通过设置长度控制显示
  3. 改为内存访问断点,进入界面就会断下,徘徊几次后,决定分析,没想到找到了关键点CTxtEdit::OnTxInPlaceActivate
  4. 知道编辑框使用了msftedit.dll的CTxtEdit的类,用ida找到符号
  5. 查询类似getvalue的接口,找到SetText、GetTextEx等,对这两个函数下断点
  6. 果然断下,回溯找到了发送的消息响应函数
  7. 详细分析响应函数,多次通过retn、jmp排除,找到真正发送消息函数,最后分析出接口函数

此次分析中CE找到地址是第一步非常关键的点,直接就进入了函数调用堆栈内部,对此次分析作用非常明显。

再就是在发送消息响应函数内部,逐个分析找到消息发送接口函数中,通过修改指令来屏蔽函数功能来确认函数功能,比每个函数去分析参数猜测确认功能来的更快,效果更明显。

调试工具非常重要,动(OD)静态(IDA)分析结合能够提高分析速度。

OD适合分析函数参数、解析数据结构、确认函数功能,IDA适合分析函数逻辑、整体函数结构、代码框架等等,各有优势。

最后,再次安利一下开源项目https://github.com/anhkgg/SuperWeChatPC,此次分析的发送消息接口也会在后续合入到项目中,欢迎starPR

相关文章:

  1. 微信PC端技术研究(2)-保存聊天语音

  2. 偏移计算工具

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 微信PC端技术研究-保存聊天语音by anhkgg(公众号:汉客儿)2019年1月31日 2.6.6.28 0x0...
    anhkgg阅读 687评论 0 0
  • Windows 常用消息大全 表A-1 Windows消息分布 消息范围说 明 0 ~ WM_USER – 1系统...
    北风知我意阅读 1,983评论 0 0
  • 9102年了,我想大部分人使用微信的频率应该都会高于QQ了吧。 以前在QQ传文件的时候,哪里会想到会有文件大小限制...
    anhkgg阅读 8,200评论 0 0
  • 哎,真的不好说,强烈的感觉是真的中了,这种感觉,不知道为什么,身边的人肯定会很开心,但于我而言,真的不是那么的强烈...
    马橙澄阅读 143评论 0 0
  • 我的宝贝又长大一岁了,她已经亭亭玉立,她已经年近芳华。都说″豆蔻梢头二月花″,在我心里,她已灿然如夏。依记初始,那...
    安之若素_a23b阅读 129评论 0 0