Understanding X64 Assembly Instruction Formats

Understanding X64 Assembly Instruction Formats
For the x86 assembly instruction format, please refer to
http://bbs.pediy.com/showthread.php?t=191802
Here, I will share the x64 assembly instruction format that I researched for a day.
1. Intel Manual
As you can see, there are significant changes in the x64 assembly instruction format. Without further ado, see the image.
Understanding X64 Assembly Instruction Formats
Clearly, there are additional elements compared to x86. The Legacy Prefix is categorized by function group, and I divide this instruction sequence into four parts: Prefix, Opcode, ModRM and SIB, Displacement, and Immediate. Among these, only the opcode is mandatory; the other components are optional! Take a good look at these structures.
1. Prefix
When AMD introduced the x86 extension for 64-bit technology, a prefix was added for accessing the extended 64-bit data, known as the REX prefix, while the original x86 prefixes became the Legacy prefix in the instruction format.
The REX prefix only exists in the 64-bit mode of x64; in the legacy x86 mode, the REX prefix is invalid, but the Legacy prefix is valid in the 64-bit mode.
The range of the REX prefix is from 40H to 4FH. In non-64-bit modes, they are the inc and dec instructions, meaning these instructions are redefined as REX prefixes in 64-bit mode. There is really little information about Prefix, but I learned a lot from the x86/x64 Instruction Encoding Insider (for AMD/Intel), link as follows:
http://www.mouseos.com/x64/index.html.
I will just briefly mention that there is a Legacy prefix and a REX prefix. These modifiers are to change the default registers. For example, in 32-bit, mov ax, bx would require the Legacy prefix 0x66. The former also serves to change the current segment register, which is not very important anymore. In x64, due to the expansion of general-purpose registers (the main reason), the original 8 general-purpose registers only needed 3 bits for identification, but now with r8 to r15, how to identify 16 general-purpose registers? Clearly, adding one bit can fully identify them.
That is the role of the REX prefix. Let’s take a look at the structure of this guy…
Understanding X64 Assembly Instruction Formats
It has a value range; the first four bits must be 4, and the last four bits are optional: W, R, X, B. W indicates changing the default operand size. For example, now x64 has an assembly code mov r8, r10.
Many instructions default to 32-bit operands, and only when CS.L==1 && CS.D==0 will it be 64-bit operands (I have not seen this). Therefore, it is generally necessary to change the default operand size, which is 0x48 (commonly referred to as REX.W in the Intel manual, you should thank me for understanding this); secondly, the R bit is used to extend the ModRM.reg field, 000 ~ 111 —> 0 000 ~ 1 111, thus the register situation now becomes as follows.
Understanding X64 Assembly Instruction Formats
X: Used to extend the SIB.index field; B: Used to extend the SIB.base, ModRM.r/m, and Opcode.reg.
2. Opcode
Most general-purpose instruction Opcodes are single-byte, at most 2 bytes. However, for x87 FPU instructions and SSEx such SMID instructions, it can have 3 bytes, and the Opcode indicates what operation the instruction performs.
The Opcode here is not complete, as it also has extension bits, which are the reg/opcode in the ModR/M, 3 bits. For example, a simple case of an absolute jump in x64 is 0xFF25 + 0x0000 + [8-byte absolute address]. Let’s check the jump instruction in the Intel manual:
Understanding X64 Assembly Instruction Formats
Understanding X64 Assembly Instruction Formats
Many people do not understand why 0xFF25 has four 0x00 appended. Currently, I cannot verify this on a x64 inline assembly environment on my work machine, but I understand this is actually an offset indicating how far from this instruction the 64-bit address is stored, and then jump to that 64-bit address. You can often see this instruction when debugging imported function tables.
Okay, without further ado, let’s look at how the ModR/M comes from. Since it is /4, the extended Opcode is 100, and this jump is followed by an immediate address. Looking up the table, we can see
ModRM= 00 101 —> ModR/M —> 00 100 101. I don’t know what this is.
3. ModR/M
This will not be elaborated; the x86 instruction to machine code mapping table applies to x64. As for how to use it, I will explain with examples later.
4. SIB & displacement
I want to add that SIB is not only used when base-plus-index addressing and scale-plus-index addressing are involved but also when it involves the esp/rsp registers, it is highly likely that SIB will be needed. Looking at the x86 instruction diagram, we can see that none involve the esp register. Now looking at the x86 SIB diagram from the Intel manual, it becomes clear.
Understanding X64 Assembly Instruction Formats
Here, I found ESP in the vertical search; it is evident that this thing does not follow the usual path!!! Displacement does not need much explanation; for example, [rsp+0x8], that is an 8-byte offset, which can be negative.
5. Immediate
This is the easiest to understand, that is the immediate value. For example, mov eax, 0x12345678. The part here would be 0x78 0x56 0x34 0x12 written in reverse…
II | Experiment
Using windbg, casually open a 64-bit process, find a few assembly instructions, and push a bit. Let’s keep it simple, sweat pouring….
Understanding X64 Assembly Instruction Formats
This is a 32-bit instruction, mov ebx, eax. First, check the MOV table… Here is a MOV instruction table with x64 included.
Understanding X64 Assembly Instruction Formats
Then, we see …8B /r MOV r32,r/m32 RM Valid Valid Move r/m32 to r32. The default is 32-bit operands, so no prefix is needed, /r indicates that the ModR/M reg/opcode represents the reg and from the table we see the ModR/M value is 11 011 000 == 0xd8 (the second register takes priority in horizontal operation). Since it does not carry SIB, the final instruction is 8B D8.
Understanding X64 Assembly Instruction Formats
Also a mov instruction, but this time it is 64-bit, mov r8,r12. First, check the MOV table… then we see…REX.W + 8B /r MOV r64,r/m64 RM Valid N.E. Move r/m64 to r64.
In fact, the Intel manual has already told you to add the REX Prefix, specifically 0x48 (why, see above). Here, note that since the register extension bit is enabled, this needs to change from the original 3 bits to 4 bits (there is no need to go into detail when pushing from assembly to machine code). r8, r12 correspond to 1000 and 1100, remove the fourth bit, and just look it up in the x86 instruction table. Thus we see ModR/M is 0xC4. So the final result is 48 8B C4. You may notice that the result is different; why? The REX Prefix was not written correctly; this is because /r indicates that there are extended registers in ModR/M, so REX.R = 1, this instruction’s ModRM.reg provides the source operand addressing, while ModRM.r/m provides the target operand addressing. The target register r8 needs REX.B for extension, and its instruction encoding is:
Understanding X64 Assembly Instruction Formats
The encoding of the target operand r12 register is extended to 1100 through REX.B, so the REX Prefix is 0x4D instead of 0x48. This must be noted…
*Please indicate the source from the Kanxue Forum @PEdiy.com* Please indicate the source from the Kanxue Forum @PEdiy.com
Understanding X64 Assembly Instruction Formats
Understanding X64 Assembly Instruction Formats
If I ever had my head down, it will be just to admire my shoes.
Even if I once lowered my head, it was only to admire my shoes.
Understanding X64 Assembly Instruction Formats

Leave a Comment