Файл vxdsrv\vxdsrv.asm
; --------------------------------------------------- ; Виртуальный драйвер VXDSRV ; Version 1.1 ; --------------------------------------------------- ; Copyright (C) Александр Фролов 1995 ; --------------------------------------------------- ; Работает совместно с приложением dos2win.exe ; и DLL-библиотекой d2w.dll ; ; Позволяет запускать приложения Windows из виртуальной ; машины MS-DOS, из командной строки Norton Commander или ; аналогичной оболочки, работающей на виртуальной ; машине MS-DOS ; ; Выполняет перехват функции 4B00h прерывания int 21h ; и сохраняет полученные этой функцией командную строку ; и строку параметров, а также определенные отдельно ; текущий диск и текущий каталог в области памяти, ; зарезервированной DLL-библиотекой d2w.dll. ; Затем драйвер вызывает функцию, определенную ; в этой библиотеке и посылающую сообщение приложению ; dos2win.exe. Приняв сообщение, приложение dos2win.exe ; запускает программу, пользуясь данными, полученными ; от виртуального драйвера ; ---------------------------------------------------
.386p include vmm.inc
; Идентификатор драйвера VXDSRV. VXDSRV_Id equ 8000h
HiVers equ 1 ; верхний номер версии драйвера LoVers equ 1 ; нижний номер версии драйвера Vers equ ((HiVers shl 8) or LoVers) CFlag equ 1
; =================================================== ; Заголовок виртуального драйвера ; =================================================== Declare_Virtual_Device VXDSRV, HiVers, LoVers, \ VXDSRV_Control, VXDSRV_Id, Undefined_Init_Order, \ VXDSRV_V86API_Handler, VXDSRV_PMAPI_Handler,
; =================================================== ; Инициализация в реальном режиме ; =================================================== VXD_REAL_INIT_SEG
RealInit proc near
; Вывод "рекламной" текстовой строки mov ah, 9 mov dx, offset VxD_Hello int 21h
; Признак успешной инициализации mov ax, Device_Load_Ok
; Страницы физической памяти не резервируются xor bx, bx
; Данные для каждого экземпляра виртуальной машины ; не резервируются xor si, si
; Значение, передаваемое процедуре Sys_Critical_Init xor edx, edx ret RealInit endp
VxD_Hello db '*VXDSRV* Copyright (C) Alexandr Frolov 1995' db 0dh, 0ah, '$' VXD_REAL_INIT_ENDS
; =================================================== ; Системная критическая инициализация ; =================================================== VXD_ICODE_SEG BeginProc VXDSRV_Sys_Crit_Init
; Устанавливаем фильтр для прерывания int 21h mov eax, 21h mov esi, offset32 V86_Int21_Handler VMMcall Hook_V86_Int_Chain
clc ; признак успешного завершения ret EndProc VXDSRV_Sys_Crit_Init VXD_ICODE_ENDS
; =================================================== ; Зафиксированный сегмент кода ; =================================================== VxD_LOCKED_CODE_SEG
; --------------------------------------------------- ; Определение управляющих процедур ; --------------------------------------------------- BeginProc VXDSRV_Control
; Процедура системной критической инициализации Control_Dispatch Sys_Critical_Init, VXDSRV_Sys_Crit_Init
clc ret EndProc VXDSRV_Control
VxD_LOCKED_CODE_ENDS
; =================================================== ; Сегмент данных ; =================================================== VxD_DATA_SEG
; --------------------------------------------------- ; Таблица адресов функций API драйвера ; --------------------------------------------------- VXDSRV_API_Call label dword
dd offset32 vxdapiGetVersion ; AX=0 dd offset32 vxdapiRegisterWnd ; AX=1 dd offset32 vxdapiUnregisterWnd ; AX=2
VXDSRV_API_MaxCall EQU ($ - VXDSRV_API_Call) / 4
CallbackSel dw 0h ; селектор функции обратного вызова CallbackOff dd 0h ; смещение функции обратного вызова V86CallFlag dd 0 ; вызов из виртуальной машины V86 CallbackBuf dd 0 ; адрес буфера для передачи строки
flatpCmdLine dd 0 ; адрес командной строки flatpParmLine dd 0 ; адрес строки параметров hSem dd 0 ; идентификатор семафора
nCurDisk db 0 ; текущий диск szCurPath db 64 dup(0) ; текущий каталог
; Сегмент буфера для получения текущего пути wPathSeg dw 0
; Соответствующий этому буферу FLAT-адрес flatpPathBuf dd 0
VxD_DATA_ENDS
; =================================================== ; Перемещаемый сегмент кода ; =================================================== VxD_CODE_SEG
; --------------------------------------------------- ; Вход API для виртуальных машин V86 ; --------------------------------------------------- BeginProc VXDSRV_V86API_Handler mov eax, 1 ; признак машины V86 mov V86CallFlag, eax
; Вызываем универсальное API драйвера call VXDSRV_API_Handler ret EndProc VXDSRV_V86API_Handler
; --------------------------------------------------- ; Вход API для виртуальных машин защищенного режима ; --------------------------------------------------- BeginProc VXDSRV_PMAPI_Handler mov eax, 0 ; признак защищенного режима mov V86CallFlag, eax
call VXDSRV_API_Handler ret EndProc VXDSRV_PMAPI_Handler
; --------------------------------------------------- ; Универсальное API драйвера, вызывается как для ; режима VM86, так и для защищенного режима ; --------------------------------------------------- BeginProc VXDSRV_API_Handler
; Загружаем номер функции API movzx eax, [ebp.Client_AX]
; Проверяем этот номер на допустимость cmp eax, VXDSRV_API_MaxCall jae short InvalidNumber
; В случае успеха сбрасываем флаг переноса и ; вызываем функцию по таблице адресов and [ebp.Client_EFlags], NOT CFlag call VXDSRV_API_Call[eax * 4] ret InvalidNumber: or [ebp.Client_EFlags], CFlag ret EndProc VXDSRV_API_Handler
; --------------------------------------------------- ; vxdapiGetVersion, номер = 0 ; Возвращает в AX номер версии: ; AH - старший номер, AL - младший номер ; --------------------------------------------------- BeginProc vxdapiGetVersion mov [ebp.Client_AX], Vers clc ; успешное завершение ret EndProc vxdapiGetVersion
; --------------------------------------------------- ; vxdapiRegisterWnd, номер = 1 ; --------------------------------------------------- BeginProc vxdapiRegisterWnd
; Можно вызывать только из защищенного режима mov eax, V86CallFlag cmp eax, 0 jnz short RW_CallFromRealMode
; Сохраняем смещение и селектор процедуры обратного ; вызова, расположенной в фиксированном сегменте ; кода DLL-библиотеки d2w.dll
movzx eax, [ebp.Client_CX] ; смещение mov [CallbackOff], eax mov ax, [ebp.Client_DX] ; селектор mov [CallbackSel], ax
; Преобразуем адрес буфера во flat-адрес
mov ax, (Client_SI shl 8) + Client_DI VMMcall Map_Flat mov [CallbackBuf], eax
; Создаем семафор с начальным значением 1 mov ecx, 1 VMMcall Create_Semaphore mov hSem, eax ; сохраняем идентификатор семафора
clc ret
RW_CallFromRealMode: stc ret EndProc vxdapiRegisterWnd
; --------------------------------------------------- ; vxdapiUnregister, номер = 2 ; --------------------------------------------------- BeginProc vxdapiUnregisterWnd
; Можно вызывать только из защищенного режима mov eax, V86CallFlag cmp eax, 0 jnz short UW_CallFromRealMode
mov eax, 0 mov [CallbackOff], eax mov [CallbackSel], ax mov [CallbackBuf], eax
; Уничтожаем семафор mov eax, hSem VMMcall Destroy_Semaphore
clc ret
UW_CallFromRealMode: stc ret EndProc vxdapiUnregisterWnd
; --------------------------------------------------- ; StrCpy, копирование строки ASCIIZ ; [ecx] - исходный адрес ; [edx] - адрес буфера ; --------------------------------------------------- BeginProc StrCpy push eax push ecx push edx
StrCpyLoop: mov al, [ecx] mov [edx], al cmp al, 0 jz short StrCpyEnd inc ecx inc edx jmp short StrCpyLoop StrCpyEnd:
pop edx pop ecx pop eax ret EndProc StrCpy
; --------------------------------------------------- ; CopyParm, копирование строки параметров ; --------------------------------------------------- BeginProc CopyParm push eax push ebx push ecx push edx
; Вычисляем адрес строки параметров в буфере mov edx, CallbackBuf add edx, 65 + 128
; Определяем размер строки параметров mov ecx, flatpParmLine movzx ebx, byte ptr [ecx] inc ecx
; Если параметров нет, закрываем строку нулем cmp ebx, 0 jz short ParmCopyEnd
; Цикл копирования строки параметров ParmCopyLoop: cmp ebx, 0 jz short ParmCopyEnd
mov al, [ecx] mov [edx], al
dec ebx inc ecx inc edx jmp short ParmCopyLoop
ParmCopyEnd:
; Закрываем строку нулем mov al, 0 mov [edx], al
pop edx pop ecx pop ebx pop eax ret EndProc CopyParm
; --------------------------------------------------- ; V86_Int21_Handler ; Фильтр для функции 4B00h прерывания INT 21h, ; вызываемого из виртуальной машины MS-DOS ; (эта функция выполняет запуск программы MS-DOS) ; --------------------------------------------------- BeginProc V86_Int21_Handler
pushad
; Проверяем номер функции. Нас интересует только ; запуск программ mov ax, word ptr [ebp.Client_AX] cmp ax, 4B00h jnz HandlerExit
; Если окно приложения dos2win не зарегистрировано, ; ничего не делаем mov eax, CallbackBuf cmp eax, 0 jz HandlerExit
; Если запускается программа MS-DOS, ничего ; не делаем call short IsWindowsApp jz HandlerExit
; Для исключения реентерабельных вызовов выполняем ; ожидание семафора mov eax, hSem mov ecx,(Block_Enable_Ints OR Block_Svc_If_Ints_Locked) VMMcall Wait_Semaphore
; Получаем текущий диск и каталог call short GetCurDir
; Сохраняем номер текущего диска mov edx, CallbackBuf mov al, nCurDisk mov byte ptr [edx], al
; Определяем FLAT-адрес командной строки mov ax, (Client_DS shl 8) + Client_DX VMMcall Map_Flat mov flatpCmdLine, eax
; Определяем FLAT-адрес блока EPB mov ax, (Client_ES shl 8) + Client_BX VMMcall Map_Flat
; Загружаем в DX:BX адрес строки параметров mov bx, [eax + 2] mov dx, [eax + 4]
; Определяем FLAT-адрес строки параметров
push dword ptr [ebp.Client_ES] push [ebp.Client_EBX]
mov [ebp.Client_ES], dx mov [ebp.Client_BX], bx mov ax, (Client_ES shl 8) + Client_BX VMMcall Map_Flat mov flatpParmLine, eax
pop [ebp.Client_EBX] pop dword ptr [ebp.Client_ES]
; Копируем командную строку в буфер, который ; находится в DLL-библиотеке d2w.dll mov ecx, flatpCmdLine mov edx, CallbackBuf add edx, 65 call short StrCpy
; Выполняем копирование строки параметров call short CopyParm
; Определяем идентификатор текущей VM VMMCall Get_Cur_VM_Handle mov edx, ebx
; Определяем идентификатор системной VM VMMcall Get_Sys_VM_Handle
; Планируем вызов функции обратного вызова mov esi, offset32 CallbackProc VMMcall Schedule_VM_Event
popad
; Если запускается приложение Windows, блокируем ; выполнение прерывания INT 21h в виртуальной ; машине MS-DOS clc
ret
HandlerExit: popad
; Если запускается программа MS-DOS, наш драйвер ; не мешает этому процессу stc
ret EndProc V86_Int21_Handler
; --------------------------------------------------- ; CallbackProc ; Функция обратного вызова ; Вызывается в системной VM по запросу фильтра ; прерывания INT 21h, установленного нашим драйвером ; --------------------------------------------------- BeginProc CallbackProc
; Сохраняем состояние системной VM Push_Client_State
; Начинаем вложенное выполнение VMMcall Begin_Nest_Exec
; Записываем в стек системной VM параметр - ; версию нашего VxD-драйвера mov ax, Vers VMMcall Simulate_Push
; Вызов функции обратного вызова, определенной ; в DLL-библиотеке d2w.dll mov edx, [CallbackOff] mov cx, [CallbackSel] VMMcall Simulate_Far_Call
; Выполняем вызов и восстановление состояния ; системной VM VMMcall Resume_Exec VMMcall End_Nest_Exec Pop_Client_State
; Сбрасываем семафор, разрешая обработку ; следующего прерывания INT 21h mov eax, hSem VMMcall Signal_Semaphore
ret EndProc CallbackProc
; --------------------------------------------------- ; GetCurDir ; Определение текущего диска и текущего каталога ; в виртуальной машине MS-DOS, выполняющей запуск ; программы с помощью прерывания INT 21h ; --------------------------------------------------- BeginProc GetCurDir
pushad
; Сохраняем состояние VM MS-DOS Push_Client_State
VMMcall Begin_Nest_Exec
; Определяем номер текущего диска в VM MS-DOS ; Используем для этого функцию 1900h прерывания ; INT 21h. Номер диска возвращается в регистре AL mov ax, 1900h mov word ptr [ebp.Client_AX], ax mov eax, 21h VMMcall Exec_Int
; Сохраняем номер текущего диска в VM MS-DOS mov ax, word ptr [ebp.Client_AX] mov nCurDisk, al
; Для определения текущего пути VM MS-DOS ; заказываем буфер размером 64 байта в адресном ; пространстве VM MS-DOS, пользуясь функцией 4800h ; прерывания INT 21h mov ax, 4800h mov word ptr [ebp.Client_AX], ax
; Размер буфера задается в параграфах (по 16 байт) mov ax, 0004h mov word ptr [ebp.Client_BX], ax mov eax, 21h VMMcall Exec_Int
; Сохраняем сегментную компоненту адреса ; (смещение полученного буфера всегда равно 0) mov ax, word ptr [ebp.Client_AX] mov wPathSeg, ax
; В маловероятном случае, когда в VM MS-DOS ; совсем нет свободной памяти (даже 64 байт), ; мы не заполняем строку параметров: ; если памяти нет, все равно нельзя запустить ; ни программу MS-DOS, ни приложение Windows mov ax, wPathSeg cmp ax, 0 jz DoNotGetPath
; Определяем FLAT-адрес полученного буфера mov [ebp.Client_ES], ax mov [ebp.Client_BX], 0 mov ax, (Client_ES shl 8) + Client_BX VMMcall Map_Flat mov flatpPathBuf, eax
; Получаем строку текущего каталога VM MS-DOS, ; для чего вызываем функцию 4700h прерывания ; INT 21h. Перед вызовом этой функции нужно ; предоставить в DS:SI адрес буфера размером ; 64 байта, в который и будет записана строка mov ax, wPathSeg mov word ptr [ebp.Client_DS], ax mov ax, 0 mov word ptr [ebp.Client_SI], ax mov ax, 0 mov word ptr [ebp.Client_DX], ax mov ax, 4700h mov word ptr [ebp.Client_AX], ax mov eax, 21h VMMcall Exec_Int
; Копируем строку текущего каталога в буфер ; CallbackBuf со смещением 1 байт mov ecx, flatpPathBuf mov edx, CallbackBuf add edx, 1 call short StrCpy
; Освобождаем буфер, полученный в адресном ; пространстве VM MS-DOS mov ax, wPathSeg mov word ptr [ebp.Client_ES], ax mov ax, 4900h mov word ptr [ebp.Client_AX], ax mov eax, 21h VMMcall Exec_Int
VMMcall End_Nest_Exec
DoNotGetPath:
Pop_Client_State popad ret EndProc GetCurDir
; --------------------------------------------------- ; IsWindowsApp ; Проверка файла запускаемой программы ; ; Функция возвращает флаг Z = 0, если запускается ; приложение Windows в формате NE или PE, и Z != 0, ; если запускается программа MS-DOS ; ; Входные параметры: ; [ebp.Client_DS] - сегмент буфера, в котором находится ; путь к запускаемой программе ; [ebp.Client_DX] - смещение буфера, в котором находится ; путь к запускаемой программе ; --------------------------------------------------- ; Copyright (C) Сергей Ноженко, 1995 ; --------------------------------------------------- BeginProc IsWindowsApp
Push_Client_State VMMcall Begin_Nest_Exec
; Индикатор, который будет равен нулю на выходе ; из функции, если запускаемый файл не содержит ; приложение Windows sub esi, esi
; Открываем файл с программой mov word ptr [ebp.Client_AX], 3D00h mov eax, 21h VMMcall Exec_Int
; Проверка ошибки test word ptr [ebp.Client_Flags], 1 jnz EndNest
; Сохраняем идентификатор файла mov ax, word ptr [ebp.Client_AX] push eax
; Заказываем 4 параграфа памяти mov word ptr [ebp.Client_AX], 4800h mov word ptr [ebp.Client_BX], 4 mov eax, 21h VMMcall Exec_Int
; Проверяем, выделена ли нам память mov ax, word ptr [ebp.Client_AX] or ax, ax jz CloseFile
; Читаем старый заголовок исполнимого файла ; (заголовок программы MS-DOS) ; Готовим ES для вызова функции освобождения ; памяти mov word ptr [ebp.Client_DS], ax mov word ptr [ebp.Client_ES], ax mov word ptr [ebp.Client_AX], 3F00h pop eax mov word ptr [ebp.Client_BX], ax mov word ptr [ebp.Client_CX], 40h ; читаем 40h байт mov word ptr [ebp.Client_DX], 0 mov eax, 21h VMMcall Exec_Int
; Проверка ошибки test word ptr [ebp.Client_Flags], 1 jnz FreeMem
; Если размер файла меньше 40h байт, это ; не приложение Windows cmp word ptr [ebp.Client_AX], 40h jl short FreeMem
; Получаем FLAT-адрес mov ax, (Client_DS shl 8) + Client_DX VMMcall Map_Flat
; Проверяем сигнатуру EXE-файла. Если это не ; EXE-файл, то мы имеем дело не с приложением ; Windows cmp word ptr [eax], "ZM" jne short FreeMem
; Проверяем смещение таблицы relocation table. ; Если оно меньше чем 40h, это программа MS-DOS cmp word ptr [eax + 18h], 40h jl short FreeMem
mov edi, eax
; Ищем заголовок NewEXE mov word ptr [ebp.Client_AX], 4200h mov bx, word ptr [eax + 3Ch] mov word ptr [ebp.Client_DX], bx mov bx, word ptr [eax + 3Eh] mov word ptr [ebp.Client_CX], bx mov eax, 21h VMMcall Exec_Int
test word ptr [ebp.Client_Flags], 1 jnz short FreeMem
; Читаем первое слово заголовка NewEXE mov word ptr [ebp.Client_AX], 3F00h mov word ptr [ebp.Client_CX], 2 mov word ptr [ebp.Client_DX], 0 mov eax, 21h VMMcall Exec_Int
test word ptr [ebp.Client_Flags], 1 jnz short FreeMem cmp word ptr [ebp.Client_AX], 2 jne short FreeMem
; Проверяем сигнатуру исполнимого сегмента. ; Допустимы сигнатуры "NE" (segmented executable) ; и "PE" (portable executable) cmp word ptr [edi], "EN" je short WinApp
cmp word ptr [edi], "EP" jne short FreeMem
WinApp:
; Устанавливаем индикатор - запускается ; приложение Windows inc esi
; Освобождаем память FreeMem: mov word ptr [ebp.Client_AX], 4900h mov eax, 21h VMMcall Exec_Int
; Закрываем файл CloseFile: mov word ptr [ebp.Client_AX], 3E00h mov eax, 21h VMMcall Exec_Int
EndNest: VMMcall End_Nest_Exec Pop_Client_State
or esi, esi ret EndProc IsWindowsApp
VxD_CODE_ENDS END
Заголовок виртуального драйвера VXDSRV мы описали раньше, поэтому не будем на нем останавливаться.