标签:
关于Sysenter、Kifastcallentry、中断之类的内核入口hook技术早就烂大街了,可是对hook的检测与恢复代码却是寥寥无几,一切抛开代码将原理的行为都是耍流氓。
下面以Sysenter hook技术为例子,重点分析下这类钩子的检测与恢复技术。
Windows2000以前用int 2e作为中断指令进入内核发起系统调用。从Windows2000以后,准确的说是Pentium II处理器开始,为了避免int 2e指令对模式切换的巨大开销,Intel引入了一对新的指令,sysenter/sysexit,实现快速的模式切换,所以也叫快速系统调用。 关于两者之间的具体异同请参考潘爱民老师《Windows内核原理与实现》第8章Windows系统服务,再次不是分析重点,不在累赘。
sysenter使用三个MSR(Model Specific Register)寄存器来指定跳转目标地址和栈位置。操作系统在内核模式下通过rdmsr/wrmsr特权指令来设置这三个寄存器,当然必须在系统初始化时(第一次系统调用发生以前)完成。
MSR寄存器 MSR地址 含义
IA32_SYSENTRY_CS 174h 低16位值制订了特权级0的代码段和栈段的段选择符
IA32_SYSENTRY_ESP 175h 内核栈指针的32位偏移
IA32_SYSENTRY_EIP 176h 目标例程的32位偏移(Kifastcallentry地址)
根据这三个MSR寄存器的属性我们看一段代码
_asm { mov ecx,0x176 rdmsr mov KifastcalllAddress,eax }
获取Kifastcallentry地址其实只需要三行汇编就可以搞定,但是有很多人说看不懂。
RDMSR将64位由ECX寄存器指定的MSR(model specific register,模式指定寄存器)的内容读出至寄存器EDX:EAX中(在支持intel64架构的处理器中RCX的高32位忽略)。MSR的高32位内容存放在EDX寄存器中,MSR的低32位内容存放在EAX寄存器(在支持intel64架构的处理器中RDX和RAX的高32位忽略)。
说这么多其实就是把MSR寄存器地址放进ECX,然后通过RDMSR特权指令就能把相应MSR寄存器的值读进EAX中。而IA32_SYSENTRY_EIP寄存器中保存的就是Kifastcallentry地址。
下面看一个替换IA32_SYSENTRY_EIP值进行sysentry hook小Demo。
#include "HookSysenter.h" ULONG OriginalAddress = 0; ULONG i = 0; __declspec(naked)FakeAddress() { _asm { mov i,eax } __asm { pushad push fs push 0x30 pop fs } if (i==0x101) { DbgPrint("Terminate\r\n"); } _asm { pop fs popad jmp [OriginalAddress] } } NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath) { DriverObject->DriverUnload = UnloadDriver; DbgPrint("Hello\r\n"); Hook(); return STATUS_SUCCESS; } VOID UnloadDriver(PDRIVER_OBJECT DriverObject) { UnHook(); } VOID Hook() { _asm { mov ecx,0x176 rdmsr mov OriginalAddress,eax mov eax, FakeAddress wrmsr } } VOID UnHook() { KIRQL oldIrql; oldIrql=KeRaiseIrqlToDpcLevel(); _asm { mov ecx,0x176 mov eax,OriginalAddress wrmsr } KeLowerIrql(oldIrql); }
只是替换Kifastcallentry地址,然后简单判断了下是否发起对NTterminateProcess的系统调用,很简单。
那么问题来了,我们如何通过内核文件去寻找原始的Kifastcallentry呢?
#include "CheckKiFastCall.h" #include "Asm.H" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { CheckKiFastCallHook(TRUE); DriverObject->DriverUnload = UnloadDriver; return STATUS_SUCCESS; } VOID UnloadDriver(PDRIVER_OBJECT DriverObject) { } int CheckKiFastCallHook(BOOLEAN UnHook) { MODULE_INFO KernelModule; PULONG KernelBaseAddress = NULL; PIMAGE_NT_HEADERS Ntheader = NULL; PUCHAR EntryPointer = NULL; //OEP int Strlen = 0; PRAW_IDT rawIDT = NULL; ULONG IDTBaseAddr = 0; ULONG ImageBase = 0; int Diff = 0; PULONG Temp = NULL; ULONG Address = 0; ULONG KiFmAddr = 0; KIRQL Irq; if (GetKernelBase(&KernelModule)==0) { DbgPrint("Get ntkrnlpa.exe Error\r\n"); return -1; } KernelBaseAddress = (PVOID)MapViewOfImage(KernelModule.FullPath); if(!KernelBaseAddress) { return -1; } Ntheader = (PIMAGE_NT_HEADERS)((ULONG)KernelBaseAddress+((PIMAGE_DOS_HEADER)(KernelBaseAddress))->e_lfanew); EntryPointer = (PUCHAR)((ULONG)KernelBaseAddress+Ntheader->OptionalHeader.AddressOfEntryPoint); //获得代码的入口OEP DbgPrint("OEP:%x\r\n",EntryPointer); while (1) { PUCHAR Code = 0; Strlen = SizeOfCode((VOID*)EntryPointer,&Code); if (Strlen<=0) //没有获得指令 { break; } if (*Code==0xc2) { break; } if (*Code==0xc3) { break; } //以上两条Opcode都是return 可以使用查看Demo /* kd> u kisystemstartup l 100 806909b2 8b7df0 mov edi,dword ptr [ebp-10h] 806909b5 befc0a6980 mov esi,offset nt!IDT (80690afc) 806909ba b900080000 mov ecx,800h 806909bf c1e902 shr ecx,2 806909c2 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] */ if (Strlen==5) { rawIDT = (PRAW_IDT)EntryPointer; DbgPrint("%x %x %x\r\n",rawIDT->d_800h,rawIDT->Opcode1,rawIDT->Opcode2); if((rawIDT->d_800h==0x800)&&(rawIDT->Opcode1==0xbe)&&(rawIDT->Opcode2==0xb9)) { IDTBaseAddr = rawIDT->OrigBase; //该值实际上也是 一个 相对偏移 //DbgPrint("%x\r\n",IDTBaseAddr); break; } } EntryPointer+=Strlen; } if (IDTBaseAddr==0) { ZwUnmapViewOfSection((HANDLE)-1,(PVOID)KernelBaseAddress); return -1; } ImageBase = GetImageBase(KernelBaseAddress); //获得PE文件中的预加载基地址 Diff = (ULONG)KernelBaseAddress - (ULONG)ImageBase; //获得差值 IDTBaseAddr += Diff; //获得真正的IDT表 /* kd> dq /c 1 80690afc 80690afc 80538e00`0008f19c 80690b04 80538e00`0008f314 80690b0c 80538e00`0008f3fc 80690b14 8053ee00`0008f6e4 80690b1c 8053ee00`0008f864 80690b24 80538e00`0008f9c0 80690b2c 80538e00`0008fb34 80690b34 80548e00`0008019c 80690b3c 80548e00`000804ce 80690b44 80548e00`000805c0 80690b4c 80548e00`000806e0 80690b54 80548e00`00080820 80690b5c 80548e00`00080a7c 80690b64 80548e00`00080d60 80690b6c 80548e00`00081450 80690b74 80548e00`00081780 */ Temp = (PULONG)IDTBaseAddr; Temp+=2; //80538e00`0008f314 = 取第一个成员的高16 和 第二成员的低16位 Address = *(Temp)+Diff; DbgPrint("%x\r\n",Address); //我们这里得到的值就是那个地址了 Address = GetKiFastCallEntryOrigEntry((PUCHAR)Address); //此时的Address 也是一个RVA if (Address==0) { ZwUnmapViewOfSection((HANDLE)-1,(PVOID)KernelBaseAddress); return -1; } //Address 地址中存储的就是KiFastCallEntry 的OpCode 指令 //获得现在的KiFastCallEntry _asm { mov ecx,0x176 rdmsr mov KiFmAddr,eax } //进行比较 if((Address+(ULONG)KernelModule.Base-ImageBase)!=KiFmAddr)//转换到内存值再比较 { DbgPrint("KiFastCallEntry Hooked!\n"); KdPrint(("KiFastCallEntry Current Addr :%08X Orig Address:%08X\n",KiFmAddr,Address+(ULONG)KernelModule.Base-ImageBase)); if(UnHook) { Irq=WPOFF(); HookKiFastCallEntry(Address+(ULONG)KernelModule.Base-ImageBase); WPON(Irq); } } ZwUnmapViewOfSection((HANDLE)-1,(PVOID)KernelBaseAddress); return 1; } ULONG GetKernelBase(PMODULE_INFO OutInfo) { NTSTATUS Status = STATUS_UNSUCCESSFUL; ULONG Base = 0; ULONG BufferSize = 0x3000; PVOID Buffer = NULL; char NameBuffer[256] = {0}; __try { Buffer= ExAllocatePool(PagedPool,BufferSize); if(Buffer==0) { return 0; } NtQuerySystemInformation(SystemModuleInformation,Buffer,BufferSize,&Base); Base = 0; if(*(ULONG*)Buffer>0) { PSYSTEM_MODULE_INFORMATION_ENTRY pInfo= (PSYSTEM_MODULE_INFORMATION_ENTRY)((char*)Buffer+sizeof(ULONG)); Base = (ULONG)pInfo[0].Base; if(OutInfo) { OutInfo->Base=pInfo[0].Base; OutInfo->ImageSize=pInfo[0].Size;//ImageSize; DbgPrint("%x\r\n",OutInfo->Base); DbgPrint("%d\r\n",OutInfo->ImageSize); DbgPrint("%s\r\n",pInfo[0].ImageName); ConvertFileName(pInfo[0].ImageName,NameBuffer); //查看注册表 Regedit ByteToWChar(NameBuffer,OutInfo->FullPath,256*2); } } if(Buffer!=NULL) { ExFreePool(Buffer); } return Base; } __except(1) { DbgPrint("Can‘t Get KernelBase!\r\n"); return 0; } } VOID ConvertFileName(CHAR *ImageName, CHAR* FileName) { int Pos = strlen(ImageName)-1; for(;Pos>0;Pos--) { if(ImageName[Pos]==‘\\‘) { Pos++; break; } } if (ImageName[0]==‘\\‘&&(_strnicmp(ImageName, "\\windows\\system32\\", 18)==0)) { strcpy(FileName, "\\SystemRoot\\System32\\"); strcat(FileName,ImageName + Pos); } } PULONG MapViewOfImage(PWCHAR ImagePath) { HANDLE hFile = NULL; HANDLE hSection = NULL; OBJECT_ATTRIBUTES oa = {0}; UNICODE_STRING FileName; IO_STATUS_BLOCK iosb; NTSTATUS Status; PULONG BaseAddress = NULL; ULONG Size = 0; RtlInitUnicodeString(&FileName,ImagePath); InitializeObjectAttributes(&oa,&FileName,OBJ_CASE_INSENSITIVE |OBJ_KERNEL_HANDLE,NULL,NULL); /* Status = NtCreateFile(&hFile, GENERIC_READ,&oa,&iosb,0,FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE|FILE_SHARE_READ, FILE_OPEN_IF,FILE_WRITE_THROUGH,0,0);*/ Status = ZwCreateFile(&hFile,GENERIC_READ,&oa,&iosb,0,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_OPEN,FILE_SYNCHRONOUS_IO_NONALERT, 0,0); if(!NT_SUCCESS(Status)) { return NULL; } if(hFile==NULL) { DbgPrint("ZwCreateFile() Failed!\r\n"); return NULL; } oa.ObjectName = 0; Status = ZwCreateSection(&hSection,SECTION_MAP_READ,&oa,0,PAGE_READONLY,SEC_IMAGE,hFile); if(!NT_SUCCESS(Status)) { DbgPrint("ZwCreateSection() Failed!\r\n"); ZwClose(hFile); return NULL; } Status = ZwMapViewOfSection(hSection, ZwCurrentProcess(),&BaseAddress,0,1000,NULL,&Size,1,MEM_TOP_DOWN,PAGE_READONLY); if(!NT_SUCCESS(Status)) { DbgPrint("ZwMapViewOfSection() Failed!\r\n"); ZwClose(hSection); ZwClose(hFile); } ZwClose(hSection); ZwClose(hFile); DbgPrint("BaseAddress:%x\r\n",BaseAddress); return BaseAddress; } ULONG GetImageBase(PULONG BaseAddr) { PIMAGE_DOS_HEADER DosHeader = NULL; PIMAGE_NT_HEADERS NtHeader = NULL; DosHeader = (PIMAGE_DOS_HEADER)BaseAddr; if (!MmIsAddressValid(DosHeader)) { return 0; } if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) { return 0; } NtHeader = (PIMAGE_NT_HEADERS)((ULONG)DosHeader + DosHeader->e_lfanew); if (!MmIsAddressValid(NtHeader)) { return 0; } if (NtHeader->Signature!=IMAGE_NT_SIGNATURE) { return 0; } return NtHeader->OptionalHeader.ImageBase; } ULONG GetKiFastCallEntryOrigEntry(UCHAR* Address) { int i = 0; UCHAR* Temp = Address; for(i=0;i<1000;i++) { if(*Temp==0x81) //查看Windbg { return *(ULONG*)(Temp+2); } Temp++; } return 0; } BOOLEAN ByteToWChar(char* szSour,wchar_t* szDest,ULONG Size) { if (mbstowcs(szDest,szSour,Size)>0) { return TRUE; } return FALSE; } ULONG HookKiFastCallEntry(ULONG Addr) { ULONG OldAddr; _asm { mov ecx,0x176 rdmsr mov OldAddr,eax mov eax,Addr wrmsr } return OldAddr; } KIRQL WPOFF() { KIRQL OldIrql; KeRaiseIrql(2, &OldIrql); _asm { push eax mov eax, cr0 and eax, 0xFFFEFFFF mov cr0, eax pop eax cli } return OldIrql; } VOID WPON(KIRQL Irq) { _asm { push eax mov eax, cr0 or eax, 0x10000 mov cr0, eax pop eax sti } KeLowerIrql(Irq); }
标签:
原文地址:http://www.cnblogs.com/zibility/p/5405397.html