I was reading this article " assembly-challenge-jump-to-a-non-relative-address-without-using-registers ".
I need to do exactly what he suggests here (Jump to a non-relative address without using registers), only I need to do it in intel syntax instead of att.
The solution he found for att syntax was:
jmp *0f(%eip)
0: .int 0x12345678
What would this look like in intel syntax?
CodePudding user response:
The blog actually suggests jmp *0f(%eip)
for use in 32-bit mode. But that is wrong; there is no EIP-relative addressing available in 32-bit mode, so this is not valid 32-bit assembly. It looks like clang 6.0 and earlier had a bug where it would accept jmp *0f(%eip)
anyway, but the output shows that the instruction it actually assembled was jmp *0
, i.e. trying to load the jump target from absolute address 0 (not *0f
, the address of the local label where you put some data). This won't work and will simply crash, assuming page 0 is unmapped as would be the case under a normal OS.
(More generally, it appears the bug would cause jmp label(%eip)
to take the displacement of label
from the next instruction, and use it as an absolute address, which would never be useful. i.e. encode as if in EIP-relative addressing worked in 32-bit mode; in 64-bit mode the same machine-code would use the 4-byte value as a relative displacement instead of an absolute address.) So the author is mistaken about this, and must not have actually tested their proposed code.
You tagged this x86-64 so I assume you are actually interested in 64-bit mode. In that case the blog's suggestion of
jmp *0f(%rip)
0:
.quad 0x1234567890
is valid. Note the use of the 64-bit program counter rip
and the use of .quad
to get a 64-bit address. (Using eip
here actually is a valid instruction, corresponding to a 0x67
address size override, but it would cause the load address to be truncated to 32 bits, which is unlikely to be desired.)
The Intel syntax for RIP-relative addressing varies between assemblers. In NASM you would write:
jmp [rel label]
label:
dq 0x1234567890
Other assemblers might want some variation, or might assemble jmp [label]
as RIP-relative by default. Check the manual of the assembler you want to use.
(If you really did want to accomplish this in 32-bit mode, it would be harder. If you know the correct selector for the code segment, which on many operating systems would be a fixed value, you could do a direct far jump and encode the 6-byte segment/offset directly into the instruction. Otherwise, I don't immediately see a way to do this without using either registers or stack.)
CodePudding user response:
OK I'll follow the main approach to answer such question by somebody's own.
Created a file with the following contents:
.text
jmp *0f(%eip)
0: .int 0x12345678
Compiled it and checked report of the same contents (well, your .int is decoded as a command):
$ gcc -c so_72135694.S
$ objdump -d so_72135694.o
so_72135694.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 67 ff 25 00 00 00 00 jmpq *0x0(%eip) # 0x7
7: 78 56 js 0x5f
9: 34 12 xor $0x12,%al
(why gcc
and not directly as
- well, I was too lazy to recall as
options.)
And then, called Intel style decoding:
$ objdump -d -Mintel-syntax so_72135694.o
so_72135694.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 67 ff 25 00 00 00 00 jmp QWORD PTR [eip 0x0] # 0x7
7: 78 56 js 0x5f
9: 34 12 xor al,0x12
Let's compare it back:
$ cat so_72135694.intel.S
.intel_syntax noprefix
.text
jmp QWORD PTR [eip 0x0]
0: .int 0x12345678
$ gcc -c so_72135694.intel.S
$ objdump -d so_72135694.intel.o
so_72135694.intel.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 67 ff 25 00 00 00 00 jmpq *0x0(%eip) # 0x7
7: 78 56 js 0x5f
9: 34 12 xor $0x12,%al
$ objdump -d -Mintel-syntax so_72135694.intel.o
so_72135694.intel.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 67 ff 25 00 00 00 00 jmp QWORD PTR [eip 0x0] # 0x7
7: 78 56 js 0x5f
9: 34 12 xor al,0x12
One can easily see they are identical, and you can follow this method for all similar questions.
NB1: It is crucial to note that Unix binutils interpretation of what is "Intel syntax" will differ in subtle details with what Intel itself thinks (and even in syntax basics, like 0x1234
vs. 1234h
), and with wide popular tools like NASM or FASM. Here I assume if you say for AT&T syntax, the most typical Binutils pack (GNU one) is utilized (and my system here is Ubuntu 20.04/x86-64, the nearly most popular one). If Iʼm wrong here, feel free to explore other tools specifics.
NB2: The really confusing thing in your code was using relative addressing over EIP. This addressing can be used only in 64-bit mode, but in that case using EIP is weird. An attempt to compile this in 32-bit mode (using e.g. .code32
) naturally fails.