Absolute addressing to replace code at run time in x86_64

I am currently using a 32-bit code replacement scheme, where code that is moved to another position reads variables and a class pointer. Since x86_64 does not support absolute addressing, it is difficult for me to get the correct addresses for variables in a new code position. The problem is that due to the relative addressing of rip, the address of the instruction pointer is different from compilation time.

So, is there a way to use absolute addressing in x86_64, or is there another way to get the addresses of variables, rather than an instruction pointer relative to?

Something like: leaq variable(%%rax), %%rbx will also help. I just want there to be no dependency on the instruction pointer.

+8
assembly gcc x86-64 64bit
source share
2 answers

Try using a larger code model for x86_64. In gcc, this can be selected with -mcmodel = large. The compiler will use 64-bit absolute addressing for both code and data.

You can also add -fno-pic to prevent the creation of position-independent code.

Edit: I am creating a small test application with -mcmodel = large, and the resulting binary contains sequences like

 400b81: 48 b9 f0 30 60 00 00 movabs $0x6030f0,%rcx 400b88: 00 00 00 400b8b: 49 b9 d0 09 40 00 00 movabs $0x4009d0,%r9 400b92: 00 00 00 400b95: 48 8b 39 mov (%rcx),%rdi 400b98: 41 ff d1 callq *%r9 

which is an absolute 64-bit immediate download (in this case, an address) followed by an indirect call or indirect load. Command sequence

 moveabs variable, %rbx addq %rax, %rbx 

is the equivalent of "leaq offset64bit (% rax),% rbx" (which does not exist), with some side effects like changing a flag, etc.

+6
source share

What you ask is doable, but not very simple.

One way to do this is to compensate for the movement of code in its instructions. You need to find all the instructions that use relative RIP addressing (they have ModRM bytes from 05h, 0dh, 15h, 1dh, 25h, 2dh, 35h or 3dh) and adjust their disp32 field to the amount (therefore the movement is limited to +/- 2 GB in the virtual address space, which may not be guaranteed if the 64-bit address space is more than 4 GB).

You can also replace these instructions with your equivalents, most likely replacing each original instruction with more than one, for example:

 ; These replace the original instruction and occupy exactly as many bytes as the original instruction: JMP Equivalent1 NOP NOP Equivalent1End: ; This is the code equivalent to the original instruction: Equivalent1: Equivalent subinstruction 1 Equivalent subinstruction 2 ... JMP Equivalent1End 

Both methods will require at least some rudimentary x86 disassembly procedures.

The first may require the use of VirtualAlloc() on Windows (or some equivalent on Linux) to ensure that the memory containing the corrected copy of the source code is within +/- 2 GB of this source code. And distribution to specific addresses may still fail.

The latter will require not just a primitive disassembly, but also full decoding and command generation.

There may be other quirks.

The boundaries of the instruction can also be found by setting the TF flag in the RFLAGS to force the CPU to generate a single-step debugging interrupt at the end of each command. The debug exception handler will need to catch them and write the RIP value of the next command. I believe this can be done with Structured Exception Handling (SEH) on Windows (never tried with debug interrupts), not sure about Linux. To do this, you will need to execute all the code, each instruction.

Btw, there is absolute addressing in 64-bit mode, see, for example, the MOV to / from instructions of the battery with opcodes from 0A0h to 0A3h.

+2
source share

All Articles