📜 ⬆️ ⬇️

Debugging Android Applications Without Java Source Code: A Few Words About Breakpoints

What is this article about


This is a continuation of my yesterday’s article on debugging Android applications without Java source code (if someone hasn’t read it, I strongly advise you to start with it). Yesterday I gave step-by-step instructions on how to set up and start using the Apk-tool plus NetBeans bundle. The last two points there sounded like this:

13. Install a breakpoint on the instructions you are interested in ... blah-blah-blah ...

14. Do something in the app 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 told why we cannot start debugging an application from the very beginning, for example, by setting a breakpoint on some instruction of the onCreate(...) method in the activity with which the application starts to run.

In this article I will tell you how you can still start debugging an application without Java source code from the very beginning. This article is again not for newbies. You need to at least understand the Smali assembler syntax and be able to use the pens to patch the .smali files, correctly writing your code there.

Instruments


We again need Apk-tool 1.4.1 and NetBeans 6.8 - and these are the outdated versions for today. With newer versions, I can't make debugging work. And judging by the discussions on the thematic forums - not just me.

I already described the installation of Apk-tool and NetBeans in yesterday’s article, but I’ll repeat it. NetBeans is installed by default, just click Next-Next-Next in the installation wizard. Installing Apk-tools consists in apktool.jar extracting the apktool.jar file from the archive into any folder.

How to set breakpoint at the very beginning of the application


The idea is simple. You need to find the activity that starts in the application first, and enter an infinite loop at the beginning of the onCreate(...) method of this activity. The application starts and immediately after calling the constructor of this activity, the onCreate(...) method will be called. As a result, management will end up in our endless loop. While the cycle is spinning there, we will leisurely attach the debugger to the running application, set a breakpoint immediately after our infinite loop, and then take advantage of the debugger’s capabilities and make control of this loop out. And immediately got on our breakpoint. As you can see, everything is elementary.

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

Please follow the instructions exactly - this is important!

  1. Decode your .apk file in the temp directory using the Apk-tool. Do not use the -d option:
     java -jar apktool.jar d my.app.apk temp 

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

    This is the activity that starts in the application first.
  3. Found an activity that starts in the application first? Now find the .smali file that implements the class for this activity (usually it is a descendant of the android.app.Activity class). Search in the depth directory temp/smali .
  4. Now find the onCreate(...) method in this class and immediately after the call (usually this call goes right at the 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 the infinite loop we talked about earlier. Naturally, instead of MyActivity , the real name activity should be used, and instead of v0 in this code you can use any suitable local register. If there is no suitable register, add it by editing the directives .locals and / or .registers accordingly.
  5. Also add a field to the class.
     .field static debugFlag:I = 0x01 

    otherwise, the infinite loop code in the previous paragraph will not work.
  6. Rebuild the temp directory back to your .apk file, again without the -d option:
     java -jar apktool.jar b temp my.app.apk 

    Of course, the original my.app.apk should be my.app.apk somewhere before.


Now we have patched my.app.apk . In the beginning of the onCreate(...) method in the class of the activity with which the application starts, we entered an infinite loop. Well, take this patched my.app.apk and follow the step-by-step instructions from my yesterday’s article (see the Debug section). Note that at the ninth step of this instruction, after you start the application, you will see a black screen. This is normal, it should be so! This simply means that immediately after launching the application, our patched onCreate(...) method was called and the 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 on strictly according to the instructions!

In the twelfth step of the instruction, open in NetBeans the .java file that contains the onCreate(...) method you patched. Use the pause button on the NetBeans debug panel. Then in this open .java file, put the breakpoint on the first instruction after the code of the infinite loop that you entered in onCreate(...) . Then, using the function of viewing and editing variables in the NetBeans debugger, change the value of the debugFlag field to 0 and click on the “continue debugging” button on the debugging panel in NetBeans. The control will exit from an infinite loop and will immediately reach your breakpoint.

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

A few words about waitForDebugger ()


The reader, who is a bit in the subject, probably read on the thematic forums about the use of the android.os.Debug.waitForDebugger() method for the same purposes for which we use an infinite loop in this article. And this same reader is probably surprised that we have built up a garden with a cycle here, although it would have been easy to write the call of just one static method to the beginning of our onCreate(...) :
 invoke-static {}, Landroid/os/Debug;->waitForDebugger()V 

Note that the method is called without parameters, which means that you do not need to suffer with the addition of local registers if there is no suitable one. It would seem - beauty! What more do you want?

In theory - nothing is needed, take it and use it. But in practice, everything is a bit more complicated. In fact, the focus with android.os.Debug.waitForDebugger() does not always work and not for everyone. For many (including me) right after the call to 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 beetle 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 running further without stopping. We just do not have time to set the breakpoint after android.os.Debug.waitForDebugger() . For a discussion of this, see, for example, here .

Why does android.os.Debug.waitForDebugger() for someone, and for someone, I don't know 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 respond as quickly as possible, but if I’m stupid, please be patient. I will try to answer all.

Happy debugging!

Source: https://habr.com/ru/post/150862/


All Articles