Ctripトラベルアプリのデータ収集
目標: 契約を通じてシートリップ旅行アプリでホテルの宿泊料金とホテルの静的データを収集する
jadx逆コンパイル
パケットキャプチャ分析
CharlesやFDではパケットが正常にキャプチャできないことが分かり、WiresharkのパケットキャプチャではTCPベースのプライベートプロトコルが使用されていることが分かる。
広範な分析の結果、SOTPConnection が通信リクエストを担当していると結論づけられました。
private boolean sendRequest(j jVar) throws Exception {
if (ASMUtils.getInterface("b009009091d7678cf389322a73af7974", 5) != null) {
return ((Boolean) ASMUtils.getInterface("b009009091d7678cf389322a73af7974", 5).accessFunc(5, new Object[]{
jVar}, this)).booleanValue();
}
long currentTimeMillis = System.currentTimeMillis();
try {
jVar.b(jVar.G() + 1);
jVar.a.add("4");
Executors.buildRequest(jVar);
currentTimeMillis = System.currentTimeMillis();
if (checkTaskCancel(jVar)) {
finishTask(jVar);
}
if (jVar.K()) {
if (this.socket != null) {
byte[] y = jVar.y();
OutputStream outputStream = this.socket.getOutputStream();
outputStream.write(y);
outputStream.flush();
jVar.a.add(Constants.VIA_REPORT_TYPE_SHARE_TO_QZONE);
jVar.j(System.currentTimeMillis());
jVar.e(System.currentTimeMillis() - currentTimeMillis);
jVar.l(System.currentTimeMillis());
return true;
}
}
jVar.j(System.currentTimeMillis());
jVar.e(System.currentTimeMillis() - currentTimeMillis);
jVar.l(System.currentTimeMillis());
return false;
} catch (Exception e) {
throw e;
} catch (Throwable th) {
jVar.j(System.currentTimeMillis());
jVar.e(System.currentTimeMillis() - currentTimeMillis);
jVar.l(System.currentTimeMillis());
}
}
カスタム パケット キャプチャ ツールを使用して、ここから要求データ パケットと応答データ パケットを取得します。
リクエストとレスポンスのデコード
デバッグ分析を通じて、ProtocolHandle がシリアル化と逆シリアル化を担当しており、具体的な逆のプロセスについては詳しく説明されていないことがわかりました。
セクションごとにシリアル化メソッドが異なります。次のクラスは、アプリ全体に関わるシリアル化メソッドを定義します。
public enum CommEncodingType {
None,
Normal,
UTF8,
PB,
Json,
SotpPB,
SotpJson,
PBSotp,
PBJson,
JsonSotp,
JsonPB,
GraphQL
}
全体的な通信プロトコルは比較的複雑で、Gzip 圧縮、AES 暗号化、カスタム リクエスト ヘッダーが含まれるため、復元には多くの時間と労力が必要です。
暗号化と復号化の逆解析
関連する暗号化と復号化は EncodeUtil に実装されます。
public native byte[] cd(byte[] bArr, int i);
public native byte[] ce(byte[] bArr, int i);
次のコードを通じて、ローカル メソッドによって実装されます。
static {
try {
System.loadLibrary("ctripenc");
} catch (Throwable th) {
if (!classVerify) {
th.printStackTrace();
System.loadLibrary("ctripenc");
}
}
}
ctripenc so ファイルに実装されていることがわかります。lib/armeabi を開くと確認できます。
cd メソッドと ce メソッドを復元する必要があります。
クライアントテストを書く
public static void testHotelRoomListRequest(int hotelID, String checkInDate, String checkOutDate) throws Exception {
RequestDataBean requestDataBean = buildHotelRoomListRequest(hotelID,checkInDate,checkOutDate);
byte[] totalData = requestDataBean.totelData;
byte[] responseBodyData = send2ctrip(totalData);
HotelRoomListResponse hotelRoomListResponse = new HotelRoomListResponse();
ProtobufIOUtil.mergeFrom(responseBodyData, hotelRoomListResponse, HotelRoomListResponse.getSchema());
System.out.println(JSON.toJSONString(hotelRoomListResponse));
}
main メソッドを記述して実行し、価格リストを取得します。
価格表のjson内容は比較的大きいので、以下に価格部分を抜粋して表示します。
"priceInfoList": [
{
"avgPrice": "224",
"avgPriceAfterDiscount": "224",
"avgPriceAfterDiscountIncludeTax": "224",
"avgPriceIncludeTax": "224",
"avgTax": "0",
"cashBackAvgAmount": "0",
"cashBackTotalAmount": "0",
"currencyCode": "RMB",
"primeDiscount": "0",
"totalDays": 1,
"totalPrice": "224",
"totalPriceAfterDiscount": "224",
"totalPriceAfterDiscountIncludeTax": "224",
"totalPriceIncludeTax": "224",
"totalTax": "0"
}
]