Unsupported code

EN NL TR

The previous snipset

Home

The next snipset

About RIP relative addressing

Something that everyone wonders about is RIP relative addressing on x64 systems.  It's the default addressing mode in 64-bit programs.

RIP relative addressing basicly means that you access memory relative to the RIP register (or relative to EIP if the 32-bit address override operand is used).

All the bugs mentioned in this article regarding ml64.exe are for version 8.00.2207, I don't know about other versions. The latest version I tried is 8.00.50215.44 and that version does not contains the bugs mentioned.

When reading this article I'm assuming you already know about assembler programming, this article isn't for beginners as I'm assuming some knowledge.

NOTE: the author of diStorm64 notified me about a (rather serious) error on this page, I hereby want to thank him :)



How RIP/EIP relative addressing works in 32-bit mode

In 32-bit programs you can't do this :

But you will have to do something like this instead :


How RIP/EIP relative addressing works in 64-bit mode

In 64-bit programs you are allowed to write this :

NOTE: ML64 is stupid and doesn't allow you to write the above code sample!

It's also nice to note that the RIP is actualy pointing to the end of the instruction, so RIP will always point to the same offset regarding of how many (useless) operands you added before the opcode.


About useless operands in 64-bit mode

I hope you know what operands, opcodes etc. are :).

In 64-bit mode the segment operands are ignored and if you encode multiple REX bytes (see the AMD64/EM64T manuals) only the last REX byte is taken into account, so if you write a disassembler or if you are writing a program to emulate/protect/encrypt 64-bit executables, you need to take this behaviour into account.  The maximum size of an instruction taking all operand etc into account is 15 bytes (atleast that's what I read in the AMD64 programmer's manual).


Absolutive addressing in 64-bit mode

Absolute addressing was not supported in older ml64 versions, in the latest version I tried (8.00.50215.44) , it worked correctly! :

You had to write this to obtain the same result :

Due to a bug, older versions of ML64 did not assemble this instruction correctly :


RIP/EIP-relative addressing in ML64

I've got bad news for you, ML64, Microsoft's Macro Assembler for x64 systems, does not give us full control over RIP relative addressing.  We can't use RIP nor EIP as a parameter in an address reference.

Instead, when we access a variable then the linker will convert it to RIP relative addressing automaticly.  This means that if you write two times the same instruction that at the binary level it will not be exactly the same :

NOTE: the actualy binary output will differ from the value of RIP at link time and the location of "test" in memory at link time


RIP/EIP-relative addressing in FASM

I've got good news!  You can use RIP relative addressing with flat assembler.


RIP/EIP-relative addressing in YASM

I've got good news!  You can use RIP relative addressing with YASM.


A test program showing the old technique versus the new technique of obtaing the current value of RIP (EIP)

I wrote the following test program which will check if the next instruction is a "nop", if RIP is obtained correctly than it will point to this "nop" instruction.   I had to hardcode some instructions because my version of ML64 does not support the usage of RIP manually (!) (Zipped source code plus executable) :

In order the compile this program, you need to invoke ml64 like this :

ml64 XXX.asm /link /subsystem:windows /defaultlib:kernel32.lib /defaultlib:user32.lib

(assuming kernel32.lib and user32.lib reside in the same directory as ml64.exe)

and

(where XXX is the name of your assembly file)
 


A test program that modifies the next instruction using RIP-relative addressing

I wrote the following test program which will modify the next instruction (Zipped source code plus executable), this program needs ML64 :

In order the compile this program, you need to invoke ml64 like this :

ml64 XXX.asm /link /subsystem:windows /defaultlib:kernel32.lib /defaultlib:user32.lib /entry:Main

(assuming kernel32.lib and user32.lib reside in the same directory as ml64.exe)

and

(where XXX is the name of your assembly file)
 


Extra: Another bug in ML64

NOTE: this bug no longer exists in newer ML64 versions!

While testing the possibilites of ML64 I found another bug : ML64 allowed you to enter 16-bit addressing instructions but when you execute them, they will actually be executed as their 32-bit variants, for example :

is at the binary level exactly the same as :

 

The previous snipset

Home

The next snipset