代码访问内存是通过古老的段+偏移的方式确定内存地址(逻辑地址--->线性地址),保护模式下段寄存器中保存的并不是段的基址,而是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之门系列[一] -- 调用门【求助】关于异常发生后保护模式下权限转移疑惑)。

段选择子

用比喻类比说明CPU的特权级DPL - CPL - RPL

段描述符伪代码:

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
}

段描述符