;VCALL0: Universelles VxD zum Aufruf beliebiger 16-bit-Programmteile
;als privilegierter Code. Sowohl für DOS-Real-Mode, DPMI und Windows-Programme.
;Leider ist (nebenbei) noch Windows im Enhanced Mode erforderlich, ob 3.0,
;3.10, 3.11 oder Windows95 sollte dabei keine Rolle spielen.
;
;Besonders geeignet für (Pascal-) Programme, die auf geschützte Ports
;oder solche >3FF in dichter Folge zugreifen müssen. Ebenso wird CLI
;und STI sowie der Zugriff auf ungeschützte Ports beschleunigt.
;Man kann aber auch allerlei anderen Unsinn treiben.
;
;Man bedenke jedoch beim Debuggen, daß diese privilegierten Code-Stücke
;undebugbar sind - es sei denn, man hat SoftICE für Windows.
.386p
.XLIST
INCLUDE TVMM.Inc
INCLUDE Debug.Inc
include vtd.inc
.LIST
LOCALS
VCall0_Major_Ver equ 1
VCall0_Minor_Ver equ 10
VCall0_Ver = VCall0_Major_Ver*256+VCall0_Minor_Ver
;"You have been assigned a device ID of 3A7Ah for your VCALL0.386 virtual
; device. This number is uniquely assigned to this device."
VCall0_Device_ID equ 3A7Ah
DIOCParams STRUC
Internal1 DD ?
VMHandle DD ?
Internal2 DD ?
dwIoControlCode DD ?
lpvInBuffer DD ?
cbInBuffer DD ?
lpvOutBuffer DD ?
cbOutBuffer DD ?
lpcbBytesReturned DD ?
lpoOverlapped DD ?
hDevice DD ?
tagProcess DD ?
DIOCParams ENDS
Declare_Virtual_Device VCall0, VCall0_Major_Ver, VCall0_Minor_Ver,\
VCall0_Control_Proc, VCall0_Device_ID,\
Undefined_Init_Order, VCall0_API, VCall0_API
TIMERFREQ equ 1193180 ;Hertz, Primfaktoren: 2*2*5*59659
TIMERINT equ 50h ;statt 08h unter DOS
;**********************************
;*** Residente Daten (gesperrt) ***
;**********************************
VxD_LOCKED_DATA_SEG
SR_Ring0 label fword
SR_Ring0_IP dd 0 ;E-Teil ist Null
SR_Ring0_CS dd 0 ;E-Teil ist Null (zumindest bis zum Pentium)
SR_Ring0_DS dd 0 ;(... sind Selektoren noch 16 bit)
SR_RETF_From_16 dd 0 ;Althergebrachtes CS:IP ist hier gemeint
TimerVal dd 0 ;üblicher Startwert
TimerAdd dd 27 ;44.1 kHz CD-Qualität
TimerLimit dd 65536 ;wahrscheinlich (oder auch nicht)
OldInt08 dd ? ;naja, ist ja eigentlich INT50
OldSetService dd ? ;für gehookte Timer-Services
OldReleaseService dd ?
OwningVM dd 0
data32seg dw 30h ;wahrscheinlich!
VxD_LOCKED_DATA_ENDS
;**********************************
;*** Residenter Code (gesperrt) ***
;**********************************
;Programmverteiler zuerst, damit man's beim Debuggen leichter findet.
VxD_LOCKED_CODE_SEG
BeginProc VCall0_Control_Proc
Control_Dispatch Device_Init, VCall0_Init
Control_Dispatch VM_Not_Executeable, VMDies
Control_Dispatch 1bh, VCall0_Dynamic_Init
Control_Dispatch 1ch, VCall0_Dynamic_Exit
; Control_Dispatch 23h, VCall0_IOCTL ;23h=W32_DeviceIOControl
Retu: clc
ret
EndProc VCall0_Control_Proc
;-------------------------------------------------------------------------
; Unterprogramm rufen, diverse globale Variablen enthalten Ansprungdaten
;-------------------------------------------------------------------------
BeginProc VCall0_CallSR, high_freq, no_log
Trace_Out "Setting up stack frames and preparing to call TSR."
push ebp ebx ;Wichtige VxD-Register retten
mov ebp,esp
push cs ;RETF (16:32) vorbereiten
push OFFSET32 VCall0_Back_To_Flat
push ds ;VxD-DS (30h) retten
push [SR_RETF_From_16] ;RETF (16:16) vorbereiten
mov ds,[SR_Ring0_DS]
jmp cs:[SR_Ring0] ;Serviceroutine anspringen
;Dieses Stückchen Code bekommt einen Extra-Selektor spendiert:
VCall0_Switch_To_Flat:
pop ds ;VxD-DS wiederherstellen
retf ;zum VxD-CS gehen
VCall0_STF_Limit = $-VCall0_Switch_To_Flat-1
VCall0_Back_To_Flat:
pop ebx ebp
Trace_Out "Back in flat model. Return from TSR = #AX"
ret
EndProc VCall0_CallSR
BeginProc NewInt08, high_freq, no_log ;um Himmels willen keine Debugstrings!
;dies ist die 16-bit-Version
pushad
push ds
mov ds,cs:[data32seg]
push es
push cs
push OFFSET32 @@cont
push ds
push [SR_RETF_From_16]
mov es,[data32seg]
mov ds,[SR_Ring0_DS]
mov ebp,esp
jmp cs:[SR_Ring0]
@@cont:
pop es
mov eax,[TimerVal]
add eax,[TimerAdd]
cmp eax,[TimerLimit]
jnc short @@overlimit ;möglichst selten springen!!
mov [TimerVal],eax
;(Alles muß man selber machen!!)
pop ds
mov al,60h
out 20h,al ;Spezifischer EOI IRQ0
popad
iretd ;nichts weiter tun
@@overlimit:
sub eax,[TimerLimit]
mov [TimerVal],eax
pop ds
mov al,60h ;TEST
out 20h,al ;Spezifischer EOI IRQ0
popad
jmp cs:[OldInt08] ;zum OldIntHandler springen (->VPICD->VTD)
EndProc NewInt08
;Millisekunden-->Timerzählerwert (EAX-->EAX) ohne Rundung
BeginProc ms2ticks
push ecx
push edx
mov ecx,TIMERFREQ
mul ecx
mov ecx,1000
div ecx
pop edx
pop ecx
ret
EndProc ms2ticks
;Hertz-->Timerzählerwert (EAX-->EAX) mit Rundung
BeginProc hz2ticks
cmp eax,100 ;Samplerate <100Hz?? ->Fehler
jc short @@e
push edx
push ecx
mov ecx,eax
mov eax,TIMERFREQ ;Frequenz = Divident für Zeitkonstante
xor edx,edx
div ecx
pop ecx
shl edx,1 ;Rest verdoppeln
cmp eax,edx ;Rest>Quotiont/2?
adc eax,0 ;Wenn ja, dann eins drauf (Runden)
pop edx
@@e: ret
EndProc hz2ticks
;Timer-Auflösung setzen wird abgefangen,
; um Timer ggf. nach UNSEREN Wünschen nachzuprogrammieren
BeginProc NewSetService
Trace_Out "SetService"
call [OldSetService]
jmp CommonTimer
EndProc NewSetService
;Timer-Auflösung rücksetzen wird abgefangen,
; um Timer ggf. nach UNSEREN Wünschen nachzuprogrammieren
BeginProc NewReleaseService
Trace_Out "ReleaseService"
call [OldReleaseService]
CommonTimer:
jc short @@e2 ;bei Fehler
push eax ;unverändert lassen
VxDcall VTD_Get_Interrupt_Period
call ms2ticks ;EAX-->EAX
pushfd
cli ;Kein Timer-Interrupt hier!
mov [TimerLimit],eax;Aktualisieren (Nachführen)
xor eax,eax
mov [TimerVal],eax ;Neu starten (wie die Hardware es tut?)
Debug_Halt
mov eax,[TimerAdd]
call ProgT0 ;Timer wieder SCHNELL machen!!
popfd
pop eax
clc
@@e2: ret
EndProc NewReleaseService
VxD_LOCKED_CODE_ENDS
;**********************************
;*** Residenter Code (pageable) ***
;**********************************
VxD_CODE_SEG
BeginProc SetupDescriptors
;PE: ebx=hVM
; ebp=Client_Reg ...
; ES:BX = Code-Zeiger
; DS = Datensegment
;PA: (allozierte Deskriptoren werden mit <start> und <limit> versehen)
; CY=1: Fehler
;Testen, welcher Betriebsmodus (PM16, PM32, V86) vorliegt und verzweigen
TESTFLAG [ebx.CB_VM_Status],VMStat_PM_Use32
jnz @@error ;Nicht aus USE32 aufrufbar!
TESTFLAG [ebx.CB_VM_Status],VMStat_PM_Exec
jnz @@protmode
;Aliasdeskriptoren für V86-Mode aufbauen und setzen
mov eax,dword ptr [ebp.Client_ES]
shl eax,4 ;Lineare Anfangsadresse CS
add eax,[ebx.CB_High_Linear]
VMMCall _BuildDescriptorDWords,<eax, 0FFFFh,\
Code_Type+D_DPL0, 0, BDDExplicitDPL>
VMMCall _SetDescriptor,<[SR_Ring0_CS],ebx,edx,eax,0>
or eax,eax
jz @@error
mov eax,dword ptr [ebp.Client_DS]
shl eax,4 ;Lineare Anfangsadresse DS
add eax,[ebx.CB_High_Linear]
VMMCall _BuildDescriptorDWords,<eax, 0FFFFh,\
RW_Data_Type+D_DPL0, 0, BDDExplicitDPL>
jmp @@comm ;gemeinsam weiter
;Aliasdeskriptoren für Protected Mode kopieren und setzen
@@protmode:
VMMCall _GetDescriptor,<<dword ptr [ebp.Client_ES]>, ebx, 0>
push eax
or eax,edx
pop eax
jz @@error
MASKFLAG edx,not 6000h ;DPL=0 setzen
VMMCall _SetDescriptor,<[SR_Ring0_CS],ebx,edx,eax,0>
or eax,eax
jz @@error
VMMCall _GetDescriptor,<<dword ptr [ebp.Client_DS]>, ebx, 0>
push eax
or eax,edx
pop eax
jz @@error
MASKFLAG edx,not 6000h ;DPL=0 setzen
@@comm:
VMMCall _SetDescriptor,<[SR_Ring0_DS],ebx,edx,eax,0>
or eax,eax
jz @@error
;Deskriptoren sind nun gesetzt, die Routine darf angesprungen werden
movzx eax,[ebp.Client_BX] ;Offset nicht vergessen!
mov [SR_Ring0_IP],eax
clc
ret
@@error:
stc
ret
EndProc SetupDescriptors
;Timer Kanal 0 auf Zeitkonstante AX programmieren
;VR: F, EAX (oberste 8bits werden nullgesetzt)
BeginProc ProgT0
shl eax,8
mov al,34h
out 43h,al ;Erst-Low-Dann-High-Kommando
shr eax,8
out 40h,al
xchg al,ah
out 40h,al
xchg ah,al
ret
EndProc ProgT0
;Patcht die IDT, um den Timerinterrupt einzuklinken
;PE: EAX=neue Adresse
;PA: EAX=alte Adresse
;VR: EAX
;N: Interrupts müssen disabled sein!!
BeginProc SwapInt50
Assert_Ints_Disabled
;IDT-Eintrags-Adresse bestimmen
push esi
sub esp,6
sidt [esp] ;IDT-Register speichern
pop si ;2 Bytes LIMIT (verwerfen)
pop esi ;4 Bytes BASIS
;IDT direkt anzapfen, um möglichst kurze Antwortzeiten zu erreichen!
rol eax,16
xchg ax,[esi+8*TIMERINT+6] ;Vektor tauschen
rol eax,16
xchg ax,[esi+8*TIMERINT]
pop esi
ret
EndProc SwapInt50
;Patcht die IDT, um den Timerinterrupt einzuklinken
;VR: EAX
;N: Interrupts müssen disabled sein, enthält Eigensicherung
; gegen "Mehrfach-Anzapfung", indem OldInt08 NULL enthält
BeginProc HookInt50
cmp [OldInt08],0
jnz @@e ;schon angezapft, nicht noch einmal!
lea eax,NewInt08
call SwapInt50
mov [OldInt08],eax
@@e: ret
EndProc HookInt50
BeginProc UnhookInt50
xor eax,eax
xchg eax,[OldInt08]
or eax,eax ;schon ausgeklinkt!
jz @@e
call SwapInt50
@@e: ret
EndProc UnhookInt50
BeginProc HookTimerServices
mov eax,VTD_Begin_Min_Int_Period
lea esi,NewSetService
VMMcall Hook_Device_Service
mov [OldSetService],esi
mov eax,VTD_End_Min_Int_Period
lea esi,NewReleaseService
VMMcall Hook_Device_Service
mov [OldReleaseService],esi
VxDcall VTD_Get_Interrupt_Period
call ms2ticks
mov [TimerLimit],eax
ret
EndProc HookTimerServices
BeginProc UnhookTimerServices
mov eax,VTD_Begin_Min_Int_Period
mov esi,[OldSetService]
VMMcall Hook_Device_Service
mov eax,VTD_End_Min_Int_Period
mov esi,[OldReleaseService]
VMMcall Hook_Device_Service
ret
EndProc UnhookTimerServices
BeginProc Kill_High_Freq
LD [OwningVM],0
mov eax,[TimerLimit]
cli
call ProgT0
call UnhookInt50
call UnhookTimerServices
sti
ret
EndProc Kill_High_Freq
; Nach außen sichtbare Schnittstelle:
; Codestück im Ring0 rufen (auch vom V86-Mode aufrufbar!)
; das in den Registern ES:BX angegebene Codestück wird in jedem
; Fall im Ring0-Protected-Mode ausgeführt (keine ungültigen Selektoren bitte!)
;
; PE: AH=0: Versionsnummer erhalten (z.Z. AX=100, entspricht 1.0)
;
; AH=1: Unterprogramm aufrufen
; ES:BX=Programmadresse, muß global und far definiert sein, keine
; Parameter, keine Bereichs- und Stackprüfungen, keine Far-Calls!
; Rückgabe über DX:AX möglich
; DS: Datensegment
; EDX: Übergabeparameter
; Ansprung der Routine erfolgt mit
; CS: Alias zu angegebenem ES
; DS: Alias zu angegebenem DS
; ES: 4-Gigabyte-VxD-Daten-Deskriptor
; EDX: Übergabeparameter
; Ein Umrechnen mit CB_High_Linear ist hierbei nicht nötig, da das
; Unterprogramm immer im Kontext mit der laufenden VM gerufen wird.
;
; AH=2: Periodischer Unterprogrammaufruf, Timer-gesteuert
; ES:BX=Programmadresse, wie bei AH=1, keine Rückgabe
; DS: Datensegment
; EDX=Aufruf-Frequenz, 100..44100 Hz (oder auch höher)
; mit ES:BX=0 oder DX:CX=0 Abschalten des Aufrufs
; Ansprung der Routine wie bei AH=1, aber ohne EDX
; Nur ein Programm kann dieses API zu einer Zeit benutzen!
;
; AH=3: Periodischer Unterprogrammaufruf, CMOS-Timer-gesteuert
; Parameter wie AH=2, jedoch gültige Frequenzen:
; EDX=2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768
;
BeginProc VCall0_API
mov al,[ebp.Client_AH] ;Funktions-Nummer
or al,al
jz @@GetVer
dec al
jz @@CallSR
dec al
jz @@High_Freq
@@error:
SETFLAG [ebp.Client_Flags],CF_Mask ;Nicht unterstützte Funktion
ret
@@GetVer:
mov [ebp.Client_AX],VCall0_Ver
jmp @@okay
@@CallSR:
call SetupDescriptors
jc @@error
mov edx,[ebp.Client_EDX] ;als Notnagel: EDX als Parameter
call VCall0_CallSR
;Hier angekommen, werden die Rückgabeparameter weitergereicht, und alles OK.
mov [ebp.Client_EAX],eax
mov [ebp.Client_EDX],edx
@@okay: MASKFLAG [ebp.Client_Flags],not CF_Mask
ret
@@High_Freq:
mov eax,[ebp.Client_EDX]
or eax,eax
jz @@End_High_Freq
cmp eax,100
jc @@error
cmp eax,1000000 ;das ist schon eine Zumutung!
ja @@error
cmp [OldInt08],0
jnz @@T0_only
mov [OwningVM],ebx
call SetupDescriptors
jc @@error
cli
call HookTimerServices
call HookInt50
@@T0_only:
mov eax,[ebp.Client_EDX]
call hz2ticks
mov [TimerAdd],eax
cli
call ProgT0
sti
jmp @@okay
@@End_High_Freq:
cmp [OldInt08],0
jz @@error ;Schon ausgeschaltet!
call Kill_High_Freq
jmp @@okay
EndProc VCall0_API
BeginProc VCall0_Dynamic_Exit
movzx eax,word ptr [SR_RETF_FROM_16+2]
VMMCall _Free_GDT_Selector, <eax,0>
VMMCall _Free_GDT_Selector, <[SR_Ring0_DS],0>
VMMCall _Free_GDT_Selector, <[SR_Ring0_CS],0>
clc
ret
EndProc VCall0_Dynamic_Exit
BeginProc VMDies
cmp [OwningVM],ebx
je Kill_High_Freq
ret
EndProc VMDies
VxD_CODE_ENDS
;****************************
;*** Initialisierungscode ***
;****************************
VxD_ICODE_SEG
;------------------------------------------------------------------------------
; VCall0_Sys_Critical_Init
; 2 GDT-Selektoren belegen, jedoch noch nicht ausfüllen
; 1 GDT-Selektor für Rücksprung belegen und ausfüllen
;------------------------------------------------------------------------------
BeginProc VCall0_Init
Trace_Out "VCall0: Device_Init"
VCall0_Dynamic_Init:
pushad
;2 leere Deskriptortabellen-Einträge besorgen
VMMCall _Allocate_GDT_Selector, <09A00h, 0, 0>
;1 PUSH 0 kostet selbst bei 32-bit-Flat nur 2 Bytes, XOR EAX,EAX lohnt nicht
or eax, eax
jz @@error
mov [SR_Ring0_CS],eax
VMMCall _Allocate_GDT_Selector, <09200h, 0, 0>
or eax, eax
jz @@error1
mov [SR_Ring0_DS],eax
;1 gefüllten Deskriptortabelleneintrag besorgen für das obige kurze
; Codeschnipsel
VMMCall _BuildDescriptorDWORDS, <<OFFSET32 VCall0_Switch_To_Flat>,\
VCall0_STF_Limit, Code_Type+D_DPL0,\
D_DEF32+D_GRAN_BYTE, BDDExplicitDPL>
VMMCall _Allocate_GDT_Selector,<edx, eax, 0>
or eax, eax
jz @@error2
mov word ptr [SR_RETF_FROM_16+2],ax
popad
clc
ret
@@error2: ;2 Deskriptoren aufräumen
VMMCall _Free_GDT_Selector, <[SR_Ring0_DS],0>
@@error1: ;1 Deskriptor aufräumen
VMMCall _Free_GDT_Selector, <[SR_Ring0_CS],0>
@@error: ;kein Deskriptor aufräumen, nur CY melden: VCALL0 nicht laden
popad
stc
ret
EndProc VCall0_Init
VxD_ICODE_ENDS
END
;**********************************
;*** Residente Daten (pageable) ***
;**********************************
VxD_DATA_SEG
IOCTL_Table dd OFFSET32 Retu ;(-1)
dd OFFSET32 Retu ;(0)
dd OFFSET32 IOCTL_GetVer ;(1)
dd OFFSET32 IOCTL_Call0 ;(2)
dd OFFSET32 IOCTL_High_Freq;(3)
dd OFFSET32 IOCTL_CMOS_Freq;(4)
VxD_DATA_ENDS
;IOCTL (Win32)
BeginProc VCall0_IOCTL
inc ecx
cmp ecx,7
jnc @@err
call [IOCTL_Table+ecx*4]
jnc @@ok
@@err: mov eax,32h ;Error_Not_Supported
jmp @@e
@@ok:
mov edi,[esi.lpcbBytesReturned]
or edi,edi
jz @@1
mov [edi],eax
@@1: xor eax,eax ;Null = Fehlercode "OK" (??)
@@e: ret
EndProc VCall0_IOCTL
BeginProc IOCTL_GetVer
cmp [esi.cbOutBuffer],2
jc @@e
mov edi,[esi.lpvOutBuffer]
mov word ptr [edi],VCall0_Ver
push 2
pop eax ;2 Return-Bytes
@@e: ret
EndProc IOCTL_GetVer
BeginProc IOCTL_Call0
cmp [esi.cbInBuffer],4
jc @@e
mov edi,[esi.lpvInBuffer]
mov edi,[edi] ;Adresse 0:32
;wie weiter? Das 32-bit-Programm muß ggf. erst "zugeschaltet" werden!
@@e: ret
EndProc IOCTL_Call0
Detected encoding: OEM (CP437) | 1
|
|