š„ Reverse engineering the Latitude app
Latitude Festival has a phone app with a schedule, but it's pretty tedious to use. Give me a spreadsheet already!
To hacking
Android apps are simpler to reverse than iOS, though I donāt have an Android phone.
I setup an Android emulator - apparently theyāre locked down these days and itās trickier to get root. An API 26 image worked for me, thereās probably other options Iām not aware of.
I then downloaded and installed the Latitude festival xapk.
Used apktool to decompile the xapk to Smali code. Thereās an outer xapk file, and an apk file inside that. I ran apktool d on both. This gives something semi-readable to look at.
I wanted to experiment with frida-server - itās a toolkit that lets you patch Android functions. Initially to monitor HTTPS traffic - I could maybe have used mitmweb, but Frida came in handy later.
Started the Frida server on the emulator:
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &" ChatGPT wrote this to log HTTP calls:
Java.perform(function () {
var URL = Java.use("java.net.URL");
var HttpURLConnection = Java.use("java.net.HttpURLConnection");
URL.$init.overload('java.lang.String').implementation = function (url) {
console.log("[*] URL initialized with string: " + url);
return this.$init(url);
};
HttpURLConnection.getOutputStream.implementation = function () {
console.log("[*] HttpURLConnection.getOutputStream called");
return this.getOutputStream();
};
HttpURLConnection.connect.implementation = function () {
console.log("[*] Connecting to: " + this.getURL());
return this.connect();
};
});
Which I ran with:
while true; do
uv run --with frida-tools frida -U -n Latitude -l hook.js
sleep 0.5
done This attempted to hook the Latitude app every 0.5 seconds. I could then clear app data and restart it, and Frida would patch the restarted app.
The most interesting URL was https://content.greencopper.net/latitude-2025/9900587ec9b348da850a206f80ee39bd/content/content_v287.zip
This URL canāt be found in the codebase, weāll see why soon.
Encryption!
The content zip files were encrypted š.
Judicious use of Ctrl+F found a Smali file containing āPassword is blank. Please provide a valid passwordā. That sounds like a decryption function.
In Smali the decryption function was:
- Class:
.class public final LIc/g; - Method:
.method public final a(Ljava/io/File;Ljava/io/File;Ljava/lang/String;LXc/d;)Ljava/lang/Object;
Feeding this to ChatGPT and asking it to hook the function gave:
Java.perform(function () {
var TargetClass = Java.use("Ic.g");
TargetClass.a.overload(
"java.io.File",
"java.io.File",
"java.lang.String",
"Xc.d"
).implementation = function(file1, file2, str, dObj) {
console.log("š¤ password: " + str);
return this.a(file1, file2, str, dObj);
};
}); Ran the same way as the previous script, andā¦
Bingo! The logged password was content_v287[redacted]zip, where [redacted] is the secret value in runConfig.json
And we now have a decrypted content file with some JSON files containing schedules, performers, etc. š
Pre-packaged content
Remember the content zip URL? We couldnāt find it in the codebase.
Thatās because the app has a content zip bundled into the APK, in the assets/content directory. Itās encrypted, but we know how to workaround that now. The decrypted content contains links to other content manifests.