Windows x86/x64 动态库劫持

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++函数的调用方式(也就是调用约定)主要有如下几种方式, 其参数均通过压栈传递:

  1. cdecl (Microsoft C++系列编译器的默认调用方式)
  2. stdcall (Windows绝大部分API的默认调用方式, 有时候也称为pascal)
  3. 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值