写在前面
搞微信逆向也有一段时间了,实现了发送文本、图片、文件、名片、xml文章消息,以及获取好友列表,查询好友信息
上面这些功能足够做一个发单机器人。但对我来讲,并没有什么实质的用处,所以,又查了点无痕清粉的资料,配合通讯录就可以看看哪些好友偷摸把自己删了。
将不会介绍自动删除好友的东西,想做无痕清粉的自己研究哈(ps:被封不关我事)
微信版本
微信电脑版3.6.0.18
资料
两篇资料都来自看雪论坛:
Dump微信PC端的界面Duilib文件
微信无痕清粉分析过程-附源码地址
感谢KongKong20大佬
Duilib界面
思路仍然适用于微信电脑版3.6.0.18版本,IDA如果搜不到字符串,请在字符串窗口右键-Setup,勾选Unicode-16风格。
不过builder.Create的位置有所变化,在找到的Duilib字符串上面一个Call,追进去多观察下就能找到。
结论:
3.6.0.18版本添加好友时的确认按钮名字是“primary_button”
定位发送CALL
思路可以参考第二篇资料,这里主要说一下IDA怎么用吧,在字符串窗口搜索字符串,然后双击定位到数据段,在鼠标移动到字符串变量名上,按下x,查看交叉引用
在弹出的地址中双击,跳转到对应的地址,如果此处IDA已经处理完毕,可以在sub函数上按F5查看伪代码,如果没有反应则要等待IDA处理完毕。
放一段根据AddFriendHelper::eventproc定位到的伪代码:
switch ( a2 )
{
case 176:
......
break;
case 177:
if ( *(_BYTE *)(this + 56) )
......
break;
case 178:
if ( *(_BYTE *)(this + 56) )
......
break;
case 181:
v15 = *(_BYTE *)(this + 48);
......
break;
default:
return;
}
这里的176-181指示几种好友状态:
176(0xB0):被对方删除
177(0xB1):好友
178(0xB2):未知
181(0xB5):被对方拉黑
接着在IDA搜索opCode : %d
,这个字符串只有一处引用,找到以后根据偏移在OD中定位、下断,然后加好友,弹出窗口之前的放过去,弹出窗口之后,点击确定按钮,再次断下
接着在堆栈翻一翻,找到下面这个CALL:
Executable modules, 条目 126
基址=02E20000
大小=02680000 (40370176.)
入口=04767B60 WeChatWi.<模块入口点>
名称=WeChatWi
文件版本=3.6.0.18
路径=D:\Tencent\WeChat\[3.6.0.18]\WeChatWin.dll
0311A8DB 83EC 14 sub esp, 14 ; begin
0311A8DE 8BCC mov ecx, esp
0311A8E0 6A FF push -1
0311A8E2 50 push eax ; Unicode类型请求信息
0311A8E3 E8 E8734000 call 03521CD0 ; 返回请求信息结构体
0311A8E8 FFB5 1CFFFFFF push dword ptr [ebp-E4] ; opcode的值,1:请求好友状态,2:添加好友
0311A8EE 8D83 AC030000 lea eax, dword ptr [ebx+3AC] ; 好友的wxid
0311A8F4 83EC 14 sub esp, 14
0311A8F7 8BCC mov ecx, esp
0311A8F9 50 push eax
0311A8FA E8 C1744000 call 03521DC0 ; 返回好友信息结构,末尾是opcode
0311A8FF 8B8D 20FFFFFF mov ecx, dword ptr [ebp-E0] ; 句柄
0311A905 C645 FC 03 mov byte ptr [ebp-4], 3
0311A909 E8 82521000 call 0321FB90 ; 请求好友状态
前面还有一个完善堆栈的CALL要执行,具体参考下面的汇编代码。
这块坑不多,需要提一嘴,mov byte ptr [ebp-4], 3
这种修改ebp-0x4值的就不要执行了
不然所有的CALL都正常执行了,却会因为__security_cookie崩溃,不了解编译机制的话,容易卡半天。
具体原理
刚才定位到了一个switch-case的伪代码,当调用请求好友状态的CALL时,会调用这段代码所在的函数,找一个合适的地方Hook到状态码即可,尽量找一个0xB0-0xB5
只断一次的地方。
关键代码
调用
VOID __stdcall CheckFriendStatus(wchar_t* wxid) {
LocalFriendStatus = 0x0;
DWORD WeChatWinBase = GetWeChatWinBase();
DWORD CheckFriendStatusCall1 = WeChatWinBase + CheckFriendStatusCall1Offset;
DWORD CheckFriendStatusCall2 = WeChatWinBase + CheckFriendStatusCall2Offset;
DWORD CheckFriendStatusCall3 = WeChatWinBase + CheckFriendStatusCall3Offset;
DWORD CheckFriendStatusCall4 = WeChatWinBase + CheckFriendStatusCall4Offset;
DWORD CheckFriendStatusParam = WeChatWinBase + CheckFriendStatusParamOffset;
WxBaseStruct pwxid(wxid);
FriendStatusParamStruct FriendStatusParam;
char* swxid = new char[wcslen(wxid) + 1];
ZeroMemory(swxid, wcslen(wxid) + 1);
WideCharToMultiByte(CP_ACP, 0, wxid, -1, swxid, wcslen(wxid), NULL, NULL);
pwxid.fill1 = (DWORD)swxid;
pwxid.fill2 = wcslen(wxid);
wchar_t* message = (WCHAR*)L"我是";
__asm {
pushad;
pushfd;
mov edi, 0x6;
mov esi, 0x0;
sub esp, 0x18;
mov eax, esp;
mov dword ptr[eax], 0x0;
mov dword ptr[eax + 0x14], 0xF;
mov dword ptr[eax + 0x10], 0x0;
sub esp, 0x18;
lea eax, FriendStatusParam;
mov ecx, esp;
push eax;
call CheckFriendStatusCall1;
push esi;
push edi;
mov edi, message;
sub esp, 0x14;
mov ecx, esp;
push -0x1;
mov eax, edi;
push eax;
call CheckFriendStatusCall2;
// 这里改成0x2就是添加好友,0x1是请求好友状态
push 0x1;
lea eax, pwxid;
sub esp, 0x14;
mov ecx, esp;
push eax;
call CheckFriendStatusCall3;
mov eax, [CheckFriendStatusParam];
mov eax, [eax];
mov ecx, eax;
call CheckFriendStatusCall4;
popfd;
popad;
}
while (!LocalFriendStatus && CheckFriendStatusHooked) {
Sleep(10);
}
#ifdef _DEBUG
printf("wxid:%ws,status:0x%02X\n", wxid,LocalFriendStatus);
#endif
delete[] swxid;
swxid = NULL;
return;
}
Hook
__declspec(naked) void doHookVerifyUserResult() {
__asm {
pushfd;
pushad;
mov eax, [esi];
push eax;
call dealVerifyUserResult;
add esp, 0x4;
popad;
popfd;
call CheckFriendStatusNextCallAddress;
jmp CheckFriendStatusHookJmpBackAddress;
}
}
VOID HookFriendStatusCode(){
if (CheckFriendStatusHooked)
return;
DWORD WeChatWinBase = GetWeChatWinBase();
DWORD dwHookAddress = WeChatWinBase + CheckFriendStatusHookOffset;
HookAnyAddress(dwHookAddress, doHookVerifyUserResult, OldAsmCode);
CheckFriendStatusHooked = true;
}
写在后面
测完后发现有20个好友删了我,多数还是妹子,然后就emo了半天。
完整代码参考Github:ComWeChatRobot
有用请给个star,感谢感谢~
最后还是老一套,请勿用于非法用途,不然轻轻松松就能吃上几年牢饭。
需要图文教程留言就好,over~