QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com % l, c7 M: f7 b- o2 D来源:邪恶八进制 中国

. j/ _& B/ O9 `. K

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

7 M1 [5 P; B" {3 J% G

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

$ _# X1 j. b* Q/ G0 V) O

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

. b' h- G) v# o0 B

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 1 f |/ I, f0 s: W9 d/ q {

+ P0 z2 r7 B& q; C. c9 m

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

L7 t" ]8 `- m/ Q: V- V6 j2 u

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

_' m! Z; i: Y

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

% w, ]* t9 T5 O6 _

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

) f! R7 I3 B& n

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

* Z6 {. g3 G/ ~) W

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

, V3 C- A' U, B5 |" {) @0 {

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

; `3 D; o1 U. ~5 G/ ~2 ]7 h

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

! E$ ?0 p# S7 V: h/ |9 T; R

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

4 X( ? {8 }" W# a+ c

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

! M- W. z1 a+ i# c0 m& y! f

反编汇这个函数得到:

: s7 u7 R7 K6 F" b. G

mov eax, 38h

$ G3 J4 O+ u, d# P

lea edx, [esp+4]

8 u, K% E; U4 G. w" J$ h1 \

int 2Eh

/ |/ y+ T5 a& D# N5 S5 P

ret 28h

& f8 Z0 |7 E2 D/ B

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

" D. ~9 [" W, G+ w; j

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

" [6 R0 d. \* y4 R5 B

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

. l4 u+ j% a# y' @

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

2 c# b; ] t j8 d

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

9 ?" O5 C1 I6 v2 c: Q, q

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

( k- A$ G5 h' }& m& X5 \

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

9 ^6 M: ^, n' v! M

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

0 z3 [7 r4 n& t2 C; {& @; D

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

7 W6 v, l3 P" X* _# ~

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

7 v7 Z9 h! l' M

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

) D& V7 L' k- u/ s' o. P" S

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

. \; m! E Y0 o f4 B' z

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

, l8 ~% e6 H" j6 Z

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

3 l% [" h2 _; X$ p/ t- R

typedef NTPROC *PNTPROC;

x0 k' Z$ n7 q

#define NTPROC_ sizeof (NTPROC)

- M P5 q$ J0 |4 [$ y) |

typedef struct _SYSTEM_SERVICE_TABLE

' p7 t" ]6 y4 K" J2 ~

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

, {' W) d/ {8 V5 @# w

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

$ { q9 D; V9 {4 O }/ x' e

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

- A! [9 O+ } b) A2 s% J% |6 U

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

$ h, P: }- {( C+ k( z0 `$ b

) SYSTEM_SERVICE_TABLE ,

$ U4 Q( y: G" e& U$ _5 X5 c

* PSYSTEM_SERVICE_TABLE ,

$ S; _4 R( r8 I( ], W" w- ~+ F

* * PPSYSTEM_SERVICE_TABLE ;

* G$ x Y1 X$ y% X

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

) b A6 h! @( N! u5 S

typedef struct _SERVICE_DESCRIPTOR_TABLE

% @1 k# g9 s1 r1 J

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

! p0 j$ d8 U' u4 ^9 {) ~% ?* `

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

; B: \% q. }! ~# @! \6 I: i: a. W& V

SYSTEM_SERVICE_TABLE Table3; // 未使用

8 m7 u+ H3 } |

SYSTEM_SERVICE_TABLE Table4; // 未使用

2 {7 @, Y5 ^- s, G. U

} SERVICE_DESCRIPTOR_TABLE ,

B5 k8 ~' f: Q. F

* PSERVICE_DESCRIPTOR_TABLE,

' C9 k/ k5 N# z7 x* `, N5 l' E0 t$ C

* PPSERVICE_DESCRIPTOR_TABLE ;

3 h; n) N E5 s1 p

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

/ q; n( o7 ~6 T5 r }; T

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

0 ?+ n' z' g, y/ ]5 Z7 g

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

3 o4 J3 K) T7 `$ V

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

; ~9 [4 ^( H# p6 T. N1 n5 D

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

% I$ |9 X4 p. @3 ~2 s) P

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

# y% v: ?) |" Z6 h% S! S# z1 }

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

+ p5 V# W; L0 U V

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

1 i/ s* j+ l6 b+ f S

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

% W* o2 X, f1 }

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

: t8 ]- ~; X& q% j3 g% ~, O

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。- R% M$ ?$ r6 s6 P4 Z) p 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

' _* c% u/ }/ ~

前四行是最重要的,对应那四个SDT成员。3 s' b. J* `2 e1 g- ^3 L$ ~ 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

9 w- P# Q M) [2 ]( i( P

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

* @- v# ?. w8 ^. `8 `$ M1 |

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

3 f% B8 A1 N# Q) e

KeAddSystemServiceTable . b# P/ _. P6 O* C此函数去填充这些位置。

" Z& v& A8 i# }; r/ w

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

% j( r2 q! s5 F5 h. `

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

0 k0 J m' C7 M, k2 @

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

( U: ~/ y4 U1 `8 ]

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

7 a7 F5 V, _2 H, Q$ k/ H

映射至ntoskrnl表格,ID在

, Z* e) D$ O2 B

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

2 w$ ~. E$ C" ^- F. X

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

) Q' M- Z+ ?4 h

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

1 Y! j \" L& [; o4 K* @' c* _

STATUS_INVALID_SYSTEM_SERVICE。

* n& {* w# ?; a; w3 H

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

0 F3 u4 c2 a) x# ?5 }- G7 `

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

# h0 @/ R3 z# c$ s2 o$ z

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

8 j- `, P' ^5 l `

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

# |9 e0 h$ r& {. S+ y

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

* t$ A, P8 O* u, p

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

& h( K, i3 U! C

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

, R0 Y0 q$ @" Y* I6 |

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

7 u1 {' \1 z1 i0 v. F; T" _

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

/ R% f1 r* W$ K

# V7 x, @" l7 f6 ]! ^' o2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

6 f1 [! `. i! F, R

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

4 M7 w/ Z$ _9 n8 s$ A; m# u- D

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

2 [7 G, u R9 O- L+ }0 t

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

" j' W5 D2 z' N5 {5 s

# ?- |! j' l' E4 QWindows 2000运行时库

# o" |8 T" \; M; ^' F4 r) ?

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

4 a& \5 n2 G8 w) S* o

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

1 S7 P& L# G7 J# w0 ^

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

/ o0 J l8 Z. O- w6 z0 A( e

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

e% [6 X$ N* d. {8 z% Q. t* B

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

* S; r6 e/ C- m0 Z$ ~

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

0 Y6 S! H, I% X5 D6 j9 P6 L

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

- n Y% e; A5 [. C* h( R/ C

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

: A# v3 t0 f. y+ O7 H8 Y* x

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

3 i/ a* L, {5 I4 Z

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

4 b3 w1 ]! [$ `# ?' P# E; e

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

9 Z4 U3 _2 g# P% M( n; g

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

2 r. M/ Q1 {1 W& v& I

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

% A5 b" p1 C9 X6 R / h7 s1 K" y, t0 r, n7 K

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

0 ]( w( ^ J& M9 [6 P8 I! j b( e

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

/ V+ x- @. x3 T- y+ [

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

4 ?5 I( j h" c$ Y# X9 l

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

, O6 t4 ^9 H. i! ^# N) w4 G( `

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

, |/ z/ Q" ?* u3 p/ r

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

4 W( h( h1 J" p6 G3 l' p

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

e8 @' C% [7 X3 a8 V( y4 W1 [

常用数据结构

# x+ F1 I$ K. A* ~: w

l 整数

& J8 M5 J+ Z! w P" P

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

% C& B/ E6 m/ U: y# G4 k8 P- K" k

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

( B/ ~( c- o3 Z9 e: e

不同。

9 z" a* o# V5 ?* z

TABLE 2-3. Equivalent Integral Data Types

6 G% H6 M" M3 w. D* K9 y

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

6 U) X1 f7 M+ j7 C/ b

8 BYTE unsigned char UCHAR CHAR- R4 R$ T8 G: l1 K. g 16 WORD unsigned short USHORT WCHAR SHORT

" P, ^, o0 ]3 @) K4 _4 O1 {

32 DWORD unsigned long ULONG LONG

- N9 u5 \7 Y5 G4 C# Y

32 DWORD unsigned int UINT INT

/ ^3 o% T0 G. Y) r/ @

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

: W# u2 a8 w0 {# }! b) ^

80 TBYTE N/A

6 v& ^; F( _- W5 y' q; o$ t

typedef union _LARGE_INTEGER

( o. q+ o, C) R( l* `

{ struct{

. S. R( L. k# A

ULONG LowPart;

) }5 t @% r* \) d7 n- |

LONG HighPart;};

* _3 m& {: i' u% y

LONGLONG QuadPart;

$ I1 N9 B. X1 O$ G

}

- K& L' D2 p" R3 `

LARGE_INTEGER , * PULARGE_INTEGER ;

( \. b h0 u$ c0 }' ~" w

typedef union _ULARGE_INTEGER{

8 V" ?6 V$ l" i: i E

struct{

, W( G: o; m. W' d2 x. G

ULONG LowPart;

* p' w8 I9 e8 u6 R8 E7 e

ULONG HighPart;}

: I9 `0 r$ l9 j, Y Q7 g0 v

ULONGLONG QuadPart;

4 C' W5 [, a: s! w2 T9 I

}ULARGE_INTEGER, *PULARGE_INTEGER;

; M, T: {: h. c0 {( `2 G+ i

l 字符

' g& v7 I6 S8 O: K3 q# _

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

' h( |1 e0 b$ V+ Y

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

. w1 V! h' k1 }) k7 F9 E

typedef struct _UNICODE_STRING{

* H& ?3 u V1 Q$ U7 g! e4 s

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

4 T6 J: s/ x# V0 s" A- e8 G

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

9 h6 J( Z5 ?9 y! x1 Z2 B

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

/ m' V& z3 A* I1 }/ r

typedef struct _STRING{

: Q9 I: w0 H+ N3 p3 K6 F. O$ Z4 v

USHORT Length;

/ |# y8 r6 E3 s2 D1 e7 J

USHORT MaximumLength;

9 L% ^% f5 \; ^0 Q }9 m

PCHAR Buffer;}STRING, *PSTRING;

) c+ P5 q& i' p. C8 O* W- y7 s

typedef STRING ANSI_STRING, *PANSI_STRING;

" D K/ ~( }5 l% K3 q/ ~

typedef STRING OEM_STRING, *POEM_STRING;

9 }/ A7 I- X/ K! P9 y

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

, J* s; L; n h0 C6 l

RtlCopyUnicodeString()等等

0 Y2 ?0 ^+ ~' k

l 结构

4 p, P4 ] p4 s% \4 S; C

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

: U# x4 e/ p) J* g

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

2 N ]. n; C: x- N# z6 |7 X

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

% t. I( d- A. J( i' ^% k

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

7 q2 g. ^' y# c8 s( l* o$ |

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

* @4 }; M `! J5 t/ b

typedef struct _OBJECT_ATTRIBUTES

# b) v$ } }) h" v, s1 w

{

6 O% V* I& ~! t q) B

ULONG Length;

3 U0 G2 j, A" A0 d

HANDLE RootDirectory;

( n9 r' S$ m+ [2 y0 m! N& G' m; f

PUNICODE_STRING ObjectName;

6 s8 w+ }% D4 [ h* V; K- ^; ~0 q

ULONG Attributes;

' e1 N! y3 ]0 Q. V; S" ]1 I- W

PVOID SecurityDescriptor;

/ u0 a: y1 D" w8 T

PVOID SecurityQualityOfService;

5 x0 |, M; x M; ?& k: h

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

: A& M6 V# O. a4 L# M7 R+ g$ v

typedef struct _IO_STATUS_BLOCK

7 k F& J/ |( B2 p" j, f

{

; |% X: I0 q7 N- ^- a+ `

NTSTATDS Status;

4 N4 g# s( j2 x( N$ o7 Y" Y

ULONG Information;

) [! c$ ?! l: A; f1 c4 f

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

) Z+ T' x+ ^0 L/ L: s

typedef struct _LIST_ENTRY

) x2 B/ H8 t g+ o/ C

{

: U+ K: e4 T! ^; h/ x

Struct _LIST_ENTRY *Flink;

' K" U3 v- Q! D& S0 a0 J

Struct _LIST_ENTRY *Blink;

9 U4 C$ S( {9 S8 X; u& j

}LIST_ENTRY, *PLIST_ENTRY;

7 l7 I( Q! j% B9 t5 s- a$ @$ ]9 c

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

2 ]' y0 i$ ?3 L+ H6 t

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

: E$ P3 c$ C, ?0 c& H$ M- |, v9 d

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

2 w+ ]" S. s+ U, s4 x9 w1 B7 x

typedef struct _CLIENT_ID

/ [+ p$ q4 F9 A4 P1 j. g

{ HANDLE UniqueProcess;

; J6 L7 r- H# N' M% T) `# a8 L' A

HANDLE UniqueThread;

1 F7 I) `# Y2 L4 `2 n! t

)CLIENT_ID, *PCLIENT_ID;

g4 H) I( E" n7 Q# M! Q% I& d

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

! @- h9 U6 \- o! d0 g) U" |

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

7 h _ V" n; Y6 k

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

' r9 e. ~5 [" I0 F4 L

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

1 i Y8 U. Q/ V, d, m- ~

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

# R9 H6 o, t* o( D4 {6 i

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

l0 U# F; |& K1 k8 |

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

1 I9 l) ^ {" j7 d* Y& z4 L

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

% R) }0 P" \7 i* @2 {3 ^

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

0 o, y3 P6 S/ \- n) n7 E) o

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

! R, C$ Z4 d& M) ~7 }. E' c

, I+ h1 d8 M: k8 S+ U. t3 n本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

; o) o: Q1 o% [2 c/ }% D8 X0 v

指导 * u% x; {; l: o2 H: x

% {& z+ V5 [1 H! p R! p

- g# H4 W0 U# @" z. o9 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-6-11 23:38 , Processed in 0.439962 second(s), 53 queries .

回顶部