一、class-dump
提前准备好砸壳🔗 的应用(人人视频5.5.1)
使用class-dump导出头文件,便于我们定位目标。
class-dump -H MachO文件名 -o 输出的文件夹/
二、确定逆向目标
这里我想要实现非VIP用户可以查看VIP视频。
一般情况下我们会通过类似isVIP
这样的字眼去进行相关的判断。
Cycript链接手机进行分析
三、登录态
通过在Headers中搜索isLogin
看到一个很可能是用来判断登录态的东西:
3.1 hook登录态
Copy @interface UserInfoConfig
+ (bool)isLogined;
- (bool)isLogined;
@end
%hook UserInfoConfig
+ (bool)isLogined {
return YES;
}
- (bool)isLogined {
return YES;
}
%end
3.2 检验登录态
原版
Hook版
通过!这里的立即登录
已经变成立即开通
了
这里因为是直接改写了登录状态的返回值,所以这里有了大量的错误提示:
2021-05-23 14:52:09.592113+0800 PUClient[6981:2390642] You should call login api before calling any im related api
3.3 找到VIP态
从UI层开始分析
a.找到容器: RRVipUserInfoView
看看它内部有没有做数据逻辑
看起来resetStatus
&reloadUIData
是有做些请求的,我们先记录一下。继续分析。
b.父视图:RRVipBannerTopCell
这里看到一个@property(retain, nonatomic) RRTopBannerModel *bannerModel;
但是没有和用户相关的模型属性,推测VIP
的判断是在RRVipUserInfoView
内部!
c.VIP相关类
通过Headers查找VIP关键字,逐个查看,发现RRIMManager
疑似。而且还是个单例。我们Hook看下有什么效果。
下面又找到几个,可能的文件
f.RrmjUser
其中有这样一个属性:@property(retain, nonatomic) RRMedalModel *vipMedal;
g.RRMedalModel
Copy @interface RRMedalModel : NSObject <NSCoding>
{
_Bool _isExpired;
long long _medalId;
NSString *_imgUrl;
NSString *_name;
NSString *_endTime;
}
+ (id)modelCustomPropertyMapper;
+ (id)replacedKeyFromPropertyName;
- (void).cxx_destruct;
@property(nonatomic) _Bool isExpired; // @synthesize isExpired=_isExpired;
@property(copy, nonatomic) NSString *endTime; // @synthesize endTime=_endTime;
@property(copy, nonatomic) NSString *name; // @synthesize name=_name;
@property(copy, nonatomic) NSString *imgUrl; // @synthesize imgUrl=_imgUrl;
@property(nonatomic) long long medalId; // @synthesize medalId=_medalId;
- (id)endTimeTextString;
- (id)endTimeString;
- (_Bool)isPermanent;
- (id)initWithCoder:(id)arg1;
- (void)encodeWithCoder:(id)arg1;
@end
查看Header后发现疑似VIP相关字段
在第二步的UserInfoConfig
中发现有RrmjUser
属性存在,这里准备在运行时set一个我们自己创建的对象,重写会员是否过期的方法。
Logos代码:
Copy #pragma mark - RRMedalModel
@interface RRMedalModel: NSObject
- (bool)isPermanent;
- (bool)isExpired;
@end
%hook RRMedalModel
- (bool)isPermanent {
return YES;
}
- (bool)isExpired {
return NO;
}
%end
#pragma mark - RrmjUser
@interface RrmjUser: NSObject
@end
%hook RrmjUser
- (id)vipMedal {
id vip = [[%c(RRMedalModel) alloc] init];
return vip;
}
%end
#pragma mark - UserInfoConfig-这里Hook了登录态
@interface UserInfoConfig
+ (bool)isLogined;
- (bool)isLogined;
- (id)userInfo;
@end
%hook UserInfoConfig
+ (bool)isLogined {
return YES;
}
- (bool)isLogined {
return YES;
}
- (id)userInfo {
id tempUser = [[%c(RrmjUser) alloc] init];
return tempUser;
}
%end
结果
之前开通会员
的入口都变成了续费了。哈哈哈哈
但是观看会员视频还是只能试看5分钟
下面的目标就是解除5分钟的限制了!
四:无限制调广告
试着直接把广告加载的方法Return了,但是直接不能加载视频了。于是改成不用等就能点跳过。
Copy #pragma mark - RRPlayerControlAdPlay
@interface RRPlayerControlAdPlay: NSObject
- (bool)canJump;
@end
%hook RRPlayerControlAdPlay
- (bool)canJump {
return YES;
}
%end
五:改变视频权限
5.1结合正常显示的剧集数据对比非会员和会员的视频区别
5.2 调试找出关键字段的区别
5.3 Hook
Copy #pragma mark - MovieEpisode
@interface MovieEpisode: NSObject
/// 2 VIP nil free
- (long long)feeModeType;
- (NSString *)feeMode;
@end
%hook MovieEpisode
- (NSString *)feeMode {
return @"free";
}
- (long long)feeModeType {
return 0;
}
%end
5.4 结果
列表页显示成功,没有了VIp标签
但是点击对应集数观看的时候就报错了
六:视频流
试了很多数据模型进行改写都不行
免费的视频流,在浏览器即可访问
Copy (lldb) po ((M3u8Model *)0x2828f1300).dibblingTotalDuration
0
(lldb) po ((M3u8Model *)0x2828f1300).url
https://tx-cdn-local.rr.tv/2a66f13043434abb9114720c3cc4f19c/d6372e9485f84ba38a4b82ab0f31bad2-0e37a229c0ddec2ae7b75fbcac9f6f55-ld.mp4?auth_key=1622396264-900b321e0b42e34f95ab888009a63e84-0-39b17c20f3adcd88504a446cd2496c25&clientType=ios_rrsp_jzsp&clientVersion=5.5.1&parseUsage=PLAY&uid=0
VIP视频免费5min看的视频流,调整参数也不能访问完整视频。从域名看的话免费的放在腾讯云收费的放在阿里云。
Copy (lldb) po ((M3u8Model *)0x2828d9280).url
https://ali-preview.rr.tv/da81107fbb414227bad8faafa68ba9e6/eb37b38567b44e34817a36d9a8b09c3f-74a18a8c38dc4be51298ea5519a1815c-ld.mp4?auth_key=1622396977-56748ddc75ea4acf87fe91146aecae62-0-fb83047d57857ce0deded4705ee8a6be&end=300
(lldb) po ((M3u8Model *)0x2828d9280).dibblingTotalDuration
0
(lldb)
6.1 暂时的结论
基本可以确定,VIP视频的逻辑是有在视频流做处理的,不能简单通过客户端调整进行破解操作。
人人视频破解暂时告一段落。
不过也算挺有收货的