在前面的文章中LyShark
一直在重復的實現對系統底層模塊的枚舉,今天我們將展開一個新的話題,內核監控,我們以監控進程線程
創建為例,在Win10
系統中監控進程與線程可以使用微軟提供給我們的兩個新函數來實現,此類函數的原理是創建一個回調事件,當有進程或線程被創建或者注銷時,系統會通過回調機制將該進程相關信息優先返回給我們自己的函數待處理結束后再轉向系統層。
PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine是Windows操作系統提供的兩個內核回調函數,它們允許開發者在進程或線程發生創建事件時攔截并處理這些事件。這兩個函數提供的回調機制是操作系統提供的最基本、最常用的內核監控進程與線程的方式。
PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine的使用方式和參數類型類似,它們都需要開發者提供一個回調函數,當進程或線程被創建時,操作系統會調用這個回調函數。這個回調函數需要滿足一定的約束條件,例如不能阻塞或掛起進程或線程的創建或訪問,不能調用一些內核API函數等。
PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine的主要區別在于它們所監控的事件不同。PsSetCreateProcessNotifyRoutineEx用于監控進程的創建事件,當有新的進程被創建時,操作系統會調用注冊的回調函數。而PsSetCreateThreadNotifyRoutine用于監控線程的創建事件,當有新的線程被創建時,操作系統會調用注冊的回調函數。
內核監控進程PsSetCreateProcessNotifyRoutineEx和線程PsSetCreateThreadNotifyRoutine回調在安全軟件、系統監控和調試工具等領域有著廣泛的應用。需要注意的是,在Windows 8及更高版本的操作系統中,微軟推薦開發者使用ExRegisterCallback和ExUnregisterCallback函數進行回調的注冊和注銷。
進程回調默認會設置CreateProcess
通知,而線程回調則會設置CreateThread
通知,我們來看ARK工具中的枚舉效果。
- 通常情況下:
- PsSetCreateProcessNotifyRoutineEx 用于監控進程
- PsSetCreateThreadNotifyRoutine 用于監控線程
PsSetCreateProcessNotifyRoutineEx
監控進程的啟動與退出可以使用 PsSetCreateProcessNotifyRoutineEx
來創建回調,當新進程創建時會優先執行回調,我們看下微軟是如何定義的結構。
// 參數1: 新進程回調函數
// 參數2: 是否注銷
NTSTATUS PsSetCreateProcessNotifyRoutineEx([in] PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine,[in] BOOLEAN Remove
);
如上,該函數只有兩個參數,第一個參數是回調函數,第二個參數是是否注銷,通常在驅動退出時可以傳入TRUE
對該回調進行注銷,通常情況下如果驅動關閉,則必須要注銷回調,而對于MyLySharkCreateProcessNotifyEx
自定義回調來說,則需要指定三個必須要有的參數傳遞。
// 參數1: 新進程的EProcess
// 參數2: 新進程PID
// 參數3: 新進程詳細信息 (僅在創建進程時有效)VOID MyLySharkCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
根據如上函數定義,就可以實現監控功能了,例如我們監控如果進程名是lyshark.exe
則直接CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL
禁止該進程打開。
#include <ntifs.h>// 兩個未公開函數導出
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);// 通過PID獲得進程名
PCHAR GetProcessNameByProcessId(HANDLE ProcessId)
{NTSTATUS st = STATUS_UNSUCCESSFUL;PEPROCESS ProcessObj = NULL;PCHAR string = NULL;st = PsLookupProcessByProcessId(ProcessId, &ProcessObj);if (NT_SUCCESS(st)){string = PsGetProcessImageFileName(ProcessObj);ObfDereferenceObject(ProcessObj);}return string;
}// 繞過簽名檢查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64typedef struct _KLDR_DATA_TABLE_ENTRY{LIST_ENTRY listEntry;ULONG64 __Undefined1;ULONG64 __Undefined2;ULONG64 __Undefined3;ULONG64 NonPagedDebugInfo;ULONG64 DllBase;ULONG64 EntryPoint;ULONG SizeOfImage;UNICODE_STRING path;UNICODE_STRING name;ULONG Flags;USHORT LoadCount;USHORT __Undefined5;ULONG64 __Undefined6;ULONG CheckSum;ULONG __padding1;ULONG TimeDateStamp;ULONG __padding2;} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#elsetypedef struct _KLDR_DATA_TABLE_ENTRY{LIST_ENTRY listEntry;ULONG unknown1;ULONG unknown2;ULONG unknown3;ULONG unknown4;ULONG unknown5;ULONG unknown6;ULONG unknown7;UNICODE_STRING path;UNICODE_STRING name;ULONG Flags;} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endifPKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;pLdrData->Flags = pLdrData->Flags | 0x20;return TRUE;
}// 進程回調函數
VOID My_LyShark_Com_CreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{char ProcName[16] = { 0 };if (CreateInfo != NULL){strcpy_s(ProcName, 16, PsGetProcessImageFileName(Process));DbgPrint("[LyShark] 父進程ID: %ld | 父進程名: %s | 進程名: %s | 進程路徑:%wZ \n", CreateInfo->ParentProcessId, GetProcessNameByProcessId(CreateInfo->ParentProcessId), PsGetProcessImageFileName(Process), CreateInfo->ImageFileName);// 判斷是否為指定進程if (0 == _stricmp(ProcName, "lyshark.exe")){// 禁止打開CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;}}else{strcpy_s(ProcName, 16, PsGetProcessImageFileName(Process));DbgPrint("[LyShark] 進程[ %s ] 退出了, 程序被關閉", ProcName);}
}VOID UnDriver(PDRIVER_OBJECT driver)
{DWORD32 ref = 0;// 注銷進程回調ref = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)My_LyShark_Com_CreateProcessNotifyEx, TRUE);DbgPrint("[lyshark] 注銷進程回調: %d \n", ref);
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{NTSTATUS status;// 繞過簽名檢查// LINKER_FLAGS=/INTEGRITYCHECKBypassCheckSign(Driver);DbgPrint("hello lyshark \n");// 創建進程回調// 參數1: 新進程的EProcess// 參數2: 新進程PID// 參數3: 新進程詳細信息 (僅在創建進程時有效)status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)My_LyShark_Com_CreateProcessNotifyEx, FALSE);if (!NT_SUCCESS(status)){DbgPrint("[lyshark] 創建進程回調錯誤");}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}
編譯并運行這個驅動程序,我們可以在ARK
工具中看到這個驅動所加載的CreateProcess
的回調事件。
當驅動加載后,如果你嘗試打開lyshark.exe
那么會提示連接的設備沒有發揮作用,我們則成功攔截了這次打開,當然如果在打開進程之前掃描其特征并根據特征拒絕進程打開,那么就可以實現一個簡單的防惡意程序,進程監控在防惡意程序中也是用的最多的。
PsSetCreateThreadNotifyRoutine
說完了PsSetCreateProcessNotifyRoutineEx
回調的使用方式,LyShark將繼續帶大家看看線程監控
如何實現,監控線程創建與監控進程差不多,檢測線程需要調用PsSetCreateThreadNotifyRoutine
創建回調函數,之后就可監控系統所有線程的創建,具體實現代碼如下。
#include <ntifs.h>// 兩個未公開函數導出
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI NTSTATUS PsLookupThreadByThreadId(HANDLE ThreadId, PETHREAD *Thread);// 繞過簽名檢查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64typedef struct _KLDR_DATA_TABLE_ENTRY{LIST_ENTRY listEntry;ULONG64 __Undefined1;ULONG64 __Undefined2;ULONG64 __Undefined3;ULONG64 NonPagedDebugInfo;ULONG64 DllBase;ULONG64 EntryPoint;ULONG SizeOfImage;UNICODE_STRING path;UNICODE_STRING name;ULONG Flags;USHORT LoadCount;USHORT __Undefined5;ULONG64 __Undefined6;ULONG CheckSum;ULONG __padding1;ULONG TimeDateStamp;ULONG __padding2;} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#elsetypedef struct _KLDR_DATA_TABLE_ENTRY{LIST_ENTRY listEntry;ULONG unknown1;ULONG unknown2;ULONG unknown3;ULONG unknown4;ULONG unknown5;ULONG unknown6;ULONG unknown7;UNICODE_STRING path;UNICODE_STRING name;ULONG Flags;} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endifPKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;pLdrData->Flags = pLdrData->Flags | 0x20;return TRUE;
}// 線程回調函數
VOID MyCreateThreadNotify(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN CreateInfo)
{PEPROCESS eprocess = NULL;PETHREAD ethread = NULL;UCHAR *pWin32Address = NULL;// 通過此函數拿到程序的EPROCESS結構PsLookupProcessByProcessId(ProcessId, &eprocess);PsLookupThreadByThreadId(ThreadId, ðread);if (CreateInfo){DbgPrint("[lyshark] 線程TID: %1d | 所屬進程名: %s | 進程PID: %1d \n", ThreadId, PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess));/*if (0 == _stricmp(PsGetProcessImageFileName(eprocess), "lyshark.exe")){DbgPrint("線程TID: %1d | 所屬進程名: %s | 進程PID: %1d \n", ThreadId, PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess));// dt _kthread// 尋找里面的 Win32StartAddress 并寫入retpWin32Address = *(UCHAR**)((UCHAR*)ethread + 0x1c8);if (MmIsAddressValid(pWin32Address)){*pWin32Address = 0xC3;}}*/}else{DbgPrint("[LyShark] %s 線程已退出...", ThreadId);}if (eprocess)ObDereferenceObject(eprocess);if (ethread)ObDereferenceObject(ethread);
}VOID UnDriver(PDRIVER_OBJECT driver)
{NTSTATUS status;// 注銷進程回調status = PsRemoveCreateThreadNotifyRoutine(MyCreateThreadNotify);
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{NTSTATUS status;DbgPrint("hello lyshark \n");// 繞過簽名檢查// LINKER_FLAGS=/INTEGRITYCHECKBypassCheckSign(Driver);// 創建線程回調// 參數1: 新線程ProcessID// 參數2: 新線程ThreadID// 參數3: 線程創建/退出標志status = PsSetCreateThreadNotifyRoutine(MyCreateThreadNotify);if (!NT_SUCCESS(status)){DbgPrint("創建線程回調錯誤");}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}
運行后則可監控到系統總所有線程的創建與退出,效果如下所示: