iOS动态库探究

静态分析可执行文件的frameworks

通过以下命令可以查看需要链接的动态库:

➜  ~ otool -L /path/to/YourApp.app/YourApp
/path/to/YourApp.app/YourApp:
    /System/Library/Frameworks/CallKit.framework/CallKit (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/Social.framework/Social (compatibility version 1.0.0, current version 87.0.0)
    /System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1349.55.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
    /usr/lib/libSystem.dylib (compatibility version 1.0.0, current version 1238.50.2)
    /System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 3600.7.47)

注意这两个目录:/System/Library/Frameworks/usr/lib/
通过以下命令查看可执行文件的所有load command

➜  ~ otool -l /path/to/YourApp.app/YourApp
...
(内容太多,省去一部分)
Load command 12
          cmd LC_LOAD_WEAK_DYLIB
      cmdsize 80
         name /System/Library/Frameworks/CallKit.framework/CallKit (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 1.0.0
compatibility version 1.0.0
Load command 13
          cmd LC_LOAD_DYLIB
      cmdsize 80
         name /System/Library/Frameworks/Social.framework/Social (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 87.0.0
compatibility version 1.0.0

注意到,LC_LOAD_WEAK_DYLIB对应的是optional frameworkLC_LOAD_DYLIB对应的是required framework

修改load command

其实MacOS已经提供了修改load command的工具叫install_name_tool

install_name_tool \
-change /System/Library/Frameworks/CallKit.framework/CallKit /System/Library/Frameworks/NotificationCenter.framework/NotificationCenter \
/path/to/YourApp.app/YourApp

# 用otool来验证修改成功与否
➜  ~ otool -L /path/to/YourApp.app/YourApp
/path/to/YourApp.app/YourApp:
    /System/Library/Frameworks/NotificationCenter.framework/NotificationCenter (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/Social.framework/Social (compatibility version 1.0.0, current version 87.0.0)
    /System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1349.55.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
    /usr/lib/libSystem.dylib (compatibility version 1.0.0, current version 1238.50.2)
    /System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 3600.7.47)

运行时加载动态库

添加这样一个命令到你的~/.lldbinit文件中:
command regex ls 's/(.+)/po @import Foundation; [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"%1" error:nil]/'
如果你的lldb已经运行,重新加载init文件:
(lldb) command source ~/.lldbinit
体验下这个命令:

(lldb) image list -d UIKit
[  0] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/UIKit.framework
(lldb) ls /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/
<__NSArrayM 0x610000240270>(
Accelerate.framework,
Accounts.framework,
AddressBook.framework,
AddressBookUI.framework,
AdSupport.framework,
...(此处省略内容)
WatchKit.framework,
WebKit.framework
)

根据列出的framework,尝试加载Speechframework进来到app的进程空间:

(lldb) process load /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/Speech.framework/Speech
Loading "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/Speech.framework/Speech"...ok
Image 0 loaded.

其实很有更酷的。dyld如果找不到framework会搜索一些目录,你不需要指定framework的完整路径:
(lldb) process load MessageUI.framework/MessageUI
完美!

探索frameworks

逆向的一个基础是探索动态库。虽然一个动态库被编译成一个位置不相关position independent的可执行文件,即使编译器将debugging symbols去除了你,仍然可以获取到关于这个动态库的大量的信息。二进制需要位置不相关的代码是因为当dyld完成它的事情后,编译器并不知道代码会存在内存的哪个位置。

清楚的了解一个应用如何和一个framework交互,同样也会帮助你探究app是如何工作的。举个栗子,如果一个stripped了的app(即去除了调试信息)使用了一个UITableView,我们可以在UIKit的特定方法里设置断点来判断哪些代码是与UITableViewDataSource相关的。

添加这样一个命令到你的~/.lldbinit文件中:
command regex dump_stuff "s/(.+)/image lookup -rn '\+\[\w+(\(\w+\))?\ \w+\]$' %1 /"
它接受一个或多个framework作为输入,导出所有的没有参数的oc方法(类方法)
可以尝试一下:

(lldb) dump_stuff Social
71 matches found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/Social.framework/Social:
        Address: Social[0x00000000000019a4] (Social.__TEXT.__text + 0)
        Summary: Social`+[SLInternalComposeServiceHostContext _extensionAuxiliaryVendorProtocol]        Address: Social[0x0000000000001a10] (Social.__TEXT.__text + 108)
        Summary: Social`+[SLInternalComposeServiceHostContext _extensionAuxiliaryHostProtocol]        Address: Social[0x0000000000001b97] (Social.__TEXT.__text + 499)
        Summary: Social`+[SLInternalComposeServiceVendorContext _extensionAuxiliaryVendorProtocol]        Address: Social[0x0000000000001c03] (Social.__TEXT.__text + 607)
        Summary: Social`+[SLInternalComposeServiceVendorContext _extensionAuxiliaryHostProtocol]        Address: Social[0x0000000000005f85] (Social.__TEXT.__text + 17889)
        Summary: Social`+[SLService allServices]        Address: Social[0x000000000000c4b4] (Social.__TEXT.__text + 43792)
        Summary: Social`+[SLWeiboSession _remoteInterface]        Address: Social[0x000000000000c804] (Social.__TEXT.__text + 44640)
        Summary: Social`+[SLWeiboUserRecord supportsSecureCoding]        Address: Social[0x000000000000c9dc] (Social.__TEXT.__text + 45112)
        Summary: Social`+[SLWeiboServerInterface consumerSecret]        Address: Social[0x000000000000cacf] (Social.__TEXT.__text + 45355)
        Summary: Social`+[SLWeiboServerInterface consumerKey]        Address: Social[0x00000000000106db] (Social.__TEXT.__text + 60727)
        Summary: Social`+[SLFacebookAlbumChooserViewController _blankSurrogateAlbumImage]        Address: Social[0x000000000001685c] (Social.__TEXT.__text + 85688)
        Summary: Social`+[SLComposeViewController _serviceTypeToExtensionIdentifierMap]        Address: Social[0x00000000000174c3] (Social.__TEXT.__text + 88863)
        Summary: Social`+[SLComposeViewController _isMultiUserDevice]        Address: Social[0x000000000001f56c] (Social.__TEXT.__text + 121800)
        Summary: Social`+[SLPlace supportsSecureCoding]        Address: Social[0x000000000002de4c] (Social.__TEXT.__text + 181416)
        ...(此处省略)

这里还有一些有用的工具命令:

# 导出继承NSObject的实例的所有ivars
command regex ivars 's/(.+)/expression -lobjc -O -- [%1 _ivarDescription]/'
# 导出出继承NSObject的实例,或NSObject类的所有方法
command regex methods 's/(.+)/expression -lobjc -O -- [%1 _shortMethodDescription]/'
# 递归导出继承NSObject的类的实例的所有方法
command regex lmethods 's/(.+)/expression -lobjc -O -- [%1 _methodDescription]/'

举个例子,你可能需要研究SLFacebookUpload的实例:

(lldb) ivars [SLFacebookUpload new]
<SLFacebookUpload: 0x60000005ddc0>:
in SLFacebookUpload:
    _uploadID (NSString*): nil
    _uploadType (long): 0
    _totalBytes (unsigned long): 0
    _transferredBytes (unsigned long): 0
in NSObject:
    isa (Class): SLFacebookUpload (isa, 0x10612b058)

又或者你可能对这个类实现了哪些方法比较好奇:

(lldb) methods SLFacebookUpload
<SLFacebookUpload: 0x10612b058>:
in SLFacebookUpload:
    Class Methods:
        + (BOOL) supportsSecureCoding; (0x1060b001a)
    Properties:
        @property (retain, nonatomic) NSString* uploadID;  (@synthesize uploadID = _uploadID;)
        @property (nonatomic) long uploadType;  (@synthesize uploadType = _uploadType;)
        @property (nonatomic) unsigned long totalBytes;  (@synthesize totalBytes = _totalBytes;)
        @property (nonatomic) unsigned long transferredBytes;  (@synthesize transferredBytes = _transferredBytes;)
    Instance Methods:
        - (id) uploadID; (0x1060b0022)
        - (void) setUploadID:(id)arg1; (0x1060b0033)
        - (long) uploadType; (0x1060b0047)
        - (void) setUploadType:(long)arg1; (0x1060b0058)
        - (unsigned long) transferredBytes; (0x1060b008b)
        - (void) setTransferredBytes:(unsigned long)arg1; (0x1060b009c)
        - (void) .cxx_destruct; (0x1060b00ad)
        - (void) encodeWithCoder:(id)arg1; (0x1060aff5e)
        - (id) initWithCoder:(id)arg1; (0x1060afe6b)
        - (unsigned long) totalBytes; (0x1060b0069)
        - (void) setTotalBytes:(unsigned long)arg1; (0x1060b007a)
(NSObject ...)

或者获取这个类和所有父类的所有方法:

lldb) lmethods SLFacebookUpload
<SLFacebookUpload: 0x10612b058>:
in SLFacebookUpload:
    Class Methods:
        + (BOOL) supportsSecureCoding; (0x1060b001a)
    Properties:
        @property (retain, nonatomic) NSString* uploadID;  (@synthesize uploadID = _uploadID;)
        @property (nonatomic) long uploadType;  (@synthesize uploadType = _uploadType;)
        @property (nonatomic) unsigned long totalBytes;  (@synthesize totalBytes = _totalBytes;)
        @property (nonatomic) unsigned long transferredBytes;  (@synthesize transferredBytes = _transferredBytes;)
    Instance Methods:
        - (id) uploadID; (0x1060b0022)
        - (void) setUploadID:(id)arg1; (0x1060b0033)
        - (long) uploadType; (0x1060b0047)
        - (void) setUploadType:(long)arg1; (0x1060b0058)
        - (unsigned long) transferredBytes; (0x1060b008b)
        - (void) setTransferredBytes:(unsigned long)arg1; (0x1060b009c)
        - (void) .cxx_destruct; (0x1060b00ad)
        - (void) encodeWithCoder:(id)arg1; (0x1060aff5e)
        - (id) initWithCoder:(id)arg1; (0x1060afe6b)
        - (unsigned long) totalBytes; (0x1060b0069)
        - (void) setTotalBytes:(unsigned long)arg1; (0x1060b007a)
in NSObject:
    Class Methods:
      ...
    Instance Methods:
      ...

在真机上加载framework

真机上这个过程没有什么不一样,唯一区别是System/Library的位置不一样。

如果你在模拟器上运行,公开Frameworks目录在这个地方:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/

但注意到上文中我们用otool -L得到的信息是路径应该是/System/Library/Frameworks。这到底神马情况?

情况是,dyld会在一些目录中查找framework。对应模拟器有一个dyld_sim
所以,这个是在iOS设备上framework所在的正确路径。如果你运行在真机上,frameworks们将会在/System/Library/Frameworks/
这是有人或许会说,"不是有沙盒限制么?"

ios内核对不同目录有着不同的限制。在iOS 10或更早的版本上,/System/Library目录对于你的进程是可读的!这是合理的,因为你的进程在它的进程空间内需要调用一些公开或私有的framework。如果沙盒机制限制了这些目录的读取,那么app就不能够加载他们导致启动失败。

来尝试一下:

(lldb) ls /
<__NSArrayM 0x17024ca20>(
.file,
.mb,
Applications,
Developer,
Library,
System,
bin,
cores,
dev,
etc,
private,
sbin,
tmp,
usr,
var
)

(lldb) ls /System/Library/
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容

  • [转]浅谈LLDB调试器文章来源于:http://www.cocoachina.com/ios/20150126/...
    loveobjc阅读 2,470评论 2 6
  • 仅以方便自己查阅记录前言1.静态库和动态库有什么异同?静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗...
    190CM阅读 4,151评论 0 4
  • two Last login: Tue Aug 4 17:09:57 on ttys000 yushengyang...
    aofeilin阅读 1,022评论 1 9
  • 静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别...
    吃瓜群众呀阅读 11,841评论 3 42
  • 随着Xcode 5的发布,LLDB调试器已经取代了GDB,成为了Xcode工程中默认的调试器。它与LLVM编译器一...
    随风飘荡的小逗逼阅读 1,390评论 0 0