这个part完全就是被同事教学的~ 欢迎探讨反正我也不懂太多~~
首先iOS里面的签名证书是哪里选的呢?
当我们改了上面的以后,下面的位置就会跟着变化:
如果已经打出包了要怎么看打包用的证书呢,可以用codesign
:
codesign -d -vv /Users/xxx/Library/Developer/Xcode/Archives/2020-07-10/Example1\ 2020-7-10\,\ 3.28\ PM.xcarchive/Products/Applications/Example1.app
Executable=/Users/xxx/Library/Developer/Xcode/Archives/2020-07-10/Example1 2020-7-10, 3.28 PM.xcarchive/Products/Applications/Example1.app/Example1
Identifier=ppp.Example1
Format=app bundle with Mach-O thin (arm64)
CodeDirectory v=20400 size=10672 flags=0x0(none) hashes=325+5 location=embedded
Signature size=4817
Authority=Apple Development: [email protected] (CLXRL46)
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=Jul 10, 2020 at 3:28:52 PM
Info.plist entries=26
TeamIdentifier=MBAR55JA23
Sealed Resources version=2 rules=10 files=43
Internal requirements count=1 size=176
- 关于证书签名是啥强推这篇:http://blog.cnbang.net/tech/3386/
那为啥要讲证书呢?当我们build现有的工程的时候,如果你本地之前木有证书,它会根据你当前的scheme去苹果后台拉对应的证书的,下载到本地的钥匙串里。
iOS的调试权限权限其实是被放在可执行文件中的,如果想可以调试加断点之类的,使用lldb命令,必须在mobileprovision里面有下面的权限:
security cms -D -i /Users/xxx/Library/Developer/Xcode/Archives/2020-07-10/Example1\ 2020-7-10\,\ 3.28\ PM.xcarchive/Products/Applications/Example1.app/embedded.mobileprovision
AppIDName
XC ppp Example1
ApplicationIdentifierPrefix
MBAR55JA23
CreationDate
2020-07-10T06:27:49Z
Platform
iOS
IsXcodeManaged
DeveloperCertificates
MIIFrjCCBJagAwIBAgIIf7+wCcqBDbIwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMjAwMTEyMDMxMDI0WhcNMjEwMTExMDMxMDI0WjCBjDEaMBgGCgmSJomT8ixkAQEMCkZTWlA5NUY5UFUxODA2BgNVBAMML0FwcGxlIERldmVsb3BtZW50OiB5YW5neWluZ0BwMS5jb20gKENMWFJMNDZTSDcpMRMwEQYDVQQLDApNQkFSNTVKQTIzMRIwEAYDVQQKDAl5aW5nIHlhbmcxCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz5Okksfo+yQin2mYQF6scJh9hRL86Y5+yQw6QVQ773VfiW5Pfk+sRKc0811/c8hI3tx+DSOBPHXVXo4oeTrjzLCW1nMmUbDfyzJNehm8Gm3LDZkovBv5deatdXe22qN1/UjOQfgkQqF7NKhnA26Cyo1RyCWjSOjWniHEZFx4QKRZ+ie+2sqReRHYL+v9fofb7ibP7kI89kWP+F55uaIuqdRAY9RJlV0WmvxvKL/xJKuGWmSTQPm/uYc6AoYQBPCtRBuPFyZ7CtsZiC/QQeqEBQmQZ+EvMMP45VMdEmiLfm/I66qXwADbpEKNsff1zkH85AkCoZrkes9uUP0SvcxErwIDAQABo4ICBjCCAgIwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBSIJxcJqbYYYIvs67r2R1nFUlSjtzA/BggrBgEFBQcBAQQzMDEwLwYIKwYBBQUHMAGGI2h0dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtd3dkcjE5MIIBHQYDVR0gBIIBFDCCARAwggEMBgkqhkiG92NkBQEwgf4wgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wNgYIKwYBBQUHAgEWKmh0dHA6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5LzAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUgpb2h1xwydhmjADkdoTP1aBOIzYwDgYDVR0PAQH/BAQDAgeAMBMGCiqGSIb3Y2QGAQIBAf8EAgUAMBMGCiqGSIb3Y2QGAQwBAf8EAgUAMA0GCSqGSIb3DQEBCwUAA4IBAQARyV9FpdBKTQC96dC8sS2vd0gKlfYnObWxImG1X1Oyy9LKPy+l1ivb8tCbJGe1PPOb+L9Idt49t27smbsBQCRIfLdZP9PoTcpYXeEH6EkP9BBd+mAJFH7YhyBh4pmfDz3ov71kWJP6BBKrFQpPuM3cEZxCZERp/3ZnYuDcBYGPBA3YzniwulV/CajgpeOsQPh+7ZgVI6FeAzuqmnIMuZxKnRjy/ALMiEnwuvJJu8uZd5KekYhqc57VglJOtgM94V3IAte3dwrx3j+NIyskZPIUE92KGR7aWNaSWs9Mq+pv+C2nco6/kbpcIFlz/cWvZCeDezxmu2rV89X2UjNHI0AR
Entitlements
application-identifier
MBAR55JA23.ppp.Example1
keychain-access-groups
MBAR55JA23.*
get-task-allow
com.apple.developer.team-identifier
MBAR55JA23
ExpirationDate
2020-07-17T06:27:49Z
Name
iOS Team Provisioning Profile: ppp.Example1
ProvisionedDevices
00008020-0008293E0AF2002E
LocalProvision
TeamIdentifier
MBAR55JA23
TeamName
xxx
TimeToLive
7
UUID
db558e84-552f-42e4-9117-da930344ce9b
Version
1
画个重点,调试是由
来决定的~~如果是distribution打包的一般这里是不允许调试的也就是false啦,这个是最上层的开关,也就是xcode里面所有开关都是在这个为true的基础上才能进行调试的。
get-task-allow
所以Debug->Attach to Process
只能调试gta为true的APP,我们通过monkeyDev重新打包的时候其实也是用了我们本地的证书给别的app打包加上了gta权限所以是可以加断点的。
gta是写在哪里的呢?其实是entitlements文件里。
但是当我把这里改为NO的时候,build会报错:Provisioning profile "iOS Team Provisioning Profile: xxx" doesn't match the entitlements file's value for the get-task-allow entitlement.
那么Provisioning profile
是什么呢可以参考:https://blog.csdn.net/kuangdacaikuang/article/details/52984398?utm_source=blogxgwz4
所以其实provisioning profile
里面也是有entitlements的,我们自己工程里面的entitlements不能和provision里面的冲突,这样就会出现build error。
下一个问题是我们通过电脑下发lldb命令是如何让手机上的进程加断点或者看内存的呢?
当我们第一次debug并将device加到Xcode的列表里面的时候,其实xcode自动帮我们在手机上装了一个debug server,然后电脑lldb就可以通过访问连接手机上的debug server,指定我们调试的app的pid,做到让手机上的进程暂停神马的。
所以其实debug server就是和手机上的app进程通讯的一个媒介,通过修改debug_server增加task_for_pid
权限,可以调试任意App(需越狱,可参考:https://www.jianshu.com/p/14fee00ab720)。有gta标志的进程是可以被debug server调试的不需要指定task_for_pid
权限~
下面言归正传~ 软件断点有三种常用的:符号断点(Symbolic Breakpoint)、异常断点(Exception Breakpoint)、手动加的断点。
如果用LLDB命令加断点可以用以下的一些:欢迎参考使用https://www.jianshu.com/p/b18991aa7a04
1.b 某文件.m:30 给某文件的第30行打个断点
2.br l 列出当前工程的所有断点信息(有断点编号)
3.br delete n 删除第n个断点
4.br enable n 使第n个断点有效
5.br disable n 使第n个断点失效
6.br set -n 某方法 设置关于这个方法的符号断点,在调用这个方式时,程序都会暂停
7.br mod -c "某文件" n 设置条件断点
8.Continue 简称c 断点继续执行 直接输入在lldb控制台上
9.stepOver 简称n 一步一步执行方法
10.stepInto 简称s 进入方法调用里面
11.stepOut 简称finish 跳出方法调用
12.br command add 断点编号 可以添加一些po self之类的,在执行到该断点以后会自动执行添加的一系列lldb命令
硬件断点主要是观察寄存器,通过调试寄存器实现,由硬件支持,iOS Arm64设备当前可以下四个硬件断点watchpoint set variable
、watchpoint set expression
、read,write,read_write
Q: 另一个问题是断点的原理是什么?
A: debugger在断点位置,会使用中断指令
(arm64下为)0xD42000000
static const uint8_t g_aarch64_opcode[] = { 0x00, 0x00, 0x20, 0xd4 };
覆盖断点位置的opcode,这样程序执行到目标位置的时候会触发中断,此时debugger会恢复中断位置之前的opcode。我会迷惑为啥已经build好以后的app加断点也可以断,如果是改了执行code,偷偷猜一下其实是在手机不断从可执行文件取址执行的时候,去替换的code,也就是在真的执行前把代码放入内存的时候进行的中断替换。
Expression
expr+方法调用
可以给在运行时加一些行为,比如在断点处调用增加转换背景色的方法。是不是特别方便,不需要重新编辑代码,也不用重新运行就可以达到效果。通过e执行代码,注意定义变量前面加$
- 为什么调试器可以动态执行代码?
正常的进程没有JIT-just in time权限(苹果通过entitlement的dynamic codesigning key控制),查看debugserver发现,他也没有这个权限。
查看XNU代码,发现进程在被ptrace的时候,内核会给这个进程加上CS_DEBUGGED标志位(同时移除CS_KILL和CS_HARD标志位,这两个标志位用于异常处理),在拥有这个标志位时,entitlement会放开对JIT的检查。所以说进程在被调试时能拥有JIT Debug能力
memory read 读取内存
memory read 内存地址
(lldb) memory read 0x100573f70
0x100573f70: 41 d1 a9 8d ff ff 1d 00 00 00 00 00 00 00 00 00 A...............
0x100573f80: 50 40 57 00 01 00 00 00 90 42 57 00 01 00 00 00 [email protected].....
memory read后边可以增加数量、格式、字节数三个参数,
格式:x是16进制,f是浮点,d是10进制
字节大小:b:byte 1字节,h:half word 2字节,w:word 4字节,g:giant word 8字节
(lldb) memory read/4xg 0x100573f70
0x100573f70: 0x001dffff8da9d141 0x0000000000000000
0x100573f80: 0x0000000100574050 0x0000000100574290
memory read 等价于x
(lldb) x 0x100573f70
0x100573f70: 41 d1 a9 8d ff ff 1d 00 00 00 00 00 00 00 00 00 A...............
0x100573f80: 50 40 57 00 01 00 00 00 90 42 57 00 01 00 00 00 [email protected].....
对象的内存的第一个指针就是isa,通过po self拿到对象以后可以通过mem read到isa,和mask (可参考 https://www.jianshu.com/p/8275a6f15bfc) 进行and操作,然后再po新的地址就是isa啦,isa的八个字节之后就是super_class。这个过程需要注意大小端问题,可以参考https://www.jianshu.com/p/8dd0009dbfe9
上面的参考里面还有提到object的内存结构,其实对象里面的property是单独存的,但是isa指针都是一样的
struct Student_IMPL {
Class isa;
int _number;
int _age;
int _height;
};
如果看两个不同button的isa会发现指向的都是一样的:
(lldb) p/x gcdButton->isa
(Class) $0 = 0x000005a1cd13554f UIButton
(lldb) p/x transButton->isa
(Class) $1 = 0x000005a1cd13554f UIButton
memory write 修改内存中的值
memory write 内存地址 数值
(lldb) memory write 0x100573f79 9
(lldb) x 0x100573f70
0x100573f70: 41 d1 a9 8d ff ff 1d 00 00 09 00 00 00 00 00 00 A...............
0x100573f80: 50 40 57 00 01 00 00 00 90 42 57 00 01 00 00 00 [email protected].....
直观的展示内存地址
参考:https://www.jianshu.com/p/a1dc1a8c43d6
Thread
可以操作线程,显示当前线程信息,在运行时,直接改变方法调用的返回值
1.thread list 显示所有线程信息
2.thread select n 选择第n个线程
3.thread backtrace 显示当前thread的堆栈信息
4.thread backtrace all 显示所有thread的堆栈信息
5.thread until i 使线程运行,并在第i行时停下
6.thread return 返回值 直接改变当前方法调用的返回值(适用于黑客调试,暴力返回,例如绕过各种检测函数)
chisel调试
Chisel is a collection of LLDB commands to assist in the debugging of iOS apps.
可以参考 https://www.jianshu.com/p/1f67bc5b582d 的安装,如果是xcode11安装如果有问题可以参考 https://www.jianshu.com/p/463f61eb3917,注意一下新版本文件不一样啦,现在是fblldb.py
下面是chisel所增加的一些command~
Command | Description | iOS | OS X |
---|---|---|---|
pviews | Print the recursive view description for the key window. | Yes | Yes |
pvc | Print the recursive view controller description for the key window. | Yes | No |
visualize | Open a UIImage , CGImageRef , UIView , CALayer , NSData (of an image), UIColor , CIColor , or CGColorRef in Preview.app on your Mac. |
Yes | No |
fv | Find a view in the hierarchy whose class name matches the provided regex. | Yes | No |
fvc | Find a view controller in the hierarchy whose class name matches the provided regex. | Yes | No |
show/hide | Show or hide the given view or layer. You don't even have to continue the process to see the changes! | Yes | Yes |
mask/unmask | Overlay a view or layer with a transparent rectangle to visualize where it is. | Yes | No |
border/unborder | Add a border to a view or layer to visualize where it is. | Yes | Yes |
caflush | Flush the render server (equivalent to a "repaint" if no animations are in-flight). | Yes | Yes |
bmessage | Set a symbolic breakpoint on the method of a class or the method of an instance without worrying which class in the hierarchy actually implements the method. | Yes | Yes |
wivar | Set a watchpoint on an instance variable of an object. | Yes | Yes |
presponder | Print the responder chain starting from the given object. | Yes | Yes |
... | ... and many more! |
寄存器
通过reg read \ write
可以读取寄存器内容了可以解运行状态,无源码调试时跟踪执行逻辑,还可以在crash时,分析寄存器的值推断出错原因
ARM64调用约定采用AAPCS64,参数小于等于8个,使用r0-r7寄存器传递,参与大于8个,超出的部分使用栈传递,浮点数作为参数传递,需要使用浮点数寄存器(S0-S30,D0-D30),返回值放在x0寄存器中,浮点数放在S0\D0
函数调用的时候,栈帧即栈空间中为函数调用维护的独立帧,sp
标识当前栈帧栈顶,fp(x29)
标识当前栈帧栈底,函数调用/返回时,对栈区进行入栈/出栈的操作
crash
强推:https://www.jianshu.com/p/cf0945f9c1f8?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
比较重要的地方是可以用符号断点断到crash的地方,然后其实我们每次看到的+xxx
其实就是多少字节的偏移,一个指令有4个字节,可以通过这个得到是crash到了第几行汇编指令。