Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `call dword 0x12345678` compile to [66,E8,72,56,34,12]?

Tags:

x86

assembly

nasm

$ cat call.s
call dword 0x12345678
$ nasm call.s
$ ndisasm call
00000000  66E872563412      call dword 0x12345678

Why is the result [66,E8,72,56,34,12] not [66,E8,78,56,34,12]? Why did 0x78 change into 0x72?

like image 765
cubuspl42 Avatar asked Dec 21 '25 04:12

cubuspl42


1 Answers

You are calling an absolute address with this instruction, although it may not seem so obvious at the first glance. Let's go through the assembly process step by step.

Firstly, you can see that your instruction has been assembled to 66 E8. 66 is the operand size prefix - I'll explain why it got there later. E8 points to the CALL instruction, which is expected, and the following bytes - 72563412 - are the little-endian representation of the 32-bit displacement relative to the instruction after the CALL instruction. This is actually how all relative jumps and calls are encoded in x86 : because the CPU always knows the current value of the instruction pointer, it can just add the value specified by the instruction to that value and execute a call/jump.

Secondly, there is a question why the assembler chose to assemble the instruction this way. If you are actually running nasm without any flags, then it defaults to the bin output mode, which just outputs raw opcodes into the file you specify. Additionally, it defaults to assembling instructions in 16-bit mode in that output mode. This is why the 66 prefix is there : it was possible to execute instructions with 32-bit operands in 16-bit mode, provided that this prefix was present in front of the opcode. Also, nasm in bin output mode assumes that the resulting binary begins at address 0 : this is why the "offset" of your call is 72563412 - if the IP starts at 0, then it will be 6 after processing the CALL, and 0x6 + 0x12345672 gives you 0x12345678, which is the address you wanted to call. You can change the offset at which the program is loaded via the ORG (origin) directive.

If you don't want to use jumps/calls with relative addressing, then you will have to put the address of the code which you want to jump to/call in a register, and then execute the instruction with the register as an operand. Something like this :

mov eax, somecode
jmp eax

somecode:
    int 3

Assembles to the following :

00000000  66B809000000      mov eax,0x9
00000006  66FFE0            jmp eax
00000009  CD03              int 0x3

You can see that the absolute address of the jump is moved directly into the register. However, if you're simply executing jumps/calls to labels, I can't think of any reasons why you wouldn't use relative addressing. The CPU always knows its current IP - and your assembler should, too.

like image 195
Daniel Kamil Kozar Avatar answered Dec 24 '25 11:12

Daniel Kamil Kozar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!