Choose IDA Debugger#
IDA supports multiple debuggers.
Supported debuggers in IDA
Select the Local Windows debugger.
Open the menu bar Debugger - Debugger Options to set some debugger features.
IDA debugger options
Debugger Interface Features#
Check Pause at process entry point
, select event conditions, and click OK.
Rename and change the color of the previous key code jinx.
After modification, press the X key to see where this function is referenced:
Color the reference points.
Set a breakpoint at 0x00401243, hover the mouse over this line, right-click - Add Breakpoint, and it will change to a red background after adding the breakpoint.
Start debugging through the menu bar Debugger - Start Process (F9)
. If debugging an executable locally, the following warning window will pop up.
Note:
When analyzing the loader program, the program will not execute locally, but debugging is different. If the program is a virus or other dangerous malware, extra caution is needed. In this case, a remote debugger should be used to execute the program in a virtual machine.
Click Yes
to continue debugging.
Since the debugger is set to pause at the entry point, as shown in the figure below, IDA pauses at the entry point (0x00401000).
Adjust the general register and flag register windows in the upper right corner for viewing.
In the above figure, the values of these registers can be seen. After adjusting normally, open the menu bar Window - Save Desktop
and check the default
option to save as the debugger's default window settings. Then, when running the debugger, the program will run according to the default settings, which can also be modified if needed.
A stack view can be placed below the general registers.
The left side and the bottom are the disassembly and hexadecimal windows.
Below the disassembly view is the current memory address and file offset, which refers to the executable file offset.
In IDA, the default G key is a shortcut to jump to a memory address. Press G and input 0x401389 to jump to the previously set breakpoint.
All static analysis, renaming, and other changes will be saved.
Open the menu bar View - Open Subview - Segments, and find that the loader has loaded 3 segments.
Loaded the code field at 0x401000, as well as the data and .idata fields below. Modifications to these segments will be saved, while modifications to other segments will not be saved because they are segments loaded by the debugger and will not be saved to the IDA database.
The L flag in the above figure indicates that this program is loaded by both the loader and the debugger, allowing for static analysis while not losing static analysis information during dynamic debugging.
Below are some small toolbars.
- Menu bar View - Toolbars - Jump, and then use the save desktop feature to set it as default enabled.
Press the back key to return to the previous program entry point.
In the menu bar Debugger - Breakpoints - Breakpoint List, you can view all program breakpoints.
Clicking anywhere will jump to the corresponding breakpoint.
Conditional Jump Instructions and Flag Registers#
The debugger is currently at the program entry point and has set two breakpoints. Press F9 to continue running.
At this point, the crackme.exe program is running, and the target program Help - Register menu bar prompts for a username and password.
Click OK.
In the above figure, the flashing red arrow on the left indicates the path the program continues to execute. At this time, the value of eax is 0x6c.
0x6c converted to a string is l.
In the above figure, the al register is compared with 0x41 to determine if it is less than 0x41 (A). The green code block below also compares the al register with 0x5a.
asm | condition | operation |
---|---|---|
JA | z=0 and c=0 | jump if above |
JAE | c=0 | jump if above or equal |
JB | c=1 | jump if below |
JBE | z=1 or c=1 | jump if below or equal |
JC | c=1 | jump if carry |
JECXZ | ecx=0 | jump if ecx is 0 |
JE | z=1 | jump if equal |
JZ | z=1 | jump if zero |
JNE | z=0 | jump if not equal |
JNZ | z=0 | jump if not zero |
JO | overflow | jump if overflow |
JP | even parity | jump if parity |
JPE | even parity check | jump if parity even |
JNP | not even parity | jump if not parity |
JPO | odd parity | jump if parity odd |
JS | sign bit is 1 | jump if sign |
JNS | sign bit is 0 | jump if not sign |
JL/JNGE | sign bit same as overflow bit | jump if less or not greater/equal |
JLE/JNG | z=1 or sign bit same as overflow bit | jump if less or equal/not greater |
JG/JNLE | z=0 and sign bit same as overflow bit | jump if greater/not less or equal |
Conditional jump instructions and jump conditions
Next, switch to the IDA flag register view, as shown below.
According to the table, if CF=0, the program will not jump and will execute towards the green code block. So what mathematical condition triggers this C flag?
The C flag indicates that there was an error in unsigned integer operations. If we consider the cmp instruction as a subtraction that does not save the result, then 0x6c - 0x41 = 0x2b
, the result is positive. If al is 0x30, then 0x30 - 0x41 = -0x11
.
-0x11 is a negative number, and the program can only continue to run with its corresponding hexadecimal representation.
As shown in the figure, the hexadecimal of -0x11 is 0xffffffef, and in decimal, it is 4294967279, which is a large value, and 0x30-0x41
will not be positive.
So how do we know if the operation considers the sign? This depends on the type of jump used. JB is used for jumps after unsigned number comparisons, while the corresponding signed number instruction is JL.
When using the JB instruction, it is typically used to check if an unsigned integer is less than a certain value. If the operation results in a borrow, it indicates that the second operand (the number being compared) is larger than the first operand (the comparison number). At this point, the JB flag is set to 1, allowing the jump. Otherwise, if the operation does not produce a borrow, the JB flag is 0, meaning the condition is not met, and no jump occurs.
Example:
mov al, 150 ; Assign 150 to al
cmp al, 255 ; Compare al and 255
jb smaller ; If al is less than 255, jump to smaller label
; Execute other operations
smaller:
; If al is less than or equal to 255, it will jump here
In this example, if the value in al is less than 255, the CF flag will be set to 1, and it will jump to the smaller label. Otherwise, if the value in al is greater than 255, the CF flag will be 0, and it will not jump, executing other operations. (In summary, if al is less than 255, then jump)
To determine if the comparison is signed, it needs to be confirmed by the subsequent conditional jump instruction.
Unsigned jumps
Symbol | Description | Flag |
---|---|---|
JE/JZ | Jumps if equal or zero | zf |
JNE/JNZ | Jumps if not equal or zero | zf |
JA/JNBE | Jumps if above or not below or equal | zf,cf |
JB/JNAE | Jumps if below or not above or equal | cf |
JBE/JNA | Jumps if below or equal or not above | cf,af |
Unsigned jumps
The instructions in the above figure do not consider the sign, and each jump has a corresponding signed jump.
Symbol | Description | Flag |
---|---|---|
JE/JZ | Jumps if equal or zero | zf |
JNE/JNZ | Jumps if not equal or zero | zf |
JG/JNLE | Jumps if greater or not less or equal | zf,sf,of |
JGE/JNL | Jumps if greater or not equal or less | sf,of |
JL/JNGE | Jumps if less or not greater or equal | sf,of |
JLE/JNG | Jumps if less or equal or not greater | zf,sf,of |
Signed jumps
In the above two figures, JE (equal)
appears in both figures because in this special case, the sign does not matter. If the two numbers are equal, zf=1
, indicating that the flag is triggered. In signed jumps, JG = Jumps if greater
, while in unsigned jumps, the corresponding instruction is JA, Jumps if above
.
Continuing to debug the program, IDA will pause at each set breakpoint. A loop is executed, where each loop reads a character from the username input in the target program and compares it with 0x41. If any character is less than 0x41, an error will be reported. Since the input is lca, each value is greater than 0x41, so no error is displayed.
Try entering a number to see.
At this point, it jumps to the red block, which is where the jb jump occurs, because the first character being compared is 2 (0x32), which is definitely less than 0x41.
The C flag is also triggered, CF=1, because 0x32 - 0x41 is a negative result when subtracting unsigned numbers, which will trigger the C flag.
Right-click on the C flag and select Zero Value to clear the C flag to 0.
Press F9 to continue the process. The program reads the second character of 22lca, and the left red arrow starts flashing again. Continue to set the CF flag to 0, and the subsequent characters lca will all be greater than 0x41, not triggering the flag, and the program will follow the left red arrow.
After character detection is complete, the program reaches the last jump.
In the above figure, the cmp instruction compares eax and ebx, and jz checks if they are equal, which is unsigned. The program moves towards the red block because these two registers are not equal.
Since they are not equal, the zf flag is not triggered.
If the zf flag is manually triggered, changing the jump direction, the program will turn towards the green block, indicating successful registration.
SET IP#
Set IP is only available in debugger mode.
Sometimes you can also modify the jump indirectly by moving the mouse to the code block you want to execute, right-clicking, and selecting SET EIP.
At 0x40124c, set eip, and the program will start running from 0x40124c.
Note: In version 7.7, doing this multiple times results in the following error:
It prompts "Attempt to execute an illegal instruction."
The error message indicates that the memory is not writable, possibly because the read content exceeds the range.
Summary#
By using the IDA debugger, dynamic debugging and static analysis can be performed to gain an in-depth understanding of the program's structure and execution process. However, this chapter does not modify the source program; it only changes the values of the flag registers in the debugger.