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函数,通过分析x0x1寄存器,查看selfcmd也能找到名称

但此时不知道自定义函数的名称,符号断点只能设置在系统函数上,然后计算自定义函数在调用时的偏移地址

在下一次运行时,使用程序首地址 + 偏移地址计算出函数地址,最后对函数地址设置断点

使用以上方式,过于繁琐,所以应该想办法恢复符号

恢复符号

符号表中的符号被剥离,但对于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使用)

XcodeStrip Style的设置:

  • All Symbols:除了间接符号表中使用的符号,其他符号都移除(上架App使用)
  • Non-Global Symbols:除了全局符号都可以移除 (动态库使用)
  • Debugging Symbols:移除调试符号(静态库使用)

恢复符号

  • 符号表中的符号被剥离,但对于OC的类和方法,MachO中还是会保留它们的名称,以供runtime使用
  • OC的类和方法,可以通过分析代码段,重新生成一份符号表
  • 使用restore-symbol工具,可恢复符号表

你可能感兴趣的:(iOS逆向实战--019:符号的剥离与恢复)