浅析本机API

2008-04-09 04:00:34来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

作者:sunwear [E.S.T]
2004/10/02
shellcoder@163.com
http://blog.csdn.net/sunwear

此文只能说是一篇笔记.本机API是除了Win32 API,NT平台开放的另一个基本接口。本机API也被很多人所熟悉,因为内核模式模块位于更低的系统级别,在那个级别上环境子系统是不可见的。尽管如此,并不需要驱动级别去访问这个接口,普通的Win32程序可以在任何时候向下调用本机API。并没有任何技术上的限制,只不过微软不支持这种应用开发方法。
User32.dll,kernel32.dll,shell32.dll,gdi32.dll,rpcrt4.dll,comctl32.dll,advapi32.dll,version.dll等dll代表了Win32 API的基本提供者。Win32 API中的所有调用最终都转向了ntdll.dll,再由它转发至ntoskrnl.exe。ntdll.dll是本机 API用户模式的终端。真正的接口在ntoskrnl.exe里完成。事实上,内核模式的驱动大部分时间调用这个模块,如果它们请求系统服务。Ntdll.dll的主要作用就是让内核函数的特定子集可以被用户模式下运行的程序调用。Ntdll.dll通过软件中断int 2Eh进入ntoskrnl.exe,就是通过中断门切换CPU特权级。比如kernel32.dll导出的函数DeviceIoControl()实际上调用ntdll.dll中导出的NtDeviceIoControlFile(),反汇编一下这个函数可以看到,EAX载入magic数0x38,实际上是系统调用号,然后EDX指向堆栈。目标地址是当前堆栈指针ESP 4,所以EDX指向返回地址后面一个,也就是指向在进入NtDeviceIoControlFile()之前存入堆栈的东西。事实上就是函数的参数。下一个指令是int 2Eh,转到中断描述符表IDT位置0x2E处的中断处理程序。

反编汇这个函数得到:

mov eax, 38h

lea edx, [esp 4]

int 2Eh

ret 28h

当然int 2E接口不仅仅是简单的API调用调度员,他是从用户模式进入内核模式的main gate。

W2k Native API由248个这么处理的函数组成,比NT 4.0多了37个。可以从ntdll.dll的导出列表中很容易认出来:前缀Nt。Ntdll.dll中导出了249个,原因在于NtCurrentTeb()为一个纯用户模式函数,所以不需要传给内核。令人惊奇的是,仅仅Native API的一个子集能够从内核模式调用。而另一方面,ntoskrnl.exe导出了两个Nt*符号,它们不存在于ntdll.dll中: NtBuildNumber, NtGlobalFlag。它们不指向函数,事实上,是指向ntoskrnl.exe的变量,可以被使用C编译器extern关键字的驱动模块导入。Ntdll.dll和ntoskrnl.exe中都有两种前缀Nt*,Zw*。事实上ntdll.dll中反汇编结果两者是一样的。而在ntoskrnl.exe中,nt前缀指向真正的代码,而zw还是一个int 2Eh的stub。也就是说zw*函数集通过用户模式到内核模式门传递的,而Nt*符号直接指向模式切换以后的代码。Ntdll.dll中的NtCurrentTeb()没有相对应的zw函数。Ntoskrnl并不导出配对的Nt/zw函数。有些函数只以一种方式出现。

2Eh中断处理程序把EAX里的值作为查找表中的索引,去找到最终的目标函数。这个表就是系统服务表SST,C的结构SYSTEM_SERVICE_TABLE的定义如下:清单也包含了结构SERVICE_DESCRIPTOR_TABLE中的定义,为SST数组第四个成员,前两个有着特别的用途。

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

typedef NTPROC *PNTPROC;

#define NTPROC_ sizeof (NTPROC)

typedef struct _SYSTEM_SERVICE_TABLE

{ PNTPROC ServiceTable; // 这里是入口指针数组

PDWORD CounterTable; // 此处是调用次数计数数组

DWORD ServiceLimit ; // 服务入口的个数

PBYTE ArgumentTable; // 服务参数字节数的数组

) SYSTEM_SERVICE_TABLE ,

* PSYSTEM_SERVICE_TABLE ,

* * PPSYSTEM_SERVICE_TABLE ;

/ / _ _ _ _ _ _ _ _ _ _ _ _

typedef struct _SERVICE_DESCRIPTOR_TABLE

{ SYSTEM_SERVICE_TABLE ntoskrnl ; // ntoskrnl所实现的系统服务,本机的API}

SYSTEM_SERVICE_TABLE win32k; // win32k所实现的系统服务

SYSTEM_SERVICE_TABLE Table3; // 未使用

SYSTEM_SERVICE_TABLE Table4; // 未使用

} SERVICE_DESCRIPTOR_TABLE ,

* PSERVICE_DESCRIPTOR_TABLE,

* PPSERVICE_DESCRIPTOR_TABLE ;

ntoskrnl通过KeServiceDescriptorTable符号,导出了主要SDT的一个指针。内核维护另外的一个SDT,就是KeServiceDescriptorTableShadow。但这个符号没有导出。要想在内核模式组件中存取主要SDT很简单,只需两行C语言的代码:

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

NTPROC为本机 API的方便的占位符,他类似于Win32编程中的PROC。Native API正常的返回应该是一个NTSTATUS代码,他使用NTAPI调用约定,它和_stdcall一样。ServiceLimit成员有在ServiceTable数组里找到的入口数目。在2000下,默认值是248。ArgumentTable为BYTEs的数组,每一个对应于ServiceTable的位置并显示了在调用者堆栈里的参数比特数。这个信息与EDX结合,这是内核从调用者堆栈copy参数到自己的堆栈所需的。CounterTable成员在free buid的2000中并没有使用到,在debug build中,这个成员指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。
可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有前四行是最重要的,对应那四个SDT成员。
运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为内核维护的第二个SDT。主要的区别在于后一个包含了win32k.sys的入口,前一个却没有。在这两个表中,Table3与Table4都是空的。Ntoskrnl.exe提供了一个方便的API函数。这个函数的名字为:

KeAddSystemServiceTable
此函数去填充这些位置。

2Eh的中断处理标记是KisystemService()。这也是ntoskrnl.exe没有导出的内部的符号,但包含在2k符号文件中。关于KisystemService的操作如下:

1 从当前的线程控制块检索SDT指针

2 决定使用SDT中4个SST的其中一个。通过测试EAX中递送ID的第12和13位来决定。ID在0x0000-0x0fff的映射至ntoskrnl表格,ID在

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:如何使用Shell.Application技术

下一篇:ASP中调用存储过程、语法、写法-sqlserver数据库