原文发于本人小笔记 http://leanote.bitcode.tk:251/blog/post/sma11case/%E9%80%86%E5%90%91PP%E5%8A%A9%E6%89%8BIOS%E7%89%88%EF%BC%8C%E8%8E%B7%E5%8F%96%E5%8E%86%E5%8F%B2%E7%89%88%E6%9C%AC%E4%B8%8B%E8%BD%BD%E5%88%97%E8%A1%A8 此处做个备份,转载请保留此段文字,原文有结果结构解析详细方法以及已编译好的命令行版本下载。
抓包分析PP助手IOS版,获取历史版本下载列表
PP助手IOS版可以安装APP的历史版本,而Mac版没这功能,今天闲着无事抓包分析了对应接口。
抓包过程略过不表,由于此APP构造自定义数据结构进行通讯,因此需要分析数据构造,然后组装数据进行接口调用。
历史版本接口需要APPID,因此先通过Mac版抓包获取APP信息的接口(接口已写成脚本,方便调用)。
#!/bin/sh
JBCHANNEL='4262469664'
StoreChannel='4262469686'
type="$1"
KEYWORD="$2"
if [ '' = "${KEYWORD}" ]; then
echo "usage: ipasearch.sh "
exit 0
fi
POST1='-d{"dcType":0, "keyword":"'
POST2='", "clFlag":1, "perCount":15, "page":0}'
POST="${POST1}${KEYWORD}${POST2}"
CHANNEL="${JBCHANNEL}"
if [ 'store' = "${CHANNEL}" ]; then
CHANNEL="${StoreChannel}"
fi
curl -Lk 'http://jsondata.25pp.com/index.html' -H 'Content-Type: application/x-www-form-urlencoded' -H "Tunnel-Command: ${CHANNEL}" "${POST}"
exit 0
随便拿个APP开刀,比如微信,调用脚本 ppsearch.sh jb 微信
(表示搜索越狱应用,关键字微信)得到如下信息,其中id
便是APPID,获取历史版本需要用到。
{
"id": 620065,
"itemId": 414478124,
"price": 0,
"package": 0,
"buid": "com.tencent.xin",
"version": "6.6.6",
"downurl": "http://r11.25pp.com/soft/2018/04/02/20180402_24703_218980057839.ipa",
"title": "微信",
"fsize": "100.29MB",
"dcType": 3,
"thumb": "http://img.25pp.com/uploadfile/app/icon/20180402/1522646114148240_54x54.jpg",
"desc": "微信是一款全方位的手机通讯应用,帮助你轻松连接全球好友。微信可以(通过SMS/MMS网络)发送短信、进行视频聊天、与好友一起玩游戏,以及分享自己的生活到朋友圈,让你感受耳目一新的移动生活方式。 为什",
"stars": 60,
"updatetime": 1523157809,
"downloads": 301217387,
"resType": 1
}
贴上历史版本接口数据结构,其中unknow1应该是对应接口哪个功能,不用理会,直接填充为固定的即可,device_token
填充为40长度的随机16进制字符串,plat_name
为对应手机平台字符串,可通过uname()
函数获得,一般写死即可,pkgId
便是上面提到的APPID。
struct __attribute__ ((packed)) QueryInfo {
char unknow1[0x10-1]; // 4b 00 00 00 14 00 e0 fd 01 7a 00 00 00 00 00
char device_token[40+1]; // device token, length=40
char plat_name[9+1]; // iPhone7,2
char unknow2[5]; // 00 02 00 09 00
int32_t pkgId; // 21 76 09 00 ==> 620065
};
typedef struct QueryInfo QueryInfo;
调用实例(语言OC,仅贴关键代码,细节部分自行填充)
static NSString *const kQueryAppInfoUrl = @"http://mobileup.25pp.com/index.php";
static const unsigned char unknow1[] = { 0x4b, 0x00, 0x00, 0x00, 0x14, 0x00, 0xe0, 0xfd, 0x01, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const unsigned char unknow2[] = { 0x00, 0x02, 0x00, 0x09, 0x00 };
QueryInfo info = {0};
memcpy(info.unknow1, unknow1, sizeof(unknow1));
memcpy(info.unknow2, unknow2, sizeof(unknow2));
strcpy(info.device_token, [NSString randomStringWithKeys:HexLowerCaseKeys length:40].UTF8String);
strcpy(info.plat_name, "iPhone7,2");
info.pkgId = 620065;
NSData *post = [NSData dataWithBytes:&info length:sizeof(info)];
NSDictionary *header = @{
// @"Cookie":@"UM_distinctid=16179b283d11f2-01c14a4f3-4c1e3b41-3d10d-16179b283d222",
@"User-Agent":@"PPHelperNS/122 CFNetwork/758.0.2 Darwin/15.0.0",
};
把post
数据和header
通过POST
方式提交到http://mobileup.25pp.com/index.php
即可获得返回,header
经测试不设置也是可以的,但设置一下防止被发现的伪造的包~~~
接口调用后返回的数据也是Binary结构,通过以下函数简单粗暴的转换为字符串。
inline static NSMutableArray *sc_parseData(const char *ptr, size_t len)
{
NSMutableArray *res = NewMutableArray();
NSMutableData *temp = nil;
for (long a= 0; a < len; ++a)
{
unsigned char x = ptr[a];
if (0 == x)
{
temp = nil;
continue;
}
if (nil == temp)
{
temp = NewClass(NSMutableData);
[res addObject:temp];
}
[temp appendBytes:&x length:sizeof(x)];
BreakPointHere;
}
BreakPointHere;
return res;
}
inline static NSMutableArray *sc_parseString(const char *ptr, size_t len)
{
NSMutableArray *res = sc_parseData(ptr, len);
NSMutableArray *sss = NewMutableArray();
[res enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
id obj1 = [obj toUTF8String];
if (obj1) [sss addObject:obj1];
}];
BreakPointHere;
return sss;
}