Disassembling Code: IDA Pro and SoftICE

4.1. Basic Information about Working with SoftIce

This section covers the basic information required to start working with this powerful instrument.

4.1.1. Getting Started

Main SoftIce Window

When the SoftIce window pops up, all system functions are "frozen." So, to obtain a screenshot of the SoftIce window, I had to use two computers (Fig. 4.1).

image from book Figure 4.1: The SoftIce main window

The SoftIce main window appears in the following four cases:

The debugger window is shown in Fig. 4.1. The main window contains several windows that show different information. The number of such windows might vary. For instance, you, at your discretion, can add or remove these windows to or from the main window. The illustration demonstrates the most frequently used windows of the debugger. Note that you not only can view information in these windows but also can change their contents, such as the contents of the processor registers. However, this should be carried out carefully, because changing these registers might result in unpredictable behavior of the application or, perhaps, of the entire system. Thus, consider the debugger windows one by one, from top to bottom (see Fig 4.1):

When working in the main (command) window, it is possible to issue commands to control the debugger. Along with the commands issued in the main window, it is possible to use keyboard shortcuts. In addition, provision has been made for using the standard mouse and the context menu.

Also note the lowest window — the help pane. When you issue a command, the help pane can assist you correctly enter that command and its parameters. In particular, the window will list all commands, starting from the characters that you have already entered. Besides, the debugger always displays the current process in the top right corner of the window. Always pay attention to this important issue to avoid confusing applications. This topic will be covered in more detail in Section 4.1.3.

In addition to the preceding windows, it is possible to use the trace window, where the values of all variables listed in the WATCH command are traced, as well as other windows, including the MMX registers window and the local variables window.

Operating Modes of the Debugger

After installing the SoftIce debugger, you can choose from the following five methods of start-up:

4.1.2. The Loader

The main window of the loader32.exe program is shown in Fig. 4.2. As mentioned before, this program is intended for loading executable modules into the debugger. This utility also can retrieve debug information from the modules being debugged (provided that it is present there) and pass this information to SoftIce. When loading the module being debugged, the loader sets a breakpoint to the program entry point.

Figure 4.2: The loader32.exe program window

Loading the Executable Module

To load an executable module into the debugger, it is necessary to proceed as follows:

  1. Open the module using the File | Open... menu option. You can use the Open button on the toolbar for the same purpose.

  2. Then, choose the Module | Load menu item. It is also possible to use the Load Symbols toolbar button. The debugger would first translate the detected symbolic information into the file with the same name as that of the module being loaded and with the NMS file name extension, after which it would load the module, along with the debug information, into SoftIce. If the debugging information is missing, the loader would inform you about this and provide you with the choice whether or not to continue loading the module being investigated into the debugger. Translation of the debug information into the file with the NMS extension can be carried out by a separate command: either use the Module | Translate menu or click the Translate toolbar button.

The Loaded Symbols list contains the list of loaded modules. Pay special attention to the SYM= column. When loading the executable module, this column contains the size of the loaded symbolic information. Modules that do not contain such information are not displayed in the Loaded Symbols list.

Loading Parameters

After loading the module being investigated into SoftIce, it is possible to specify the start-up settings. To achieve this, use the Module | Settings... menu. The window, in which you can specify these settings is shown in Fig. 4.3. This window has four tabs, which must be considered in more detail.

Figure 4.3: The Settings window allows you to set the loading parameters for the modules to be debugged

4.1.3. Techniques of Working with SoftIce

Getting Started with Processes

Consider the main issues related to working with SoftIce:

Breakpoints

When investigating specific executable code, one task always consists of searching for the required location within the program being investigated. When program code written in one of the high-level programming languages is not available (which most often is the case, unless you are debugging your own program), breakpoints are indispensable in research.

Nonpermanent Breakpoints

Nonpermanent (one-shot) breakpoints operate only once. Actually, nonpermanent breakpoint is a line in the code window, to which the cursor (highlighted string) points. To move the cursor, use the U command. The HERE command (or the <F7> shortcut) runs the executable code from the current command to the line of code marked using this method. Bear in mind that the HERE command is issued from the code window; it is necessary to switch to this window by pressing <F6> before issuing the command. It is also possible to use the G address command, in which case the code will execute to the specified address.

Persistent Breakpoints

A typical example of a persistent (sticky) breakpoint is a breakpoint set to a specific command (a specific virtual address of the process). To set a persistent breakpoint, it is necessary to switch to the code window and use the BPX command without parameters. You can scroll the code and set breakpoints at required addresses. In this case, the lines of code, to which the breakpoints are set, will be highlighted. The same result can be achieved by using the <F9> shortcut. To remove the existing breakpoint, either move the cursor to the existing breakpoint and issue the BPX command, or press <F9>.

The general method of controlling breakpoints is applicable to the following breakpoints: BL to find the numbered list of breakpoints, BC n to delete the breakpoint with the specified number, BC * to delete all breakpoints, and BE n to edit the breakpoint with the specified number. Finally, if you know the address, at which you need to set a breakpoint, you can use that address in the BPX command, for example: BPX 0008:806CEFAB. If you issue the BPX command with the same address again, the breakpoint with the specified address will be deleted. Do not forget that the breakpoint set to the command address is related to a specific address space — in other words, to a specific process.

Conditional Breakpoints

Conditional breakpoints are activated if the conditions specified for them are satisfied. It is impossible to set two different breakpoints to the same address or to the same API function; however, you can use conditional constructs to take into account different variants of calling the same breakpoints. Here are typical examples of using conditional breakpoints.

Example 1

The breakpoint set to a specific address is activated only if the content of the EAX register takes the specified value:

: BPX 0008:806CEFAB if(EAX == 406090)

Example 2

Consider a small investigation for the breakpoint set to the MessageBox function call (the application under consideration is the WinRar archiving utility). Start the WinRar application, open the SoftIce window, and determine the application identifier using the ADDR command. The identifier turned out to be equal to 0x328. Then, issue the following command to create a conditional breakpoint:

: BPX MessageBoxA if(PID == 0x328)

Issue the BL command to make sure that the breakpoint is set as desired. Note that the A suffix has been specified. This suffix is mandatory, because SoftIce distinguishes API functions by their actual names.

Exit the debugger by pressing <F5>, and execute one of the application's commands that must cause the MessageBox window to appear. The SoftIce window will pop up immediately. The command window will display the message informing you why the SoftIce window has popped up. In this example, this message will appear as follows:

Break due to BP 00: USER32!MessageBoxA IF(PID = 0x328) (ET = 2.65 seconds)

Now, consider the code window. The first line of the entry into the MessageBox procedure will be highlighted there:

USER32!MessageBoxA 001B:77D56471 CMP DWORD PTR [77D8C3DO], 0

You can easily investigate the stack of the MessageBoxA function call and find the return address and parameter values. Having executed the ? * (ESP + 4) command, you'll obtain the value of the window handle for the window that initialized the MessageBox call (if something is unclear to you, return to Section 3.2.1, "Stack Structures"). The HWND value turns out to be equal to 100EC. View the list of windows opened by the WinRar application using the HWND 328 command, and you'll see that such a window exists and that it corresponds to the WinRarWindow class. By the way, in the same table you'll see the address of the window function of this window. Thus, it is possible to dive into studying the operation of this window. However, return to the first line of the MessageBox function call and find the return address. The ESP register points to the return address, Thus, by issuing the ? *(ESP) command, you'll discover that this address is equal to 43C76D.

There is another method of obtaining the return address. To use this method, press <F11>. After the MessageBox window appears, click one of the buttons in this window. The SoftIce window will appear, and you'll find yourself on the line that directly follows the MessageBox function call.

Note 

In general, searching for calls to individual API functions is not a trivial task. To succeed, you must know these functions well and understand that the same result can be achieved using different methods. For example, assume that you need to know where the window is created. At first glance, it seems natural to look for the CreateWindow function call. However, this is not so.

First, there is no such function as CreateWindow. Even if you call the CreateWindow function in your program, the CreateWindowEx function is always used.

Second, it is necessary to look for CreateWindowExA and CreateWindowExW functions instead of CreateWindowEx.

Finally, the window might be created by modal dialog functions, such as DialogBoxIndirect, DialogBoxParam, and DialogBoxIndirectParam. Or it might be created by nonmodal functions, such as CreateDialogParam, CreateDialogIndirect, and CreateDialogIndirectParam. Also, do not forget that for all functions it is necessary to take into account the A and W suffixes.

Example 3

Consider how the register contents can be traced:

: BPX EIP IF(EAX == 0x10)

The breakpoint specified by this command will be activated when the value of the EAX register becomes equal to 0x10, regardless of in which thread this event takes place.

Breakpoints to Windows Messages

As you know, the main events in GUI applications take place in window functions. Discovering the reaction of the window function to a specific message is the most important goal of program code investigation. Here, breakpoints set to Windows messages are indispensable. An example of the command that sets such a breakpoint is as follows:

: BMSG 100EC WM_CREATE

The first parameter of this command is the window handle, the function of which must receive the message. By the window handle value, the debugger determines the thread that has created this window, so the investigator doesn't need to care about solving this problem. When the required message arrives, the SoftIce debugger is activated and the code window will display the start of the window function. An interesting point is that the same result can be achieved using a standard BPX command, for example:

: BPX 43C76D IF((ESP -> 8) == WM_CREATE)

The first parameter is the address of the first command of the window function. Later, the command exploits the fact that the second parameter of the window function is located 8 bytes from the stack top.

Searching for a Window Procedure

How is it possible to locate a window procedure? Here are some helpful and easy tips:

Working with Applications that Contain Debug Information

SoftIce is a full-featured debugger, which means that it can load debug information and supply it with the executable code. Thus, when debugging custom applications, SoftIce can be used instead of the standard debugger built into the integrated development environment (IDE). Consider this approach on the example of debugging user applications written in C++ using Visual Studio .NET.

When the "add debug info" option is chosen (for this purpose, the best approach is using the DEBUG project configuration), the debug information database is created with the executable module. This database represents a file that by default has the same name as the executable module and the PDB file name extension (see Section 1.6.1). Information stored in that file is enough to represent the structure of the source program, along with the names of local and global variables, and to map this structure to the machine code.

When the loader32.exe program loads the executable module, it also loads the debug information and passes this to the debugger. By default, if the debug information is available for the program, SoftIce presents the program source code without Assembly commands in the source code window. Later, you can use the SRC command to switch to the mixed program representation (the source code of the program and machine code) or to pure machine code. In the first case, step-by-step program execution means that the program is executed one operator at a time. When using mixed representation, one step is equivalent to one machine command. Accordingly, it is possible to set breakpoints both to the operators of a high-level programming language and to machine commands. Listing 4.1 demonstrates several lines from the SoftIce code window when mixed representation was used.

Listing 4.1: Several lines from the Softlce code window when mixed representation was used

00006 a = 10; 001B:00411A2E MOV DWORD PTR [EBP - a], 0000000A 00007 b = 11; 001B:00411A35 MOV DWORD PTR [EBP - b], 0000000B 00008 c = 10; 001B:00411A3C MOV DWORD PTR [EBP - c], 0000000C 00009 printf("%d\n", max(a, b, c)); 001B:00411A43 MOV EAX, [EBP - c] 001B:00411A46 PUSH EAX ...

You should understand that in expressions such as [EBP - a] the a value is the address of the a variable in the stack (to be precise, the offset in relation to the address where the old EBP is stored — in other words, simply four).

Категории