Opcode | Description | CPU Flags |
---|---|---|
JA | Above | CF = 0 and ZF = 0 |
JAE | Above or equal | CF = 0 |
JB | Bellow | CF |
JBE | Bellow or equal | CF or ZF |
JC | Carry | CF |
JE | Equality | ZF |
JG | Greater(s) | ZF = 0 and SF = OF |
JGE | Greater of equal(s) | SF = OF |
JL | Less(s) | SF ≠ OF |
JLE | Less equal(s) | ZF or SF ≠ OF |
JNA | Not above | CF or ZF |
JNAE | Neither above nor equal | CF |
JNB | Not bellow | CF = 0 |
JNBE | Neither bellow nor equal | CF = 0 and ZF = 0 |
JNC | Not carry | CF = 0 |
JNE | Not equal | ZF = 0 |
JNG | Not greater | ZF or SF ≠ OF |
JNGE | Neither greater nor equal | SF ≠ OF |
JNL | Not less | SF = OF |
JNLE | Not less nor equal | ZF = 0 and SF = OF |
JNO | Not overflow | OF = 0 |
JNP | Not parity | PF = 0 |
JNS | Not negative | SF = 0 |
JNZ | Not zero | ZF = 0 |
JO | Overflow(s) | OF |
JP | Parity | PF |
JPE | Parity | PF |
JPO | Not parity | PF = 0 |
JS | Negative(s) | SF |
JZ | Null | ZF |
(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.
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_type | Jump(s) |
0 | JO |
1 | JNO |
2 | JB JC JNAE |
3 | JAE JNB JNC |
4 | JE JZ |
5 | JNE JNZ |
6 | JBE JNA |
7 | JA JNBE |
8 | JS |
9 | JNS |
A | JP JPE |
B | JNP JPO |
C | JL JNGE |
D | JGE JNL |
E | JLE JNG |
F | JG JNLE |
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.
Machine code is composed by an opcode and optionally some extra bytes. A conditional jump is composed by this way:
opcode
|
$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:
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:
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).
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.
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:
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
I made this little compiler to help you to undestand. It supports only jumps instructions and is only for 32-bits programs.
: EBBE
Just look again to previous table seen before:
$jump_type | Jump(s) |
0 | JO |
1 | JNO |
2 | JB JC JNAE |
3 | JAE JNB JNC |
4 | JE JZ |
5 | JNE JNZ |
6 | JBE JNA |
7 | JA JNBE |
8 | JS |
9 | JNS |
A | JP JPE |
B | JNP JPO |
C | JL JNGE |
D | JGE JNL |
E | JLE JNG |
F | JG 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.
Parity | Last bit |
---|---|
Even | 0 |
Odd | 1 |
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
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 jumps | Ctrl + F1 |
jump long | Ctrl + 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.
This document is quite long, then I've kindly decided to make a jump calculator to operate on conditional jumps directly using machine code.