dal 2015 - visita n. 2675
CommandLine
CommandLine

 

Recuperare la command line, e non solo


Quando un programma elf viene lanciato eredita una serie di informazioni depositate come dword sullo stack. Queste informazioni riguardano sia la command line, completa di tutti gli argomenti, sia le variabili d'ambiente associate. La situazione dello stack, all'inizio dell'esecuzione, è schematizzata nella figura seguente.



argC integer: Numero di argomenti (C=N+1)
arg0 pointer: Nome del programma
arg1 pointers: Argomenti command line
arg2
....
argN
null integer: Terminatore argomenti
env0  pointers: Variabili d'ambiente
env1
....
envM
null integer: Terminatore variabili


L'esempio seguente estrae dallo stack tutto il contenuto schematizzato nella tabella precedente e lo visualizza sul terminale. La conversione del contatore è effettuata per semplicità, nell'ipotesi che il suo valore sia minore di 10, aggiungendo semplicemente una costante opportuna.

;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 
; commline_1.asm 
; Visualizza command line e variabili d'ambiente.
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; compilare con nasm+ld

%define STDOUT 1
%define SYSCALL_EXIT  1
%define SYSCALL_WRITE 4

global  _start				;necessario per il linker ld

        section .data   		; Sezione dati inizializzati
msg1    db  	0Dh,0Ah,"Numero di argomenti: "
argc    dd	0
        db      " incluso il nome del programma.",0Dh,0Ah 
MSG1    equ	$-msg1   
msg2    db  	0Dh,0Ah,"Elenco argomenti: ",0Dh,0Ah
MSG2    equ	$-msg2   
msg3    db  	0Dh,0Ah,"Elenco variabili d'ambiente: ",0Dh,0Ah
MSG3    equ	$-msg3   
crlf	db	0Dh,0Ah	
xflag   dd	0

	section	.text
_start:			
;:::::::::::::::::::::::::::::::::::::::: Visualizza Numero degli Argomenti 
	pop	ecx			; Estrae il contatore degli argomenti
	add     ecx,20202030h		; Conversione in ASCII (Ipotesi N<10)
	mov     [argc],ecx		; Inserimento nel corpo di msg1
        mov     eax, SYSCALL_WRITE      ; write function
        mov     ebx, STDOUT             ; Arg1: file descriptor
        mov     ecx, msg1
        mov     edx, MSG1
        int     80h                     ; syscall kernel to write
;:::::::::::::::::::::::::::::::::::::::; Visualizza Messaggio Primo Elenco	
        mov     eax, SYSCALL_WRITE      ; write function
        mov     ebx, STDOUT             ; Arg1: file descriptor
        mov     ecx, msg2
        mov     edx, MSG2
        int     80h                     ; syscall kernel to write
;:::::::::::::::::::::::::::::::::::::::: Ciclo principale estrazione/visualizzazione
                                        ;    utilizzato sia per gli argomenti sia per
					;    le variabili d'ambiente
xloop:	pop	ecx			; Estrae un puntatore
	or	ecx,ecx			; Verifica se null
	jz	exit1			; Se SI: va a exit1
					; Se NO: prosegue
	mov	esi,ecx			; Utilizza Puntatore esi per usare la lodsb
	xor	edx,edx			; Azzera contatore lunghezza (edx)
	dec	edx
strlen:	inc	edx			; calcola la lunghezza di msg
	lodsb
	or	al,al
	jnz	strlen
                                        ; Visualizza msg
        mov     eax, SYSCALL_WRITE      ; write function
        mov     ebx, STDOUT             ; Arg1: file descriptor
        int     80h                     ; syscall kernel to write
	call	newln			; Aggiunge un newline
	jmp	short xloop             ; Ricicla
;:::::::::::::::::::::::::::::::::::::::: Controllo per il secondo giro	
exit1:	mov	eax,[xflag]
	inc	eax
	mov	[xflag],eax
	cmp	eax,2
	je	exit
;:::::::::::::::::::::::::::::::::::::::: Visualizza Messaggio Secondo Elenco
        mov     eax, SYSCALL_WRITE      ; write function
        mov     ebx, STDOUT             ; Arg1: file descriptor
        mov     ecx, msg3
        mov     edx, MSG3
        int     80h                     ; syscall kernel to write
	jmp	short xloop		; Rientra Secondo giro
;:::::::::::::::::::::::::::::::::::::::: Uscita
exit:   mov     EBX, 0                  ; exit code, normal=0
        mov     EAX, SYSCALL_EXIT       ; exit function
        int     80h                     ; syscall kernel to take over

;:::::::::::::::::::::::::::::::::::::::: Routine NewLine	
newln:  mov     eax, SYSCALL_WRITE      ; write function
        mov     ebx, STDOUT             ; Arg1: file descriptor
        mov     ecx, crlf
        mov     edx, 2
        int     80h                     ; syscall kernel to write
	ret
; EOF ::::::::::::::::::::::::::::::::::::::::::::::::::::::



Poichè le variabili d'ambiente sono molte è opportuno avviare il programma con:

./commline_1 ... ... ... |more

in modo da potere scorrere agevolmente tutto l'output. Si noterà, tra l'altro, che l'ultima voce delle variabili d'ambiente contiene ancora il nome del programma, che era già presente come arg0 nella lista degli argomenti.

Talvolta è necessario, invece, avere dei puntatori diretti ad alcuni punti speciali dello stack, senza dovere essere costretti a scaricarlo tutto, ecco alcuni suggerimenti:

        pop     eax                     ; Lettura contatore degli argomenti
        pop     esi                     ; Il reg. esi punta il primo degli argomenti
oppure	
        pop     eax                     ; Lettura contatore degli argomenti
        mov     esi,[esp+eax*4]         ; Il reg. esi punta l'ultimo degli argomenti
oppure	
        pop     eax                     ; Lettura contatore degli argomenti
        mov     esi,[esp+(eax+1)*4]     ; Il reg. esi punta la prima var. d'ambiente

E' evidente che queste istruzioni devono essere usate proprio all'inizio del programma, prima di avere alterato lo stato dello stack.

L'esempio precedente ha, secondo me, l'inconveniente di alterare la situazione dello stack in quanto c'è una palese quantità di istruzioni di pop non equilibrate o da altrettante istruzioni di push o da una opportuna modifica del valore del registro esp. A tal proposito, c'è una scuola di pensiero che vuole che il programmatore, all'uscita del programma, lasci il puntatore di stack con lo stesso valore trovato all'inizio.
Quella che segue è una variante breve dell'esercizio precedente che illustra questo secondo modo di agire, per semplicità tratta soltanto gli argomenti della command line.

;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 
; commline_2.asm 
; Visualizza la command line
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; compilare con nasm+gcc

%define STDOUT 1
%define SYSCALL_WRITE 4

	global	main
	extern	printf

	section .data
	msg1    db  	0Dh,0Ah,"Numero di argomenti: "
argc    dd	0
        db      " incluso il nome del programma.",0Dh,0Ah 
MSG1    equ	$-msg1   
format:	db	'%s', 10, 0		; Stringa di formato
	
	section .text
main:
;::::::::::::::::::::::::::::::::::::::::
	mov	ecx, [esp+4]	        ; Lettura di argC
	push    ecx			; Salvataggio ecx
	add     ecx,20202030h		; Conversione in ASCII (Ipotesi N<10)
	mov     [argc],ecx		; Inserimento nel corpo di msg1
        mov     eax, SYSCALL_WRITE      ; write function
        mov     ebx, STDOUT             ; Arg1: file descriptor
        mov     ecx, msg1
        mov     edx, MSG1
        int     80h                     ; syscall kernel to write
	pop     ecx			; Ripristino ecx
	mov	edx, [esp+8]		; Lettura puntatore di arg0
;:::::::::::::::::::::::::::::::::::::::: Ciclo di lavoro
xloop:	push	ecx			; Salvataggio registri usati da printf 
	push	edx			;
;........................................        
	push	dword [edx]		;    Passa puntatore stringa
	push	dword format		;    Passa stringa formato
	call	printf			;    Visualizza argomento
	add	esp, 8			;    Rimozione parametri dallo stack
;........................................	
	pop	edx			; Ripristino registri
	pop	ecx			;
	add	edx, 4			; Modifica puntatore ad arg successivo
	dec	ecx			; Decrementa contatore
	jnz	xloop			; Ricicla
	ret
;EOF :::::::::::::::::::::::::::::::::::::::::::::::::::

Abbiamo usato il linker gcc in quanto si è preferito, per semplicità, effettuare l'output tramite la printf; si noti, a questo proposito, l'uso della stringa di formato dentro il segmento .data per concorrere al formato della printf. Il puntatore di stack esp, alla fine del programma, rimane inalterato.






Menù
Introduzione
I registri del Pentium
Il modello della memoria
Interazione CPU-RAM
I Servizi Linux - Int 80h
I File in Linux
Schema programma NASM
Esempi Write/Read
Esercizi
lods-stos-movs
gcc + nasm
Gestione Command Line
Stack
Stack-Esercizi
Libreria Funzioni Base
Esercizi con la Libreria
Libreria Macro Base
Test 5 esercizi


Sistemi di numerazione
Elementi Bistabili
Strutture di Controllo
Istruzioni JMP
Hello World
I/O di Testo
Command Line
Colori e Macro
Introduzione I/O
Porta Parallela


AppuntiAsm-386.html
Linux ELF Howto
Linux Assembly Howto
Il linguaggio PC Assembly
Assembly di Paul A.Carter
Assembly di Claudio Daffra



La lunghezza effettiva della vita è data dal numero di giorni diversi che un individuo riesce a vivere. Quelli uguali non contano.
L. de Crescenzo

Valid CSS!
pagina generata in 0.001 secondi