注意:下面的全部案例必須使用.C結尾的文件,且必須在連接選項中加入 /INTEGRITYCHECK
選項,不然編譯根本沒法經過(整合修正,Win10可編譯,須在測試模式下進行),內核代碼相對固定,若是對內核編程不太熟的話,建議不要隨意修改代碼,任何一處錯誤的調用都會致使系統藍屏,大佬繞過!編程
下方全部代碼,均在 Windows 10 LTSC 企業版中測試,通過修改後代碼均無任何問題,放心不會藍屏!函數
內核枚舉進線程/模塊
內核枚舉進程: 進程就是活動起來的程序,每個進程在內核裏,都有一個名爲 EPROCESS
的結構記錄它的詳細信息,其中就包括進程名,PID,PPID,進程路徑等,一般在應用層枚舉進程只列出全部進程的編號便可,不過在內核層須要把它的 EPROCESS 地址給列舉出來。測試
內核枚舉進程使用PspCidTable
這個未公開的函數,它能最大的好處是能獲得進程的EPROCESS地址,因爲是未公開的函數,因此咱們須要變相的調用這個函數,經過PsLookupProcessByProcessId
函數查到進程的EPROCESS,若是PsLookupProcessByProcessId
返回失敗,則證實此進程不存在,若是返回成功則把EPROCESS、PID、PPID、進程名等經過DbgPrint打印到屏幕上。優化
#include <ntifs.h> NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process); //未公開的進行導出便可 NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);//未公開進行導出 // 根據進程ID返回進程EPROCESS結構體,失敗返回NULL PEPROCESS LookupProcess(HANDLE Pid) { PEPROCESS eprocess = NULL; NTSTATUS Status = STATUS_UNSUCCESSFUL; Status = PsLookupProcessByProcessId(Pid, &eprocess); if (NT_SUCCESS(Status)) return eprocess; return NULL; } VOID EnumProcess() { PEPROCESS eproc = NULL; for (int temp = 0; temp < 100000; temp += 4) { eproc = LookupProcess((HANDLE)temp); if (eproc != NULL) { DbgPrint("進程名: %s --> 進程PID = %d --> 父進程PPID = %d\r\n",PsGetProcessImageFileName(eproc),PsGetProcessId(eproc), PsGetProcessInheritedFromUniqueProcessId(eproc)); ObDereferenceObject(eproc); } } } VOID UnDriver(PDRIVER_OBJECT driver) { DbgPrint(("Uninstall Driver Is OK \n")); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { EnumProcess(); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
內核終止進程: 結束進程的標準方法就是使用ZwOpenProcess
打開進程得到句柄,而後使用ZwTerminateProcess
結束,最後使用ZwClose
關閉句柄,代碼以下:spa
#include <ntifs.h> NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process); // 根據進程ID返回進程EPROCESS結構體,失敗返回NULL PEPROCESS GetProcessNameByProcessId(HANDLE pid) { PEPROCESS ProcessObj = NULL; NTSTATUS Status = STATUS_UNSUCCESSFUL; Status = PsLookupProcessByProcessId(pid, &ProcessObj); if (NT_SUCCESS(Status)) return ProcessObj; return NULL; } // 根據ProcessName獲取到進程的PID號 HANDLE GetPidByProcessName(char *ProcessName) { PEPROCESS pCurrentEprocess = NULL; HANDLE pid = 0; for (int i = 0; i < 1000000000; i += 4) { pCurrentEprocess = GetProcessNameByProcessId((HANDLE)i); if (pCurrentEprocess != NULL) { pid = PsGetProcessId(pCurrentEprocess); if (strstr(PsGetProcessImageFileName(pCurrentEprocess), ProcessName) != NULL) { ObDereferenceObject(pCurrentEprocess); return pid; } ObDereferenceObject(pCurrentEprocess); } } return (HANDLE)-1; } int KillProcess(char *ProcessName) { PEPROCESS pCurrentEprocess = NULL; HANDLE pid = 0; HANDLE Handle = NULL; OBJECT_ATTRIBUTES obj; CLIENT_ID cid = { 0 }; NTSTATUS Status = STATUS_UNSUCCESSFUL; for (int i = 0; i < 10000000; i += 4) { pCurrentEprocess = GetProcessNameByProcessId((HANDLE)i); if (pCurrentEprocess != NULL) { pid = PsGetProcessId(pCurrentEprocess); if (strstr(PsGetProcessImageFileName(pCurrentEprocess), ProcessName) != NULL) { ObDereferenceObject(pCurrentEprocess); DbgPrint("已經找到對應的PID,開始執行結束代碼..."); InitializeObjectAttributes(&obj, NULL, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); cid.UniqueProcess = (HANDLE)pid; cid.UniqueThread = 0; Status = ZwOpenProcess(&Handle, GENERIC_ALL, &obj, &cid); if (NT_SUCCESS(Status)) { ZwTerminateProcess(Handle, 0); ZwClose(Handle); } ZwClose(Handle); return 0; } ObDereferenceObject(pCurrentEprocess); } } return -1; } VOID UnDriver(PDRIVER_OBJECT driver) { DbgPrint(("Uninstall Driver Is OK \n")); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { int Retn = 0; Retn = KillProcess("calc.exe"); DbgPrint("結束狀態: %d \n", Retn); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
內核枚舉線程: 內核線程的枚舉與進程類似,線程中也存在一個ETHREAD結構,但在枚舉線程以前須要先來枚舉到指定進程的eprocess結構,而後在根據eprocess結構對指定線程進行枚舉。線程
#include <ntddk.h> #include <windef.h> //聲明API NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process); NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE Id, PEPROCESS *Process); NTKERNELAPI NTSTATUS PsLookupThreadByThreadId(HANDLE Id, PETHREAD *Thread); NTKERNELAPI PEPROCESS IoThreadToProcess(PETHREAD Thread); //根據進程ID返回進程EPROCESS,失敗返回NULL PEPROCESS LookupProcess(HANDLE Pid) { PEPROCESS eprocess = NULL; if (NT_SUCCESS(PsLookupProcessByProcessId(Pid, &eprocess))) return eprocess; else return NULL; } //根據線程ID返回線程ETHREAD,失敗返回NULL PETHREAD LookupThread(HANDLE Tid) { PETHREAD ethread; if (NT_SUCCESS(PsLookupThreadByThreadId(Tid, ðread))) return ethread; else return NULL; } //枚舉指定進程中的線程 VOID EnumThread(PEPROCESS Process) { ULONG i = 0, c = 0; PETHREAD ethrd = NULL; PEPROCESS eproc = NULL; for (i = 4; i<262144; i = i + 4) // 通常來講沒有超過100000的PID和TID { ethrd = LookupThread((HANDLE)i); if (ethrd != NULL) { //得到線程所屬進程 eproc = IoThreadToProcess(ethrd); if (eproc == Process) { //打印出ETHREAD和TID DbgPrint("線程: ETHREAD=%p TID=%ld\n",ethrd,(ULONG)PsGetThreadId(ethrd)); } ObDereferenceObject(ethrd); } } } // 經過枚舉的方式定位到指定的進程,這裏傳遞一個進程名稱 VOID MyEnumThread(char *ProcessName) { ULONG i = 0; PEPROCESS eproc = NULL; for (i = 4; i<100000000; i = i + 4) { eproc = LookupProcess((HANDLE)i); if (eproc != NULL) { ObDereferenceObject(eproc); if (strstr(PsGetProcessImageFileName(eproc), ProcessName) != NULL) { EnumThread(eproc); // 相等則說明是咱們想要的進程,直接枚舉其中的線程 } } } } VOID DriverUnload(IN PDRIVER_OBJECT DriverObject){} NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { MyEnumThread("calc.exe"); DriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
內核枚舉進程模塊: 枚舉進程中的全部模塊信息,DLL模塊記錄在 PEB 的 LDR 鏈表裏,LDR 是一個雙向鏈表,枚舉鏈表便可,相應的卸載可以使用MmUnmapViewOfSection
函數,分別傳入進程的EPROCESS,DLL模塊基址便可。3d
#include <ntddk.h> #include <windef.h> //聲明結構體 typedef struct _KAPC_STATE { LIST_ENTRY ApcListHead[2]; PKPROCESS Process; UCHAR KernelApcInProgress; UCHAR KernelApcPending; UCHAR UserApcPending; } KAPC_STATE, *PKAPC_STATE; typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY64 InLoadOrderLinks; LIST_ENTRY64 InMemoryOrderLinks; LIST_ENTRY64 InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; PVOID SectionPointer; ULONG CheckSum; PVOID LoadedImports; PVOID EntryPointActivationContext; PVOID PatchInformation; LIST_ENTRY64 ForwarderLinks; LIST_ENTRY64 ServiceTagLinks; LIST_ENTRY64 StaticLinks; PVOID ContextInformation; ULONG64 OriginalBase; LARGE_INTEGER LoadTime; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; ULONG64 LdrInPebOffset = 0x018; //peb.ldr ULONG64 ModListInPebOffset = 0x010; //peb.ldr.InLoadOrderModuleList //聲明API NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process); NTKERNELAPI PPEB PsGetProcessPeb(PEPROCESS Process); NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process); //根據進程ID返回進程EPROCESS,失敗返回NULL PEPROCESS LookupProcess(HANDLE Pid) { PEPROCESS eprocess = NULL; if (NT_SUCCESS(PsLookupProcessByProcessId(Pid, &eprocess))) return eprocess; else return NULL; } //枚舉指定進程的模塊 VOID EnumModule(PEPROCESS Process) { SIZE_T Peb = 0; SIZE_T Ldr = 0; PLIST_ENTRY ModListHead = 0; PLIST_ENTRY Module = 0; ANSI_STRING AnsiString; KAPC_STATE ks; //EPROCESS地址無效則退出 if (!MmIsAddressValid(Process)) return; //獲取PEB地址 Peb = (SIZE_T)PsGetProcessPeb(Process); //PEB地址無效則退出 if (!Peb) return; //依附進程 KeStackAttachProcess(Process, &ks); __try { //得到LDR地址 Ldr = Peb + (SIZE_T)LdrInPebOffset; //測試是否可讀,不可讀則拋出異常退出 ProbeForRead((CONST PVOID)Ldr, 8, 8); //得到鏈表頭 ModListHead = (PLIST_ENTRY)(*(PULONG64)Ldr + ModListInPebOffset); //再次測試可讀性 ProbeForRead((CONST PVOID)ModListHead, 8, 8); //得到第一個模塊的信息 Module = ModListHead->Flink; while (ModListHead != Module) { //打印信息:基址、大小、DLL路徑 DbgPrint("模塊基址=%p 大小=%ld 路徑=%wZ\n",(PVOID)(((PLDR_DATA_TABLE_ENTRY)Module)->DllBase), (ULONG)(((PLDR_DATA_TABLE_ENTRY)Module)->SizeOfImage),&(((PLDR_DATA_TABLE_ENTRY)Module)->FullDllName)); Module = Module->Flink; //測試下一個模塊信息的可讀性 ProbeForRead((CONST PVOID)Module, 80, 8); } } __except (EXCEPTION_EXECUTE_HANDLER){;} //取消依附進程 KeUnstackDetachProcess(&ks); } // 經過枚舉的方式定位到指定的進程,這裏傳遞一個進程名稱 VOID MyEnumModule(char *ProcessName) { ULONG i = 0; PEPROCESS eproc = NULL; for (i = 4; i<100000000; i = i + 4) { eproc = LookupProcess((HANDLE)i); if (eproc != NULL) { ObDereferenceObject(eproc); if (strstr(PsGetProcessImageFileName(eproc), ProcessName) != NULL) { EnumModule(eproc); // 相等則說明是咱們想要的進程,直接枚舉其中的線程 } } } } VOID DriverUnload(IN PDRIVER_OBJECT DriverObject){} NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { MyEnumModule("calc.exe"); DriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
內核枚舉加載SYS文件: 內核中的SYS文件也是經過雙向鏈表的方式相鏈接的,咱們能夠經過遍歷LDR_DATA_TABLE_ENTRY
結構(遍歷自身DriverSection成員),就可以獲得所有的模塊信息。code
#include <ntddk.h> #include <wdm.h> typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImages; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { struct { ULONG TimeDateStamp; }; struct { PVOID LoadedImports; }; }; }LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; VOID DriverUnload(IN PDRIVER_OBJECT DriverObject){} NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { ULONG count = 0; NTSTATUS Status; DriverObject->DriverUnload = DriverUnload; PLDR_DATA_TABLE_ENTRY pLdr = NULL; PLIST_ENTRY pListEntry = NULL; PLDR_DATA_TABLE_ENTRY pModule = NULL; PLIST_ENTRY pCurrentListEntry = NULL; pLdr = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection; pListEntry = pLdr->InLoadOrderLinks.Flink; pCurrentListEntry = pListEntry->Flink; while (pCurrentListEntry != pListEntry) { pModule = CONTAINING_RECORD(pCurrentListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if (pModule->BaseDllName.Buffer != 0) { DbgPrint("基址:%p ---> 偏移:%p ---> 結束地址:%p---> 模塊名:%wZ \r\n", pModule->DllBase, pModule->SizeOfImages - (LONGLONG)pModule->DllBase, (LONGLONG)pModule->DllBase + pModule->SizeOfImages,pModule->BaseDllName); } pCurrentListEntry = pCurrentListEntry->Flink; } DriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
<br> ### 監控進程與線程建立orm
監控進程的啓動與退出可使用 PsSetCreateProcessNotifyRoutineEx
來建立回調,當新進程產生時,回調函數會被率先執行,而後執行咱們本身的MyCreateProcessNotifyEx
函數,並在內部進行打印輸出。對象
#include <ntddk.h> NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process); NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process); 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; } VOID MyCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo) { char ProcName[16] = { 0 }; if (CreateInfo != NULL) { strcpy(ProcName, PsGetProcessImageFileName(Process)); DbgPrint("父進程ID: %ld --->父進程名: %s --->進程名: %s---->進程路徑:%wZ", CreateInfo->ParentProcessId, GetProcessNameByProcessId(CreateInfo->ParentProcessId), PsGetProcessImageFileName(Process),CreateInfo->ImageFileName); } else { strcpy(ProcName, PsGetProcessImageFileName(Process)); DbgPrint("進程[ %s ] 離開了,程序被關閉了",ProcName); } } VOID UnDriver(PDRIVER_OBJECT driver) { PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, TRUE); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { NTSTATUS status; status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, FALSE); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
在上方代碼基礎上進行必定的改進,思路:經過PsGetProcessImageFileName
即將PID轉換爲進程名,而後經過_stricmp
對比,若是發現是calc.exe
進程則拒絕執行,禁止特定服務的運行,實現代碼以下:
#include <ntddk.h> NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process); NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process); 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; } VOID MyCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo) { char ProcName[16] = { 0 }; if (CreateInfo != NULL) { strcpy(ProcName, PsGetProcessImageFileName(Process)); if (!_stricmp(ProcName, "calc.exe")) { CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL; } } } VOID UnDriver(PDRIVER_OBJECT driver) { PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, TRUE); DbgPrint(("驅動卸載成功")); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { NTSTATUS status; status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, FALSE); Driver->DriverUnload = UnDriver; DbgPrint("驅動加載成功!"); return STATUS_SUCCESS; }
將上方代碼編譯,當咱們加載驅動程序之後,再次打開C:\Windows\System32\calc.exe
計算器進程則提示沒法打開,咱們的驅動已經成功的攔截了本次的請求。
而檢測線程操做與檢測進程差很少,檢測線程須要調用PsSetCreateThreadNotifyRoutine
建立回調函數,而後就能夠檢測線程的建立了,具體代碼以下:
#include <ntddk.h> NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process); NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process); VOID MyCreateThreadNotify(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create) { PEPROCESS eprocess = NULL; PsLookupProcessByProcessId(ProcessId, &eprocess); // 經過此函數拿到程序的EPROCESS結構 if (Create) DbgPrint("線程TID: %1d --> 所屬進程名: %s --> 進程PID: %1d \n", ThreadId, PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess)); else DbgPrint("%s 線程已退出...", ThreadId); } VOID UnDriver(PDRIVER_OBJECT driver) { PsRemoveCreateThreadNotifyRoutine(MyCreateThreadNotify); DbgPrint(("驅動卸載成功")); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { NTSTATUS status; status = PsSetCreateThreadNotifyRoutine(MyCreateThreadNotify); DbgPrint("PsSetCreateThreadNotifyRoutine: %x", status); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
<br> ### 監控進程與線程對象操做
監控進程對象和線程對象操做,可使用ObRegisterCallbacks
這個內核回調函數,經過回調咱們能夠實現保護calc.exe進程不被關閉,具體操做從OperationInformation->Object
得到進程或線程的對象,而後再回調中判斷是不是計算器,若是是就直接去掉TERMINATE_PROCESS
或TERMINATE_THREAD
權限便可,附上進程監控回調的寫法:
#include <ntddk.h> #include <ntstrsafe.h> PVOID Globle_Object_Handle; OB_PREOP_CALLBACK_STATUS MyObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) { DbgPrint("執行了咱們的回調函數..."); return STATUS_SUCCESS; } VOID UnDriver(PDRIVER_OBJECT driver) { ObUnRegisterCallbacks(Globle_Object_Handle); DbgPrint("回調卸載完成..."); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { OB_OPERATION_REGISTRATION Base; // 回調函數結構體(你所填的結構都在這裏) OB_CALLBACK_REGISTRATION CallbackReg; CallbackReg.RegistrationContext = NULL; // 註冊上下文(你回調函數返回參數) CallbackReg.Version = OB_FLT_REGISTRATION_VERSION; // 註冊回調版本 CallbackReg.OperationRegistration = &Base; CallbackReg.OperationRegistrationCount = 1; // 操做計數(下鉤數量) RtlUnicodeStringInit(&CallbackReg.Altitude, L"600000"); // 長度 Base.ObjectType = PsProcessType; // 進程操做類型.此處爲進程操做 Base.Operations = OB_OPERATION_HANDLE_CREATE; // 操做句柄建立 Base.PreOperation = MyObjectCallBack; // 你本身的回調函數 Base.PostOperation = NULL; if (ObRegisterCallbacks(&CallbackReg, &Globle_Object_Handle)) // 註冊回調 DbgPrint("回調註冊成功..."); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
上方代碼運行後,咱們能夠打開Xuetr掃描一下內核Object鉤子,能夠看到已經成功掛鉤了。
檢測計算器進程的關閉狀態,代碼以下:
#include <ntddk.h> #include <wdm.h> #include <ntstrsafe.h> #define PROCESS_TERMINATE 1 PVOID Globle_Object_Handle; NTKERNELAPI UCHAR * PsGetProcessImageFileName(__in PEPROCESS Process); char* GetProcessImageNameByProcessID(ULONG ulProcessID) { NTSTATUS Status; PEPROCESS EProcess = NULL; Status = PsLookupProcessByProcessId((HANDLE)ulProcessID, &EProcess); if (!NT_SUCCESS(Status)) return FALSE; ObDereferenceObject(EProcess); return (char*)PsGetProcessImageFileName(EProcess); } OB_PREOP_CALLBACK_STATUS MyObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Operation) { char ProcName[256] = { 0 }; HANDLE pid = PsGetProcessId((PEPROCESS)Operation->Object); // 取出當前調用函數的PID strcpy(ProcName, GetProcessImageNameByProcessID((ULONG)pid)); // 經過PID取出進程名,而後直接拷貝內存 //DbgPrint("當前進程的名字是:%s", ProcName); if (strstr(ProcName, "win32calc.exe")) { if (Operation->Operation == OB_OPERATION_HANDLE_CREATE) { if ((Operation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE) { DbgPrint("你想結束進程?"); // 若是是計算器,則去掉它的結束權限,在Win10上無效 Operation->Parameters->CreateHandleInformation.DesiredAccess = ~THREAD_TERMINATE; return STATUS_UNSUCCESSFUL; } } } return STATUS_SUCCESS; } VOID UnDriver(PDRIVER_OBJECT driver) { ObUnRegisterCallbacks(Globle_Object_Handle); DbgPrint("回調卸載完成..."); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { NTSTATUS obst = 0; OB_CALLBACK_REGISTRATION obReg; OB_OPERATION_REGISTRATION opReg; memset(&obReg, 0, sizeof(obReg)); obReg.Version = ObGetFilterVersion(); obReg.OperationRegistrationCount = 1; obReg.RegistrationContext = NULL; RtlInitUnicodeString(&obReg.Altitude, L"321125"); obReg.OperationRegistration = &opReg; memset(&opReg, 0, sizeof(opReg)); opReg.ObjectType = PsProcessType; opReg.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; opReg.PreOperation = (POB_PRE_OPERATION_CALLBACK)&MyObjectCallBack; obst = ObRegisterCallbacks(&obReg, &Globle_Object_Handle); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
首先運行計算器,而後啓動驅動保護,此時咱們在任務管理器中就沒法結束計算器進程了。
<br> ### 監控進程中模塊加載
系統中的模塊加載包括用戶層模塊DLL和內核模塊SYS的加載,在 Windows X64 環境下咱們能夠調用 PsSetLoadImageNotifyRoutine
內核函數來設置一個映像加載通告例程,當有驅動或者DLL被加載時,回調函數就會被調用從而執行咱們本身的回調例程。
#include <ntddk.h> #include <ntimage.h> PVOID GetDriverEntryByImageBase(PVOID ImageBase) { PIMAGE_DOS_HEADER pDOSHeader; PIMAGE_NT_HEADERS64 pNTHeader; PVOID pEntryPoint; pDOSHeader = (PIMAGE_DOS_HEADER)ImageBase; pNTHeader = (PIMAGE_NT_HEADERS64)((ULONG64)ImageBase + pDOSHeader->e_lfanew); pEntryPoint = (PVOID)((ULONG64)ImageBase + pNTHeader->OptionalHeader.AddressOfEntryPoint); return pEntryPoint; } VOID MyLoadImageNotifyRoutine(PUNICODE_STRING FullImageName,HANDLE ProcessId,PIMAGE_INFO ImageInfo) { PVOID pDrvEntry; if (FullImageName != NULL && MmIsAddressValid(FullImageName)) // MmIsAddress 驗證地址可用性 { if (ProcessId == 0) { pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase); DbgPrint("模塊名稱:%wZ --> 裝載基址:%p --> 鏡像長度: %d", FullImageName, pDrvEntry,ImageInfo->ImageSize); } } } VOID UnDriver(PDRIVER_OBJECT driver) { PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)MyLoadImageNotifyRoutine); DbgPrint("驅動卸載完成..."); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)MyLoadImageNotifyRoutine); DbgPrint("驅動加載完成..."); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
接着咱們給上方的代碼加上判斷功能,只需在上方代碼的基礎上小改一下便可,須要注意回調函數中的第二個參數,若是返回值爲零則表示加載SYS,若是返回非零則表示加載DLL
VOID UnicodeToChar(PUNICODE_STRING dst, char *src) { ANSI_STRING string; RtlUnicodeStringToAnsiString(&string, dst, TRUE); strcpy(src, string.Buffer); RtlFreeAnsiString(&string); } VOID MyLoadImageNotifyRoutine(PUNICODE_STRING FullImageName,HANDLE ModuleStyle,PIMAGE_INFO ImageInfo) { PVOID pDrvEntry; char szFullImageName[256] = { 0 }; if (FullImageName != NULL && MmIsAddressValid(FullImageName)) // MmIsAddress 驗證地址可用性 { if (ModuleStyle == 0) // ModuleStyle爲零表示加載sys非零表示加載DLL { pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase); UnicodeToChar(FullImageName, szFullImageName); if (strstr(_strlwr(szFullImageName), "hook.sys")) { DbgPrint("準備攔截SYS內核模塊:%s", _strlwr(szFullImageName)); } } } }
上方代碼就能夠判斷加載的模塊並做出處理動做了,可是咱們仍然沒法判斷究竟是那個進程加載的hook.sys
驅動,由於回調函數很底層,到了必定的深度以後就沒法判斷究竟是誰主動引起的行爲了,一切都是系統的行爲。
判斷了是驅動後,接着咱們就要實現屏蔽驅動,經過ImageInfo->ImageBase
來獲取被加載驅動程序hook.sys
的映像基址,而後找到NT頭的OptionalHeader節點,該節點裏面就是被加載驅動入口的地址,經過彙編在驅動頭部寫入ret返回指令,便可實現屏蔽加載特定驅動文件。
#include <ntddk.h> #include <intrin.h> #include <ntimage.h> PVOID GetDriverEntryByImageBase(PVOID ImageBase) { PIMAGE_DOS_HEADER pDOSHeader; PIMAGE_NT_HEADERS64 pNTHeader; PVOID pEntryPoint; pDOSHeader = (PIMAGE_DOS_HEADER)ImageBase; pNTHeader = (PIMAGE_NT_HEADERS64)((ULONG64)ImageBase + pDOSHeader->e_lfanew); pEntryPoint = (PVOID)((ULONG64)ImageBase + pNTHeader->OptionalHeader.AddressOfEntryPoint); return pEntryPoint; } VOID UnicodeToChar(PUNICODE_STRING dst, char *src) { ANSI_STRING string; RtlUnicodeStringToAnsiString(&string, dst, TRUE); strcpy(src, string.Buffer); RtlFreeAnsiString(&string); } // 使用開關寫保護須要在 C/C++ 優化中啓用內部函數 KIRQL WPOFFx64() // 關閉寫保護 { KIRQL irql = KeRaiseIrqlToDpcLevel(); UINT64 cr0 = __readcr0(); cr0 &= 0xfffffffffffeffff; _disable(); __writecr0(cr0); return irql; } void WPONx64(KIRQL irql) // 開啓寫保護 { UINT64 cr0 = __readcr0(); cr0 |= 0x10000; _enable(); __writecr0(cr0); KeLowerIrql(irql); } BOOLEAN DenyLoadDriver(PVOID DriverEntry) { UCHAR fuck[] = "\xB8\x22\x00\x00\xC0\xC3"; KIRQL kirql; /* 在模塊開頭寫入如下彙編指令 Mov eax,c0000022h ret */ if (DriverEntry == NULL) return FALSE; kirql = WPOFFx64(); memcpy(DriverEntry, fuck,sizeof(fuck) / sizeof(fuck[0])); WPONx64(kirql); return TRUE; } VOID MyLoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ModuleStyle, PIMAGE_INFO ImageInfo) { PVOID pDrvEntry; char szFullImageName[256] = { 0 }; if (FullImageName != NULL && MmIsAddressValid(FullImageName)) // MmIsAddress 驗證地址可用性 { if (ModuleStyle == 0) // ModuleStyle爲零表示加載sys非零表示加載DLL { pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase); UnicodeToChar(FullImageName, szFullImageName); if (strstr(_strlwr(szFullImageName), "hook.sys")) { DbgPrint("攔截SYS內核模塊:%s", szFullImageName); DenyLoadDriver(pDrvEntry); } } } } VOID UnDriver(PDRIVER_OBJECT driver) { PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)MyLoadImageNotifyRoutine); DbgPrint("驅動卸載完成..."); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)MyLoadImageNotifyRoutine); DbgPrint("驅動加載完成..."); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
屏蔽DLL加載,只須要在上面的代碼上稍微修改一下就好,這裏提供到另外一種寫法。
char *UnicodeToLongString(PUNICODE_STRING uString) { ANSI_STRING asStr; char *Buffer = NULL;; RtlUnicodeStringToAnsiString(&asStr, uString, TRUE); Buffer = ExAllocatePoolWithTag(NonPagedPool, uString->MaximumLength * sizeof(wchar_t), 0); if (Buffer == NULL) return NULL; RtlCopyMemory(Buffer, asStr.Buffer, asStr.Length); return Buffer; } VOID MyLoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ModuleStyle, PIMAGE_INFO ImageInfo) { PVOID pDrvEntry; char *PareString = NULL; if (MmIsAddressValid(FullImageName)) { if (ModuleStyle != 0) // 非零則監控DLL加載 { PareString = UnicodeToLongString(FullImageName); if (PareString != NULL) { if (strstr(PareString, "hook.dll")) { pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase); if (pDrvEntry != NULL) DenyLoadDriver(pDrvEntry); } } } } }
咱們以屏蔽SYS內核模塊爲例,當驅動文件WinDDK.sys
被加載後,嘗試加載hook.sys
會提示拒絕訪問,說明咱們的驅動保護生效了。
<br> 關鍵的內核進程騷操做已經分享完了,是否是一臉懵逼十臉茫然?這尼瑪是什麼鬼,有啥用?其實這東西用處可大了,殺軟的主動防護系統,遊戲的保護系統等都會用到這些東西,還以爲這些東西沒用嗎?