QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com 5 N. y+ \% T# s( C9 y来源:邪恶八进制 中国

0 T# V2 S$ Y& @1 n+ G/ X- F3 S

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

8 t8 W+ k; N8 [- P$ L# I

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

6 U6 K c/ A: Y7 a2 O* m) ^& ]

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

, i7 @. w* k9 ^* w9 D `

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 , `1 i: ]( Y: B

9 S6 W8 s; v1 t! z. u4 k3 ?, K

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

+ F$ L' H7 L# v+ D1 S

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

& {/ N6 C* V8 d# J2 f6 ~& K" W

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

7 ~ }* g% B6 i. ^

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

+ q) D; c) w8 s4 Y8 {" T. ^

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

& m: w/ P0 d& S4 b

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

. t& X T) V3 |% A

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

2 w s* O3 H0 i2 E6 j7 ]6 G

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

1 F# J! C3 `( E7 C! ?1 i! y

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

; p- f6 [5 P; ~% J; ~: L3 N+ ~

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

) M: F. p, [2 g

反编汇这个函数得到:

; k& L& v+ o2 s0 X1 H% M. t6 p& t

mov eax, 38h

, a& @! p% d) Z$ R

lea edx, [esp+4]

3 n3 t1 f! V" e2 L

int 2Eh

2 z0 b/ |* w4 F0 b( L+ G

ret 28h

) L3 J& E# w. Y. |+ W* B) X* ^

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

5 A+ z+ E% q& s) q* s# Y- ?

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

( p g& {, t1 j1 _

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

# I- E" C5 | G& d C( Q" D

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

) M1 y) E) X- S4 d% |

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

& q! L4 Z/ z) J

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

% V7 g; C4 C3 ]3 q3 [. o E4 ?

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

8 `6 A4 j# C/ A' v- D( w/ u/ b

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

5 x( E4 t K; K' {

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

J# o5 G6 z) _3 @' d! e; W: c

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

8 Z# N8 u6 v l+ `( G4 ^) C3 D

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

0 [/ r1 ~7 ?! k

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

E, Z( v3 U% A) g1 @! Z

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

& A" q7 e* u7 b

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

# t' U4 y9 g( Y5 Z

typedef NTPROC *PNTPROC;

( y$ q0 ~: E. G0 D, N

#define NTPROC_ sizeof (NTPROC)

3 C0 ^) \; ^7 t' d# B

typedef struct _SYSTEM_SERVICE_TABLE

6 f- W$ s/ R% q$ N4 J9 t8 m3 k. \2 f

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

6 l c7 f5 u* \: k5 ^

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

5 u* q! l- K# V5 ^

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

4 J4 k) }# K5 @& H

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

# Z8 V* ?# y$ x; a' y

) SYSTEM_SERVICE_TABLE ,

% e$ C( p) E) q+ }% e

* PSYSTEM_SERVICE_TABLE ,

2 x1 B# n( k8 I. @2 V/ Y4 u

* * PPSYSTEM_SERVICE_TABLE ;

, S' k* S% A# x, r

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

; F$ Y* S1 z5 B( z, W9 p k( g

typedef struct _SERVICE_DESCRIPTOR_TABLE

4 m, @3 c9 T: r5 ^2 y4 K- x) J

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

, f& M: z) n; s9 {

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

' }* h6 C+ u5 s, D

SYSTEM_SERVICE_TABLE Table3; // 未使用

+ L" a2 x' T7 [- `+ D2 ]

SYSTEM_SERVICE_TABLE Table4; // 未使用

4 f9 |3 x) _9 p

} SERVICE_DESCRIPTOR_TABLE ,

- {! I% T6 f$ M# T$ f' A

* PSERVICE_DESCRIPTOR_TABLE,

@9 o5 d, ^0 T; W

* PPSERVICE_DESCRIPTOR_TABLE ;

3 F0 p1 [, P4 L5 o. b" ~

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

2 F0 p- l4 \ l/ K9 n8 b* f

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

. c9 f0 y) o& ~

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

1 r0 V' n- \4 E9 u9 n3 ~- p

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

! G/ B: d4 v+ q# o/ J0 \

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

" l4 p6 {1 t3 Z A' F9 k

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

8 J! A# p, k9 O2 b( T4 @

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

/ F8 t4 P* f0 S0 X

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

5 r* t6 v8 n ?# F( t4 l

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

! F; x& c5 j- ?4 x) v! ?

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

) w0 K/ a. n1 M. m7 A ^8 q

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。 : U5 T$ Y% v1 @" [) A; D+ ] 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

0 M. s3 m' L& G7 Z2 C# b

前四行是最重要的,对应那四个SDT成员。 N8 I& n. Y6 a2 J* E" b% I 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

3 U4 w6 P) g( S- P, O

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

# w* @: K, j" K7 L

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

& u2 A7 Z% q; p# W {

KeAddSystemServiceTable$ p k6 c& L3 f! U) ~, c' ~ 此函数去填充这些位置。

+ J5 z; Q7 P9 Y# `2 d* \7 K

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

1 K- v+ m/ Z2 T: g

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

8 h: ` r" W N4 n8 O" b

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

- o1 h, Q! Z; r

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

9 o# f4 g* G! ]( @( k

映射至ntoskrnl表格,ID在

7 d1 Z3 \% h s

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

5 k) U0 j9 z2 Q6 [* a, f

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

/ W5 i5 Y* w. g

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

- A ?# R( ?$ k6 y& R$ I

STATUS_INVALID_SYSTEM_SERVICE。

$ L5 o3 e: x1 j6 @ k

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

3 ^( D8 X4 M7 s h, q. D( w

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

' M9 y; J3 ~+ Q8 D% [

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

9 d, T# J. ^5 D+ @) O

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

2 h3 O; L2 ~* L+ i4 V" H% }

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

. L: V9 E( \$ a. a+ `) r( v

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

4 U) ], E0 I3 X! N

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

: ?+ s6 l/ P: X0 h, T6 [* ~

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

* W) {$ @6 m0 y/ g) M; j% u- Z

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

- _8 E5 H3 F: C. v

- z: T; g! X1 r4 q- }! I6 y 2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

2 _/ _( U( f& q

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

6 \) g# ?; U9 [/ S' n( p

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

, a: v" ?, t. G7 E2 o8 g

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

: D0 O( H+ z8 S% N# q/ h1 G

& U ?. D; n) V0 TWindows 2000运行时库

" m# R9 ]! ~9 y

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

0 w. `2 C( {3 R6 w2 M2 Z8 J

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

: \" l$ q. x& C

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

( s# y: n" T( @/ Y% [4 L9 W

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

/ ] _* f0 q8 S: `( R9 u: T, P

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

% F. k) g! Y$ h, S9 U6 p3 g8 W

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

: t# l8 V1 P& z5 g" T

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

+ O- D5 s# @! h3 t2 q$ e. g7 c A

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

3 I0 o; r2 _; K7 h) F; w/ l

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

5 b1 c, S3 L( h, H

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

3 q) Q& _) \5 o* v8 ~, Q

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

: M- d# q7 W/ C/ R2 ~, [4 F

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

5 Y: L {+ [8 l4 _5 |* j

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

" U4 q. Z7 A7 D' ^4 H9 P2 i 8 N& p3 i4 v0 Y

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

6 N/ f q% M( F( a! b# z

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

- K! Q, ?: C9 b# n& i0 l

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

2 [+ o* D, {; q6 f: m

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

0 z% c) @! ?" z- ~ T5 j8 F. O

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

/ p6 @& V$ I6 \' `$ H$ l5 ?% i

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

, ~" v: V3 ^5 T8 V- o

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

) D* f! B8 u+ n# u# H, J! D( y

常用数据结构

) Q2 W, P; u. A! x

l 整数

/ n) A3 i- l1 q. q# g/ Y# T( H* X8 a

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

0 D, c- F9 U9 [5 d

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

1 ], g- t1 i8 C# ~. \" l

不同。

1 V. t. [8 P) {' o; a

TABLE 2-3. Equivalent Integral Data Types

: ?9 {2 |; i( Q0 N

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

' {/ l' I* }& V

8 BYTE unsigned char UCHAR CHAR $ e* J/ i4 v4 I* R16 WORD unsigned short USHORT WCHAR SHORT

8 M. F+ K7 A4 K5 }+ e, ~7 }

32 DWORD unsigned long ULONG LONG

8 U) F, K; |( g. W8 _' t" F: M

32 DWORD unsigned int UINT INT

6 S" m/ i. d& r) z9 M

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

4 b5 }6 E- f, U+ s2 W9 E( {

80 TBYTE N/A

3 p4 y7 V1 p+ R! H- H/ v8 A

typedef union _LARGE_INTEGER

. [; l9 C+ k: v0 B+ h8 S7 h/ ^+ X

{ struct{

6 V7 `" R# P( v7 Y3 j1 y9 V6 O

ULONG LowPart;

( M0 f( H1 X, F8 D

LONG HighPart;};

P9 G) e2 a* n2 w. }

LONGLONG QuadPart;

# Q" O* f( U, C' F9 r% ]7 W& K

}

1 l& d$ @. j7 J- ?! t! N& F1 ^; O

LARGE_INTEGER , * PULARGE_INTEGER ;

6 V0 v; J: t$ j' c# [. `# o

typedef union _ULARGE_INTEGER{

0 X/ T6 |: A2 ~4 Z; W/ O

struct{

+ h3 z# v: i5 V7 x) p0 y) F9 ?

ULONG LowPart;

& U: `9 O: n$ C- x k# v$ S

ULONG HighPart;}

7 m! x/ F4 j. d; z' s

ULONGLONG QuadPart;

3 T) w$ d6 u0 @' C* k

}ULARGE_INTEGER, *PULARGE_INTEGER;

1 ^6 s# {+ i! u$ K) N: Y1 }3 G) }

l 字符

( ]$ b' m: k$ Q! {) z7 Q

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

# ?9 M. ^7 R" {. G! a0 F, P

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

4 Y4 [5 p2 q9 R* H W2 G

typedef struct _UNICODE_STRING{

6 j$ c! L! \( X

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

& a l" k6 S: z4 d; G

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

: L1 q7 W7 s( i" p( c

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

4 a$ B: U/ t! B* a; F2 _) r5 }5 z0 G

typedef struct _STRING{

, K: r; a! _: K! t( r1 [2 {7 W

USHORT Length;

' Q `+ [& c! X$ j# l7 G( H

USHORT MaximumLength;

s/ ^. E }% H+ G+ _, u

PCHAR Buffer;}STRING, *PSTRING;

5 i- D. i9 r; w& F7 K$ C

typedef STRING ANSI_STRING, *PANSI_STRING;

( [; u7 \( [/ f. T4 f& D

typedef STRING OEM_STRING, *POEM_STRING;

% {7 q. k" O) r; u6 T& O* o

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

& a7 n. z4 x# U

RtlCopyUnicodeString()等等

0 E1 ?' y/ Z4 ?2 T

l 结构

9 x6 Q: G7 D1 @/ ~* _

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

' a( }' G8 F" F- N5 [/ l5 l# I" N N

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

3 @- K3 w6 ^5 i! X- w; h/ q

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

4 [+ V8 \% ?9 \8 ^) K6 Q8 G5 b6 d1 [ F. G

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

# Q8 g" p: w* d9 `

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

! V& t$ V0 Y) w& `! b

typedef struct _OBJECT_ATTRIBUTES

! A2 Y5 n" P' s. Z; i7 v5 O

{

2 @0 e C: G* t5 C$ E* O

ULONG Length;

& Q! Y2 F }3 f: \5 h ?3 o+ l/ ~

HANDLE RootDirectory;

, t0 G, x! [' F @

PUNICODE_STRING ObjectName;

6 ^, Y' M' W1 k- b( {3 G) r# \

ULONG Attributes;

+ w' F0 a/ w+ u, S, l( U: \" f

PVOID SecurityDescriptor;

/ t) W1 y" ]' W- N

PVOID SecurityQualityOfService;

# o( z" {' q3 d3 |3 }

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

/ e! C' H, Q1 U& [; [, m

typedef struct _IO_STATUS_BLOCK

( O& l& s" j& j& N8 S

{

& e4 a# N3 b- I

NTSTATDS Status;

]6 s, b& M# _# J: N, M6 @

ULONG Information;

. |6 C% Q- x: {% Q

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

6 q8 p/ j1 o$ d+ q# ]

typedef struct _LIST_ENTRY

/ ?* q5 ]2 l4 I0 a1 `

{

6 F" Z3 f1 B, [$ L# b6 c

Struct _LIST_ENTRY *Flink;

4 E3 R6 f8 {7 P

Struct _LIST_ENTRY *Blink;

, H& A8 C/ T- @9 C3 }& h0 S/ {; t

}LIST_ENTRY, *PLIST_ENTRY;

P& M- j6 s- C5 S

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

1 {; ]9 @7 ]" `5 L' e) N

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

8 i, P5 h0 V, Q4 A5 |

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

0 ~" C ]- H( n. E+ w+ P8 G7 P

typedef struct _CLIENT_ID

! k" {$ z% _( @7 N, X4 q

{ HANDLE UniqueProcess;

4 ?! Y& t0 n& N! b& A

HANDLE UniqueThread;

: N: }, W9 N/ F( T6 \3 G' M

)CLIENT_ID, *PCLIENT_ID;

" W3 _# x1 o5 t/ ^' M

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

& x8 Z+ m& d, o/ k

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

4 {: d/ S! {% G y B% p1 {

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

& G+ J" V5 D0 E. {+ \

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

: x, B& _2 M* k9 t& o& |* \% q

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

% O0 g8 ~1 U; V2 H. F

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

6 ^4 [' z+ E3 n1 H$ Q2 t* z5 l

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

V0 @4 r" G3 e# w# U/ R

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

- {% m0 W/ f6 V+ L$ x' w. Z( C' ]

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

! C2 s, c/ P) y/ s

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

; W# K8 b5 d5 J$ e3 ]5 P0 _

. l" P7 ~( }+ |; M1 k4 a8 ~4 P 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

* J0 E7 T# |! _( Q

指导 $ Q: Z* Q# R0 C1 t

) b* k e5 L8 v/ ~

4 ]1 M+ D+ B- B/ E& N

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-6-13 08:26 , Processed in 0.312354 second(s), 52 queries .

回顶部