0%

lldb_help

[TOC]

some gdb tips

在具体地址下断

break *0x0000000000400448

some lldb tips

通用

bug

error: process launch failed: unable to locate lldb-server

sudo ln -s /usr/bin/lldb-server-3.8 /usr/bin/lldb-server

加载so 动态库等

  (lldb) expr (void*)dlopen("/path/to/build/SSLKillSwitch.framework/Versions/A/SSLKillSwitch", 1)
(lldb) expr (void*)dlopen("/Library/MobileSubstrate/DynamicLibraries/Reveal2Loader.dylib",1)
(lldb)
command alias reveal_load_sim expr (void*)dlopen("/Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/RevealServer.framework/RevealServer", 0x2);
(lldb) expr (void*)dlopen("/Library/RHRevealLoader/libReveal.dylib",1)
(void *) $1 = 0x00007f92e74d10c0
Using DYLD_INSERT_LIBRARIES to inject SSLKillSwitch and start the process.

lldb expr 相关

[xxx setHidden:YES] show/hide
caflush Force Core Animation to flush. This will ‘repaint’ the UI but also may mess with ongoing animations

断点相关

1. 对函数下断点

breakpoint set -n text: -c ret == YES    //使用-c设置条件断点
breakpoint set -f xxx.m -l 38 //使用-f指定文件 使用-l指定文件某一行设置断点
breakpoint set -n "-[xxx xxx]" //根据函数名下断点(调试信息)
breakpoint set -a VitrualAddress //根据虚拟地址下断点

b isEven, br s -F isEven //符号断点
breakpoint set -f main.m -l 16:在源码文件的某一行断点
b main.m:17。b是_regexp-break的缩写

breakpoint modify -c 'i == 99' 1 //条件断点
断点时附加自定义操作:breakpoint command add 1

2.观察断点

watchpoint set expression -w write ―- 0xl01801a48 :给某个地址设置观察断点,内存进行写操作时就会触发断点

// 获取需要监控的内存地址
p (ptrdiff_t)ivar_getOffset((struct Ivar *)class_getInstanceVariable([MyView class], "_layer"))

(ptrdiff_t) $0 = 8

watchpoint set expression -- (int *)$myView + 8:监控_layer的地址

变量监控:watchpoint set variable -w read_write
条件监控:watchpoint modify -c '(global==5)'

3.断点后执行命令

b +[Manager performLoginWithUsernameOnEmail: password: preAuthToken: twoFAMethod: confirmReactivation:rememberDevice:fromDeepLink:onComplete:]
Breakpoint 2: where = Snapchat'+[Manager
br com add 2
> po $x2
> po $x3
> c
> po F.viewCFG()
> DONE

7.如何将断点设置在动态库的入口

LLDB命令行进行调试时自 动中断在 _dyld_start 处,因为此时dyld已经加载,所以在dyld里面设置一个在所有库中加载并且在constructor 函数执行之前执行的断点,例如initializeMainExecutable
settings set target.process.stop-on-sharedlibrary-events 1

8.插件断点

bdisable 用正则查找并关闭一组断点
benable 用正则查找并开启一组断点
binside 用相对地址设置断点,自动加上 ALSR 偏移
bmessage 给某个类的 method 设置断点,同时会在其父类上查找 method
pinvocation 打印方法调用堆栈,仅支持x86

9.某个类的所有方法下断点并跟踪打印调用参数

command regex bclass 's/(.+)/rb \[%1 /'
rb -> breakpoint set -r %1
bclass ULLoginViewController -> breakpoint set -r \[ULLoginViewController

br set -r '\[WAChatSessionViewController .*\]'

如果不在MethodTraCeCcmflg.PliSt文件里面配置需要跟踪的类,那么如下设置:
@interface MethodTrace : NSObject
+ (void)addClassTrace:(NSString*) className;
+ (void)addClassTrace: (NSString *)className methodName: (NSString*) methodName;
+ (void)addClassTrace: (NSString *)className methodList: (NSArray*) methodList;
@end

e [MethodTrace addClassTrace:@"WAChatDataStore"]
笔者在MethodTrace的代码里面调试了—下,发现 NSLog没有被输出到Xcode的控制台,但是在Console.app里面可以看到NSLog,所以改用printf来输出。
ENABLE_METHODTRACE

Finding a class with a click

(lldb) b -[NSView hitTest:]
Breakpoint 1: where = AppKit`-[NSView hitTest:], address = 0x000000010338277b
breakpoint set -n "-[NSView hitTest:]" -C "real" -G1
b -[NSResponder mouseUp:]

Filter breakpoints for important content

breakpoint modify -c '(BOOL)[NSStringFromClass((id)[$rdi class]) containsString:@"IDESourceEditorView"]' -G0
最后-G0说的是修改断点,使其在执行动作后不会自动恢复执行。

(lldb) breakpoint set -n "-[UIViewController viewDidLoad]" -C "po $arg1" -G1
-G1选项告诉断点在执行命令后自动继续。

在模块下断

(lldb) rb appendSignal.*_block_invoke -s Commons

启动后断在

-s ( --stop-at-entry )
Stop at the entry point of the program when launching a process.

(lisa)target create tests/binaries/abort
(lisa)process launch -s

设置lldb 使用python2

defaults write com.apple.dt.lldb DefaultPythonVersion 2

计算偏移/ida地址

p/x 0X000000010095ECCC+0X0000000000038000
(lldb) p/x 0x0000000100e595b4-0x0000000000038000 (long) $18 = 0x0000000100e215b4
查看某个地址所在模块的信息

读取目标地址的内存指令

x/10xg 0xl01801a48 这里的x 表示用十六进制来显示结果。"g"代表giant word(8字节)大小。所以就是用十六进制显示 0x101801a48所指恐惧的10个64位的元素内容。常见的大小格式为"b-byte"(1字节),"h-half word"(2字节),"w- word”(4字节),"g-giantword”(8字节)。

dis-a$pc 反汇编指定地址

这里是pc寄存器所对应的地址。

切换分支

f2:切换到当前调用栈为2的位置,也就是bt中的frame #2。

threadinfo:输出当前线程的信息。

b ptrace -c xxx:满足某个条件之后程序才会中断。

help/apropos

xcode预处理快捷键

单击 Product -> PerformAction ->Preprocess xxxx 可以对文件进行预处理,还可以将代码转换成汇编代码。可以帮助我们理解这些宏的作用

pdb来排查了一个脚本中的问题

(lldb) findclass
error: libarclite_macosx.a(arclite.o) failed to load objfile for /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_macosx.a
error: libarclite_macosx.a(arclite.o) failed to load objfile for /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_macosx.a
Traceback (most recent call last):
File "/Users/gogleyin/lldb/findclass.py", line 40, in findclass
raise AssertionError("Uhoh... something went wrong, can you figure it out? :]")
AssertionError: Uhoh... something went wrong, can you figure it out? :]

(lldb) script import pdb
(lldb) findclass
Traceback (most recent call last):
File "/Users/gogleyin/lldb/findclass.py", line 40, in findclass
raise AssertionError("Uhoh... something went wrong, can you figure it out? :]")
AssertionError: Uhoh... something went wrong, can you figure it out? :]

(lldb) script pdb.pm()
> /Users/gogleyin/lldb/findclass.py(40)findclass()
-> raise AssertionError("Uhoh... something went wrong, can you figure it out? :]")

(Pdb) print(codeString) # 这个东西包含了一段oc代码,用oc runtime来找出runtime的所有类
@import Foundation;
int numClasses;
Class * classes = NULL;
classes = NULL;
numClasses = objc_getClassList(NULL, 0);
NSMutableString *returnString = [NSMutableString string];
classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
Class c = classes[i];
[returnString appendFormat:@"%s,", class_getName(c)];
}
free(classes);
returnString; # 返回returnString的值给Python脚本

(Pdb) l 35, 45 # 会列出35到45行代码,注意40行的 -> 表示当前pdb停在的位置
35 '''
36
37 res = lldb.SBCommandReturnObject()
38 debugger.GetCommandInterpreter().HandleCommand("expression -lobjc -O -- " + codeString, res)
39 if res.GetError():
40 -> raise AssertionError("Uhoh... something went wrong, can you figure it out? :]")
41 elif not res.HasResult():
42 raise AssertionError("There's no result. Womp womp....")
43
44 returnVal = res.GetOutput()
45 resultArray = returnVal.split(",")
# 嗯,似乎res.GetError()看起来更加有趣,玩一下先

(Pdb) print res.GetError()
error: 'objc_getClassList' has unknown return type; cast the call to its declared return type
error: 'objc_getClassList' has unknown return type; cast the call to its declared return type
error: 'class_getName' has unknown return type; cast the call to its declared return type
# 到这里可以看到,问题是codeString里面的代码让LLDB迷惑了。
# 实际这种错误在LLDB里面是非常常见的。你需要告诉LLDB一个函数的返回类型,因为它无法知道那是啥。
# 在这个case下, objc_getClassList 和 class_getName 都有未知的返回类型
# Google一下便知这两个函数的签名如下:
int objc_getClassList(Class *buffer, int bufferCount);
const char * class_getName(Class cls);
# 所有我们需要做的事转换返回类型到正确的值就可以啦。如下:

codeString = r'''
@import Foundation;
int numClasses;
Class * classes = NULL;
classes = NULL;
numClasses = (int)objc_getClassList(NULL, 0);
NSMutableString *returnString = [NSMutableString string];
classes = (__unsafe_unretained Class *)malloc(sizeof(Class) *numClasses);
numClasses = (int)objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
Class c = classes[i];
[returnString appendFormat:@"%s,", (char *)class_getName(c)];
}
free(classes);
returnString;

--debug选项是定位JIT代码中的问题的非常好的手段,调试lldb 的好方法
expression --debug -lobjc -O --
可以选择使用frame variable命令打印变量

基本的类:

  • lldb.SBDebugger:在你的脚本中用来访问类的实例的类,非常中心,还处理LLDB命令的输入和输出
  • lldb.SBTarget:与被调试的可执行文件有关(相关调试文件,磁盘上的文件)。
    你可以用SBDebugger的实例来获取到当前选择的SBTarget。然后 你就可以通过SBTarget访问大部分其余类。
  • lldb.SBProcess:SBTarget和SBProcess是一对多的关系:SBTarget管理者一个或多个SBProcess实例。SBProcess处理内存读写还有它自己的线程。
  • lldb.SBThread:管理对应线程的栈帧和stepping的控制逻辑
  • lldb.SBFrame:管理局部变量(debug信息有提供的)和当时的寄存器快照
  • lldb.SBModule:代表着一个可执行文件。
  • lldb.SBFunction:这代表着一个加载到内存中的函数(或者对应代码),它与SBFrame是一对一的关系。
    实例是 lldb.debugger/lldb.target…
    https://lldb.llvm.org/python_reference/index.html

    演示了LLDB Python主要的几个类之间的相互关系

    暂停在某函数时几个类的交互

android

iOS

砸壳

Cronet.framework git:(master) X otool -hf Cronet Fat headers
fat_magic 0xcafebabe
nfat_arch 2
architecture 0 cputype 12 cpusubtype 9 capabilities 0x0
offset 16384 size 2749664 align 2A14 (16384)
architecture 1 cputype 16777228 cpusubtype 0 capabilities 0x0
offset 2768896 size 3612224 align 2A14 (16384)
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
Oxfeedface 12 9 0x00 6 27 3328 0x00118085
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
0xfeedfacf 16777228 0 0x00 6 27 3816 0X00118085

otool -arch arm64 -1 Cronet | grep crypt
cryptoff 16384
cryptsize 3309568
cryptid 1
(lldb) im li Cronet
[0] 188F5BF7-B4C4-36EF-BB9A-976FA870F9D7 0x0000000105920000 /private/var/mobile/Containers/Bundle/Application/3A4C68EB-4059-47D4-ACE6-BE9C492DF205/ Snapchat.app/Frameworks/Cronet.framework/Cronet (0x0000000105920000)
(lldb) memory read --force --outfile ~/Desktop/dumpoutput --binary count 3309568 16384+0x0000000105920000
因为dump出来的文件都没有Mach-0文件头,所以在这里要先把dump出来的数据写回原 来加密的文件,以替换原来加密的部分
2768896(之前获取的ARM64架构的偏移值)+16384(加密数据的偏移值)=2785280(写入的加密数据在文件中的偏移值)
seek=n Seek n blocks from the beginning of the output before copying.
bs=n Set both input and output block size to n bytes
conv=value[,value ...]
notrunc Do not truncate the output file.
Cronet.framework git:(master) X dd seek=2785280 bs=l conv=notrunc if=/Users/monkey/Desktop/dumpoutput of=./Cronet
3309568+0 records in
3309568+0 records out
3309568 bytes transferred in 4.698067 secs (704453 bytes/sec)
Cronet.framework git:(master) X lipo Cronet -thin arm64 -output Cronet_arm64
MachOView.app 修改Cronet_arm64 crypid 为0

查询按钮事件

error: error: use of undeclared identifier 'UIApplication'
None
(lldb) expression @import UIKit
pviews
| | | | | | | | | | <UIButtonLabel: 0x101c730a0; frame = (7.5 12.5; 69.5 20.5); text = '所有图书'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x174295a90>>
| | | | | | | | | | | <_UILabelContentLayer: 0x170425fa0> (layer)
| | | | | | | | | <IMTouchInsetsButton: 0x101c73f60; baseClass = UIButton; frame = (324 5.5; 35 33); opaque = NO; layer = <CALayer: 0x17422d880>>
| | | | | | | | | | <UIButtonLabel: 0x101c31460; frame = (9 6.5; 35 20.5); text = '选择'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x174295360>>

查看 “登录”按钮UIButtonLabel的响应链
presponder 0x101c31460
(lldb) presponder 0x101c31460
<UIButtonLabel: 0x101c31460; frame = (9 6.5; 35 20.5); text = '选择'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x174295360>>
| <IMTouchInsetsButton: 0x101c73f60; baseClass = UIButton; frame = (324 5.5; 35 33); opaque = NO; layer = <CALayer: 0x17422d880>>
| | <IMToolbar: 0x101c1d8b0; baseClass = UIToolbar; frame = (0 20; 375 44); opaque = NO; autoresize = W; tintColor = UIExtendedSRGBColorSpace 0 0.478431 1 1; layer = <CALayer: 0x174224ec0>>
查看“登录”按钮的Action事件
(lldb) pactions 0x101c73f60
<BKLibraryViewController: 0x102817600>: editButtonPressed:

chisel 打印结构

brew install chisel –verbose

< NSMallocBlock : 0Xl7444b6d0>
(lldb) pblock 0xl7444b6d0

(lldb) pvc
<ICSplitViewController 0x10079eab0>, state: appeared, view: <UILayoutContainerView 0x1009261c0>
| <UIMultiColumnViewController 0x10079f410>, state: appeared, view: <UIView 0x1007aee70>

(lldb) methods 0x10079eab0
<ICSplitViewController: 0x10079eab0>:
in ICSplitViewController:
Properties:
@property (nonatomic, getter=isDetailDimmed) BOOL detailDimmed; (@synthesize detailDimmed = _detailDimmed;)

对象查找

fv 用正则查找所有类的 view 实例
fvc 用正则查找所有类的 view controller 实例
findinstances 在内存中查找某个类的所有实例
flicker 闪烁某个 view,用于快速定位

对象分析

pinternals 打印对象内部的所有实例变量
pkp 用 -valueForKeyPath:获取对象的数据
pmethods 打印类的所有方法
poobjc 用 ObjC++ 语言执行和获取表达式的结果,expression -O -l ObjC++ —的缩写
pproperties 打印对象或者类的属性
pivar 打印对象的某个 ivar
wivar 给对象的某个实例变量地址设置 watchpoint,监控变化
pclass 打印某个对象的类继承链
pbcopy 打印对象并且把结果复制到粘贴板
pblock 打印 block 的实现函数地址和签名
pactions 打印 UIControl 的 target 和 action

视图查找

visualize 显示 UIImage, CGImageRef, UIView 或 CALayer 的图片内容,用 Mac 的预览打开,在调试绘图时非常有用
taplog 打印触摸到的 view,用于快速定位
border 给 view 加上边框,用于定位某个 view 对象
unborder 移除 view 或 layer 的边框
caflush 修改 UI 后刷新 Core Animation 界面
hide 隐藏 view 或 layer
show 显示一个 view 或者 layer,相当于执行view.hidden = NO
mask 给 view 添加半透明的 mask,可以用来查找被隐藏的 view
unmask 移除 view layer 的 mask
setinput 给作为 first responder 的 text field 或 text view 输入文本
slowanim 减慢动画速度
unslowanim 动画速度回复正常
present Present 一个 view controller
dismiss 消除 present 出来的 view controller

视图层级

pvc 循环打印 view controller 的层级
pviews 循环打印 view 的层级
pca 打印 layer 树
vs 在 view 层级中搜索 view
ptv 打印最顶层的 table view
pcells 打印最顶层 table view 的所有可见的 cell
presponder 打印 UIResponder 响应者链

其他工具

sequence 执行多条命令,用;分隔
pjson 打印 NSDictionary 或 NSArray 的 JSON 格式
pcurl 用 curl 的格式显示 NSURLRequest (HTTP)
pdata 用字符串的形式显示 NSData
mwarning 模拟内存警告

视图调试

alamborder 给有约束错误的 view 加上边框
alamunborder 有约束错误的 view 加上边框
paltrace 打印 view 的约束信息,相当于调用_autolayoutTrace
panim 是否正在执行动画,相当于调用[UIView _isInAnimationBlock]

NSObject私有方法,可以方便查看对象的内容:

_methodDescription:打印对象或者类的整个继承链上的方法列表,同时显示方法的地址,可以直接用于断点
_shortMethodDescription :打印对象或者类的方法列表,不显示父类
_ivarDescription:打印对象或者类的所有实例变量和值

搜索UITextField的实例对象和Cycript中的choose—样的

search UIButton
search UITextField
# Find all UIViews, ignore subclasses
find UIView -e

# Find all instances of UIViews (and subclasses) where tag == 5
find UIView -c "[obj tag] == 5"

读取内存

memory read --size 4 --format x --count 4 0xbffff3c0
me r -s4 -fx -c4 0xbffff3c0

查看对象内存关系,xcode观看

/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/Resources/Python/lldb/macosx/heap.py
command alias iheap command script import lldb.macosx.heap
"malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs"

ptr_refs
可以在内存中找出哪些地址引用了某个指针,也就相当于查看某个变量在哪里被引用。

cstr_refs
在内存中寻找某个C String在哪里被引用。

find_variable
在当前栈帧上寻找某个局部变量在哪里被引用。

objc_refs
在内存中寻找某个类的实例。

为了查看某个对象内存分配的调用堆栈,需要在程序启动的环境变量中设置 MallocSlack Logging。
在环境变量中增加 MallocStackLogging 的值1
单击Xcode调试工具栏上的“Debug Momery Graph”按钮

malloc_info --stack-history 0x10010d680。可以快速追溯对象的创建来源,
参考iOS逆向:在任意app上开启malloc stack追踪内存来源
https://www.jianshu.com/p/759015369b6f
lldb有一个内存调试工具malloc stack,开启以后就可以查看某个内存地址的malloc和free记录,追踪对象是在哪里创建的。

这个工具可以打印出对象创建的堆栈,而在逆向时,也经常需要追踪某些方法的调用栈,如果可以随时打印出某个对象的创建记录,也就能直接找到其所在的类和方法,不用再花费大量的时间去打log和动态调试追踪了。

执行script

(lldb) e @import UIKit
(lldb) e UIApplication *$app = [UIApplication sharedApplication];
(lldb) e UlWindow *$keyWindow = $app.keyWindow
(lldb) e UlViewController *$root = $keyWindow.rootViewController
(lldb) po $root
<NavigationController: 0xl2c03d200>
(lldb) e [(SCButton *)0xl2bd4b760 setTitle:@"AloneMonkey" forStaterUIControlStateNormal]
(lldb) e (void)[CATransaction flush]
(lldb) c

在 Snapchat模块中查看与login有关的符号信息

image lookup -rn login UserLogin

(lldb) b -[UIView setTail:]
Breakpoint 3: where = UserLogin`-[UIView(Frame) setTail:] at UIView+Frame.m:102, address = 0x0000000100b5ee34
(lldb) il 0x0000000100b5ee34
image lookup -a 0x0000000100b5ee34

xcode 设置环境变量

打印参数和当前的环境变量, segment加载的详细信息,加载dylib,显示是否加载,各阶段的时间消耗
xcode DYLD_PRINT_OPTS, DYLD_PRINT_EN, DYLD_PRINT_SEGMENTS

签名InsertDyUb.dylib,拷贝 InsertDylib.dylib 到Bundle Resources
xcode 设置环境变量DYLD_INSERT_LIBRARIES @executable_path/InsertDylib.dylib

xcode 设置环境变量 DYLD_PRINT_INTERPOSING 运行 App,日志如下即为hook生效
dyld: interposing 2 tuples onto image: /var/containers/Bundle/Application/AB57C532-19F2-4022-B757-7D211296E64D/AppStart.app/InsertDylib.dylib

xcode 设置 DYLD_PRINT_STATISTICS,DYLD_PRINT_STATISTICS_DETAILS 打印各阶段的时间消耗。

@executable_path:表示可执行程序所在的目录,一般是xxx.app
@loader_path:表示每一个被加载的二进制文件的目录。例如,xxxx.plugin/aaa/abc依赖xxx.plugin/bb/ccc.dylib,那么依赖的路径可以写成 @loader_path/../bbb. 这样不管xxx.plugin放在那都能找到ccc.dylib
@rpath:这个变量是在 Xcode build里面设置, Dynamic Libray Install Name设置为(#=@path/xxx/xxx,就可以在使用的工程中设置一个或多个RunPath Search Paths 来指定搜索路径。在运行时,会将@rpath分别替换为Runpath Search Paths中指定的路径来査找动态库。

iOS 分析常见点

见整理的xmind

macos

查询相关 dash lldb3:
记录一些常用的点,基本可以用于iOS 或其他

调试开启配置

wait for pid

This will tell LLDB to attach to the process named Finder whenever it next launches.

lldb -n Finder -w
process attach --name "Finder" --waitfor

直接调试启动

lldb -f /bin/ls
process launch

将Speech框架加载到DeleteMe流程空间

(lldb) process load /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/Speech.framework/Speech

异常

Disabling Rootless
Command + R,start your macOS machine.
csrutil disable && reboot

-w工作目录

选项在哪里启动来更改当前的工作目录。输入以下内容:
(lldb) process launch -w /Applications

环境变量

(lldb) process launch -v LSCOLORS=Af -v CLICOLOR=1 – /Applications/

设置新目标

(lldb) target delete
(lldb) target create /usr/bin/wc

打印相关

设置后刷新

po [$rdi setHidden:!(BOOL)[$rdi isHidden]]; [CATransaction flush]

输出模块中的函数

(lldb) image lookup -rn _block_invoke Commons

调试函数单步相关

步入无符号

(lldb) step -a0
This tells LLDB to step in regardless of whether you have the required debug symbols or not.

pdb调试

def your_first_command(debugger, command, result, internal_dict):
import pdb; pdb.set_trace()
print ("hello world")