进程深入
===========
每一个Windows进程都是由一个叫做executive process(EPROCESS)的块表示的。它除了包含许多跟进程相关的属性之外,还包含和指向另外的一系列其他相关的数据结构。比如说,每一个进程都有一个或更多的用来表示线程的结构executive thread(ETHREAD)。后面的会讲到executive thread的。EPROCESS和它的相关数据结构存在于操作系统空间,只有process environment block(PEB)除外。PEB存在于进程的地址空间之内,因为它包含了可以被用户态代码修改的信息。
除了EPROCESS块以外,win32子系统进程(csrss)为每一个执行win32程序保持一个平行结构. 同样, 在一个线程调用由内核态实现的win32 USER或者GDI函数的时候, win32子系统的内核态部分(win32k.sys)会为每一个进程创建一个数据结构.
下图展示了EPROCESS块的结构
元素 | 目的 |
Kernel process (KPROCESS) block | Common dispatcher object header, 指向进程页目录的指针, 属于该进程的kernel thread (KTHREAD) blocks 的列表, 默认的基础优先级, 配额, 倾向的mask值, 还有进程中的总共的kernel和user线程时间 |
Process identification | 独一无二的进程ID, 创建进程的进程ID, 被运行的镜像的名字, 正运行在哪个window station process之上 |
Quota block | 对于nonpaged pool, paged pool, page file usage, current and peak process nonpaged and paged pool usage的限制 (注意: 许多进程可以共享这个结构,所有的系统进程指向这个独一无二的,系统范围内的默认配额数据块;所有的交互进程会话(session)共享一个配额块Winlogon.) |
Virtual address descriptors (VADs) | 一系列的数据结构, 这些数据结构描述了存在于进程中的地址空间部分的状态. |
Working set information | 指向working set 列表(MMWSL structure); 当前的, 顶峰的, 最小的, 最大的working set的尺寸; 最后修剪的时间; 页面错误计数; 内存优先级; outswap标志; 页面错误历史. |
Virtual memory information | 当前的和最高的时候的virual memory的大小, 为进程页面目录而创建的硬件页表 |
Exception local procedure call (LPC) port | 当一个进程的线程引发了异常的时候, 进程管理器会发出一条信息, 该信息通过跨进程通信的管道来传送。这里指定的就是这样的一个管道。 |
Debugging LPC port | 跨进程管道,该管道在一个进程的线程引发了debug事件的时候,让进程管理器通过它来发送信息。 |
Access token (ACCESS_TOKEN) | 描述进程安全轮廓的Executive object |
Handle table | 进程的句柄地址表 |
Device map | 用于解析设备名引用的对象目录的地址(supports multiple users). |
Process environment block (PEB) | 镜像信息(基地址, 版本号, 模块列表), 进程堆的信息, 线程-局部存储实用工具(Note: The pointers to the process heaps start at the first byte after the PEB.) |
Win32 subsystem process block (W32PROCESS) | Win32子系统中的内核态组件需要的进程细节信息.Process details needed by the kernel-mode component of the Win32 subsystem. |
Kernel Processor Block (PCB)
EPROCESS块有两个关键的子数据结构, 一个是kernel process block(KPROCESS), 另一个就是process environment block(PEB).
PEB存在于用户进程的地址空间中, 包含image loader, heap manage和其他在用户态下可写的Win32系统dll所需要的信息. 注意, EPROCESS和KPROCESS只能通过内核态访问. PEB总是被映射到地址0x7FFDF000上.
线程深入
===============
在操作系统级, 一个线程是通过一个ETHREAD的数据结构来表示的. ETHREAD块和它指向的其他数据结构存在于系统的地址空间中, 除了thread environment block(TEB). 另外,Win32子系统进程csrss, 为每一个Win32进程创建出来的线程保持一个平行的数据结构. 同样的, 内核态的Win32子系统部分会保持一个每个线程都有的数据结构(叫做W32THREAD结构), ETHREAD块有成员指向它.
下表列出了ETHREAD的重要数据域
元素 | 描述 |
KTHREAD block | 指向KTHREAD数据结构 |
Thread time information | 线程创建和退出的时间 |
Process identification | 进程的ID和指向线程所属的进程的EPROCESS block |
Start address | 线程启动函数的地址 |
Impersonation information | 访问令牌和扮演角色的水平(当一个线程在扮演一个客户端的时候) |
LPC information | 线程在等待的消息ID, 和消息的地址. |
I/O information | 等待处理的IO请求包的列表 |
KTHREAD一些信息, 操作系统内核需要访问这些信息来替运行中的线程来执行thread scheduling和synchronization.
Element | Description |
Dispatcher header | 因为线程是一个可以等待的对象, 所以它以一个标准的 kernel dispatcher object header来开头 |
Execution time | 用户态和内核态一共的cpu时间. |
Pointer to kernel stack information | 内核栈的最低和最高地址. |
Pointer to system service table | 每一个线程都从这个志向主系统服务表(KeServiceDescriptorTable)的成员开始 当一个线程第一次调用Win32 GUI服务的时候, 他的系统服务表就被改变了, 变得包括在Win32k.sys中存在的GDI和user服务. |
Scheduling information | 基础优先级, 当前优先级, 配额, affnity mask, ideal processor, scheduling state,冻结计数, 悬挂计数 |
Wait blocks | 线程块包含四个内置的wait block, 所以wait block不需要每一次线程等待什么东西的时候都被分配和初始化.(有一个wait block是专门服务于timer的.) |
Wait information | 线程等到的对象的列表, 等待的原因, 和线程进入等待状态的时间 |
Mutant list | 线程拥有的mutant objects的列表 |
APC queues | 用户态和内核态的等待状态的APC的列表, 和可修改的标志 |
Timer block | 内置的timer block, 也就是wait block |
Queue list | 指向线程相关的队列对象 |
Pointer to TEB | Thread ID, TLS 信息, PEB 指针, 和GDI 以及OpenGL 信息. |
Thread Environment Block
TEB存储image loader和许多Win32 DLL的上下文信息. 因为这些组件是在用户态下运行的, 他们需要一个用户态下可写的数据结构. 这就是为什么这个结构存在于用户地址空间, 而不是仅仅是在内核态下才可写的系统地址空间了. 你可以通过运行!thread命令来找到TEB的地址,