QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com # n& T6 a7 }8 ~" {+ M5 B$ q2 j来源:邪恶八进制 中国

) ^% \% Z" A% F% c: c

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

' O8 x9 H+ H; e) r+ W0 V

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

* O6 b2 z+ o) M4 d. }

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

- P# M& _8 v: m

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 , c; C! v- K7 Q: V- ~6 U

3 S X# x+ j* f$ K& I

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

* H) _+ ~! C% ^4 a) Z( |. s' D3 q( d

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

9 J) r) r- r1 M6 W5 f

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

7 ^. ?: e H. k

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

+ w* z1 u0 f- E+ d4 A/ w) n0 A4 [

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

7 Z+ J% t0 e& m9 Z; U0 ^' g

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

% c7 f% q' {; x# @; J

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

1 j2 i, M' q+ m" N5 ]; E

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

& ~" y" S$ r5 C+ q0 N/ A/ D; K

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

& r4 Y) L- z7 R7 J- x( p

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

! ]+ j: Z* e. m' [& W3 i

反编汇这个函数得到:

" i3 J2 `* |# A' V# i$ M6 v

mov eax, 38h

& i1 J( r+ ?- d+ G5 i( B4 D; @

lea edx, [esp+4]

$ a7 x7 ~, ^. o( x/ y- K W

int 2Eh

! J; ^6 b9 D& Z

ret 28h

1 N# d1 {/ L4 S7 `. a# X0 F" I! T

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

9 z/ T- l5 w) T u$ A7 S

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

6 `5 y/ o+ }% H% I/ S

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

- {8 W! ?, }( D& I

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

" @2 N3 n% z, o6 M% t

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

0 o* F, J. I/ {! E

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

3 J6 ~: h6 x. M6 Z% a& P# r" r) Y

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

3 J' e6 O3 ~5 P: M

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

9 }( {" N. Y7 H; T# e1 N

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

9 j6 R* n$ i; N1 H, j# d% t R

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

* C9 \+ ?5 I. A

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

) U( r$ u; @3 P, ~- i

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

; N0 }! `! ]2 A- g( Q

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

7 u! e% ]* W, X! p* e- b

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

3 u7 ]6 o% p3 D2 u7 {

typedef NTPROC *PNTPROC;

- _) ^; w% J! g, H7 o9 s% n

#define NTPROC_ sizeof (NTPROC)

$ p4 b( f+ f% W9 Y. r/ b

typedef struct _SYSTEM_SERVICE_TABLE

1 ?# t, L4 [: w

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

6 Q- v' E4 q- x+ e. p

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

( f* a+ C# J, e/ c& t2 n

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

' F. `: d5 U. ]

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

- D9 i0 U) g9 A) ]9 d( u0 X& K* ?

) SYSTEM_SERVICE_TABLE ,

) |, N$ G! T- Z8 V- H( I* J( a, R! _

* PSYSTEM_SERVICE_TABLE ,

, U6 I3 \" t' `7 c2 k( M

* * PPSYSTEM_SERVICE_TABLE ;

1 L( `5 r A$ z- ?

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

' A8 l. x5 M7 K( n; {2 G

typedef struct _SERVICE_DESCRIPTOR_TABLE

% G/ T* i% R3 z& Z% [) l' A

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

4 t' @! U+ `* j4 _: N) H4 g7 u6 ~4 W

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

, V2 M ]1 V, B9 Z# X1 ~

SYSTEM_SERVICE_TABLE Table3; // 未使用

' i# p' A; |1 q* ^3 q: n

SYSTEM_SERVICE_TABLE Table4; // 未使用

, G D+ {; d; {$ Q4 Y f+ \

} SERVICE_DESCRIPTOR_TABLE ,

- D. m9 \! J4 r4 ~6 S V0 f

* PSERVICE_DESCRIPTOR_TABLE,

/ _3 U6 p1 l/ z

* PPSERVICE_DESCRIPTOR_TABLE ;

" C+ b5 C! k- I W! u8 H" y

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

Q/ b$ W% z$ d4 i3 b8 d: n

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

' W. M4 m) W6 A

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

$ Y, U9 C) C: ?% M0 U5 w

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

( i$ g! s( Q+ O& {/ e( @

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

* D0 l7 D+ @. C. _

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

5 ?. o* n* ~$ O( G9 u! i8 B9 n0 S

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

8 A- T% c( d$ o$ A

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

3 _4 j0 P3 r+ J- P

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

# @. t7 I4 n+ z0 [: q& J

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

1 ~$ H5 d7 T/ ~

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。% O' J1 ]6 k1 L3 {" b 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

1 U0 X6 x* s9 P$ B8 K

前四行是最重要的,对应那四个SDT成员。1 c/ h" A8 ^+ d( L/ P7 I 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

* s$ ?8 C3 t- C) H' X A* ~

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

+ g, t$ ~- y$ f8 m4 ^8 F! l" p

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

" U+ E$ b7 ]! `* v( F, {( P0 w

KeAddSystemServiceTable$ z7 Y. t$ `# Z* ~2 | q 此函数去填充这些位置。

) O# f } |% K1 [

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

! Z/ {. Y. E; G2 i

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

4 L# U# Z O2 [0 d3 V( w5 S

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

: E N' v, E/ A8 D' F

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

. K! M4 O8 z8 d) \% s' I

映射至ntoskrnl表格,ID在

1 J! X/ R9 D* G) x0 [2 u4 u

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

1 R! S' f7 {, O; D

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

# D7 |" W9 q, u' C/ R

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

( d% ~, G5 s. [5 K. K! c

STATUS_INVALID_SYSTEM_SERVICE。

& |* V7 Y! {2 l3 W* \

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

% y( k/ v! i( }9 x1 A9 x( a

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

$ [. X8 E: d% H8 X3 o) J, N

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

/ O1 ^' }9 ]# R# v9 b4 I

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

& p n8 U6 \- [) h7 i9 U8 ~

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

% n& K% G+ B2 U) }+ x& S' C

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

! e6 P" C, r( Y9 K5 I

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

, \8 o4 r4 Z' {' K# F& a# q& i

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

( T* R7 F! U1 l4 J5 I

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

) y$ o j: D" _9 L! e" y4 `1 }

$ L6 I+ U+ b; w+ a 2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

" U0 D/ N5 T8 P o! P9 y5 U7 O) R

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

# K3 u( Y" t# ?( R

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

1 [5 E0 i" f d& K/ }# Q- T, m' h$ a

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

: Y. y# I" X. B/ n( F/ c) d

: K3 f% e9 f0 ]Windows 2000运行时库

K# T8 C8 t, H" p U

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

7 O/ f% ^+ l& ~ g9 c5 n2 n

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

# D$ n0 {* V) Q! M

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

; h" V, @2 [/ ]

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

8 A1 z- b/ E# t% J) f

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

+ u- x- p9 Z2 R/ H' e+ L9 b

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

$ v# v- o( S7 l% _( U0 H3 S' F

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

/ `( u: d/ X- ]' K7 |

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

7 b$ ^4 @6 O' O) ~" `

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

" x- ^! N+ n" t Y+ G) }

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

$ S, P- s" K$ h

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

. c6 a0 B; c3 O) U5 ]* |( R

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

7 K/ G$ K5 n! y8 L3 L) r6 }

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

0 ^8 _7 ~- \. j. D* d$ ^; ^6 P, f' d" E; Y7 m

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

! }3 m' W5 p: k9 u. ~2 p! D

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

a" S# T' Y5 v: \/ [% t

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

+ k7 ]# z6 N0 R: ?; E2 G6 Q

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

5 O4 |$ u3 g* @8 p, _

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

1 E) D/ ]* ~: J- v M: _8 y. }* G

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

* l8 J% Y* \: i/ L5 r9 y9 Q3 a5 p

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

& Z7 p) B- G* ~* e, t0 l

常用数据结构

. ^, I) V: [) R/ {3 s

l 整数

, h- F) W+ h0 {

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

5 W% I. P3 X. ]0 p+ h5 O

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

. ~' [6 G( E7 m4 c; D) p6 x

不同。

8 ], w" d }- L7 S/ q: I; d& U0 S

TABLE 2-3. Equivalent Integral Data Types

- [$ \, h. g( O7 M, R C. `

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

% Y2 S e2 b% r2 p# L, M- h

8 BYTE unsigned char UCHAR CHAR& y# T! M5 C/ F6 C7 ` 16 WORD unsigned short USHORT WCHAR SHORT

2 p$ Q& \$ V" u/ [9 M

32 DWORD unsigned long ULONG LONG

7 V4 l: A/ S2 x; O. _

32 DWORD unsigned int UINT INT

9 \( G3 Y! y0 p Q: k7 G) x

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

( b7 x0 D8 ~0 [! Y

80 TBYTE N/A

+ {- ?6 Y K# J( |! o

typedef union _LARGE_INTEGER

: w4 u% A7 n4 u

{ struct{

4 m6 C) o: N% D% V7 ~: w

ULONG LowPart;

4 h. ~/ p* {8 T) M1 t4 C

LONG HighPart;};

' P' r/ x0 B8 n% h( r0 ~9 F

LONGLONG QuadPart;

' l+ |" v) Z2 ~% o+ U4 X8 Y' A

}

1 U1 _9 x# Y/ [& U+ J3 z, B8 p

LARGE_INTEGER , * PULARGE_INTEGER ;

) a7 c6 z+ E' S9 _; `

typedef union _ULARGE_INTEGER{

+ X9 O( k- V. o4 g- i) Q( w' ^# j h

struct{

& \ [2 q1 A+ k" Q! C' W1 h) `

ULONG LowPart;

# v8 S9 c- Q0 u O1 J

ULONG HighPart;}

8 p6 \8 S% ]$ z/ ~

ULONGLONG QuadPart;

7 G& P9 \' L+ w& P0 b! {

}ULARGE_INTEGER, *PULARGE_INTEGER;

- |) K- S7 z. n& I7 K/ ~

l 字符

' [1 P0 b m+ Y! C

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

- R7 o% U0 U7 w9 T$ z! `# w! Y

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

. M; k2 V5 W! G9 L8 {. _

typedef struct _UNICODE_STRING{

5 F0 f. k+ ^6 ?1 {1 N7 A1 R

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

( j2 m: ?# E# T, ?6 z

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

3 ]# K6 A) S: p% X+ D

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

5 X0 _! D" B6 X [8 l, S# M9 Q; Q; G

typedef struct _STRING{

1 m- n; k4 g: M2 Y+ ~) @

USHORT Length;

4 p) y, A$ O5 E# R5 n! Z, \. }: }

USHORT MaximumLength;

" B7 y" L( |, o: O' m! I/ D

PCHAR Buffer;}STRING, *PSTRING;

! c! e- _$ G! o; i0 ~" G+ L# L0 p

typedef STRING ANSI_STRING, *PANSI_STRING;

1 C0 Z- S; k+ o, t) D. U6 n) m

typedef STRING OEM_STRING, *POEM_STRING;

; a+ G$ x: q V# E6 P K

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

* T- b, G w* W

RtlCopyUnicodeString()等等

7 V& i. x0 {: `7 T/ E3 k

l 结构

" j% e( F* c, m" e( @+ c/ i; ?

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

+ x5 Q- `# h! d$ v$ [

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

3 J0 |5 h) Z% Q. |0 E( }3 [2 Z

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

5 V: D( X: P$ Z

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

. p: J' z3 C, k- o1 T; W

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

: o; {: x, M# V1 y _) `! x

typedef struct _OBJECT_ATTRIBUTES

! `- k. ~9 T/ |% Y& H2 q x

{

% N# U1 K: e: T/ X3 l# d$ c ~4 Z2 `

ULONG Length;

! D1 s3 }: u2 I; b5 l' p. d

HANDLE RootDirectory;

, _% N n+ N1 O) a

PUNICODE_STRING ObjectName;

- N; z$ M2 _: |

ULONG Attributes;

/ r+ o/ |7 ]+ ~; |! }$ _/ ?9 g

PVOID SecurityDescriptor;

8 y/ M, ?. C: y/ N, d, A

PVOID SecurityQualityOfService;

( d) H0 p+ v: `1 }- n

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

* L; S, D4 e! w0 M9 z7 F

typedef struct _IO_STATUS_BLOCK

7 M: E- z' r- |; J; A1 y" a% N( K1 I

{

, e. f' C& R' Q5 e! U

NTSTATDS Status;

6 Z& r/ N2 Q' E) E- N$ W9 R

ULONG Information;

, Y8 J# @, V6 @

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

0 d+ @' A7 b4 O

typedef struct _LIST_ENTRY

: O! D6 @, o$ T% ~6 N! B

{

7 l5 ]) o6 o* X/ L$ H: D# w

Struct _LIST_ENTRY *Flink;

2 W6 v$ x' Q! {1 T& h

Struct _LIST_ENTRY *Blink;

( h% T+ k" S4 X: M. }. {

}LIST_ENTRY, *PLIST_ENTRY;

6 ~- ?* l6 ~1 z3 l' D1 t

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

) h( s4 Y6 D# ^+ ]9 D, T0 }

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

( `; n6 G0 O8 Q, g

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

/ d( v: N7 q) F0 P0 v+ n$ Y

typedef struct _CLIENT_ID

' O, Y# D- U9 L! O+ K

{ HANDLE UniqueProcess;

) ~2 R/ Z$ u% N& v) Y8 x

HANDLE UniqueThread;

! c) M; h3 b- i3 n

)CLIENT_ID, *PCLIENT_ID;

1 z/ j) p4 o# r4 V& {7 j

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

1 k4 [: o8 E# ~3 x

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

9 K& t; t! r0 q/ C

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

* }5 g2 k* ]+ r, X3 A3 J- e+ T1 R

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

; o7 P& {" v6 B, a9 R ?5 k8 m

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

7 Y/ A+ d: a) k- E

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

4 B0 M5 w/ X. t

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

- {8 h, a" I0 ^) s9 O

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

( d7 e/ ?. Y5 b' |# n+ v

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

2 ^& l9 j3 L& E2 E( L" C' B f: z

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

+ `# P' ?9 O4 ~, ^

3 }& y5 w7 D1 T2 v. c" u& c 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

5 s" g& A- J; j* s9 o

指导 # W; a# w. D2 U* l0 P/ I8 Y. R$ ~5 Z

2 x d4 n4 @4 R. d+ [, O

7 o8 J5 ^9 F! P: I# N: B2 s

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-8-14 09:20 , Processed in 0.670039 second(s), 52 queries .

回顶部