首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Windows C 語言遠程 DLL 注入完整實現:OpenProcess、VirtualAllocEx、CreateRemoteThread 核心鏈路

     編輯:關於C語言

遠程 DLL 注入是 Windows 系統程式開發、安全研究、熱修復、調試器實現的核心技術。本文提供一個完整、可編譯的 C 語言實現:從提權 `SeDebugPrivilege`、遍歷進程獲取 PID、打開進程、分配遠程記憶體寫入 DLL 路徑、獲取 `LoadLibraryA` 地址、創建遠程線程執行載入、到資源清理,每步含錯誤處理與關鍵 API 解析。

一、核心流程圖

  1. 提權:`EnablePrivilege(SE_DEBUG_PRIVILEGE)` — 獲取操作系統進程權限。
  2. 尋標:`CreateToolhelp32Snapshot` + `Process32First/Next` — 按名稱獲取目標 PID (示例:`explorer.exe`)。
  3. 開門:`OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)` — 獲取目標進程句柄。
  4. 佈局:`VirtualAllocEx` — 目標進程空間分配頁面 (RW),寫入 DLL 完整路徑。
  5. 寫入:`WriteProcessMemory` — 將本地 DLL 路徑字串複製到遠程分配區。
  6. 找槍:`GetModuleHandle("Kernel32")` + `GetProcAddress("LoadLibraryA")` — 獲取載入函數地址 (系統 DLL 基址通常共享)。
  7. 扣板機:`CreateRemoteThread` — 在目標進程創建線程,起始地址 = `LoadLibraryA`,參數 = 遠程路徑地址。
  8. 待命:`WaitForSingleObject` — 等待遠程線程結束 (DLL 已載入)。
  9. 收場:`VirtualFreeEx` 釋放遠程記憶體,`CloseHandle` 關閉線程/進程句柄。

二、完整代碼實現

1. 提權函數


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 表示成功
}

2. 獲取進程 PID


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

3. 核心注入函數


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

4. 入口點


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 參數解析表

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` 執行完畢

四、架構兼容性陷阱 (32/64 位元)

  • 注入器與目標進程位數必須一致:32 位注入器只能注入 32 位進程,64 位同理。
  • DLL 位數必須與目標進程一致。
  • Visual Studio 預設 32 位編譯,需在 Configuration Manager 選擇 `x64` 平台編譯 64 位版本。
  • `GetModuleHandle("Kernel32")` 返回基址在同位數進程中通常共享 (ASLR 系統重啟後固定),跨位數則完全不同。

五、現代防禦與繞過 (僅供防禦視角理解)

  1. EDR/HOOK 攔截:`CreateRemoteThread`、`VirtualAllocEx`、`WriteProcessMemory` 常被監控。繞過:`NtCreateThreadEx`、手動映射、APC 注入、執行緒劫持。
  2. PPL (Protected Process Light):`explorer.exe` 非 PPL,但 `lsass.exe` 等受保護,`OpenProcess` 會失敗 `ERROR_ACCESS_DENIED`。
  3. CFG (Control Flow Guard):間接調用目標需在有效列表,`LoadLibraryA` 通常在列表中。
  4. AMSI:掃描 DLL 內容,非簽名/惡意特徵會攔截。

六、合法用途與道德邊界

  • ✅ 合法:調試器附加、熱修復補丁、插件架構 (如圖形外掛)、互操作性橋接。
  • ❌ 非法:繞過授權、竊取憑證、隱藏惡意代碼、規避防病毒。
  • ⚠️ 原則:僅在擁有授權的系統/進程上測試,代碼簽名、審計日志、最小權限原則。

掌握這條經典鏈路 (`OpenProcess` → `VirtualAllocEx` → `WriteProcessMemory` → `CreateRemoteThread`),是理解 Windows 進程間控制、記憶體管理、線程調度的最佳切入點,也是安全研究員必備的基礎技能樹根節點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved