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 FilesMicrosoft Platfrom SDKLib 目录中 。如果没有这个库,将很难调用 ntdll.dll 导出的函数 。
推荐阅读
- 2 《Undocumented Windows 2000 Secrets》翻译 --- 2
- 使用 Windows 2000 中的 IPSec IP 筛选器列表
- 火影忍者是哪一年出的
- 体验 Windows 2000 运行如飞的感觉
- Windows 2000 运行速度优化
- Windows 2000 自动更新桌面的实现
- Windows 2000磁盘文件的备份与还原
- Windows 2000 与其它系统启动速度差异的奥秘
- 三国杀2v2排位上分技巧
- 退出Windows 2000