甲方IOS二进制入门记录
字数 1619 2025-08-25 22:59:03

iOS二进制安全入门教程

工具准备

核心工具列表

  1. dumpdecrypted: 用于对苹果加密的App进行砸壳
  2. class-dump: 导出MachO文件中ObjC类及方法定义
  3. CydiaSubstrate: 将第三方动态库注入进程
  4. Cycript: 使用JavaScript语法编写ObjC方法
  5. Theos: 越狱插件开发工具
  6. IDA: 反汇编、反编译工具
  7. Hopper: OSX反汇编、反编译工具
  8. Debugserver + LLDB: 动态调试器组合

ARM指令基础

寄存器与指令集

ARM是RISC架构,数据在内存和CPU之间移动只能通过L/S指令完成:

  • ldr: 把数据从内存移到CPU
  • str: 把CPU的数据转移到内存

寄存器用途

寄存器 用途
R0-R3 用于函数参数及返回值的传递,超过4个参数时其他参数存在栈中
R7 栈帧指针,指向母函数与被调用子函数在栈中的交界
R13(SP) 栈顶指针
R14(LR) 存放函数的返回地址
R15(PC) 指向当前指令地址

常用指令

  • ADC: 带进位的加法
  • ADD: 加法
  • AND: 逻辑与
  • B: 分支跳转
  • BL: 带返回的分支跳转
  • BX: 带状态切换的分支跳转
  • CMP: 比较值,结果存在程序状态寄存器
  • BEQ/BNE: 结果为0/不为0则跳转
  • LDR/LDRB/LDRH: 加载数据到寄存器
  • MOV: 传送值/寄存器到另一个寄存器
  • STR/STRB/STRH: 存储寄存器值到内存
  • PUSH/POP: 堆栈操作

LLDB调试技巧

基本命令

  • image list -o -f: 查看进程在虚拟内存中相对模块基地址
  • br s -a [addr]: 设置断点
  • breakpoint delete <breakpoint>: 删除断点
  • s/n: 源代码级单步执行
  • si/ni: 汇编级单步执行(si进入函数,ni不进入)
  • c: 继续执行
  • p: 输出寄存器值
  • register read --all: 读取所有寄存器内容

高级用法

  1. 在Object-C类所有函数设置断点:
    breakpoint set -r '
    

\[ClassName .* \]

$'


2. **查看堆栈**:
```bash
bt  // 查看堆栈
frame select <framenum>  // 选择特定帧
  1. 内存操作:
    memory read <start_address> <end_address>  // 读取内存值
    malloc_info -s <address>  // 查看堆信息
    

Hopper与地址转换

地址转换公式

偏移后模块基地址 = 偏移前模块地址 + ALSR偏移
  • 偏移前地址从Hopper查看
  • ALSR偏移从LLDB获取

使用示例

  1. 在Hopper中找到方法偏移地址(如0x14B6A66
  2. 获取ASLR偏移(如0x30000
  3. 计算实际地址:0x14B6A66 + 0x30000 = 0x14E6A66
  4. 在LLDB中设置断点:br s -a 0x14E6A66

Cycript使用指南

基本操作

  1. 连接进程:

    cycript -p <pid>
    
  2. 查看UI层次:

    [[UIApp keyWindow] recursiveDescription].toString()
    
  3. 查看对象信息:

    [#0x18b1c070 _ivarDescription].toString()
    

高级功能

  1. 查找类实例:

    choose(ClassName)
    
  2. 调用方法:

    [#0x166b4fb0 show]  // 调用实例方法
    [ClassName classMethod]  // 调用类方法
    
  3. 打印对象属性:

    function tryPrintIvars(a){
      var x={}; 
      for(i in *a){ 
        try{ x[i] = (*a)[i]; } catch(e){} 
      } 
      return x; 
    }
    

调试流程

远程调试设置

  1. 端口映射:

    iproxy 1234 1234
    
  2. 启动debugserver:

    debugserver *:1234 -a <pid>
    
  3. LLDB连接:

    process connect connect://localhost:1234
    

debugserver签名

  1. 获取ldid和ent.xml
  2. 签名:
    ldid -Sent.xml debugserver
    
  3. 上传到iOS设备

Objective-C运行时分析

方法调用原理

[person sayHello] 实际转换为:

objc_msgSend(person, @selector(sayHello))

函数原型:

id objc_msgSend(id self, SEL _cmd, ...)

方法返回值

  • 单个返回值存储在R0寄存器
  • 多个返回值通过栈返回

实用技巧

IDA命名约定

  • sub_xxx: 地址xxx处的子例程
  • loc_xxx: 地址xxx处的指令
  • byte_xxx: 位置xxx处的8位数据
  • word_xxx: 位置xxx处的16位数据
  • dword_xxx: 位置xxx处的32位数据

栈帧分析

  • SP(stack pointer): 栈顶指针
  • BP(base pointer): 栈基址指针
  • 栈向下生长,用于存储自动变量和函数调用上下文

参考资料

  1. IDA通过USB调试iOS下的app
  2. 如何防止app被破解
  3. 栈帧详解
iOS二进制安全入门教程 工具准备 核心工具列表 dumpdecrypted : 用于对苹果加密的App进行砸壳 class-dump : 导出MachO文件中ObjC类及方法定义 CydiaSubstrate : 将第三方动态库注入进程 Cycript : 使用JavaScript语法编写ObjC方法 Theos : 越狱插件开发工具 IDA : 反汇编、反编译工具 Hopper : OSX反汇编、反编译工具 Debugserver + LLDB : 动态调试器组合 ARM指令基础 寄存器与指令集 ARM是RISC架构,数据在内存和CPU之间移动只能通过L/S指令完成: ldr : 把数据从内存移到CPU str : 把CPU的数据转移到内存 寄存器用途 | 寄存器 | 用途 | |--------|------| | R0-R3 | 用于函数参数及返回值的传递,超过4个参数时其他参数存在栈中 | | R7 | 栈帧指针,指向母函数与被调用子函数在栈中的交界 | | R13(SP) | 栈顶指针 | | R14(LR) | 存放函数的返回地址 | | R15(PC) | 指向当前指令地址 | 常用指令 ADC : 带进位的加法 ADD : 加法 AND : 逻辑与 B : 分支跳转 BL : 带返回的分支跳转 BX : 带状态切换的分支跳转 CMP : 比较值,结果存在程序状态寄存器 BEQ/BNE : 结果为0/不为0则跳转 LDR/LDRB/LDRH : 加载数据到寄存器 MOV : 传送值/寄存器到另一个寄存器 STR/STRB/STRH : 存储寄存器值到内存 PUSH/POP : 堆栈操作 LLDB调试技巧 基本命令 image list -o -f : 查看进程在虚拟内存中相对模块基地址 br s -a [addr] : 设置断点 breakpoint delete <breakpoint> : 删除断点 s/n : 源代码级单步执行 si/ni : 汇编级单步执行(si进入函数,ni不进入) c : 继续执行 p : 输出寄存器值 register read --all : 读取所有寄存器内容 高级用法 在Object-C类所有函数设置断点 : 查看堆栈 : 内存操作 : Hopper与地址转换 地址转换公式 偏移前地址从Hopper查看 ALSR偏移从LLDB获取 使用示例 在Hopper中找到方法偏移地址(如 0x14B6A66 ) 获取ASLR偏移(如 0x30000 ) 计算实际地址: 0x14B6A66 + 0x30000 = 0x14E6A66 在LLDB中设置断点: br s -a 0x14E6A66 Cycript使用指南 基本操作 连接进程 : 查看UI层次 : 查看对象信息 : 高级功能 查找类实例 : 调用方法 : 打印对象属性 : 调试流程 远程调试设置 端口映射: 启动debugserver: LLDB连接: debugserver签名 获取ldid和ent.xml 签名: 上传到iOS设备 Objective-C运行时分析 方法调用原理 [person sayHello] 实际转换为: 函数原型: 方法返回值 单个返回值存储在R0寄存器 多个返回值通过栈返回 实用技巧 IDA命名约定 sub_xxx : 地址xxx处的子例程 loc_xxx : 地址xxx处的指令 byte_xxx : 位置xxx处的8位数据 word_xxx : 位置xxx处的16位数据 dword_xxx : 位置xxx处的32位数据 栈帧分析 SP(stack pointer): 栈顶指针 BP(base pointer): 栈基址指针 栈向下生长,用于存储自动变量和函数调用上下文 参考资料 IDA通过USB调试iOS下的app 如何防止app被破解 栈帧详解