我读了这篇文章:http://static.patater.com/gbaguy/day3pc.htm
它包括这句话
永远不要改变CS!!
但是,如果您修改了
CS
段寄存器,到底会发生什么? 为什么这么危险?
cs
是代码段。 cs:ip
,即cs
与ip
(指令指针)一起指向下一条指令的位置。因此,对 cs
或 ip
或两者的任何更改都会更改获取和执行下一条指令的地址。
通常您可以将
cs
更改为 jmp
(跳远)、call
(长叫)、retf
、int3
、int
或 iret
。在 8088 和 8086 中,pop cs
也可用(操作码 0x0F)。 pop cs
在 186+ 中不起作用,其中操作码 0x0F 是为多字节指令保留的。 http://en.wikipedia.org/wiki/X86_instruction_listings
跳远或长叫本身并没有什么危险。您只需要知道您跳转或调用的位置,并且在保护模式下您需要有足够的权限才能执行此操作。在 16 位实模式(例如 DOS)下,您可以跳转并调用您想要的任何地址,例如
jmp 0xF000:0xFFF0
将 cs
设置为 0xF000
,并将 ip
设置为 0xFFF0
,这是 BIOS 代码的起始地址,从而重新启动计算机。不同的内存地址有不同的代码,从而导致不同的结果,理论上一切可能发生(如果你跳到用于格式化硬盘的BIOS代码,具有有效的寄存器和/或堆栈值,那么硬盘将被格式化'按要求')。实际上,大多数地址的 jmp
和 call
可能很快就会导致无效操作码或其他一些异常(除以零、除溢出等)。
在保护模式和长模式(即非16位实模式)下,设置包括CS在内的任何段寄存器不再只是将段基址设置为
Sreg<<4
;这只是在实数模式中作为 8086 获得额外 4 位线性地址的一种方式。 相反,sregs 索引到段描述符表 (GDT),具有基数 + 限制(正常基数 = 0 限制 = 4GiB,即平面内存模型),但也具有其他属性。
代码段描述符决定CPU模式(例如32位兼容模式与64位长模式)。 在 64 位内核上,64 位用户空间进程可以对某些 32 位代码生成
far jmp
。 这在实践中没有用,甚至可能在上下文切换后操作系统返回到您的进程时中断。
TODO:挖掘一个链接,其中有人展示了如何做到这一点。 我认为最近甚至有一个与此相关的问题,并提供了有关如何找到正确的段号的详细答案。