1

Okay so I have been studying Chapter 2 of Volume 2 of the Intel Manuals.

I have as best an understanding as I can get of the ModR/M byte, the REX byte, the SIB byte, and the general instruction encoding (by just reading with no practice or practical tinkering yet). Now, from my understanding, the "instruction" encoding is different from the whole "call" encoding (as I would call it): that is, the instruction plus the operands. That took me days to realize, and I'm still not certain of it.

So I am going to try to apply my knowledge now on these 4 types of calls.

opcode              instruction           operand encoding
04 ib               ADD AL, imm8          I
REX + 80 /0 ib      ADD r/m8*, imm8       MI
00 /r               ADD r/m8, r8          MR
REX.W + 03 /r       ADD r64, r/m64        RM

First some background:

  • ib is an unsigned byte
  • imm[digit] is an immediate value
  • capital letters in the instruction are registers
  • the operand encoding letters are for referencing the lookup table in the docs, that's it.
  • r/m[digit] means register or memory bytes
  • r[digit] means only register bytes

Looking at the "Instruction Operand Encoding" table, I see this:

operand encoding    operand 1             operand 2
RM                  ModRM:reg (r, w)      ModRM:r/m (r)
MR                  ModRM:r/m (r, w)      ModRM:reg (r)
MI                  ModRM:r/m (r, w)      imm8
I                   AL/AX/EAX/RAX         imm8
  • The (r) means the operand is read.
  • The (w) means the operand is written to.

So I see those "operand encoding" strings like MR. That gives me 00 /r from the example instructions. So we have two hex values, 00, and then there's the /r. I have re-read it's meaning a dozen times but still don't know what it means.

I have thought a lot about all of the above parts, but it just feels like something is missing. I can't piece it together to apply this knowledge yet.

So already I am lost. I just studied the ModRM, reg, and r/m stuff, but I don't know what they mean here. Going back and rereading.

The ModR/M byte is an addressing-form specifier byte. It is divided into 3 parts or "fields".

  • The mod field combines with the r/m field to form 32 possible values: eight registers and 24 addressing modes.
  • The reg/opcode field specifies either a register number or three more bits of opcode information. The purpose of the reg/opcode field is specified in the primary opcode.
  • The r/m field can specify a register as an operand or it can be combined with the mod field to encode an addressing mode.

We don't have to deal with the SIB codes in this question.

That doesn't really give me any actionable information. Then there are the rich tables of every value the ModR/M byte takes up in tables 2-1 through 2-3. I am not really sure how the columns and rows line up and what they mean yet.

So, I don't know how to write down those 4 examples of instructions into opcodes, even if I were to select registers or memory addresses for them at random.

Skipping to section 3.1.1.1, I have studied all of the keywords there too.

  • REX.W — Indicates the use of a REX prefix that affects operand size or instruction semantics.
  • /digit — A digit between 0 and 7 indicates that the ModR/M byte of the instruction uses only the r/m (register or memory) operand. The reg field contains the digit that provides an extension to the instruction's opcode.
  • /r — Indicates that the ModR/M byte of the instruction contains a register operand and an r/m operand.

Those 3 are relevant for the instructions above.

Then there is "Table 3-1. Register Codes", which I think shows you something about what the decimal/binary value is of each register, which is useful somewhere in this equation.

That's pretty much it on what I need to know. Everything else in this book seems to be the individual instruction details, or the advanced instruction info which I don't care about yet (like VEX).

But I've read this stuff about 5 times through so far and I still don't have a solid understanding of how to write 1 instruction call yet, for the goal of writing a machine code generator without using gcc/clang/llvm/etc..

I see this image too, but don't know what to do with it entirely yet.


enter image description here


How do you write these 4 example x86 assembly instructions in opcodes, according to the Intel docs? How do I interpret the "operand encoding" values in the table above? Say we select some random registers or slots of memory that fit the instruction type constraints above, then how do you figure out how to write it down into hex code using the Intel Manuals?

  • This is somewhat helpful so far.
  • This too. But still not enough yet unfortunately.
  • This too.
1

1 Answer 1

1

This Answer is not a forward engineering answer like you seek

but a reverse engineereed answer

there exists many utilities,frameworks that can encode the Mnemonic of Your choice

one of them is the keystone-engine (pip install keystone-engine for python binding)

using one of those utilities we can encode what we seek to understand
and look at the encoded bytes and try to make sense of those
ib/r/modrm/sib/rex/lock theories

shown below is a simple encoding of add r,imm8 variant using keystone

from keystone import *
from binascii import *
print( "REGISTER_GROUP names are based on legends,epics&lores from arachnid yarn")
print( "ACCUMULATOR_REGISTER_GROUP  = [AL,AX,EAX,RAX]")
print( "BASE_REGISTER_GROUP         = [BL,BX,EBX,RBX]")
print( "COUNT_REGISTER_GROUP        = [CL,CX,ECX,RCX]")
print( "DATA_REGISTER_GROUP         = [DL,DX,EDX,RDX]")
print( "Mnemonic Add r,imm8")

CODE_R_IMM8 = [
b"add   al  ,0xff",
b"add   bl  ,0xff",
b"add   cl  ,0xff",
b"add   dl  ,0xff",
b"add   ax  ,0xff",
b"add   bx  ,0xff",
b"add   cx  ,0xff",
b"add   dx  ,0xff",
b"add   eax ,0xff",
b"add   ebx ,0xff",
b"add   ecx ,0xff",
b"add   edx ,0xff",
b"add   rax ,0xff",
b"add   rbx ,0xff",
b"add   rcx ,0xff",
b"add   rdx ,0xff"
]

for i in range (0,len(CODE_R_IMM8),1):
    print("%40s" % CODE_R_IMM8[i],end="\t")
    print(hexlify((Ks(KS_ARCH_X86,KS_MODE_64).asm(CODE_R_IMM8[i],0x10000000,as_bytes=True))[0]))

executed

:\>python openc.py
REGISTER_GROUP names are based on legends,epics&lores from arachnid yarn
ACCUMULATOR_REGISTER_GROUP  = [AL,AX,EAX,RAX]
BASE_REGISTER_GROUP         = [BL,BX,EBX,RBX]
COUNT_REGISTER_GROUP        = [CL,CX,ECX,RCX]
DATA_REGISTER_GROUP         = [DL,DX,EDX,RDX]
Mnemonic Add r,imm8
                      b'add   al  ,0xff'        b'04ff'
                      b'add   bl  ,0xff'        b'80c3ff'
                      b'add   cl  ,0xff'        b'80c1ff'
                      b'add   dl  ,0xff'        b'80c2ff'
                      b'add   ax  ,0xff'        b'6605ff00'
                      b'add   bx  ,0xff'        b'6681c3ff00'
                      b'add   cx  ,0xff'        b'6681c1ff00'
                      b'add   dx  ,0xff'        b'6681c2ff00'
                      b'add   eax ,0xff'        b'05ff000000'
                      b'add   ebx ,0xff'        b'81c3ff000000'
                      b'add   ecx ,0xff'        b'81c1ff000000'
                      b'add   edx ,0xff'        b'81c2ff000000'
                      b'add   rax ,0xff'        b'4805ff000000'
                      b'add   rbx ,0xff'        b'4881c3ff000000'
                      b'add   rcx ,0xff'        b'4881c1ff000000'
                      b'add   rdx ,0xff'        b'4881c2ff000000'

you can also refer to this site for explanation of each bytes in the encoded bytes of chosen memmonic

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.