Home > Back-end >  Inline asm jmp - 'invalid operand for instruction'
Inline asm jmp - 'invalid operand for instruction'

Time:09-26

I'm doing some code injections, and I find it difficult to transcribe the code from C to Rust.

The situation is as follows, at the relative address 0x40147D we put a jump to the address of the instruction injected by the dll, this was done by inserting the bytes manually.

let rel = target_address - to_hook - 5;

let bytes: [u8; 4] =  transmute(rel.to_be());

buf[0] = 0xE9;

for x in 1..5 {
    buf[x] = bytes[4-x]
}

So we run any code in asm and at the end it should return to 0x401484 which is instruction 0x40147D 7. In C this works as expected, in Rust it doesn't.

C:

DWORD jmp_back = 0x40147D   7;

__asm {
    add dword ptr[ebp - 0x0C], 03
    mov eax, [ebp - 0x0C]
    jmp [jmp_back]
}

Rust (I put a label to not crash the program):

let jump_back: u32 = 0x40147D   7;

unsafe {
    asm!(
        "2:",
        "add dword ptr [ebp-0x0C],01",
        "mov eax,[ebp-0x0C]",
        // "jmp 2b", //back to label 2
        "jmp [{}]", in(reg) jump_back,

        // 'invalid operand for instruction' 
        //"jmp 0x401484" 

    );
 }

What is going unnoticed ?

EDIT

As commented by @prl, removing the square brackets resulted in:

enter image description here

I'm researching to know what is actually inside the eax register. But with this modification the program crashes and closes.

OBS To test, I removed the previous jmp instruction ("jmp 5D7B145D")

EDIT 2

As described enter image description here

The ideal is not having to use registers for such an action.

EDIT 3

So far I leave my answer as a solution to this question, but it still has problems.

Rust as the first instruction of the asm block moves the values you want to use into a register, a few points:

  • If the register contains values in it ?
  • Why not use constants in block asm ?

CodePudding user response:

Use jmp {} instead of jmp [{}]. Rust puts the operand in a register instead of in memory like the C code does. jmp [{}] would cause it to try to read the destination address from the memory location specified in the register. The C version needs brackets because the destination address hasn't been loaded into a register.

You also need to let the compiler know that inline assembly code uses eax, so the compiler doesn't use eax for the operand. To indicate that eax is clobbered, add another output operand out("eax") _. The _ indicates that the value can be discarded.

asm!(
    "add dword ptr [ebp-0x0C],01",
    "mov eax,[ebp-0x0C]",
    "jmp {}", in(reg) jump_back, out("eax") _
);

This has the drawback that it uses a register chosen by the compiler, which may already be in use at the location of the code injection.

If the compiler by default chooses a register that isn't available, but another register is available, the register to use can be specified. For example, in("edx") jump_back would tell the compiler to use edx.

CodePudding user response:

Here's a possible solution that only uses eax:

asm!(
    "push eax",
    "add dword ptr [ebp-0x0C],01",
    "mov eax,[ebp-0x0C]",
    "ret", in("eax") jump_back,
);

The push puts the location to jump to onto the stack, and the ret jumps to that location after loading eax, avoiding the need for a separate register to hold the address.

CodePudding user response:

This may be a temporary solution, but as mentioned in the post, it may result in future issues.

As commented by enter image description here

Highlighted is what can cause future problems.

  • Related