Customer register structure
We will learn another important structure in this tutorial called the customer register structure. In this article, V86 refers to virtual 8086 mode.
theory
VxDs is very different from normal Win32 / Win16 / DOS app. In most cases, they are sleeping when other applications work normally. They work as a regulator, and their role is to monitor Ring-3 applications and correct them when it is wrong. Here is a typical situation in its work:
1
Interested in the above process is that VMM can only affect the interrupted application, that is, modify the stored register image. For example, the VMM believes that the interrupted program should return to another address, which modifies the value of the CS: IP in the stored register image. When this program is reprecated, it will start executing at the new CS: IP.
VMM stores the register value at the interrupt point in the client register structure.
Client_Reg_Struc STRUCClient_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_res12 dw? Client_reg_struc ends
You can see this structure is divided into two parts: client_xxx and client_alt_xxx. In this slight explanation, in a given VM, there may be two running threads: V86 and protection mode. When the V86 program is running, if an interrupt is generated, the client_xxx will include the register image of the V86 program, and the client_alt_xxx will contain register images of the protected mode program. Correspondingly, when the protected mode program is running, if an interrupt is generated, the client_xxx will contain the register image of the protection mode program, and the client_alt_xxx will contain the register image of the V86 program. Client_resx is reserved without use.
After viewing this structure, you may have a problem: how to change one byte in the register, such as Al? The above structure only describes the word and double word size register group. Don't worry, find a found on VMM.inc. There are two structures for this additional structure: client_word_reg_struc and client_byte_reg_struc. If you want to access registers with word or byte size, convert client_reg_struc to client_word_reg_ruc or client_byte_reg_struc based on your needs. Next question: How do we get a pointer to the customer register structure?
This is quite simple: Generally, when VMM calls our VXD, put the address of the client register structure in the EBP. The customer register structure here is current VM. You can get this pointer from the handle of the VM. Remember, the handle of the VM is the linear address of the VM control block.
CB_S STRUCCB_VM_STATUS DD? CB_HIGH_LINEAR DD? CB_CLIENT_POINTER DD? CB_VMID DD? CB_SIGNATURE DD? CB_S Endscb
The CB_Client_Pointer contains a pointer to the client register structure of the VM. For example: You can use the code below to get a pointer to the customer register structure in the current VM:
VMMCALL GET_CUR_VM_HANDLE; RETURN THE CURRENT VM HANDLE IN EBXASSUME EBX: PTR CB_S MOV EBP, [EBX CB_CLIENT_POINTER]; Pointer to Client Reg Struct
Now we understand the customer register structure, we can use it to start working. We will use the customer register structure to transfer the value of the register group to a DOS interrupt, that is, INT 21h, function 2H, display a character. This DOS service puts the characters you want to display in DL. If we deliver the bell character (07h) to this service, it will sound through the PC speaker.
Remember, INT 21h is a DOS service, so it is available in V86 mode, how do we call a V86 interrupt in VXD? One method is to use the EXEC_INT service. This VMM service puts the interrupt number to be called in EAX. It simulates the specified interrupt and then returns to the called VM. However, it must be called in a nested execution block. Nested execution blocks are included by begin_nest_v86_exec (or begin_nest_exec) and end_nest_exec. If we want to call the int 21h function 2H, we need to convert the client_ah and client_dl of the client_byte_reg_struc structure in the nested execution block, and then put the value of 21H in Eax. When everything is ready, call EXEC_INT.
example:
The example is a dynamic VXD that invokes the function 2 of INT 21H 2 to make the PC speaker.
.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
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
explain
Push_client_state
This is nothing to explain. When a VXD receives a DeviceIocontrol message, EBP has point to the current VM's client register structure. We call the PUSH_CLIENT_STATE Macro to store the status of the client register structure in the stack. Then use the POP_CLIENT_STATE macro to recover the client register.
VMMCALL BEGIN_NEST_V86_EXEC
Start the nested execution block by calling Begin_NEST_V86_EXEC.
Assume EBP: PTR Client_byte_reg_strucmov [EBP] .client_dl, 7 MOV [EBP] .client_ah, 2
Change the image of the DL and AH registers in the customer register. This change will be used by the interrupt.
MOV EAX, 21H VMMCALL EXEC_INT
EXEC_INT requires an interrupt number in Eax. We want to use INT 21h. Waiting for us to call EXEC_INT to simulate interrupts.
VMMCALL END_NEST_EXECPOP_CLIENT_STATE
When EXEC_INT returns, we completed the nested execution block and recovered the value of the client register structure in the stack. You will hear your PC speaker sound.