QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com' y$ D4 U/ p: G5 B6 A; Z 来源:邪恶八进制 中国

& z. n8 S: P s& V% A- p

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

- M; G- f: l) p, _

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

# @( k; [. ~; `' h9 v

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

# Z! s8 v {- Q( R9 ?! q" C9 u

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 1 B1 g4 }6 Y% r+ {: V" v' r& W

& @+ d% d' _# B

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

$ n* V" T( T: ^* o" N" g1 H

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

' W* C5 H9 r$ _; C* }+ @0 v$ @6 H; p" [

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

( i) E9 p3 a/ C& r

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

, l9 q% s' a& [! A8 q5 ?

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

: W# ~( v; A$ w$ e

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

- h# m" _/ V6 P m `

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

) z+ X- k6 ]7 X; c

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

5 Y2 j4 Y! C/ a0 U

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

p' c8 d8 U8 R8 `6 I

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

: S( r8 y4 V# O& Z( G7 ~! _4 d

反编汇这个函数得到:

0 z1 P8 y, |/ ~

mov eax, 38h

2 A4 X9 j& \8 c7 Q0 i( X' A4 h

lea edx, [esp+4]

( r ]: u9 T' G7 k

int 2Eh

) D4 y& Z* P0 k+ W" r) }

ret 28h

) k0 }3 P7 l' H. [

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

s# U" d7 o- H

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

0 Q2 F5 E( O. y) ]0 j2 R7 T7 @6 r

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

( H/ {6 b. S; N- `, {& ?

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

$ p2 W' ~+ D! i0 \$ {& J: H

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

& N# p4 t q% P! B6 C8 `; l

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

% u, p5 e0 ^ u+ i' w2 C) j

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

& p% F2 G, h$ P7 p* R* s' [

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

( c+ S2 _; f7 S* g

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

3 s6 O. l$ X1 m

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

# g1 k" c# }7 |$ R, p! D+ o

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

. r0 ?8 r5 J' O) H

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

0 N+ P1 d) f u2 o) |9 P5 H$ v1 U

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

+ a% K- ]$ d* u: Y! Z7 |, |

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

0 n; K" Z# Z y" j

typedef NTPROC *PNTPROC;

* u- p3 ^, C1 ~! A1 q6 i3 s

#define NTPROC_ sizeof (NTPROC)

3 \% W/ k Q/ w7 w: l; x) a) x

typedef struct _SYSTEM_SERVICE_TABLE

& r' L* }# R" V+ H' {' ?2 `2 N8 N

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

$ K" k+ S( ^, M) C

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

6 J/ _% v* L3 x5 O, d$ i3 m

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

4 L" q" }; d4 I" k

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

/ k* h. b/ E5 }7 K# t+ Y3 l

) SYSTEM_SERVICE_TABLE ,

1 J, |* J9 k# \- [# W( m$ s

* PSYSTEM_SERVICE_TABLE ,

/ ?4 V8 u5 L2 I% c5 \( o

* * PPSYSTEM_SERVICE_TABLE ;

' N; B) f9 Z) I- f ?

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

4 c; F; z" c+ }- M

typedef struct _SERVICE_DESCRIPTOR_TABLE

$ Y5 B c# D" H: Y( }* r

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

+ d4 {: ~& ]. [7 F% k$ o9 \

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

* `' q" T2 k; t- t3 l$ U

SYSTEM_SERVICE_TABLE Table3; // 未使用

i% B2 t2 T a8 w2 j3 M# q

SYSTEM_SERVICE_TABLE Table4; // 未使用

$ w5 x2 i- ~) y! ]: m4 F3 ]

} SERVICE_DESCRIPTOR_TABLE ,

8 I/ k- R" ^$ \' B' ~* V5 ?6 m

* PSERVICE_DESCRIPTOR_TABLE,

! U' q6 [ ~! p: D) x, I- v! \

* PPSERVICE_DESCRIPTOR_TABLE ;

, T/ r2 _' l) `8 S$ E2 K" ]

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

3 D3 r3 q6 ~) M. e! e2 r

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

$ L4 I0 R' l' i% |6 K" I

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

* g0 ~7 P; P' b2 F

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

A" F% P6 |$ h! F. a

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

2 w+ W5 }' `$ i* N' g. A! P8 M3 r6 O

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

' @6 _" i. q2 K a, ]) p/ Z! |' \

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

& Q, W( l# w" v7 l; X

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

2 _/ V# d6 Q9 j. G

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

4 J4 W- A1 t1 q. w; d5 W6 R6 }. _

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

2 d: k: l, o! z! P

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。) Y! F3 Z; P* n# Y1 b6 | 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

! {2 U8 c6 B m: S5 o

前四行是最重要的,对应那四个SDT成员。! ?8 i9 l9 `+ t 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

0 X; q) A7 t2 k

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

# K7 q& H# L4 L7 a

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

$ B" T- ?$ z& [

KeAddSystemServiceTable+ D1 g0 o' p/ B1 ^4 O 此函数去填充这些位置。

$ C# |8 w& Y( x: h" |& G5 `3 K, l

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

' J- w7 A3 H8 R/ L# _# }9 J) Z6 L+ j

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

5 f! F: w0 w( f$ B

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

6 t0 K2 U. R o2 ^2 H T$ _& P

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

4 S1 X+ i# e' E& e) e8 }, M

映射至ntoskrnl表格,ID在

8 g0 I _( X" i+ L3 m# s

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

* O7 N: C4 {2 b% @4 R

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

; }1 H- P! f6 o. g. }& s- X

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

$ [: f/ B2 W2 }. o. ], f' c

STATUS_INVALID_SYSTEM_SERVICE。

; _( x; [& i$ T4 g7 a

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

+ `# H! D0 ~! f" T0 m9 [

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

& R; v/ u/ J. E/ r+ z7 I

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

9 O- l! ~. f) N- {

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

; b. |% k% l7 \

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

4 V2 o) \( |" `) `# l

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

4 q, l. S$ o! B3 u0 b) N

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

6 a" S7 n" f- n/ _# N$ T# e

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

' A$ C9 E. F! g5 F% e2 [

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

9 H% \" J" U; ?7 x: [& W

, t/ K9 V m! S/ O; |2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

5 J1 v" }7 ?. z! k9 |

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

' z2 k5 W. o s: q9 E0 M

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

: I% `6 @2 L! J4 `' e5 U

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

) G% V, p, |" a1 G

8 h( H2 ?; y1 c Windows 2000运行时库

4 E5 ~/ h# c" i$ h4 b0 J

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

' y$ w7 T6 c% R$ ?

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

5 ? R: q% `* e% X7 ^+ R. {2 L

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

" P e. Y; D! W6 W% C# E7 d0 i

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

. @; o. i; ` }* g* e5 d9 D

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

3 r$ V4 T9 Y) U5 D% m: w% X

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

* ^" v' {( \! Y1 ?4 C$ J

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

, @0 c5 D$ u, I0 {! K

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

0 ~( r. l' p, s: |

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

' p7 c1 r: F' O' C( C

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

9 ?9 y, V* _% ~) ^& o5 G8 y7 n

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

' A, h0 O% S/ B$ c3 w3 ^* {

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

# _: G* P3 ^% o: I

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

/ r+ ~8 t" Q e( g1 g# n! r * A& n1 Y5 w' k5 E

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

0 i5 a7 m& K# @

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

; x. B3 X2 l( t$ y: B+ J, M) F

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

) z) |/ t; [% C$ {% f2 N' l. H5 C

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

2 Z! ]9 v7 p) g0 Q% |1 B5 w$ B: w

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

5 _6 d% f x! A" T; o) r

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

, t3 d+ \- d! _

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

0 j0 J0 q5 V5 y

常用数据结构

, V, r5 _6 b6 N6 }# _/ e

l 整数

/ {% D9 t' s. {+ e# {5 O

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

Z# @- W8 P1 }% D

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

: J5 S! S4 S3 D$ k+ f# ?# z9 {

不同。

1 X; s7 F; y7 D5 o. _' @

TABLE 2-3. Equivalent Integral Data Types

9 X6 f- t8 ^ {! k' z& v

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

' { E" R4 K6 k1 D3 N7 R

8 BYTE unsigned char UCHAR CHAR2 O: \) R0 ^& X* L8 T$ k 16 WORD unsigned short USHORT WCHAR SHORT

5 d6 P; S# S4 ^) O* ~5 h- G0 }

32 DWORD unsigned long ULONG LONG

u& X! j t" `2 j# {+ G, q

32 DWORD unsigned int UINT INT

, h& f k* n7 s6 Q5 ?

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

: o: Q4 o& i$ K" s% R, x

80 TBYTE N/A

, u. H# B! Q; q F: H

typedef union _LARGE_INTEGER

7 \+ p3 l. C( C3 c& H6 [

{ struct{

. o" [) e% H6 V9 \) {/ I( {

ULONG LowPart;

; a Q6 X5 g( Q, g6 T

LONG HighPart;};

: c; ^) H& A+ u9 c

LONGLONG QuadPart;

: `& ]# ?4 A, d! e

}

' ^6 w) k7 V( C. _, J7 u

LARGE_INTEGER , * PULARGE_INTEGER ;

. b8 R" j) W' l- t( ^

typedef union _ULARGE_INTEGER{

' W* i. P% U$ i4 G0 U; H1 E. E

struct{

6 P G" c- R3 l' C5 q1 P% k

ULONG LowPart;

3 g/ P9 ~; m! n7 r' ~

ULONG HighPart;}

8 _/ j8 x0 |! Z

ULONGLONG QuadPart;

3 m5 M3 P- f! z

}ULARGE_INTEGER, *PULARGE_INTEGER;

0 e) v' P- J( k2 z! J1 L

l 字符

# i. p2 Y; @3 m' P

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

. y! s$ v, T( Z, F1 F; P- C$ b

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

) {9 U( V3 \' H( y6 q/ ]$ O$ j

typedef struct _UNICODE_STRING{

. t( O* w1 Q3 u- S0 F4 f+ z

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

5 d9 S5 y! y* l) ]. V' J

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

' Z: _' u6 \) M4 _% H f$ q

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

' Z3 p. P- c4 }

typedef struct _STRING{

% ~5 D+ w( X. Q2 ~, o

USHORT Length;

; T5 @& j, V6 [; |1 q

USHORT MaximumLength;

2 ^' [, x) n9 r: t; P

PCHAR Buffer;}STRING, *PSTRING;

& d: F9 G) [* e3 d. K

typedef STRING ANSI_STRING, *PANSI_STRING;

' U! h6 B6 V& ?& @: r& m

typedef STRING OEM_STRING, *POEM_STRING;

; \+ |/ B8 G+ r- o5 X7 ~

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

7 }, f8 H- |) _# r4 A$ y

RtlCopyUnicodeString()等等

1 }4 ~& [% _0 R' H# g V2 g

l 结构

9 {2 u" ^6 h8 q

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

$ H0 C) R* s) S6 Q9 S$ x; `

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

" e0 `/ `. L# R# G1 v

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

( w& `# d- ]. W/ [

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

- h# e0 T. @/ n6 T; W- L

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

* a" e& f& W3 n" m7 I

typedef struct _OBJECT_ATTRIBUTES

! v/ A- a% R' C4 w+ H7 X# w! g

{

% y: x; u2 U7 j$ }5 D

ULONG Length;

! ]- ?+ e# o4 M" L* e+ E( N

HANDLE RootDirectory;

, v% d' r* L( m

PUNICODE_STRING ObjectName;

+ C7 e, J. P' Q4 f

ULONG Attributes;

) V& C& a& i2 {! W% `# ~) u3 l

PVOID SecurityDescriptor;

3 K* B5 |3 i& o0 ]: _6 Q

PVOID SecurityQualityOfService;

5 z- ^" V* v8 n# y8 l" C

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

( h& _1 q* G% Y' e1 F

typedef struct _IO_STATUS_BLOCK

4 G8 |; O0 }6 H* A

{

+ y& I: h: _0 V% b

NTSTATDS Status;

" O9 Q! |& w' P' Y" q

ULONG Information;

4 [# w q5 q: z: x c5 e6 g6 t

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

3 f b: C9 B& e9 S* E4 i

typedef struct _LIST_ENTRY

- P+ a+ C- A1 _9 F; a

{

! C: ]/ ?" L! L' M' W( Y/ a

Struct _LIST_ENTRY *Flink;

& X! j+ R6 A+ J6 E

Struct _LIST_ENTRY *Blink;

7 ?1 L6 W, d) w5 P7 }4 ^

}LIST_ENTRY, *PLIST_ENTRY;

* L( J9 j7 O: | s1 J- S- Y

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

, V. ^- X( ^* Y8 J- @

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

9 V: Z- h& ]" b# Y9 u

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

/ x5 f) W$ n1 c+ r! D: b+ ]& F8 K( ?

typedef struct _CLIENT_ID

0 B" x! w6 w9 g5 K* W, E% J/ H: G6 J! W

{ HANDLE UniqueProcess;

- }9 u5 S: ?7 m$ F5 s

HANDLE UniqueThread;

* }4 n2 G" p+ s0 A

)CLIENT_ID, *PCLIENT_ID;

7 V1 C5 g Q- b3 K

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

# k( E+ Z+ v$ V8 n+ u* r

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

, n3 [7 K1 y$ }. @. ]$ z

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

1 N8 ^& Z j/ r1 a7 [" J

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

) M7 d$ X" `) W; V+ O

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

, B/ J# S% t4 [; }0 A

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

+ \3 H4 }; `6 l7 x% N

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

6 B. W9 P+ Z% A6 @+ x$ \1 v' [

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

* ~& X' z6 F, n& J& s1 \& \% e

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

* Z9 P6 s& s6 }0 \4 z

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

P, H6 m7 h0 O& U9 A$ a2 F

* I' Y1 J+ Z+ K3 M+ ?0 _" ?; @ 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

( J7 a; H7 \* ` `

指导& Y" P* V: q0 X, r: _

5 x' `( ~1 F* U9 e

" j1 k$ w% y7 |) d: G1 b+ }8 m& 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-6-10 23:34 , Processed in 0.587733 second(s), 51 queries .

回顶部