Programmazione di VxD:
Fondamenti
Ora sappiamo costruire un VxD che non fa nulla. In questo capitolo, lo
renderemo piu' produttivo, aggiungendo un handler di messaggi di
controllo.
Inizializzazione e chiusura del VxD
Ci sono due tipi di VxD: quelli statici e
quelli dinamici. Ognuno di questi due tipi ha diversi metodi di caricamento.
Essi quindi ricevono tipi diversi di messaggi di inizializzazione e di
chiusura.
VxD
statici:
La
VMM carica un VxD specifico:
- Quando un programma residente in
real-mode genera un int 2Fh, 1605h per caricarlo
- Quando il VxD e' specificato nel
registro sotto la chiave:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\key\StaticVxD=percorso
- Quando il VxD
e' specificato nel system.ini nella sezione [386enh]:
Nel periodo di sviluppo,
vi consiglio di caricare il VxD dal system.ini dato che, se qualcosa non va per
il verso giusto, e windows non riesce a partire, potete comunque modificare il
file dal DOS. Non potete invece far cio' con il registro.
Quando la VMM
carica il vostro VxD statico, il VxD ricevera' tre messaggi di controllo dal
sistema nel seguente ordine:
- Sys_Critical_Init La VMM
invia questo messaggio di controllo dopo essere entrata in modalita' protetta
ma comunque prima di avere reso disponibili gli interrrupt. La maggior parte
dei VxD non necessita di gestire questi messaggi, tranne
quando:
- Il vostro
VxD "hooka" alcuni interrupts che saranno chiamati successivamente da
altri VxD o programmi in modalita' protetta. Dato che gli interrupt vengono
disattivati quando elaborate il messaggio di controllo, potete essere sicuri
che l'interrupt che state "hookando" non verra' chiamato nell'arco di tempo
nel quale lo "hookate".
- Il vostro
VxD fornisce alcuni servizi che saranno chiamati durante l'inizializzazione
di altri VxD. Per esempio, alcuni VxD che si caricano dopo il vostro VxD
potrebbero avere la necessita' di chiamare uno dei servizi del vostro VxD
durante l'elaborazione del messaggio di controllo Device_Init. Dato che il
messaggio Sys_Critical_Init viene inviato prima del messaggio Device_Init,
dovrete inizializzare i vostri servizi durante il messaggio
Sys_Critical_Init.
Se elaborate questo
messaggio, dovrete fare la vostra inizializzazione il piu' velocemente
possibile per prevenire che l'interrupt hardware vada perso a causa
dell'eccessiva latenza. (Ricordatevi che gli interrupt sono
disattivati)
- Device_Init La VMM invia questo
messaggio di controllo dopo che gli interrupt vengono attivati. La maggior
parte dei VxD esegue l'inizializzazione in risposta a questo messaggio.
Poiche' gli interrupt sono disattivati le operazioni che comportano un consumo
di tempo, possono essere fatte senza la paura di perdere l'interrupt hardware.
Dovete fare qui la vostra inizializzazione.
- Init_Complete Dopo che tutti i
VxD hanno elaborato il messaggio Device_Init ma prima che la VMM abbia
rilasciato tutti i segmenti di inizializzazione (classi di segmenti ICODE e
RCODE), la VMM invia questo messaggio di controllo. Pochi VxD hanno la
necessita' di elaborare questo messaggio.
Il vostro VxD
deve resettare il flag di carry se l'inizializzazione ha avuto successo,
altrimenti , se c'e' stato un errore, lo dovete impostare ad 1 prima del return
. Se il vostro VxD non necessita di alcu na inizializzazione non
e' necessario che elaboriate alcuno di questi tre messaggi
.
Quando giunge il momento di chiudere il VxD statico, la VMM invia i
seguenti messaggi di controllo:
- System_Exit2 Quando il
vostro VxD riceve questo messaggio, Windows 95 sta per essere chiuso. Tutte le
altre VM sono gia' state distrutte. Comunque la CPU si trova ancora in
modalita' protetta ed e' ancora sicuro eseguire codice in real-mode nella VM
di sistema. In questo momento la kernel32.dll e' gia' stata scaricata dalla
memoria.
- Sys_Critical_Exit2 Il
vostro VxD riceve questo messaggio quando tutti i VxD hanno elaborato la
System_Exit2 e gli interrupt sono stati disattivati.
Alla
maggior parte dei VxD non serve elaborare questi due messaggi, tranne quando
volete preparare il sistema ad entrare in real-mode. Dovete sapere che quando si
chiude Windows 95, esso entra in real-mode. Quindi se il vostro VxD fa qualcosa
allimmagine real-mode che la rende instabile, esso la deve ripristinare in
questo momento.
Potreste magari chiedervi perche' questi due
messaggi hanno attaccato un "2". Ricordate che quando la VMM carica i VxD statici, essa carica
innanzitutto quelli con il piu' basso ordine di inizializzazione in modo che i
VxD possano affidarsi a servizi che altri VxD hanno gia' caricato in precedenza.
Per esempio, se il VxD2 si affida ai servizi del VxD1, esso deve impostare un
ordine di inizializzazione maggiore di quello del VxD1. L'ordine con il quale
verranno caricati sara':
..... VxD1 ===> VxD2 === > VxD3
.....
Ora, durante lo scaricamento dalla memoria, cio'
comporta che i VxD inizializzati per primi devono poi anche essere chiusi per
primi dato che essi potrebbero chiamare nuovamente servizi di VxD caricati prima
di loro. Riferendoci all'esempio di prima l'ordine sara':
.... VxD3 ===> VxD2 === >
VxD1.....
Nell'esempio sopra, se il VxD2 ha chiamato alcuni
servizi del VxD1 durante la sua inizializzazione, esso potrebbe anche utilizzare
i servizi del VxD1 durante il suo scaricamento. Le System_Exit2 e Sys_Critical_Exit2 sono inviate in
un ordine inverso all'
inizializzazione. Cio' significa che, quando il
VxD2 riceve questi messaggi, il VxD1 non e' ancora stato chiuso e si possono
quindi utilizzare ancora i servizi di tutti i VxD caricati precedentemente.
Questi messaggi non verranno usati per i VxD piu' recenti.
Ci sono anche
altri due messaggi di uscita:
- Device_Reboot_Notify2
Notifica ai VxD che la VMM sta per riavviare il sistema. Gli interrupt in
questo momento sono ancora attivi.
- Crit_Reboot_Notify2
Notifica ai VxD che la VMM sta per riavviare il sistema. Gli interrupt sono
disattivati.
Ora potete supporre ci sono i messaggi
Device_Reboot_Notify e Crit_Reboot_Notify, ma essi non
vengono inviati in un ordine inverso, come invece avviene per la versione
"2".
VxD
dinamici:
Questi VxD possono essere caricati e scaricati
dinamicamente durante una sessione di Windows 95. Questa opzione non e'
disponibile sotto Windows 3.x. L'obiettivo fondamentale dei VxD dinamici e' di
supportare la riconfigurazione dinamica delle periferiche hardware Plug and
Play. Comunque, li potete caricare e scaricare dalla vostra applicazione win32,
e cio' li rende ideali come estensione a ring-0 della vostra
applicazione.
L'esempio della spiegazione precedente e' un VxD
statico. Potete convertirlo in un VxD dinamico aggiungendo la parola chiave
DYNAMIC alla
dichiarazione VXD
nel file .DEF.
VXD FIRSTVXD
DYNAMIC
Cio' e tutto quello che dovete fare per convertire
un VxD statico in uno dinamico.
I VxD dinamici possono
essere caricati:
- Mettendoli
nella cartella \SYSTEM\IOSUBSYS nella cartella di Windows. I VxD in questa cartella verranno
caricati dall'Input Output Supervisor
(IOS). I VxD in questa cartella dovrebbero
supportare i layer device drivers, e quindi potrebbe non essere una buona idea
caricare i vostri VxD dinamici in questo modo.
- Usando il
servizio VxD Loader. Il VxDLDR e' un VxD statico che puo'
caricare i VxD dinamici. Potete chiamare i suoi servizi da altri VxD o da del
codice a 16-bit.
- Usando l'API
CreateFile da una
applicazione Win32. Specificate il VxD dinamico che volete caricare alla
CreateFile nel seguente formato:
Per
esempio, se volete caricare un VxD dinamico chiamato FirstVxD che e' nella
directory corrente, dovete fare come qui descritto:
.data
VxDName db
"\\.\FirstVxD.VXD",0
......
.data?
hDevice dd
?
.....
.code
.....
invoke CreateFile, addr
VxDName,0,0,0,0, FILE_FLAG_DELETE_ON_CLOSE,0
mov
hDevice,eax
......
invoke
CloseHandle,hDevice
......
La flag FILE_FLAG_DELETE_ON_CLOSE specifica
che il VxD viene scaricato quando l'handle ritornato dalla CreateFile viene
chiuso.
Se usate la CreateFile per caricare un VxD dinamico, il VxD
deve gestire il
messaggio w32_DeviceIoControl. La VWIN32 invia questo messaggio di controllo al vostro VxD quando esso
viene caricato per la prima volta col metodo CreateFile. Il vostro VxD deve
ritornare 0 in eax in risposta a questo messaggio. Il messaggio w32_DeviceIoControl viene inviato
anche quando l'applicazione chiama l'API DeviceIoControl per comunicare con
il VxD. Esamineremo l'interfaccia DeviceIoControl in spiegazioni
successive.
Un VxD dinamico riceve un messaggio durante
l'inizializzazione:
E un messaggio di
controllo al momento della sua chiusura:
Un VxD dinamico non
ricevera' i messaggi di controllo Sys_Critical_Init, Device_Init e Init_Complete poiche' questi
messaggi vengono inviati durante l'inizializzazione della VM di sistema. Oltre a
cio' i VxD dinamici ricevono tutti gli altri messaggi di controllo quando sono
in memoria. Possono fare tutto cio' che fa un VxD statico. In poche parole, un
VxD dinamico viene caricato con diversi meccanismi e riceve differenti messaggi
di inizializzazione/chiusura, ma tranne cio' esso e' in grado di fare cio' che
fa un VxD statico.
Altri messaggi
di controllo di sistema
Durante il tempo in ci un
VxD sta in memoria, esso ricevera' molti altri messaggi di controllo oltre a
quelli relativi all'inizializzazione ed alla chiusura. Alcuni di essi sono
relativi alla gestione di macchine virtuali e altri a vari eventi. Per esempio i
messaggi di controllo relativi alla VM sono:
- Create_VM
- VM_Critical_Init
- VM_Suspend
- VM_Resume
- Close_VM_Notify
- Destroy_VM
Sta nella vostra
responsabilita' rispondere ai messaggi di controllo ai quali siete
interessati.
Creare
procedure all'interno di un VxD
Dichiarate una procedura
in un VxD all'interno di un segmento. Dovete innanzitutto definire un segmento e
quindi inserire al suo interno una procedura. Per esempio, se volete che la
vostra funzione stia in un segmento pageable, dovete prima definirlo come
questo:
VxD_PAGEABLE_CODE_SEG
[Mettete
qui la vostra procedura]
VxD_PAGEABLE_CODE_ENDS
Potete mettere piu' procedure all'interno di
un segmento. Voi, in quanto programmatori del VxD dovete scegliere in quale
segmento mettere le vostre procedure. Se esse devono stare in memoria in ogni
momento come dei gestori di interrupt hardware, mettetele in un segmento locked.
Altrimenti potete metterle in un segmento pageable.
Definite le vostre procedure con le macro
BeginProc e EndProc.
BeginProcnome
EndProcnome
nome e' il
nome della vostra procedura. La macro BeginProc puo' ricevere molti piu' parametri,
consultate la documentazione DDK di Win95 per maggiori dettagli. Ma la maggior
parte delle volte userete solo il nome della procedura.
Dovreste usare le macro BeginProc-EndProc all'interno delle normali direttive
proc-endp poiche' le macro BeginProc-EndProc forniscono maggiori funzionalita'
rispetto proc-endp.
Convenzioni di
programmazione del VxD
Utilizzo dei
registri
Il vostro VxD puo' usare ogni registro, FS e GS. Ma
dovreste stare attenti nel modificare i registri di segmento. Specialmente, non
dovreste modificare CS e SS a meno che non siate totalmente sicuri di cio' che
state facendo. Potete usare DS e ES purche' vi ricordiate di ripristinare i loro
valori al ritorno della funzione. Due flag sono particolarmente importanti: le
flag di direzione e di interrupt. Non dovreste disattivare gli interrupt per un
periodo di tempo troppo esteso e se modificate la flag di direzione, non
dimenticate di riportarla allo stato originario al ritorno della
funzione.
Convenzione di
passaggio dei parametri
Ci sono due convenzioni
di chiamata per i servizi dei VxD: register-based e stack-based. Nei
servizi basati sul registro, passate i parametri al servizio tramite vari
registri e potete controllare il flag di carry dopo la chiamata al servizio per
vedere se essa ha avuto successo. Non potete presumere che i valori nei registri
generali vengano mantenuti al ritorno della chiamata. Nei servizi basati sullo
stack, "pushate" i parametri sullo stack e ottenete il valore di ritorno in eax.
I servizi basati sullo stack preservano i registri ebx, esi, edi e ebp. La
maggior parte dei servizi basati sui registri hanno avuto origine in Windows
3.x. La maggior parte delle volte, potete differenziare questi due tipi di
servizi guardando i loro nomi. Se esso comincia con una sottolineatura come
_HeapAllocate, esso
e' un servizio basato sullo stack (C) (eccetto per pochi servizi esportati dal
VWIN32.VXD). Se il
servizio non inizia con una sottolineatura, esso e' un servizio basato sui
registri.
Chiamata dei
servizi dei VxD
Chiamate la VMM e i servizi dei VxD usando le macro
VMMCall e
VxDCall. Entrambe
le macro hanno la stessa identica sintassi. Usate la macro VMMCall quando volete chiamare i
servizi VxD esportati dalla VMM e utilizzate invece a VxDCall quando chiamate i servizi
esportati da VxD diversi dalla VMM.
VMMCallservizio
; per chiamate ai servizi basate sui registri
VMMCall _servizio, <lista
dei parametri> ; per
chiamate ai servizi basate sullo stack
La VMMCall e VxDCall sono espanse in un int
20h seguito da una dword che vi ho gia' descritto nei capitoli precedenti ma
esse sono piu' convenienti da usare. Nel caso di servizi basati sullo stack,
dovete inserire la lista dei parametri fra i simboli di minore e
maggiore:
VMMCall _HeapAllocate,
<<dimensione del miobuffer>,
HeapLockedIfDP>
_HeapAllocate e' un servizio basato
sullo stack. Accetta due parametri. Dobbiamo inserirli all'interno di altri
simboli di minore e maggiore. Tuttavia il primo parametro e' una espressione che
la macro puo' interpretare scorrettamente, di conseguenza la delimitiamo
ulteriormente con dei simboli di minore e maggiore.
Indirizzi
Flat
Nei vecchi
tools, l'assembler e il linker generavano indirizzi incorretti quando usavate
l'operatore offset.
Cosi' i programmatori di VxD usano offset
flat: invece di offset. La vmm.inc contiene una
macro per rendere cio' piu' facile. OFFSET32
si espande in offset flat:. Cosi' se volete usare
l'operatore offset, potete invece usare OFFSET32.
Nota: Ho sperimentato l'operatore
offset quando ho
scritto questa guida. Esso ha generato indirizzi corretti e quindi penso che il
bug sia stato rimosso nel MASM 6.14. Ma per essere sicuri, dovreste utilizzare
comunque la macro OFFSET32 al posto del normale offset.
Traduzione italiana a cura di: fabio@privacy.nu
|
Torna alla
pagina principale |