之前[原创]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:
{
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:
{
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;
call OriginAddress;
mov xmlAddress, eax;
}
saveXml(xmlNameAddress, xmlAddress);
__asm
{
jmp BackAddress;
}
}
inline
__declspec
(
naked
)
void
ret_hook()
{
DWORD
xmlNameAddress;
DWORD
xmlAddress;
__asm
{
mov xmlNameAddress, ecx;
call OriginAddress;
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课