CCStation 发表于 2020-1-17 22:38

微信hook的原理一点分享与讨论

本帖最后由 CCStation 于 2020-1-17 22:38 编辑

前端时间吧,易语言的微信hook似乎蛮火的,而我只会delphi。微信hook也就研究了一下,感觉像写外挂一下,对微信进行逆向,分析地址,分析call。分享与讨论:
一、注入自己的dll。
一些教程视频中,易语言的注入就一句代码。etcp的一个模块。我能力有限,手头也没有现成的代码。所以就像还是用键盘hook的方式注入。后期在考虑进程间通讯做群控。另外就是觉得在dll里面写窗口似乎简单一点。
键盘hook注入的相关代码如下
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;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回来。期间用全局变量把寄存器值都截取下来了。关键代码如下:
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追溯内存地址偏移。发现通讯录存储为典型的二叉树结构如下:

接下来就是通过找到的二叉树根。对二叉树进行遍历。我只会递归的二叉遍历,代码如下:
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 := 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的结构。实现代码如下:
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 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 ,那种比较合适,为啥。

houtbin 发表于 2020-1-17 22:49

一楼火钳刘明

cornfieldchase 发表于 2020-1-17 23:16

感谢分享

cherrypi 发表于 2020-1-18 00:27

谢谢分享,学习学习。

cqliu 发表于 2020-1-18 01:20

可以制作一个安卓共存版微信吗?

奇妙 发表于 2020-1-18 05:36

楼主你好 可以公开下源码么!? 谢谢你

lyghost 发表于 2020-1-18 07:19

感觉用c写起来可能方便点

iYolo丶moye 发表于 2020-1-18 08:51

zjk414 发表于 2020-1-18 09:23

感谢分享

沅澧潇湘 发表于 2020-1-18 09:30

感谢分享
页: [1] 2 3
查看完整版本: 微信hook的原理一点分享与讨论