iczelion Vxd cntut7

请求执行时间段与Shell函数


 

下载例子程序

理论:

请求执行时间段通常被简称为"appy time"。是指当系统VM稳定到充许VxDs和ring-3级别的应用软件(特别是16-bit的应用软件)交互时的时间段。例如,在一个特定时间段,VxDs能加载并调用在16-bit Dlls中的函数。这个appy time在Windows 3.x中是无效的。在Windows3.x,一个VxD能包含在16-bit DLLs中的任意函数的地址,并模拟一个远调用到这个地址。然而,因为造成了VMM重入,这个操作将中断所有正在ring-3中执行的任务。所以能被VxDs能调用的APIs被要求是中断安全的,象PostMessage。在Windows 95,一个VxD在appy time的帮助下能调用任意一个在16-bit DLLs中的函数。

假如你的VxDs被通知正处在appy time,它就能加载16-bit DLLs并调用其中的函数。VxDs怎么知道appy time到来了呢?这就要使用Shell VxD请求一个appy time。当系统VM在稳定状态,Shell VxD将调用某VxD的一个回调函数,此函数是在VxD请求appy time时指定的。Shell VxD发生一次appy time事件仅仅调用一次你的回调函数。这就象找工作。你到职业介绍所,登记你的名字和电话号码。当你回到家,如有一个工作适合你,职业介绍所将电话通知你这个好消息。当你收到了这个消息,他们就不再通知你了。

在一个appy time起作用前要花上一些时间进行相关处理。appy time事件以下环境中将不起作用:

1、系统启动或关机时。
2、当系统VM在临界段或等待一个信号量时。

管理一个appy time事件

你可以通过调用_SHELL_CallAtAppyTime来注册一个appy time事件,它的定义如下:

VxDCall _SHELL_CallAtAppyTime, <<OFFSET32 pfnCallback>,  dwRefData, dwFlags, dwTimeout>

  • pfnCallBack -- 当appy time事件发生时你要Shell VxD调用的回调函数的平板地址。这个函数接收两个参数,dwRefData和dwFlags,与你传送给_SHELL_CallAtAppyTime的两个一样。记住,Shell VxD采用C调用顺序调用你的回调函数。总而言之,你要象这样定义你的回调函数:

    BeginProc OnAppyTime, CCALL, PUBLIC
    ArgVar dwRefData,DWORD ; declare argument name and type
    ArgVar dwFlags, DWORD
    EnterProc
    你的代码...
    LeaveProc
    Return
    EndProc OnAppyTime
  • dwRefData -- 你要Shell VxD传送给你的回调函数的参考数据。可以是你想要的任何东西。
  • dwFlags -- 事件标志。

    如下值之一:
    CAAFL_RING0 ring-0事件
    CAAFL_TIMEOUT 由dwTimeout指定的时间到期事件。

    假如你只想在一定的时间内等待appy time事件,使用CAAFL_TIMEOUT标志。如你想一直等待appy time事件,使用NULL。CAAFL_RING0作用不明。
  • dwTimeout -- 在appy time事件发生前,VxD能等待时间段长度。时间段的单位不明。

这个服务是异步的,意味着你为appy time事件注册回调函数之后立即返回。

如果这个服务调用是成功的,在eax中返回appy time事件句柄。假如调用失败,在eax返回0。

你可以调用_SHELL_CancelAppyTimeEvent来撤消appy time事件注册,它仅有一个参数,就是由_SHELL_CallAtAppyTime返回的appy time事件句柄。

你应当在appy time事件到来时检查系统。例如,当你在系统关闭时注册appy time会发生什么?你的VxD的回调函数将不会得到调用!当appy time事件到来时,你应当调用_SHELL_QueryAppyTimeAvailable来查询系统状态。这个服务没有参数。如果appy time无效,在eax中返回0,例如:当系统关闭时或消息服务程序产生一个一般保护性错误时。

这个服务不会告诉你现在是不是appy time:它仅仅告诉你可能有一个appy time事件到来。简而言之,如果你想安全运行,首先调用_SHELL_QueryAppyTimeAvailable并检查eax中的值是否非零值,然后才可继续调用_SHELL_CallAtAppyTime。

请求执行时间段Shell服务

当appy time到达时,你可以使用几个Shell服务调用:

  • _SHELL_CallDll
  • _SHELL_FreeLibrary
  • _SHELL_GetProcAddress
  • _SHELL_LoadLibrary
  • _SHELL_LocalAllocEx
  • _SHELL_LocalFree

使用提供的这6个服务,VxDs可以调用在16-bit DLLs/EXE中的16-bit函数,象WinHelp。然而,我们马上要进步到32-bit时代(未来是64-bit) ,所以我不会仔细研究它们。如你对此感兴趣,你可以在Windows 95/98DDK文档中了解它们。

另外一些只请求执行时间段服务我想更有用处:_SHELL_ShellExecute和 _SHELL_BroadcastSystemMessage。使用_SHELL_BroadcastSystemMessage,你能在一次调用中发送消息到顶端的窗口和所有的VxDs。如果appy time有效,你可以发送消息到窗口和VxDs。如果appy time无效,你只能发送消息到VxDs。

_SHELL_ShellExecute是在ring-3中的函数ShellExecute在ring-0中的对应函数。实际上,它调用ring-3中的ShellExecute完成这个工作。使用外壳服务,你可以运行/打开/打印任意文件。

_SHELL_ShellExecute的定义如下所示:

VxDCall _SHELL_ShellExecute,<OFFSET32 ShexPacket>

它仅有一个参数,SHEXPACKET结构的平板地址。它从ShellExecute函数中返回一个值到eax中。让我们研究一下SHEXPACKET结构:

shex_dwTotalSize SHEXPACKET结构的字节数加上可选的参数rgchBaggage的大小,它直接跟随在这个结构之后。我等一下描述rgchBaggage。

  • shex_dwTotalSize SHEXPACKET结构的字节数加上可选的参数rgchBaggage的大小,它直接跟随在这个结构之后。我等一下描述rgchBaggage。
  • shex_dwSize SHEXPACKET结构的字节数,不包括rgchBaggage。结合上面的shex_dwTotalSize值,外壳VxD能计算任意长度的rgchBaggage的大小。
  • shex_ibOp 你要完成的操作。假如你指定0,意味着你想要打开一个文件。如果是一个可执行文件,就运行它。如果你想要完成其他的操作,你必须在rgchBaggage中指定操作的名字,在这个域中,必须包括从这个SHEXPACKET结构开始到一个ASCII字符串的距离,此距离大小以字节计数,字符串指定你要完成的操作的名称。SHEXPACKET结构的大小是32字节。如果操作字符串紧跟在SHEXPACKET结构后,shex_ibOp中的值必须是32。要知道你可以完成的操作,请查看ShellExecute服务。有三个操作被定义,"open"、"print"和"explore"。
  • shex_ibFile 从此结构开始到一个ASCII字符串的距离,这个字符串是你想要传递给ShellExecute的文件名,就象Shex_ibOp成员。
  • shex_ibParams 你想传递到由shex_ibFile指定的文件的可选参数。假如此文件是一个文档文件,你不想传递任何参数给它,就用0。假如你想传递参数给那个文件,在把从此结构开始到此成员指定的字符串的距离放在这个成员中。简而言之,就象shex_ibOp和shex_ibFile成员。
  • shex_ibDir 工作目录。假如你想使用Windows目录就指定0,否则指定其为在此结构后的首选目录名字符串,并把从此结构开始到目录名字符串的距离值放到这个成员中。
  • shex_dwReserved 如其名字所指,它是保留的。不要理它。
  • shex_nCmdShow 应用程序窗口怎样被显示。这是一个你正常传递给ShowWindow的值,比如,SW_XXXX值。可在windows.inc中查看这些值。

所有成员的大小都是双字的。在这里我介绍刚才我承诺的rgchBaggage成员。仅有一点不同,因为其大小是可变的,所以它作为SHEXPACKET结构的一员却不能包括在此结构的定义中。查看shell.inc,你看到rgchBaggage并不在SHEXPACKET结构的定义中,尽管在Windows 9x DDK文档中,声明其是SHEXPACKET结构的一员。

rgchBaggage是什么?简单说来其是跟在SHEXPACKET结构后的一个字符串数组。在这个数组中,你可以把你要对文件完成的操作名称、文件的名字、你要传递给文件的参数和工作目录放在其中。首先得到从SHEXPACKET结构到这些字符串的第一个字符之间的距离(即此结构的一些成员值),再把这些值加上SHEXPACKET结构的平板编移量,Shell VxD就能得到这些字符串在rgchBaggage数组中的偏移量。例如,假如SHEXPACKET结构从60000h开始,字符串紧跟其后,结构与字符串之间的距离是结构本身的大小,32字节(20h)。所以Shell VxD知道字符串定位在60020h。

例子

这是一个显示如何注册一个appy time事件并且使用_SHELL_ShellExecute的例子。VxD是动态的,被一个简单的Win32应用程序使用。当一个用户按下"run Calculator"按纽,win32应用程序调用DeviceIoControl去要求VxD注册一个appy time事件并且运行在Windows目录中的calc.exe。

;--------------------------------------------------------------------------------- 
;                                   VxD Source Code 
;--------------------------------------------------------------------------------- 
.386p 
include /masm/include/vmm.inc 
include /masm/include/vwin32.inc 
include /masm/include/shell.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 
  

BeginProc OnDeviceIoControl 
 assume esi:ptr DIOCParams 
 .if [esi].dwIoControlCode==1 
      VxDCall _SHELL_CallAtAppyTime,<
    
    
     
     ,0,0,0> 
 .endif 
 xor eax,eax 
 ret 
EndProc OnDeviceIoControl 
VXD_LOCKED_CODE_ENDS 

VXD_PAGEABLE_CODE_SEG 
BeginProc OnAppyTime, CCALL 
 ArgVar RefData,DWORD 
 ArgVar TheFlag,DWORD 
 EnterProc 
 mov File.shex_dwTotalSize,sizeof SHEXPACKET 
 add File.shex_dwTotalSize,sizeof EXEName 
 mov File.shex_dwSize,sizeof SHEXPACKET 
 mov File.shex_ibOp,0 
 mov File.shex_ibFile,sizeof SHEXPACKET 
 mov File.shex_ibParams,0 
 mov File.shex_ibDir,0 
 mov File.shex_dwReserved,0 
 mov File.shex_nCmdShow,1 
 VxDCall _SHELL_ShellExecute, 
     
     
      
       
 LeaveProc 
 Return 
EndProc OnAppyTime 
VXD_PAGEABLE_CODE_ENDS 

VXD_PAGEABLE_DATA_SEG 
 File SHEXPACKET <> 
 EXEName db "calc.exe",0 
VXD_PAGEABLE_DATA_ENDS 

end
     
     
    
    
   
   
  
  

讲解

VxD等待一个DeviceIoControl消息:第1号服务。当它收到上述消息,它将注册一个appy time事件。

VxDCall _SHELL_CallAtAppyTime,<<OFFSET32 OnAppyTime>,0,0,0>

它将传送OnAppyTime函数的平板地址到_SHELL_CallAtAppyTime,这样,当appy time事件发生时,Shell VxD将调用它。因我们不需要使用任何参考数据并且不需要处理过期情况,所以紧跟OnAppyTime函数的三个参数都是0。

当appy time事件发生时, Shell VxD调用OnAppyTime函数。

BeginProc OnAppyTime, CCALL

我们用BeginProc描述一个函数。因为Shell VxD用C调用顺序调用OnAppyTime,我们需要指定CCALL属性。

ArgVar RefData,DWORD
ArgVar TheFlag,DWORD
EnterProc
...
LeaveProc
Return

因为Shell VxD用两个参数调用OnAppyTime,我们必须设置堆栈结构。ArgVar宏用来调整每个要传递到函数的参数的堆栈结构。它的语法如下:

ArgVar varname, size, used

varname是参数的名字。你可以使用你喜欢的任意名字。size是参数的大小。你可以使用BYTE、WORD、DWORD或1,2,4。used通常被忽略。

紧接着ArgVar宏,我们需要使用EnterProc和LeaveProc宏来标志在程序中存放变量和参数的结构的开始与结束,使其能被正确访问。使用Return宏返回到调用者。

    mov File.shex_dwTotalSize,sizeof SHEXPACKET 
    add File.shex_dwTotalSize,sizeof EXEName 
    mov File.shex_dwSize,sizeof SHEXPACKET 
    mov File.shex_ibOp,0 
    mov File.shex_ibFile,sizeof SHEXPACKET 
    mov File.shex_ibParams,0 
    mov File.shex_ibDir,0 
    mov File.shex_dwReserved,0 
    mov File.shex_nCmdShow,1 
    VxDCall _SHELL_ShellExecute, 
  
  

在这个程序中的指令是简单的:初始化SHEXPACKET结构并且调用_SHELL_ShellExecute服务。记住 shex_dwTotalSize包含SHEXPACKET结构自身的和跟着它的字符串的组合大小。这是一个简单的事情。假如字符串不紧跟在此结构之后的,你必须计算从结构第一个字节到到字符串最后一个字节之间的距离。shex_ibFile包含此结构自身的大小,因为程序名紧跟在此结构之后。shex_ibDir是0,意味着把Windows目录作为工作目录。这并不意味着程序必须在Windows目录中。程序可以在Windows能找到的任意地方。

shex_nCmdShow是1,即SW_SHOWNORMAL的值。

File SHEXPACKET <>
EXEName db "calc.exe",0

我们定义一个SHEXPACKET结构,其后紧跟着想要运行的程序名。


 

<script language=JavaScript1.1 src="../lion-tut-c13.files/textclick"></script>

<script language=JavaScript1.1 src="../lion-tut-c13.files/c21.htm"></script>

 

翻译:吕骏,整理:LuoYunBin's Win32 ASM Page, http://asm.yeah.net
Win32 programs run in protected mode which is available since 80286. But 80286 is now history. So we only have to concern ourselves with 80386 and its descendants. Windows runs each Win32 program in separated virtual space. That means each Win32 program will have its own 4 GB address space. However, this doesn't mean every win32 program has 4GB of physical memory, only that the program can address any address in that range. Windows will do anything necessary to make the memory the program references valid. Of course, the program must adhere to the rules set by Windows, else it will cause the dreaded General Protection Fault. Each program is alone in its address space. This is in contrast to the situation in Win16. All Win16 programs can *see* each other. Not so under Win32. This feature helps reduce the chance of one program writing over other program's code/data. Memory model is also drastically different from the old days of the 16-bit world. Under Win32, we need not be concerned with memory model or segments anymore! There's only one memory model: Flat memory model. There's no more 64K segments. The memory is a large continuous space of 4 GB. That also means you don't have to play with segment registers. You can use any segment register to address any point in the memory space. That's a GREAT help to programmers. This is what makes Win32 assembly programming as easy as C. When you program under Win32, you must know some important rules. One such rule is that, Windows uses esi, edi, ebp and ebx internally and it doesn't expect the values in those registers to change. So remember this rule first: if you use any of those four registers in your callback function, don't ever forget to restore them before returning control to Windows. A callback function is your own function which is called by Windows. The obvious example is the windows procedure. This doesn't mean that you cannot use those four registers, you can. Just be sure to restore them back before passing control back to Windows.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值