Background
I was distributing software for mobile app developers.
A few clients started to complain that our android product is not working for them, in this specific case it was reproducible 100% of times on their live applications, but 0% of times in the internal tools I was using to test my software.
Imagine a scenario where you have no idea what is the problem, there is no stack trace of a crash, no internal events, or application logs that can give hints about the issue we’re facing.
You get the opportunity to solve the problem, what would you do next?
Android Emulator
Usually, the first thing on my mind is building a working local environment to reproduce the issue.
Android emulator is super important because you can decide on the os version, device type, size, etc…
This medium article assisted me a lot when raising a local environment the first time:
https://medium.com/@daptronic/the-android-emulator-and-charles-proxy-a-love-story-595c23484e02
The issue reported by our clients happened to them on a live application. Fortunately, android AVD manager has the option to install emulators with google play inside
You can install any application directly into the emulator.
Monitor live application network calls
In the initiation of our product, we have a network call to fetch data from the server.
After installing the app, I opened it and used Charles proxy to monitor the network traffic — a good tutorial of using Charles proxy with mobile devices can be found here: https://medium.com/@hackupstate/using-charles-proxy-to-debug-android-ssl-traffic-e61fc38760f7
Unfourtently, I couldn’t monitor the HTTPS network calls because a signed application in the store usually has protection against sniffing its network calls…
The great thing about software development is that there is always a solution, just don’t give up on finding one.
Copy apk from the emulator into a local folder
In order to continue and debug the application, I copied it from the emulator into my computer.
ADB comes to the rescue, read more about how to do it here: https://gist.github.com/ctrl-freak/24ac0e61b7cf550a6945
adb shell pm list packagesadb shell pm path com.app.tocopyadb pull /data/app/com.app.tocopy.apk
Copy apk from the google play into a local folder
Raccoon is a utility application for PC that lets you connect into google account from your computer and download apk’s directly from google play store.
Deconstruction using apktool
A super useful tool, especially when you want to wrap an existing apk with custom modifications, which is exactly what I wanted to achieve.
The idea was to change the network configuration for this apk, so it will be feasible to sniff the HTTPS network request through Charles proxy.
There are already some nice articles that explain how to achieve this, here is one for a reference: https://medium.com/keylogged/bypassing-androids-network-security-configuration-575819a8f317
If you want the main terminal commands of decompiling and compiling back an apk you can also see them below:
1. apktool d [PATH_TO_YOUR_APK]2. modify the application in the folder created in step 13. apktool b [PATH_TO_FOLDER_CREATED_IN_STEP_1]4. now you have a new apk under the dist/your.apk folder, before you can install it you'll need to sign it (if you don't have an android keystore file, make sure to create one)5. jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore [PATH_TO_KEYSTORE] [PATH_TO_YOUR_NEW_APK] [KEYSTORE_ALIAS_NAME]6. adb install -f [PATH_TO_SIGNED_APK]
Decompiling using JadX
JadX is another useful tool, super handy when you want to decompile an APK and read its code.
Note the main difference between JadX and Apktool, the latter gives you the option to deconstruct the code while with JadX you can only decompile it and see the source code of an application.
You can install it directly into your computer, i.e for mac:
brew install jadx
If you are lazy, another option will be to decompile the apk using jadx online: http://www.javadecompilers.com/apk
Enjoy :)
Webview Debugging
So now we open the app again, see the network calls using Charles proxy, and then stuck again.
In my case, this network call was for an HTML file that was loaded into a webview.
But I didn’t have any data about what is the issue, we’re on a release version app, with no logs.
Android webview debugging come to the rescue: https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews
setWebContentsDebuggingEnabled is a static method, which means you can call it on runtime, also after the webview is opened, and it will allow you the inspect your html/css/js code.
In our SDK, you can achieve this from a server configuration, but if it wasn’t the case, try and manipulate the code using apktool, as achieved with the network modification explained in a previous section
Chrome inspect
For beginners guide read this: https://medium.com/@chanisa.suw/debugging-webview-via-native-android-application-by-chrome-inspect-english-version-aa271dff77e1
To enter the chrome inspect webpage:
chrome://inspect/#devices
So I’m opening the client application, now with the ability to inspect our webview. Inside the inspector page, I don’t see any logs, the code is uglified and minified in order to reduce the file size…
Again, I found myself in a situation where it's super hard to debug…I needed a small break, that was the time to get my motivation back so I listened to We Shall Overcome by Joan Baez
Charles Proxy map-local
I shall overcome.
The code was indeed uglified but because it is part of our codebase it was easy to manipulate our build process and create the same file without modifications.
Now you can take advantage of Charles Proxy map-local feature, which can download a local file from your computer instead of the original file that the application is about to download.
Logging to debug
There are many ways for debugging code, when possible I like to use breakpoints to find the root cause.
This time I couldn’t add breakpoints, so logging came to the rescue.
I wasn’t familiar with the code, so I’ve added logs in critical paths and used a binary search approach in order to pinpoint the issue fast.
If you want to read about different debugging techniques, I recommend this article: https://medium.com/@scraggo/debugging-strategies-checklist-a405603894dd
If without Else
function void someFunction() {
if(isTrue()) {
//Main code
}
}
When writing our code, we tend to think that if our code works, it won’t break. This is normal because if our minds will think about all the edge cases in life it will be impossible to progress.
A good practice that I’m trying to follow, is adding a log/event in the edge cases parts during development.
If we are in a debug phase, it is also a good practice to add more information in those areas.
So the new code will be:
function void someFunction() {
if(!isTrue()) {
//Log or Event the edge cases
}
//Main Code
}
Notice that someFunction now starts with all the possible edge cases, and the main logic will be stacked at the end. But in terms of code clarity, it is super easy to find the main code logic.
I Shall Be Released
Don’t forget that the purpose of this process is to bring some value. If you debug a client bug, server slowness, reverse engineering of a competitive analysis task, or looking into an internal tool,
Once you finish, make sure you’re getting things done and release something meaningful afterward!