terça-feira, 22 de julho de 2008

Bem,
há um tempo atráz fiquei imaginando como uma instrução em assembly é transformada em uma instrução em hex. Depois de algumas pesquisas percebi que todas as intruções, opCodes, tem um número. Depois da tabela dos opCodes veio outra dúvida como é passado os parâmetros em uma instrução Mov Al, 0x20, como sabem os parâmetros e valores que será passado para a instrução. E para instruções do tipo Mov [Edx], Cl, ou então para
Add [Ecx+Ebx*4+439BCE28], Cl, ai que a coisa começa ficar elegante. E é justamente isso que será visto nesse tutorial.

Abaixo está a tabela onde verificamos qual é o valor em hex de uma instrução.
Bem para encontrar como por exemplo o que qual é a instrução do número 0x46 o primeiro dígito(high digit) quer dizer a linha e o segundo digito(low digit) a coluna, que seria a instrução INC SI.

Por exemplo a instrução NOP é 90.
O high digit é o 9 e o low digit é o 0.
A instrução Inc Di o seu valor é 47.
O high digit é o 4 e o low digit é o 7.

Agora para saber mais sobre as intruções aqui você encontra mais informações.


Bem encontramos várias letras com b, w, r/m, i, t, etc. Abaixo a legenda de cada uma.
b = Byte-Operand
w = Word-Operand
f = from CPU-Register
t = to CPU-Register
m = memory
r/m = Register/Memory
sr = Segmentregister
d = direct
si = short intrasegment
i = immediate
ia = immediate accu
d = direct addressing
id = indirect addressing
is = immediate byte sign-extended
v = variable (IO-addr, in DX or shift-count in CL)
d8 = direkte 8-Bit-Adresse(I/O)
nr = near(16 - 64K)
fr = far (32 bit - 1M)
Bem vamos começar a analizar do começo :P
Usando um exemplo simples, usando uma instrução Mov.
Ele movimenta dados de uma origem para um destino.

Mov destino, origem

Um exemplo de colocar um valor 30 para o registrador EAX.

Mov EAX, 30

Utilizando o opCode 88, de acordo com a tabela temos os seguintes valores.

88 - MOV (b, f, r/m)

Bem o valor 88 é um byte (b) o valor tá vindo do CPU (f) e é do registrador para memória (r/m).
Vamos analizar a instrução abaixo:

88 0a - Mov [Edx], Cl

Aqui é movimentado o valor do registrador cl para a mémoria [edx].
Como é apenas um bye temos como "parâmetro" o byte 0a. O nome desse byte é ModR/M. Esse "parâmetro" é que define para quem que vai e de onde vai(Destino e Origem).
Convertendo o 0a para binário temos 00001010, o binário é divido em 3 grupos 00 001 010.

00 - Byte Mod
001 - Reg/Opcode
010 - R/M
Para esse tipo de instrução 001 é a fonte o destino é 010. A tabela abaixo mostra os valores de cada registrador em binário.

Register Byte
Register Word
Register DWord
AL = 000
CL = 001
DL = 010
BL = 011
AH = 100
CH = 101
DH = 110
BH = 111
AX = 000
CX = 001
DX = 010
BX = 011
SP = 100
BP = 101
SI = 110
DI = 111
EAX = 000
ECX = 001
EDX = 010
EBX = 011
ESP = 100
EBP = 101
ESI = 110
EDI = 111
Como o primeiro operador é b quer dizer byte ou seja 8 bits, por isso usamos o registrador de origem de 8 byte. E o opCode nos diz que está vindo do CPU (f - From CPU) para a memória(R/M - Register/Memory).
Analizando a tabela obtemos os seguintes valores.

001 - Registrador Cl (8 bytes)
010 - Edx como é r/m [Edx]

Então analizando os opCode's 88 0a quer dizer a seguinte instrução em assembly.

Mov [Edx], Cl

Bem mais um exemplo simples.

89 03 - Mov [Ebx], Ax

De acordo com a tabela de opCode's.

89 - Mov (w, f, r/m)

Bem o valor 89 vamos mexer com word(w) o valor tá vindo do CPU (f) e é do registrador para memória (r/m).
Transformando o 03 para binário temos 00 000 100 resultando na instrução.

000 - registrador origem (Ax)
100 - registrador fonte (Bx)

Como é r/m é do registrador para memória temos.

Mov [bx], ax

O outro tipo de instrução que vamos encontrar são instruções que movimenta valore imediatamente, os chamados immediate, em cada opCode nos diz qual é o tamanho do immediate por exemplo o opCode b0.

b0 (i -> Al)

Como o registrador Al é um registrador de 8 bytes, então o valor só vai ter 8 bytes, um exemplo.

b0 6e - Mov Al, 6e

Observer que o próximo byte é passado para o registrador em questão que é especificado na tabela de opCodes.
Agora o opCode B8 trabalha com 32 bits.

b8 63 bc 38 4e - Mov Eax, 4e38bc63

A única diferença é que usamos agora 32 bits, observe que os bytes são invertidos.
Bytes da intrução:

63 bc 38 4e
<------------:

Vão ser colocados no registrador na seguinte forma:

Mov Eax, 4e38bc63

Vamos falar agora de alguns instruções um pouco diferentes como:

00 a8 8a 6e e1 58 - Add [eax+58e16e8a], Ch

De acordo com a tabela de opCodes 00 é:

Add (b, f, r/m)

O opCode trabalha com 8 bytes, de um registrador(f From CPU) para memoria(r/m Register/Memory).
a8 em binário é igual a 10101000 então:

10 - Byte Mod
101 - Registrador Origem (Ch)
000 - R/M

Bem agora vimos que o byte Mod é 10.
Abaixo uma tabela de do byte Mod para podermos encontrar os valores.
Dependendo do tamanho da instrução 16 ou 32 bit.




Para o mod igual á 10 e R/M igual á 000 temos:

[bx+si]+disp16

o deslocamento é 8a 6e e1 58 arrumando para:

58 e1 6e 8a

Agora vem a pergunta onde encontramos o Eax bem juntando o mod + R/M temos
10 000 000 transformando para hexadecimal temos 0x80 que é a coluna do registrador Eax.
Juntando tudo tempos.

Add [Eax+58e16e8a], Ch

Temos também instruções de salto como:
00280721 - ea 66 bc 38 4e 23 e2 - jmp e223:4e38bc66

De acordo com a tabela o opCode ea.

Jmp (fr, d)

O pula vai ser longo(fr) e o endereço vai ser direto(d).
Bem aqui pegamos os 16 bytes iniciais ou seja e223 é o seguimento e o restante é offset.

e223:4e38bc66


Bem a idéia principal foi passada, ainda tem algumas intruções para serem analizadas, mas o básicão é isso.
Agora alguns links uteis.

http://pdos.csail.mit.edu/6.828/2007/readings/i386/c17.htm
http://labts101.zhaw.ch/~tham/TIn1/unterlagen/opcodes.pdf
http://www.intel.com/products/processor/manuals/
http://pdos.csail.mit.edu/6.828/2006/readings/i386/s17_02.htm

Valeuw.


2 comentários:

Anônimo disse...

Grande Danilão!! Apavorando com mais um otimos post!!

[s]

by

6_Bl4ck9_f0x6

Tiago Natel de Moura disse...

oww !

Muito bom, a pergunta: como funciona? também me incomoda muito. Hehhe

Parábens.