Dopo un po 'di hacking e giocherellando, sono riuscito a farlo funzionare. Non è così semplice come speravo sarebbe, quindi aggrappati al tuo posto (s).
In primo luogo, è necessario rendersi conto (per quanto astratto possa sembrare) che DOS sia un sistema single-user, non-multitasking. In questo caso particolare, significa che non è possibile avere due processi in esecuzione contemporaneamente. È necessario attendere che un processo finisca l'esecuzione prima di passare a un altro processo. La concorrenza di processo può essere in qualche modo emulata con i processi TSR (Terminate and Stay Resident), che rimangono in memoria nonostante siano stati terminati ed è possibile riprendere la loro esecuzione agganciando alcuni interrupt dal loro codice e chiamandolo successivamente da qualche altro codice. Tuttavia, non è lo stesso tipo di concorrenza che viene utilizzato dai sistemi operativi moderni, come Windows e Linux. Ma non era questo il punto.
Hai detto che stai utilizzando la NASM come assemblatore di scelta, quindi ho presupposto che tu avessi inviato il tuo codice ai file COM, che a loro volta vengono eseguiti dal prompt dei comandi DOS. I file COM vengono caricati dal prompt dei comandi all'offset 100h
(dopo aver caricato un salto in quella posizione viene eseguito) e non contengono nient'altro che codice e dati "snelli", senza intestazioni, quindi sono i più facili da produrre.
Ho intenzione di spiegare la sorgente di assemblaggio a pezzi, in modo che tu possa (forse) avere un'idea migliore di ciò che sta succedendo sotto il cofano.
Il programma inizia con
org 100h
section .data
exename db "C:\hello.com",0
exename2 db "C:\nasm\nasm.exe",0
cmdline db 0,0dh
direttiva org
, che specifica l'origine del file quando in realtà caricato nella memoria - nel nostro caso, si tratta di 100h
. Seguono dichiarazioni di tre etichette, exename
e exename2
, che sono percorsi con terminazione null dei programmi da eseguire e cmdline
, che specifica la riga di comando che deve essere ricevuta dal processo appena creato. Si noti che non è solo una stringa normale: il primo byte è il numero di caratteri nella riga di comando, quindi la riga di comando stessa e un ritorno a capo. In questo caso, non abbiamo parametri della riga di comando, quindi l'intera cosa si riduce a db 0,0dh
. Supponiamo di voler passare -h -x 3
come parametri: in tal caso, dovremmo dichiarare questa etichetta come db 8," -h -x 3",0dh
(notare lo spazio extra all'inizio!). Passando ...
dummy times 20 db 0
paramblock dw 0
dw cmdline
dw 0 ; cmdline_seg
dw dummy ; fcb1
dw 0 ; fcb1_seg
dw dummy ; fcb2
dw 0 ; fcb2_seg
L'etichetta dummy
soli 20 byte che contengono zeri. Quello che segue è l'etichetta paramblock
, che è una rappresentazione della struttura EXEC menzionata da Daniel Roethlisberger. Il primo elemento è uno zero, il che significa che il nuovo processo dovrebbe avere lo stesso ambiente del suo genitore. Seguono tre indirizzi: alla riga di comando, al primo FCB e al secondo FCB. È necessario ricordare che gli indirizzi in modalità reale sono costituiti da due parti: l'indirizzo del segmento e l'offset nel segmento. Entrambi gli indirizzi sono lunghi 16 bit. Sono scritti nella memoria in modo little endian, con il primo offset. Pertanto, si specifica la riga di comando come offset cmdline
e gli indirizzi degli FCB come offset sull'etichetta dummy
, poiché gli FCB stessi non verranno utilizzati, ma gli indirizzi devono puntare a una posizione di memoria valida in entrambi i casi. I segmenti devono essere riempiti in fase di esecuzione, poiché il caricatore sceglie il segmento in cui viene caricato il file COM.
section .text
entry:
mov ax, cs
mov [paramblock+4], ax
mov [paramblock+8], ax
mov [paramblock+12],ax
Iniziamo il programma impostando i campi dei segmenti nella struttura paramblock
. Poiché per i file COM, CS = DS = ES = SS
, vale a dire che tutti i segmenti sono uguali, è sufficiente impostare tali valori su ciò che si trova nel registro cs
.
mov ax, 4a00h
mov bx, 50
int 21h
Questo è in realtà uno dei punti più difficili dell'applicazione. Quando un file COM viene caricato nella memoria da DOS, viene assegnata tutta la memoria disponibile per impostazione predefinita (la CPU non ne ha idea, dal momento che è in modalità reale, ma gli interni DOS ne tengono comunque traccia). Pertanto, la chiamata al syscall di EXEC causa il fallimento con No memory available
. Pertanto, dobbiamo dire a DOS che non abbiamo davvero bisogno di tutta quella memoria eseguendo il "RESIZE MEMORY BLOCK" AH=4Ah
chiamata (Ralf Brown). Il registro bx
dovrebbe avere la nuova dimensione del blocco di memoria in unità da 16 byte ("paragrafi"), quindi lo impostiamo su 50, con 800 byte per il nostro programma. Devo ammettere che questo valore è stato scelto in modo casuale, ho provato a impostarlo su qualcosa che avrebbe senso (ad esempio un valore basato sulla dimensione effettiva del file), ma non sono riuscito a ottenere nulla.ES
è il segmento che vogliamo "ridimensionare", nel nostro caso quello è CS
(o qualsiasi altro, poiché sono tutti uguali quando viene caricato un file COM). Dopo aver completato questa chiamata, siamo pronti per caricare il nostro nuovo programma in memoria ed eseguirlo.
mov ax, 0100h
int 21h
cmp al, '1'
je .prog1
cmp al, '2'
je .prog2
jmp .end
.prog1:
mov dx, exename
jmp .exec
.prog2:
mov dx, exename2
Questo codice dovrebbe essere abbastanza auto-esplicativo, si sceglie il percorso del programma inserito nel DX
in base alla stdin.
.exec:
mov bx, paramblock
mov ax, 4b00h
int 21h
Questo è dove è chiamato l'attuale EXEC
syscall (AH=4Bh
). AL
contiene 0, il che significa che il programma deve essere caricato ed eseguito. DS:DX
contiene l'indirizzo del percorso dell'eseguibile (scelto dalla parte di codice precedente) e ES:BX
contiene l'indirizzo dell'etichetta paramblock
, che contiene la struttura EXEC
.
.end:
mov ax, 4c00h
int 21h
Dopo aver terminato l'esecuzione del programma chiamato da exec
, il programma principale viene terminato con un codice di uscita pari a zero eseguendo il AH=4Ch
syscall.
Grazie a vulture-
da ## asm su Freenode per assistenza. Ho provato questo con DOSBox e MS-DOS 6.22, quindi spero che funzioni anche per te.
Ho davvero bisogno di aiuto, dando tutta la mia reputazione come ricompensa. –
L'API DOS ('int 21h') è una parte di software assoluta, inefficace, piuttosto in disuso e indesiderata che non deve essere più utilizzata. Sei ** assolutamente ** sicuro di aver bisogno di usarlo? –
@DanielKozar Sì, assolutamente. Non darei la mia reputazione altrimenti. Ho davvero bisogno di aiuto su questo. –