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

不注入 DLL 也能拿命令列:跨進程讀取 GetCommandLine 內存地址技術

     編輯:關於VC++

常見需求:監控工具、除錯器、自動化腳本需取得其他進程的命令列參數。GetCommandLine() 僅能取得自身進程參數,傳統做法是 DLL 注入 + 遠端呼叫,但這觸發防毒軟體、提高複雜度。本文揭示一個「零注入」技巧:直接讀取目標進程記憶體中 GetCommandLine 函式內部儲存命令列字串指標的位置。

核心原理

  1. kernel32.dll!GetCommandLineA/W 在各進程中的虛擬位址相同 (ASLR 下系統 DLL 基址一致)。
  2. 反組譯發現該函式極簡單:mov eax, [固定位址] / ret,該固定位址即儲存命令列字串指標。
  3. 我們在自身進程解析該指令取得「指標的位址」,再用 ReadProcessMemory 遠端讀取兩次:
    第一次拿到字串指標,第二次拿到字串內容。

不同 Windows 版本的指令碼差異

OSGetCommandLineA 反組譯 (關鍵部分)解析邏輯
NT/2K/XP/2003/Vista/7/10/11A1 xx xx xx xx mov eax, [addr]第 2-5 位元組即為 addr (儲存字串指標的全域變數位址)
95/98/Me多層間接:mov eax, [addr1] → mov ecx, [eax] → mov eax, [ecx+0xC0] → 可能再跳 [ecx+0x40] → [eax+8]需模擬指令流程,逐層 ReadProcessMemory 解析

完整跨平台實作 (Unicode 就緒)


#include 
#include 

DWORD GetRemoteCommandLine(DWORD dwPID, LPTSTR lpBuffer, DWORD cchBuffer)
{
    HANDLE hProc = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwPID);
    if (!hProc) return GetLastError();

    // 1. 取得自身 GetCommandLineW 位址
    FARPROC pfn = GetProcAddress(GetModuleHandle(_T("kernel32.dll")), _T("GetCommandLineW"));
    if (!pfn) { CloseHandle(hProc); return ERROR_PROC_NOT_FOUND; }

    // 2. 解析指令取得「儲存字串指標的全域變數位址」
    //    x86:  mov eax, [imm32]  -> 0xA1 + 4 bytes
    //    x64:  mov rax, [rip+disp32] -> 0x48 0xA1 + 4 bytes disp
    //    這裡示範 x86;生產碼需判斷目標進程位元數並解析對應指令
    BYTE* code = (BYTE*)pfn;
    DWORD_PTR dwAddrOfPtr = 0;
    #ifdef _WIN64
        // 簡化:假設前 2 bytes 為 48 A1
        if (code[0] == 0x48 && code[1] == 0xA1) {
            INT32 disp = *(INT32*)(code + 2);
            dwAddrOfPtr = (DWORD_PTR)(code + 6) + disp; // RIP-relative
        }
    #else
        if (code[0] == 0xA1) {
            dwAddrOfPtr = *(DWORD_PTR*)(code + 1);
        }
    #endif
    if (!dwAddrOfPtr) { CloseHandle(hProc); return ERROR_INVALID_FUNCTION; }

    // 3. 遠端讀取兩次
    DWORD_PTR remoteStrPtr = 0;
    SIZE_T bytesRead = 0;
    if (!ReadProcessMemory(hProc, (LPCVOID)dwAddrOfPtr, &remoteStrPtr, sizeof(remoteStrPtr), &bytesRead) ||
        bytesRead != sizeof(remoteStrPtr)) {
        CloseHandle(hProc); return ERROR_PARTIAL_COPY;
    }
    if (!ReadProcessMemory(hProc, (LPCVOID)remoteStrPtr, lpBuffer, cchBuffer * sizeof(TCHAR), &bytesRead)) {
        CloseHandle(hProc); return ERROR_PARTIAL_COPY;
    }
    lpBuffer[cchBuffer - 1] = 0; // 保底終止
    CloseHandle(hProc);
    return ERROR_SUCCESS;
}

實戰注意事項

  • 位元數匹配:32-bit 進程讀 64-bit 進程需 PROCESS_VM_READ + PROCESS_QUERY_LIMITED_INFORMATION 並在 WoW64 下正確解析 x64 指令;建議編譯為 64-bit 專案統一處理。
  • ASLR 與 DLL 重新基址:系統 DLL (kernel32.dll, kernelbase.dll) 在同一啟動會話中跨進程基址一致,重開機後才會變,故可直接用自身位址。
  • Unicode/ANSI:GetCommandLineW 回傳 wchar_t*,讀取時以 WCHAR 緩衝區接收,再 WideCharToMultiByte 轉碼。
  • 權限:需目標進程同用戶或具 SeDebugPrivilege;UAC 高完整性進程需以管理員執行。
  • 防禦規避:純 ReadProcessMemory 無執行緒建立、無代碼注入,行為較隱蔽,但仍可能被 EDR 監控跨進程讀取行為。

替代方案對比

結語

「讀取 GetCommandLine 內部指標」是一項經典且實用的逆向工程技巧,適用於除錯器、進程監控、自動化測試等場景。掌握 x86/x64 指令解析、ReadProcessMemory 跨進程讀取、PEB 結構等基礎知識,即可靈活變通、舉一反三。完整專案原始碼 (含 x86/x64 自動偵測、Unicode 支援、錯誤處理) 請參考文章附件。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方法優點缺點
本文法 (讀記憶體)無注入、速度快、程式碼簡潔需解析機器碼、跨位元數麻煩、可能被 EDR 攔截
WMI Win32_Process.CommandLine官方 API、支援遠端機器需 WMI 服務、權限高、查詢較慢
NtQueryInformationProcess + PEB核心級、穩定非公開 API、結構跨版本變動
DLL 注入 + GetCommandLine通用、簡單觸發防毒、注入失風險、需管理載入/卸載