QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com . Q9 X" \+ `; |8 {; {2 U% x来源:邪恶八进制 中国

' d8 q; p- y0 D ~0 g

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

- R7 J% Q* P& a8 x0 N

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

6 J* l+ d% V" ?3 ]/ W0 @

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

7 s9 U7 v, n+ i) ~, H1 D

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 : X- ?2 w) q {. W- H

* {: E" `4 a/ n

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

5 U5 t4 i$ p# N2 Y% f ~+ }

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

% f! B& i. m6 \( W5 V' b N1 @& r

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

. C, e- r7 g, ^( h& o7 _

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

8 b* Q: b1 b" _0 t# k% h

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

* z' L* o! A; P5 {: i

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

4 l7 U. x) p0 o( L4 U5 p; w- k* e

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

% F# \, a) G" ?

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

7 X) M+ L) F$ U! k4 M4 n+ s+ u

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

+ @$ J' w3 s8 [# z s

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

% F9 q- x2 q6 M0 s* v* {

反编汇这个函数得到:

6 h9 s, O0 o+ N4 n6 C/ R6 H

mov eax, 38h

7 d" y9 z1 }4 Z5 q. h+ m, T) Y' e

lea edx, [esp+4]

/ k. Z1 d" L7 h% }1 u

int 2Eh

! g, W' T) A1 D4 D5 m; H9 I! R5 y

ret 28h

$ U2 q8 S$ E9 s

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

- C* k3 _. m3 h$ t! d# B

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

% Y$ w- t3 w9 p: n1 r" v

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

5 {, r3 M6 S5 {% j

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

' b# V: z2 |% T$ e: b/ y

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

6 W/ ?% u$ ]& E2 ?1 L; g% b

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

7 i! S# W9 @' q: O; w: N1 j5 v

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

( o4 B9 [: Q7 H" m$ R0 s

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

6 l4 r x; f5 b6 w# p

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

4 h, k" Z A( q5 R

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

2 N4 \0 e' C7 \0 L% x E

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

9 D( | `5 J) Q, o4 L

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

# e( v! t) N2 d! K) B9 [! Z( E

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

: k* M, t- L+ O7 D8 I

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

. l- x5 U3 U( X8 o2 t3 p

typedef NTPROC *PNTPROC;

, }+ M4 }9 V# S' T0 |

#define NTPROC_ sizeof (NTPROC)

. W6 d6 o1 o# I7 S

typedef struct _SYSTEM_SERVICE_TABLE

+ _( J# J! ?; X2 ]4 L" k

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

. N% \$ t! I' D& Q

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

& I3 M' D n! T2 u3 W: x4 V

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

9 w8 ~5 v8 d! m" a1 T6 X( K: M

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

# B9 M) _- Y D, o1 W

) SYSTEM_SERVICE_TABLE ,

5 b% V6 o7 E' M

* PSYSTEM_SERVICE_TABLE ,

, ~& u7 W% Y T$ b3 q& X

* * PPSYSTEM_SERVICE_TABLE ;

( e& v8 j8 U8 Y) C* e9 W

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

. \$ e+ ]* z e" F. d

typedef struct _SERVICE_DESCRIPTOR_TABLE

, p# E6 S, H4 @6 j6 b U: q

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

# M; L+ X* z$ p+ @& ^$ F( U7 S

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

" q, n! m+ `* k X

SYSTEM_SERVICE_TABLE Table3; // 未使用

, f" A9 u9 b+ ]0 c

SYSTEM_SERVICE_TABLE Table4; // 未使用

% r% R N' f# E& B

} SERVICE_DESCRIPTOR_TABLE ,

% N1 _* Q, u C7 t7 E) b

* PSERVICE_DESCRIPTOR_TABLE,

( a& A$ v4 j8 b2 }

* PPSERVICE_DESCRIPTOR_TABLE ;

# }$ P% j' o" I+ k) X) w

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

( H! J! }- v1 p7 [! F8 H

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

" ~( r7 }4 y+ k# Y: ~ h. m* ^& Z N

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

3 `& S4 x/ C# i ?

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

; B# P# y2 v5 i& C0 G7 [3 v

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

$ p* D7 C+ y# f0 L) [2 h

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

+ U) d, \7 n8 x; a8 Q

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

- T' y0 p& V1 c4 B- W4 P* i

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

, j8 p* Q2 I7 A3 o" E- [0 X' j

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

1 J9 W: V& V$ I' [- Q6 g( v; F

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

; R R [1 A& Y; P2 z

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。2 c' x# a" v6 P4 k! y" B 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

3 ~4 |1 e8 b, x* p, t7 @. T6 [1 b

前四行是最重要的,对应那四个SDT成员。- c+ V: l2 T" P$ l7 I 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

4 r/ Y* [9 A9 p& a# m$ E& s. {

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

9 q- J! g) O7 a! M

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

; X8 {7 i4 {9 ?* J& ?' z

KeAddSystemServiceTable: `0 W* u) r1 c9 F0 |: C 此函数去填充这些位置。

& l% q$ l0 h8 k+ H. Q1 g

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

$ r! c* c2 ~ _1 m9 l8 G1 d

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

! c2 L; D2 i% u( q5 K/ d

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

4 T" P6 A6 r0 G# w- ?

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

) w2 p! W& _4 m% E' a; a A# V, G1 H

映射至ntoskrnl表格,ID在

. n x) b: g2 W/ k; C: W9 ^- c

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

$ ^! v- L! @8 S3 l! T \ _

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

1 c: u e/ d7 n

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

1 q$ c$ Z. g, I" G. e" j s: O

STATUS_INVALID_SYSTEM_SERVICE。

$ n3 O: N5 J! O( O

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

( V; ?- }0 ]! }+ A* ]

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

6 f# P" R1 M8 |# H

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

7 ~7 n3 H, ^0 S5 L& H

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

+ ~; c2 o$ d$ n6 f5 t" e; h% h

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

6 U( l) n# o' Y i* W( \

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

0 L2 }9 Q7 _$ [. F

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

- H2 ^% V2 L4 a1 @3 L( t$ y

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

6 O+ V5 U) q. z7 V0 O! M8 c6 `, c5 q

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

* A' B% D! t0 N- |% ?

) F1 { ?( D! b' s+ M! X 2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

% {* |% e3 `) r( ]+ u4 r

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

3 r4 ], @" j' |4 J5 t8 k1 W

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

# B4 m4 [2 C) t: @0 A% _0 ^) d, d/ x

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

/ {( n0 @1 X; f7 k* X

0 B/ B7 K/ D9 F8 B+ ~ Windows 2000运行时库

9 `, I9 e% q4 L0 V' ^0 |

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

$ H, z0 K0 q+ n: _( `! _9 m D

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

4 O6 w% I6 @( p) V

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

3 T, |; k5 L$ t6 R4 q

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

% M$ o2 G1 P1 l. @: Z

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

5 r4 R: q$ c7 N/ ^/ _" W/ N

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

6 ^+ ~9 y6 y8 W/ x" k8 U# A

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

5 i" O3 O* I6 Q) Y

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

2 Q# \8 r0 }% w2 l! W! G, m

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

' M, I5 T+ h. i: p

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

0 Q# [4 p9 b8 e

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

* [9 ^& }7 ~9 m# l1 G

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

* W0 g! Z8 w; R; [* d

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

1 Y9 L; x, Q& P8 ?. Q9 S- K1 y ' f! J, Q+ C& d" i/ A

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

$ d' F& h8 L( }6 y% i( w! @3 J

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

8 P T! G- V( f9 V3 H

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

# y9 Y' j( y d) n+ E

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

! y; U9 Z8 Q+ Y' |

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

: A1 ], ~# G: Y6 Q! b

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

0 I2 e3 T1 B) Q2 ~; K7 P

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

& _* h! h+ b" y9 \# ?% M8 c

常用数据结构

( V6 r5 W9 r5 s" O& M6 Q5 n2 X# E0 F* L

l 整数

8 X( r# o' {$ W/ q2 u6 A

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

8 l- z1 L6 J, K8 N2 `3 \

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

3 D5 f) P/ o3 F& a$ t4 H" e

不同。

% h# a$ g D f7 P' l6 r7 W

TABLE 2-3. Equivalent Integral Data Types

+ p5 I( F; p+ w9 |! z6 @ C; K

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

- i: e0 w5 O, I/ ^) B4 ]0 j3 ?3 o

8 BYTE unsigned char UCHAR CHAR \& C4 Y, `4 |+ w( y) H& F16 WORD unsigned short USHORT WCHAR SHORT

; ^8 N* C, e7 X- h1 N8 l

32 DWORD unsigned long ULONG LONG

: e5 n7 F( r; {4 _

32 DWORD unsigned int UINT INT

0 h; `3 q0 W D5 T- }

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

& x, j$ {) E2 g

80 TBYTE N/A

. d& p; @ T8 ?- u) f

typedef union _LARGE_INTEGER

! \' f- V5 T o q9 O

{ struct{

! q+ J( u- k. @: Q; s

ULONG LowPart;

+ | ~1 n8 D" j% ?9 Y. G% h+ e

LONG HighPart;};

' v' q& M+ J5 w2 [+ N, l( o

LONGLONG QuadPart;

% @8 H* j- ]6 @( B& J" C

}

: ? n$ t1 K, R

LARGE_INTEGER , * PULARGE_INTEGER ;

% p% h$ s ~! y$ Z x& p

typedef union _ULARGE_INTEGER{

( v' k {8 ~+ B6 N: L! s: G

struct{

( ^- Z' f+ W1 T- ]

ULONG LowPart;

" t- N3 c: u" x1 k! z0 o

ULONG HighPart;}

, S, P) J" _9 d! z

ULONGLONG QuadPart;

! Q$ S, R: b/ e; H2 x0 T( u

}ULARGE_INTEGER, *PULARGE_INTEGER;

* ^$ c) B; n. R( J) A

l 字符

! y. G5 V4 k9 ?5 O4 L% c% V

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

: |4 M Y9 I) }, l5 D, n

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

; s( ] [* `% e, v4 b$ T. h

typedef struct _UNICODE_STRING{

8 k8 `; M D( ~

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

4 s6 l+ J( i: B7 j" D7 J0 p

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

0 l+ G( |4 y2 x4 Y3 U$ P

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

1 K) H5 x6 L: O1 _, H

typedef struct _STRING{

) Q4 P y+ \8 m9 a# L

USHORT Length;

, I$ Y4 t) |7 t {

USHORT MaximumLength;

+ d# x o3 k# a W8 @7 a3 l

PCHAR Buffer;}STRING, *PSTRING;

$ H9 m* E$ D! v" e3 @" n

typedef STRING ANSI_STRING, *PANSI_STRING;

2 B) W/ L! w; d* K+ h

typedef STRING OEM_STRING, *POEM_STRING;

3 ^3 t" w e, D

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

/ {8 p; O! s: B1 _

RtlCopyUnicodeString()等等

- d4 c$ E7 Z1 b2 o6 J/ n5 I+ z- W

l 结构

! P$ \" a% d" G/ |# Y5 H) W

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

2 b/ K8 d) k) {4 P

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

6 r S1 R8 J9 f& e- |

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

9 b; Y; T* u$ ^; `4 c

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

' \( @. j! A8 H( J) t+ |+ [' u

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

$ B. Q2 P5 [1 [. S& U& J' h

typedef struct _OBJECT_ATTRIBUTES

4 S2 |1 h& z+ `0 t

{

- [5 m6 `1 `1 ? C2 E

ULONG Length;

+ N$ H( x, [" o

HANDLE RootDirectory;

% w$ `% j3 |* f& D1 I

PUNICODE_STRING ObjectName;

9 v& x5 C1 R6 q8 X

ULONG Attributes;

; q5 T& B! `& p$ }& Q4 z$ M% d. U

PVOID SecurityDescriptor;

& g" |5 o2 n( H/ P3 Q

PVOID SecurityQualityOfService;

0 w8 ]' m) V# y5 K1 N% i4 E

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

+ s% f& a* |. A5 B1 x7 Y& Z

typedef struct _IO_STATUS_BLOCK

' T- U/ [% G O Q9 ~$ B

{

) n3 Q# \ k. s- z) H# }: b

NTSTATDS Status;

% e" {7 O/ T% b7 ~

ULONG Information;

$ k+ a. u$ }* v) o' x3 n

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

1 r! g2 o. Z% q

typedef struct _LIST_ENTRY

/ M; U$ v3 b; q

{

2 i6 L- N8 v2 u$ O8 [% |

Struct _LIST_ENTRY *Flink;

6 t* u M! s* F. r7 k

Struct _LIST_ENTRY *Blink;

" O X, D' n& W5 I8 n+ L. G U. o

}LIST_ENTRY, *PLIST_ENTRY;

! p( r5 K" v( Q9 b

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

( L: g) l( s- a/ @' k4 b! G

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

% s' t$ @4 K3 Z) R* I

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

& {6 z' o1 x5 B) E* q m1 @9 g

typedef struct _CLIENT_ID

8 e' P# a( y7 p+ j

{ HANDLE UniqueProcess;

. u- Q I% P' X% K' e9 ?- I

HANDLE UniqueThread;

6 w# c# E- H/ N' [7 D7 ]0 X/ j

)CLIENT_ID, *PCLIENT_ID;

$ b3 ?4 e# f/ m% I9 @5 Y+ J' k8 _

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

/ j$ _' `/ Q( W2 [

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

. j' w1 T. O. _: \9 X, c

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

- B1 m- V' D' S" y5 o$ R. i$ b5 Q0 d

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

- A/ Q+ U* y5 R5 b8 e; k. s! J ~

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

: G y* v2 [) q( g! H

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

/ y7 F% w8 i8 l# k: O% G4 s

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

6 U. o- @( n2 z+ N

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

$ D2 V- G5 _0 B) F

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

% ?8 M9 X1 k+ m8 r' |

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

" G+ D1 a% b. t" n; F1 G: h

4 X( V) u' k& a6 q+ o4 @! O) B- h 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

" Z' o/ j- e* I# @6 M8 [; k

指导 T+ U+ Z* \% M p% J

* J3 L* i% T/ A) B

! ^1 r1 j: i, U @* F

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 02:03 , Processed in 0.371823 second(s), 52 queries .

回顶部