俘获违例的INTxx指令
作者:陆麟
转载请征得作者同意.
2000.5.28


E文.其实是SEH的小应用.
稍微改动就可以写成全局的SEH处理器从而实现挂接INTxx指令.
写NT版的乃当世NT顶尖高手之一Slava M. Usov.
9X版的乃是全局勾挂.正是前一段时间所干的.仅实现INT82H.稍微改动就可以拦截其他INTxx指令.更为完善.:)

1.FOR NT

From: "Slava M. Usov" <[email protected]>
Subject: Re: Trapping Software Interrupts in NT... Possible?
Date: Thursday, March 30, 2000 10:42 PM

Sal <[email protected]> wrote in message news:#[email protected]
> Hi,
>
>     I'm developing a WinNT5 / Win2k application that needs to trap
software
> interrupts.  I am relatively new to kernel/driver development, so any info
> would be _greatly_ appreciated.
>
>     For example, the software executes something along the lines of:
>     __asm {
>             int 0xNN      ; NN == number of interrupt not available
>                                 ; currently in windows NT ring 3
> applications
>         }
>
>     And (logically) windows generates an error because the interrupt isn't
> available.  Is there *any* way to enable the interrupt, so that a custom
> routine gets executed when software makes the request?
>
>     I realize that the 16bit MS-DOS VM environment does somethign similair
> to this to enable MS-DOS applications to call int 20h.  Could anyone point
> me to info on how this is done?

Well, with some creative use of SEH, this can be done in user mode.
Specifically, you may have the function you want to be executed each time an
int instruction is executed. The sample code follows.

There are some comments in the code that may give you a hint how it works.
Note that the technique exemplified is very slow, because it requires two
kernel mode transitions for each INT, resulting in ca 3270 CPU clocks for
each INT. You should not really be using it!
 
 
 
 

#include <windows.h>
#include <iostream>
using namespace std;

typedef unsigned char uchar;
typedef unsigned long ulong;

// both PushFrame() and PopFrame()
// invalidate edx

#define PushFrame(_handler) __asm \
    {                             \
        __asm lea  edx, _handler  \
        __asm push edx            \
        __asm push fs:[0]         \
        __asm mov fs:[0], esp     \
    }

// IMPORTANT: PopFrame assumes that the stack
// is in the state as if PushFrame just executed.
//
// it is deadly incorrect to return from a
// function without executing PopFrame()
//
// well, in some cases it may work;
// but if you have to ask, don't do it!

#define PopFrame() __asm         \
    {                            \
        __asm mov edx,[esp]      \
        __asm mov fs:[0], edx    \
        __asm add esp, 8         \
    }

EXCEPTION_DISPOSITION __cdecl _nested_eh(
    struct _EXCEPTION_RECORD *er,
    void * ef,
    struct _CONTEXT *ctx,
    void * DispatcherContext
)
{
 static unsigned long zero = 0;
 ctx->Ecx = (unsigned long)&zero;
 return ExceptionContinueExecution;
}

__declspec(naked) ulong __fastcall _get_two(void *addr)
{

 // due to the __fastcall convention,
 // the first parameter is in ecx

 PushFrame(_nested_eh);
 __asm mov ax, word ptr [ecx]
 PopFrame();
 __asm ret
}

// __stcall convention is IMPORTANT
typedef void (__stdcall *int_r)(uchar int_num);

struct int_context
{
 ulong eax; // eax is saved here
 int_r ir;  // int routine
 ulong ret; // return address
 ulong num; // int number
};

__declspec(naked) void _int_dispatch()
{
 __asm push [eax]int_context.num
 __asm push [eax]int_context.ret
 __asm push [eax]int_context.ir
 __asm mov  eax, [eax]int_context.eax
 __asm ret
}

EXCEPTION_DISPOSITION __cdecl _int_except_handler(
    struct _EXCEPTION_RECORD *er,
    void * ef,
    struct _CONTEXT *ctx,
    void * DispatcherContext
)
{
    // see if it is a real exception or just unwinding
    if( er->ExceptionFlags & (2 | 4) )
    {
        // unwinding in progress...
        // we don't have to do anything
        return ExceptionContinueSearch;
    }

 // will parse only two byte INT instructions
 // thus INT 3 and INTO will not be processed
 // INT 3 is a breakpoint and may be handled
 // in a more straight forward manner

 // note that INT 2C won't be handled, either
 // the exception handler won't even see it
 // because it's used as a syscall

 ulong _int = _get_two(er->ExceptionAddress);
 if( (_int & 0xff) != 0xcd )
  return ExceptionContinueSearch;

 // get to int_context structure pushed on stack
 // just before the SEH frame
 int_context *ic = (int_context*)((ulong*)ef + 2);

 ic->eax = ctx->Eax;
 ic->ret = (ulong)er->ExceptionAddress + 2;
 ic->num = (_int >> 8) & 0xff;

 // load the address of the int_context into eax
 // note eax has already been saved in int_context
 ctx->Eax = (ulong)ic;

 ctx->Eip = (ulong)_int_dispatch;

 return ExceptionContinueExecution;
}

// InvokeIntInterceptor()/RemoveIntInterceptor()
// invalidate edx, too

#define InvokeIntInterceptor(r)  \
 {                            \
  __asm sub esp, 16        \
  __asm lea edx, r         \
  __asm mov [esp].ir, edx  \
  PushFrame(_int_except_handler); \
 }

#define RemoveIntInterceptor()   \
 {                            \
  PopFrame();              \
  __asm add esp, 16        \
 }

// END of implementation
// user code goes below
 
 

// must conform to int_r definition
// __stcall convention is IMPORTANT
void __stdcall int_routine(uchar int_num)
{
 // generally, we should be careful here to save
 // and restore the registers for we have the *exactly*
 // same state as it was when INT XX was executed,
 // and after the function returns, the next instruction
 // after INT XX will see the *exactly* same registers
 // this function leaves behind

 // in this specific sample it is irrelevant
 // so we don't care

 cout << "INT " << hex << (ulong)int_num << endl;
}

int main()
{
 InvokeIntInterceptor(int_routine);

 // the following int instruction
 // will get executed unnoticed
 // in general you should not execute
 // int 0x21 because chances are you may
 // have a valid register cominataion
 // for a syscall to execute successfully

 __asm int 0x2c

 // the rest should work as expected

 __asm int 0x21
 __asm int 0x22
 __asm int 0x23
 __asm int 0x24
 __asm int 0x25
 __asm int 0x26
 __asm int 0x26

 RemoveIntInterceptor();

 return 0;
}
 
 
 

--

Slava

Please send any replies to this newsgroup.
microsoft.public.win32.programmer.kernel

2. FOR 9X
.386p
;;
;;Int82 supporter
;;Written by Lu Lin,2000.4.13
;;Abstract:
;; Dynamic loadable vxd used to install
;; interrupt 82 handler
;;
;;    We need a dll for handle the interrupt 82. Its name
;;exist under regestery key "HKLM\SOFTWARE\",value "Int82"
;;contents a ASCIIZ string shows the interrupt handler dll
;;name, e.g. "c:\windows\system\abc.dll"
;;Please be noted that we will use a default handler
;;"c:\windows\systemInthdl.dll" if the value "Int82" doesn't
;;present.

;;Future release will support deviceio to install the handler.

BLD_COFF EQU 1
IS_32 EQU 1
MASM6 EQU 1
DEV_IO EQU 60
include vmm.inc
include vxdldr.inc
Declare_Virtual_Device INT82,6,0,ctrlproc,Undefined_Device_ID,1000000h,,,

Vxd_DATA_SEG
PreviousGPHandler dd 0
Int82Handler dd 0
hModule dd 0
pModuleName dd 0
cbname dd 260
hkey dd 0
key db "Software",0
value db "Int82",0
ModuleName db "c:\windows\system\Inthdl.dll",0
FunctionName db "InterruptHandle",0
Vxd_DATA_ENDS

VxD_CODE_SEG

;;
;;interrupt 82h handler
;;
BeginProc v82
 pushad
 push ds
 mov bx,[ebp.Client_CS]
 mov ds,bx
 mov ebx,[ebp.Client_EIP]
 cmp word ptr ds:[ebx],82cdh
 jz CatchInt82
 pop ds
 popad
 jmp dword ptr PreviousGPHandler
CatchInt82:
 add [ebp.Client_EIP],2
 pop ds
 popad
 cmp Int82Handler,0
 jz back_home
        mov eax,[ebp.client_EAX]
        mov ebx,[ebp.client_EBX]
        mov ecx,[ebp.client_ECX]
        mov edx,[ebp.client_EDX]
        mov esi,[ebp.client_ESI]
        mov edi,[ebp.client_EDI]
        call dword ptr Int82Handler
        mov [ebp.client_EAX],eax
        mov [ebp.client_EBX],ebx
        mov [ebp.client_ECX],ecx
        mov [ebp.client_EDX],edx
        mov [ebp.client_ESI],esi
        mov [ebp.client_EDI],edi
back_home:
 ret
EndProc v82

;==============================================================================
; Device IO interface:
; ecx = Service No
; ebx = DDB
; edx = hDevice ,it is opened by a ring3 application
; esi = lpDIOCParms,passed from DeviceIoControl
;==============================================================================
BeginProc DEVICE_IO
 xor eax,eax
 cmp ecx,DIOC_OPEN  ;;Must return 0 to tell WIN32 that this VxD supports DEVIOCTL
 jz Normal_Exit
 cmp ecx,DIOC_CLOSEHANDLE ;;sent when VxD is unloaded just before SYS_DYNAMIC_EXIT
 jz Normal_Exit
        cmp ecx,DEV_IO
 jz Run_io
 mov eax,32h ;;error:not supported
 ret
Normal_Exit:
 ret
Run_io:
 ;;
 ;;Add codes here
 ;;
 ret
EndProc DEVICE_IO
VxD_CODE_ENDS

VxD_LOCKED_CODE_SEG
;;
;;Called when dynamic init
;;
BeginProc Dynamic_Init
;;step1:
 mov eax,13
 mov esi,offset32 v82
 VMMCall Hook_PM_Fault
 mov PreviousGPHandler,esi
 jnc step2
 stc
 ret
step2:
        VMMCall _HeapAllocate,<260,HEAPZEROINIT>
 mov pModuleName,eax
 VMMCall _RegOpenKey,<HKEY_LOCAL_MACHINE,<OFFSET32 key>,<offset32 hkey>>
 cmp eax,0
 jnz step3
 VMMCall _RegQueryValueEx,<hkey,<OFFSET32 value>,0,0,pModuleName,<OFFSET32 cbName>>
 cmp eax, 0
 jz step3
 VMMCall _HeapFree,<pModuleName,0>
 mov pModuleName,offset32 ModuleName
step3:
 VxDCall _PELDR_LoadModule,<<offset32 hModule>,pModuleName,0>
 cmp eax,0
 jz step4
 jmp step_n
step4:
 VxDCall _PELDR_GetProcAddress,<hModule,<offset32 FunctionName>,0>
 mov Int82Handler,eax
step_n:
 VMMCall _RegCloseKey,<hkey>
 clc
        ret
EndProc Dynamic_Init

;;
;;Called when dynamic exit
;;
BeginProc Dynamic_EXIT
 stc ;;We don't allow this VXD unload
 ret
EndProc  Dynamic_EXIT
 

;;
;;Dispatch routine
;;
BeginProc ctrlproc
 Control_Dispatch KERNEL32_INITIALIZED, Dynamic_Init
 Control_Dispatch SYS_DYNAMIC_DEVICE_INIT, Dynamic_Init
 Control_Dispatch SYS_DYNAMIC_DEVICE_EXIT, Dynamic_EXIT
 Control_Dispatch W32_DEVICEIOCONTROL, DEVICE_IO
 clc
 ret
EndProc ctrlproc
VxD_LOCKED_CODE_ENDS
END