1. 前言
近期分析了一下64位的PC微信,对其中的一些内容做一些分享。本期分享内容:Hook微信自身日志。
本次分析使用版本:3.9.12.51。
2. 逆向分析
众所周知,PC微信的主要业务功能均实现于WechatWin.dll,使用IDA分析。
一般日志都会存在格式化字符串,比如%s、{}等,直接在IDA中搜索。
随便选择一个看着很像(带有一些微信业务属性的,比如消息、图片等)的,追踪一下。

定位到函数sub_182627590,进去看看。

函数内发现代码获取了时间和线程ID并格式化,基本可以确定这里就是日志函数了。并发现代码循环处理字符串后在尾部添加结束符\x00。

将Hook点选择在添加结束符\x00之后,即0x18262780F处。这时候字符串已经组装完毕。

调试发现在0x18262780F处,rdx指向字符串。

3. Hook
64位的Hook和32位的有点区别,32位一般在Hook点直接jmp XXXXXXXX(立即数地址)或者call XXXXXXXX(立即数地址)即可,但是64位不支持直接jmp/call立即数地址,所以这里采用mov rax,XXXXXXXXXXXXXXXX(立即数地址),然后jmp rax的方式来Hook。由于这种方式会改写rax的值,所以在这之前还得保存一下rax的值,并在跳转后恢复,防止rax的值丢失。
梳理一下Hook流程:
- 保存rax
- mov rax并jmp rax,就跳转到我们自己的Hook代码处
- 恢复rax
- 保存寄存器环境,由于64位没有pushad指令,只能手动保存
- 调用我们的功能代码
- 恢复寄存器环境,由于64位没有popad指令,只能手动恢复
- 执行因为Hook操作而被覆盖的原始代码
- 再次保存rax
- mov eax并jmp eax,返回到本来的功能代码处
- 恢复rax,随后即可正常执行
4. 先手动Hook
4.1. Hook点

4.2. Hook代码

5. 编写代码自动Hook
5.1. 定义偏移地址
1 2 3
| uint64_t hookaddr_wxlog = 0x18262780F - 0x180000000 + wxbase; uint64_t retaddr_wxlog = 0x18262781C - 0x180000000 + wxbase;
|
5.2. 编写功能函数
这里rdx就是字符串指针,可以根据情况编写功能函数,比如输出到文件等。
1 2 3
| void hookdeal_wxlog(uint64_t rdx) {
}
|
5.3. 编写Hook功能代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| LPVOID memory = VirtualAlloc(NULL, 1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(GetCurrentProcess(), memory, "\x58\x54\x50\x53\x51\x52\x55\x56\x57\x41\x50\x41\x51\x41\x52\x41\x53\x41\x54\x41\x55\x41\x56\x41\x57\x9C", 26, NULL);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)((uint64_t)memory + 26), "\x48\x89\xD1\x48\xB8", 5, NULL);
void (*ptr)(uint64_t) = hookdeal_wxlog; WriteProcessMemory(GetCurrentProcess(), (LPVOID)((uint64_t)memory + 26 + 5), &ptr, 8, NULL);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)((uint64_t)memory + 26 + 5 + 8), "\x53\xFF\xD0", 3, NULL);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)((uint64_t)memory + 26 + 5 + 8 + 3), "\x5B\x9D\x41\x5F\x41\x5E\x41\x5D\x41\x5C\x41\x5B\x41\x5A\x41\x59\x41\x58\x5F\x5E\x5D\x5A\x59\x5B\x58\x5C", 26, NULL);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)((uint64_t)memory + 26 + 5 + 8 + 3 + 26), "\x44\x89\x75\x80\x0F\x57\xC0\x0F\x11\x85\xC0\x10\x00\x00", 14, NULL);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)((uint64_t)memory + 26 + 5 + 8 + 3 + 26 + 14), "\x50\x48\xB8", 3, NULL);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)((uint64_t)memory + 26 + 5 + 8 + 3 + 26 + 14 + 3), &retaddr_wxlog, 8, NULL);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)((uint64_t)memory + 26 + 5 + 8 + 3 + 26 + 14 + 3 + 8), "\xFF\xE0", 2, NULL);
|
5.4. Hook原始代码
需要占用14字节的空间。
1 2 3 4 5 6 7
|
WriteProcessMemory(GetCurrentProcess(), (LPVOID)hookaddr_wxlog, "\x50\x48\xB8", 3, NULL);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)(hookaddr_wxlog + 3), &memory, 8, NULL);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)(hookaddr_wxlog + 3 + 8), "\xFF\xE0\x58", 3, NULL);
|
6. 最终效果
获取到微信日志。
