目的:通过XX助手分析网络协议,然后通过代码模拟网络协议,请求数据。
1. 从界面分析程序逻辑:
来到XX助手的榜单页面,使用cycript打印界面布局:
[[UIApp keyWindow] recursiveDescription].toString()
找到每一个下载控件,这里随便找一个:
通过UITableViewCellContentView这个控件找它的包含关系。
使用[#0x15baf520 nextResponder]往上找,可以得到TRTableMultipleViewCell分为3列,每一列包含一个TRAppListSubCell,而每个TRAppListSubCell对应一个TRAppInfo对象。对应的viewcontroller是TRPTabBarViewController。
所以我猜这个TRAppInfo就是下载后的数据对象了。
使用原来说过的http://www.alonemonkey.com/ioss-theos-logify.htmlHook这个类的方法可以看到:
对应一个App的相关信息,包括下载地址。
那么,这些信息是从哪来的?
在-[TRAppInfo setAdsite:]下断点。
b -[TRAppInfo setAdsite:]
然后查看调用堆栈:
其中的一个类TRApiServices很像是网络请求的接口。Hook该类的调用输出。
点击 榜单 ,并有了以下log输出:
首先调用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:
可以看到先写入body头部信息,然后根据参数填充,最后发起请求。
跟进writeBodyHeader函数:
这些信息hook api调用填充进去即可。
最后的问题来了,这个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:]负责返回数据的解析。
写出解析代码:
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); } } } } |
返回的结果:
反正协议已经出来了,使用什么语言去实现都一样。
其它几个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 |
Permalink
66666