QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2724|回复: 0
打印 上一主题 下一主题

浅析本机API

[复制链接]
字体大小: 正常 放大
韩冰        

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

跳转到指定楼层
1#
发表于 2005-1-23 13:27 |只看该作者 |倒序浏览
|招呼Ta 关注Ta

作者:sunwear[E.S.T] shellcoder@163.com8 P2 P# a5 b5 ?1 f% Q7 O' T7 h 来源:邪恶八进制 中国

0 O6 |9 _* W( |* w

此文只能说是一篇笔记,是关于本机API的.本机API是除了Win32 API,NT平台开放了另一个基本接口。本

Y% c; m+ n: [6 j5 E" y

机API也被很多人所熟悉,因为内核模式模块位于更低的系统级别,在那个级别上环境子系统是不可见的

$ o" ?" E( L! L: |4 b

。尽管如此,并不需要驱动级别去访问这个接口,普通的Win32程序可以在任何时候向下调用本机API。并

1 Q8 E, M( h* h3 N9 K _7 ]

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 + b1 v7 M6 d& \& G9 l; l) m

- e1 T6 e1 |6 ^7 Q

User32.dll,kernel32.dll,shell32.dll,gdi32.dll,rpcrt4.dll,comctl32.dll,advapi32.dll,version.d

( u8 a3 e0 n2 e6 T# z

ll等dll代表了Win32 API的基本提供者。Win32 API中的所有调用最终都转向了ntdll.dll,再由它转发至

5 H+ D5 M( x2 R3 q: `9 Y0 [

ntoskrnl.exe。ntdll.dll是本机 API用户模式的终端。真正的接口在ntoskrnl.exe里完成。事实上,内

3 w4 O- Z% {5 t7 ~& U3 E

核模式的驱动大部分时间调用这个模块,如果它们请求系统服务。Ntdll.dll的主要作用就是让内核函数

) P0 z% z: B+ R* I) k5 X$ W a7 H

的特定子集可以被用户模式下运行的程序调用。Ntdll.dll通过软件中断int 2Eh进入ntoskrnl.exe,就是

5 f) y- e5 Q3 X8 E+ F4 \

通过中断门切换CPU特权级。比如kernel32.dll导出的函数DeviceIoControl()实际上调用ntdll.dll中导

! C1 J" F$ c9 i, w

出的NtDeviceIoControlFile(),反汇编一下这个函数可以看到,EAX载入magic数0x38,实际上是系统调

- u. m! H: ?7 Q$ R

用号,然后EDX指向堆栈。目标地址是当前堆栈指针ESP+4,所以EDX指向返回地址后面一个,也就是指向

& g4 i! E$ F6 R0 O$ H8 W

在进入NtDeviceIoControlFile()之前存入堆栈的东西。事实上就是函数的参数。下一个指令是int 2Eh,

' \7 G* D2 d( R- i6 e

转到中断描述符表IDT位置0x2E处的中断处理程序。

+ `/ ]0 A# G: D. ?5 I

反编汇这个函数得到:

' X4 H& ^9 q7 q, q0 \) |) j6 z

mov eax, 38h

J; k' Q' P8 I6 |

lea edx, [esp+4]

. r: b- J8 l) b

int 2Eh

& U' J2 n7 V \3 t9 d4 I, L

ret 28h

2 }0 C" i0 A4 J6 R. Z2 w* R! a

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

2 C6 s* D- f' R, ?' u2 S8 d2 @

W2k Native API由248个这么处理的函数组成,比NT 4.0多了37个。可以从ntdll.dll的导出列表中很容易

* Y# m, A2 d; e- @

认出来:前缀Nt。Ntdll.dll中导出了249个,原因在于NtCurrentTeb()为一个纯用户模式函数,所以不需

. P! J! U9 \- r& G. F( u @

要传给内核。令人惊奇的是,仅仅Native API的一个子集能够从内核模式调用。而另一方面,

) y7 L. x6 r2 g9 t( d( Z$ b

ntoskrnl.exe导出了两个Nt*符号,它们不存在于ntdll.dll中: NtBuildNumber, NtGlobalFlag。它们不

1 f$ g* o4 w, S; P" B: p, W

指向函数,事实上,是指向ntoskrnl.exe的变量,可以被使用C编译器extern关键字的驱动模块导入。

% e: J6 x4 n& G$ |0 Y( v3 K* f) X

Ntdll.dll和ntoskrnl.exe中都有两种前缀Nt*,Zw*。事实上ntdll.dll中反汇编结果两者是一样的。而在

* t" G! q: {- e! G+ y$ h

ntoskrnl.exe中,nt前缀指向真正的代码,而zw还是一个int 2Eh的stub。也就是说zw*函数集通过用户模

- B# z" Q7 [5 _5 T- B9 O- c, x g

式到内核模式门传递的,而Nt*符号直接指向模式切换以后的代码。Ntdll.dll中的NtCurrentTeb()没有相

2 t; z6 s3 a* {% Z

对应的zw函数。Ntoskrnl并不导出配对的Nt/zw函数。有些函数只以一种方式出现。

3 m8 A5 H3 m7 Z/ a

2Eh中断处理程序把EAX里的值作为查找表中的索引,去找到最终的目标函数。这个表就是系统服务表SST

* J( D6 T# H; E- ` f

,C的结构SYSTEM_SERVICE_TABLE的定义如下:清单也包含了结构SERVICE_DESCRIPTOR_TABLE中的定义,为

& Q9 C8 Q# }* v+ T" M

SST数组第四个成员,前两个有着特别的用途。

1 {! y% C! E" ^/ U

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

& s1 O1 P3 o5 g6 _/ Q

typedef NTPROC *PNTPROC;

# V) R1 ]' I v# X i2 J0 s

#define NTPROC_ sizeof (NTPROC)

; C# @# P N1 K; ~. k/ j

typedef struct _SYSTEM_SERVICE_TABLE

; l. N) q- O% p/ }% k

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

V5 s5 l! k V" i b/ H1 V9 L

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

8 _( O* }7 b+ d1 N8 }

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

! C: }8 Q9 k& Q- ~& x% `0 l% }

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

$ T+ A# Z4 t5 z- L- X0 X9 N% _

) SYSTEM_SERVICE_TABLE ,

' S; W! y4 Z1 G$ O' \$ Y+ Z$ y9 p

* PSYSTEM_SERVICE_TABLE ,

+ C1 e- r' R/ _6 z. a% V9 F

* * PPSYSTEM_SERVICE_TABLE ;

2 P+ _- X# _4 |

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

* ?3 x( q# |8 f9 s8 |

typedef struct _SERVICE_DESCRIPTOR_TABLE

6 n# K, t& t, M4 R2 G# `

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

. B8 H7 ?' M1 d( [3 L

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

/ ~" ]; a& `+ [ b2 d) n6 n

SYSTEM_SERVICE_TABLE Table3; // 未使用

a' Y0 C+ k/ p3 C% t

SYSTEM_SERVICE_TABLE Table4; // 未使用

; K' t8 x; T6 M* W6 d5 A) ]

} SERVICE_DESCRIPTOR_TABLE ,

1 i1 w( R2 n- ^9 U

* PSERVICE_DESCRIPTOR_TABLE,

7 f- o# I! q! g$ B h- d8 ^4 `2 e6 b

* PPSERVICE_DESCRIPTOR_TABLE ;

7 p$ L# {$ P8 h9 U6 x

ntoskrnl通过KeServiceDescriptorTable符号,导出了主要SDT的一个指针。内核维护另外的一个SDT,就

& T, N. j7 H; X% q* n2 k; e

是KeServiceDescriptorTableShadow。但这个符号没有导出。要想在内核模式组件中存取主要SDT很简单

$ {4 X# ^* X W) |- Y3 R7 I

,只需两行C语言的代码:

8 C8 O7 [$ y2 G* ?5 f' T

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

$ w* E9 p- `0 Y. y+ F: |

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

$ L/ R% j. ?" X/ u

NTPROC为本机 API的方便的占位符,他类似于Win32编程中的PROC。Native API正常的返回应该是一个

; h Y, v4 |4 P

NTSTATUS代码,他使用NTAPI调用约定,它和_stdcall一样。ServiceLimit成员有在ServiceTable数组里

, q9 X* R( L5 @; m; [* c

找到的入口数目。在2000下,默认值是248。ArgumentTable为BYTEs的数组,每一个对应于ServiceTable

) e$ z, B3 M w' b1 P1 @: J. f0 V

的位置并显示了在调用者堆栈里的参数比特数。这个信息与EDX结合,这是内核从调用者堆栈copy参数到

" [4 ]( i7 M# D5 F" b

自己的堆栈所需的。CounterTable成员在free buid的2000中并没有使用到,在debug build中,这个成员

+ m% {# ?. k0 H" B: R- i

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。 9 C# S) P& n( B) q0 }6 A+ M' E 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

; R# O- D! v& p" Q/ r8 ?

前四行是最重要的,对应那四个SDT成员。0 U# X" F% V% Z9 d% u 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

( D/ N$ B4 Y$ g+ I

内核维护的第二个SDT。主要的区别在于后一个包含了win32k.sys的入口,前一个却没有。在这两个表中

5 c* I) \; P; i; P5 z1 `! r

,Table3与Table4都是空的。Ntoskrnl.exe提供了一个方便的API函数。这个函数的名字为:

6 j( ]8 s- ]% o7 D/ D. B3 {2 w

KeAddSystemServiceTable5 e* N% C( G0 ^1 G$ Q; Y0 ` 此函数去填充这些位置。

j: a7 w7 {9 X" m+ u7 k6 b

2Eh的中断处理标记是KisystemService()。这也是ntoskrnl.exe没有导出的内部的符号,但包含在2k符号

# z2 y1 a8 t& X. o2 S( F

文件中。关于KisystemService的操作如下:

: k5 _8 x9 ?2 e# Z9 a( Z9 o

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

/ I, \9 f8 v) s# T

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

# W" x% a0 X7 C9 x% }" a3 {/ F& b

映射至ntoskrnl表格,ID在

3 j2 Q3 j# R; w/ A5 _, {# @0 v" d

0x1000与0x1ffff的分配给win32k表格。剩下的0x2000-0x2ffff与

" v3 `; Z1 h( B5 s2 _8 [ J# d) r4 N

0x3000-0x3ffff则是Table3和Table4保留。

! u9 D9 z+ L0 R1 ^- u6 s/ P$ [

3 通过选定SST中的ServiceLimit成员检查EAX的0-11位。如果ID超过了范围,返回错误代码为

5 @+ v. N/ `. e, O3 G4 |; E

STATUS_INVALID_SYSTEM_SERVICE。

( @2 S9 Y; r6 i1 M) `7 Z

4 检查EAX中的参数堆栈指针与MmUserProbeAddress。这是一个ntoskrnl导出的全局变量。通常等于

4 J' ?; N$ H6 J/ e

0x7FFF0000,如果参数指针不在这个地址之下,返回STATUS_ACCESS_VIOLATION。

2 X! f( U! A' T M

5 查找ArgumentTable中的参数堆栈的字节数,从调用者的堆栈copy所有的参数至当前内核模式堆栈。

: {7 K; Y( Q% z8 k2 Z7 B' }8 Y

6 搜索serviceTable中的服务函数指针,并调用这个函数。

, U1 y5 i- o+ g U+ v! k! P

7 控制转到内部的函数KiserviceExit,在此次服务调用返回之后。

; t- |' p8 ~: I% W

从对SDT的讨论可以看到与本机API一起还有第二个内核模式接口。这个接口把Win32子系统的图形设备接

; q; }" D3 W7 Y1 m+ L' D. m5 N

口和窗口管理器和内核模式组件Win32k连接起来。Win32k接口一样是基于int 2eh。本机API的服务号是从

3 J' J7 g% ^2 D: ?; l

0x0000到0x0fff,win32k的服务号是从0x1000到0x1fff。(ddW32pServiceTable认定win32k.sys的符号可

i: T5 w+ ~. h9 t6 z

用。)win32k总共包含639个系统服务。

0 W# W# }7 x( W7 W" m: W" w

; j( G8 t3 _0 ~1 Y# w 2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

$ F: ?4 |5 d0 e1 ?$ l

而是一个与线程相关的指针。显然,线程可以有不同得SDT相关到自身。线程初试化的时

) j/ {3 V6 k5 Q4 t& D8 j( s

候,KeInitializeThread()把KeServiceDescriptorTable写到线程的控制块。尽管这样,这个默认设置之

, t' V" K: R. u- U9 y

后可能被改变为其它值,例如KeServiceDescriptorTableShadow。

; ~/ u2 `& q5 s# }$ ?( e

, @% K4 L4 } T2 P5 d Windows 2000运行时库

& f( g1 M' ?1 Y- ]3 X# V4 i

Ntdll.dll至少导出了不少于1179个符号。其中的249/248是属于Nt*/zw*集合。所以还有682个函数不是通

, D; P( H3 O% |# d0 w2 W

过int 2eh门中转。很显然,这么多的函数不依靠2k的内核。

4 {3 g6 v/ F3 f2 q; ?

其中一些是和c运行时库几乎一样的函数。其实ntoskrnl也实现了一些类似C运行时库的一些函数。可以

* v" Q u; u7 h/ J$ t

通过ddk里的ntdll.lib来链接和使用这些函数。反汇编ntdll.dll与ntoskrnl.exe的C运行时函数能发现

- Z* o# |1 I" S: Z, M2 w( W

,ntdll.dll并不是依赖ntoskrnl.exe。这两个模块各自实现了这些函数。

, P j# I) m4 y6 g, K! R' a

除了C运行时库外,2000还提供了一个扩展的运行时函数集合。再一次,ntdll.dll与ntoskrnl.exe各自

3 v- @) ?! ^$ Q) _9 ^" X

实现了它们。同样,实现集合有重复,但是并不完全匹配。这个集合的函数都是以Rtl开头的。2000运行

7 w6 v2 q, k3 t8 R7 Y3 g

时库包括一些辅助函数用于C运行时候无法完成的任务。例如有些处理安全事务,另外的操纵2000专用的

- m$ @/ Z* y9 D, p) G; r, J m/ _

数据结构,还有些支持内存管理。微软仅仅在DDK中记录了很有用的406个函数中的115个函数。

" B' V, g- _3 S

Ntdll.dll还提供了另外一个函数集合,以__e前缀开头。实际上它们用于浮点数模拟器。

/ o+ y! \" c" d& q% ?0 d

还有很多的函数集合,所有这些函数的前缀如下:

) ], C0 ]; ^( a$ T" D

__e(浮点模拟),Cc(Cache管理),Csr(c/s运行时库),Dbg(调试支持),Ex(执行支持),FsRtl(文件系统运行

* j( K) O6 ^0 w. z7 E5 K f7 b

时),Hal(硬件抽象层),Inbv(系统初试化/vga启动驱动程序bootvid.dll),Init(系统初试

/ |$ C, p9 a4 x# d2 C: @1 u6 {9 N9 { v: k* v$ N

化),Interlocked(线程安全变量操作),Io(IO管理器),Kd(内核调试器支持),Ke(内核例程),Ki(内核中断处

; n' \2 I: C( r) K7 ]

理),Ldr(映象装载器),Lpc(本地过程调用),Lsa(本地安全授权),Mm(内存管理),Nls(国际化语言支持),Nt

7 x& ^/ c8 F7 t! C' ]$ P

(NT本机API),Ob(对象管理器),Pfx(前缀处理),Po(电源管理),Ps(进程支持),READ_REGISTER_(从寄存器

/ ~# |. S G1 E& U6 t

地址读),Rtl(2k运行时库),Se(安全处理),WRITE_REGISTER_(写寄存器地址),Zw(本机API的替换叫法)

. L! ^6 |, k) G( z

,<其它>(辅助函数和C运行时库)。

! ^% y! C" {0 k+ x, _" U

当编写从用户模式通过ntdll.dll或内核模式通过ntoskrnl.exe和2000内核交互的软件的时候,需要处理

' ?' I# k, J; d/ K# q5 h1 @* s( t2 q

很多基本的数据结构,这些结构在Win32世界中很少见到。

, f5 ~( o4 |5 N% x

常用数据结构

$ j# E) J1 o7 [; w

l 整数

% F) Y0 E( ?- o. P& u& g

ANSI字符是有符号的,而Unicode WCHAR是无符号的

4 K3 V2 y+ ?, a, Q1 ?0 ^3 f$ ?2 ~4 M! [

MASM的TBYTE是80位的浮点数,用于高精度浮点运算单元操作,注意它与Win32的TBYTE(text byte)完全

( m, d1 M- R( N' c! j: k1 F0 o$ g% M9 B

不同。

, e' Y2 G8 v" L# D2 J( q+ i

TABLE 2-3. Equivalent Integral Data Types

# H$ k( P) O- S6 L' |

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

. @2 F8 ?. M4 m9 g; S$ b

8 BYTE unsigned char UCHAR CHAR) ^4 L( c2 L8 P6 H 16 WORD unsigned short USHORT WCHAR SHORT

; O. S" R8 e0 K7 ]) Q4 x8 W9 R6 p

32 DWORD unsigned long ULONG LONG

7 C$ y' `8 s4 q

32 DWORD unsigned int UINT INT

4 c# s; P8 X" j+ e% i# t! D

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

0 h( L. h1 p9 w$ @6 G

80 TBYTE N/A

+ o. F2 P& l/ l

typedef union _LARGE_INTEGER

. ~0 ?1 O- P2 B# M

{ struct{

0 _' H% K. P* ^# p

ULONG LowPart;

" U Z5 C( S, d2 g* g

LONG HighPart;};

- a4 ?4 ~0 \6 h* ^, I- F; y

LONGLONG QuadPart;

]: T) {3 A* V' U- l) t$ ?2 ]

}

' N8 u9 h# S2 u7 g+ M% D6 u

LARGE_INTEGER , * PULARGE_INTEGER ;

5 F6 h& I$ ~8 o w

typedef union _ULARGE_INTEGER{

! u+ G2 c" Z5 r. r2 x

struct{

5 T8 R* |0 S d1 ~

ULONG LowPart;

% u# D. I7 v/ R) Y

ULONG HighPart;}

7 k/ N" a0 l2 |0 |

ULONGLONG QuadPart;

* z) |* U5 d4 T+ [" h' ^9 e) h

}ULARGE_INTEGER, *PULARGE_INTEGER;

k0 h8 Y+ Z ^" }9 t. `& D+ U

l 字符

+ v3 J' n" ?+ h% i

Win32编程中PSTR用户CHAR*,PWSTR用于WCHAR*。取决于是否定义了UNICODE,PTSTR解释为PSTR或者

/ o5 z/ b8 T) @4 d' Q% z

PWSTR。在2k内核模式下,常用的数据类型是UNICODE_STRING,而STRING用来表示ANSI字符串:

. |$ \4 i, ]% Y1 j1 D

typedef struct _UNICODE_STRING{

( q1 L$ j/ O/ q3 @7 _8 U

USHORT Length; //当前字节长度,不是字符!!!

. R6 N) H) G; D) e* Y' C

USHORT MaximumLength; //Buffer的最大字节长度

) p8 P' _& c# A% V

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

) j9 T; Y0 @/ `2 X- f/ k

typedef struct _STRING{

" U; w7 w9 r U% I( D

USHORT Length;

- p0 |7 [& S* @ D* A

USHORT MaximumLength;

% c8 h+ ]2 E( ?) d

PCHAR Buffer;}STRING, *PSTRING;

# |7 v9 o6 o. M2 t

typedef STRING ANSI_STRING, *PANSI_STRING;

, r& e0 {& R# e

typedef STRING OEM_STRING, *POEM_STRING;

, e% F' _0 U m* S

操纵函数:RtlCreatUnicodeString(),RtlInitUnicodeString(),

1 y# |0 C! x0 S

RtlCopyUnicodeString()等等

0 R! R w7 x/ c' |; v8 g

l 结构

1 T7 x9 U! U4 ]9 a4 u8 d. K; r- `

许多内核API函数需要一个固定大小的OBJECT_ATTRIBUTES结构,比如NtOpenFile()。对象的属性是OBJ_*

6 Z4 _& h/ h! B

值的组合,可以从ntdef.h中查到。

$ ]+ M. |8 ^. i/ m

IO_STATUS_BLOCK结构提供了所请求操作结果的信息,很简单,status成员包含一个NTSTATUS代码, 如果

0 p" }: ?4 {0 U6 u: T1 G0 Z

操作成功 information成员提供特定请求的信息。

6 |% D5 C+ Y# w- Z

还有一个结构是LIST_ENTRY,这是一个双向环链表。

/ e) ?- C T7 r% M# E3 ?# s+ r

typedef struct _OBJECT_ATTRIBUTES

1 r5 W* F" ^: @

{

1 m; m% X$ @+ R

ULONG Length;

) n0 h2 Z2 [! Q$ r; d9 F+ B3 S

HANDLE RootDirectory;

/ V7 a: L' \8 [* ]: a r4 d4 `. ?0 q

PUNICODE_STRING ObjectName;

6 i% u( _8 z9 K4 G x) u

ULONG Attributes;

0 }& J! f% Q8 a% m

PVOID SecurityDescriptor;

) Q7 W1 B0 _3 W, V+ g, l* y7 |

PVOID SecurityQualityOfService;

9 o* p: o8 K1 v6 K# ?4 {% Q# B

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

3 R* ?0 U* |& z1 {

typedef struct _IO_STATUS_BLOCK

: G3 S, V& R# ~5 k1 |; ~

{

5 A+ o+ z5 @9 z' J% r! V' b6 l" I

NTSTATDS Status;

1 o# y' _+ n- D- x7 a6 t2 C

ULONG Information;

+ V7 R8 S; g9 G1 ?, y

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

a/ q0 `! Z0 |3 x8 c0 r

typedef struct _LIST_ENTRY

; \$ I) V: g$ P. o0 e2 ~/ I

{

/ S; r9 U8 `( P) S) U) t$ N( ^

Struct _LIST_ENTRY *Flink;

8 u* u9 U' X$ M7 H" g! v

Struct _LIST_ENTRY *Blink;

* }2 N: F/ ^1 M! h- G( X

}LIST_ENTRY, *PLIST_ENTRY;

+ ~) |7 l; z3 i( H' v8 B4 m* w

双向链表的典型例子就是进程和线程链。内部变量PsActiveProcessHead是一个LIST_ENTRY结构,在

7 Y3 ^8 _/ ~ f' n5 \

ntoskrnl.exe的数据段中,指定了系统进程列表的第一个成员。

! O4 g: C# V* w6 T

CLIENT_ID结构由进程和线程ID组成。

# ?$ Q; l1 r O

typedef struct _CLIENT_ID

$ U+ z% P9 I- N

{ HANDLE UniqueProcess;

8 I( k# O: s% S9 Q8 x; t! P

HANDLE UniqueThread;

0 ]+ x. h( R& }8 Q2 u4 N- ~5 V

)CLIENT_ID, *PCLIENT_ID;

% `8 O) F" {6 K

想要从用户模式调用ntdll.dll中的API函数,必须考虑到以下四点:

) G8 s- E9 L2 }3 k

1 SDK头文件没有包括这些函数的原型

! z% E/ f) m& ~1 a) L$ v

2 这些函数使用的若干基本数据类型没有包括在SDK文件中

- j: c7 f7 E. |

3 SDK和DDK头文件不兼容,不能在win32的c源文件包含ntddk.h中

6 n8 a5 b! H' l8 B

4 ntdll.lib没有包括在VC的默认导入库列表中。

/ d7 w w! b6 M- L% \

第4个很容易解决:#progma comment(linker,“/defaultlib:ntdll.lib”)

: o7 U( h/ h' o* E7 x0 Y# `, d

缺失的定义比较难解决,最简单的方法是写一个自定义的头文件,刚刚包含需要调用ntdll.dll中函数的

& B. J$ ~: C+ i* D/ h2 \$ n

定义。幸运的是,已经在光盘的w2k_def.h文件中做了这个工作。因为这个头文件将用于用户模式和内核

{ o3 k8 W; h; b) s! y) U6 E1 ^

模式程序,所以必须在用户模式代码中,#include<w2k_def.h>之前#define _USER_MODE_,使得DDK中出

6 |: R' n* W1 `0 K! O/ }

现而SDK中没有的定义可用。

3 u: `5 e& ~; }; e

2 B9 t) j( S/ Z: j" ?% R 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

0 z$ D, I! m; z. B9 y e4 ^0 Q- b4 y

指导; b# k' Z6 w' G7 d5 w3 C3 {" J

) X/ t8 H( U4 P9 a( K# \% H# \

9 I6 y" i9 B* ^( C0 r

zan
转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
您需要登录后才可以回帖 登录 | 注册地址

qq
收缩
  • 电话咨询

  • 04714969085
fastpost

关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

手机版|Archiver| |繁體中文 手机客户端  

蒙公网安备 15010502000194号

Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

GMT+8, 2026-10-18 02:02 , Processed in 0.555949 second(s), 52 queries .

回顶部