QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com . l' v0 w0 E) W9 w2 L: d来源:邪恶八进制 中国

3 @5 T3 G' v8 Z2 W

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

E- ?' e8 V4 X+ n- A- Y

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

- ]* u! U9 M9 t2 Y1 E

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

; T* d+ \9 N: ?) i; `, V3 Y

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 . z, w6 Q" h2 K, _( [8 Q: N

: V ]( x2 L% G0 R5 M! _3 J- w

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

; o' M$ o+ z ~4 ?( r

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

7 O6 j- u& t$ o* g( u( J

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

7 w( a L7 `, t w c8 l) }9 J

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

3 P! I, ?+ G q8 j

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

+ n2 q2 F4 i* a8 x) X% o) }5 ^

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

) b+ a- J8 |; I! e( _

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

9 Y$ y( }. c+ x4 q/ z

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

& A% ]2 F# R& } j% P

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

* k* B Q, M9 [

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

- C" b6 K6 N. N% f

反编汇这个函数得到:

q. I; `, G0 Q8 H& d

mov eax, 38h

$ m K# o$ k9 N! x0 p1 [! T

lea edx, [esp+4]

# M2 _6 v! X- r8 ?* R0 Q

int 2Eh

' [( \5 t8 @4 W3 b2 F. y7 s

ret 28h

6 d3 U( E. p' Z1 i1 J

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

) M! u8 T* V8 c9 K0 P

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

/ r& i6 M9 E) ]

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

/ v% u+ a. H& P, ^! D9 p

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

5 O' |) s: O+ h7 ] p0 R& T

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

! V6 W% j7 i! t8 L: m/ {8 [

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

) \+ M; q- z) r& b

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

- Y7 r, a- o' n6 c

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

3 U& Y! B2 N" B( @9 }

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

- A) Y/ d' G) ^3 M4 j! T

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

/ b, \9 P) a0 [5 f3 w2 e0 U

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

: w7 K- x9 z% [* _

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

) B; a* @* h7 a2 Z5 k" a

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

0 g* r- K) @; f2 C" d; o. u3 c

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

f6 T) H% A$ B M( w/ n$ v

typedef NTPROC *PNTPROC;

# _, {+ _0 V& u! [9 A" n( o1 L! s

#define NTPROC_ sizeof (NTPROC)

1 T$ D/ g/ j6 Y# V

typedef struct _SYSTEM_SERVICE_TABLE

M$ ]" X( \# l& T" {1 H, `

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

% G8 e9 t% Z T0 y; |$ s

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

8 Y' Z( g8 S5 S

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

+ E5 C' q$ f. J

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

2 K+ l" y' S, B, ]

) SYSTEM_SERVICE_TABLE ,

% R( }7 S u3 }

* PSYSTEM_SERVICE_TABLE ,

, T8 T" ^- N8 E, k

* * PPSYSTEM_SERVICE_TABLE ;

6 u; e ^1 u: O

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

7 ]* v) Y8 ]3 C: d, D

typedef struct _SERVICE_DESCRIPTOR_TABLE

2 w' r+ E4 J" }1 k0 `8 z2 b

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

& A, G- q+ G* s: V7 V+ t! X$ Y

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

5 I/ i6 K5 b3 H

SYSTEM_SERVICE_TABLE Table3; // 未使用

/ O6 E' z, x: S5 V

SYSTEM_SERVICE_TABLE Table4; // 未使用

- z' s! P( V7 S& R& o

} SERVICE_DESCRIPTOR_TABLE ,

# d: b% s7 O9 Z$ T, K+ C% `! O

* PSERVICE_DESCRIPTOR_TABLE,

% t1 `0 H* @5 a3 [& M

* PPSERVICE_DESCRIPTOR_TABLE ;

, n7 S- d. S U& u# |* A: x4 Z

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

( t; z4 q; ]2 r7 Q$ E; V

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

; ?1 v0 i% i* r6 ^# Q( @/ i

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

8 R% p; L* k$ ^/ B0 L3 @: K

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

$ | _/ S/ E* A5 L/ U$ s) w: T

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

2 E* l r; B h- t

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

$ Q, I9 u3 @3 j7 ^# W+ x

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

?- R4 g! G5 y0 k! z7 ^: v

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

9 S2 h7 m n# R) I1 b3 z

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

2 s0 @: i! V: Y4 F1 P d

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

5 {: z: d' o; F. F$ {8 s8 a

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。 ! h: R' v3 x% J) I, j: g4 {5 z* N 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

: U% H/ u, J6 c, n

前四行是最重要的,对应那四个SDT成员。+ O* a' |4 J! B' I- O7 f 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

3 r7 e. q( R+ n# ^7 q

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

* W( ]3 Y7 N3 V: @

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

5 k* @' ~# G; m- w6 u6 N) l

KeAddSystemServiceTable Z3 D- e: D) G, l 此函数去填充这些位置。

" M/ M2 k j# ~

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

. ~ i& g% r$ v5 S

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

6 m: K2 W# N2 |3 s1 y! k

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

* ?! |/ t/ B; @

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

2 F) \8 ^; Y7 x% V

映射至ntoskrnl表格,ID在

+ } O8 X0 j" M* ~ G7 u

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

5 c7 Z: `- H! d3 {

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

5 R' M- |+ w/ k3 D8 v5 J# O

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

0 t, X; t: Y7 ~$ n, ^4 Q

STATUS_INVALID_SYSTEM_SERVICE。

0 M9 C- N2 y' a

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

) U, ^6 u2 p, d% F6 ^

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

, U \- v! Q6 y. B8 d1 T

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

* r s. z \ q0 `( ?# s

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

o8 c8 b) d4 e/ W

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

5 v2 \8 X9 N$ q# ]3 C; b1 C- b

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

, I. U6 b& O) S* h

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

! h8 l- ]) z9 L/ I; F5 [

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

7 M1 T: l# \; X% x4 K

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

( n& Y+ w; b- j/ k' i8 B

3 _& h6 w# u3 \! g" x5 Q* }. b 2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

! {$ Z R: k# N9 l; |+ n9 c

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

6 ?2 L9 o. u/ g0 u$ `6 G

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

! _& U' u _2 R: |4 H

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

. C3 e4 p+ d8 S5 D3 f$ z1 ?

( q+ u- S$ ^8 C O8 [ Windows 2000运行时库

. z, ?; S1 l% [- ]. N( a3 Z+ y& ^- B

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

7 C3 n1 T3 F4 E2 o0 Z1 y

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

( j+ u* V" U4 o' u% k" r6 a, y

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

% B% I6 i. P4 x1 j8 \' D. ]) i. D* P

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

7 _& B; Q: |4 D

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

& I7 O9 ~ C& S5 C

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

; r& V8 G" y1 ^2 X+ \. V) b

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

' x) @* q9 K8 \' E7 f$ M1 T

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

N" z' b2 u/ i, w0 c2 g5 Q2 e7 Z

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

@; ~1 z% u7 M5 Y8 i# L

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

) q0 [1 m. h! r/ o2 d6 Z, Q5 K$ Y

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

/ O/ k( ]; e! d6 q9 s1 Q/ y( t

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

5 R# m' r4 I/ X. _7 g4 C2 u) I0 I

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

# _: l1 O& m! v$ j# a # f& L) @( ]. V( f1 s" a2 @

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

_& w0 |& k: `. k, y$ K

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

, r9 M3 H3 d( @; m. n: E6 E

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

* {$ `# ]* j( L9 g7 T

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

' Z3 @2 C5 B1 |$ V' b9 \

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

5 k% l: B: S5 W8 L

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

" U& c4 i5 w( B* n4 ?% w( }

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

* L8 h, w [/ f% L: r% K

常用数据结构

& p+ ~0 y1 W0 B2 v8 D l. X9 R' R. {

l 整数

% n3 D0 k8 E3 |$ K) E0 ^

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

# X6 y1 U; n: j8 }1 J

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

7 t6 `" `6 @8 O" F/ w

不同。

X3 V' y5 H( ^0 a

TABLE 2-3. Equivalent Integral Data Types

6 y9 C) B% R) H2 f- H2 x9 q: m% k

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

2 R! i N, e) |! @ u; G

8 BYTE unsigned char UCHAR CHAR 3 g; I+ _3 g, R3 u3 [. u1 c) L6 y16 WORD unsigned short USHORT WCHAR SHORT

3 A" u6 n! @- ^% K; D1 q

32 DWORD unsigned long ULONG LONG

9 j, S) M" G/ ], H3 Z

32 DWORD unsigned int UINT INT

0 L+ d# }6 Y6 q

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

2 A4 c/ z& m, t# P3 ?" G

80 TBYTE N/A

6 ]1 @4 ^- | d( P/ y

typedef union _LARGE_INTEGER

' }2 c, w$ r6 |" P( v. N/ N0 i1 V

{ struct{

9 R" q: L) z' F. H* I# c

ULONG LowPart;

7 z, k' M; b# k8 s+ R+ Q

LONG HighPart;};

+ f3 p( ^3 g& d2 ~5 |) I

LONGLONG QuadPart;

3 x0 ]- x+ o8 ?: `( a' ~

}

) {/ w- i8 g% ^6 Y0 Q; w4 G

LARGE_INTEGER , * PULARGE_INTEGER ;

4 {: m1 }( {0 t* w+ t

typedef union _ULARGE_INTEGER{

9 V# |6 _' m4 }+ @% |

struct{

! r1 z$ E7 Q8 o/ D. X7 H

ULONG LowPart;

# N" \$ w6 Z8 H

ULONG HighPart;}

' {5 I) [9 Y) i0 x# i" ]* p" L/ K

ULONGLONG QuadPart;

8 x. Q2 t5 Z( J9 w$ S1 N' g

}ULARGE_INTEGER, *PULARGE_INTEGER;

5 f" S6 g8 t1 n# ~8 k% r

l 字符

1 x8 _2 \8 D( i; e8 ^$ m' k2 m0 {$ T

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

6 X7 y! c# p1 T3 X1 V' M8 m" z

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

; E' f$ E& V p+ {$ Z5 z

typedef struct _UNICODE_STRING{

5 z) \. S( f+ E! e) ], A# K* g5 j) c

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

4 [6 P* I! M) f# J

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

! [' n$ b0 V( T* M% E

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

4 _& c$ q" h: b- }/ `" K! P4 C

typedef struct _STRING{

8 A' j) F8 I F, z9 @

USHORT Length;

4 q7 R) G2 [4 v# C6 r$ f" W! X5 l

USHORT MaximumLength;

4 `, Q& V; a4 m6 @, x

PCHAR Buffer;}STRING, *PSTRING;

( L' D; L1 z4 L! K9 n6 I- J

typedef STRING ANSI_STRING, *PANSI_STRING;

; N$ \+ U; v% A! [0 w' ]5 L

typedef STRING OEM_STRING, *POEM_STRING;

; U/ |# g& X, v; A+ b) ?7 l

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

% p/ m* j" q7 }, T' Q# L5 B

RtlCopyUnicodeString()等等

2 V+ {- o* V; J0 f

l 结构

" P6 ]# p+ ]+ p

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

/ u' Q3 S* u, q7 V2 e c8 @. e# ?

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

3 R" Y9 e! L4 \5 _7 V8 f

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

! ^5 ~% a' P3 s0 `0 Z

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

! `3 P5 d1 p0 E7 n5 H0 t( N

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

4 W# s# K9 @ A1 i5 ^1 g- k

typedef struct _OBJECT_ATTRIBUTES

j; l; P' Q) L

{

0 d# S- \, |% `% G9 K: x6 g2 E

ULONG Length;

+ D0 H% y+ g9 ^5 {; n

HANDLE RootDirectory;

3 _4 F: N* E, ?0 k

PUNICODE_STRING ObjectName;

4 B( \% z& h0 T* a" e9 M9 E

ULONG Attributes;

9 P1 M$ M# k9 W' t* [2 ^

PVOID SecurityDescriptor;

1 T0 `, ?+ w7 E$ D1 m

PVOID SecurityQualityOfService;

" W7 Y5 u& t$ {# G" c

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

2 V( f# r2 Y5 [8 z. q

typedef struct _IO_STATUS_BLOCK

. y9 b1 p2 z8 @) t

{

/ M+ S6 |; N* V+ ?' f7 z

NTSTATDS Status;

+ N) F0 T; _$ Q, V4 K# r0 l& R' }

ULONG Information;

8 s3 R0 ~7 m4 ~8 b9 S @5 q

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

8 e' O' w2 a1 k; z

typedef struct _LIST_ENTRY

# x. D% z9 e; J7 B! {

{

+ ` U3 C7 Q" ?. U1 @# t

Struct _LIST_ENTRY *Flink;

5 X9 r8 g+ K" I/ {2 k

Struct _LIST_ENTRY *Blink;

' y* i% p$ ^3 p; D

}LIST_ENTRY, *PLIST_ENTRY;

, `* }* F9 P1 z+ z* { ?

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

; O2 k$ x4 P; l0 Q% G5 \

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

6 l+ d. m1 G* `3 g+ o4 s

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

, c$ g# ~4 ]/ r" p

typedef struct _CLIENT_ID

$ D% H3 F2 P# @- Z. w5 g5 Y. \

{ HANDLE UniqueProcess;

1 S- p0 t. T+ p# n; G( m+ E

HANDLE UniqueThread;

N+ [7 P( Z3 j8 l: w

)CLIENT_ID, *PCLIENT_ID;

% f. I. q, U$ y9 |, X* y: M+ r

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

; m& q8 I( j6 Z/ U/ A# h/ `$ U- s

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

3 F5 u$ d( z" E1 R0 r- J2 W0 u8 r

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

% t2 N% A% B3 V6 N* I1 i

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

+ k. m& I! d! k# q; ~

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

K; t) |1 X' j! u$ M9 `$ X

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

: X" N% I: @' a. ]# U6 j$ E

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

9 w0 D# t; ^+ l- P3 \9 G

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

1 H8 A$ P8 y2 Y1 g' V- y

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

2 E8 c, K$ ^. T/ x

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

7 M% r, W( y% F6 z5 B+ d" s

6 T! w+ Z( N( J. w6 A' m! P( U 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

4 b" l( e3 h, `3 {& Q

指导. f7 a0 a. Q* d. N

8 D8 x5 j3 l% v2 v$ d' [; m+ i

1 q' |3 G0 ]! L) J1 w& K2 A

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 08:16 , Processed in 0.394799 second(s), 51 queries .

回顶部