這里假裝iOS上的
Social
框架是個私有框架,不會使用它的任何頭文件或module來幫助我們使用發(fā)現(xiàn)的API参滴。我們將會使用dlopen
動態(tài)加載Social
框架,結(jié)合LLDB探索其中的API并加以利用很洋。
加載并探索Social框架
在開始進(jìn)攻Social之前宪哩,最好使用LLDB進(jìn)行觀察做些準(zhǔn)備,來找些重要的方法和類纬乍。
打開Watermark
項目碱茁,并run在模擬器上》卤幔可以看到這個項目暫時沒有實現(xiàn)分享按鈕的回調(diào)纽竣。
暫停執(zhí)行并使用LLDB來動態(tài)加載
Social
框架:
(lldb) process load /System/Library/Frameworks/Social.framework/Social
Loading "/Systen/Library/Frameworks/Social.framework/Social"...ok
Image 0 loaded.
現(xiàn)在我們有了在我們的可執(zhí)行文件中訪問Social
框架任何代碼的權(quán)限,到了探索這個框架到底提供了些啥的時候了茧泪。但從哪里開始呢蜓氨?你可以用image lookup -rn
來導(dǎo)出所有這框架里的所有東西,但那樣會得到太多內(nèi)容了队伟。幸運(yùn)的是穴吹,我們有更優(yōu)雅的方式來尋找比較重要的代碼。
當(dāng)你完全找不到一個框架的切入點(diǎn)時嗜侮,最好的方式是搜索并嘗試啟動框架里的view controller:
(lldb) image lookup -rn 'ViewController\ init' Social
嗯港令,我們會發(fā)現(xiàn)Social
框架有以init開頭的方法的vc還是太多了啥容。該是試一下別的思路了,看能否找到比較特別的一個方法顷霹。
試下這個:
(lldb) image lookup -rn '\+\[.*ViewController\ [a-zA-Z]+' Social
9 matches found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/Social.framework/Social:
Address: Social[0x0000000000016d45] (Social.__TEXT.__text + 86945)
Summary: Social`+[SLComposeViewController extensionIdentifierForActivityType:] Address: Social[0x0000000000017705] (Social.__TEXT.__text + 89441)
Summary: Social`+[SLComposeViewController isAvailableForExtension:] Address: Social[0x00000000000178c0] (Social.__TEXT.__text + 89884)
Summary: Social`+[SLComposeViewController isAvailableForServiceType:] Address: Social[0x0000000000017c3d] (Social.__TEXT.__text + 90777)
Summary: Social`+[SLComposeViewController isAvailableForExtensionIdentifier:] Address: Social[0x00000000000187d6] (Social.__TEXT.__text + 93746)
Summary: Social`+[SLComposeViewController composeViewControllerForExtension:] Address: Social[0x000000000001888a] (Social.__TEXT.__text + 93926)
Summary: Social`+[SLComposeViewController composeViewControllerForServiceType:] Address: Social[0x00000000000188e7] (Social.__TEXT.__text + 94019)
Summary: Social`+[SLComposeViewController composeViewControllerForExtensionIdentifier:] Address: Social[0x000000000002de4c] (Social.__TEXT.__text + 181416)
Summary: Social`+[SLFacebookComposeViewController serviceBundle] Address: Social[0x0000000000044734] (Social.__TEXT.__text + 273808)
Summary: Social`+[SLMicroBlogComposeViewController serviceBundle]
這個稍微有點(diǎn)復(fù)雜咪惠。它匹配那些不是私有的類方法(即不以下劃線開頭的方法)。
觀察輸出淋淀,看看有哪些方法看起來比較像UIViewControllers
的初始化方法遥昧。
可以看到有3個方法比較吸引我們,這三個方法都以+[SLComposeViewController composeViewController
開頭朵纷。
這有點(diǎn)尷尬渠鸽。這三個哪個才是我們應(yīng)該使用呢(公開)?記住柴罐,因為我們把它當(dāng)成了一個『私有』框架,我們不可以去查看Social
框架的頭文件說明憨奸。
我們可以嘗試執(zhí)行每一個方法并傳nil為參數(shù)革屠。我們會發(fā)現(xiàn)這3個有一個有點(diǎn)特殊,它會打印一些輸出并返回nil而不是某實例:
+[SLComposeViewController composeViewControllerForServiceType:]
ok排宰。我們就繼續(xù)探索這個方法似芝。
現(xiàn)在面臨一個逆向時很普遍的問題:我們到底該傳什么參數(shù)給這個方法?板甘?党瓮?
我們先來創(chuàng)建一個斷點(diǎn),在它被deallocated的時候保留住它盐类,以供我們慢慢玩耍和研究寞奸。
首先,我們需要判斷VC是否存在重寫過的dealloc
方法:
(lldb) image lookup -rn UIViewController.dealloc
1 match found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/UIKit.framework/UIKit:
Address: UIKit[0x00000000001c7594] (UIKit.__TEXT.__text + 1855012)
Summary: UIKit`-[UIViewController dealloc]
很好在跳,只有一個匹配枪萄。接下來給它設(shè)置一個斷點(diǎn):
(lldb) rb UIViewController.dealloc
并再次嘗試運(yùn)行這個方法:(-i0
表示lldb中不忽略斷點(diǎn))
expression -i0 -O -lobjc -- [SLComposeViewController composeViewControllerForServiceType:nil]
上述命令執(zhí)行后,app的執(zhí)行就會停在-[UIViewController dealloc]
的實現(xiàn)上猫妙。這是查看調(diào)用棧就會發(fā)現(xiàn)SLComposeViewController
有一個重寫的dealloc
方法瓷翻。
沒關(guān)系。你所需要的只是內(nèi)存中的一個實例割坠,而且這個類還沒有將自己從內(nèi)存中移除齐帚。如果你在不同的棧幀上,在Xcode中通過選擇頂部的棧幀或通過LLDB命令frame select 0
確保處在棧頂上彼哼。
現(xiàn)在我們就可以獲取到這個類的內(nèi)存實例了:
(lldb) po $rdi
<SLComposeViewController: 0x7ffc834089c0>
查看這個實例的所有ivar
:
(lldb) po [0x7ffc834089c0 _ivarDescription]
<SLComposeViewController: 0x7ffc834089c0>:
in SLComposeViewController:
_extension (NSExtension*): nil
_initialText (NSString*): nil
_itemProviders (NSArray*): nil
_extensionItems (NSArray*): nil
_keyboardTopConstraint (NSLayoutConstraint*): nil
_keyboardTrackingView (UIView*): nil
_savedStatusBarStyle (long): 0
_wasPresented (BOOL): NO
(內(nèi)容太多对妄,此處有省略)
搜索serviceType
:
_serviceType (NSString*): nil
完美!這在提示我們應(yīng)該傳一個NSString
給這個方法:composeViewControllerForServiceType:
現(xiàn)在又面臨另外一個逆向遇到的經(jīng)典問題:這個參數(shù)會有哪些取值呢沪羔?
這個我們可以到Social
框架的DATA
段里面去探尋饥伊。
導(dǎo)出Social
框架的的符號表symbol table
:
(lldb) image dump symtab Social -s address
這個命令會按實現(xiàn)地址排序?qū)С隹蚣艿姆柋怼?br>
既然框架使用SL
作為類前綴并且你要查找一個包含有serviceType
信息的NSString
象浑,很自然的我們要搜索SLServiceType
。于是有了下列驚喜:
[ 4297] 4297 X Data 0x000000000009fe20 0x000000011726ee20 0x0000000000000008 0x000f0000 SLServiceTypeTwitter
[ 4291] 4291 X Data 0x000000000009fe28 0x000000011726ee28 0x0000000000000008 0x000f0000 SLServiceTypeFacebook
[ 4294] 4294 X Data 0x000000000009fe30 0x000000011726ee30 0x0000000000000008 0x000f0000 SLServiceTypeSinaWeibo
[ 4295] 4295 X Data 0x000000000009fe38 0x000000011726ee38 0x0000000000000008 0x000f0000 SLServiceTypeTencentWeibo
[ 4296] 4296 X Data 0x000000000009fe40 0x000000011726ee40 0x0000000000000008 0x000f0000 SLServiceTypeTudou
[ 4299] 4299 X Data 0x000000000009fe48 0x000000011726ee48 0x0000000000000008 0x000f0000 SLServiceTypeYouku
[ 4298] 4298 X Data 0x000000000009fe50 0x000000011726ee50 0x0000000000000008 0x000f0000 SLServiceTypeVimeo
[ 4292] 4292 X Data 0x000000000009fe58 0x000000011726ee58 0x0000000000000008 0x000f0000 SLServiceTypeFlickr
[ 4293] 4293 X Data 0x000000000009fe60 0x000000011726ee60 0x0000000000000010 0x000f0000 SLServiceTypeLinkedIn
至此我們成功的找到了枚舉值的值域琅豆!
接下來我們選擇SLServiceTypeTwitter嘗試一下:
(lldb) po SLServiceTypeTwitter
==> com.apple.social.twitter
很好愉豺。這個值看起來就是我們要尋找的。再確認(rèn)下它是一個NSString:
(lldb) po [SLServiceTypeTwitter class]
==> __NSCFConstantString
完美茫因!將這個值傳入上述方法再次調(diào)用:
(lldb) po [SLComposeViewController composeViewControllerForServiceType:@"com.apple.social.twitter"]
<SLComposeViewController: 0x7ffc83511b90>
接下來就可以查看這個類有哪些方法了:
(lldb) po [0x7ffc83511b90 _shortMethodDescription]
<SLComposeViewController: 0x7ffc83511b90>:
in SLComposeViewController:
Class Methods:
+ (id) composeViewControllerForServiceType:(id)arg1; (0x1171e788a)
+ (id) extensionIdentifierForActivityType:(id)arg1; (0x1171e5d45)
+ (BOOL) isAvailableForExtension:(id)arg1; (0x1171e6705)
+ (id) composeViewControllerForExtension:(id)arg1; (0x1171e77d6)
+ (id) _serviceTypeToExtensionIdentifierMap; (0x1171e585c)
+ (BOOL) _isMultiUserDevice; (0x1171e64c3)
+ (id) _serviceTypeForExtensionIdentifier:(id)arg1; (0x1171e5a39)
+ (BOOL) _isAvailableForService:(id)arg1; (0x1171e63a9)
+ (BOOL) _isAvailableForMediaShareExtension:(id)arg1; (0x1171e64cb)
+ (BOOL) _isServiceType:(id)arg1; (0x1171e5c3d)
+ (id) _extensionIdentifierForServiceType:(id)arg1; (0x1171e59b0)
+ (id) _shareExtensionWithIdentifier:(id)arg1; (0x1171e5ee1)
+ (BOOL) isAvailableForServiceType:(id)arg1; (0x1171e68c0)
+ (BOOL) isAvailableForExtensionIdentifier:(id)arg1; (0x1171e6c3d)
+ (id) composeViewControllerForExtensionIdentifier:(id)arg1; (0x1171e78e7)
Properties:
@property (retain) UIViewController* remoteViewController; (@synthesize remoteViewController = _remoteViewController;)
@property (readonly, nonatomic) NSString* serviceType; (@synthesize serviceType = _serviceType;)
@property (copy, nonatomic) ^block completionHandler; (@synthesize completionHandler = _completionHandler;)
Instance Methods:
- (BOOL) setInitialText:(id)arg1; (0x1171e799c)
- (BOOL) addImage:(id)arg1; (0x1171e7fe2)
- (void) .cxx_destruct; (0x1171ea976)
- (void) dealloc; (0x1171e78f9)
- (^block) completionHandler; (0x1171ea92b)
- (BOOL) shouldAutorotateToInterfaceOrientation:(long)arg1; (0x1171ea837)
- (void) viewWillAppear:(BOOL)arg1; (0x1171ea274)
- (void) viewDidAppear:(BOOL)arg1; (0x1171ea77b)
- (void) viewWillDisappear:(BOOL)arg1; (0x1171ea743)
- (void) viewDidDisappear:(BOOL)arg1; (0x1171ea749)
- (id) remoteViewController; (0x1171ea94f)
- (void) viewDidUnload; (0x1171ea808)
- (BOOL) _useCustomDimmingView; (0x1171ea26c)
- (void) setRemoteViewController:(id)arg1; (0x1171ea965)
- (BOOL) addItemProvider:(id)arg1; (0x1171e9614)
- (BOOL) addExtensionItem:(id)arg1; (0x1171e96c1)
- (void) setCompletionHandler:(^block)arg1; (0x1171ea93e)
- (id) initWithServiceType:(id)arg1; (0x1171e761e)
- (void) completeWithResult:(long)arg1; (0x1171e9af3)
- (id) initWithExtension:(id)arg1 requestedServiceType:(id)arg2; (0x1171e6c4f)
- (BOOL) canAddContent; (0x1171e7988)
- (id) _urlForUntypedAsset:(id)arg1; (0x1171e7aef)
- (BOOL) _addImageAsset:(id)arg1 preview:(id)arg2; (0x1171e7c30)
- (BOOL) supportsImageAsset:(id)arg1; (0x1171e7a01)
- (BOOL) _addImageJPEGData:(id)arg1 preview:(id)arg2; (0x1171e809a)
- (BOOL) supportsVideoAsset:(id)arg1; (0x1171e7a78)
- (BOOL) addURL:(id)arg1 withPreviewImage:(id)arg2; (0x1171e8cf8)
- (BOOL) _addURL:(id)arg1 type:(long)arg2 preview:(id)arg3; (0x1171e8d8c)
- (BOOL) _addVideoData:(id)arg1 preview:(id)arg2; (0x1171e8998)
- (BOOL) _addVideoAsset:(id)arg1 preview:(id)arg2; (0x1171e85fb)
- (void) _handleRemoteViewFailure; (0x1171ea01a)
- (void) didLoadSheetViewController; (0x1171e9d2e)
- (void) remoteController:(id)arg1 didLoadWithError:(id)arg2; (0x1171ea084)
- (void) remoteViewController:(id)arg1 didTerminateWithError:(id)arg2; (0x1171ea1f5)
- (id) initWithExtensionIdentifier:(id)arg1; (0x1171e77c4)
- (BOOL) addImageAsset:(id)arg1; (0x1171e7bd1)
- (BOOL) removeAllImages; (0x1171e839b)
- (BOOL) addURL:(id)arg1; (0x1171e8c99)
- (BOOL) removeAllURLs; (0x1171e91a8)
- (BOOL) addAttachment:(id)arg1; (0x1171e97be)
- (^block) addDownSampledImageDataByProxyWithPreviewImage:(id)arg1; (0x1171e9a84)
- (void) setLongitude:(double)arg1 latitude:(double)arg2 name:(id)arg3; (0x1171e9aed)
- (void) userDidCancel; (0x1171e9d03)
- (void) userDidPost; (0x1171e9d17)
- (void) remoteViewControllerLoadDidTimeout; (0x1171ea008)
- (BOOL) canSendTweet; (0x1171ea8e5)
- (id) serviceType; (0x1171ea91a)
(UIViewController ...)
我們著重看這幾個方法:
- (BOOL) setInitialText:(id)arg1; (0x11a80b7aa)
- (BOOL) addImage:(id)arg1; (0x11a80bdf4)
@property (copy, nonatomic) ^block completionHandler; (@synthesize
completionHandler = _completionHandler;)
小試牛刀
分析了辣么多蚪拦,該是動手操作的時候了。Xcode的Project Navigator
中選擇HookingC
目錄冻押,File\New\File\Objective-c File
驰贷,新建文件命名為P_SLComposeViewController
,選擇NSObject
為category
洛巢,保存文件括袒。
打開NSObject+P_SLComposeViewController.m
替換一下內(nèi)容:
#import "NSObject+P_SLComposeViewController.h"
#import <dlfcn.h>
@implementation NSObject (P_SLComposeViewController)
+ (void)load {
dlopen("Social.framework/Social", RTLD_NOW);
}
@end
這里使用了OC的load
類方法(Swift沒有這個)來說明,一旦這個類加載到runtime中稿茉,就用dlopen把Social
framework加載進(jìn)來锹锰。RTLD_NOW
則代表著直到加載完畢程序才恢復(fù)執(zhí)行。
接著打開NSObject+P_SLComposeViewController.h
并用一下內(nèi)容替換:
#import <Foundation/Foundation.h>
@interface NSObject (P_SLComposeViewController)
+ (id)composeViewControllerForServiceType:(NSString *)serviceType;
- (BOOL)setInitialText:(id)text;
- (BOOL)addImage:(id)image;
@property (copy, nonatomic) id completionHandler;
@end
最后漓库,在project navigator
中選擇NSObject+P_SLComposeViewController.h
恃慧,然后在右側(cè)的Target Membershi
的下面,保證HookingC
旁邊的Public
是勾選的渺蒿。
此時構(gòu)建并啟動app痢士,會得到一些warning:在頭部文件定義的方法未找到。
打開NSObject+P_SLComposeViewController.m
然后在import下面添加一些編譯選項茂装,在@implementation下面一行添加對completionHandler
的說明:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincomplete-implementation"
@implementation NSObject (P_SLComposeViewController)
@dynamic completionHandler;
...
@end
#pragma clang diagnostic pop
這樣就禁用了關(guān)于實現(xiàn)缺失的告警怠蹂,并告訴編譯器completionHandler
實現(xiàn)在其他地方。
OC vc Swift
雖然Swift逐漸成為蘋果開發(fā)的趨勢训唱,但它給私有代碼的研究帶來了不少麻煩褥蚯。
回憶上節(jié)我們創(chuàng)建了一個頭部文件,在其中聲明了任何NSObject
都實現(xiàn)了上述方法况增。這看起來很不妥當(dāng)赞庶,但我們得必須這樣做,因為我們在跟Swift打交道澳骤。
如果我們的項目僅僅使用了OC歧强,我們可以使用更簡潔的方法:創(chuàng)建一個實現(xiàn)了這些方法的協(xié)議protocol
:
@protocol P_SLComposeViewControllerProtocol <NSObject>
+ (id)composeViewControllerForServiceType:(NSString *)serviceType;
- (BOOL)setInitialText:(id)text;
- (BOOL)addImage:(id)image;
@property (copy, nonatomic) id completionHandler;
@end
然后你可以這樣使用這個協(xié)議:
id<P_SLComposeViewControllerProtocol> vc =
[(id<P_SLComposeViewControllerProtocol>)
NSClassFromString(@"SLComposeViewController")
composeViewControllerForServiceType:@"com.apple.social.twitter"];
[vc setInitialText:@"hello world"];
在OC中,我們可以創(chuàng)建并強(qiáng)制轉(zhuǎn)換一個對象的類型为肮,告訴編譯器它實現(xiàn)了這個協(xié)議摊册,因此有著對應(yīng)的方法和屬性。但在Swift中颊艳,存在著對協(xié)議實現(xiàn)的運(yùn)行時檢查茅特,Swift運(yùn)行時發(fā)現(xiàn)實際的SLComposeViewController
并沒有實現(xiàn)這個協(xié)議忘分,然后就會crash掉app。因此就有了上面這個看起來很不妥當(dāng)?shù)慕鉀Q方法:讓所有NSObject
都實現(xiàn)上述方法白修。
調(diào)用私有的UIViewController
到現(xiàn)在你已經(jīng)實現(xiàn)了NSObject
category妒峦,打開ViewController.swift
并添加下面代碼到sharingButtonTapped(_:)
中:
guard let vcClass =
NSClassFromString("SLComposeViewController") else { return }
let vc = vcClass.composeViewController(forServiceType:
"com.apple.social.twitter") as! UIViewController
vc.setInitialText("Yay! Doggie Love!")
if let originalImage = imageView.image {
vc.addImage(originalImage)
}
present(vc, animated: true)
接著打開HookingC.h
并文件末尾添加以下:#import "NSObject+P_SLComposeViewController.h"
。
現(xiàn)在再次構(gòu)建并運(yùn)行app兵睛。點(diǎn)擊頂部右側(cè)的分享按鈕肯骇。然后觀察下發(fā)生神馬了。
如果你已經(jīng)在模擬器中添加了一個Twitter賬號祖很,就會得到一個沒有錯誤的Twitter分享小窗口笛丙;如果你尚未登錄Twitter,就會得到類似下面的錯誤:
恭喜你成功地完成了一次逆向假颇!
后記
偉大的墻讓我們免受Twitter等的傷害胚鸯,何不這里嘗試把serviceType
換成新浪微博再試一下?
從上面的分析知道新浪微博的serviceType為com.apple.social.sinaweibo
笨鸡。ok簡單改下重新構(gòu)建運(yùn)行蠢琳,點(diǎn)擊分享按鈕,但是……crash了镜豹!
冷靜地分析下crash信息:
fatal error: unexpectedly found nil while unwrapping an Optional value
原來是因為模擬器上的不支持新浪微博!
切換到真機(jī)上重新構(gòu)建運(yùn)行蓝牲,點(diǎn)擊分享按鈕趟脂,Suuuuuuuuuuuuuuccess!