Вызов функции обратного вызова
После окончательного формирования буфера, расположенного в DLL-библиотеке d2w.dll, драйвер может вызвать функцию обратного вызова из d2w.dll, адрес которой был зарегистрирован ранее. Однако драйвер не может просто вызвать эту функцию оператором call или другим аналогичным способом.
Дело в том, что задачей функции обратного вызова, расположенной в d2w.dll, является запись сообщения в очередь приложения dos2win.exe. Для этого надо сделать так, чтобы в момент вызова была активна системная виртуальная машина, в рамках которой работают приложения Windows.
В то же время на момент вызова процедуры фильтра прерывания INT21h активна виртуальная машина MS-DOS. Поэтому нам нужно обеспечить переключение на системную виртуальную машину и вызвать функцию обратного вызова из среды системной виртуальной машины.
Эта задача решается сервисом Schedule_VM_Event. Перед вызовом сервиса в регистр EBX необходимо записать идентификатор виртуальной машины, для которой нужно сделать вызов функции обратного вызова, в регистр ESI - адрес процедуры обратного вызова, расположенной внутри виртуального драйвера, а в регистр EDX - произвольное значение, которое будет передано функции обратного вызова.
Заметьте, что для данного сервиса в регистре ESI необходимо указать адрес процедуры обратного вызова, расположенной в нулевом кольце защиты, т. е. в виртуальном драйвере, но не в DLL-библиотеке d2w.dll. В этом нет ничего страшного, нужная нам функция обратного вызова будет вызвана той процедурой, адрес которой передается сервису Schedule_VM_Event в регистре ESI.
Для определения идентификатора системной виртуальной машины мы использовали сервис Get_Sys_VM_Handle, возвращающий искомый идентификатор в регистре EBX.
Через регистр EDX мы передаем идентификатор текущей виртуальной машины (т. е. виртуальной машины MS-DOS, запустившей приложение Windows). Этот идентификатор возвращается в регистре EBX сервисом Get_Cur_VM_Handle.
Что происходит после вызова сервиса Schedule_VM_Event?
Через некоторое, достаточно малое время, происходит переключение на системную виртуальную машину, после чего управление передается процедуре CallbackProc, расположенной в нашем виртуальном драйвере.
Эта процедура, прежде всего, сохраняет контекст (состояние) системной виртуальной машины, вызывая специально предназначенную для этого макрокоманду Push_Client_State.
Затем она вызывает функцию обратного вызова, расположенную в d2w.dll, используя специально предназначенный для этого сервис. Опять это нельзя сделать прямым вызовом, так как функция обратного вызова находится в памяти системной виртуальной машины в третьем кольце защиты, а процедура CallbackProc - в памяти виртуального драйвера в нулевом кольце защиты. При этом в частности, используются разные стеки.