内容简介:作者 |
作者 | cinvoke,目前在某社交App担任iOS组长,做过社交、直播、智能家居等,喜欢逆向、音视频,有一只叫狗蛋的胖蓝猫~
起因
起因是女朋友喜欢追综艺,现在的综艺节目中间有时候会插播很长的广告,正常速度看完的话太浪费时间了,直接拖进度条的话又容易拖过内容,只能点击右上角更多,切换到2x倍速播放,等播放完后,再点击更多,切换到1x倍速,操作比较繁琐,体验太差。而芒果tv有个很好的功能就是,长按直接切换到2x倍速,放开恢复1x倍速,大大提升了看剧体验,所以本文的主题就是,将这功能通过逆向注入,添加到腾讯视频App里。
其实逆向开发在iOS领域内已经有很完善的 工具 链了,本文主要是提供下分析思路。
脱壳
逆向App的前提是需要已脱壳的ipa包,我们从app store下的ipa包都是经过苹果加密的,可以从一些第三方市场比如PP助手之类的下载越狱版ipa,这种一般都是脱壳的,还有就是可以通过越狱手机运行App然后把内存dump出来重新打成ipa包。
这里我们用第二种方式,dump的方式也有好几种,比如越狱应用CrackerXI,可以直接把App dump到手机目录里,不过需要手动传输ipa包到电脑上。
早期的dump工具基本是用 dumpdecrypted [1] ,不过作者已经不维护了,这里推荐用Monkey大神的 frida-ios-dump [2] ,在越狱手机上装对应的frida插件,然后运行dump.py就可以一步导出脱壳的ipa包到电脑上:
安装流程上面地址已经注明,这里简单给大家列下:
-
在越狱手机上用Cydia商店搜索安装frida
-
clone下上面的项目,使用命令安装相关 python 依赖库
sudo pip install -r requirements.txt --upgrade
-
安装usbmuxd,用来把SSH转发到USB设备的手机上,安装后在终端输入iproxy 2222 22
-
终端运行python ./dump.py 对应App的bundleID 开始dump
ShellCopy
腾讯视频的BundleID对应com.tencent.live4iphone,现在我们开始dump:
python /dump.py com.tencent.live4iphone
完成后就会在当前目录下生成一个已脱壳的ipa包了。
分析
下面分步骤分析怎么定位到快进的相关函数:
1.Debug View
新建MonkeyApp工程
MonkeyApp是一个可以直接运行脱壳ipa的项目,非常方便用于逆向调试,详情可以查看 Github [3]
除了用MonkeyApp外,还可以通过注入Reveal.dylib到App内然后用Reveal来查看,这里就暂时不扩展了。
项目名随便起个,我这里用TVHook,然后把我们上面脱壳的ipa包放入TargeApp目录中。
修改下BundelID和开发者账号后我们开始运行,第一次运行一般会失败,这时候我们可以把Targe.plist里的信息拷贝到Info.plist里,重新改下BundelID后我们继续运行
修改完毕后可以看到运行成功了,这里需要注意的是,上面这ipa包是通过哪个架构脱壳的,就只能在哪个架构的机子上运行,比如我的是在arm64上脱壳,就不能再模拟器上(x86_64)和armv7上运行。
我们直接去到视频播放页面,然后开启Xcode的Debug View功能:
接下来我们就可以直接看到视图结构了:
这里腾讯视频的开发小哥已经帮我们注释好这些View的作用了,省了不少力气~
2.定位手势View
QNBPlayerGestureView就是我们要的手势层,我们需要在这一层上面添加Long手势,但在这之前,我们可以先查看它有哪些方法,说不定里面已经有了Long手势了:
一开始我是用class-dump来获取QNBPlayerGestureView的.h文件来查看,但是class-dump后居然没发现这个文件,估计是打包在某个Framework里了,但是一个个Framework查又比较麻烦,于是我直接使用了LLDB命令:
image lookup -rn \[QNBPlayerGestureView\
这条命令就是查找QNBPlayerGestureView的所有方法,非常方便
这里我们可以看到QNBPlayerGestureView确实是打包在QNBAutomatic.framework里,而且我们还看到了-[QNBPlayerGestureView didLongPress:],这样的话我们就不需要自己添加long手势了,后面直接Method swizzle该方法就行。
3.定位2x速入口
手势的入口我们已经定位到了,接下来就是定位2x快进的方法了,还是和开始一样,在有2x播放的界面我们使用Debug View,来定位到相关View:
可以看到相关2x按钮所在是视图是QNBPlayerRateSelectPanel,继续用image lookup查看相关方法:
image lookup -rn \[QNBPlayerRateSelectPanel\
这里我们看到了关键入口[QNBPlayerRateSelectPanel btnPress:]。
结合[QNBPlayerRateSelectPanel btnArray]和[QNBPlayerRateSelectPanel rateArray]方法我们可以猜测大概逻辑就是,点击触发btnPress然后通过btnArray和rateArray的对应关系获取到相应的rate,也就是我们只要知道rateArray的内容就可以知道对应哪个Button了。
下面我们将打印[QNBPlayerRateSelectPanel rateArray]的值,先打印出QNBPlayerRateSelectPanel的地址:
这里获取到的是0x14541b400,接下来打印rateArray
可以看到2倍数就再最后一个,想就是相应的Button就是[btnArray lastObject],也就是我们只要调用btnPress:[btnArray lastObject],就能触发2x倍数的逻辑了。
到此基本也就分析完毕,我们只需要在-[QNBPlayerGestureView didLongPress:]里触发QNBPlayerRateSelectPanel的btnPress:[btnArray lastObject]就能实现长按切换2x速播放了。下面我们开始实现功能。
开发功能
MonkeyApp内部已经实现了注入功能,省去了我们自己注入的步骤,所以我们可以直接在它注入的dylib里编写我们的代码:
上面的NSObject+CPSwizzle是我自己简单封装的Method Swizzle方法,因为后续代码编写都需要用,大概代码如下:
+ (void)mothodSwizzleClass:(NSString *)className
old:(NSString *)oldMothod
new:(NSString *)newMothod
isClass:(BOOL)isClass {
if (isClass) {
Method oriSessionMethod = class_getClassMethod(NSClassFromString(className), NSSelectorFromString(oldMothod));
Method mySessionMethod = class_getClassMethod(NSClassFromString(className), NSSelectorFromString(newMothod));
method_exchangeImplementations(oriSessionMethod, mySessionMethod);
} else {
Method oldMothods = class_getInstanceMethod(NSClassFromString(className), NSSelectorFromString(oldMothod));
Method newMothods = class_getInstanceMethod(NSClassFromString(className), NSSelectorFromString(newMothod));
BOOL didAddMethod =
class_addMethod(NSClassFromString(className),
NSSelectorFromString(oldMothod),
method_getImplementation(newMothods),
method_getTypeEncoding(newMothods));
if (didAddMethod) {
class_replaceMethod(NSClassFromString(className),
NSSelectorFromString(newMothod),
method_getImplementation(oldMothods),
method_getTypeEncoding(oldMothods));
} else {
method_exchangeImplementations(oldMothods, newMothods);
}
}
};
当然我们也可以用MonkeyApp自己的Hook功能,不过还是Method Swizzle用的顺手点,代码入口在CHConstructor里,我们先实现Loog手势的Hook:
[NSObject mothodSwizzleClass:@"QNBPlayerGestureView" old:@"didLongPress:" new:@"cp_didLongPress:" isClass:NO];
然后添加对应的方法:
@implementation NSObject (Hook)
- (void)cp_didLongPress:(UILongPressGestureRecognizer *)avg1 {
[self cp_didLongPress:avg1];
//这里开始编写我们的代码
}
接下来我们需要获取到QNBPlayerRateSelectPanel的对象地址做方法调用,这里有两种方法,
第一种是通过Hook QNBPlayerRateSelectPanel的init方法,然后通过一个全局变量保存init后的对象,然后直接使用这个全局变量就行,但这有个弊端,就是QNBPlayerRateSelectPanel的retainCount会+1,导致不能正常释放,想正常释放还得Hook他父视图的dealloc方法,然后手动释放,计较麻烦。
第二种就是通过我们之前获取到的View结构,遍历查找到对应QNBPlayerRateSelectPanelclass
获取到QNBPlayerRateSelectPanel后我们就可以编写具体实现代码了,具体代码如下:
- (void)cp_didLongPress:(UILongPressGestureRecognizer *)avg1 {
[self cp_didLongPress:avg1];
//遍历获取到QNBPlayerRateSelectPanel
UIView *superView = [(UIView *)self superview];
id panle = [superView findQNBPlayerRateSelectPanel];
//获取到[QNBPlayerRateSelectPanel btnArray]
NSArray *btnArray = [panle valueForKey:@"btnArray"];
for (UIButton *btn in btnArray) {
btn.selected = NO;
}
if (avg1.state == UIGestureRecognizerStateBegan) {
//手势开始,取到2x的Button
UIButton *button = btnArray.lastObject;
button.selected = YES;
[panle performSelector:@selector(btnPress:) withObject:button];
} else if (avg1.state == UIGestureRecognizerStateEnded) {
//手势结束,取1x的Button
UIButton *button = btnArray[2];//通过之前的分析可以知道1x是数组的第2个下标
button.selected = YES;
[panle performSelector:@selector(btnPress:) withObject:button];
}
}
大概逻辑已经做了注释,到此已经实现了想要的功能,直接运行看效果!由于效果动图有点大,无法上传,所以可以点击“阅读原文”,查看实际效果!
结语
虽然到此已经实现了想要的功能,但是每次都会有个Toast提示,很影响观看,于是想通过函数调用逻辑查找到最终的实现函数,最终发现函数调用链如下:
直接调用[QNBPlayerInfo setRate:@2]就能实现效果,具体的查找过程等后续有时间在给大家补上。
最后感谢AloneMonkey大神开发了相关的逆向工具,简化了逆向的复杂度,也推荐大家去了解他写的逆向书籍~
参考
[1]https://github.com/stefanesser/dumpdecrypted
[2]https://github.com/AloneMonkey/frida-ios-dump
[3]https://github.com/AloneMonkey/MonkeyDev
看到这里费了很多脑细胞吧吃点水果补充下能量,推荐下我种的油桃嘎嘣脆且甜!如需要微信联系我如下:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- flutter 学习使用自定义view并添加手势动作
- 利用opencv借取相机摄像头为手机和电脑添加手势或者体感操控
- TensorFlow 实现手势识别
- Flutter 手势密码控件
- Flutter学习指南:交互、手势和动画
- iOS —— 触摸事件传递及响应与手势
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Large-Scale Inference
Bradley Efron / Cambridge University Press / 2010-8-5 / GBP 48.00
We live in a new age for statistical inference, where modern scientific technology such as microarrays and fMRI machines routinely produce thousands and sometimes millions of parallel data sets, each ......一起来看看 《Large-Scale Inference》 这本书的介绍吧!
JS 压缩/解压工具
在线压缩/解压 JS 代码
RGB CMYK 转换工具
RGB CMYK 互转工具