iOS逆向-利用LLDB动态调试app

引言

动态调试是逆向工程中“透视”应用运行逻辑的核心手段。相比静态分析,动态调试能实时观察内存状态、函数调用栈和寄存器变化,尤其适用于破解加密算法、分析复杂业务逻辑等场景。本文将以LLDB为核心工具,详解如何对iOS App进行动态调试,覆盖从环境搭建到高级技巧的全流程。

一、核心工具与环境准备

1. 必要工具
  • LLDB:Apple官方调试器,支持源码级调试与内存操作(集成在Xcode中)

  • debugserver:iOS端调试服务程序(需通过越狱设备获取)

  • iOS越狱设备:推荐使用Checkra1n或Unc0ver越狱的iPhone(iOS 14-15.5)

  • Python脚本:通过LLDB的Python API实现自动化调试

2. 环境配置步骤

步骤1:安装debugserver

  • 从越狱设备的/Developer/usr/bin/复制debugserver到Mac

  • 添加调试权限(Entitlements):

    <key>com.apple.springboard.debugapplications</key>  
    <true/>  
    <key>get-task-allow</key>  
    <true/>  
    <key>task_for_pid-allow</key>  
    <true/>  
    <key>run-unsigned-code</key>  
    <true/>  

    重签名:

codesign -s - --entitlements entitlements.plist -f debugserver  

步骤2:配置SSH与端口转发

ssh -p 2222 root@iOS_IP   # 默认越狱SSH端口  
iproxy 1234 1234          # 将设备端口1234映射到本地  

二、动态调试六步法

1. 附加目标进程

命令:

# 启动debugserver监听  
debugserver *:1234 -a "AppName"  

# Mac端连接  
lldb  
(lldb) process connect connect://iOS_IP:1234  

技巧:

  • 若App有反调试保护,需先使用kill -0绕过或Frida Hook ptrace

2. 定位关键函数地址

方法1:符号断点

(lldb) breakpoint set -n "-[UIViewController viewDidLoad]"  

方法2:ASLR偏移计算

(lldb) image list -o -f | grep TargetApp  
  • 计算真实地址:基址 + 偏移

3. 设置断点与观察点

普通断点:

(lldb) br s -a 0x0000000100123456  

内存读写观察点:

(lldb) watchpoint set expression -w write -- 0x16dff0a50  
4. 寄存器与内存操作

查看寄存器:

(lldb) register read x0  

修改内存值:

(lldb) memory write 0x12345678 "AAAA"  

导出内存数据:

(lldb) memory read --outfile /tmp/dump.bin --count 256 0x12345678  
5. 堆栈回溯与流程控制

查看调用栈:

(lldb) bt  

单步执行:

(lldb) ni    # 汇编级单步  
(lldb) s     # 源码级单步  

继续执行:

(lldb) continue  

6. 实时Hook与代码注入

执行任意代码:

(lldb) expr -- (void)printf("Hooked!\\n")  

调用Objective-C方法:

(lldb) expr -- (void)[[NSUserDefaults standardUserDefaults] setObject:@"test" forKey:@"key"]  

三、实战案例:破解登录加密算法

场景描述

目标App的登录请求参数包含加密字段encryptedToken,需逆向其生成逻辑。

调试过程
  1. 定位加密函数

    • 通过字符串搜索找到encryptedToken的赋值位置

    • 设置断点:br s -a 0x100012300+0x1234

  2. 分析寄存器与参数

    (lldb) po $x1           # 查看Objective-C方法名  
    (lldb) x/s $x2          # 查看输入字符串地址  
    (lldb) register read x3 # 查看密钥指针  

  3. 追踪加密结果

    • 在函数返回前(ret指令)捕获X0寄存器的值

    • 导出内存:memory read --format bytes --count 32 $x0

  4. 验证算法

    • 对比多次调试结果,确认是否为AES-CBC模式

    • 提取密钥与IV参数,使用Python还原加密过

四、高级技巧:LLDB自动化

1. Python脚本扩展
# 自动化断点回调  
def breakpoint_callback(frame, bp_loc, dict):  
    print("Hit breakpoint! RAX =", frame.registers["rax"].value)  
    return False  

target = lldb.debugger.GetSelectedTarget()  
bp = target.BreakpointCreateByAddress(0x12345678)  
bp.SetScriptCallbackFunction("breakpoint_callback")  
2. 自定义命令
# 实现命令"dump_encrypted"  
def dump_encrypted(debugger, command, result, dict):  
    frame = debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()  
    encrypted_ptr = frame.FindRegister("x0").GetValueAsUnsigned()  
    data = debugger.GetSelectedTarget().GetProcess().ReadMemory(encrypted_ptr, 32, lldb.SBError())  
    print(bytes(data).hex())  

lldb.debugger.HandleCommand('command script add -f dump_encrypted dump_encrypted')  

五、避坑指南

  1. 代码签名问题

    • 调试前需重签名App:codesign -fs "Your Cert" --entitlements entitlements.plist Target.app

    • 关闭SIP(macOS)与AMFI(iOS):amfi_get_out_of_my_way=0x1

  2. 多线程调试

    • 使用thread list查看所有线程

    • thread continue -t 2恢复指定线程

  3. 性能优化

    • 禁用无关符号加载:settings set target.load-script-from-symbol-file false

    • 使用target modules list管理加载模块

六、扩展思路

  1. 逆向系统框架

    • 结合Dyld Shared Cache分析UIKit内部逻辑

  2. 与Frida联动

    • 通过frida-ll-bridge实现双向通信

  3. 非越狱调试

    • 利用debugserver定制版本绕过签名验证


结语

LLDB动态调试如同为逆向工程师装上“X光透视眼”,不仅能观察应用运行时的每个细节,更能实时干预逻辑走向。掌握本文所述技巧后,读者可快速定位关键代码、破解加密逻辑,甚至挖掘隐藏功能。但切记,技术探索需在合法合规的边界内进行。

“技术是钥匙,但选择打开哪扇门取决于你。”

附录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值