QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com - ?) D G: f) `. p3 D5 {来源:邪恶八进制 中国

# a# d! Z3 X/ J" w# ^

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

7 J2 J3 c( ?. Q

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

* V; t; W# k: [9 p8 V+ k f

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

# U" Q6 X: r. s3 F, J

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 " @9 {' i* y2 _* s) D( S

B3 T9 A: w6 A2 C0 d

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

! p4 M+ T$ ^3 D& V* g4 Z' G

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

/ a% V. F9 B3 E1 M/ j o. S, |4 H3 ^

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

9 g3 c! m4 P7 X0 q! n# H' W

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

. ~7 d8 g/ t3 q

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

! p1 r4 j) Q* e: C

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

& Q- {$ X) c9 a- A9 v7 E, d& [2 Y

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

9 s" n4 ~% R* P" P

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

' J8 e$ o2 z0 n- d8 l

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

2 L" f, _' N8 A3 Q8 X. u

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

/ O/ \% n* T$ Y7 H e

反编汇这个函数得到:

5 ~: r; I0 c# N- M+ |2 H, |! c

mov eax, 38h

0 O3 _ Z' t7 ]+ c8 n8 I- c# j i; G

lea edx, [esp+4]

; J+ b. P' B& W$ W2 u# q

int 2Eh

8 F* V; X# `4 {

ret 28h

) r d8 [- z7 v& K" E+ k$ D

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

8 k7 h; @6 q$ z+ F& W8 e

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

, g& t/ I; }: X

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

& n" F. A/ y* W+ m1 I0 o

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

( A1 e( Y9 K$ I) a3 r

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

. L/ h6 `8 F, \* P

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

0 [7 @ P" n) `# s- C& z u1 n

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

, O8 y/ Z) o, [9 F: j

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

5 ^! J7 d* T; e' x# U- L/ }; [

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

0 W8 H+ ]5 @; f2 n% _% ]

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

. _ r0 n# G+ m5 c* B& d

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

' @, z& B5 w+ o% N& a- I

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

& o6 B- @8 @3 ]$ [* \

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

+ \' N* f# {' @/ l; {

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

l2 I7 O3 ~" E' P. U& B

typedef NTPROC *PNTPROC;

' ?8 K( v! \. x% W

#define NTPROC_ sizeof (NTPROC)

' J4 ?0 b+ K2 e8 G; v

typedef struct _SYSTEM_SERVICE_TABLE

* @9 i: z4 o$ I, V/ v

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

4 Z5 {" C# @. O" I! A3 y

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

6 @/ d; v& S2 z

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

& y: v: N# B) c3 a2 S3 X

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

, A$ x" M: J7 ?' B6 s' s9 }

) SYSTEM_SERVICE_TABLE ,

# j1 s# E" I0 c5 ^5 L7 _

* PSYSTEM_SERVICE_TABLE ,

+ t6 G- |' X. O$ H

* * PPSYSTEM_SERVICE_TABLE ;

, @6 d! [9 m1 ^2 i

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

" ?' F- z; f& J

typedef struct _SERVICE_DESCRIPTOR_TABLE

' l$ _ E$ m3 G, i$ I4 q6 g

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

! X' C9 u+ ~+ D) i* x7 B- [8 }" ?4 Y

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

( z- U( m) Q, s8 j, b

SYSTEM_SERVICE_TABLE Table3; // 未使用

4 _* J7 |: g, R; K) Y7 u' D

SYSTEM_SERVICE_TABLE Table4; // 未使用

0 B+ }# j/ l! g# W g

} SERVICE_DESCRIPTOR_TABLE ,

! h9 f1 G9 Y0 P4 \' k

* PSERVICE_DESCRIPTOR_TABLE,

8 @% ^" ?2 P( ~6 Q' X

* PPSERVICE_DESCRIPTOR_TABLE ;

1 C0 p! f# [/ J) a' ^

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

! k3 A& B4 {% n' |

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

" W% G" X9 E1 a# q4 ~/ Q

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

- @2 G: M. R N

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

9 u% v6 z' A5 ]7 {0 J

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

, r/ G$ `4 J) C. U% ~9 a4 O

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

: _+ C8 Y! i( w

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

, x/ Z# O. a5 y1 M' w' ]

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

B3 @3 l! H/ ?+ z

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

7 T0 h3 G( N I P

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

8 V2 B/ V; }) ~/ U; e

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。 9 C B) h7 L7 d& P 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

. F" j5 ~- x1 r: `- I

前四行是最重要的,对应那四个SDT成员。 ~1 ?, \- B5 {1 E 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

, `; k* }" {6 O/ T) [) j0 j# Y7 Y Y) Q

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

' V3 K* G! R$ R- |& [2 {% m

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

6 H, J( ?6 c# O% V* V

KeAddSystemServiceTable 6 G' P9 e" Y2 P5 l9 k0 }此函数去填充这些位置。

( b- Q$ t: @. `' e: r) }* ?# O

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

* V' i7 p) V& g( v U6 @ g

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

, G! ^, `: p+ h" k

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

! B! [$ y& r( W& B+ k# v( o# B

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

1 ~ p- H$ ]! j- s- _% b

映射至ntoskrnl表格,ID在

* q: S `# W: ^

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

1 r) {- [; Y& u/ j S

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

) Z2 P8 k" G5 X

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

5 [+ A, p% b& o6 _. x: m. n. _% @" [4 U

STATUS_INVALID_SYSTEM_SERVICE。

: d5 k+ b8 G' G5 g& P

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

: l3 \% s2 H3 x

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

! m9 h' f7 z2 y8 S9 K

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

1 e/ j F9 T1 U6 ~ L. p

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

! v3 U0 L. O* d; w! Z7 D

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

J; d, | r+ t2 l* K/ h

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

, R" A% O" G# b! v$ Z1 x3 S7 ]

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

7 y2 ]8 K. y$ L, y, B2 C4 g

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

0 u1 X3 U. n8 q7 ~

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

/ C6 z% g- v3 _* ]- W" _+ c

& T$ a5 w5 @% v- K+ i4 [& r0 V/ [+ f 2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

' a5 c# Z7 }1 h4 h5 h8 \$ P

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

0 }8 Q. Z8 D4 _( z

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

, @& W' W4 N8 A4 |) c' I

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

7 o0 q! i4 T6 D$ N0 \& ^% h. W' w

& v( A! g* {5 j Windows 2000运行时库

+ p- w; }+ E8 U+ G' _5 W2 b1 ]

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

], ?1 S2 j5 J

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

( a- f% t, t4 a. h$ w% A" ?7 ] N! N

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

* \7 @! ~% s }( t0 i

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

( K @0 I* M4 c# }

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

4 G, f. L( }4 h0 ]/ Z& J) C) L# @

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

1 O4 l0 x$ t9 }8 ?" U; H- t

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

! H( g D$ A r. X! C5 ?

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

& E; j1 s: g# R4 Q9 v

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

! _& u' r: u% \

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

7 }% }" q: R* r: g5 Q7 ^& y

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

5 v$ Z& y2 ]7 A# a4 ]+ h

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

8 G# _4 ~: c) T8 U6 _0 M- ~( r

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

1 v% H+ ?% ]7 K" S# C5 d' X; e 4 [) Q& h" K3 F1 D) b& Q8 x+ d

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

* ?2 p* J+ w- u" \8 i

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

F. P3 ?6 S5 P) _, n" e( u$ b8 x

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

- _4 _8 q3 p% I3 ?4 j4 E. E

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

* ^ F* t7 S D

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

9 v: M2 p+ }7 _4 ^

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

: `- K ~' {4 O5 G/ c3 T! [

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

+ s2 @, I* s0 h, Q5 i

常用数据结构

) G1 R5 [, ] E% I" r3 S5 ?) V( g

l 整数

( q0 i6 O4 a. [/ R5 u4 x

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

! y; P$ |) E6 l) g8 F

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

3 ^9 R7 ~: }; A

不同。

! S* f: O' }7 l4 _

TABLE 2-3. Equivalent Integral Data Types

6 Y& C: c3 J2 V& _ N; A

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

g) Q9 t* ] S8 V/ y% B8 L

8 BYTE unsigned char UCHAR CHAR, Y7 M% ~+ d- W 16 WORD unsigned short USHORT WCHAR SHORT

+ i: a8 T9 t+ ~6 s3 h

32 DWORD unsigned long ULONG LONG

2 W0 V( y; c: g- U1 a4 ?! z( }2 t

32 DWORD unsigned int UINT INT

; `2 I. c# \( l9 U

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

( k+ O( X, a4 u

80 TBYTE N/A

d5 q+ u' q; m& h

typedef union _LARGE_INTEGER

/ s# j: E6 t- p$ l: {9 U( n

{ struct{

4 z& c+ e7 h* E9 g5 j2 F3 J

ULONG LowPart;

4 L( Y2 z2 Y0 \' L3 g

LONG HighPart;};

+ o' R+ h% P& W( z

LONGLONG QuadPart;

7 N8 ^- H% N6 F+ j. V% Z) m+ C

}

/ _& k, d0 H% Z: f& d9 n

LARGE_INTEGER , * PULARGE_INTEGER ;

! f+ Q% _* e% q2 N0 }/ G3 D

typedef union _ULARGE_INTEGER{

1 x/ }. Z7 O0 z% ^# |

struct{

0 g+ m6 b7 `$ R* k

ULONG LowPart;

0 p# [4 x3 C5 X. ] v/ ?, l/ G+ ]4 n

ULONG HighPart;}

) R2 N) b4 O: R- H

ULONGLONG QuadPart;

7 I; Q y) Y' k. n% T

}ULARGE_INTEGER, *PULARGE_INTEGER;

+ m+ r& s" E( h) c& J8 P4 _

l 字符

2 L! _" _/ }6 M) y1 c( F; s1 q

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

$ J# r" |; ]8 y

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

' m0 ~: h1 K7 f% f

typedef struct _UNICODE_STRING{

' U! U! r! w1 e5 J6 x: j" V+ n

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

0 b! U Z& l/ `3 ^( ^' o* ]! E

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

0 D/ C( f) G# V% c7 T, I. ?

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

3 v' E9 {! m, j( [9 i6 a$ M

typedef struct _STRING{

, B/ i0 v# S" Z' D2 Y

USHORT Length;

T$ t4 M6 g$ G; k0 D/ B

USHORT MaximumLength;

6 y! O4 g. V/ B

PCHAR Buffer;}STRING, *PSTRING;

2 ` C5 G q* T( S

typedef STRING ANSI_STRING, *PANSI_STRING;

8 n, @2 P, q; h

typedef STRING OEM_STRING, *POEM_STRING;

0 F3 `% R* Q* @8 R! }& p3 t

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

7 P5 H; g( Q$ D H% Z5 I

RtlCopyUnicodeString()等等

) d0 n5 B+ V% s6 b5 N/ n6 n! X8 n

l 结构

; Y: y, \2 E$ p3 v2 o2 h) O3 u0 ]; N

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

: \' [/ J5 S' n

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

G @" t( g( Z8 x+ {2 b8 c

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

. z) A6 P/ q) m6 G" c: M4 Y

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

: b* e! B( Q4 j3 Z( a

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

, W( b. u6 Q0 X7 _* h1 J

typedef struct _OBJECT_ATTRIBUTES

2 s& L2 ` h" o7 V

{

' b" ]) K' a" x5 i

ULONG Length;

! ^ j: ?! N) k; D+ f/ P1 V

HANDLE RootDirectory;

7 L: y3 N: L8 w

PUNICODE_STRING ObjectName;

! F! G7 Z8 ?. ?* q7 w8 ~8 y

ULONG Attributes;

/ E2 i1 b! P8 ^0 ^6 A

PVOID SecurityDescriptor;

( F" E4 T8 {3 J- H% G! j6 P

PVOID SecurityQualityOfService;

$ U( F; U& q: ~' _' j

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

" y/ B& b3 _6 e& R3 H1 t

typedef struct _IO_STATUS_BLOCK

b8 F( H# }/ O4 Y3 q) \0 |

{

9 k- z0 {; m( b2 v8 G

NTSTATDS Status;

6 e4 j* x) ~" j1 o# S

ULONG Information;

! ]! i: E6 ~5 m# w

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

, t+ `8 m8 e7 L1 }3 Z( g8 K

typedef struct _LIST_ENTRY

$ D0 u5 {. ?0 {8 M

{

) `8 G. W; S; }( x" v; M) v

Struct _LIST_ENTRY *Flink;

2 o0 @7 ]2 c( z' j0 J. c

Struct _LIST_ENTRY *Blink;

1 {9 ^% x" `7 Q6 M" F9 f+ T

}LIST_ENTRY, *PLIST_ENTRY;

; t9 @% H8 \5 D; b+ E

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

% M' ~/ V0 u) {$ B

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

$ o% a/ C5 ~: f+ x4 `; H

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

0 ~% K( |4 Z4 `/ S+ |# E5 _

typedef struct _CLIENT_ID

% ?! j W- {2 f

{ HANDLE UniqueProcess;

7 ?. K1 t3 a. S9 B8 n3 ?. p `. [

HANDLE UniqueThread;

9 \: a- N0 E" q" B9 K

)CLIENT_ID, *PCLIENT_ID;

" W, K2 `* ]# [6 j" x( H3 e

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

2 f3 F* A% W- `- `

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

7 q W2 L0 K6 u4 |- C6 l

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

' [. _' C) \5 I2 o( |

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

, G5 L# D/ ~" H1 H/ q" M

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

" j( }/ B# |6 }6 t$ t7 s; m

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

% ~& q; v& N# b/ L: k! W) @! `

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

4 L8 o1 M |. e* G7 a* h" `( t

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

+ W; O: p( y( B2 t

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

, L; Y" k0 N0 ^& n

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

2 j/ b3 ?" q ~

" D5 H. w" o, J2 l 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

7 t& b4 P* j f& I. K3 F$ n8 V

指导- B9 R6 r3 v0 P6 c6 \1 d: O9 v

+ ?/ n5 [8 w6 |) g

4 L; p: ?" a7 F% ]4 C

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-8-14 08:15 , Processed in 0.434388 second(s), 51 queries .

回顶部