%include "util.mac"
%include "vxdn.inc"
%include "icedump.inc"
%include "wiat.inc"
%include "plug.inc"


global sdata
global ICEDUMP_Control
global StaticEntryV86CB
global StaticEntryPMCB
global StaticSimIretCB
global TempBuffer
global VMCB.oPMIDT
global VMCB.selLDT
global VMCB.oLDT


extern TracerSysDynamicDeviceInit
extern TracerSysDynamicDeviceExit
extern TracerThreadInit
extern TracerThreadNotExecuteable
extern TracerVmInit
extern TracerVmNotExecuteable

extern ProtectSysDynamicDeviceInit
extern ProtectSysDynamicDeviceExit
extern ProtectThreadInit
extern ProtectThreadNotExecuteable
extern ProtectVmInit
extern ProtectVmNotExecuteable

extern BreakR3SysDynamicDeviceInit
extern BreakR3SysDynamicDeviceExit
extern BreakR3ThreadInit
extern BreakR3ThreadNotExecuteable
extern BreakR3VmInit
extern BreakR3VmNotExecuteable

extern CallBackSysDynamicDeviceInit
extern CallBackSysDynamicDeviceExit

extern PbpmSysDynamicDeviceInit
extern PbpmSysDynamicDeviceExit
extern PbpmThreadInit
extern PbpmThreadNotExecuteable
extern PbpmVmInit
extern PbpmVmNotExecuteable

extern ApplyPatchOnSharedDLL

extern GetAPIs
extern GetK32Info
extern Parser
extern AllocCallBacks
extern MP3MainHook
extern entryV86CB
extern entryPMCB
extern SimIret
extern IDT.original
extern ParseAddress
extern FindKernel32Thread


bits 32


segment _LDATA
Declare_Virtual_Device ICEDUMP_NAME, ICEDUMP_VERSION_MAJOR, ICEDUMP_VERSION_MINOR1


segment _SDATA
;-------------------------------------------------------------------------------
; stuff here will be initialized once only, even thru multiple loads/unloads
;-------------------------------------------------------------------------------
	align 4
sdata:
istruc StaticData
  at loadcount,			dd 0
  at oWinice,			dd 0
  at oOld_CMD_INVALID,		dd Parser
  at s_fWiniceIsActive,		dd 0
  at s_pSendSpecificEOI,	dd 0
  at PMCB,			istruc CallBack
					at CallBack.offset,	dw 0
					at CallBack.selector,	dw 0
				iend
  at SimIretCB,			istruc CallBack
					at CallBack.offset,	dw 0
					at CallBack.selector,	dw 0
				iend
  at V86CB,			istruc CallBack
					at CallBack.offset,	dw 0
					at CallBack.selector,	dw 0
				iend
  at V86IDT,			istruc HookedInts
					at HookedInts.VMCB,	dd 0
					at HookedInts.cAPP,	dd 0
				iend
  at MP3.PlayerPresent,		db 0
  at MP3.nIRQ,			db 0xFF
  at OldSoundCardIntProc.off,	dd 0
  at OldSoundCardIntProc.sel,	dd 0

%ifndef MAKEDEP
%assign ICEDUMP_DEBUG_FLAGS (1 << ICEDUMP_DEBUG_PEDUMP)
%assign ICEDUMP_DEBUG_FLAGS ICEDUMP_DEBUG_FLAGS + (1 << ICEDUMP_DEBUG_TASKMOD)
%assign ICEDUMP_DEBUG_FLAGS ICEDUMP_DEBUG_FLAGS + (1 << ICEDUMP_DEBUG_COMMON)
%assign ICEDUMP_DEBUG_FLAGS ICEDUMP_DEBUG_FLAGS + (1 << ICEDUMP_DEBUG_TRACE)
%assign ICEDUMP_DEBUG_FLAGS ICEDUMP_DEBUG_FLAGS + (1 << ICEDUMP_DEBUG_PROTECT)
%assign ICEDUMP_DEBUG_FLAGS ICEDUMP_DEBUG_FLAGS + (1 << ICEDUMP_DEBUG_FDUMP)
%endif

;  at DebugFlags,		times (ICEDUMP_DEBUG_FLAG_COUNT+31)/32 dd 0
  at DebugFlags,		dd ICEDUMP_DEBUG_FLAGS
  				times (ICEDUMP_DEBUG_FLAG_COUNT+31)/32-1 dd 0
iend


segment _LTEXT
;-------------------------------------------------------------------------------
; SysControl message dispatcher
;
; ebx: VMCB
;-------------------------------------------------------------------------------
Begin_Control_Dispatch ICEDUMP
	Control_Dispatch SYS_DYNAMIC_DEVICE_INIT,ICEDUMP_OnSysDynamicDeviceInit
	Control_Dispatch SYS_DYNAMIC_DEVICE_EXIT,ICEDUMP_OnSysDynamicDeviceExit
	Control_Dispatch VM_INIT,ICEDUMP_OnVmInit
	Control_Dispatch BEGIN_PM_APP,ICEDUMP_OnBeginPMApp
	Control_Dispatch END_PM_APP,ICEDUMP_OnEndPMApp
	Control_Dispatch VM_NOT_EXECUTEABLE,ICEDUMP_OnVmNotExecuteable
	Control_Dispatch THREAD_INIT,ICEDUMP_OnThreadInit
	Control_Dispatch THREAD_NOT_EXECUTEABLE,ICEDUMP_OnThreadNotExecuteable
;	Control_Dispatch W32_DEVICEIOCONTROL,ICEDUMP_OnW32DeviceIoControl
End_Control_Dispatch


segment _LDATA
init_data_begin ICEDUMP
init_data_item	AllocateTempBuff,		FreeTempBuff
init_data_item	FindWiniceBase
init_data_item	RelocWIAT
init_data_item	DeactivateWiniceBPs,		ActivateWiniceBPs
init_data_item	AllocCallBacks
init_data_item	HookInts,			UnhookInts
init_data_item	ApplyWinicePatches,		RemoveWinicePatches
init_data_item	GetAPIInfo
init_data_item	TracerSysDynamicDeviceInit,	TracerSysDynamicDeviceExit
init_data_item	ProtectSysDynamicDeviceInit,	ProtectSysDynamicDeviceExit
init_data_item	BreakR3SysDynamicDeviceInit,	BreakR3SysDynamicDeviceExit
init_data_item	CallBackSysDynamicDeviceInit,	CallBackSysDynamicDeviceExit
init_data_item	PbpmSysDynamicDeviceInit,	PbpmSysDynamicDeviceExit
init_data_item	ChangeDLLExports
init_data_item	HydraInit,			HydraExit
init_data_item	ActivateWiniceBPs,		DeactivateWiniceBPs
init_data_end

TempBuffer:	dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; find winice base address by not using VMM functions
; relocate WIAT
; allocate callbacks used by certain new commands
; patch winice and hook IDTs of existing VMs
;
; ebx: VMCB
;-------------------------------------------------------------------------------
ICEDUMP_OnSysDynamicDeviceInit:
	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	mov	eax,[sdata+loadcount]
	Trace_Out "ICEDUMP: SYS_DYNAMIC_DEVICE_INIT #eax"
	debug_end

	cmp	dword [sdata+loadcount],byte 0		; don't load twice
	jz	@F

	stc
	retn

@@
	init_construct ICEDUMP
	jc	.error

	inc	dword [sdata+loadcount]
	clc
	retn

.error:
	call	ICEDUMP_OnSysDynamicDeviceExit

	stc
	retn



;-------------------------------------------------------------------------------
; remove all patches/hooks from winice and IDTs
;
; ebx: VMCB
;-------------------------------------------------------------------------------
ICEDUMP_OnSysDynamicDeviceExit:
	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	mov	eax,[sdata+loadcount]
	Trace_Out "ICEDUMP: SYS_DYNAMIC_DEVICE_EXIT #eax"
	debug_end

	init_destruct ICEDUMP

	dec	dword [sdata+loadcount]
	clc
	retn


;-------------------------------------------------------------------------------
; hook vectors in the IDT if it's the first time we see a V86 IDT
; (which is the same for all VMs, so we patch it once only)
;
; ebx: VMCB
;-------------------------------------------------------------------------------
ICEDUMP_OnVmInit:
	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	mov	eax,[ebx]
	Trace_Out "ICEDUMP: VM_INIT VMCB: #ebx, VMSTATUS: #eax"
	debug_end

	pushfd				; necessary since our list is LF_Async
	cli				; and we're tweaking the IDT after all

	call	HookIntsInVM

	popfd

	call	TracerVmInit
	call	ProtectVmInit
	call	BreakR3VmInit
	call	PbpmVmInit

	clc
	retn


;-------------------------------------------------------------------------------
; hook vectors in PM IDT if it is the first PM app in the given VM
; (can happen more than once during the lifetime of a VM)
;
; ebx: VMCB
;-------------------------------------------------------------------------------
ICEDUMP_OnBeginPMApp:
	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	mov	eax,[ebx]
	Trace_Out "ICEDUMP: BEGIN_PM_APP VMCB: #ebx, VMSTATUS: #eax"
	debug_end

	pushfd				; necessary since our list is LF_Async
	cli				; and we're tweaking the IDT after all

	mov	esi,[HooksList]
	VMMCall	List_Get_First
	jz	.hook

.next:
	cmp	[eax+HookedInts.VMCB],ebx
	jnz	@F

	inc	dword [eax+HookedInts.cAPP]

	jmp	short .ret

@@
	VMMCall	List_Get_Next
	jnz	.next

.hook:
	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	Trace_Out "ICEDUMP: first PM APP VMCB: #ebx"
	debug_end

	call	HookIntsInVM

.ret:
	popfd
	clc
	retn


;-------------------------------------------------------------------------------
; find/free list element if it is the last PM app in the given VM,
; do nothing to IDT (it's been freed already)
; (can happen more than once during the lifetime of a VM)
;
; ebx: VMCB
;-------------------------------------------------------------------------------
ICEDUMP_OnEndPMApp:
	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	mov	eax,[ebx]
	Trace_Out "ICEDUMP: END_PM_APP VMCB: #ebx, VMSTATUS: #eax"
	debug_end

	pushfd
	cli

	mov	esi,[HooksList]
	VMMCall	List_Get_First
	jz	.ret

.next:
	cmp	[eax+HookedInts.VMCB],ebx
	jnz	@F

	dec	dword [eax+HookedInts.cAPP]
	jz	.free

	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	mov	eax,[eax+HookedInts.cAPP]
	Trace_Out "ICEDUMP: not last PM APP VMCB: #ebx, apps: #eax"
	debug_end

	jmp	short .ret

@@
	VMMCall	List_Get_Next
	jnz	.next

	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	Trace_Out "ICEDUMP: END_PM_APP in unknown VMCB: #ebx"
	debug_end

	jmp	short .ret

.free:
	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	Trace_Out "ICEDUMP: last PM APP VMCB: #ebx"
	debug_end

	call	UnhookIntsInVM

.ret:
	popfd
	clc
	retn


;-------------------------------------------------------------------------------
; find/free list element, do nothing to IDT (it's been freed already)
;
; ebx: VMCB
;-------------------------------------------------------------------------------
ICEDUMP_OnVmNotExecuteable:
	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	mov	eax,[ebx]
	Trace_Out "ICEDUMP: VM_NOT_EXECUTEABLE VMCB: #ebx, VMSTATUS: #eax"
	debug_end

	call	PbpmVmNotExecuteable
	call	BreakR3VmNotExecuteable
	call	ProtectVmNotExecuteable
	call	TracerVmNotExecuteable

	pushfd
	cli

	mov	esi,[HooksList]
	VMMCall	List_Get_First
	jz	.ret

@@
	cmp	[eax+HookedInts.VMCB],ebx
	jz	.free

	VMMCall	List_Get_Next
	jnz	@B

	debug_start debugflags, ICEDUMP_DEBUG_SYSCTRL
	Trace_Out "ICEDUMP: VM_NOT_EXECUTEABLE in unknown VMCB: #ebx"
	debug_end

	jmp	short .ret

.free:
	push	eax
	VMMCall	List_Remove
	pop	eax
	VMMCall	List_Deallocate

.ret:
	popfd
	clc
	retn


;-------------------------------------------------------------------------------
;
;-------------------------------------------------------------------------------
ICEDUMP_OnThreadInit:
	VMMCall	Begin_Critical_Section
	call	TracerThreadInit
	call	ProtectThreadInit
	call	BreakR3ThreadInit
	call	PbpmThreadInit
	VMMCall	End_Critical_Section
	retn


;-------------------------------------------------------------------------------
;
;-------------------------------------------------------------------------------
ICEDUMP_OnThreadNotExecuteable:
	VMMCall	Begin_Critical_Section
	call	PbpmThreadNotExecuteable
	call	BreakR3ThreadNotExecuteable
	call	ProtectThreadNotExecuteable
	call	TracerThreadNotExecuteable
	VMMCall	End_Critical_Section
	clc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
AllocateTempBuff:
	push	ecx
	push	edx

	VMMCall _HeapAllocate, dword TempBuffer_size, byte HEAPZEROINIT

	pop	edx
	pop	ecx

	mov	[TempBuffer],eax
	sub	eax,byte 1
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
FreeTempBuff:
	cmp	dword [TempBuffer],byte 0
	jz	@F

	push	ecx
	push	edx

	VMMCall	_HeapFree, dword [TempBuffer], byte 0

	pop	edx
	pop	ecx

@@
	clc
	retn


;-------------------------------------------------------------------------------
; find base address of winice by not using VMM services (so that we don't
; need to know how winice has been protected against detection - but please,
; not yet another cat/mouse game...)
;
; stc on error
;-------------------------------------------------------------------------------
FindWiniceBase:
	cmp	dword [sdata+oWinice],byte 0
	jnz	.ok

	push	ecx
	push	esi
	push	edi

	pushfd
	cli
	cld
	push	eax
	push	eax
	sgdt	[esp+2]			; store GDT limit and start address
	pop	ecx			; get GDT limit
	shr	ecx,16+3		; compute number of descriptors
	pop	esi			; get GDT start address

@@
	lodsd				; read first dword of descriptor
	mov	edi,eax			; save it
	lodsd				; read second dword of descriptor
	cmp	ah,0xFF			; is it Winice's special code segment?
	loopnz	@B			; if not, search further

	popfd

	jecxz	.error			; did we find what we were looking for?

	rol	eax,8			; compute start address of code segment
	xchg	ah,al
	shrd	edi,eax,16

	cmp	[edi],edi		; 1st check
	jnz	.error

; compute Winice base, using NON relocated addresses...
	sub	edi,[wSelector_WINICE_Code]
	add	edi,byte 4

	mov	eax,[c_PCI_]
	add	eax,edi
	cmp	dword [eax],0x83EC8B55	; push ebp/mov ebp,esp/sub esp,30
	jnz	.error

	mov	[sdata+oWinice],edi

	pop	edi
	pop	esi
	pop	ecx

.ok:
	clc
	retn

.error:
	pop	edi
	pop	esi
	pop	ecx
	stc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
RelocWIAT:
	push	ecx
	push	esi

	mov	eax,[sdata+oWinice]
	mov	ecx,[wiat_size]
	mov	esi,wiat

@@
	add	[esi],eax
	add	esi,byte 4
	loop	@B

	mov	eax,[fWiniceIsActive]
	mov	[sdata+s_fWiniceIsActive],eax

	mov	eax,[pSendSpecificEOI]
	mov	[sdata+s_pSendSpecificEOI],eax

	pop	esi
	pop	ecx
	clc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
ApplyWinicePatches:
	call	HookWiniceMain
	jb	.error

	call	HookINT41015034
	jb	.unhookwinicemain

	call	Swap_CMD_INVALID

	clc
	retn

.unhookwinicemain:
	call	UnhookWiniceMain

.error:
	stc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
RemoveWinicePatches:
	call	Swap_CMD_INVALID
	call	UnhookWiniceMain
	call	UnhookINT41015034

	clc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
ChangeDLLExports:
	pushad

	call	FindKernel32Thread
	jnc	@F

	Trace_Out "ICEDUMP: failed to find a kernel32 thread"
	popad
	stc
	retn

@@
	cmp	dword [.event],byte 0
	jz	@F

	Trace_Out "ICEDUMP: ChangeDLLExportsEvent is already in progress"
	popad
	stc
	retn

@@
	mov	ebx,eax
	mov	eax,LOW_PRI_DEVICE_BOOST
	mov	ecx,PEF_WAIT_NOT_CRIT | PEF_THREAD_EVENT
	mov	esi,ChangeDLLExportsEvent
	xor	edi,edi
	VMMCall	Call_Restricted_Event
	mov	[.event], esi

	popad
	clc
	retn


segment _SDATA
	align 4
.event:		dd 0


segment _LTEXT
ChangeDLLExportsEvent:
	xor	eax,eax
	xchg	[ChangeDLLExports.event],eax
	test	eax,eax
	jz	@F

	call	ApplyPatchOnSharedDLL

@@
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
GetAPIInfo:
	call	GetAPIs
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_HOOKS
	Trace_Out "ICEDUMP: GetAPIs failed"
	debug_end

	stc
	retn

@@
	call	GetK32Info
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_HOOKS
	Trace_Out "ICEDUMP: GetK32Info failed"
	debug_end

	stc

@@
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
HydraInit:
	call	InitPlug
	clc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
HydraExit:
	call	ExitPlug
	clc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
ActivateWiniceBPs:
	call	[pActivateBPs]
	clc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
DeactivateWiniceBPs:
	call	[pDeactivateBPs]
	clc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
Swap_CMD_INVALID:
	mov	eax,[tCommands]

	xchg	[sdata+oOld_CMD_INVALID],edx
	xchg	[eax+4*CMD_INVALID],edx
	xchg	[sdata+oOld_CMD_INVALID],edx

	clc
	retn


segment _LDATA
	align 4
VMCB:
.oPMIDT:	dd 0
.selLDT:	dd 0x1A
.oLDT:		dd 0x28
HooksList:	dd 0
IntHandlers:	dd Handler_Int00
		dd Handler_Int01
		dd Handler_Int02
		dd Handler_Int03
		dd Handler_Int04
		dd Handler_Int05
		dd Handler_Int06
		dd Handler_Int09
		dd Handler_Int0B
		dd Handler_Int0C
		dd Handler_Int0D
		dd Handler_Int0E
		dd Handler_Int0F
		dd Handler_Int41
		dd Handler_Int51
		dd Handler_Int53
		dd Handler_Int54
		dd Handler_Int57
		dd Handler_Int5C
		dd 0			; end of table marker

; we redirect ints 00-09,0B-0F,41,51,53,54,57,5C - set the corresponding bits
IntHandlerMask:
;	    11111111111111110000000000000000
;	    FEDCBA9876543210FEDCBA9876543210
	dd  00000000000000001111101001111111b ; 1F-00
	dd  00000000000000000000000000000000b ; 3F-20
	dd  00010000100110100000000000000010b ; 5F-40



segment _LTEXT

;-------------------------------------------------------------------------------
; find the IDTR in the VMCB and then hook all IDTs in all existing VMs
;-------------------------------------------------------------------------------
HookInts:
	push	ebx
	push	ecx
	push	esi
	push	edi

; save original int3 IDT descriptor
	pushfd
	cli

	push	eax
	sidt	[esp-2]
	pop	eax
	lea	eax,[eax+8*3]			; eax: int3 IDT descriptor offset

	push	dword [eax]
	pop	dword [IDT.original]
	push	dword [eax+4]
	pop	dword [IDT.original+4]

	popfd

; hook ints in each VM
	VMMCall	Get_Sys_VM_Handle	; figure out VMCB.oPMIDT
	mov	edi,ebx			; by looking for 0x2FF (IDT limit)
	mov	ecx,0x30		; *below* a VMCB (that of the SysVM
	mov	eax,0x2FF		; since it must have a PM IDT by now)
	std
	repnz	scasw
	jecxz	.error

	sub	edi,ebx
	add	edi,byte 4
	mov	[VMCB.oPMIDT],edi
	cld

	mov	eax,LF_ALLOC_ERROR + LF_ASYNC
	mov	ecx,HookedInts_size
	VMMCall	List_Create
	jnc	@F

.error:
	pop	edi
	pop	esi
	pop	ecx
	pop	ebx
	stc
	retn

@@
	mov	[HooksList],esi

	pushfd
	cli

.nextVM:
	push	ebx
	call	HookIntsInVM
	pop	ebx
	jnc	@F

	call	UnhookInts

	popfd

	pop	edi
	pop	esi
	pop	ecx
	pop	ebx
	stc
	retn

@@
	VMMCall	Get_Next_VM_Handle
	VMMCall	Test_Sys_VM_Handle
	jnz	.nextVM

	popfd

	pop	edi
	pop	esi
	pop	ecx
	pop	ebx
	clc
	retn


;-------------------------------------------------------------------------------
; hook vectors in a V86 (once in a life) or PM (in each VM) IDT
;
; ebx: VMCB
;-------------------------------------------------------------------------------
HookIntsInVM:
	pushad

	test	dword [ebx],VMSTAT_PM_APP | VMSTAT_PM_EXEC
	jnz	.hookPM

	mov	eax,sdata+V86IDT
	cmp	dword [eax+HookedInts.VMCB],byte 0
	jz	@F

	popad
	clc
	retn

@@
	mov	[eax+HookedInts.VMCB],ebx
	add	ebx,[VMCB.oPMIDT]
	mov	esi,[ebx]		; esi: V86 IDT, same in all VMs

	debug_start debugflags, ICEDUMP_DEBUG_HOOKS
	mov	ebx,[eax+HookedInts.VMCB]
	Trace_Out "ICEDUMP: hooking V86 ints in VM: #ebx, IDT: #esi"
	debug_end

	jmp	short .hookInts

.hookPM:
	mov	esi,[HooksList]
	VMMCall	List_Allocate
	jnc	@F

.error:

	debug_start debugflags, ICEDUMP_DEBUG_HOOKS
	Trace_Out "ICEDUMP: failed to hook ints in VM: #ebx"
	debug_end

	popad
	stc
	retn

@@
	VMMCall	List_Attach
	mov	[eax+HookedInts.VMCB],ebx
	mov	dword [eax+HookedInts.cAPP],1
	add	ebx,[VMCB.oPMIDT]
	mov	esi,[ebx]		; esi: IDT

	debug_start debugflags, ICEDUMP_DEBUG_HOOKS
	mov	ebx,[eax+HookedInts.VMCB]
	Trace_Out "ICEDUMP: hooking ints in VM: #ebx, IDT: #esi"
	debug_end

.hookInts:
	mov	edi,eax			; edi: hooks
	or	ecx,byte -1		; ecx: int counter
	mov	edx,IntHandlers

	mov	ebx,[esi+8*8+4]		; save double fault handler (task gate)
	mov	[eax+Handler_Int08-8],ebx
	mov	ebx,[esi+8*8]
	mov	[eax+Handler_Int08-4],ebx

	mov	dword [eax+Handler_Int08],0x2DFF36	; jmp fword [ss:off32]
	mov	[eax+Handler_Int08+3],eax
	add	dword [eax+Handler_Int08+3],Handler_Int08-8+2

	lea	ebx,[eax+Handler_Int08]
	mov	word [esi+8*8+2],0x28
	mov	[esi+8*8],bx
	shr	ebx,16
	mov	[esi+8*8+6],bx
	mov	word [esi+8*8+4],0x8E00	; 32 bit interrupt gate, DPL=0 so that the kids don't play with it

.nextINT:
	mov	ebx,[edx]		; get Handler_IntX offset
	add	edx,byte 4
	or	ebx,ebx			; table is 0-terminated
	jnz	@F

	popad
	clc
	retn

@@
	inc	ecx
	cmp	ecx,byte 0x60		; max 0x60 ints, safety measure in case
	jae	.nextINT		; someone screwed up the bitmasks ;-)

	lodsd				; get current handler
	shl	eax,16
	lodsw
	lodsw
	ror	eax,16

	bt	[IntHandlerMask],ecx	; check if we redirect this INT
	jnc	@B			; no bit shifted into CF

	add	ebx,edi			; ebx: edi+Handler_IntX
	mov	dword [ebx],0x25FF36	; jmp dword [ss:off32]
	mov	[ebx+3],ebx
	sub	dword [ebx+3],byte 4
	mov	[ebx-4],eax

	mov	[esi-8],bx
	shr	ebx,16
	mov	[esi-2],bx

	jmp	short .nextINT


;-------------------------------------------------------------------------------
; unhook all vectors in all IDTs then free our list
;-------------------------------------------------------------------------------
UnhookInts:
	push	ebx
	push	esi

	pushfd
	cli

	mov	eax,sdata+V86IDT
	mov	ebx,[eax+HookedInts.VMCB]
	cmp	ebx,byte 0
	jz	@F

	call	UnhookIntsInVM
	and	dword [eax+HookedInts.VMCB],byte 0

@@
	cmp	dword [HooksList],byte 0
	jnz	@F

	debug_start debugflags, ICEDUMP_DEBUG_HOOKS
	mov	ebx,[eax+HookedInts.VMCB]
	Trace_Out "ICEDUMP: UnhookInts: empty list"
	debug_end

	jmp	short .ret

@@
	mov	esi,[HooksList]
	VMMCall	List_Get_First
	jz	@F

	mov	ebx,[eax+HookedInts.VMCB]
	call	UnhookIntsInVM

	push	eax
	mov	esi,[HooksList]
	VMMCall	List_Remove
	pop	eax
	VMMCall	List_Deallocate

	jmp	short @B

@@
	xor	esi,esi
	xchg	esi,[HooksList]
	VMMCall	List_Destroy

.ret:
	popfd
	pop	esi
	pop	ebx
	retn


;-------------------------------------------------------------------------------
; unhook all vectors
;
; eax: HookedInts
; ebx: VMCB
;-------------------------------------------------------------------------------
UnhookIntsInVM:
	pushad

	debug_start debugflags, ICEDUMP_DEBUG_HOOKS
	Trace_Out "ICEDUMP: unhooking ints in VM: #ebx"
	debug_end

	add	ebx,[VMCB.oPMIDT]
	mov	edi,[ebx]		; edi: IDT
	mov	edx,eax
	or	ecx,byte -1		; ecx: int counter
	mov	esi,IntHandlers

	mov	eax,[edx+Handler_Int08-4]	; restore double fault handler (task gate)
	mov	[edi+8*8],eax
	mov	eax,[edx+Handler_Int08-8]
	mov	[edi+8*8+4],eax

.nextINT:
	lodsd				; get Handler_IntX offset
	or	eax,eax			; table is 0-terminated
	jnz	@F

	popad
	retn

@@
	inc	ecx
	cmp	ecx,byte 0x60		; max 0x60 ints, safety measure in case
	jae	.nextINT		; someone screwed up the bitmasks ;-)

	add	edi,byte 8

	bt	[IntHandlerMask],ecx	; check if we redirect this INT
	jnc	@B			; no bit shifted into CF

	add	eax,edx			; eax: edx+Handler_IntX
	mov	eax,[eax-4]		; get original handler

	mov	[edi-8],ax
	shr	eax,16
	mov	[edi-2],ax

	jmp	short .nextINT


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
HookWiniceMain:
	push	ebx
	push	ecx
	push	edi

; hook pWiniceMain at pDisableAllIRQs call
	mov	ebx,[pDisableAllIRQs]	; absolute adjusted call addr
	sub	ebx,byte 4
	mov	edi,[pWiniceMain]
	mov	ecx,0x400	; look thru 1k of code
	mov	al,0xE8		; search for 'call rel32'

.again:
	repnz	scasb
	jecxz	.error

	push	ebx
	sub	ebx,edi		; rel32 offset of call
	cmp	ebx,[edi]	; is it our call ?
	pop	ebx
	jne	.again

; found location of call, now hook
	mov	[WiniceMainHookSource],edi
	mov	ebx,WiniceMainHook-4
	sub	ebx,edi
	xchg	[edi],ebx
	mov	[WiniceMainHookTarget],ebx

	pop	edi
	pop	ecx
	pop	ebx
	clc
	retn

.error:
	pop	edi
	pop	ecx
	pop	ebx
	stc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
UnhookWiniceMain:
	mov	eax,[WiniceMainHookSource]
	or	eax,eax
	jz	@F

	push	ebx
	mov	ebx,[WiniceMainHookTarget]
	mov	[eax],ebx
	pop	ebx

@@
	clc
	retn


segment _LDATA
	align 4
WiniceMainHookSource dd 0
WiniceMainHookTarget dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
;
;-------------------------------------------------------------------------------
WiniceMainHook:
	pushad

; we hooked the call to this func - we have to call it ourselves
	call	[pDisableAllIRQs]

	debug_start debugflags, ICEDUMP_DEBUG_HOOKS
	mov	esi,.msgHookCalled
	call	[pPrintToCommandWindow]
	debug_end

	call	MP3MainHook

	popad
	retn


segment _LDATA
.msgHookCalled:	db 'WiniceMainHook called',0


%if WINICE_VERSION_MAJOR = 3
  %if WINICE_VERSION_MINOR = 22 || WINICE_VERSION_MINOR = 23 || WINICE_VERSION_MINOR = 24 || WINICE_VERSION_MINOR = 25
    %undef UNSUPPORTED_VERSION
  %else
    %define UNSUPPORTED_VERSION
  %endif
%elif WINICE_VERSION_MAJOR = 4
  %if WINICE_VERSION_MINOR = 00 || WINICE_VERSION_MINOR = 01
    %undef UNSUPPORTED_VERSION
  %elif WINICE_VERSION_MINOR = 05
    %if WINICE_BUILD = 334 || WINICE_BUILD = 526
      %undef UNSUPPORTED_VERSION
    %else
      %define UNSUPPORTED_VERSION
    %endif
  %elif WINICE_VERSION_MINOR = 21
    %if WINICE_BUILD = 53
      %undef UNSUPPORTED_VERSION
    %else
      %define UNSUPPORTED_VERSION
    %endif
  %elif WINICE_VERSION_MINOR = 25
    %if WINICE_BUILD = 824
      %undef UNSUPPORTED_VERSION
    %else
      %define UNSUPPORTED_VERSION
    %endif
  %elif WINICE_VERSION_MINOR = 26
    %if WINICE_BUILD = 922
      %undef UNSUPPORTED_VERSION
    %else
      %define UNSUPPORTED_VERSION
    %endif
  %elif WINICE_VERSION_MINOR = 27
    %if WINICE_BUILD = 562
      %undef UNSUPPORTED_VERSION
    %else
      %define UNSUPPORTED_VERSION
    %endif
  %else
    %define UNSUPPORTED_VERSION
  %endif
%else
  %define UNSUPPORTED_VERSION
%endif

%ifndef UNSUPPORTED_VERSION
segment _LTEXT

;-------------------------------------------------------------------------------
; hook into a function which places a BP on the entry point of a module
; (used by loader32/wldr) and fix the problem with win32 modules not having
; their first section marked as executable (and thus winice not breaking at
; all)
;
; stc on error
;-------------------------------------------------------------------------------
HookINT41015034:
	push	ecx
	push	edi

	mov	edi,[pINT_41_0150_3_4]
	mov	ecx,64
	mov	al,0x0F				; lar ecx,eax

@@
	repnz	scasb
	jecxz	.error

	cmp	word [edi],0xC802		; lar ecx,eax
	jnz	@B

	cmp	byte [edi+2],0x75		; jnz .nobp
	jnz	.error

	movsx	eax,byte [edi+3]		; eax: rel32 .nobp
	mov	[INT41015034Hookrel8],al
	lea	eax,[eax+edi+4]

	sub	eax,INT41015034Hook.nobp+5
	mov	[INT41015034Hook.nobp+1],eax	; jmp .nobp

	lea	eax,[edi+0x10]			; eax: rel32 .bp
	sub	eax,INT41015034Hook.bp+5
	mov	[INT41015034Hook.bp+1],eax	; jmp .bp

	mov	byte [edi-1],0xE9		; jmp INT41015034Hook
	mov	eax,INT41015034Hook-4		; eax: rel32 INT41015034Hook
	sub	eax,edi
	mov	[edi],eax

	lea	edi,[edi+3]
	mov	[INT41015034HookSource],edi

	pop	edi
	pop	ecx
	clc
	retn

.error:
	pop	edi
	pop	ecx
	stc
	retn


;-------------------------------------------------------------------------------
; remove hook from winice (enjoy the bug again ;-)
;-------------------------------------------------------------------------------
UnhookINT41015034:
	mov	edi,[INT41015034HookSource]
	or	edi,edi
	jz	@F

	mov	dword [edi-4],0x75C8020F
	mov	al,[INT41015034Hookrel8]
	mov	[edi],al

@@
	retn


segment _LDATA
	align 4
INT41015034HookSource:	dd 0
INT41015034Hookrel8:	db 0

%else
%error unsupported version
%endif


segment _LTEXT

;-------------------------------------------------------------------------------
; hook simulates what winice does then fixes the bug
;-------------------------------------------------------------------------------
INT41015034Hook:
	lar	ecx,eax
	jnz	.nobp

	and	cx,0x1800
	cmp	cx,0x1800
	jz	.bp

	cmp	word [ebx+WiniceDeviceParams.LogicalSeg],byte 1
	jnz	.nobp

	mov	ecx,[selKernel32Data]		; bug to be triggered?
	cmp	ax,[ecx]
	jnz	.nobp

	mov	ecx,[selKernel32Code]		; check for CS:EIP will succeed
	mov	ax,[ecx]			; in the INT3 handler now

.bp:
	jmp	$

.nobp:
	jmp	$


; nasm bug:
; intersegment relocs in same file don't work properly
; that's why we had to split the static/non-static parts
; of the callbacks into two files, the linker does it right at least

segment _STEXT
;-------------------------------------------------------------------------------
StaticEntryV86CB:
	cmp	dword [sdata+loadcount],byte 0
	jnz	near entryV86CB

	retn


;-------------------------------------------------------------------------------
StaticEntryPMCB:
	cmp	dword [sdata+loadcount],byte 0
	jnz	near entryPMCB

	retn


;-------------------------------------------------------------------------------
StaticSimIretCB:
	cmp	dword [sdata+loadcount],byte 0
	jnz	near SimIret

	retn
