动态调试
一、什么是动态调试
- 1.动态调试就是将程序运行起来,通过打断点、打印等方式,查看参数、返回值、函数调用流程等信息
- 2.之前我们说的静态分析,就是程序不运行的时候,对程序的可执行文件进行分析,分析头文件、伪代码之类的信息
- 3.学会动态调试之后,我们就可以分析某个程序的整体调用流程了,例如:分析微信抢红包的时候,就可以知道微信调用了哪些方法去抢红包,以便我们hook
二、动态调试任意App
第一步:将有权限的debugserver安装到手机
1/进入目录
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport
找到手机对应版本,点开dmg;拷贝debugserver备用
/usr/bin/debugserver
2/修改权限
导出权限
$ ldid -e debugserver > debugserver.entitlements
修改内容
温馨提示->
最好整体copy
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.backboardd.debugapplications</key>
<true/>
<key>com.apple.backboardd.launchapplications</key>
<true/>
<key>com.apple.frontboard.debugapplications</key>
<true/>
<key>com.apple.frontboard.launchapplications</key>
<true/>
<key>com.apple.springboard.debugapplications</key>
<true/>
<key>com.apple.system-task-ports</key>
<true/>
<key>get-task-allow</key>
<true/>
<key>platform-application</key>
<true/>
<key>run-unsigned-code</key>
<true/>
<key>task_for_pid-allow</key>
<true/>
</dict>
</plist>
编辑后将其合入debugserver【注意-S后面不要空格】
$ ldid -Sdebugserver.entitlements debugserver
3/将编辑完成的文件放入手机的/usr/bin目录
电脑端操作->
$ scp -r /Users/gn/Desktop/333/debugserver root@10.0.0.17:/usr/bin
手机端操作->如果有过操作,需要删除debugserver
iPhone-7:~ root# rm /usr/bin/debugserver
第二步:让debugserver与App建立交互
在Mac上打开命令行窗口,让10011端口与10011端口映射、10010与22端口映射
新建命令行窗口
$ iproxy 10011 10011
新建命令行窗口
$ iproxy 10010 22
在Mac上新建命令行窗口,然后SSH登陆到手机
gndeMacBook-Pro:~ gn$ ssh -p 10010 root@127.0.0.1
提示
server端密码或是其他发生改变的时候。
解决方法一般就需要删除~/.ssh/known_hosts的东西,然后再登录即可。
登录到手机后,启动手机的debugserver服务,让其与App建立交互
iPhone-7:~ root# debugserver 127.0.0.1:10011 -a WeChat
debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-900.3.106
for arm64.
Attaching to process WeChat...
Listening to port 10011 for a connection from localhost...
Waiting for debugger instructions for process 0.
第三步:让debugserver与LLDB建立交互
在Mac上新建命令行窗口
$ lldb
(lldb) process connect connect://127.0.0.1:10011
Process 1079 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x0000000189d2d198 libsystem_kernel.dylib`mach_msg_trap + 8
libsystem_kernel.dylib`mach_msg_trap:
-> 0x189d2d198 <+8>: ret
libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x189d2d19c <+0>: mov x16, #-0x20
0x189d2d1a0 <+4>: svc #0x80
0x189d2d1a4 <+8>: ret
Target 0: (WeChat) stopped.
(lldb)
使用LLDB命令c,先让程序继续运行
(lldb) c
Process 1079 resuming
(lldb)
测试
(lldb) image list
[ 0] 12AE6955-466B-365D-B4AD-4414062D4AA0 0x0000000100e20000 /private/var/containers/Bundle/Application/155D1862-E927-4904-B513-B48BDA84DE08/WeChat.app/WeChat (0x0000000100e20000)
[ 1] D3BB8E91-0746-3426-8707-5B717D156B4E 0x000000010d2a0000 /Library/Caches/cy-B6mYK0.dylib (0x000000010d2a0000)
[ 2] BF3B96C2-BD3B-390E-BFCF-33656588C86E 0x000000010cf90000 /usr/lib/substrate/SubstrateBootstrap.dylib (0x000000010cf90000)
[ 3] 46F72B20-370B-352E-ABCA-3896633F251D 0x00000001b4191000 /Users/gn/Library/Developer/Xcode/iOS DeviceSupport/13.7 (17H35)/Symbols/System/Library/Frameworks/AuthenticationServices.framework/AuthenticationServices
[ 4] 7602EB30-DE1D-3996-8D0E-024450B58325 0x000000019885d000 /Users/gn/Library/Developer/Xcode/iOS DeviceSupport/13.7 (17H35)/Symbols/System/Library/Frameworks/AVKit.framework/AVKit
[ 5] E545F1E8-28B7-3580-9770-97D87896929B 0x00000001c08be000 /Users/gn/Library/Developer/Xcode/iOS DeviceSupport/13.7 (17H35)/Symbols/usr/lib/libresolv.9.dylib
第四步:动态调试建立好之后,我们就可以使用LLDB指令,来正式开始调试了
LLDB指令
LLDB指令的格式是:
<command> [<subcommand> [<subcommand>...]] <action> [-options [option- value]] [argument [argument...]],其中,command代表命令,subcommand代表子命令,action代表命令的动作,- options 代表命令的选项,argument代表命令的参数,[]中的可以省略。
help命令,用于查看指令的用法,例如:help breakpoint、help breakpoint set
expression命令,执行一个表达式,例如:expression self.view.backgroundColor = [UIColor redColor],expression与print、p、call的效果一样
thread backtrace命令,打印线程的堆栈信息,与bt命令效果一样
thread return []命令,让函数直接返回某个值,不会执行断点后面的代码了,例如:thread return 3,返回了3
frame variable []命令,打印当前栈帧的变量
thread continue、continue、c命令,让程序继续运行
thread step-over、next、n命令,单步执行,把子函数当做一个整体,不会进入子函数
thread step-in、step、s命令,单步执行,遇到子函数会进入子函数内部
breakpoint set命令,设置断点,参数主要有以下几种:
breakpoint set -a 函数地址
breakpoint set -n 函数名
breakpoint set -r 正则表达式
breakpoint set -s 动态库 -n 函数名
breakpoint list命令,列出所有的断点,每个断点都有自己的编号
breakpoint delete 断点编号命令,删除某个断点
breakpoint command add 断点编号命令,给断点预先设置需要执行的命令,到触发断点时,就会按顺序执行
breakpoint command list 断点编号命令,查看某个断点的预设命令
watchpoint内存断点,就是当内存数据改变时,触发此断点,以便确认是谁修改了内存,子命令主要有以下几种:
watchpoint set variable 变量,例如:watchpoint set variable self->_age,当age变量改变时,断点就会触发,以便找到修改age内存的代码
watchpoint set expression 地址,例如:watchpoint set expression &self->_age
watchpoint list,列出所有的内存断点
watchpoint delete 断点编号,删除此内存断点
watchpoint command add 断点编号,给此内存断点,增加预设命令
image lookup,寻找模块信息,如果你想找某个类型、某个方法、某个地址在模块中的什么位置,就可以用这个命令,主要参数如下:
image lookup -t 类型,查找某个类型的信息,例如image lookup -t NSInterger
image lookup -a 地址,看看某个内存地址在模块中的位置
image lookup -n 符号或者函数名,查找某个符号或者函数的位置
image list,列出所加载的模块信息
一些小技巧:敲Enter会自动执行上次的命令、绝大部分命令可以使用缩写
lldb常用命令
breakpoint使用
1、下断点
breakpoint set --name test1
或
breakpoint set -n test1
打印:
Breakpoint 2: where = LLDB`-[ViewController test1] + 23 at ViewController.m:25:5, address = 0x0000000109de9f97
断点位置信息,执行便能在该处断住。
连续下多个断点:
breakpoint set -n "-[ViewController save:]" -n "-[ViewController pause:]" -n "-[ViewController continues:]"
运行c
继续运行,n
单步执行,s
进入函数内部执行,finish执行到函数尾部。
2、查看断点列表
breakpoint list
打印:
1: file = '/Users/hibo/Documents/test/LLDB/LLDB/ViewController.m', line = 21, exact_match = 0, locations = 1 Options: disabled
1.1: where = LLDB`-[ViewController touchesBegan:withEvent:] + 70 at ViewController.m:22:6, address = 0x0000000109de9f46, unresolved, hit count = 2 Options: disabled
2: name = 'test1', locations = 1, resolved = 1, hit count = 7
2.1: where = LLDB`-[ViewController test1] + 23 at ViewController.m:25:5, address = 0x0000000109de9f97, resolved, hit count = 7
3、禁用断点
breakpoint disable //禁用所有断点
breakpoint disable 1.1 //禁用第一个断点
4、启用断点
breakpoint enable //启用所有断点
breakpoint enable 1.1 //启用1处断点
5、删除所有断点
breakpoint delete
breakpoint delete 1
删除只能删除一组,不能单个删除
6、设置selector
breakpoint set --selector touchesBegan:withEvent:
将为所有该方法设置断点
7、设置文件中的selector
断点
breakpoint set --file ViewController.m --selector touchesBegan:withEvent:
8、设置带有相同字符串的方法断点
breakpoint set -r Game:
打印:
Current breakpoints:
1: regex = 'Game:', locations = 3, resolved = 3, hit count = 0
1.1: where = LLDB`-[ViewController pauseGame:] + 43 at ViewController.m:31:5, address = 0x00000001010dff0b, resolved, hit count = 0
1.2: where = LLDB`-[ViewController continueGame:] + 43 at ViewController.m:34:5, address = 0x00000001010dff5b, resolved, hit count = 0
1.3: where = LinkPresentation`-[LPGameCenterInvitationMetadata setGame:], address = 0x00007fff2733e5e9, resolved, hit count = 0
如上也给其他带有Game
字符的类下了断点。
给某一个文件下的带有相同字符串的方法下断点:
breakpoint set --file ViewController.m -r Game
简写:
breakpoint->b
打印列表需要写全:breakpoint list
或者break list
bt、frame命令
1、查看函数相关信息,使用p、down
追踪函数的调用和被调用关系
frame select
使用bt
命令查看函数调用堆栈
2、查找方法的调用者及方法名称
frame variable
methods、pviews
1、methods
打印当前对象的属性和方法
methods self
2、pviews
打印当前视图的层级结构
以上两个命令是lldb
插件名中的命令。chisel安装
……
网友评论