La Structure Client Register

Dans ce nouveau Tutorial nous allons examiner une autre structure importante, à savoir la structure 'Client Register'. Downloadez l'exemple.

Un peu de théorie

Les VxDs diffèrent grandement des applications win32, win16, ou DOS normales. Les VxDs sont, la plupart du temps, inerte tandis que les autres progs normaux eux, font leur petit business (ils Runs, il ne sont pas inertes). Ils agissent comme des surperviseurs qui lancent d'autres progs Ring-3 et les corrigent (nous font part d'éventuelles erreurs) quand quelque chose se passe mal. La situation typique est la suivante :
  1. Une interruption (du programme en cours) se produit.
  2. Le VMM prend le contrôle.
  3. Le VMM sauvegarde les différents registres (du programme).
  4. Les services d'interruption du VMM ou les appels d'autres VxDs font leur boulot.
  5. Le VMM redonne le contrôle au programme qui avait été interrompu.
Ce qui est intéressant dans ce processus c'est que, la seule façon pour le VMM (le Manager de Machines Virtuelles) d'affecter le programme (l'application) interrompu c'est de modifier l'image des registers sauvegardés. Par exemple, si le VMM considère que le programme interrompu doit reprendre à une adresse différente, il peut changer la valeur de CS:IP (le EIP) dans l'image des registers sauvegardés et ensuite quand le programme reprend son cours, il reprendra l'exécution au nouveau CS:IP.
Le VMM sauvegarde les valeurs des registres au point d'interruption dans la structure Client Register .
Client_Reg_Struc STRUC
Client_EDI DD ?
Client_ESI DD ?
Client_EBP DD ?
Client_res0 DD ?
Client_EBX DD ?
Client_EDX DD ?
Client_ECX DD ?
Client_EAX DD ?
Client_Error DD ?
Client_EIP DD ?
Client_CS DW ?
Client_res1 DW ?
Client_EFlags DD ?
Client_ESP DD ?
Client_SS DW ?
Client_res2 DW ?
Client_ES DW ?
Client_res3 DW ?
Client_DS DW ?
Client_res4 DW ?
Client_FS DW ?
Client_res5 DW ?
Client_GS DW ?
Client_res6 DW ?
Client_Alt_EIP DD ?
Client_Alt_CS DW ?
Client_res7 DW ?
Client_Alt_EFlags DD ?
Client_Alt_ESP DD ?
Client_Alt_SS DW ?
Client_res8 DW ?
Client_Alt_ES DW ?
Client_res9 DW ?
Client_Alt_DS DW ?
Client_res10 DW ?
Client_Alt_FS DW ?
Client_res11 DW ?
Client_Alt_GS DW ?
Client_res12 DW ?
Client_Reg_Struc ENDS
Vous pouvez voir qu'il y a deux groupes dans cette structure : Client_xxx et Client_Alt_xxx. Ceci exige une petite explication. Dans une VM (Machine Virtuelle) donnée, il peut y avoir deux liens d'exécution : le mode V86 et le mode-protégé. Si une interruption se produit pendant qu'un programme V86 est actif, le Client_xxx contiendra les images des registres du programme V86, le Client_Alt_xxx contiendra ceux du programme PM (en Mode-Protégé). Alternativement, si une interruption arrive quand le programme PM est actif, le Client_xxx contiendra les valeurs des registres du programme PM alors que le Client_Alt_xxx contiendra les valeurs des registres du programme V86. Le Client_resX est réservé et n'est pas employé.
Après avoir examiné cette structure, vous pouvez vous poser une question: Comment puis-je faire pour changer uniquement un byte  dans les registres, comme AL ? La structure ne décrit que des registres de taille "WORD" et "DWORD". N'ayez aucune crainte. Jetez un coup d'oeil à l'intérieur du fichier vmm.inc. Il y a deux structures complémentaires justement dans ce but : Client_Word_Reg_Struc et Client_Byte_Reg_Struc. Si vous voulez avoir accès à des registres de taille "BYTE" ou "WORD", vous devez utiliser les types Client_Reg_Struc dans Client_Word_Reg_Struc ou Client_Byte_Reg_Struc en accord avec vos besoins.
 

La question suivante : Comment pouvons-nous obtenir le Pointer sur la structure 'Client Register' ?

C'est en réalité facile : la plupart du temps, le VMM met l'adresse de la structure Client Register dans ebp quand il appelle notre VxD. La structure Client Register dans ce cas est la VM actuelle (La Machine Virtuelle). Sinon, vous pouvez obtenir ce Pointer à partie du handle de la VM. Rappelez-vous que le handle d'une VM est en réalité l'adresse linéaire du du bloc de contrôle de la VM.
cb_s STRUC
CB_VM_Status DD ?
CB_High_Linear DD ?
CB_Client_Pointer DD ?
CB_VMID DD ?
CB_Signature DD ?
cb_s ENDS
CB_Client_Pointer contient le Pointer sur la structure Client Register de Cette VM. Par exemple, vous pouvez obtenir le Pointer de la structure 'Client Register' de la Machine Virtuelle courante par le code suivant :
VMMCall Get_Cur_VM_Handle   ; retourne le handle de la VM actuelle dans ebx
assume ebx:ptr cb_s
mov ebp,[ebx+CB_Client_Pointer] ; pointe sur la structure Client Register
Maintenant que nous comprenons le fonctionnement de la structure 'Client Register', nous pouvons passer à son utilisation. Nous emploierons la structure Client Register' pour passer les valeurs des registers à une interruption MS-DOS, à savoir, int 21h service 2h, qui est le service : Affichage des Caractères. Ce service MS-DOS prend comme paramètre le caractère qui doit être affiché dans dl. Si nous passons le caractère de sonnerie (07h) à ce service, il fera jouer un petit buzzer par le haut-parleur du PC.
Rappelez-vous que l'int 21h est un service MS-DOS et il est ainsi disponible sous le mode V86, comment pouvons-nous appeler une interruption V86 à partir d'un VxD ? Une façon est d'employer le service Exec_Int. Ce service du VMM prend le numéro de l'interruption qui doit être appelé dans eax. Il simule l'interruption indiquée et reprend l'exécution de la VM. Cependant, il doit être appelé dans un bloc d'exécution emboîté. Un bloc d'exécution emboîté est entre parenthèses, c'est à dire borné par Begin_Nest_V86_Exec (ou bien) Begin_Nest_Exec et End_Nest_Exec. Ainsi si nous voulons appeler l'int 21h, service 2h, nous avons besoin de changer les membres Client_ah et Client_Dl de la structure Client_Byte_Reg_Struc dans le bloc d'exécution emboîté et stocker ensuite la valeur 21h dans eax. Quand tout est prêt, appelez Exec_Int.

Exemple

L'exemple est un VxD dynamique qui utilise le service 2h de l'Int 21h pour jouer un petit son.
 
.386p
include \masm\include\vmm.inc
include \masm\include\vwin32.inc
include \masm\include\v86mmgr.inc

VxDName TEXTEQU <VXDINT>
ControlName TEXTEQU <VXDINT_Control>
VxDMajorVersion TEXTEQU <1>
VxDMinorVersion TEXTEQU <0>

VxD_STATIC_DATA_SEG
VxD_STATIC_DATA_ENDS

VXD_LOCKED_CODE_SEG
;----------------------------------------------------------------------------
; Souvenez-vous : le nom du VxD DOIT ÊTRE majuscule sinon il ne se Chargera/Déchargera pas
;----------------------------------------------------------------------------
DECLARE_VIRTUAL_DEVICE %VxDName,%VxDMajorVersion,%VxDMinorVersion,
            \%ControlName,UNDEFINED_DEVICE_ID,UNDEFINED_INIT_ORDER

Begin_control_dispatch %VxDName
        Control_Dispatch W32_DEVICEIOCONTROL, OnDeviceIoControl
End_control_dispatch %VxDName

VXD_LOCKED_CODE_ENDS

VXD_PAGEABLE_CODE_SEG
BeginProc OnDeviceIoControl
 assume esi:ptr DIOCParams
 .if [esi].dwIoControlCode==1
  Push_Client_State
  VMMCall Begin_Nest_V86_Exec
  assume ebp:ptr Client_Byte_Reg_Struc
  mov [ebp].Client_dl,7
  mov [ebp].Client_ah,2
  mov eax,21h
  VMMCall Exec_Int
  VMMCall End_Nest_Exec
  Pop_Client_State
EndI:
 .endif
 xor eax,eax
 ret
EndProc OnDeviceIoControl
VXD_PAGEABLE_CODE_ENDS

end

Analyse

Push_Client_State
Il n'y a pas grand chose à analyser. Quand le VxD reçoit le message DeviceIoControl, ebp controle déjà la structure Client Register de la VM actuelle. Nous appelons la macro Push_Client_State pour sauvegarder l'état actuel du Client Registers (la totalité des registres) sur la pile. Nous rétablissons par la suite le Client Register grâce à Pop_Client_State.
VMMCall Begin_Nest_V86_Exec
marque le début du bloc d'exécution emboîté en appelant Begin_Nest_V86_Exec.
assume ebp:ptr Client_Byte_Reg_Struc
mov [ebp].Client_dl,7
mov [ebp].Client_ah,2
modifie les images des registres dl et ah dans la structure Client Register. Ces valeurs une fois changées seront employées par l'interruption.
mov eax,21h
VMMCall Exec_Int
Exec_Int attend le numéro de l'interruption dans eax. Nous voulons nous servir de l'int 21h. Alors nous appelons Exec_Int pour Simuler l'interruption.
VMMCall End_Nest_Exec
Pop_Client_State
Une fois que Exec_Int retourne, nous terminons l'exécution du bloque emboîté et rétablissons les valeurs sauvegardées sur la pile du Client Register.
Vous entendrez que le haut-paleur de votre PC joue le caractère sonnore.

[Iczelion's Win32 Assembly Homepage]


Traduit par Morgatte