QQ登录

只需要一步,快速开始

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

浅析本机API

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

823

主题

3

听众

4048

积分

我的地盘我做主

该用户从未签到

发帖功臣 元老勋章

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

作者:sunwear[E.S.T] shellcoder@163.com + O+ W" z: N1 y来源:邪恶八进制 中国

, v9 i8 W8 V. K5 r/ p! A

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

0 k5 r; t: O% k. t( G+ ?; @

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

# P! M6 v, V- ^8 @/ {5 Y! n: m

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

1 O7 D6 L* T# F& d

没有任何技术上的限制,只不过微软不支持这种应用开发方法。 5 f+ n0 o- F! D4 d2 B

- ] E9 E$ i$ N# C- J7 o3 _5 w4 ^9 _

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

7 F5 E* m- r4 v) Y5 w

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

j u: C5 \3 W- \$ a, g

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

: Q2 l }. I; S* r* c. R; e

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

( F3 m6 R5 L9 e2 u' w

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

! ^' V; z' A$ {9 E

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

! ]2 H5 _% A. U1 S, `0 H4 I- J

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

7 p( f: E; K( P; b/ z

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

- p; q* J7 c. C0 ~# k" n% d0 Q

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

5 ~ J$ K3 V7 f) |8 h

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

5 l( V$ q) i% m

反编汇这个函数得到:

/ C& g3 q) U/ _& _: N* q

mov eax, 38h

! H e. l8 ]# i4 j K

lea edx, [esp+4]

0 i9 C, l( N/ S# ?: x

int 2Eh

# ?5 A( }) i" _

ret 28h

0 v5 D' D: ]4 C7 V; Z) {

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

8 L( b5 N: p5 x0 b# `( n

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

$ y8 o1 r0 H' T8 [& c/ T/ Y( n6 \

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

: c2 N/ L+ `! U4 U* {+ l

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

: n' j. a% ]' F9 s" {% }

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

: v% v3 e: Y. p! E% @. l

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

+ A" I4 ], X" V' l2 ^

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

9 K$ q, e/ I) r: G" {

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

9 O8 [! @7 \4 u! _5 W7 _8 Z

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

9 O) |3 w# ^6 l5 i9 a5 [9 I) t

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

# w: d! v$ x3 ^/ U* H0 f' c- j

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

- ^) C2 Y4 |7 [. ]/ w& W

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

! }# k2 c; V6 f- b+ t

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

+ ~! m0 M5 k" [

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

{ Z: Y ~6 l( x4 [' d

typedef NTPROC *PNTPROC;

. E" t7 D T) C+ [( f' i0 o* l B+ R: _

#define NTPROC_ sizeof (NTPROC)

) F5 v7 Y7 C- N/ h) e; m

typedef struct _SYSTEM_SERVICE_TABLE

* |4 w; z) @& G+ F5 Q# j

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

7 i, y4 f/ Z I$ }

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

2 Q6 w- B6 M* J3 N. J, P0 g

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

* }# }! W9 w0 E, I

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

$ B5 c6 C# r1 J, m$ R/ b. k- q

) SYSTEM_SERVICE_TABLE ,

' H9 Q o1 L4 W: ?; [/ A" O

* PSYSTEM_SERVICE_TABLE ,

# a+ ~% r& @/ Z+ t

* * PPSYSTEM_SERVICE_TABLE ;

! @% @- Z J7 \: @- }* {

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

: a4 F* Q- T. U' X' G/ [

typedef struct _SERVICE_DESCRIPTOR_TABLE

7 t) C6 A5 |% p3 k. f2 L3 Q

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

9 D0 T$ L: ?' y

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

5 Y5 w) b7 }/ ^+ Q% l0 t

SYSTEM_SERVICE_TABLE Table3; // 未使用

# W3 A. n6 ~- ~1 E2 t: J

SYSTEM_SERVICE_TABLE Table4; // 未使用

% [( e4 i/ r5 {9 y* s

} SERVICE_DESCRIPTOR_TABLE ,

6 g) b7 A6 L8 T

* PSERVICE_DESCRIPTOR_TABLE,

+ s u/ ]6 H7 F0 |

* PPSERVICE_DESCRIPTOR_TABLE ;

% P8 {" H8 w$ t, e' m; J% U- W- ~

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

) R# C. r9 k7 }, f

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

1 ?4 \# w4 _& P7 }5 u% A2 V

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

0 }5 }! b" j) p- }6 X

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

5 g. a5 v; O8 b5 i2 @

PSERVICE_DESCRIPTOR_TABLE psdt= KeServiceDescriptorTable;

9 H( Q$ e1 p! F2 L G

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

3 m: W7 Y9 d* d. c4 Z

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

! l5 u3 ?6 N0 ]- t

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

1 I& z# U0 v8 S

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

1 t% t d+ @% s; O% z* \; w# A, i

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

0 F0 s: V H2 H0 U4 v- c0 f

指向代表所有函数使用计数的DWORDS数组,这个信息能用于性能分析。& b& X% g4 h4 `7 M8 ^/ j% M 可以使用这个命令来显示:dd KeServiceDescriptorTable,调试器把此符号解析为0x8046e0c0。只有

- @+ X- r2 Z% s

前四行是最重要的,对应那四个SDT成员。 ! E' L) D' ~! m& z( X! x 运行这个命令:ln 8046e100,显示符号是KeServiceDescriptorTableShadow,说明第五个开始确实为

% g7 ^# |0 o1 j5 j

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

' J# I: e8 S% w, w7 ]2 A

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

7 x% ^) C6 Y/ U& |; L& T4 [

KeAddSystemServiceTable + x: ?% f* T# K6 j" t8 h+ Q3 ?此函数去填充这些位置。

( g3 C, Q3 W+ n# I; ]

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

$ ~' d4 u' C4 C. b8 u

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

% H( y! p) |# W" J2 C

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

9 }% N+ S4 x7 c( _; Z

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

9 [0 k/ S. j0 u4 `

映射至ntoskrnl表格,ID在

# p1 F0 w7 o/ h* R

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

7 S, @. i1 V& v# M( j7 S# X; U

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

* Q" Z7 a1 O0 g+ x& F

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

6 x I( O2 e, x1 [5 c) ]

STATUS_INVALID_SYSTEM_SERVICE。

/ s' R. P9 ?+ p( _/ R

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

8 t3 g8 R5 ^% M+ }" N+ |: D8 w5 ^- P4 M: n

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

. O0 z7 d) u( m G8 k

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

' l: ?) g6 D1 `5 r) J+ H

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

- t" q: E- \6 Y( u2 ?$ M7 Y2 G

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

/ B2 k5 P# H$ m% u9 N2 \' D- H

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

- U, }! ]. y$ m% |7 h% J

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

0 m3 c c/ S# L6 J, x& s/ [6 P

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

9 e# F& N0 W( J0 r( ]2 x

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

! A# R# X/ t3 _/ d) Z, S; w

3 s8 x1 R7 s/ v R2 o8 `. p2Eh的处理过程没有使用全局SDT KeServiceDescriptorTable。

3 D; u7 A& M) i, J3 b

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

) z/ }4 G+ _" s3 p

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

5 l& m2 [1 _4 b

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

7 \. p. m) p `# n

8 L$ ]6 ]/ Z( Y! K+ I3 W; M Windows 2000运行时库

+ y, A N! j# M/ g: q

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

7 m: g% R& f2 ~0 Q5 x& M% K

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

! w* P# [* L$ m5 S y! \

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

$ p* Q* |5 i e: s9 f5 _+ ^

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

% `5 `+ a4 \- G! d

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

8 ^9 d+ F4 V% z; R* M+ }! r

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

/ F' v, q: M& G' [# P/ S

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

7 ~3 ~! W: F# P

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

8 _# v1 ~* L, l6 ]! i7 Q

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

+ u! D$ _) t) g& E5 x6 w

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

' f% m; X! T9 x8 Y9 R- T

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

: m6 `$ y# Q3 ~9 g! m" \

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

( E. s3 M! q! q

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

" A/ @9 Y8 r* D3 t u4 o' Q5 u* E0 c- |2 n" H x1 m

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

D7 }, I* O! R# V: [

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

0 d8 m$ |& r2 H$ E& c) V

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

8 q$ R4 i+ ^% J+ m" V: R) F+ s ~

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

' s O9 r: f+ c8 r/ z5 P- S/ k

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

5 p+ S J6 [# v) r, S% k

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

/ r6 h4 c: G' V! }

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

7 y W0 G2 } q% [1 K- a6 B

常用数据结构

8 g8 D. k! ^) D

l 整数

* w1 N" M+ D0 I( D5 W m# p2 \

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

. _0 Q, B4 n/ r. f: M

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

: n4 D: E2 f( x9 J

不同。

2 E8 \3 ]/ m. c$ i

TABLE 2-3. Equivalent Integral Data Types

. \4 }/ v6 O @7 l$ z0 `# A) v! D' z

BITS MASM FUNDAMENTAL ALIAS #1 ALIAS #2 SIGNED

* M8 |3 R$ I0 P# X3 J% c+ a

8 BYTE unsigned char UCHAR CHAR 6 x# G+ ], I/ m/ V* ]9 Z- g) W/ T16 WORD unsigned short USHORT WCHAR SHORT

9 M0 e8 X" K( [0 H

32 DWORD unsigned long ULONG LONG

" a; b# D1 y- N

32 DWORD unsigned int UINT INT

0 s& s/ w# j2 N, N6 M

64 QWORD unsigned _int64 ULONGLONG DWORDLONG LONGLONG

( A% N8 D+ f. x2 [

80 TBYTE N/A

; N# G4 X0 Y. l

typedef union _LARGE_INTEGER

3 X( K1 |0 B( B! k, F

{ struct{

& R0 g9 H. E T9 v" j

ULONG LowPart;

* V0 R* A5 Q% n$ o

LONG HighPart;};

: z, \1 @: k( O0 V# G. N/ ^3 x

LONGLONG QuadPart;

Q. c/ }, x1 D. q( C! Y

}

: f! m4 X5 m8 n: ?6 o( [

LARGE_INTEGER , * PULARGE_INTEGER ;

+ Y& ^ E* g1 k2 A

typedef union _ULARGE_INTEGER{

$ @4 f0 b( Q G

struct{

9 K/ t& u8 O1 c! Q8 [

ULONG LowPart;

9 B1 D5 c6 ^$ j; \8 z

ULONG HighPart;}

7 P! I+ ] E) H9 s9 x" G

ULONGLONG QuadPart;

! V! D+ O+ N5 `

}ULARGE_INTEGER, *PULARGE_INTEGER;

" e+ W* k( w* @5 j5 ~2 X

l 字符

% @$ J4 @9 Y6 d( D

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

) a) F6 i. z: e

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

[# [9 W( l; S. X; f J/ p$ Y

typedef struct _UNICODE_STRING{

6 g) M6 I* ~4 U/ V% |

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

4 ]; u7 ?/ h0 S

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

6 Q8 A* _6 _6 \

PWSTR Buffer;}UNICODE_STRING , * PUNICODE_STRING ;

! Z! Y+ x0 O- Z

typedef struct _STRING{

" c u, E$ R7 U! Q4 J

USHORT Length;

( N( q9 M0 ?( Z' i" ~8 l

USHORT MaximumLength;

& s, i3 ?* B- G& x: a, `5 h6 ~

PCHAR Buffer;}STRING, *PSTRING;

( H2 v( v% D2 z. Z

typedef STRING ANSI_STRING, *PANSI_STRING;

8 v: T$ T& _* Z! ~, q1 @

typedef STRING OEM_STRING, *POEM_STRING;

4 ?( A' Y! d* K

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

9 C7 h( G" W( Q

RtlCopyUnicodeString()等等

& n2 G3 e% Z6 ~: _$ c

l 结构

1 t6 [% Y: F* m+ a

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

. U9 J0 \' y2 J2 G" j3 z

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

' ~" i U! w% G! q( e4 z6 |, V

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

! d4 W0 T! U; g( }

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

) b* j8 [6 G8 G- j( P9 Q

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

9 n2 S9 B2 O. O& r7 _) k/ P

typedef struct _OBJECT_ATTRIBUTES

5 @ t2 \( e4 M6 G! N

{

0 K6 t; @0 o! E! _' p5 @: E, a

ULONG Length;

7 Q, n) y( J3 A& j1 q$ Y# o1 r

HANDLE RootDirectory;

) W' c1 s" l1 m1 ^8 y4 V

PUNICODE_STRING ObjectName;

5 X3 b* @( R& E- ?) w; R0 V, u4 k

ULONG Attributes;

) p; ^/ W& T% Y4 @% d. q1 B: \

PVOID SecurityDescriptor;

8 l4 o$ j* u- b2 m9 S/ P& D

PVOID SecurityQualityOfService;

1 O3 b& P* v8 E" s

} OBJECT_ATTRIBDTES, *POBJECT_ ATTRIBUTES;

% f& ~& {7 c! G9 N5 D) G' q* f

typedef struct _IO_STATUS_BLOCK

2 V6 s% R" j( N; I( @) H: x

{

; r% ]2 B8 @/ {+ y

NTSTATDS Status;

7 Q. f" c; p; Y. w/ T* q

ULONG Information;

7 {; n7 y9 j, I2 Q+ j/ \

}IO_STATUS_BLOCK , * PIO_STATUS_BLOCK ;

' k+ X. k3 T" A8 b# Y. \& i

typedef struct _LIST_ENTRY

+ ^5 p" W$ \' b0 m

{

& `5 s B) T" T0 A

Struct _LIST_ENTRY *Flink;

0 }" K) e" n% T6 f! D

Struct _LIST_ENTRY *Blink;

; i* _3 A& k& g" M

}LIST_ENTRY, *PLIST_ENTRY;

# u$ j+ G6 _8 l0 B+ i

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

`4 S, R: Y y" J9 B$ |

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

5 l1 i2 V+ }1 h: N

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

# y5 v0 y; k- c9 T6 p+ H

typedef struct _CLIENT_ID

Y; d" f" x0 A, Q1 r

{ HANDLE UniqueProcess;

6 v! e; G5 y* C

HANDLE UniqueThread;

u! V, C4 Z1 e6 f3 ]- y8 \: n8 x

)CLIENT_ID, *PCLIENT_ID;

9 c$ O. y; w/ l+ r @% z8 L, I

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

: ^2 ^; E6 l+ O. S) p& m

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

8 D! f/ Y* a+ C. |" ]

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

! w# l, \* s1 p9 K" W9 Q4 H$ |% W

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

a" \* t0 o) Z/ _

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

) q, X& q( x- w

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

$ M A4 V( \2 j2 [3 M& k

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

( G3 k l% M4 \0 t- ^

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

2 ^/ X7 S2 P: b1 s3 ^/ ]

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

( B8 Q# O7 e! I' h' G

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

% ^. x3 }* S- s9 J- x

' {- w( C1 Z* a e- X( D 本文部分翻译于一篇电子书<win api about>.也感谢朋友GameHunter这位英语极好的朋友帮忙.与Free的

' M& I2 \: Y7 ^; Z6 i

指导2 }3 o. F {8 S1 Y; e( q7 R

5 j2 F* P5 [; p% J3 U7 ]& a) Z

8 W" O# j: t R) l/ ?

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

回顶部