今天在调试自己编写的inline hook时,蓝屏了,然后用windbg调试dump文件,下面是windbg得到的信息:

PEB is paged out (Peb.Ldr = 7ffd500c).  Type ".hh dbgerr001" for details
PEB is paged out (Peb.Ldr = 7ffd500c).  Type ".hh dbgerr001" for details

我已经看了peb 和 _PEB_LDE_DATA结构,但是还是不明白上面的信息是什么意思.高手帮我看看,改怎么解决。

我是inline hook ObReferenceObjectByHandle(),下面是我的代码:

#include <ntddk.h>
#include <string.h>

extern POBJECT_TYPE *PsProcessType;

void close_write_protected()
{
  //cancel write protected
  __asm
  {
    CLI           
    MOV eax, CR0     
    AND eax, NOT 10000H 
    MOV CR0, eax
  }
}

void open_write_protected()
{
  __asm
  {
    MOV eax, CR0
    OR eax, 10000H
    MOV CR0, eax
    STI
  }  
}

//this routine unload current driver
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
  KIRQL irql;

  unsigned char routine_head[5] = {0x8b,0xff,0x55,0x8b,0xec};

  irql = KeRaiseIrqlToDpcLevel();
  close_write_protected();
  
  //unload inline hook
  RtlCopyMemory(ObReferenceObjectByHandle,routine_head,5);
  
  open_write_protected();
  KeLowerIrql(irql);

  DbgPrint("inline hook success unload.");

  return;
}

int call_failed(
        IN HANDLE Handle,
        IN ACCESS_MASK DesiredAccess,
        IN POBJECT_TYPE ObjectType,
        IN KPROCESSOR_MODE AccessMode,
        OUT PVOID *Object,
        OUT POBJECT_HANDLE_INFORMATION HandleInfo
        )
{  
  KIRQL irql;
  
  //mov edi,edi
  //push ebp
  //mov ebp,esp
  unsigned char routine_head[5] = {0x8b,0xff,0x55,0x8b,0xec};
  
  //jmp address
  unsigned char jmp_code[5] = {0xe9,0x00,0x00,0x00,0x00};

  if(ObjectType == *PsProcessType)  //this object is a process
  {
    
    irql = KeRaiseIrqlToDpcLevel();
    close_write_protected();

    //unload inline hook
    RtlCopyMemory(ObReferenceObjectByHandle,routine_head,5);
    
    ObReferenceObjectByHandle(
        Handle,        
        DesiredAccess,
        ObjectType,
        AccessMode,
        Object,
        HandleInfo
      );
    
    open_write_protected();
    KeLowerIrql(irql);

    if(_stricmp((const char *)Object+0x174,"notepad.exe") != 0)  //this process is protected
    {
      return 1;
    }
    else
    {
      *Object = (PVOID)-1;
      return 0;
    }    
  }
  return 0;
}

//jmp this routine
__declspec(naked) my_routine()
{
  __asm
  {
    mov edi,edi
    push ebp
    mov ebp,esp

    //parameter enter the stack
    push [ebp+0x1c]
    push [ebp+0x18]
    push [ebp+0x14]
    push [ebp+0x10]
    push [ebp+0xc]
    push [ebp+8]

    call call_failed
  }
}

//driver program entry routine
//rewrite ObReferenceObjectByHandle() start 5 byte for jmp address to my_routine
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
  int jmp_offset;  //a jmp operation from current to my_routine
  KIRQL irql;
  unsigned char jmp_code[5] = {0xe9,0x00,0x00,0x00,0x00};

  DriverObject -> DriverUnload = DriverUnload;  //set this driver unload routinue
  
  irql = KeRaiseIrqlToDpcLevel();
  close_write_protected();
  
  //inline hook ObReferenceObjectByHandle()
  jmp_offset = (char *)my_routine - (char *)ObReferenceObjectByHandle - 5;
  RtlCopyMemory(jmp_code+1,&jmp_offset,4);
  RtlCopyMemory(ObReferenceObjectByHandle,jmp_code,5);

  open_write_protected();
  KeLowerIrql(irql);
  
  DbgPrint("install inline hook success.");

  return STATUS_SUCCESS;
}

  • 标 题:答复
  • 作 者:轩辕小聪
  • 时 间:2009-07-16 02:53

楼主还是没有看明白上一帖大家的提醒,第一次inline hook就在Ring0下玩……

代码错误至少包括:
1. call_failed中没有判断ObReferenceObjectByHandle的调用是否成功,如果调用失败,Object就不是有效的指针,下面对其的访问就可能导致错误
2. 没有判断ObjectType,ObReferenceObjectByHandle可以获取各种类型的Object,如果不是进程Object的话,后面对Object的访问同样可能出错
3. my_routine函数的内容不全,从这里看出楼主没有必要的Win32汇编基础,作为一个naked的函数,所有汇编代码都要你自己写,包括开始的push ebp和mov ebp, esp,更包括call call_failed之后怎么根据情况处理参数最后jmp到原始函数,sysnap原代码里也是有的,显然楼主又没有领会他的意思而把这部分代码随意抹掉了。
4. call_failed调用ObReferenceObjectByHandle后都没有ObDereferenceObject……

首先sysnap早年的这份代码本身就不是很完善,再加上楼主借鉴的时候不理解其意义而随意更改了和抹去了一些必要的内容,这样的代码是必蓝无疑,而且以楼主这样写代码的状态,在Ring0级恐怕蓝N多次也不一定能把错误全纠正过来。

楼主如果真要学好inline hook,还是应该老老实实从Ring3开始,当你对inline hook足够熟悉,怎么摆弄它都可以的时候,你再考虑拓展到Ring0,这样效率才会更高。

  • 标 题:答复
  • 作 者:emc
  • 时 间:2009-07-16 08:44

to 轩辕小聪:

昨天在QQ群,flowercode帮我解答了很久,最后他总结:

1. 你覆盖了ObRef前五个字节实现Hook。(正确)
2. my_routine()执行了原来的前五个字节。(正确)
3. my_routine()调用了call_failed()。(没问题)
4. call_failed()里Unhook了ObRef。(于是又变成了原来的前五个字节)
5. 你又调用了原版的ObRef。(蓝屏)

我今天早上修改了下,准备晚上调试看看,因为我白天还要在公司写代码。

很谢谢你2个帖子都打了这么多汉字。

第1条,我确实没做判断,不过貌似不应该失败呀~ 不过我这样写确实不严谨,不过我现在写第一个inline hook,只要可以实现功能就可以了,其它我没想。

第2条,我已经加上了处理其它对象类型的代码了,这个确实是很大的问题。

第3条,push ebp mov ebp,esp 我写了,不过my_routine()我没有接收call_failed()的返回值,因为我打算在call_failed()中把所有操作做完。

第4条,晕,原来还有一个销毁对象的函数要调用呀~ 之前看sysnap的代码也没看到,自己就不知道了,好的,我会添加进去的。我会慢慢把这个代码写好的。

  • 标 题:答复
  • 作 者:轩辕小聪
  • 时 间:2009-07-16 16:18

引用:
最初由 loqich发布 查看帖子
这几点不对的 对调用频率高的函数 在多核cpu下不能使用覆盖的方式
这里先不考虑多核CPU下可能出错的问题,FC说的是函数流程问题,这本来应该是楼主自己必须搞清楚的。在inline hook的函数里,整个流程自己一定要有清晰的概念(你的函数执行到哪一步了,执行到这一步的时候,原函数头部是处于什么样的状态,被Hook了,还是暂时解除Hook了,你什么时候要call进或jmp进原函数里面……),否则函数流程没有理清楚,就极容易出现你难以预料的错误。
引用:
第1条,我确实没做判断,不过貌似不应该失败呀~ 不过我这样写确实不严谨,不过我现在写第一个inline hook,只要可以实现功能就可以了,其它我没想。

第3条,push ebp mov ebp,esp 我写了,不过my_routine()我没有接收call_failed()的返回值,因为我打算在call_failed()中把所有操作做完。
1. 为什么不可能失败?你调用前没有对参数作任何判断,你就不能说调用一定成功。另外,你要的的确只是要实现功能就可以了,但是在Ring0,只要一点错误就很可能蓝屏(尽管有时这个错误跟你的inline hook功能实现无关),因此你在Ring0玩,这样情况可能会遇到N次,这样的干扰不利于你弄清楚inline hook本身。这也是我们几次劝告希望你先从Ring3开始的原因。
2. 你打算在call_failed()中完成,但是实际上你没有完成,你还是得仔细看看sysnap的原来的代码的流程,call_failed()只是确定是否是有保护的进程,当然你在call_failed()中顺便再根据是否要保护来修改Object那是可以的,但是最后你回到my_routine之后,还是要jmp到原ObRef五个字节之后的代码继续执行,这是sysnap有而你没有的。
3. sysnap早年的这份代码,本身就有不完善的地方,如反复hook和unhook等等。

  • 标 题:上源码……
  • 作 者:轩辕小聪
  • 时 间:2009-07-16 18:46

我看光讲楼主一下子是不会明白的,特别是inline hook不要老是重复HOOK和UNHOOK的话,应该怎么做。
所以干脆现写了一个同样功能的驱动,同样inline hook ObReferenceObjectByHandle,同样是改前面五字节,同样是使用改CR0的方法去掉写保护,同样是阻止对notepad.exe进程EPROCESS的访问。
但是替代函数流程不完全一样,当判断访问的是notepad.exe进程时,替代函数返回STATUS_ACCESS_DENIED。
替代函数进入原始函数不需要使用重复多次的HOOK和UNHOOK。


源码、sys、pdb都在压缩包里了,用Kmdkit写的,楼主如果看不懂源代码也没关系,或者IDA下分析一下驱动文件,加载完后用windbg的Local Kernel Debug看一看ObReferenceObjectByHandle。
效果:
加载驱动后,双击记事本图标,弹出如下错误框:

启动其他进程则正常。

说明:这个驱动目的是让楼主看看到底inline hook是怎么做的。EPROCESS中ImageFileName偏移同样直接采用XP下的0x174,因此不能移植到其他系统。

上传的附件 HookObRef.rar

  • 标 题:答复
  • 作 者:轩辕小聪
  • 时 间:2009-07-16 20:39

引用:
最初由 loqich发布 查看帖子
..一直不明白 为什么大家都不用 PsGetProcessImageFileName  这个函数呢。要硬编码偏移呢?
我也想用,但是我不清楚是不是2000也有这个函数,而且KmdKit的ntoskrnl.lib没有这个函数,要调用还要动态获取地址,所以索性硬编码了。
查了一下DDK里w2k和wxp的ntoskrnl.lib里都是有这个函数的,看来KmdKit里的太老了。

update:
把DDK里的ntoskrnl.lib替换掉KmdKit中老的版本,自己声明PsGetProcessImageFileName并调用,编译出来的版本见附件,功能一样。
上传的附件 HookObRef2.rar

  • 标 题:答复
  • 作 者:emc
  • 时 间:2009-07-17 08:53

我摸索了一下sysnap那份代码,流程大概如下。如果不对,还请各位指出来:

1 代理函数以naked形式编写,并且函数体要用asm来实现,形式参数与ObRef相同。

2 代理函数体中的开辟新栈帧并且调用过滤函数:
   push ebp
   mov ebp,esp

3 然后使用push指令把实参入栈:
   push [...]
   push [...]

4 现在就可以直接call 过滤函数了

5 过滤函数返回int,并且代理函数根据这个int判断,是否正确调用原始ObRef,过滤函数的形参与ObRef相同。

6 首先判断用户所请求的Object是不是一个Process。

7 关闭写保护,改写ObRef前5字节为jmp address。

8 调用ObRef以获得用户想要的Object,然后判断这个Object中的ImageFileName是不是要保护的进程。

9 过滤函数返回1或0来告诉调用者是否正却调用ObRef

  • 标 题:答复
  • 作 者:轩辕小聪
  • 时 间:2009-07-17 16:00

引用:
最初由 emc发布 查看帖子
我摸索了一下sysnap那份代码,流程大概如下。如果不对,还请各位指出来:

1 代理函数以naked形式编写,并且函数体要用asm来实现,形式参数与ObRef相同。

2 代理函数体中的开辟新栈帧并且调用过滤函数:
   push ebp
   mov ebp,esp

3 然后使用push指令把...
sysnap的代理函数:
{
    实现原函数的前面五个字节代码,也相当于开辟新栈帧;
    将所有参数重新入栈并调用过滤函数:
    过滤函数:
    {
        若ObjectType不为PsProcessType,直接返回0;
        ObjectType为PsProcessType:
        {
            将ObRef的前五字节还原;
            调用ObRef;
            判断EPROCESS的ImageFileName是否是notepad.exe:
            是:
            {
                重新将ObRef的前五字节改为jmp address;
                return 1;
            }
            否:
            {
                重新将ObRef的前五字节改为jmp address;
                return 0;
            }
        }
    }
    判断过滤函数返回值:
    为0:
    {
        直接跳到ObRef五个字节后的代码执行;
    }
    为1:
    {
        把Handle参数改为-1,即无效句柄,以使调用失败;
        直接跳到ObRef五个字节后的代码执行;
    }
}

其中红字部分,是一个替代函数要完整完成原函数功能所要做到的。蓝字部分则是一个UNHOOK->调用原函数->重新HOOK的过程。

  • 标 题:答复
  • 作 者:emc
  • 时 间:2009-07-18 12:27

to 轩辕小聪:

谢谢你一直的关注与帮助,你分析的流程和我之前理解的很接近,而且我又对照了你的流程分析和sysnap的原始代码重新修改了我的代码,但是又一次蓝屏,大概看了下dump的分析,貌似和irql有关系,你帮我看下,我现在去google上熟悉下irql的知识。

to achillis:

教主兄,帮帮我啊。


最近的inline hook ObReferObjectByHandle()代码:

#include <ntddk.h>
#include <string.h>

extern POBJECT_TYPE *PsProcessType;

//mov edi,edi
//push ebp
//mov ebp,esp
unsigned char routine_head[5] = {0x8b,0xff,0x55,0x8b,0xec};  

//jmp address
unsigned char jmp_code[5] = {0xe9,0x00,0x00,0x00,0x00};

void close_write_protected()
{
  //cancel write protected
  __asm
  {
    CLI           
    MOV eax, CR0     
    AND eax, NOT 10000H 
    MOV CR0, eax
  }
}

void open_write_protected()
{
  __asm
  {
    MOV eax, CR0
    OR eax, 10000H
    MOV CR0, eax
    STI
  }  
}

//this routine unload current driver
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
  KIRQL irql;

  irql = KeRaiseIrqlToDpcLevel();
  close_write_protected();
  
  //unload inline hook
  RtlCopyMemory(ObReferenceObjectByHandle,routine_head,5);
  
  open_write_protected();
  KeLowerIrql(irql);

  DbgPrint("inline hook success unload.");

  return;
}

//this routine return 0 success run ObReferenceObjectByHandle()
//this routine return 1 failed run ObReferenceObjectByHandle()
int call_failed(
        IN HANDLE Handle,
        IN ACCESS_MASK DesiredAccess,
        IN POBJECT_TYPE ObjectType,
        IN KPROCESSOR_MODE AccessMode,
        OUT PVOID *Object,
        OUT POBJECT_HANDLE_INFORMATION HandleInfo
        )
{  
  PEPROCESS process;
  KIRQL irql;    

  //this object is a process
  if(ObjectType == *PsProcessType)
  {    
    irql = KeRaiseIrqlToDpcLevel();
    close_write_protected();

    //Unload inline hook in ObReferenceObjectByHandle()
    RtlCopyMemory(ObReferenceObjectByHandle,routine_head,5);

    //run the kernel routine
    ObReferenceObjectByHandle(Handle,DesiredAccess,ObjectType,AccessMode,&process,NULL);
    
    //install inline hook to ObReferenceObjectByHandle()
    RtlCopyMemory(ObReferenceObjectByHandle,jmp_code,5);

    //this request point to notepad.exe    
    if(_stricmp((char *)((char *)process+0x174),"notepad.exe") == 0)
      return 1;
    else
      return 0;

    open_write_protected();
    KeLowerIrql(irql);    
  }
  else  //this object not a process
  {
    return 0;
  }  
}

//jmp this routine
__declspec(naked) my_routine(
              IN HANDLE  Handle,
              IN ACCESS_MASK  DesiredAccess,
              IN POBJECT_TYPE  ObjectType,
              IN KPROCESSOR_MODE  AccessMode,
              OUT PVOID  *Object,
              OUT POBJECT_HANDLE_INFORMATION  HandleInfor
              )
{
  __asm
  {    
    mov edi,edi
    
    //create next stack for sub routine using  
    push ebp
    mov ebp,esp

    //parameter enter the stack
    push [ebp+0x1c]
    push [ebp+0x18]
    push [ebp+0x14]
    push [ebp+0x10]
    push [ebp+0xc]
    push [ebp+8]

    //enter the routine
    call call_failed
    cmp eax,1
    jz start_new

    mov eax,ObReferenceObjectByHandle
    add eax,5
    jmp eax

start_new:
    mov [ebp+8],-1
    mov eax,ObReferenceObjectByHandle
    add eax,5
    jmp eax
  }
}

//in this routine install inline hook
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
  //this is offset of jmp to my routine
  int jmp_offset;
  
  KIRQL irql;  

  //set current driver unload routine
  DriverObject->DriverUnload = DriverUnload;

  irql = KeRaiseIrqlToDpcLevel();
  close_write_protected();
  
  //count and get to my_routine offset
  jmp_offset = (char *)my_routine - (char *)ObReferenceObjectByHandle - 5;
  
  //create the jmp offset to jmp_offset
  RtlCopyMemory(jmp_code+1,&jmp_offset,4);  

  //inline hook ObReferenceObjectByHandle routine
  RtlCopyMemory(ObReferenceObjectByHandle,jmp_code,5);

  open_write_protected();
  KeLowerIrql(irql);
  
  DbgPrint("install inline hook success.");

  return STATUS_SUCCESS;
}

windbg分析的dump信息:

DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high.  This is usually
caused by drivers using improper addresses.
If kernel debugger is available get stack backtrace.
Arguments:
Arg1: 0041f4a4, memory referenced
Arg2: 00000002, IRQL
Arg3: 00000000, value 0 = read operation, 1 = write operation
Arg4: 0041f4a4, address which referenced memory

Debugging Details:
------------------

PEB is paged out (Peb.Ldr = 7ffde00c).  Type ".hh dbgerr001" for details
PEB is paged out (Peb.Ldr = 7ffde00c).  Type ".hh dbgerr001" for details

READ_ADDRESS:  0041f4a4 

CURRENT_IRQL:  2

FAULTING_IP: 
+41f4a4
0041f4a4 ??              ???

PROCESS_NAME:  Dbgview.exe

DEFAULT_BUCKET_ID:  DRIVER_FAULT

BUGCHECK_STR:  0xD1

LAST_CONTROL_TRANSFER:  from 0041f4a4 to 804e287f

FAILED_INSTRUCTION_ADDRESS: 
+41f4a4
0041f4a4 ??              ???

STACK_TEXT:  
f318c854 0041f4a4 badb0d00 0012f370 00000000 nt!KiTrap0E+0x233
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012f354 00000000 00000000 00000000 00000000 0x41f4a4


STACK_COMMAND:  kb

FOLLOWUP_IP: 
nt!KiTrap0E+233
804e287f f7457000000200  test    dword ptr [ebp+70h],20000h

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  nt!KiTrap0E+233

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: nt

IMAGE_NAME:  ntoskrnl.exe

DEBUG_FLR_IMAGE_TIMESTAMP:  42250ff9

FAILURE_BUCKET_ID:  0xD1_CODE_AV_BAD_IP_nt!KiTrap0E+233

BUCKET_ID:  0xD1_CODE_AV_BAD_IP_nt!KiTrap0E+233

Followup: MachineOwner

  • 标 题:答复
  • 作 者:轩辕小聪
  • 时 间:2009-07-18 13:06

看起来是DbgPrint的时候出了问题,IRQL==2,KeLowerIrql没效果?把KeRaiseIrqlToDpcLevel和KeLowerIrql注释掉试试。

  • 标 题:答复
  • 作 者:轩辕小聪
  • 时 间:2009-07-19 16:40

引用:
最初由 emc发布 查看帖子
to 轩辕小聪:

呵呵,我们都大意了,犯了一个最常见的错误。

open_write_protected();
KeLowerIrql(irql);   

上面的2条代码永远不可能被执行,所以我在return之前调用这2条就可以了。问题已经解决了,谢谢你的帮助。
请看:http://bbs.pe...
倒,还真没注意你在之前已经return了……
一定要注意,去掉写保护与加上写保护,以及提IRQL与还原IRQL的操作,一定要一一对应,绝不能有前无后……