493 lines
10 KiB
NASM
493 lines
10 KiB
NASM
|
;
|
||
|
; UT.ASM
|
||
|
; Tracing goop, debug only
|
||
|
; Mouse/Keyboard event interrupt junk, all flavors
|
||
|
;
|
||
|
|
||
|
|
||
|
.386
|
||
|
option oldstructs
|
||
|
option readonly
|
||
|
option segment:use16
|
||
|
.model large,pascal
|
||
|
|
||
|
ifdef DEBUG
|
||
|
externDef _wsprintf:far16
|
||
|
externDef OutputDebugString:far16
|
||
|
externDef DebugBreak:far16
|
||
|
endif ; DEBUG
|
||
|
|
||
|
externDef DrvMouseEvent:far16
|
||
|
externDef DrvKeyboardEvent:far16
|
||
|
externDef mouse_event:far16
|
||
|
externDef keybd_event:far16
|
||
|
externDef MaphinstLS:far16
|
||
|
|
||
|
.data
|
||
|
|
||
|
ifdef DEBUG
|
||
|
externDef g_szDbgBuf:word
|
||
|
externDef g_szNewline:word
|
||
|
externDef g_dbgRet:word
|
||
|
externDef g_trcConfig:word
|
||
|
endif ; DEBUG
|
||
|
|
||
|
if 0
|
||
|
; DOS key redirection
|
||
|
externDef g_imDOSShellVDDEntry:dword
|
||
|
externDef g_imDOSVKDEntry:dword
|
||
|
endif
|
||
|
|
||
|
.code _TEXT
|
||
|
|
||
|
|
||
|
ifdef DEBUG
|
||
|
|
||
|
;
|
||
|
; We come in here with _cdecl var args. We use DebugOutput() to spit out
|
||
|
; the message. Then we do the debugbreak ourself.
|
||
|
;
|
||
|
_DbgZPrintWarning proc near
|
||
|
; Save IP of caller. What's left on stack is var args
|
||
|
pop [g_dbgRet] ; Save IP of output caller
|
||
|
|
||
|
; Push g_szDbgBuf to put result into.
|
||
|
push ds
|
||
|
push offset g_szDbgBuf
|
||
|
|
||
|
; We now have _cdecl args to wsprintf
|
||
|
call _wsprintf
|
||
|
|
||
|
;
|
||
|
; The same args are left on the stack, since wsprintf is _cdecl. The
|
||
|
; first is g_szDbgBuf. So we can convienently pass this to OutputDebugString().
|
||
|
; That routine is NOT _cdecl, so when it returns, we have exactly the
|
||
|
; same args that were passed in to us.
|
||
|
;
|
||
|
call OutputDebugString
|
||
|
|
||
|
; Now output a new line
|
||
|
push ds
|
||
|
push offset g_szNewline
|
||
|
call OutputDebugString
|
||
|
|
||
|
; Now we just need to do a near RET to the caller
|
||
|
push [g_dbgRet]
|
||
|
ret
|
||
|
_DbgZPrintWarning endp
|
||
|
|
||
|
|
||
|
;
|
||
|
; We come in here with _cdecl var args
|
||
|
;
|
||
|
_DbgZPrintTrace proc near
|
||
|
; Is tracing on?
|
||
|
test [g_trcConfig], 0001h
|
||
|
jnz _DbgZPrintWarning
|
||
|
ret
|
||
|
_DbgZPrintTrace endp
|
||
|
|
||
|
|
||
|
|
||
|
_DbgZPrintError proc near
|
||
|
; Save IP of caller. What's left on stack is var args
|
||
|
pop [g_dbgRet] ; Save IP of output caller
|
||
|
|
||
|
; Push g_szDbgBuf to put result into.
|
||
|
push ds
|
||
|
push offset g_szDbgBuf
|
||
|
|
||
|
; We now have _cdecl args to wsprintf
|
||
|
call _wsprintf
|
||
|
|
||
|
;
|
||
|
; The same args are left on the stack, since wsprintf is _cdecl. The
|
||
|
; first is g_szDbgBuf. So we can convienently pass this to OutputDebugString().
|
||
|
; That routine is NOT _cdecl, so when it returns, we have exactly the
|
||
|
; same args that were passed in to us.
|
||
|
;
|
||
|
call OutputDebugString
|
||
|
|
||
|
; Now output a new line
|
||
|
push ds
|
||
|
push offset g_szNewline
|
||
|
call OutputDebugString
|
||
|
|
||
|
|
||
|
; Break into the debugger
|
||
|
call DebugBreak
|
||
|
|
||
|
; Now we just need to do a near RET to the caller
|
||
|
push [g_dbgRet]
|
||
|
ret
|
||
|
_DbgZprintError endp
|
||
|
|
||
|
endif
|
||
|
|
||
|
|
||
|
|
||
|
;
|
||
|
; ASMMouseEvent()
|
||
|
; This passes the registers as parameters to a C function, DrvMouseEvent.
|
||
|
; It is basically the inverse of CallMouseEvent().
|
||
|
;
|
||
|
; NOTE:
|
||
|
; To be on the safe side, we preserve all registers just like keybd_event().
|
||
|
; USER trashes some registers, but it would not come as a surprise to find
|
||
|
; mouse drivers that expect incorrectly a register or two to not be
|
||
|
; altered.
|
||
|
;
|
||
|
ASMMouseEvent proc far
|
||
|
|
||
|
; Save registers that C code doesn't preserve
|
||
|
push eax
|
||
|
push ebx
|
||
|
push ecx
|
||
|
push edx
|
||
|
|
||
|
; Save original flags for turning ints off/on
|
||
|
pushf
|
||
|
|
||
|
; Push AX for DrvMouseEvent() call
|
||
|
push ax
|
||
|
|
||
|
; Do we need to turn interrupts off? We don't if they are already
|
||
|
pushf
|
||
|
pop ax
|
||
|
test ax, 0200h
|
||
|
jz SkipCli
|
||
|
cli
|
||
|
SkipCli:
|
||
|
|
||
|
; AX has already been pushed; push the rest of the parameters
|
||
|
push bx
|
||
|
push cx
|
||
|
push dx
|
||
|
push si
|
||
|
push di
|
||
|
cld
|
||
|
call DrvMouseEvent
|
||
|
|
||
|
; If interrupts were not disabled before, enable them now.
|
||
|
pop cx ; saved flags
|
||
|
pushf
|
||
|
pop ax ; current flags
|
||
|
|
||
|
; Find out what is different
|
||
|
xor ax, cx
|
||
|
test ax, 0200h
|
||
|
jz InterruptsOk
|
||
|
|
||
|
; The interrupt flag needs to be changed, do it
|
||
|
test cx, 0200h
|
||
|
jnz EnableInterrupts
|
||
|
cli
|
||
|
jmp InterruptsOk
|
||
|
|
||
|
EnableInterrupts:
|
||
|
sti
|
||
|
|
||
|
InterruptsOk:
|
||
|
; Does the direction flag need to be changed?
|
||
|
test ax, 0400h
|
||
|
jz DirectionOk
|
||
|
|
||
|
; The direction flag needs to be changed, do it
|
||
|
test cx, 0400h
|
||
|
jnz SetDirectionFlag
|
||
|
cld
|
||
|
jmp DirectionOk
|
||
|
|
||
|
SetDirectionFlag:
|
||
|
std
|
||
|
|
||
|
DirectionOk:
|
||
|
; Restore registers
|
||
|
pop edx
|
||
|
pop ecx
|
||
|
pop ebx
|
||
|
pop eax
|
||
|
|
||
|
retf
|
||
|
ASMMouseEvent endp
|
||
|
|
||
|
|
||
|
|
||
|
;
|
||
|
; CallMouseEvent()
|
||
|
; This puts the parameters into registers and calls the original mouse_event.
|
||
|
;
|
||
|
; There are two ways we can call this function:
|
||
|
; (1) Injection code is piping mouse events through USER. It is
|
||
|
; responsible for disabling/enabling interrupts before calling us.
|
||
|
; (2) mouse_event patch is calling through to USER.
|
||
|
;
|
||
|
CallMouseEvent proc near
|
||
|
;
|
||
|
; This is the stack, BP relative:
|
||
|
; WORD bpSave
|
||
|
; WORD near_ret
|
||
|
; WORD regDI
|
||
|
; WORD regSI
|
||
|
; WORD regDX
|
||
|
; WORD regCX
|
||
|
; WORD regBX
|
||
|
; WORD regAX
|
||
|
;
|
||
|
; We must preserve SI and DI
|
||
|
;
|
||
|
push bp
|
||
|
mov bp, sp
|
||
|
push si
|
||
|
push di
|
||
|
|
||
|
mov di, word ptr ss:[bp+4]
|
||
|
mov si, word ptr ss:[bp+6]
|
||
|
mov dx, word ptr ss:[bp+8]
|
||
|
mov cx, word ptr ss:[bp+10]
|
||
|
mov bx, word ptr ss:[bp+12]
|
||
|
mov ax, word ptr ss:[bp+14]
|
||
|
|
||
|
call mouse_event
|
||
|
|
||
|
pop di
|
||
|
pop si
|
||
|
mov sp, bp
|
||
|
pop bp
|
||
|
ret 6*2
|
||
|
CallMouseEvent endp
|
||
|
|
||
|
|
||
|
;
|
||
|
; ASMKeyboardEvent()
|
||
|
; This passes the registers as parameters to a C function, DrvKeyboardEvent.
|
||
|
; It is basically the inverse of CallKeyboardEvent().
|
||
|
;
|
||
|
; NOTE:
|
||
|
; keybd_event() MUST preserve all registers, unlike mouse_event().
|
||
|
;
|
||
|
ASMKeyboardEvent proc far
|
||
|
|
||
|
; Save flags and registers that aren't preserved in C code
|
||
|
push eax
|
||
|
push ebx
|
||
|
push ecx
|
||
|
push edx
|
||
|
pushf
|
||
|
|
||
|
; Push AX for DrvKeyboardEvent() call
|
||
|
push ax
|
||
|
|
||
|
; Check if interrupts off, w/o trashing CX permanently
|
||
|
pushf
|
||
|
pop ax
|
||
|
test ax, 0200h
|
||
|
jz SkipCli
|
||
|
cli
|
||
|
SkipCli:
|
||
|
|
||
|
; AX has already been pushed; push the rest of the parameters
|
||
|
push bx
|
||
|
push si
|
||
|
push di
|
||
|
cld
|
||
|
call DrvKeyboardEvent
|
||
|
|
||
|
;
|
||
|
; Restore the interrupt and string move direction flags to what they
|
||
|
; were before.
|
||
|
;
|
||
|
pop cx ; Original flags
|
||
|
pushf
|
||
|
pop ax ; Current flags
|
||
|
|
||
|
; What has changed?
|
||
|
xor ax, cx
|
||
|
|
||
|
; Has the interrupt state been altered?
|
||
|
test ax, 0200h
|
||
|
jz InterruptsOk
|
||
|
|
||
|
; Interrupts need to be turned on/off
|
||
|
test cx, 0200h
|
||
|
jnz EnableInterrupts
|
||
|
cli
|
||
|
jmp InterruptsOk
|
||
|
|
||
|
EnableInterrupts:
|
||
|
sti
|
||
|
|
||
|
InterruptsOk:
|
||
|
; Has the direction flag been altered?
|
||
|
test ax, 0400h
|
||
|
jz DirectionOk
|
||
|
|
||
|
; Direction flag needs to be set/cleared
|
||
|
test cx, 0400h
|
||
|
jnz SetDirection
|
||
|
cld
|
||
|
jmp DirectionOk
|
||
|
|
||
|
SetDirection:
|
||
|
std
|
||
|
|
||
|
DirectionOk:
|
||
|
; Restore registers
|
||
|
pop edx
|
||
|
pop ecx
|
||
|
pop ebx
|
||
|
pop eax
|
||
|
|
||
|
retf
|
||
|
ASMKeyboardEvent endp
|
||
|
|
||
|
|
||
|
|
||
|
;
|
||
|
; CallKeyboardEvent()
|
||
|
; This puts the parameters in registers and calls USER's keybd_event.
|
||
|
;
|
||
|
; There are two ways we can call this function:
|
||
|
; (1) Injection code is piping keybd events through USER. It is
|
||
|
; responsible for disabling/enabling interrupts before calling us.
|
||
|
; (2) keybd_event patch is calling through to USER.
|
||
|
;
|
||
|
CallKeyboardEvent proc near
|
||
|
;
|
||
|
; This is the stack, BP relative:
|
||
|
; WORD bpSave
|
||
|
; WORD near_ret
|
||
|
; WORD regDI
|
||
|
; WORD regSI
|
||
|
; WORD regBX
|
||
|
; WORD regAX
|
||
|
;
|
||
|
; We must preserve SI and DI
|
||
|
;
|
||
|
push bp
|
||
|
mov bp, sp
|
||
|
push si
|
||
|
push di
|
||
|
|
||
|
mov di, word ptr ss:[bp+4]
|
||
|
mov si, word ptr ss:[bp+6]
|
||
|
mov bx, word ptr ss:[bp+8]
|
||
|
mov ax, word ptr ss:[bp+10]
|
||
|
|
||
|
call keybd_event
|
||
|
|
||
|
pop di
|
||
|
pop si
|
||
|
mov sp, bp
|
||
|
pop bp
|
||
|
ret 4*2
|
||
|
CallKeyboardEvent endp
|
||
|
|
||
|
|
||
|
;
|
||
|
; This is our wrapper around krnl386's MaphinstLS() routine, which expects
|
||
|
; the 32-bit instance handle in EAX
|
||
|
;
|
||
|
MapInstance32 proc far
|
||
|
; Pop far return, pop 32-bit instance into eax, and replace far return
|
||
|
pop edx
|
||
|
pop eax
|
||
|
push edx
|
||
|
|
||
|
; Call krnl386 -- when MaphinstLS returns, it will return to our caller
|
||
|
jmp MaphinstLS
|
||
|
MapInstance32 endp
|
||
|
|
||
|
|
||
|
|
||
|
if 0
|
||
|
;
|
||
|
; DOS box key injection gunk. We use the shell vdd service.
|
||
|
;
|
||
|
|
||
|
IMGetDOSShellVDDEntry proc near
|
||
|
; Save DI, int2f will trash it
|
||
|
push di
|
||
|
|
||
|
; int2f 0x1684, vdd 0x0017 (shell) gets the service entry point
|
||
|
; It is returned in es:di
|
||
|
mov ax, 1684h
|
||
|
mov bx, 017h
|
||
|
int 2F
|
||
|
|
||
|
; Save the address (even if null)
|
||
|
mov word ptr ds:[g_imDOSShellVDDEntry], di
|
||
|
mov word ptr ds:[g_imDOSShellVDDEntry+2], es
|
||
|
|
||
|
pop di
|
||
|
ret
|
||
|
IMGetDOSShellVDDEntry endp
|
||
|
|
||
|
|
||
|
IMGetDOSVKDEntry proc near
|
||
|
; Save DI, int2f will trash it
|
||
|
push di
|
||
|
|
||
|
; int2f 0x1684, vkd 0x000d (vkd) gets the service entry point
|
||
|
; It is returned in es:di
|
||
|
mov ax, 1684h
|
||
|
mov bx, 00dh
|
||
|
int 2Fh
|
||
|
|
||
|
mov word ptr ds:[g_imDOSVKDEntry], di
|
||
|
mov word ptr ds:[g_imDOSVKDEntry+1], es
|
||
|
|
||
|
pop di
|
||
|
ret
|
||
|
IMGetDOSVKDEntry endp
|
||
|
|
||
|
|
||
|
|
||
|
IMForceDOSKey proc near
|
||
|
; ss:[sp] is near ret
|
||
|
; ss:[sp+2] is scanCode
|
||
|
; ss:[sp+4] is keyState
|
||
|
|
||
|
push bp
|
||
|
mov bp, sp
|
||
|
|
||
|
; Preserve extended registers
|
||
|
push ebx
|
||
|
push ecx
|
||
|
push edx
|
||
|
|
||
|
; Setup for VKD call
|
||
|
mov eax, 1 ; Service 1, stuff key
|
||
|
xor ebx, ebx ; VM 0, current VM
|
||
|
movzx ecx, word ptr ss:[bp+4]
|
||
|
shl ecx, 8 ; Scan code in high byte
|
||
|
or ecx, 1 ; Repeat count in low byte
|
||
|
movzx edx, word ptr ss:[bp+6] ; Shift state
|
||
|
|
||
|
call dword ptr ds:[g_imDOSVKDEntry]
|
||
|
mov ax, 0
|
||
|
|
||
|
; Failure?
|
||
|
jc DoneForceKey
|
||
|
|
||
|
; Success!
|
||
|
inc ax
|
||
|
|
||
|
DoneForceKey:
|
||
|
pop edx
|
||
|
pop ecx
|
||
|
pop ebx
|
||
|
|
||
|
mov sp, bp
|
||
|
pop bp
|
||
|
|
||
|
ret 2+2
|
||
|
IMForceDOSKey endp
|
||
|
|
||
|
endif ; if 0 for DOS key redirection
|
||
|
|
||
|
end
|
||
|
|
||
|
|
||
|
|