<格蠹汇编>_第29章_在调试中细品CPU

引用栈上的变量

         通常,在函数入口处会执行以下代码,这样子函数的栈底就是父函数栈顶。在ebp值上面的空间就是子函数需要使用的栈空间,下面是父函数使用的栈空间。所以ebp+正数代表的是传入子函数的参数,ebp+负数代表的是子函数的局部变量。

1
2
push esp
mov ebp, esp

淡化段机制

         操作系统大都使用分页机制作为内存管理的主要手段,这是因为段机制是不可禁止的,同时考虑到软件的兼容性,只能淡化段机制的作用。比如讲段基地址设置为0,边界设置为最大,再如共享段描述符。让不同的进程使用相同的段描述符,这就是调试不同的程序但是其段地址是相同的了。
         每个CPU都有自己的全局描述表(GDT)
         如下图,后面的数值表示的是段描述符在全局描述表的位置,当段选择子的第三位全部置零后得到对应的段描述符的偏移值。例如在cs的值为0x1b,但是对应的段描述符表的偏移则是0x18,对应的就是全局描述符表的KGDT_R3_CODE.

特殊的FS段

         windows中FS寄存器有特殊的用法,当CPU在内核中运行时,FS指向的是PCR,当CPU在用户态运行时,FS指向的是TEB。利用dg命令可以查看段描述信息。如下可以发现,使用dg 38和!teb查看的FS指向的TEB结构的基地址都是相同的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
0:009> dg 38
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0038 7ffd5000 00000fff Data RW Ac 3 Bg By P Nl 000004f3
0:009> !teb
TEB at 7ffd5000
ExceptionList: 02eaffe4
StackBase: 02eb0000
StackLimit: 02eaf000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7ffd5000
EnvironmentPointer: 00000000
ClientId: 000001b8 . 00000ae4
RpcHandle: 00000000
Tls Storage: 00000000
PEB Address: 7ffdf000
LastErrorValue: 0
LastStatusValue: 0
Count Owned Locks: 0
HardErrorMode: 0

我是谁?

         PCR处理器控制区是用于存放CPU的编号,IDT和GDT表和重要状态的内存区域,使用!pcr命令可以查看信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
kd> !pcr
KPCR for Processor 0 at ffdff000:
Major 1 Minor 1
NtTib.ExceptionList: 8054a4b0
NtTib.StackBase: 8054acf0
NtTib.StackLimit: 80547f00
NtTib.SubSystemTib: 00000000
NtTib.Version: 00000000
NtTib.UserPointer: 00000000
NtTib.SelfTib: 00000000
SelfPcr: ffdff000
Prcb: ffdff120
Irql: 00000000
IRR: 00000000
IDR: ffffffff
InterruptMode: 00000000
IDT: 8003f400
GDT: 8003f000
TSS: 80042000
CurrentThread: 80553740
NextThread: 00000000
IdleThread: 80553740
DpcQueue: 0x80553da0 0x80500df0 [Normal] nt!KiTimerExpiration
kd> dg 30
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0030 ffdff000 00001fff Data RW Ac 0 Bg Pg P Nl 00000c93

不可缺少的TSS段

         TSS段,用于存放一个任务的执行状态的段,一般的会给每个任务分配一个TSS段,便于进行任务的切换,但是,windows这样的操作系统只是创建少数的TSS段,例如KGDT_TSS,KGDT_DF_TSS和KGDT_NMI_TSS,但是后面两个TSS是处理双误和不可屏蔽中断的任务切换的,也就是说,CPU会根据这两个TSS进行任务切换,关于第一个TSS是所有线程共享的,它的内容是随着任务的切换而改变的,也就是说os在切换线程时候,并不跟换TSS,而是更新TSS的内容。

拓展到64位

         和x32相比,x64取消了硬件方式下的任务切换,对TSS结构发生了很大修改。但是硬件还是保留了自动切换栈的功能,新增了一个名为IST的域。