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\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, &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\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_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;
}

首先運行計算器,而後啓動驅動保護,此時咱們在任務管理器中就沒法結束計算器進程了。

<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> 關鍵的內核進程騷操做已經分享完了,是否是一臉懵逼十臉茫然?這尼瑪是什麼鬼,有啥用?其實這東西用處可大了,殺軟的主動防護系統,遊戲的保護系統等都會用到這些東西,還以爲這些東西沒用嗎?

相關文章
相關標籤/搜索