3

I'm trying to skip exactly 1 instruction without using labels, an example with labels would be:

cmp r12, r13
je dest ; skip the jmp
jmp whatever
dest:
nop

However, my limitation is that I can't use labels, so I assume I must create a jump relative to the RIP register. For example (pseudo):

cmp r12, r13
je rip+0x05 ; this would obviously depend on the length of the next instruction
jmp whatever
nop

However, I lack knowledge to produce anything working, also as far as I know, it's not possible to read/write the RIP register without hacks.

EDIT: I'm only familiar with Intel syntax and I use Keystone as assembler. I will take the bytes from the assembly and load it into an executable memory location. I'm using my own website to get the bytes from the assembly, you might get the idea if you look at it.

EDIT 2: I tried Jesters and Margaret Blooms comments, which suggest to use:

je .+0x05
; or
je $+0x05

However, I can confirm that both don't work with Keystone! Luckily, I noticed that Keystone is able to process this code:

je +0x05

Is anyone able to confirm that this works?

EDIT 3: I tried it out with NASM and the $ prefix works fine with my tested code. I used this to test it:

section .text       
global _WinMain@16       

_WinMain@16:
    jmp $+2+2 ; skip this jmp and the next jmp (each 2 bytes)
    jmp $
    ret 16

It works as expected. Also defuse produces the same output as Keystone. The only difference is that defuse uses the $ prefix and Keystone doesn't need one at all! Keystone equivalent is:

jmp +4
jmp .
ret 16
ioncodes
  • 104
  • 3
  • 9
  • 1
    `je .+5` or whatever syntax your assembler (which you didn't specify) supports for the current address. Also, what's wrong with labels? – Jester Dec 12 '17 at 14:55
  • 3
    If you know the length of the next instruction this can be done the way Jester says. However if you don't know the length of the next instruction you'll have no chance! – Martin Rosenau Dec 12 '17 at 14:57
  • @Jester, the assembler won't assemble that. – ioncodes Dec 12 '17 at 15:01
  • @MartinRosenau, I'm preprocessing assembly of a different architecture. So I definitely can get the length after initial processing. – ioncodes Dec 12 '17 at 15:02
  • 2
    Branches are relative, you don't need the RIP-relative addressing mode. But you must know the instruction length unless you can afford to make a [nop sled](https://en.wikipedia.org/wiki/NOP_slide) of length 15 and always jump 2 + 15 = 17 bytes ahead. – Margaret Bloom Dec 12 '17 at 15:05
  • @MargaretBloom, I already thought about nop sledding it, but it might be better if I just implement it properly. The structure of the project allows me to get the length after initial processing, so I will definitely do it. – ioncodes Dec 12 '17 at 15:08
  • I just noticed something thanks to @Jester! I updated my question. – ioncodes Dec 12 '17 at 15:11
  • Note that Jester also included a dot before the plus. Some assemblers use a dollar ($) instead. – Margaret Bloom Dec 12 '17 at 15:12
  • @MargaretBloom, I just edited my question with the result. – ioncodes Dec 12 '17 at 15:16
  • Examine the generated machine code, you should be able to verify whether it works. – Jester Dec 12 '17 at 15:22
  • @Jester, As far as I know, 0x74 is JE and other byte seems correct. I just quickly tried it with NASM and it works! Can you create an answer, so this can be marked as answered! – ioncodes Dec 12 '17 at 16:08

1 Answers1

5

Note that in NASM and MASM $ isn't a prefix. It's a stand-alone keyword / symbol which refers to the address of the current line.

je $+5 won't work: jmp rel32 is 5 bytes, and the short JCC is 2. So you need je $+7 to skip a 5-byte instruction, or $+4 to skip a 2-byte instruction.

The rel8 displacement will be 0x05 or 0x02, because jumps encode their displacement from the end of the instruction. But NASM's $ label gives the address of the start of the instruction, and the assembler always calculates the relative displacement for you, based on the target address. So with $+x, you need to include the length of the current instruction.

If you want to encode the rel8 yourself, you could use db pseudo-instructions to emit the bytes you want. e.g. db 0xEB, 0x02


Your entire approach / goal of not using any labels is fundamentally flawed unless you have a reliable way to know how long the next instruction is. (e.g. by using jmp NEAR or jmp SHORT to force a long or short encoding). All you can do is encode a jump that goes forward a fixed number of bytes, not one that skips one instruction, regardless of width.

But seriously just use a label; x86 jumps are always relative anyway, so let your assembler make life easier for you and calculate the correct relative displacement.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Thanks for the answer! I do know that $ is a symbol, but I couldn‘t find a better name at that time. I have a reliable way to calculate the length of the next instruction. – ioncodes Dec 13 '17 at 07:19