软件调试

CPU 基础

CPU的操作模式

保护模式和实模式

保护模式指的是具有强大的虚拟内存支持和完善的任务保护机制,实模式是直接操作物理IO地址。虚拟8086模式即保护模式下的类实模式。

寄存器

寄存器相关的逆向工程核心原理已介绍

特权级

描述符特权级 : Descriptor Privilege Level 简称DPL,位于段描述符或门描述符中,用于表示一个段或门的特权级别

当前符特权级 : Current Privilege Level 简称CPL,位于CS和SS寄存器的为0和位1中,用于表示政治执行的程序或任务的特权级别,通常CPL等于当前整备执行的代码段的DPL。当处理器切换到一个不同的DPL段时,CPL会随之变化。当CPU访问DPL>CPL的代码段时,CPL保持不变。
请求者特权级别: Requestor Privilege level 简称RPL,用于系统调用的情况,位于保存在栈中的段选择子的 0 和 1 位上,用来代表请求操作系统服务的应用程序的特权级别,当CPU判断是否可以访问一个段时,CPU既要检查CPL,也要检查RPL,防止越权操作

特权指令

CLTS 清除CR0寄存器中Task Switched标志
HLT 使CPU停止执行指令,并进入HALT状态,中断,调试异常或BINIT#,RESET#等可以使CPU脱离HALT状态
INVD 使高速缓存无效,不回写(数据到内存)
WBINVD 使高速缓存无效,回写(数据到内存)
INVLPG 使TLB表项无效
LGDT 加载GDTR寄存器
LIDT 加载IDTR寄存器
LLDT 加载LDTR寄存器
LMSW 加载机器状态字,也就是CR0寄存器的0-15位
LTR 加载任务寄存器TR
MOV to/from CRn 读取或为控制寄存器赋值
MOV to/from Drn 读取或为调试寄存器赋值
RDMSR 读MSR寄存器
WRMSR 写MSR寄存器
RDPMC 读性能监控计数器
RDTSC 读时间戳计数器

段机制

段描述符

在保护模式下,每个内存段都有一个段描述符,这是其他代码访问该段的基本条件,每个段描述符是一个8字节长的数据结构,用来描述一个段的位置,大小,访问控制状态等信息。

段描述符最基本的内容是这个段的基地址和边界,基地址是以2347 4个字节组成的一个地址,他可以使4GB地址空间的任意地址:00000000-0xFFFFFFFF。段便捷式用20比特位表示的,字节0,1 和6的低4位,其单位有G位来控制,即字节6的最高位,当G等于0时,边界的单位是1字节,当G等于1时,边界的单位是4KB。

端描述符其他成员的含义:

S 表示系统,S=1表示该描述符是一个系统段,S=1表示该描述符是代码段、数据段或堆栈段。

P 数据存放方式,该为代表被描述的段是否在内存中,P=1表示该段在内存中,P=1表示该段不再内存中,内存管理软件可以使用该位来控制哪些段实际加载到物理内存中,这为虚拟内存管理提供了页机制之外的另一个方法。

DPL 描述符特权级,2个bit,所以从0-3,作用就是用来防止用户空间访问系统空间,当CPU等于或高于这个段的访问级别的时候,才会被运行访问。

D/B位 对于代码段,该位表示的是这个代码段的默认位数,D=0 表示16位代码段,D=1 表示32位代码段,对于栈数据段,该位被称为B表示位,B=1表示使用32位堆栈指针,B=0表示使用16位指针

Type 段类型,位0简称A位,表示该段是否被访问过

  • 对于数据段,位2是拓展方向位,简称E,E=0表示向高端拓展,位1是读写控制位,简称W,W=0表示只读,W=1表示读写
  • 对于代码段,位2表示该段是否是一致代码段,简称C位,位1表示该段是否刻度,简称R位,R=1表示可执行,可读,R=0表示该段只可执行

位3是D/C位,决定了该段是数据段/堆栈段(D/C=0),还是代码段(D/C=1)

L 64位代码段 L=1表示代码段时64位的,L=0表示代码段时32位的

AVL 操作系统使用

特殊情况:

对于向下拓展的栈数据段,段便捷指的是该段的最小偏移,B标志用来指定偏移的最大有效值(上边界),当B等于1时,最大偏移是0xFFFFFFFF,这样,如果limit=0,那么段的总长度便是4GB(G=0),如果B=0时,那么上边界便是0XFFFF

段描述表

在多任务系统中通常会有很多任务,每个任务会涉及多个段,每个段都需要一个段描述符,因此,为了便于管理段描述符,系统用一个线性表来存放段描述符,根据用途可以分为3中描述符表,全局描述符GDT,局部描述符LDT和中断描述符IDT。

GDT 表示全局表,一个系统通常只有一个GDT表,供系统中的所有程序和任务使用,LDT与任务相关,每个任务有一个LDT,也可以让多个任务共享一个LDT,IDT表的数量和处理器的数量相关,系统通常会为每个CPU建立IDT表。

GDTR和IDTR寄存器分别用来标识GDT表和IDT表的位置和边界,这2个寄存器的格式相同,在32位模式下,长度是48,高32基地址,低16位是边界,在64位下,长度是80,高64位是基地址,低16位是边界。

LGDT 和 SGDT指令分别用来读取和设置 GDTR寄存器。
LIDT 和 SIDT指令分别用来设置IDTR寄存器。

操作系统在启动初期会建立GDT和IDT,并初始化 GDTR和IDTR寄存器。

位于GDT表的第一个表项 0 所指向的描述符保留不用,被称为空描述符,当把指向空描述符的段选择子加载到段寄存器时不会产生异常。

当创建LDT表时,GDT已经准备好了,因此LDT被创建为一种特殊的系统段,其段描述符被放在GDT表中,GDT表本身只是一个数据结构,没有对应的段描述符。

windbg的使用

使用 r gdtr 和 r gdtl 可以查看GDT的地址和边界
使用 r idtr 和 r idtl 可以查看idt的地址和边界

段选择子

局部描述符表寄存器LDTR表示当前任务的LDT表在GDT中的索引

段选择子的TI位代表要索引的段描述符表,TI=0表示全局描述符表,TI=1表示局部描述符表。

段选择子的高13位是段描述符索引,即要选择的段描述符在TI所表示的段描述符表中的索引号。因为这里使用的是13位,意味着最多可索引2^13 = 8KB 8192个描述符,所以GDT和LDT表的最大项数都是8192.因为X86CPU最多支持256个中断向量,所以IDT表最多的表项数是256

段选择子的RPL用于特权检查。

任务状态寄存器TR中存放的也是一个段选择子,指向的是全局段描述表中描述当前任务状态段(Task State Segmentation,简称TSS)的段描述符。任务状态段时保存任务上下文信息的特殊段,其基本长度是104字节,操作系统可以附加更多的内容。

TSS是实现任务切换的重要数据结构,当进行任务切换时,处理器先把当前任务的执行现场———包括CS:EIP在内的寄存器保存到TR所指定的TSS中,然后用把指向下一任务的TSS的选择子装入TR(使用LTR指令),接下来再从TSS中把下一任务的寄存器信息加载到各个寄存器中,然后执行下一任务。

除了LDTR 和 TR,在保护模式下所有的段寄存器(CS,DS,ES,FS和GS)中存放的也是段选择子,不再是实模式时的高16位基地址。

观察段寄存器

除了代码段和数据段的段描述符外,还有一类重要的描述符是系统描述符,即S位为0,包括描述符LDT所在段的段描述符,描述TSS段的段描述符,调用门描述符,中断门描述符陷阱门描述符和任务门描述符,后四种称为门描述符。

分页机制

分页机制的主要目的是高效的利用内存,按页来组织和管理内存空间,把暂时不用的数据放到外部存储器—硬盘,在启用分页机制后,操作系统将线性地址空间划分位固定大小的页面4KB,2MB 或 4MB。每个页面可以被映射到物理内存或外部存储器上的虚拟内存为文件中。

当程序中的指令访问某一内存地址,CPU会先根据段寄存器的内容将虚拟地址转为线性地址,具体过程是根据段寄存器中的段选择子找到段描述符,然后将段描述符中的基地址加上程序中的偏移地址得到线性地址。接下来,如果CPU发现包含该线性地址的内存页不再物理内存中便会产生一个页面错误异常(#PF)。该异常的处理程序通常是操作系统的内存管理例程。内存管理器得到异常报告后会根据异常的状态信息,特别是CR2寄存器中包含的线性地址,将所需的内存页加载到物理的内存中,所以便不会导致页错误异常了。

控制寄存器中的3个与分页机制相关的标志:

CR0的PG标志:用于启用分页机制 CR0[31]
CR4的PAE标志:启用物理地址拓展,可以最多寻址64G物理内存CR4[5]
CR4的PSE标志:用于启用大页面支持,当PAE=1时,大页面为2MB,当PAE=0位4MB。

页目录

页目录是用来存放页目录表项(Page-Directory Entry,PDE)的线性表。每个页目录占一个4KB的内存页,每个PDE的长度为32个比特位,因此每个也目录最多包含1024个PDE。当没有启动PDE格式,分贝用于指向4KB的页表和4MB的内存页。图2-9画出了指向4KB页表的PDE格式。中高20位代表PDE所指向页面的起始地址的高20位,该起始物理地址的低12位固定为0,所以页表一定按照4KB对齐。

页表结构

关于逻辑地址与物理地址的转换

首先从CR3中取出页目录表地址

断点

软件断点

int3 0xcc 0xcd 0x03

硬件断点

IA32处理器定义了8个调试寄存器,分别称为DR0-DR7.在32位模式下,他们都是32位的,64系统则为64位,以32位为例。

DR6 和 DR7 在64位时,高32位保留

当CR4寄存器DE标志为1时 DR4 5 等于于 DR 6 7

DR 0-4 用来存放要产生中断的内存地址

DR6 是当调试事件发生时,向调试器报告时间的详细信息,以供调试器判断发生的事件。

在保护模式中,无法使用调试寄存器设置物理内存断点,只能设置线性内存断点。

其中R/W 0-3分别对应4个调试寄存器,用来指定访问类型:

  • 00 执行中断
  • 01 写入内存中断
  • 10 IO读写中断
  • 11 读写数据都中断

其中 LEN0-3 分别与DR0~DR3四个调试地址寄存器想对应,用来指定监控长度:

  • 00: 1字节长度
  • 01: 2字节长度
  • 10: 8字节长度
  • 11: 4字节长度

L0~L3 局部断点启用,分别对应DR0~DR3四个调试地址寄存器匹配,中断时自动清除此位,为1启用中断,为0禁用中断。

G0~G3 全局断点启用,分别对应DR0~DR3四个调试地址寄存器匹配,为1启用中断,为0禁用中断。

GD 调试寄存器保护,当此位为1时,对任何调试寄存器进行修改都会触发1号调试陷阱中断。

BD置位表示是GD位置位情况下访问调试寄存器引发的陷阱,BT置位表示是因为TS置位即任务切换时TSS中TS位置1时切到第二个任务时第一条指令时引发的,BS置位表示是单步中断引发的断点。。。。即EFLAGS的TF置位时引发的调试陷阱。(可作为状态值参考)

操作系统的调试支持

通常操作系统的功能包括文件管理,内存管理,进程管理,打印管理,网络管理等。除了这些功能外,如何支持调试也是操作系统设计的一项根本任务,从被调试对象的角度来看,可以把操作系统的调试分为三个方面,应用程序调试,设备驱动调试,操作系统自身的调试

Windows概要

进程与进程空间

在32位系统,每个进程的进程空间是4GB,即地址0x0-0xffffffff。为了高效的调用和执行操作系统的各项服务,windows会把操作系统的内核数据映射到系统所有进程的进程空间。

因此,4GB的进程空间通常被划分位2个部分,低2GB用户空间和高2GB系统空间。

在64位系统下,用户空间和系统空间都增大了很多:

  • 用户空间0x0-0x7fff`ffffffff
  • 系统空间0xffff8000`00000000-0xffffffff’ffffffff

进程资源

除了虚拟地址空间,每个进程还拥有如下资源:

  • 一个全局唯一的进程ID (PID)
  • 一个可执行镜像(image)
  • 一个或多个线程
  • 一个位于内核空间的名为EPROCESS的进程执行块结构,记录进程关键信息
  • 一个位于内核空间中的对象句柄表
  • 一个用于描述内存目录表起始位置的基地址,简称页目录基地址,当CPU切换到该任务是,会将该地址加载到CR3寄存器,翻译成正确物理地址
  • 一个用户空间的进程环境块,简称PEB
  • 一个访问令牌,用于表示进程的用户,安全组以及优先级别