Hacking Book | Free Online Hacking Learning

Home

reverse practice of a video client

Posted by graebner at 2020-02-25
all

Recently I watched "IOS application reverse engineering analysis and practice". When you hold a hammer in your hand, the whole world becomes a nail, so you can't wait to practice. Just recently, I have been following American TV series on a video client. Sometimes I want to cache them for offline viewing. However, due to copyright reasons, many videos cannot be cached. So the main goal of reverse practice today is to cache copyrighted videos.

Some people may ask: how do you know that you can achieve this goal? How can you cache the videos with copyright in Wan'an without providing download address at all? Good question. In fact, before I got the hammer, I was able to download the American TV series with pure physical strength. Use the Charles capture tool to get the episode information (see here for the use of Charles). Even copyrighted videos will have the download "URL" field, and then copy each download address to thunderbolt to download. Fortunately, with this painful experience, I know that today's reverse goal is achievable.

download_url

Using tools

Some assistant software, class dump, cycript, Charles, IDA, theos, jailbroken iPhone.

Analysis and practice process

Download the video client xxxxvideo.ipa to reverse through a helper software (save the process of breaking the shell after downloading the app store). After decompression, copy the binary file and use class dump to export the header file:

One

$ class-dump -H xxxxVideo -o headers

Create a new project with Xcode and import the header file into the project:

Install the video client on the jailbreak mobile phone and enter the video playing interface. You can see that the "cache" button below is gray. It's natural to think of this button as the starting point for analysis.

Next, start to find the class of the button through cycript (if you have installed reveal, the next step can be implemented directly with reveal)

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

$ ssh [email protected]

~ root

PID TT STAT TIME COMMAND

1438 ?? Ss 0:02.19 /var/mobile/Applications/414D20A3-EA72-4AB4-87E4-5A209F648EAB/xxxxVideo.app/xxxxVideo

~ root

Therefore, the view controller class corresponding to the current playback interface is videodetailviewcontroller, but the cache button or the following bar is not found in the header file. However, the following property has attracted my attention:

VideoDetailViewController

One

@property(retain, nonatomic) VideoDetailBarController *videoDetailBarController;

Enter the header file corresponding to this class, and finally find the download button downloadbtn, and it is obvious that clicking this button calls the - (void) downloadwithbutton: (ID) arg1 method. Because the cache button has two states: can cache and can't cache, the judgment of whether can cache should be implemented in this method. Since there is no other member variable or method tag can cache, the judgment basis should exist in an object. At this time, you can notice the datamanager property, which is a data management object, and the management object is video Album.

downloadBtn - (void)downloadWithButton:(id)arg1 dataManager videoAlbum

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

Eleven

Twelve

Thirteen

Fourteen

Fifteen

Sixteen

Seventeen

Eighteen

Nineteen

Twenty

Twenty-one

#import "BaseViewController.h"

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

@interface VideoDetailBarController : BaseViewController

{

FollowButton *_followBtn;

UIButton *_downloadBtn;

}

@property(retain, nonatomic) UIButton *downloadBtn;

@property(retain, nonatomic) FollowButton *followBtn;

@property(retain, nonatomic) VideoAlbumDataManager *dataManager;

- (void)downloadWithButton:(id)arg1;

- (void)updateDownloadBarButtonItem;

- (id)videoAlbum;

- (id)initWithVideoDetailDataManager:(id)arg1;

@end

Entering the videoalbum header file, my dear, a model class is more than 300 lines faster and has more methods than properties, but I found what I wanted in a moment - can be downloaded, which should be the basis for judging whether the video can be cached.

VideoAlbum canBeDownloaded

One

Two

Three

Four

Five

Six

Seven

@interface VideoAlbum : NSObject

- (BOOL)canBeShared;

- (BOOL)canBeSubscribed;

- (BOOL)canBeDownLoaded;

- (BOOL)canBePlayed;

Verify now, create theos tweet project, and configure other information:

One

Two

Three

Four

Five

Six

Seven

Eight

%hook VideoAlbum

- (BOOL)canBeDownLoaded

{

Return YES;

}

%end

Compile, package and install:

One

Two

$ export THEOS_DEVICE_IP=192.168.1.118

$ make package install

Run the video client again, and the "cache" button returns to normal. Click to open the cache interface, and select the episode to download normally.

This article should be over when we get here. Haven't we had a good time? Then let's continue to toss, see the video above the login VIP 30? Non VIP users have 30 seconds of ads in front of each video, and the video in VIP area can only watch the 5 minutes in front. Wasting time is a waste of life, but I can't afford VIP, OK? Let's say goodbye to the advertisement and become a VIP.

登录VIP 30

Because the client will communicate every time it enters the "profile", it should obtain the user information, and use Charles to grab the following information:

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

Eleven

Twelve

Thirteen

Fourteen

Fifteen

Sixteen

Seventeen

Eighteen

Nineteen

Twenty

Twenty-one

Twenty-two

Twenty-three

{

"attachment": {

"Status": 0,

"MSG": "OK".

"Jifen": 0,

"Dengji": 0,

"uid": xxxxxxxxx,

"passport": "[email protected]",

"nickname": "TracyYih",

"smallimg": "http://tp3.sinaimg.cn/1342106870/50/5664617611/1",

"mobile": "",

"Email":

"birthday": "",

"Gender": 1,

"Utype": 31,

"token": "1ee4863ec29030730e624afdb401a3e6",

"isVip": "0",

"vipexpire": ""

}

"Message": "success",

"debug": null,

"status": 200

}

Did you see? The isvip field is 0. Search "isvip" in the header file and find two classes: userdatamodel and userinterface. Compare the properties with the JSON format returned by communication, we can see that userdatamodel and JSON data correspond one by one, that is, the user information model class. In the userinterface class, there is a userdatamodel instance as the property, and there are some other methods.

isVip UserDataModel UserInterface UserDataModel UserInterface UserDataModel

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

Eleven

Twelve

Thirteen

Fourteen

Fifteen

Sixteen

Seventeen

Eighteen

Nineteen

Twenty

Twenty-one

Twenty-two

Twenty-three

Twenty-four

Twenty-five

Twenty-six

Twenty-seven

Twenty-eight

Twenty-nine

Thirty

Thirty-one

Thirty-two

Thirty-three

Thirty-four

Thirty-five

Thirty-six

@interface UserDataModel : NSObject

{

BOOL isVip;

NSString *passport;

NSString *password;

NSString *nickname;

NSString *profileImage;

NSString *mobile;

NSString *email;

NSString *birthday;

NSString *requestToken;

NSString *vipExpire;

NSString *score;

NSString *grade;

NSString *uid;

Int gender;

int loginTpye;

}

@property int loginTpye;

@property int gender;

@property(copy, nonatomic) NSString *uid;

@property(copy) NSString *grade;

@property(copy) NSString *score;

@property(copy) NSString *vipExpire;

@property BOOL isVip;

@property(copy) NSString *requestToken;

@property(copy) NSString *birthday;

@property(copy) NSString *email;

@property(copy) NSString *mobile;

@property(copy) NSString *profileImage;

@property(copy) NSString *nickname;

@property(copy) NSString *password;

@property(copy) NSString *passport;

@end

So we should pay attention to the most basic model (userdatamodel). Whether or not the user information returned by the interface is VIP, we set it as VIP:

UserDataModel

One

Two

Three

Four

Five

Six

Seven

Eight

%hook UserDataModel

- (BOOL)isVip

{

Return YES;

}

%end

Compile, package, install and rerun the video client without any change. What should be the problem. Take a closer look at the userdatamodel class and find that the vipexpire field returned by the interface is an empty string, so it should be related to this field. Besides, we only know that vipexpire is a string (it should be a time), and we don't know its specific format. We can't try it one by one. Go to IDA.

UserDataModel vipExpire vipExpire

Or search "isvip". In the isvip method of userdatamodel, it is just a simple storage, and there is no other logical judgment:

UserDataModel UserInterface

First, obtain the isvip attribute of the datamodel attribute. If it is no, return no directly. Otherwise, enter the following judgment. The corresponding code is as follows:

One

Two

Three

Four

Five

Six

Seven

- (BOOL)getModelIsVip

{

if (self.dataModel.isVip) {

}

Retrun NO;

}

Then we will judge whether the vipexpire field is empty, which is consistent with our previous assumption. The corresponding code is as follows:

vipExpire

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

- (BOOL)getModelIsVip

{

if (self.dataModel.isVip) {

NSString *vipExpire = self.dataModel.vipExpire;

if (vipExpire && vipExpire.length) {

}

}

Retrun NO;

}

Determine whether the dateformatter property is empty. If it is empty, create the object.

dateFormatter

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

Eleven

Twelve

Thirteen

Fourteen

Fifteen

Sixteen

Seventeen

- (BOOL)getModelIsVip

{

if (self.dataModel.isVip) {

NSString *vipExpire = self.dataModel.vipExpire;

if (vipExpire && vipExpire.length) {

if (!self.dateFormatter) {

self.dateFormatter = [[[NSDateFormatter alloc] init] autorelease];

[self.dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];

[self.dateFormatter setLocale:locale];

}

}

}

Retrun NO;

}

Compare VIP validity and current time:

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

Eleven

Twelve

Thirteen

Fourteen

Fifteen

Sixteen

Seventeen

Eighteen

Nineteen

Twenty

- (BOOL)getModelIsVip

{

if (self.dataModel.isVip) {

NSString *vipExpire = self.dataModel.vipExpire;

if (vipExpire && vipExpire.length) {

if (!self.dateFormatter) {

self.dateFormatter = [[[NSDateFormatter alloc] init] autorelease];

[self.dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];

[self.dateFormatter setLocale:locale];

}

NSDate *vipExpireDate = [self.dateFormatter dateFormString:vipExpire];

if ([vipExpireDate compare:[NSDate date]]) {

Return YES;

}

}

}

Retrun NO;

}

Here we basically restore the method of judging whether it is VIP or not. The logic is clear. Now we want to realize VIP identity simply:

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

Eleven

Twelve

Thirteen

Fourteen

Fifteen

Sixteen

Seventeen

Eighteen

#define kYearInterval 31536000.0

%hook UserDataModel

- (BOOL)isVip

{

Return YES;

}

- (NSString *)vipExpire

{

NSDate *date = [[NSDate date] dateByAddingTimeInterval:kYearInterval];

NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];

[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

return [dateFormatter stringFromDate:date];

}

%end

Compile, package, install, and run the client again. Is there any VIP on the big platform? There's no advertisement in the video. Is there any wood? Do you have any VIP videos?

summary

At this point, the whole reverse process is over. It is better to teach people to fish than to teach people to fish, so I described the analysis process in detail rather than the results. In addition, the content of this article is only for personal learning and communication, so even if you know which client I am reversing, do not use the content of this article for the purpose of damaging the interests of the client.

Raspberry PI Bluetooth application and ibeacon base station construction

I program, I'm happy