Back to homepage

8086 conditional jump opcode structure

I. Type of conditional jumps

a. List of existing jumps

OpcodeDescriptionCPU Flags
JAAboveCF = 0 and ZF = 0
JAEAbove or equalCF = 0
JBBellowCF
JBEBellow or equalCF or ZF
JCCarryCF
JEEqualityZF
JGGreater(s)ZF = 0 and SF = OF
JGEGreater of equal(s)SF = OF
JLLess(s)SF ≠ OF
JLELess equal(s)ZF or SF ≠ OF
JNANot aboveCF or ZF
JNAENeither above nor equalCF
JNBNot bellowCF = 0
JNBENeither bellow nor equalCF = 0 and ZF = 0
JNCNot carryCF = 0
JNENot equalZF = 0
JNGNot greaterZF or SF ≠ OF
JNGENeither greater nor equalSF ≠ OF
JNLNot lessSF = OF
JNLENot less nor equalZF = 0 and SF = OF
JNONot overflowOF = 0
JNPNot parityPF = 0
JNSNot negativeSF = 0
JNZNot zeroZF = 0
JOOverflow(s)OF
JPParityPF
JPEParityPF
JPONot parityPF = 0
JSNegative(s)SF
JZNullZF

(s) signed mode

Two jumps are not included in previous list: jumps JCXZ and JECXZ wich are not dependent on CPU's FLAG but are on register CX (16 bits) or ECX (32 bits).

Unconditional jump instruction is JMP.

b. Equivalence class

First, looking in array above, we see that several jumps have same flag conditions and would get same opcode.

That's why a JE instruction is equivalent to a JZ. There are also single jumps:

If you count enumerated jumps, you get 16 different cases. The number 16 is 24 or one hexadecimal digit or also a half of a byte.
Intel have then allocated 4 bits (a nibble) in the opcode to define jump type.

This value, which identifies the jump type, is part of opcode. This value is be noted $jump_type

$jump_typeJump(s)
0JO
1JNO
2JB JC JNAE
3JAE JNB JNC
4JE JZ
5JNE JNZ
6JBE JNA
7JA JNBE
8JS
9JNS
AJP JPE
BJNP JPO
CJL JNGE
DJGE JNL
EJLE JNG
FJG JNLE

II. Instruction coding

a. Destination of jump

Programmer uses defined labels (instruction blocks, line number, named label, etc.) but machine code uses memory addresses. The linker's job is to compute and insert these addresses after compilation step to make machine code runnable.

Conditionnal jumps use relative addresses, jumps are done from current instruction and distances defined in bytes. These relative address value is noted $bytes_count

Important note: address is computed from the end of instruction.

Actually, a fulfilled conditional jump is similar to "add eip, $bytes_count + instruction_size"

$bytes_count can be positive or negative, processor is able to jump backward or forward. Value can be stored on one byte or one word (2 ou 4 bytes).
The linker does this choice during building: if $bytes_count is too big to be stored on one byte, then linker uses a long jump.

b. Conditionnal jump

Machine code is composed by an opcode and optionally some extra bytes. A conditional jump is composed by this way:

opcode
$jump_type
$bytes_count

The machine code of a short conditional jump is 7x yy; for a long conditional jump the opcode is 0F 8x yy yy yy yy.

Let's see a exemple (using w32dasm):

:00401000 7410 je 00401012 [...] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401000(C) | :00401012 40 push eax

Machine code is 7410:

  1. 7 -> first opcode nibble (short jump)
    $jump_type = 4 -> JE / JZ (w32dasm prints JE)
  2. $bytes_count = 10 -> 16 bytes

Program jumps from 00401000 to 00401012:
We have eip = 00401000
we add instruction size (2 bytes) + $bytes_count (value is 0x10): 2 + 0x10 = 0x12
After jump instruction, we got: eip = 00401012

When it's a long jump, $bytes_count is stored on 2 or 4 bytes contingent on program type :

Contrary to Motorola, Intel reverses the byte order for its instruction coding. This way of ordering is named little endian.

Let's see a long jump example:

:00401000 0F8500010000 jne 00401106 [...] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401000(C) | :00401106 40 push eax

Machine code is 0F8500100000:

  1. 0F8 -> first opcode part (long jump)
    $jump_type = 5 -> JNE / JNZ
  2. $bytes_count is '00010000'

To get the value of $bytes_count, we reverse each bytes (each couple of hex digit):
00010000 is 00 01 00 00 and becomes 00 00 01 00.
$bytes_count = 0x100

The jump goes 0x106 bytes forward in memory ($bytes_count + instruction's size).

c. How the linker select the long or the short jump

To konw from how many bytes, the linker has to use a long jump (instead a short one), we must know what is the maximal move of a short jump.

Instruction's size is always 2 bytes (in 16 or 32 bits program) and $bytes_count is stored on one byte (8 bits).
$bytes_count is a signed number, then we have the sign bit and the 7 other bits are used for the value.

27 = 128, $bytes_count would go from -128 to +127 (0 is a positive value here).
If we add the instruction's size, the real range is from -126 to 129.

This is then the limits from where linker would change jump type: [-126; 129]

d. Unconditional jumps

With JMP instruction, we have many more possibilities :
  1. relative jump, short an long jumps like conditional jumps
  2. absolute address jumps
  3. jump regarding a register value
  4. jump regarding a memory value
  5. ...

Let's talk about most common and interesting jumps, the short and long relative jump:
These jumps work like conditionnal jumps, only the opcode header changes. For short jump, the header is EB, for long jumps opcode is E9.

A short jump example:

:00401000 EB10 jmp 00401012 [...] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401000(C) | :00401012 40 push eax

A 16 bits program long jump:

:00401000 E90001 jmp 00401103 [...] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401000(C) | :00401103 40 push eax

A 32 bits program long jump:

:00401000 E900010000 jmp 00401105 [...] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401000(C) | :00401105 40 push eax

Changing a short conditional jump to unconditional is easy: just replace first byte 7x with EB

If you are handling a long conditional jump, it's a bit more complicated:

  1. the two opcodes do not have the same size
  2. remember: $bytes_count is computed to end of instruction

In this case, we overwrite with a NOP instruction the first byte opcode of conditional long jump and replace the second opcode byte of instruction with the long JMP opcode.
The long conditional jump becomes two instructions: a NOP and a JMP.

NOP is a specific instruction used for padding, it does nothing and cost only one bytes in memory and one CPU cycle. Opcode of a NOP is 90.

As example, if I want to force this conditional JNS to always jump

:00401000 0F8900010000 jns 00401106

I will propose as the most efficient modification this one

:00401000 90 nop :00401001 E900010000 jmp 00401106

III. Online jump compiler

I made this little compiler to help you to undestand. It supports only jumps instructions and is only for 32-bits programs.

: EBBE

IV. Advanced study of machine code

a. How transform machine code

Just look again to previous table seen before:

$jump_typeJump(s)
0JO
1JNO
2JB JC JNAE
3JAE JNB JNC
4JE JZ
5JNE JNZ
6JBE JNA
7JA JNBE
8JS
9JNS
AJP JPE
BJNP JPO
CJL JNGE
DJGE JNL
EJLE JNG
FJG JNLE

If you look carefully, Intel ingineers have ordered very well the conditional jump list:
They have attributed a $jump_type value to one jump condition and, have reserved the the next value to opposite jump condition.

You can check with the first table of this document (using CPU flags) that:

Regarding this, it's not so difficult to find an algorithm to reverse a conditional jump:

Binary is base-2 system, so the parity of a number (which is a modulo 2 operation) results only from last digit in binary, the last bit.

ParityLast bit
Even0
Odd1

Previous algorithm is like if you was working only of last bit:

Then, changing the last bit of $jump_type inverts the jump condition.

Using XOR operator (⊕) with the last bit mask (0x01) does the job.

A example:

:00401000 0F8400010000 je 00401106 ^

$jump_type is 4
4 ⊕ 1 = 5

:00401000 0F8500010000 jne 00401106

b. Build a soft-ice macro

The macro has to reverse a conditional jump: it reads a byte in the process memory, xor it and write back the modified value in memory.

Memory address will be computed using eip register. This macro has to be used when process is stopped on a conditional jump.

Two macros have to be defined to work on short jumps and long jumps:

short jumpsCtrl + F1
jump longCtrl + F2

To write in memory, we use the instruction "f" (fill memory) which has these paremeters:
f (address) l (size) (data)

For example, write "hello world!" on the stack is:
f esp l 8 "hello world!"

To access to memory address, operator '*' is used:
*(address)

The eip pointed data is *eip. We can cast it in one byte doing this:
byte(*eip)

Xor instruction is done with operator '^', to write modified value with xor, we got:
f eip l 1 byte(*eip)^1

Adding the character ^ before instruction makes a transparent instruction.

Now you can add this line to your file winice.dat, this line for first macro:
CF1="^f eip l 1 byte(*eip)^1"

For a long jump, offset of $jump_type is not eip but eip+1:
CF2="^f eip+1 l 1 byte(*(eip+1))^1"

Do not forget to restart service to make these macros work.

V. Online jump opcode calculator

This document is quite long, then I've kindly decided to make a jump calculator to operate on conditional jumps directly using machine code.

contact/mail protection