文章来源|MS08067红队手册
本文作者:为执着℡strugg*
DLL Proxying
我们通过DLL 侧载可以实现隐匿执行恶意代码,但存在一个问题,那就是需要去找到被劫持的DLL文件的导出函数以及确定哪个导出函数在DLL 被加载时会被执行。这个问题可以通过 DLLProxying技术去解决,该技术在保证自己想要执行的代码被执行的情况下简化这一查找的步骤又能保持原有二进制文件正常的调用被劫持 DLL 文件里的导出函数,进一步实现隐匿化。
一、DLL Proxying 介绍
在传统编程意义下的 DLL Proxying 是一种拦截和重定向对某个 DLL 调用的技术,这种技术通常用于以下几种场景:
1.功能扩展:通过代理某个现有的 DLL,开发者可以在不修改现在 DLL 的情况下添加新的功能或者修改现有功能。
2.调试与测试:代理 DLL 可以用于调试和测试,帮助开发者追踪和记录函数的调用及其参数。
3.安全与保护:通过代理 DLL,可以对某些函数的调用进行验证和过滤,提高系统的安全性。
从以上功能场景不难看出,该技术在传统编程意义下有点类似 hook的作用:扩展,监控,过滤。但在恶意代码编程的上下文中,DLL Proxying 更加类似请求转发,也可以看做一中DLL劫持技术。它具体的实现逻辑是,以上篇文章中提及到的 Dism.exe为例:
1.我们将 Dism.exe 执行要调用的 dismcore.dll 这个合法的 dll 文件重命名为任意名字,比如whatever.dll。
2.创建我们自己的 Dll 文件,导出所有的 whatever.dll 中的导出函数,命名为 dismcore.dll。
3.当我们自己创建的 dismcore.dll 被 dism.exe 载入进执行进程时,dismcore.dll 中的恶意代码得到执行。
4.当 Dism.exe 要执行 DllGetObjectClass 这个函数时,dismcore.dll 将调用请求转发给 whatever.dll文件,然后由 whatever.dll 执行对应要被调用的函数。这种情况下,我们自己代码可以得到执行,并且dism.exe 的功能还不会遭到破坏,进一步增加隐匿性。
可以参考如下流程图:
二、DLL Proxying 实现
从以上的逻辑流程中,我们不难看出,dismcore.dll 是DLL Proxying 技术中的核心,主要执行有两个功能:一是承载我们自己的恶意代码,当被主进程加载时执行我们自己的代码;二是转发whatever.dll 的导出函数调用请求给whatever.dll。下面我们依次来说明这两点如何实现。
2.1 进程加载自动执行代码
这个相对来说比较简单,我们可以使用DllMain 这个函数,来让操作系统自己加载执行我们的代码,代码如下:
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
// 执行代码
break;
case DLL_PROCESS_DETACH:
// 执行代码
break;
case DLL_THREAD_ATTACH:
// 执行代码
break;
case DLL_THREAD_DETACH:
// 执行代码
break;
}
return TRUE;
}
代码解释:
因此,我们可以定义一个我们想执行的函数,然后将该函数在在“caseDLL_PROCESS_ATTACH:”这个条件下,这样当进程加载执行我们自定义编写的 DLL 时,我们要执行的代码就会自动执行。我们可以使用以下代码进行测试:
include "pch.h"
BOOL WINAPI DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBox(NULL, L"Dll Proxying!", L"Test", MB_OK);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
使用rundll32.exe来模拟二进制文件加载执行DLL文件:
这就实现了当进程加载DLL 时,代码自动执行的功能。
2.2 函数调用转发
我们先看下面一行代码:
#pragma comment(linker, "/export:DllGetObjectClass=whatever.DllGetObjectClass")
其中关键的点其实就是#pragma comment 这条c/c++预编译指令,它告诉链接器导出/转发DllGetObjectClass 这个函数到whatever.dll 这个文件。这种情况下,当主进程调用这个函数时,我们恶意的dll 文件就会转发这个调用请求到原始的dll 文件。我们创建一个实验性的原始Dll 文件来测试这样是否可行,原始dll 文件代码如下:
恶意的dll文件如下:
将两个dll文件放在同一目录,使用rundll32来调用恶意dll文件中的whatever1函数:
可以看到,不仅whatever1 函数被转发执行了,DllMain 函数也自动被加载执行了。这就成功执行了Dll Proxying。大家可以使用dism.exe + dismcore.dll 练练手。
2.3 自动生成代理Dll 文件
在我们执行DLL Proxying 的过程中,有些被劫持的DLL 文件可能有几十个导出函数,那这种情况下手动去添加转发预编译指令就显得很费时间。Github 上有个工具可以帮我们节省这个过程: https://github.com/Flangvik/SharpDllProxy
第一个红框是被重命名后的原始dll 文件,第二个红框则是代理dll 文件的原始代码。我们看下代理dll 的源码:
从代码中可以看出,DismCore.dll 文件的四个导出函数都已被导出。这时,我们只需要编写自己的代码,放入DllMain 函数中执行,就可以隐匿实现执行自定义代码。
本文分享自 Ms08067安全实验室 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!