QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com * s: B% ^ p" Y( s6 ^8 A9 M来源:邪恶八进制 中国

. r- m, [, C5 J9 T: F+ O1 s

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

& S% t/ Q3 L$ Z4 c& @ Z/ |; B$ ]' d; ~

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

( L7 w X* S8 X4 l5 J1 F0 @

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

1 l8 r) Q7 I8 r1 r+ b- v

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 0 m' J, k2 h7 e5 A' U$ u; X

3 K& M6 v. J8 Z, l% u& N

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

" Q: d, D- y, x \( E

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

, h8 a3 X% A2 w9 y% b

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

; R: H+ D$ ~- J7 P; Q( U

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

; q8 ?8 J o+ ?! N$ h& l) v

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

' Z5 B; b& e, s2 v4 K! s

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

7 U; ]3 }0 W$ x |

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

B3 A- s' N! G9 r% E+ n$ q0 Z" @

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

- F! F- u6 O& d2 T# V1 J

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

- m0 t9 @6 k8 L4 ]; e. z9 ]

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

! J7 S R$ [. a3 a4 n; l7 {

反编汇这个函数得到:

* w+ ^+ M' ^3 k& J; B

mov eax, 38h

F1 t$ j; O) h$ G U* |; [

lea edx, [esp+4]

4 K% A! M- G7 s0 d

int 2Eh

+ v* O0 U, Z4 h1 E g7 V. Q/ s4 T

ret 28h

% N2 t) S: u1 q: ?$ ^+ T: m& Q

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

# i; l, K6 b* I$ X+ Z( U* x

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

% O; m0 D2 [4 a+ s+ }" L( I" U

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

) Y9 a% y& s; s# ~

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

! b3 M. `" S: t0 U7 D) @

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

0 }* g w+ q8 T# e

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

4 a, g4 e$ Y7 p

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

2 E8 N, e( o6 t9 o2 H

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

% c/ T; t) T! V$ D& f

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

+ u, i! S6 `' a" A; d2 F" A3 U

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

1 S+ e0 r! d- N6 q2 _! A# b/ Q

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

$ m; h9 U* s. r8 b% Y

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

Y" D' V0 B* V- V& \$ d

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

# D* E8 [& a7 r

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

: e: H/ w& b3 J9 P5 @

typedef NTPROC *PNTPROC;

, ~) e# k) A% r/ x2 X, A

#define NTPROC_ sizeof (NTPROC)

+ A% ]: u; ?6 c: N/ Z

typedef struct _SYSTEM_SERVICE_TABLE

, R1 t- a; V& z x. a

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

/ O5 A9 n4 O D1 Y( v) S

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

1 U i) P# Y% a. p% F+ ?" b- e

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

0 E6 Z! Q' G+ J4 V; h: y0 K5 i! r% R

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

- i6 S& t* j X! j- T: X

) SYSTEM_SERVICE_TABLE ,

" J3 ~ O# B, _. b7 n5 E

* PSYSTEM_SERVICE_TABLE ,

3 ]6 E* l7 X/ C. f' J* N

* * PPSYSTEM_SERVICE_TABLE ;

$ y# K! y* Y+ ~8 @2 b

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

8 j3 z5 n; i0 E

typedef struct _SERVICE_DESCRIPTOR_TABLE

; w- _* G- x6 m0 k8 x

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

0 j T* J. e, I

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

1 p V! N W& F! @4 f$ d

SYSTEM_SERVICE_TABLE Table3; // 未使用

$ b1 u5 t) N* I/ i3 u0 H; [

SYSTEM_SERVICE_TABLE Table4; // 未使用

/ o9 C" J+ r5 \6 _* O o6 s a

} SERVICE_DESCRIPTOR_TABLE ,

% X8 e' O9 s/ i8 I$ ]

* PSERVICE_DESCRIPTOR_TABLE,

9 P1 E: K1 o3 J& K1 s& l1 G

* PPSERVICE_DESCRIPTOR_TABLE ;

8 W9 X) v. ^$ N$ \% `

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

2 T8 x# k" u7 n; c6 }& z/ Y

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

% S- z$ y; W- F9 ?

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

9 O! }0 o. o/ @! f8 r/ }

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

) U5 q& c- B* {9 B/ V% F9 \: c# |1 F

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

$ C/ C5 t4 W3 {( k

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

$ U ]8 m/ P% p, k( l

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

( a, p8 X$ K1 L7 c" Z9 F( X

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

# |- o! d8 u( J7 e

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

8 ?! l3 U! I6 A+ ^

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

" Y: _* ]6 \( i( J9 d

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。 * {1 ?3 v3 |6 \' ]! Z/ l 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

& }% I8 y1 l8 y1 \1 y

前四行是最重要的,对应那四个SDT成员。 # G& J. R6 O& T, J$ X1 C+ F$ d/ ^ 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

3 _3 H; [, c" n. p

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

2 d8 s6 W% @- q# a

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

/ M/ {9 @0 o% v- M \7 \

KeAddSystemServiceTable 8 S+ D$ _& X% n此函数去填充这些位置。

, P/ q" P* }! a2 B

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

+ D* r `. f8 Y3 r9 a8 n

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

5 O v: W9 W, }

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

) U; V& x, u: q6 N' n0 O. h' |

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

# n6 @" T5 B9 `8 Q& ~7 x

映射至ntoskrnl表格,ID在

' J v: q3 |5 \1 N2 L

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

- v! g) [1 D' R

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

% L/ \: z" k! W8 Y" T9 [# |

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

" o2 r* H* k' f* ^

STATUS_INVALID_SYSTEM_SERVICE。

% G* A) ^ I, n0 H. j

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

0 m: m/ L) H3 g; W

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

' p, S; l* T$ d7 q+ u

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

/ _* R' s ]" Q1 h1 r! ~- a

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

, S ^2 x, |# b c t$ r

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

: n! `! w- |/ V# l+ ~! Y' f/ v

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

# z0 }$ ]4 z# C# l

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

* g+ s* w) u$ Q& }& J4 T3 _

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

6 p2 d/ p+ Q! @8 K; ?

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

9 {3 A4 O. C4 x

4 U6 J9 w' n% i& j+ z/ v' \7 M 2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

+ ~. Q. O9 b; p$ v `

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

( l% f: O* p/ P3 w2 q8 G

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

# e- G& Z3 {9 \1 U y: o) q1 `

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

# j4 y" x1 K* y( e+ {

! }; I: @6 c) L% x* i. K Windows 2000运行时库

' m5 D. J# i: h, c) P' w

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

% n' x& @0 [/ n+ V8 p: O9 U3 Z

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

; d! D ~# g: A# x# ?4 ~4 {5 B& I

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

+ q4 B* l* d; N5 M0 m+ Y. h2 W0 T

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

& b0 ?( }, `; q+ n0 d# n0 _

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

- |$ n0 y; S: k+ P: {. g; J! [

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

6 _* j0 l7 Y$ G

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

1 Z( W* v$ i% G- M% q3 V

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

2 C2 r" e- t# t& w# j/ f* t

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

5 Q& o0 _% Y4 ]1 y

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

% H' f/ V: S( U

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

* I+ p7 o9 b+ ]& b* _

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

& {7 v$ j; \' _

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

. k, N+ N8 H" x @" A& v7 x% g2 L) h2 h- C0 [4 {& l9 p- }

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

6 c z' {: a; H8 L3 O; k4 V2 Q r

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

8 j% V+ n! S& O8 Y

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

e( I/ ^% I7 I9 a1 j$ i0 F

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

+ R4 I% w+ h+ c7 r' T

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

! p9 f( T3 o; `- P; } |# r

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

$ d# o! Y+ ~2 p$ Z( c; e. v

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

7 M3 K8 u! g3 K" p4 H

常用数据结构

' }4 c* D2 l B& c6 w

l 整数

+ {9 [ v- {1 j, s

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

' _5 T! n9 S c# ? R1 \( {- B

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

8 c& D, i* Q9 t+ T7 ?

不同。

2 V. j% W4 J: S) Z9 v. f. O4 ^1 Y

TABLE 2-3. Equivalent Integral Data Types

6 q8 \( v: e* G( `! E e# ]

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

4 t; D' v. [/ ~- M5 r# p

8 BYTE unsigned char UCHAR CHAR 9 V- R0 ]6 s( F$ s5 T16 WORD unsigned short USHORT WCHAR SHORT

; A- l; d5 P8 d& b( m& K

32 DWORD unsigned long ULONG LONG

* o# G. `# @, B5 K

32 DWORD unsigned int UINT INT

! X* G! ~3 h9 q7 s4 Q( F1 V

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

7 G8 L$ Y9 K* C( E' `8 N5 Z

80 TBYTE N/A

" W: L$ A2 u! t9 S0 k5 r) z

typedef union _LARGE_INTEGER

; Q$ a n3 ]" j u; u( u/ F

{ struct{

7 p9 P4 O+ d( [' D

ULONG LowPart;

. M6 C- B7 V% S+ e& a

LONG HighPart;};

3 ]! [- A0 p! A& {" e7 [ S2 w. t. A8 J

LONGLONG QuadPart;

, Z+ m9 Y( y: u- U. m( {. n3 b' H

}

6 c2 M! z# j. c! T- A3 u2 W( p

LARGE_INTEGER , * PULARGE_INTEGER ;

# K" n* n, e; x5 O2 O& A

typedef union _ULARGE_INTEGER{

* L) d! E" l5 [$ r$ r- O* a" t

struct{

' X' X% d2 [9 {4 ^5 |& `% r1 ]1 }$ \

ULONG LowPart;

; ^9 w/ U, f: P) U5 G0 M9 q

ULONG HighPart;}

, S7 e* f" X* t' x/ v* M

ULONGLONG QuadPart;

6 C) g& W3 V; y! S( i9 w+ Z1 u$ K

}ULARGE_INTEGER, *PULARGE_INTEGER;

0 _# q' p5 W8 \! D$ O$ Q8 \% r

l 字符

, `$ N# F* U8 H8 t" {. @

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

, e4 t' y* }# [+ b7 F% v' S

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

0 v- \+ r0 ~% ~' w! v5 c5 O

typedef struct _UNICODE_STRING{

7 }8 P; Z" p. @' e/ m x

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

$ j/ L* b4 i# N3 D7 L# O# @' R* B

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

% Z9 r$ v1 D" t

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

5 M7 Y& [) `: Q$ m% J# m

typedef struct _STRING{

) P, ?1 q! N( M5 M" Q! G4 A

USHORT Length;

) P; ^ f7 ^8 t. @

USHORT MaximumLength;

) n* L' Z' Q u' J

PCHAR Buffer;}STRING, *PSTRING;

% |! F% w1 ~4 o$ h: U. K3 ~; G

typedef STRING ANSI_STRING, *PANSI_STRING;

8 H7 D" |0 q/ r2 O+ A

typedef STRING OEM_STRING, *POEM_STRING;

/ S: Y& J9 I7 u9 e+ |

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

/ W" d% U( N. t0 @, O- A( g

RtlCopyUnicodeString()等等

" p0 n" X1 |) Y5 Z9 R

l 结构

G4 w! E! }) q) j- L7 g

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

8 Q8 h1 n7 ]( `' H

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

6 X, v$ U! N- N, a: g# r

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

+ P& v: Z# ?9 @3 P: P0 j$ q2 Z

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

5 X4 q1 G7 ?, ?3 Q6 I

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

* ?4 j2 V5 }4 n J( T+ }- d

typedef struct _OBJECT_ATTRIBUTES

1 s+ J4 y9 Y9 b; _% P0 x% [1 W8 f6 O

{

* t0 [5 z9 R# v+ x( G8 G

ULONG Length;

2 V! _" W+ v( S/ ^' r5 S

HANDLE RootDirectory;

& }$ w! j( D: s; M+ B9 R

PUNICODE_STRING ObjectName;

f$ h1 N; }: S7 M: e

ULONG Attributes;

& N' x$ W( h) |1 Y3 y4 D

PVOID SecurityDescriptor;

3 m) x. X8 E' L0 D" `

PVOID SecurityQualityOfService;

" T; d0 n5 z6 Y# ~* [. f

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

1 h8 S% X9 x* i6 }

typedef struct _IO_STATUS_BLOCK

$ C2 I8 V4 m4 B1 n- z7 f. r

{

/ |4 S+ }1 Y5 V( ?* ]

NTSTATDS Status;

5 l) p+ z6 E) G; ?

ULONG Information;

$ P/ G* ]9 e" P& w. s# @

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

2 v" @4 ] b. B( W

typedef struct _LIST_ENTRY

& s! ?! Q H5 ^, G% U) q

{

7 Q; r. d7 R9 S

Struct _LIST_ENTRY *Flink;

- i& A& B$ A9 A. N

Struct _LIST_ENTRY *Blink;

1 |; e W8 R a: }8 I

}LIST_ENTRY, *PLIST_ENTRY;

* p+ {/ b1 z3 s! V

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

1 ?7 y) W) k# K: O3 W+ \

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

7 ~) Q; Q& v' z' f+ W

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

3 B0 E: S5 H& w* E5 \9 k- R& n: L

typedef struct _CLIENT_ID

9 E# w2 f& t% X* L, V/ q

{ HANDLE UniqueProcess;

. {- q* x8 e$ V/ l9 x- f

HANDLE UniqueThread;

3 Q r6 w9 i: S z, M2 p& p' ]

)CLIENT_ID, *PCLIENT_ID;

+ u, J7 \, ?5 Y. F$ b p

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

( u7 @1 I8 M8 ?2 p6 I/ C

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

. B- b; Y/ R7 P3 }& q

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

1 H8 T% t* c" l3 `- L

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

7 a! n- e3 K7 I2 f! x- A

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

" P8 T2 C8 ~ d* i

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

0 N7 X0 r6 z1 R9 i; ^

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

- J) w2 w+ |2 h, |8 r4 c

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

! S Z" S' V w- O

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

4 \8 F- [! [4 j8 e* ?* O& U, n

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

, Z. `3 r$ Z$ C- T! s6 V

9 d1 g0 Q/ c r- o8 E 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

3 _0 H& l4 {# d

指导 $ | c3 H4 v8 y6 f1 d& L) q

. S: N1 M8 S& {9 B/ o3 P4 m8 ]% P$ U

9 |/ w- l! c3 x: M. H

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-11 18:09 , Processed in 0.346565 second(s), 52 queries .

回顶部