首页
社区
课程
招聘
[原创]2025企业微信4.1.36.6011使用内联汇编导出所有的界面XML文件
发表于: 2025-4-21 18:18 4261

[原创]2025企业微信4.1.36.6011使用内联汇编导出所有的界面XML文件

2025-4-21 18:18
4261

之前[原创]2025企业微信4.1.36.6011调试Duilib获取界面XML实现了在调试器里面获取 XML
但是很不方便 毕竟企业微信的文件太多了 一个一个手动处理不现实
经过几天的学习 学会了用 inline hook(内联汇编) 修改指令 直接内容到文件里

按照上次文章的方法 找到 CDialogBuilder::LoadContent 方法所在的地方
图片描述
往上几行 有一个 LoadXml 方法 入参是文件名称 出参是文件内容
正好都是我们要的数据 我们可以替换这个指令

这里贴一下原始的字节码 我们需要修改的就是 E8 F724F8FF 这个 call <duilib.public 指令

然后找个AI写一个 inline hook 的模板代码 稍微修改一下

这里讲一下几个地址的获取方法

找到对应地址 然后右键 复制 选中 RVA(相对虚拟地址)
图片描述
原始地址 选中那个 call 然后 双击一下 就可以跳到对应的地址 然后复制一下 RVA
图片描述
这里原始地址也可以直接代码读取然后写入 暂时不会 就先手写了

在 DLL 的 DLL_PROCESS_ATTACH 部分执行一下 inline_hook

然后我们生成一下 DLL 注入一下 看看效果
可以看到 E8 F724F8FF 被替换成了 E9 376DB8F6

先写一个裸函数 然后用字节码保存一下相关的指针地址 然后执行保存的方法 然后跳回去

!!!注意!!!
!!!注意!!!
!!!注意!!!
这里 x32dbg 要打开一下 入口断点 不然等启动后注入 会缺一部分文件
x32dbg 启动企业微信 然后在入口断点停下 然后注入 dll

可以看一下 ret_hook 的汇编代码

然后按 F9 继续运行 去目录下面看一下 会生成很多文件
图片描述

项目属性 C++20标准 不使用预编译头 Unicode 字符集

最近一直在整收发消息 然后发现企业微信是多线程的

暂时还没搞清楚具体怎么实现的 大概可以找到是哪里发出去和哪里收回来的
但是栈上面找不到具体的消息内容 只能找到用户ID
我猜可能是传递了对象 所以找不到
希望大佬们可以指点一下

70027863 | 50                       | push eax
70027864 | E8 F724F8FF              | call <duilib.public: static class std::basic_string<wchar_t, struct std::char_traits<wchar_t>, class std::allocator<wchar_t>> __cdecl DuiLib::CResManager::LoadXML(wchar_t const *, int)>
70027869 | 83C4 0C                  | add esp,0xC
7002786C | 8365 FC 00               | and dword ptr ss:[ebp-0x4],0x0
70027863 | 50                       | push eax
70027864 | E8 F724F8FF              | call <duilib.public: static class std::basic_string<wchar_t, struct std::char_traits<wchar_t>, class std::allocator<wchar_t>> __cdecl DuiLib::CResManager::LoadXML(wchar_t const *, int)>
70027869 | 83C4 0C                  | add esp,0xC
7002786C | 8365 FC 00               | and dword ptr ss:[ebp-0x4],0x0
// 生成跳转代码
void GenerateJmpCode(DWORD value, BYTE *byteArray)
{
    byteArray[0] = 0xE9;                       // 跳转指令 JMP E9
    byteArray[1] = (value & 0x000000FF);       // 最低位字节
    byteArray[2] = (value & 0x0000FF00) >> 8// 第二字节
    byteArray[3] = (value & 0x00FF0000) >> 16; // 第三字节
    byteArray[4] = (value & 0xFF000000) >> 24; // 最高位字节
}
void inline_hook()
{
    DWORD baseAddress = reinterpret_cast<DWORD>(GetModuleHandle(L"DuiLib.dll")); // 获取模块基址
    DWORD TargetAddress = (DWORD)ret_hook;                                       // 目标地址(自己定义的执行代码位置)
    BeginAddress = baseAddress + 0xE7864;                                        // 起始地址
    BackAddress = baseAddress + 0xE7869;                                         // 返回地址
    OriginAddress = baseAddress + 0x69D60;                                       // 原始Call地址
 
    std::wstring message = std::format(L"BaseAddress: {:x} BeginAddress: {:x} BackAddress: {:x} OriginAddress: {:x}", //
                                       baseAddress, BeginAddress, BackAddress, OriginAddress);
 
    MessageBoxW(NULL, message.c_str(), L"注入", MB_OK);
 
    // 读取保存原来的字节码
    ReadProcessMemory(GetCurrentProcess(), (LPCVOID)BeginAddress, (LPVOID)&OriginBytes, 5, NULL);
 
    BYTE jmpCode[5];
    GenerateJmpCode(TargetAddress - BeginAddress - 5, jmpCode); // 目标地址 - 当前地址 - 5
 
    // 写入JMP跳转字节码
    WriteProcessMemory(GetCurrentProcess(), (LPVOID)BeginAddress, (LPVOID)&jmpCode, 5, NULL);
}
// 生成跳转代码
void GenerateJmpCode(DWORD value, BYTE *byteArray)
{
    byteArray[0] = 0xE9;                       // 跳转指令 JMP E9
    byteArray[1] = (value & 0x000000FF);       // 最低位字节
    byteArray[2] = (value & 0x0000FF00) >> 8// 第二字节
    byteArray[3] = (value & 0x00FF0000) >> 16; // 第三字节
    byteArray[4] = (value & 0xFF000000) >> 24; // 最高位字节
}
void inline_hook()
{
    DWORD baseAddress = reinterpret_cast<DWORD>(GetModuleHandle(L"DuiLib.dll")); // 获取模块基址
    DWORD TargetAddress = (DWORD)ret_hook;                                       // 目标地址(自己定义的执行代码位置)
    BeginAddress = baseAddress + 0xE7864;                                        // 起始地址
    BackAddress = baseAddress + 0xE7869;                                         // 返回地址
    OriginAddress = baseAddress + 0x69D60;                                       // 原始Call地址
 
    std::wstring message = std::format(L"BaseAddress: {:x} BeginAddress: {:x} BackAddress: {:x} OriginAddress: {:x}", //
                                       baseAddress, BeginAddress, BackAddress, OriginAddress);
 
    MessageBoxW(NULL, message.c_str(), L"注入", MB_OK);
 
    // 读取保存原来的字节码
    ReadProcessMemory(GetCurrentProcess(), (LPCVOID)BeginAddress, (LPVOID)&OriginBytes, 5, NULL);
 
    BYTE jmpCode[5];
    GenerateJmpCode(TargetAddress - BeginAddress - 5, jmpCode); // 目标地址 - 当前地址 - 5
 
    // 写入JMP跳转字节码
    WriteProcessMemory(GetCurrentProcess(), (LPVOID)BeginAddress, (LPVOID)&jmpCode, 5, NULL);
}
BOOL APIENTRY DllMain(HMODULE hModule,
                      DWORD ul_reason_for_call,
                      LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        // 附加的时候进行 hook
        saveXmlPath = L"D:\\repos\\wxworkdebug";
        inline_hook();
        break;
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
    {
        // 卸载的时候恢复原来的代码
        WriteProcessMemory(GetCurrentProcess(), (LPVOID)BeginAddress, (LPVOID)&OriginBytes, 5, NULL);
        break;
    }
    }
    return TRUE;
}
BOOL APIENTRY DllMain(HMODULE hModule,
                      DWORD ul_reason_for_call,
                      LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        // 附加的时候进行 hook
        saveXmlPath = L"D:\\repos\\wxworkdebug";
        inline_hook();
        break;
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
    {
        // 卸载的时候恢复原来的代码
        WriteProcessMemory(GetCurrentProcess(), (LPVOID)BeginAddress, (LPVOID)&OriginBytes, 5, NULL);
        break;
    }
    }
    return TRUE;
}
70027863 | 50                       | push eax
70027864 | E9 376DB8F6              | jmp <wxworkxmlexport.void __cdecl ret_hook(void)>
70027869 | 83C4 0C                  | add esp,0xC
7002786C | 8365 FC 00               | and dword ptr ss:[ebp-0x4],0x0
70027863 | 50                       | push eax
70027864 | E9 376DB8F6              | jmp <wxworkxmlexport.void __cdecl ret_hook(void)>
70027869 | 83C4 0C                  | add esp,0xC
7002786C | 8365 FC 00               | and dword ptr ss:[ebp-0x4],0x0
inline __declspec(naked) void ret_hook()
{
    DWORD xmlNameAddress;
    DWORD xmlAddress;
    // 原程序代码务必要执行
    __asm
    {
         mov xmlNameAddress, ecx; // 保存入参 也就是 xml 的名称
         call OriginAddress; //执行原始的 LoadXml 的那个Call
         mov xmlAddress, eax; // 保存出参 也就是 xml 的内容
    }
 
    saveXml(xmlNameAddress, xmlAddress); // 这里执行一下自定义的保存方法
 
    __asm
    {
         jmp BackAddress; // 跳回原来的逻辑 继续执行
    }
}
inline __declspec(naked) void ret_hook()
{
    DWORD xmlNameAddress;
    DWORD xmlAddress;
    // 原程序代码务必要执行
    __asm
    {
         mov xmlNameAddress, ecx; // 保存入参 也就是 xml 的名称
         call OriginAddress; //执行原始的 LoadXml 的那个Call

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 18
支持
分享
最新回复 (15)
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
666666
2025-4-22 14:20
0
雪    币: 2235
活跃值: (1355)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
ida搜  presendnewmessage   即可定位到发消息
2025-4-22 16:49
0
雪    币: 2868
活跃值: (2910)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
2025-4-22 19:43
0
雪    币: 208
活跃值: (1287)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
5
不错啊
2025-4-23 14:39
0
雪    币: 56
活跃值: (71)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
厉害大佬
2025-4-23 21:46
0
雪    币: 582
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
FANTASYING ida搜 presendnewmessage 即可定位到发消息
这个我之前找到过 然后消息内容的引用地址也找到过 好像固定在 0019F114 但是直接改这个内容好像没用 感觉是赋值给某个对象 然后传递到发送线程了 之前根据 sqlite 去追踪 数据库执行的SQL 倒是找出来了 不过还是没啥头绪 怎么构造发送的消息体
2025-4-24 14:15
0
雪    币: 226
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
m
2025-4-24 16:39
0
雪    币: 2235
活跃值: (1355)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
9
MiaoWoo 这个我之前找到过 然后消息内容的引用地址也找到过 好像固定在 0019F114 但是直接改这个内容好像没用 感觉是赋值给某个对象 然后传递到发送线程了 之前根据 sqlite 去追踪 数据库执行的SQ ...
他参数全是对象,向上分析,去找pb构造,即可构造出参数拉~
2025-4-24 19:41
0
雪    币: 67
活跃值: (1996)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
谢谢如此好的分析
2025-5-4 16:49
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
谢谢分享
2025-5-4 17:37
0
雪    币: 370
活跃值: (1525)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
谢谢分享
2025-5-8 18:54
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
你的帖子非常有用,感谢分享!
2025-5-13 16:35
0
雪    币: 135
活跃值: (2561)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
不错
2025-6-5 19:38
0
雪    币: 4257
活跃值: (2680)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
15
你的帖子非常有用,感谢分享!
2025-6-5 19:42
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
感谢分享
2025-6-11 15:58
0
游客
登录 | 注册 方可回帖
返回