亚航app航班价格抓取
条件:去程日期、回程日期、成人数、儿童数、婴儿数、出发机场、到达机场
结果:符合行程的所有航班信息( 主要包括价格、航班号、起飞机场、到达机场、起飞时间、降落时间、舱位等)
抓包分析
从上面的Headers和Form视图中可以得知,亚航采用的是https post 请求,请求及响应报文是加密的,请求头中wToken也是一个密文。
解密wToken及报文体
通过jeb反编译客户端,搜索wToken关键字,找到位于
ConstantHelper.ॱˋ(r2);
方法已经经过混淆处理过,可读性并不友好。
可以通过标记的方法,对相应变量作注释,还原涉及的功能函数。
经过分析及标记得知:
1):报文体的加解密位于MCrypt类中
2):wToken的计算位于IJAQAVMPSignComponent的avmpSign方法中
还原关键代码
出于对目标app的安全考虑,MCrypt及IJAQAVMPSignComponent的具体实现代码就不公开了,以免对其造成不良影响
编写调用客户端测试
//请求userSession,后续接口中需要此参数
public static String requestSession(String wTokenServerURL, String deviceID) throws Exception {
byte[] reqParams = createUserSessionData(deviceID);
String rs = request(wTokenServerURL, "103", deviceID, reqParams);
System.out.println("rs---> " + rs);
return (String) ((Map) (JSON.parseObject(rs).get("data"))).get("userSession");
}
//请求航班价格搜索
public static String requestFlightSearch(String wTokenServerURL, String userSession, String deviceID, int adultPax, int childPax, int infantPax, String departureStation, String arrivalStation, String departureDate, String returnDate) throws Exception {
byte[] reqParams = createFlightSearchData(userSession, deviceID, adultPax, childPax, infantPax, departureStation, arrivalStation, departureDate, returnDate);
return request(wTokenServerURL, "302", deviceID, reqParams);
}
写一个main方法执行一下
获取 清迈(CNX) - 武汉(WUH) 2020-01-08 1位成人的价格列表:
public static void main(String[] args) throws Exception {
String deviceID = "b20be1e49f423119";
String wTokenServerURL = "http://[ip]:[port]";
String userSession = requestSession(wTokenServerURL, deviceID);
System.out.println("userSession:" + userSession);
int adultPax = 1;
int childPax = 0;
int infantPax = 0;
String departureStation = "CNX";
String arrivalStation = "WUH";
String departureDate = DateFormatUtils.format(DateUtils.addDays(new Date(), 2), "yyyy-MM-dd");
String returnDate = "";
String rate = requestFlightSearch(wTokenServerURL, userSession, deviceID, adultPax, childPax, infantPax, departureStation, arrivalStation, departureDate, returnDate);
System.out.println("rs ---> " + rate);
}
解密后的返回结果报文:
{
"response": {
"responseTime": "2020-01-06 14:22:45",
"responseCode": 1000,
"responseMessage": "Success"
},
"meta": {
"cultureCode": "zh-CN",
"iosVersion": 1,
"androidVersion": 1,
"iosCompulsory": 1,
"androidCompulsory": 1,
"countryCacheVersion": 0,
"ssrCacheVersion": 0,
"stationCacheVersion": 0,
"routeCacheVersion": 0
},
"data": {
"SpecialNote": [
[
""
],
[
""
]
],
"SpecialNote2": [
[
""
],
[
""
]
],
"FlightSearch": [
[
{
"IsDirectFlight": true,
"JourneySellKey": "FD~ 570~ ~~DMK~01\/08\/2020 07:10~WUH~01\/08\/2020 11:35~~",
"LowestJourneyFare": 1,
"JourneyDiscount": 0,
"FlightStatus": 1,
"ValuePackAvailable": 1,
"HaveLowFare": 1,
"HavePremiumFare": 1,
"ValuePackageTotal": 830,
"ValuePackageTotalOffset": -86,
"LessThan24Hrs": 0,
"LessThan2Hrs": 0,
"ValuePackageSsrList": {
"Baggage": [
[
{
"SSRCode": "PBAB",
"Amount": 670,
"SsrCodeType": 0
},
{
"SSRCode": "VBPB",
"Amount": -68.8,
"SsrCodeType": 1
}
]
],
"Snack": [
[