QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com 6 u. }8 [6 ^7 s6 U" O1 D* ^来源:邪恶八进制 中国

# b' x6 ~$ G; ]' {2 F

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

( b2 h5 I# P6 \1 q% {' \6 [. x

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

+ u& q; K0 D4 ^& e$ f7 ?" x

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

X9 F5 E$ w+ O+ v; P! Z! Z

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 c; t. v5 r" c5 S) d' ^, N3 [

8 c9 J6 ]" i8 P4 H

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

. y1 r% b9 O' {& n, r" C# i, E$ w

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

5 |6 E& C, r+ }

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

3 C6 I) V0 e. L/ O0 _: b

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

+ [; b% W, j& U8 b- D9 Z

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

4 M' v+ w3 u1 g$ z0 ], q

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

# n0 z7 U- Q/ U3 T* d. o8 m$ l

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

: ^, u/ r- k) c

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

/ J2 r5 G( @" u+ I. x3 s

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

5 ?* ^# g' E1 J! | w/ i1 @( }

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

7 w q$ x' a9 c, v

反编汇这个函数得到:

+ G# v( b! X% m0 ]9 X

mov eax, 38h

# w& `8 Y7 {% O5 n+ C

lea edx, [esp+4]

8 t) F- [3 k1 i) J

int 2Eh

T: e/ {) v$ `( X

ret 28h

# `6 B# J% H! k9 [9 O+ n

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

: P5 l# A+ c3 L5 t& P

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

. b; X9 L4 |" _

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

3 H* {) N( ?! p: F' l4 y

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

; {7 R7 ^! d% a6 m/ d/ A; p, J) m+ r

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

i' m" A1 S% }( f9 Y- @# p, X4 u

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

5 h* g8 }) J n1 b# X$ ~, ?

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

7 q! I" O) P4 W: k

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

/ E: S: M4 }' {8 u+ o! G+ z2 x* q

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

! C& j7 y% U: p5 a8 |

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

# ^6 F% m3 b# Y$ x) b6 p! ?1 F

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

^6 ?; l. B( n( P/ G9 H! F: s

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

+ k" z4 L5 m- X$ v9 t: @

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

/ A+ N W7 |" }) k6 k+ n

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

- [: s; [, O& a4 L" Z

typedef NTPROC *PNTPROC;

3 ]7 q; q* r' l$ z

#define NTPROC_ sizeof (NTPROC)

" a5 H! B- @2 z; D/ Y6 S6 J

typedef struct _SYSTEM_SERVICE_TABLE

2 p! m7 D, ]+ |

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

* t" J. B- v3 F9 o2 ?8 Z

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

f/ y" i& P4 }( D6 p4 E

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

. y; |) F- r9 I. ]

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

* f+ T$ j! ?5 z3 h0 _2 H. C) A3 ]

) SYSTEM_SERVICE_TABLE ,

- _& e7 _* I3 L) B; z

* PSYSTEM_SERVICE_TABLE ,

3 @1 a8 f) T4 W1 C/ L: A7 k

* * PPSYSTEM_SERVICE_TABLE ;

1 u6 h, J. V# s$ L; }2 X: `

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

- R) n3 p6 J f( A# u# I

typedef struct _SERVICE_DESCRIPTOR_TABLE

! N3 P5 F/ C: n6 s( d

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

. \, \/ { a! v2 t9 f5 r+ t

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

5 C2 c5 q6 ~4 N# D

SYSTEM_SERVICE_TABLE Table3; // 未使用

' a) A! a. W/ d2 G k0 V$ {1 H

SYSTEM_SERVICE_TABLE Table4; // 未使用

& Q9 n9 B4 ^$ y) |6 B

} SERVICE_DESCRIPTOR_TABLE ,

% H8 I6 C$ ^# O' w6 Z8 d0 b

* PSERVICE_DESCRIPTOR_TABLE,

+ ?+ [& x1 G7 c8 N9 \* o6 L0 N

* PPSERVICE_DESCRIPTOR_TABLE ;

) C) C7 M0 X* g! x: I, k* F

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

4 l: V+ a ~ K

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

$ ?$ Q7 T6 _# U4 L' A( T) J

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

2 F7 S: p5 k. e

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

/ z5 P: y! a9 f/ t1 D1 h) ~3 j$ [8 b& ]

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

+ D( q8 ] z8 C

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

+ l0 y) e3 g6 T5 v9 h' B% x

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

% R! n' B6 k1 ~3 k% y3 f

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

6 ?# Q7 `- [8 R/ `; N

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

& t, {3 Y3 T) S5 W# F2 P, b+ b

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

( H: Q& g7 b9 d/ E" q6 C

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。 , y2 } `' v! t& m" y 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

' h1 P# q. j& a9 [" P6 `

前四行是最重要的,对应那四个SDT成员。5 R9 R; w' Y+ Y, c, j* E M 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

4 E p- D% Y B! i$ U2 j

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

/ q4 [# r$ j' t' c/ n

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

& L2 N; L; R4 ^0 N8 T: u

KeAddSystemServiceTable: z% T: B) |1 `7 p* j& p! y6 [ 此函数去填充这些位置。

$ W$ {& |; I' z% S E

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

5 L: H) F* o4 x4 J# i' G

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

* k; G4 G" X& W- A5 u

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

. S/ X, V! p q8 i6 x; B. I6 V4 j

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

8 ~$ z$ m' R5 l5 M" }0 J: O: ^0 W! Y

映射至ntoskrnl表格,ID在

8 p) `0 p8 k4 y1 d

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

8 N( }) J" X( q2 I6 W+ k% o; R6 P

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

$ {8 q* r3 B# \. t! I. E. b

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

& j+ d! A, P2 m6 C( S' K

STATUS_INVALID_SYSTEM_SERVICE。

: }# c- e( r; {+ w8 H, |0 P# D5 B

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

0 f8 {8 m% C- c% X _ d

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

/ u5 G5 M4 x- b7 H

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

' {3 m! D2 v- X9 x

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

* } U2 F! ~: s) f3 j, ^4 j) [5 i/ S! y

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

3 X P3 F5 s' d9 }1 H3 R3 s. ]

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

9 [( M0 X9 I, W8 H+ {9 V

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

8 k0 e2 u! f9 o- ~

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

! ~# Q: D/ r4 _' }1 ]7 x' |

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

+ Z. S9 Q' D8 W1 s6 A

0 u- Q' Q7 t" |: Z 2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

9 ?& B: x8 A8 Q

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

7 |0 |4 z& f l; K) Q

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

/ [ d) ]. g1 J9 ?2 R" @2 S5 E

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

. a/ Q2 T! ?1 K7 I, E5 H

" {4 M$ i) z+ D# f% d Windows 2000运行时库

4 A: O/ j; R/ H/ O1 o% b

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

8 ?) h7 t3 d. `8 e' @

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

, l3 P! k( g1 t& m/ j

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

! K X4 G! M& H& T& U

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

; y, i6 L5 U8 n3 F( Q4 \8 w/ w

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

9 M( w8 d0 x1 C

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

6 U! t6 v& x- q1 V

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

8 `) g) y3 l" E5 t4 h3 A

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

& G6 a. B& ~5 O5 j

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

# e+ X) h) Z/ q; ^

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

: J- g( ^& v; ]

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

8 ?8 F' r' J$ o

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

# O6 t6 T9 m, d( b

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

: L6 p0 {$ S. h2 ? ; `/ g- |6 p# S8 A5 g

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

* @$ {7 W9 f) [, E3 Q1 V

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

4 m6 a" t: ]' b" K( Y8 t# L

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

/ h! V! H7 j4 Z4 L' }

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

& `' E1 D) z f; L8 L, r9 ]

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

, t1 N. @/ |" g; N: F

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

# S5 ^& ~$ V/ P8 i

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

( l, f. d' }9 I! H* U

常用数据结构

2 g$ s2 ~: b4 U5 Y+ R" E

l 整数

9 Q$ r% q3 k/ p# E& ^# m

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

Z3 i0 X- W+ v7 E. S0 u$ M

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

; g2 h2 n* ^4 ^( a4 }% y R

不同。

! J4 p5 I0 C" D( E/ A* ?- I: l

TABLE 2-3. Equivalent Integral Data Types

: Z$ S: w* G( m. g% p/ {' k# I& ?6 u& A

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

& K& v$ y5 ?, m: R J

8 BYTE unsigned char UCHAR CHAR+ H q8 h; ], D! g) c+ S: x, E4 { 16 WORD unsigned short USHORT WCHAR SHORT

# G$ y% `" j$ ]: A. x/ Y, C

32 DWORD unsigned long ULONG LONG

! _# N' }: W+ x( v

32 DWORD unsigned int UINT INT

. c2 w3 f3 D* k' S" o% z

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

! `3 g5 [, H, v# O4 _

80 TBYTE N/A

$ D" N/ l$ Y: g) X

typedef union _LARGE_INTEGER

{- {5 p# r3 v/ d, l8 w

{ struct{

* J# q% y7 s: _$ }

ULONG LowPart;

# S) V( [8 R: D; M; R. J8 E" V" U

LONG HighPart;};

$ S! }" l: W( I

LONGLONG QuadPart;

( w; o+ V5 W: h* R1 @

}

6 p" N; r6 E9 S

LARGE_INTEGER , * PULARGE_INTEGER ;

' K; W8 A: s$ g0 P, k* v- l

typedef union _ULARGE_INTEGER{

- ~) Q& M* g, h# D

struct{

, y _) D) S/ P" \0 Q

ULONG LowPart;

+ C6 c) Q; x9 B9 x) h( G

ULONG HighPart;}

3 M& f! p, E' E1 w9 a! O

ULONGLONG QuadPart;

' R$ Q M& J; W/ i' E9 P

}ULARGE_INTEGER, *PULARGE_INTEGER;

' I, ?' Z. ], ]0 l: Y+ T& P* \

l 字符

- c- {2 W3 v( _" d( C1 i

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

4 N+ ?; b+ {4 i. A

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

0 K/ ?( v) c% b7 X( c! Z$ {

typedef struct _UNICODE_STRING{

' f8 X& E y7 a+ U5 }6 b7 Y6 f$ K

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

8 {" R; g) n/ N' K" `- x

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

1 U/ X- [8 R$ }" @# V% ~

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

2 x9 c( b9 f' [$ H5 s) N) K$ M

typedef struct _STRING{

& z4 d1 J5 e' `

USHORT Length;

: a; ^6 A5 Y% N2 z5 R+ s, q; \( q

USHORT MaximumLength;

2 Z0 L; n- W8 e5 ]% K6 |4 ]

PCHAR Buffer;}STRING, *PSTRING;

' N" [9 X9 m R0 ]

typedef STRING ANSI_STRING, *PANSI_STRING;

3 G6 @! Z0 U# A" r( D

typedef STRING OEM_STRING, *POEM_STRING;

) F& C& ], t+ Y0 y' Z9 b

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

" M7 V# u/ ^5 e2 N+ |% X

RtlCopyUnicodeString()等等

; ]* w2 o3 B' U$ Z* R

l 结构

" c1 V6 f/ X# Q, K+ {/ l% t u

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

) _9 p8 W6 z( @# Y Z; B

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

9 [$ ]% E, G* D, Y! \' p) |) g

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

, ~/ V; G( ]( ?+ E

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

' G2 i0 n7 S" ^* }- A

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

- P9 C! Q& ? N% b

typedef struct _OBJECT_ATTRIBUTES

& ~7 E% U: ?1 ~1 y2 J

{

. M( e# q$ Y7 P' T( H! j8 V+ W0 {4 a

ULONG Length;

9 D" v& y6 n- p( I) U1 o/ U

HANDLE RootDirectory;

! a: {: n% k* d( U+ [, \

PUNICODE_STRING ObjectName;

3 }) {, k. F/ f, d( \6 m

ULONG Attributes;

- e0 U! \$ ~" |- ~5 W

PVOID SecurityDescriptor;

6 d: j. @4 Y& Y

PVOID SecurityQualityOfService;

9 N; k, m+ I. u" \$ y9 ]

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

2 v1 V$ X2 c" T$ M8 @* s A

typedef struct _IO_STATUS_BLOCK

- \) o" D, S9 y! N6 |4 K8 W/ n, ^

{

5 x5 K' P6 `; }. T, ?% U* U( U) Z

NTSTATDS Status;

) v, l4 X8 h8 z

ULONG Information;

7 G! I/ O& ]1 a. G

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

R# j, @) v( @, U+ v

typedef struct _LIST_ENTRY

# n; l% p' K* H8 d" C3 c

{

. T1 p, ~; Z/ ?9 g# J

Struct _LIST_ENTRY *Flink;

1 y$ S p+ i9 c! e

Struct _LIST_ENTRY *Blink;

% O% J2 H1 s; ?( X9 U

}LIST_ENTRY, *PLIST_ENTRY;

1 }, d' u( F# r# u) w* I* g

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

" C, W; n( f; L( ]# h8 u

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

+ c. ~" m/ V) c

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

1 c. S/ Y& d8 }# E% Z3 v7 G

typedef struct _CLIENT_ID

% @# [2 v/ N. N+ G; o. N

{ HANDLE UniqueProcess;

, T) j2 w5 j! L: ?" x

HANDLE UniqueThread;

4 k: C* B, N: n B) o' a! ]4 p

)CLIENT_ID, *PCLIENT_ID;

8 B6 z0 x3 j9 u M7 \! ~+ C

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

% V; j. M/ r f. S" q( P( D

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

+ g+ U% D- X7 U& N$ P7 ?

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

. e5 P, n: a% n, ^

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

! w8 A) j/ |. H3 g: s

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

6 Y5 U$ y$ |5 R5 ?4 | t$ y

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

; }. I' n' o6 c9 W4 H8 @

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

5 U7 g' S" m7 F: T" G0 y

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

0 O% V2 w4 F1 V, u6 {* c

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

9 n) w/ d2 ^1 M) @8 e

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

% Y5 ~# x8 S) P0 P

% y' N3 J. U9 x* _- V0 E+ [/ ^8 W 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

* X L S% s5 l8 N" E: i

指导) s7 ]* K% V2 ~! \) V I

7 G$ c4 ]* E$ w* N: a3 A: F

1 c- p/ ]# N( z

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-4-23 06:52 , Processed in 0.336753 second(s), 52 queries .

回顶部