# 24.实战人人视频破解

## 一、class-dump

> 提前准备好[砸壳🔗](https://github.com/RyukieSama/RyukieDevGitBook/tree/571119c68a246a6c21c887dca5f05e105ecbd920/逆向/逆向/23.应用砸壳.md)的应用（人人视频5.5.1）

使用class-dump导出头文件，便于我们定位目标。

`class-dump -H MachO文件名 -o 输出的文件夹/`

## 二、确定逆向目标

这里我想要实现非VIP用户可以查看VIP视频。

一般情况下我们会通过类似`isVIP`这样的字眼去进行相关的判断。

Cycript链接手机进行分析

## 三、登录态

通过在Headers中搜索`isLogin`看到一个很可能是用来判断登录态的东西：

![1](/files/-MaNQBf9848ONp1P_7ul)

### 3.1 hook登录态

```cpp
@interface UserInfoConfig

+ (bool)isLogined;

- (bool)isLogined;

@end

%hook UserInfoConfig

+ (bool)isLogined {
    return YES;
}

- (bool)isLogined {
    return YES;
}

%end
```

### 3.2 检验登录态

***原版***

![3](/files/-MaNQBfAUkXSSnhOfgkH)

***Hook版***

通过！这里的`立即登录`已经变成`立即开通`了

![2](/files/-MaNQBfBoJUpHswlRL18)

> 这里因为是直接改写了登录状态的返回值，所以这里有了大量的错误提示：
>
> `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层开始分析

![4](/files/-MaNQBfCxp44tB1v0Buc)

#### a.找到容器: `RRVipUserInfoView`

> 看看它内部有没有做数据逻辑

![5](/files/-MaNQBfDxpDvxTGkYBmW)

> 看起来`resetStatus`&`reloadUIData`是有做些请求的，我们先记录一下。继续分析。

#### b.父视图：`RRVipBannerTopCell`

![6](/files/-MaNQBfEdMvPqUv4aJm-)

> 这里看到一个`@property(retain, nonatomic) RRTopBannerModel *bannerModel;`
>
> 但是没有和用户相关的模型属性，推测`VIP`的判断是在`RRVipUserInfoView`内部！

#### c.VIP相关类

![7](/files/-MaNQBfFC7y7EWxG4IsO)

通过Headers查找VIP关键字，逐个查看，发现`RRIMManager`疑似。而且还是个单例。我们Hook看下有什么效果。

> 下面又找到几个，可能的文件

#### f.RrmjUser

其中有这样一个属性：`@property(retain, nonatomic) RRMedalModel *vipMedal;`

#### g.RRMedalModel

```cpp
@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代码：

```cpp
#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
```

**结果**

![8](/files/-MaNQBfGEEMGIwQpiggq)

> 之前`开通会员`的入口都变成了续费了。哈哈哈哈
>
> 但是观看会员视频还是只能试看5分钟
>
> 下面的目标就是解除5分钟的限制了！

## 四：无限制调广告

> 试着直接把广告加载的方法Return了，但是直接不能加载视频了。于是改成不用等就能点跳过。

```cpp
#pragma mark - RRPlayerControlAdPlay
@interface RRPlayerControlAdPlay: NSObject

- (bool)canJump;

@end

%hook RRPlayerControlAdPlay

- (bool)canJump {
    return YES;
}

%end
```

## 五：改变视频权限

### 5.1结合正常显示的剧集数据对比非会员和会员的视频区别

![](/files/-MbEfHCP69A9vsTPg5-J)

### 5.2 调试找出关键字段的区别

![](/files/-MbEfHCQywA2-yqcLlbr)

### 5.3 Hook

```cpp
#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标签

![](/files/-MbEfHCR-0vszrHzvAR-)

但是点击对应集数观看的时候就报错了

![](/files/-MbEfHCSoeUZrcADkukR)

## 六：视频流

试了很多数据模型进行改写都不行

免费的视频流，在浏览器即可访问

```cpp
(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看的视频流，调整参数也不能访问完整视频。从域名看的话免费的放在腾讯云收费的放在阿里云。

```cpp
(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视频的逻辑是有在视频流做处理的，不能简单通过客户端调整进行破解操作。

人人视频破解暂时告一段落。

不过也算挺有收货的


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ni-xiang/24.-shi-zhan-ren-ren-shi-pin-po-jie.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
