VxD programs are divided into two types: static and dynamic. Each loading method is different, and the received initialization and ending control messages are also different.
Static VxD:
In the following cases, the VMM loads a static VxD:
A real-mode resident program calls this VxD by calling interrupt 2FH, 1605H.
This VxD is defined in the following location in the registry:
HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Services\\VxD\\key\\StaticVxD=VxD with path file name
This VxD is defined under the [386enh] line in: [386enh] section:
device=VxD file name with path
During development, I suggest you load VxD programs from the program, because if your VxD program is wrong and Windows cannot be started, you can modify it under Dos, but if you use the registry loading method, it cannot be modified.
When the VMM loads your static VxD program, your VxD program will receive three system control messages in the following order:
Sys_Critical_Init After the VMM is transferred to protected mode, it sends this control message before opening the interrupt. Most VxD programs do not use this message unless:
Your VxD program needs to take over the interrupts that other VxD programs or protected mode programs need to use. Since the interrupt is not turned on when you process this message, you can be sure that the interrupt will not be called when you take over the interrupt.
Your VxD program provides some VxD services for other VxD programs. For example, some VxD programs loaded after your VxD program need to call some of your VxD services when processing Device_Init control messages. Since the Sys_Critical_Init control messages are sent before the Device_Init message, you should initialize your program when the Sys_Critical_Init message is sent.
If you want to process this message, you should complete the initialization work as quickly as possible to avoid loss of hard interrupts caused by too long execution time. (Remember: The interrupt has not been turned on yet)
Device_Init VMM sends this message after an open interrupt. Most VxD programs are initialized when they get this message. Because interrupts are all open, time-consuming operations can also be performed here without fear of losing hard interrupts. You can initialize it at this point (if you need it).
Init_Complete After all VxD programs process the Device_Init message, the VMM sends this control message before the VMM releases the initialization segment (ICODE and RCODE segment classes). There are only a few VxDs that need to process this message.
After your VxD program is initialized successfully, it must clear the return flag. Otherwise, the return flag must be set to the error message before returning. If your VxD does not need to be initialized, you do not have to process these messages.
When static VxD is to be terminated, the VMM sends the following control message:
System_Exit2 When your VxD program receives this message, Windows 95 is shutting down the system, and all other virtual machines except the system virtual machines have been exited. Nevertheless, the CPU is still in protected mode, and it is safe to perform real-mode encoding on the system virtual machine. It has been uninstalled at this time.
Sys_Critical_Exit2 When all VxDs complete the response processing to System_Exit2 and the interrupts are closed, your VxD receives this message.
Many VxD programs do not respond to these two messages unless you are ready for the system to switch to real mode. You should know that when Window95 is turned off, it enters real mode. So if your VxD program does some operations on the real-mode image that will cause it to be unstable, it needs to be restored at this time.
You may be wondering why both messages are followed by a "2\" . This is because when VMM loads VxD programs, it is loaded in the order in which VxD with small initialization values are loaded first, so that the VxD program can use the services provided by the VxD program loaded before them. For example, if VxD2 wants to use the services in VxD1, it must define its initialization order value smaller than VxD. The loading order is:
..... VxD1 ===> VxD2 ===> VxD3 .....
Then when uninstalling, it is natural that VxD programs with larger initialization order values are uninstalled first, so that they can still use services provided by those VxD programs that are loaded later. As in the example above, the order is:
.... VxD3 ===> VxD2 ===> VxD1.....
In the above example, if VxD2 calls some services in VxD1 during initialization, then it may also use some services in VxD1 again during uninstallation. System_Exit2 and Sys_Critical_Exit2 are sent in de-initialization order. This means that when VxD2 receives these messages, VxD1 has not been uninstalled yet, and it can still call VxD1's service, while System_Exit and Sys_Critical_Exit messages are not sent in de-initialization order. This means that you are not sure if you can still call the VxD service provided by VxD that was loaded before you. The next generation of VxD programs should not use these messages.
There are two more exit messages:
Device_Reboot_Notify2 tells VxD program VMM is preparing to restart the system. At this time, the interruption is still open.
Crit_Reboot_Notify2 tells VxD program VMM is preparing to restart the system. At this time the interrupt has been closed.
At this point, you can guess there are Device_Reboot_Notify and Crit_Reboot_Notify messages, but they are not sent in de-initialization order like the "2" version of the messages.
Dynamic VxD:
Dynamic VxD can be loaded and uninstalled dynamically in Windows 9x. This feature is not available below. The main function of the dynamic VxD program is to support the reinstallation of certain dynamic hardware devices, such as: plug and play devices. Nevertheless, you can load/uninstall it from your Win32 program, or think of it as an extension to ring-0 of your program.
The example we mentioned in the previous section is a static VxD, which you can convert into a dynamic VxD, just add the keyword DYNAMIC to the VxD tag in the .def file.
VxD FIRSTVxD DYNAMIC
This is 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 your Windows directory \\SYSTEM\\IOSUBSYS directory. VxD in this directory will be loaded by the input and output monitor (ios). These VxDs must support layer device drivers. So using this method to load your dynamic VxD is not a good idea.
Load the service with VxD. VxDLDR is a static VxD that can load dynamic VxD. You can call its services in other VxD or in 16-bit code.
Use the CreateFile API in Win32 applications. When you call CreateFile, your dynamic VxD needs to be filled in the following format:
\\\\.\\VxD full path name
For example, if you want to load a dynamic VxD named FirstVxD in the current directory, you need to do the following:
.data
VxDName db \"\\\\.\\\",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 is uninstalled when the handle returned by CreateFile is closed.
If you use CreateFile to load a dynamic VxD, then this dynamic VxD must process 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 it returns. When the application calls the DeviceIoControl API to communicate with a dynamic VxD, the w32_DeviceIoControl message is also sent. We will talk about the DeviceIoControl interface in the next chapter.
A dynamic VxD receives a message when initialized:
Sys_Dynamic_Device_Init
At the end, a control message was also received:
Sys_Dynamic_Device_Exit
Dynamic VxD will 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, dynamic VxD can receive all control messages as long as it is still in memory. It can do everything static VxD can do. Simply put, dynamic VxD can do everything static VxD can do except the loading mechanism and the received initialization/end message different from static VxD.
Other system control messages
When VxD is in memory, in addition to receiving, initializing and ending related messages, it also receives many other control messages. Some messages are about virtual machine managers, and some are about various events. For example, the message about the virtual machine is as follows:
Create_VM
VM_Critical_Init
VM_Suspend
VM_Resume
Close_VM_Notify
Destroy_VM
It is your own responsibility to selectively respond to the messages you are interested in.
Create a function in VxD
You need to define your function in a segment. You should first define a segment and then put your function in it. For example, if you want to put your function into a pivot section. You should first define a pivotable page segment, 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 functions must exist in memory at all times, such as certain hardware interrupt handlers, put them in the locked page segment, otherwise, you should put them in the adjustable page segment.
You need to use BeginProc and EndProc macros to define your function:
BeginProc function name
EndProc function name
You can also add some parameters using the BeginProc macro. If you want to know these details, you can check out the documentation of Win95 DDK. Most of the time, you just need to fill in the name of the function.
Because the BeginProc-EndProc macro is stronger than the proc-endp instruction, you should use the BeginProc-EndProc macro instead of the proc-endp instruction.
VxD Programming Convention
Register usage
Your VxD program can use all registers, FS and GS. But be careful when changing segment registers. In particular, be sure not to change the content of CS and SS 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 returning. There are two feature bits that are particularly important: the direction and the interrupt feature bit. Do not block interrupts for a long time. Also, if you want to change the direction feature bit, don't forget to restore its initial value before returning.
Parameter passing agreement
There are two calling conventions for VxD service functions: register method and stack method. When calling the register service function, you pass the parameters of the service function through various registers. And, check the register value after the call is completed to see if the operation is successful. Don't always think that the value of the main register is the same as before after calling the service function. When calling the stack method service function, you press the parameter to be passed and get the return value in eax. The service function of the stack call method holds the values of ebx, esi, edi and ebp. Many register call service functions originated from the era. Most of the time, you can distinguish these two service functions by name. If the name of a function starts with a scribing, such as _HeapAllocate, it is a stack service function (except a few exported functions). If the function name does not start with a scribing, it is a register method service function.
Call VxD service function
You can call VMM and VxD services through the VMMCall and VxDCall macros. The syntax of these two macros is the same. When you want to call the VxD service function exported by VMM, use VMMCall. When you want to use VxD service functions exported by other VxD programs, use VxDCall.
VMMCall service ; Call register method service function e
VMMCall_service, ; Calling stack method service function
As I said earlier, VMMCall and VxDCall decompose a 20h interrupt with a double word, which is very convenient to use. When you call the stack method service, you must enclose your parameter columns in corner brackets.
VMMCall _HeapAllocate, <, HeapLockedIfDP>
_HeapAllocate is a stack service function. It has two parameters, we have to enclose them in corner brackets. Since the first parameter is an expression that this macro cannot interpret correctly, we have to enclose it in a corner bracket.
Flat address
In old compilation tools, when you use the offset operator, the compiler and the connector will generate an error address, so VxD writers use offset flat: instead of offset. Includes a macro that makes this simpler: OFFSET32 instead of offset flat:. So if you want to use an address to operate, use OFFSET32 instead of the offset operator.
Note: When I was writing this tutorial, I tried using the offset operator. It generates the correct address. So I think MASM6.14 fixes this bug. But for safety reasons, you should still use OFFSET32 macro instead of offset.