📄 vxd-c8.htm
字号:
<HTML>
<head>
<link rel="stylesheet" href="../../asm.css">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Iczelion's win32 asm tutorial</title>
</head>
<body bgcolor="#FFFFFF" background="../../images/back01.jpg">
<P align=center><font face="宋体" size="+3"><b><font color="#0000FF">客户寄存器结构</font></b></font></P>
<HR SIZE=1>
<p>我们将学习本教程中另外一个重要的结构,叫客户寄存器结构。在本文中,V86指虚拟8086模式。在这里下载<a href="files/vxdbeep.zip">例子程序</a></p>
<p><font color="#FF0000">理论</font></p>
<p>VxDs与正常的win32/win16/DOS应用程序有很大不同。大多数情况下,当其他应用程序正常工作时,它们是休眠的。它们象一个监管者一样工作,其作用是监视ring-3应用程序并在其出错时改正它们。下面是其工作时的典型的情况:</p>
<p> 1、中断发生时<br>
2、VMM得到控制权时 <br>
3、VMM存贮寄存器组的值时 <br>
4、VMM服务于中断或调用其他VxDs完成此工作时 <br>
5、VMM交还控制权给被中断的程序时 </p>
<p>在以上过程中令人感兴趣的是,VMM只有这一种方式能影响被中断的应用程序,即修改存储的寄存器映象。例如,VMM认为被中断的程序应该返回到另外一个地址,它就修改存储的寄存器映象中CS:IP的值,当这个程序被重新分派时,它将在新的CS:IP处开始执行。</p>
<p>VMM在客户寄存器结构中存储中断点处的寄存器值。</p>
<p> <font color="#006666">Client_Reg_Struc STRUC<br>
Client_EDI DD ? <br>
Client_ESI DD ?<br>
Client_EBP DD ?<br>
Client_res0 DD ?<br>
Client_EBX DD ?<br>
Client_EDX DD ?<br>
Client_ECX DD ?<br>
Client_EAX DD ?<br>
Client_Error DD ?<br>
Client_EIP DD ?<br>
Client_CS DW ?<br>
Client_res1 DW ?<br>
Client_EFlags DD ?<br>
Client_ESP DD ?<br>
Client_SS DW ?<br>
Client_res2 DW ?<br>
Client_ES DW ?<br>
Client_res3 DW ?<br>
Client_DS DW ?<br>
Client_res4 DW ?<br>
Client_FS DW ?<br>
Client_res5 DW ?<br>
Client_GS DW ?<br>
Client_res6 DW ?<br>
Client_Alt_EIP DD ?<br>
Client_Alt_CS DW ?<br>
Client_res7 DW ?<br>
Client_Alt_EFlags DD ?<br>
Client_Alt_ESP DD ?<br>
Client_Alt_SS DW ?<br>
Client_res8 DW ?<br>
Client_Alt_ES DW ?<br>
Client_res9 DW ?<br>
Client_Alt_DS DW ?<br>
Client_res10 DW ?<br>
Client_Alt_FS DW ?<br>
Client_res11 DW ?<br>
Client_Alt_GS DW ?<br>
Client_res12 DW ?<br>
Client_Reg_Struc ENDS </font></p>
<p> 你可以看到这个结构分为两个部分:Client_xxx和Client_Alt_xxx。在这稍作说明,在一个给定的VM中,可能有两个运行的线程:V86和保护模式。当V86程序运行时,假如一个中断产生,Client_xxx将包含V86程序的寄存器映象,Client_Alt_xxx将包含保护模式程序的寄存器映象。相应的,当保护模式程序运行时,假如一个中断产生,Client_xxx将包含保护模式程序的寄存器映象,Client_Alt_xxx将包含V86程序的寄存器映象。Client_resX被保留而没有使用。</p>
<p> 在查看过这个结构后,你可能有一问题:怎样改变寄存器中的一个字节,比如al?上面的结构仅仅描述了字和双字大小的寄存器组。不用担心,在vmm.inc找一找。那有两个为此附加的结构:Client_Word_Reg_Struc和Client_Byte_Reg_Struc。假如你想以字或字节大小来访问寄存器,根据你的需要转换Client_Reg_Struc到Client_Word_Reg_Struc或Client_Byte_Reg_Struc。</p>
<p> 下一个问题:我们如何得到一个指向客户寄存器结构的指针?</p>
<p> 这相当简单:一般地,当VMM调用我们的VxD时,把客户寄存器结构的地址放在ebp中。在这里的客户寄存器结构是当前VM的。你可以从VM的句柄中得到这个指针。记住,VM的句柄是VM控制块的线性地址。</p>
<p> <font color="#006666">cb_s STRUC<br>
<br>
CB_VM_Status DD ?<br>
CB_High_Linear DD ? <br>
CB_Client_Pointer DD ? <br>
CB_VMID DD ? <br>
CB_Signature DD ?<br>
<br>
cb_s ENDS<br>
CB </font></p>
<p> CB_Client_Pointer包含指向VM的客户寄存器结构的指针。例如:你可用下边的代码得到指向当前VM中的客户寄存器结构的指针:</p>
<p> <font color="#006666">VMMCall Get_Cur_VM_Handle ; return the current VM handle
in ebx<br>
assume ebx:ptr cb_s <br>
mov ebp,[ebx+CB_Client_Pointer] ; pointer to client reg struct </font></p>
<p> 现在我们了解了客户寄存器结构,我们可以用它来开始工作了。我们将使用客户寄存器结构去传送寄存器组的值到一个DOS中断中,也就是,int 21h,功能2h,显示一个字符。这个DOS服务把要显示的字符放在dl中。假如我们传送响铃字符(07h)到这个服务,将通过PC喇叭发出一声响。
</p>
<p> 记住,int 21h是一个DOS服务,因而其在V86模式下是可用的,我们如何在VxD中调用一个V86中断?一个方法是使用Exec_Int服务。这个VMM服务把要调用的中断号放在eax中。它模拟指定的中断然后返回到调用的VM。然而,它必须在一个嵌套执行块中被调用。嵌套执行块被Begin_Nest_V86_Exec
(或 Begin_Nest_Exec)和End_Nest_Exec包括起来。如果我们要调用int 21h功能2h,我们需要在嵌套执行块内转换Client_Byte_Reg_Struc结构的Client_ah和Client_Dl,然后把值21h放在eax中。当一切准备好了,就调用Exec_Int。</p>
<p><font color="#FF0000">例子:</font></p>
<p>例子是一个动态VxD,它调用int 21h的功能2使PC喇叭发声。</p>
<pre><font face="宋体">.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</font>
</pre>
<p><font color="#FF0000">讲解 </font></p>
<p><font color="#006666">Push_Client_State</font></p>
<p> 这没什么好讲解的。当一个VxD接收到一个DeviceIoControl消息,ebp已经指向了当前VM的客户寄存器结构。我们调用Push_Client_State宏在堆栈中存储客户寄存器结构的状态。然后用Pop_Client_State宏恢复客户寄存器。
</p>
<p><font color="#006666">VMMCall Begin_Nest_V86_Exec </font></p>
<p>通过调用Begin_Nest_V86_Exec开始嵌套执行块。</p>
<p> <font color="#006666">assume ebp:ptr Client_Byte_Reg_Struc<br>
mov [ebp].Client_dl,7 <br>
mov [ebp].Client_ah,2 </font></p>
<p>改变在客户寄存器中的dl和ah寄存器的映象。这个改变的值将由中断使用。 </p>
<p><font color="#006666">mov eax,21h <br>
VMMCall Exec_Int </font></p>
<p>Exec_Int要求在eax存有一个中断号。我们想使用int 21h。等会我们调用Exec_Int去模拟中断。 </p>
<p><font color="#006666">VMMCall End_Nest_Exec<br>
Pop_Client_State</font></p>
<p> 当Exec_Int返回,我们完成了嵌套执行块,并且由堆栈中恢复了客户寄存器结构的值。 你将听到你的PC喇叭发出一声响。 </p>
<HR SIZE=1>
<DIV align=center> <font face="宋体">
<SCRIPT language=JavaScript1.1 src="../lion-tut-c13.files/textclick"></SCRIPT>
<BR>
</font></DIV>
<font face="宋体"><!-- 10:1 文本广告交换 --> </font>
<DIV align=center> <font face="宋体">
<SCRIPT language=JavaScript1.1 src="../lion-tut-c13.files/c21.htm"></SCRIPT>
<!-- 10:1 文本广告交换 --></font></DIV>
<HR SIZE=1>
<DIV align=center><font face="宋体">翻译:吕骏,整理:LuoYunBin's Win32 ASM Page, <A
href="http://asm.yeah.net/">http://asm.yeah.net</A></font></DIV>
</BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -