Debugging Android apps without Java source code: a few words about breakpoints

  • Tutorial

What is this article about


This is a continuation of my article yesterday about debugging Android applications without Java source code (if someone has not read it, I highly recommend starting with it). Yesterday I gave step-by-step instructions on how to configure and start using the Apk-tool bundle plus NetBeans. The last two points there sounded something like this:

13. Set breakpoint to the instruction you are interested in ... blah-blah-blah ...

14. Do something in the application to make your breakpoint work. After that, you can do step-by-step debugging, view the values ​​of fields and variables, etc.

Further, in the “Pitfalls” section, I talked about why we cannot start debugging the application from the very beginning, for example by setting a breakpoint on some method instructiononCreate(...)in the activity with which the application starts to run.

In this article, I’ll show you how you can start debugging an application without Java source code from the very beginning. This article is again not for beginners. You need to at least understand the syntax of the Smali assembler and be able to patch .smali files with your pens, correctly entering your code there.

Instruments


We again need Apk-tool 1.4.1 and NetBeans 6.8 - and these are the most outdated versions to date. With newer versions, I cannot get debugging to work. And judging by the discussions on thematic forums - not just me.

I already described the installation of Apk-tool and NetBeans in yesterday’s article, but still I will repeat. NetBeans is installed by default, just click Next-Next-Next in the installation wizard. Installing Apk-tools consists in simply extracting the file apktool.jarfrom the archive to any folder.

How to put breakpoint at the very beginning of the application


The idea is generally simple. You need to find the activity that starts in the application first, and enter an infinite loop at the beginning of the method of onCreate(...)this activity. The application starts and immediately after calling the constructor of this activity, the method will be called onCreate(...). As a result, control will fall into our endless cycle. While the loop will be spinning there, we leisurely attach the debugger to the running application, put a breakpoint right after our endless loop, and then use the debugger features and make the control exit from this loop. And immediately got to our breakpoint. As you can see, everything is elementary.

This section provides step-by-step instructions. The instruction is written for Windows, but most likely it will work on Linux and Mac OS.

Please follow the instructions exactly - this is important!

  1. Decode your .apk file to a directory tempusing Apk-tool. Do not use the option -d:
    java-jarapktool.jardmy.app.apktemp

    As a result temp/smali, you will have a bunch of .smali files in the directory .
  2. In the file, temp/AndroidManifest.xmlfind activity with
    <intent-filter="-filter"><actionandroid:name="android.intent.action.MAIN"><categoryandroid:name="android.intent.category.LAUNCHER"></intent>

    This is the activity that starts in the application first.
  3. Found the activity that starts in the application first? Now find the .smali file that implements the class for this activity (usually a descendant of the class android.app.Activity). Search deep in the directory temp/smali.
  4. Now find the method in this class onCreate(...)and immediately after the call (usually this call goes at the very beginning)
    invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V

    in onCreate(...)enter the following code:
    :debug
    sget v0, Lmy/activity/class/MyActivity;->debugFlag:I
    if-nez v0, :debug

    The attentive reader probably already guessed that this code is that endless loop that we talked about before. Naturally, the MyActivityreal name activity should be used instead , and instead v0of this code, you can use any suitable local register. If there is no suitable register, add it by editing the directives .localsand / or .registersaccordingly.
  5. Also add a field to the class
    .field static debugFlag:I = 0x01

    otherwise, the code of the infinite loop in the previous paragraph does not work.
  6. Rebuild the directory tempback to your .apk file, again without the option -d:
    java-jarapktool.jarbtempmy.app.apk

    Of course, the original my.app.apkstands somewhere before that.


Now we have patched my.app.apk. At the beginning of the method onCreate(...)in the class of the activity with which the application starts, we entered an infinite loop. Well, take this patched one my.app.apkand follow the step-by-step instructions from my article yesterday (see the “Debugging” section). Please note that in the ninth step of this instruction, after you launch the application, you will see a black screen. This is normal, it should be so! It just means that right after launching the application, our patched method was called onCreate(...)and control fell into our endless loop. If after some time Android prompts you to close the application because it does not respond, refuse and go further strictly according to the instructions!

In the twelfth step of the instruction, open in NetBeans the .java file in which the method you patched is located onCreate(...). Use the pause button on the debug panel in NetBeans. Then, in this open .java file, put a breakpoint on the first statement after the endless loop code that you entered in onCreate(...). Then, using the function of viewing and editing variables in the NetBeans debugger, change the value of the field debugFlagto 0and click on the “continue debugging” button on the debugging panel in NetBeans. Management will exit an infinite loop and immediately fall on your breakpoint.

That's it, now you can safely debug the application from the very beginning - from the first call of the very first method onCreate(...)!

A few words about waitForDebugger ()


The reader, who is a little in the subject, probably read on thematic forums about using the method android.os.Debug.waitForDebugger()for the same purposes for which we use an infinite loop in this article. And this same reader is probably surprised that we have planted some kind of garden with a cycle here, although we could just put just onCreate(...)one static method at the beginning of our call:
invoke-static {}, Landroid/os/Debug;->waitForDebugger()V

Note that the method is called without parameters, which means you don’t have to worry about adding local registers if there is no suitable one. It would seem - beauty! What else do you need?

In theory - do not need anything, take it and use it. But in practice, everything is a bit more complicated. In fact, focus c android.os.Debug.waitForDebugger()does not always work and not for everyone. Many people (including me) immediately after the call, the android.os.Debug.waitForDebugger()application really "freezes" and waits for the debugger to join it. This can be seen even in DDMS - a small “red bug” icon appears opposite the application. But as soon as we attach the debugger to the application, control immediately passes to the next instruction after android.os.Debug.waitForDebugger()and the application starts to run further without stopping. We just do not have time to put a breakpoint afterandroid.os.Debug.waitForDebugger(). For a discussion about this, see for example here .

Why android.os.Debug.waitForDebugger()does it work for someone like this, but for someone it’s not known to me yet. Maybe in the comments someone will give an explanation about this. Also in the comments you can and should ask questions about the article. I will try to answer as soon as possible, but if I am stupid - please be patient. I will try to answer everyone.

Happy debugging!

Also popular now: