This article is currently an experimental machine translation and may contain errors. If anything is unclear, please refer to the original Chinese version. I am continuously working to improve the translation.
For Android reverse engineering study only. Sensitive data has been anonymized.
Recently, a university introduced a mandatory running check-in system for students. Although the pace requirements aren’t particularly strict, one must complete 24 runs per semester, each at least 2,400 meters… which is still slightly challenging for me. So, I decided to dive into the app and see how it works.
0x00 Preliminary Observation & Information Gathering
By analyzing the package name of relevant Activities, I discovered that the running feature isn’t developed in-house by the university, but rather integrated from a third-party vendor into the school’s official app. According to the software documentation, it likely checks several factors: step frequency (via accelerometer), GPS location, pace, and device integrity.
Searching the CoolApk forum for discussions about this app, I found many users rely on Fake Location apps to spoof their runs. But that’s clearly not elegant at all—and some users reported that newer versions detect such tricks and invalidate the results. The app also includes anti-emulator detection. (Lucky for me, I’ve got a real device—no worries here~)
0x01 Packet Capture
When in doubt, start with packet capture. On newer Android versions, using Http Canary for HTTPS interception requires an Xposed module to bypass SSL pinning. Since my daily driver runs GSI-based AOSP + Magisk + LSPosed, capturing packets was a breeze. I quickly intercepted the HTTP traffic. (Irrelevant headers omitted, sensitive data replaced with *.)
1 | POST /v3/api.php/Run/getTimestampV278 HTTP/1.1 |
1 | HTTP/1.1 200 OK |
Looking at the request and response, we can see the request includes timestamp, nonce, and sign. The response is obviously encrypted, so to simulate requests successfully, we first need to crack the signature and decryption mechanisms.
0x02.0 Decryption Method 1: Hook Crypto APIs to Intercept Keys
The laziest (and often most effective) way to reverse engineer encryption is to simply hook relevant APIs and extract the keys—provided standard crypto/hashing algorithms are used, and the key is either static or follows a predictable pattern (and isn’t user-specific).
While these conditions might sound strict, in practice, many apps with signing/encryption meet them, making it easy to mimic requests and decrypt responses.
You can find plenty of Frida scripts online for hooking crypto APIs, but I personally used Inspeckage, which hooks a wider range of interfaces.
Of course, many such tools don’t support the latest Android versions. A Google Pixel device is almost essential for modern reverse engineering. After setting up the environment, running the target app, and filtering ADB logs with grep, I quickly found the following critical info:
(btw. Our school’s app is such a mess—it integrates so many third-party (garbage) services… The crypto calls are all over the place. It’s like running a survival-of-the-fittest experiment on my phone.)
1 | Inspeckage_Crypto:SecretKeySpec(****************,AES) , Cipher[AES/CBC/PKCS5Padding] IV: **************** |
Clearly, AES/CBC/PKCS5Padding is used for encryption, and we now have the Key and IV—allowing us to decrypt all request/responses. The signature is generated by concatenating all HTTP parameters (sorted by key), appending a secret string, then applying MD5.
0x02.1 Decryption Method 2: Unpacking + IDA Pro
Originally, Method 1 had already achieved the goal. But since I happened to use FART, I accidentally unpacked the app… Well, since it’s done, might as well analyze the code and see if we can locate the keys directly.
Opening the unpacked DEX with jadx, I found that although the app was packed, it wasn’t obfuscated at all—so all logic and variable names were crystal clear. A quick keyword search revealed the final encryption call:
1 | public class JNIUtils { |
The keys are clearly stored in native code. Extract the relevant .so file and drop it into IDA Pro.
IDA Pro Reverse Engineering
No obstacles at all—immediately found the required keys. (Honestly, you might not even need to unpack—the keys could probably be found by just grepping the APK’s .so files. This JNI protection is weaker than the packer. At least the packer hides strings. 🤡)
0x03 Full Packet Analysis Workflow
Now that we’ve figured out the signing and decryption, we can write Python code to simulate requests and decrypt responses.
1 | def sign(ret): |
We can now also decrypt the requests captured via Http Canary. So I took my phone, fired up Http Canary, went for a short run, and整理ed the captured traffic. It wasn’t hard to identify the full sequence of requests during a single run:
/User/User— Retrieve current logged-in user info. (Login uses OAuth from the school’s main app.)/Run2/beforeRunV260— Returns run-related constraints, such as speed, time, and location requirements./Run/getTimestampV278— Fetch a timestamp and a “record_str”, which is needed when ending the run.During the run,
/Run/setRunLocationRecordis called every ~1–2 minutes to upload current GPS coordinates.Before ending the run,
/Report/getOssSignis called to get a signature for uploading two files to Alibaba Cloud OSS.- One file is a screenshot of the phone screen during the run.
- The other is an AES-encrypted JSON file containing all GPS coordinates and phone-recorded pace data, sampled every 2 seconds.
Finally, using all the above (start/end time, duration, distance, URLs of uploaded files,
record_str, step frequency per minute, etc.), the app calls/Run/stopRunV278to submit the full run record to the server—completing one “legitimate” run.
0x04 Writing the Simulation Script
After mapping out the entire flow, writing a Python script to generate a plausible-looking run record and send the requests in order becomes straightforward—voilà, a successful cyber run. 😎
Running resultThis article is licensed under the CC BY-NC-SA 4.0 license.
Author: lyc8503, Article link: https://blog.lyc8503.net/en/post/nju-cyber-run/
If this article was helpful or interesting to you, consider buy me a coffee¬_¬
Feel free to comment in English below o/