iOS Crazies

iOS动态库探究

2017-07-17  本文已影响79人  blueshadow

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

猜你喜欢

热点阅读