本帖最后由 CCStation 于 2020-1-17 22:38 编辑
前端时间吧,易语言的微信hook似乎蛮火的,而我只会delphi。微信hook也就研究了一下,感觉像写外挂一下,对微信进行逆向,分析地址,分析call。分享与讨论:
一、注入自己的dll。
一些教程视频中,易语言的注入就一句代码。etcp的一个模块。我能力有限,手头也没有现成的代码。所以就像还是用键盘hook的方式注入。后期在考虑进程间通讯做群控。另外就是觉得在dll里面写窗口似乎简单一点。
键盘hook注入的相关代码如下
[Delphi] 纯文本查看 复制代码 function installKeyhook: Boolean; stdcall;var
h: hwnd;
GameTid: THandle;
begin
Result := False;
h := FindWindow('WeChatLoginWndForPC', nil);
if h = 0 then
begin
h := FindWindow('WeChatMainWndForPC', nil);
if h = 0 then
begin
MessageBox(0, '未找到微信窗口', '提示', 0);
exit;
end;
end;
GameTid := GetWindowThreadProcessId(h);
keyhhk := SetWindowsHookEx(WH_KEYBOARD, @keyhook, GetModuleHandle('WeHelp.dll'), GameTid);
if keyhhk > 0 then
begin
Result := True;
end;
end; [Delphi] 纯文本查看 复制代码 function keyhook(icode, wp, lp: Integer): LRESULT; stdcall;
begin
if (icode = HC_ACTION) then
begin
if (wp = VK_HOME) and ((1 shl 31) and lp = 0) then
begin
if Form_main = nil then
begin
Form_Main := TForm_Main.Create(nil);
end;
Form_Main.Visible := not Form_Main.Visible;
end;
end;
Result := CallNextHookEx(keyhhk, icode, wp, lp);
end;
dll中加上以上代码,在注入exe中调用,就实现了键盘hook注入 。我是hook了home键。
二、微信消息的hook。这个网上有很多查找hook位置的教程。我纠结了很久的是易语言所谓的超级hook功能。
每次微信接受消息就会运行图示这段asm,网上传的inlinehook 就是在790028c3的位置jmp至自己的内存位置,然后运行自己的代码,自己代码运行完后运行 原来790028c3位置的mov ecx,0x7A2DFA20
然后再jmp会790028c8 继续微信的运行。也就是再微信每次接受消息的时候运行一段自己的代码,比如说把消息内容保存下来,这就是所谓的微信消息hook。
delphi写的时候没什么参考。jmp跳转实在写的有点乱。所以我直接修改790028c3位置的代码为call我的自设代码。运行完成后再retn回来。期间用全局变量把寄存器值都截取下来了。关键代码如下:
[Asm] 纯文本查看 复制代码 Fhookaddr := AHookAddr;
FByteLen := ByteLength;
FMyproc := Integer(DspProc);
FOldCode := GetMemory(SizeOf(Pointer) + FByteLen * sizeof(Byte)); //申请FOldCode空间
CopyMemory(FOldCode, Pointer(FHookAddr), FByteLen * sizeof(Byte));
FMyAsm2 := GetMemory(100 * SizeOf(Byte)); //申请FMyAsm空间
VirtualProtect(FMyAsm2, 100 * sizeof(Byte), PAGE_EXECUTE_READWRITE, @oldprotect); //调整FmyAsm内存空间可读写
FillMemory(FMyAsm2, 100 * SizeOf(Byte), $90); //初始化空间全部为nop
temp := Integer(Self); //temp赋值为superhook自身指针
PByte(Integer(FMyAsm2))^ := $60; //pushad
PByte(Integer(FMyAsm2) + 1)^ := $C7; //mov
Pbyte(Integer(FMyAsm2) + 2)^ := $C1; //ecx
PInteger(Integer(FMyAsm2) + 3)^ := temp; //self
CopyMemory(Pointer(Integer(FMyAsm2) + 7), @codearr, 73 * sizeof(Byte)); //codeArr写入,内容为将寄存器的值取出至对象字段
Pbyte(Integer(FMyAsm2) + 81)^ := $E8; //call
PInteger(Integer(FMyAsm2) + 82)^ := FMyProc - Integer(FMyAsm2) - 82 - 4; //FMyProc的地址
PByte(Integer(FMyAsm2) + 86)^ := $61; //popad
CopyMemory(Pointer(Integer(FMyAsm2) + 87), FOldCode, FByteLen * sizeof(Byte)); //运行截取过来的值
PByte(Pointer(Integer(FMyAsm2) + 88 + Fbytelen))^ := $C2; // 在内存字段的最终写下(retn )
Pword(Pointer(Integer(FMyAsm2) + 89 + Fbytelen))^ := $0000;
HookAddr;
下面是消息截取的图片示意一下
三、通讯录的读取。
网络教程是类似消息截取一样再登陆的时候做了一个hook。但是像我这样只用一个账号,且用键盘hook的模式,就感觉不是很方面。总感觉有很多时候是开启微信以后再注入的。
所以我想总归有一个地方存储了通讯了的相关讯息。于是分以下几步描述下(截图实在他麻烦,有ce,od基础的我想应该能理解我说的):
1、ce搜索微信特定名称,比如讨厌的人1111
2、不停切换,最小化,最大化,再搜索讨厌的人1111,ce数据再50以内。
3、修改一半的数据为讨厌的人2222,通过是否改变通讯录名称来筛选数据。
4、重复3确认讨厌的人1111的内存地址。
5、od追溯内存地址偏移。发现通讯录存储为典型的二叉树结构如下:
接下来就是通过找到的二叉树根。对二叉树进行遍历。我只会递归的二叉遍历,代码如下:
[Delphi] 纯文本查看 复制代码 procedure AddListTraver;
procedure DoAddListTraver(ARoot: Integer);
begin
if pbyte(ARoot + $D)^ = 0 then
begin
//Form_Client.DspMsg(ARoot.ToHexString);
SetLength(arr_Addlist, Length(arr_Addlist) + 1);
arr_Addlist[High(arr_Addlist)] := ARoot;
DoAddListTraver(pinteger(ARoot + $8)^);
DoAddListTraver(pinteger(ARoot)^);
end;
end;
var
addr: LongInt;
base: LongInt;
header: DWORD;
begin
SetLength(arr_Addlist, 0);
SupermemScan(AddrListSig, GetModuleHandle('WeChatWin') + $1000, GetModuleHandle('WeChatWin') + $1000 + $400000, addr);
addr := addr - $4;
base := Pdword(addr)^;
header := pdword(pdword(pdword(base)^ + $28 + $84)^ + $4)^;
//二叉遍历
// DoAddListTraver(pdword(header)^);
// DoAddListTraver(pdword(header + $8)^);
DoAddListTraver(header);
end;
四、消息发送call。delphi写的难点似乎就是构造以下消息及id的结构。实现代码如下:
[Delphi] 纯文本查看 复制代码 procedure SendMsg(wx_ID, Amsg: string; aite: string = '');
type
Tmsg = record
content: PChar;
len1: LongInt;
len2: LongInt;
end;
var
p_ID, Pamsg, paite: TMsg;
callAddr: LongInt;
buff: array[0..$87c] of Byte;
p1, p2, p3: Pointer;
begin
SupermemScan(SendCallSig, GetModuleHandle('WeChatWin') + $1000, GetModuleHandle('WeChatWin') + $1000 + $100000, callAddr);
callAddr := callAddr + $F + pdword(callAddr + $b)^;
p_ID.content := PChar(wx_ID);
p_ID.len1 := Length(wx_ID);
p_ID.len2 := Length(wx_ID) * 2;
Pamsg.content := PChar(Amsg);
Pamsg.len1 := Length(Amsg);
Pamsg.len2 := Length(Amsg) * 2;
if aite = '' then
begin
paite.content := nil;
end
else
begin
paite.content := PChar(aite);
end;
paite.len1 := Length(aite);
paite.len2 := Length(aite) * 2;
p1 := @p_ID;
p2 := @Pamsg;
p3 := @paite;
asm
mov edx, p1;
mov eax, p3;
push 1;
push eax;
mov ebx, p2;
push ebx;
lea ecx, buff;
call calladdr;
add esp, $C;
end;
end;
这段代码我自己都觉得挺烂的 尤其 p1 p2 p3那段,欢迎介绍一些更好的写法。
我自己还有一些不清楚的地方:
1、做群控的话,涉及进程间通信,delphi没有易语言那种一句话代码,不知道用什么组件实现比较合适。
2、上面的这些代码 尤其是发信的这段 挺烂的 有没有大佬优化下。
3、注入的方式我自己熟悉的只有键盘hook ,还有远程线程注入。但以前捣鼓的时候 ,还有远程线程注入。就微信hook ,那种比较合适,为啥。 |