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

Windows 進程枚舉完整技術指南:PSAPI、ToolHelp、WMI 與遠端枚舉實戰

     編輯:關於C語言

進程結構基礎:什麼是進程?

Windows 進程包含兩大核心部分:

  1. 核心物件 (Kernel Object):系統管理進程的內部結構,存放 PID、優先權、安全描述符、統計資訊 (CPU 時間、記憶體用量) 等。
  2. 位址空間 (Address Space):包含 EXE/DLL 程式碼、資料、堆疊、堆積、執行緒上下文等。每進程獨立 4GB (32-bit) 或 8TB+ (64-bit) 虛擬空間。

四大枚舉技術對比

技術API/DLL支援版本遠端支援資訊豐富度複雜度
PSAPIpsapi.dll / psapi.hNT 4.0+否 (僅本機)基本 (名稱、PID、模組)低
ToolHelp32tlhelp32.h / kernel32.dllWin95/NT 5.0+否豐富 (模組、執行緒、堆積)中
WMI (Win32_Process)wbemcli / System.ManagementWin2K+是 (需管理員權限)極豐富 (命令列、擁有者、WQL 查詢)高 (COM)
Native API (NtQuerySystemInformation)ntdll.dll (非文檔)所有 NT否最完整 (核心結構直接映射)極高 (不穩定)

PSAPI 實作:最簡單的本機枚舉


#include 
#include 
#include 
#pragma comment(lib, "psapi.lib")

void PrintProcessNameAndID(DWORD processID)
{
    char szProcessName[MAX_PATH] = "unknown";
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
    if (hProcess) {
        HMODULE hMod;
        DWORD cbNeeded;
        if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) {
            GetModuleBaseNameA(hProcess, hMod, szProcessName, sizeof(szProcessName));
        }
    }
    printf("%-20s %-8u\n", szProcessName, processID);
    if (hProcess) CloseHandle(hProcess);
}

int main()
{
    DWORD aProcesses[1024], cbNeeded, cProcesses;
    if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) return 1;
    cProcesses = cbNeeded / sizeof(DWORD);
    printf("PID      Process Name\n");
    for (unsigned int i = 0; i < cProcesses; i++)
        if (aProcesses[i] != 0) PrintProcessNameAndID(aProcesses[i]);
    return 0;
}

關鍵函數:EnumProcesses、OpenProcess、EnumProcessModules、GetModuleBaseName、GetModuleFileNameEx (完整路徑)。

ToolHelp32 實作:支援模組/執行緒/堆積枚舉


#include 
#pragma comment(lib, "kernel32.lib")

void ListProcessModules(DWORD dwPID)
{
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, dwPID);
    if (hSnap == INVALID_HANDLE_VALUE) return;
    MODULEENTRY32 me32 = { sizeof(me32) };
    if (Module32First(hSnap, &me32)) {
        do {
            printf("  Module: %-30s Base: 0x%p Size: %u\n",
                me32.szModule, me32.modBaseAddr, me32.modBaseSize);
        } while (Module32Next(hSnap, &me32));
    }
    CloseHandle(hSnap);
}

void ListProcessThreads(DWORD dwPID)
{
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    THREADENTRY32 te32 = { sizeof(te32) };
    if (Thread32First(hSnap, &te32)) {
        do {
            if (te32.th32OwnerProcessID == dwPID)
                printf("  Thread ID: %u Priority: %d\n", te32.th32ThreadID, te32.tpBasePri);
        } while (Thread32Next(hSnap, &te32));
    }
    CloseHandle(hSnap);
}

遠端進程枚舉:WMI 實作 (需管理員權限)


#import "wbemdisp.tlb" raw_interfaces_only
using namespace WbemScripting;

void EnumRemoteProcesses(LPCWSTR pszServer, LPCWSTR pszUser, LPCWSTR pszPwd)
{
    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

    _bstr_t ns = L"\\\\" + _bstr_t(pszServer) + L"\\root\\cimv2";
    IWbemLocatorPtr pLoc; pLoc.CreateInstance(CLSID_WbemLocator);
    IWbemServicesPtr pSvc; pLoc->ConnectServer(ns, _bstr_t(pszUser), _bstr_t(pszPwd), NULL, 0, NULL, NULL, &pSvc);
    CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);

    IEnumWbemClassObjectPtr pEnum;
    pSvc->ExecQuery(_bstr_t("WQL"), _bstr_t("SELECT Name, ProcessId, CommandLine, ExecutablePath FROM Win32_Process"),
        WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);

    IWbemClassObjectPtr pObj; ULONG uRet;
    while (pEnum->Next(WBEM_INFINITE, 1, &pObj, &uRet) == S_OK) {
        _variant_t vName, vPid, vCmd, vPath;
        pObj->Get(L"Name", 0, &vName, NULL, NULL);
        pObj->Get(L"ProcessId", 0, &vPid, NULL, NULL);
        pObj->Get(L"CommandLine", 0, &vCmd, NULL, NULL);
        pObj->Get(L"ExecutablePath", 0, &vPath, NULL, NULL);
        wprintf(L"PID: %d  Name: %s\n  Cmd: %s\n  Path: %s\n\n", (int)vPid, (BSTR)vName, (BSTR)vCmd, (BSTR)vPath);
    }
    CoUninitialize();
}

進階:獲取完整命令列與使用者 (本機)

PSAPI/ToolHelp 無法直接取得命令列參數。解法:

  • WMI Win32_Process.CommandLine (本機/遠端)。
  • Native API NtQueryInformationProcess + ProcessCommandLineInformation (Win10 1607+)。
  • 註入 DLL 讀取 PEB->ProcessParameters->CommandLine (複雜、風險高)。

64-bit 相容性注意事項

  • 32-bit 進程枚舉 64-bit 進程需 CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) 於 64-bit 進程中執行,或使用 WMI。
  • EnumProcessModulesEx (PSAPI) 支援 LIST_MODULES_64BIT/32BIT 過濾。
  • WMI 天然跨架構。

安全性與權限

  • OpenProcess 需 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ。
  • 受保護進程 (PPL)、系統進程 (PID 0, 4) 可能拒絕存取。
  • 遠端 WMI 需目標機開啟 WMI 服務、防火牆開放 135/445 埠、帳號屬 Administrators 群組。

總結

按需選擇技術:簡單本機列表用 PSAPI;需模組/執行緒細節用 ToolHelp;需命令列、遠端、WQL 查詢用 WMI;極致效能/核心級資訊研究 Native API。實際專案常組合使用:ToolHelp 列舉 PID → PSAPI 取模組路徑 → WMI 補充命令列/擁有者。

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