Dll导出函数劫持通用方法

  • 问题发现
  • 劫持方法
    • 劫持思路
    • 可能问题
    • 劫持实现
      • 导出函数列表
      • 函数上下文
      • 获取真函数的地址
      • eax值的保存
      • 完整代码

问题发现

dll劫持是一种常见的攻击方法,但是也可以用在不知道程序源码的情况下调试dll的函数。之前在滴水教程的视频中注意到一个问题,视频作者演示了一个劫持messagebox函数,打印输出参数的过程,当时学生提问是否存在一种通用的方法可以劫持所有的函数,当时视频作者回答是无法做到。最近有些空闲想到了这个问题,觉得从模块化的角度来讲,应该存在一种可以劫持所有dll函数的方法。所以在此尝试,尝试后发现了一种伪造dll的通用方法,使用这中方法,可以通过脚本的方式,实现任意一个dll的伪造。

劫持方法

劫持思路

本文采取的方式为新建一个dll,新建的dll满足一下特点:

1.导出原dll所有的函数
2. dll的名称和原dll相同
3. 所有原dll的导出函数都有对应的实现

这样在应用程序中就无法分辨自己加载的dll是原本合法的还是伪造的。当然,此方法仅限于不检查签名的dll。

可能问题

1.函数的返回类型未知

个人见解:从汇编的角度讲,函数的返回类型在汇编语言的运行过程中是未知的,返回值大都存储在eax中,主程序则根据设定的不同,去使用函数返回的eax的值。因此,我们如果在自己的函数中,让真正的函数去执行,那么返回值就无所谓了。
2.函数的参数未知

个人见解:从汇编的角度讲,函数的参数列表只是在调用的时候,为调用者提供一个压栈的顺序。在函数调用的过程中,函数调用者负责压栈参数,函数的执行者只需要依据调用约定不同,做到保持堆栈平衡和按顺序使用参数就可以。所以,只要我们在我们的函数最后调用真正的函数,那么就不会影响到原本程序的堆栈空间和执行流程。

劫持实现

导出函数列表

在实验中,我使用了lordpe这个应用的procs.dll。该函数的导出函数列表为:

Dll导出函数劫持通用方法_第1张图片
因此,我们的dll应该导出并实现十个对应的函数。

函数上下文

既然本方法是一个通用的方法,那么自然只需要实现一个通用的代码流程即可。考虑到调用者调用dll中的程序时,堆栈的分布情况如下所示:

返回地址
参数1
参数2
…
参数n

因此我们在调用真正的方法之前,必须要将堆栈等信息恢复到这个状态。

所以此处我们选择pushad、pushfd、popfd、popad来对环境进行保存和恢复。

获取真函数的地址

并且,我们需要获取到原本函数的位置,用来跳转,因此我们需要使用getprocaddress函数以及对应的函数名称。通过对dll的导出表进行,遍历,获取名称十分的简单。我们可以定一个全局变量,用于存放每一个函数的名称。如下:

char funname1[] = "GetModuleHandleEx";
DWORD (DWORD)GetProcAddress(LoadLibraryA("kernel32.dll"), "GetProcAddress");//获取GetProcAddress函数的地址
hm = LoadLibraryA("PROCSold.DLL");//加载原dll,写在dllmain中

eax值的保存

由于要使用popad,那么运算得到的eax的值就会同样被覆盖,所以我们需要将eax的值传送出去。
这里我们通过将eax的值mov到pushad的时候eax被压栈的地方即可,我们在popad命令执行之前,增加语句

mov[esp+28],eax;

这样eax的值就被保存起来了。

完整代码

代码已上传至github,有兴趣可以访问我们github。上传的代码只限于提供一个劫持的实验,具体应用则需要编写一个脚本,这里就不提供脚本了。
https://github.com/guanginuestc/DLL-hijacking

你可能感兴趣的:(windows安全)