Conditionals and jump instructions in x86
This article will briefly discuss conditionals and jump instructions. Conditionals are commonly used in assembly for comparison so that other instructions can make use of the output resulting from these. Jump instructions in assembly are a way to permanently transfer the execution to another instruction located at a different memory address.
Let us explore how all these concepts fit together in assembly, specifically x86.
Intro to x86 Disassembly
What are conditionals?
Making a decision based on the result of a comparison operation is possible in any programming language. Assembly is no different, and it is possible to make comparisons and make decisions based on the comparisons in assembly language too.
TEST and CMP are the instructions that are commonly used for comparison in x86, and these instructions are known as conditionals.
TEST instruction
The TEST instruction computes the bitwise logical AND of first operand and the second operand. According to the result, the status flags SF, ZF and PF will be set.
It should be noted that the TEST instruction doesn't make any changes to the operands used with the instruction. The following is an example of a TEST instruction.
Example: TEST EAX, EAX
CMP instruction
The CMP instruction is another example of conditionals. A CMP instruction performs a subtract operation on both operands, and the status flags ZF and CF will be set according to the result.
It should be noted that the CMP instruction also does not affect the operands. When the destination operand and source operand are equal, ZF will be set to 1. If the destination operand is less than the source operand, CF will be set to 1. In all the remaining conditions, the respective flags will be set to 0.
Following is an example of CMP instruction.
Example: CMP EAX, 1
Let’s see an example of both TEST and CMP instructions. The following code is an excerpt from the disassembly of a compiled C binary.
XOR EBX,EBX
CMP EAX,1
JE function.004013E3
MOV EAX,DWORD PTR DS:[4053E4]
TEST EAX,EAXThis excerpt has both CMP and TEST instructions. We can observe the results of these two instructions by setting a breakpoint on them.
Before executing CMP EAX, 1, the following is the status of the EAX register and the status flags:
C 0 ES 002B 32bit 0(FFFFFFFF)
P 1 CS 0023 32bit 0(FFFFFFFF)
A 0 SS 002B 32bit 0(FFFFFFFF)
Z 1 DS 002B 32bit 0(FFFFFFFF)
S 0 FS 0053 32bit 2D2000(FFF)
T 0 GS 002B 32bit 0(FFFFFFFF)
D 0
O 0 LastErr ERROR_FILE_NOT_FOUND (00000002)After stepping through the CMP instruction, the following is the status of the EAX register and the status flags.
C 1 ES 002B 32bit 0(FFFFFFFF)
P 1 CS 0023 32bit 0(FFFFFFFF)
A 1 SS 002B 32bit 0(FFFFFFFF)
Z 0 DS 002B 32bit 0(FFFFFFFF)
S 1 FS 0053 32bit 2D2000(FFF)
T 0 GS 002B 32bit 0(FFFFFFFF)
D 0
O 0 LastErr ERROR_FILE_NOT_FOUND (00000002)As we can observe in both the excerpts, there is no difference in the value of EAX before and after the execution of the CMP instruction. But the only change we see is in the status flag registers. Now, let’s do single steps until we hit the next breakpoint, where TEST instruction is located.
Before executing the TEST EAX, EAX instruction, the following is the status of the EAX register and the status flags.
C 1 ES 002B 32bit 0(FFFFFFFF)
P 1 CS 0023 32bit 0(FFFFFFFF)
A 1 SS 002B 32bit 0(FFFFFFFF)
Z 0 DS 002B 32bit 0(FFFFFFFF)
S 1 FS 0053 32bit 2D2000(FFF)
T 0 GS 002B 32bit 0(FFFFFFFF)
D 0
O 0 LastErr ERROR_FILE_NOT_FOUND (00000002)Let’s do a single step. The following is the status of the EAX register and the status flags after stepping through the TEST instruction.
C 0 ES 002B 32bit 0(FFFFFFFF)
P 1 CS 0023 32bit 0(FFFFFFFF)
A 0 SS 002B 32bit 0(FFFFFFFF)
Z 1 DS 002B 32bit 0(FFFFFFFF)
S 0 FS 0053 32bit 2D2000(FFF)
T 0 GS 002B 32bit 0(FFFFFFFF)
D 0
O 0 LastErr ERROR_FILE_NOT_FOUND (00000002)As we can observe in both the excerpts, once again there is no difference in the value of EAX before and after the execution of CMP instruction. But the only change we see is in the status flag SF.
Introduction to JUMP instructions
Jump instructions in assembly are used for branching, which describes the control flow of the program. There are two popular types of jump instructions: unconditional jump and conditional jump.
Unconditional jump
Unconditional jumps are the simplest form of jump instructions. As the name suggests, the execution will always flow to the target location specified. Following is the syntax of an unconditional jump instruction.
JMP <target location>
Conditional jump
Conditional jumps are used to take jumps based on the value of status flags. Conditional jumps are commonly used when concepts like IF statements and loops are needed to be used in Assembly. Because assembly language doesn't support statements like if statements, conditional jumps are used to determine whether to take a jump or not.
There are more than 30 different conditional jump instructions, but following are some commonly used ones:
JE — Jump if Equal; checks for ZF = 1
JNZ — Jump if Not Zero; checks for ZF = 0
JNE — Jump if Not Equal; checks for ZF = 0
JC — Jump if Carry; checks for CF = 1
JNC — Jump if No Carry; checks for CF = 0
JS — Jump if Signed (Negative); checks for SF = 1
JG — Jump if Greater
JNLE — Jump if Not Less or Equal
JGE — Jump if Greater or Equal
JNL — Jump if Not Less
JL — Jump if Less
JLE — Jump if Less or Equal
JNG — Jump if Not GreaterThe following example shows how jump instructions work.
MOV EBP,ESP
AND ESP,FFFFFFF0
SUB ESP,20
CALL if.004015F0
MOV DWORD PTR SS:[ESP+1C],1E
MOV DWORD PTR SS:[ESP+18],14
MOV EAX,DWORD PTR SS:[ESP+1C]
CMP EAX,DWORD PTR SS:[ESP+18]
JLE SHORT if.00401546
MOV DWORD PTR SS:[ESP],if.00404000 ; |ASCII "a is greater than b"
CALL <JMP.&msvcrt.puts>
JMP SHORT if.00401552
MOV DWORD PTR SS:[ESP],if.00404014 ; |ASCII "b is greater than a"
CALL <JMP.&msvcrt.puts> NOP
LEAVE
RETNThe preceding excerpt is a simple example of how both unconditional and conditional jumps work. In this example, JLE is a conditional jump instruction and JMP SHORT <TARGET> is an unconditional jump instruction. Let’s set breakpoints on both JLE and JMP instructions and understand how they work.
Conditional jumps usually depend on the results of other instructions like CMP. In this case, the following is the status of the status flags after the CMP instruction is executed.
P 1 CS 0023 32bit 0(FFFFFFFF)
A 0 SS 002B 32bit 0(FFFFFFFF)
Z 0 DS 002B 32bit 0(FFFFFFFF)
S 0 FS 0053 32bit 249000(FFF)
T 0 GS 002B 32bit 0(FFFFFFFF)
D 0
O 0 LastErr ERROR_SUCCESS (00000000)A JLE instruction usually checks for the flags O, S and Z. In our case, none of these flags are set by a CMP instruction. Thus, a conditional jump will not be taken and the next instruction will be executed.
When JMP instruction is executed, it will not look for any dependencies and the jump will always be taken to the target address specified in the JMP instruction.
Conclusion
In this article, we explored the concepts of conditionals and jump instructions. We used various examples with sample instructions to better understand these concepts.
We showed that conditionals are used for comparisons and jump instructions are used for changing the execution flow. It was also understood that unconditional jump instructions will always take jumps and conditional jump instructions rely on the output of other instructions such as CMP.
Intro to x86 Disassembly
Sources
- Randal Hyde, “The Art of Assembly Language,” No Starch Press, March 2010
- Michael Sikorski and Andrew Honig, “Practical Malware Analysis,” No Starch Press, February 2012
- Reverse Engineering for Beginners, Dennis Yurichev