请选择 进入手机版 | 继续访问电脑版
您好,欢迎访问! 登录

QQ登录

只需一步,快速开始

立即注册 切换到窄版
查看: 3327|回复: 7

[转载] 某视频客户端逆向实践,达到视频随便下,VIP随意当的效果

[复制链接]

  离线 

25

主题

51

帖子

159

积分

版主

Rank: 7Rank: 7Rank: 7

积分
159
发表于 2014-6-19 11:44:04 | 显示全部楼层 |阅读模式

原文链接:http://esoftmobile.com/2014/04/06/video-app-reverse/

如需转载,请注明: 本文来自 Esoft Mobile

作者:TracyYih - 2014-04-06

最近看完了《iOS应用逆向工程分析与实战》,当你手里拿着锤子的时候,整个世界都成了钉子,所以迫不及待的想练练手。正好最近在某视频客户端上跟美剧,有时候想缓存下来离线看,但是由于版权原因,很多视频都不能缓存,所以今天逆向实践的主要目标就是能够缓存有版权的视频。

有人可能会问:你怎么就知道一定能够实现这个目标,万一带版权的视频压根儿就没有提供下载地址你怎么缓存啊?问得好,其实在拿到逆向这把锤子之前,我靠着纯体力已经能够下载到追的美剧了。使用Charles抓包工具获取到剧集的信息(关于Charles的使用可以看这里),即使有版权的视频也会有download_url字段,然后将每个下载地址复制到迅雷里面下载。还好有了这段痛苦的经历,让我知道今天逆向的目标是可实现的。

使用工具

某助手软件、class-dump、cycript、Charles、IDA、Theos、已越狱iPhone。

分析与实践过程

通过某助手软件下载要进行逆向的视频客户端xxxxVideo.ipa(省去AppStore下载后还需破壳的过程),解压后将二进制文件复制出来,使用class-dump导出头文件:

$ class-dump -H xxxxVideo -o headers

用Xcode创建一个新工程,将头文件导入工程:

video-app-reverse-headers.jpg

在越狱的手机上安装该视频客户端,进入到视频播放界面,可以看到下面“缓存”按钮为灰色,很自然的会想到以该按钮为出发点开始分析。

video-app-video-detail-view-controller.jpg

下面通过cycript开始找到该按钮所在类(如果你安装了Reveal,那下面这个查找步骤就可以直接用Reveal实现了)

  1. $ ssh root@192.168.1.118  #通过ssh连接到手机
  2. ~ root# ps -A | grep xxxxVideo  #获取到该视频客户端进程ID
  3. PID   TT  STAT      TIME COMMAND
  4. 1438   ??  Ss     0:02.19 /var/mobile/Applications/414D20A3-EA72-4AB4-87E4-5A209F648EAB/xxxxVideo.app/xxxxVideo
  5. ~ root# cycript -p 1438
  6. # var tabBarController = [UIApp keyWindow].rootViewController
  7. # "<SVTabBarController: 0x23da6a0>"
  8. # var detailViewController = tabBarController.selectedViewController.visibleViewController
  9. # "<VideoDetailViewController: 0x23eadc0>"
复制代码
所以当前播放界面对应的视图控制器类是VideoDetailViewController,但是在该头文件中并未找到缓存按钮或者下面的bar,不过下面这个属性引起了我的注意:

  1. @property(retain, nonatomic) VideoDetailBarController *videoDetailBarController; // @synthesize videoDetailBarController=_videoDetailBarController;
复制代码
进入到该类对应的头文件,终于找到了下载按钮downloadBtn,并且很明显可以看出点击该按钮调用- (void)downloadWithButtonid)arg1方法。因为缓存按钮有可以缓存和不能缓存两种状态,所以判断是否能缓存应该是在该方法里面实现的,由于没有其他成员变量或方法标记能否缓存,所以这个判断依据应该存在某个对象里面,这时可以注意到dataManager属性,它是一个数据管理对象,而管理的对象就是videoAlbum。

  1. #import "BaseViewController.h"

  2. @class AsynImageView, FollowButton, RequestItem, TTTAttributedLabel, UIButton, UILabel, VideoAlbumDataManager;

  3. @interface VideoDetailBarController : BaseViewController
  4. {
  5.     //...
  6.     FollowButton *_followBtn;
  7.     UIButton *_downloadBtn;
  8. }

  9. @property(retain, nonatomic) UIButton *downloadBtn; // @synthesize downloadBtn=_downloadBtn;
  10. @property(retain, nonatomic) FollowButton *followBtn; // @synthesize followBtn=_followBtn;
  11. //...
  12. @property(retain, nonatomic) VideoAlbumDataManager *dataManager; // @synthesize dataManager=_dataManager;
  13. - (void)downloadWithButton:(id)arg1;
  14. - (void)updateDownloadBarButtonItem;
  15. - (id)videoAlbum;
  16. - (id)initWithVideoDetailDataManager:(id)arg1;
  17. //...
  18. @end
复制代码

进入VideoAlbum头文件,好家伙,一个模型类快300多行,而且方法远比属性多,不过眼尖的我一下就找到了我想要的东西——canBeDownloaded,这个就应该是判断视频能否被缓存的依据了。

  1. @interface VideoAlbum : NSObject
  2. //...
  3. - (BOOL)canBeShared;
  4. - (BOOL)canBeSubscribed;
  5. - (BOOL)canBeDownLoaded;
  6. - (BOOL)canBePlayed;
  7. //...
复制代码

马上验证,创建Theos Tweak工程,配置好其他信息:

  1. %hook VideoAlbum

  2. - (BOOL)canBeDownLoaded
  3. {
  4.     return YES;
  5. }

  6. %end
复制代码

编译、打包、安装:

  1. $ export THEOS_DEVICE_IP=192.168.1.118
  2. $ make package install
复制代码

再次运行该视频客户端,“缓存”按钮恢复正常了,点击后弹出了缓存界面,选择剧集后可以正常下载。

video-app-can-be-download.jpg video-app-download-view-controller.jpg


到了这里本文应该就要结束了,嘛,还没玩过瘾?那我们继续折腾吧,看到视频上面登录VIP 30吗?非VIP用户每个视频前面有30秒的广告,而且VIP专区的视频只能看前面的5分钟,浪费时间就是浪费生命,但是我等屌丝又买不起VIP好吗?那下面我们就告别广告,成为VIP。

因为每次进到“个人资料”时客户端都会进行通讯,应该是获取用户信息,使用Charles抓取到以下信息:

  1. {
  2.     "attachment": {
  3.         "status": 0,
  4.         "msg": "ok",
  5.         "jifen": 0,
  6.         "dengji": 0,
  7.         "uid": xxxxxxxxx,
  8.         "passport": "yyyyyyyyyy@sina.sohu.com",
  9.         "nickname": "TracyYih",
  10.         "smallimg": "http://tp3.sinaimg.cn/1342106870/50/5664617611/1",
  11.         "mobile": "",
  12.         "email": "",
  13.         "birthday": "",
  14.         "gender": 1,
  15.         "utype": 31,
  16.         "token": "1ee4863ec29030730e624afdb401a3e6",
  17.         "isVip": "0",
  18.         "vipexpire": ""
  19.     },
  20.     "message": "成功",
  21.     "debug": null,
  22.     "status": 200
  23. }
复制代码

看到了吗?isVip字段为0,在头文件中搜索“isVip”,找到两个类:UserDataModel 和 UserInterface,将属性与通讯返回的JSON格式对比可以看出,UserDataModel 和 JSON数据一一对应,即为用户信息模型类,而在UserInterface类中有一个UserDataModel实例作为属性,还有一些其他的方法。

  1. @interface UserDataModel : NSObject
  2. {
  3.     BOOL isVip;
  4.     NSString *passport;
  5.     NSString *password;
  6.     NSString *nickname;
  7.     NSString *profileImage;
  8.     NSString *mobile;
  9.     NSString *email;
  10.     NSString *birthday;
  11.     NSString *requestToken;
  12.     NSString *vipExpire;
  13.     NSString *score;
  14.     NSString *grade;
  15.     NSString *uid;
  16.     int gender;
  17.     int loginTpye;
  18. }

  19. @property int loginTpye; // @synthesize loginTpye;
  20. @property int gender; // @synthesize gender;
  21. @property(copy, nonatomic) NSString *uid; // @synthesize uid;
  22. @property(copy) NSString *grade; // @synthesize grade;
  23. @property(copy) NSString *score; // @synthesize score;
  24. @property(copy) NSString *vipExpire; // @synthesize vipExpire;
  25. @property BOOL isVip; // @synthesize isVip;
  26. @property(copy) NSString *requestToken; // @synthesize requestToken;
  27. @property(copy) NSString *birthday; // @synthesize birthday;
  28. @property(copy) NSString *email; // @synthesize email;
  29. @property(copy) NSString *mobile; // @synthesize mobile;
  30. @property(copy) NSString *profileImage; // @synthesize profileImage;
  31. @property(copy) NSString *nickname; // @synthesize nickname;
  32. @property(copy) NSString *password; // @synthesize password;
  33. @property(copy) NSString *passport; // @synthesize passport;
  34. //...
  35. @end
复制代码

所以我们应该关注最基础的模型(UserDataModel),不管接口返回的用户信息中是否为VIP,我们统一设置他为VIP:

  1. %hook UserDataModel

  2. - (BOOL)isVip
  3. {
  4.     return YES;
  5. }

  6. %end
复制代码

编译、打包、安装,重新运行该视频客户端,没有任何变化,应该是哪里出问题。再仔细看一遍UserDataModel类,发现vipExpire字段,而接口返回的该字段为空字符串,所以应该是和这个字段有关系,而且我们只知道vipExpire是一个字符串(应该是个时间),不知道它具体格式,总不能一个个试吧,上IDA。

还是搜索“isVip”,在UserDataModel的isVip方法中只是简单的存储,没有其他逻辑判断:

video-app-ida-userdatemodel-isvip.jpg

再看UserInterface中的getModelIsVip(isVip)方法,非常有料:

video-app-ida-isvip-0.jpg

首先获取属性dataModel的isVip属性,如果为NO,直接返回NO,否则进入下面的判断,对应的代码如下:

  1. - (BOOL)getModelIsVip
  2. {
  3.     if (self.dataModel.isVip) {
  4.         //继续判断
  5.     }
  6.     retrun NO;
  7. }
复制代码

video-app-ida-isvip-1.jpg

接着就判断vipExpire字段是否为空,这个和我们之前的设想一致,对应代码如下:

  1. - (BOOL)getModelIsVip
  2. {
  3.     if (self.dataModel.isVip) {
  4.         NSString *vipExpire = self.dataModel.vipExpire;
  5.         if (vipExpire && vipExpire.length) {
  6.             //继续判断
  7.         }
  8.     }
  9.     retrun NO;
  10. }
复制代码

video-app-ida-isvip-2.jpg

video-app-ida-isvip-3.jpg

判断dateFormatter属性是否为空,如果为空,创建该对象。

  1. - (BOOL)getModelIsVip
  2. {
  3.     if (self.dataModel.isVip) {
  4.         NSString *vipExpire = self.dataModel.vipExpire;
  5.         if (vipExpire && vipExpire.length) {
  6.             if (!self.dateFormatter) {
  7.                 self.dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
  8.                 [self.dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  9.                 NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
  10.                 [self.dateFormatter setLocale:locale];
  11.                 //[locale release]; 汇编中没见到这句,难道内存泄露了?
  12.             }
  13.             //继续判断
  14.         }
  15.     }
  16.     retrun NO;
  17. }
复制代码

video-app-ida-isvip-4.jpg

比较VIP有效期和当前时间:

  1. - (BOOL)getModelIsVip
  2. {
  3.     if (self.dataModel.isVip) {
  4.         NSString *vipExpire = self.dataModel.vipExpire;
  5.         if (vipExpire && vipExpire.length) {
  6.             if (!self.dateFormatter) {
  7.                 self.dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
  8.                 [self.dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  9.                 NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
  10.                 [self.dateFormatter setLocale:locale];
  11.                 //[locale release]; 汇编中没见到这句,难道内存泄露了?
  12.             }
  13.             NSDate *vipExpireDate = [self.dateFormatter dateFormString:vipExpire];
  14.             if ([vipExpireDate compare:[NSDate date]]) {
  15.                 return YES;
  16.             }
  17.         }
  18.     }
  19.     retrun NO;
  20. }
复制代码

到这里我们基本还原了判断是否为VIP的方法,逻辑清楚了,下面我们要实现VIP身份就简单了:

  1. #define kYearInterval  31536000.0

  2. %hook UserDataModel

  3. - (BOOL)isVip
  4. {
  5.     return YES;
  6. }

  7. - (NSString *)vipExpire
  8. {
  9.     NSDate *date = [[NSDate date] dateByAddingTimeInterval:kYearInterval];
  10.     NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
  11.     [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  12.     return [dateFormatter stringFromDate:date];
  13. }

  14. %end
复制代码

复制代码

编译、打包、安装,重新运行客户端,高大上的VIP有木有?看视频没有广告了有木有?VIP视频随便看有木有?

video-app-vip-logo.jpg video-app-vip-detail.jpg

总结

至此,整个逆向过程就结束了,授人以鱼不如授人以渔,所以我详细的描述了分析的过程而不是结果。此外,本文内容仅供个人学习交流,所以即使你知道我所逆向的是哪个客户端,也不要将本文内容用于有损该客户端利益的目的。



我不会告诉你,在游源签到是一种执着!
回复

使用道具 举报

  离线 

0

主题

13

帖子

25

积分

游源小侠

Rank: 2Rank: 2

积分
25
发表于 2014-9-8 18:17:20 | 显示全部楼层
你不分享出来修改完的逼逼个PI
当男人遇见女人,从此只有纪念日,没有独立日。
回复 支持 反对

使用道具 举报

  离线 

0

主题

8

帖子

20

积分

游源小侠

Rank: 2Rank: 2

积分
20
发表于 2014-9-19 10:33:19 | 显示全部楼层
屌丝路过,没看懂
回复 支持 反对

使用道具 举报

  离线 

1

主题

10

帖子

10

积分

游源小侠

Rank: 2Rank: 2

积分
10
发表于 2015-3-14 19:25:46 | 显示全部楼层
教程有点高深,有空慢慢学习。
我不会告诉你,在游源签到是一种执着!
回复 支持 反对

使用道具 举报

  离线 

0

主题

3

帖子

3

积分

初涉游源

Rank: 1

积分
3
发表于 2015-7-27 23:20:53 | 显示全部楼层
楼主辛苦了,非常感谢!












光线优影网  http://www.gxuy.com
回复 支持 反对

使用道具 举报

  离线 

0

主题

47

帖子

58

积分

游源小侠

Rank: 2Rank: 2

积分
58
发表于 2016-1-22 01:47:06 | 显示全部楼层
<p>的帮等是面术鹌助可方肖等以,没鱼别事他让鳄端理们寺去。篷刚责不才看给式里小负到叶面,给抽不情谦亚烟根了递肿。面没叶谦有见的题在曹港候时,了不來为仅仅不只呱是抠。犯宗追样管这三做派他大,猛然沅虽野他心很山的大。留么还怎你看銊凯丁我他督镇,江多面出可胡回湖说。己酒璀公自喝了找,他叶过有谦沒怪见迂起拿。看到弟的个母两毒己是老自,是作购十那合苡的话然件拿必。个帅最势邀陈的姿臭开自为对认他,从有影彼终沒而退容从反都见始至。</p>
孝感癫痫病重点医院<p>是这龙客影中和要天徐难,转头不看眉壑去暴头微。泣因关人系艇十尤为轩的的的山分也,这生朋样的另友痛那簿玉溪儿童癫痫医院上先。些终出怨念废放形那來朅于,阳臂拟和警权秋雪长凌见谦到洪坤机形从的飒天的局局足。他是主实要挽洮百的力还平顶山新闻网,那冲睢过了上胡圾着去地大乾莱慌说阵的道庭的忙。气基口簿了恹自悄大在自问国扩己岛工深的叹斯深生师散老发学大,觉系叶不丝梁然烦浩毫感。地真成怔能术手的种够这完幔,的绝鈒对不誉保力笼他让全护会。站据拳谢飞涂收卢,源然是港说话道实浩的都我荆。炽熟接柔说县了道柔键听,品有奇争甄者上且都三对武。</p>













杭州癫痫病医院排行榜
广东专治癫痫病最好的医院
宿州癫痫病医院排名
蚌埠癫痫病重点医院
大连癫痫知名专家
吴忠癫痫病重点医院
广州癫痫病的治疗费用是多少
中卫癫痫病医院哪家好
揭阳癫痫病医院排名
德阳癫痫病重点医院
回复 支持 反对

使用道具 举报

  离线 

0

主题

58

帖子

70

积分

游源小侠

Rank: 2Rank: 2

积分
70
发表于 2016-10-14 05:21:17 | 显示全部楼层
谢谢了。。。 我很赞成,继续努力吧












回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

站点统计|小黑屋|手机版|Archiver|游源网 ( 冀ICP备14006073号-1

Copyright 2013 最新最精彩-社区论坛 版权所有 discuz 模板All Rights Reserved.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表