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


global TracerSysDynamicDeviceInit
global TracerSysDynamicDeviceExit
global TracerThreadInit
global TracerThreadNotExecuteable
global TracerVmInit
global TracerVmNotExecuteable
global SimIret
global Parse_Trace
global Parse_TraceX
global Service_Trace
global Service_TraceX
global VWIN32.W32ServiceTable
global VWIN32.W32_GetThreadContext
global VWIN32.W32_SetThreadContext
global VWIN32.W32_ResumeThread
global VWIN32.W32_SuspendThread
global VWIN32.W32_DIOC
global VWIN32.TDS
global TDBX.R0TCB
global TDBX.pPDB
global TDBX.pTDB
global selKrnl386CS


extern SetCB
extern SetCBX
extern sdata
extern Parser.error
extern Parser.errorMsg
extern Error_V86
extern Error_PM16
extern Error_PMR0
extern ParseExpression
extern OT.OLastbutone
extern OT.OBranch
extern OT.OAll
extern OT.OTraceSameProcess
extern OT.OTraceChildProcess
extern OT.OBreaknew
extern OT.ONTsel
extern IsThreadWin32
extern BreakIn
extern R3PID
extern R3TID
extern R3TCB.TDBX
extern IsPageCommitted


bits 32


; shamelessly ripped from sandpile.org ;-)
DR6_BT_MASK		equ	00008000h	; break due to TSS.T=1
DR6_BT_BIT		equ	15
DR6_BS_MASK		equ	00004000h	; break due to EFLAGS.TF=1
DR6_BS_BIT		equ	14
DR6_BD_MASK		equ	00002000h	; break due to DR7.GD=1
DR6_BD_BIT		equ	13
DR6_SMM_MASK		equ	00001000h	; (indicated SMM on some CPUs)
DR6_SMM_BIT		equ	12
DR6_B3_MASK		equ	00000008h	; break due to DR3 match
DR6_B3_BIT		equ	3
DR6_B2_MASK		equ	00000004h	; break due to DR2 match
DR6_B2_BIT		equ	2
DR6_B1_MASK		equ	00000002h	; break due to DR1 match
DR6_B1_BIT		equ	1
DR6_B0_MASK		equ	00000001h	; break due to DR0 match
DR6_B0_BIT		equ	0


; state information for the tracer on a per thread basis
;
; the theory goes like this: we attach a simple state machine to each thread
; we trace which tries to take care of the thread's awareness of its being
; traced. in other words, we try to trick the thread into beleiving that it's
; not traced and has full control over the single stepping mechanism should
; it wish to use it.
;
; the state set is two dimensional in the first approach, one dimension tells
; whether the thread is trying to trace itself or not, and the second dimension
; maintains emulation related information (all instructions able to read/write
; EFLAGS.T and DR6.BS must be properly emulated).
;
; the inputs to the state machine allow monitoring context inquiries/changes
; and reacting on exceptions (most importantly on the single step trap).
;
; the state transition functions will simply modify the state information
; and the thread's context if necessary.

IDT_SIZE		EQU 0x60

struc TraceInfo
.State0:	resd 1		; 00
.State1:	resd 1		; 04
.EIPlow:	resd 1		; 08
.EIPhigh:	resd 1		; 0C
.TickCount:	resd 2		; 10
.NTsel:		resd 1		; 18
.lastCS:	resd 1		; 1C
.lastEIP:	resd 1		; 20
.lastSS:	resd 1		; 24
.lastESP:	resd 1		; 28
.IDTR:		resd 1		; 2C
.IRETD:		resb 16*IDT_SIZE; 30
.IDT:		resb 8*IDT_SIZE	; 630
endstruc

ABORT			EQU 0
PASSDOWN		EQU 1
DONTPASS		EQU 2

ST0_SELFTRACEOFF	EQU 0
ST0_SELFTRACEON		EQU 1
ST0_SIZE		EQU 2

ST1_NOOP		EQU 0
ST1_PUSHF		EQU 1
ST1_POPF		EQU 2
ST1_IRET		EQU 3
ST1_SIDT		EQU 4
ST1_GETTICKCOUNT	EQU 5
ST1_RDTSC		EQU 6
ST1_NTSEL		EQU 7
ST1_SIZE		EQU 8

;ST1_ICEBP		EQU ?

IN_DEBUG_BS		EQU 0
IN_DEBUG_BS_BX		EQU 1
IN_EXCEPTION_IN		EQU 2
IN_EXCEPTION_OUT	EQU 3
IN_SIZE			EQU 4


segment _LDATA
	align 4
Transitions:
ST0_SelfTraceOff:
	;	IN_DEBUG_BS     IN_DEBUG_BS_BX     IN_EXCEPTION_IN IN_EXCEPTION_OUT
.NONE:	dd	TrRet,          TrPassLie,         TrPassLie,      TrReinforce
.PUSHF:	dd	TrRetEmuPushf,  TrPassLieEmuPushf, TrPassLie,      TrReinforce
.POPF:	dd	TrRetEmuPopf,   TrPassLieEmuPopf,  TrPassLie,      TrReinforce
.IRET:	dd	TrRetEmuIret,   TrPassLieEmuIret,  TrPassLie,      TrReinforce
.SIDT:	dd	TrRetEmuSidt,   TrPassLieEmuSidt,  TrPassLie,      TrReinforce
.GETTC:	dd	TrRetEmuGetTC,  TrPassLieEmuGetTC, TrPassLie,      TrReinforce
.RDTSC:	dd	TrRetEmuRDTSC,  TrPassLieEmuRDTSC, TrPassLie,      TrReinforce
.NTSEL:	dd	TrRetEmuNTsel,  TrPassLieEmuNTsel, TrPassLie,      TrReinforce

ST0_SelfTraceOn:
.NONE:	dd	TrPass,         TrPass,            TrPass,         TrReinforce
.PUSHF:	dd	TrPassEmuPushf, TrPassEmuPushf,    TrPass,         TrReinforce
.POPF:	dd	TrPassEmuPopf,  TrPassEmuPopf,     TrPass,         TrReinforce
.IRET:	dd	TrPassEmuIret,  TrPassEmuIret,     TrPass,         TrReinforce
.SIDT:	dd	TrPassEmuSidt,  TrPassEmuSidt,     TrPass,         TrReinforce
.GETTC:	dd	TrPassEmuGetTC, TrPassEmuGetTC,    TrPass,         TrReinforce
.RDTSC:	dd	TrPassEmuRDTSC, TrPassEmuRDTSC,    TrPass,         TrReinforce
.NTSEL:	dd	TrPassEmuNTsel, TrPassEmuNTsel,    TrPass,         TrReinforce


segment _LTEXT
;-------------------------------------------------------------------------------
; state transition functions
;
; EBX: VMCB
; EDI: R0TCB
; ESI: R0TCB->TDS (TranceInfo)
; EBP: Client Registers
;
; each function must preserve registers except for EAX which holds the return
; value (where used, currently in response to IN_DEBUG_BS and IN_DEBUG_BS_BX)
;
; only the thread's context and the statemachine is allowed to be changed
;-------------------------------------------------------------------------------
TrRet:
	btr	word [ebp+CRS.EFlags],8
	mov	eax,DONTPASS
	retn

;-------------------------------------------------------------------------------
TrRetEmuPushf:
	btr	word [ebp+CRS.EFlags],8

	call	GetLinearSSESP
	jc	@F

	btr	word [eax],8

@@
	mov	eax,DONTPASS
	retn

;-------------------------------------------------------------------------------
TrRetEmuPopf:
	bt	word [ebp+CRS.EFlags],8
	jnc	@F

	mov	dword [esi+TraceInfo.State0],ST0_SELFTRACEON

@@
	mov	eax,DONTPASS
	retn

;-------------------------------------------------------------------------------
TrRetEmuIret:
	bt	word [ebp+CRS.EFlags],8
	jnc	@F

	mov	dword [esi+TraceInfo.State0],ST0_SELFTRACEON

@@
	mov	eax,DONTPASS
	retn

;-------------------------------------------------------------------------------
TrRetEmuSidt:
	btr	word [ebp+CRS.EFlags],8

	lea	eax,[esi+TraceInfo.IDT]
	push	eax
	mov	eax,[esi+TraceInfo.IDTR]
	pop	dword [eax+2]

	mov	eax,DONTPASS
	retn

;-------------------------------------------------------------------------------
TrRetEmuGetTC:
	btr	word [ebp+CRS.EFlags],8

	call	TrEmuGetTC
	mov	eax,DONTPASS
	retn

;-------------------------------------------------------------------------------
TrRetEmuRDTSC:
	btr	word [ebp+CRS.EFlags],8

	call	TrEmuRDTSC
	mov	eax,DONTPASS
	retn

;-------------------------------------------------------------------------------
TrRetEmuNTsel:
	btr	word [ebp+CRS.EFlags],8

	call	TrEmuNTsel
	mov	eax,DONTPASS
	retn

;-------------------------------------------------------------------------------
TrPass:
	bts	word [ebp+CRS.EFlags],8
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassEmuPushf:
	bts	word [ebp+CRS.EFlags],8
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassEmuPopf:
	bt	word [ebp+CRS.EFlags],8
	jc	@F

	mov	dword [esi+TraceInfo.State0],ST0_SELFTRACEOFF

	mov	eax,[ebp+CRS.EIP]
	Trace_Out "ICEDUMP: TrPassEmuPopf: selftraceoff at #eax"

@@
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassEmuIret:
	bt	word [ebp+CRS.EFlags],8
	jc	@F

	mov	dword [esi+TraceInfo.State0],ST0_SELFTRACEOFF

	mov	eax,[ebp+CRS.EIP]
	Trace_Out "ICEDUMP: TrPassEmuIret: selftraceoff at #eax"

@@
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassEmuSidt:
	bts	word [ebp+CRS.EFlags],8

	lea	eax,[esi+TraceInfo.IDT]
	push	eax
	mov	eax,[esi+TraceInfo.IDTR]
	pop	dword [eax+2]

	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassEmuGetTC:
	bts	word [ebp+CRS.EFlags],8

	call	TrEmuGetTC
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassEmuRDTSC:
	bts	word [ebp+CRS.EFlags],8

	call	TrEmuRDTSC
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassEmuNTsel:
	bts	word [ebp+CRS.EFlags],8

	call	TrEmuNTsel
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassLie:
	mov	eax,dr6
	and	eax,~DR6_BS_MASK
	mov	dr6,eax
	btr	word [ebp+CRS.EFlags],8
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassLieEmuPushf:
	mov	eax,dr6
	and	eax,~DR6_BS_MASK
	mov	dr6,eax
	btr	word [ebp+CRS.EFlags],8

	call	GetLinearSSESP
	jc	@F

	btr	word [eax],8

@@
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassLieEmuPopf:
	mov	eax,dr6
	and	eax,~DR6_BS_MASK
	mov	dr6,eax
	bt	word [ebp+CRS.EFlags],8
	jnc	@F

	mov	dword [esi+TraceInfo.State0],ST0_SELFTRACEON

@@
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassLieEmuIret:
	mov	eax,dr6
	and	eax,~DR6_BS_MASK
	mov	dr6,eax
	bt	word [ebp+CRS.EFlags],8
	jnc	@F

	mov	dword [esi+TraceInfo.State0],ST0_SELFTRACEON

@@
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassLieEmuSidt:
	mov	eax,dr6
	and	eax,~DR6_BS_MASK
	mov	dr6,eax
	btr	word [ebp+CRS.EFlags],8

	lea	eax,[esi+TraceInfo.IDT]
	push	eax
	mov	eax,[esi+TraceInfo.IDTR]
	pop	dword [eax+2]

	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassLieEmuGetTC:
	mov	eax,dr6
	and	eax,~DR6_BS_MASK
	mov	dr6,eax
	btr	word [ebp+CRS.EFlags],8

	call	TrEmuGetTC
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassLieEmuRDTSC:
	mov	eax,dr6
	and	eax,~DR6_BS_MASK
	mov	dr6,eax
	btr	word [ebp+CRS.EFlags],8

	call	TrEmuRDTSC
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrPassLieEmuNTsel:
	mov	eax,dr6
	and	eax,~DR6_BS_MASK
	mov	dr6,eax
	btr	word [ebp+CRS.EFlags],8

	call	TrEmuNTsel
	mov	eax,PASSDOWN
	retn

;-------------------------------------------------------------------------------
TrReinforce:
	call	TrBreakIn
	jc	@F

	retn

@@
	mov	dword [esi+TraceInfo.State0],ST0_SELFTRACEOFF
	bts	word [ebp+CRS.EFlags],8
	jnc	@F

	mov	dword [esi+TraceInfo.State0],ST0_SELFTRACEON

@@
	add	dword [esi+TraceInfo.TickCount],byte 1
	adc	dword [esi+TraceInfo.TickCount+4],byte 0

	call	GetLinearCSEIP
	jnc	.find_opcode

	cmp	dword [esi+TraceInfo.State0],byte ST0_SELFTRACEON
	jz	@F

	btr	word [ebp+CRS.EFlags],8

@@
;	call	BreakIn
	call	TracerFree

	push	ebx

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	movzx	eax,word [ebp+CRS.CS]
	mov	ebx,[ebp+CRS.EIP]
	Trace_Out "ICEDUMP: TrReinforce: GetLinearCSEIP failed, CS:EIP: #ax:#ebx"
	debug_end

	pop	ebx
	retn

.find_opcode:
	push	ecx
	push	edx

	call	SkipPrefixes
	jb	near .noop

.protect_start:
	cmp	byte [eax],0x9C
	jnz	@F

	mov	dword [esi+TraceInfo.State1],ST1_PUSHF
	jmp	.ret

@@
	cmp	byte [eax],0x9D
	jnz	@F

	mov	dword [esi+TraceInfo.State1],ST1_POPF
	jmp	.ret

@@
	cmp	byte [eax],0xCF
	jnz	@F

	cmp	eax,0x80000000
	jae	near .ret

	mov	dword [esi+TraceInfo.State1],ST1_IRET
	jmp	.ret

@@
	cmp	ecx,byte 3
	jbe	@F

	cmp	word [eax],0x010F		; SIDT and similar
	jnz	@F

	mov	dl,[eax+2]
	and	dl,00111000b
	cmp	dl,00001000b			; SIDT
	jnz	@F

	add	eax,byte 2
	sub	ecx,byte 2
	call	GetLenAndAddr
	jc	@F

	mov	[esi+TraceInfo.IDTR],eax
	mov	dword [esi+TraceInfo.State1],ST1_SIDT
	jmp	short .ret

@@
	cmp	word [eax+ecx-15],0xA165	; mov eax,[gs:0]
	jnz	@F

	cmp	dword [eax+ecx-15+2],byte 0
	jnz	@F

	mov	dx,[VTDAPI.selTC]
	xor	dx,[ebp+CRS.GS]
	and	dx,0xFFFC
	jnz	@F

	mov	dword [esi+TraceInfo.State1],ST1_GETTICKCOUNT
	jmp	short .ret

@@
	cmp	ecx,byte 2
	jb	@F

	cmp	word [eax],0x310F		; RDTSC
	jnz	@F

	mov	dword [esi+TraceInfo.State1],ST1_RDTSC
	jmp	short .ret

@@
	cmp	ecx,byte 2
	jb	@F

	cmp	byte [eax],0x8C			; mov reg,segment reg
	jnz	@F

	mov	dl,[eax+1]
	and	dl,11000000b
	cmp	dl,11000000b			; mov reg,segment reg
	jnz	@F

	movzx	edx,byte [eax+1]		; get target reg index
	and	dl,7
	mov	[esi+TraceInfo.NTsel],edx
	mov	dword [esi+TraceInfo.State1],ST1_NTSEL
	jmp	short .ret

@@

.protect_end:

.noop:
.EH:
	mov	dword [esi+TraceInfo.State1],ST1_NOOP

.ret:
	pop	edx
	pop	ecx
	retn


;-------------------------------------------------------------------------------
; EBX: VMCB
; EDI: R0TCB
; ESI: R0TCB->TDS (TranceInfo)
; EBP: Client Registers
;-------------------------------------------------------------------------------
TrEmuGetTC:
	push	eax
	push	edx

	mov	eax,[esi+TraceInfo.TickCount]
	mov	edx,[esi+TraceInfo.TickCount+4]
	shrd	eax,edx,17			; simulate a 128 mips machine
	mov	[ebp+CRS.EAX],eax

	pop	edx
	pop	eax
	retn


;-------------------------------------------------------------------------------
; EBX: VMCB
; EDI: R0TCB
; ESI: R0TCB->TDS (TranceInfo)
; EBP: Client Registers
;-------------------------------------------------------------------------------
TrEmuRDTSC:
	push	eax
	push	edx

	mov	eax,[esi+TraceInfo.TickCount]
	mov	edx,[esi+TraceInfo.TickCount+4]
	shrd	eax,edx,17			; simulate a 128 mips machine
	mov	[ebp+CRS.EAX],eax
	shr	edx,17
	mov	[ebp+CRS.EDX],edx

	pop	edx
	pop	eax
	retn


;-------------------------------------------------------------------------------
; EBX: VMCB
; EDI: R0TCB
; ESI: R0TCB->TDS (TranceInfo)
; EBP: Client Registers
;-------------------------------------------------------------------------------
TrEmuNTsel:
	cmp	byte [OT.ONTsel],'D'
	jz	@F

	push	eax
	mov	eax,7
	sub	eax,[esi+TraceInfo.NTsel]
	and	byte [ebp+4*eax],11111011b	; make it into the GDT
	pop	eax

@@
	retn


;-------------------------------------------------------------------------------
; EBX: VMCB
; EBP: Client Registers
;
; EAX: linear SS:ESP
; stc on error
;-------------------------------------------------------------------------------
GetLinearSSESP:
	push	ecx
	push	edx

	movzx	eax,word [ebp+CRS.SS]

	test	byte [ebp+CRS.EFlags+2],2	; is client in V86 mode?
	jz	.PM

	movzx	edx,word [ebp+CRS.ESP]
	shl	eax,4
	add	eax,edx

	pop	edx
	pop	ecx
	clc
	retn

.PM:
	VMMCall	_SelectorMapFlat, ebx, eax, byte 0
	cmp	eax,byte -1
	jnz	@F

	pop	edx
	pop	ecx
	stc
	retn

@@
	mov	edx,[ebp+CRS.ESP]
	lar	ecx,[ebp+CRS.SS]		; is client stack 32 bit?
	bt	ecx,22
	jc	@F

	movzx	edx,dx

@@
	add	eax,edx

	pop	edx
	pop	ecx
	clc
	retn


;-------------------------------------------------------------------------------
; EBX: VMCB
; EBP: Client Registers
;
; EAX: linear CS:EIP
; stc on error
;-------------------------------------------------------------------------------
GetLinearCSEIP:
	push	ecx
	push	edx

	movzx	eax,word [ebp+CRS.CS]

	test	byte [ebp+CRS.EFlags+2],2	; is client in V86 mode?
	jz	.PM

	movzx	edx,word [ebp+CRS.EIP]
	shl	eax,4
	add	eax,edx

	pop	edx
	pop	ecx
	clc
	retn

.PM:
	VMMCall	_SelectorMapFlat, ebx, eax, byte 0
	cmp	eax,byte -1
	jnz	@F

	pop	edx
	pop	ecx
	stc
	retn

@@
	mov	edx,[ebp+CRS.EIP]
	lar	ecx,[ebp+CRS.CS]		; is client 32 bit?
	bt	ecx,22
	jc	@F

	movzx	edx,dx

@@
	add	eax,edx

	pop	edx
	pop	ecx
	clc
	retn


;-------------------------------------------------------------------------------
; G-RoM's stuff
;
; EAX: address
; ECX: max length
; EBP: Client Registers
;
; EAX: linear address (operand)
; stc on error (length exceeded)
;-------------------------------------------------------------------------------
GetLenAndAddr:
	push	ebx
	push	ecx
	push	edx
	push	edi
	mov	edi,esp		; needed 'cos of EH

.protect_start:
	movzx	edx,byte [eax]

	ror	edx,3
	shr	dl,3
	rol	edx,3
	inc	eax
	push	dword .ret
	jmp	[edx*4+.Jumptable]

.ill:
.EH:
	stc

.ret:
	mov	esp,edi
	pop	edi
	pop	edx
	pop	ecx
	pop	ebx
	retn

.ga32AX:
	mov	eax,[ebp+CRS.EAX]
	sub	ecx,byte 1
	retn

.ga32BX:
	mov	eax,[ebp+CRS.EBX]
	sub	ecx,byte 1
	retn

.ga32CX:
	mov	eax,[ebp+CRS.ECX]
	sub	ecx,byte 1
	retn

.ga32DX:
	mov	eax,[ebp+CRS.EDX]
	sub	ecx,byte 1
	retn

.ga32DD:

	mov	eax,[eax]
	sub	ecx,byte 5		;new size
	retn

.ga32SI:
	mov	eax,[ebp+CRS.ESI]
	sub	ecx,byte 1
	retn

.ga32DI:
	mov	eax,[ebp+CRS.EDI]
	sub	ecx,byte 1
	retn

.ga32AXD:
	mov	eax,[eax]
	add	eax,[ebp+CRS.EAX]
	sub	ecx,byte 5
	retn

.ga32BXD:
	mov	eax,[eax]
	add	eax,[ebp+CRS.EBX]
	sub	ecx,byte 5
	retn

.ga32CXD:
	mov	eax,[eax]
	add	eax,[ebp+CRS.ECX]
	sub	ecx,byte 5
	retn

.ga32DXD:
	mov	eax,[eax]
	add	eax,[ebp+CRS.EDX]
	sub	ecx,byte 5
	retn

.ga32BPD:
	mov	eax,[eax]
	add	eax,[ebp+CRS.EBP]
	sub	ecx,byte 5
	retn

.ga32SID:
	mov	eax,[eax]
	add	eax,[ebp+CRS.ESI]
	sub	ecx,byte 5
	retn

.ga32DID:
	mov	eax,[eax]
	add	eax,[ebp+CRS.EDI]
	sub	ecx,byte 5
	retn

.ga32AXB:
	movsx	eax,byte [eax]
	add	eax,[ebp+CRS.EAX]
	sub	ecx,byte 2
	retn

.ga32BXB:
	movsx	eax,byte [eax]
	add	eax,[ebp+CRS.EBX]
	sub	ecx,byte 2
	retn

.ga32CXB:
	movsx	eax,byte [eax]
	add	eax,[ebp+CRS.ECX]
	sub	ecx,byte 2
	retn

.ga32DXB:
	movsx	eax,byte [eax]
	add	eax,[ebp+CRS.EDX]
	sub	ecx,byte 2
	retn

.ga32SIB:
	movsx	eax,byte [eax]
	add	eax,[ebp+CRS.ESI]
	sub	ecx,byte 2
	retn

.ga32DIB:
	movsx	eax,byte [eax]
	add	eax,[ebp+CRS.EDI]
	sub	ecx,byte 2
	retn

.ga32BPB:
	movsx	eax,byte [eax]
	add	eax,[ebp+CRS.EBP]
	sub	ecx,byte 2
	retn

.ga32S:
	movzx	edx,byte [eax]
	and	dl,111b			; implied clc
	call	[.getBaseS+4*edx]

.ga32Scommon:
	jc	@F

	mov	ebx,edx

	movzx	edx,byte [eax]
	shr	dl,3
	and	dl,111b
	call	[.getIndex+4*edx]

	movzx	eax,byte [eax]
	shr	al,6
	call	[.scaleIndex+4*eax]

	lea	eax,[edx+ebx]
	sub	ecx,byte 1

@@
	retn

.ga32SB:
	movzx	edx,byte [eax]
	and	dl,111b			; implied clc
	call	[.getBaseSB+4*edx]
	jc	@B

	movsx	ebx,byte [eax+1]
	add	edx,ebx

	sub	ecx,byte 1
	jmp	short .ga32Scommon

.ga32SD:
	movzx	edx,byte [eax]
	and	dl,111b			; implied clc
	call	[.getBaseSD+4*edx]
	jc	@B

	add	edx,[eax+1]

	sub	ecx,byte 4
	jmp	short .ga32Scommon

.gbAX:
.giAX:
	mov	edx,[ebp+CRS.EAX]
	retn

.gbCX:
.giCX:
	mov	edx,[ebp+CRS.ECX]
	retn

.gbDX:
.giDX:
	mov	edx,[ebp+CRS.EDX]
	retn

.gbBX:
.giBX:
	mov	edx,[ebp+CRS.EBX]
	retn

.gbSP:
.giSP:
	mov	edx,[ebp+CRS.ESP]
	retn

.gbBP:
	mov	edx,[eax+1]
	sub	ecx,byte 4
	retn


.gbBPB:
	movsx	edx,byte [eax+1]
	add	edx,[ebp+CRS.EBP]
	sub	ecx,byte 1
	retn

.gbBPD:
	mov	edx,[eax+1]
	add	edx,[ebp+CRS.EBP]
	sub	ecx,byte 4
	retn

.giBP:
	mov	edx,[ebp+CRS.EBP]
	retn

.gi:
	xor	edx,edx
	retn

.gbSI:
.giSI:
	mov	edx,[ebp+CRS.ESI]
	retn

.gbDI:
.giDI:
	mov	edx,[ebp+CRS.EDI]
	retn

.si1:
	retn

.si2:
	shl	edx,1
	retn

.si4:
	shl	edx,2
	retn

.si8:
	shl	edx,3
	retn

.protect_end:


segment _LDATA
	align 4
.getBaseS:
	dd	.gbAX
	dd	.gbCX
	dd	.gbDX
	dd	.gbBX
	dd	.gbSP
	dd	.gbBP
	dd	.gbSI
	dd	.gbDI

.getBaseSB:
	dd	.gbAX
	dd	.gbCX
	dd	.gbDX
	dd	.gbBX
	dd	.gbSP
	dd	.gbBPB
	dd	.gbSI
	dd	.gbDI

.getBaseSD:
	dd	.gbAX
	dd	.gbCX
	dd	.gbDX
	dd	.gbBX
	dd	.gbSP
	dd	.gbBPD
	dd	.gbSI
	dd	.gbDI

.getIndex:
	dd	.giAX
	dd	.giCX
	dd	.giDX
	dd	.giBX
	dd	.gi
	dd	.giBP
	dd	.giSI
	dd	.giDI

.scaleIndex:
	dd	.si1
	dd	.si2
	dd	.si4
	dd	.si8

.Jumptable:
	dd 	.ga32AX
	dd	.ga32CX
	dd	.ga32DX
	dd	.ga32BX
	dd	.ga32S
	dd	.ga32DD
	dd	.ga32SI
	dd	.ga32DI

	dd	.ga32AXB
	dd	.ga32CXB
	dd	.ga32DXB
	dd	.ga32BXB
	dd	.ga32SB
	dd	.ga32BPB
	dd	.ga32SIB
	dd	.ga32DIB

	dd	.ga32AXD
	dd	.ga32CXD
	dd	.ga32DXD
	dd	.ga32BXD
	dd	.ga32SD
	dd	.ga32BPD
	dd	.ga32SID
	dd	.ga32DID

	times	8 dd .ill


segment _LTEXT
;-------------------------------------------------------------------------------
; decide if winice should pop up or continue tracing
;
; EBX: VMCB
; EDI: R0TCB
; ESI: R0TCB->TDS (TranceInfo)
; EBP: Client Registers
;
; stc if will not break in
;-------------------------------------------------------------------------------
TrBreakIn:
	mov	eax,[ebp+CRS.EIP]
	cmp	eax,[esi+TraceInfo.EIPlow]
	jae	@F

	stc
	retn

@@
	cmp	eax,[esi+TraceInfo.EIPhigh]
	jbe	@F

	stc
	retn

@@
	cmp	byte [OT.OLastbutone],'D'
	jz	@F

	cmp	byte [OT.OBranch],'E'
	jz	@F

	cmp	byte [OT.OAll],'E'
	jz	@F

	push	ebx
	push	ecx
	push	edx

	mov	eax,[esi+TraceInfo.lastEIP]
	mov	ecx,[esi+TraceInfo.lastCS]
	mov	ebx,[esi+TraceInfo.lastESP]
	mov	edx,[esi+TraceInfo.lastSS]
	Trace_Out "ICEDUMP: LOG: CS:EIP: #cx:#eax, SS:ESP: #dx:#ebx, R0TCB: #edi"

	mov	eax,[esi+TraceInfo.TickCount]
	mov	edx,[esi+TraceInfo.TickCount+4]
	Trace_Out "ICEDUMP: LOG: instruction count: #edx:#eax, R0TCB: #edi"

	pop	edx
	pop	ecx
	pop	ebx

@@
	btr	word [ebp+CRS.EFlags],8
	pushfd

	call	BreakIn
	jc	@F

	popfd
	call	TracerFree
	clc
	retn

@@
	Trace_Out "ICEDUMP: TrBreakIn: failed, continuing tracing, R0TCB: #edi"
	popfd
	jnc	@F

	bts	word [ebp+CRS.EFlags],8

@@
	stc
	retn


;-------------------------------------------------------------------------------
; hook VWIN32 Get/Set Thread Context VxD APIs
;
; stc on error
;-------------------------------------------------------------------------------
HookVxDServices:
	push	esi

	GetDeviceServiceOrdinal eax, _VWIN32_Get_Thread_Context
	mov	esi,HookedGetThreadContext
	VMMCall	Hook_Device_Service
	jc	near .0

	GetDeviceServiceOrdinal eax, _VWIN32_Set_Thread_Context
	mov	esi,HookedSetThreadContext
	VMMCall	Hook_Device_Service
	jc	near .1

	GetDeviceServiceOrdinal eax, Simulate_Iret
	mov	esi,HookedSimulateIret
	VMMCall	Hook_Device_Service
	jc	.2

	GetDeviceServiceOrdinal eax, VTD_Get_Date_And_Time
	mov	esi,HookedGetDateAndTime
	VMMCall	Hook_Device_Service
	jc	.3

	mov	eax,VTDAPI_Device_ID
	mov	esi,HookedVTDAPIPMAPI
	VMMCall	Hook_Device_PM_API
	jc	.4

	mov	[OrgVTDAPIPMAPI],esi

	pop	esi
	clc
	retn

.4:
	GetDeviceServiceOrdinal eax, VTD_Get_Date_And_Time
	mov	esi,HookedGetDateAndTime
	VMMCall	Unhook_Device_Service

.3:
	GetDeviceServiceOrdinal eax, Simulate_Iret
	mov	esi,HookedSimulateIret
	VMMCall	Unhook_Device_Service

.2:
	GetDeviceServiceOrdinal eax, _VWIN32_Set_Thread_Context
	mov	esi,HookedSetThreadContext
	VMMCall	Unhook_Device_Service

.1:
	GetDeviceServiceOrdinal eax, _VWIN32_Get_Thread_Context
	mov	esi,HookedGetThreadContext
	VMMCall	Unhook_Device_Service

.0:
	pop	esi
	stc
	retn


;-------------------------------------------------------------------------------
; unhook VWIN32 Get/Set Thread Context VxD APIs
;
; stc on error
;-------------------------------------------------------------------------------
UnhookVxDServices:
	push	esi

	mov	eax,VTDAPI_Device_ID
	mov	esi,[OrgVTDAPIPMAPI]
	VMMCall	Hook_Device_PM_API

	GetDeviceServiceOrdinal eax, VTD_Get_Date_And_Time
	mov	esi,HookedGetDateAndTime
	VMMCall	Unhook_Device_Service

	GetDeviceServiceOrdinal eax, Simulate_Iret
	mov	esi,HookedSimulateIret
	VMMCall	Unhook_Device_Service

	GetDeviceServiceOrdinal eax, _VWIN32_Set_Thread_Context
	mov	esi,HookedSetThreadContext
	VMMCall	Unhook_Device_Service

	GetDeviceServiceOrdinal eax, _VWIN32_Get_Thread_Context
	mov	esi,HookedGetThreadContext
	VMMCall	Unhook_Device_Service

	pop	esi
	clc
	retn


;-------------------------------------------------------------------------------
;
;-------------------------------------------------------------------------------
	jmp	short HookedGetThreadContext
	jmp	[OrgGetThreadContext]

HookedGetThreadContext:
	push	esi
	push	edi

	push	dword [esp+16]		; CONTEXT
	push	dword [esp+16]		; R0TCB, can be NULL
	call	[OrgGetThreadContext]
	pop	edi			; oh yeah, these little undoc tricks ;-)
	pop	esi

	add	edi,[TDS]
	mov	edi,[edi]		; TraceInfo
	or	edi,edi
	jz	@F

	test	dword [esi+CONTEXT.cx_ContextFlags],CONTEXT_CONTROL & ~CONTEXT_i386
	jz	@F

	cmp	dword [edi+TraceInfo.State0],byte ST0_SELFTRACEOFF
	jnz	@F

	btr	word [esi+CONTEXT.cx_EFlags],8

@@
	pop	edi
	pop	esi
	retn


segment _LDATA
	align 4
OrgGetThreadContext: dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
;
;-------------------------------------------------------------------------------
	jmp	short HookedSetThreadContext
	jmp	[OrgSetThreadContext]

HookedSetThreadContext:
	push	eax
	push	ebx
	push	esi
	push	edi
	push	ebp

	mov	eax,[esp+28]		; CONTEXT
	test	byte [eax+CONTEXT.cx_ContextFlags],CONTEXT_DEBUG_REGISTERS & ~CONTEXT_i386
	jz	@F

	btr	word [eax+CONTEXT.cx_Dr7],13	; DR7.GD
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: HookedSetThreadContext: prevented setting DR7.GD"
	debug_end

@@
	push	dword [esp+28]		; CONTEXT
	push	dword [esp+28]		; R0TCB, can be NULL
	call	[OrgSetThreadContext]
	pop	edi
	pop	esi

	mov	esi,[TDS]
	mov	esi,[esi+edi]		; TraceInfo
	or	esi,esi
	jz	.ret

;	debug_start debugflags, ICEDUMP_DEBUG_TRACE
;	mov	eax,[ebp+CRS.EFlags]
;	mov	ebx,[ebp+CRS.EIP]
;	Trace_Out "ICEDUMP: HookedSetThreadContext: EIP: #ebx, EFLAGS: #eax"
;	debug_end

	mov	ebx,[edi+TCB_VMHandle]
	mov	ebp,[edi+TCB_ClientPtr]

	call	TrReinforce

.ret:
	pop	ebp
	pop	edi
	pop	esi
	pop	ebx
	pop	eax
	retn


segment _LDATA
	align 4
OrgSetThreadContext: dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; EBP: CRS
;-------------------------------------------------------------------------------
	jmp	short HookedSimulateIret
	jmp	[OrgSimulateIret]

HookedSimulateIret:
	pushad

	VMMCall	Get_Nest_Exec_Status
	jnz	.calldown

	VMMCall	Get_Cur_Thread_Handle

	mov	esi,[TDS]
	mov	esi,[esi+edi]		; TraceInfo
	or	esi,esi
	jz	.calldown

	mov	cx,[sdata+SimIretCB+CallBack.selector]
	movzx	edx,word [sdata+SimIretCB+CallBack.offset]
	VMMCall	Simulate_Far_Jmp
	popad
	retn

.calldown:
	popad
	jmp	[OrgSimulateIret]


segment _LDATA
	align 4
OrgSimulateIret: dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
SimIret:
	call	[OrgSimulateIret]

	pushad

	VMMCall	Get_Cur_Thread_Handle
	mov	esi,[TDS]
	mov	esi,[esi+edi]		; TraceInfo
	mov	ebx,[edi+TCB_VMHandle]
	mov	ebp,[edi+TCB_ClientPtr]
	btr	word [ebp+CRS.EFlags],8
	call	TrReinforce

;	debug_start debugflags, ICEDUMP_DEBUG_TRACE
;	mov	eax,[ebp+CRS.EFlags]
;	mov	ebx,[ebp+CRS.EIP]
;	Trace_Out "ICEDUMP: SimIret: EIP: #ebx, EFLAGS: #eax"
;	debug_end

	popad
	retn


;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
	jmp	short HookedGetDateAndTime
	jmp	[OrgGetDateAndTime]

HookedGetDateAndTime:
	push	ecx
	push	esi
	VMMCall	Get_Nest_Exec_Status
	pop	esi
	pop	ecx
	jnz	.passdown

	push	edi

	VMMCall	Get_Cur_Thread_Handle

	add	edi,[TDS]
	mov	edi,[edi]		; TraceInfo
	or	edi,edi
	jz	@F

	call	[OrgGetDateAndTime]

	mov	eax,[edi+TraceInfo.TickCount]
	mov	edi,[edi+TraceInfo.TickCount+4]
	shrd	eax,edi,17			; simulate a 128 mips machine

	pop	edi
	retn

@@
	pop	edi

.passdown:
	jmp	[OrgGetDateAndTime]


segment _LDATA
	align 4
OrgGetDateAndTime: dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; EBX: VMCB
; EBP: CRS
;-------------------------------------------------------------------------------
HookedVTDAPIPMAPI:
	cmp	word [ebp+CRS.EAX],4
	jz	@F

	jmp	[OrgVTDAPIPMAPI]

@@
	push	edi

	VMMCall	Get_Cur_Thread_Handle
	mov	eax,[TDS]
	mov	eax,[eax+edi]			; eax: TraceInfo
	or	eax,eax
	jnz	@F

	pop	edi
	jmp	[OrgVTDAPIPMAPI]

@@
	mov	edi,[eax+TraceInfo.TickCount+4]
	mov	eax,[eax+TraceInfo.TickCount]
	shrd	eax,edi,17			; simulate a 128 mips machine

	mov	[ebp+CRS.EAX],ax
	shr	eax,16
	mov	[ebp+CRS.EDX],ax

	pop	edi
	retn


segment _LDATA
	align 4
OrgVTDAPIPMAPI: dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
FindVxDInfo:
	call	FindVTDAPIselTC
	jb	.ret

	call	GetVWIN32ServiceTables
	jb	.ret

	call	FindVWIN32TDS
	jb	.ret

	call	FindTDBXInfo
	jb	.ret

	call	FindVWIN32Win32APIs
	jb	.ret

	call	FindKrnl386CS

.ret:
	retn


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

	push	eax
	push	eax
	sgdt	[esp+2]
	pop	ecx
	shr	ecx,16+3
	inc	ecx			; ecx: GDT size in descriptors
	pop	edi			; edi: GDT base
	mov	ebx,edi
	VMMCall	Get_System_Time_Address	; eax: descriptor base to look for

.loop:
	cmp	word [edi],3		; limit
	jnz	.next

	cmp	word [edi+2],ax		; base
	jnz	.next

	ror	eax,16
	cmp	[edi+4],al		; base
	jnz	@F

	cmp	[edi+7],ah		; base
	jnz	@F

	sub	edi,ebx
	mov	[VTDAPI.selTC],edi

	pop	edi
	pop	ecx
	pop	ebx
	clc
	retn

@@
	ror	eax,16

.next:
	add	edi,byte 8
	loop	.loop

	Trace_Out "ICEDUMP: failed to find VTDAPI.selTC"

	pop	edi
	pop	ecx
	pop	ebx
	stc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
FindVWIN32TDS:
	push	ecx
	push	edi

	GetDeviceServiceOrdinal edi, VWIN32_TerminateApp
	movzx	edi,di
	shl	edi,2
	add	edi,[VWIN32.VxDServiceTable]
	mov	edi,[edi]

	mov	ecx,128
	mov	al,3			; add esi,[TDS]

@@
	repnz	scasb
	jecxz	@F

	cmp	byte [edi],0x35		; add esi,[TDS]
	jnz	@B

	cmp	word [edi-3],0xF78B	; mov esi,edi
	jnz	@B

	mov	edi,[edi+1]
	push	dword [edi]
	pop	dword [VWIN32.TDS]

	pop	edi
	pop	ecx
	clc
	retn

@@
	Trace_Out "ICEDUMP: failed to find VWIN32.TDS"

	pop	edi
	pop	ecx
	stc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
FindTDBXInfo:
	push	ebx
	push	ecx
	push	edx
	push	edi

	VMMCall	Get_Cur_Thread_Handle
	mov	edx,edi

.next_thread:
	VMMCall	Get_Next_Thread_Handle
	cmp	edx,edi
	jz	near .check_offsets

	mov	ebx,[VWIN32.TDS]
	mov	ecx,[ebx+edi]		; ecx: TDBX
	jecxz	.next_thread

	mov	ebx,ecx			; ebx: TDBX

; edi: R0TCB with non-zero TDBX
; ebx: TDBX
	mov	ecx,44

.next_offset:
	jecxz	.next_thread

	sub	ecx,byte 4

	mov	eax,[ebx+ecx]
	shr	eax,12
	call	IsPageCommitted
	jz	.next_offset

	cmp	eax,0x80000
	jb	.next_offset

	cmp	eax,0xC0000
	jb	.find_pdb_tdb

	cmp	[ebx+ecx],edi
	jnz	.next_offset

	cmp	dword [TDBX.R0TCB],byte -1
	jz	@F

	cmp	ecx,[TDBX.R0TCB]
	jz	.next_offset

	mov	eax,[TDBX.R0TCB]
	Trace_Out "ICEDUMP: conflicting values for TDBX.R0TCB: #ecx vs. #eax"
	jmp	.error

@@
	mov	[TDBX.R0TCB],ecx
	jmp	short .next_offset

.find_pdb_tdb:
	mov	eax,[ebx+ecx]
	mov	al,[eax]
	xor	al,[R3PID]
	jnz	.find_tdb

	cmp	dword [TDBX.pPDB],byte -1
	jz	@F

	cmp	ecx,[TDBX.pPDB]
	jz	.next_offset

	mov	eax,[TDBX.pPDB]
	Trace_Out "ICEDUMP: conflicting values for TDBX.pPDB: #ecx vs. #eax"
	jmp	.error

@@
	mov	[TDBX.pPDB],ecx
	jmp	.next_offset

.find_tdb:
	mov	eax,[ebx+ecx]
	mov	al,[eax]
	xor	al,[R3TID]
	jnz	near .next_offset

	cmp	dword [TDBX.pTDB],byte -1
	jz	@F

	cmp	ecx,[TDBX.pTDB]
	jz	near .next_offset

	mov	eax,[TDBX.pTDB]
	Trace_Out "ICEDUMP: conflicting values for TDBX.pTDB: #ecx vs. #eax"
	jmp	short .error

@@
	mov	[TDBX.pTDB],ecx
	jmp	.next_offset

.check_offsets:
	cmp	dword [TDBX.R0TCB],byte -1
	jnz	@F

	Trace_Out "ICEDUMP: failed to find TDBX.R0TCB, will use slower method for R0TCB lookup"

@@
	cmp	dword [TDBX.pPDB],byte -1
	jnz	@F

	Trace_Out "ICEDUMP: failed to find TDBX.pPDB"
	jmp	short .error

@@
	cmp	dword [TDBX.pTDB],byte -1
	jnz	@F

	Trace_Out "ICEDUMP: failed to find TDBX.pTDB"
	jmp	short .error

@@
	pop	edi
	pop	edx
	pop	ecx
	pop	ebx
	clc
	retn

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


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
FindVWIN32Win32APIs:
	push	ecx

	mov	eax,[VWIN32.W32ServiceTable]
	mov	ecx,[eax]		; W32 API count
	sub	ecx,byte 4
	add	eax,byte 4		; walk the parameter count list

.next:
	add	eax,byte 8
	cmp	[eax],byte 2
	loopnz	.next

	jnz	@F
	
	cmp	[eax+8],byte 2
	jnz	.next

	cmp	[eax+8+8],byte 5
	jnz	.next

	cmp	[eax+8+8+8],byte 5
	jnz	.next

	sub	eax,[VWIN32.W32ServiceTable]
	shr	eax,3

	mov	[VWIN32.W32_SetThreadContext],eax
	dec	eax
	mov	[VWIN32.W32_GetThreadContext],eax
	add	eax,byte 6
	mov	[VWIN32.W32_SuspendThread],eax
	inc	eax
	mov	[VWIN32.W32_ResumeThread],eax
	add	eax,byte 4
	mov	[VWIN32.W32_DIOC],eax

	pop	ecx
	clc
	retn

@@
	Trace_Out "ICEDUMP: failed to find VWIN32 Win32 APIs"

	pop	ecx
	stc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
FindKrnl386CS:
	push	ecx
	push	edi

	mov	eax, VWIN32_Device_ID
	VMMCall	Get_DDB
	jecxz	.error

	mov	ecx,[ecx+DDB_PM_API_Proc]
	jecxz	.error

	mov	edi,ecx
	mov	ecx,16
	mov	al,0x85			; jmp [table+4*eax]

@@
	repne	scasb
	jne	.error

	cmp	word [edi-3],0x24FF	; jmp [table+4*eax]
	jne	@B

	mov	edi,[edi]		; edi: jump table
	mov	edi,[edi+0x0C*4]	; get VWIN32.Set_Fault_Info
	mov	ecx,16
	mov	al,0xA3			; mov [selKrnl386CS],ax

@@
	repne	scasb
	jne	.error

	cmp	byte [edi-2],0x66	; mov [selKrnl386CS],ax
	jne	@B

	mov	edi,[edi]		; edi: selKrnl386CS
	movzx	eax,word [edi]
	mov	[selKrnl386CS],eax

	pop	edi
	pop	ecx
	clc
	retn

.error:
	pop	edi
	pop	ecx
	stc
	retn


;-------------------------------------------------------------------------------
; hook VWIN32 Get/Set Thread Context W32 APIs
;
; stc on error
;-------------------------------------------------------------------------------
HookW32Services:
	mov	eax,[VWIN32.W32_GetThreadContext]
	lea	eax,[eax*8+8]
	add	eax,[VWIN32.W32ServiceTable]
	push	dword [eax]
	pop	dword [OrgW32GetThreadContext]
	mov	dword [eax],HookedW32GetThreadContext

	mov	eax,[VWIN32.W32_SetThreadContext]
	lea	eax,[eax*8+8]
	add	eax,[VWIN32.W32ServiceTable]
	push	dword [eax]
	pop	dword [OrgW32SetThreadContext]
	mov	dword [eax],HookedW32SetThreadContext

	clc
	retn


;-------------------------------------------------------------------------------
; unhook VWIN32 Get/Set Thread Context W32 APIs
;
; stc on error
;-------------------------------------------------------------------------------
UnhookW32Services:
	mov	eax,[VWIN32.W32_GetThreadContext]
	lea	eax,[eax*8+8]
	add	eax,[VWIN32.W32ServiceTable]
	push	dword [OrgW32GetThreadContext]
	pop	dword [eax]

	mov	eax,[VWIN32.W32_SetThreadContext]
	lea	eax,[eax*8+8]
	add	eax,[VWIN32.W32ServiceTable]
	push	dword [OrgW32SetThreadContext]
	pop	dword [eax]

	clc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
GetVWIN32ServiceTables:
	push	ecx

	mov	eax,VWIN32_Device_ID
	VMMCall Get_DDB			; get Win32 Services Table pointer
	mov	eax,[ecx+DDB_Win32_Service_Table]
	mov	[VWIN32.W32ServiceTable],eax
	sub	eax,byte 1		; stc if eax == 0
	jc	@F

	mov	eax,[ecx+DDB_Service_Table_Ptr]
	mov	[VWIN32.VxDServiceTable],eax
	sub	eax,byte 1		; stc if eax == 0

@@
	pop	ecx
	retn


segment _LDATA
	align 4
VWIN32:
.VxDServiceTable:	dd 0
.W32ServiceTable:	dd 0
.W32_GetThreadContext:	dd 0
.W32_SetThreadContext:	dd 0
.W32_ResumeThread:	dd 0
.W32_SuspendThread:	dd 0
.W32_DIOC:		dd 0
.TDS:			dd 0
TDBX:
.R0TCB:			dd -1
.pPDB:			dd -1
.pTDB:			dd -1
VTDAPI:
.selTC:			dd 0
selKrnl386CS:		dd 0
OrgW32GetThreadContext:		dd 0
OrgW32SetThreadContext:		dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
;
;-------------------------------------------------------------------------------
HookedW32GetThreadContext:
	push	esi
	push	edi

	push	dword [esp+24]		; CONTEXT
	push	dword [esp+24]		; R0TCB, can be NULL
	push	dword [esp+24]
	push	dword [esp+24]
	call	[OrgW32GetThreadContext]

	mov	esi,[esp+24]

	test	dword [esi+CONTEXT.cx_ContextFlags],CONTEXT_CONTROL & ~CONTEXT_i386
	jz	@F

	mov	edi,[esp+20]
	test	edi,edi
	jnz	@F

	VMMCall	Get_Cur_Thread_Handle

@@
	add	edi,[TDS]
	mov	edi,[edi]		; TraceInfo
	or	edi,edi
	jz	@F

	cmp	dword [edi+TraceInfo.State0],byte ST0_SELFTRACEOFF
	jnz	@F

	btr	word [esi+CONTEXT.cx_EFlags],8

@@
	pop	edi
	pop	esi
	retn	16


;-------------------------------------------------------------------------------
;
;-------------------------------------------------------------------------------
HookedW32SetThreadContext:
	push	eax
	push	ebx
	push	esi
	push	edi
	push	ebp

	mov	eax,[esp+36]		; CONTEXT
	test	byte [eax+CONTEXT.cx_ContextFlags],CONTEXT_DEBUG_REGISTERS & ~CONTEXT_i386
	jz	@F

	btr	word [eax+CONTEXT.cx_Dr7],13	; DR7.GD
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: HookedW32SetThreadContext: prevented setting DR7.GD"
	debug_end

@@
	push	dword [esp+36]		; CONTEXT
	push	dword [esp+36]		; R0TCB, can be NULL
	push	dword [esp+36]
	push	dword [esp+36]
	call	[OrgW32SetThreadContext]

	mov	edi,[esp+32]		; R0TCB
	test	edi,edi
	jnz	@F

	VMMCall	Get_Cur_Thread_Handle

@@
	mov	esi,[TDS]
	mov	esi,[esi+edi]		; TraceInfo
	or	esi,esi
	jz	.ret

;	debug_start debugflags, ICEDUMP_DEBUG_TRACE
;	mov	eax,[ebp+CRS.EFlags]
;	mov	ebx,[ebp+CRS.EIP]
;	Trace_Out "ICEDUMP: HookedW32SetThreadContext: EIP: #ebx, EFLAGS: #eax"
;	debug_end

	mov	ebx,[edi+TCB_VMHandle]
	mov	ebp,[edi+TCB_ClientPtr]

	call	TrReinforce

.ret:
	pop	ebp
	pop	edi
	pop	esi
	pop	ebx
	pop	eax
	retn	16


segment _LTEXT
;-------------------------------------------------------------------------------
; hook fault handlers which eventually call win32 SEH
;
; stc on error
;-------------------------------------------------------------------------------
HookPMFaults:
	push	ecx
	push	esi
	push	edi

	mov	ecx,[HookedPMFaults.size]
	mov	edi,HookedPMFaults-PMFaultHook_size

@@
	add	edi,byte PMFaultHook_size
	movzx	eax,byte [edi+PMFaultHook.id]
	mov	esi,[edi+PMFaultHook.new]
	VMMCall	Hook_PM_Fault
	jc	@F

	loop	@B

	pop	edi
	pop	esi
	pop	ecx
	clc
	retn

@@
	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: Hook_PM_Fault failed for INT #eax"
	debug_end

	call	UnhookPMFaults

	pop	edi
	pop	esi
	pop	ecx
	stc
	retn


;-------------------------------------------------------------------------------
; unhook our fault handlers
;
; stc on error
;-------------------------------------------------------------------------------
UnhookPMFaults:
	push	ecx
	push	esi
	push	edi
	clc
	pushfd

	mov	ecx,[HookedPMFaults.size]
	mov	edi,HookedPMFaults-PMFaultHook_size

.next:
	add	edi,byte PMFaultHook_size

	cmp	dword [edi+PMFaultHook.old],byte 0
	jz	@F

	movzx	eax,byte [edi+PMFaultHook.id]
	mov	esi,[edi+PMFaultHook.new]
	VMMCall	Unhook_PM_Fault
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: Unhook_PM_Fault failed for INT #eax"
	debug_end

	or	byte [esp],1		; stc

@@
	loop	.next

	popfd
	pop	edi
	pop	esi
	pop	ecx
	retn


struc PMFaultHook
.id:	resd 1
.new:	resd 1
.old:	resd 1
endstruc

segment _LDATA
	align 4
HookedPMFaults:
.int00:	dd 0x00, HookedPMFault_INT00, 0
.int01:	dd 0x01, HookedPMFault_INT01, 0
.int03:	dd 0x03, HookedPMFault_INT03, 0
.int04:	dd 0x04, HookedPMFault_INT04, 0
.int05:	dd 0x05, HookedPMFault_INT05, 0
.int06:	dd 0x06, HookedPMFault_INT06, 0
.int0B:	dd 0x0B, HookedPMFault_INT0B, 0
.int0C:	dd 0x0C, HookedPMFault_INT0C, 0
.int0D:	dd 0x0D, HookedPMFault_INT0D, 0
.int0E:	dd 0x0E, HookedPMFault_INT0E, 0
.size:	dd ($-HookedPMFaults)/PMFaultHook_size


segment _LTEXT
;-------------------------------------------------------------------------------
; EBX: VMCB
; EBP: Client Registers
;-------------------------------------------------------------------------------
	jmp	short HookedPMFault_INT00; *MUST* assemble to EB,06 or EB,0A
	jmp	[HookedPMFaults.int00+PMFaultHook.old]

HookedPMFault_INT00:
	push	byte 0
	push	dword  [HookedPMFaults.int00+PMFaultHook.old]
	jmp	HookedPMFault_stub


;-------------------------------------------------------------------------------
	jmp	short HookedPMFault_INT03; *MUST* assemble to EB,06 or EB,0A
	jmp	[HookedPMFaults.int03+PMFaultHook.old]

HookedPMFault_INT03:
	push	byte 3
	push	dword  [HookedPMFaults.int03+PMFaultHook.old]
	jmp	short HookedPMFault_stub


;-------------------------------------------------------------------------------
	jmp	short HookedPMFault_INT04; *MUST* assemble to EB,06 or EB,0A
	jmp	[HookedPMFaults.int04+PMFaultHook.old]

HookedPMFault_INT04:
	push	byte 4
	push	dword  [HookedPMFaults.int04+PMFaultHook.old]
	jmp	short HookedPMFault_stub


;-------------------------------------------------------------------------------
	jmp	short HookedPMFault_INT05; *MUST* assemble to EB,06 or EB,0A
	jmp	[HookedPMFaults.int05+PMFaultHook.old]

HookedPMFault_INT05:
	push	byte 5
	push	dword  [HookedPMFaults.int05+PMFaultHook.old]
	jmp	short HookedPMFault_stub


;-------------------------------------------------------------------------------
	jmp	short HookedPMFault_INT06; *MUST* assemble to EB,06 or EB,0A
	jmp	[HookedPMFaults.int06+PMFaultHook.old]

HookedPMFault_INT06:
	push	byte 6
	push	dword  [HookedPMFaults.int06+PMFaultHook.old]
	jmp	short HookedPMFault_stub


;-------------------------------------------------------------------------------
	jmp	short HookedPMFault_INT0B; *MUST* assemble to EB,06 or EB,0A
	jmp	[HookedPMFaults.int0B+PMFaultHook.old]

HookedPMFault_INT0B:
	push	byte 0x0B
	push	dword  [HookedPMFaults.int0B+PMFaultHook.old]
	jmp	short HookedPMFault_stub


;-------------------------------------------------------------------------------
	jmp	short HookedPMFault_INT0C; *MUST* assemble to EB,06 or EB,0A
	jmp	[HookedPMFaults.int0C+PMFaultHook.old]

HookedPMFault_INT0C:
	push	byte 0x0C
	push	dword  [HookedPMFaults.int0C+PMFaultHook.old]
	jmp	short HookedPMFault_stub


;-------------------------------------------------------------------------------
	jmp	short HookedPMFault_INT0D; *MUST* assemble to EB,06 or EB,0A
	jmp	[HookedPMFaults.int0D+PMFaultHook.old]

HookedPMFault_INT0D:
	push	byte 0x0D
	push	dword  [HookedPMFaults.int0D+PMFaultHook.old]
	jmp	short HookedPMFault_stub


;-------------------------------------------------------------------------------
	jmp	short HookedPMFault_INT0E; *MUST* assemble to EB,06 or EB,0A
	jmp	[HookedPMFaults.int0E+PMFaultHook.old]

HookedPMFault_INT0E:
	push	byte 0x0E
	push	dword  [HookedPMFaults.int0E+PMFaultHook.old]


;-------------------------------------------------------------------------------
HookedPMFault_stub:
	push	eax
	push	ecx
	push	edx
	push	esi
	push	edi

	VMMCall	Get_Nest_Exec_Status
	jnz	@F

	VMMCall	Get_Cur_Thread_Handle
	mov	esi,[TDS]
	mov	esi,[esi+edi]		; TraceInfo
	cmp	esi,byte 0
	jnz	.handle

@@
	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	pop	eax
	retn	4			; chain to old

.handle:
	imul	eax,[esi+TraceInfo.State0],byte ST1_SIZE*IN_SIZE
	imul	edx,[esi+TraceInfo.State1],byte IN_SIZE
	lea	eax,[eax+edx+IN_EXCEPTION_IN]
	call	[Transitions+4*eax]

	cmp	eax,byte PASSDOWN
	jz	.passdown

	cmp	eax,byte DONTPASS
	jz	.dontpass

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: INTxxHook: error tracing thread, giving up... R0TCB: #edi"
	debug_end

	cmp	[esi+TraceInfo.State0],byte ST0_SELFTRACEON
	jz	@F

	call	StopTracing

@@
	call	TracerFree

	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	pop	eax
	retn	4			; chain to old

.passdown:
	push	esi
	push	edi
	mov	eax,[esp+24]
	mov	ecx,[esp+20]
	mov	edx,[esp+16]
	mov	esi,[esp+12]
	mov	edi,[esp+8]

	call	[esp+28]		; chain to old
	pop	edi
	pop	esi

.dontpass:
	imul	eax,[esi+TraceInfo.State0],byte ST1_SIZE*IN_SIZE
	imul	edx,[esi+TraceInfo.State1],byte IN_SIZE
	lea	eax,[eax+edx+IN_EXCEPTION_OUT]
	call	[Transitions+4*eax]

	call	RecordAndLog

	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	pop	eax
	add	esp,byte 8
	retn


;-------------------------------------------------------------------------------
; this mess simulates a HOOK_PROC
;-------------------------------------------------------------------------------
	jmp	short HookedPMFault_INT01; *MUST* assemble to EB,06 or EB,0A
	jmp	[HookedPMFaults.int01+PMFaultHook.old]

;-------------------------------------------------------------------------------
; the heart of the tracer engine
;
; EBX: VMCB
; EBP: Client Registers
;
; 1. detect traced thread
; 2. check EIP for being in the specified range
; 3. emulate certain instructions
; 4. handle SEH
;-------------------------------------------------------------------------------
HookedPMFault_INT01:
	push	eax
	push	ecx
	push	edx
	push	esi
	push	edi

	VMMCall	Get_Nest_Exec_Status
	jnz	@F

	VMMCall	Get_Cur_Thread_Handle
	mov	esi,[TDS]
	mov	esi,[esi+edi]		; TraceInfo
	cmp	esi,byte 0
	jnz	.handle

@@
	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	pop	eax
	jmp	[HookedPMFaults.int01+PMFaultHook.old]

.handle:
; send first input to the state machine
	imul	eax,[esi+TraceInfo.State0],byte ST1_SIZE*IN_SIZE
	imul	edx,[esi+TraceInfo.State1],byte IN_SIZE

	mov	ecx,dr6
	test	ecx,DR6_BS_MASK
	jnz	@F

	lea	eax,[eax+edx+IN_EXCEPTION_IN]
	jmp	short .send_first

@@
	test	ecx,DR6_B0_MASK | DR6_B1_MASK | DR6_B2_MASK | DR6_B3_MASK | DR6_BD_MASK
	jnz	@F

	lea	eax,[eax+edx+IN_DEBUG_BS]
	jmp	short .send_first

@@
	lea	eax,[eax+edx+IN_DEBUG_BS_BX]

.send_first:
	call	[Transitions+4*eax]

	cmp	eax,byte PASSDOWN
	jz	.passdown

	cmp	eax,byte DONTPASS
	jz	.dontpass

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: INT01Hook: error tracing thread, giving up... R0TCB: #edi"
	debug_end

	cmp	[esi+TraceInfo.State0],byte ST0_SELFTRACEON
	jz	@F

	call	StopTracing

@@
	call	TracerFree

	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	pop	eax
	jmp	[HookedPMFaults.int01+PMFaultHook.old]

.passdown:
	push	esi
	push	edi
	mov	eax,[esp+24]
	mov	ecx,[esp+20]
	mov	edx,[esp+16]
	mov	esi,[esp+12]
	mov	edi,[esp+8]

	call	[HookedPMFaults.int01+PMFaultHook.old]
	pop	edi
	pop	esi

.dontpass:
	imul	eax,[esi+TraceInfo.State0],byte ST1_SIZE*IN_SIZE
	imul	edx,[esi+TraceInfo.State1],byte IN_SIZE
	lea	eax,[eax+edx+IN_EXCEPTION_OUT]
	call	[Transitions+4*eax]

	call	RecordAndLog

	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	pop	eax
	retn


;-------------------------------------------------------------------------------
; EDI: R0TCB
; ESI: R0TCB->TDS (TranceInfo)
; EBP: Client Registers
;-------------------------------------------------------------------------------
RecordAndLog:
	push	eax
	push	ebx
	push	ecx
	push	edx

	cmp	byte [OT.OAll],'D'
	jz	@F

	VMMCall	Get_Cur_VM_Handle
	call	GetLinearCSEIP
	jc	.record

	cmp	eax,0xC0000000
	jb	.log
	jmp	short .record

@@
	cmp	byte [OT.OBranch],'D'
	jz	.record

	VMMCall	Get_Cur_VM_Handle
	call	GetLinearCSEIP
	jc	.record

	cmp	eax,0xC0000000
	jae	.record

	call	SkipPrefixes
	jc	.record

	call	CanChangeFlow
	jc	.record

.log:
	mov	eax,[ebp+CRS.EIP]
	movzx	ecx,word [ebp+CRS.CS]
	mov	ebx,[ebp+CRS.ESP]
	movzx	edx,word [ebp+CRS.SS]
	Trace_Out "ICEDUMP: LOG: CS:EIP: #cx:#eax, SS:ESP: #dx:#ebx, R0TCB: #edi"

.record:
	push	dword [ebp+CRS.EIP]
	pop	dword [esi+TraceInfo.lastEIP]

	movzx	eax,word [ebp+CRS.CS]
	push	eax
	pop	dword [esi+TraceInfo.lastCS]

	push	dword [ebp+CRS.ESP]
	pop	dword [esi+TraceInfo.lastESP]

	movzx	eax,word [ebp+CRS.SS]
	push	eax
	pop	dword [esi+TraceInfo.lastSS]

	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
	retn


;-------------------------------------------------------------------------------
; eax: client EIP, after known prefixes
; ecx: max length for real opcode (considering the 15 byte limit)
;
; stc: if instruction cannot change execution flow
;-------------------------------------------------------------------------------
CanChangeFlow:
	push	esi
	mov	esi,eax

.protect_start:
	movzx	eax,byte [esi]
	bt	[FlowChangersXXno],eax
	jc	.no_change

	bt	[FlowChangersXXyes],eax
	jnc	.change

	dec	ecx
	jecxz	.no_change

	movzx	eax,byte [esi+1]
.protect_end:

	cmp	al,0x0F
	jnz	@F

	bt	[FlowChangers0F],eax
	jc	.no_change
	jmp	short .change

@@
	cmp	al,0xFF
	jnz	.no_change

	bt	[FlowChangers0F],eax
	jc	.no_change

.change:
.EH:
	pop	esi
	clc
	retn

.no_change:
	pop	esi
	stc
	retn


segment _LDATA
	align 4
FlowChangersXXno:
	  ;11111111111111110000000000000000
	  ;FEDCBA9876543210FEDCBA9876543210
	dd 11111111111111110111111111111111b ; 1F-00
	dd 11111111111111111111111111111111b ; 3F-20
	dd 11111111111111111111111111111111b ; 5F-40
	dd 00000000000000001111111111111111b ; 7F-60
	dd 11111011111111111111111111111111b ; 9F-80
	dd 11111111111111111111111111111111b ; BF-A0
	dd 11111111111111110000001111110011b ; DF-C0
	dd 01111111111111011111000011110000b ; FF-E0
	dd 0

FlowChangersXXyes:
	  ;11111111111111110000000000000000
	  ;FEDCBA9876543210FEDCBA9876543210
	dd 11111111111111111111111111111111b ; 1F-00
	dd 11111111111111111111111111111111b ; 3F-20
	dd 11111111111111111111111111111111b ; 5F-40
	dd 00000000000000001111111111111111b ; 7F-60
	dd 11111011111111111111111111111111b ; 9F-80
	dd 11111111111111111111111111111111b ; BF-A0
	dd 11111111111111110000001111110011b ; DF-C0
	dd 11111111111111011111000011110000b ; FF-E0
	dd 0

FlowChangers0F:
	  ;11111111111111110000000000000000
	  ;FEDCBA9876543210FEDCBA9876543210
	dd 11111111111111111110001101001111b ; 1F-00
	dd 00000000000011111111111101011111b ; 3F-20
	dd 11110011111111111111111111111111b ; 5F-40
	dd 11000000111111111100111111111111b ; 7F-60
	dd 11111111111111110000000000000000b ; 9F-80
	dd 11111100111111111111111111111111b ; BF-A0
	dd 11111111101011101111111111110111b ; DF-C0
	dd 01110111111011101111111110111111b ; FF-E0
	dd 0

FlowChangersFF:
	  ;11111111111111110000000000000000
	  ;FEDCBA9876543210FEDCBA9876543210
	dd 00000000000000001111111111111111b ; 1F-00
	dd 11111111111111110000000000000000b ; 3F-20
	dd 00000000000000001111111111111111b ; 5F-40
	dd 11111111111111110000000000000000b ; 7F-60
	dd 00000000000000001111111111111111b ; 9F-80
	dd 11111111111111110000000000000000b ; BF-A0
	dd 00000000000000001111111111111111b ; DF-C0
	dd 11111111111111110000000000000000b ; FF-E0
	dd 0


segment _LDATA
init_data_begin ICEDUMP_TRACER
init_data_item	InstallExceptionHandlers,	RemoveExceptionHandlers
init_data_item	AllocateThreadDataSlot,		FreeThreadDataSlot
init_data_item	FindVxDInfo
init_data_item	HookVxDServices,		UnhookVxDServices
init_data_item	HookW32Services,		UnhookW32Services
init_data_item	HookPMFaults,			UnhookPMFaults
init_data_item	HookV86Ints,			UnhookV86Ints
init_data_end


segment _LTEXT
;-------------------------------------------------------------------------------
; 1. install exception handlers
; 2. allocate TDS
; 3. init TDS in all existing threads
; 4. hook VWIN32's Get/Set Thread Context related APIs (VxD/W32)
; 5. hook PM faults
;-------------------------------------------------------------------------------
TracerSysDynamicDeviceInit:
	init_construct ICEDUMP_TRACER
	jc	.error

	clc
	retn

.error:
	call	TracerSysDynamicDeviceExit

	stc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
InstallExceptionHandlers:
	push	esi

	mov	esi,EHS_SkipPrefixes
	VMMCall	Install_Exception_Handler
	jc	@F

	mov	esi,EHS_TrReinforce
	VMMCall	Install_Exception_Handler
	jc	@F

	mov	esi,EHS_CanChangeFlow
	VMMCall	Install_Exception_Handler
	jc	@F

	mov	esi,EHS_GetLenAndAddr
	VMMCall	Install_Exception_Handler
	jc	@F

	pop	esi
	clc
	retn

@@
	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: InstallExceptionHandlers: failed to install exception handler"
	debug_end

	call	RemoveExceptionHandlers

	pop	esi
	stc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
RemoveExceptionHandlers:
	push	esi

	mov	esi,EHS_SkipPrefixes
	cmp	dword [esi+EHS_Reserved],byte 0
	jz	@F

	VMMCall	Remove_Exception_Handler
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: RemoveExceptionHandlers: failed for SkipPrefixes"
	debug_end

@@
	mov	esi,EHS_TrReinforce
	cmp	dword [esi+EHS_Reserved],byte 0
	jz	@F

	VMMCall	Remove_Exception_Handler
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: RemoveExceptionHandlers: failed for TrReinforce"
	debug_end

@@
	mov	esi,EHS_CanChangeFlow
	cmp	dword [esi+EHS_Reserved],byte 0
	jz	@F

	VMMCall	Remove_Exception_Handler
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: RemoveExceptionHandlers: failed for CanChangeFlow"
	debug_end

@@
	mov	esi,EHS_GetLenAndAddr
	cmp	dword [esi+EHS_Reserved],byte 0
	jz	@F

	VMMCall	Remove_Exception_Handler
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: RemoveExceptionHandlers: failed for GetLenAndAddr"
	debug_end

@@
	pop	esi
	clc
	retn


segment _LDATA
	align 4
EHS_SkipPrefixes:
istruc Exception_Handler_Struc
	at EHS_Reserved,	dd 0
	at EHS_Start_EIP,	dd SkipPrefixes.protect_start
	at EHS_End_EIP,		dd SkipPrefixes.protect_end
	at EHS_Handler,		dd SkipPrefixes.EH
iend

EHS_TrReinforce:
istruc Exception_Handler_Struc
	at EHS_Reserved,	dd 0
	at EHS_Start_EIP,	dd TrReinforce.protect_start
	at EHS_End_EIP,		dd TrReinforce.protect_end
	at EHS_Handler,		dd TrReinforce.EH
iend

EHS_CanChangeFlow:
istruc Exception_Handler_Struc
	at EHS_Reserved,	dd 0
	at EHS_Start_EIP,	dd CanChangeFlow.protect_start
	at EHS_End_EIP,		dd CanChangeFlow.protect_end
	at EHS_Handler,		dd CanChangeFlow.EH
iend

EHS_GetLenAndAddr:
istruc Exception_Handler_Struc
	at EHS_Reserved,	dd 0
	at EHS_Start_EIP,	dd GetLenAndAddr.protect_start
	at EHS_End_EIP,		dd GetLenAndAddr.protect_end
	at EHS_Handler,		dd GetLenAndAddr.EH
iend


segment _LTEXT
;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
AllocateThreadDataSlot:
	push	ebx
	push	ecx
	push	edx
	push	edi

	VMMCall _AllocateThreadDataSlot
	or      eax,eax
	jz	.error

	mov	[TDS],eax
	VMMCall	Get_Sys_VM_Handle
	VMMCall	Get_Initial_Thread_Handle
	mov     ebx,edi
	mov     ecx,[TDS]
	xor     eax,eax

@@
	mov	[ecx+edi],eax
	VMMCall	Get_Next_Thread_Handle
	cmp	ebx,edi
	jnz	@B

	pop	edi
	pop	edx
	pop	ecx
	pop	ebx
	clc
	retn

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


segment _LDATA
	align 4
TDS:			dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
FreeThreadDataSlot:
	push	ebx
	push	ecx
	push	edx
	push	edi

	VMMCall	Get_Sys_VM_Handle
	VMMCall	Get_Initial_Thread_Handle
	mov     ebx,edi
	mov     ecx,[TDS]

.loop:
	mov	eax,[ecx+edi]
	or	eax,eax
	jz	.skip

	cmp	[eax+TraceInfo.State0],byte ST0_SELFTRACEON
	jz	@F

	call	StopTracing

@@
	call	TracerFree

.skip:
	VMMCall	Get_Next_Thread_Handle
	cmp	ebx,edi
	jnz	.loop

	VMMCall	_FreeThreadDataSlot, dword [TDS]

	pop	edi
	pop	edx
	pop	ecx
	pop	ebx
	clc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
HookV86Ints:
	push	esi

	mov	eax,0x21
	mov	esi,HookedV86Int21
	VMMCall	Hook_V86_Int_Chain

	mov	eax,0x1A
	mov	esi,HookedV86Int1A
	VMMCall	Hook_V86_Int_Chain

	pop	esi
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
UnhookV86Ints:
	push	esi

	mov	eax,0x1A
	mov	esi,HookedV86Int1A
	VMMCall	Unhook_V86_Int_Chain

	mov	eax,0x21
	mov	esi,HookedV86Int21
	VMMCall	Unhook_V86_Int_Chain

	pop	esi
	clc
	retn


;-------------------------------------------------------------------------------
; EBP: CRS
;
; stc if not handled
;-------------------------------------------------------------------------------
	jmp	short HookedV86Int21
	jmp	[OrgV86Int21]

HookedV86Int21:
	cmp	byte [ebp+CRS.EAX+1],0x2C
	jz	@F

	stc
	retn

@@
	push	eax
	push	edx
	push	edi

	VMMCall	Get_Cur_Thread_Handle
	mov	eax,[TDS]
	mov	eax,[eax+edi]			; eax: TraceInfo
	or	eax,eax
	jnz	@F

	pop	edi
	pop	edx
	pop	eax
	stc
	retn

@@
	mov	edx,[eax+TraceInfo.TickCount+4]
	mov	eax,[eax+TraceInfo.TickCount]
	shrd	eax,edx,17			; simulate a 128 mips machine
	shr	edx,17

	mov	edi,10
	div	edi				; tickcount in 10 msec

	xor	edx,edx
	mov	edi,100
	div	edi				; tickcount in 1 sec
	mov	[ebp+CRS.EDX],dl

	xor	edx,edx
	mov	edi,60
	div	edi				; tickcount in 1 min
	mov	[ebp+CRS.EDX+1],dl

	xor	edx,edx
	mov	edi,60
	div	edi				; tickcount in 1 hour
	mov	[ebp+CRS.ECX],dl
	mov	[ebp+CRS.ECX+1],al

	pop	edi
	pop	edx
	pop	eax
	clc
	retn


segment _LDATA
	align 4
OrgV86Int21:	dd 0


;-------------------------------------------------------------------------------
; EBP: CRS
;
; stc if not handled
;-------------------------------------------------------------------------------
	jmp	short HookedV86Int1A
	jmp	[OrgV86Int1A]

HookedV86Int1A:
	cmp	byte [ebp+CRS.EAX+1],0x02
	jz	@F

	stc
	retn

@@
	push	eax
	push	edx
	push	edi

	VMMCall	Get_Cur_Thread_Handle
	mov	eax,[TDS]
	mov	eax,[eax+edi]			; eax: TraceInfo
	or	eax,eax
	jnz	@F

	pop	edi
	pop	edx
	pop	eax
	stc
	retn

@@
	mov	edx,[eax+TraceInfo.TickCount+4]
	mov	eax,[eax+TraceInfo.TickCount]
	shrd	eax,edx,17			; simulate a 128 mips machine
	shr	edx,17

	mov	edi,1000
	div	edi				; tickcount in 1 sec

	mov	byte [ebp+CRS.EDX],0

	xor	edx,edx
	mov	edi,60
	div	edi				; tickcount in 1 min

	push	eax
	mov	al,dl
	aam
	shl	ah,4
	or	al,ah
	mov	[ebp+CRS.EDX+1],al
	pop	eax

	xor	edx,edx
	mov	edi,60
	div	edi				; tickcount in 1 hour

	push	eax
	mov	al,dl
	aam
	shl	ah,4
	or	al,ah
	mov	[ebp+CRS.ECX],al
	pop	eax

	aam
	shl	ah,4
	or	al,ah
	mov	[ebp+CRS.ECX+1],al

	and	byte [ebp+CRS.EFlags],0xFE	; clc

	pop	edi
	pop	edx
	pop	eax
	clc
	retn


segment _LDATA
	align 4
OrgV86Int1A:	dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; clean up all tracer structures
;-------------------------------------------------------------------------------
TracerSysDynamicDeviceExit:
	init_destruct ICEDUMP_TRACER
	clc
	retn


;-------------------------------------------------------------------------------
; init TDS in new thread
; check if it belongs to a process one of whose threads is already being traced
; if so and multithread tracing is enabled, trace this guy as well
;
; EDI: R0TCB
;-------------------------------------------------------------------------------
TracerThreadInit:
	pushad

	mov	edx,[TDS]
	and	dword [edx+edi],byte 0

	mov	ebp,[edi+TCB_ClientPtr]

	call	IsThreadWin32
	jc	.ret

	mov	ecx,[VWIN32.TDS]
	mov	ecx,[ecx+edi]		; ecx: TDBX
	jecxz	.ret

	mov	eax,ecx
	shr	eax,12
	call	IsPageCommitted
	jz	.ret

	add	ecx,[TDBX.pPDB]
	mov	ecx,[ecx]		; ecx: PDB
	jecxz	.ret

	mov	eax,ecx
	shr	eax,12
	call	IsPageCommitted
	jz	.ret

	mov	eax,[R3PID]
	cmp	[ecx],al
	jnz	.ret

	cmp	byte [OT.OTraceSameProcess],'D'
	jz	.check_parent

	call	ShallTraceIt
	jc	.check_parent

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: ThreadInit: detected new thread in current process, R0TCB: #edi"
	debug_end

	jmp	short .trace

.ret:
	popad
	clc
	retn

.check_parent:
	cmp	byte [OT.OTraceChildProcess],'D'
	jz	.ret

	mov	ecx,[ecx+0x48]		; ecx: parent PDB
	jecxz	.ret

	mov	eax,ecx
	shr	eax,12
	call	IsPageCommitted
	jz	.ret

	mov	eax,[R3PID]
	cmp	[ecx],al
	jnz	.ret

	call	ShallTraceIt
	jc	.ret

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: ThreadInit: detected new thread in child process, R0TCB: #edi"
	debug_end

.trace:
	mov	ebp,[edi+TCB_ClientPtr]
	mov	ebx,[edi+TCB_VMHandle]

	call	TracerAlloc
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: ThreadInit: failed to allocate TraceInfo, R0TCB: #edi"
	debug_end

	jmp	short .ret

@@
	push	edi
	mov	esi,[edx+esi]
	mov	edi,[edx+edi]
	mov	ecx,TraceInfo_size
	rep	movsb
	pop	edi

	mov	esi,[TDS]
	mov	esi,[esi+edi]

	cmp	byte [OT.OBreaknew],'D'
	jz	@F

	mov	dword [esi+TraceInfo.EIPlow],0x400000
	mov	dword [esi+TraceInfo.EIPhigh],0x80000000

@@
	call	TrReinforce

	popad
	clc
	retn


;-------------------------------------------------------------------------------
; decide if the new thread should be traced or not
;
; ECX: PDB
; EDX: TDS
;
; ESI: R0TCB of an already existing thread or null
; clc: trace it
;-------------------------------------------------------------------------------
ShallTraceIt:
	push	ecx

	mov	ecx,[ecx+0x50]		; get ThreadList
	jecxz	.ret

	mov	ecx,[ecx]		; get Thread_Ref pointer

@@
	jecxz	.ret

	mov	esi,[ecx+8]		; get Thread Database
	mov	ecx,[ecx]		; prefetch next Thread_Ref
	or	esi,esi
	jz	@B

	mov	eax,esi
	shr	eax,12
	call	IsPageCommitted
	jz	@B

	mov	eax,[R3TID]
	cmp	[esi],al
	jnz	@B

	add	esi,[R3TCB.TDBX]	; [esi]: TDBX
	mov	esi,[esi]		; esi: TDBX
	or	esi,esi
	jz	@B

	mov	eax,esi
	shr	eax,12
	call	IsPageCommitted
	jz	@B

	cmp	dword [TDBX.R0TCB],byte -1
	jnz	.fast_lane

	call	FindR0TCB
	or	esi,esi
	jz	@B
	jmp	short .check_tds

.fast_lane:
	add	esi,[TDBX.R0TCB]	; [esi]: R0TCB
	mov	esi,[esi]		; esi: R0TCB

.check_tds:
	cmp	dword [edx+esi],byte 0
	jz	@B

	pop	ecx
	clc
	retn

.ret:
	xor	esi,esi
	pop	ecx
	stc
	retn


;-------------------------------------------------------------------------------
; ESI: TDBX
;
; ESI: R0TCB or null
;-------------------------------------------------------------------------------
FindR0TCB:
	push	ebx
	push	ecx
	push	edi

	VMMCall	Get_Sys_VM_Handle
	VMMCall	Get_Initial_Thread_Handle
	mov     ebx,edi
	mov     ecx,[VWIN32.TDS]

.next_thread:
	cmp	[ecx+edi],esi
	jnz	@F

	mov	esi,edi
	pop	edi
	pop	ecx
	pop	ebx
	retn

@@
	VMMCall	Get_Next_Thread_Handle
	cmp	ebx,edi
	jnz	.next_thread

	pop	edi
	pop	ecx
	pop	ebx
	xor	esi,esi
	retn


;-------------------------------------------------------------------------------
; stop tracing thread
;
; EDI: R0TCB
;-------------------------------------------------------------------------------
TracerThreadNotExecuteable:
	mov     eax,[TDS]
	mov	eax,[eax+edi]		; TraceInfo
	or	eax,eax
	jz	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	push	edx
	mov	edx,[eax+TraceInfo.TickCount+4]
	mov	eax,[eax+TraceInfo.TickCount]
	Trace_Out "ICEDUMP: thread died, instruction count: #edx:#eax, R0TCB: #edi"
	pop	edx
	debug_end

	call	TracerFree

@@
	clc
	retn


;-------------------------------------------------------------------------------
; ebx: VMCB
;
; stc on error
;-------------------------------------------------------------------------------
TracerVmInit:
	VMMCall	Get_Initial_Thread_Handle
	call	TracerThreadInit
	retn


;-------------------------------------------------------------------------------
; ebx: VMCB
;
; stc on error
;-------------------------------------------------------------------------------
TracerVmNotExecuteable:
	VMMCall	Get_Initial_Thread_Handle
	call	TracerThreadNotExecuteable
	retn


;-------------------------------------------------------------------------------
; alloc tracer's structure
;
; EDI: R0TCB
;
; stc on error
;-------------------------------------------------------------------------------
TracerAlloc:
	mov     eax,[TDS]
	mov	eax,[edi+eax]
	or	eax,eax
	jz	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: thread is already being traced, R0TCB: #edi"
	debug_end

	stc
	retn

@@
	push	ecx
	push	edx
	VMMCall	_HeapAllocate, dword TraceInfo_size, byte HEAPZEROINIT
	pop	edx
	pop	ecx
	or	eax,eax
	jnz	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: failed to allocate TraceInfo for thread, R0TCB: #edi"
	debug_end

	stc
	retn

@@
	push	eax
	mov     eax,[TDS]
	add	eax,edi
	pop	dword [eax]

; set up fake IDT: int32 gates to a simple iretd
	push	ebx
	push	ecx
	push	edx

	mov	ecx,0x60
	mov	eax,[eax]		; TraceInfo
	mov	ebx,0x00280000

@@
	lea	edx,[eax+8*ecx+TraceInfo.IRETD-16]
	lea	edx,[edx+8*ecx]
	mov	byte [edx],0xCF
	mov	bx,dx
	mov	dx,0x8E00		; int32, dpl=0
	
	mov	[eax+8*ecx-8+TraceInfo.IDT],ebx
	mov	[eax+8*ecx-8+TraceInfo.IDT+4],edx
	loop	@B

; special handling of int 00/01/02 -> 02/00/01
	lea	edx,[eax+TraceInfo.IRETD+16]
	mov	bx,dx
	mov	dx,0x8E00		; int32, dpl=0
	
	mov	[eax+TraceInfo.IDT],ebx
	mov	[eax+TraceInfo.IDT+4],edx

	lea	edx,[eax+TraceInfo.IRETD+2*16]
	mov	bx,dx
	mov	dx,0x8E00		; int32, dpl=0
	
	mov	[eax+8+TraceInfo.IDT],ebx
	mov	[eax+8+TraceInfo.IDT+4],edx

	lea	edx,[eax+TraceInfo.IRETD]
	mov	bx,dx
	mov	dx,0x8E00		; int32, dpl=0
	
	mov	[eax+2*8+TraceInfo.IDT],ebx
	mov	[eax+2*8+TraceInfo.IDT+4],edx

	pop	edx
	pop	ecx
	pop	ebx

	clc
	retn


;-------------------------------------------------------------------------------
; free tracer's structure if thread was traced
;
; EDI: R0TCB
;-------------------------------------------------------------------------------
TracerFree:
	push	ecx
	push	edx

	mov     ecx,[TDS]
	xor	eax,eax
	xchg	eax,[edi+ecx]
	or	eax,eax
	jz	@F

	VMMCall	_HeapFree, eax, byte 0

@@
	pop	edx
	pop	ecx
	clc
	retn


;-------------------------------------------------------------------------------
; try to set EFLAGS.T in thread's context
;
; EDI: R0TCB
;-------------------------------------------------------------------------------
StartTracing:
	push	ebp

	mov	ebp,[edi+TCB_ClientPtr]
	bts	word [ebp+CRS.EFlags],8		; set single step flag
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: StartTracing: EFLAGS.T was already set, R0TCB: #edi"
	debug_end

@@
	pop	ebp
	clc
	retn


;-------------------------------------------------------------------------------
; try to reset EFLAGS.T in thread's context
;
; EDI: R0TCB
;-------------------------------------------------------------------------------
StopTracing:
	push	ebp

	mov	ebp,[edi+TCB_ClientPtr]
	btr	word [ebp+CRS.EFlags],8		; reset single step flag
	jc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: StopTracing: EFLAGS.T was not set, R0TCB: #edi"
	debug_end

@@
	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: stopped tracing thread, R0TCB: #edi"
	debug_end

	pop	ebp
	clc
	retn


;-------------------------------------------------------------------------------
; eax: client EIP, skip over known prefixes
;
; eax: first non-prefix byte (real opcode)
; ecx: max length for real opcode (considering the 15 byte limit)
;-------------------------------------------------------------------------------
SkipPrefixes:
	push	esi
	mov	esi,eax

	mov	ecx,15		; max instruction length

.loop:

.protect_start:
	movzx	eax,byte [esi]
.protect_end:

	bt	[Prefixes],eax
	jc	@F

	mov	eax,esi
	pop	esi
	clc
	retn

@@
	inc	esi
	loop	.loop

.EH:
	mov	eax,esi
	pop	esi
	stc
	retn


;segment _LDATA
; prefixes:	db 0x26,0x2E,0x36,0x3E,0x64,0x65,0x66,0x67,0xF0,0xF2,0xF3
	align 4
Prefixes: ;11111111111111110000000000000000
	  ;FEDCBA9876543210FEDCBA9876543210
	dd 00000000000000000000000000000000b ; 1F-00
	dd 01000000010000000100000001000000b ; 3F-20
	dd 00000000000000000000000000000000b ; 5F-40
	dd 00000000000000000000000011110000b ; 7F-60
	dd 00000000000000000000000000000000b ; 9F-80
	dd 00000000000000000000000000000000b ; BF-A0
	dd 00000000000000000000000000000000b ; DF-C0
	dd 00000000000011010000000000000000b ; FF-E0
	dd 0


segment _LTEXT

;-------------------------------------------------------------------------------
; TRACEX <EIP low> [<EIP high>]
;-------------------------------------------------------------------------------
Parse_TraceX:
	mov	edi,Error_V86
	mov	ebp,[dClient_EFLAGS]
	test	byte [ebp+2],2			; is client in V86 mode?
	jnz	near Parser.errorMsg

	mov	edi,Error_PM16
	mov	ebp,[dClient_CS]
	lar	eax,[ebp]			; is client 32 bit?
	bt	eax,22
	jnc	near Parser.errorMsg

	mov	edi,Error_BadEIPlow
	call	ParseExpression			; parse <EIP low>
	jb	near Parser.errorMsg

	mov	[.EIPlow],eax

	call	[pSkipWhiteSpace]		; skip to <EIP high>
	mov	eax,[.EIPlow]
	jz	@F

	mov	edi,Error_BadEIPhigh
	call	ParseExpression			; parse <EIP high>
	jb	near Parser.errorMsg

@@
	mov	[.EIPhigh],eax

; save client EAX/CS/EIP
	mov	ebp,[dClient_EAX]
	push	dword [ebp]
	pop	dword [.EAX]

	mov	ebp,[dClient_CS]
	push	dword [ebp]
	pop	dword [.CS]

	mov	ebp,[dClient_EIP]
	push	dword [ebp]
	pop	dword [.EIP]

; set up registers for service
	push	byte SERVICE_TRACEX
	mov	ebp,[dClient_EAX]
	pop	dword [ebp]

	call	SetCBX

	mov	ebp,[fExecuteMoreCommands]	; set internal Winice flag to 0
	mov	byte [ebp],0

	popad
	retn


segment _LDATA
	align 4
.EAX:		dd 0
.CS:		dd 0
.EIP:		dd 0
.EIPlow:	dd 0
.EIPhigh:	dd 0


segment _LTEXT

Service_TraceX:
	VMMCall	Get_Cur_VM_Handle
	VMMCall	Get_Cur_Thread_Handle
	call	TracerFree
	call	TracerAlloc
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: TRACEX failed to allocate TraceInfo"
	debug_end

	jmp	short .ret

@@
	mov	esi,[TDS]
	mov	esi,[esi+edi]			; TraceInfo

	push	dword [Parse_TraceX.EIPlow]
	pop	dword [esi+TraceInfo.EIPlow]

	push	dword [Parse_TraceX.EIPhigh]
	pop	dword [esi+TraceInfo.EIPhigh]

	push	dword [Parse_TraceX.EAX]
	pop	dword [ebp+CRS.EAX]

	push	dword [Parse_TraceX.CS]
	pop	dword [ebp+CRS.CS]

	push	dword [Parse_TraceX.EIP]
	pop	dword [ebp+CRS.EIP]

	call	TrReinforce

.ret:
	popfd
	popad
	retn


;-------------------------------------------------------------------------------
; TRACE [<R0TCB> [<EIP low> [<EIP high>]]]
;-------------------------------------------------------------------------------
Parse_Trace:
	push	byte SERVICE_TRACE
	mov	ebp,[dClient_EAX]
	pop	dword [ebp]

	and	dword [.R0TCB],byte 0
	or	dword [.EIPlow],byte -1
	and	dword [.EIPhigh],byte 0

	call	ParseExpression			; parse <R0TCB>
	jb	.setCB

	mov	[.R0TCB],eax

	call	[pSkipWhiteSpace]		; skip to <EIP low>
	jz	.setCB

	mov	edi,Error_BadEIPlow
	call	ParseExpression			; parse <EIP low>
	jb	near Parser.errorMsg

	mov	[.EIPlow],eax

	call	[pSkipWhiteSpace]		; skip to <EIP high>
	mov	eax,[.EIPlow]
	jz	@F

	mov	edi,Error_BadEIPhigh
	call	ParseExpression			; parse <EIP high>
	jb	near Parser.errorMsg

@@
	mov	[.EIPhigh],eax

.setCB:
	call	SetCB

	mov	ebp,[fExecuteMoreCommands]	; set internal Winice flag to 0
	mov	byte [ebp],0

	popad
	retn


segment _LDATA
	align 4
.R0TCB:		dd 0
.EIPlow:	dd 0
.EIPhigh:	dd 0


segment _LDATA
Error_BadEIPlow:	db 'invalid EIP low',0
Error_BadEIPhigh:	db 'invalid EIP high',0


segment _LTEXT
;-------------------------------------------------------------------------------
; prepare for tracing the specified thread, take care of self-tracing guys too
;-------------------------------------------------------------------------------
Service_Trace:
	cmp	dword [Parse_Trace.R0TCB],byte 0	; print trace info?
	jnz	.trace

	Trace_Out "ICEDUMP: threads under trace:"

	xor	ecx,ecx
	VMMCall	Begin_Critical_Section

	VMMCall	Get_Sys_VM_Handle
	VMMCall	Get_Initial_Thread_Handle
	mov     ebx,edi
	mov     ecx,[TDS]

@@
	cmp	dword [ecx+edi],byte 0

	Trace_OutNZ "#edi"

	VMMCall	Get_Next_Thread_Handle
	cmp	ebx,edi
	jnz	@B

	VMMCall	End_Critical_Section

.ret1:
	popfd
	popad
	retn

.trace:
	VMMCall	Get_Cur_VM_Handle
	VMMCall	Get_Cur_Thread_Handle

	cmp	[Parse_Trace.R0TCB],edi		; trace current thread?
	jnz	near .trace_notcurrent

	mov	esi,[TDS]
	mov	esi,[esi+edi]			; TraceInfo
	cmp	esi,byte 0			; tracing current thread?
	jnz	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: use TRACEX to trace the current thread"
	debug_end

	jmp	short .ret1

@@
	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: you shot yourself in the foot, use TRACEX to reactivate tracing the current thread"
	debug_end

	call	TracerFree
	jmp	short .ret1

.trace_notcurrent:
	mov	edi,[Parse_Trace.R0TCB]
	VMMCall	Validate_Thread_Handle
	jnc	@F

	Trace_Out "ICEDUMP: invalid thread handle, R0TCB: #edi"

	jmp	.ret2

@@
	mov	esi,[TDS]
	mov	esi,[esi+edi]

	mov	ecx,[Parse_Trace.EIPlow]
	mov	edx,[Parse_Trace.EIPhigh]
	cmp	ecx,edx
	jbe	.start

	cmp	esi,byte 0
	jnz	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: thread was not traced, R0TCB: #edi"
	debug_end

	jmp	short .ret2

@@
	cmp	dword [esi+TraceInfo.State0],byte ST0_SELFTRACEON
	jz	@F

	call	StopTracing

@@
	call	TracerFree
	jmp	short .ret2

.start:
	cmp	esi,byte 0			; already tracing?
	jnz	.update_range

	mov	ebp,[edi+TCB_ClientPtr]
	test	byte [ebp+CRS.EFlags+2],2	; is client in V86 mode?
	jz	@F

;	call	IsThreadWin32
;	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: thread is in V86 mode, R0TCB: #edi"
	debug_end

	jmp	short .ret2

@@
	call	TracerAlloc
	jnc	@F

	debug_start debugflags, ICEDUMP_DEBUG_TRACE
	Trace_Out "ICEDUMP: TRACE failed to allocate TraceInfo, R0TCB: #edi"
	debug_end

	jmp	short .ret2

@@
	mov	esi,[TDS]
	mov	esi,[esi+edi]
	call	TrReinforce

.update_range:
	mov	[esi+TraceInfo.EIPlow],ecx
	mov	[esi+TraceInfo.EIPhigh],edx

.ret2:
	popfd
	popad
	retn
