QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com) Z. ^" s2 S4 v* H/ d7 I' D' f 来源:邪恶八进制 中国

/ [, [% ?8 b' b5 t7 J2 s( E

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

4 z: I M- e& h' w

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

t2 [6 \" S0 J g5 H1 |+ H5 G% r% A

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

" G2 v9 {" T/ }! a$ G7 `

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 6 ^/ v `9 G. P5 D- f# X

" s; ~& }% `" d$ w; Z+ U

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

+ P2 @5 A2 i" L! i+ ]9 b

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

* r: ~7 j: {0 m' z

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

* J* T) H6 N) F" W# b

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

! I+ j1 C; w9 ?

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

$ E) x0 L/ U% ?' J

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

" x' v4 j! Z- q, k

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

( t, p0 D# a0 W* R3 P+ f, ~5 p+ x9 P

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

) d/ N: d/ P% z' E8 o) `3 \

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

# r0 }! D$ F2 |" U

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

2 J. L. q) [3 J# s0 | M* ?# m: R

反编汇这个函数得到:

- A' n% r! _1 o( F$ R

mov eax, 38h

, }) S8 V2 B% \" g) n9 V

lea edx, [esp+4]

5 b$ w" s6 ]. s, Z

int 2Eh

! l9 o# e4 K O) q& f

ret 28h

5 B: d& L7 h% H. k K" t3 b7 ~

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

+ b: r( Z" L" N1 }

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

- k$ o$ V1 v9 B( Y

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

5 w; d7 m Q! }( |7 m8 H

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

z4 i" F1 t: v# {6 U$ u3 W

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

& ^1 e2 P7 `5 P

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

( |; F! I5 x( H) k! v, G% d

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

# \, X1 T& A P3 h" P6 C( }

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

( {/ J( }# \4 _7 r' E) ` C! g

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

( J) |8 h3 c3 L/ t |& Q1 F

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

" ^' K! N& v7 v" J7 Z2 {

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

q, P3 E, h* l. l

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

5 h9 }9 W% `# J1 q* w. n0 Q

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

5 ]5 X0 I( {! a; I

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

4 @+ P% V( y: v5 T1 J" }8 N

typedef NTPROC *PNTPROC;

u% y+ G" E2 _& s( b0 ?

#define NTPROC_ sizeof (NTPROC)

% t; u- a# z) U1 L. i% ]2 k7 t

typedef struct _SYSTEM_SERVICE_TABLE

+ l) |, W1 m6 l- L! X& R6 E

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

) O0 N' U2 ^+ h' _, R

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

6 ?; ?* i6 k/ b; \

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

0 |( }' ?. A/ f W) _! S% V

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

5 c* @% X3 D7 F' ]6 u

) SYSTEM_SERVICE_TABLE ,

1 O& B3 ?9 r# D! L9 ^- p

* PSYSTEM_SERVICE_TABLE ,

, o/ R1 g8 R) z6 J5 W0 \0 X8 n

* * PPSYSTEM_SERVICE_TABLE ;

3 G* S5 L5 E' d4 Z* G8 y1 K9 s% Y

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

, n4 @ J% T. h

typedef struct _SERVICE_DESCRIPTOR_TABLE

' O* e! s& a; Q

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

2 g0 r/ ]2 K3 C! @* G0 v

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

# ]) t6 H4 G/ L0 O

SYSTEM_SERVICE_TABLE Table3; // 未使用

; C1 z' Z! G1 j3 Z

SYSTEM_SERVICE_TABLE Table4; // 未使用

( c1 m1 `4 _, x2 K

} SERVICE_DESCRIPTOR_TABLE ,

5 W# Y2 I) b6 M4 v! P7 w9 C% G0 d2 g

* PSERVICE_DESCRIPTOR_TABLE,

6 d& ]# D$ K9 ^

* PPSERVICE_DESCRIPTOR_TABLE ;

5 S% d3 ]! s5 d

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

0 Z3 j0 B1 @! _, T. p1 H

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

3 ]. U) X! ]2 m5 ]- s' s- J a" v

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

" }# Z8 I6 O* W) v2 w+ B: p! s- ^

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

# f! |9 g* v% e: {4 D1 V& }* @: ~

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

* h _+ v) y. {: p: F3 `! G

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

% B B# I; D: Q4 O' U! F, S

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

4 R2 Y9 t( z0 b, W# y

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

) r. W. e) W7 F* e# ~

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

9 [' ^" k' e) ~5 a

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

! }2 U* C8 I+ `9 D

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。 / Q- I" o+ U5 O; t, Z) z 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

; |+ [8 u f% c2 Y' R$ M3 Z

前四行是最重要的,对应那四个SDT成员。 3 \ B. K9 p1 r" x3 q 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

4 f M# g& a# M e- Y- Z$ z0 e

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

% T" K( {: c' n+ r* y

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

( o4 N0 l6 T+ H

KeAddSystemServiceTable * w2 g1 c" E5 T4 X8 d, x此函数去填充这些位置。

6 Y; m x6 K2 y1 @

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

N- E; |8 a' u9 n

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

! }: u$ \; r3 w0 X3 q8 |' o$ Q

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

2 S# X/ l( _+ E$ b7 ~+ P

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

e4 w4 C4 ~+ C: Q. s% J

映射至ntoskrnl表格,ID在

2 c% J! c6 V; j X6 \; D& O: ^) C

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

. C/ M; O" `" n, l5 z

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

& O4 z1 a1 |! w& a# d

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

7 ^( g9 l& n0 k* }

STATUS_INVALID_SYSTEM_SERVICE。

3 u$ G/ A& }+ w

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

* p& B' a. G& H3 g! c) B

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

0 ?. t+ g( o. L, V

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

5 w( J6 R+ ]0 I6 x/ J

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

/ p: ?$ {2 I2 L+ _1 `4 B: {6 R4 b

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

" c/ m. y' |8 X# p

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

y8 s% f3 S% P

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

( P" N" d+ G, S0 y" }3 d/ }

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

/ ^$ c2 r0 b$ u2 r6 E8 B

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

! h' g9 p; s5 k. p6 _* i2 [7 A u

- G) l% j( l$ @6 R 2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

" E/ S% C) s1 X* N0 [; D

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

1 c u& y7 V# t1 a- z) C

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

& W$ i' H6 G3 X" F+ l& |- j

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

8 O s: @/ o5 K/ {

! S: q0 I/ t2 f2 U9 R$ m3 y3 T# Z Windows 2000运行时库

9 X+ `/ @7 V, g$ S7 I, k1 g

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

" _9 R- O4 D( g' Y+ f/ Z

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

/ O' [ W( z5 G% S

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

! v* n0 K( ]5 Y

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

/ l3 E. m( v2 ? @) W9 i

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

: {) W1 \& V+ ?6 W

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

. e4 b9 O6 k3 u3 `

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

& f7 F" q5 |' R" r- Y1 @

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

2 M+ D$ E4 R5 D. o6 r

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

2 J/ \* w1 V4 S/ i& Q4 U

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

4 [# C$ f) e; d4 v6 K# u

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

/ D1 I, d+ Q, ]$ o" ]) ~4 |$ Y& G" N

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

+ Z8 d2 w* W1 t C4 o1 {

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

& \ h: B2 y/ ~9 X6 D" d8 P . Y! D& y# H1 s. C; x6 X

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

, L' r* T- h* U0 N0 L/ u2 h1 `

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

$ {& q0 l; `, Q# g: ?8 Q/ [

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

5 _ [, F+ \ r O) Q5 O3 z# @

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

5 j% W+ [ H& E( \% ?: B

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

1 M/ Q. n. P8 a6 n- ^, z# ]( x

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

! e z9 L/ j7 d* _2 _

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

8 O2 |5 j+ f M2 ?8 U

常用数据结构

; S; u6 W/ p) A4 v( J! F8 d I1 D

l 整数

5 h, N1 O) R! g6 G v# i1 i

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

$ J1 ^$ V* H H

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

) v. j% c! ~1 w4 P+ }4 a+ c2 Y

不同。

2 d+ s) R, j/ C( `5 V. q' g ^

TABLE 2-3. Equivalent Integral Data Types

; G2 b" A- b/ ?( Y5 W! M

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

" K& j( V. Z- o8 r6 y6 H

8 BYTE unsigned char UCHAR CHAR0 B- |- z0 t# j2 d, C* Q5 @) e 16 WORD unsigned short USHORT WCHAR SHORT

( p ^2 {; v$ o6 e, }; W3 H4 G

32 DWORD unsigned long ULONG LONG

4 B: S/ X M. f, e4 m1 Q/ w4 C0 G& j$ W

32 DWORD unsigned int UINT INT

- W( Q. } B- P7 _) D

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

. i2 k4 h+ ^- M+ I" G0 T& ~

80 TBYTE N/A

- }9 r$ l" u+ w' p% K8 p

typedef union _LARGE_INTEGER

# ]1 |8 {: Z* R/ H

{ struct{

! y0 C/ S0 \. }9 Z

ULONG LowPart;

8 L0 @. K( w( U

LONG HighPart;};

" G/ k1 N: f. u, c

LONGLONG QuadPart;

6 ^- b8 y% w- @6 j b

}

5 n5 K- l+ G7 o: A7 y' o6 o

LARGE_INTEGER , * PULARGE_INTEGER ;

& Q# d( v9 l+ Z) i

typedef union _ULARGE_INTEGER{

* \" O# V2 w' B' o

struct{

! q; {+ S% O- k m, {3 i: O& ?

ULONG LowPart;

8 w) w: E0 @% f6 ?0 D

ULONG HighPart;}

3 u U% m; w% q# v

ULONGLONG QuadPart;

2 D! `! {6 }' E1 h& C' K( o

}ULARGE_INTEGER, *PULARGE_INTEGER;

' d. G Q$ S+ S/ v! Z/ S

l 字符

4 P# F& O7 S; B2 P' ?- N

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

0 f7 v' _& L3 U. c% S8 q$ s

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

# \% n4 @, U# e

typedef struct _UNICODE_STRING{

' g* r! M+ l6 m

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

3 b( r3 ^* G" O0 o# p0 e

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

" a$ x9 T9 R, U' \

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

& z/ b2 }! O0 }# E8 {

typedef struct _STRING{

5 |+ V* X% |: J

USHORT Length;

/ ~3 ]8 o5 W$ u) E/ J. c' i3 `$ C

USHORT MaximumLength;

$ f+ }) \/ H# X3 O

PCHAR Buffer;}STRING, *PSTRING;

1 r- g6 Q# J3 I% q+ x

typedef STRING ANSI_STRING, *PANSI_STRING;

- `) e. |! m, ^8 }4 W

typedef STRING OEM_STRING, *POEM_STRING;

% X8 f1 y/ m3 U& u: a- Y

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

: e5 {5 R% c& Y1 o6 h/ q; c

RtlCopyUnicodeString()等等

9 C% ` m$ s8 q8 i. c" r

l 结构

' j$ t* T" O' J4 g6 b( P+ H

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

2 n) ~5 A7 P; i, O1 V

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

, _% O# {8 [2 S

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

3 R* w* ?" u1 q* y7 S- A$ ~

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

! h+ c: g8 Y# A1 H4 F) j; U

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

# J5 P; q1 k& J, T9 T

typedef struct _OBJECT_ATTRIBUTES

" L. `, Q1 a$ c2 o

{

$ |0 I! }$ y' c8 ^

ULONG Length;

3 c1 B+ o; j9 t) H2 | j

HANDLE RootDirectory;

5 g1 p1 R1 ?+ e; }6 q

PUNICODE_STRING ObjectName;

0 K2 I/ S- Z$ N; k

ULONG Attributes;

2 l: n' |& j1 h" L4 j0 j6 f$ p5 s

PVOID SecurityDescriptor;

; G2 b4 z$ ]- e4 C: ^

PVOID SecurityQualityOfService;

' Y, Q% ^& j( i9 R) i/ E

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

! j9 [! `2 t5 U) j4 l: o- c t t

typedef struct _IO_STATUS_BLOCK

, a1 A: P8 L9 K! W7 d$ [' z0 Z

{

" P, P4 X" K7 x+ {4 i. a

NTSTATDS Status;

% @! m# U2 V! b8 M x: M8 M

ULONG Information;

. q* Z( Q( C; V* h8 R1 p

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

7 @" Y, x. x/ v. X! ~9 \

typedef struct _LIST_ENTRY

1 E. D4 j# I* f% O( S

{

, z ]" n! }* x9 O9 g% T% q

Struct _LIST_ENTRY *Flink;

$ p4 S" N H5 ~

Struct _LIST_ENTRY *Blink;

2 |8 W! J+ @" ?! E3 `

}LIST_ENTRY, *PLIST_ENTRY;

9 h) ]! _8 |! w M6 Z

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

7 v, V* H! b G I* q

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

, f7 E1 w# B* ?) c0 u

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

7 x8 B; |9 f" s$ |( L$ F

typedef struct _CLIENT_ID

: Y* o/ T6 u9 @6 k1 ?" W0 ?, a# _

{ HANDLE UniqueProcess;

+ w1 ?% f) _% e/ c; J

HANDLE UniqueThread;

' W: m' w* x' z& i& p6 Y7 o. M+ S

)CLIENT_ID, *PCLIENT_ID;

6 ~* v/ S1 a+ t D( Q, `2 {) O

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

: `+ |5 |4 W, Q

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

3 _- Q- p( I/ v( f

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

8 X8 S6 P! o w. M

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

6 Q" i" S) s1 B# Z

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

# y1 ]( q( D7 i

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

( [" [( Q3 p4 W7 ?

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

* A/ ?) L# Q! g. b

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

9 y5 |' i* q# D7 [) l' l- s; U2 Y: W

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

& T6 w1 u4 P1 f/ Y5 N' [

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

4 z$ s: e- q; Z$ a

1 x, F) \) f8 G% O" {: T本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

( k& p- {' r0 c

指导( P- }) J3 E3 v' B- D/ S* J

6 v. j$ m# h9 e% m( A# Y/ q4 I

* W7 ?: s& r/ M

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-12 00:54 , Processed in 0.423859 second(s), 57 queries .

回顶部