iOS逆向实战--019:符号的剥离与恢复
剥离符号
strip
:移除指定符号。在Xcode
中默认strip
是在Archive
的时候才会生效,移除对应符号
strip
命令的使用:
strip -x
:除了全局符号都可以移除 (动态库使用)strip -S
:移除调试符号(静态库使用)strip
:除了间接符号表中使用的符号,其他符号都移除(上架App
使用)
案例1:
剥离符号在
Xcode
中的设置来到
Build Setting
,设置Deployment Postprocessing
- 打开后在编译阶段就会运行
strip
设置
Strip Debug Symbols During Copy
- 当应用在编译阶段
copy
了某些二进制文件时,打开该选项会脱掉该二进制的调试符号。但是不会脱去链接的最终产物(可执行文件\动态库)的符号信息。要脱去链接的产物(App
的可执行文件)的符号信息设置
Strip Linked Product
- 如果没有打开
Deployment Postprocessing
,则会在Archive
处理链接的最终产物(可执行文件)的符号信息。否则,在链接完成之后就会处理符号信息设置
Strip Style
(符号剥离级别),它会在生成可执行文件后进行优化,相当于对Mach-O
文件进行修改
All Symbols
:除了间接符号表中使用的符号,其他符号都移除(上架App
使用)Non-Global Symbols
:除了全局符号都可以移除 (动态库使用)Debugging Symbols
:移除调试符号(静态库使用)
Strip Style
原理
App
:可以剥离除间接符号表以外的所有符号- 动态库:可以剥离除全局符号以外的所有符号
- 静态库:静态库是
.o
文件的合集,符号都存储在重定位符号表中。静态库只能剥离调试符号
Debugging Symbols
:剥离.o/静态库
的调试符号
Debugging Symbols
:剥离动态库/可执行文件
的调试符号
All Symbols
:剥离除间接符号表以外的所有符号
Non-Global Symbols
:剥离除全局符号以外的所有符号
案例2:
剥离除间接符号之外的所有符号
查看未剥离符号时的
MachO
- 包含本地符号、全局符号、间接符号
Xcode
中设置All Symbols
,编译项目
- 多出
.bcsymbolmap
文件,用于Bitcode
线上崩溃,恢复符号使用查看剥离符号之后的
MachO
- 只剩下间接符号
使用
MachOView
查看
- 粉色:本地符号
- 橙色:全局符号
- 绿色:间接符号
- 符号的
Value
值,代表函数实现地址,即:isa
。间接符号此时还无法确定实现地址,所以全部为0
案例3:
对剥离调式符号的程序,设置
Xcode
断点在
touchesBegan
方法上设置断点
真机运行项目,点击屏幕,不会触发断点,直接输出结果
symbolDemo[4798:893531] 第二次外部函数的调用!
因为调试符号已经被剥离,所以
Xcode
断点不会触发
案例4:
对上述案例,设置符号断点
对
NSLog
设置符号断点
真机运行项目,点击屏幕,触发
NSLog
断点
使用
bt
命令,查看函数调用栈bt ------------------------- * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 * frame #0: 0x000000019d9327f0 Foundation`NSLog frame #1: 0x0000000100f95e44 symbolDemo`___lldb_unnamed_symbol2$$symbolDemo + 32 frame #2: 0x0000000100f95e78 symbolDemo`___lldb_unnamed_symbol3$$symbolDemo + 40 frame #3: 0x0000000100f95eec symbolDemo`___lldb_unnamed_symbol4$$symbolDemo + 104 frame #4: 0x000000019eefe774 UIKitCore`forwardTouchMethod + 316 frame #5: 0x000000019eefe624 UIKitCore`-[UIResponder touchesBegan:withEvent:] + 60 frame #6: 0x000000019ef0c9cc UIKitCore`-[UIWindow _sendTouchesForEvent:] + 640 frame #7: 0x000000019ef0e550 UIKitCore`-[UIWindow sendEvent:] + 3824 frame #8: 0x000000019eee9934 UIKitCore`-[UIApplication sendEvent:] + 744
由于调试符号被剥离,自定义的方法和函数,都会显示为
_unnamed_symbol
。在逆向过程中,动态调试这样的符号不利于分析
动态调试剥离符号后的
App
,如果是OC
方法,会调用objc_msgSend
函数,通过分析x0
、x1
寄存器,查看self
和cmd
也能找到名称但此时不知道自定义函数的名称,符号断点只能设置在系统函数上,然后计算自定义函数在调用时的偏移地址
在下一次运行时,使用
程序首地址 + 偏移地址
计算出函数地址,最后对函数地址设置断点使用以上方式,过于繁琐,所以应该想办法恢复符号
恢复符号
符号表中的符号被剥离,但对于
OC
的类和方法,MachO
中还是会保留它们的名称,以供runtime
使用
__TEXT,__objc_methname
中,保留方法列表
__TEXT,__objc_classname
中,保留类的列表
所以
OC
的类和方法,可以通过分析代码段,重新生成一份符号表
案例1:
使用
restore-symbol
恢复符号表将
MachO
文件,拷贝到restore-symbol
同级目录
恢复符号表
./restore-symbol symbolDemo -o symbolDemo2 ------------------------- 2021-05-08 18:40:55.494 restore-symbol[89516:35801034] Unknown load >command: 0x00000032 Scan OC method in mach-o-file. Scan OC method finish.
- 第一个参数:将要恢复的
MachO
文件- 第二个参数:恢复符号表后,导出的
MachO
文件使用
MachOView
,查看恢复符号表后的MachO
文件
- 恢复的符号添加到符号表的最后面
- 静态函数的符号无法恢复
恢复符号表后的
MachO
文件,可以使用重签名技术,进行动态调式
总结
剥离符号
strip
:移除指定符号。在Xcode
中默认strip
是在Archive
的时候才会生效,移除对应符号
strip
命令的使用:
strip -x
:除了全局符号都可以移除 (动态库使用)strip -S
:移除调试符号(静态库使用)strip
:除了间接符号表中使用的符号,其他符号都移除(上架App
使用)
Xcode
中Strip Style
的设置:
All Symbols
:除了间接符号表中使用的符号,其他符号都移除(上架App
使用)Non-Global Symbols
:除了全局符号都可以移除 (动态库使用)Debugging Symbols
:移除调试符号(静态库使用)恢复符号
- 符号表中的符号被剥离,但对于
OC
的类和方法,MachO
中还是会保留它们的名称,以供runtime
使用OC
的类和方法,可以通过分析代码段,重新生成一份符号表- 使用
restore-symbol
工具,可恢复符号表