注意:下面的全部案例必须使用.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> 关键的内核进程骚操做已经分享完了,是否是一脸懵逼十脸茫然?这尼玛是什么鬼,有啥用?其实这东西用处可大了,杀软的主动防护系统,游戏的保护系统等都会用到这些东西,还以为这些东西没用吗?