罗云彬VxD教程--虚拟8086模式的内存管理

2016-02-19 15:06 2 1 收藏

get新技能是需要付出行动的,即使看得再多也还是要动手试一试。今天图老师小编跟大家分享的是罗云彬VxD教程--虚拟8086模式的内存管理,一起来学习了解下吧!

【 tulaoshi.com - 编程语言 】

  下边我们用到的V86即指虚拟8086模式。 在以前的教程中,你学习了怎样模拟V86中断,但还有一个问题没有解决:在VxD和V86代码之间交换数据。我们将在此学习如何使用V86内存管理器来实现这个功能。在这里下载例子程序

  理论

  假如你的VxD和一些V86程序一起运行,如何传送大量数据到V86程序中或从V86程序中传送大量数据迟早是一个大问题。通过寄存器传送大量数据是不现实的。可能你的下一个想法是在ring0中分配一大块内存,并且通过一些寄存器传送其指针到V86程序,使其能访问这些数据。假如你这样做,可能会破坏你的系统,因为V86的地址定位方式需要segment:offset对,而不是线性定位方式。对这个问题,有很多解决的方法。然而,我选择了一个由V86内存管理器提供的一种简便的方法。

  如你能在你可使用的V86内存范围内找到一个空闲的内存块作为通讯缓冲区,这将解决其中的一个问题。然而,指针传送的问题依然存在。你可以通过V86内存管理器的服务来解决这两个问题。V86内存管理器是为V86应用管理内存的静态VxD。它还为V86应用提供EMS和XMS服务和为其他VxD提供API传送服务。API传送是一个从ring0拷贝数据到V86范围内的缓冲区并且传送V86缓冲区地址到V86代码的过程。V86内存管理器有一个在V86内存范围内的传送缓冲区,其含有VxD拷贝到V86内存范围内的数据,反之亦然。初始的缓冲区是4K。你以调用V86MMGR_Set_Mapping_Info来增加它的大小。

  现在你知道了传送缓冲区,我们如何拷入或拷出数据呢?这个问题通过调用两个服务来解决:V86MMGR_Allocate_Buffer和V86MMGR_Free_Buffer。

  V86MMGR_Allocate_Buffer从传送缓冲区分配一块内存并且从ring0拷贝一些数据到分配的V86缓冲区。V86MMGR_Free_Buffer正好相反:它从分配的V86内存块拷贝一些数据到ring0缓冲区并且释放由V86MMGR_Allocate_Buffer分配的内存块。

  记住,V86在内存管理器象堆栈一样管理被分配的缓冲区。这意味着分配/释放必须按先进后出的规则。所以如你调用了两次V86MMGR_Allocate_Buffer,第一个V86MMGR_Free_Buffer将释放由第二个V86MMGR_Allocate_Buffer调用而分配的缓冲区。

  我们来看一下V86MMGR_Allocate_Buffer的定义,它是一个基本寄存器传送参数的服务。

  EBX 当前VM的句柄

  EBP 指向当前VM的客户寄存器结构的指针

  ECX 从传送缓冲区分配的字节数 CARRY FLAG 进位标志位,如你不想从ring0缓冲区拷贝数据到分配的内存块就清零, 如你想从ring0缓冲区拷贝数据到分配的内存块就置1

  FS:ESI 指向ring0缓冲区的selector:offset指针,缓冲区中有要被拷贝到被分配的 缓冲区中的数据如果进位标志位被清零,则忽略它。

  假如调用成功,进位标志位被清零并且ECX包含在传送缓冲区中的字节数。这个数值应小于你要求的数值,所以你应保持这个数值,V86MMGR_Free_Buffer待会要用到它。EDI的高字包含被分配的内存块的V86段地址,偏移地址在在低字中。进位标志位当错误发生时被置位。

  V86MMGR_Free_Buffer和V86MMGR_Allocate_Buffer接受同样的参数。

  当你调用V86MMGR_Allocate_Buffer时,你在当前VM的V86内存范围内分配了一块内存,并且把其地址放到了EDI中。你可以使用这些服务传送数据到V86中断中或从V86中断中取得数据。

  在附加的API传送中,V86内存管理器也给其他VxDs提供了API映射服务。API映射服务是映射一些在扩展内存中的页到每个VM的V86内存范围。你可以使用V86MMGR_Map_Pages执行API映射。使用这个服务,页被映射到每个VM的同一线性地址空间上。如你仅仅工作在一个VM上,这将浪费地址空间。因为API映射比API传送要慢,所以你尽可能使用API传送方式。API映射仅仅使用在一些要访问同一线性地址空间并作用到所有VM的V86操作上。

  例子:

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  这个例子演示了API传送方式,使用了int 21h的440Dh功能(从代码66h)。这个中断调用得到媒体ID,你的第一个固定磁盘的卷标号。

  

;---------------------------------------------------------------
;              VxDLabel.asm
;---------------------------------------------------------------
.386p
include masmincludevmm.inc
include masmincludevwin32.inc
include masmincludev86mmgr.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_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 ; 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 loader.
;------------------------------------------------------------
.386
.model flat,stdcall
option casemap:none
include masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
includelib masm32libuser32.lib
includelib masm32libkernel32.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",0
OutputTemplate 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
 .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

  讲解

  我们首先分析lable.asm,它是一个加载了VxD的WIN32应用程序。

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  invoke DeviceIoControl,hVxD,1,NULL,0,addr DiskLabel,12,addr BytesReturned,NULL

  它调用DeviceIoControl,设备代码是1,没有输入缓冲区,一个指向输出缓冲区的指针及其大小。DiskLable是一个接收由VxD返回的卷标号的缓冲区。BytesReturned变量存有返回的字节数。这个例子说明了怎样传送数据和从VxD接收数据:你传送输入/输出缓冲区给VxD并且VxD读取/写入数据到指定的缓冲区。

  我们下面看看VxD代码。

  

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

  当一个VxD接收W32_DeviceIoControl消息,它调用Get_Sys_VM_Handle得到系统VM的句柄并把它存在一个叫Handle的变量中。下面将从VM控制块中提取指向客户寄存器结构的指针到EBP。

  

  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

  下面,准备传送到V86MMGR_Allocate_Buffer的参数。我们必须初始化被分配的缓冲区。我们把MediaID的偏移量送到ESI中,并且把选择子放在FS中,然后调用V86MMGR_Allocate_Buffer。你等会要恢复指向DIOCParams的指针,所以我们必须通过push esi 和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

  我们在客户寄存器结构中准备参数值来执行int 21h的440Dh功能(从代码66h),得到盘C的媒体ID。我们拷贝edi的值到edx中(edi中有由V86MMGR_Allocate_Buffer分配的内存块的V86地址)。

  

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

  调用了int 21h的440Dh功能(从代码66h)后,在ds:dx中得到一指向一个MID结构的指针,我们必须把在edx中的segment:offset对转换成两个部分并把它们放到合适的寄存器映象中。

  

  mov eax,21h
  VMMCall Exec_Int
  VMMCall End_Nest_Exec
  Pop_Client_State

  当一切都准备好了,我们将运行Exec_Int去模拟一个中断。

  

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

  当Exec_Int返回时,分配的缓冲已经由我们想要的信息填满了。下一步是检索信息。我们使用 V86MMGR_Free_Buffer来完成这个目标。这个服务释放由V86MMGR_Allocate_Memory分配的内存块,并且从分配的内存块中拷贝数据到ring0中的内存块。象V86MMGR_Allocate_Memory,如果你想要进行拷贝操作,你必须先把进位标志位置1,再调用服务。

  

  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

  我们在ring0中得到这个信息后,拷贝这个卷标值到Win32应用程序提供的缓冲区中。我们可以用DIOCParams的成员lpvOutBuffer来访问它。

来源:https://www.tulaoshi.com/n/20160219/1608826.html

延伸阅读
标签: windows 操作系统
现在内存的价钱越来越便宜,很多新装机的朋友都已经在使用1GB的内存,而还有很多人也在给老电脑的内存升级。在升级了物理内存之后,如何有效设置虚拟内存才能获得最大限度的性能提升?网上流传的一些优化虚拟内存的方法真的有效吗?这些就是本文将要讨论的内容。 网上有很多广泛流传的关于虚拟内存优化的技巧,其实很多都是经不起推敲的。大家...
如何设置虚拟内存: (一)合理设置虚拟内存 虚拟内存的设定主要根据你的物理内存大小和计算机的用途来设定,在桌面上用鼠标右击本人的计算机,选择属性,就可以看到内存了。根据微软公司的建议,虚拟内存设为物理内存容量的1.5--3倍,例如512MB的内存,虚拟内存设定为768--1536MB;1G的内存,虚拟内存设定为  1536--3072MB。也可让Windows...
电脑提示虚拟内存不足或虚拟内存太低消息, 现在虽然主流的内存配置大都是2G+了, 但是应用软件和一些游戏这么内存对他们来说还是不够,现在用电脑打开的应该程序又会比较多,无论大家的电脑配置的多么高档,虚拟内存在现在来说还是很有必要的 ,下面就来了解一下虚拟内存的电脑知识。 虚拟内存是什么 通常内存一般都是指RAM空间(RAM又分为静...
标签: 内存 虚拟内存
鼠标右击 在电脑桌面上找到“我的电脑”图标,选中后鼠标右击,在弹出的快捷菜单中找到“属性”,点击。 打开对话框 打开属性设置对话框,在左侧点击“高级系统设置”,打开“系统属性”对话框,切换到“高级”选项卡,点击性能下的“设置”按钮。 点击更改 打开“性能选项”对话框,在其中找到“高级选项卡”,在...
标签: 虚拟内存
打开控制面板 点击开始,在设置里面打开电脑控制面板。 点击系统与安全 点击“系统与安全”快捷图标,在上图可以看见,点击便进入了系统与安全框,在这时点击系统图标。 点击高级系统设置 在系统属性窗口,点击“高级系统设置”,然后选择“性能”下的“设置”命令。 点击高级 这时弹出性能选项窗口,...

经验教程

972

收藏

52
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部