美文网首页iOS Crazies
iOS动态库探究

iOS动态库探究

作者: blueshadow | 来源:发表于2017-07-17 19:22 被阅读79次

    静态分析可执行文件的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/
    

    相关文章

      网友评论

        本文标题:iOS动态库探究

        本文链接:https://www.haomeiwen.com/subject/ylofkxtx.html