QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com0 W7 P* }# T" |, N' V) U0 v" Q D 来源:邪恶八进制 中国

7 q! U( Y8 o2 b

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

7 b5 ^ h! A! T; ?9 Q

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

3 U5 ^% [8 c! B" \, r

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

; n2 q7 N- b+ I* o6 n h7 U

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 2 k* r. p9 e6 f6 m2 T- Z; \% P1 o

3 D+ `+ d$ G' J1 t t

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

; k7 g! a4 P+ i( H# L

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

6 f2 d! U0 Z2 Z7 N0 J2 {

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

' w% e! U* y5 v8 }

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

$ n% s( k8 V4 P; F+ {4 R1 c! h0 N8 x

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

1 G* y6 \; t" E

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

$ w& ]; y) M& e8 g" B3 n

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

3 Y& S& A) H# N8 v

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

0 w& K! W3 }& r0 h5 @9 X+ l! R

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

$ i; A/ r0 Z1 l( L) I

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

& B, W a+ ]# `/ U) C' `

反编汇这个函数得到:

6 q, e& i0 Y+ U

mov eax, 38h

! p, ]4 w; V" D' y( B" |- B! L# y

lea edx, [esp+4]

# W* d/ x0 v; e# J

int 2Eh

& g& B- `( e4 \3 H; }( b5 Q

ret 28h

; K, K& V+ r8 O6 n4 ^

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

9 S) A9 `/ k4 b) ]7 [) ]

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

7 w4 T* P1 ]/ t1 k

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

) `/ i: b2 V" ?

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

$ \2 @& o9 ]: c7 O

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

$ Z g8 M y* Y& p8 a- E- }: D. _

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

3 M- l& F; ?# Q- C- u) s& B

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

! K5 L& x2 G2 e" L) [, m" ]

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

9 _9 A/ {' p( f' x2 n# k

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

) M8 P' s) y; \6 ~3 E; P

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

8 V* Y, K0 n( e

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

& t9 I9 m7 e4 E

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

v( W9 g. w) q3 T6 h- `3 k1 ~" F

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

3 q4 s6 @- Z) ^$ F; X4 _

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

8 b8 u8 m, V, o! Y% E5 K

typedef NTPROC *PNTPROC;

. v8 F& b4 N# }0 B# L" {/ _

#define NTPROC_ sizeof (NTPROC)

5 `( Y; }, K: W) a& z

typedef struct _SYSTEM_SERVICE_TABLE

, N4 d' e# z" r% k# q( r j2 s. L

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

% K& u* I' {+ v+ ?3 p5 u

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

, Q: ^( I% \+ Q$ |) ?

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

+ G' x- ^& e7 \" h- ?! S. A

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

- g* J# d! R) ^6 `. U. y

) SYSTEM_SERVICE_TABLE ,

$ k8 E+ b! D/ y/ i

* PSYSTEM_SERVICE_TABLE ,

7 V% }2 p1 N y; G# M, \$ ^

* * PPSYSTEM_SERVICE_TABLE ;

3 G5 Y& U( x& L# ?) \

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

1 y; k% k0 n8 k* R

typedef struct _SERVICE_DESCRIPTOR_TABLE

. \8 h6 o3 }& e! L- M

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

G( y" x' p0 ?# n

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

+ ?1 i2 H" t# c: g$ m

SYSTEM_SERVICE_TABLE Table3; // 未使用

3 r" x( O; G, @

SYSTEM_SERVICE_TABLE Table4; // 未使用

6 `8 a+ R5 q0 M

} SERVICE_DESCRIPTOR_TABLE ,

. B' B2 g9 g* P+ @* r: ]

* PSERVICE_DESCRIPTOR_TABLE,

: S( i1 A% _; O% L1 L1 L

* PPSERVICE_DESCRIPTOR_TABLE ;

; z8 E q6 y) n7 M

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

3 ?% v7 {8 O% T

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

: ^+ H: t. v1 I! R: }

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

2 j% Y! F$ p4 i. z

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

, {4 c" x' V* a) G q* P" y

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

- k3 x2 E1 G3 Q" ?# \( t

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

( s* ~3 R& P9 r) H, W6 s" _) M

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

% n# m# J% D& n7 u

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

: ]2 w# w, u6 u/ ^6 ]8 K- c- ?

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

5 T8 ~9 ]$ f5 i! L$ f

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

9 n; m i. G& G! R0 Y

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。, c; B; @: o! P( y7 t- K4 @: @4 t 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

; x& {1 e9 k6 |: F; m6 S6 U# ]

前四行是最重要的,对应那四个SDT成员。 4 d- r( b7 B* v9 i: } 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

8 @# o8 }0 r$ _! j( b+ A9 x! a8 W# d

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

^9 F! C2 f+ n- U" b

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

5 f+ Z4 s: U, `/ v$ Y

KeAddSystemServiceTable4 b# n; x3 C3 Y" n) t! x, i4 y3 {$ L 此函数去填充这些位置。

i6 E2 N R% ]2 N9 o( B1 [8 B4 V( x

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

$ S6 u- M7 N" U' ]6 _

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

" K: n3 Q: \4 v; _' \

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

( b/ s l& Y2 J& m) @5 }# E) B

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

% U* z# i" Z2 e3 z- s% N- v' E

映射至ntoskrnl表格,ID在

+ K0 k$ L, J% {. s1 A3 g

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

1 a0 R, F; D3 j! J4 v

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

( i% |$ i8 B/ s, p

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

$ s3 r0 S( f% q

STATUS_INVALID_SYSTEM_SERVICE。

! B* {2 }+ K; b: R: D- [. }& |

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

# O4 E6 h0 O) g- P( ?+ ?2 M5 }4 p

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

* k6 @0 I3 a7 [, ?5 W+ \, ~: P

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

. w- A7 }( F+ P2 R+ V |7 o

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

W1 v! O& F* M) s8 G

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

0 [/ \) }% d* U8 V; }# X( r7 @/ G

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

6 ^, p. T7 r8 g! C

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

# P( L' w% l) l5 w1 ?: [. K0 q

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

6 G9 `; I% B' _+ d- F5 B' R3 W

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

4 h ^& _3 V! j. Y$ J

5 `3 @) G7 f# E2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

6 d; b! I* t" M1 ^

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

8 O2 E/ m5 q( b9 r5 d4 t9 C

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

+ T' Z3 F+ a' P3 i. O- A1 L

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

, N! T& V0 x( ]( c

8 t) a( t8 e/ I9 |( u Windows 2000运行时库

! ?- L& r8 O6 B/ E: c

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

7 I4 v2 ]" E( u+ |

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

$ v. E; J- ~' p: a, _* R5 d1 T

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

" ~% U$ d8 i/ h# p* ]

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

" p1 @# L$ s1 J/ j" B

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

3 D2 F- n+ b! R9 O- n

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

5 m: V2 x" B; k( |4 o

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

5 X8 D, c" X) n- l/ N/ O

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

. t5 `# ^8 v/ f5 y" Z+ c

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

D( d- P, \! }, R" ~4 o% i. \

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

6 G& n7 V1 \3 U9 \7 x3 i" ^

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

: a7 i4 a) s; k

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

1 F* X* ^4 H2 P8 p, y0 m x

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

$ u1 N1 X) I% a6 T. P- J) H9 ^$ \3 {0 d# s3 E, M9 I

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

) P$ L3 m. z' E3 j$ g% x" a

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

$ K4 B* M; T0 Z$ l4 y# b* V

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

0 U9 {, f1 g7 x

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

" w2 b( G/ @5 Y) S' X }; v

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

# `3 \5 K8 ?$ L+ L- j! C3 ?

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

1 Z: b/ i8 y' K( s4 m$ h* r

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

$ G- t1 j4 q1 E9 w; m- o, V

常用数据结构

* S$ s! u7 J" i- [

l 整数

% O' k$ p" O; a2 ~, t% G M8 |

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

2 c; M, m& R3 @6 `) f; S u

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

0 J* X0 y* i5 j5 k6 ]- ]- n

不同。

9 B! m6 ^1 L' V8 b

TABLE 2-3. Equivalent Integral Data Types

: A: Z @' n' ~2 I' M# A/ [4 p

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

6 o) K% r- {0 U

8 BYTE unsigned char UCHAR CHAR: _/ P) A1 [1 B1 v' P- } 16 WORD unsigned short USHORT WCHAR SHORT

$ B7 y1 a% q* f7 j% P% C: d/ B8 R

32 DWORD unsigned long ULONG LONG

+ U: _! l8 C$ D7 ~9 X

32 DWORD unsigned int UINT INT

8 E( j! \4 k0 A0 ^, ]

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

8 S0 d. h6 C' f2 P* C u: x6 [

80 TBYTE N/A

4 t" l& v2 r# \2 L

typedef union _LARGE_INTEGER

; [! I2 b( A6 f- F, e0 M" C

{ struct{

0 T4 ?5 X( j! J! m' a- N$ m: B

ULONG LowPart;

- ?4 H9 e" X& y$ M

LONG HighPart;};

/ f e/ `% d3 e! T

LONGLONG QuadPart;

2 }2 {' `) R4 J3 u U1 y% W

}

8 W. b; ?* t( g) x/ ?* n2 W3 ^

LARGE_INTEGER , * PULARGE_INTEGER ;

& g8 X: }! ?3 M: B7 @' m9 C0 d0 c

typedef union _ULARGE_INTEGER{

" _9 G6 q4 w' ]: H/ [& o7 t9 K

struct{

% r/ ~- [4 u- v

ULONG LowPart;

e" u# ]6 ?/ e4 {

ULONG HighPart;}

# J$ _: t8 ~, O9 s8 [

ULONGLONG QuadPart;

% p+ m' W8 V# J0 D

}ULARGE_INTEGER, *PULARGE_INTEGER;

! ^( ^6 g% E0 u J; l

l 字符

2 Z$ G+ H/ o I( m

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

/ q) D/ f( _# @: o, I

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

7 ]1 d7 l! t9 W# ]3 ~6 ~; \% N

typedef struct _UNICODE_STRING{

" M& s/ m& }' M4 Z: ]

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

C) g3 t3 O7 _ Q$ M

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

5 d* V: z5 m4 O: D7 o" X% A* r# v

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

7 ?$ ^+ |/ G7 ?% p3 @

typedef struct _STRING{

: b9 N. k7 o& s. G4 W

USHORT Length;

! V; p- s; `* L) S( X4 c

USHORT MaximumLength;

& G& {3 m6 r3 ~6 A, L

PCHAR Buffer;}STRING, *PSTRING;

0 ]4 E4 }) q% s6 Y9 n# W9 i& O

typedef STRING ANSI_STRING, *PANSI_STRING;

/ l0 r' F- }4 B5 m$ n

typedef STRING OEM_STRING, *POEM_STRING;

2 N% m/ x- U1 l; V. J2 l8 `

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

. e4 S* W0 l6 {/ z! j

RtlCopyUnicodeString()等等

) h( u7 S& U. t) \5 ?" P' \: K

l 结构

5 [5 v: V# c7 r8 f1 N

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

# ~& w/ c% E" L }) n: K

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

1 E) l2 X! H4 b! b8 W3 e9 w

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

! |& E2 L7 Q: x( E4 t' I% b

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

8 G6 g% [$ v5 s' e2 s: E: }" Y

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

# F( G- E! V$ u

typedef struct _OBJECT_ATTRIBUTES

, g6 I. ~" y, f, Y. n* E' x7 [

{

0 y+ \% Z! S9 G- h, a

ULONG Length;

1 C/ t# g" ^+ ^

HANDLE RootDirectory;

5 W: ^+ `* H( I- B8 C- Y

PUNICODE_STRING ObjectName;

9 J7 Y7 P* U* A0 C! O3 R2 a

ULONG Attributes;

: f0 F: h' X0 ^5 F

PVOID SecurityDescriptor;

; X5 `9 x# t9 \3 e' T" ?

PVOID SecurityQualityOfService;

8 Y! W' Y7 M9 [( r; Q: f& `8 _

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

& B# S9 m0 r1 B4 |

typedef struct _IO_STATUS_BLOCK

2 l; \5 [/ n5 K0 D* T8 C2 x9 H8 e4 W8 L

{

1 \* m( ^* h' t+ G

NTSTATDS Status;

! q* V5 G9 I9 M, |* n& G$ c

ULONG Information;

, {/ w4 f* O7 S2 d1 R5 r& L' `

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

. |( ~5 c9 n7 o

typedef struct _LIST_ENTRY

2 C) e1 I9 F1 J" ~1 K

{

4 Q/ W1 }- t" j, ~ I

Struct _LIST_ENTRY *Flink;

4 J# ?/ y4 _3 m5 M- }4 x. m3 U# P5 l

Struct _LIST_ENTRY *Blink;

( J& Q: T# ~3 g6 N

}LIST_ENTRY, *PLIST_ENTRY;

- o" o/ O/ O5 Y/ R2 ~# p* U& U6 u# h

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

4 }! M% W3 @6 }4 t" x3 N' s

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

- ~& J! R! i0 `# M" C6 n

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

# p3 d: ^0 S* ]/ a4 J, h% G

typedef struct _CLIENT_ID

0 C( C& z& |5 R) i

{ HANDLE UniqueProcess;

/ `3 i1 l3 M& s( [- j) m

HANDLE UniqueThread;

* o& P/ I7 K5 ]: B4 ~" b

)CLIENT_ID, *PCLIENT_ID;

1 i" U! W0 `# l( Y

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

6 ? W0 e# a. ]8 z1 b5 g, n

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

: E [ p% w$ ]& A

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

1 i- B6 l, p3 ~# a$ B) @8 H/ O6 N

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

# U1 @4 u% V6 n+ o5 j! Z* ~" c

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

9 {- x1 H( Y7 |7 @8 `2 G0 n! I

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

0 s4 J1 x, U) M4 M

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

3 J$ K& w: v3 G) B! b/ d, q

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

4 u+ v) G7 T1 S3 `

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

* i" f4 @/ E' ?" g8 B T; l

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

& J2 R9 ~0 m2 u3 t0 v6 b

; m$ E# h4 f7 F' f# j本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

4 ` p" d9 j n3 z1 y3 S$ B( M1 D

指导 0 ~" \: P x3 {% g

% o% X6 e2 R& a& a

8 M" [: m9 W/ |1 } u

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-4-19 08:22 , Processed in 0.426769 second(s), 52 queries .

回顶部