Windows 2000/XP 內建的任務管理器是系統管理員和開發人員最常用的工具之一。XP 版本相比 2000 功能更強大、資訊更詳細,但你是否覺得它仍缺少某些關鍵資訊?或者覺得系統管理工具過於分散?本文將帶你深入探討任務管理器的開發原理,並動手實作一個功能更完整的任務管理器。我們將調用 Win32 API 實現核心功能,後續還將深入探討 Windows 核心機制。
進程管理是任務管理器最熟悉的功能。當懷疑中毒或遭遇木馬時,檢查運行中的進程是首要步驟。除了進程名稱,我們還需要獲取:
CPU 和記憶體資訊有助於分析進程運行狀態,優先權則反映進程在處理器調度中的優先級。此外,父進程 ID、創建時間、完整程式路徑等資訊在排查問題時同樣關鍵。
Windows 採用雙模式架構:核心模式和用戶模式。進程在兩者間頻繁切換,我們可以分別統計其在各模式下的 CPU 時間。記憶體管理相關指標包括:工作集、分頁池、非分頁池、頁面檔案使用量。I/O 操作統計涵蓋讀/寫/其他操作的次數與傳輸位元組數。
若懷疑某進程為木馬,僅有名稱遠遠不夠。獲取可執行檔完整路徑有助於確認程式身份。針對「創建遠程線程」等注入技術,枚舉進程加載的所有模組(主要是 DLL)至關重要。線程是 Windows 的執行實體,進程提供運行環境。枚舉進程內所有線程資訊,配合掛起/恢復/終止操作,構成完整的進程控制能力。值得注意的是,Windows 2000 下某些系統關鍵進程在原生任務管理器中無法終止,但我們的實作可突破此限制。
桌面窗口是主要交互介面。獲取窗口標題、關聯進程/線程 ID、窗口句柄屬性等資訊,功能類似 VC++ 的 Spy++ 工具,但整合在單一介面中更方便。
涵蓋系統句柄總數、進程/線程計數、物理記憶體總量/使用量、系統快取大小、認可記憶體狀態、核心分頁/非分頁池使用量等,幾乎囊括 Windows 記憶體管理的所有核心指標。
一次性查看所有磁碟(軟碟、硬碟、光碟等)的容量、使用量、檔案系統類型(FAT/NTFS/CDFS 等)及磁碟標籤,避免重複進入「我的電腦」查看。
環境塊包含環境變數字串,對應控制面板「系統/進階」中的設定。支援新增、刪除、編輯系統環境變數。
事件日誌分為應用程式、系統、安全三類,每類包含資訊、警告、錯誤類型。關鍵欄位:記錄序號、事件類型、事件 ID、來源、生成時間、用戶名、描述資訊。網管人員不可或缺的分析工具。
獲取網卡詳細資訊:名稱、描述、硬體位址、IP/子網路遮罩、閘道、DHCP 伺服器位址。同時統計收發廣播/非廣播資料報、錯誤計數、總收發位元組數,掌握網路流量狀況。
列舉所有共享資源的路徑、類型,支援刪除不必要的共享以防資料洩露。
Windows NT 核心支援多用戶。獲取密碼過期時間、用戶/群組 SID、帳號類型(權限區分)、磁碟配額限制、登入次數與時間等,便於審計用戶行為。
探測每個服務/驅動的啟動路徑、狀態、類型、啟動方式,支援啟動、停止、刪除操作。更多細節請參考《淺析 Windows 2000/XP 服務與後門技術》。
支援註銷、鎖定、定時關機/重啟(含訊息提示)、休眠、冬眠。Windows 2000 也能享用 XP 風格的倒數計時關機功能。
作業系統指紋、註冊組織/用戶、主機名、系統目錄等固定資訊。
HDESK hDesk = OpenDesktop(lpszDesktop, 0, FALSE, DESKTOP_ENUMERATE);
// 開啟預設桌面
EnumDesktopWindows(hDesk, (WNDENUMPROC)EnumWindowProc, 0);
// 枚舉桌面所有窗口,透過回調函數處理
BOOL __stdcall EnumWindowProc(HWND hWnd, LPARAM lParam) {
GetWindowText(hWnd, szWindowText, dwMaxCount); // 獲取標題
GetWindowThreadProcessId(hWnd, &dwPID); // 獲取進程/線程 ID
return TRUE;
}
SC_HANDLE schManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
// 以完整權限開啟服務控制管理器
EnumServicesStatus(schManager, dwDeviceType, dwDeviceState,
EnumStatus, dwBufSize, &dwBytesNeeded,
&dwDevicesReturned, &dwResumeHandle);
// 枚舉所有裝置驅動程式狀態
CloseServiceHandle(schManager); // 關閉句柄
SC_HANDLE schDevice = OpenService(schManager, szDeviceName, SERVICE_ALL_ACCESS);
// 開啟特定驅動程式
QueryServiceConfig(schDevice, lpDeviceConfig, 1024*8, &dwBytesNeeded); // 服務配置
QueryServiceStatus(schDevice, &DeviceStatus); // 當前狀態
QueryServiceConfig2(schDevice, SERVICE_CONFIG_DESCRIPTION,
(LPBYTE)lpDeviceDescription, 8*1024, &dwBytesNeeded); // 描述資訊
StartService(schDevice, 0, NULL); // 啟動
ControlService(schDevice, SERVICE_CONTROL_STOP, &DeviceStatus); // 停止
DeleteService(schDevice); // 刪除
GetLogicalDriveStrings(dwBufferLength, lpBuffer); // 獲取邏輯磁碟機字串
GetVolumeInformation(lpRootPathName, lpVolumeNameBuffer, dwVolumeNameSize,
&dwVolumeSerialNumber, &dwMaximumComponentLength,
&dwFileSystemFlags, lpFileSystemNameBuffer, dwFileSystemNameSize);
// 獲取磁碟區資訊:卷標、序號、檔案系統類型
GetDiskFreeSpaceEx(lpRootPathName, &FreeBytesAvailable,
&TotalNumberOfBytes, &TotalNumberOfFreeBytes); // 空間使用情況
RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKey, 0, KEY_QUERY_VALUE, &hKey);
// 開啟註冊表鍵:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
RegEnumValue(hKey, dwIndex, EnvironVariable, &dwVariableLength, NULL, NULL, NULL, NULL);
// 枚舉環境變數名稱
GetEnvironmentVariable(EnvironVariable, EnvironString, 1024); // 獲取變數值
HANDLE hEvent = OpenEventLog(NULL, szLog); // 開啟日誌
GetOldestEventLogRecord(hEvent, &dwThisRecord); // 獲取最舊記錄位置
ReadEventLog(hEvent, EVENTLOG_FORWARDS_READ | EVENTLOG_SEQUENTIAL_READ,
0, pEventLogRecord, 1024*32, &dwRead, &dwNeeded); // 讀取記錄
LookupAccountSid(NULL, pSid, szName, &dwName, szDomain, &dwDomain, &SNU); // SID 轉用戶名
GetNumberOfEventLogRecords(hEvent, &dwTotal); // 總記錄數
CloseEventLog(hEvent); // 關閉
NetShareEnum(NULL, dwLevel, (PBYTE*)&pBuf, MAX_PREFERRED_LENGTH,
&entriesread, &totalentries, &resume); // 級別 2 共享枚舉
NetApiBufferFree(pBuf); // 釋放緩衝區
NetShareDel(NULL, (char*)lpShareNameW, 0); // 刪除共享
GetAdaptersInfo(&AdapterInfo, &OutBufLen); // 獲取網卡資訊
GetPerformanceInfo(&PerfInfo, sizeof(PERFORMACE_INFORMATION));
// 獲取系統效能資訊
// 提升權限以獲取完整資訊
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken);
AdjustTokenPrivileges(hToken, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
// 啟用 SeDebugPrivilege
// 進程快照與枚舉
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
Process32First(hProcessSnap, &pe32);
do {
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
GetProcessTimes(hProcess, &CreateTime, &ExitTime, &KernelTime, &UserTime); // 時間資訊
GetProcessMemoryInfo(hProcess, &PMCounter, sizeof(PMCounter)); // 記憶體資訊
GetPriorityClass(hProcess); // 優先權
GetProcessIoCounters(hProcess, &IoCounters); // I/O 計數器
// 模組枚舉
HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pe32.th32ProcessID);
MODULEENTRY32 me32 = { sizeof(MODULEENTRY32) };
Module32First(hModuleSnap, &me32);
do { /* 處理模組資訊 */ } while (Module32Next(hModuleSnap, &me32));
// 線程枚舉
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 te32 = { sizeof(THREADENTRY32) };
Thread32First(hThreadSnap, &te32);
do {
if (te32.th32OwnerProcessID == pe32.th32ProcessID) {
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
// 可執行 SuspendThread/ResumeThread/TerminateProcess 等控制
}
} while (Thread32Next(hThreadSnap, &te32));
} while (Process32Next(hProcessSnap, &pe32));
AdjustTokenPrivileges(hToken, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
// 調整權限支援關機
ExitWindowsEx(EWX_LOGOFF, 0); // 註銷
LockWorkStation(); // 鎖定工作站
InitiateSystemShutdown(NULL, szMessage, dwTimeout, FALSE, bSig); // 定時關機/重啟
SetSystemPowerState(bSig, FALSE); // 休眠/冬眠
NetUserEnum(NULL, dwLevel, FILTER_NORMAL_ACCOUNT, (LPBYTE*)&pBuf,
dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries, &dwResumeHandle);
// 枚舉用戶資訊
NetUserDel(NULL, lpUserNameW); // 刪除用戶
GetVersionEx((LPOSVERSIONINFO)&osviex); // OS 版本
// 註冊表:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
GetTickCount(); // 開機時間
GetComputerName(szInfo, &dwInfo); // 電腦名
GetUserName(szInfo, &dwInfo); // 用戶名
GetWindowsDirectory(szInfo, MAX_PATH+1); // Windows 目錄
GetSystemDirectory(szInfo, MAX_PATH+1); // System 目錄
雖然我們已實現超越原生任務管理器的功能,但代碼多為直接調用 Win32 API。真正的系統程式設計師應深入探究核心機制:這些 API 內部如何實現?物件管理器、進程/線程調度、記憶體管理器的運作細節才是關鍵。微軟隱藏了大量內部細節,但這正是驅動我們深入研究 Windows 核心的動力。
本文配套完整原始碼供下載參考。