2.进程和线程的结构是什么?
先不要着急它们是如何创建的,要说明白它们是如何被创建的是很复杂的,步骤繁多,而且这也不是我们的核心内容,在KPCRB这个非常复杂的结构里我们找到了KTHREAD的结构,接下来我们就去找找这个结构,在WRK-v1.2\base\ntos\inc\Ke.h里我们找到了这个结构的定义:
typedef struct _KTHREAD {
DISPATCHER_HEADER Header;//分发器头
LIST_ENTRY MutantListHead;
......
KSPIN_LOCKThreadLock;//线程自旋锁
......
UCHARWaitReason;//线程等待原因
SCHARPriority;//优先级
UCHAREnableStackSwap;
volatileUCHAR SwapBusy;
BOOLEANAlerted[MaximumMode];
union {
LIST_ENTRY WaitListEntry;
SINGLE_LIST_ENTRYSwapListEntry;
};
PRKQUEUEQueue;
PVOIDTeb;//线程环境块
union{ //定时器信息
KTIMERTimer;
struct{
UCHAR TimerFill[KTIMER_ACTUAL_LENGTH];
PKTRAP_FRAME TrapFrame;
PVOIDCallbackStack;
PVOIDServiceTable;
......
BOOLEANKernelStackResident;
SCHARBasePriority;//基础优先级
SCHARPriorityDecrement;
CHARSaturation;
KAFFINITYUserAffinity;
PKPROCESSProcess;
KAFFINITYAffinity;
......
LIST_ENTRYThreadListEntry;
PVOID SListFaultAddress;
......
} KTHREAD, *PKTHREAD, *PRKTHREAD;
这又是一个非常变态长的结构,一句话,越长的数据结构越重要,这句话在内核中绝对正确,找啊找啊,发现了一些有趣的事情,这个结构里面有很重要的信息,有很多表,但是我们这时候不能一下子跳进去,在source insight里面找一下调用这个结构的信息发现了一个很重要的结构ETHREAD,找到这个ETHREAD结构,看看有什么发现没有,如果没有的话我就只能回到EPRCB结构中找一些重要的信息了,幸好在WRK-v1.2\base\ntos\inc\Ps.h 里面找到了ETHREAD,其结构如下:
typedefstruct _ETHREAD {
KTHREAD Tcb;//进程控制块
LARGE_INTEGER CreateTime;
union {
LARGE_INTEGER ExitTime;
LIST_ENTRY LpcReplyChain;
LIST_ENTRY KeyedWaitChain;
};
union {
NTSTATUS ExitStatus;
PVOID OfsChain;
};
LIST_ENTRY PostBlockList;
struct_ETHREAD *ReaperLink;
KSPIN_LOCK ActiveTimerListLock;
LIST_ENTRY ActiveTimerListHead;
CLIENT_ID Cid;//本进程的CID
......
union {
PVOID LpcReplyMessage;
PVOID LpcWaitingOnPort;
};
......
LIST_ENTRY IrpList;
......
PEPROCESS ThreadsProcess;
PVOID StartAddress;
......
LIST_ENTRY ThreadListEntry;//挂入EPROCESS中的线程队列
......
BOOLEAN ForwardClusterOnly;
BOOLEAN DisablePageFaultClustering;
UCHAR ActiveFaultCount;
......
}ETHREAD, *PETHREAD;
又是一个非常变态长的结构,上来就给我一个超大的惊喜,Tcb!!!上课老师重点讲的这玩意,终于在代码中看到了,冷静一下,这时候不是马上去看Tcb的结构代码,我们没有解决我们的核心问题,THREAD翻译成线程,那进程呢?进程的结构是什么?而且KTHREAD和ETHREAD到底哪个是线程的结构,这两个结构之间到底是什么关系?有了ETHREAD和KTHREAD,那么我们可以猜想一下是不会会存在E进程和K进程?一般来说进程的英文单词是PROCESS,所以我们尝试查一下EPROCESS和KPROCES,果然不负重托,和ETHREAD结构在同一个文件里就有:
typedef struct _EPROCESS {
KPROCESSPcb;
EX_PUSH_LOCK ProcessLock;
LARGE_INTEGERCreateTime;//进程创建时间
LARGE_INTEGER ExitTime;//退出时间
......
HANDLEUniqueProcessId;
LIST_ENTRYActiveProcessLinks;//活动的进程链接
......
struct_ETHREAD *ForkInProgress;
ULONG_PTRHardwareTrigger;
PMM_AVL_TABLE PhysicalVadRoot;
PVOIDCloneRoot;
PFN_NUMBERNumberOfPrivatePages;
PFN_NUMBERNumberOfLockedPages;
PVOIDWin32Process;//指向本进程的Win32Process结构
struct_EJOB *Job;
PVOIDSectionObject;//指向可执行程序映像的文件映射区对象
PVOIDSectionBaseAddress;
PEPROCESS_QUOTA_BLOCKQuotaBlock;
PPAGEFAULT_HISTORY WorkingSetWatch;
HANDLEWin32WindowStation;
HANDLEInheritedFromUniqueProcessId;
PVOIDLdtInformation;
PVOIDVadFreeHint;
PVOIDVdmObjects;
PVOIDDeviceMap;
......
PVOID Session;
UCHARImageFileName[ 16 ];所执行程序映像的文件名
LIST_ENTRYJobLinks;
PVOIDLockedPagesList;
LIST_ENTRY ThreadListHead;//本进程的线程队列
......
ULONGActiveThreads;
ACCESS_MASK GrantedAccess;//允许访问方式
ULONGDefaultHardErrorProcessing;
NTSTATUSLastThreadExitStatus;
......
PPEBPeb;//指向用户空间的“进程环境块”
......
UCHARPriorityClass;//本进程的优先级类别
MM_AVL_TABLE VadRoot;//指向本进程用户空间的数据结构
ULONGCookie;
} EPROCESS, *PEPROCESS;
这个结构也是老长老长的,关键是他们还非常重要,哎,这个结构里面的信息就不得了,基本上课堂上老师和我们讲的进程的很多信息这里么就包括了,君不见第一个就是传说中的PCB,有EPROCESS,那就有KPROCESS了,不过这次是在WRK-v1.2\base\ntos\inc\Ke.h里面找到这个结构:
typedef struct _KPROCESS {
DISPATCHER_HEADER Header;//分发器头
LIST_ENTRYProfileListHead;
ULONG_PTR DirectoryTableBase[2];//本进程页面映射表的物理地址
......
LIST_ENTRYReadyListHead;//本进程的就绪线程队列
SINGLE_LIST_ENTRY SwapListEntry;
......
LIST_ENTRYThreadListHead;//本进程的线程队列
KSPIN_LOCK ProcessLock;//进程自旋锁
......
SCHARBasePriority;//本进程的基本优先级
SCHAR QuantumReset;
......
ULONG_PTRStackCount;
LIST_ENTRYProcessListEntry;//挂入内核的进程队列
} KPROCESS, *PKPROCESS, *PRKPROCESS;
这个结构也是不得了,后面的很多信息都是跟这里么打交道的,我加了注释的都是很重要的,很多内容的中文翻译那是如雷贯耳啊,看着英文你就可以翻译出来很多了,这一点微软做的真的很好,命名是很有水平的,也可以直接把这个结构叫做进程控制块结构了。总结一下我们的问题:EPROCESS中的E是Executive(管理层之意),KPROCESS中的K是kernel的意思,不过这里不是我们概念中的内核,这是因为微软把Kernel当成内核的一部分,而LINUX里面Kernel就是内核。线程同样的道理,而在进程的结构中就有线程队列,到此我们已经明白了进程和线程的结构已经它们之间是什么关系了。
注意:在我的这种探索方式下,我们漏过了一些很重要的东西,比如TEB和PEB,而WRK-v1.2\public\sdk\inc\Pebteb.h:
typedef struct PEBTEB_STRUCT(_PEB) {
BOOLEANInheritedAddressSpace; // These four fields cannot change unless the
BOOLEANReadImageFileExecOptions; //
BOOLEAN BeingDebugged; //
union {
BOOLEAN BitField; //
struct {
BOOLEANImageUsesLargePages : 1;
BOOLEAN SpareBits : 7;
};
};
PEBTEB_POINTER(HANDLE)Mutant; // INITIAL_PEB structure isalso updated.
PEBTEB_POINTER(PVOID)ImageBaseAddress;
PEBTEB_POINTER(PPEB_LDR_DATA)Ldr;
PEBTEB_POINTER(struct_RTL_USER_PROCESS_PARAMETERS*) ProcessParameters;
PEBTEB_POINTER(PVOID)SubSystemData;
PEBTEB_POINTER(PVOID)ProcessHeap;
PEBTEB_POINTER(struct_RTL_CRITICAL_SECTION*) FastPebLock;
PEBTEB_POINTER(PVOID)AtlThunkSListPtr; // Used only forAMD64
PEBTEB_POINTER(PVOID)SparePtr2;
ULONG EnvironmentUpdateCount;
PEBTEB_POINTER(PVOID)KernelCallbackTable;
ULONG SystemReserved[1];
ULONG SpareUlong;
PEBTEB_POINTER(PPEB_FREE_BLOCK)FreeList;
ULONG TlsExpansionCounter;
PEBTEB_POINTER(PVOID)TlsBitmap;
ULONG TlsBitmapBits[2]; // TLS_MINIMUM_AVAILABLE bits
......
PEBTEB_POINTER(struct_ASSEMBLY_STORAGE_MAP *) SystemAssemblyStorageMap;
PEBTEB_POINTER(SIZE_T)MinimumStackCommit;
PEBTEB_POINTER(PPVOID) FlsCallback;
PEBTEB_STRUCT(LIST_ENTRY)FlsListHead;
PEBTEB_POINTER(PVOID)FlsBitmap;
ULONGFlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)];
ULONG FlsHighIndex;
} PEBTEB_STRUCT(PEB), * PEBTEB_STRUCT(PPEB);
简单来说PEB和TEB描述了进程和线程的运行环境,包括映像基地址,加载器数据库,线程局部存储区数据,代码页数据,进程标志,进程堆,映像进程亲和性掩码,FLS/TLS数据等信息,开头我们就说过FS段寄存器在用户空间的时候指向的是TEB也就是当前线程环境块,在这里系统就知道当前线程的信息了,