OldWildWeb Logo

Stampare caratteri su schermo in assembler con Linux

Come stampare su schermo con linux in assembly a 64bit


Leggere file in ASM con Linux ASM x64

In questo articolo vedremo come leggere dei file in assembly con Linux, utilizzando l'assembly per processori a 64 bit.
Per creare un programma interessante, creeremo un programma clone del comando cat di Linux scritto ovviamente in assembly.

Qui sotto riportiamo le parti di codice più interessanti del programma acat spiegate e commentate, il sorgente completo lo trovate compresso alla fine dell'articolo, è possibile compilarlo con NASM necessita un sistema operativo a 64bit, il codice sorgente è stato testato con Debian 6.

Come prima cosa per realizzare un programma clone del cat in assembly dobbiamo poter leggere i parametri passati al programma, in particolare ci interessa l'equivalente in C del parametro ARGV, ossia del file da stampare sullo schermo passato come parametro alla posizione 1.
Il nostro programma "acat" verrà utilizzato nel seguente modo per stampare a monitor il contenuto di un file:

./acat nome_del_file_da_stampare

Come leggere i parametri nei programmi scritti in assembly (argc e argv)

I parametri passati ad un programma in assembly verrano memorizzati dal kernel nello stack e saranno disponibili in modo molto semplice al programma assembler nelle prime posizioni dello stack, qui sotto riportiamo il listato di codice per leggerli.
1) pop rcx ;Program Filename
2) pop rcx ;ARGC tells the number of ARGV to read
3) pop rcx ; ARGV[1] pointer!
4) mov rsi,rcx ;RSI is the argv[1] pointer

Per leggere i parametri in assembly basta eseguire delle istruzioni "pop", per recuperare le informazioni passate dal kernel al programma mediante la memoria dello stack.
Il primo parametro letto è l'equivalente in C di argv[0], ossia il nome del programma.
Il secondo parametro è invece l'argc, il numero di parametri disponibili
Dal terzo parametro in poi si iniziano a leggere le stringhe passate da argv[1] ad argv[x].
Nel nostro esempio scartiamo le prime due, mentre memorizziamo il puntatore alla stringa argv[1] nel registro rsi, dopo di che per comodità salveremo la stringa argv[1] in una zona di memoria.

Come stampare una stringa su schermo in assembly x64 Linux

1) mov rdx,lenmsg1 ; arg3, length of string to print
2) mov rcx,msg1 ; arg2, pointer to string
3) mov rbx,1 ; arg1, where to write, screen
4) mov rax,4 ; write sysout command to int 80 hex
5) int 0x80 ; interrupt 80 hex, call kernel

Per stampare una stringa in assembly con linux è molto semplice, basta caricare i registri della CPU nel seguente modo:
rdx: Lunghezza della stringa da stampare
rcx: Locazione della memoria contenente il primo carattere della stringa da stampare, non è altro che un puntatore.
rbx: Dove si desidera stampare il valore 1 indica lo schermo
rax: La funzione del kernel da eseguire, 4 indica il comando write
infine basta generare l'interrupt 80h del kernel di linux con l'istruzione della riga 5, il Kernel linux penserà al resto, nella stessa maniera possiamo chiamare altre funzioni del kernel utilizzando i parametri corretti.

Come leggere un file in assembly

Per leggere un file in assembly come prima cosa bisogna utilizzare la funzione del kernel linux open file tramite il seguente listato:
1) mov rax,5 ; the syscall number for open()
2) mov rbx,filename ; File Name pointer
3) mov rcx,2 ; Read Write flag
4) int 80h ; call the kernel
5) test rax,rax ; lets make sure it is valid
6) jns readstart ; if the file descriptor does not have

In maniera analoga al metodo per stampare caratteri sullo schermo, bisogna caricare i registri della cpu nel seguente modo:
rax: 5, funzione file open
rbx: puntatore alla stringa con il percorso del file
rcx: 2, flag read/write, indica la modalità di apertura del file
rdx: lunghezza del filename da aprire
Infine basta invocare l'interrupt 80h per dire al kernel di aprire il file.

Se tutto è andato bene, il registro rax conterrà il file descriptor, un numero che sarà maggiore di zero nel caso in cui l'apertura del file è andata a buon fine.

Possiamo ora finalmente leggere dal file con il seguente codice:

1) mov rbx,rax ; sys_open returned file descriptor into eax
2) mov rax,3 ; sys_read
3) mov rcx,buffer ; we are putting the ADDRESS of buf in ecx
4) mov rdx,bufsize ; we are putting the ADDRESS of bufsize in edx
5) int 80h ; call the kernel

Una volta aperto un file, è possibile leggerlo caricando i registri nel seguente modo:
rbx: file descriptor restituito dalla funzione open file nel registro rax
rax: 3, funzione per leggere file
rcx: locazione di una memoria di tipo byte
rdx: Lunghezza della memoria di tipo byte
Infine basta invocare l'interrupt 80h.

Il registro rax, conterrà il numero di byte letti dal file, si può inserire questa procedura in un loop per leggere interamente un file finche il registro rax sarà maggiore di 0.