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.
Summary
Assembly language is a low-level language that uses instructions to
communicate directly with the computer system. Logic used in computers is based on an on-and-off concept, from which binary 1s and 0s were derived. We have learned how to read and write binary from various number bases, and how to do arithmetic and bitwise computations.
We introduced popular assemblers and debuggers that we can use to build and validate our program. Then, we used FASM to code and build our Win32 low-level hello world program that uses APIs to communicate with the kernel. We validated our built executable program using x64dbg to debug it. Debugging our hello world program is a good start for us to get introduced to the world of reverse engineering.
Practice makes perfect. We have a listed a few suggested programs that can be developed using assembly language.
Knowing the lowest level of a code is a good start for our reverse engineering journey. As you finish up this book, assembly language will feel somewhat like a walk in the park.
Further reading
Intel's documentation contains the complete list of x86 instructions and describes the syntax and use of each instruction in assembly language. You can get these documents from http://www.intel.com/products/processor/manuals/.
Static and Dynamic Reversing 4
Like a patient in a hospital, a file needs to undergo some triage to determine the right allocation of resources. The result of the file assessment will tell us what tools need to be used, what kind of reversing steps need to be taken, and what resources will be used. The steps involved in carrying out reversing are categorized into static and dynamic analysis.
In this chapter, we will introduce the methods and tools used in assessing a file. We will be focusing on a 32-bit Windows operating system for our examples. This will be followed by an examination of tools we can use for static and dynamic analysis. This chapter can help you to generate a checklist that will serve as a guide for you to retrieve all information on a file in the least amount of time.
In this chapter, you will do the following:
Gain an understanding of Target assessment Perform static analysis
Perform dynamic analysis