Memory management of virtual 8086 mode
The V86 we used below refers to the virtual 8086 mode. In the previous tutorial, you learned how to simulate V86 interrupts, but there is still a problem: exchange data between VXD and V86 code. We will learn how to use the V86 Memory Manager to implement this feature.
theory
If your VXD is running with some V86 programs, how to transfer a large amount of data into the V86 program or transfer a lot of data from the V86 program sooner or later. Transferring a large amount of data through the register is unrealistic. Maybe your next idea is to allocate a large number of memory in RING0, and transfer its pointer to the V86 program by some registers to access these data. If you do this, you may destroy your system because the address location of V86 requires segment: offset pair, not linear positioning. There are many solutions to this problem. However, I chose a simple way to provide by the V86 memory manager.
If you can find an idle memory block as a communication buffer in the V86 memory you can use, this will solve one of the problems. However, the problem of pointer transmission still exists. You can solve these two problems through the V86 Memory Manager service. The V86 memory manager is a static VXD for the V86 application management memory. It also provides EMS and XMS services for V86 applications and provides API delivery services for other VXDs. The API transmission is a process of buffer from Ring0 copy data to the V86 range and transmits the V86 buffer address to the V86 code. The V86 Memory Manager has a transfer buffer within the V86 memory, which contains data from VXD to V86 memory, and vice versa. The initial buffer is 4K. You use the V86MMGR_SET_MAPPING_INFO to add its size.
Now you know the transfer buffer, how do we copy or copy out data? This problem resolves two services: V86mmgr_allocate_buffer and v86mmgr_free_buffer.
V86mmgr_allocate_buffer assigns a memory from the transfer buffer and copies some data from Ring0 to allocated V86 buffers. V86MMGR_FREE_BUFFER is exactly the opposite: it copies some data to the Ring0 buffer from allocated V86 memory blocks and releases the memory block allocated by V86MMGR_allocate_buffer.
Remember, V86 manages the assigned buffer like the memory manager icon stack. This means that allocation / release must be based on the rules of advanced. So if you call two V86MMGR_allocate_buffers, the first V86MMGR_Free_buffer will release buffers allocated by the second V86MMGR_Allocate_buffer call.
Let's take a look at the definition of v86mmgr_allocate_buffer, which is a service of a basic register transmitting parameters.
EBX Current VM's handle EBP points to the pointer of the current VM's client register structure ECX that assigns the number of bytes assigned from the transfer buffer, if you don't want to copy data from the RING0 buffer to the allocated memory block, such as You want to copy data from the Ring0 buffer to the allocated memory block 1 fs: ESI points to the Selector: OFFSET pointer to the Ring0 buffer, there is data to be copied to the assigned buffer if the carry flag is Obtain it clearly.
If the call is successful, the carrier bits are cleared and the ECX is included in the number of bytes in the transfer buffer. This value should be less than your requirements, so you should keep this value, V86MMGR_Free_buffer will use it. The high character of the EDI contains the V86 segment address of the assigned memory block, and the offset address is in the low word. The carry flag is set when the error occurs.
V86mmgr_free_buffer and v86mmgr_allocate_buffer accept the same parameters.
When you call V86MMGR_Allocate_buffer, you assign a memory within the current VM V86 memory and put its address in the EDI. You can use these service to transfer data to the V86 interrupt or acquire data from the V86 interrupt. In additional API transmission, the V86 memory manager also provides an API mapping service to other VXDs. The API mapping service is a V86 memory range that maps some pages in the extended memory to each VM. You can perform API mappings using V86MMGR_MAP_PAGES. With this service, the page is mapped to the same linear address space of each VM. If you just work on a VM, this will waste address space. Because the API mapping is slower than the API transmission, you use the API transmission method as much as possible. The API mapping is only used in some V86 operations to access the same linear address space and act to all VMs.
example:
This example demonstrates the API transmission mode using the 440DH function of INT 21H (from code 66h). This interrupt is called to get the media ID, your first fixed disk of the volume mark.
; ------------------------------------------------- ----------------
Vxdlabel.asm
; ------------------------------------------------- ----------------
.386P
INCLUDE /MASM/INCLUDE / VMM.INC
INCLUDE /MASM/INCLUDE/vwin32.inc
INCLUDE /MASM/INCLUDE /V86MMGR.INC
VxDName Textequ
ControlName Textequ
Vxdmajorversion TextEqu <1>
VxdminorVersion TextEqu <0>
Vxd_static_data_seg
VXD_STATIC_DATA_ENDS
Vxd_locked_code_seg
; ------------------------------------------------- ---------------------------
; Remember: The name of the vxd must be uppercase else it Won't work / unload
; ------------------------------------------------- ---------------------------
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_bufferpop 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; Drive 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
; -------------------------------
Retrieve The Data
; -------------------------------
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
; ------------------------------------------------- -----------
Label.asm
The win32 vxd loading.
; ------------------------------------------------- -----------
.386
.Model flat, stdcall
Option CaseMAP: NONE
INCLUDE /MASM32/INCLUDE/Windows.inc
INCLUDE /MASM32/INCLUDE/USER32.INC
INCLUDE /MASM32/INCLUDE / WANEL32.INC
INCLUDELIB /MASM32/LIB/USER32.LIB
INCLUDELIB /MASM32/LIB/kernel32.lib
DLGPROC Proto: DWORD,: DWORD,: DWORD,: DWORD
.DATA
Failure DB "Cannot Load Vxdlabel.vxd", 0
Appname DB "Get Disk Label", 0
Vxdname db "//./vxdlabel.vxd" 0OUTPUTTEMPLATE DB "VOLUME Label of Drive 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
.lse
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_ICONITIONMATION
.endif
.endif
.Lse
Mov Eax, False
RET
.Endif
Mov Eax, True
RET
DLGPROC ENDP
End Start
explain
We first analyze Lable.asm, which is a Win32 application loaded with VXD.
Invoke DeviceioControl, HVXD, 1, NULL, 0, AddR Disklabel, 12, Addr Bytesreturned, Null
It calls Deviceiocontrol, the device code is 1, no input buffer, a pointer to the output buffer and its size. Disklable is a buffer that receives a volume marking number returned by VXD. The BYTESRETURNED variable has the number of bytes returned. This example illustrates how to transmit data and receive data from VXD: You transmit the input / output buffer to VXD and VXD read / write data to the specified buffer.
Let's take a look at the VXD code.
VMMCALL GET_SYS_VM_HANDLE
Mov Handle, EBX
Assume EBX: PTR CB_S
MOV EBP, [EBX CB_CLIENT_POINTER]
When a VXD receives the W32_Deviceiocontrol message, it calls get_sys_vm_handle to get the handle of the system VM and exist it in a variable called Handle. The pointer to the EBP pointing to the client register structure is extracted from the VM control block. 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
Next, you are ready to transmit to the parameter of V86MMGR_Allocate_buffer. We must initialize the assigned buffer. We sent the offset of MediaI to ESI and put the selection in the FS and then call V86MMGR_allocate_buffer. You will want to resume the pointer to Diocparams, so we must protect it through the PUSH ESI and 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; Drive C
MOV [EBP] .client_ax, 440dh
We prepare a parameter value in the client register structure to perform the 440DH function of INT 21h (from code 66h) to obtain the media ID of the disk C. We copy the value of EDI to EDX (EDI has a V86 address of the memory block allocated by V86MMGR_Allocate_buffer).
MOV [EBP] .client_dx, dx
SHR EDX, 16
MOV [EBP] .client_ds, dx
After calling the 440DH function of INT 21H (from code 66h), get a pointer to a MID structure in DS: DX, we must convert the segment: offset in EDX into two parts and put them in the right The register image is in the mapping.
MOV EAX, 21h
VMMCALL EXEC_INT
VMMCALL END_NEST_EXEC
POP_CLIENT_STATE
When everything is ready, we will run the exec_int to simulate an interrupt.
MOV ECX, Allocsize
STC
Mov EBX, HANDLE
PUSH ESI
Mov ESI, Offset32 Mediaid
Push DS
POP FS
Vxdcall v86mmgr_free_buffer
POP ESI
When EXEC_INT returns, the assigned buffer has filled with the information we want. The next step is to retrieve information. We use v86mmgr_free_buffer to complete this goal. This service releases the memory block allocated by V86MMGR_allocate_memory, and copy data into the memory block in RING0 from the assigned memory block. Like V86mmgr_allocate_memory, if you want to make a copy, you must first put the carry mark position 1, call the 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