Windows x86/x64 动态库劫持
本文来源:Zero’s Donkey Den
源文地址:https://7-0.cc/windows-x64-x86-dll-hijack/
源文标题:论Windows x64下动态库劫持技术
转载许可:未经授权,禁止转载
从调用约定开始简述x64动态库劫持技术,并通过AheadLib实现对x64架构下动态库的劫持,以及如何在目标函数调用前后记录传递的参数和返回值。
1. 引子
很多时候出于各种各样的原因,我们需要用到动态库劫持技术来做一些研究或者实现某种功能,这时候我们会用到一些DLL劫持工具来简化工作。
比如 AheadLib:它可以根据目标DLL的导出函数,自动生成劫持相关的代码,免去了我们手动提取导出函数、编写劫持代码。我们将生成的代码编译成DLL, 冒名顶替目标DLL,就可以将目标函数的调用转接到新的DLL。
之前用过AheadLib来分析一个商业软件与第三方组件的交互流程, 效果不错所以印象深刻.
这里主要描述Windows x64/x86动态库劫持技术,以及由此展开的相关调用约定简述,默认读者具备以下基础:
- C/C++基础
- x86汇编(INTEL风格)
就在不久前我又有了一个类似的需求,目标是64位软件,而手里面的AheadLib是32位。经过一番搜索在看雪论坛找到了64位的版本,但结果却不如我的预期,这才有了接下来的故事;
64位的AheadLib生成的代码比之前有了很大的改变,原因是Win x64环境下函数的前4个参数是通过寄存器传递, 与x86环境下所有参数经过压栈传递(cdecl, stdcall, …)大有不同;
2. 简述x86 与 x64 下C/C++函调用的差异
x86下C/C++函数的调用方式(也就是调用约定)主要有如下几种方式, 其参数均通过压栈传递:
- cdecl (Microsoft C++系列编译器的默认调用方式)
- stdcall (Windows绝大部分API的默认调用方式, 有时候也称为pascal)
- thiscall (C++成员函数的调用方式)
下面我们来看看在汇编层面上x86与x64下调用约定的细节:
// x86 cdecl调用约定下的参数传递及返回值
int main()
{
test_function(0x5555aaaa, 0x3333eeee,
3.141592656245, 0x2222dddd, 0x1111bbbb, 0x6666ffff);
return 0;
}
; 这是对应的汇编代码
fld qword ptr [__real@400921fb549f688a]
push 6666FFFFh ; 参数6, 放到栈上
push 1111BBBBh ; 参数5, 放到栈上
push 2222DDDDh ; 参数4, 放到栈上
sub esp,8
fstp qword ptr [esp] ; 参数3, 放到栈上
push 3333EEEEh ; 参数2, 放到栈上
push 5555AAAAh ; 参数1, 放到栈上
call dword ptr [__imp_test_function] ; 调用
add esp,1Ch ; 平栈
xor eax,eax ; 返回值清0
ret
而到了x64就只剩下了一种快速调用约定(x64) fastcall, 虽然在x86下该约定也存在但极少见到我们这里不讨论:
// x64 环境下的参数传递
int main()
{
test_function(0x5555aaaa, 0x3333eeee,
3.141592656245, 0x2222dddd, 0x1111bbbb, 0x6666ffff);
return 0;
}
; 这是对应的汇编代码
sub rsp,38h ; 20h(影子空间) + 10h(参数5,6放在栈上) + 8h(对齐用的)
movsd xmm2,mmword ptr [__real@400921fb549f688a] ; 参数3, 浮点数放在xmm2
mov r9d,2222DDDDh ; 参数4
mov edx,3333EEEEh ; 参数2
mov ecx,5555AAAAh ; 参数1
mov dword ptr [rsp+28h],6666FFFFh ; 参数6, 放到栈上
mov dword ptr [rsp+20h],1111BBBBh ; 参数5, 放到栈上
call qword ptr [__imp_test_function] ; 调用
xor eax,eax ; 清空返回值
add rsp,38h ; 平栈
ret
从上面我们可以看到x64 fastcall
的前4个参数通过寄存器传递, 如果参数大于4才会走栈, 而x86的调用约定都是通过栈传递;
为了推动剧情的发展, 在这里要重点说明一下x64 fastcall
调用约定的细节 (略过x86 如果对x86的调用约定感兴趣的话可以参考相关的资料)
3. x64 fastcall调用约定
参考文档: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019
总的来说x64 fastca