PDA

View Full Version : [Assembly x86] Perchè non posso "pushare" solo 8bit nello stack?


AnonimoVeneziano
20-05-2004, 23:35
Ciao ragazzi,

mi trovavo nella situazione di dover "pushare" dei caratteri singoli nello stack di un programmino del cavolo che avevo fatto per imparare un po' l'assembly , quando mi sono accorto che , nonostante i caratteri siano 8bit di dimensione , venivano pushati 32bit di dati nello stack , così ho tentato di fargli pushare solo 8bit così :

push byte 50 <codice ASCII per il mio carattere>
(ASSEMBLATO CON NASM)
ma non funziona , continua a pushare 32bit di roba, in sintassi AT&T sarebbe:

pushb $50

ma se lo provo ad assemblare con GAS mi da un errore , ma è possibile pushare solo 32bit o no? Possibile che se devo passare delle Stringhe come parametri ad una funzione mi devo perdere 24bit per ogni carattere?

La mia idea era quella dopo di usare una funzione per printare a schermo i caratteri che avevo pushato nello stack , così :


Supponendo:
push byte 50
push byte 51
call print


print:

push ebp <Calling Function del C>
mov ebp,esp <''>

mov ebx, epb <Sposto epb in ebx>
add ebx, 8 <Sommo 8 a ebx>

mov edx, 2 <Uso la SYS CALL 4 di Linux , "EDX" numero di caratteri
mov ecx, ebx (Lunghezza del buffer in byte), "ECX" indirizzo dell' inizio del
mov ebx, 1 buffer, "EBX" File Descriptor , "EAX" Codice di della SYSCALL
mov eax, 4 (4) >
int 0x80 <Kernel Linux>


L'output che mi dovrei attendere sarebbero i caratteri corrispondenti ai numeri "3" e "2" , quindi :
32

dovrebbe essere l'output , ma purtroppo dato che appunto la lunghezza del buffer è di 8byte anzichè 2byte come invece gli ho impostato nel registro EDX lui mi visualizza solo il "3" , se imposto ovviamente EDX a 8byte visualizzo i 2 caratteri , ma nello stack perdo 6byte di "0" :( (obbiettivo efficienza :D ) Non posso pushare solo gli il byte facendo incrementare il registro ESP di 1 anzichè di 4 ? O devo farlo io a mano aggiungendo 3 a ESP dopo il PUSH?

Un altra cosa che volevo chiedere è questa :

Per poter dare l'indirizzo del primo carattere ASCII ho dovuto usare questo inghippo :
mov ebx, epb <Sposto epb in ebx>
add ebx, 8 <Sommo 8 a ebx>

Cioè quello di memorizzare EBP dentro EBX , e poi aggiungere 8 a EBX per ottenere in questo l'indirizzo di memoria del primo carattere , che poi passerò tramite REGISTER Addressing a ECX durante la Chiamata di sistema , ma mi chiedevo se non ci fosse un modo per integrare questo passaggio in un unica istruzione , del tipo :

mov ecx, ebp + 8 :D

Questa roba ovviamente non funziona (sennò non ve l'avrei chiesto :D ), quindi , è possibile comunque fare una cosa del genere o devo per forza memorizzare il valore di EBP in qualche registro per poi modificarlo e passarlo (come ho fatto) ?

Grazie delle risposte

Ciao :)

PS= Adesso che ci penso per la seconda invece che passare per EBX sarei potuto andare direttamente in ECX , ma comunque la domanda rimane , ossia se esite un modo per eseguire il Register Addressing in questo modo passando il valore modificato "on the fly" all' interno dell' unica istruzione (non so se mi sono spiegato)

mmx[ngg]
21-05-2004, 01:38
Non ho ben capito se è una semplice prova o se vuoi fare un semplice passaggio di una stringa da una parte all'altra :confused:

Se è valida la seconda alternativa allora devi passargli l'offset della stringa e non un carattere alla volta.

Cmq l'istruzione Push ti permette di agire solo su valori a 16 o 32 bits, mentre x questo caso

mov ebx, ebp <Sposto epb in ebx>
add ebx, 8 <Sommo 8 a ebx>

poi fare così

lea ebx, [ebp + 8] <Sposto epb in ebx e sommo 8>

AnonimoVeneziano
21-05-2004, 07:20
Ok mmx , tramite LEA mi hai fatto risolvere il primo problema :)


Per il secondo non ho capito bene cosa intendi per "passare l'offset della stringa" (cosa intendi? e a cosa dovrei passarlo ? )
Ora, cercherò di spiegare meglio le mie intenzioni

Quello che voglio io è dare come parametro alla funzione "print" del pezzo di programma precedente una stringa , caricandola carattere per carattere tramite "PUSH" , per poi farla visualizzare alla funzione tramite quella SYSCALL "4" che c'è nel programma . Ora, la domanda è , dato che non posso PUSHARE caratteri facendogli occupare solo 8 bit per volta, come faccio a caricare una stringa come parametro a una funzione ? L'unica alternativa è mettere una stringa nella [SECTION .data] del programma?

Grazie

CIao

cionci
21-05-2004, 11:29
La passi per indirizzo...

Supponi che il primo elemento della stringa sia memorizzato all'indirizzo contenuto in esi e che sia lunga 20 caratteri (ps non conosco la sintassi di NASM)...

push dword esi
push dword 20

call print

print:
push ebp <Calling Function del C>
mov ebp,esp <''>
add ebp, 8

mov edx, [ebp - 4] <Uso la SYS CALL 4 di Linux , "EDX" numero di caratteri
mov ecx, [ebp - 8] (Lunghezza del buffer in byte), "ECX" indirizzo dell' inizio del
mov ebx, 1 buffer, "EBX" File Descriptor , "EAX" Codice di della SYSCALL
mov eax, 4 (4) >
int 0x80 <Kernel Linux>

AnonimoVeneziano
21-05-2004, 14:25
Originariamente inviato da cionci
La passi per indirizzo...

Supponi che il primo elemento della stringa sia memorizzato all'indirizzo contenuto in esi e che sia lunga 20 caratteri (ps non conosco la sintassi di NASM)...

push dword esi
push dword 20

call print

print:
push ebp <Calling Function del C>
mov ebp,esp <''>
add ebp, 8

mov edx, [ebp - 4] <Uso la SYS CALL 4 di Linux , "EDX" numero di caratteri
mov ecx, [ebp - 8] (Lunghezza del buffer in byte), "ECX" indirizzo dell' inizio del
mov ebx, 1 buffer, "EBX" File Descriptor , "EAX" Codice di della SYSCALL
mov eax, 4 (4) >
int 0x80 <Kernel Linux>


Fammi capire quello che hai fatto :

In pratica tu hai memorizzato la stringa di caratteri in un altro punto nella memoria , e poi hai pushato l'indirizzo del primo carattere + lunghezza del carattere nello stack per poi andarli a ripescare con [ebp - 4] e [ebp - 8] , giusto?

Grazie

Ciao

cionci
21-05-2004, 15:46
Originariamente inviato da AnonimoVeneziano
In pratica tu hai memorizzato la stringa di caratteri in un altro punto nella memoria
Questo non l'ho fatto...ho solo supposto che la stringa fosse stata precedentemente memorizzata in un'area di memoria il cui indirizzo era contenuto in ESI... Per il resto hai capito...

cionci
21-05-2004, 15:47
Comunque ho sbgliato l'ordine:
push dword 20
push dword esi

Oppure semplicemente inverti il -4 con il -8...

AnonimoVeneziano
21-05-2004, 17:19
Originariamente inviato da cionci
Comunque ho sbgliato l'ordine:
push dword 20
push dword esi

Oppure semplicemente inverti il -4 con il -8...


Ok, soltanto una cosa , non dovrebbe essere [ebp + 4] [ebp + 8] ?

Lo stack cresce verso il basso , perciò tecnicamente i dati pushati stanno sopra all' attuale valore puntato da EBP , o sbaglio?

Ciao

mmx[ngg]
21-05-2004, 18:26
State facendo un pò do confusione :uh:

Il codice corretto è questo


Push DWord Ptr ESI <offset>
Push DWord Ptr 020 <lunghezza>
Call Print

Print:
Push EBP
Mov EBP, ESP
Add EBP, 8
Mov EDX, [EBP] <lunghezza>
Mov ECX, [EBP + 4] <offset>


Come regola di base considera che l'istruzione Push decrementa di 4/2 bytes lo stack (registro ESP/SP), mentre Pop lo incrementa (a prescindere dal flag di direzione). Tieni inoltre presente che la call sfrutta lo stack per memorizzarsi il punto di ritorno (l'Instruction Pointer o IP/EIP che viene ripristinato tramite il Ret....ke di conseguenza incrementa di nuovo lo stack)

Spero di essere stato kiaro :)

cionci
21-05-2004, 19:18
Sì...ho fatto un po' di casino...comunque l'idea era quella (è un po' che non metto amno a queste cose)...

AnonimoVeneziano
22-05-2004, 13:44
Ok, grazie ragazzi , ora mi sfugge solo un concetto del (anzi 2 ) del programma di mmx[ngg] .

Mov EDX, [EBP] <lunghezza>

Perchè? [EBP] non dovrebbe puntare al vecchio valore di EBP che è stato inserito nello Stack con "Push EBP" all' inizio della funzione?

Un altra cosa :

Push DWord cosa fa precisamente ? DWord si intende Double Word? Double Word sono 2 o 4 byte in un sistema a 32bit?

Grazie della pazienza :)

mmx[ngg]
22-05-2004, 14:16
Dunque...partiamo con le cose facili :)

Byte Ptr = 8 bits
Word Ptr = 16 bits
DWord Ptr = 32 bits
QWord Ptr = 64 Bits

Poi...

Supponi ke ESP sia 20, quindi :


Push ESI ESP diventa 16 e punta al valore di ESI (se fai Mov ESI, [ESP] lo verifichi)
Push 20 ESP = 12 e punta al volore 20
Call Print ESP = 8 e punta all EIP successivo (al max questo te lo spiego dopo)

Print:
Push EBP ESP = 4 e punta a EBP
Mov EBP, ESP EBP = ESP cioè 4
Add EBP, 8 EBP = 12, quindi punta a 20


Ci siamo ?

Tutte le istruzioni sono presenti in un offset (indirizzo) di memoria gestito in modo automatico dal registro IP (EIP a 32 bits)..quindi :


Push ESI offset 100h
Push 20 offset 101h (ossia l'indirizzo iniziale + la lunghezza dell'istruzione precedente...appunto 1 bytes)
Call Print offset 106h (push 20 è di 5 bytes) ora nello stack la cpu salva l'offset + la lunghezza dell'istruzione call e quindi 10Bh
Xor EAX, EAX offset 10Bh

Print:
NOP
Ret imposta EIP dallo stack quindi torna all'offset 10Bh


Ci siamo ?

AnonimoVeneziano
23-05-2004, 13:43
Ok , Grazie :)