p/x 0X000000010095ECCC+0X0000000000038000
(lldb) p/x 0x0000000100e595b4-0x0000000000038000 (long) $18 = 0x0000000100e215b4
查看某个地址所在模块的信息
watchpoint set expression -w write ―- 0xl01801a48 :给某个地址设置观察断点,内存进行写操作时就会触发断点
x/10xg 0xl01801a48 这里的x 表示用十六进制来显示结果。"g"代表giant word(8字节)大小。所以就是用十六进制显示 0x101801a48所指恐惧的10个64位的元素内容。常见的大小格式为"b-byte"(1字节),"h-half word"(2字节),"w- word”(4字节),"g-giantword”(8字节)。
这里是pc寄存器所对应的地址。
b +[Manager performLoginWithUsernameOnEmail: password: preAuthToken: twoFAMethod: confirmReactivation: rememberDevice:fromDeepLink:onComplete:]
Breakpoint 2: where = Snapchat'+[Manager
br com add 2
> po $x2
> po $x3
> c
> DONE
单击 Product -> PerformAction ->Preprocess xxxx 可以对文件进行预处理,还可以将代码转换成汇编代码。可以帮助我们理解这些宏的作用
(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命令行进行调试时自 动中断在 _dyld_start 处,因为此时dyld已经加载,所以在dyld里面设置一个在所有库中加载并且在constructor 函数执行之前执行的断点,例如initializeMainExecutable
settings set target.process.stop-on-sharedlibrary-events 1
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
| | | | | | | | | | >
| | | | | | | | | | | <_UILabelContentLayer: 0x170425fa0> (layer)
| | | | | | | | | >
| | | | | | | | | | >
查看 “登录”按钮UIButtonLabel的响应链
presponder 0x101c31460
(lldb) presponder 0x101c31460
>
| >
| | >
查看“登录”按钮的Action事件
(lldb) pactions 0x101c73f60
: editButtonPressed:
< NSMallocBlock : 0Xl7444b6d0>
(lldb) pblock 0xl7444b6d0
(lldb) pvc
, state: appeared, view:
| , state: appeared, view:
(lldb) methods 0x10079eab0
:
in ICSplitViewController:
Properties:
@property (nonatomic, getter=isDetailDimmed) BOOL detailDimmed; (@synthesize detailDimmed = _detailDimmed;)
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"
/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"
为了查看某个对象内存分配的调用堆栈,需要在程序启动的环境变量中设置 MallocSlack Logging。
在环境变量中增加 MallocStackLogging 的值1
单击Xcode调试工具栏上的“Debug Momery Graph”按钮
(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
(lldb) e [(SCButton *)0xl2bd4b760 setTitle:@"AloneMonkey" forStaterUIControlStateNormal]
(lldb) e (void)[CATransaction flush]
(lldb) c
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
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
打印参数和当前的环境变量, 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中指定的路径来査找动态库。
handleLongPressAtPoint函数就是用于处理长按事件的函数
第一响应者实现 canPerformAction:withSender: 来确定当前哪些操 作是被允许的、哪些操作是不被允许的。