在我的成品WeChatRobot里面实现了四个自动系列的功能,自动添加好友分享名片,自动收款,自动同意好友请求,还有自动聊天。今天分享一下如何实现自动添加好友分享名片。其他的三个功能思路大致相同。
想要实现自动添加好友分享的名片的功能,首先需要找到加好友的call,分析加好友需要用到哪些参数,然后在接收到好友名片推送的时候,取出这些参数,调用加好友的call。这样就能实现自动添加好友分享的名片了
首先思考一下一个加好友函数背后的编程逻辑,肯定是需要传入两个参数,第一个参数是需要添加好友的微信ID,第二个参数是加好友时发送给对方的消息。当我们点击发送朋友验证时,就会调用这个加人的call。
那么我们就可以从需要添加的好友的微信ID入手,先在当前窗口找到添加好友时用到的那个微信ID,然后通过对微信ID下内存访问断点,点击发送朋友验证,触发断点,再通过栈回溯,从而找到加人的call。
首先用CE搜索已删除好友的微信ID,将所有地址添加到下方地址栏
接着点击添加发送朋友验证,你会发现这是CE的结果有变化。这是因为当我们点击的时候,客户端会拿到我们当前点击的微信ID,然后放到一个变量里面,将这个变量作为参数传入到函数中。所以我们点击之后搜索的结果一定会有这个变量,接下来我们要做的就是找到这个变量。
那么怎么找到这个变量呢?通过下方地址栏的点击之前的数据和现在搜索结果中的点击之后的数据进行对比,就能找到作为参数传入到加人call里面的微信ID。
通过这个在线的文本对比的网站http://www.jq22.com/textDifference,放入点击前和点击之后的数据(A是点击前,B是点击后),就能筛选出两组数据中不同的地方。我们需要找到点击后的数据中有的地址,点击前的数据中没有的地址。
我这里找到了两处点击后不同的地址,接着我们直接在CE中搜索这两个地址,看看有哪些地址保存有这两个地址
从搜索结果中可以看到第一个地址已经变成了其他字符串,而第二个地址已经被一个指针保存,所以可以确定这个地址就是我们要的传入加人call的微信ID的地址。哈哈,这一步需要有点耐心,我也是找了好几次才找到。
使用OD附加微信,在找到的微信ID地址下内存访问断点,接着点击确定,此时断点断下,删除内存访问断点
这里我们看到在堆栈中的第二个返回地址中传入了要添加的好友的微信ID,那么这里就非常有可能是我们要找的加人的call
验证加好友call
我们在这里下一个断点,然后F9运行,让程序再次断在这个位置
此时我们修改这个微信ID,如果此时被添加的微信没有收到加好友消息的话,那么就说明这个call就是我们需要的call
F9运行,此时显示由于对方隐私设置,你无法将其添加至通讯录,说明这个call就是我们要找的加好友的call了
微信加好友call的分析
接下来分析一下加好友的call的参数
ebx指向的是微信ID的结构体,这个结构体是有五个成员的
中间的这一句汇编指令mov dword ptr ss:[ebp-0xE0],esp经过测试可以不需要写。
然后这一句将0xFFFFFFFF压入了堆栈,也就是-1。但是我们在这个call所在的寄存器并没有找到加好友时发送的消息。这个结构体是必须要找到的,所以我们继续分析它上面一个call
上面一个call传入的是消息内容,并没有拿到消息的结构体,所以我们继续再分析这个call上面的一个call
单步步过这个函数,这里push了一个6,这个参数是代表添加的渠道,群外加好友是6,群内加好友的E,名片推送是0x11
另外,这个call写代码调用的时候会比较麻烦,需要同时调用6个call,6个call里面少了任何一个微信都会奔溃
微信添加好友名片的call和加好友用的是同一个call,区别只有两点。我们点击添加一个好友分享的名片,让程序断下。
这个地方传入的是0x11而不再是6
这个地方传入的是V1结构体 而不再是微信ID的结构体。而V1的结构体我们可以在收到名片推送消息的时候,从消息结构体中获取。
分析接收名片消息
加好友的call我们已经找到了,而且也已经知道需要传入哪些数据,那么下一步就是去接收名片消息的地方,去拿到这些数据。
直接来到接收消息的地方,至于怎么找到接收消息的call,请看我上一篇文章
http://blog.3w3k.com/post-3.html
这里直接给出偏移2.6.8.52版本的接收消息的偏移是0x315E98
直接来到接收消息的地方,在这里下断点,然后用小号分享一个名片,让程序断下
程序断下后,我们查看[[esp]]里面的内容,这里面保存了接收到的消息参数,往下拉
这里有消息发送者的微信ID和xml格式的消息内容
数据窗口跟随进去,里面有我们需要的V1数据,至于其他的数据我们并不关心。OK,分析到这里,自动添加好友分享的名片基本也就完成了。接下来贴代码
代码实现自动添加好友分享名片
第一步 取出V1数据 我这里用的是字符串查找和匹配的方法,你们可以xml解析库来解析数据
void AutoAddCardUser(wstring msg)
{
//拿到V1
int v1strat = msg.find(L"v1_");
int v1end = msg.find(L"@stranger");
wstring v1;
v1 = msg.substr(v1strat, v1end - v1strat + 9);
//调用添加名片好友函数
AddCardUser((wchar_t*)v1.c_str(), (wchar_t*)L"快通过~快通过~ 吼吼!");
}
第二步 调用call添加好友
void AddCardUser(wchar_t* v1, wchar_t* msg)
{
DWORD dwWeChatWinAddr = (DWORD)GetModuleHandle(L"WeChatWin.dll");
DWORD dwParam1 = dwWeChatWinAddr + WxAddWxUserParam1;
DWORD dwCall1 = dwWeChatWinAddr + WxAddWxUserCall1;
DWORD dwCall2 = dwWeChatWinAddr + WxAddWxUserCall2;
DWORD dwCall3 = dwWeChatWinAddr + WxAddWxUserCall3;
DWORD dwCall4 = dwWeChatWinAddr + WxAddWxUserCall4;
DWORD dwCall5 = dwWeChatWinAddr + WxAddWxUserCall5;
struct TextStruct
{
wchar_t* pStr;
int strLen;
int strMaxLen;
};
TextStruct pV1 = { 0 };
pV1.pStr = v1;
pV1.strLen = wcslen(v1) + 1;
pV1.strMaxLen = (wcslen(v1) + 1) * 2;
char* asmV1 = (char*)&pV1.pStr;
char buff3[0x100] = { 0 };
char* buff = buff3;
__asm
{
sub esp, 0x18;
mov ecx, esp;
mov dword ptr ss : [ebp - 0xDC], esp;
push dwParam1;
call dwCall1;
sub esp, 0x18;
mov eax, buff;
mov dword ptr ss : [ebp - 0xE4], esp;
mov ecx, esp;
push eax;
call dwCall2;
push 0x11;
sub esp, 0x14;
mov ecx, esp;
mov dword ptr ss : [ebp - 0xE8], esp;
push - 0x1;
mov edi, msg;
push edi;
call dwCall3;
push 0x2;
sub esp, 0x14;
mov ecx, esp;
mov dword ptr ss : [ebp - 0xE0], esp;
mov ebx, asmV1;
push ebx;
call dwCall4;
mov ecx, eax;
call dwCall5;
}
}
拓展
实现自动聊天思路
首先在好友消息的时候保存一下好友的微信ID,然后调用获取到好友的消息内容,并且将消息内容转发给图灵机器人的公众号
接着拿到图灵机器人回复的内容,将内容转发给好友
这样就实现了自动聊天
实现自动同意好友请求思路
我们在接收消息的地方下个断点,让好友发送请求时断下,并且查看[[esp]]里的消息内容
这里保存有两个重要的数据,一个是V1,一个是V2,我们只要在这个地方取出V1和V2,然后调用同意好友请求的call,就能实现自动同意好友请求
同意好友请求的call,我这里直接给出偏移 微信2.6.8.52版本
#define WxAgreeUserRequestCall1 0x1865B0; //同意好友请求
#define WxAgreeUserRequestCall2 0x4F4F0; //同意好友请求
#define WxAgreeUserRequestCall3 0xCE4F0; //同意好友请求
#define WxAgreeUserRequestCall4 0x16BD40; //同意好友请求
#define WxAgreeUserRequestParam 0x126E050; //同意好友请求
这里需要同时调用4个call,接着附上代码
//取出V1和V2
void AutoAgreeUserRequest(wstring msg)
{
int v1strat = msg.find(L"v1_");
int v1end = msg.find(L"@stranger");
wstring v1;
v1 = msg.substr(v1strat, v1end - v1strat + 9);
//找到v2
int v2strat = msg.find(L"v2_");
int v2end = msg.rfind(L"@stranger");
wstring v2;
v2 = msg.substr(v2strat, v2end - v2strat + 9);
//调用同意好友请求的call
AgreeUserRequest((wchar_t*)v1.c_str(), (wchar_t*)v2.c_str());
}
//调用同意好友请求call
void AgreeUserRequest(wchar_t* v1, wchar_t* v2)
{
struct v1Info
{
int fill = 0;
wchar_t* v1 = 0;
int v1Len;
int maxV1Len;
char fill2[0x41C] = { 0 };
DWORD v2 = { 0 };
};
struct v2Info
{
char fill[0x24C] = { 0 };
DWORD fill3 = 0x25;
char fill4[0x40] = { 0 };
wchar_t* v2;
int v2Len;
int maxV2Len;
char fill2[0x8] = { 0 };
};
DWORD base = (DWORD)LoadLibrary(L"WeChatWin.dll");
DWORD callAdd1 = base + WxAgreeUserRequestCall1;
DWORD callAdd2 = base + WxAgreeUserRequestCall2;
DWORD callAdd3 = base + WxAgreeUserRequestCall3;
DWORD callAdd4 = base + WxAgreeUserRequestCall4;
DWORD params = base + 0x126E050;
DWORD* asmP = (DWORD*)params;
v1Info userInfoV1 = { 0 };
v2Info userInfoV2 = { 0 };
userInfoV1.v2 = (DWORD)&userInfoV2.fill;
userInfoV1.v1 = v1;
userInfoV1.v1Len = wcslen(v1);
userInfoV1.maxV1Len = wcslen(v1) * 2;
userInfoV2.v2 = v2;
userInfoV2.v2Len = wcslen(v2);
userInfoV2.maxV2Len = wcslen(v2) * 2;
char* asmUser = (char*)&userInfoV1.fill;
char buff[0x14] = { 0 };
char buff2[0x48] = { 0 };
char* asmBuff = buff2;
__asm
{
mov ecx, asmUser;
push 0x6;
sub esp, 0x14;
push esp;
call callAdd1;
mov ecx, asmUser;
lea eax, buff;
push eax;
call callAdd2;
mov esi, eax;
sub esp, 0x8;
mov ecx, asmP;
call callAdd3;
mov ecx, asmBuff;
mov edx, ecx;
push edx;
push eax;
push esi;
call callAdd4;
}
}
同样在接收消息的地方下个断点,接收转账消息,并且查看[[esp]]里的消息内容
这里重要的数据只有一个,就是这个transferid,拿到这个转账ID和微信ID,然后调用收款的call,就能实现自动收款了
这里提供收款call的偏移,微信2.6.8.52版本
#define WxCllectMoneyCall1 0x676B10 //收款
#define WxCllectMoneyCall2 0x676B90 //收款
接着附上代码
//取出转账ID
void AutoCllectMoney(wstring msg,wchar_t* wxid)
{
// 找到<transferid>字符串的位置
int pos1 = msg.find(L"<transferid>");
//找到]]></transferid>字符串的位置
int pos2 = msg.find(L"]]></transferid>");
//取出多余的字符串长度
wstring noneed = L"<transferid><![CDATA[";
int noneedLen = noneed.length();
//取出转账ID
wstring transferid;
transferid = msg.substr(pos1 + noneedLen, (pos2 - pos1) - noneedLen);
//调用收款call 实现自动收款
CllectMoney((wchar_t*)transferid.c_str(), wxid);
}
//调用收款call
void CllectMoney(wchar_t* transferid, wchar_t* wxid)
{
struct CllectMoneyStruct
{
wchar_t* ptransferid;
int transferidLen;
int transferidMaxLen;
char full[0x8] = { 0 };
wchar_t* pwxid;
int wxidLen;
int wxidMaxLen;
char full2[0x8] = { 0 };
};
CllectMoneyStruct cllect;
cllect.ptransferid = transferid;
cllect.transferidLen = wcslen(transferid) + 1;
cllect.transferidMaxLen = (wcslen(transferid) + 1) * 2;
cllect.pwxid = wxid;
cllect.wxidLen = wcslen(wxid) + 1;
cllect.wxidMaxLen = (wcslen(wxid) + 1) * 2;
char* asmBuff = (char*)&cllect.ptransferid;
DWORD dwWeChatWinAddr = (DWORD)GetModuleHandle(L"WeChatWin.dll");
DWORD dwCall1 = dwWeChatWinAddr + WxCllectMoneyCall1;
DWORD dwCall2 = dwWeChatWinAddr + WxCllectMoneyCall2;
__asm
{
sub esp, 0x30;
mov ecx, esp;
mov eax, asmBuff;
push eax;
call dwCall1;
call dwCall2;
add esp, 0x30;
}
}