iczelion Vxd tut4 (转)[@more@]
VxD Programming: Primer
We know how to build a VxD which does nothing. In this tutorial, we will make it more productive by adding control message handlers.
VxD Initialization and TeRmination
There are two types of VxD: static and dynamic. Each type has different load method. They also receive different initialization and termination control messages.
VMM loads a static VxD when:
- A real-mode resident programs issue int 2Fh, 1605h to load it
- The VxD is specified in the registry under the key:
- The VxD is specified in system.ini under [386enh] section:
During development period, I suggest you load the VxD from system.ini because if something goes wrong with your VxD to the extent that windows
cannot start, you can edit system.ini from DOS. You can't do that with the registry loading method.
When the VMM loads your static VxD, your VxD will receive three system control messages in the following order:
- Sys_Critical_Init VMM sends this control message after switching into protected mode but before enabling interrupts. Most VxDs don't need to handle this message except when:
If you process this message, you should do your initialization as quickly as possible to prevent hardware interrupt losses due to excessive latency. (Remember: the interrupts are disabled)
- Your VxD hooks some interrupts that will be called later by other VxDs or protected mode programs. Since the interrupts are disabled when you process this control message, you can be sure that the interrupts you're hooking won't be called during the time you're hooking them.
- Your VxD provides some VxD services that will be called during initialization by other VxDs. For example, some VxD that loads after your VxD may need to call one of your VxD's services during Device_Init control message processing. Since Sys_Critical_Init message is sent before Device_Init message, you must initialize your services during Sys_Critical_Init message.
- Device_Init VMM sends this control message after the interrupts are enabled. Most VxDs perfoRM initialization in response to this message. Because the interrupts are enabled, time-consuming operations can be done without the fear of losing hardware interrupts. You should do your initialization here (if needed).
- Init_Complete After all VxDs processed Device_Init message but before the VMM releases all initialization segments (ICODE and RCODE segment classes), the VMM sends this control message. Few VxDs need to process this message.
Your VxD must
clear the carry flag if the initialization is successf
ul else you must set the carry flag in case of error before returning. You don't have to process any of the three initialization messages if your VxD doesn't need any initialization.
When it's time to terminate the static VxD, the VMM sends the following control messages:
- System_Exit2 When your VxD receives this message, Windows 95 is about to shut down. All other VMs except the system VM are already destroyed. However the cpu is still in protected mode and it's still safe to execute real-mode code in the system VM. Kernel32.dll was already unloaded by this time.
- Sys_Critical_Exit2 Your VxD receives this message when all VxDs have processed System_Exit2 and the interrupts are disabled.
Most VxDs don't need to process those two messages, except when you want to prepare the system to enter real mode. You should know that when Windows 95 shuts down, it enters real mode. So if your VxD did something to the real-mode image that will make it unstable, it should restore the change during this time.
You may wonder why those two exit messages have "2
" appended to them.Remember that when the VMM loads the static VxDs, it loads the VxDs with the lower initialization order first so that the VxDs can rely upon services of the VxDs that load before them. For example, if VxD2 relies on the services of VxD1, it must specify its initialization order to be larger than that of VxD1. The load order would be:
..... VxD1 ===> VxD2 ===> VxD3 .....
Now during unloading, it stands to reason that the VxDs that initialize later should uninitialize first so that they may still call VxD services of the VxDs that were loaded before them. In the above example, the order should be:
.... VxD3 ===> VxD2 ===> VxD1.....
In the above example, if VxD2 called some VxD1's services during initialization, it may need to rely on VxD1's services again during unloading. System_Exit2
are sent in reverse initialization order
. It means that, when VxD2 receives those messages, VxD1 hasn't done uninitialization yet and it can still call VxD1's services. System_Exit
messages are not sent in reverse initialization order. It means that when you process those two messages, you can't be sure that you can still call VxD's services of the VxDs that were loaded before you. Those messages should not be used for newer VxDs.
There are two more exit messages:
- Device_Reboot_Notify2 Notifies the VxD that the VMM is going to restart the system. The interupts are still enabled during this time.
- Crit_Reboot_Notify2 Notifies the VxD that the VMM is going to restart the system. The interrupts are disabled.
By now, you should be able to guess that there are Device_Reboot_Notify
messages but they are not sent in reverse initialization order as the "2" version.
Dynamic VxDs can be dynamically loaded and unloaded during Windows 9x sessions. This feature is not available under Windows 3.x. The primary goal of dynamic VxDs is to support dynamic hardware reconfiguration such as Plug and Play devices. However, you can load/unload them from your win32
applications as well, making them ideal as your applications' ring-0 extension.
The example in the previous tutorial is a static VxD. You can convert the example into a dynamic VxD by adding the keyword DYNAMIC
to the VXD
statement in .DEF file.
VXD FIRSTVXD DYNAMIC
That's all you have to do to convert a static VxD into a dynamic one.
Dynamic VxD can be loaded by:
- Putting it in SYSTEMIOSUBSYS folder in your Windows folder. The VxDs in this folder will be loaded by Input Output Supervisor (IOS). The VxDs in this folder should support layer device drivers so it may not be a good idea to load your dynamic VxD this way.
- Using VxD Loader service. VxDLDR is a static VxD that can load dynamic VxDs. You can call its services from other VxDs or from 16-bit code.
- Using CreateFile api from a Win32 application. You specify the dynamic VxD you want to load to CreateFile in the format below:
For example, if you want to load a dynamic VxD named FirstVxD which is in the current directory, you should do it as follows:
VxDName db ".FirstVxD.VXD",0
hDevice dd ?
invoke CreateFile, addr VxDName,0,0,0,0, FILE_FLAG_DELETE_ON_CLOSE,0
flag specifies that the VxD is unloaded when the handle returned by CreateFile
If you use CreateFile to load a dynamic VxD, the VxD must
sends this control message to your dynamic VxD when it is first loaded by CreateFile method. Your VxD must return 0 in eax in response to this message. w32_DeviceIoControl
messages are also sent when the application calls DeviceIoControl
API to communicate with the VxD. We will examine DeviceIoControl
interface in the later tutorial.
A dynamic VxD receives one message during initialization:
And one control message during termination:
A dynamic VxD won't receive Sys_Critical_Init
control messages because those messages are sent during system VM initialization. Other than that, dynamic VxDs receives all other control messages when it's in memory. It can do anything a static VxD can do. In short, a dynamic VxD is loaded with different mechanisms and receives different initialization/termination control messages, other than that, it can do anything a static VxD can do.
Other System Control Messages
During the time a VxD stays in memory, it will receive many control messages other than those related to initialization and termination. Some of them are related to virtual machine management and some to miscellaneous events. For example, VM-related control messages are:
It's your responsibility to choose to respond to the control messages you're interested in.
Creating procedures inside VxD
You declare a procedure in VxD inside a segment. You should define a segment first and then put your procedure inside it. For example, if you want your function to be in a pageable segment, you should define a pageable segment first, like this:
[Your procedure here]
You can put many procedures inside a segment. You as the VxD writer must decide in which segment you should put your procedures. If your procedures must be in memory at all time such as hardware interrupt handlers, put them in a locked segment. Otherwise you should put them in the pageable segment.
You define your procedure with BeginProc
is the name of your procedure. BeginProc
macro can take several more parameters, you should consult Win95 DDK documentation for detail. But most of the time, you can get by with only the name of the procedure.
You should use BeginProc-EndProc
instead of the normal proc-endp
directives because BeginProc-EndProc
macros provide more functionality than proc-endp
VxD Coding Convention
Your VxD can use any general register, FS and GS. But you should beware about modifying segment registers. Especially, you should definitely not alter CS and SS unless you are quite positive you know what you're doing. You can use DS and ES so long as you remember to restore their values when you return. Two flags are especially important: direction and interrupt flags. You should not disable interrupts for an extended period of time and if you modify the direction flag, don't forget to restore its previous state before you return.
There are two calling conventions for VxD services: register-based and stack-based. With register-based services, you pass parameters to the services via various registers and you can check the carry flag after calling the service to see if the operation is successful. You can not assume that the values in the general registers will be preserved after calling the services. With stack-based services, you push the parameters on the stack and you got the return value in eax. Stack-based services preserve ebx, esi, edi and ebp. Most of the register-based services originate from Windows 3.x days. Most of the time, you can differentiate between those two kinds of services by looking at the names. If the name of the service begins with an underSCO
re like _HeapAllocate
, it's a stack-based (C) service (except for a few services exported by VWIN32.VXD
). If the service name doesn't begin with an underscore, it's a register-based service.
Calling VxD Services
You call VMM and VxD services by using VMMCall
macros. Both macros have exactly the same syntax. You use VMMCall
when you want to call VxD services exported by VMM and you use VxDCall
when you call services exported by VxDs other than the VMM.
VMMCall service ; for calling register-based service VMMCall
VMMCall _service, ; for calling stack-based service
decompose to int 20h followed by a dword that I described in the previous tutorial but they are much more convenient to use. In the case of stack-based services, you must enclose the argument list with a pair of angle bracket.
VMMCall _HeapAllocate, <, HeapLockedIfDP>_HeapAllocate
is a stack-based service. It accepts two parameters. We must enclose them inside an angle bracket. However, the first parameter is an expression that the macro may interpret incorrectly, so we put it inside another angle bracket.
In older tools, the assembler and linker generate incorrect addresses when you use offset
operator. So VxD programmers use offset flat:
instead of offset
. vmm.inc contains a macro to make it easier. OFFSET32
expands to offset flat:
. So if you want to use offset operator, you should use OFFSET32
Note: I experimented with offset operator when I wrote this tutorial. It generated correct addresses so I think the bug has been removed in MASM 6.14. But just to play safe, you should use OFFSET32 macro instead of the plain offset.
[.NET/">Iczelion's Win32 Assembly Homepage]
来自 “ ITPUB博客 ” ，链接：http://blog.itpub.net/10752043/viewspace-990501/，如需转载，请注明出处，否则将追究法律责任。