MCA - Machine Code Analyzer (x86/x64)

DispatchCode

Moderatore
24 Maggio 2016
499
14
375
224
Nei software comuni non è sicuramente una necessità, lo ammetto. Ma ci sono casi nei quali può servire conoscere la lunghezza di un'istruzione in codice macchina, diciamo di x86/x64.
Qualcuno ogni tanto necessita proprio di questo, come Get size of assembly instructions o Get size of x86-64 instruction.

MCA (Machine Code Analyzer) consente di stabilire la lunghezza delle istruzioni per x86 e x64. Oltre a questa, porta con sè tutta una serie di altre informazioni che riguardano l'istruzione.
Riporto un esempio preso dal readme.

Codice:
008910BC  |.  C785 68FFFFFF 00000000  MOV DWORD PTR SS:[LOCAL.38],0
008910C6  |.  8D45 CC                 LEA EAX,[LOCAL.13]
008910C9  |.  8D5D 9C                 LEA EBX,[LOCAL.25]
008910CC  |.  33C9                    XOR ECX,ECX
008910CE  |>  83F9 30                 /CMP ECX,30
008910D1  |.  7D 18                   |JGE SHORT 008910EB
008910D3  |.  0F100408                |MOVUPS XMM0,DQWORD PTR DS:[ECX+EAX]
008910D7  |.  0F100C0B                |MOVUPS XMM1,DQWORD PTR DS:[ECX+EBX]
008910DB  |.  0F58C1                  |ADDPS XMM0,XMM1
008910DE  |.  0F11840D 6CFFFFFF       |MOVUPS DQWORD PTR SS:[ECX+EBP-94],XMM0
008910E6  |.  83C1 04                 |ADD ECX,4
008910E9  |.^ EB E3                   \JMP SHORT 008910CE

MCA lo decodifica in questo modo:

Codice:
C7 85 68 FF FF FF 00 00 00 00
8D 45 CC
8D 5D 9C
33 C9
83 F9 30
7D 18
0F 10 04 08
0F 10 0C 0B
0F 58 C1
0F 11 84 0D 6C FF FF FF
83 C1 04

dove ogni "riga" è un'istruzione (come si vede dal codice asm sopra).
Ogni istruzione è rappresentata da una struttura che al suo interno, oltre ai vari campi che compongono l'istruzione, ha un array di bytes chiamato instr. L'output che si vede qui sopra è la stampa a video di questo array.

Qui riporto invece due delle istruzioni viste sopra, con il dettaglio dei campi che compongono l'istruzione:

Codice:
/**
 * Ref only - Instruction Line 1:
 * MOV DWORD PTR SS:[LOCAL.38],0
 */

RAW bytes (hex): C7 85 68 FF FF FF 00 00 00 00
Instr. length: 10
Print instruction fields:
        Located Prefixes 0:

        OP: 0xC7
        mod_reg_rm: 0x85
        disp (4): 0xFFFFFF68
        Iimm: 0x0


/**
 * Ref only - Instruction Line 10:
 * MOVUPS DQWORD PTR SS:[ECX+EBP-94],XMM0
 */

RAW bytes (hex): 0F 11 84 0D 6C FF FF FF
Instr. length: 8
Print instruction fields:
        Located Prefixes 1:
                0xF
        OP: 0x11
        mod_reg_rm: 0x84
        SIB byte: 0xD
        disp (4): 0xFFFFFF6C

Attualmente si trova in versione beta. L'ho provato su qualche codice e si è comportato bene, ma vista la complessità, qualche errore nella decodifica me lo aspetto. Sono presenti anche i test nella cartella /test (mi hanno permesso di trovare qualche problema).


Il progetto lo si trova su GitHub: MCA - Machine Code Analyzer.
 
Ultima modifica da un moderatore:
Notavo questo thread, un po' vecchio forse, volevo aggiungere alcune note di carattere generale:

- qualsiasi debugger per il suo compilatore/formato mostra le istruzioni in codice macchina.
Su linux si usa gdb o objdump, tool gratuiti e gia inclusi in ogni distro installando gdb/gcc.
- le istruzioni sono composte da "codici operativi" (op codes) e operandi / indirizzi. L'op code e' all'inizio, in genere uno o due bytes, dipende se si tratta di una cpu semplice o complessa come x86_64 (cisc), cioe' dal numero di istruzioni che supporta.
- certe architetture hanno istruzioni della stessa lunghezza, altre variabili, dunque fondamentale
che il debugger processi il codice dall'inizio.
- le istruzioni macchina sono sempre composte da un codice, e alcuni operandi opzionali (ad un "nop" ovviamente non serve nulla)
- ogni architettura ha un manuale di riferimento dove ogni codice operativo e' ben spiegato. Tuttavia, per moderne cpu come x86_64 il manuale diventa piuttosto complesso, quindi per ogni architettura si trovano delle tavole riassuntive:
http://ref.x86asm.net/coder64.html
(google "x86 opcodes" o "avr opcodes" etc)

Sostanzialmente, per semplici architetture con poche istruzioni, come pic, 8051 e simili, conscendo i codici operativi si puo scrivere per gioco/esercizio un compilatore assembly.
 
  • Mi piace
Reazioni: DispatchCode
No problem, questo tipo di topic non ha "scadenza". ;)

Solo due cose:
- le istruzioni sono composte da "codici operativi" (op codes) e operandi / indirizzi. L'op code e' all'inizio, in genere uno o due bytes, dipende se si tratta di una cpu semplice o complessa come x86_64 (cisc), cioe' dal numero di istruzioni che supporta.

Ni, non è proprio sempre così, almeno con x86/x86-64. Ci possono essere i prefissi (segment override, address override, REX...), e in questo caso sono presenti prima dell'opcode stesso.
L'opcode varia, va da 1 a 3-byte, ma in generale si, è l'unico ad essere sempre presente (la lunghezza minima di un'istruzione è 1byte).
Se l'opcode inizia con 0x0F allora è multi-byte, quindi il byte successivo identifica l'operazione; gli altri due casi sono 0x3A e 0x38, e in questo caso i byte sono 3.

Ci sono poi alcune "eccezioni", e sono i gruppi: in questi casi sotto ad un opcode ci sono più istruzioni, che vengono poi riconosciute dal byte ModRm (che codifica appunto 8 istruzioni diverse). L'opcode 0x81 ad esempio è uno di questi casi.

- certe architetture hanno istruzioni della stessa lunghezza, altre variabili, dunque fondamentale che il debugger processi il codice dall'inizio.

Esatto, in pratica CISC è a lunghezza variabile e non fissa (RISC), anche se x86 e x86-64 sono poi "ibride", sono CISC "in superficie", diciamo.
 
Ultima modifica da un moderatore:
Nei CISC per altro le istruzioni si traducono in "microcode", cioe' l'istruzione assembly che tu utilizzi/vedi e' poi composta da una serie di microistruzioni. Con le molte pipleine (superscalare) i cicli di clock restano contenuti, spesso 1 ciclo anche su x86_64.
Messaggio unito automaticamente:

ooooh, sono bronze, hurra :)
 
  • Mi piace
Reazioni: DispatchCode