QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com: G6 t+ L7 o: ~9 b 来源:邪恶八进制 中国

3 u7 _8 A- d7 m2 V

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

# j4 a" K4 ` @* E

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

L0 T7 t: @' X! [) X+ f' S

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

_+ w% U8 B& L j

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 ; C6 [' a/ Y0 {

# c9 G% O" T& O6 p# ~

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

6 f5 p+ G( X2 [5 y$ r6 z. E

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

+ l: w! R, o' k! x

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

& X0 }' ?" L5 ]! {+ v# v- q

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

L- ~9 q. P t" X4 z% i1 o* V

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

/ J7 n1 U9 c$ Y

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

8 l% ^; Z( X& Q) {

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

. }! P3 _* o M5 F+ X/ {1 ~5 l/ g

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

0 i& q# P) s& D) D

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

# A: _1 j4 S) Z1 d5 T) t* d2 K: Z

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

5 T/ L% Q5 I* S l9 ?+ s

反编汇这个函数得到:

' f- I7 H1 H( c) i: W

mov eax, 38h

`0 a7 l4 T' W( Y

lea edx, [esp+4]

. O8 {6 z/ K. S/ g

int 2Eh

& W i% I$ S* y" h d

ret 28h

, Y& K) N# x. k3 s- D& ~

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

; _9 @, o" j b9 u

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

7 B/ i: w0 G1 j

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

; H* i+ x, u9 D# O% L' Z& \% c

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

! O2 x6 k1 B, n

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

8 ^# v' h! v5 i

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

3 f/ }% B/ x9 B- m6 d1 ^8 w& B

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

& t4 D! @7 P' N+ E

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

, |/ T5 }: b0 {2 k1 n

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

2 {+ [, N$ f. F' Z

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

8 J! S' Z. t( ?, @7 t6 ^

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

8 U- y# h( k2 ^: |( q1 f. M

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

* R( ]- d; X3 H2 v' E

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

# A3 x' m* i9 P, G7 }

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

+ C: S4 H: J: `. a+ ^6 j* f: D( S

typedef NTPROC *PNTPROC;

6 {& c+ d$ Y7 x

#define NTPROC_ sizeof (NTPROC)

2 D# I1 V" Q- [ V

typedef struct _SYSTEM_SERVICE_TABLE

" g. X4 A" t* o/ g, w

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

# t' | G* o4 ]& P. {6 [% ^

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

4 D7 b" ?" F* t" U' f

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

7 d! [$ T/ r- g. N1 }3 f$ [4 |

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

6 s& @7 [5 g, ]' ?: k8 W

) SYSTEM_SERVICE_TABLE ,

- |) u9 J' g+ z/ a6 E: S

* PSYSTEM_SERVICE_TABLE ,

3 O6 p/ z% p& Z2 F

* * PPSYSTEM_SERVICE_TABLE ;

5 x" @# G0 f E( l

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

2 _4 h5 R- I0 H% N3 y+ {

typedef struct _SERVICE_DESCRIPTOR_TABLE

# c3 |5 b1 z+ P2 I o2 h* T

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

4 Q0 Q! t# e& K

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

! O+ y0 R5 {: Q, ?2 X3 Y1 f6 n

SYSTEM_SERVICE_TABLE Table3; // 未使用

2 w# b' C5 t4 b

SYSTEM_SERVICE_TABLE Table4; // 未使用

: y# _7 D( p3 V' u# S6 F3 B

} SERVICE_DESCRIPTOR_TABLE ,

, j2 X; }/ V3 p) I0 |1 D

* PSERVICE_DESCRIPTOR_TABLE,

( G8 i' J* T- y9 \6 D8 T2 z' E

* PPSERVICE_DESCRIPTOR_TABLE ;

. w5 E* m. ~+ y, p5 a1 S! W) B

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

$ t. m0 C1 I8 h( O/ L+ Y; T- S

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

1 B/ X$ K8 d( I0 P1 W/ J/ `: I. {

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

" q" F3 V# b: H% H# y2 z7 X# ^

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

9 U$ n8 b7 [: c! k2 v

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

) S5 @3 i6 T. `% H" t

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

: d8 p8 [* b/ V" l1 k G B5 A

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

$ K- ^2 J* ^6 F/ J* D0 A7 y

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

6 r* x I; j( J* Y# y

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

& L9 t- \) l3 F4 n4 l7 i3 W/ ~. q" I

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

# p( H& j% a. p; d* H- J* {

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。9 I7 @ m' [+ y; E4 Q3 I9 J 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

9 F7 J; d' f0 k# z( L- Y

前四行是最重要的,对应那四个SDT成员。 - v* |; v# U/ P9 [0 }; ~ 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

% I% L( d( I7 N4 l1 U, t; N

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

; U3 a u: A( h- f1 c: K e$ i

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

8 T {. T9 b+ | `% g, O. h4 E

KeAddSystemServiceTable $ [, X8 T) A! z# g9 A: l9 E- }此函数去填充这些位置。

9 L" c3 l, X5 _5 p5 ?

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

5 @ W( ?9 E6 V/ `) m0 g

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

- R; {) Z$ A9 C) a3 J

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

' N: c1 G! Y- C

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

" J7 d1 M# @8 X& G# j$ G

映射至ntoskrnl表格,ID在

- v) E& A8 G" k" ]3 ^

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

7 s/ h9 l* P2 t3 [

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

! k( @ z% {8 V! n. Z F# D- ~1 L

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

( l% `0 o2 s9 ?

STATUS_INVALID_SYSTEM_SERVICE。

5 T! a* {) c$ T

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

9 a( J; }1 o" C: {' g- c4 M

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

8 f$ ^4 K/ n/ L3 N

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

0 ?+ Q/ m: V7 U/ i0 \3 b" H! A

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

+ Z* t, V# i, K: D, R

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

, C8 H8 P/ r; n( L" p; h

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

' A A( Z! C4 C# a6 v6 [* y

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

% z* B6 ^4 H: r$ X3 \' W

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

, Z: ~% @* [: @- z* P! l

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

; \0 Y \ m' n

' j+ A3 ~7 n1 D% ^4 M( Q- c' d* o J$ U2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

3 T; y! w& m6 E3 ^4 Y

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

; }# m$ \# @# k& z# `7 m$ ^0 T

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

$ s+ M! |3 H% n3 o8 {

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

: F! w. \( T: R! ^

9 J; W( N' S5 z8 b- t- f Windows 2000运行时库

* u$ u% t- t7 w) p f$ C3 d

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

+ D! k# h6 m; m7 \

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

; h6 T4 `, x/ G6 D5 z% @

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

; j( f/ V& n6 N2 q( P* ^8 b

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

4 d" P+ J' V" N

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

# x2 B, q; t# N; r- o, Q' Y- C1 _' M

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

1 |; [; a, R c( d) B) l' s( Q! m

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

2 q* M& @% D3 w% F1 _0 ^4 L9 c. q4 w

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

5 R' c: d" i- [2 {: n% [6 Q a6 V

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

' c" s5 t7 |2 c/ j H3 g4 i/ [

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

; u% j' u9 M, [, o) Q

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

! K6 |. i, C, g% Z0 Z

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

0 X h4 n1 N( F8 D: J* h4 t5 j

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

; r$ E% M$ s: k% z 7 Q( G! o9 B- w

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

5 m- k. T4 ]7 y' x! M9 N

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

7 _+ p7 L" A% G* d% V

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

$ U/ _% l9 z( l4 r! t

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

# ^( p9 s6 _) e! W

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

9 K$ r& [) W4 R1 \. Z6 h

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

, R9 Y9 y( N" w, n- r

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

/ m# x; g! @8 o" Q B* a$ l

常用数据结构

1 C* L4 }# {7 D" @

l 整数

9 ]( |% D+ h) a( j7 h, q0 S

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

3 _6 N& W; o7 o$ n Y

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

! j% |: z- n5 x* v/ e' j* i

不同。

5 ^/ V3 a5 F; f1 ^5 U$ o+ \8 l

TABLE 2-3. Equivalent Integral Data Types

! r: t! {& _5 L7 i. u- g6 H

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

, J \) N+ [+ U3 P6 Z; S

8 BYTE unsigned char UCHAR CHAR # Q7 _3 G, f9 M( h' b: T7 o16 WORD unsigned short USHORT WCHAR SHORT

) {' A% f# b% k* d/ T/ ~

32 DWORD unsigned long ULONG LONG

. j' @9 n) i( J P0 w! n8 W, ]

32 DWORD unsigned int UINT INT

6 T7 ^4 R: T: Q. q% r! n. I% e9 A

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

# W+ C% o1 x6 h- w" c9 X

80 TBYTE N/A

" U) l. V9 E# t9 q9 |( l& V& u

typedef union _LARGE_INTEGER

$ {7 ~" E/ w! W7 K

{ struct{

% L- I' B: i! } ^

ULONG LowPart;

- b; B( b' D' m# D

LONG HighPart;};

' z4 U, g/ k* h% J

LONGLONG QuadPart;

6 C. m. O1 P7 w& D7 z7 f8 K

}

( y4 h8 W/ o' d& V# L' C

LARGE_INTEGER , * PULARGE_INTEGER ;

k0 W' j/ A X6 ?$ [4 y# d8 F

typedef union _ULARGE_INTEGER{

/ t' ^0 J0 \- n2 N

struct{

' N: U( t" A2 m1 { h& S1 T+ _' b# V

ULONG LowPart;

$ `2 |: z% K: _" C

ULONG HighPart;}

$ ~( U, R7 x% F2 _3 }4 g6 f

ULONGLONG QuadPart;

6 G% }' F2 R1 ^

}ULARGE_INTEGER, *PULARGE_INTEGER;

1 f% O2 m5 W. m( n$ l& \' H! B

l 字符

3 ~8 g3 B z5 Z5 W

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

2 U% S. P1 _" @, J: Z

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

8 K, h$ f& M( U6 `4 F3 _

typedef struct _UNICODE_STRING{

" t) g# {0 H, c" R5 U) c" U( `

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

$ G! }; A( z% b a; u

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

3 O% B& R" t% X" H# F

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

p1 U3 Y. ^/ a* G0 Z) ?

typedef struct _STRING{

: T+ q+ L/ ^! ]+ d* c# p; ~# ~

USHORT Length;

- j# x& B- m- @! Z3 B U

USHORT MaximumLength;

* R8 b2 u- [3 e* o

PCHAR Buffer;}STRING, *PSTRING;

}7 B" H4 j# g! ~

typedef STRING ANSI_STRING, *PANSI_STRING;

0 Z: U2 t- M" F* d- p% y

typedef STRING OEM_STRING, *POEM_STRING;

+ i2 t. C" _5 M. L( {" K

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

# k' l2 b2 z2 e: Y. A6 ]+ s( U

RtlCopyUnicodeString()等等

( E H1 b C; Z3 t

l 结构

2 |# _, l4 j1 I- A+ }- H

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

* k4 W0 F9 r, g9 k

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

7 x' H( s0 w: p% N- t- m8 R2 \& W0 B

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

8 O( V. C/ G9 U1 w1 P) ~

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

6 c: X/ z: s2 T* J C% D% x: d

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

: W" u/ p4 v+ C

typedef struct _OBJECT_ATTRIBUTES

" o$ G$ T7 M% D, c' c: L

{

: G1 e# ~8 }7 \1 b' {, @

ULONG Length;

- r, O" a) S. s# u: e) _# U

HANDLE RootDirectory;

E, p* g8 p5 U2 W

PUNICODE_STRING ObjectName;

5 @$ P( J$ |5 ^2 ]% m4 v; }' p

ULONG Attributes;

7 z6 r7 a. g7 f s9 B: m, B" u

PVOID SecurityDescriptor;

; u4 `; ^5 c" C4 v& {% C( j6 \& A

PVOID SecurityQualityOfService;

9 s% j5 B3 D h6 `

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

& \* u9 M6 R' P# m

typedef struct _IO_STATUS_BLOCK

# b# B* {4 a4 i

{

/ o3 }( A+ F2 b% N) `

NTSTATDS Status;

3 t- z* U4 N4 g: ^7 i* F% }2 O8 ]4 d

ULONG Information;

4 |1 h) b+ T, K6 v% @: n4 F" f; a

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

: f& m) w& G/ _. H& a7 \1 c7 Q) {

typedef struct _LIST_ENTRY

! @: Z2 H, Q1 n- r: r/ a, _4 b/ T

{

$ Z4 o8 r" m* [ f2 y, B" d

Struct _LIST_ENTRY *Flink;

9 ?; w: \; L( P( g9 @" j

Struct _LIST_ENTRY *Blink;

3 p" _) z7 w6 \7 {

}LIST_ENTRY, *PLIST_ENTRY;

* N2 b! J: Z6 [ v/ q D

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

, B3 p8 o P. i w; }7 X

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

2 n$ E& o: U/ Y

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

) }- k% X) {* E8 V* z2 L

typedef struct _CLIENT_ID

" h h4 F8 |5 x* f7 k' N `) t

{ HANDLE UniqueProcess;

3 b" \1 l+ Q9 Q; |: S& V# l# T

HANDLE UniqueThread;

$ X J: }" n7 s, c# ]1 u% M

)CLIENT_ID, *PCLIENT_ID;

) t# `+ ]0 j: M+ E: ?( n6 Y7 H

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

0 f9 m5 R8 ?& t1 U/ `$ v+ ]8 q- {

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

. d# E6 A ^8 }& `& R

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

6 X5 B: w8 w: D0 D6 c7 n

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

: P& z, V7 K7 d. n2 K8 P* D# O9 u

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

( w; M2 T+ ^7 T" Y3 _$ T, U

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

3 [7 n0 h" m$ c; M

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

1 q, H0 }% r4 L( n# A' P; n* E9 A

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

7 [# Z0 ?& {) L: s9 G0 X- e

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

6 e9 ~5 W" m# g/ Q' H) _

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

1 _. B. u/ _ m0 {% M

# M7 N( _1 o" Y) Y 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

H8 @* W3 {) W, z

指导7 p/ q! {% B: T1 s7 S

# Z3 d) @# Y4 g( ^1 q. b; Z4 X

; d2 S1 i' u- 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-10-17 08:55 , Processed in 0.434900 second(s), 52 queries .

回顶部