《Undocumented Windows 2000 Secrets》翻译 --- 第七章(3)

第七章  Windows 2000的对象管理

翻译:Kendiv( [email protected] )

更新:Sunday, May 22, 2005

声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利。

进程和线程对象

或许最吸引人同时也是最复杂的Windows 2000对象就是进程、线程对象了。这些对象通常是软件开发人员必须处理的顶层对象。一个内核模式组件总是运行在某个线程的上下文中,而一个线程也总是属于某个进程。因此,从本质上来看,进城和线程对象都是类型对象,在调试过程中它们被研究的频率最高。Windows 2000内核调试器解决了这一问题,它提供了两个命令:!processfields!threadfields译注,最新版的内核调试器已不在支持这两个命令,对应的新命令为:dt nt!_EPROCESSdt nt!_ETHREAD),这两个命令由kdextx86.dll导出。这两个命令可针对EPROCESSETHREAD结构,分别输出由名称/偏移量构成的简单列表(参考第一章的示例1-1示例1-2)。这两个结构体都没有文档化,这两个命令是目前唯一的官方信息来源。

 

译注:

Windows XP Professional + SP2下,HANDLE_TABLE结构发生了很大的变化,如下所示:

kd> dt nt!_HANDLE_TABLE

   +0x000 TableCode        : Uint4B

   +0x004 QuotaProcess     : Ptr32 _EPROCESS

   +0x008 UniqueProcessId  : Ptr32 Void

   +0x 00c HandleTableLock  : [4] _EX_PUSH_LOCK

   +0x 01c HandleTableList  : _LIST_ENTRY

   +0x024 HandleContentionEvent : _EX_PUSH_LOCK

   +0x028 DebugInfo        : Ptr32 _HANDLE_TRACE_DEBUG_INFO

   +0x 02c ExtraInfoPages   : Int4B

   +0x030 FirstFree        : Uint4B

   +0x034 LastFree         : Uint4B

   +0x038 NextHandleNeedingPool : Uint4B

   +0x 03c HandleCount      : Int4B

   +0x040 Flags            : Uint4B

   +0x040 StrictFIFO       : Pos 0, 1 Bit

 

可以预见本章所给的w2k_obj.exeWindows XP下将无法工作。

 

 

很不幸,!processfields命令的输出(参见第一章中的示例1-1)中最开始的是一个名为Pcb的成员,该成员指向一个0x 6C 字节大小的子结构(这是因为下一个成员ExitStatus的偏移量为0x 6c )。Pcb是一个KPROCESS结构,该结构没有任何文档记载。这种排列非常有趣:显然,一个进程可由嵌入在庞大的可执行对象中的一个很小的内核对象来描述。这种嵌套式的结构也出现在线程对象中。从调试器的!threadfields命令(参见第一章中的示例1-2)的输出可以看到在ETHREAD结构的开始位置存在一个大小为0x1B0字节的Tcb成员。这就是KTHREAD结构,它代表可执行对象中的另一个内核对象。

 

译注:

本书中所给的EPROCESSETHREAD结构的定义,在Windows XP+SP2中已发生了很大变化。

 

尽管内核调试器提供的有关进程和线程对象的符号化信息非常有帮助,但没有提供足够的信息来说明这些结构体成员的类型。而且,PcbTcb成员的不透明性进一步加大了掌握这些对象的本质的难度。在内核调试器生成的反汇编列表中,你会发现有很多指令涉及到这些不透明的数据结构。它们使用的偏移量对于我们来说毫无价值,它无法提供任何有关这些数据的名字和类型的信息。因此,我收集了各方面的资料再加上我自己的研究结果,以推断出这些对象的具体类型。列表7-11和列表7-12给出部分研究结果,这两个列表分别给出了KPROCESSKTHREAD结构的定义。在这两个结构体开始处的DISPATCHER_HEADER结构明确表示了这两个结构体都属于Dispatcher对象。这意味着可使用KeWaitForSingleObject()KeWaitForMultipleObjects()来等待它们。当线程结束执行时,线程对象才会变为“有信号”状态,一个进程仅当其所有线程都终止时才会进入“有信号”状态。这对于Win32程序员来说没什么新鲜的。不过,现在你终于明白了为什么等待一个进程和线程对象是可行的了。

 

typedef struct _KPROCESS

        {

/*000*/ DISPATCHER_HEADER Header; // DO_TYPE_PROCESS (0x1B)

/*010*/ LIST_ENTRY        ProfileListHead;

/*018*/ DWORD             DirectoryTableBase;

/* 01C */ DWORD             PageTableBase;

/*020*/ KGDTENTRY         LdtDescriptor;

/*028*/ KIDTENTRY         Int21Descriptor;

/*030*/ WORD              IopmOffset;

/*032*/ BYTE              Iopl;

/*033*/ BOOLEAN           VdmFlag;

/*034*/ DWORD             ActiveProcessors;

/*038*/ DWORD             KernelTime; // ticks

/* 03C */ DWORD             UserTime;   // ticks

/*040*/ LIST_ENTRY        ReadyListHead;

/*048*/ LIST_ENTRY        SwapListEntry;

/*050*/ LIST_ENTRY        ThreadListHead; // KTHREAD.ThreadListEntry

/*058*/ PVOID             ProcessLock;

/* 05C */ KAFFINITY         Affinity;

/*060*/ WORD              StackCount;

/*062*/ BYTE              BasePriority;

/*063*/ BYTE              ThreadQuantum;

/*064*/ BOOLEAN           AutoAlignment;

/*065*/ BYTE              State;

/*066*/ BYTE              ThreadSeed;

/*067*/ BOOLEAN           DisableBoost;

/*068*/ DWORD             d068;

/* 06C */ }

        KPROCESS,

     * PKPROCESS,

    **PPKPROCESS;

 

#define KPROCESS_ /

        sizeof (KPROCESS)

列表7-11.  KPROCESS结构

 

 

typedef struct _KTHREAD

        {

/*000*/ DISPATCHER_HEADER         Header; // DO_TYPE_THREAD (0x 6C )

/*010*/ LIST_ENTRY                MutantListHead;

/*018*/ PVOID                     InitialStack;

/* 01C */ PVOID                     StackLimit;

/*020*/ struct _TEB              *Teb;

/*024*/ PVOID                     TlsArray;

/*028*/ PVOID                     KernelStack;

/* 02C */ BOOLEAN                   DebugActive;

/*02D*/ BYTE                      State; // THREAD_STATE_*

/*02E*/ BOOLEAN                   Alerted;

/* 02F */ BYTE                      bReserved01;

/*030*/ BYTE                      Iopl;

/*031*/ BYTE                      NpxState;

/*032*/ BYTE                      Saturation;

/*033*/ BYTE                      Priority;

/*034*/ KAPC_STATE                ApcState;

/* 04C */ DWORD                     ContextSwitches;

/*050*/ DWORD                     WaitStatus;

/*054*/ BYTE                      WaitIrql;

/*055*/ BYTE                      WaitMode;

/*056*/ BYTE                      WaitNext;

/*057*/ BYTE                      WaitReason;

/*058*/ PLIST_ENTRY               WaitBlockList;

/* 05C */ LIST_ENTRY                WaitListEntry;

/*064*/ DWORD                     WaitTime;

/*068*/ BYTE                      BasePriority;

/*069*/ BYTE                      DecrementCount;

/* 06A */ BYTE                      PriorityDecrement;

/*06B*/ BYTE                      Quantum;

/* 06C */ KWAIT_BLOCK               WaitBlock [4];

/*0CC*/ DWORD                     LegoData;

/*0D0*/ DWORD                     KernelApcDisable;

/*0D4*/ KAFFINITY                 UserAffinity;

/*0D8*/ BOOLEAN                   SystemAffinityActive;

/*0D9*/ BYTE                      Pad [3];

/*0DC*/ PSERVICE_DESCRIPTOR_TABLE pServiceDescriptorTable;

/*0E0*/ PVOID                     Queue;

/*0E4*/ PVOID                     ApcQueueLock;

/*0E8*/ KTIMER                    Timer;

/*110*/ LIST_ENTRY                QueueListEntry;

/*118*/ KAFFINITY                 Affinity;

/* 11C */ BOOLEAN                   Preempted;

/*11D*/ BOOLEAN                   ProcessReadyQueue;

/*11E*/ BOOLEAN                   KernelStackResident;

/* 11F */ BYTE                      NextProcessor;

/*120*/ PVOID                     CallbackStack;

/*124*/ struct _WIN32_THREAD     *Win32Thread;

/*128*/ PVOID                     TrapFrame;

/* 12C */ PKAPC_STATE               ApcStatePointer;

/*130*/ PVOID                     p130;

/*134*/ BOOLEAN                   EnableStackSwap;

/*135*/ BOOLEAN                   LargeStack;

/*136*/ BYTE                      ResourceIndex;

/*137*/ KPROCESSOR_MODE           PreviousMode;

/*138*/ DWORD                     KernelTime; // ticks

/* 13C */ DWORD                     UserTime;   // ticks

/*140*/ KAPC_STATE                SavedApcState;

/*157*/ BYTE                      bReserved02;

/*158*/ BOOLEAN                   Alertable;

/*159*/ BYTE                      ApcStateIndex;

/* 15A */ BOOLEAN                   ApcQueueable;

/*15B*/ BOOLEAN                   AutoAlignment;

/* 15C */ PVOID                     StackBase;

/*160*/ KAPC                      SuspendApc;

/*190*/ KSEMAPHORE                SuspendSemaphore;

/* 1A 4*/ LIST_ENTRY                ThreadListEntry;  // see KPROCESS

/* 1AC */ BYTE                      FreezeCount;

/*1AD*/ BYTE                      SuspendCount;

/*1AE*/ BYTE                      IdealProcessor;

/*1AF*/ BOOLEAN                   DisableBoost;

/*1B0*/ }

        KTHREAD,

     * PKTHREAD,

    **PPKTHREAD;

 

#define KTHREAD_ /

        sizeof (KTHREAD)

列表7-12.  KTHREAD结构

 

KPROCESS结构通过自己的ThreadListHead成员来组织它下属的线程对象,对于由KTHREAD对象构成的双向链表来说,ThreadListHead既是该链表的起点也是该链表的终点(呵呵,又是一个环形链表)。每个线程也有一个类似的结构来保存各自的对象,那就是它们的ThreadListEntry成员,线程自己的链表是由一个个LIST_ENTRY结构组成。要得到这一个个LIST_ENTRY所在的对象的基地址,则必须从LIST_ENTRY结构的地址值中减去其在所属结构中的偏移量才能得到,这是因为LIST_ENTRY结构的FlinkBlink成员总是指向链表中的下一个LIST_ENTRY结构,而不是拥有该节点结构的对象。这种特性可以很容易的将同一个对象加入到多个链表中而不会产生冲突。

 

列表7-11列表7-12以及下面即将给出的列表中,你会发现一个特殊的成员,该成员的名字由一个小写字符和三个十六进制数组成。这些成员的确切含义我目前还不清楚。开始的第一个字符反映出了该成员可能的类型(比如,d表示DWORDp表示PVOID),随后的数字表示该成员相对于结构基地址的偏移量。

 

列表7-13列表7-14分别给出了EPROCESSETHREAD可执行对象。这些结构包含一些还未确定的成员,希望在本书的鼓励下,有人能把它们确定下来。不过,最重要和最常用的成员都已确定下来,至少我们知道丢失了那些信息。

 

typedef struct _EPROCESS

        {

/*000*/ KPROCESS               Pcb;

/* 06C */ NTSTATUS               ExitStatus;

/*070*/ KEVENT                 LockEvent;

/*080*/ DWORD                  LockCount;

/*084*/ DWORD                  d084;

/*088*/ LARGE_INTEGER          CreateTime;

/*090*/ LARGE_INTEGER          ExitTime;

/*098*/ PVOID                  LockOwner;

/* 09C */ DWORD                  UniqueProcessId;

/* 0A 0*/ LIST_ENTRY             ActiveProcessLinks;

/* 0A 8*/ DWORD                  QuotaPeakPoolUsage [2]; // NP, P

/*0B0*/ DWORD                  QuotaPoolUsage     [2]; // NP, P

/*0B8*/ DWORD                  PagefileUsage;

/*0BC*/ DWORD                  CommitCharge;

/* 0C 0*/ DWORD                  PeakPagefileUsage;

/* 0C 4*/ DWORD                  PeakVirtualSize;

/* 0C 8*/ LARGE_INTEGER          VirtualSize;

/*0D0*/ MMSUPPORT              Vm;

/*100*/ DWORD                  d100;

/*104*/ DWORD                  d104;

/*108*/ DWORD                  d108;

/* 10C */ DWORD                  d 10C ;

/*110*/ DWORD                  d110;

/*114*/ DWORD                  d114;

/*118*/ DWORD                  d118;

/* 11C */ DWORD                  d 11C ;

/*120*/ PVOID                  DebugPort;

/*124*/ PVOID                  ExceptionPort;

/*128*/ PHANDLE_TABLE          ObjectTable;

/* 12C */ PVOID                  Token;

/*130*/ FAST_MUTEX             WorkingSetLock;

/*150*/ DWORD                  WorkingSetPage;

/*154*/ BOOLEAN                ProcessOutswapEnabled;

/*155*/ BOOLEAN                ProcessOutswapped;

/*156*/ BOOLEAN                AddressSpaceInitialized;

/*157*/ BOOLEAN                AddressSpaceDeleted;

/*158*/ FAST_MUTEX             AddressCreationLock;

/*178*/ KSPIN_LOCK             HyperSpaceLock;

/* 17C */ DWORD                  ForkInProgress;

/*180*/ WORD                   VmOperation;

/*182*/ BOOLEAN                ForkWasSuccessful;

/*183*/ BYTE                   MmAgressiveWsTrimMask;

/*184*/ DWORD                  VmOperationEvent;

/*188*/ HARDWARE_PTE           PageDirectoryPte;

/* 18C */ DWORD                  LastFaultCount;

/*190*/ DWORD                  ModifiedPageCount;

/*194*/ PVOID                  VadRoot;

/*198*/ PVOID                  VadHint;

/* 19C */ PVOID                  CloneRoot;

/* 1A 0*/ DWORD                  NumberOfPrivatePages;

/* 1A 4*/ DWORD                  NumberOfLockedPages;

/* 1A 8*/ WORD                   NextPageColor;

/*1AA*/ BOOLEAN                ExitProcessCalled;

/*1AB*/ BOOLEAN                CreateProcessReported;

/* 1AC */ HANDLE                 SectionHandle;

/*1B0*/ struct _PEB           *Peb;

/*1B4*/ PVOID                  SectionBaseAddress;

/*1B8*/ PQUOTA_BLOCK           QuotaBlock;

/*1BC*/ NTSTATUS               LastThreadExitStatus;

/* 1C 0*/ DWORD                  WorkingSetWatch;

/* 1C 4*/ HANDLE                 Win32WindowStation;

/* 1C 8*/ DWORD                  InheritedFromUniqueProcessId;

/*1CC*/ ACCESS_MASK            GrantedAccess;

/*1D0*/ DWORD                  DefaultHardErrorProcessing; // HEM_*

/*1D4*/ DWORD                  LdtInformation;

/*1D8*/ PVOID                  VadFreeHint;

/*1DC*/ DWORD                  VdmObjects;

/*1E0*/ PVOID                  DeviceMap; // 0x24 bytes

/*1E4*/ DWORD                  SessionId;

/*1E8*/ DWORD                  d1E8;

/*1EC*/ DWORD                  d1EC;

/* 1F 0*/ DWORD                  d 1F 0;

/* 1F 4*/ DWORD                  d 1F 4;

/* 1F 8*/ DWORD                  d 1F 8;

/*1FC*/ BYTE                   ImageFileName [16];

/* 20C */ DWORD                  VmTrimFaultValue;

/*210*/ BYTE                   SetTimerResolution;

/*211*/ BYTE                   PriorityClass;

/*212*/ union

            {

            struct

                {

/*212*/         BYTE               SubSystemMinorVersion;

/*213*/         BYTE               SubSystemMajorVersion;

                };

            struct

                {

/*212*/         WORD               SubSystemVersion;

                };

            };

/*214*/ struct _WIN32_PROCESS *Win32Process;

/*218*/ DWORD                  d218;

/* 21C */ DWORD                  d 21C ;

/*220*/ DWORD                  d220;

/*224*/ DWORD                  d224;

/*228*/ DWORD                  d228;

/* 22C */ DWORD                  d 22C ;

/*230*/ PVOID                  Wow64;

/*234*/ DWORD                  d234;

/*238*/ IO_COUNTERS            IoCounters;

/*268*/ DWORD                  d268;

/* 26C */ DWORD                  d 26C ;

/*270*/ DWORD                  d270;

/*274*/ DWORD                  d274;

/*278*/ DWORD                  d278;

/* 27C */ DWORD                  d 27C ;

/*280*/ DWORD                  d280;

/*284*/ DWORD                  d284;

/*288*/ }

        EPROCESS,

     * PEPROCESS,

    **PPEPROCESS;

 

#define EPROCESS_ /

        sizeof (EPROCESS)

列表7-13.  EPROCESS结构

 

 

typedef struct _ETHREAD

        {

/*000*/ KTHREAD       Tcb;

/*1B0*/ LARGE_INTEGER CreateTime;

/*1B8*/ union

            {

/*1B8*/     LARGE_INTEGER ExitTime;

/*1B8*/     LIST_ENTRY    LpcReplyChain;

            };

/* 1C 0*/ union

            {

/* 1C 0*/     NTSTATUS      ExitStatus;

/* 1C 0*/     DWORD         OfsChain;

            };

/* 1C 4*/ LIST_ENTRY    PostBlockList;

/*1CC*/ LIST_ENTRY    TerminationPortList;

/*1D4*/ PVOID         ActiveTimerListLock;

/*1D8*/ LIST_ENTRY    ActiveTimerListHead;

/*1E0*/ CLIENT_ID     Cid;

/*1E8*/ KSEMAPHORE    LpcReplySemaphore;

/*1FC*/ DWORD         LpcReplyMessage;

/*200*/ DWORD         LpcReplyMessageId;

/*204*/ DWORD         PerformanceCountLow;

/*208*/ DWORD         ImpersonationInfo;

/* 20C */ LIST_ENTRY    IrpList;

/*214*/ PVOID         TopLevelIrp;

/*218*/ PVOID         DeviceToVerify;

/* 21C */ DWORD         ReadClusterSize;

/*220*/ BOOLEAN       ForwardClusterOnly;

/*221*/ BOOLEAN       DisablePageFaultClustering;

/*222*/ BOOLEAN       DeadThread;

/*223*/ BOOLEAN       Reserved;

/*224*/ BOOL          HasTerminated;

/*228*/ ACCESS_MASK   GrantedAccess;

/* 22C */ PEPROCESS     ThreadsProcess;

/*230*/ PVOID         StartAddress;

/*234*/ union

            {

/*234*/     PVOID         Win32StartAddress;

/*234*/     DWORD         LpcReceivedMessageId;

            };

/*238*/ BOOLEAN       LpcExitThreadCalled;

/*239*/ BOOLEAN       HardErrorsAreDisabled;

/* 23A */ BOOLEAN       LpcReceivedMsgIdValid;

/*23B*/ BOOLEAN       ActiveImpersonationInfo;

/* 23C */ DWORD         PerformanceCountHigh;

/*240*/ DWORD         d240;

/*244*/ DWORD         d244;

/*248*/ }

        ETHREAD,

     * PETHREAD,

    **PPETHREAD;

 

#define ETHREAD_ /

        sizeof (ETHREAD)

列表7-14.  ETHREAD结构

 

!processfields!threadfields命令列出的成员之外,在EPROCESSETHREAD结构中实际上还包含一些附加成员。有两种主要的方式可发现有关未文档化成员的详细信息。其一是:观察系统函数是如何访问这些对象成员的;其二是:检查对象是如何被创建并被初始化的。第二种方法可以获得对象的实际大小。基本的对象创建函数是ntoskrnl.exe导出的ObCreateObject()函数,该函数为对象表头和对象体分配内存,并初始化常见的对象参数。不过,ObCreateObject()对它创建的对象是什么类型却丝毫不知,因此,调用者必须给定对象体所需内存的确切字节数。因此,找出对象实际大小这一问题就转化为针对此类对象所调用的ObCreateObject()。进程对象是由Native API函数NtCreateProcess()创建的,而NtCreateProcess()又调用PspCreateProcess()来完成实际工作。在PspCreateProcess()函数中,可找到调用ObCreateObject()的地址,在这里你会发现所需的对象体的大小为0x288字节(即648字节)。这就是为什么在列表7-13中会包含几个没有确切名称的成员的原因,就是为了让对象大小达到0x288ETHREAD结构也是如此:NtCreateThread()函数调用PspCreateThread(),后者在转而调用ObCreateObject()以获取大小为0x248字节的对象。

 

当前正在运行的进程使用EPROCESS结构中的ActiveProcessLinks成员互相链接起来构成了一个链表。该链表的表头保存在全局变量PsActiveProcessHead中,与该链表相关的同步对象是类型FAST_MUTEXPspActiveProcessMutex。很不幸,ntoskrnl.exe并未导出PsActiveProcessHead变量,但它导出了一个名为PsInitialSystemProcess的变量,该变量实际上是一个指向进程ID8的系统进程的EPROCESS结构。通过该结构的ActiveProcessLinks成员的Blink指针就可找到PsActiveProcessHead7-3给出了进程和线程链接构成的结构图。7-3只是一个简化图,它给出的进程链表中仅包含两项。在实际情况下,这个链表会非常的长。为了使该图简洁明了,我仅给出了一个进程的线程链表,并假设该进程只有两个线程。

 

列表7-12列表7-13可看出,在内核和执行体之上还存在一个进程和线程对象,它们分别指向位于EPROCESSKTHREAD中的WIN32_PROCESSWIN32_THREAD结构。这些未文档化的结构构成了Win32子系统中的进程和线程对象。尽管这些结构体的某些成员的含义非常明显,但它们仍包含很多用意不明的成员。这或许是将来要探索的领域了。

 

《Undocumented Windows 2000 Secrets》翻译 --- 第七章(3)_第1张图片

7-3.  进程和线程对象链表

 

 

线程和进程的上下文(Context

当系统执行代码时,执行将总是在该进程的某个线程的上下文中进行。在很多情况下,系统必须从当前上下文中寻找与线程或进程相关的信息。因此,系统总是将当前线程的指针保存在一个内核处理器控制块(Kernels Processor Control BlockKPRCB)中。该结构定义于ntddk.h中,列表7-15给出了该结构。

 

typedef struct _KPRCB // processor control block

        {

/*000*/ WORD                   MinorVersion;

/*002*/ WORD                   MajorVersion;

/*004*/ struct _KTHREAD       *CurrentThread;

/*008*/ struct _KTHREAD       *NextThread;

/* 00C */ struct _KTHREAD       *IdleThread;

/*010*/ CHAR                   Number;

/*011*/ CHAR                   Reserved;

/*012*/ WORD                   BuildType;

/*014*/ KAFFINITY              SetMember;

/*018*/ struct _RESTART_BLOCK *RestartBlock;

/* 01C */ }

        KPRCB,

     * PKPRCB,

    **PPKPRCB;

 

#define KPRCB_ /

        sizeof (KPRCB)

列表7-15.  内核处理器控制块(KPRCB

 

在线性地址0xFFDFF120处可找到KPRCB结构,指向该结构的指针存放在KPCR结构(Kernels Processor Control Region)的Prcb成员中。KPCR结构的定义可在Ntddk.h中找到,该结构位于线性地址0xFFDFF000处。就像在第四章解释的那样,内核模块通过FS寄存器可以很容易的访问该结构体。从地址:FS:0处读取等价于从线性地址DS:0xFFDFF000处读取。系统将最基本的CPU信息保存在地址0xFFDFF 13C 处(紧随KPRCB结构之后)的CONTEXT结构(见列表7-17)中。

 

typedef struct _KPCR // processor control region

        {

/*000*/ NT_TIB             NtTib;

/* 01C */ struct _KPCR      *SelfPcr;

/*020*/ PKPRCB             Prcb;

/*024*/ KIRQL              Irql;

/*028*/ DWORD              IRR;

/* 02C */ DWORD              IrrActive;

/*030*/ DWORD              IDR;

/*034*/ DWORD              Reserved2;

/*038*/ struct _KIDTENTRY *IDT;

/* 03C */ struct _KGDTENTRY *GDT;

/*040*/ struct _KTSS      *TSS;

/*044*/ WORD               MajorVersion;

/*046*/ WORD               MinorVersion;

/*048*/ KAFFINITY          SetMember;

/* 04C */ DWORD              StallScaleFactor;

/*050*/ BYTE               DebugActive;

/*051*/ BYTE               Number;

/*054*/ }

        KPCR,

     * PKPCR,

    **PPKPCR;

 

#define KPCR_ /

        sizeof (KPCR)

列表7-16.  内核处理器控制区域(KPCR

 

 

#define SIZE_OF_80387_REGISTERS 80

 

typedef struct _FLOATING_SAVE_AREA

        {

/*000*/ DWORD ControlWord;

/*004*/ DWORD StatusWord;

/*008*/ DWORD TagWord;

/* 00C */ DWORD ErrorOffset;

/*010*/ DWORD ErrorSelector;

/*014*/ DWORD DataOffset;

/*018*/ DWORD DataSelector;

/* 01C */ BYTE  RegisterArea [SIZE_OF_80387_REGISTERS];

/* 06C */ DWORD Cr0NpxState;

/*070*/ }

        FLOATING_SAVE_AREA,

     * PFLOATING_SAVE_AREA,

    **PPFLOATING_SAVE_AREA;

 

#define FLOATING_SAVE_AREA_ /

        sizeof (FLOATING_SAVE_AREA)

 

 

#define MAXIMUM_SUPPORTED_EXTENSION 512

 

typedef struct _CONTEXT

        {

/*000*/ DWORD       ContextFlags;

/*004*/ DWORD       Dr0;

/*008*/ DWORD       Dr1;

/* 00C */ DWORD       Dr2;

/*010*/ DWORD       Dr3;

/*014*/ DWORD       Dr6;

/*018*/ DWORD       Dr7;

/* 01C */ FLOATING_SAVE_AREA FloatSave;

/* 08C */ DWORD       SegGs;

/*090*/ DWORD       SegFs;

/*094*/ DWORD       SegEs;

/*098*/ DWORD       SegDs;

/* 09C */ DWORD       Edi;

/* 0A 0*/ DWORD       Esi;

/* 0A 4*/ DWORD       Ebx;

/* 0A 8*/ DWORD       Edx;

/* 0AC */ DWORD       Ecx;

/*0B0*/ DWORD       Eax;

/*0B4*/ DWORD       Ebp;

/*0B8*/ DWORD       Eip;

/*0BC*/ DWORD       SegCs;

/* 0C 0*/ DWORD       EFlags;

/* 0C 4*/ DWORD       Esp;

/* 0C 8*/ DWORD       SegSs;

/*0CC*/ BYTE        ExtendedRegisters [MAXIMUM_SUPPORTED_EXTENSION];

/*2CC*/ }

        CONTEXT,

     * PCONTEXT,

    **PPCONTEXT;

 

#define CONTEXT_ /

        sizeof (CONTEXT)

列表7-17   CPUCONTEXTFLOATING_SAVE_AREA结构

 

对照列表7-15,可看出KPRCB结构包含三个指向KTHREAD结构的指针,其偏移量分别为:0x0040x0080x 00C

1.         CurrentThread指向当前正在执行的线程的KTHREAD对象。内核代码经常访问该成员。

 

2.         NextThread指向在下一次上下文切换后将要运行的线程的KTHREAD对象。

 

3.         IdleThread指向空闲线程的KTHREAD对象,当没有线程准备好去执行时,该线程将执行后台任务。系统为每个已安装CPU提供了一个专用的空闲线程。在单处理器系统中,唯一的空闲线程对象被称作POBootThread,并且它是PsIdleProcess对象的线程链表中仅有的一个线程。

 

由于ETHREAD结构中的第一个成员是KTHREAD,而KTHREAD指针又总是指向ETHREAD。所以KTHREADETHREAD之间是可以相互进行类型转化的。KPROCESSEPROCESS也是如此。

 

由于Windows 2000内核将线性地址0xFFDFF000映射到了CPUFS的内核模式段的0x00000000,所以系统总是能在地址:FS:0x0FS:0x120FS: 13C 处找到当前的KPCRKPRCBCONTEXT结构。当你在调试器中反编译内核代码时,你会发现系统经常从FS:0x124处取出一个指针,很明显,该指针指向的是当前的线程对象。示例7-1给出了内核调试器命令u  PsGetCurrentProcessId的执行结果,该命令将使内核调试器从PsGetCurrentProcessId所处的地址开始反编译10行代码。可看出,PsGetCurrentProcessId()函数只是简单的取出当前线程的KTHREAD/ETHREAD结构,然后返回结构中偏移量为0x1E0的数值,此处恰是CLIENT_ID类型的cid成员(属于ETHREAD结构)的UniqueProcessID(参见列表7-14)。PsGetCurrentThreadId()与之类似,不同之处是它在偏移量0x1E4处取出UniqueThreadID。在第二章的列表2-8中有CLIENT_ID结构的定义。

 

kd> u PsGetCurrentProcessId

ntoskrnl!PsGetCurrentProcessId:

8052ba52 64a 124010000     mov     eax,fs:[00000124]

8052ba58 8b80e0010000     mov     eax,[eax+0x1e0]

8052ba5e c3               ret

8052ba 5f cc               int     3

ntoskrnl!PsGetCurrentThreadId:

8052ba60 64a 124010000     mov     eax,fs:[00000124]

8052ba66 8b80e4010000     mov     eax,[eax+0x1e4]

8052ba 6c c3               ret

8052ba6d cc               int     3

示例7-1.  获取进程和线程ID

 

有时,系统需要当前线程所属进程对象的指针。可通过当前KTHREAD结构中的ApcState子结构的Process成员来获取该指针。

你可能感兴趣的:(《Undocumented Windows 2000 Secrets》翻译 --- 第七章(3))