逆向微博跳广告和内部打开链接

前言

  • 工具:
    • Reveal查看App视图层级
    • class-dump导出App所有头文件
    • theos插件开发工具
    • IDA反汇编,可以友好地生成伪代码
    • restore-symbol恢复App符号信息
  • 注意:此文仅限于技术交流,如果损害了App方利益,请发邮件zhulongfei28@gmail.com,谢谢。
  • 目的:去除开屏广告和允许内部打开网页链接

新建插件

  • 新建内容如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ nic.pl
    NIC 2.0 - New Instance Creator
    ------------------------------
    ...
    [10.] iphone/tweak
    [11.] iphone/xpc_service
    Choose a Template (required): 10
    ...
    [iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.sina.weibo
    ...
    Done.
  • 更改Makefile,增加内容如下
    1
    2
    ARCHS = arm64 arm64e
    TARGET = iPhone:latest:10.0
  • 目前支持的架构有armv7 armv7s arm64 arm64e等
    • 添加ARCHS = armv7 armv7s arm64 表示支持 armv7 armv7s arm64 三种处理器架构
    • 对于最新的A12处理器(iPhoneX以后)的设备,需要添加arm64e,否则生成的dylib文件不能正常加载
  • 指定目标规范: TARGET = iPhone:BaseSDK:DeploymentTarget
    • BaseSDK代表编译用的SDK版本
    • Deployment Target是最低兼容的系统版本,

开屏广告

  • 定位到微博开屏广告界面,使用Reveal查看界面层级,发现WBAdFlashAdView是广告视图。
  • 使用class-dump导出所有头文件。打开WBAdFlashAdView.h文件,很容易发现closeAdshowAd。我们首先这样做:在执行弹出广告的时候去关闭广告,代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @interface WBAdFlashAdView
    - (void)closeAd;
    @end

    %hook WBAdFlashAdView
    - (void)showAd {
    [self closeAd];
    }
    %end
  • 执行安装,安装成功后,重新打开微博发现还是有开屏广告,why?第一反应是在执行closeAd方法时肯定是有其它判断的,判断通过了才会真正地执行跳过广告的代码。
  • 把微博Mach-O文件拖进去IDA,使用IDA查看closeAd伪代码,发现如下伪代码
    1
    2
    3
    if ( (unsigned int)-[WBAdFlashAdView isShowing](self, "isShowing") ) {

    }
  • 查看以上说明,要想真正地执行closeAd,[self isShowing]必须成立,所以最终代码为
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @interface WBAdFlashAdView
    - (void)closeAd;
    - (void)setIsShowing:(BOOL)isShowing;
    @end

    %hook WBAdFlashAdView
    - (void)showAd {
    [self setIsShowing: YES];
    [self closeAd];
    }
    %end
  • 安装后发现确实跳过了开屏广告。

内部打开网页链接

寻找内部浏览器

  • 如上图所示,当点击网页链接时,并不会执行打开,而是显示链接地址,提示用户手动复制到到浏览器中进行打开。这样的操作太麻烦了,所以有了下面的文章
  • 点击网页链接会进入一个控制器,使用Reveal查看界面层级,发现WBTopNavigationWebViewController就是展示网页的控制器。
  • 那么接下来要干什么呢,其实是一脸懵逼的。那我们不妨先这么干:恢复符号,然后断点WBTopNavigationWebViewController所有方法,判断初始化时会调用哪些方法。

恢复符号

  • macOS步骤如下:
    • restore-symbol Weibo -o WeiboWithSymbol
    • ldid -e WeiboWithSymbol > WeiboWithSymbol.entitlements
    • 使用VSCode软件打开WeiboWithSymbol.entitlements,添加如下,然后保存。
      1
      2
      <key>platform-application</key>
      <true/>
    • ldid -SWeiboWithSymbol.entitlements WeiboWithSymbol
    • 把WeiboWithSymbol上传到iPhone里面的Weibo.app目录
  • iPhone上步骤如下:
    • cd /var/containers/Bundle/Application/62CA4E3D-37BE-4CF9-8564-3863BD3928CF/Weibo.app/
    • mv Weibo WeiboNoSymbol // 备份原有的
    • mv WeiboWithSymbol Weibo // 改成新的
  • 重新启动微博

寻找原始URL

  • 执行如下命令,断点WBTopNavigationWebViewController 所有方法,找到了73个断点位置
    1
    2
    (lldb) br set -r '\[WBTopNavigationWebViewController .*\]'
    Breakpoint 1: 73 locations.
  • 点击网页链接首先命中了-[WBTopNavigationWebViewController generateWithOpenUrlLink:inAppParas:]方法,下文把这个方法叫做生成;然后命中了-[WBTopNavigationWebViewController initWithURL:parameters:]方法,下文把这个方法叫做初始化
  • 我们试想一下:为什么有的链接可以直接打开,而有的链接只能在外部浏览器打开?答案是在发送微博时,微博对链接进行过包装,在点击链接时,会传入包装过的URL,然后通过判断包装好的参数来区分是内部还是外部打开。那么我们要做的就是拿到最原始的URL替换掉微博封装的URL,这样就可以内部打开链接。
  • 生成初始化都传入了URL,我们在这两个方法里面都可以拿到原始URL,我们这里在初始化里面拿最原始的URL,Why?当LLDB里面命中初始化时,执行bt查看一下调用堆栈,发现生成里面调用了初始化初始化层级更深,更核心。
  • 命中初始化断点时,打印URL参数。确实如我们上面所说,原始的URL被封装了,我们需要提取出来。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    (lldb) c
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000103ae2bd4 Weibo` -[WBTopNavigationWebViewController initWithURL:parameters:]
    Weibo`-[WBTopNavigationWebViewController initWithURL:parameters:]:
    (lldb) po $x2
    http://weibo.cn/sinaurl?toasturl=https%3A%2F%2Fiosre.com%2Ft%2Fswift-runtime-frida-alamofire%2F17602

    (lldb) po [$x2 class]
    NSURL
  • 从打印值发现初始化的第一个参数是NSURL类型并且进行了转义操作,所以我们进行反转义,得到能被肉眼识别的字符串
    1
    2
    3
    (lldb) e NSString *$query =  (NSString *)[((NSURL *)$x2) query]
    (lldb) po [$query stringByRemovingPercentEncoding]
    toasturl=https://iosre.com/t/swift-runtime-frida-alamofire/17602
  • 在这里就很容易提取原始URL,然后在进行转义,最后封装成NSURL类型,代码如下
    1
    2
    NSString *lastStr = [[query componentsSeparatedByString:@"="] lastObject];
    NSURL *lastURL = [NSURL URLWithString:lastStr];
  • Tweak.x里面填写如下代码,进行安装,安装成功后,发现和原来效果一样,还是不能内部打开链接,怎么回事呢?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    %hook WBTopNavigationWebViewController

    - (id)initWithURL:(NSURL *)url parameters:(NSDictionary *)arg2 {
    NSString *query = [[url query] stringByRemovingPercentEncoding];
    if (![query containsString:@"toasturl="]) {
    return %orig;
    }
    NSString *lastStr = [[query componentsSeparatedByString:@"="] lastObject];
    NSURL *lastURL = [NSURL URLWithString:lastStr];
    return %orig(lastURL,arg2);
    }

    %end

禁用包装的URL

  • 初始化传入的URL为包装过的URL,而我们传入的URL为原始的URL,可能需要禁用掉包装过URL的其它逻辑,让它可以加载原始URL。往上面几步找线索,包装过URL里面有个sinaurl,这个代表包装URL的特征。我们去WBTopNavigationWebViewController查找关键字sinaurl,发现- (_Bool)disablesSinaURL;方法,禁用这个方法也许可以加载原始URL。最终代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    %hook WBTopNavigationWebViewController

    - (BOOL)disablesSinaURL {
    return YES;
    }

    - (id)initWithURL:(NSURL *)url parameters:(NSDictionary *)arg2 {
    NSString *query = [[url query] stringByRemovingPercentEncoding];
    if (![query containsString:@"toasturl="]) return %orig;
    NSString *lastStr = [[query componentsSeparatedByString:@"="] lastObject];
    NSURL *lastURL = [NSURL URLWithString:lastStr];
    return %orig(lastURL,arg2);
    }

    %end
  • 安装后测试成功,网页链接都可以在微博内部打开