Debugging

At certain points, our program may produce unpredictable errors or invalid output. In that case, we need to trace what went wrong, by debugging each line of code. But before that, there are some general debug commands we need to know.

Single-stepping a program means debugging per line of code. There are two modes to single step: step into and step over. During debugging, when the line being debugged is a CALL instruction, single-step debugging continues in the subroutine when a step into mode is used. The step over mode, however doesn't enter the subroutine, but rather lets the subroutine finish up running and the single step continues on the line after the CALL instruction. See the following comparison:

Step into Step over
    CALL 00401000 ; <-- STEP INTO SUBROUTINE
MOV EBX, EAX
...
00401000:
MOV EAX, 37173 ; <- DEBUG POINTER GOES HERE
RET
    CALL 00401000 ; <-- STEP OVER SUBROUTINE
MOV EBX, EAX ; <- DEBUG POINTER GOES HERE
...
00401000:
MOV EAX, 37173
RET

 

A run or continue makes the debugger execute instructions continuously until the program terminates, encounters an error, or until it encounters a manually set breakpoint.

Placing a breakpoint is a way to enable to the debugger to interrupt a code that was set to freely run. For example, if I placed a breakpoint at address 0040200A in the following code, and let the debugger automatically run every instruction starting from 00402000, the debugger stops at address 0040200A and leaves the user to continue doing single steps or run:

00402000  push 0040100D
00402005 push 0040100D
0040200A call dword ptr [printf] ; <-- breakpoint set here
00402010 push 0
00402012 call dword ptr [ExitProcess]

Let's debug our Hello World program.

Download x64dbg from https://x64dbg.com/.

It is a ZIP archive that you will have to extract. And once extracted, open the x96dbg.exe from the release folder. This will show the launcher dialog where you get to select x32dbg (for 32-bit debugging) and x64dbg (for 64-bit debugging) as your debugger:

The Hello World program we developed is a 32-bit program, thus, select x32dbg. Then click on File->Open, then browse and open the helloworld.exe program. Opening it will show you where the EIP is at in the disassembly window as follows:

At the bottom of the window, it says: "System breakpoint reached!" EIP is at a high-memory region address and the window title also indicates "Module: ntdll.dll - Thread: Main Thread." All of this suggests that we are not yet in the helloworld program, but rather still in the ntdll.dll code that loads up the helloworld program to memory, initializes it and then starts to run it. If you go to Options->Preferences, and in the Events table of the Settings window, by default, the System Breakpoint* is checked. This causes the debugger to pause in the ntdll.dll before we even reach our helloworld code. Uncheck the System Breakpoint*, click on Save, then exit the debugger, as shown here:

Now that we have removed the System Breakpoint, repeat loading the helloworld program. The EIP should now be in the helloworld code:

Click on the Debug menu. You should see that there are keyboard keys assigned to Step into, Step over, Run and more debugging options:

The stack frame window is located at the lower right pane. Take note of the information there, then press F7 or F8 to do a single step. The PUSH helloworld.401000 instruction just placed the address of "Hello World" text string at the top of the stack. At the upper right pane where the registers and flags are, all changes have their text colored red. With the stack moving its address, ESP should change. And since we are now on the next line of instruction code, EIP should have also changed.

Do another single step to push the address of "%s" to the stack. You should now be in address 0040200A. At this point, doing a step over will execute the printf function and be at address 00402010. Out of curiosity, let's do a step into instead. This leads us in the msvcrt library, where the printf function is:

To get back to our helloworld program, we can do a "Run to user code," which has a mapped key of Alt + F9 or an "Execute till return" Ctrl + F9. The user code pertains to our hello world program. Doing a "Run to user code" will bring us to address 00402010, which is the instruction after the printf call. Doing an "Execute till return" will bring us to the address where the RET instruction is. Let's do an "Execute till return" instead:

Now take a look at the stack. As discussed previously about the CALL-RET instructions, a CALL stores the address of the next instruction at the top of the stack. At this point, the address stored at the top of the stack is 00402010. Make a single step and we should be back in our hello world program.

Just continue doing step overs. The last two instructions should terminate the program and the debugging will stop.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.236.174