|
Modules, Processes, Threads(3) WIN32WLK 原始码中的 MODULE32.H 内含一个 MODREF 结构定义。每一个MODREF 有以下字段: l 00h PMODREF pNextModRef 这个指针指向串行中的下一个 MODREF 结构。串行最后以 NULL 收尾。只要从行程的 process database 中取得 MODREFs 串行的头,然后一一追踪下去,就可以把行程所用到的每一个模块找出来。WIN32WLK 之中就有这样的示范。 l 10h WORD mteIndex 一个以 0 为基准的值,代表 pModuleTableArray 数组的索引。 l 18h PVOID ppdb 这是一个 PPROCESS_DATABASE,也就是一个指向 PROCESS_DATABASE 结构的指标,它提供一个从 MODREF 回头联结至「拥有此一 MODREF 之行程」的数据。稍后我会探讨 PROCESS_DATABASE。 由于 Windows 95 必须看起来像是每一个行程有自己的一个模块串行,所以和模块有关的 API 函式如 GetModuleHandle 不能够在全域模块表格(pModuleTableArray)上动作,它们只能够在自己的 MODREF 串行上动作,然后再藉由获得的索引参考到全域模块表格的元素项目。例如,GetProcAddress 只能够寻找陈列于current MODREF 串行中的模组。即使这个模块已经被其它行程加载,GetProcAddress 还是无法找到它 -- 除非现行(current)行程也载入了它。 KERNEL32 物件K32 对象是关键性的系统数据结构,放在 KERNEL32 heap 之中。有各式各样的K32 对象,统统都以相同的表头开始。决定它是否为一个 K32 对象的方法就是询问一个问题:应用程序之中可有 handles 代表此一对象?例如,应用程序可以拥有 file handles 或event handles,所以 file 和 event 都是K32 对象。我不曾看过任何应用程序代码拥有MODREF 或 IMTE 的 handles,所以它们并不是 K32 对象。 每一个 K32 对象都以一个共通的表头开始。此表头拥有以下字段: l 00h DWORD 对象型态。此值决定后续的结构成员如何解释。 l 04h DWORD 这是对象的参用次数(reference count),代表对象被使用的次数。例如,当你呼叫GetFileInformationByHandle,被询问的档案对象的参用次数就会累加 1;而在函式回返之前,参用次数又会减 1。 现在,你或许渴望知道有哪些 K32 对象型态。以下就是一份清单: K32OBJ_SEMAPHORE(0x1) K32OBJ_event(0x2) K32OBJ_mutex(0x3) K32OBJ_CRITICAL_SECTION(0x4) K32OBJ_PROCESS(0x5) K32OBJ_THREAD(0x6) K32OBJ_FILE(0x7) K32OBJ_CHANGE(0x8; 请看 FindFirstChangeNotification) K32OBJ_CONSOLE(0x9) K32OBJ_SCREEN_BUFFER(0xA) K32OBJ_MEM_MAPPED_FILE(0xB;请看 CreateFileMapping) K32OBJ_SERIAL(0xC) K32OBJ_DEVICE_IOCTL(0xD;请看 DeviceIoControl) K32OBJ_PIPE(0xE) K32OBJ_MAILSLOT(0xF) K32OBJ_TOOLHELP_SNAPSHOT(x10;请看 CreateToolhelp32Snapshot) K32OBJ_SOCKET(0x11) 本章剩余部份,我们主要的焦点放在行程对象和执行绪对象(IDs 5 和 6)。一个 processdatabase 其实就是一个 K32_PROCESS 对象,而一个 thread database 其实就是一个K32_THREAD 对象。就像你在「什么是 process handle?什么是 process ID?」一节即将看到的,一个 process handle table 其实就是一个指针数组,每一个指针指向各式各样的 K32 对象。 Windows 95 行程(Processes)行程其实就是一大堆对象的拥有权的集合。也就是说,行程拥有对象。行程可以拥有内存(更精确说是拥有 memory context),可以拥有 file handle,可以拥有执行绪,可以拥有一串行的 DLL 模块(被加载于此一行程的地址空间中)。 注意,行程并不代表「执行事实」(执行绪才是)。行程也不是 EXE 檔。在被载入之前,一个 EXE 档案只不过是一个程序。只有在被加载之后,Windows 95 才为它产生一个行程。一旦 Windows 95 产生一个行程,它也产生一个 memory context,容纳行程的执行绪在其中执行。此外,Windows 95 也产生出第一个执行绪,用来执行行程本身。如有必要,行程可以再产生执行绪。系统还会产生一个 file handle table,行程可以在其中持有一些开启的档案。最后,也是最重要的,Windows 95 产生一个 process database,用以表现这个行程。 Process database 是一种 K32 对象,内含与行程有关的大量信息。稍后我们将在「Windows95 Process Database(PDB)」一节中详细观察其字段。Process database 所使用的内存来自 KERNEL32 heap,因此所有的process database 都可以被其它行程看到。 Process database 含有一系列的执行绪、一系列的被加载模块、预设之process heap 的handle、指向 process handle table 的指标、以及指向 memory context 的指标。此外还有更多更多的东西。 什么是process handle?什么是process ID?Process handle 基本上和 file handle 一样。它是一个不被明了的数值,你不能够说它是任何东西的指标。系统内部事实上是把 K32 对象的 handle 当作 process handle table 的索引。而从该数组中获得的,才是一个 K32 对象指针。然而,由于应用程序不需要直接处理 handle table,所以 process handle 是没有用的。 记住,因为每一个应用程序有它自己的 handle table,所以不同的行程在各自的地址空间中拥有相同的 process handle 是绝对可能的。例如,正常而言每一个行程都有一个 process handle 为它而开,其值总是 1。也就是说,process handle 并不是可以赖以判别行程的资料。另一个例子是:如果程序为自己这个行程打开另一个 process handle,那么就有两个handles,对应至同一个行程。 一个 process handle 就像一个 file handle。在其行程之外别无意义。至于一个 process ID,则是在各行程之间独一无二不会冲突的数值,它是一个指标,指向process database 结构,甚至虽然微软在其中加了点料。WIN32WLK 示范神奇的转换公式,把 process ID 转换为一个有用的指标。 Windows 95 Process Database(PDB)Windows 95 的每一个 process database 都是一块从 KERNEL32 heap 配置而来的记忆体。KERNEL32 通常以 PDB 缩写字取代又臭又长的 "process database"。每一个 PDB 就是一个「第一字组为 5(K32OBJ_PROCESS)」的K32 物件。WIN32WLK 程序的PROCDB.H 中有一个 PDB 的C 语言定义,我们把每一个字段看清楚些: l 00h DWORD Type 此值必为 5(K32OBJ_PROCESS) l 04h DWORD cReference 参用次数(reference count)。也就是此一 PDB 被使用的次数。 l 08h DWORD un1 此字段之真实意义未知。似乎总是 0。 l 0Ch DWORD someEvent 这是一个指向 K32OBJ_EVENT 对象的指针。Event 对象用于WaitForSingleObject 这样的函式。 l 10h DWORD TerminationStatus 当你呼叫 GetExitCodeProcess,传回的就是这个值。所谓退出代码(exit code)就是 main或 WinMain 的回返值。它也可以被 ExitProcess 或 TerminateProcess 指定。当一个行程还在执行时,此字段为 0x103(STILL_ACTIVE)。 l 14h DWORD un2 此字段之真实意义未知。似乎总是 0。 l 18h DWORD DefaultHeap Default process heap 的地址。GetProcessHeap 传回的就是这个值。 l 1Ch DWORD MemoryContext 一个指标,指向行程的 memory context。所谓 memory context,内含 page directorymapping,用以提供行程在 4GB 地址空间中的私人区域。第5章对于 memory context 有更多描述。 l 20h DWORD flags 这个旗标值的意义如下: fDebugSingle 0x00000001 如果设立,表示行程正被除错中 fCreateProcessEvent 0x00000002 设立于除错行程中(在起始之后) fExitProcessEvent 0x00000004 可以在除错行程中(在结束时候)被设立 fWin16Process 0x00000008 表示一个 16 位程序 fDosProcess 0x00000010 表示一个 DOS 程序 fConsoleProcess 0x00000020 表示一个 console(文字模式)Win32 程序 fFileApisAreOem 0x00000040 请看 API 文件中的 SetFileApisToOEM 说明 fNukeProcess 0x00000080 fServiceProcess 0x00000100 例如 MSGSRV32.EXE fLoginScriptHack 0x00000800 可能是一个 Novell 网络签入行程 fSendDllNotifications 0x00200000 fDebugEventPending 0x00400000 例如,在除错器中被停下来 fNearlyTerminating 0x00800000 fFaulted 0x08000000 fTerminating 0x10000000 fTerminated 0x20000000 fInitError 0x40000000 fSignaled 0x80000000 l 24h DWORD pPSP 这个值是此一行程之 DOS PSP 的线性地址。Win16 和 Win32 程序都会设定此字段。此一线性地址总是在 1MB(真实模式 DOS 码所能看到的最高地址)之下。请参考28h 栏位。 l 28h WORD PSPSelector 这是一个 selector,指向此一行程之 DOS PSP。Win16 和 Win32 程序都有 DOS PSP。请参考 24h 字段。 l 2Ah WORD MTEIndex 这里内含一个全域模块表格(pModuleTableArray)的索引值。经由此索引值而取出之 IMTE正是此一行程对应的IMTE。IMTE 和pModuleTableArray 已于本章先前讨论过。 l 2Ch WORD cThreads 此字段记录此一行程拥有的执行绪个数。 l 2Eh WORD cNotTermThreads 此字段记录属于行程所有而尚未结束之执行绪个数。它怎么看都应该和上一字段相同。 l 30h WORD un3 此字段之真实意义未知。似乎总是 0。 l 32h WORD cRing0Threads 此字段记录由VMM32.VXD 管理的ring0 执行绪个数。对于一般程序而言,其值应该与cThreads 字段值相同。然而在KERNEL32.DLL 之中,此值比cThreads 字段值多1。 l 34h HANDLE HeapHandle 此字段是一个 HEAP handle,此 HEAP 内含属于这个行程的表格(或可能其它东西)。这里记录的总是 KERNEL32 的 shared heap handle。 l 38h HTASK W16TDB 这是一个 selector,指向行程相关的 Win16 Task Database(TDB)。Win16 和 Win32 程序都有 TDB selector,并且维护一个合法的 TDB。 l 3Ch DWORD MemMapFiles 一个指标,指向「此一行程所使用之内存映像文件所组成的串行」中的第一个节点。每一个内存映像文件都出现为串行中的一个节点。节点的格式是: DWORD 内存映像文件的基地址 DWORD 指向下一个节点;或是 0。 l 40h PENVIRONMENT_DATABASE pEDB 一个指标,指向 environment database。Environment database 中内含目前的子目录、环境变量、行程命令列、标准 handles(例如 stdin)、以及其它项目。我将在「EnvironmentDatabase」一节中描述其详细格式。 l 44h PHANDLE_TABLE pHandleTable 这是一个指标,指向 process handle table。所有的 handles 都在这里面,包括file handles、event handles、process handles 等等等。在 DOS/Win16 环境中的对等物品是 DOS 的System File Table(SFT)。关于 SFT 请参考Schulman 等人所著的 Undocumented DOS,第二版。然而毕竟有所不同。SFT 适用于整个系统,Win32 process handle table 则只适用于一个行程 l 48h strUCt _PROCESS_DATABASE * ParentPDB 这是一个指标,指向父行程的 PROCESS_DATABASE。对一般程序而言父行程是Windows95 的档案总管(EXPlorer)。MSGSRV32 则又是 Explorer 和initial "service" processes 的父行程。 l 4Ch PMODREF MODREFlist 这个字段指向行程的模块串行的起头。这也就是稍早描述过的 MODREFs 串行。 l 50h DWORD ThreadList 一个指标,指向此一行程所拥有的执行绪的串行。目前我还不知道这个串行的真正格式。 l 54h DWORD DebuggeeCB 这是一个被除错程序的 context。当一个行程被除错,这个字段即指向 2GB 以上的一个区域,该区域包含一个指针,指向被除错者的 process database。 l 58h DWORD LocalHeapFreeHead 这个指标指向「此一行程预设之 heap」的自由区块串行起头。第5章对于其格式有详细的描述。 l 5Ch DWORD InitialRing0ID 此字段意义未知。似乎总是为 0。 l 60h CRITICAL_SECTION crst 这个字段是一个 CRITICAL_SECTION,被各式各样的 API 用来同步控制同一行程中的各执行绪。稍后在许多伪码之中你会看到这个 critical section 的作用。 l 78h DWORD un4[3] 这三个 DWORD 之真实意义未知。似乎总是 0。 l 84h DWORD pConsole 如果这个行程使用 console(也就是说它是一个文字模式程序),此一字段即指向一个用于输出的 console 对象(K32OBJ_CONSOLE |