Reputation: 419
I'm needing to write a bit of assembly that I will be writing into the address space of another process (already running.)
Essentially what I am wanting to do is every time this assembly is invoked, it will compare a value from the stack to a predefined value and then if it matches the pre defined value I want to call a function whose pointer will be hard coded into the assembly and if not, I want the assembly to essentially do nothing, so return.
So far I have the following (just going to post the snippet for x86 to keep things simple)
mov eax, [esp+0x04] ; this is the value from the stack
cmp eax, 0x01 ; this is the predefined value I am comparing to
As stated above, if the value in eax matches the 0x01 in this case, I want it to jump to a function (predefined pointer so can hardcode into the assembly,) else I want the sub routine to return. The problem is I don't know how to do this without the use of a label, which I don't believe I will be able to use as this is going to be executed in the context of another process and so the addresses will not start at 0 (for the assembly I will be calling.)
I've heard that you can do a relative jump but I was having trouble implementing something along those lines.
Could somebody show me how I could achieve this using a relative jump or another method?
Upvotes: 0
Views: 1714
Reputation: 364428
You're injecting this into an already-running process, so you definitely have the source and target addresses available while you're copying this in.
x86 conditional branches are available with a 32-bit rel32
displacement, relative to the end of the branch instruction. i.e. if the branch condition is true, they do RIP += rel32
after the usual setting RIP = end of this instruction.
jz rel32
is a good way to make a conditional tailcall to another function, or fall through to a ret
instruction.
See https://www.felixcloutier.com/x86/jcc for the opcodes. How does $ work in NASM, exactly? has an example of manually encoding a call rel32
, and the resulting machine code.
Also Write a jump command to a x86-64 binary file for another Q&A about branch encoding.
mov eax, [esp+0x04]
cmp eax, 1
db 0x0f, 0x84 ; opcode for je rel32
branch_offset: dd 0 ; the rel32 itself
; fall-through path
ret
After you assemble this into machine code, you should write code to modify that dword (aka int32_t
) 0 once you know the source and target addresses in the target process. The 0
is just a placeholder.
(je +0
will just go to the next instruction whether ZF is set or not.)
Or if you know the source and target addresses, you can get YASM to do the math for you at assemble time:
bits 64
org 0x12345 ; this block of code will start at this address
cmp dword [rsp+4], 1
je 0x123456
ret
Assembling this into a flat binary gives us:
$ yasm -f bin -l /dev/stdout jz.asm
1 %line 1+1 jz.asm
2 [bits 64]
3 [org 0x12345]
4
5 00000000 837C240401 cmp dword [rsp+4], 1
6 00000005 0F84FD101100 je 0x123456
7 0000000B C3 ret
8
9 0000000C B854230100 mov eax, $
The ORG
directive doesn't seem to work with NASM, only YASM. I don't know why.
The mov
instruction is there to see what address the assembler thinks its assembling at. With NASM we get B8[0C000000]
instead of the expected 54 23 01 00
as the immediate of the mov
instruction (its own address).
If you know the relative displacement ahead of time / as an assemble-time constant (but not either absolute address), that's fine, too.
In NASM syntax, je +0x555555
assembles to 0F 84 55 55 55 00
.
But not in YASM: in YASM +0x555555
is just the absolute address 0x555555
as a branch target.
Related:
jmp reg
if you can't make a jcc rel32
work.Upvotes: 1