QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com( `6 U# s3 k/ @ 来源:邪恶八进制 中国

5 I7 D* J+ f2 A, \! t8 ^- @

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

; e7 i" L6 f) }3 R/ W% @

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

K8 `7 F6 a1 z6 |% p

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

- `. P( {2 q/ s* t4 e; t

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 4 x7 A6 l6 [% x, i9 ]: @' R

* {% K( v3 J- x) C3 `

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

- g$ Y, ]$ @3 R5 I x

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

, A( ^+ p! ]$ R' a: P. o

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

6 Q4 X( c% ]! D5 J+ g7 `: L

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

, {4 ~( S8 L: N! n' L% E* M

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

. X* t7 U- @: ]4 z- f& o: O

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

7 c) s( q, w3 ^. H

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

: D1 I; K8 Z1 x2 j" @( F. c

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

* m8 ?( `7 J: Q8 d. b

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

# l+ J: J7 [' j7 C; K

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

, j$ c5 g. Z# ?3 o4 }% t/ @2 M

反编汇这个函数得到:

! V; q9 D6 D( u" v9 a6 y# J2 s& l) [: J

mov eax, 38h

" v5 M) b' @& Y

lea edx, [esp+4]

# W1 W1 ^3 ?% H6 {% X- d, M

int 2Eh

$ s' |( J3 U5 I

ret 28h

. ] p" x& s$ l, J

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

# H0 o" q) D; j

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

8 _+ v/ [+ n4 T% ]- ]

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

- I1 r p, ?, o h0 x3 a* Z

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

! e/ p1 r: h# ^) o: J

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

: |: x% A; S* _

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

" t/ F3 q- A* v2 d- |3 K4 z

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

( G! p6 P7 R* Q/ Q

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

( x" V b1 |( a5 s. v1 o

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

8 G$ w% O. W6 ]6 C7 V

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

1 V8 V5 W- D9 A/ i: o: J

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

$ r1 ]; W( b$ h4 b0 A

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

( W# R# i1 c! U( C' {& s

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

. o. X1 i# j' B, x0 W& |8 h; [

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

# K. i/ p P5 B2 n9 w

typedef NTPROC *PNTPROC;

& T$ n7 ?$ K0 B% e# `5 Q, J

#define NTPROC_ sizeof (NTPROC)

1 U+ o d4 p0 p+ f6 ^

typedef struct _SYSTEM_SERVICE_TABLE

0 t; i, Q& b" E- R( C# E

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

0 {5 g5 K' `* y3 J

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

& l6 t3 q6 O* _6 e3 P, W h9 P

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

; h5 m0 {& R: }* j8 }0 j

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

1 a# H8 {, d. ^

) SYSTEM_SERVICE_TABLE ,

1 h- F/ b: D9 n& p3 |* a8 f

* PSYSTEM_SERVICE_TABLE ,

! F. \, n1 e; W. T6 X

* * PPSYSTEM_SERVICE_TABLE ;

}+ T1 [8 k0 N% z0 Y+ Z

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

4 z) m$ B* r' Y! s @3 ^6 ^7 S- _

typedef struct _SERVICE_DESCRIPTOR_TABLE

" q* @& p) m( g2 c2 Y+ l

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

* d" u4 C1 s2 e. ?6 T

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

- s7 U/ z' |6 d5 p9 u

SYSTEM_SERVICE_TABLE Table3; // 未使用

) H2 B$ ^# B2 |- i9 t

SYSTEM_SERVICE_TABLE Table4; // 未使用

' J, a. v$ D) W6 R

} SERVICE_DESCRIPTOR_TABLE ,

8 p1 g& m9 y: x3 e9 ]

* PSERVICE_DESCRIPTOR_TABLE,

$ `8 E* ~. j2 \4 x) C( Q: c# [

* PPSERVICE_DESCRIPTOR_TABLE ;

! z' q* {/ `( J0 J

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

& t, q, d! h0 O$ t

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

: y& V6 {# Z1 U2 b

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

$ Q K0 d0 v5 m0 Z1 ]0 c

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

6 J3 U0 ]* h! R- r3 z2 }# X7 ^7 n

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

L/ e& i5 P( ] C, w

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

, W! f; m. W+ ^3 |; t) s

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

. Y+ `4 ]' [& G

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

2 y: t8 r( M# P L4 A6 }8 T

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

7 m3 N, g) Q& s

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

) Z+ [* a$ P: X8 Z- I9 }/ i. \* Y' K

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。 G5 X& K! S V+ V" b5 j 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

5 U- }1 @" @! f! r8 x8 C$ @+ \

前四行是最重要的,对应那四个SDT成员。 * }- L/ l" H) y i& v& v* s 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

+ b2 U- Z% F5 d4 r. V- u

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

E) b4 F$ ^6 O0 f0 D

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

) `) N/ O; y- z% \/ p, n8 D

KeAddSystemServiceTable 8 f$ D8 @' p/ L' e/ p此函数去填充这些位置。

1 f" F# N* G) O s4 _- n$ `9 C4 e

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

3 ]. d: N5 I# R3 c+ Z& J0 D4 A

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

5 W2 z) D( \3 R/ K" b+ j( L

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

% g, d2 a) J s4 H( {7 |

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

6 ]+ A W5 W3 D! h8 B0 `: K$ I, H0 g

映射至ntoskrnl表格,ID在

3 G4 b7 J2 C. q9 N

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

0 x( a' ^9 X [; r4 x

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

/ F2 {% y( c6 U5 ]

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

4 L+ U0 @7 ~" A' u6 `, U- D

STATUS_INVALID_SYSTEM_SERVICE。

. G; o$ C$ S) B

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

, r8 c# m2 i$ m3 Q; N1 a) k" n. |

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

0 l' H& J8 a& a0 X+ v g

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

1 @8 y+ C" b2 C

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

+ B1 I \4 [3 Q& }

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

/ l" m; } G% _0 A ~5 o

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

. W% O1 c% h1 E

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

2 t/ w* e! B6 z* p/ y

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

. q+ v% `; v! Z% x g" p# ~

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

+ m! a: p% o9 M6 E( F; T- k+ A

- ~. h2 x% x+ b7 y2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

# X. r! _% J6 P6 E

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

, a+ `$ F; I3 ]4 y

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

3 ?' ?% Q4 r% t# r O- c" L0 e" }

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

% b- ^ j3 Y$ a: @* Z7 _; N

- D* J8 o8 |- l' I$ { Windows 2000运行时库

1 }1 k* D( _' a2 T/ B' i

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

; [4 `( Y: k6 L+ I/ }/ P

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

( L$ x( i& T+ ]2 e

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

/ o: j" O- v1 g Y: Y* g5 [: ^) W

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

# M( C5 o% L" Q; A! o$ e5 |6 H

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

% @4 c- P3 L, Z% B0 V: G' w! R: m4 ~

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

* G( D; [+ d: T6 W

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

4 A- @+ Y2 O( J7 l4 J% J& f

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

{5 W# M' ?$ r4 f

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

, E" E! R/ i$ K1 e) ]

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

3 U3 i7 l0 b2 ]1 M/ `

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

7 Q" o5 f- Y2 [& z. M8 b

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

& ]2 u6 O3 I4 { m H

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

7 }! V$ E4 p) m/ T2 p# K! w , `, R( z8 @6 J+ B9 d8 S

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

0 ^, \# Y+ d/ u' Q' Y

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

8 n" s5 {! f- |' V8 _

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

1 }3 h+ p6 ?8 L p; x8 z9 |

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

6 Y( S2 i. i1 T. D( k

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

o' w2 H; q; j0 T+ U6 D

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

( R8 [$ p" g1 j4 ^! @( T

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

5 c( G( m* A/ y+ F

常用数据结构

$ {, ?" V' F+ }9 X

l 整数

, _/ z# L* {2 H! L2 s4 m# e

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

, b8 R" B$ Q: Y5 f2 o5 T, t" B

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

" ]# Z( v3 P5 o

不同。

" [9 [! T' ?. y# b1 ~

TABLE 2-3. Equivalent Integral Data Types

E) [7 d% n% C2 I, t$ {8 i

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

. y9 j5 s/ @" s$ e2 M/ ?

8 BYTE unsigned char UCHAR CHAR 3 u! l9 q0 _! T" z& Y$ B6 O6 C* b. i16 WORD unsigned short USHORT WCHAR SHORT

; Q8 m$ e0 L' N9 u( O1 X

32 DWORD unsigned long ULONG LONG

7 S4 S: B0 G0 ~. @7 A$ t' H

32 DWORD unsigned int UINT INT

3 S/ @" ~( X6 [2 l8 S0 f

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

7 U' J! d4 a3 V' l8 f6 |8 A

80 TBYTE N/A

1 O; x7 g3 I4 W; C

typedef union _LARGE_INTEGER

* W5 K' o. O" }" Q2 ~

{ struct{

* h4 [1 ~. p2 [2 R U8 f

ULONG LowPart;

/ C. A* t) ?4 [9 \- P

LONG HighPart;};

/ L2 i3 N |$ t/ R

LONGLONG QuadPart;

( d# @. e, c- E. [& f

}

3 @: p# u8 k5 W7 A6 w1 W

LARGE_INTEGER , * PULARGE_INTEGER ;

& B# {: V% o9 L$ U7 y9 @+ B

typedef union _ULARGE_INTEGER{

+ ~8 N7 T8 x* u$ H& ]

struct{

; J4 ]$ q( f1 p, h, v9 t

ULONG LowPart;

% N3 x! k' |0 o9 j

ULONG HighPart;}

O0 B( s1 V6 V3 o, g

ULONGLONG QuadPart;

2 |4 m+ v1 Y; x+ b% }5 i4 }

}ULARGE_INTEGER, *PULARGE_INTEGER;

! O- @ |5 [" k/ s& a

l 字符

% [% c* X3 R# }, e+ P$ N' }6 \

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

+ W, F- |$ H* ^+ \: n3 l. W

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

8 W: {2 Z9 }8 {9 t. }! _

typedef struct _UNICODE_STRING{

4 w5 Y+ F8 A' Q u& Q% W& o9 c2 K2 H

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

, q# e5 R& G+ E

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

- X+ Q5 y8 t3 d- L. t5 z

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

& q0 b* O x6 Q- z( O6 A3 p7 |

typedef struct _STRING{

! }2 \# _8 a! A9 ^

USHORT Length;

$ z: ~9 M: F" `4 j, [

USHORT MaximumLength;

- E2 O* U* y4 H$ G, a* I+ n" e

PCHAR Buffer;}STRING, *PSTRING;

0 h$ A u- X+ w. F' d8 k

typedef STRING ANSI_STRING, *PANSI_STRING;

/ D2 x; O' M3 S. g( e9 X

typedef STRING OEM_STRING, *POEM_STRING;

$ R# u7 f+ k- Z7 \" O2 z* b

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

" ~: N2 _8 b! J3 ~0 b- G

RtlCopyUnicodeString()等等

D4 @8 L' S5 D' t8 C2 q4 |

l 结构

- q+ O# G* i7 p4 A7 g: Q

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

; `5 d- R! T1 R; C; ~% X

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

. Q9 [3 o/ a% L5 p4 S5 M5 l$ `# Q

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

# A7 B ^3 `/ K$ Y$ o

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

6 J( n; ~+ D3 u/ L% ^

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

- l) p# A7 F1 @: E: ~0 Q

typedef struct _OBJECT_ATTRIBUTES

5 s: J' @0 ~/ E2 t* n! q$ Q

{

8 S! p. L5 O0 c! {! k

ULONG Length;

6 b# W' E2 u" t; M S [7 }

HANDLE RootDirectory;

* b- L" Q4 X6 R0 s/ J$ v

PUNICODE_STRING ObjectName;

& O% F& z6 [% H6 O

ULONG Attributes;

* U) m( ?+ a, s O6 |

PVOID SecurityDescriptor;

* Y. Z; T7 ~. b- f9 P. x+ y" z9 @) D

PVOID SecurityQualityOfService;

8 L8 d& n; i9 N+ x, b& X

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

$ g" t: |8 @4 x/ S6 R) O

typedef struct _IO_STATUS_BLOCK

0 Y0 b* l0 r. k- ?

{

( v1 o2 U' x1 G V" b

NTSTATDS Status;

' V+ V0 |% d" o8 {% h$ }

ULONG Information;

/ g, z7 {& ?- A7 `$ {6 \

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

# V+ \& _- S7 d0 r. e5 s' {% Z |, z

typedef struct _LIST_ENTRY

& a3 ~: |* D) O1 W3 b

{

) c8 F/ v, h8 B" n5 L7 @; X; ^

Struct _LIST_ENTRY *Flink;

8 \2 D, V2 E2 ~. x {* U

Struct _LIST_ENTRY *Blink;

; n! d$ X! h7 q0 e% A

}LIST_ENTRY, *PLIST_ENTRY;

9 q. D" A) `8 g% @3 J

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

0 N" b' o9 {: H9 v1 E

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

2 ^( R8 [# y% b8 z

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

5 h3 C3 T% K' W M; F

typedef struct _CLIENT_ID

) j. P/ r/ }3 x

{ HANDLE UniqueProcess;

1 p6 R( d( l( }7 \* }& J

HANDLE UniqueThread;

1 r$ o' a. x+ O$ k1 n

)CLIENT_ID, *PCLIENT_ID;

$ N$ h7 o( b- b! T# Y( x4 M* u

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

9 w, [3 r: g% g5 L7 C

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

: q7 @2 D& u J8 H- p N

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

& G G, N+ X! [9 o

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

" q4 n' E+ _0 e! J! B7 J2 \. D9 {

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

* e% a! U1 l" N* T2 C; {

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

$ { S& c7 I9 N4 G) u* f+ h

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

, ?2 S: k B% ^. O8 f

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

6 V9 r1 s4 I$ r) p! \

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

4 h' R& \5 Y; _3 m3 r/ y/ K

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

# }/ k. D+ t3 h) }% u

0 z0 p h. t% v* j7 e# p 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

4 e% U8 ?9 @( A

指导1 y! L) G! q8 z0 \

1 @0 s: p0 j& _' T* D

* p, V' `; f. ~' 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-4-19 03:32 , Processed in 0.469100 second(s), 52 queries .

回顶部