Accueil
Qui suis-je ?
 
Mes livres
Les pompes rotodynamiques
Commander un livre
 
Mes programmes
Nopopup
Aide française
Télécharger
English help
Download
 
Optipump
English help
Download
 
SaveRes
Aide française
Télécharger
English help
Download
 
TTFName
Aide française
Télécharger
 
Mes articles
Composant ASP en C/C++
VxD W9x en ASM
 
Mes photos
Afrique australe
Autres photos
 
 visiteurs.
 pages vues.
 connecté(s).
Accueil-->VxD W9x en ASM - Chapitre 9

Le Virtual Memory Manager

1 Introduction

Dans le chapitre précédent vous avez appris à simuler une interruption V86. Cependant, il y a un problème qui n'a pas été vu encore : l'échange de données entre code VxD et code V86. Nous apprendrons comment employer le Memory Manager V86 pour vous faciliter la tache.

2 Théorie

Si votre VxD travaille avec des routines V86, ce sera nécessaire tôt ou tard de passer des données de grande taille vers ou depuis un programme V86. Le passage d'une grande quantité de données via les registres est hors de question. La tentative suivante pourrait être d'allouer un bloc de mémoire dans le ring-0 et de passer un pointeur vers ce bloc de mémoire via des registres de manière à ce que le code de V86 puisse avoir accès aux données. Si vous faites ainsi, votre système a toute les chances de planter parce que le mode V86 exige une adresse de type segment:offset, et non une adresse linéaire.

Il y a pas mal de solutions à ce problème. Cependant, je choisis une solution aisée pour faire la démonstration des services fournis par le Memory Manager V86.

Si vous pouvez trouver un bloc de mémoire libre dans la région V86 que vous utilisez comme buffer de communication, cela résoudrait un des problèmes. Cependant, la traduction d'adresse de pointeur reste un problème. Vous pouvez résoudre ces deux problèmes en employant les services du Memory Manager V86.

Le Memory Manager V86 est un VxD statique qui gère la mémoire des applications V86. Il fournit aussi des services EMS et XMS aux applications V86 et des services de traduction d'API aux autres VxD. La traduction d'API est en réalité un processus de copie de données en ring-0 vers un buffer en région V86 et de passation d'adresse du buffer au code V86. Aucune magie ici. Le Memory Manager V86 gère un buffer de traduction qui est un bloc de mémoire dans la région V86 pour la passation de données de VxD vers la région V86 et vice versa. De base ce buffer fait 4 kilo-octets. Vous pouvez augmenter la taille de ce buffer V86MMGR_Set_Mapping_Info.

Maintenant que vous savez ce qu’est le buffer de traduction, comment copier ou extraire des données de ce buffer ?

Cette question implique deux services : V86MMGR_Allocate_Buffer et V86MMGR_Free_Buffer.

  • V86MMGR_Allocate_Buffer alloue un bloc de mémoire à partir su buffer de et copie optionnellement les données du ring-0 dans le bloc V86 alloué
  • V86MMGR_Free_Buffer fait l’inverse: il copie optionnellement les données du bloc V86 alloué dans un buffer en ring-0 et libère le bloc de mémoire alloué par V86MMGR_Allocate_Buffer.
  • Notez que le Memory Manager V86 gère les buffers alloués comme une pile. Cela signifie que l’allocation et la désallocation doit observer la règle FIFO. Ainsi si vous faites deux appels à V86MMGR_Allocate_Buffe, le premier appel V86MMGR_Free_Buffer libérera le buffer alloué lors du second appel V86MMGR_Allocate_Buffer.

    Nous pouvons maintenant continuer à examiner la définition V86MMGR_Allocate_Buffer. C'est un service basé sur les registres.

  • ebx
    Handle de la VM courante.
  • ebp
    Pointeur vers la structure de registres clients de la VN courante.
  • ecx
    Nombre d’octets à allouer depuis le buffer de traduction.
  • carry flag
    Désarmé  si vous ne voulez pas copier de données depuis le buffer en ring-0 vers le bloc alloué.
    Activé si vous voulez copier de données depuis le buffer en ring-0 vers le bloc alloué.
  • fs:esi
    Offset du sélecteur du bloc de mémoire en ring-0 qui contient les données à copier vers le buffer alloué. Ignoré si carry flag est désarmé.
  • Si l’appel est réussi :

  • carry flag est désarmé.
  • ecx contient le nombre d’octets effectivement alloué dans le buffer de traduction.
    Cette valeur peut être inférieure à la valeur demandée, aussi devez vous sauvegarder cette valeur pour l’appel futur de V86MMGR_Free_Buffer.
  • edi contient l’adresse V86 du bloc alloué. Le segment est dans le mot de poids fort, l’offset dans celui de poids faible.
  • Le carry flag est armé si une erreur survient.

    V86MMGR_Free_Buffer accepte exactement les mêmes paramètres que V86MMGR_Allocate_Buffer.

    Ce qui arrive en réalité quand vous appelez V86MMGR_Allocate_Buffer est que vous allouez un bloc de mémoire dans la région V86 de la VM courante et obtenez l'adresse V86 de ce bloc de mémoire dans edi. Nous pouvons employer ces services pour échanger des données.

    En plus de la traduction d'API, le Memory Manager V86 offre aussi le mapping d'API vers d'autres VxD. Le mapping d'API est en réalité le processus consistant à mapper des pages de mémoire étendue dans la région V86 de chaque VM. Vous pouvez employer V86MMGR_Map_Pages pour faire le mapping d'API. Avec ce service, les pages sont mappées dans la même adresse linéaire dans chaque VM. C'est un gaspillage d'espace mémoire si vous voulez travailler avec une seule VM. Le mapping d'API est également plus lent que la traduction d'API, aussi préférez la traduction d'API autant que possible. Le mapping d'API est exigé pour quelques opérations V86 qui doivent avoir accès à la même adresse linéaire et être présents dans toutes les VM.

    3 Exemple

    3.1 Le VxD

    Cet exemple utilise la traduction d’API avec le service 440Dh code mineur 66h de l’int 21h (Get_Media_ID), pour obtenir le label de volume de votre premier disque non amovible.

    ;---------------------------------------------------------------
    ;                            VxDLabel.asm
    ;---------------------------------------------------------------
    .386p
    include \masm\include\vmm.inc
    include \masm\include\vwin32.inc
    include \masm\include\v86mmgr.inc

    VxDName TEXTEQU <VXDLABEL>
    ControlName TEXTEQU <VXDLABEL_Control>
    VxDMajorVersion TEXTEQU <1>
    VxDMinorVersion TEXTEQU <0>

    VxD_STATIC_DATA_SEG
    VxD_STATIC_DATA_ENDS

    VXD_LOCKED_CODE_SEG
    ;------------------------------------------------------------------------
    ;Le nom du vxd DOIT être en majuscules
    ;------------------------------------------------------------------------
    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
      VMMCall Get_Sys_VM_Handle
      mov Handle, ebx
      assume ebx:ptr cb_s
      mov ebp,[ebx+CB_Client_Pointer]
      mov ecx, sizeof MID
      stc
      push esi
      mov esi,OFFSET32 MediaID
      push ds
      pop fs
      VxDCall V86MMGR_Allocate_Buffer
      pop esi
      jc EndI
      mov AllocSize, ecx
      Push_Client_State
      VMMCall Begin_Nest_V86_Exec
      assume ebp:ptr Client_Byte_Reg_Struc
      mov [ebp].Client_ch, 8
      mov [ebp].Client_cl, 66h
      assume ebp:ptr Client_word_reg_struc
      mov edx, edi
      mov [ebp].Client_bx, 3 ; lecteur A
      mov [ebp].Client_ax, 440dh
      mov [ebp].Client_dx, dx
      shr edx,16
      mov [ebp].Client_ds, dx
      mov eax,21h
      VMMCall Exec_Int
      VMMCall End_Nest_Exec
      Pop_Client_State
      ;-------------------------------
      ; retrouve les données
      ;-------------------------------
      mov ecx, AllocSize
      stc
      mov ebx, Handle
      push esi
      mov esi,OFFSET32 MediaID
      push ds
      pop fs
      VxDCall V86MMGR_Free_Buffer
      pop esi
      mov edx, esi
      assume edx:ptr DIOCParams
      mov edi,[edx].lpvOutBuffer
      mov esi,OFFSET32 MediaID.midVolLabel
      mov ecx,11
      rep movsb
      mov byte ptr [edi],0
      mov ecx,[edx].lpcbBytesReturned
      mov dword ptr [edx],11
    EndI:
     .endif
     xor eax, eax
     ret
    EndProc OnDeviceIoControl
    VXD_PAGEABLE_CODE_ENDS

    VXD_PAGEABLE_DATA_SEG
     MID struct
      midInfoLevel dw 0
      midSerialNum dd ?
      midVolLabel db 11 dup(?)
      midFileSysType db 8 dup(?)
     MID ends

     MediaID MID <>
     Handle dd ?
     AllocSize dd ?
    VXD_PAGEABLE_DATA_ENDS

    end

    3.2 Le chargeur du VxD

    ;------------------------------------------------------------
    ;                        Label.asm
    ; Le chargeur win32 du VxD
    ;------------------------------------------------------------
    .386
    .model flat, stdcall 
    option casemap : none

    include \masm32\include\windows.inc
    include \masm32\include\user32.inc
    include \masm32\include\kernel32.inc
    includelib \masm32\lib\user32.lib
    includelib \masm32\lib\kernel32.lib

    DlgProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
    .data
    Failure db "Impossible de charger VxDLabel.VXD",0
    AppName db "Get Disk Label",0
    VxDName db "\\.\vxdLabel.vxd",0
    OutputTemplate db "Label du volume 1 du lecteur C",0

    .data?
    hInstance HINSTANCE ?
    hVxD dd ?
    DiskLabel db 12 dup(?)
    BytesReturned dd ?

    .const
      IDD_VXDRUN    equ 101
      IDC_LOAD      equ 1000

    .code
    start:
     invoke GetModuleHandle, NULL
     mov    hInstance, eax
     invoke DialogBoxParam, hInstance, IDD_VXDRUN ,NULL,addr DlgProc, NULL
     invoke ExitProcess, eax

    DlgProc proc hDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
     .IF uMsg==WM_INITDIALOG
      invoke CreateFile, addr VxDName,0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,0
      .if eax==INVALID_HANDLE_VALUE
       invoke MessageBox, hDlg, addr Failure, addr AppName, MB_OK+MB_ICONERROR
       mov hVxD,0
       invoke EndDialog, hDlg, NULL
      .else
       mov hVxD, eax
      .endif
     .elseif uMsg==WM_CLOSE
      .if hVxD!=0
       invoke CloseHandle, hVxD
      .endif
      invoke EndDialog, hDlg,0
     .ELSEIF uMsg==WM_COMMAND
      mov eax, wParam
      mov edx, wParam
      shr edx,16
      .if dx==BN_CLICKED
       .IF ax==IDC_LOAD
         invoke DeviceIoControl, hVxD,1,NULL,0,addr DiskLabel,12,addr BytesReturned, NULL
         invoke MessageBox, hDlg, addr  DiskLabel, addr OutputTemplate, MB_OK+MB_ICONINFORMATION
       .endif
      .endif
     .ELSE
      mov eax, FALSE
      ret
     .ENDIF
     mov eax, TRUE
     ret
    DlgProc endp
    end start

    4 Analyse

    4.1 Le chargeur de VxD

    invoke Device IoControl, hVxD,1,NULL,0,addr DiskLabel,12,\
                    addr BytesReturned, NULL

    Il appelle DeviceIoControl avec le code de périphérique égal à 1, aucun buffer , un pointeur sur un buffer de sortie et sa taille. DiskLabel est le buffer dimensionné pour recevoir le label de volume retourné par le VxD. Le nombre d'octets réel retourné sera stocké dans la variable BytesReturned. Cet exemple démontre comment passer des données à un VxD et recevoir des données d'un VxD : vous passez des buffers d'entrée / sortie au VxD et celui ci lit ou écrit dans les buffers.

    4.2 Le VxD

    VMMCall Get_Sys_VM_Handle
    mov Handle, ebx
    assume ebx:ptr cb_s
    mov ebp,[ebx+CB_Client_Pointer]

    Quand le VxD reçoit le message W32_DeviceIoControl, il appelle Get_Sys_VM_Handle pour obtenir le handle de la VM système et le stocker dans une variable nommée Handle. Il place ensuite dans ebp un pointeur sur la structure de registres client du bloc de contrôle de la VM.

    mov ecx, sizeof MID
    stc
    push esi
    mov esi,OFFSET32 MediaID
    push ds
    pop fs
    VxDCall V86MMGR_Allocate_Buffer
    pop esi
    jc EndI
    mov AllocSize, ecx

    Ensuite, nous préparons les paramètres qui seront passés à V86MMGR_Allocate_Buffer.

    Nous devons initialiser le buffer avec l'instruction stc. Nous mettons l'offset de MediaID dans esi et le sélecteur dans fs puis appelons V86MMGR_Allocate_Buffer. Rappellerez vous que esi contient le pointeur sur DIOCPARAMS que nous sauvegardons par push esi et pop esi.

    Push_Client_State
    VMMCall Begin_Nest_V86_Exec
    assume ebp:ptr Client_Byte_Reg_Struc
    mov [ebp].Client_ch,8
    mov [ebp].Client_cl,66h
    assume ebp:ptr Client_word_reg_struc
    mov edx, edi
    mov [ebp].Client_bx,3 ; lecteur C
    mov [ebp].Client_ax,440dh

    Nous initialisons. les valeurs de la structure de registres du client pour l'int 21, 440Dh, 66h. Nous spécifiions que nous voulons obtenir le nom du disque C. Nous copions aussi la valeur contenue dans edi dans edx (edi contient l'adresse V86 du bloc de mémoire alloué par V86MMGR_Allocate_Buffer).

    mov [ebp].Client_dx, dx
    shr edx,16
    mov [ebp].Client_ds, dx

    Comme le service 440Dh, 66h de l’int 21h attend un pointeur sur une structure MID dans ds:dx, nous devons découper les parties segment et offset de edx et mettre ces éléments dans les images de registre correspondantes.

    mov eax,21h
    VMMCall Exec_Int
    VMMCall End_Nest_Exec
    Pop_Client_State

    Quand tout est prêt, nous appelons Exec_Int pour simuler l'interruption

    mov ecx, AllocSize
    stc
    mov ebx, Handle
    push esi
    mov esi,OFFSET32 MediaID
    push ds
    pop fs
    VxDCall V86MMGR_Free_Buffer
    pop esi

    Après le retour de Exec_Int, le buffer alloué est rempli par l'information que nous voulons. L'étape suivante est de récupérer cette information. Nous réalisons cela en appelant V86MMGR_Free_Buffer. Ce service libère le bloc de mémoire alloué par V86MMGR_Allocate_Memory et copie les données du bloc alloué dans le bloc de mémoire spécifié en ring-0. Comme V86MMGR_Allocate_Memory, si vous voulez copier, vous devez armer le carry flag avant l'appel au service.

    mov edx, esi
    assume edx:ptr DIOCParams
    mov edi,[edx].lpvOutBuffer
    mov esi,OFFSET32 MediaID.midVolLabel
    mov ecx,11
    rep movsb
    mov byte ptr [edi],0
    mov ecx,[edx].lpcbBytesReturned
    mov dword ptr [edx],11

    Après que nous ayons reçu l'information dans le buffer ring-0, nous copions le label de volume dans le buffer fourni par l'application win32. Nous pouvons avoir accès au buffer en employant lpvOutBuffer, membre de DIOCParams.

    Page:  1  2  3  4  5  6  7  8 

    Précédent       Suivant

    Copyright © Jean-Pierre Fayeulle