Ecco alcuni semplici esempi di programmazione in assembly, che dimostrano la “potenza” di questo linguaggio.
Il linguaggio assembly è sicuramente il più “potente” che ci sia; infatti vi permette di fare qualsiasi cosa, a vostro rischio e pericolo. Con “potenza” intendo la possibilità teorica che offre di poter fare le cose… non certo la semplicità di programmazione; quest’ultima appartiene ai linguaggi di alto livello.
Partendo da un semplicissimo esempio, scritto in c, vi mostrerò altri programmi “derivati” in assembly. Andiamo subito al cuore della questione.
Ecco il sorgente c del programma di partenza ( file nominato “salta.c” )
#include <stdio.h>
int main(void) {
puts("sono qui");
return 0;
}
A partire da questo sorgente c è possibile ottenere un sorgente assembly, generato dallo stesso compilatore gcc, attraverso il comando
PROMPT> gcc salta.c -S -masm=intel
Questo comando genera un file nominato “salta.s”, scritto in assembly, con sintassi intel (vicina alla sintassi nasm, che utilizzeremo).
Ora rinomino il file con estensione .asm digitando: PROMPT> ren salta.s salta.asm
Dopo qualche necessaria elaborazione manuale, ottengo il sorgente assembly seguente ( file “salta-asm.asm”)
bits 64
extern puts
global main
section .text
main:
push rbp
mov rbp, rsp
sub rsp, 32
lea rcx, LC0
call puts
mov eax, 0
add rsp, 32
pop rbp
ret
section .data
LC0: db "sono qui",0
Ora è possibile ottenere l’eseguibile, con i 2 passaggi, sotto descritti:
PROMPT> nasm -f win64 salta-asm.asm -o salta-asm.o
PROMPT> gcc salta-asm.o -o salta-asm.exe
Con il primo comando creo il file oggetto “salta-asm.o” e con il secondo creo l’eseguibile. L’uso di gcc è necessario, per il fatto che utilizzo la funzione puts del c.
Lanciando il programma “salta-asm.exe”, verrà visualizzato il messaggio: “sono qui”.
E adesso viene il bello…
Adesso viene il bello. E’ infatti possibile manipolare il sorgente assembly per ottenere alcuni interessanti risultati. Ecco un esempio…
bits 64
extern puts
global main
section .text
main:
push rbp
mov rbp, rsp
sub rsp, 32
eti:
; call funzione
lea rax, funzione ; carico in rax l'indirizzo di "funzione"
jmp rax ; salto all'indirizzo contenuto in rax
eti2:
nop
nop
nop
nop
nop
mov eax, 0
add rsp, 32
pop rbp
ret
funzione:
; lea rcx, LC0 con questa istruzione, carico in rcx l'indirizzo di LC0.
lea rcx, eti2 ; carico in rcx l'indirizzo dell'etichetta eti2
call puts
jmp eti2
; ret
section .data
LC0: db "sono qui",0
Rispetto al precedente sorgente, ci sono alcune differenze. Per prima cosa, mi sono inventato la funzione chiamata semplicemente “funzione” che chiama la puts del c, per stampare a video un messaggio. Inutile complicazione, direte voi. Tutto però serve per capire meglio l’assembly.
A questo punto ho sostituito “lea rcx, LC0” con “lea rcx, eti2”. Cosa comporta questo?
Semplice, invece di stampare la stringa “sono qui”, delimitata dal carattere ‘\0’, stamperò a video tutto ciò che parte dall’etichetta “eti2” fino a un carattere ‘\0’ che incontrerò in memoria, o prima o poi…
Che importa se fa parte del codice!!! I bit e i byte possono essere sia codice, sia istruzioni; dipende da come vengono interpretati dalla CPU. Quando sto chiamando la puts all’interno di “funzione”, le cinque nop ( codice operativo 0x90 ) e quel che segue, verranno considerati dati.
Dopo aver eseguito “jmp eti2” le cinque nop e tutto quel che segue, sarà considerato codice da eseguire.
Ecco, dunque la POTENZA dell’assembly, di cui parlavo all’inizio… I byte possono essere interpretati, ora come DATI, ora come CODICE.
Non è sicuramente consigliabile programmare in questo modo, anzi è pericoloso, ma, con l’assembly, è possibile. E allora, buon divertimento!
Ultima raccomandazione: certi programmi fateli girare in un computer che usate per gli “esperimenti”, non su quello che vi serve per lavoro! Se sbagliate, infatti, potete fare anche gravi danni. Meglio essere prudenti.
Andrea Paolini (Amministratore del Blog)