先前發表的《如何在 NT 下獲取進程的路徑》一文中,使用 PSAPI 枚舉處理序並取得模組路徑,但無法存取受保護的系統程序(如 csrss.exe、smss.exe、winlogon.exe)。VCKbase 網友 rovershen 指出:為枚舉處理序授予 SE_DEBUG_NAME 特權即可解決。經查證與實作,確認此法有效,特此補充完善。
自訂函式 EnablePrivilege 用於為目前程序授予指定特權(本例為 SE_DEBUG_NAME):
BOOL EnablePrivilege(HANDLE hToken, LPCSTR szPrivName)
{
TOKEN_PRIVILEGES tkp;
// 查詢特權 LUID
LookupPrivilegeValue(NULL, szPrivName, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// 通知系統修改權限
AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL);
return (GetLastError() == ERROR_SUCCESS);
}
流程:以 OpenProcessToken 取得目前程序存取權杖,呼叫 EnablePrivilege 啟用 SE_DEBUG_NAME,即可用 OpenProcess 開啟系統程序並取得路徑。
////////////////////////////////////////////////
// ShowProcessPath 2.0
// 版權所有 (C) 2005 趙春生
// 2005.09.02
// https://timw.yeah.net / https://timw.126.com
// 適用環境:WinNT 系列
// 測試環境:Win2000 Pro + SP4 + VC6 + SP6
////////////////////////////////////////////////
#include <stdio.h>
#include <windows.h>
#include "PSAPI.H"
#pragma comment(lib, "PSAPI.LIB")
// 自訂函式:授予指定特權
BOOL EnablePrivilege(HANDLE hToken, LPCSTR szPrivName);
int main(void)
{
DWORD processId[1024], needed, processCount, i;
HANDLE hProcess;
HMODULE hModule;
char path[MAX_PATH] = "", temp[256];
HANDLE hToken;
printf("ShowProcessPath 2.0 with [Process Status API]\n\n");
// 取得目前程序權杖並提升權限
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
if (EnablePrivilege(hToken, SE_DEBUG_NAME))
{
// 列舉所有處理序 ID
EnumProcesses(processId, sizeof(processId), &needed);
processCount = needed / sizeof(DWORD);
for (i = 0; i < processCount; i++)
{
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId[i]);
if (hProcess)
{
EnumProcessModules(hProcess, &hModule, sizeof(hModule), &needed);
GetModuleFileNameEx(hProcess, hModule, path, sizeof(path));
GetShortPathName(path, path, 256);
itoa(processId[i], temp, 10);
printf("%s --- %s\n", path, temp);
CloseHandle(hProcess);
}
else
{
printf("Failed!!! (PID: %lu)\n", processId[i]);
}
}
}
}
itoa(processCount, temp, 10);
printf("\nProcess Count: %s\n\n", temp);
return 0;
}
//////////////////////////////////////////////////////////////////////
BOOL EnablePrivilege(HANDLE hToken, LPCSTR szPrivName)
{
TOKEN_PRIVILEGES tkp;
LookupPrivilegeValue(NULL, szPrivName, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL);
return (GetLastError() == ERROR_SUCCESS);
}
加入 SE_DEBUG_NAME 特權後,程式成功列出 csrss.exe、smss.exe、winlogon.exe 等系統程序的完整路徑,解決了原版本無法存取受保護程序的問題。
本文配套原始碼可供下載。