|
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
列表 2-5. OBJECT_ATTRIBUTES 结构
OBJECT_ATTRIBUTES 结构仅描述函数使用的数据的细节, 列表 2-6 给出的 IO_STATUS_BLOCK 结构则用于记录对用户所提交的操作的处理结果。该结构很简单 ---Staus 成员存放一个 NTSTATUS 类型的代码,其值可能是 STATUS_SUCCESS 或定义于 ntstatus.h 中的所有可能的错误代码。 Information 成员在操作成功的情况下,提供与操作相关的附加数据。比如,如果函数返回一个数据块,该成员将被设置为该数据块的大小。
typedef struct _IO_STRATUS_BLOCK
{
NTSTATUS Status;
ULONG Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
列表 2-6. IO_STATUS_BLOCK 结构
另一个常见的 Windows 2000 数据类型是 LIST_ENTRY 结构,列表 2-7 给出了该结构的定义。内核使用该结构将所有对象维护在一个双向链表中。一个对象分属多个链表是很常见的, Flink 成员是一个向前链接,指向下一个 LIST_ENTRY 结构, Blink 成员则是一个向后链接,指向前一个 LIST_ENTRY 结构。通常情况下,这些链表都成环形,也就是说,最后一个 Flink 指向链表中的第一个 LIST_ENTRY 结构,而第一个 Blink 指向最后一个。这样就很容易双向遍历该链表。如果一个程序要遍历整个链表,它需要保存第一个 LIST_ENTRY 结构的地址,以判断是否已遍历了整个链表。如果链表仅包含一个 LIST_ENTRY 结构,那么该 LIST_ENTRY 结构必须引用其自身,也就是说, Flink 和 Blink 都指向其自己。
typedef struct _LIST_ENTRY
{
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY;
列表 2-7. LIST_ENTRY 结构

图 2-4 展示了对象链表各成员间的关系。对象 A1 、 A2 、 A3 属于同一链表。注意, A3 的 Flink 指向 A1 , A1 的 Blink 指向 A3 。最右边的对象 B1 仅有一个成员,因此,其 Flink 和 Blink 都指向相同的地址 --- 即对象 B1 的地址。典型的双向链表的例子是进程和线程链表。内部变量 PsActiveProcessHead 就是一个 LIST_ENTRY 结构,位于 ntoskrnl.exe 的 .data 节中。该变量指向系统进程列表的首部(通过其 Blink 指针)。你可以在内核调试器中使用 dd PsActiveProcessHead 来获取该链表的首部,然后通过其 Flink 和 Blink 指针遍历整个链表(仍使用 dd 命令)。当然,这种探测 Windows 进程的方法非常繁琐,但这可使你深入的观察基本的系统结构。 Windows 2000 Native API 提供了更便利的方法来枚举进程,如 NtQuerySystemInformation() 函数。
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
列表 2-8. CLIENT_ID 结构
处理进程和线程的 API 函数,如: NtOpenProcess() 和 NtOpenThread() ,使用 列表 2-8 给出的 CLIENT_ID 结构来和特定的进程、线程相关联。尽管其类型为 HANDLE ,实际上,从严格的意义上来讲 UniqueProcess 和 UniqueThread 成员并不是句柄( Handle ),它们都是整数型的进程 ID 和线程 ID 。即标准 Win32 函数 GetCurrentProcessId() 和 GetCurrentThreadId() 返回的 DWORD 类型的数值。
Windows 2000 执行体( Executive )还使用 CLIENT_ID 结构在全局范围内标识唯一的线程。例如,如果你使用内核调试器的 !thread 命令来显示当前线程参数,就会在输出的第一行看到类似“ Cid ppp.ttt ”的显示,其中“ ppp ”就是 CLIENT_ID 的 UniqueProcess 成员,而“ ttt ”则代表 UniqueThread ,如下所示。注意,我用黑体标出的地方。
kd> !thread
THREAD 83a51ba8 Cid 0a5c.0e64 Teb: 7ffdd000 Win32Thread: e14f4eb0 RUNNING on processor 0
Not impersonating
DeviceMap e20fb208
Owning Process 83a14708
Wait Start TickCount 906512 Elapsed Ticks: 68570
Context Switch Count 266 LargeStack
UserTime 00:00:00.0312
KernelTime 00:00:00.0015
。。。。。。。。。。。。。。。。。。。
Native API 的接口
对于内核模式的驱动程序,使用 Native API 的接口非常平常,就像在用户模式下的程序中调用 Win32 API 一样。 Windows 2000 DDK 提供的头文件和库包含了所有在调用 ntoskrnl.exe 导出的 Native API 时所需的信息。而另一方面, Win32 SDK 几乎不支持在程序中调用 ntdll.dll 导出的 Native API 。我说“几乎不”是因为 Win32 SDK 实际上提供了一个重要的东西:导入库 ntdll.lib ,该文件位于 \Program Files\Microsoft Platfrom SDK\Lib 目录中。如果没有这个库,将很难调用 ntdll.dll 导出的函数。
译注:
你需要安装 Windows 2000 DDK 才能获得 ntdll.lib
可以到 http://www.microsoft.com/msdownload/platformsdk/sdkupdate/ 下载最新的 SDK
将 NTDLL.DLL 导入库添加到工程中
在你能成功的编译和链接在用户模式下使用 ntdll.dll 导出函数的代码之前,你必须考虑如下的四个重点:
1. SDK 的头文件中,没有包含这些函数的原型。
2. SDK 文件中缺少这些函数使用的几个基本的数据类型。
3. SDK 和 DDK 头文件并不兼容,你不能将 #include <ntddk.h> 加入你的 Win32 C 源代码文件中。
4. ntdll.lib 并没有加入 Visual C/C++ 默认的导入库列表中
最后一个问题很容易解决,只需要编辑工程的设置属性,或者将如下内容加入你的源代码中, #pragma comment(linker,”defaultlib:ntdll.lib”) ,像在前面的 Windows 2000 运行时库一节解释的那样,这会在编译时,将 ntdll.dll 加入链接器的 /defaultlib 设置中。解决缺失的定义比较困难。因为不可能将 SDK 和 DDK 头文件整合到 C 程序中,最简易的解决方法是写一格自定义的头文件,在该头文件中包含所有调用 ntdll.dll 导出函数必须的定义。幸运的是,你不需要开始这项工作了,在本书光盘的 \src\common\include 目录下的 w2k_def.h 文件包含了你所需要的所有基本信息。该头文件将在第六、七两章中扮演重要角色。因为它被设计为可同时兼容用户模式和内核模式的工程,在用户模式代码中,你必须在 #include <w2k_def.h> 之前插入 #define _USER_MODE_ ,以加入仅出现在 DDK 中的一些定义。
有关 Native API 编程的很多详细信息都已经出版,目前看来,针对 Windows 2000 平台的好书是 Gary Nebbett's 的《 Windows NT/2000 Native API Reference 》。该书提供的示例程序较少,但它覆盖了 Windows NT/2000 平台上的所有 Native API ,还包括这些函数需要的数据结构定义以及其他必须的一些结构定义。
将在第六章介绍的 w2k_call.dll 示例库,演示了 w2k_def.h 的典型用法。第六章还将讨论另一种在用户模式进入 Windows 2000 内核的方法,此种方法不受限于 Native API 。事实上,这种技巧也可用于 ntoskrnl.exe ,对于所有加载到内核空间的模块,只要它们导出了函数或者可以和 .dbg 或 .pdb 符号文件相匹配都可以使用此方法。如你所见,在本书剩余章节中还有很多有趣的信息。但是,在我们到达那儿之前,我们会继续讨论一些基本的概念和技术。 上一页 [1] [2] [3] |