美文网首页IOS技术
逆向系列0x01-在Swift中使用Social框架

逆向系列0x01-在Swift中使用Social框架

作者: blueshadow | 来源:发表于2017-07-19 10:13 被阅读89次

    这里假装iOS上的Social框架是个私有框架,不会使用它的任何头文件或module来帮助我们使用发现的API。我们将会使用dlopen动态加载Social框架,结合LLDB探索其中的API并加以利用。

    加载并探索Social框架

    在开始进攻Social之前,最好使用LLDB进行观察做些准备,来找些重要的方法和类。

    打开Watermark项目,并run在模拟器上。可以看到这个项目暂时没有实现分享按钮的回调。

    Watermak截图
    暂停执行并使用LLDB来动态加载Social框架:
    (lldb) process load /System/Library/Frameworks/Social.framework/Social
    Loading "/Systen/Library/Frameworks/Social.framework/Social"...ok
    Image 0 loaded.
    

    现在我们有了在我们的可执行文件中访问Social框架任何代码的权限,到了探索这个框架到底提供了些啥的时候了。但从哪里开始呢?你可以用image lookup -rn来导出所有这框架里的所有东西,但那样会得到太多内容了。幸运的是,我们有更优雅的方式来寻找比较重要的代码。

    当你完全找不到一个框架的切入点时,最好的方式是搜索并尝试启动框架里的view controller:
    (lldb) image lookup -rn 'ViewController\ init' Social
    嗯,我们会发现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]
    

    这个稍微有点复杂。它匹配那些不是私有的类方法(即不以下划线开头的方法)。
    观察输出,看看有哪些方法看起来比较像UIViewControllers的初始化方法。
    可以看到有3个方法比较吸引我们,这三个方法都以+[SLComposeViewController composeViewController开头。

    这有点尴尬。这三个哪个才是我们应该使用呢(公开)?记住,因为我们把它当成了一个『私有』框架,我们不可以去查看Social框架的头文件说明。

    我们可以尝试执行每一个方法并传nil为参数。我们会发现这3个有一个有点特殊,它会打印一些输出并返回nil而不是某实例:
    +[SLComposeViewController composeViewControllerForServiceType:]
    ok。我们就继续探索这个方法。

    现在面临一个逆向时很普遍的问题:我们到底该传什么参数给这个方法???
    我们先来创建一个断点,在它被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]
    

    很好,只有一个匹配。接下来给它设置一个断点:
    (lldb) rb UIViewController.dealloc
    并再次尝试运行这个方法:(-i0表示lldb中不忽略断点)
    expression -i0 -O -lobjc -- [SLComposeViewController composeViewControllerForServiceType:nil]
    上述命令执行后,app的执行就会停在-[UIViewController dealloc]的实现上。这是查看调用栈就会发现SLComposeViewController有一个重写的dealloc方法。

    没关系。你所需要的只是内存中的一个实例,而且这个类还没有将自己从内存中移除。如果你在不同的栈帧上,在Xcode中通过选择顶部的栈帧或通过LLDB命令frame select 0确保处在栈顶上。

    现在我们就可以获取到这个类的内存实例了:

    (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
    (内容太多,此处有省略)
    

    搜索serviceType
    _serviceType (NSString*): nil
    完美!这在提示我们应该传一个NSString给这个方法:composeViewControllerForServiceType:

    现在又面临另外一个逆向遇到的经典问题:这个参数会有哪些取值呢?
    这个我们可以到Social框架的DATA段里面去探寻。

    导出Social框架的的符号表symbol table
    (lldb) image dump symtab Social -s address
    这个命令会按实现地址排序导出框架的符号表。
    既然框架使用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
    很好。这个值看起来就是我们要寻找的。再确认下它是一个NSString:
    (lldb) po [SLServiceTypeTwitter class] ==> __NSCFConstantString
    完美!将这个值传入上述方法再次调用:

    (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,选择NSObjectcategory,保存文件。
    打开NSObject+P_SLComposeViewController.m替换一下内容:

    #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把Socialframework加载进来。RTLD_NOW则代表着直到加载完毕程序才恢复执行。

    接着打开NSObject+P_SLComposeViewController.h并用一下内容替换:

    #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,然后在右侧的Target Membershi的下面,保证HookingC旁边的Public是勾选的。

    此时构建并启动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
    

    这样就禁用了关于实现缺失的告警,并告诉编译器completionHandler实现在其他地方。

    OC vc Swift

    虽然Swift逐渐成为苹果开发的趋势,但它给私有代码的研究带来了不少麻烦。

    回忆上节我们创建了一个头部文件,在其中声明了任何NSObject都实现了上述方法。这看起来很不妥当,但我们得必须这样做,因为我们在跟Swift打交道。

    如果我们的项目仅仅使用了OC,我们可以使用更简洁的方法:创建一个实现了这些方法的协议protocol

    @protocol P_SLComposeViewControllerProtocol <NSObject>
    + (id)composeViewControllerForServiceType:(NSString *)serviceType;
    - (BOOL)setInitialText:(id)text;
    - (BOOL)addImage:(id)image;
    @property (copy, nonatomic) id completionHandler;
    @end
    

    然后你可以这样使用这个协议:

    id<P_SLComposeViewControllerProtocol> vc =
      [(id<P_SLComposeViewControllerProtocol>)
        NSClassFromString(@"SLComposeViewController")
        composeViewControllerForServiceType:@"com.apple.social.twitter"];
    [vc setInitialText:@"hello world"];
    

    在OC中,我们可以创建并强制转换一个对象的类型,告诉编译器它实现了这个协议,因此有着对应的方法和属性。但在Swift中,存在着对协议实现的运行时检查,Swift运行时发现实际的SLComposeViewController并没有实现这个协议,然后就会crash掉app。因此就有了上面这个看起来很不妥当的解决方法:让所有NSObject都实现上述方法。

    调用私有的UIViewController

    到现在你已经实现了NSObjectcategory,打开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"
    现在再次构建并运行app。点击顶部右侧的分享按钮。然后观察下发生神马了。

    如果你已经在模拟器中添加了一个Twitter账号,就会得到一个没有错误的Twitter分享小窗口;如果你尚未登录Twitter,就会得到类似下面的错误:

    TwitterPopup

    恭喜你成功地完成了一次逆向!

    后记

    伟大的墙让我们免受Twitter等的伤害,何不这里尝试把serviceType换成新浪微博再试一下?

    从上面的分析知道新浪微博的serviceType为com.apple.social.sinaweibo。ok简单改下重新构建运行,点击分享按钮,但是……crash了!

    冷静地分析下crash信息:
    fatal error: unexpectedly found nil while unwrapping an Optional value
    原来是因为模拟器上的不支持新浪微博!

    Settings

    切换到真机上重新构建运行,点击分享按钮,Suuuuuuuuuuuuuuccess!

    新浪微博

    相关文章

      网友评论

        本文标题:逆向系列0x01-在Swift中使用Social框架

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