We learned in the last section how to write a VxD program that doesn't do anything. In this section, we want to give it the ability to process control messages.
Initialization and end of VxD
The VXD program is divided into two types: static and dynamic. Each load method is different, and the received initialization and end control messages are different.
Static VxD:
VMM loads a static VxD in the following cases:
A real-mode resident program calls this VxD by calling the interrupt 2fh,1605h.
This VXD is defined in the registry at the following location:
Hkey_local_machine\system\currentcontrolset\services\vxd\key\staticvxd=vxd with path file name
This VXD is defined under the [386enh] line in system.ini: [386enh] section:
Device=vxd with path file name
At the time of development, I recommend that you load the VXD program from the System.ini, because if your VxD program is wrong and the Windows can't start, you can modify the System.ini in DOS, and if you use the registration form, you can't change it.
When VMM loads your static VxD program, your VXD program receives three system control messages in the following order:
The Sys_critical_init VMM emits this control message before opening interrupts after it has been transferred to protection mode. Most VXD programs do not use this message unless:
Your VxD program takes over some other VXD programs or interrupts that the protected mode program uses. Since this interruption has not been opened when you are handling the message, you can be sure that this interrupt will not be invoked when you take over the interrupt.
Your VXD program provides a number of VxD services for other VXD programs. For example, some VXD programs loaded after your VXD program need to invoke some of your VxD service when processing Device_init control messages, since Sys_critical_init control messages are sent before Device_init messages, so you should be in Sys_ Initialize your program when the CRITICAL_INIT message is sent.
If you want to handle this message, you should do the initialization as soon as possible so that the hard interrupts are lost due to too long execution time. (Remember: Interrupts are not open)
The Device_init VMM sends this information after an open interrupt. Most VXD programs are initialized when they get this message. Because interrupts are open, time-consuming operations can also be performed here without fear of causing a loss of hard interrupts. You can initialize it at this point (if you want).
Init_complete The VMM emits this control message before the VMM releases the initialization segment (Icode and Rcode segment classes) after all the VxD programs have processed the DEVICE_INIT message. Only a few VxD have to deal with this message.
After successfully initializing your VXD program, you must have the return flag cleared, otherwise you must set the return flag to an error message before returning. If your VxD does not need to be initialized, you do not have to process the messages.
When you want to end a static VxD, VMM sends the following control message:
System_exit2 when your VXD program receives this message, WINDOWS95 is shutting down the system and all other virtual machines have exited except the system virtual machine. However, the CPU is still in protected mode, and it is safe to perform real mode encoding on the system virtual machine. At this time Kernel32.dll has also been unloaded.
Sys_critical_exit2 when all the VxD finishes responding to the SYSTEM_EXIT2 and the interrupts are closed, your VxD receives the message.
Many VXD programs do not respond to these two messages unless you are prepared to convert the system to real mode. You know, when Window95 closes, it goes into real mode. So if your VxD program does something that will cause it to be unstable, then it needs to be restored.
You may be wondering why the two messages followed by a "2". This is because, when VMM loads a VxD program, it is loaded in the order in which the VXD in the initialization order is small, so the VXD program can use the services that are provided by the VXD program that was loaded before them. For example, to use a service in VxD1, VxD2 must define its initialization order value to be smaller than a VxD. The order of loading is:
..... VxD1 ===> VxD2 ===> VxD3 .....
Then the uninstall, of course, is the initialization of the large order of the VXD program is uninstalled, so they can still use than they loaded after the VXD program to provide services. As in the example above, the order is:
.... VxD3 ===> VxD2 ===> VxD1 .....
In the example above, if VxD2 calls some of the services in the VxD1 when it is initialized, it may also need to use some of the services in VxD1 again when uninstalling. System_exit2 and Sys_critical_exit2 are sent in reverse initialization order. This means that when VxD2 accepts these messages, VxD1 has not yet been unloaded, and it can still invoke VxD1 services, while System_exit and sys_critical_exit messages are not sent in the order of reverse initialization. This means that you are not sure whether you can still invoke the VxD service provided before you load the VXD. The next generation of VxD programs should not use these messages.
There are also two exit messages:
Device_reboot_notify2 tells the VXD program VMM is preparing to reboot the system. The interruption is still open.
Crit_reboot_notify2 tells the VXD program VMM is preparing to reboot the system. This time the interruption has been closed.
Here, you can guess that there are device_reboot_notify and crit_reboot_notify messages, but they are not sent in the reverse initialization order as the "2" version of the message.
Dynamic VXD:
Dynamic VxD can be loaded and unloaded dynamically in windows9x. This feature is not in the window3.x. The main function of a dynamic VXD program is to support the reloading of certain dynamic hardware devices, such as Plug and Play devices. Still, you can load/unload it from your Win32 program, or you can think of it as a ring-0 extension of your program.
The example we mentioned in the previous section is a static VxD that you can convert into a dynamic VxD as long as you add the keyword dynamic to the back of the VXD tag in the. def file.
VxD Firstvxd DYNAMIC
That's all you have to do to convert a static VxD into a dynamic VxD.
A dynamic VxD can be loaded in the following ways:
Put it in the \system\iosubsys directory under your Windows directory. The VxD in this directory will be loaded by the Input Output monitor (iOS). These VxD must support layer device drivers. So it's not a good idea to load your dynamic VxD in this way.
Load the service with a VxD. VxDLdr is a static VxD that can load a dynamic VxD. You can call its services in other VxD or in 16-bit code.
Use the CreateFile API in the Win32 application. When you call CreateFile, your dynamic VxD is filled in the following format:
\\.\vxd Full path name
For example, if you are loading a dynamic VxD named Firstvxd in the current directory, you will need to do the following:
. Data
Vxdname db "\\.\firstvxd.vxd", 0
......
. Data?
Hdevice DD?
.....
. Code
.....
Invoke CreateFile, addr vxdname,0,0,0,0, file_flag_delete_on_close,0
MOV hdevice,eax
......
Invoke Closehandle,hdevice
......
File_flag_delete_on_close This flag is used to indicate that the VxD was unloaded when the handle returned by CreateFile was closed.
If you use CreateFile to load a dynamic VxD, then the dynamic VxD must handle the W32_deviceiocontrol message. When your dynamic VxD is first loaded by the CreateFile function, VWIN32 sends this message to your VXD. Your VxD responds to this message and the value in EAX must be zero when returned. When an application invokes the DeviceIoControl API to communicate with a dynamic VxD, the W32_deviceiocontrol message is also sent. We'll talk about the DeviceIoControl interface in the next chapter.
A dynamic VxD receives a message at initialization:
Sys_dynamic_device_init
You also receive a control message at the end:
Sys_dynamic_device_exit
Dynamic VxD does not receive sys_critical_init, Device_init, and init_complete control messages because these messages are sent when the system virtual machine is initialized. In addition to these three messages, the dynamic VxD receives all control messages as long as it is still in memory. It can do all the things a static VxD can do. Simply put, a dynamic VxD can do everything a static VxD can do in addition to the loading mechanism and received initialization/end messages that are different from static VxD.
Other system control messages
When a VxD is in memory, it receives many other control messages in addition to receiving and initializing and ending related messages. Some messages are about virtual Machine Manager, and some are about various events. For example, the message about a virtual machine is as follows:
Create_vm
Vm_critical_init
Vm_suspend
Vm_resume
Close_vm_notify
Destroy_vm
It is your responsibility to choose to respond to the information you are interested in.
Create a function within a VxD
You want to define your function in a section. You should first define a segment and then put your function in it. For example, if you want to put your function in an adjustable page section. You should first define an adjustable page section, like this:
Vxd_pageable_code_seg
(Your function is written here)
Vxd_pageable_code_ends
You can insert multiple functions into a segment. As a VxD writer, you have to decide which segment each function should be placed in. If your function must be in memory all the time, such as some hardware interrupt handlers, put them in the Locked page section, otherwise you should put them on the adjustable page.
You want to use Beginproc and ENDPROC macros to define your function:
Beginproc Name of the letter
Endproc Name of the letter
You can also use the BEGINPROC macro to add some parameters, and you can look at the Win95 DDK documentation for these details. Most of the time, you can just fill in the name of the function is enough.
Because Beginproc-endproc macros are more powerful than PROC-ENDP commands, you should use BEGINPROC-ENDPROC macros instead of PROC-ENDP instructions.
VXD Programming conventions
Use of Registers
Your VXD program can use all registers, FS and GS. But be careful when you change the segment register. In particular, be sure not to change the CS and SS content unless you are absolutely sure of what will happen. You can use DS and ES, but be sure to remember to restore their initial values when you return. There are two characteristic bits of particular importance: direction and interrupt characteristic bits. Do not interrupt for long time shielding. And if you want to change the direction feature bit, don't forget to restore its initial value before returning.
Parameter passing conventions
The VXD service function has two calling conventions: The Register method and the Stack method. When invoking the Register method service function, you pass the parameters of the service function through various registers. Also, check the value of the register after the call completes to see if the operation was successful. Do not always assume that the value of the primary register is the same as before when the service function is invoked. When calling the Stack method service function, you put the parameter to pass the stack, in EAX get the return value. The service function of the stack-tuning usage holds the values of Ebx,esi,edi and EBP. Many register usage service functions are derived from the windows3.x era. Most of the time, you can distinguish between these two service functions by name, and if the name of a function begins with an underscore, such as _heapallocate, it is a service function of a stack method (except for a handful of functions derived from Vwin32.vxd). If the function name does not begin with an underscore, it is a service function of the Register method.
Calling the VxD service function
You can invoke VMM and VxD services through Vmmcall and Vxdcall macros. The syntax of the two macros is the same. When you want to invoke the VxD service function exported by VMM, use Vmmcall. When you want to export a VxD service function with another VXD program, use Vxdcall.
Vmmcall Service; Call Register method service function E
Vmmcall _service, ; Call stack method Service function
As I said earlier, Vmmcall and Vxdcall break out a 20h interrupt that follows a single word, which is convenient to use. When you call the Stack method service, you have to enclose your parameter columns with parentheses.
Vmmcall _heapallocate, < , heaplockedifdp>
_heapallocate is a stack method service function. It has two parameters and we have to enclose them in brackets. Because the first argument is an expression that the macro does not interpret correctly, we have to enclose it with an angle bracket.
Flat address
In the old compilation tool, when you use the offset operator, the compiler and the linker generate an error address, so the VxD writer uses offset flat: to replace offset. Imm.inc includes a simpler macro: OFFSET32 to replace the offset flat:. So if you want to use the address operation, use OFFSET32 instead of the offset operator.
Note: When I wrote this tutorial, I tried using the offset operator. It can generate the correct address. So I think MASM6.14 fixed the bug. But for security reasons, you should use OFFSET32 macros instead of offset.