windows2-保护机制
首先铺垫,在前面学过pe文件格式,且写过反射型注入,有一定知识前置
我们知道,一个软件在运行的时候,会分配一个内存中的基地址(虚拟地址),很好,那么明明是在内存中实体存在的为什么叫虚拟地址?我们还知道,地址是线性的0 1 2 …… 0xffffffffh,线性排布,那么问题来了,我的程序加载到内存中,假如说是0x87654321,欸?此时我有一个指令是mov dword ptr [0] , 1,那么会把内存开始地方的东西改动掉,但是内存最开始的东西肯定是很内核的东西,什么对程序的调用啊,什么的之前在反调试有接触过
自然是不能让你随便动他的,那么怎么才能很安全的做到呢
目前我学到的是分段分页,相当于在原本的0x55555555h的地方一刀斩断,后面又开始0x0h(应该如此,后续回来补充)
嗯不是这样的,是分成一段一段然后通过代码段,数据段来区分,这是段保护机制
页保护估计就是进程线程的吧。。。
段检测
段
CS 1B 代码段
SS 23 栈段(栈,局部变量)
DS 23 数据段(堆,全局变量)
ES 23 扩展段(可以还原ds)
FS 上下文环境段 R3代表TEB R0代码是KPCR
1 | // study02.cpp : 定义控制台应用程序的入口点。 |
该程序结果是1b,所以cs的值是1b
windbg的一些指令
r 查看一些寄存器(r eax | r al | r gdtr)
d 查看地址(byte(db) | word(dw) | … ) | (L limit 限制要看多长) | (s 竖着看)
e 写内存(用法和d一致)
段选择符
长度一字节,比如前面的CS里的1B值就是段选择符(0x18是打印cs值会打印出来的)
1B:0000 0000 0001 1 0 11
0011 查找表 请求权限
3,在表中的第3位 0=GDT Global Decsrctor Table(全局描述表)
1=LDT Local Decsrctor Table(本地描述表)
段描述符
也就是前面的GDT和LDT表的内容
举个例子:
kd> r gdtr
gdtr=80b98800
通过查找指令找到gdt表,gdt表是按照1字也就是两字节的形式为最小单位,所以查找的时候要用dq
kd> dq 80b98800
80b98800 00000000
00000000 00cf9b00
0000ffff80b98810 00cf9300
0000ffff 00cffb00
0000ffff80b98820 00cff300
0000ffff 80008bb9
8c0020ab80b98830 804093b9
b0004fff 0040f300
00000fff80b98840 0000f200
0400ffff 00000000
0000000080b98850 800089b9
ad200067 800089b9
acb0006780b98860 00000000
00000000 00000000
0000000080b98870 800092b9
880003ff 00000000
00000000
按照上面索引第三个(注意,从0开始数) 00cffb00`0000ffff,和段选择符一样,要拆分分析
对照上表,高字节部分和低字节部分
31 24 19 16 12 8 0
00 c f f b 00
1100 1111
h: b gb0a l hd s t b
0000 ffff
l: b l
limit:f+ffff=0xfffff 段限制大小(sizeof(cs)):(h&l)+(l&l)
base:00+00+0000 基址大小(base):(h&b)+(h&b)+(l&b)
type:(h&t)b tpye作用见下表
s:(h&s)1
DPL:(h&d)3
p:(h&p)1 p用来证明该段是否有效,1有效,0无效
avl:(h&a)0
0:(h&0)0
D/B:(h&b)1
G:(h&g)1 若为1,则以页为单位(影响的是段限制的单位)(一个页4096字节) 转换为hex->0x1000
故真正限制大小:(limit+1)*(g) 按照上面的计算也就是(0xfffff+1)*0x1000=0x1 0000 0000,范围就是0-0xf ffff fff 也就是4个g
type作用:
练习代码
1 | // study02.cpp : 定义控制台应用程序的入口点。 |
三
再次总结一下,cs,ds这些段如果是cs:[x],那么会调用代码段中的x地址,但是如果只是cs,也就是cs中的值,并不是基地址,而是段选择符,也就是可以拆解的,有时候就会去对照gdt表,然后查看gdt里的东西
d/b
话解上题,通过windbg更改端段描述符cs,ds可以做到全局改变,d/b的作用是用来规定操作符大小的,比如正常push指令push的是一字节,但是如果d/b位改变,这里有可能只push一字等,也就是本来的push xx xx xx xx就会变成push xx xx
个人感觉就像是改变这个电脑是32位还是16位还是64位,存在用处也很明显,就是可以兼容低版本(这里注意改变的是段描述符里的d/b也就是说,该段相关改变,比如ss(栈)改了的话,call指令就会有问题,因为call相当于pop jmp,但是操作字节变少了,原本是call 0x12345678,pop出来就只有0x1234,jmp 0x1234,所以就会出现问题