Skip to main content

ASM x86 – Jumps & Conditions

· 7 min read
Strider

Hi, in the third part of my x86 assembler series, I want to talk about jumps and conditions.

We know from the last post, what the most important arithmetic commands are. How an assembler program is generally structured and how to compile an assembler program.

In assembler we can not only calculate but also compare things similar to C or C++ and act accordingly. Comparisons are also connected with jumps in x86 assembler. But there is also the simple jump. This is simply realized with the command jmp.

A simple jump looks like this.

;Simple jump
jmp label ;Jump to label

As you can see, we have a small assembler program which has implemented the simple jump command. The command has as said an operand. Here it is the label. It jumps directly to the given position without any detours. Jumps can be linked with conditions to build more complex structures.

With jumps and conditions, we can build IF-ELSE structures as in other higher programming languages.

;Simple If-Equal
section .text

global _start

_foo:
mov eax, 0x0

_start:
nop
mov eax, 0xdeadbeef
cmp eax, 0xdeadbeef
je _foo
nop
nop

Here we have a few new commands that require a bit of explanation. The first unknown command is nop for "No Operation" and does exactly as the name says, nothing. This command simply tells the CPU "Move to the next" so just keep moving and do nothing.

What we do when we start the program is set the register EAX to 0xdeadbeef. And just after this instruction, we learn another unknown instruction. This instruction is the simple comparison operator. It compares the value from the register EAX with the fixed constant 0xdeadbeef. The 2nd operand can also be a register instead of a fixed value.

This operator is comparatively seen in C or C++ like this:
"eax == 0xdeadbeef".

The cmp command sets the registers correspondingly, with which the conditions can work later. The conditions work with the flags which are set by the cmp command. Each condition has as operand the indication where to jump to. This can be defined by jump marks or addresses.

I said in the last posts that the instruction pointer EIP is not writable by us. Here I must say a little bit I have fibbed 😅, since we can change only with jump commands the value of the pointer.

Here in the example we see that a jump to the program section _foo is executed when the truth is true. After this program section has been processed, the program is not terminated, but continues to run in the program section where we started. Here a problem can arise quickly, and that is that we end up in an infinite loop. For this I have made a small recording.

asciicast

What does the cmp command do in detail? The command internally makes a subtraction of both operands. If the result is 0, the zero flag is set. It is also possible to link our jump with a condition, e.g. by negating the je command.

;Simple If-Not-Equal
section .text

global _start

_foo:
mov eax, 0x0

_start:
nop
mov eax, 0xdeadbeef
cmp eax, 0xdeadbeef
jne _foo
nop
nop

Here in the example, we see that we have replaced the command je with jne. je stands for "Jump if equal" and jne for "Jump if not equal". Here we only jump if the zeroflag is not set to 0. This condition is not fulfilled here, which is why no jump is made in this example.

But there are more conditions for the jumps. I have packed these as a small table. The principle is the same as the examples just shown.

note

The indicated operands are used with the cmp command, these serve here, only for the simple understanding. The operand "label" represents the jump label you specified.

CommandOperandsFlagsDescription
ja labelop1, op2Carryflag = 0 Zeroflag = 0Jump if above "If op1 is greater than op2, then...", op1 >\gt op2 \rightarrow 3 >\gt 2
jae labelop1, op2Carryflag = 0Jump if above or equal "If op1 is greater than or equal to op2, then...", op1 \geq op2 \rightarrow 2 \geq 2
jb labelop1, op2Carryflag = 1Jump if below "If op1 is less than op2, then...", op1 <\lt op2 \rightarrow 2 <\lt 3
jbe labelop1, op2Carryflag = 1 Zeroflag = 1Jump if below or equal "If op1 is less than or equal to op2, then...", op1 \leq op2 \rightarrow 2 \leq 2
je labelop1, op2Zeroflag = 1Jump if equal "If op1 equals op2, then...", op1 == op2 \rightarrow 1 == 1
jg labelop1, op2Zeroflag = 0
Signflag = Overflowflag
Jump if greater (Signed values) If op1 is greater than op2, then...", op1 >\gt op2 \rightarrow 3 >\gt -1
jge labelop1, op2Signflag = OverflowflagJump if greater or equal "If op1 is greater than or equal to op2, then...", op1 \geq op2 \rightarrow 2 \geq 2
jge labelop1, op2Zeroflag = 0
Signflag \neq Overflowflag
Jump if less "If op1 is less than op2, then...", op1 <\lt op2 \rightarrow -1 <\lt 2
jle labelop1, op2Zeroflag = 1
Signflag \neq Overflowflag
Jump if less or equal "If op1 is less than or equal to op2, then...", op1 \leq op2 \rightarrow 2 \leq 2
jne labelop1, op2Zeroflag = 0Jump if not equal "If op1 is not equal to op2, then...", op1 \neq op2 \rightarrow 2 \neq 1

There are also here, with the whole Jumpconditions, still many other variants, but these here in the upper table, are pretty much the most important conditions. The other somewhat more special commands I have packed here again separately in a table. These commands have no additional operands, because they can work without the command cmp.

CommandOperandsFlagsDescription
jc label-Carryflag = 1Jump if carry is set
jnc label-Carryflag = 0Jump if carry is not set
jo label-Overflowflag = 1Jump if overflow is set
jno label-Overflowflag = 0Jump if overflow is not set
jp label-Parityflag = 1Jump if parity is set
jnp label-Parityflag = 0Jump if parity is not set
jnp label-Signflag = 1Jump if sign is set
jns label-Signflag = 0Jump if sign is not set
jz label-Zeroflag = 1Jump if zero is set
jnz label-Zeroflag = 0Jump if zero is not set

The jumps and conditions form the basis for loops. From simple counting loops, up to more complex time loops, e.g. to create delays, if the program should do something only every 5 seconds.

A counting loop looks like this:

;Simple counting loop
section .text

global _start

_start:
mov eax, 0x0

_foo:
inc eax
cmp eax, 0x0a
jne _foo

_end:
xor eax, eax

;---------------------------
for(int i = 0; i < 10; i++)
{ //do staff }

What we achieve with this is that we build a simple For loop like in C. Here we also get to know a new command, which increments the EAX register by 1. This command is the inc command. This simply increments. But there is also the "dec"-command, which decrements the register by 1 instead of incrementing.

After 10 passes our EAX register is cleared again.

A small recording in GDB shows how the loop works.

asciicast

I hope you enjoyed it and see you at the next post 😄