使用 PsSetCreateProcessNotifyRoutine、PsSetCreateThreadNotifyRoutine 註冊回調實現進程/執行緒監控時,驅動動態卸載若未清除回調,會導致 BUGCHECK 0xCE: DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS。原因:驅動卸載後,系統仍嘗試呼叫已卸載記憶體中的回調函數指標。
// 進程建立回調陣列
#define PSP_MAX_CREATE_PROCESS_NOTIFY 8
ULONG PspCreateProcessNotifyRoutineCount;
PCREATE_PROCESS_NOTIFY_ROUTINE PspCreateProcessNotifyRoutine[PSP_MAX_CREATE_PROCESS_NOTIFY];
// 執行緒建立回調陣列
#define PSP_MAX_CREATE_THREAD_NOTIFY 8
ULONG PspCreateThreadNotifyRoutineCount;
PCREATE_THREAD_NOTIFY_ROUTINE PspCreateThreadNotifyRoutine[PSP_MAX_CREATE_THREAD_NOTIFY];
// 回調函數原型
typedef VOID (*PCREATE_THREAD_NOTIFY_ROUTINE)(IN HANDLE ProcessId, IN HANDLE ThreadId, IN BOOLEAN Create);
typedef VOID (*PCREATE_PROCESS_NOTIFY_ROUTINE)(IN HANDLE ParentId, IN HANDLE ProcessId, IN BOOLEAN Create);
| API | 註冊 | 移除機制 |
|---|---|---|
PsSetCreateProcessNotifyRoutine | 陣列尋找 NULL 填入,計數器 +1 | 傳入 Remove=TRUE,匹配函數指標設 NULL,計數器 -1 |
PsSetCreateThreadNotifyRoutine (XP 前) | 同進程回調 | 無官方移除 API,導致無法乾淨卸載 |
PsRemoveCreateThreadNotifyRoutine (XP/2003+) | — | 遍歷陣列匹配指標清零,配合 ExWaitForRundownProtectionRelease 等待進行中回調完成 |
釋放 detport.sys 常駐核心、隱藏檔案、從不卸載,規避藍屏但屬權宜之計。
PspCreateThreadNotifyRoutine 陣列基址DriverUnload) 中遍歷陣列,將屬於本驅動的函數指標設為 NULL// 驅動卸載時清零回調陣列
VOID CleanupNotifyRoutines() {
// 定位陣列基址 (需根據 OS 版本動態解析)
PULONG pThreadArray = LocatePspCreateThreadNotifyRoutineArray();
PULONG pProcessArray = LocatePspCreateProcessNotifyRoutineArray();
for (INT i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
if (pThreadArray[i] == (ULONG)MyThreadNotifyRoutine)
pThreadArray[i] = 0;
}
for (INT i = 0; i < PSP_MAX_CREATE_PROCESS_NOTIFY; i++) {
if (pProcessArray[i] == (ULONG)MyProcessNotifyRoutine)
pProcessArray[i] = 0;
}
}
// 遠端執行緒建立偵測:掛鉤 NtCreateThreadEx / RtlCreateUserThread
NTSTATUS HookedNtCreateThreadEx(...) {
// 記錄目標進程 PID、執行緒起始位址、呼叫堆疊
LogRemoteThreadCreation(TargetProcess, StartAddress);
return OriginalNtCreateThreadEx(...);
}
RtlGetVersion 取得 BuildNumber,載入對應偏移量表InstallMonitor()/UninstallMonitor() 介面