coordinamento di Francesco Sileno
In principio era il byteAd esempio possiamo usarlo per generare piccoli eseguibili con estensione .COM, programmando direttamente in assembly. I .COM hanno due principali limitazioni rispetto ai .EXE:
Come si usa il debug? Bene, avviatelo, e vi ritroverete al suo prompt ('-'). Dando il comando '?', avrete la lista di comandi disponibili e una breve spiegazione per ciascuno di essi (spiegazione assolutamente idiota, se avete il DOS tradotto in italiano). I comandi che per ora ci interessano sono, pressapoco:
Salve, mondo!Detto questo passiamo ad un semplice programmino che stampa la solita, banale, ripugnante frase: "Hello World!".Diamo il comando 'A 100' (il debug lavora SOLO con numeri esadecimali). -a100 24D6:0100 _Il debug è in attesa dell'istruzione. Immettiamo 'PUSH CS'. -a100 3678:0100 push cs 3678:0101 _automaticamente l'offset viene incrementato all'indirizzo della prossima istruzione. Scrivete l'intero programma: -a100 3678:0100 push cs 3678:0101 pop ds 3678:0102 mov ah,9 3678:0104 mov dx,10e 3678:0107 int 21 3678:0109 mov ax,4c00 3678:010C int 21 3678:010E _Come vedete abbiamo già fatto uso di 2 chiamate ad un INT. L'INT 21, che raccoglie in sè la maggior parte delle funzioni messe a disposizione dal DOS. Il registro AH è usato per specificare quale funzione si intende usare. La funzione 09h è per stampare stringhe su schermo, vuole che DS:DX punti all' inizio della stringa, che deve terminare col carattere '$'. Sento che il solito curiosone chiede come facciamo a stampare il carattere '$'... beh, non me lo ricordo! Ma non c'è da preoccuparsi, quando s'inizierà a scrivere direttamente in memoria video questi problemi da DOS non si avranno più. La funzione 4Ch è per terminare il programma, ogni programma DOS DEVE avere questa chiamata alla fine. Il parametro AL serve per l'ERRORLEVEL, spesso usato nei batch. Ora dobbiamo inserire la stringa, come vedete ho calcolato l'offset a 10Eh. Il comando E permette di inserire valori esadecimali, per cui dovremo convertire la nostra stringa in una sequenza di codici ASCII esadecimali. H e l l o w o r l d ! $ 48 65 6C 6C 6F 20 77 6F 72 6C 64 21 24 -e 10e 3678:010E 00.48 00.65 3678:0110 00.6c 00.6c 00.4f 00.20 00.77 00.6f 00.72 00.6c 3678:0118 00.64 00.21 00.24 - _Notate che per modificare il byte successivo, dovete premere SPAZIO, mentre all'ultimo byte terminate l'inserimento con ENTER. Adesso salviamo, prima di inchiodare il PC. -nhello.com -rcx CX 0000 :200 -w 100 Scrittura di 00200 byte in corso - _Per non tagliare fuori pezzi di codice o dati, mettiamo in CX un valore abbastanza alto. Oppure facciamo un paio di semplici calcoli per sapere il valore esatto (ultimobyte - 100h + 1, in questo caso dovrebbero essere 22h byte). Uscite dal debug, e lanciate HELLO.COM. Il risultato dovrebbe essere, inchiodamenti a parte, qualcosa molto simile (uguale, oserei dire) a C:\DEP>hello Hello world! C:\DEP> _Bene, avete fatto il vostro primo programma in assembly! Facile no? Modifichiamo.Adesso supponiamo che vogliate modificare la stringa in modo che l'output su schermo siaC:\DEP>prova Hello world! I'm Cthulhu, the beast. C:\DEP> _dove ovviamente al posto di 'Cthulhu' potete mettere il vostro nome, a seconda di quanto vi sentiate animaleschi in questo momento. Come fare? Semplice: C:\DEP>debug prova.com -d10e 3C01:0100 48 65 He 3C01:0110 6C 6C 6F 20 77 6F 72 6C-64 21 24 36 34 00 C9 3B llo world!$64..; 3C01:0120 AA 00 74 03 E9 62 FD E9-62 FD 8B 1E 5F AE B8 00 ..t..b..b..._... 3C01:0130 44 CD 21 80 E2 80 88 16-61 AE 74 0D 80 3E BD AE D.!.....a.t..>.. 3C01:0140 00 74 06 BA EB 9F E9 49-04 8B 1E 5F AE 8B 0E 88 .t.....I..._.... 3C01:0150 AA 8B 16 8C AA 2B CA 75-0E E8 F4 02 80 3E C0 AE .....+.u.....>.. 3C01:0160 00 75 65 8B 0E 88 AA 1E-8E 1E 69 AA B4 3F CD 21 .ue.......i..?.! 3C01:0170 1F 72 8F 8B C8 E3 51 80-3E 61 AE 00 75 07 80 3E .r....Q.>a..u..> 3C01:0180 C1 AE 00 74 1B 8B D1 8B-3E 8C AA B0 1A 06 ...t....>..... - _Sapendo che per andare a capo in DOS sono necessari 2 caratteri, CR e LF, rispettivamente 0Dh e 0Ah, quello che dobbiamo fare noi è aggiungere a partire dall'offset 11Ah la nuova parte di stringa. NOTA: l'uso dei due caratteri per andare a capo è stato deciso per mantenere la compatibilità con le normali macchine da scrivere meccaniche, che hanno una leva per incrementare la riga (LF), e il carrello da spostare per tornarne all'inizio (CR). I primi prototipi di PC infatti non avevano il tasto ENTER, ma un meccanismo molto simile alla coppia carrello-leva delle macchine da scrivere. Poi Bill Gates ha suggerito l'uso di un singolo tasto, in uno dei suoi rari momenti di lucidità. Più tardi fu sempre lui a suggerire la creazione di un tasto di reset, per evitare di dover spegnere e riaccendere la macchina durante l'uso di Windows... ma questa è un altra storia. Dopo aver effettuato le modifiche, il dump dovrà apparire così: -d10e 3C01:0100 48 65 He 3C01:0110 6C 6C 6F 20 77 6F 72 6C-64 21 0D 0A 0D 0A 49 27 llo world!....I' 3C01:0120 6D 20 43 74 68 75 6C 68-75 2C 20 74 68 65 20 62 m Cthulhu, the b 3C01:0130 65 61 73 74 2E 0D 0A 24-61 AE 74 0D 80 3E BD AE east...$a.t..>.. 3C01:0140 00 74 06 BA EB 9F E9 49-04 8B 1E 5F AE 8B 0E 88 .t.....I..._.... 3C01:0150 AA 8B 16 8C AA 2B CA 75-0E E8 F4 02 80 3E C0 AE .....+.u.....>.. 3C01:0160 00 75 65 8B 0E 88 AA 1E-8E 1E 69 AA B4 3F CD 21 .ue.......i..?.! 3C01:0170 1F 72 8F 8B C8 E3 51 80-3E 61 AE 00 75 07 80 3E .r....Q.>a..u..> 3C01:0180 C1 AE 00 74 1B 8B D1 8B-3E 8C AA B0 1A 06 ...t....>..... - _Salvate di nuovo, con lo stesso procedimento di poco fa, e lanciate. In alternativa potete anche usare degli hex editor, come il DiskEdit o l'HackerView, per effettuare la modifica della stringa, facendo attenzione a non sovrascrivere il codice. Per non impazzirePer quanto questo metodo faccia sentire tanto dei pionieri, e ci aiuti a capire cosa succede ad un livello molto basso, ha dei grossi limiti. Anzitutto, gli offset per i riferimenti a dati o a locazioni di salto non possono essere determinati se non una volta scritto TUTTO il programma, a meno di non avere uno schema dei codici operativi delle istruzioni assembly e una gran DOSe di masochismo. A questo limite si ovvia lasciando tutti i riferimenti a valori casuali (magari FFFF per riconoscerli facilmente dopo), provvedendo poi ad aggiornarli al valore corretto una volta inserito tutto il codice.Ma il fatto peggiore è la correzione dei bug: la modifica di una sola istruzione può portare alla riscrittura di TUTTE le istruzioni seguenti, e magari allo spostamento di aree dati. Es: 3BDA:0100 B409 MOV AH,09 3BDA:0102 B200 MOV DL,00 3BDA:0104 CD21 INT 21 3BDA:0106 B8004C MOV AX,4C00 3BDA:0109 CD21 INT 21Supponiamo che all'offset 102h avremmo dovuto scrivere MOV DX,200. Correggendo, viene fuori 3BDA:0100 B409 MOV AH,09 3BDA:0102 BA0002 MOV DX,200 3BDA:0105 21B8004C AND [BX+SI+4C00],DI 3BDA:0109 CD21 INT 21e quindi dobbiamo riscrivere tutto quello che segue. Si può rimediare usando la redirezione del DOS e dei template batch, ovvero creando un file che contiene tutti i comandi per il debug, e mandarglielo in ingresso. Per il nostro esempio, dovremmo creare un file prova.dbg così fatto: a100 push cs pop ds mov ax,9 mov dx,ffff int 21 mov ax,4c00 int 21 % n prova.com rcx 200 w100 q %(con il carattere % ho indicato una linea vuota che DEVE esserci) Dando il comando TYPE PROVA.DBG | DEBUG, otterremo il file PROVA.COM. Adesso dobbiamo ancora rimetterci mano per aggiornare il riferimento alla stringa e per inserire quest'ultima. I compilatoriIl passo successivo consiste nel passare ad un compilatore assembly, magari lo avete già sperduto nei 40Mb del C++, oppure ripiegare su un compilatore share/freeware, e ce ne sono parecchi in giro. Con uno di questi compilatori potrete usare riferimenti simbolici sia per le locazioni di salto che per le variabili.Quello che segue è lo scheletro che uso io per iniziare un nuovo programma: .MODEL SMALL .STACK 2048 .DATA .CODE ASSUME CS:@CODE,DS:@DATA MOV AX,@DATA MOV DS,AX ; [ ... ] FINE: MOV AX,4C00H INT 21H ENDVediamo un po' cosa significano alcune di queste buffe parole:
.MODEL SMALL .STACK 2048 .DATA frase db 'Hallo World!',0Dh,0Ah,0Dh,0Ah,'I'm Cthulhu, the beast.'0Dh,'$' .CODE ASSUME CS:@CODE,DS:@DATA MOV AX,@DATA MOV DS,AX mov ah,9 mov dx,offset frase int 21h FINE: MOV AX,4C00H INT 21H ENDDB sta per Define Byte, inizializza una sequenza di byte e fa si che l'etichetta FRASE contenga l'offset del primo di essi. Vedete che ho usato la direttiva OFFSET ad un certo punto? Ricordate che quando usavate il debug dovevate inserirlo a mano DOPO aver scritto tutto? Ora è il compilatore che se lo calcola! Notate anche che ogni numero espresso in esadecimale deve essere seguito da un H, in quanto il compilatore assume per default la notazione decimale. Per creare il programma, dovrete prima compilare (*ASM), poi eseguire il link (*LINK), ed avrete il vostro .EXE. Evolution...Adesso, per rimanere nella scia dell'incredibile fantasia che ci precede, facciamo un programma che ci chiede un nome e poi ci ricama qualche considerazione sopra. Per leggere una stringa da tastiera, usiamo la funzione DOS 0Ah, che prevede in ingresso un buffer particolare. Il primo byte di questo buffer indica la lunghezza massima di se stesso (quindi max 255 caratteri, in pratica 254), il secondo è usato per dirci quanti caratteri sono stati effettivamente letti, escludendo il carattere ENTER (che però è incluso nel buffer).Guardate un po' se vi piace: .MODEL SMALL .STACK 2048 .DATA domanda db 'Ciao, quale sarebbe il tuo nome?',0Dh,0Ah,'> $' nome db 255, 0, 254 DUP (00) ricamo1 db 0Dh,0Ah,'Così ti chiami $' ricamo2 db '... bah, pensavo meglio.',0Dh,0Ah,'$' .CODE ASSUME CS:@CODE,DS:@DATA MOV AX,@DATA MOV DS,AX mov ah,9 mov dx,offset domanda int 21h mov ax,0A00h mov dx,offset nome int 21h mov bx,0 ; nota1 mov bl,[nome+1] mov [nome+bx+2],'$' mov ah,9 mov dx,offset ricamo1 int 21h mov ah,9 ; nota2 mov dx,offset nome inc dx inc dx int 21h mov ah,9 mov dx,offset ricamo2 int 21h FINE: MOV AX,4C00H INT 21H ENDNota 1: per stampare il nostro nome, usando la funzione DOS, dobbiamo inserire il carattere '$' alla fine del medesimo. Allora ci andiamo a prendere il secondo byte del buffer, che ci dice di quanti caratteri è composto il nostro nome, lo incrementiamo di 2 per tenere in conto i primi 2 byte usati per altri scopi, lo sommiamo all'offset di partenza della stringa, e sbattiamo lì il nostro '$'. Ah, quelle strane mov si spiegano col fatto che in realtà l'etichetta NOME è un puntatore, e le parentesi quadre hanno lo stesso effetto di '*' in C, o del '^' in PASCAL. Nota 2: Per stampare il nome, dobbiamo partire DOPO i 2 byte che servono ad altro, quindi incrementiamo un paio di volte l'offset di partenza. Il risultato è questo (mi auguro): C:\DEP>prova.exe Ciao, quale sarebbe il tuo nome? > GianEzechiele Così ti chiami GianEzechiele... bah, pensavo meglio. C:\DEP> _Beh, anche per questa volta è tutto, vi lascio col vostro stupefacente programma, immaginando già le cose sconce che gli farete dire tra breve... assemblatamente, Cthulhu |
Copyright © 1996 Beta Working Group. Tutti i diritti riservati.