遠程 DLL 注入是 Windows 系統程式開發、安全研究、熱修復、調試器實現的核心技術。本文提供一個完整、可編譯的 C 語言實現:從提權 `SeDebugPrivilege`、遍歷進程獲取 PID、打開進程、分配遠程記憶體寫入 DLL 路徑、獲取 `LoadLibraryA` 地址、創建遠程線程執行載入、到資源清理,每步含錯誤處理與關鍵 API 解析。
DWORD EnablePrivilege(PCSTR name) {
HANDLE hToken;
TOKEN_PRIVILEGES tp = {1, {{0, 0, SE_PRIVILEGE_ENABLED}}};
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
LookupPrivilegeValue(NULL, name, &tp.Privileges[0].Luid);
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
DWORD err = GetLastError();
CloseHandle(hToken);
return err; // ERROR_SUCCESS = 0 表示成功
}
BOOL GetProcessIdByName(LPSTR szProcessName, LPDWORD lpPID) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE) return FALSE;
PROCESSENTRY32 pe = {0};
pe.dwSize = sizeof(pe);
if (!Process32First(hSnap, &pe)) { CloseHandle(hSnap); return FALSE; }
do {
if (lstrcmpi(pe.szExeFile, szProcessName) == 0) {
*lpPID = pe.th32ProcessID;
CloseHandle(hSnap);
return TRUE;
}
} while (Process32Next(hSnap, &pe));
CloseHandle(hSnap);
return FALSE;
}
BOOL LoadRemoteDll(DWORD dwProcessId, LPTSTR lpszLibName) {
BOOL bResult = FALSE;
HANDLE hProcess = NULL, hThread = NULL;
LPVOID pRemotePath = NULL;
__try {
// 1. 打開進程
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (!hProcess) __leave;
// 2. 分配遠程記憶體 (含結束符)
DWORD cch = 1 + lstrlen(lpszLibName);
pRemotePath = VirtualAllocEx(hProcess, NULL, cch, MEM_COMMIT, PAGE_READWRITE);
if (!pRemotePath) __leave;
// 3. 寫入 DLL 路徑
if (!WriteProcessMemory(hProcess, pRemotePath, lpszLibName, cch, NULL))
__leave;
// 4. 獲取 LoadLibraryA 地址
PTHREAD_START_ROUTINE pfnLoadLib = (PTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");
if (!pfnLoadLib) __leave;
// 5. 創建遠程線程
hThread = CreateRemoteThread(hProcess, NULL, 0, pfnLoadLib, pRemotePath, 0, NULL);
if (!hThread) __leave;
// 6. 等待載入完成
WaitForSingleObject(hThread, INFINITE);
bResult = TRUE;
}
__finally {
if (pRemotePath) VirtualFreeEx(hProcess, pRemotePath, 0, MEM_RELEASE);
if (hThread) CloseHandle(hThread);
if (hProcess) CloseHandle(hProcess);
}
return bResult;
}
int WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) {
DWORD pid;
if (EnablePrivilege(SE_DEBUG_NAME) != ERROR_SUCCESS) return 1;
if (!GetProcessIdByName("explorer.exe", &pid)) return 2;
if (!LoadRemoteDll(pid, "msg.dll")) return 3; // 需存在同目錄下的 msg.dll
return 0;
}
| API | 關鍵參數 | 說明 |
|---|---|---|
| OpenProcess | `PROCESS_ALL_ACCESS` | 需 `VM_OPERATION`/`VM_WRITE`/`CREATE_THREAD` 等權限組合 |
| VirtualAllocEx | `MEM_COMMIT \| PAGE_READWRITE` | 提交物理頁面,可讀寫。遠程路徑字串不需執行權限 |
| WriteProcessMemory | `lpBaseAddress = pRemotePath` | 複製本地緩衝區到遠程進程虛擬地址空間 |
| GetProcAddress | `"LoadLibraryA"` | ANSI 版本匹配 `LPTSTR` (編譯為 MBCS 時) / `LPSTR`。Unicode 專案需用 `LoadLibraryW` 並寫入寬字串 |
| CreateRemoteThread | `lpStartAddress = pfnLoadLib`, `lpParameter = pRemotePath` | 遠程線程從 `LoadLibraryA(pRemotePath)` 開始執行 |
| WaitForSingleObject | `INFINITE` | 阻塞等待 DLL 進入點 `DllMain` 執行完畢 |
掌握這條經典鏈路 (`OpenProcess` → `VirtualAllocEx` → `WriteProcessMemory` → `CreateRemoteThread`),是理解 Windows 進程間控制、記憶體管理、線程調度的最佳切入點,也是安全研究員必備的基礎技能樹根節點。