JOP 利用思想和 JOP 链构造分析全过程,以一个题目为例
字数 1805 2025-08-29 08:30:24
JOP利用思想和JOP链构造分析
前言
本文以Hack the Box困难难度的pwn题目"no return"为例,详细讲解Jump-Oriented Programming(JOP)的思想和构造JOP链的全过程。该题目是一个栈溢出题目,但没有可用的ret指令,gadget多以jmp结尾,因此需要使用JOP技术进行利用。
题目分析
基本情况
- 题目名称:no return
- 难度:困难
- 类型:栈溢出
- 特点:
- 没有可用的ret指令
- gadget多以jmp结尾
- 提供了栈地址泄露
- 没有PIE保护
逆向分析
- start函数提供了地址泄露,位于rsp的值
- 程序通过jmp方式跳转到栈上的返回地址而非ret
利用思路
目标
构造execve系统调用,需要满足:
- rax = 0x3b
- rdi = 指向"/bin/sh"的指针
- rsi = 0
- rdx = 0
- 最后调用syscall
程序特点利用
- 栈地址已知 → 缓冲区地址可知
- 没有PIE → 可以任意跳转到其他代码片段
- 通过控制寄存器值来控制跳转
ROP与JOP对比
ROP (Return-Oriented Programming)
- 依赖ret指令
- 链式调用gadget
- 通过覆盖栈上的返回地址,依次执行多个以ret结尾的代码片段
- 栈本身就是夹杂数据的跳转表
JOP (Jump-Oriented Programming)
- 利用间接跳转指令(jmp)
- 链接gadget
- 通过篡改寄存器或内存中的跳转目标地址构造执行链
- 可能通过"调度器gadget"动态选择后续执行的代码片段
- 攻击链更复杂且非线性
JOP链构造原理
基本结构
需要找到一组分发器-分发表组合:
- 通过分发器选择分发表
- 跳转到指定的以jmp返回的gadget
- 然后jmp回分发器
- 再次进入分发表跳转到下一个gadget
分发器gadget选择
理想情况:
- 有一组寄存器在后续gadget中不会被影响
- 通过同一组gadget进行地址选择
题目中的情况:
- 大多数gadget以
jmp [rcx]和jmp [rdx]结尾 - 这两个寄存器变化频繁,不适合用作分发器
- 最终选择
jmp qword ptr [rbp - 0x39]作为分发器
JOP链构造过程
初始形态
payload初始结构:
- 可控区域共0xc0大小
- 0xb0位置是栈溢出控制的返回地址
- 设置为初始化寄存器的跳转gadget
- 0xb8是新rsp
- 设置其他寄存器后通过
jmp qword ptr [rdi+1]跳转到分发器 - 分发表从0x80处开始
寄存器赋值分析
需要处理的寄存器:
- rax = 0x3b
- rdi = 指向"/bin/sh"的指针
- rsi = 0
- rdx = 0
可用gadget分析:
- 控制rdi的gadget:通过rcx赋值,跳转到rdx
- 需要rcx和rdx可控
- rcx执行字符串地址
- rdx指向分发器
- 控制rax的gadget:通过rdx赋值,跳转到rcx
- 需要rdx=0x3b
- rcx指向分发器
- rsi可在初始化阶段完成赋值
- rdx可通过栈pop赋值
构造步骤
第一步:设置rdi指向字符串
- 设置rcx指向字符串地址
- rdx指向分发表
- 完成第一次跳转
第二步:设置rdx
- 通过栈提供rdx需要的值(0x3b)
- 修复rcx的值
- 此时:
- rsi=0
- rdx=0x3b
- rdi=sh字符串地址
- rax=0
第三步:设置rax
- 使用rdx=0x3b设置rax
- rcx依然指向分发器
最后一步:执行syscall
- 参数已齐备
- 下一次跳转指向syscall地址
关键点总结
-
JOP链构造思路:
- 找分发器和分发表
- 找到需要的gadget
- 在分发器结构不变的情况下串联gadget
-
与ROP的区别:
- JOP使用jmp而非ret
- 需要维护分发器和分发表
- 需要更注意寄存器状态
-
本题特殊技巧:
- 利用
jmp qword ptr [rbp - 0x39]作为分发器 - 通过栈控制分发表跳转顺序
- 精心安排寄存器赋值顺序
- 利用
参考资料
- Hack-The-Box-pwn-challenge[no-return] | 0xfd's blog
- Will's Root: Jump Oriented Programming and Call Oriented Programming (JOP and PCOP)
- asiaccs11.pdf