This article walks the readers through debugging java programs using a command line tool called JDB. Though this article doesn’t touch android concepts, this is a prerequisite to understand the next article coming in the series which is “Exploiting Debuggable Android Applications”.
What is JDB?
JDB is a java debugger is a simple command line debugger for java classes. It comes preinstalled with JDK.
I am using Ubuntu machine for this article, so we can locate JDB by navigating to /usr/bin directory as shown below
ls | grep jdb
Note: If you are using a windows machine, it will be available in bin directory of java path. This article is written in Ubuntu machine and all the techniques shown here are almost same even in windows.
In this article, rather than typing jdb commands blindly and looking at the usage, we take a sample java application to understand how to use jdb commands based on the scenario. The whole idea is to understand jdb and its usage in debugging java programs.
Below is the sample code I have taken as an example:
Name of the source code file: Debug.java
Class file generated: Debug.class
The code snippet shown above has two methods which are invoked from main method of Debug class and when we execute it after compiling, it shows the output as shown in the screenshot below, which is expected.
We have compiled our java program with “–g” option so that we get some extra debugging information in the generated class file.
To debug java applications, there must be a communication channel between JDB and JVM, since our java program would run inside Java Virtual Machine.
There are multiple ways to connect JDB to JVM as shown below.
In this method, we can directly load our class file into jdb. JDB will automatically start JVM and a connection will be established.
Debug is the class file generated after compiling the source code.
In this method, we first launch the JVM using the following command so that JVM will start listening on port 54321.
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=54321 Debug
Then start jdb using the following command to connect to the JVM on port 54321.
jdb -attach 54321
This second method can be used to do remote debugging also. We will use remote debugging in the next article to exploit debuggable android applications. In this article we will use method 1 to cover both the flavors.
Beginning with Debugging:
Let’s start debugging the sample java application using method 1 by invoking JVM directly with JDB. But, to start JVM, we need to explicitly give the command “run” as shown in the following figure.
The above figure shows that JVM has been started and the program has finished executing and the application exited. To stop the flow and manually execute each line, we need to set break points before running the application.
We can set breakpoints at the beginning of a method using “stop in” command. We can do this as shown in the following figure.
We can see that we have set up a break point in method main which is in the class Debug. Type of parameters is also specified.
Let’s now start JVM and run the program to hit the break point. We use “run” command as shown earlier.
As expected, breakpoint is hit, and jdb is also showing the next line to be executed which is
System.out.println(“We are in main method”);
To look at the source code around the current location, we can execute the command “list” as shown below.
To see all the breakpoints set, we can use the command “clear” as shown below.
As we set, it is showing the place where we set the breakpoint.
To see all the thread groups, we can run the command threadgoups.
As we can see, there are two thread groups available “system” and “main”.
We can see all the threads, by running the command “theards” as shown below.
If you observe the above figure, we have three threads in system thread group and one thread with the name “main” in main thread group in running state. This is what we are going to debug in this article.
We can see the information about all the classes loaded in JVM using “classes” command as shown below.
The above figure shows all the classes loaded in JVM currently (the output is truncated to save space).
To see more information about a particular class, we can use the following command.
Below screenshot shows specific details of our current class “Debug”.
Similarly, we can see the information about any class. As an example let’s inspect java.io.DataInputStream. The output looks as shown below.
To see the list of methods loaded, we can use the command “methods <classname>” as shown below.
The above mentioned commands are few important commands that one may need. Now, let’s start looking at the flow of the program and see how jdb can help us in debugging the application.
To go execute the next line, which is System.out.println(“We are in main method”); we can type the command “next”.
As expected, the execution is completed and JDB is showing the next line to be executed, which is a call to the method “test”.
Here, if we give “next” command, it will finish executing the definition and control comes back to the next line, which is call to “passCheck” method in our case. This is shown in the figure below.
As explained, it has finished executing the definition of test method and now waiting to execute the next line passCheck(“srini0x00);
Now, if we want to get into the method definition and control the flow inside the definition, we should run the command “step” rather than “next”.
I have restarted my program and now, giving “step” command at the same line as shown in the figure.
Now, we can run “next” command to continue to the next line as shown below.
At this point, for some reason if we want to get out of the method definition, rather than executing the rest of the lines inside the definition, we can run the command “step up” as shown below.
As expected, it has exited from the definition and control is now in main method waiting to execute the next line.
The next couple of lines are going to be interesting as we are going to see the commands to view the secret messages stored in variables. Before going there, I would like to explain one more interesting command called “where”.
“where” command shows the current call stack. So, let’s run it inside main method and see the current call stack. Later let’s ruin the same command inside a method definition to understand its functionality.
As we could see, we are currently inside the method Debug.main.
Let’s now, give a “step” command and get into the method definition and check the current call stack inside the method. This is shown in the following figures.
The above figure shows the current location as “Debug.passCheck” and it is called from “Debug.main”.
Now, we are inside passCheck method. So, let’s see if there are any interesting local variables holding some sensitive information. We can see all the local variables using the command “locals”. (This doesn’t work if the code is not compiled using “-g” option).
As we can see, the call has sent a password to the method definition as an argument and it is displayed. The above figure is showing, only method arguments since they are not yet assigned to the local variable declared inside the definition yet. We can execute the next line and see the value of local variable “password”.
We can use print command to print the value of a specific variable as shown below.
This article has explained the basics of JDB, various JDB commands and how to apply them for debugging java applications. It has also provided the necessary information to understand the next article in the series.
Note: This article is originally written by me for Infosec Institute