一、背景

经过之前对 iOS 逆向的初步了解,想了个实现 TikTok 自动播放下一个视频的小需求实践一下。

二、实现过程

1、使用 class-dump 工具获取类信息

class-dump 工具通过解析 MachO 文件生成类信息,包括 OC 方法、属性、成员变量等。

有几个点需要留意:

  1. C 语言函数无法 dump;
  2. OC 方法的参数如果是对象类型,则只会显示 id;
  3. dump 出来的 OC 方法包含了 .h .m 文件里面的。

2、通过视图堆栈找出对应文件

88d9f22ea0434052530532d18a38a1cc

从视图堆栈里面可以很方便找出播放器相关的文件,从 class-dump 出来的文件中找到相关文件,在文件中找出相关方法。

比如我需要实现自动播放下一个视频的话,分为两个步骤:1.监听播放完一个视频的事件;2.播放下一个视频。

找到“播放下一个视频”的方法:

  1. 我从视图堆栈里定位到 AWEFeedTableViewController 类;
  2. 从 class-dump 出来的文件中打开 AWEFeedTableViewController.h
  3. 在文件中搜索 NextVideo,很容易可以找到 - (void)scrollToNextVideo; 方法。

类似的方法找出“播放完一个视频的事件”。

3、快速验证

从上一步找到的类还有对应方法是否真的可以实现“播放下一个视频”呢?我们可以在代码中 Hook 对应方法验证,但是这样验证的方法比较低效。

使用 Cycript 可以动态调试 App,这里简单介绍如何通过 Cycript 快速验证:

  1. 越狱手机上通过 Cydia 安装 Cycript;
  2. 通过 SSH 连接手机;
  3. 找到进程:root# ps -e | grep 'TikTok'
  4. 依附到进程:cycript -p 进程号
  5. 打印视图堆栈,输入命令:UIApp.keyWindow.recursiveDescription()
  6. 找到 AWEFeedTableViewController 内存地址并执行 [#0x1118da200 scrollToNextVideo](内存地址前需要加 # 号)

此时发现 App 真的切换到了下一个视频,证明这个方法就是我们需要找的方法。

a2ec7e2196f6ed28cec07fe2ad68bb34

4、实现

CHDeclareClass(AWEAwemePlayVideoViewController)
CHDeclareClass(AWEFeedTableViewController)

CHOptimizedMethod1(self, void, AWEAwemePlayVideoViewController, playerWillLoopPlaying, id, arg1) {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"DSScrollToNextVideo" object:nil];
    CHSuper1(AWEAwemePlayVideoViewController, playerWillLoopPlaying, arg1);
}

CHOptimizedMethod0(self, void, AWEFeedTableViewController, _addNotifications) {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollToNextVideo) name:@"DSScrollToNextVideo" object:nil];
    CHSuper0(AWEFeedTableViewController, _addNotifications);
}

CHConstructor {
    CHLoadLateClass(AWEAwemePlayVideoViewController);
    CHLoadLateClass(AWEFeedTableViewController);
    
    CHHook1(AWEAwemePlayVideoViewController, playerWillLoopPlaying);
    CHHook0(AWEFeedTableViewController, _addNotifications);
}

5、优化

编译运行后即可以实现自动播放下一个视频了,但是还存在一些问题,比如短视频列表中可能存在广告、直播,这两个场景就不会触发“播放完一个视频的事件”,但是我们也可以使用类似的方法去解决。