代码间的跳转(段间跳转)

段寄存器

段寄存器的种类

ES,CS,SS,DS,FS,GS,LDTR,TR

段寄存器的读写

除了CS外,其他寄存器都可以通过MOV,LES,LSS,LDS,LFS,LGS指令进行修改
CS为什么不能直接修改?
CS的改变意味着EIP的改变,改变CS的同时必须修改EIP,所以我们不能使用上面的指令进行修改。

段寄存器究竟有什么用?

保护模式的存在是为了保护内存
保护内存的方式就是通过 段保护 和 页保护
段保护和段寄存器有关

CPL/RPL/DPL

CPL是存寄存器如CS中,
RPL是代码中根据不同段跳转而确定,以动态刷新CS里的CPL
DPL是在GDT/LDT描述符表中,静态的。

我们可以通过CPU当前的执行位置的段寄存器判断目前CPU执行在什么级别下,此处判断就是通过CPL
有人可能会问,那不是RPL吗?
举一个例子
JMP FAR 这个指令,这个指令不同于我们使用的JMP,这个是段间跳转的指令
JMP FAR 选择子:偏移
此时这个指令中的选择子,就是RPL,因为它通过索引去查找了GDT表
我们直接在段寄存器中看到的,其实以及是加载过后的了,是静态的,通过这个指令是动态请求,需要经过查表。

详细想了解可以去查阅这篇文章 一致代码段和非一致代码段 https://blog.csdn.net/feijj2002_/article/details/4597174

一致代码段 和 非一致代码段

不大了解,所以去查阅了资料!

一致代码段

简单理解,就是操作系统拿出来被共享的代码段,可以被低特权级的用户直接调用访问的代码
通常这些共享代码,是"不访问"受保护的资源和某些类型异常处理。比如一些数学计算函数库,为纯粹的数学运算计算,被作为一致代码段

一致代码段的限制作用

1.0环程序 不允许访问 3环数据,核心态不允许调用用户态的数据
2.3环程序 可以访问到 0环数据,但是特权级不会改变:用户态还是用户态

非一致代码段

为了避免低特权级的访问而被操作系统保护起来的系统代码

非一致代码段的限制作用

1.只允许同级间访问.
2.绝对禁止不同级访问:核心态不用用户态.用户态也不使用核心态.

代码间的跳转

(段内跳转)

不改变 CS(段)
近跳转 (Near Call):提供一个8位(1字节)偏移地址
短跳转 (Short): 提供一个16位 / 32位 偏移地址

(段间跳转)

不仅会改变EIP/RIP,,还会还会把全长跳转的16位选择子加载到 CS段
长跳转 (Far Call) :提供一个48位全指针(16位段选择子 + 32位偏移),且其中的段选择子指向代码段描述符

代码间的跳转(段间跳转 非调用门之类)的步骤

段间跳转有两种情况,即要跳转的段是 一致代码段 还是 非一致代码段

同时修改CS和EIP的指令
JMP FAR/CALL FAR/RETF/INT/IRETED
注意JMP/CALL/JCC/RET 只改变EIP的指令

以JMP FAR 为例,语法如下
JMP 段选择子(WORD):偏移(DWORD)
JMP 0x20:0x004183D7

1.拆分选择子

0x20 = 0000 0000 0010 0000
RPL = 0
TI = 0
Index = 4

2.根据Index查GDT表对应的段描述符

四种情况可以跳转:代码段、调用门、TSS任务段、任务门

3.权限检查

非一致段代码段: CPL = DPL && RPL <= DPL
一致段代码段: CPL >= DPL

4.加载段描述符

通过权限检查之后,CPU会将段描述符加载到CS段寄存器中

5.代码执行

CPU 将 CS.Base + Offset 的值写入EIP,然后执行EIP处的代码,实现段间跳转

练习

kd> r gdtr
gdtr=8003f000
kd> dq 8003f000 l30
ReadVirtual: 8003f000 not properly sign extended
8003f000  00000000`00000000 00cf9b00`0000ffff
8003f010  00cf9300`0000ffff 00cffb00`0000ffff
8003f020  00cff300`0000ffff 80008b04`200020ab
8003f030  ffc093df`f0000001 0040f300`00000fff
8003f040  0000f200`0400ffff 00000000`00000000
8003f050  80008954`af000068 80008954`af680068
8003f060  00009302`2f40ffff 0000920b`80003fff
8003f070  ff0092ff`700003ff 80009a40`0000ffff
8003f080  80009240`0000ffff 00009200`00000000
8003f090  00000000`00000000 00000000`00000000
8003f0a0  8200891e`c8b80068 00000000`00000000
8003f0b0  00000000`00000000 00000000`00000000
8003f0c0  00000000`00000000 00000000`00000000
8003f0d0  00000000`00000000 00000000`00000000
8003f0e0  f8009f71`a000ffff 00009200`0000ffff
8003f0f0  8000984f`b68803b7 00009200`0000ffff
8003f100  f840933a`f400ffff f840933a`f400ffff
8003f110  f840933a`f400ffff 00000000`8003f120
8003f120  00000000`8003f128 00000000`8003f130
8003f130  00000000`8003f138 00000000`8003f140
8003f140  00000000`8003f148 00000000`8003f150
8003f150  00000000`8003f158 00000000`8003f160
8003f160  00000000`8003f168 00000000`8003f170
8003f170  00000000`8003f178 00000000`8003f180

访问一个非一致代码段

00cffa00 0000ffff
f = 1111 P = 1 S = 1 DPL = 3
a = 1010
Base = 0x00000000

PixPin_2026-07-23_14-05-57

PixPin_2026-07-23_14-06-10

访问一个一致代码段

在访问非一致代码段时,我们可以从GTD表中找到符合我们要求的段描述符,所以我一开始的尝试就是去找一个一致代码段
结果呢,GTD表中似乎并没有一致代码段。

下面是错误示范:
我一开始去找了 Index = 28
f8009f71 a000ffff
9 = 1001 P = 1 S = 1 DPL = 0
f = 1111
Base = f871a000
JMP FAR 00E0:004018BB
CPL < DPL 这里权限检查不通过,后续不能正常访问
如果强制执行会跳转到ntdll这个模块,此处估计是异常处理
PixPin_2026-07-23_20-08-21

那么该如何访问一致代码段呢,既然没有,那我们自己创造一个就可以了,GDT表是可以修改的
这样我们就可以随心所欲的制造我们想要的结果了

我们按条件来设置 段描述符
00CFFF00 0000FFFF

PixPin_2026-07-25_19-55-31
根据GDT表,我们可以更改Index = 9 的位置,画红圈的位置

windbg下输入指令
eq ffffffff8003f048 00CFFF000000FFFF
如果eq edtr后,8003f048的结果没变化
输入g指令,然后Break,再输入eq edtr
修改后的结果如下
PixPin_2026-07-25_19-56-59

接下来我们就可以开始一致代码段的实验了
PixPin_2026-07-25_20-02-44

PixPin_2026-07-25_20-03-07

大功告成。

posted @ 2026-07-25 20:07  书与兔子  阅读(4)  评论(0)    收藏  举报