Branching to Absolute Addresses

The x86-64 architecture is very annoying sometimes. Especially when you want to hook functions or do some magical assembly tricks :)

Suppose you want to hook a function, you will have to change its first instruction to a jump instruction to your code, so the next time the function is being called, the control will be transferred to your stub. Everybody knows this technique, basically you will need 5 bytes, for the opcode E9 (JMP relative) and another DWORD for the relative offset itself. Then you are able to jump backward or forward by 2GB. Sometimes, it’s not enough, just because you have some limitations.
Now the problem with the processor is that there’s no instruction that can branch to an absolute address which is given straight to the instruction as an immediate operand. Try to think of a MOV EAX, 0x12345678,
but instead, JMPABS 0x12345678. What a shame, really. But no worries, of course it’s possible to work it out, the cost is a few more bytes. Sometimes you are short on bytes. However, just to open your appetite you can sometimes even hook a 1 byte function that only RETs in an efficient way. That’s worth another blog post sometime.

Anyway, what you can do is changing the code a bit and do something like this:

MOV EAX, 
JMP EAX ; <--- Suddenly the processor supports absolute addresses, ah ha!

But now, this code takes 7 bytes and it also screws up one of the registers. There are two possible fixes, either store and then reload EAX at the callee site (BAHH!!) or change the code again, which is trivial:

PUSH 
RET

This is very nice one, although, technically, I am not sure whether it's slower than a simple JMP. The algorithm for the RET instruction is quite impressive (to branch predict the caller(/return) address...), mind you. Now the code takes only 6 bytes, plus, you can access the whole address space.

Let's move to another similar issue, suppose you want to CALL an absolute address, what you gonna do now?

PUSH EBX
CALL $ + 5
HERE:
POP EBX
LEA EBX, [EBX + NEXT-HERE]
PUSH EBP
MOV EBP, ESP
XCHG EBX, [EBP + 4] ; 1337
POP EBP
PUSH 
RET
NEXT:

I will leave it to you to figure it out, hint - it doesn't screw any register.

8 Responses to “Branching to Absolute Addresses”

  1. anonymous says:

    In 32-bit mode a relative jump from any address will reach any address. There is no need for an absolute jump/call instruction.

    In 64-bit mode this becomes a limitation but an indirect jump/call works just fine and is much simpler.

  2. arkon says:

    I would be glad if you can correct me about 32 bits mode! Cause just saying, without showing it, is nothing really. And I consider myself as one who knows the instructions set quite well.

    About 64-bit, I know what you are talking about, but there are still some problems, I will cover it another time.

  3. Peter Ferrie says:

    In 32-bit mode, jmp sel:abs (ea aaaaaaaa ssss)
    also 7 bytes, full memory space, no registers.
    Of course, the sel value is different depending on XP or earlier vs Vista or later. :-(
    64-bit mode is all RIP-relative, including for absolute indirect jumps, which is very inconvenient. So I have no solution yet… ;-)

    A bit off-topic, but did you see this amazing thing?
    http://vx.netlux.org/lib/vrg02.html
    jump into 64-bit mode from 32-bit mode.

  4. arkon says:

    Yey, JMP FAR… com’on.. So yes, technically you are right, but setting the selector is a headache, besides in my example I was only asking for address without the sel.

    Amazing indeed, I guess MS didn’t expect that :)

  5. […] Insanely Low-Level An Arkon Blog « Branching to Absolute Addresses […]

  6. anonymous says:

    All EIP-relative calculations are modulu 2**32 so if you think you need to move 3GB forward and you cannot represent this number as signed 32-bit — you go instead 1GB backward.

    The thing is that you do not even need to think about it. Just calculate TARGET – EIP using a 32-bit register (or __int32 in C) and you get the correct answer.

    My guess is that you do not have format computer science education. Right?

  7. arkon says:

    I was also talking about x64. I have to admit I am embarrassed, to be wrong like the rest of them. It seems it’s a common pitfall. Thanks!

    No, I haven’t studied yet. I don’t think it has anything to do with studying at a formal place. I think the problem is that when you learn something you take it totally as-is and you don’t doubt it.

  8. Jo says:

    PUSH
    RET

    is guaranteed to generate a branch misprediction, costing you 50 cycles or thereabouts, instead of 1 or 0 for a correctly predicted branch.

    BAD idea.

Leave a Reply