转载自:http://hi.baidu.com/resoft007/item/eb510a0d0ae2ac03addc7019
http://bbs.pediy.com/showthread.php?t=106888
http://bbs.pediy.com/showthread.php?t=55142
解惑:http://blog.csdn.net/u012410612/article/details/17093079
lkd> u ZwOpenProcess
nt!ZwOpenProcess:
804de044 b87a000000 mov eax,7Ah
804de049 8d542404 lea edx,[esp+4]
804de04d 9c pushfd
804de04e 6a08 push 8
804de050 e8dc150000 call nt!KiSystemService (804df631)
804de055 c21000 ret 10h
lkd> u NtOpenProcess
nt!NtOpenProcess:
80573d06 68c4000000 push 0C4h
80573d0b 6810b44e80 push offset nt!ObWatchHandles+0x25c (804eb410)
80573d10 e826f7f6ff call nt!_SEH_prolog (804e343b)
80573d15 33f6 xor esi,esi
80573d17 8975d4 mov dword ptr [ebp-2Ch],esi
80573d1a 33c0 xor eax,eax
80573d1c 8d7dd8 lea edi,[ebp-28h]
80573d1f ab stos dword ptr es:[edi]
这是看雪上某人反的两个函数。
从用户模式调用Nt和Zw API,如NtReadFile和ZwReadFile,二者没有任何区别,通过设置系统服务表中的索引和在堆栈中设置参数,经由SYSENTER指令进入内核态(而不是象w2k中通过int 0x2e中断),并最终由KiSystemService跳转到KiServiceTable对应的系统服务例程中。由于是从用户模式进入内核模式,因此代码会严格检查用户空间传入的参数。
从内核模式调用Nt和Zw API,连接nooskrnl.lib:
Nt系列API将直接调用对应的函数代码,而Zw系列API则通过KiSystemService,最终跳转到对应的函数代码。
重要的是两种不同的调用对内核中previous mode的改变,如果是从用户模式调用Native API则previous mode是用户态,如果从内核模式调用Native API则previous mode是内核态。previous为用户态时Native API将对传递的参数进行严格的检查,而为内核态时则不会。
调用Nt API时不会改变previous mode的状态,调用Zw API时会将previous mode改为内核态,因此在进行Kernel Mode Driver开发时可以使用Zw系列API可以避免额外的参数列表检查,提高效率。(Zw*会设置KernelMode已避免检查,Nt*不会自动设置,如果是KernelMode当然没问题,如果就UserMode就挂了)
我们只要把kthread中的PreviousMode值设为0就代表来自内核态
Zw*系列是通过SSDT来间接调用,同时设置Ehread->previous mode 为 kernelmode
Nt*系列是直接调用函数,但是却不设置Ehread->previous mode
所以如果是在内核mode当然没问题,同时还可以跳过SSDT HOOK ps:当然Inline Hook肯定不行
但是内核组件可能运行在任意进程的上下文中 所以Nt*可能在usermode中,这是就会STATUS_ACCESS_VIOLATION 了
当然这是Nt*函数中检查当前线程结构的PreviousMode 来确定调用是来自用户态还是内核态 如果是来自用户态 函数会检测参数地址是否小于_MmUserProbeAddress.如果不是 将会产生一个错误.. 所以我们的目标就是把PreviousMode改为Kernel Mode 这就是Zw*和Nt*的区别