iOS安全–XX助手网络协议分析

目的:通过XX助手分析网络协议,然后通过代码模拟网络协议,请求数据。

1. 从界面分析程序逻辑:

来到XX助手的榜单页面,使用cycript打印界面布局:

[[UIApp keyWindow] recursiveDescription].toString()

找到每一个下载控件,这里随便找一个:

图片 1

通过UITableViewCellContentView这个控件找它的包含关系。

使用[#0x15baf520 nextResponder]往上找,可以得到TRTableMultipleViewCell分为3列,每一列包含一个TRAppListSubCell,而每个TRAppListSubCell对应一个TRAppInfo对象。对应的viewcontroller是TRPTabBarViewController。

所以我猜这个TRAppInfo就是下载后的数据对象了。

使用原来说过的http://www.alonemonkey.com/ioss-theos-logify.htmlHook这个类的方法可以看到:

图片 2 

对应一个App的相关信息,包括下载地址。

那么,这些信息是从哪来的?

在-[TRAppInfo setAdsite:]下断点。

b -[TRAppInfo setAdsite:]

然后查看调用堆栈:

图片 3

其中的一个类TRApiServices很像是网络请求的接口。Hook该类的调用输出。

点击 榜单 ,并有了以下log输出:

图片 4

首先调用api接口:

-[<TRApiServices: 0x14e53670> getChartsAppListForCountryId:143465 listType:61 pageNum:0 userInfo:(null) delegate:<TRResourceListManager: 0x14e674c0>]

然后构造请求头:

-[<TRApiServices: 0x14e53670> writeBodyHeader:<TRBuffer: 0x14d64f30> command:4278202423 flags:0]

再添加到请求队列:

-[<TRApiServices: 0x14e53670> addOperaionToQueue:<4e000000 373000ff 01700000 00000033 66663161 36633136 32333037 36326165 65353134 38396236 30313861 38326233 34646165 63333000 69506164 322c3500 00020009 0a693002 003d0000 0000> url:http://mobileup.25pp.com/index.php parseSelector:parseGetChartsAppListData:error: userInfo:(null) delegate:<TRResourceListManager: 0x14e674c0>]

最后处理返回结果:

-[<TRApiServices: 0x14e53670> parseGetChartsAppListData:<c1260100 373000ff 00000a69 3002003d 02000000 78000000 15b91c00 00020300 0000636f 6d2e3131 62697473 74756469 6f732e74 776f6d6d 6f62696c 6500312e 31330031 2e31332e 3100e688 91e79a84 e68898e4 ba892054 68697320 57617220 6f66204d 696e6500 3335332e 31394d42 0000

再把处理后的结果,初始化成一个个TRAppInfo对象。

2. 逆向分析

初步的分析完毕,开始逆向分析的工作:

首先分析调用api:

图片 5

可以看到先写入body头部信息,然后根据参数填充,最后发起请求。

跟进writeBodyHeader函数:

图片 6

这些信息hook api调用填充进去即可。

图片 7

最后的问题来了,这个TRBuffer是一个什么样的类。

从log和ida的逆向可以看出来,这个TRBuffer是把传入的参数转换成字节的一个类,可能为了避免被抓包分析,所以以自己定义的格式,将参数变成字节的形式按指定的规则填充好buffer,最后再以这个buffer数据流直接发给服务器,然后服务器再按指定的协议来解析参数,返回结果。当然最后返回的结果也是字节流的形式,需要在本地按指定的协议格式进行解析。最后才能得到一个个TRAppInfo对象。这个对象里面就包含了一个应用的所有信息。包括下载地址,包名,应用名等等。

这里我把TRBuffer这个类逆向后写了一个一样的类也叫TRBuffer。

首先构造bodyheader:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TRBuffer* buffer = [TRBuffer defaultForAPI];
[buffer setUint32:0xFF003037];     //用来识别哪个api的请求
[buffer setInt8:1];                  
[buffer setInt16:111];              
[buffer setInt32:0];
[buffer setNSString:@"3ff1a6c162307******1489b6018a82b34daec30"];   //UUID
[buffer setNSString:@"iPad2,5"];   //platform
[buffer setInt32:150995456];       //systemVersionInt
构造api参数:
[buffer setInt8:10];                
[buffer setInt32:countryId];        //中国 143465
[buffer setInt32:listtype];         //列表类型
[buffer setInt32:pagenum];          //第几页
[buffer setLengthWithFirst];       //数据长度

到此post data body已经构造出来了:

NSData* data = [NSData dataWithBytes:[buffer getBody] length:[buffer getLength]];

直接post到url:http://mobileup.25pp.com/index.php

解析返回数据:

从上面看到从服务器返回的数据也是字节流的形式,所以要把字节流的形式按指定的格式解析成对象及属性。

上面看到-[TRApiServices parseGetChartsAppListData:error:]负责返回数据的解析。

图片 8

写出解析代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
NSData* returnData = [self SynPost:data];
//得到返回结果
if(returnData != nil){
        TRBuffer* buffer = [[TRBuffer alloc] init];
        [buffer setData:returnData];
        [buffer getInt32];
       
        NSMutableArray* appinfos = [[NSMutableArray alloc] init];
        //判断返回数据类型
        if([buffer getUint32] == -16764873){
            if(![buffer getInt16]){
                NSLog(@"Resource Type: %d",[buffer getInt8]);
                NSLog(@"Category Id: %d",[buffer getInt16]);
                NSLog(@"Tag Id: %d",[buffer getInt16]);
                NSLog(@"ListType: %d",[buffer getInt8]);
                NSLog(@"AllPageNum: %d",[buffer getInt32]);
               
                int appNum = [buffer getInt32];
                appNum = appNum>1000?1000:appNum;
               
                for (int i = 0; i < appNum; i++) {
                    AppInfo *appInfo = [[AppInfo alloc] init];
                    [appInfo setItemId:[buffer getInt32]];
                    [appInfo setAdsite:[buffer getInt8]];
                    [appInfo setResourceType:[buffer getInt8]];
                    [appInfo setDeviceFamily:[buffer getInt32]];
                    [appInfo setBundleID:[buffer getNSString]];
                    [appInfo setShortVersion:[buffer getNSString]];
                    [appInfo setVersion:[buffer getNSString]];
                    [appInfo setName:[buffer getNSString]];
                    [appInfo setSize:[buffer getNSString]];
                    [appInfo setSoftMarkFlag:[buffer getInt8]];
                    [appInfo setImageUrl:[buffer getNSString]];
                    [appInfo setDownloadUrl:[buffer getNSString]];
                    [appInfo setDownloadUrlCRC:[buffer getInt32]];
                    [appInfo setResCategoryId:[buffer getInt32]];
                    [appInfo setResCategoryName:[buffer getNSString]];
                    [appInfo setScreenshotArray:[buffer getNSString]];
                    [appInfo setCurVersionRate:[buffer getInt8]];
                    [appInfo setDownloadCount:[buffer getInt32]];
                    [appInfo setReviewCount:[buffer getInt32]];
                    [appInfo setUpdateTime:[buffer getInt32]];
                    [appInfo setRemarkOn:[buffer getNSString]];
                    [appInfo setPrice:[buffer getInt32]];
                    [appInfo setStatStr:[buffer getNSString]];
                    NSLog(@"appInfo %@:",appInfo);            
                }
            }
        }
    }

返回的结果:

图片 9

反正协议已经出来了,使用什么语言去实现都一样。

其它几个api接口:

//总榜,飙升榜…

getSelectionListForAppType: listType: pageNum: pageItemCount:

//必备

getNecessaryAppListForResourceType: listType: pageNum: itemCountEachPage:

//AppStore榜

getChartsAppListForCountryId: listType: pageNum:

//软件分类

getAppListForAppType: categoryId: tagId: listType: pageNum:

//Cydia插件

getCydiaPluginListForCategoryId: listType: pageNum:

TRBuffer逆向源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
TRBuffer.h
#import <Cocoa/Cocoa.h>

@class NSString, NSData;

@interface TRBuffer : NSObject {
@private
    char* _body;
    unsigned long _bufferLen;
    unsigned long _len;
    unsigned long _wseek;
    unsigned long _rseek;
    int _int_t;
}
@property(retain, nonatomic, getter=getNSString, setter=setNSString:) NSString* NSString;
@property(retain, nonatomic, getter=getNSData, setter=setNSData:) NSData* NSData;
@property(assign, nonatomic, getter=getNSTimeInterval, setter=setNSTimeInterval:) double NSTimeInterval;
@property(assign, nonatomic, getter=getNSUInteger, setter=setNSUInteger:) unsigned NSUInteger;
@property(assign, nonatomic, getter=getBOOL, setter=setBOOL:) BOOL BOOL_t;
@property(assign, nonatomic, getter=getNSInteger, setter=setNSInteger:) int NSInteger;
@property(assign, nonatomic, getter=getLongLong, setter=setLongLong:) long long longlong_t;
@property(assign, nonatomic, getter=getLong, setter=setLong:) long long_t;
@property(assign, nonatomic, getter=getFloat, setter=setFloat:) float float_t;
@property(assign, nonatomic, getter=getInt64, setter=setInt64:) long long int64;
@property(assign, nonatomic, getter=getUint32, setter=setUint32:) unsigned uint32;
@property(assign, nonatomic, getter=getInt32, setter=setInt32:) int int32;
@property(assign, nonatomic, getter=getInt16, setter=setInt16:) short int16;
@property(assign, nonatomic, getter=getInt8, setter=setInt8:) BOOL int8;
@property(assign, nonatomic, getter=getInt, setter=setInt:) int int_t;
@property(readonly, assign, getter=getLength) unsigned long length;
@property(readonly, assign, getter=getBody) const char* body;
@property(retain, nonatomic, getter=getData, setter=setData:) NSData* data;
+(TRBuffer*)defaultForAPI;
-(id)getRestData;
-(void)setLengthWithSeek:(unsigned long)seek;
-(void)setLengthWithFirst;
-(void)setChar:(BOOL)aChar index:(int)index;
-(void)getMem:(char*)mem len:(unsigned long)len;
-(void)setMem:(const char*)mem len:(unsigned long)len;
-(int)getInt_t;
-(void)setInt_t:(int)t;
-(long)getStringLen;
-(void)read:(char*)read len:(unsigned long)len;
-(void)write:(const char*)write len:(unsigned long)len;
-(void)addLen:(unsigned long)len;
-(id)init;
@end
TRBuffer.m
//
//  TRBuffer.m
//
//  Created by AloneMonkey on 16/1/28.
//  Copyright © 2016年 AloneMonkey. All rights reserved.
//

#import "TRBuffer.h"

@implementation TRBuffer

+(TRBuffer*)defaultForAPI{
    TRBuffer* buffer = [[self class] new];
    if(buffer){
        [buffer setInt32:0];
    }
    return buffer;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _bufferLen = 2048;
        _body = calloc(2048, 1);
    }
    return self;
}

-(void)addLen:(unsigned long)len{
    _len += len;
    while(_len >= _bufferLen){
        _bufferLen += 2048;
        _body = realloc(_body, _bufferLen);
    }
}

-(void)write:(const char *)write len:(unsigned long)len{
    [self addLen:len];
    while (len) {
        _body[_wseek] = *write;
        _wseek++;
        write++;
        len--;
    }
}

-(void)read:(char *)read len:(unsigned long)len{
    if(_rseek + len > _len){
        NSLog(@"read len over");
    }else{
        while (len) {
            *read = _body[_rseek];
            _rseek++;
            read++;
            len--;
        }
    }
}

-(long)getStringLen{
    int len = 0;
    if(_rseek >= _len){
        return 1;
    }else{
        unsigned long seek = _rseek;
        while (true) {
            if(!_body[seek])
                break;
            seek++;
            len++;
            if(seek >= _len)
                return len;
        }
    }
    return len+1;
}

-(void)setInt_t:(int)t{
    [self write:&t len:4];
}

-(int)getInt_t{
    int ret = 0;
    [self read:&ret len:4];
    return ret;
}

-(void)setInt8:(BOOL)int8{
    [self write:&int8 len:1];
}

-(BOOL)getInt8{
    char ret = 0;
    [self read:&ret len:1];
    return ret;
}

-(void)setInt16:(short)int16{
    [self write:&int16 len:2];
}

-(short)getInt16{
    short ret = 0;
    [self read:&ret len:2];
    return ret;
}

-(void)setInt32:(int)int32{
    [self write:&int32 len:4];
}

-(int)getInt32{
    int ret = 0;
    [self read:&ret len:4];
    return ret;
}

-(void)setUint32:(unsigned int)uint32{
    [self write:&uint32 len:4];
}

-(unsigned int)getUint32{
    unsigned int ret = 0;
    [self read:&ret len:4];
    return ret;
}

-(void)setInt64:(long long)int64{
   [self write:&int64 len:8];
}

-(long long)getInt64{
    long long ret = 0;
    [self read:&ret len:8];
    return ret;
}

-(void)setFloat:(float)float_t{
    [self write:&float_t len:4];
}

-(float)getFloat{
    float ret = 0;
    [self read:&ret len:4];
    return ret;
}

-(void)setLong:(long)long_t{
    [self write:&long_t len:4];
}

-(long)getLong{
    long ret = 0;
    [self read:&ret len:4];
    return ret;
}

-(void)setLongLong:(long long)longlong_t{
    [self write:&longlong_t len:8];
}

-(long long)getLongLong{
    long long ret = 0;
    [self read:&ret len:8];
    return ret;
}

-(void)setNSInteger:(int)NSInteger{
    [self write:&NSInteger len:4];
}

-(int)getNSInteger{
    int ret = 0;
    [self read:&ret len:4];
    return ret;
}

-(void)setNSUInteger:(unsigned int)NSUInteger{
    [self write:&NSUInteger len:4];
}

-(unsigned int)getNSUInteger{
    unsigned int ret = 0;
    [self read:&ret len:4];
    return ret;
}

-(void)setBOOL:(BOOL)BOOL_t{
     [self write:&BOOL_t len:1];
}

-(BOOL)getBOOL{
    BOOL ret = 0;
    [self read:&ret len:1];
    return ret;
}

-(void)setNSTimeInterval:(double)NSTimeInterval{
    [self write:&NSTimeInterval len:8];
}

-(double)getNSTimeInterval{
    double ret = 0;
    [self read:&ret len:8];
    return ret;
}

-(void)setNSData:(NSData *)data{
    if(data){
        [self setNSUInteger:[data length]];
        if([data length]){
            [self write:[data bytes] len:[data length]];
        }
    }
}

-(NSData *)getNSData{
    NSData * data = nil;
    int len = [self getNSUInteger];
    if(len){
        void* temp = calloc(len, 1);
        [self read:temp len:len];
        data = [NSData dataWithBytes:temp length:len];
        free(temp);
    }
    return data;
}

-(void)setNSString:(NSString *)str{
    if(str && str.length){
        [self write:[str UTF8String] len:strlen([str UTF8String])+1];
    }
}

-(NSString *)getNSString{
    int strLen = [self getStringLen];
    void* temp = calloc(strLen, 1);
    memset(temp, 0, strLen);
    [self read:temp len:strLen];
    NSString * str = [NSString stringWithCString:temp encoding:NSUTF8StringEncoding];
    free(temp);
    return str?str:@"";
}

-(void)setMem:(const char *)mem len:(unsigned long)len{
    [self write:mem len:len];
}

-(void)getMem:(char *)mem len:(unsigned long)len{
    [self read:mem len:len];
}

-(void)setChar:(BOOL)aChar index:(int)index{
    if(index > 0 && index < [self getLength]){
        _body[index] = aChar;
    }
}

-(void)setLengthWithFirst{
    [self setLengthWithSeek:0];
}

-(void)setLengthWithSeek:(unsigned long)seek{
    char* len = (char*)&_len;
    for (int i = 0; i < 4; i++) {
        _body[seek+i] = len[i];
    }
}

-(NSData *)getData{
    return [NSData dataWithBytes:_body length:_len];
}

-(void)setData:(NSData *)data{
    [self write:[data bytes] len:[data length]];
}

-(id)getRestData{
    return [NSData dataWithBytes:_body[_rseek] length:_len-_rseek];
}

-(unsigned long)getLength{
    return _len;
}

@end

本文链接:http://www.alonemonkey.com/pphelper-analyze.html

没有评论

Comments are closed.