转帖翻译跟踪Native API函数调用Word文档下载推荐.docx
- 文档编号:19694134
- 上传时间:2023-01-08
- 格式:DOCX
- 页数:14
- 大小:30.01KB
转帖翻译跟踪Native API函数调用Word文档下载推荐.docx
《转帖翻译跟踪Native API函数调用Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《转帖翻译跟踪Native API函数调用Word文档下载推荐.docx(14页珍藏版)》请在冰豆网上搜索。
1.必须用某些方法向其它进程的地址空间中注入自己的代码(设置全局HOOK(SetWindowsHookEx()),通过注册标加载DLL,直接向进程地址空间注入代码(WriteProcessMemory(),CreateRemoteThread()…))。
2.不能对任意进程进行此种操作,因为WinNT为系统中所有的对象都赋予了一个securitydescriptor,这个securitydescripter定义了一个对象能否拥有另一对象以及拥有的程度。
3.如果被拦截的系统函数A本身基于另一个系统函数B而进程突然直接调用了B,这时该怎么办?
这时又得找到函数B并对其进行拦截。
4.所跟踪到的函数调用情况只是针对于“被处理过”的进程,而不是所有的进程。
方法2:
修改包含所要跟踪的函数的那部分库代码(splicing法,例如在函数起始部分注入指向我们处理程序的机器指令Jmp0xXXXXXXXX)。
不要需要修改进程模块的import表!
在Win9X中此法可以跟踪到所有进程对系统库函数的调用。
1.有让系统宕掉的危险,因为在修改库代码的时候可能会发生上下文的切换,如果修改尚未完成而被修改的函数被另一个进程或线程调用的话,则发生异常的可能性非常大,而可能更坏的是,系统会挂起或者是发生BSOD。
如果此函数的拦截代码位于非调用进程的进程上下文中,也会有同样的问题。
2.要实现这种方法必须修改库代码的segments,而这些segments却有着“ReadExecute”属性,但修改还是可以的。
最后还有一种办法——特别是用在Windows的调试机制中(DebugAPI)。
讲到这里,我想大家都能明白。
顺便说一句,在wasm.ru有相当多的文章是讲前面这些拦截方法的。
特别值得注意的是90210/HI-TECH的文章,所有这些方法在那篇文章里都有。
现在我们假设我们需要监视所有的进程来取得某些资源使用情况和对这些资源的操作(创建/打开/读取/写入某个文件、注册表、修改某个进程页的保护属性等等)。
准备好了么?
现在请再看一下前面的内容并告诉我,借助于前面讲的方法这可能实现吗?
当然,在读者当中一定会有乐观主义者认为是可以的,但这说起来容易做起来难。
但是有一种办法更为简单,最主要的是它更为有效。
NATIVEAPI
我们知道,Windows2000的设计理念是它不止能运行Win32应用程序,还能运行旧的Win16,MsDos,Os/2v1.x程序和POSIX程序。
简单的说,系统里内建了三个子系统,每个子系统执行自己的程序。
实际上,没人能影响我们向系统中添加对其它操作系统的支持,所要做的只是编写程序并将他绑定到相应的子系统模块上就行了。
然而,程序是怎么工作的呢?
为了回答这个问题我们来看下面这幅图。
(图欠奉)
如果所启动的程序不是Win32应用程序,而是其它操作系统的程序,此时Win2k会寻找与其相对应的能识别并启动此程序的子系统。
如果没找到,则会返回错误。
我们来详细讲一下。
任何调用,比如说OS/2应用程序的,会由此应用程序的支持模块转换为Win32子系统库中的相应的函数,这个Win32子系统库就是KERNEL32.DLL。
KERNEL32.DLL会将调用转到NTDLL.DLL中的一个函数。
(斜体加粗)尽管如此,Win32应用程序可以直接调用ntdll.dll模块中的函数,甚至连这一步都可以省掉,直接调用int0x2e。
(斜体加粗小号字体)只是像这样做的应该都是些特殊的应用程序,显然它们应该知道作些什么。
然而,在WinNT中这样的程序并不少见,而且它们都被成为服务,确切讲叫服务应用程序。
其中的一个例子就是众所周知的Svchost.exe。
这样的应用程序只能存在于WinNT中,在Win9x中则不能工作。
现在我们开始讲最有意思的东西。
实际上ntdll.dll这个库中的所有函数都是某种stub,看上去是下面这个样子:
.text:
77F84F40publicZwWriteFile;
NtDll!
ZwWriteFile
77F84F40ZwWriteFileprocnear
77F84F40
77F84F40arg_0=byteptr4
77F84F40moveax,0EDh;
NtWriteFile
77F84F45leaedx,[esp+arg_0]
77F84F49int2Eh
77F84F4Bretn24h
77F84F4BZwWriteFileendp
我们来分析一下这段代码。
函数ZwWriteFile()在堆栈中接收9个参数,每个都是4个字节长。
(斜体加粗)几乎所有关于NativeApi函数的信息都能在GarryNebetta的《WindowsNT/2000NativeAPI参考》一书中找到。
但是要强调的是,本书是在Win2K时代写成的,所以在书中只讲了Win2K中存在的函数,而在新版的5.1(WinXP)和5.2(Win.Net)内核中添加了数量未知的新函数,遗憾的是对这些函数的描述我还没有找到。
(斜体加粗)还有一个地方值得一提——函数的名称。
正如您看到的,前缀分别为Ntxxx和Zwxxx的函数是一样的。
如果看一下NTDLL.DLL模块的反汇编代码的话,就会发现除了前缀不一样外这两种函数没有区别——Nt与相应的Zw函数指向的是同一处代码。
在模块NTOSKRNL.EXE中情形又有些不同。
前缀为Zw的函数,实际上是由此模块导出的函数,并且是直接调用的。
而前缀为Nt的函数则需要在相应的Zw函数取得控制之前做一系列的安全验证。
然而,我建议不要为这个问题太费脑筋。
我们将看到,这里直接使用到两个寄存器。
在EAX寄存器中放的是函数的系统调用号(index,我们后面会讲到)。
在EDX寄存器中放的是函数参数堆栈的指针,这个指针的值减去4个字节就是函数返回点的地址。
之后就是使用中断调用指令INT0x2E进行处理器模式的切换,这个中断被保留用作系统调用handlers的入口点。
这正是我们所需要的。
实际上,所有用户模式(UserMode)应用程序发出的系统函数调用最终都要发向内核并通过INT0x2E,而通过这个INT0x2E处理器从ring3切换到了ring0,即KernelMode。
好,到这里系统调用门儿外的情况我们已经分析了,现在我们看一下门儿里面的东西。
KiSystemService&
ServiceDescriptorTable
;
_KiSystemService:
00464FCDpush0;
被M$称为ENTER_SYSCALLmacro的代码块,不只在这个函数的prolog中能见到。
最有意思的是从3.51版的WindowsNT开始,这个函数就没再发生过变化。
在堆栈中保存主要的寄存器。
00464FCFpushebp
00464FD0pushebx
00464FD1pushesi
00464FD2pushedi
00464FD3pushfs
向FS中加载PCR指针
00464FD5movebx,30h
00464FDAdb66h
00464FDAmovfs,bx
在堆栈中保存前exceptionhandler
00464FDDpushdwordptrds:
0FFDFF000h
初始化新的exceptionhandler链表,就是叫做SEHframe的东西。
00464FE3movdwordptrds:
0FFDFF000h,0FFFFFFFFh
取得当前线程结构体的地址
00464FEDmovesi,ds:
0FFDFF124h
在堆栈中保存前User/Kernel模式以及相关的地址
00464FF3pushdwordptr[esi+134h]
00464FF9subesp,48h
00464FFCmovebx,[esp+6Ch]
00465000andebx,1
00465003mov[esi+134h],bl
修正当前的stackframe
00465009movebp,esp
保存当前的stackframe
0046500Bmovebx,[esi+128h]
建立新的stackframe
00465011mov[ebp+3Ch],ebx
00465014mov[esi+128h],ebp
0046501Acld
到这儿就有意思了,验证当前线程是否正被调试,如果是的话,可以自己通过地址看。
0046501Btestbyteptr[esi+2Ch],0FFh
0046501Fjnzloc_464F49
00465025
00465025loc_465025:
00465025sti
00465026
00465026loc_465026:
_KiSystemServiceRepeat:
向edi寄存器拷贝服务号以进行下面的操作
00465026movedi,eax
00465028shredi,8
0046502Bandedi,30h
0046502Emovecx,edi
取得线程descriptortable指针(每个线程都有一个自己的SERVICE_DESCRIPTOR_TABLE结构体,因为_KTHREAD中就保存有指向它的指针)
00465030addedi,[esi+0DCh]
00465036movebx,eax
还有一项验证,突然此调用进入了驱动程序win32k.sys,抑或者这个服务根本就不存在?
00465038andeax,0FFFh
0046503Dcmpeax,[edi+8]
00465040jnbloc_464E02
00465046cmpecx,10h
00465049jnzshortloc_465065
取得当前TEB的地址,之后又有一个问题,这个调用是GDI类型的吗?
0046504Bmovecx,ds:
0FFDFF018h
00465051xorebx,ebx
00465053orebx,[ecx+0F70h]
00465059jzshortloc_465065
0046505Bpushedx
0046505Cpusheax
0046505Dcalldword_482220
00465063popeax
00465064popedx
00465065
00465065loc_465065:
增加系统调用计数器。
这个计数器是性能计数器中的一个,供PerformanceMonitor之类的程序使用
00465065incdwordptrds:
0FFDFF5DCh
在esi中放入用户参数堆栈的指针。
还记着ntdll.dll库中的ZwWriteFile函数的stub中的edx寄存器吗?
我希望您还记着为什么这里使用堆栈
指针的参数传递机制,而不是通常的堆栈方式。
因为在调用了0x2e并进行了模式切换之后,处理器向SS中加载了一个值。
这个值之前保存在
TSS段(0x28)里,现在它将指向GDT表中的另一个descriptor。
因为这个结构体所有线程都有一个(对于DoubleFault(_KiTrap08)的处理
用的是自己的TSS段,不用考虑得太周全),这样在模式切换后会对当前线程堆栈进行调整。
线程的用户模式下的堆栈和内核模式下的是不一
样的,因为如果一样的话,则我们只会在屏幕上看见一样东西——BSOD。
除此之外,具体线程间的堆栈也是不一样的。
对此您可一定要有个
清晰的认识。
我们继续来看系统服务函数的代码。
0046506Bmovesi,edx
取得服务参数表KiArgumentTable的指针。
0046506Dmovebx,[edi+0Ch]
为了文章的完整性,我们来对servicetable及其参数进行一点研究。
来看下图:
这就是我们的descriptortable。
请注意灰白色的区域。
实际上,我只着重区分出了我们真正感兴趣的域。
剩下的域一般都是NULL指针或是零值。
现在我们来看这些东西用C语言如何描述。
typedefstruct_SERVICE_DESCRIPTOR_TABLE
{
SYSTEM_SERVICE_TABLENtoskrnlTable;
//ntoskrnl.exe(nativeapi)
SYSTEM_SERVICE_TABLETable2;
//空闲
SYSTEM_SERVICE_TABLETable3;
//用于InternetInformationServices
SYSTEM_SERVICE_TABLETable4;
//空闲
}
SERVICE_DESCRIPTOR_TABLE,
*PSERVICE_DESCRIPTOR_TABLE,
**PPSERVICE_DESCRIPTOR_TABLE;
typedefstruct_SYSTEM_SERVICE_TABLE
PNTPROCServiceTable;
//指向handler入口点数组的指针
PDWORDCounterTable;
//服务调用计数器(未使用)
DWORDServiceLimit;
//所支持的服务的数目
PBYTEArgumentTable;
//服务参数数组指针
SYSTEM_SERVICE_TABLE,
*PSYSTEM_SERVICE_TABLE,
**PPSYSTEM_SERVICE_TABLE;
(加粗)除了这个由Ntoskrnl.exe公开导出的结构体外,在WinNT中还有一个这样的结构体,名为ServiceDescriptorTableShadow,这个结构体未被导出,只在模块内部使用。
(斜体加粗)如果内部的函数名没有被导出,则就意味着这个函数我们无需了解。
我们有非常简单的办法可以对它们进行访问。
总之一定要使用一种调试器,或是DDK中的KernelDebugger(i386kd.exe)、WinDbg(windbg.exe),或是我的最爱——SoftIce,但是使用前一定要设置好调试符号,这些调试符号可以从微软的站点上下载。
除此之外,如果您已经弄好了调试符号(参见PDBFile或是Ctrl+F12)可以轻松地搞定相关的.PDB文件。
(加粗)在Windows2000里它就在在ServiceDescriptorTable后面,可以很快地找到。
但是并非在所有版本的WindowsNT中都是这样。
这个结构体的有趣之处在于,除了NtoskrnlTable域,第二个域的值并不为零,而是一个SystemServiceTable结构体指针,指向GDI函数入口点的数组,而这些GDI函数都位于驱动程序Win32k.sys中。
我们知道,在WinNT中图形部分被封装进了内核,这是为了相应的进程能运行得更快。
但是,大多数情况下,人们对这一问题都不太在意,因为很少有人对拦截图形函数感兴趣。
现在我们来更详细地讲一下这个结构体。
我们将看到,KeServiceDescriptorTable结构体中有四个完全相同的SystemServiceTable结构体。
其中只有第一个结构体被赋值,我把这个结构体叫做NtoskrnlTable。
SystemServiceTable结构体由四个域构成。
我们来逐一讲解。
ServiceTable–指向系统服务handler入口点数组的指针。
让我们感兴趣的是,有一些入口点指向的函数是存在的,并且被Ntoskrnl.exe导出了,而另一些则不是这样。
(斜体加粗)例如,函数NtCreateProcess并未被导出,然而入口点却指向了某段代码,这段代码实现了相应的功能。
所以在内部,比如说在驱动程序里,实现创建进程这样的功能的时候工作就变得复杂,因为我们不可能使用已经准备好的导出函数,而且除了KiSystemService函数之外,没有一个通用的接口。
但是NtCreateProcess只是冰山的一角,与进程创建有关的大部分准备工作都封装在了Kernel32.dll和Ntdll.dll之中。
这样的函数可不止这一个。
如果您急切地想在驱动中实现类似的操作,请先坐下来,然后分析WinNT的实现方法。
这个活儿可不是那么轻松的,而且要求要有大量的相关知识和相当的耐性,除此之外更为复杂的是,这段代码可能广泛分布于不同的模块中,而不止是局限在某个函数的内部。
您得好好想想您到底要干什么,但是在Windows9x中在内核模式下创建进程是可以轻松完成的,这靠的是驱动程序Shell.vxd的“导出”函数_ShellExecute。
对于为什么Microsoft在WindowsNT中采取了这样的措施,我脑子里只有一个答案,那就是为了系统的安全。
CounterTable–一个内核变量的指针,这个内核变量被用作KiSystemService使用次数的计数器,只在checkedbuild版本里才有值。
在普通版本里其值为NULL。
ServiceLimit–此版本WindowsNT实现的服务函数的数目。
例如在Windows2000build2195,此值为248(0xF8),而在WindowsXPbuild2600里则为284(0x11C)。
显然这个值增大了。
ArgumentTable–指向参数数目字节数组的指针。
这些参数都是以字节为单位传给函数的。
也就是说,如果函数NtAcceptConnectPort在堆栈中接收0x18个字节,那就是6个双字。
双字在ServiceTable数组中的序号对应着ArgumentTable表中有此相同序号的成员。
除了上面所讲的,还有一个有意思的细节值得我们注意。
您可能已经注意到,在结构体ServiceDescriptorTable中最好的位置上只占据了两个SystemServiceTable结构体(除了Ntoskrnl.exe外,第二个可能会被IIS服务占用)。
自然还剩下了两个,用户可以用其来想系统中添加自己的服务。
为此需要调用KeAddSystemServiceTable函数,而这个函数在DDK中有说明。
我想,到这里已经全都讲清了。
我们继续拆解_KiSystemService的代码。
向CL加载相应的ArgumentTable数组成员
来确切地知道要向内核堆栈传递多少个字节
00465070xorecx,ecx
00465072movcl,[eax+ebx]
00465075movedi,[edi]
向EBX寄存器中加载相应服务函数的入口点
00465077movebx,[edi+eax*4]
0046507Asubesp,ecx
传递双字
0046507Cshrecx,2
0046507Fmovedi,esp
检验用户堆栈是否位于用户空间
00465081cmpesi,MmUserProbeAddress;
=0x7FFFFFFF
00465087jnbloc_465271
0046508D
0046508Dloc_46508D:
将堆栈中的参数传递给要调用的函数
0046508Drepemovsd
所有都准备好并
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 转帖翻译跟踪Native API函数调用 翻译 跟踪 Native API 函数 调用