; ; 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