Client Register StructureWe will examine another important structure in this tutorial, namely the client register structure. Download the example.
Some theoryVxDs are very different from normal win32/win16/DOS applications. VxDs are, most of the time, dormant while other normal apps do their businesses. They act like supervisors which look over other ring-3 apps and correct them when they did something wrong. The typical situation is as follows:
An interrupt occurs
The VMM gains control
The VMM saves the values of the registers
The VMM services the interrupt or calls other VxDs to do the job
The VMM returns control to the interrupted program. The interesting thing about the above process is that, the only way VMM can affect the interrupted app is by modifying the saved register image. For example, if the VMM deems the interrupted program should resume at a different address, it can alter the value of CS:IP in the saved register image and then when the program is redispatched, it will resume execution at the new CS:IP.
The VMM saves the values of the registers at the interrupted point in the client register structure.
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 ENDSYou can see that there are two sets of members in this structure: Client_xxx and Client_Alt_xxx. This requires a little explanation. In a given VM, there can be two threads of execution: V86 and protected-mode. If an interrupt occurs when a V86 program is active, the Client_xxx will contain the images of the registers of the V86 program, the Client_Alt_xxx will contain those of the PM program. Alternately, if an interrupt occurs when the PM program is active, the Client_xxx will contain the values of the PM program's registers while the Client_Alt_xxx will contain the values of the V86 program's registers. The Client_resX are reserved and not used.
You may have a question after examining the structure: what if I want to alter only a byte in a register, say al ? The above structure only describes word-and dword-sized registers. Have no fear. Take a look inside vmm.inc. There are additional two structures for just this purpose: Client_Word_Reg_Struc and Client_Byte_Reg_Struc. If you want to access word-or byte-sized registers, typecast the Client_Reg_Struc into Client_Word_Reg_Struc or Client_Byte_Reg_Struc according to your need. You can also
The next question: How can we obtain the pointer to the client register structure?It's actually easy: most of the time, the VMM puts the address of the client register structure in ebp when it calls our VxD. The client register structure in this case is the current VM's. Alternatively, you can obtain this pointer from the VM handle. Remember that a VM handle is actually the linear address of the VM control block.
cb_s STRUC
CB_VM_Status DD ?
CB_High_Linear DD ?
CB_Client_Pointer DD ?
CB_VMID DD ?
CB_Signature DD ?
cb_s ENDSCB_Client_Pointer contains the pointer to the client register structure of that VM. For example, you can obtain the pointer to the client register structure of the current VM by the following code:
VMMCall Get_Cur_VM_Handle ; return the current VM handle in ebx
assume ebx:ptr cb_s
mov ebp,[ebx+CB_Client_Pointer] ; pointer to client reg structNow that we understand the client register structure, we can proceed to using it. We will use the client register structure to pass values in registers to an MS-DOS interrupt, namely, int 21h, service 2h, Display Character service. This MS-DOS service takes the character to be displayed in dl. If we pass the bell character (07h) to this service, it will play the bell through the PC speaker.
Remember that int 21h is an MS-DOS service thus it's available under V86 mode, how can we call a V86 interrupt from a VxD? One way is to use Exec_Int service. This VMM service takes the interrupt number to be called in eax. It simulates the specified interrupt and resumes the execution of the VM. However, it must be called within a nested execution block. A nested execution block is bracketed by Begin_Nest_V86_Exec (or Begin_Nest_Exec) and End_Nest_Exec. So if we want to call int 21h, service 2, we need to alter the Client_ah and Client_Dl of the Client_Byte_Reg_Struc structure within the nested execution block and then store the value 21h into eax. When everything is ready, call Exec_Int.
The ExampleThe example is a dynamic VxD that issues int 21h service 2 to play the PC speaker.
.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
;----------------------------------------------------------------------------
; 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
Analysis
Push_Client_StateThere is not much to analyze. When the VxD receives DeviceIoControl message, ebp already points to the current VM's client register structure. We call Push_Client_State macro to save the current state of the client registers on the stack. We later restore the client registers with Pop_Client_State.
VMMCall Begin_Nest_V86_ExecBegin the nested execution block by calling Begin_Nest_V86_Exec.
assume ebp:ptr Client_Byte_Reg_Struc
mov [ebp].Client_dl,7
mov [ebp].Client_ah,2Alter the images of dl and ah registers in the client register structure. This altered values will be used by the interrupt.
mov eax,21h
VMMCall Exec_IntExec_Int expects the interrupt number in eax. We want to issue int 21h. Then we call Exec_Int to simulate the interrupt.
VMMCall End_Nest_Exec
Pop_Client_StateWhen Exec_Int returns, we end the nested execution block and restore the saved values of the client registers from the stack.
You'll hear your PC speaker plays the bell character.