Flight search app built on FlightConnections.com API data. Features: airport search with autocomplete, browse by country/state/map, flight schedules by route and date, multi-airline support with per-airline schedule loading. Includes 4,561-airport GPS database for map browsing. Adaptive light/dark mode UI inspired by Flighty. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
207 lines
7.1 KiB
JavaScript
207 lines
7.1 KiB
JavaScript
/*
|
|
* Universal OkHttp Response Interceptor
|
|
* Hooks OkHttp's response chain to capture ALL HTTP responses.
|
|
* Works with Spirit, Delta, United (all use OkHttp/Retrofit).
|
|
*
|
|
* Sends captured data to a local server via Frida's send().
|
|
*/
|
|
|
|
Java.perform(function() {
|
|
console.log("[*] OkHttp Response Interceptor starting...");
|
|
|
|
// Filter: only capture responses from these domains
|
|
var targetDomains = [
|
|
"api.spirit.com",
|
|
"www.delta.com",
|
|
"mobileapi.united.com",
|
|
"content.spirit.com",
|
|
"content.delta.com"
|
|
];
|
|
|
|
// Filter: only capture these API paths
|
|
var targetPaths = [
|
|
// Spirit
|
|
"/customermobileprod/",
|
|
"/v1/getboastatus",
|
|
"/v1/getboaparameters",
|
|
"/v2/Token",
|
|
"/v3/mytrips",
|
|
"/v1/booking",
|
|
"/v3/GetFlightInfoBI",
|
|
"/v5/Flight/Search",
|
|
|
|
// Delta
|
|
"/api/mobile/asl",
|
|
"/api/mobile/getFlightStatus",
|
|
"/api/mobile/getFlightStatusByLeg",
|
|
"/api/mobile/login",
|
|
"/api/mobile/getDashboard",
|
|
"/api/mobile/getUpgradeEligibilityInfo",
|
|
|
|
// United
|
|
"/standbylistservice/",
|
|
"/upgradelistservice/",
|
|
"/flightstatusservice/",
|
|
"/checkinservice/",
|
|
"/passriderlistservice/"
|
|
];
|
|
|
|
function shouldCapture(url) {
|
|
var domainMatch = false;
|
|
var pathMatch = false;
|
|
|
|
for (var i = 0; i < targetDomains.length; i++) {
|
|
if (url.indexOf(targetDomains[i]) !== -1) {
|
|
domainMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!domainMatch) return false;
|
|
|
|
for (var j = 0; j < targetPaths.length; j++) {
|
|
if (url.indexOf(targetPaths[j]) !== -1) {
|
|
pathMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pathMatch;
|
|
}
|
|
|
|
// === Hook OkHttp3 RealCall.getResponseWithInterceptorChain ===
|
|
try {
|
|
var OkHttpClient = Java.use("okhttp3.OkHttpClient");
|
|
var Request = Java.use("okhttp3.Request");
|
|
var Response = Java.use("okhttp3.Response");
|
|
var ResponseBody = Java.use("okhttp3.ResponseBody");
|
|
var BufferClass = Java.use("okio.Buffer");
|
|
var MediaType = Java.use("okhttp3.MediaType");
|
|
|
|
// Hook the Interceptor.Chain.proceed to capture request+response
|
|
var RealInterceptorChain = Java.use("okhttp3.internal.http.RealInterceptorChain");
|
|
|
|
RealInterceptorChain.proceed.overload("okhttp3.Request").implementation = function(request) {
|
|
var url = request.url().toString();
|
|
var response = this.proceed(request);
|
|
|
|
if (shouldCapture(url)) {
|
|
try {
|
|
var method = request.method();
|
|
var reqBody = null;
|
|
|
|
// Capture request body
|
|
if (request.body() !== null) {
|
|
var reqBuffer = BufferClass.$new();
|
|
request.body().writeTo(reqBuffer);
|
|
reqBody = reqBuffer.readUtf8();
|
|
}
|
|
|
|
// Capture request headers
|
|
var reqHeaders = {};
|
|
var headerNames = request.headers();
|
|
for (var i = 0; i < headerNames.size(); i++) {
|
|
reqHeaders[headerNames.name(i)] = headerNames.value(i);
|
|
}
|
|
|
|
// Capture response
|
|
var statusCode = response.code();
|
|
var respBody = null;
|
|
var respHeaders = {};
|
|
|
|
// Response headers
|
|
var respHeaderObj = response.headers();
|
|
for (var j = 0; j < respHeaderObj.size(); j++) {
|
|
respHeaders[respHeaderObj.name(j)] = respHeaderObj.value(j);
|
|
}
|
|
|
|
// Response body (need to peek without consuming)
|
|
var body = response.body();
|
|
if (body !== null) {
|
|
var source = body.source();
|
|
source.request(Long.MAX_VALUE);
|
|
var buffer = source.getBuffer().clone();
|
|
respBody = buffer.readUtf8();
|
|
}
|
|
|
|
var captured = {
|
|
type: "HTTP_RESPONSE",
|
|
timestamp: new Date().toISOString(),
|
|
method: method,
|
|
url: url,
|
|
status: statusCode,
|
|
requestHeaders: reqHeaders,
|
|
requestBody: reqBody,
|
|
responseHeaders: respHeaders,
|
|
responseBody: respBody
|
|
};
|
|
|
|
// Send to Frida host
|
|
send(captured);
|
|
|
|
console.log("[+] CAPTURED: " + method + " " + url + " -> " + statusCode + " (" + (respBody ? respBody.length : 0) + " chars)");
|
|
|
|
} catch(e) {
|
|
console.log("[-] Capture error for " + url + ": " + e);
|
|
}
|
|
}
|
|
|
|
return response;
|
|
};
|
|
|
|
console.log("[+] OkHttp RealInterceptorChain.proceed hooked");
|
|
|
|
} catch(e) {
|
|
console.log("[-] OkHttp3 hook failed: " + e);
|
|
console.log("[*] Trying alternative hook...");
|
|
|
|
// Alternative: Hook at a higher level
|
|
try {
|
|
var Interceptor = Java.use("okhttp3.Interceptor");
|
|
// This approach hooks via adding our own interceptor
|
|
console.log("[*] Alternative approach needed - see app-specific hooks");
|
|
} catch(e2) {
|
|
console.log("[-] Alternative also failed: " + e2);
|
|
}
|
|
}
|
|
|
|
// === Also hook Retrofit response callbacks ===
|
|
try {
|
|
var CallbackClass = Java.use("retrofit2.OkHttpCall");
|
|
CallbackClass.parseResponse.implementation = function(rawResponse) {
|
|
var response = this.parseResponse(rawResponse);
|
|
try {
|
|
var url = rawResponse.request().url().toString();
|
|
if (shouldCapture(url)) {
|
|
var body = response.body();
|
|
if (body !== null) {
|
|
console.log("[+] Retrofit response: " + url);
|
|
console.log("[+] Body class: " + body.getClass().getName());
|
|
console.log("[+] Body: " + body.toString().substring(0, Math.min(500, body.toString().length)));
|
|
|
|
send({
|
|
type: "RETROFIT_RESPONSE",
|
|
timestamp: new Date().toISOString(),
|
|
url: url,
|
|
bodyClass: body.getClass().getName(),
|
|
body: body.toString()
|
|
});
|
|
}
|
|
}
|
|
} catch(e) {
|
|
// Ignore parse errors
|
|
}
|
|
return response;
|
|
};
|
|
console.log("[+] Retrofit parseResponse hooked");
|
|
} catch(e) {
|
|
console.log("[-] Retrofit hook failed: " + e);
|
|
}
|
|
|
|
// === Java Long for buffer request ===
|
|
var Long = Java.use("java.lang.Long");
|
|
Long.MAX_VALUE.value;
|
|
|
|
console.log("[*] OkHttp Response Interceptor ready. Waiting for traffic...");
|
|
});
|