|
|
|
|
|
[求助]竹君,你hook的开头5个字节
代码如下: // push ecx //80565bee 51 push ecx //80565bef 53 push ebx //80565bf0 56 push esi //80565bf1 57 push edi #include <ntddk.h> #define dprintf DbgPrint #include <windef.h> #include <ntstatus.h> //字节型数据 unsigned char ULONG XXXX; ULONG CR0VALUE; BYTE OriginalBytes[5]={0}; //保存原始函数前五个字节 BYTE JmpAddress[5]={0xE9,0,0,0,0}; //跳转到HOOK函数的地址 extern POBJECT_TYPE *PsProcessType; NTKERNELAPI NTSTATUS ObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL ); //HOOK函数 NTSTATUS DetourMyObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL); // //hook流程 HookObReferenceObjectByHandle---DetourMyObReferenceObjectByHandle---UnHookObReferenceObjectByHandle void HookObReferenceObjectByHandle() { //赋值前面定义的数组 KIRQL Irql; // DbgPrint("[ObReferenceObjectByHandle] :%08x\n",ObReferenceObjectByHandle); //地址验证 //保存函数前五个字节内容 RtlCopyMemory(OriginalBytes,(BYTE *)XXXX,5); //保存新函数五个字节之后偏移 *(ULONG *)(JmpAddress+1)=(ULONG)DetourMyObReferenceObjectByHandle-((ULONG)XXXX+5); //开始inline hook __asm{//去掉内存保护 cli mov eax,cr0 and eax,not 10000h mov cr0,eax } //提升IRQL中断级 Irql=KeRaiseIrqlToDpcLevel(); //函数开头五个字节写JMP RtlCopyMemory((BYTE *)XXXX,JmpAddress,5); //恢复Irql KeLowerIrql(Irql); //开启内存写保护 __asm{//恢复内存保护 mov eax,cr0 or eax,10000h mov cr0,eax sti } } _declspec (naked) NTSTATUS OriginalObReferenceObjectByHandle(IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL) { _asm { mov edi,edi push ebp mov ebp,esp push ecx push ecx push ebx push esi push edi mov eax,XXXX add eax,5 jmp eax } } char* ProtectName = "NOTEPAD.EXE"; NTSTATUS DetourMyObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL) { NTSTATUS status; //调用原函数 status=OriginalObReferenceObjectByHandle(Handle,DesiredAccess,ObjectType,AccessMode,Object,HandleInformation); if((status==STATUS_SUCCESS)&&(DesiredAccess==1)) { if(ObjectType== *PsProcessType) { if( _stricmp((char *)((ULONG)(*Object)+0x174),"notepad.exe")==0) { ObDereferenceObject(*Object); return STATUS_INVALID_HANDLE; } } } return status; } void UnHookObReferenceObjectByHandle() { //把五个字节再写回到原函数 KIRQL Irql; //关闭写保护 __asm{//去掉内存保护 cli mov eax,cr0 and eax,not 10000h mov cr0,eax } //提升IRQL到Dpc Irql=KeRaiseIrqlToDpcLevel(); RtlCopyMemory((BYTE *)XXXX,OriginalBytes,5); KeLowerIrql(Irql); //开启写保护 __asm{//恢复内存保护 mov eax,cr0 or eax,10000h mov cr0,eax sti } } VOID OnUnload( IN PDRIVER_OBJECT DriverObject ) { UnHookObReferenceObjectByHandle(); dprintf("[UnHookObReferenceObjectByHandle] Unloaded\n"); } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj,PUNICODE_STRING pRegistryString) { KdPrint(("内核函数地址1=%x",ObReferenceObjectByHandle)); XXXX=(ULONG)ObReferenceObjectByHandle + 5; KdPrint(("XXXX=%x",XXXX)); pDriverObj->DriverUnload = OnUnload; HookObReferenceObjectByHandle(); return STATUS_SUCCESS; } |
|
|
|
|
|
|
|
竹君兄弟,你这个还能堆栈平衡吗
4 楼的朋友,我不明白的是:ObReferenceObjectByHandle函数本身是平衡的, 但在函数体里有个push ebp,这样,在函数调用结束时pop,是不是少了一个? 但在人家竹君的程序,并没有蓝屏,说明是平衡的 但是,我不知道平衡的原因 |
|
|
|
|
|
|
|
[讨论]对竹君同学那篇文章的一个疑问
我写的代码如下, 总是蓝屏,不知道错在哪里 谢谢大家 #include <ntddk.h> #include <windef.h> #include <ntstatus.h> ULONG TestFunctionAddr=0; //NtOpenProcess函数的首地址 ULONG TestFunctionAddrNew=0;//NtOpenProcess函数的第六个字节到第十个字节的首地址 UCHAR SoureCode[5]={0x68,0xd8,0xb0,0x4e,0x80}; //保存原始函数第六个到第十个字节 ULONG CR0VALUE; BYTE JmpAddress[5]={0xE9,0,0,0,0}; //跳转到HOOK函数的地址 NTKERNELAPI NTSTATUS NtOpenProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK AccessMask, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId ); //HOOK函数 NTSTATUS MyNtOpenProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK AccessMask, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId ); void HookNtOpenProcess() { //赋值前面定义的数组 KIRQL Irql; KdPrint(("[NtOpenProcess111] :0x%x",NtOpenProcess)); //地址验证 KdPrint(("[TestFunctionAddrNew] :0x%x",TestFunctionAddrNew)); //地址验证 KdPrint(("[MyNtOpenProcess] :0x%x",MyNtOpenProcess)); //地址验证 //保存新函数五个字节之后偏移 *(ULONG *)(JmpAddress+1)=(ULONG)MyNtOpenProcess-((ULONG)TestFunctionAddrNew+5); KdPrint(("[JmpAddress] :0x%x",JmpAddress)); //开始inline hook //关闭内存写保护 _asm { push eax mov eax, cr0 mov CR0VALUE, eax and eax, 0fffeffffh mov cr0, eax pop eax } //提升IRQL中断级 Irql=KeRaiseIrqlToDpcLevel(); //函数开头五个字节写JMP RtlCopyMemory((BYTE *)TestFunctionAddrNew,JmpAddress,5); //恢复Irql KeLowerIrql(Irql); //开启内存写保护 __asm { push eax mov eax, CR0VALUE mov cr0, eax pop eax } } _declspec (naked) NTSTATUS OriginalNtOpenProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK AccessMask, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId ) { _asm { push 0xc4//原始函数NtOpenProcess的前两句就是push 0xc4 push 0x804eb0d8//和push 0x804eb0d8这两句,构造一下这两句。 mov eax,TestFunctionAddrNew add eax,5 jmp eax } } NTSTATUS MyNtOpenProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK AccessMask, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId ) { NTSTATUS rc; ULONG PID; rc = (NTSTATUS)OriginalNtOpenProcess( ProcessHandle, AccessMask, ObjectAttributes, ClientId ); if( (ClientId != NULL) ) { PID = (ULONG)ClientId->UniqueProcess; if( PID == 3652 ) { DbgPrint( "Some want to open pid 3652!\n" ); ProcessHandle = NULL; rc = STATUS_ACCESS_DENIED; } } return rc; } void UnHookNtOpenProcess() { //把五个字节再写回到原函数 KIRQL Irql; //关闭写保护 _asm { push eax mov eax, cr0 mov CR0VALUE, eax and eax, 0fffeffffh mov cr0, eax pop eax } //提升IRQL到Dpc Irql=KeRaiseIrqlToDpcLevel(); RtlCopyMemory((BYTE *)TestFunctionAddrNew,SoureCode,5); KeLowerIrql(Irql); //开启写保护 __asm { push eax mov eax, CR0VALUE mov cr0, eax pop eax } } VOID Unload(IN PDRIVER_OBJECT pDriverObj) { UnHookNtOpenProcess(); KdPrint(("[UnHookNtOpenProcess] Unloaded\n")); } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj,PUNICODE_STRING pRegistryString) { UNICODE_STRING FunctionName; RtlInitUnicodeString(&FunctionName,L"NtOpenProcess"); TestFunctionAddr=(ULONG)MmGetSystemRoutineAddress(&FunctionName);//得到NtOpenProcess的地址赋给TestFunctionAddr TestFunctionAddrNew=TestFunctionAddr+0x5;//然后把TestFunctionAddr加上0x5,就得到了NtOpenProcess函数的 pDriverObj->DriverUnload = Unload;//从开头数第六个字节的地址,赋给然后TestFunctionAddrNew HookNtOpenProcess(); return STATUS_SUCCESS; |
|
[讨论]对竹君同学那篇文章的一个疑问
我仿照竹君兄弟的代码,编了一个inline hook NtOpenProcess的驱动 如果hook开头的5个字节,能够成功,效果还不错 但是,我想hook从第六个到第十个这5个字节 这5个字节是一个压栈,push 0x804eb0d8 我就把这5个字节修改成了jmp xxxxxx 但是没成功,总是蓝屏 不知道问题出在什么地方,麻烦竹君兄弟帮助 |
|
|
|
[讨论]对竹君同学那篇文章的一个疑问
首先感谢竹君的亲自帮助 但是还有两个问题,请竹君解答一下: 1.难道你的那段代码,就能表示ObReferenceObjectByHandle函数? _declspec (naked) NTSTATUS OriginalObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL) { _asm { mov edi,edi push ebp mov ebp,esp mov eax,ObReferenceObjectByHandle add eax,5 jmp eax } } 第二个问题是: 如果我想从中间hook,也就是说不是开头的5个字节, 而是中间某个地方的5个字节,应该怎么办呢? |
|
[讨论]对竹君同学那篇文章的一个疑问
附上竹君同学的源代码 代码如下: 前置知识:汇编 驱动 windbg 函数参数调用 关键词:堆栈平衡 inline hook 详谈内核三步走Inline Hook实现 文/图 wofeiwo (一)Inline hook原理 Inline hook通俗的说就是对函数执行流程进行修改,达到控制函数过滤操作的目的。理论上我们可以在函数任何地方把原来指令替换成我们的跳转指令,也确实有些人在inline 的时候做的很深,来躲避inline 的检测,前提是必须对函数的流程和指令非常熟悉,且这种深层次的inlline 不具有通用性,稳定性也是问题。本文讨论的是具有通用性的两类inline的实现。 Inline hook原理:解析函数开头的几条指令,把他们Copy到数组保存起来,然后用一个调用我们的函数的几条指令来替换,如果要执行原函数,则在我们函数处理完毕,再执行我们保存起来的开头几条指令,然后调回我们取指令之后的地址执行。用下图来解释: 原函数: 开头指令A 指令B Inline 后: JMP MyFunction 指令B MyFunction: 处理函数 JMP ResumeFunction ResumeFunction: 开头指令A JMP 回去 整个Inline hook的过程就大体这样,中间牵扯到对函数的检查,地址的获取就直接调用函数即可。 本文所要讨论的两类Inline hook都是基于上面原理。 说明三点: 1、堆栈平衡是重中之重,参数压栈也需要格外注意 2、R0模式下内存是不允许写的,需要去除写保护,设置CR0寄存器 3、提高中断级别到DPC,禁止线程切换 (二)inline hook应用 Inline hook可分为两类: (1)inline 导出函数,选择ObReferenceObjectByHandle做例子。 (2)inline 未导出函数,选择KiInsertQueueApc做例子。 导出函数前几个字节可以利用windbg自己查看是什么内容,而未导出函数就需要自己解析指令确定需要hook几个字节,其间还有很多问题需要注意。当大家真正的弄懂了我这篇文章,回头再看inline hook就会觉得inline也不过如此。 下面通过2个例子来讲inline hook的使用(这部分知识网上也有很多,但都很零散不系统,本文部分思路及代码的确参考了网上资源,有抄袭之嫌,希望读者谅解。我一直强调“授人以鱼不如授人以渔”,代码并不重要,关键是思想。) 1、inline hook ObReferenceObjectByHandle保护进程 ObReferenceObjectByHandle属于ntoskrnl.exe导出函数,在内核中调用频繁。 NtCreateProcess创建进程需要调用ObReferenceObjectByHandle,NtTerminateProcess需要调用ObReferenceObjectByHandle,基于这我们就可以利用Hook来保护进程同时屏蔽进程的创建。 效果:已经运行的记事本任务管理器无法结束 流程: HookObReferenceObjectByHandle------DetourMyObReferenceObjectByHa ndle----------UnHookObReferenceObjectByHandle 核心代码分析如下: //====================inlineHOOKObReferenceObjectByHandle=========================== //ObReferenceObjectByHandle是ntoskrnl.exe导出函数,采用HOOK前五个字节的方式 //字节型数据 unsigned char ULONG CR0VALUE; BYTE OriginalBytes[5]={0}; //保存原始函数前五个字节 BYTE JmpAddress[5]={0xE9,0,0,0,0}; //跳转到HOOK函数的地址 extern POBJECT_TYPE *PsProcessType; NTKERNELAPI NTSTATUS ObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL); //HOOK函数 NTSTATUS DetourMyObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL); void HookObReferenceObjectByHandle() { //赋值前面定义的数组 KIRQL Irql; KdPrint(("[ObReferenceObjectByHandle] :0x%x",ObReferenceObjectByHandle)); //地址验证 //保存函数前五个字节内容 RtlCopyMemory(OriginalBytes,(BYTE *)ObReferenceObjectByHandle,5); //保存新函数五个字节之后偏移 *(ULONG *)(JmpAddress+1)=(ULONG)DetourMyObReferenceObjectByHandle-((ULONG)ObReferenceObjectByHandle+5); //开始inline hook //关闭内存写保护 _asm { push eax mov eax, cr0 mov CR0VALUE, eax and eax, 0fffeffffh mov cr0, eax pop eax } //提升IRQL中断级 Irql=KeRaiseIrqlToDpcLevel(); //函数开头五个字节写JMP RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,JmpAddress,5); //恢复Irql KeLowerIrql(Irql); //开启内存写保护 __asm { push eax mov eax, CR0VALUE mov cr0, eax pop eax } } _declspec (naked) NTSTATUS OriginalObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL) { _asm { mov edi,edi push ebp mov ebp,esp mov eax,ObReferenceObjectByHandle add eax,5 jmp eax } } NTSTATUS DetourMyObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL) { NTSTATUS status; //调用原函数 status=OriginalObReferenceObjectByHandle(Handle,DesiredAccess,ObjectType,AccessMode,Object,HandleInformation); if((status==STATUS_SUCCESS)&&(DesiredAccess==1)) { if(ObjectType== *PsProcessType) { if( _stricmp((char *)((ULONG)(*Object)+0x174),"notepad.exe")==0) { ObDereferenceObject(*Object); return STATUS_INVALID_HANDLE; } } } return status; } void UnHookObReferenceObjectByHandle() { //把五个字节再写回到原函数 KIRQL Irql; //关闭写保护 _asm { push eax mov eax, cr0 mov CR0VALUE, eax and eax, 0fffeffffh mov cr0, eax pop eax } //提升IRQL到Dpc Irql=KeRaiseIrqlToDpcLevel(); RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,OriginalBytes,5); KeLowerIrql(Irql); //开启写保护 __asm { push eax mov eax, CR0VALUE mov cr0, eax pop eax } } 驱动加载后,结束记事本程序如下: (图 一) 详细分析: 1、ObReferenceObjectByHandle分析 NTSTATUS ObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType OPTIONAL, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL ); 函数原型如上,由句柄获取对象指针,函数返回值: STATUS_SUCCESS 调用成功 STATUS_OBJECT_TYPE_MISMATCH STATUS_ACCESS_DENIED 权限不够 STATUS_INVALID_HANDLE 无效句柄 调用NtTerminateProcess需要调用ObReferenceObjectByHandle,因此我们通过对函数返回值进程修改来达到保护进程。但是NtCreateProcess(最终调用的PspCreateProcess)同样调用这个函数,如果不加区分的话,创建进程同样被禁止了,那么如何区分到底是谁在调用呢。参考WRK,我发现可以通过第二个参数DesiredAccess来判别,创建进程和结束进程第二个参数明显不同,PROCESS_CREATE_PROCESS和PROCESS_TERMINATE,问题就解决了。 PspCreateProcess位于 WRK-v1.2\base\ntos\ps\create.c 调用ObReferenceObjectByHandle代码: Status = ObReferenceObjectByHandle (ParentProcess, PROCESS_CREATE_PROCESS, PsProcessType, PreviousMode, &Parent, NULL); NtTerminateProcess位于 WRK-v1.2\base\ntos\ps\psdelete.c 调用ObReferenceObjectByHandle代码: st = ObReferenceObjectByHandle (ProcessHandle, PROCESS_TERMINATE, PsProcessType, KeGetPreviousModeByThread(&Self->Tcb), &Process, NULL); DesiredAccess参数说明: #define PROCESS_TERMINATE (0x0001) // winnt #define PROCESS_CREATE_THREAD (0x0002) // winnt #define PROCESS_SET_SESSIONID (0x0004) // winnt #define PROCESS_VM_OPERATION (0x0008) // winnt #define PROCESS_VM_READ (0x0010) // winnt #define PROCESS_VM_WRITE (0x0020) // winnt // begin_ntddk begin_wdm begin_ntifs #define PROCESS_DUP_HANDLE (0x0040) // winnt // end_ntddk end_wdm end_ntifs #define PROCESS_CREATE_PROCESS (0x0080) // winnt #define PROCESS_SET_QUOTA (0x0100) // winnt #define PROCESS_SET_INFORMATION (0x0200) // winnt #define PROCESS_QUERY_INFORMATION (0x0400) // winnt #define PROCESS_SET_PORT (0x0800) #define PROCESS_SUSPEND_RESUME (0x0800) // winnt 2、函数调用说明 C语言中我们调用一个函数就直接写函数名就可以,但是实际是进行了下面的操作: 把函数参数压入堆栈,压入函数返回地址,调用函数,为新函数开辟堆栈空间申请局部变量, 恢复堆栈保持堆栈平衡 (_stdcall调用方式)汇编代码就是: Push 参数4 Push 参数3 Push 参数2 Push 参数1 Call 函数 ;call指令同时完成2个操作,一是把返回地址压入堆栈,二跳转到调用函数入口地址 Push ebp Mov ebp,esp Sub esp, XX ;开辟栈帧空间 …… Add esp ,XX Pop ebp Retn ;恢复堆栈平衡 堆栈详细情况: ESP 局部变量 EBP 返回地址 参数1 参数2 参数3 参数4 堆栈是由高地址到低地址。 参数就通过EBP来去,四字节对齐的 参数4----------------------EBP+0x14 参数3----------------------EBP+0x10 参数2----------------------EBP+0xc 参数1--------------------- EBP+0x8 局部变量则通过Ebp-XX来获取 因此inline的时候要时刻考虑堆栈平衡,破坏了堆栈平衡就会导致函数崩溃。 我通常inline hook的思路就是三步走: HOOK函数-----DetourMy处理函数----------UnHook函数 处理函数中对返回结果或者中间数据进行修改处理,然后调用原始函数。由于在我们处理的时候原始函数已经被hook了,所以我自己构造了一个原始函数,但是由于参数在我们hook前已经压人堆栈了,所以这里我们不用重新开辟栈帧,因此声名函数类型为_declspec (naked) 。有人就会问那么你调用处理函数的时候,参数不是重复压栈了,这里请注意,我们是通过JMP方式跳转到我们处理函数入口地址的,而不是Call的形式,所以并没有执行上面所说的函数调用过程,参数仍然是原始函数的。也就是说在真个inline hook过程中我们不能破坏原始栈帧的EBP。 关于函数调用很栈帧的相关联系可能比较难理解,我也在尽肯能的用通俗的话来解释清楚,有什么不理解的地方或者个人见解欢迎大家跟我交流。 2、inline hook KiInsertQueueApc对抗插APC杀进程 KiInsertQueueAPc为内核未导出函数,我下面提供的代码可以作为未导出函数inline的通用模板来使用,大家根据自己需要进行修改,基于inline ObReferenceObjectByHandle已经把原理分析了,这部分我就不详加分析,仍然采用的但不走,Hook函数---DetourMy函数---UnHook函数 直接看核心代码: //===================inline hook KiInsertQueueApc==================== //KiInsertQueueApc为内核未导出函数,可以从导出函数KeInsertQueueApc定位 //修改KiInsertQueueApc开头5字节 //处理函数思路:apc-->kthread---apc_state--eprocess--进程名字 //HookKiInsertQueueApc---DetourMyKiInsertQueueApc---UnHookKiInsertQueueApc ULONG CR0VALUE; ULONG g_KiInsertQueueApc; BYTE JmpAddress[5]={0xE9,0,0,0,0}; //跳转到HOOK函数的地址 BYTE OriginalBytes[5]={0}; //保存原始函数前五个字 VOID FASTCALL DetourMyKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment); VOID WPOFF() { _asm { push eax mov eax, cr0 mov CR0VALUE, eax and eax, 0fffeffffh mov cr0, eax pop eax cli }; } VOID WPON() { __asm { sti push eax mov eax, CR0VALUE mov cr0, eax pop eax }; } //1、获取KiInsertQueueApc地址 ULONG GetFunctionAddr( IN PCWSTR FunctionName) //PCWSTR常量指针,指向16位UNICODE { UNICODE_STRING UniCodeFunctionName; RtlInitUnicodeString( &UniCodeFunctionName, FunctionName ); return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName ); } ULONG GetKiInsertQueueApcAddr() { ULONG sp_code1=0x28,sp_code2=0xe8,sp_code3=0xd88a; //特征码,sp_code3 windbg显示错误,应该为d88a ULONG address=0; PUCHAR addr; PUCHAR p; addr=(PUCHAR)GetFunctionAddr(L"KeInsertQueueApc"); for(p=addr;p<p+PAGE_SIZE;p++) { if((*(p-1)==sp_code1)&&(*p==sp_code2)&&(*(PUSHORT)(p+5)==sp_code3)) { address=*(PULONG)(p+1)+(ULONG)(p+5); break; } } KdPrint(("[KeInsertQueueApc] addr %x\n",(ULONG)addr)); KdPrint(("[KiInsertQueueApc] address %x\n",address)); return address; } VOID HookKiInsertQueueApc() { KIRQL Irql; g_KiInsertQueueApc=GetKiInsertQueueApcAddr(); KdPrint(("[KiInsertQueueApc] KiInsertQueueApc %x\n",g_KiInsertQueueApc)); // 保存原函数的前字节内容 RtlCopyMemory (OriginalBytes, (BYTE*)g_KiInsertQueueApc, 5); //新函数对原函数的偏移地址 *( (ULONG*)(JmpAddress + 1) ) = (ULONG)DetourMyKiInsertQueueApc - (ULONG)g_KiInsertQueueApc - 5; // 禁止系统写保护,提升IRQL到DPC WPOFF(); Irql = KeRaiseIrqlToDpcLevel(); //inline hook函数 RtlCopyMemory ( (BYTE*)g_KiInsertQueueApc, JmpAddress, 5 ); // 恢复写保护,降低IRQL KeLowerIrql(Irql); WPON(); } //原函数 _declspec (naked) VOID FASTCALL OriginalKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment) { _asm { //前五个字节 mov edi,edi push ebp mov ebp,esp mov eax,g_KiInsertQueueApc add eax,5 jmp eax } } //处理函数 //apc--kthread--apc_state--eprocess VOID FASTCALL DetourMyKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment) { ULONG thread; ULONG process; if(MmIsAddressValid((PULONG)((ULONG)Apc+0x008))) //地址验证 KAPC结构+008--->kthread thread=*((PULONG)((ULONG)Apc+0x008)); else return ; if(MmIsAddressValid((PULONG)((ULONG)thread+0x044))) //kthread+30-->KAPC_STATE+10-->eprocess process=*((PULONG)((ULONG)thread+0x044)); else return ; if(MmIsAddressValid((PULONG)((ULONG)process+0x174))) //eprocess+174---->进程名字 { if((_stricmp((char *)((ULONG)process+0x174),"notepad.exe")==0)&&(Increment==2)) { return ; } else OriginalKiInsertQueueApc(Apc,Increment); } else return; } //卸载函数 VOID UnHookKiInsertQueueApc() { KIRQL Irql; WPOFF(); Irql = KeRaiseIrqlToDpcLevel(); //inline hook函数 RtlCopyMemory ( (BYTE*)g_KiInsertQueueApc, OriginalBytes, 5); // 恢复写保护,降低IRQL KeLowerIrql(Irql); WPON(); } 考虑到大家水平不一,对一些问题我详细如下: 1、特征码的寻找 利用windbg的kernel debug来查找: uf KeInsertQueueApc nt!KeInsertQueueApc+0x3b: 804e6d0a 8b450c mov eax,dword ptr [ebp+0Ch] 804e6d0d 8b5514 mov edx,dword ptr [ebp+14h] 804e6d10 894724 mov dword ptr [edi+24h],eax 804e6d13 8b4510 mov eax,dword ptr [ebp+10h] 804e6d16 8bcf mov ecx,edi 804e6d18 894728 mov dword ptr [edi+28h],eax 804e6d1b e8523fffff call nt!KiInsertQueueApc (804dac72) 804e6d20 8ad8 (错误) mov bl,al 特征码就是sp_code1=0x28 sp_code2=0xe8 sp_code3=0xd88a(windbg显示有误,应该是d88a ) 这种方法就是通过已导出函数定位未导出函数通常使用的方法,具有通用性。详细见代码。 2、取EPRocess的过程 Apc-----kthread-----apc_state—eprocess dt _KAPC 偏移0x008指向KTHREAD dt _KTHREAD 偏移0x034指向KAPC_STATE dt _KAPC_STATE 偏移0x10指向EPROCESS dt _EPROCESS 偏移0x174指向进程名 (三) 总结 很多人觉得inline hook比较难,处理起来很麻烦。但是我相信看完我这篇文章,你一定不会这么认为了,inline hook其实只要细心,注意细节跟别的hook没什么两样。本人采用的三步走inline hook做到了把inline简单化,同时有保证了堆栈的平衡。 由于代码采用的硬编码,编译环境是sp3+VMware,请根据自己操作系统自行修改。欢迎读者跟我交流。 |
|
|
操作理由
RANk
{{ user_info.golds == '' ? 0 : user_info.golds }}
雪币
{{ experience }}
课程经验
{{ score }}
学习收益
{{study_duration_fmt}}
学习时长
基本信息
荣誉称号:
{{ honorary_title }}
能力排名:
No.{{ rank_num }}
等 级:
LV{{ rank_lv-100 }}
活跃值:
在线值:
浏览人数:{{ visits }}
最近活跃:{{ last_active_time }}
注册时间:{{ user_info.create_date_jsonfmt }}
勋章
兑换勋章
证书
证书查询 >
能力值