QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com$ A8 c0 [& X; r( d 来源:邪恶八进制 中国

) j3 J7 `5 |8 ^* ^7 h$ J' |

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

) Q7 Z3 N: o9 r: t- G6 u- d

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

" l8 H/ [7 N6 v% m% K+ a4 S

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

) e7 i$ @. V1 @# a& l% b; v! b; w

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 / m' F! |$ i$ d" p8 |

! A* L/ v2 p6 X: J: r: s; O

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

5 X! M$ o: b4 X$ O, z1 l

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

; [ n# [/ }% V; H; @

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

2 I' ?3 j! O: N4 }% U

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

- q; |) g! z5 k. j1 c3 d# q5 j5 {$ E

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

8 u7 m: y7 x. {$ X7 u# q# o

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

* q# ]& }- v: H9 X) D1 E$ |: c: I

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

3 G& z$ S, p4 l+ v% ~

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

6 F7 j& r7 c' O* h4 X

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

$ I# H/ y4 B+ m, g' _; c! T

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

9 c! ], ]) N9 X$ p: {% @

反编汇这个函数得到:

) G: Y! W* H6 o

mov eax, 38h

' m& j" N2 P; m1 i- a

lea edx, [esp+4]

# o7 f* } X0 A" Y" o2 D

int 2Eh

# A1 w. ~5 }% q) Q4 r

ret 28h

: K! [0 _- f. g- L

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

2 m2 F* l* O; i5 l/ y# B- ~

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

6 W+ q9 n2 Y2 {& X

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

. q! S' |3 h5 q3 u! b, E

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

: \ N- T5 ~" W# `! G

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

; V, m( }5 s- M* T6 [/ ]+ O

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

$ C7 l! D! c0 E

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

4 C# j( ]6 W8 V( X* T9 J2 e

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

* a# _' q8 t% z; v

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

: I5 K2 l( h4 h+ e% [6 e

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

! F6 u+ W v5 l$ h. f' o7 b

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

% n* c4 s& T( Z8 _* _

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

b1 e( O0 `* L0 ?: c5 e

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

% M7 ?9 S$ M- s2 _/ ^' @$ k% R8 z& m

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

: K2 B3 G* R2 S4 G+ N( b0 D

typedef NTPROC *PNTPROC;

+ c1 _0 _- W+ A |) Q1 S& q

#define NTPROC_ sizeof (NTPROC)

! ?% N; l1 z" K: [4 }- @% w

typedef struct _SYSTEM_SERVICE_TABLE

8 P3 A- Y3 E) ^) I

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

4 ]# @, b/ G$ K& d' J7 w/ p9 Z3 [

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

/ G% l5 h/ {3 b5 T

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

( X% q* @8 x7 S {/ Q( m

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

2 d+ S s" y. {8 f

) SYSTEM_SERVICE_TABLE ,

& K; H; X, g c7 X8 I% Z

* PSYSTEM_SERVICE_TABLE ,

* @8 x0 W* e1 F3 ]' E% _& p" i

* * PPSYSTEM_SERVICE_TABLE ;

" {6 A1 g+ K; Y: ?0 k3 k

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

/ E! {" Y0 K4 |) O! {1 ?8 E! m0 L

typedef struct _SERVICE_DESCRIPTOR_TABLE

1 P* V' ~, S* l7 z# Y

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

: r( P$ l' b+ ?

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

) @2 ]1 Y) ^# ^# T A% {

SYSTEM_SERVICE_TABLE Table3; // 未使用

# _: y3 b6 W- D3 h6 v% Y/ L

SYSTEM_SERVICE_TABLE Table4; // 未使用

& f% c2 }- u# w0 V' A/ M z ~

} SERVICE_DESCRIPTOR_TABLE ,

' O- v$ x; f: @; T# X; l; T

* PSERVICE_DESCRIPTOR_TABLE,

3 o* J) A1 y* s( L! U; t* p

* PPSERVICE_DESCRIPTOR_TABLE ;

1 k \& Q& |$ h$ u5 \& F& ^

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

9 ]+ U5 D3 z4 k' f

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

v9 T' Z* P8 L# Z

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

$ K9 c+ ?' f! U. I5 X& U

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

) ]& R7 u! d3 q( V2 `1 ~ a

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

0 q/ C: G, c3 \* C& s/ e; T# D2 }

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

. G: S! e, s' ]) p8 {8 v

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

* | A- m0 t' S8 X- c

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

7 O) V: r8 f0 ?7 ?& s' {% v+ i

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

2 p, W! [" _4 I5 z/ {" k

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

- @ ]& p' c( x9 I- [

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。 , Q; ~( ^# Q- m1 k 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

: i a' F* `, v4 Y2 E m( V3 W

前四行是最重要的,对应那四个SDT成员。' p+ s$ K! ? L, E: S5 `8 _8 q 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

1 c2 e: ]/ @7 p1 |6 G4 E

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

* H, F+ P' ~3 L: ?, M

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

! E+ G% H# ]8 @6 v9 q/ y

KeAddSystemServiceTable " S. S. R( ^2 W此函数去填充这些位置。

3 f$ g; P: N6 i2 j- M

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

, P/ Z+ {) K4 p. r1 h2 C

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

+ b1 l0 X4 _' s e2 e- w) a

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

5 V0 v" H" q6 v3 [) s$ o. V" s

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

* |) e. {* R, U9 M; `1 Y

映射至ntoskrnl表格,ID在

3 y. h' ?6 O- |3 x' q0 {( A

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

3 v8 z# w! k0 o1 A' W8 R

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

9 p, P) Z; o6 o- `/ L) b

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

7 b/ a0 b) x. k$ o4 s

STATUS_INVALID_SYSTEM_SERVICE。

; o) g3 r5 @* U' D

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

) @/ ~5 w( {+ z

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

v0 H- y g& l( r3 Z% V: P4 O+ y

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

+ O2 \+ o- F7 h& k- `9 m8 ?

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

' S$ t, z( }( W* ^# _

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

) a" `8 g! e: i1 C

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

! @* |; |' C' m+ n5 C

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

$ l. g# P' x' B0 b$ U2 |9 e

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

. g4 O3 X) |4 Y6 `

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

8 d0 R4 ~7 u0 { u1 j

+ N7 q: X/ G8 T& J 2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

& q, Y% [$ ]; q$ o" l, _

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

' H* i. i! Y' C% i, ]% c

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

" l. @1 ^- y( T* C X5 @7 C8 G8 Y

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

a; o+ P4 K: ^# Y7 @! f% H0 d, q

, L1 n% X8 K/ y, b Windows 2000运行时库

8 V i& l5 X, T; t" M

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

: }5 r4 Y0 d1 ^- c$ n9 W( ?* Q

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

$ l/ i5 n( O# C( i

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

0 @" \2 Y7 N( l6 \& i# B* c

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

6 {. N u8 z- y z

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

8 N* ?- u5 D! l# F* t

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

$ a& r/ {0 z! G8 d) G$ a b s4 p

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

d; e3 N+ ~) [& L, P6 W! @

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

# v8 D6 W8 U5 l$ k; ~$ F

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

4 ]. B4 d# D: S, R5 i* r( {

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

/ T u9 X2 k7 v* R) Z7 N9 `3 `5 v7 m

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

( c# M% Z; F8 ^: {! F6 d

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

) w; R# i& h1 W! B7 |" Z

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

@" n- F& J4 K) k e' N* K+ i0 B 1 E. X: q9 r' h% I: P$ Z7 j8 s+ G

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

8 e2 O }# N/ M

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

4 e' A( m, Z) j0 `

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

# ?8 c7 Y5 X5 I1 p8 A9 ~! v( q

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

1 q2 h0 t0 E1 k: _

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

2 s1 ?; G: e' Q. D" R* l4 i

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

! z% N, A* y% F4 Y$ y! k

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

- I, S5 q3 c% V, Y$ {3 j$ L

常用数据结构

$ ], v/ x) }) o% V' a$ D3 O7 |

l 整数

" X' j7 |/ D+ |* Z0 E+ J

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

; N8 H/ N1 |+ ^$ K: ^8 A" g. `/ ?

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

3 f% }2 J6 X4 q9 z5 C9 ^" t

不同。

' l3 U& e" K& d8 X& H' J& M

TABLE 2-3. Equivalent Integral Data Types

' y" q% t; R7 p+ q! `2 m6 {7 ^

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

4 C: Y. z. G. S O% S7 o+ ]0 h5 O

8 BYTE unsigned char UCHAR CHAR - U: W( B& W8 [; Z! z8 _16 WORD unsigned short USHORT WCHAR SHORT

; P. J! h+ v1 Y- N

32 DWORD unsigned long ULONG LONG

. m, Y# R! i% B* V$ Y6 ?- G

32 DWORD unsigned int UINT INT

8 r' ~5 }0 u1 V* i, N# b9 f# {

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

^4 ^0 {8 {! A

80 TBYTE N/A

' V& ^. O% N$ [

typedef union _LARGE_INTEGER

8 H: `% o2 I7 |, a

{ struct{

1 _1 g5 U% J- m+ v( g% P9 w$ @

ULONG LowPart;

, t3 p/ s7 |( S( ~! i( a# ~. X

LONG HighPart;};

- U$ C" `6 U0 m* V

LONGLONG QuadPart;

( Y5 p/ J+ Z4 i) n

}

/ o3 m) H- L1 E& d

LARGE_INTEGER , * PULARGE_INTEGER ;

. |8 ^2 n& `; S

typedef union _ULARGE_INTEGER{

1 G; W7 Z1 M, p! J: t

struct{

2 m$ y8 {( m% I% G) }9 ^

ULONG LowPart;

0 h5 w; g6 w* s1 @

ULONG HighPart;}

7 Y- B3 q5 `6 O2 Z! z% w

ULONGLONG QuadPart;

) z, i" |' \# u9 W" i! I

}ULARGE_INTEGER, *PULARGE_INTEGER;

0 a6 F6 C) o" h

l 字符

& f) V* A5 C& [2 r

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

# N4 t' E# k) P9 d1 q( q

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

, M1 e" w+ v1 m9 e [/ y

typedef struct _UNICODE_STRING{

, ?2 W$ P7 g# {9 b) n$ K3 w5 h$ F5 h

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

) t% v9 o( }2 r

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

2 I+ j5 i9 L( F+ N+ q

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

; x. `4 v0 |/ q6 z7 O. V

typedef struct _STRING{

* h- ~' W7 F- Z3 F

USHORT Length;

+ ]- e% V; B, u' {2 k& V8 {" _# H/ j b

USHORT MaximumLength;

9 |5 F% N9 J6 e

PCHAR Buffer;}STRING, *PSTRING;

; f$ d/ z M( l- y, K2 f3 Y1 F

typedef STRING ANSI_STRING, *PANSI_STRING;

z- T t& s# k1 k/ d

typedef STRING OEM_STRING, *POEM_STRING;

3 z, N: P# [; k4 b4 Z; d

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

. E3 d0 ?( E: j2 V# V

RtlCopyUnicodeString()等等

9 O+ b0 L2 d* ?& }- q6 H

l 结构

: q# l1 ]% K7 V5 g

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

4 H( V% O' g+ m7 z' A) B2 v

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

5 l" ], b+ i+ s* p7 G, c

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

- r1 }5 ]# a/ f; z, \' V/ [

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

4 k# B W, L( t; ]) B0 ~

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

5 b2 H ]7 C7 ?" h" d8 Y$ w, p% |

typedef struct _OBJECT_ATTRIBUTES

$ u! m" f* g$ g& R2 B' [% x! n

{

$ m5 P" j" Z" l$ d7 ]/ @0 G. }

ULONG Length;

8 U: z2 ], M7 ~) O# N8 H7 p" x

HANDLE RootDirectory;

2 @0 X$ \* R* N' l

PUNICODE_STRING ObjectName;

3 E M8 ~; V7 H) B1 L* u% @

ULONG Attributes;

9 a+ ~5 m7 L1 X( a: G

PVOID SecurityDescriptor;

3 g6 u1 m/ \) V+ `+ d+ K: L( M# |( z

PVOID SecurityQualityOfService;

8 Q# X8 w+ z! [4 M: U

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

" m4 K% R' n7 t; C* q( X) K

typedef struct _IO_STATUS_BLOCK

2 R0 L) H( X, P& v2 j

{

. Y: J/ \# D$ j

NTSTATDS Status;

2 |. @3 n, t4 p# h

ULONG Information;

. [$ J5 j) z1 F" i, I4 o. i

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

+ J1 V; o6 r$ T& O

typedef struct _LIST_ENTRY

) O' g/ G1 b( o' z" h- |5 |, |& s

{

4 H, F7 Q2 o" U( @

Struct _LIST_ENTRY *Flink;

) Z" i4 F/ g- F$ G! k

Struct _LIST_ENTRY *Blink;

2 N! o' A% x2 M

}LIST_ENTRY, *PLIST_ENTRY;

3 b8 Q5 V1 v9 C

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

E8 x9 V* ?- Y, `8 f( p

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

! p; l, }- N+ a) j: O, i6 c

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

0 H/ R( C7 S7 c: p) U' ]5 R

typedef struct _CLIENT_ID

9 y. F0 P/ l' K0 H

{ HANDLE UniqueProcess;

1 J5 J1 d' M, p& m

HANDLE UniqueThread;

6 X7 w- R/ s0 d& g$ p2 y* `

)CLIENT_ID, *PCLIENT_ID;

! t" C2 G2 V7 G; J, L6 N# q

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

& Q) f& c( @- U; O! |' p

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

( p& V- Y6 V( m. U$ C9 P

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

5 ?1 ~8 `' L* j7 ~9 E; U( s8 I) O

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

0 f" C* w" Q; `. E1 U6 h& v- m& j" Z

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

# U5 Q3 ~& ~% }9 V n5 R) s

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

! u1 }9 j6 K3 I0 C/ C1 q: V0 d5 W' p: c

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

. t5 X4 t, y, {- Q8 [3 }9 p

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

( s+ c1 W2 w5 B

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

6 I% M, B3 F/ K7 K5 U! H1 ]- y

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

$ o6 b7 N* t6 C+ e# \# w; i8 ^+ N6 e6 A

2 C0 H5 @3 R# Y/ g- H8 y 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

) l, W+ @/ t1 D& |+ f7 K1 m0 O$ K0 @

指导( v9 d" Z$ ]4 o$ ?

- X/ V$ e# V( ?& w

# G$ ?, e) W1 _( B3 T9 v/ w

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 07:14 , Processed in 0.452008 second(s), 53 queries .

回顶部