PEB和TEB
TEB(Thread Environment Block,线程环境块)系统在此TEB中保存频繁使用的线程相关的数据。位于用户地址空间,在比 PEB 所在地址低的地方。进程中的每个线程都有自己的一个TEB。一个进程的所有TEB都以堆栈的方式,存放在从0x7FFDE000开始的线性内存中,每4KB为一个完整的TEB,不过该内存区域是向下扩展的。在用户模式下,当前线程的TEB位于独立的4KB段,可通过CPU的FS寄存器来访问该段,一般存储在[FS:0]。在用户态下WinDbg中可用命令$thread取得TEB地址。
PEB(Process Environment Block,进程环境块)存放进程信息,每个进程都有自己的PEB信息。位于用户地址空间。在Win 2000下,进程环境块的地址对于每个进程来说是固定的,在0x7FFDF000处,这是用户地址空间,所以程序能够直接访问。准确的PEB地址应从系统的EPROCESS结构的0x1b0偏移处获得,但由于EPROCESS在系统地址空间,访问这个结构需要有ring0的权限。还可以通过TEB结构的偏移0x30处获得PEB的位置,FS段寄存器指向当前的TEB结构:
mov eax,fs:[0x30]
mov PEB,eax
在用户态下WinDbg中可用命令$proc取得PEB地址。
见图1: PEB和TEB
见图2: 进程和线程
KPCR(Kernel's Processor Control Region,内核进程控制区域)是一个不会随WINDOWS版本变动而改变的固定结构体,在它的末尾(偏移0x120)指向KPRCB结构。KPRCB同样是一个不会随WINDOWS版本变动而改变的固定结构体。它包含有指向当前KTHREAD的指针(偏移值0x004)。ETHREAD的第一项是KTHREAD,也就知道了当前的ETHREAD基地址。KTHREAD中的_KAPC_STATE结构包含当前KPROCESS的地址每个进程都有一个EPROCESS结构,里面保存着进程的各种信息,和相关结构的指针。EPROCESS结构位于系统地址空间,所以访问这个结构需要有ring0的权限。EPROCESS的第一项是KPROCESS。
CreateProcess创建进程的大致步骤:
1.打开可执行文件[.exe],以FILE_EXECUTE存取方式打开。
2.把可执行映像装载进RAM。
2.创建进程内核对象(EPROCESS,KPROCESS和PEB结构)。
4.为该新建进程分配地址空间。
3.在该进程中创建主线程的线程执行对象(ETHREAD,KTHREAD和TEB结构),为主线程分配堆栈,建立该进程主线程的执行上下文。
4.Kernel32.dll通知Win32子系统已经创建了一个新进程。
5.如果标志不是CREATE_ SUSPENDED,那么就开始执行进程中的线程
6.在进程和线程的context里,完成地址空间的初始化(比如,加载需要的DLL),开始执行程序。
【附录】基础
1. 主要组成
用户模式的一些主要模块:
l 系统支持进程(System Support Processes) -- 如 Logon 进程 (位于\%SystemRoot%\System32\Winlogon.exe) ;
l 服务进程(Service Processes) -- 如 Spooler 进程 (位于\%SystemRoot%\System32\spoolsv.exe) ;
l 用户应用程序(User Applications) -- 任何 Win32、Windows 3.1、DOS、POSIX 或者 OS/2 程序;
l 子系统(Environment Subsystems) -- Windows 内置3个子系统:Win32 (位于\%SystemRoot%\System32\Csrss.exe) 、POSIX子系统 (位于\%SystemRoot%\System32\Psxss.exe) 和OS/2子系统 (位于\%SystemRoot%\System32\Os2ss.exe) 。
注:Windows XP及后续的操作系统中, POSIX和OS/2子系统已被去掉。 |
内核模式的一些主要模块:
l 运行模块(Executive) -- 内存管理、进程和线程的管理、安全机制等;
l 内核(Kernel) -- 线程调度、中断、异常的分派等 (运行模块和内核位于\%SystemRoot%\System32\Ntoskrnl.exe) ;
l 设备驱动程序(Device Drivers) -- 硬件设备驱动程序、文件系统和网络驱动程序;
l 硬件抽象层 (Hardware Abstraction Layer, HAL) -- 将内核、设备驱动程序和运行模块和具体的硬件平台隔离开 (位于\%SystemRoot%\System32\Hal.dll) ;
l 窗口和图形系统(Windowing And Graphics System) -- 实现 GUI 函数, 如处理窗口、用户界面的控制和绘画等 (位于\%SystemRoot%\System32\Win32k.sys) 。
详尽点的大图见NT_Structure。
2. 线程上下文环境 (Thread Context)
在大多数情况下, 我们的系统中只安装了一个 CPU,所以, 对于所有这些运行中的程序来说, 操作系统对每个进程中的线程所使用的 CPU 时间进行调度, 循环为每个线程分配时间片, 这就造成了多个程序同时执行的假象。如果系统中安装了多个 CPU, 那么操作系统的调度算法将复杂得多, 因为它要将各 CPU 上的线程进行平衡。如果 Windows 检测到一个新线程要开始运行了, 它将进行一次上下文环境切换 (context switch) (注:上下文环境 (Content) 实际上就是线程运行的环境, 也就是运行时各寄存器和其他东东的状态, 更自然的理解就是 "线程状态") 。所谓上下文切换就是保存线程运行时的机器状态, 然后将另一个线程的状态恢复并重新开始执行。如果重新开始执行的线程属于另一个进程, 那么该进程的地址空间也将被同时切换过来 (通过在 CR3 寄存器中装入页表) 。上下文切换比较耗 CPU 时间。
3. 系统内存堆
系统内存堆表现为系统地址空间的两个所谓的内存池。
l 不分页池(Nonpaged Pool)。不分页池不会分页到交换文件 (swap file), 自然也不需要分页回来。它们总是老老实实在物理内存里活动, 在你想访问它们的时候总能找到它们 (任何 IRQL 等级), 并且不会出现分页错误。这也正是它的优点, 任何访问都不会出现页面错误! 页面错误可是往往导致系统瘫掉(当 IRQL >= DISPATCH_LEVEL)!
l 分页池(Paged Pool)。顾名思义, 就是可以分页 (分入和分出) 的了。你只能使用 (IRQL < DISPATCH_LEVEL) 的内存。
以上两种池都位于系统地址空间,在进程上下文环境中可以使用它们。
4. 注册表的结构
注册表 (Registry) 是基本数据的中心, 在系统设置和管理方面扮演着重要的角色。注册表的内容随系统的工作进程而动态改变。注册表由 keys 构成,最上层的keys叫做root keys。keys本身就是一个容器,装着其它的keys,装在里面的这些keys叫做subkeys和数据入口 values。values 保存着实际数据。对注册表的操作与管理由 Configuration Manager 负责。
root keys有六个:
HKEY_USER |
包含计算机上的所有以活动方式加载的用户配置文件; 缩写为“HKU”。 |
HKEY_CURRENT_USER |
保存着当前用户的注册信息; 链接到子键HKEY_USER\<SID_当前用户>系统用户中对应于当前用户的注册信息; 缩写为“HKCU”。 |
HKEY_LOCAL_MACHINE |
保存着系统配置信息: 硬件设备支持记录, 安全策略, 用户口令, 应用程序设置和服务及驱动程序配置; 缩写为“HKLM”。 |
HKEY_CURRENT_CONFIG |
包含当前硬件配置信息; 链接到子键HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Hardware Profiles\Current。 |
HKEY_CLASSES_ROOT |
保存着文件关联和 COM 类的注册数据; 链接到子键HKEY_LOCAL_MACHINE\SOFTWARE\Classes和HKEY_CURRENT_USER\SOFTWARE\Classes; 缩写为“HKCR”。 |
HKEY_PERFORMANCE_DATA |
包含着性能信息。 |
在用户模式下, 关于性能统计的信息是通过Performance Data Helper库来访问,这个库实现在模块 pdh.dll里(位于\%SystemRoot%\System32\pdh.dll)。标准程序Performance Monitor正是利用了这个库。除性能统计之外这个键还包含着许多补充信息,例如,用于枚举进程、线程和模块等等的Process Status函数 (在 psapi.dll里(位于\%SystemRoot%\System32\psapi.dll)实现)正是从HKEY_PERFORMANCE_DATA键中获得的信息。访问HKEY_PERFORMANCE_DATA需要有Administrators组的权限,注册表编辑器 Regedit 和 Regedt32 并不能显示出这个键的内容,因此无法编辑,但RegOpenKey或RegConnectRegistry可以打开。