ring0 SSDTHook 实现x64/x86
#include "HookSSDT.h" #include <ntimage.h> #define SEC_IMAGE 0x001000000 ULONG32 __NtOpenProcessIndex = 0; PVOID __ServiceTableBase = NULL; ULONG32 __OldNtOpenProcessOffset = 0; PVOID __OldNtOpenProcess = NULL; UCHAR __OldCode[15] = {0}; BOOLEAN __IsHook = FALSE; NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath) { NTSTATUS Status = STATUS_SUCCESS; ULONG32 v1 = 0; char FunctionName[] = "NtOpenProcess"; ULONG64 SSDTAddress = 0; DriverObject->DriverUnload = DriverUnload; if (GetSSDTAddress(&SSDTAddress) == FALSE) { return Status; } DbgPrint("Win7x64 SSDT:%p\r\n", SSDTAddress); //寻找23H if (GetSSDTFunctionIndexFromNtdllExportTableByFunctionName(FunctionName, &__NtOpenProcessIndex) == FALSE) { return STATUS_UNSUCCESSFUL; } __ServiceTableBase = ((PSERVER_SERVICE_DESCRIPTOR_TABLE)SSDTAddress)->Unknow0; __OldNtOpenProcessOffset = ((PULONG32)__ServiceTableBase)[__NtOpenProcessIndex]; v1 = __OldNtOpenProcessOffset >> 4; __OldNtOpenProcess = (PVOID)((ULONG64)__ServiceTableBase + v1); HookSSDT(__ServiceTableBase,__NtOpenProcessIndex,FakeOpenProcess,KeBugCheckEx,5,__OldCode,15); return Status; } BOOLEAN GetSSDTAddress(ULONG64* SSDTAddress) { //kd> rdmsr c0000082 PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082); PUCHAR EndSearchAddress = StartSearchAddress + PAGE_SIZE; PUCHAR i = NULL; UCHAR v1 = 0, v2 = 0, v3 = 0; INT64 Offset = 0; //002320c7 ULONG64 VariableAddress = 0; *SSDTAddress = NULL; for (i = StartSearchAddress; i < EndSearchAddress; i++) { if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2)) { v1 = *i; v2 = *(i + 1); v3 = *(i + 2); if (v1 == 0x4c && v2 == 0x8d && v3 == 0x15) { memcpy(&Offset, i + 3, 4); *SSDTAddress = Offset + (ULONG64)i + 7; break; } } } //Win32 导出表 搜索 KeServiceDescriptorTable if (*SSDTAddress == NULL) { return FALSE; } return TRUE; } BOOLEAN GetSSDTFunctionIndexFromNtdllExportTableByFunctionName(CHAR* FunctionName,ULONG32* SSDTFunctionIndex) { /* 0:004> u zwopenprocess ntdll!NtOpenProcess: 00000000`774ddc10 4c8bd1 mov r10,rcx 00000000`774ddc13 b823000000 mov eax,23h 00000000`774ddc18 0f05 syscall 00000000`774ddc1a c3 ret 00000000`774ddc1b 0f1f440000 nop dword ptr [rax+rax] */ ULONG i; BOOLEAN IsOk = FALSE; WCHAR FileFullPath[] = L"\\SystemRoot\\System32\\ntdll.dll"; //C:\Windows\ SIZE_T MappingViewSize = 0; PVOID MappingBaseAddress = NULL; PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL; PIMAGE_NT_HEADERS NtHeader = NULL; UINT32* AddressOfFunctions = NULL; UINT32* AddressOfNames = NULL; UINT16* AddressOfNameOrdinals = NULL; CHAR* v1 = NULL; ULONG32 FunctionOrdinal = 0; PVOID FunctionAddress = 0; ULONG32 Offset_SSDTFunctionIndex = 4; //将Ntdll.dll 当前的空间中 *SSDTFunctionIndex = -1; IsOk = MappingPEFileInRing0Space(FileFullPath,&MappingBaseAddress, &MappingViewSize); if (IsOk==FALSE) { return FALSE; } else { __try{ NtHeader = RtlImageNtHeader(MappingBaseAddress); //extern if (NtHeader && NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) { ImageExportDirectory =(IMAGE_EXPORT_DIRECTORY*)((UINT8*)MappingBaseAddress + NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); //AddressOfFunctions 指向函数地址数组 AddressOfFunctions = (UINT32*)((UINT8*)MappingBaseAddress + ImageExportDirectory->AddressOfFunctions); AddressOfNames = (UINT32*)((UINT8*)MappingBaseAddress + ImageExportDirectory->AddressOfNames); AddressOfNameOrdinals = (UINT16*)((UINT8*)MappingBaseAddress + ImageExportDirectory->AddressOfNameOrdinals); for(i = 0; i < ImageExportDirectory->NumberOfNames; i++) { v1 = (char*)((ULONG64)MappingBaseAddress + AddressOfNames[i]); //获得函数名称 if (_stricmp(FunctionName, v1) == 0) { FunctionOrdinal = AddressOfNameOrdinals[i]; FunctionAddress = (PVOID)((UINT8*)MappingBaseAddress + AddressOfFunctions[FunctionOrdinal]); *SSDTFunctionIndex = *(ULONG32*)((UINT8*)FunctionAddress+Offset_SSDTFunctionIndex); break; } } } }__except(EXCEPTION_EXECUTE_HANDLER) { ; } } ZwUnmapViewOfSection(NtCurrentProcess(), MappingBaseAddress); //解除映射 if (*SSDTFunctionIndex==-1) { return FALSE; } return TRUE; } BOOLEAN MappingPEFileInRing0Space(WCHAR* FileFullPath,PVOID* MappingBaseAddress, PSIZE_T MappingViewSize) { NTSTATUS Status; UNICODE_STRING v1; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; HANDLE FileHandle = NULL; HANDLE SectionHandle = NULL; if (!FileFullPath &&MmIsAddressValid(FileFullPath)) { return FALSE; } if (!MappingBaseAddress&&MmIsAddressValid(MappingBaseAddress)) { return FALSE; } RtlInitUnicodeString(&v1, FileFullPath); //初始化一个OBJECT_ATTRIBUTES结构体ObjectAttributes,它指定对象句柄的属性,供打开句柄的例程使用。 //驱动程序运行进程上下文中,若要运行在系统进程,需要设置OBJ_KERNEL_HANDLE标志到Attributes参数。 //OBJ_KERNEL_HANDLE标志限制,使用此打开的句柄的进程仅能运行在内核模式。否则句柄可以在驱动运行的进程上下文中访问。 InitializeObjectAttributes(&ObjectAttributes, &v1, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL ); //传递ObjectAttributes结构的指针到实际打开句柄的例程 //获得文件句柄 Status = IoCreateFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ObjectAttributes, //文件绝对路径 &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0, CreateFileTypeNone, NULL, IO_NO_PARAMETER_CHECKING ); if (!NT_SUCCESS(Status)) { return FALSE; } ObjectAttributes.ObjectName = NULL; //创建一个Section Object ,即共享内存 Status = ZwCreateSection(&SectionHandle, SECTION_QUERY | SECTION_MAP_READ, &ObjectAttributes, NULL, PAGE_WRITECOPY, SEC_IMAGE, //内存对齐 0x1000 FileHandle ); /*NTSTATUS ZwCreateSection( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize OPTIONAL, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL );*/ ZwClose(FileHandle); if (!NT_SUCCESS(Status)) { return FALSE; } //生成一个可以访问的MappingBaseAddress Status = ZwMapViewOfSection(SectionHandle, NtCurrentProcess(), //映射到当前进程的内存空间中 MappingBaseAddress, 0, 0, 0, MappingViewSize, ViewUnmap, 0, PAGE_WRITECOPY ); ZwClose(SectionHandle); if (!NT_SUCCESS(Status)) { return FALSE; } return TRUE; } VOID HookSSDT(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex,PVOID FakeFunctionAddress, PVOID OldFunctionAddress,ULONG32 OldFunctionParameterCount,UCHAR* OldFunctionCode,ULONG32 PatchCodeLength) { //寻找一个内核不常用的函数(KeBugCheckEx) 进行InlineHook使其跳转到Fake_NtOpenProcess函数 ULONG32 v1 = 0; WPOFF(); // KeBugCheckEx // mov rax,10 Jmp Fake 15 // mov rbx,rax InlineHook(OldFunctionAddress, FakeFunctionAddress, OldFunctionCode, PatchCodeLength); WPON(); //寻找一个内核不常用的函数(KeBugCheckEx) 计算SSDT中的偏移 进行置换 v1 = CalcFunctionOffsetInSSDT(ServiceTableBase,OldFunctionAddress, OldFunctionParameterCount); WPOFF(); ((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)v1; WPON(); __IsHook = TRUE; } VOID UnHookSSDT(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex,ULONG32 OldFunctionOffset,PVOID OldFunctionAddress, UCHAR* OldFunctionCode,ULONG32 PatchCodeLength) { WPOFF(); UnInlineHook(OldFunctionAddress, OldFunctionCode, PatchCodeLength); WPON(); WPOFF(); ((PULONG32)ServiceTableBase)[SSDTFunctionIndex] = (ULONG32)OldFunctionOffset; WPON(); } VOID InlineHook(ULONG64 OldFunctionAddress, ULONG64 FakeFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength) { ULONG64 v1 = 0; UCHAR PatchCode[] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; //InlineHook Jmp 函数地址 v1 = FakeFunctionAddress; memcpy(OldFunctionCode, (PVOID)OldFunctionAddress, PatchCodeLength); //保存原先函数的指令 memcpy(PatchCode + 6, &v1, 8); memset((PVOID)OldFunctionAddress, 0x90, PatchCodeLength); //打补丁 NOP = 0x90 memcpy((PVOID)OldFunctionAddress, PatchCode, 14); } VOID UnInlineHook(PVOID OldFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength) { memcpy((PVOID)OldFunctionAddress, OldFunctionCode, PatchCodeLength); } ULONG32 CalcFunctionOffsetInSSDT(PVOID ServiceTableBase, PVOID FunctionAddress,ULONG32 ParameterCount) { //v1 : INT8 Temp = 0 CHAR v1 = 0; CHAR Bits[4] = {0}; ULONG32 v2 = 0,i; v2 = (ULONG32)((ULONG64)FunctionAddress-(ULONG64)ServiceTableBase); v2 = v2<<4; //0110 0001 a1 ----> 0001 0000 if(ParameterCount>4) { ParameterCount = ParameterCount-4; //NtReadFile 9个参数 } else { ParameterCount = 0; } memcpy(&v1,&v2,1); //处理低四位,填写参数个数 如果一个函数的参数为5 那么dwTemp的低4位就是 0001 如果参数是6 就是0002 因为 6要减4 #define SETBIT(x,y) x|=(1<<y) //将X的第Y位置1 #define CLRBIT(x,y) x&=~(1<<y) //将X的第Y位清0 #define GETBIT(x,y) (x & (1 << y)) //取X的第Y位,返回0或非0 for(i=0;i<4;i++) //一个16进制 4个二进制 0000 { Bits[i]=GETBIT(ParameterCount,i); if(Bits[i]) { SETBIT(v1,i); } else { CLRBIT(v1,i); } } /* ulParamCount i Bits[i] b i b 0101 0 1 0000 0 0001 set 0101 1 0 0001 1 0001 clr 0101 2 1 0001 2 0101 set 0101 3 0 0101 3 0101 clr */ //把数据复制回去 memcpy(&v2,&v1,1); return v2; } NTSTATUS FakeOpenProcess( _Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientID) { //EnumProcessByForce.exe //OpenProcess---->Ntdll(ZwOpenProcess eax,23H syscall)----->NtosKrnl.exe(ZwOpenProcss eax,23H SSDT[23H])----->KeCheckBugEx---->Jmp FakeOpenProcess PEPROCESS EProcess = PsGetCurrentProcess(); //EnumProcessByForce进程上下背景文 if (EProcess != NULL&&MmIsAddressValid(EProcess)) { //通过EProcess 获得进程名称 char *ProcessImageName = PsGetProcessImageFileName(EProcess); //0x2e0 if (strstr(ProcessImageName, "EnumProcess") != 0) { return STATUS_ACCESS_DENIED; //黑名单 } } ((pfnNtOpenProcess)__OldNtOpenProcess)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientID); //白名单 } VOID WPOFF() { _disable(); __writecr0(__readcr0() & (~(0x10000))); } VOID WPON() { __writecr0(__readcr0() ^ 0x10000); _enable(); } VOID DriverUnload(PDRIVER_OBJECT DriverObject) { DbgPrint("ByeByeDriver\r\n"); if (__IsHook) { UnHookSSDT(__ServiceTableBase, __NtOpenProcessIndex, __OldNtOpenProcessOffset, KeBugCheckEx, __OldCode, 15); __IsHook = FALSE; } }
#pragma once #include <ntddk.h> typedef struct _SERVER_SERVICE_DESCRIPTOR_TABLE_ { PVOID Unknow0; PVOID Unknow1; PVOID Unknow2; PVOID Unknow3; }SERVER_SERVICE_DESCRIPTOR_TABLE, *PSERVER_SERVICE_DESCRIPTOR_TABLE; typedef NTSTATUS(*pfnNtOpenProcess)( _Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientID); extern PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader(PVOID BaseAddress); extern char* PsGetProcessImageFileName(PEPROCESS EProcess); BOOLEAN GetSSDTFunctionIndexFromNtdllExportTableByFunctionName( CHAR* FunctionName, ULONG32* SSDTFunctionIndex); BOOLEAN MappingPEFileInRing0Space(WCHAR* FileFullPath, PVOID* MappingBaseAddress, PSIZE_T MappingViewSize); BOOLEAN GetSSDTAddress(ULONG64* SSDTAddress); VOID HookSSDT(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, PVOID FakeFunctionAddress); ULONG32 CalcFunctionOffsetInSSDT(PVOID ServiceTableBase, PVOID FunctionAddress, ULONG32 ParameterCount); VOID HookSSDT(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, PVOID FakeFunctionAddress, PVOID OldFunctionAddress, ULONG32 OldFunctionParameterCount, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength); VOID UnHookSSDT(PVOID ServiceTableBase, ULONG32 SSDTFunctionIndex, ULONG32 OldFunctionOffset, PVOID OldFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength); VOID InlineHook(ULONG64 OldFunctionAddress, ULONG64 FakeFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength); VOID UnInlineHook(PVOID OldFunctionAddress, UCHAR* OldFunctionCode, ULONG32 PatchCodeLength); NTSTATUS FakeOpenProcess( _Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientID); VOID WPOFF(); VOID WPON(); VOID DriverUnload(PDRIVER_OBJECT DriverObject);