• X64驱动:内核操作进线程/模块


    注意:下面的所有案例必须使用.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
    ",PsGetProcessImageFileName(eproc),PsGetProcessId(eproc),
    				PsGetProcessInheritedFromUniqueProcessId(eproc));
    			ObDereferenceObject(eproc);
    		}
    	}
    }
    
    VOID UnDriver(PDRIVER_OBJECT driver)
    {
    	DbgPrint(("Uninstall Driver Is OK 
    "));
    }
    
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
    {
    	EnumProcess();
    	Driver->DriverUnload = UnDriver;
    	return STATUS_SUCCESS;
    }
    


    内核终止进程: 结束进程的标准方法就是使用ZwOpenProcess打开进程获得句柄,然后使用ZwTerminateProcess结束,最后使用ZwClose关闭句柄,代码如下:

    #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 
    "));
    }
    
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
    {
    	int Retn = 0;
    	Retn = KillProcess("calc.exe");
    	DbgPrint("结束状态: %d 
    ", 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, &ethread)))
    		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
    ",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模块基址即可。

    #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
    ",(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成员),就能够得到全部的模块信息。

    #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 
    ", pModule->DllBase, pModule->SizeOfImages - (LONGLONG)pModule->DllBase, 
    				(LONGLONG)pModule->DllBase + pModule->SizeOfImages,pModule->BaseDllName);
    		}
    		pCurrentListEntry = pCurrentListEntry->Flink;
    	}
    	DriverObject->DriverUnload = DriverUnload;
    	return STATUS_SUCCESS;
    }
    


    ### 监控进程与线程创建

    监控进程的启动与退出可以使用 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:WindowsSystem32calc.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 
    ", 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;
    }
    


    ### 监控进程与线程对象操作

    监控进程对象和线程对象操作,可以使用ObRegisterCallbacks这个内核回调函数,通过回调我们可以实现保护calc.exe进程不被关闭,具体操作从OperationInformation->Object获得进程或线程的对象,然后再回调中判断是否是计算器,如果是就直接去掉TERMINATE_PROCESSTERMINATE_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;
    }
    

    首先运行计算器,然后启动驱动保护,此时我们在任务管理器中就无法结束计算器进程了。


    ### 监控进程中模块加载

    系统中的模块加载包括用户层模块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[] = "xB8x22x00x00xC0xC3";
    	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会提示拒绝访问,说明我们的驱动保护生效了。


    关键的内核进程骚操作已经分享完了,是不是一脸懵逼十脸茫然?这尼玛是什么鬼,有啥用?其实这东西用处可大了,杀软的主动防御系统,游戏的保护系统等都会用到这些东西,还觉得这些东西没用吗?

  • 相关阅读:
    简述密码
    w命令
    Python打包-Pyinstaller
    [转]油猴Tampermonkey-让百度云下载飞起来
    百度云高速下载Pandownload
    AdoConnect-获取连接字符串 (工具)
    Python爬虫-豆瓣电影 Top 250
    C++中使用ODBC API访问数据库例程
    Python爬虫-什么是爬虫?
    Python+Django(Python Web项目初体验)
  • 原文地址:https://www.cnblogs.com/LyShark/p/11706656.html
Copyright © 2020-2023  润新知