x64カーネルフック



X64 Kernel Hook



x64システムは、主に次の3つの関数をフックするのに役立つAPIを提供します

PsSetCreateProcessNotifyRoutineEx、この関数の機能は プロセスが作成されたときに通知します



PsSetCreateThreadNotifyRoutine、この関数の機能は スレッドが作成されると通知されます。

PsSetLoadImageNotifyRoutine、この関数の機能は モジュールがロードされると通知されます。



Callback function prototype VOID CreateProcessNotifyEx( __inout PEPROCESS Process, //The EPROCESS of the process will be provided to you. __in HANDLE ProcessId, //The process ID will be provided to you. __in_opt PPS_CREATE_NOTIFY_INFO CreateInfo //Additional information of process information will be provided to you. Note that the parameter is operable. That is to say, it may be NULL ) NTSTATUS PsSetCreateThreadNotifyRoutine( IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine //Callback function address. When the thread is created, but your callback is not yet run, it will be called. ) NTSTATUS PsSetLoadImageNotifyRoutine( IN PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine )

プロセスとスレッドのコールバックコードは次のとおりです。

#include NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process) NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process) /*typedef struct _PS_CREATE_NOTIFY_INFO { SIZE_T Size union { ULONG Flags struct { ULONG FileOpenNameAvailable :1 ULONG Reserved :31 } } HANDLE ParentProcessId CLIENT_ID CreatingThreadId struct _FILE_OBJECT *FileObject PCUNICODE_STRING ImageFileName PCUNICODE_STRING CommandLine NTSTATUS CreationStatus } PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO*/ /*VOID MyCreateProcessNotifyEx //Monitor only ( __inout PEPROCESS Process, __in HANDLE ProcessId, __in_opt PPS_CREATE_NOTIFY_INFO CreateInfo ) { if(CreateInfo==NULL) DbgPrint('[monitor_create_process_x64] Process exit: %s',PsGetProcessImageFileName(Process)) else DbgPrint('[monitor_create_process_x64] Process create: %wZ',CreateInfo->CommandLine) }*/ 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 ( __inout PEPROCESS Process, __in HANDLE ProcessId, __in_opt PPS_CREATE_NOTIFY_INFO CreateInfo ) { char procName[16] = { 0 } if (CreateInfo != NULL) //Process creation event { DbgPrint('[monitor_create_process_x64][%ld]%s create process: %wZ', CreateInfo->ParentProcessId, GetProcessNameByProcessId(CreateInfo->ParentProcessId), CreateInfo->ImageFileName) strcpy(procName, PsGetProcessImageFileName(Process)) DbgPrint('The name of the created process is: %p/n', procName) if (!_stricmp(procName, 'calc.exe')) { DbgPrint('Prohibit the creation of calculator process!') CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL //Prohibit the creation process } } else { DbgPrint('[monitor_create_process_x64] Process exit: %s', PsGetProcessImageFileName(Process)) } } VOID MyCreateThreadNotify ( IN HANDLE ProcessId, IN HANDLE ThreadId, IN BOOLEAN Create ) { if (Create) DbgPrint('[monitor_create_process_x64]Thread creation! PID=%ldTID=%ld', ProcessId, ThreadId) else DbgPrint('[monitor_create_process_x64] Thread exits! PID=%ldTID=%ld', ProcessId, ThreadId) PEPROCESS Process = NULL PETHREAD Thread = NULL UCHAR *pszImageName = NULL NTSTATUS status UCHAR *pWin32Address = NULL status = PsLookupProcessByProcessId(ProcessId, &Process)//1. Get EPROCESS by process ID if (!NT_SUCCESS(status)) return status = PsLookupThreadByThreadId(ThreadId, &Thread)//2. Get ETHREAD by thread ID pszImageName = PsGetProcessImageFileName(Process)//3. Get the process name through EPROCESS if (Create) { //dprintf('[Hello] Create Thread pid=%d tid=%d ImageName=%s ', ProcessId, ThreadId, pszImageName) if (strstr(pszImageName, 'calc') != NULL) //4. Determine whether the process name is a calculator { //KdBreakPoint() //Modify the callback function code pWin32Address = *(UCHAR**)((UCHAR*)Thread + 0x410) //5. If yes, find the callback function address. Change to C3 //KeAttachProcess() if (MmIsAddressValid(pWin32Address)) { KdBreakPoint() //*pWin32Address = 0xC3 //Modify to C3, but note that whether you modify the memory protection attribute needs to be removed. However, in 64-bit, inline assembly is not allowed. But you can write binary to change. Or assembly Generate obj, drive to use //Here directly change to C3 manually. } //KeUnstackDetachProcess() } } else //dprintf('[Hello] Exit Thread pid=%d tid=%d ', ProcessId, ThreadId) if (Process) ObDereferenceObject(Process) //Reference count-- if (Thread) ObDereferenceObject(Thread) } VOID Monitor() { // set create process/thread notify NTSTATUS st = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, FALSE) if (!NT_SUCCESS(st)) { DbgPrint('PsSetCreateProcessNotifyRoutineEx return false') } st = PsSetCreateThreadNotifyRoutine(MyCreateThreadNotify) if (!NT_SUCCESS(st)) { DbgPrint('PsSetCreateThreadNotifyRoutine return false') } } // Uninstall the driver, you must remove the callback, otherwise blue screen VOID RemoveMonitor() { //remove create process/thread notify PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, TRUE) PsRemoveCreateThreadNotifyRoutine(MyCreateThreadNotify) }

モジュール読み込みコールバック関数は次のとおりです

#include #include VOID UnicodeToChar(PUNICODE_STRING dst, char *src) { ANSI_STRING string RtlUnicodeStringToAnsiString(&string, dst, TRUE) strcpy(src, string.Buffer) RtlFreeAnsiString(&string) } PVOID GetDriverEntryByImageBase(PVOID pImageBase) { PIMAGE_DOS_HEADER pDOSHeader PIMAGE_NT_HEADERS64 pNTHeader PVOID pEntryPoint pDOSHeader = (PIMAGE_DOS_HEADER)pImageBase pNTHeader = (PIMAGE_NT_HEADERS64)((ULONG64)pImageBase + pDOSHeader->e_lfanew) return (PVOID)((ULONG64)pImageBase + pNTHeader->OptionalHeader.AddressOfEntryPoint) } BOOLEAN VxkCopyMemory(PVOID pDestination, PVOID pSourceAddress, SIZE_T SizeOfCopy) { PMDL pMdl = NULL PVOID pSafeAddress = NULL pMdl = IoAllocateMdl(pSourceAddress, (ULONG)SizeOfCopy, FALSE, FALSE, NULL) if (!pMdl) return FALSE __try { MmProbeAndLockPages(pMdl, KernelMode, IoReadAccess) } __except (EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl(pMdl) return FALSE } pSafeAddress = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority) if (!pSafeAddress) return FALSE RtlCopyMemory(pDestination, pSafeAddress, SizeOfCopy) MmUnlockPages(pMdl) IoFreeMdl(pMdl) return TRUE } VOID DenyLoadDriver(PVOID DriverEntry) { // 'Access denied' machine code // mov eax, c0000022h // ret // Machine code bit xB8x22x00x00xC0xC3 UCHAR fuck[] = 'xB8x22x00x00xC0xC3' VxkCopyMemory(DriverEntry, fuck, sizeof(fuck)) } VOID LoadImageNotifyRoutine( _In_ PUNICODE_STRING FullImageName, _In_ HANDLE ProcessId, // pid into which image is being mapped _In_ PIMAGE_INFO ImageInfo ) { PVOID pDrvEntry char szFullImageName[260] = { 0 } if (FullImageName != NULL && MmIsAddressValid(FullImageName)) { //KdPrint(('processid = %lld', ProcessId)) // According to the second parameter of the callback function LoadImageNotifyRoutine, if the PID is 0, it means that the driver is loaded, if the PID bit is non-zero, it means that the DLL is loaded. if (0 == ProcessId) { pDrvEntry = GetDriverEntryByImageBase(ImageInfo->ImageBase) UnicodeToChar(FullImageName, szFullImageName) KdPrint(('The currently loaded module is: %wZ', FullImageName)) // The function of strlwr function is to convert the S parameter in the string to lower case. if (strstr(szFullImageName, 'Win7_x64_SSDT_Hook.sys')) { DenyLoadDriver(pDrvEntry) } } } } VOID MonitorLoadModule() { NTSTATUS nt = PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine) if (!NT_SUCCESS(nt)) { KdPrint(('call PsSetLoadImageNotifyRoutine failed')) } } VOID RemoveMonitorLoadModule() { PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine) }