Since the official NUS NextBus Android app requires Google Play services which my phone does not support. I wanted to develop a custom app for NUS NextBus. To do so, I need to be able to access the APIs used by NUS NextBus. A quick google search led to this reverse engineered API by SuibianP:
However, I noticed that executing any of these requests would give a 401 Unauthorized error:
It seems that SuibianP did not document how to make authorized requests here. Let's reveal how the NUS NextBus app does it by reverse-engineering the app ourselves.
Since it is my first attemp to reverse-engineer an Android app, I did a quick search and found this wonderful article:
Following that article, let's first get the package name of NUS NextBus by opening the app on my phone and runing the following command:
> adb shell dumpsys activity recents | grep 'Recent #0' | cut -d= -f2 | sed 's| .*||' | cut -d '/' -f1
nus.ais.mobile.android.shuttlebus
The next step is to get the apk path:
> adb shell pm path nus.ais.mobile.android.shuttlebus
package:/data/app/nus.ais.mobile.android.shuttlebus-gywHKqGywO_xoIevMtAYrA==/base.apk
And then pull the apk to the computer:
> adb pull /data/app/nus.ais.mobile.android.shuttlebus-gywHKqGywO_xoIevMtAYrA==/base.apk
Let's decompile the apk with apktool:
> apktool d base.apk
We can now see a new folder under the current directory that holds the decompiled source.
Let's view the source code using jadx:
> jadx-gui base.apk
After glancing through some of the code, I noticed that the Java source code does not seem to hold any logic. I also found that there are imports of the com.facebook.react
package (see the screenshot below). Perhaps this indicates that it is a React Native app?
Some googling reveals that the js source is located at assets/index.android.bundle
:
Since the code is obfuscated, let's first format the code to make it more readable. I used the Prettier plugin for VS Code:
Now it's time for some searching. Since we already know the endpoints of the APIs, let's search for their occurrences in the source code. I have chosen BusStops
as the keyword and it led me here:
This is followed by:
From the code snippets above, we can see that the API is called using fetch
, whose first parameter is the URL and the second parameter is additional options. This second parameter is what we are looking for! With the Go to Definition
function from VS Code, I navigated to the definition of C
:
Here there are a few things we need to know:
- What is
T.encode()
- What does the
_.sayHi
method do
To answer the first question, I tried to search for the definition of T
, but I got this:
Hmmm that does not look useful. Let's try encode
, shall we?
Ha! This looks like it! The extracted definition of encode
is:
s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
A = /[\t\n\f\r ]/g
encode = function (t) {
(t = String(t)),
/[^\0-\xFF]/.test(t) &&
f(
"The string to be encoded contains characters outside of the Latin1 range."
);
for (
var n, o, c, h, A = t.length % 3, l = "", p = -1, u = t.length - A;
++p < u;
)
(n = t.charCodeAt(p) << 16),
(o = t.charCodeAt(++p) << 8),
(c = t.charCodeAt(++p)),
(l +=
s.charAt(((h = n + o + c) >> 18) & 63) +
s.charAt((h >> 12) & 63) +
s.charAt((h >> 6) & 63) +
s.charAt(63 & h));
return (
2 == A
? ((n = t.charCodeAt(p) << 8),
(o = t.charCodeAt(++p)),
(l +=
s.charAt((h = n + o) >> 10) +
s.charAt((h >> 4) & 63) +
s.charAt((h << 2) & 63) +
"="))
: 1 == A &&
((h = t.charCodeAt(p)),
(l += s.charAt(h >> 2) + s.charAt((h << 4) & 63) + "==")),
l
);
};
Now what is left is the sayHi
method of _
. Specifically, we want to know what value of t
it passes to its second parameter in which it is passed to the encode()
function. The definition of _
seems readable:
Since it says NativeModules
, the guess would be it is a Java class called ToastModule
which has a method sayHi
. A search in jadx gave us:
Aha! We can see in the definition of sayHi
that it invokes the callback2
function with a string "██████████"
.
With this information, we should be able to trick the API server to believe we are making requests from the actual NUS NextBus app. Let's first encode the basic auth token. We can simple call the encode
function defined above in a Chrome console:
Now we can try to call the API with the correct headers. I recently discoverd this awesome CS2103 project called imPoster by some of my classmates. Let's test the API with imPoster:
And we seccessfully got a response!
References:
Comments
Hi :P So glad that the documentation might be of some help!
I did document the credentials, but hid it deep into the disclaimer as a hidden raw comment just so as to avoid any trouble with our beloved IT management XDD
I am also planning to develop a telegram bot for that purpose during the upcoming vacation, since Telegram does not seem to require GMS :) Maybe can we possibly collaborate on that, just by any chance?
@SuibianP Hi! Thanks for the comment! Your documentation is awesome! It is the only source I found online that shows the new base URL. I would have no clue on how to start if I didn't read your work!
When I read your disclamer, I thought is unfinished. Guess I should dig deeper next time XD.
Sure!! Developing a telegram bot would certainly be fun! I'm currently working on a clone of the app with Flutter. If you are interested, we can also work on that together!