wechat逆向

wechat逆向

date: 2024/6/25

使用软件,cheat engine与x64dbg

使用cheat engine排查出消息存放地址,通过消息存放地址在x64dbg中对该地址添加内存写入断点

通过排查将断点添加到mov行,每次运行完该行消息地址的内容就会更新。

2024/6/26 01:29:12

上面的方法始终找不到具体的值,然后尝试硬件写入断点,通过查看调用信息发现,这个函数执行时传递的参数正是我们的消息,或许可以考虑从这个函数入手,注入后就能在收到微信消息后后台处理。call wechatwin.7FFDF4888D90

wechatwin.dll的基地址为:00007FFDF2AF1000

相减得:1d97d90

2024/6/26 19:25:58

现在我们需要拦截该地址的函数,在该函数调用时获取该寄存器中的值,进行处理。创建一个dll实现对该寄存器获取到的值处理,编写钩子函数拦截目标函数的调用(修改目标进程的导入地址表(IAT)或使用内联钩子(如修改目标函数的前几条指令)),钩子函数中直接访问寄存器中的值(通过汇编语言插入来直接操作,或者通过C/C++内联汇编)。然后注入到目标进程

目前使用detours的方法编写钩子函数,使用CreateRemoteThreadLoadLibraryAPI函数实现的dll注入。注入后测试时并未对自定义函数调用。

x64dbg中分析发现,函数地址依然能正常调用,编写的dll确实成功注入,对编写的dll中的自定义函数断点,从未被调用。查阅有,detours时通过修改目标函数的相关指令实现hook,但dbg查看该地址的函数并未被改变

多次启动关闭wechat,其中函数地址始终保持不变,按理来说不应该wechat重启dll重新加载的地位会不一样吗?

2024/6/26 22:48:05

经过编写c++demo程序,使用detours对打印内容的函数进行拦截函数,得出结论:拦截的函数地址确实为函数的入口地址,需要根据模块动态获取。dbg查看后发现底层为将原始函数入口地址的第一条指令替换为jmp 自定义的函数入口地址。但是在第1次调用后报错,因为在自定义函数调用完后,回调原始函数时直接闪退;在将原始函数注释后自定义函数运行一次后就没有动静了。

分析可能是因为函数的参数是存在寄存器中,调用我们的函数后寄存器中的值被修改了,回调回去就对应值不存在了,就闪退了。还可能是在我们获取该函数地址后,转换为函数后调用时没有传递指定参数。

2024/6/27 01:05:07

在次分析,函数调用堆栈如下,最上面的为栈顶:

00007FFD70930000(wechatwin.dll)->(00007FFD724F5060 +1D0AC9=7ffd726c5b29)

经分析查看发现,应该是函数地址定位不对,所以导致无法调用

2024/6/27 20:54:15

通过demo测试detours是否能拦截指定函数并回调原始函数

目标函数:

void TargetFunction() {
    std::cout << "Original TargetFunction called." << std::endl;
}

int main() {
    while (true) {
        Sleep(5000); // 5 seconds delay
        TargetFunction();
    }
    return 0;
}

需要注解的dll,通过x64dbg中的strings插件搜索关键字“original”定位到需要打印的字符串,结合硬件访问断点,函数调用堆栈等,分析出来一个条指令callxxx后目标程序进行打印,通过查看符号信息如下:<test.public:_int64_cdecl std:basic_streambuf<char,struct std::char_traits<char>>:sputn(char const *,int64)>。该函数是一个类中的一函数,符号信息中不显示this的调用,需要补全。

typedef std::streamsize(WINAPI* TargetFunctionType)(std::basic_streambuf<char, std::char_traits<char>>* pBuf, const char* s, std::streamsize n);
TargetFunctionType OriginalFunction = nullptr;

std::streamsize WINAPI HookedTargetFunction(std::basic_streambuf<char, std::char_traits<char>>* pBuf, const char* s, std::streamsize n) {
    //std::cout << "Intercepted sputn with n=" << n << std::endl;
    MessageBoxA(NULL, "Intercepted!", "Hook", MB_OK);
    // 调用原始函数
    return OriginalFunction(pBuf, s, n);
}

DllMain中部分代码{
    HMODULE hModuleWechat = GetModuleHandleA("msvcp140d.dll");
	DWORD64 baseAddress = (DWORD64)hModuleWechat;
	DWORD64 targetAddress = baseAddress + 0x86040; // 偏移量
    OriginalFunction = (TargetFunctionType)targetAddress;
}

注入后程序按预期执行

2024/6/28 23:14:48

重新对消息接受函数排查

1.ce确定消息写入地址: 24504711180
2.下断后发现,执行到如下指令时,寄存器中有我们需要的消息
00007FFD8ACE5C88  | E8 6316DAFF            | call wechatwin.7FFD8AA872F0 
3.查看堆栈信息,定位该指令的函数
00007FFD8C884B36  | E8 251046FE            | call wechatwin.7FFD8ACE5B60 
点击定位到的call指令得到入口地址:00007FFD8ACE5B60
wechatwin.public: __cdecl IChannelLogWriter::IChannelLogWriter(void)
4.入口地址-wechatwin.dll基地址=1d95b60
5.hook注入后,回调原始函数正常,在回调前添加弹窗消息,wechat弹出错误窗口后卡死或退出
6.将添加弹窗信息,替换为创建指定文件,运行正常

定为到hook函数入口地址,对比不同消息的寄存器值变化排查得出

消息:rflags:300 & r15:L"印踋翽" & r14:6 -> 取rax 。
昵称:rflags:300 & r15:L"印踋翽" & 14:5 -> 取rax。

接下来需要在hook函数中获取到需要的值

vs中直接在hook函数添加内联汇编代码,编译失败。查询得知x64下不支持内联汇编
解决办法:
项目文件夹中创建asm后缀的文件,vs中右键添加现有项。
选中asm文件,属性,常规中项类型选择microsoft macro assembler
选中项目,右键选择生成依赖项->自定义依赖项,选中masm
*注意:asm中需要用masm的相关语法否则不能通过编译。
*要想在cpp中访问asm中定义的变量,可以在asm中定义pulic声明为外部变量,在cpp中使用extern
//cpp
extern "C" {
    void GetRegisters();
    extern unsigned long long RegValues;
}
//asm
RegValues QWORD 1 dup(0)  ; 初始化为0
PUBLIC RegValues	;将RegValues标记为公共符号