QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com - E) [0 l6 v' N8 Y3 F- Y z; C来源:邪恶八进制 中国

- O4 K+ y, `! b

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

! u, |2 }1 O, l0 h

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

" H$ o+ T2 ]/ X7 t! b4 Q

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

' r; K+ X1 w4 U! l) ?+ \2 d

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 0 O' w$ {7 P4 x( f7 I7 Q

( b) l" I& V* V& U" p

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

% f7 K2 u+ {, q: c7 u

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

$ y% c5 z3 `( N

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

3 Y' g- `! q# x# \/ V& Y# F

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

4 M. T* b5 e# N5 W7 Q8 [( s

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

* R" e" c4 N2 p; a2 o0 N7 Q8 g ~- U

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

2 q2 s- I0 m. L8 t& } ~

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

6 ? @" G- e/ |

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

, D6 v- Z' K8 y0 V: p: |) `

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

: R3 n( T( E6 I7 ]( s2 w- b" g

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

% [8 V! o v# z0 F2 P; J

反编汇这个函数得到:

2 N6 H7 \. U- \

mov eax, 38h

, l9 Q/ \9 ]* y/ y9 ^8 r: V

lea edx, [esp+4]

; [( l3 q& c3 Y9 Z" ?/ {

int 2Eh

) P. X; f; c! f# l( L. k: B) b0 A

ret 28h

+ R. S b: u/ {; q# I0 r

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

2 ~! k" l% }% j; y' J

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

$ e- m# ^: a& K* l& @- |

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

8 p; P, `/ c# y) v8 Z1 i

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

' P0 F; j7 n6 _

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

$ Y' }, `* q- x5 \. ~+ h5 g' o

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

* W6 l! j4 V. ^* W

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

, e$ \' u f; d! ?3 R) v, m3 {

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

! a) o4 U3 _( x9 R; n1 X

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

" u0 J' E. e5 ?# [2 Y2 H

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

7 W) B4 w7 V% ?

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

! p/ e; n$ j) {+ [

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

$ c# r. W: X& V; p% B, \5 n

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

! c+ ?( j$ g; i

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

; \* A) o% j$ K

typedef NTPROC *PNTPROC;

5 u3 ^5 R5 F9 }) ?

#define NTPROC_ sizeof (NTPROC)

5 \! }/ l* S6 X6 `. G" i( k* @' n

typedef struct _SYSTEM_SERVICE_TABLE

/ ^# t1 B, H2 i

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

1 K. O2 g& H% a1 h3 \

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

7 ^) G) s i2 M, t4 H: `0 x$ e" Q% a

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

$ W5 } X% }$ u4 |1 N5 t

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

4 |2 ] L* ~$ c p; G0 L) v8 O

) SYSTEM_SERVICE_TABLE ,

& U( L1 m; ]' c3 g

* PSYSTEM_SERVICE_TABLE ,

8 K* f9 `* K5 @

* * PPSYSTEM_SERVICE_TABLE ;

% E! z+ F) @) C6 F: ]/ \/ Q- |$ v0 {

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

; f/ K+ M9 [* w- [ y

typedef struct _SERVICE_DESCRIPTOR_TABLE

" h! X& |% w" f* X" Y* s9 l+ ]1 p3 j6 H

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

" v+ d+ a# `" e' w; J

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

8 f$ T/ u0 E& U% c2 }" i

SYSTEM_SERVICE_TABLE Table3; // 未使用

9 O m( u+ j$ ^4 T4 L6 G( ~

SYSTEM_SERVICE_TABLE Table4; // 未使用

8 e, L/ J" a' |3 O/ P

} SERVICE_DESCRIPTOR_TABLE ,

+ T4 A# W/ R# ~" ?0 x! K

* PSERVICE_DESCRIPTOR_TABLE,

7 k- F6 c- w* ~" k1 b. X* n

* PPSERVICE_DESCRIPTOR_TABLE ;

' r9 Y; w) b8 e+ Z9 K

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

" [' {5 V+ P5 H6 K7 N7 M$ p- @4 u

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

+ s6 o$ S; i2 I1 E- L! I- I: X

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

; c% y; w; T0 Z7 p

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

3 g. f& U0 D8 f5 S/ f- H5 I

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

4 M( i9 s8 x6 E1 B, g- D9 M4 q

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

- X- i- ^& z1 H2 Y& j( X

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

" W5 y" z% I( ?

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

* m* n2 k/ \& U

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

; k W& A7 ?/ _' l

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

: L! C6 P' Q% f* _- [3 Y* W- v

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。2 b/ ?' |: T: k u4 V 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

9 Q2 ?' H3 _7 Q y

前四行是最重要的,对应那四个SDT成员。 ( s7 B+ Z. M( j; \- T 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

$ @9 o, ?* Z: o* | w4 ^

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

/ e0 `9 ?, D5 b1 \( m& M

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

8 {" O$ ~2 u, v4 C* R/ A8 x6 C- |' b

KeAddSystemServiceTable 7 g2 j7 L- X+ l; W( h此函数去填充这些位置。

% W" j9 n7 \; C- K

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

6 b" r& C" P" J3 ^ U

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

7 r# J+ J- O Y3 E! s0 O

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

+ b) a- D8 V4 f2 p# E1 ]' W

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

4 P1 g# o& G, s* o0 r* r

映射至ntoskrnl表格,ID在

- v- I0 H/ {, q' M2 Z

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

2 x5 V$ A6 T- K& U

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

# [3 _, R9 \% S( w Z

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

, O! X6 Z! s* Y( V4 G

STATUS_INVALID_SYSTEM_SERVICE。

& Y6 Y4 |* z3 P/ G1 x* G% u

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

' Z& q, m+ Y( i

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

6 W+ T0 i. | w+ g

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

) b7 k# F' V. Q; e O Z& f$ p% I

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

" ?. Q/ n+ [& P' P6 v# `+ S3 Q. J

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

" p. U7 u; V& s m) N4 O

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

; N6 A/ U! k" N9 B+ F1 a

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

) u$ Y2 ]2 Q' Z! Y

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

$ G+ \; _3 j3 O& w+ l% k4 w7 ~/ I

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

6 x1 I+ U8 Z2 q: Y8 S4 K$ N% k

! q4 r. `' A$ D+ N! Q2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

5 z8 \- z* F: u" y3 V

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

; |4 ?1 ?: D: G3 u8 P# g" i+ I& `5 l

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

6 F/ |6 K+ u$ w% J; B

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

4 ]* B/ J5 O" V3 c6 e6 V4 m

1 g# Q" |* h- F% Z& ~9 R0 HWindows 2000运行时库

7 v. u+ s, D7 N

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

, v" p( N/ Y! M

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

4 l; l7 X- o f+ s% n4 r

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

; e5 z" g+ ^% f+ H' W, C

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

5 I! ?0 Z8 B0 M) K

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

6 n% ^1 R; S, I/ j: W3 B

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

9 E" G( l4 h! d

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

' _! i! `8 S# P2 t; X& U7 M

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

& C4 f0 N4 }: ~9 v- y# q

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

" _; d- t, i7 _) X6 Y

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

3 M" x3 n; V1 t3 L

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

, w& ]4 Y3 y: ^! t: Q

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

; J1 r, B# o, V; W$ ?2 X2 a! \

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

: n' ~2 |/ _) |+ g( a. U % z8 z9 }3 g+ J$ t9 e* Y: V

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

/ G; B/ j" p+ N4 Y

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

4 ~% @5 v% J" ?: k! s) c0 s' m! P

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

, s! e3 @& ]0 `$ X* a$ E6 S

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

) e) A+ J4 X; f5 a' Z: t* L9 \) W

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

. V' G* _# r* R4 d

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

7 E$ J, U/ [! u) _, o& E

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

( v$ ~5 [/ G8 `$ u3 b9 U( a

常用数据结构

q& a2 {0 w; }# e% z& D

l 整数

- y* B/ A, p" o$ @$ v

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

. I" U# h, V1 E; L- o

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

M3 ? R" _& W2 v1 |

不同。

, G' y% m) X l$ a4 g

TABLE 2-3. Equivalent Integral Data Types

0 G* c" p4 o6 ?2 f1 H3 n: Q3 ?

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

: X8 ~* @6 `3 |3 j

8 BYTE unsigned char UCHAR CHAR1 W5 |( j3 J* U2 V 16 WORD unsigned short USHORT WCHAR SHORT

) k) y8 [$ t1 {) X6 h, g

32 DWORD unsigned long ULONG LONG

, ~8 Q5 s0 q5 k y0 d

32 DWORD unsigned int UINT INT

# e. L& i7 X/ e3 n& {% w

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

, B) V0 ]& C1 p$ o& |8 F b

80 TBYTE N/A

& v9 s! p. x' K

typedef union _LARGE_INTEGER

0 A7 b& ?& |1 X- T

{ struct{

& u- H# T. M# Y$ ^# m

ULONG LowPart;

2 w% w9 A0 e" F- a

LONG HighPart;};

- X, z0 f; o9 b& W& |

LONGLONG QuadPart;

: U, r" |6 N/ E! W% ^

}

6 U+ J9 a2 B5 A6 t" e

LARGE_INTEGER , * PULARGE_INTEGER ;

/ h; {$ ~. e; R

typedef union _ULARGE_INTEGER{

% ]6 m3 @8 d. X1 s; z* Z% ~0 h0 T1 {9 Z9 U

struct{

% W$ W3 @1 L. @) G& Q3 O% X% f

ULONG LowPart;

. W3 Q/ a8 j9 H9 k0 W; |/ x! }/ n

ULONG HighPart;}

6 Q, _' B2 [! f' E. I J$ B

ULONGLONG QuadPart;

D# W8 ?" _: ]- O& |2 x; z

}ULARGE_INTEGER, *PULARGE_INTEGER;

/ @& s4 I9 {# |2 n! F

l 字符

Y/ p' z7 @8 _6 T

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

8 [, R- X# L3 B# [% u$ _4 J

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

$ j9 M! j( N4 O7 K3 r

typedef struct _UNICODE_STRING{

) p& [& |& W& F6 c( E6 J3 N

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

: T6 N, h" j! E# k& {1 L+ [ o

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

/ r2 c* \. N% f, i3 e8 S4 m

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

% t( H, ^2 b4 u2 s& r

typedef struct _STRING{

* [3 p/ h! q/ o6 C5 n

USHORT Length;

4 N9 x9 s: W3 R9 t; b

USHORT MaximumLength;

- x2 W- x' p! u Y' r$ r5 ]' ~) j; {3 L

PCHAR Buffer;}STRING, *PSTRING;

) s3 i+ a# `6 Q, \/ q

typedef STRING ANSI_STRING, *PANSI_STRING;

) W0 h" Q/ v5 V: G; a, _2 a

typedef STRING OEM_STRING, *POEM_STRING;

& y) k( d1 A6 D4 x g

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

1 q4 n" U [, Y. P% J9 f7 t+ k

RtlCopyUnicodeString()等等

2 o# G1 a+ X( _9 a/ H, l/ |

l 结构

4 D$ T0 u1 }: R1 w" h

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

. N( s* z+ O& F

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

! M& o0 k$ \* }! N% P

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

" T1 A2 ?6 V9 c' V' i2 a

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

' v. O4 S$ I1 N7 {# }

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

$ e# w2 G# a& J: P% u

typedef struct _OBJECT_ATTRIBUTES

/ n3 ^/ S# X: u/ r: a) Z* Y9 ^

{

2 ^ Y# ?8 s* u; n

ULONG Length;

4 `5 _4 u4 h2 ~" ^. N6 y

HANDLE RootDirectory;

3 y, P4 v4 M) _$ Z7 k! s

PUNICODE_STRING ObjectName;

6 G' v' `) E1 }& _

ULONG Attributes;

: Z$ B, }+ \! w+ A/ ?8 M) |

PVOID SecurityDescriptor;

( D4 H- ~) w' i2 [! p. O- s

PVOID SecurityQualityOfService;

. A' d/ l2 o1 @5 Y

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

) d$ ^; T; ^* }1 g' p( |3 W

typedef struct _IO_STATUS_BLOCK

9 v' X7 {% K$ j# [

{

4 c- z/ D7 z& n5 K' _6 H6 }

NTSTATDS Status;

$ u0 s# c- |& C

ULONG Information;

7 b+ ?# Z1 q( B- E: T

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

! v/ |9 T; Q/ ?7 H1 d1 L3 Z" E

typedef struct _LIST_ENTRY

5 _6 g$ p$ J4 D3 L7 {6 x

{

5 O$ W" O' P! W( P1 N% V+ @) m

Struct _LIST_ENTRY *Flink;

4 X: M5 F! }$ H6 ?' O3 q- [# t

Struct _LIST_ENTRY *Blink;

, Y" m6 f8 i* k0 h7 K- A

}LIST_ENTRY, *PLIST_ENTRY;

* W/ A' J# M; A: F+ U3 K

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

# N/ b$ I; b- I3 x: o0 c: T

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

4 y. V) T% Y0 v9 }" k& ?- P* R

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

5 G# ^% o4 P5 R% }' t: a( k

typedef struct _CLIENT_ID

3 J( L6 W# I1 S$ M

{ HANDLE UniqueProcess;

z% H o; @2 i8 f7 a6 C- \! n

HANDLE UniqueThread;

% T6 W! s% t4 @ w

)CLIENT_ID, *PCLIENT_ID;

" N( D. A' y* i `

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

9 d2 E ~% \& A1 J3 k5 r1 d

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

& A# ~" J# c: a ]* @

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

8 i: F- f2 R+ c' @) s

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

( u) ~' m- |2 U1 B6 Y# s: U- C

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

$ U1 O5 A/ a7 k

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

0 a4 |% G- y; l. g' t; M: k h3 h- i

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

9 K! A# L) F0 Y' D$ m+ }

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

0 r) D; j' l2 H! ~( ~$ ^

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

+ O: ]% R; E# Z% H" I0 z

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

. t6 f6 U* b& A$ d& o+ w

. K, D0 \; ^9 P0 x3 z本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

( g- b# s0 e3 L- r

指导 ; G3 K. h2 B# b& Y* O1 E

1 L8 s0 s9 u# d9 r& O

- E" N+ e( o% K- h/ L5 H( i

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-14 06:04 , Processed in 0.463562 second(s), 52 queries .

回顶部