代码访问内存是通过古老的段+偏移的方式确定内存地址(逻辑地址—>线性地址),保护模式下段寄存器中保存的并不是段的基址,而是16位的段选择子,由段描述符表的索引号(即高13位,低3位置0时就是偏移量了)+1位 TI+2位RPL组成。其中CS寄存器的低两位保存着当前正在执行的代码所在的段的特权级(CPL),而段描述符表中又保存着DPL,因此用户空间和系统空间得以分离。举例来说,Ring3下某进程访问Ring0中的数据,CPU会先从CS寄存器中取得CPL=3,然后比较要访问的数据所在段的段描述符DPL=0,当不满足CPL<=DPL时,为访问违例。
CPU取指也需要访问内存,但应该不会通过段寄存器—>段描述符表—>比较CPL和DPL这么复杂,而是由硬件完成,因此独立的一段代码段应该在一致CPL/RPL中。要跳转到非一致的代码段必须通过系统机制(【原创】rootkit ring3进ring0之门系列[一] — 调用门、【求助】关于异常发生后保护模式下权限转移疑惑)。
段描述符伪代码:
union
{
struct
{
unsigned short Segment Limit 0…15
unsigned short Base Address 0…15
}
int 32
}
union
{
struct
{
unsigend char Base Address 16…23
unsigend char TYPE 4bit/S 1bit/DPL 2bit/P 1bit (先定义的在低位)
unsigend char Segment Limit 16…19 4bit/AVL 1bit/Reserved 1bit/D/B 1bit/G 1bit (先定义的在低位)
unsigend char Base Address 24…31
}
int 32
}
