L'Interface DeviceIoControl

Nous allons nous intéresser aux VxDs dynamiques dans ce Tutorial. Spécifiquement, nous allons apprendre à les créer, les charger et les employer.
Downloadez l'exemple ici.

VxD Interfaces

Les VxDs constituent un ensemble de 4 types d'interfaces. Nous connaissons déjà les services des VxDs. Les interfaces V86 et PM sont des fonctions qui sont CALLables à partir d'applications tournant respectivement en mode V86 et PM. Vu que les applications V86 et PM sont des 16 bits, nous ne pouvons employer aucune de ces deux interfaces pour une application win32. Avec Windows 95, Microsoft ajoute une autre interface pour les applications win32 ainsi ils peuvent appeler les services des VxDs, c'est l'interface: DeviceIoControl.

L'interface DeviceIoControl

Pour rester simple, l'interface DeviceIoControl est une manière pour les applications win32 d'appeler des fonctions à l'intérieur des VxDs. Ne confondez pas les fonctions appelées grâce à DeviceIoControl et les services VxD : ce ne sont pas les mêmes. Par exemple, la fonction n°1 DeviceIoControl n'est pas la même que le service VxD n°1. Vous pouvez vous représenter la fonctions DeviceIoControl un peu comme un groupe de fonctions à part, uniquement prévues pour des applications win32.
Puisque c'est une interface, il y a deux parties :

Du côté de l'application win32 :

Notre programme (l'application) doit d'abord appeler la fonction CreateFile pour ouvrir/charger un VxD. Si l'appel est couronné de succès, le VxD sera en mémoire et CreateFile renverra l' handle du VxD dans eax.
Ensuite on appelle la fonction API DeviceIoControl pour choisir la fonction à exécuter. DeviceIoControl présente la syntaxe suivante :

Du côté du VxD :

Il doit traiter le message w32_deviceIoControl. Lorsque le VxD reçoit le message w32_deviceIoControl, ses registres ont les valeurs suivantes : DIOCParams est ainsi définie: Avec la structure DIOCParams, vous avez toute l'information que votre application win32 a passée à votre VxD.
Votre VxD doit au moins traiter DIOC_Open (qui est une valeur a passer dans dwIoControlCode) lequel VWIN32 enverra à votre VxD lorsque une application win32 appellera l'API CreateFile pour ouvrir votre VxD. Si votre VxD est prêt, il doit mettre 0 dans eax et l'appel de CreateFile sera couronné de succès. Si votre VxD n'est pas prêt, il doit renvoyer une valeur non-nille dans eax et CreateFile échouera. A part DIOC_Open, votre VxD recevra le code DIOC_Closehandle en provenance de VWIN32 lorsque l'application win32 referme l'handle du Device.

Voici le squelette minimum d'un VxD dynamique, il est chargeable grâce à l'API 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
;---------------------------------------------------------------------------------------------------------------------------------
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





Exemple complet

Ci-dessous voici le code source de l'application win32 qui charge le VxD dynamique et appelle une fonction dans ce VxD via 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 "The VxD is successfully loaded!",0
    Failure db "The VxD is not loaded!",0
    Unload db "The VxD is now unloaded!",0
    MsgTitle db "DeviceIoControl Example",0
    MsgText db "I'm called from a 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

Voici ci-dessous le code source du VxD dynamique qui est appelé par 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
        ;-----------------------------------
        ; Copie le titre du message dans le buffer
        ;-----------------------------------
        VMMCall _lstrlen, <[edi]>
        inc eax
        push eax
        VMMCall _HeapAllocate,<eax,HEAPZEROINIT>
        mov pTitle,eax
        pop eax
        VMMCall _lstrcpyn,<pTitle,[edi],eax>
        ;-----------------------------------
        ; Copie le texte du message dans le 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

Analyse:

Commençons par 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
Nous appelons CreateFile pour charger le VxD dynamique. Notez le flag FILE_flag_DELETE_ON_CLOSE. Ce flag charge Windows de décharger le VxD quand CreateFile renvoie l'handle du VxD fermé. Si CreateFile est couronné de succès, nous stockons l'handle du VxD pour une future utilisation.
        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
Le programme affiche une MessageBox lorsque le VxD est chargé ou déchargé. Il appelle DeviceIoControl avec dwIoControlCode 1 et passe l'adresse de InBuffer au paramètre lpInBuffer et la taille de InBuffer (8) à nInBufferSize. InBuffer est un tableau dword de deux éléments : chaque élément représente l'adresse d'une chaîne de caractères.
    MsgTitle db "DeviceIoControl Example",0
    MsgText db "I'm called from a VxD!",0
    InBuffer dd offset MsgTitle
                  dd offset MsgText
Passons au VxD maintenant.
Il traite uniquement le message w32_deviceIoControl. Quand le message w32_deviceIoControl est envoyé, la procédure OnDeviceIoControl est appelée.
BeginProc OnDeviceIoControl
    assume esi:ptr DIOCParams
    .if [esi].dwIoControlCode==DIOC_Open
        xor eax,eax
OnDeviceIoControl traite le code de DIOC_OPEN en rentournant 0 dans eax.
    .elseif [esi].dwIoControlCode==1
        mov edi,[esi].lpvInBuffer
Il traite aussi le contrôle code 1. La première chose c'est de l'extraire des données de lpvInBuffer qui sont deux dwords a passer au membre lpInBuffer de l'API DeviceIoControl. Il met l'adresse du tableau dword dans edi pour l'extraction. Le premier dword est l'adresse du texte qui doit être utilisé comme titre de la MessageBox. Le second dword est l'adresse du texte qui doit être utilisé comme texte de cette même MessageBox.
        ;-----------------------------------
        ; Copie le titre du message dans le buffer
        ;-----------------------------------
        VMMCall _lstrlen, <[edi]>
        inc eax
        push eax
        VMMCall _HeapAllocate,<eax,HEAPZEROINIT>
        mov pTitle,eax
        pop eax
        VMMCall _lstrcpyn,<pTitle,[edi],eax>
Il calcule la longueur du titre de la MessageBox en appelant le service _lstrlen du VMM (Manager de Machines Virtuelles). La valeur dans eax rendu par _lstrlen est la longueur de la chaîne de caractères. Nous augmentons la longueur de 1 pour tenir compte du caratère NULL de terminaison. Ensuite nous réservons un bloc de mémoire assez grand pour contenir la chaîne de caractères avec en plus le NULL en appelant _HeapAllocate. Le flag HEAPZEROINIT fait débuter _HeapAllocate au bloc mémoire zéro. _HeapAllocate renvoie l'adresse du bloc mémoire dans eax. Nous recopions alors la chaîne de caractères de de l'application win32 dans le bloc mémoire que nous avons réservé. Nous faisons la même opération sur la chaîne de caractères que nous emploierons en tant que texte (contenu) de la MessageBox.
        mov edi,pTitle
        mov ecx,pMessage
        mov eax,MB_OK
        VMMCall Get_Sys_VM_Handle
        VxDCall SHELL_sysmodal_Message
Nous stockons les adresses du titre et du contenu respectivement dans edi et ecx. Mettez le flag désirable dans eax, obtenez l'handle de la VM 'système VM' (Machine Virtuelle principale) en appelant Get_Sys_VM_handle et appelez ensuite SHELL_Sysmodal_Message. SHELL_SysModal_Message est le système de la version modale de SHELL_Message. Il gèle le système avant que l'utilisateur ne réponde à la MessageBox. On est stoppé sur le Call de la MessageBox tant qu'on a pas cliqué sur un de ses boutons.
        VMMCall _HeapFree,pTitle,0
        VMMCall _HeapFree,pMessage,0
Quand SHELL_Sysmodal_Message retourne, nous pouvons libérer les blocs mémoires en appelant _HeapFree.

Conclusion

L'interface DeviceIoControl est l'idéal pour employer un VxD dynamique en tant que DLL en Ring-0 comme extension de votre application win32.

[Iczelion's Win32 Assembly Homepage]


Traduit par Morgatte