静态分析可执行文件的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 framework
,LC_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,尝试加载Speech
framework进来到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/
网友评论