[iOS] 高级调试&证书

这个part完全就是被同事教学的~ 欢迎探讨反正我也不懂太多~~

首先iOS里面的签名证书是哪里选的呢?

code signing identity

当我们改了上面的以后,下面的位置就会跟着变化:


signing & capacity

如果已经打出包了要怎么看打包用的证书呢,可以用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

画个重点,调试是由get-task-allow来决定的~~如果是distribution打包的一般这里是不允许调试的也就是false啦,这个是最上层的开关,也就是xcode里面所有开关都是在这个为true的基础上才能进行调试的。

所以Debug->Attach to Process只能调试gta为true的APP,我们通过monkeyDev重新打包的时候其实也是用了我们本地的证书给别的app打包加上了gta权限所以是可以加断点的。

gta是写在哪里的呢?其实是entitlements文件里。

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组成

所以其实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 variablewatchpoint set expressionread,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].....

直观的展示内存地址
image
image

参考: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 返回值 直接改变当前方法调用的返回值(适用于黑客调试,暴力返回,例如绕过各种检测函数)
image

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)标识当前栈帧栈底,函数调用/返回时,对栈区进行入栈/出栈的操作

image.png

crash

强推:https://www.jianshu.com/p/cf0945f9c1f8?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

比较重要的地方是可以用符号断点断到crash的地方,然后其实我们每次看到的+xxx其实就是多少字节的偏移,一个指令有4个字节,可以通过这个得到是crash到了第几行汇编指令。

你可能感兴趣的:([iOS] 高级调试&证书)