DeviceIoControl Interface

In questo tutorial studieremo i VxD dinamici. Specificatamente, impareremo come crearli, caricarli e utilizzarli.
Scaricate qui l'esempio.

Interfacce del VxD

Esistono un totale di 4 interfacce che il VxD ci fornisce. Conosciamo gia' i servizi del VxD. Le interfacce V86 e PM sono funzioni che sono richiamabili rispettivamente da applicazioni V86 e PM. Sebbene le applicazioni V86 e PM siano a 16-bit, non possiamo usare queste due interfacce da una applicazione win32. In Windows 95, la Microsoft ha aggiunto un'interfaccia addizionale per le applicazioni win32 in modo tale da richiamare i servizi dei VxD: l'interfaccia DeviceIoControl.

L'interfaccia DeviceIoControl

Molto semplicemente, l'interfaccia DeviceIoControl e' un modo, per le applicazioni win32, di chiamare funzioni contenute all'interno dei VxD. Non confondete le funzioni chiamate tramite la DeviceIoControl con i servizi del VxD: non sono la stessa cosa. Per esempio, la funzione 1 del DeviceIoControl puo' non essere la stessa cosa del servizio 1 del VxD. Dovete pensare le funzioni del DeviceIoControl come un gruppo separato di funzioni, fornito per l'uso solo con le applicazioni win32.
Dal momento che essa e' un interfaccia, ci sono due lati:

Dal lato dell'applicazione win32:

Essa deve innanzitutto chiamare la CreateFile per aprire/caricare il VxD. Se la chiamata ha successo, il VxD sara' in memoria e la CreateFile ritornera' l'handle del VxD in eax.
Successivamente chiamate la funzione API DeviceIoControl per selezionare la funzione da eseguire. La DeviceIoControl ha la seguente sintassi:

Dal lato del VxD:

Esso deve elaborare il messaggio w32_deviceIoControl. Quando il VxD riceve il messaggio w32_deviceIoControl, i suoi registri hanno i seguenti valori: DIOCParams e' definita come segue: Dalla struttura DIOCParams, ottenete tutte le informazioni che l'applicazione win32 ha passato al vostro VxD.
Esso deve almeno elaborare la DIOC_Open (un valore e' passato in dwIoControlCode) che la VWIN32 inviera' al vostro VxD quando una applicazione win32 chiama la CreateFile per aprire il vostro VxD. Se il VxD e' pronto, deve ritornare 0 in eax e la chiamata a CreateFile avra' successo. Se il vostro VxD non e' pronto, deve ritornare un valore diverso da zero in eax e la CreateFile fallira'. Oltre alla DIOC_Open, il vostro VxD ricevera' il codice DIOC_Closehandle dalla VWIN32 quando l'applicazione win32 chiude il device handle.

Scheletro minimo di un VxD dinamico caricabile dalla CreateFile

.386p
include vmm.inc
include vwin32.inc

DECLARE_VIRTUAL_DEVICE DYNAVXD,1,0, DYNAVXD_Control,\
     UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER

Begin_control_dispatch DYNAVXD
    Control_Dispatch w32_DeviceIoControl, OnDeviceIoControl
End_control_dispatch DYNAVXD

VxD_PAGEABLE_CODE_SEG
BeginProc OnDeviceIoControl
    assume esi:ptr DIOCParams
    .if [esi].dwIoControlCode==DIOC_Open
        xor eax,eax
    .endif
   ret
EndProc OnDeviceIoControl
VxD_PAGEABLE_CODE_ENDS

end

;--------------------------------------------------------------------------------------------------------------------------------
;   Module Definition File (NDT: File di definizione del modulo)
;---------------------------------------------------------------------------------------------------------------------------------
VXD DYNAVXD DYNAMIC

SEGMENTS
    _LPTEXT    CLASS 'LCODE'    PRELOAD NONDISCARDABLE
    _LTEXT      CLASS 'LCODE'    PRELOAD NONDISCARDABLE
    _LDATA      CLASS 'LCODE'    PRELOAD NONDISCARDABLE
    _TEXT        CLASS 'LCODE'    PRELOAD NONDISCARDABLE
    _DATA        CLASS 'LCODE'    PRELOAD NONDISCARDABLE
    CONST       CLASS 'LCODE'    PRELOAD NONDISCARDABLE
    _TLS          CLASS 'LCODE'    PRELOAD NONDISCARDABLE
    _BSS          CLASS 'LCODE'    PRELOAD NONDISCARDABLE
    _LMGTABLE    CLASS 'MCODE'    PRELOAD NONDISCARDABLE IOPL
    _LMSGDATA    CLASS 'MCODE'    PRELOAD NONDISCARDABLE IOPL
    _IMSGTABLE   CLASS 'MCODE'    PRELOAD DISCARDABLE IOPL
    _IMSGDATA     CLASS 'MCODE'    PRELOAD DISCARDABLE IOPL
    _ITEXT             CLASS 'ICODE'     DISCARDABLE
    _IDATA             CLASS 'ICODE'    DISCARDABLE
    _PTEXT            CLASS 'PCODE'    NONDISCARDABLE
    _PMSGTABLE    CLASS 'MCODE'    NONDISCARDABLE IOPL
    _PMSGDATA    CLASS 'MCODE'    NONDISCARDABLE IOPL
    _PDATA            CLASS 'PDATA'    NONDISCARDABLE SHARED
    _STEXT            CLASS 'SCODE'    RESIDENT
    _SDATA            CLASS 'SCODE'    RESIDENT
    _DBOSTART    CLASS 'DBOCODE'    PRELOAD NONDISCARDABLE CONFORMING
    _DBOCODE       CLASS 'DBOCODE'    PRELOAD NONDISCARDABLE CONFORMING
    _DBODATA        CLASS 'DBOCODE'    PRELOAD NONDISCARDABLE CONFORMING
    _16ICODE          CLASS '16ICODE'    PRELOAD DISCARDABLE
    _RCODE             CLASS 'RCODE'

EXPORTS

    DYNAVXD_DDB  @1

Esempio Completo

Di seguito c'e' il codice sorgente di una applicazione win32 che carica un VxD dinamico e chiama una funzione nel VxD tramite l'API DeviceIoControl.

; VxDLoader.asm

.386
.model flat,stdcall
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib

.data
    AppName db "DeviceIoControl",0
    VxDName db "\\.\shellmsg.vxd",0
    Success db "Il VxD e' stato caricato con successo!",0
    Failure db "Il VxD non e' stato caricato!",0
    Unload db "Il VxD viene ora scaricato!",0
    MsgTitle db "Esempio di DeviceIoControl",0
    MsgText db "Sono stato chiamato da un VxD!",0
    InBuffer dd offset MsgTitle
                  dd offset MsgText
.data?
    hVxD dd ?
.code
start:
    invoke CreateFile,addr VxDName,0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,0
    .if eax!=INVALID_HANDLE_VALUE
        mov hVxD,eax
        invoke MessageBox,NULL,addr Success,addr AppName,MB_OK+MB_ICONINFORMATION
        invoke DeviceIoControl,hVxD,1,addr InBuffer,8,NULL,NULL,NULL,NULL
        invoke CloseHandle,hVxD
        invoke MessageBox,NULL,addr Unload,addr AppName,MB_OK+MB_ICONINFORMATION
    .else
        invoke MessageBox,NULL,addr Failure,NULL,MB_OK+MB_ICONERROR
    .endif
    invoke ExitProcess,NULL
end start

Segue il codice sorgente di un VxD dinamico che viene chiamato da vxdloader.asm
; ShellMsg.asm
.386p
include vmm.inc
include vwin32.inc
include shell.inc

DECLARE_VIRTUAL_DEVICE SHELLMSG,1,0, SHELLMSG_Control,\
     UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER

Begin_control_dispatch SHELLMSG
    Control_Dispatch w32_DeviceIoControl, OnDeviceIoControl
End_control_dispatch SHELLMSG

VxD_PAGEABLE_DATA_SEG
    pTitle dd ?
    pMessage dd ?
VxD_PAGEABLE_DATA_ENDS

VxD_PAGEABLE_CODE_SEG
BeginProc OnDeviceIoControl
    assume esi:ptr DIOCParams
    .if [esi].dwIoControlCode==DIOC_Open
        xor eax,eax
    .elseif [esi].dwIoControlCode==1
        mov edi,[esi].lpvInBuffer
        ;-----------------------------------
        ; copia il titolo del messaggio in un buffer
        ;-----------------------------------
        VMMCall _lstrlen, <[edi]>
        inc eax
        push eax
        VMMCall _HeapAllocate,<eax,HEAPZEROINIT>
        mov pTitle,eax
        pop eax
        VMMCall _lstrcpyn,<pTitle,[edi],eax>
        ;-----------------------------------
        ; copia il testo del messaggio in un buffer
        ;-----------------------------------
        VMMCall _lstrlen, <[edi+4]>
        inc eax
        push eax
        VMMCall _HeapAllocate,<eax,HEAPZEROINIT>
        mov pMessage,eax
        pop eax
        VMMCall _lstrcpyn,<pMessage,[edi+4],eax>
        mov edi,pTitle
        mov ecx,pMessage
        mov eax,MB_OK
        VMMCall Get_Sys_VM_Handle
        VxDCall SHELL_sysmodal_Message
        VMMCall _HeapFree,pTitle,0
        VMMCall _HeapFree,pMessage,0
        xor eax,eax
    .endif
    ret
EndProc OnDeviceIoControl
VxD_PAGEABLE_CODE_ENDS

end

Analisi:

Cominciamo dal VxDLoader.asm.
    invoke CreateFile,addr VxDName,0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,0
    .if eax!=INVALID_HANDLE_VALUE
        mov hVxD,eax
        ....
    .else
        invoke MessageBox,NULL,addr Failure,NULL,MB_OK+MB_ICONERROR
    .endif
Chiamiamo la CreateFile per caricare il VxD dinamico. Notate la flag FILE_FLAG_DELETE_ON_CLOSE . Questa flag indica a Windows di rilasciare il VxD quando l'handle del VxD ritornato dalla CreateFile viene chiuso. Se la CreateFile ha avuto successo, salviamo l'handle del VxD per un uso futuro.
        invoke MessageBox,NULL,addr Success,addr AppName,MB_OK+MB_ICONINFORMATION
        invoke DeviceIoControl,hVxD,1,addr InBuffer,8,NULL,NULL,NULL,NULL
        invoke CloseHandle,hVxD
        invoke MessageBox,NULL,addr Unload,addr AppName,MB_OK+MB_ICONINFORMATION
Il programma visualizza una message box quando il VxD viene caricato/scaricato. Esso chiama la DeviceIoControl con il dwIoControlCode impostato su 1 e passa l'indirizzo del InBuffer nel parametro lpInBuffer, e la dimensione del InBuffer (8) nel nInBufferSize. InBuffer e' un vettore dword con due elementi: ogni elemento e' l'indirizzo di una stringa di testo.
    MsgTitle db "Esempio di DeviceIoControl",0
    MsgText db "Sono stato chiamato da un VxD!",0
    InBuffer dd offset MsgTitle
                  dd offset MsgText
Ora spostiamo la nostra attenzione sul VxD.
Esso elabora solo il messaggio w32_deviceIoControl. Quando il messaggio w32_deviceIoControl viene inviato, la procedura OnDeviceIoControl viene chiamata.
BeginProc OnDeviceIoControl
    assume esi:ptr DIOCParams
    .if [esi].dwIoControlCode==DIOC_Open
        xor eax,eax
La OnDeviceIoControl elabora il codice DIOC_Open ritornando 0 in eax.
    .elseif [esi].dwIoControlCode==1
        mov edi,[esi].lpvInBuffer
Essa elabora inoltre il codice di controllo 1. La prima cosa che fa e' estrarre i dati nel lpvInBuffer che contiene le due dword passate nel lpInBuffer dell'API DeviceIoControl. Esso mette l'indirizzo del vettore di dword in edi per l'estrazione. La prima dword e' l'indirizzo del testo che sara' usato come titolo della message box. La seconda dword e' l'indirizzo del testo che sara' usato come testo della message box.
        ;-----------------------------------
        ; copia il titolo del messaggio in un buffer
        ;-----------------------------------
        VMMCall _lstrlen, <[edi]>
        inc eax
        push eax
        VMMCall _HeapAllocate,<eax,HEAPZEROINIT>
        mov pTitle,eax
        pop eax
        VMMCall _lstrcpyn,<pTitle,[edi],eax>
Esso calcola la lunghezza del titolo della message box chiamando il servizio _lstrlen della VMM. Il valore in eax ritornato dalla _lstrlen e' la lunghezza della stringa. Incrementiamo la lunghezza di 1 per tenere in considerazione il NULL conclusivo. Successivamente allochiamo un blocco di memoria largo abbastanza per contenere la stringa terminante con il NULL chiamando _HeapAllocate. La flag HEAPZEROINIT indica alla _HeapAllocate di azzerare il blocco di memoria. La _HeapAllocate ritorna l'indirizzo del blocco di memoria in eax. Quindi copiamo la stringa dall'indirizzo di memoria dell'applicazione win32 nel blocco di memoria che abbiamo allocato. Faremo la stessa operazione sulla stringa di testo che useremo come testo della message box.
        mov edi,pTitle
        mov ecx,pMessage
        mov eax,MB_OK
        VMMCall Get_Sys_VM_Handle
        VxDCall SHELL_sysmodal_Message
Salviamo gli indirizzi del titolo e del messaggio rispettivamente in edi e in ecx. Mettiamo la flag desiderata in eax, ottenendo l'handle della VM del sistema chiamando la Get_Sys_VM_handle e quindi la SHELL_Sysmodal_Message. La SHELL_SysModal_Message e' la versione modale di sistema della SHELL_Message. Essa congela il sistema finche' l'utente non risponde alla message box.
        VMMCall _HeapFree,pTitle,0
        VMMCall _HeapFree,pMessage,0
Quando la SHELL_Sysmodal_Message ritorna, possiamo liberare i blocchi di memoria chiamando _HeapFree.

Conclusione

L'interfaccia DeviceIoControl rende ideale l'utilizzo di un VxD dinamico come una estensione di DLL a ring-0 per la vostra applicazione win32.

Traduzione italiana a cura di: fabio@privacy.nu

Torna alla main page! Torna alla pagina principale