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


global ProtectSysDynamicDeviceInit
global ProtectSysDynamicDeviceExit
global ProtectThreadInit
global ProtectThreadNotExecuteable
global ProtectVmInit
global ProtectVmNotExecuteable
global Parse_Protect
global GetSetWiniceInt


extern sdata
extern Parser.error
extern Parser.errorMsg
extern GetFileName1
extern GetFileName2
extern VWIN32.W32ServiceTable
extern VWIN32.W32_SetThreadContext
extern VWIN32.W32_DIOC
extern VWIN32.TDS
extern TDBX.pPDB
extern VMCB.oPMIDT
extern VMCB.selLDT
extern VMCB.oLDT
extern selKrnl386CS


bits 32


segment _LDATA
init_data_begin ICEDUMP_PROTECT
init_data_item	AllocateThreadDataSlot,	FreeThreadDataSlot
init_data_item	HookWiniceInts,		UnhookWiniceInts
init_data_item	HookWiniceSaveDRx,	UnhookWiniceSaveDRx
init_data_item	HookV86Ints,		UnhookV86Ints
init_data_item	HookDeviceServices,	UnhookDeviceServices
;init_data_item	ProtectOn,		ProtectOff
init_data_item	0,			ProtectOff
init_data_end


segment _LTEXT
;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
ProtectSysDynamicDeviceInit:
; turn off V86 int68 handler
	push	ebx

	VMMCall	Get_Sys_VM_Handle

	mov	eax,0x68
	VMMCall	Get_Instanced_V86_Int_Vector

	movzx	ebx,ax
	shr	eax,16-4
	and	al,0xF0
	add	eax,ebx
	mov	byte [eax],0xCF		; IRET

	pop	ebx

	init_construct ICEDUMP_PROTECT
	jc	.error

	clc
	retn

.error:
	call	ProtectSysDynamicDeviceExit

	stc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
ProtectSysDynamicDeviceExit:
	init_destruct ICEDUMP_PROTECT
	clc
	retn


;-------------------------------------------------------------------------------
; init TDS in new thread
;
; EDI: R0TCB
;
; stc on error
;-------------------------------------------------------------------------------
ProtectThreadInit:
	call	ThreadInit
	clc
	retn


;-------------------------------------------------------------------------------
; check if the thread will be allowed to use the protected services
;
; EDI: R0TCB
;-------------------------------------------------------------------------------
ThreadInit:
	push	ebx
	push	ecx
	push	edx
	push	edi

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

	VMMCall	Get_Sys_VM_Handle
	cmp	ebx,[edi+TCB_VMHandle]
	jnz	near .ret

	mov	eax,[VWIN32.TDS]
	mov	eax,[eax+edi]		; eax: TDBX
	test	eax,eax
	jz	.ret

	add	eax,[TDBX.pPDB]
	mov	eax,[eax]		; eax: PDB
	test	eax,eax
	jz	.ret

	call	GetFileName1
	jnc	@F

	call	GetFileName2
	jc	.ret

; check for allowed clients
@@
	call	GetClientRights
	or	eax,eax
	jz	.ret

	push	eax

	VMMCall	_HeapAllocate, dword AppInfo_size, byte HEAPZEROINIT
	or	eax,eax
	jnz	@F

	debug_start debugflags, ICEDUMP_DEBUG_PROTECT
	Trace_Out "ICEDUMP: ThreadInit: failed to allocate AppInfo in thread, R0TCB: #edi"
	debug_end

	pop	eax
	jmp	short .ret

@@
	mov	ecx,[TDS]
	add	ecx,[esp+4]
	mov	[ecx],eax

	pop	dword [eax+AppInfo.Rights]
	and	dword [eax+AppInfo.Start],byte 0
	and	dword [eax+AppInfo.Size],byte 0
	and	dword [eax+AppInfo.Count],byte 0

.ret:
	pop	edi
	pop	edx
	pop	ecx
	pop	ebx
	clc
	retn


;-------------------------------------------------------------------------------
; check if the executable will be allowed to use the protected services
; (ICELOAD,LOADER32,NMSYM,WLDR,FILEMON,REGMON)
;
; edi: process name ptr to last extension '.'
;
; eax: access rights (bitmask), or 0
;-------------------------------------------------------------------------------
GetClientRights:
	mov	eax,[edi-7]
	xor	eax,'icel'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,[edi-4]
	xor	eax,'load'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,PROTECT_ALLOW_ICE_MASK
	retn

@@
	mov	eax,[edi-8]
	xor	eax,'load'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,[edi-4]
	xor	eax,'er32'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,PROTECT_ALLOW_ICE_MASK
	retn

@@
	mov	eax,[edi-5]
	xor	eax,'nmsy'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,[edi-4]
	xor	eax,'msym'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,PROTECT_ALLOW_ICE_MASK
	retn

@@
	mov	eax,[edi-4]
	xor	eax,'wldr'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,PROTECT_ALLOW_ICE_MASK
	retn

@@
	mov	eax,[edi-7]
	xor	eax,'file'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,PROTECT_ALLOW_ICE_MASK
	retn

	mov	eax,[edi-4]
	xor	eax,'emon'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

@@
	mov	eax,[edi-6]
	xor	eax,'regm'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,[edi-4]
	xor	eax,'gmon'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,PROTECT_ALLOW_ICE_MASK
	retn

@@
	xor	eax,eax
	retn


;-------------------------------------------------------------------------------
; EDI: R0TCB
;
; stc on error
;-------------------------------------------------------------------------------
ProtectThreadNotExecuteable:
	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


;-------------------------------------------------------------------------------
; ebx: VMCB
;
; stc on error
;-------------------------------------------------------------------------------
ProtectVmInit:
	VMMCall	Get_Initial_Thread_Handle
	call	ProtectThreadInit
	retn


;-------------------------------------------------------------------------------
; ebx: VMCB
;
; stc on error
;-------------------------------------------------------------------------------
ProtectVmNotExecuteable:
	VMMCall	Get_Initial_Thread_Handle
	call	ProtectThreadNotExecuteable
	retn


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

@@
	call	ThreadInit
	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

.loop:
	call	ProtectThreadNotExecuteable

	VMMCall	Get_Next_Thread_Handle
	cmp	ebx,edi
	jnz	.loop

	VMMCall	_FreeThreadDataSlot, dword [TDS]

	pop	edi
	pop	edx
	pop	ecx
	pop	ebx
	clc
	retn


;-------------------------------------------------------------------------------
; hook winice's int03/int0D/int0E/int41 handlers (tWiniceIntHandlers)
;
; stc on error
;-------------------------------------------------------------------------------
HookWiniceInts:
	pushfd
	cli

	push	esi

	mov	al,0x03
	mov	esi,HookedWiniceInt03
	call	GetSetWiniceInt
	mov	[HookedWiniceInt03.old],eax
	jc	.0

	mov	al,0x0D
	mov	esi,HookedWiniceInt0D
	call	GetSetWiniceInt
	mov	[HookedWiniceInt0D.old],eax
	jc	.1

	mov	al,0x0E
	mov	esi,HookedWiniceInt0E
	call	GetSetWiniceInt
	mov	[HookedWiniceInt0E.old],eax
	jc	.2

	mov	al,0x41
	mov	esi,HookedWiniceInt41
	call	GetSetWiniceInt
	mov	[HookedWiniceInt41.old],eax
	jc	.3

	pop	esi
	popfd
	clc
	retn

.3:
	mov	al,0x0E
	mov	esi,[HookedWiniceInt0E.old]
	call	GetSetWiniceInt
	and	dword [HookedWiniceInt0E.old],byte 0

.2:
	mov	al,0x0D
	mov	esi,[HookedWiniceInt0D.old]
	call	GetSetWiniceInt
	and	dword [HookedWiniceInt0D.old],byte 0

.1:
	mov	al,0x03
	mov	esi,[HookedWiniceInt03.old]
	call	GetSetWiniceInt
	and	dword [HookedWiniceInt03.old],byte 0

.0:
	pop	esi
	popfd
	stc
	retn


;-------------------------------------------------------------------------------
; al: winice int to hook
; esi: int handler
;
; eax: old handler
; stc on error
;-------------------------------------------------------------------------------
GetSetWiniceInt:
	push	ebx
	push	ecx

	mov	ebx,[tWiniceIntHandlers]
	mov	ecx,[dWiniceIntHandlerCounter]
	mov	ecx,[ecx]

.next:
	cmp	dword [ebx+WiniceIntHandler.oIDT],byte 0
	jz	@F

	cmp	[ebx+WiniceIntHandler.IntNum],al
	jnz	@F

	mov	eax,[ebx+WiniceIntHandler.IntHandler]
	lea	eax,[eax+ebx+WiniceIntHandler.IntHandler+4]

	sub	esi,ebx
	sub	esi,byte WiniceIntHandler.IntHandler+4
	mov	[ebx+WiniceIntHandler.IntHandler],esi

	pop	ecx
	pop	ebx
	clc
	retn

@@
	add	ebx,byte WiniceIntHandler_size
	loop	.next

	xor	eax,eax
	pop	ecx
	pop	ebx
	stc
	retn


;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
HookedWiniceInt03:
	pushfd
	cld
	cmp	ebp,'KHCB'
	jnz	@F

	popfd
	retn

@@
	cmp	si,'GF'
	jnz	.passdown

	cmp	di,'MJ'
	jnz	.passdown

	push	eax
	push	edi
	push	ds
	push	es

	mov	ax,0x30
	mov	ds,eax
	mov	es,eax

	VMMCall	Get_Cur_Thread_Handle
	add	edi,[TDS]
	mov	eax,[edi]
	or	eax,eax
	jz	.forbid

	test	byte [eax+AppInfo.Rights],PROTECT_ALLOW_ICE_MASK
	jz	.forbid

	cmp	dword [eax+AppInfo.Size],byte 0		; check CS:IP?
	jz	@F

	push	eax
	movzx	eax,word [esp+7*4+4]			; CS
	shl	eax,4
	movzx	edi,word [esp+7*4]			; IP
	add	edi,eax					; linear CS:IP
	pop	eax

	sub	edi,[eax+AppInfo.Start]
	jb	.forbid

	cmp	edi,[eax+AppInfo.Size]
	ja	.forbid

@@
	pop	es
	pop	ds
	pop	edi
	pop	eax

.passdown:
	popfd
	jmp	[ss:.old]

.forbid:
	pop	es
	pop	ds
	pop	edi
	pop	eax
	popfd
	retn


segment _LDATA
	align 4
.old:	dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; pop up winice if there is a CS.RPL/CS.DPL mismatch at an VMM.IRETD
; (probably illegal ring-0 entry attempt)
;-------------------------------------------------------------------------------
HookedWiniceInt0D:

%define NewCS  esp+0x20
%define NewEIP esp+0x1C
%define EFlags esp+0x18
%define CS     esp+0x14
%define EIP    esp+0x10
%define Error  esp+0x0C
%define OldEIP esp+0x08

	pushfd
	cld
	push	eax

	cmp	dword [ss:msgProtectStatus.onoff],'OFF'
	jz	near .passdown

	test	byte [EFlags+2],2	; V86 mode cannot do any harm
	jnz	.passdown

	cmp	word [CS],0x28		; did it happen while in ring-0?
	jnz	.passdown

	mov	eax,[EIP]		; get faulting EIP
	cmp	byte [ss:eax],0xCF	; was it an IRETD?
	jnz	.passdown

	test	byte [Error],0x03	; was it due to a gdt/ldt selector?
	jnz	.passdown

	mov	ax,[Error]
	xor	ax,[NewCS]		; compare against NewCS
	and	ax,0xFFFC		; did NewCS cause the fault?
	jnz	.passdown

	mov	ax,[Error]
	lar	ax,ax			; get DPL of bad selector
	jnz	.passdown

	shr	ax,13
	and	al,3			; ax: DPL
	or	ax,[Error]
	mov	[NewCS],ax

	mov	eax,[ss:dTraceCount]
	mov	dword [ss:eax],1
	or	byte [EFlags+1],1		; EFlags.T=1

	push	ds
	push	es

	mov	ax,0x30
	mov	ds,ax
	mov	es,ax

	Trace_Out "Break due to Attempting to Enter ring-0"

	pop	es
	pop	ds

	pop	eax
	popfd
	lea	esp,[esp+8]			; get rid of old handler + error code
	iretd

.passdown:
	pop	eax
	popfd
	jmp	[ss:.old]

%undef NewCS
%undef NewEIP
%undef EFlags
%undef CS
%undef EIP
%undef Error
%undef OldEIP


segment _LDATA
	align 4
.old:	dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; detect accesses to GDT, SysVM.IDT and SysVM.LDT
;-------------------------------------------------------------------------------
HookedWiniceInt0E:
; stack:
;
; EFlags
; CS
; EIP
; error code
; OldEIP
	pushfd
	cld

	cmp	dword [ss:msgProtectStatus.onoff],'OFF'
	jz	@F

	test	byte [esp+0x14+2],2	; V86 mode cannot do any harm
	jz	.check_error_code

@@
	popfd
	jmp	[ss:.old]

.check_error_code:
	push	eax
	mov	al,[esp+0x0C]		; error code
	and	al,101b			; Read/Write masked off
	cmp	al,101b			; User/Read-or-Write/Present
	pop	eax
	jz	@F

	popfd
	jmp	[ss:.old]

@@
	push	eax
	push	ebx
	push	ecx

	mov	ebx,cr2

	call	CheckFaultInGDT
	jmp	[ss:.dispatchGDT+4*eax]

.checkIDT:
	call	CheckFaultInIDT
	jmp	[ss:.dispatchIDT+4*eax]

.checkLDT:
	call	CheckFaultInLDT
	jmp	[ss:.dispatchLDT+4*eax]

.popup_ldt:
; check for krnl386/kernel32 and allow their accesses
	mov	eax,[ss:selKernel32Code]
	movzx	eax,word [ss:eax]
	cmp	ax,[esp+0x1C]			; win32 CS?
	jnz	.check_krnl386

	cmp	dword [esp+0x18],0xBFF00000	; kernel32 is above
	jae	.enable_ldt

.check_krnl386:
	mov	ax,[ss:selKrnl386CS]
	cmp	[esp+0x1C],ax			; krnl386 CS?
	jz	.enable_ldt

.popup:
	push	ds
	push	es

	mov	ax,0x30
	mov	ds,ax
	mov	es,ax

	push	dword .msgBreak
	VMMCall	_Trace_Out_Service

	pop	es
	pop	ds

	pop	ecx
	pop	ebx
	pop	eax
	popfd
	lea	esp,[esp+8]			; get rid of old handler + error code
	call	[ss:pWiniceMain]
	iretd

.enable_gdt_idt:
	xor	eax,eax
	shld	eax,ebx,20
	lea	eax,[4*eax+0xFF800000]
	or	byte [ss:eax],0x04

	mov	ecx,cr3
	mov	cr3,ecx				; flush TLBs
	mov	cl,[ss:ebx]			; fill in TLB
	and	byte [ss:eax],~0x04		; restore PTE
	mov	cl,[ss:ebx]			; fill in TLB, tweak LRU algo

	pop	ecx
	pop	ebx
	pop	eax
	popfd
	lea	esp,[esp+8]			; get rid of old handler + error code
	iretd

.enable_ldt:
	xor	eax,eax
	shld	eax,ebx,20
	lea	eax,[4*eax+0xFF800000]
	or	byte [ss:eax],0x02

	mov	ecx,cr3
	mov	cr3,ecx				; flush TLBs
	mov	cl,[ss:ebx]			; fill in TLB
	and	byte [ss:eax],~0x02		; restore PTE
	mov	cl,[ss:ebx]			; fill in TLB, tweak LRU algo

	pop	ecx
	pop	ebx
	pop	eax
	popfd
	lea	esp,[esp+8]			; get rid of old handler + error code
	iretd

.passdown:
	pop	ecx
	pop	ebx
	pop	eax
	popfd
	jmp	[ss:.old]


segment _LDATA
	align 4
.old:	dd 0

.dispatchGDT:	dd .checkIDT,	.enable_gdt_idt,	.popup
.dispatchIDT:	dd .checkLDT,	.enable_gdt_idt,	.popup
.dispatchLDT:	dd .passdown,	.enable_ldt,		.popup_ldt
.msgBreak:	db 'Break due to Access Attempt to'
.xDT:		db 0,0,0,0,13,10,0


PROTECT_PAGEFAULT_PASSDOWN	EQU 0
PROTECT_PAGEFAULT_ENABLE	EQU 1
PROTECT_PAGEFAULT_POPUP		EQU 2

segment _LTEXT
;-------------------------------------------------------------------------------
; ebx: CR2
;
; eax: PROTECT_PAGEFAULT_
;-------------------------------------------------------------------------------
CheckFaultInGDT:
	push	eax
	push	eax
	sgdt	[esp+2]

	pop	eax
	shr	eax,16
	add	eax,[esp]		; eax: linear GDT limit

	cmp	ebx,[esp]
	xchg	eax,[esp]		; eax: GDT base
	pop	eax			; eax: linear GDT limit
	jb	@F

	cmp	ebx,eax
	lea	eax,[eax+0x1000]
	ja	@F

	mov	dword [ss:HookedWiniceInt0E.xDT],' GDT'
	mov	eax,PROTECT_PAGEFAULT_POPUP
	retn

@@
	and	ax,0xF000
	cmp	ebx,eax
	mov	eax,PROTECT_PAGEFAULT_PASSDOWN
	jb	@F

	mov	eax,PROTECT_PAGEFAULT_ENABLE

@@
	retn


;-------------------------------------------------------------------------------
; ebx: CR2
;
; eax: PROTECT_PAGEFAULT_
;-------------------------------------------------------------------------------
CheckFaultInIDT:
	push	eax
	push	eax
	sidt	[esp+2]

	pop	eax
	shr	eax,16
	add	eax,[esp]		; eax: linear IDT limit

	cmp	ebx,[esp]
	xchg	eax,[esp]		; eax: IDT base
	pop	eax			; eax: linear IDT limit
	jb	@F

	cmp	ebx,eax
	lea	eax,[eax+0x1000]
	ja	@F

	mov	dword [ss:HookedWiniceInt0E.xDT],' IDT'
	mov	eax,PROTECT_PAGEFAULT_POPUP
	retn

@@
	and	ax,0xF000
	cmp	ebx,eax
	mov	eax,PROTECT_PAGEFAULT_PASSDOWN
	jb	@F

	mov	eax,PROTECT_PAGEFAULT_ENABLE

@@
	retn


;-------------------------------------------------------------------------------
; ebx: CR2
;
; eax: PROTECT_PAGEFAULT_
;-------------------------------------------------------------------------------
CheckFaultInLDT:
	sldt	ax
	and	ax,0xFFF8
	jnz	@F

	mov	eax,PROTECT_PAGEFAULT_PASSDOWN
	retn

@@
	push	eax
	sgdt	[esp-2]
	add	eax,[esp]		; eax: LDT descriptor
	pop	ecx

	mov	ch,[ss:eax+7]
	mov	cl,[ss:eax+4]
	shl	ecx,16
	mov	cx,[ss:eax+2]		; ecx: LDT base

	movzx	eax,word [ss:eax]	; eax: LDT limit
	add	eax,ecx			; eax: linear LDT limit

	cmp	ebx,ecx
	jb	@F

	lea	ecx,[eax+0x1000]
	cmp	ebx,eax
	ja	@F

	mov	dword [ss:HookedWiniceInt0E.xDT],' LDT'
	mov	eax,PROTECT_PAGEFAULT_POPUP
	retn

@@
	and	cx,0xF000
	cmp	ebx,ecx
	mov	eax,PROTECT_PAGEFAULT_PASSDOWN
	jb	@F

	mov	eax,PROTECT_PAGEFAULT_ENABLE

@@
	retn


;-------------------------------------------------------------------------------
; disallow detection, try to reflect it down to V86 mode
;-------------------------------------------------------------------------------
HookedWiniceInt41:
%define SS     ebp+0x1C
%define ESP    ebp+0x18
%define EFlags ebp+0x14
%define CS     ebp+0x10
%define EIP    ebp+0x0C
%define OldEIP ebp+0x08

	pushfd
	cld
	push	ebp
	mov	ebp,esp

	test	byte [EFlags+2],2	; did it come from V86 mode?
	jz	.PM

	push	eax
	push	ebx
	push	ds
	push	es

	mov	ax,0x30
	mov	ds,ax
	mov	es,ax

	movzx	eax,word [ESP]
	cmp	eax,byte 6		; enough space for interrupt frame?
	jb	@F

	movzx	ebx,word [SS]
	shl	ebx,4

	add	ebx,eax			; linear (E)SP
	sub	ebx,byte 6

	mov	ax,[EFlags]
	mov	[ebx+4],ax

	mov	ax,[CS]
	mov	[ebx+2],ax

	mov	ax,[EIP]
	mov	[ebx],ax

	sub	word [ESP],byte 6

	mov	ax,[0x41*4]		; simulate int
	mov	[EIP],ax

	mov	ax,[0x41*4+2]
	mov	[CS],ax

@@
	pop	es
	pop	ds
	pop	ebx
	pop	eax

@@
	pop	ebp
	popfd
	lea	esp,[esp+4]		; get rid of OldEIP
	iretd

.PM:
	cmp	ax,0x4F
	jz	@B

	pop	ebp
	popfd
	jmp	[ss:.old]


segment _LDATA
	align 4
.old:	dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
UnhookWiniceInts:
	pushfd
	cli

	push	esi

	mov	al,0x03
	mov	esi,[HookedWiniceInt03.old]
	call	GetSetWiniceInt
	and	dword [HookedWiniceInt03.old],byte 0

	mov	al,0x0D
	mov	esi,[HookedWiniceInt0D.old]
	call	GetSetWiniceInt
	and	dword [HookedWiniceInt0D.old],byte 0

	mov	al,0x0E
	mov	esi,[HookedWiniceInt0E.old]
	call	GetSetWiniceInt
	and	dword [HookedWiniceInt0E.old],byte 0

	mov	al,0x41
	mov	esi,[HookedWiniceInt41.old]
	call	GetSetWiniceInt
	and	dword [HookedWiniceInt41.old],byte 0

	pop	esi
	popfd
	clc
	retn


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

	mov	eax,0x21
	mov	esi,HookedV86Int21
	VMMCall	Hook_V86_Int_Chain
	jc	.0

	mov	eax,0x2F
	mov	esi,HookedV86Int2F
	VMMCall	Hook_V86_Int_Chain
	jc	.1

	pop	esi
	clc
	retn

.1:
	mov	eax,0x21
	mov	esi,HookedV86Int21
	VMMCall	Unhook_V86_Int_Chain

.0:
	pop	esi
	stc
	retn


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

	mov	eax,0x2F
	mov	esi,HookedV86Int2F
	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],0x50	; set PSP
	jz	@F

	stc
	retn

@@
	push	eax
	push	ecx
	push	esi
	push	edi

	VMMCall	Get_Cur_Thread_Handle
	mov	ecx,[TDS]
	mov	ecx,[edi+ecx]			; ecx: AppInfo
	jecxz	.ret

	cmp	dword [ecx+AppInfo.Count],byte 1
	jz	@F

.ret:
	pop	edi
	pop	esi
	pop	ecx
	pop	eax
	stc
	retn

@@
	mov	edi,OldCRS
	VMMCall	Save_Client_State
	VMMCall	Begin_Nest_V86_Exec

	mov	byte [ebp+CRS.EAX+1],0x51	; get current PSP
	mov	eax,0x21
	VMMCall	Exec_Int

	movzx	eax,word [ebp+CRS.EBX]
	shl	eax,4

	VMMCall	End_Nest_Exec

	mov	esi,OldCRS
	VMMCall	Restore_Client_State

	VMMCall	Get_Cur_Thread_Handle
	mov	ecx,[TDS]
	mov	ecx,[edi+ecx]			; ecx: AppInfo
	jecxz	.ret

	cmp	[ecx+AppInfo.Start],eax
	jnz	.ret

	pop	edi
	pop	esi
	pop	ecx
	pop	eax

	pop	eax				; get return EIP
	push	dword [ebp+CRS.EBX]		; save BX
	push	dword .cont
	stc
	jmp	eax

.cont:
	pop	eax				; get saved BX
	movzx	eax,ax
	shl	eax,4

	push	ecx
	push	edi

	VMMCall	Get_Cur_Thread_Handle
	mov	ecx,[TDS]
	mov	ecx,[edi+ecx]			; ecx: AppInfo
	mov	[ecx+AppInfo.Start],eax

	movzx	edi,word [eax-16+3]		; image size in paragraphs
	shl	edi,4				; image size in bytes
	mov	[ecx+AppInfo.Size],edi

	pop	edi
	pop	ecx
	clc
	retn


segment _LDATA
	align 4
OrgV86Int21:	dd 0
OldCRS:	istruc CRS
	iend


segment _LTEXT
;-------------------------------------------------------------------------------
; EBP: CRS
;
; stc if not handled
;-------------------------------------------------------------------------------
	jmp	short HookedV86Int2F
	jmp	[OrgV86Int2F]

HookedV86Int2F:
	cmp	word [ebp+CRS.EAX],0x1605	; windows init broadcast
	stc
	jnz	@F

	clc

@@
	retn


segment _LDATA
	align 4
OrgV86Int2F:	dd 0


%macro hook_service 2
	GetDeviceServiceOrdinal eax, %{1}
	mov	esi,%{2}
	VMMCall	Hook_Device_Service
%endmacro

%macro unhook_service 2
	GetDeviceServiceOrdinal eax, %{1}
	mov	esi,%{2}
	VMMCall	Unhook_Device_Service
%endmacro

segment _LTEXT
;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
HookDeviceServices:
	push	esi

	hook_service Get_DDB, HookedGetDDB
	jc	near .0

	hook_service _GetVxDName, HookedGetVxDName
	jc	near .1

	hook_service Test_Debug_Installed, HookedTestDebugInstalled
	jc	near .2

	hook_service DOSMGR_Begin_V86_App, HookedDOSMGRBeginV86App
	jc	near .3

	hook_service DOSMGR_End_V86_App, HookedDOSMGREndV86App
	jc	.4

	hook_service VXDLDR_UnloadDevice, HookedVXDLDRUnloadDevice
	jc	.5

	call	CreateVxDLocationList
	jc	.6

	hook_service VMM_GetVxDLocationList, HookedVMMGetVxDLocationList
	jc	.7

	call	HookVWIN32W32Services

	pop	esi
	clc
	retn

.7:
	call	FreeVxDLocationList

.6:
	unhook_service VXDLDR_UnloadDevice, HookedVXDLDRUnloadDevice

.5:
	unhook_service DOSMGR_End_V86_App, HookedDOSMGREndV86App

.4:
	unhook_service DOSMGR_Begin_V86_App, HookedDOSMGRBeginV86App

.3:
	unhook_service Test_Debug_Installed, HookedTestDebugInstalled

.2:
	unhook_service Get_DDB, HookedGetVxDName

.1:
	unhook_service Get_DDB, HookedGetDDB

.0:
	pop	esi
	stc
	retn


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
CreateVxDLocationList:
	push	ecx
	push	edx
	push	esi
	push	edi

	VMMCall	VMM_GetVxDLocationList
	jz	.err

	mov	esi,eax
	mov	[NewVxDLocationListSize],ecx
	mov	[NewNumberOfDevices],edx

	VMMCall	_HeapAllocate, ecx, byte HEAPZEROINIT
	test	eax,eax
	jz	.err

	mov	edi,eax
	mov	[NewVxDLocationList],eax

; walk location list and copy everything except for the protected devices
	xor	ecx,ecx
	xchg	ecx,[NewNumberOfDevices]

.device_loop:
	push	ecx

	movzx	eax,byte [esi+DLL_NumObjects]
	imul	ecx,eax,byte ObjectLocation_size
	add	ecx,byte Device_Location_List_size-ObjectLocation_size

	push	edi
	mov	edi,[esi+DLL_DDB]
	lea	edi,[edi+DDB_Name]
	call	IsProtectedDeviceName
	pop	edi
	jb	@F

	inc	dword [NewNumberOfDevices]
	rep	movsb

@@
	add	esi,ecx
	pop	ecx
	loop	.device_loop

	sub	edi,[NewVxDLocationList]
	mov	[NewVxDLocationListSize],edi

	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	clc
	retn

.err:
	pop	edi
	pop	esi
	pop	edx
	pop	ecx
	stc
	retn


FreeVxDLocationList:
	push	ecx
	push	edx
	VMMCall	_HeapFree, dword [NewVxDLocationList], byte 0
	pop	edx
	pop	ecx
	clc
	retn


HookVWIN32W32Services:
	push	esi
	mov	esi,[VWIN32.W32ServiceTable]

	mov	eax,[VWIN32.W32_DIOC]
	lea	eax,[esi+8*eax+8]
	push	dword [eax]
	pop	dword [OrgW32DIOC]
	mov	dword [eax],HookedW32DIOC

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

	pop	esi
	retn


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

	call	UnhookVWIN32W32Services
	unhook_service VMM_GetVxDLocationList, HookedVMMGetVxDLocationList
	call	FreeVxDLocationList
	unhook_service VXDLDR_UnloadDevice, HookedVXDLDRUnloadDevice
	unhook_service DOSMGR_End_V86_App, HookedDOSMGREndV86App
	unhook_service DOSMGR_Begin_V86_App, HookedDOSMGRBeginV86App
	unhook_service Test_Debug_Installed, HookedTestDebugInstalled
	unhook_service Get_DDB, HookedGetVxDName
	unhook_service Get_DDB, HookedGetDDB

	pop	esi
	clc
	retn


UnhookVWIN32W32Services:
	push	esi
	mov	esi,[VWIN32.W32ServiceTable]

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

	mov	eax,[VWIN32.W32_DIOC]
	lea	eax,[esi+eax*8+8]
	push	dword [OrgW32DIOC]
	pop	dword [eax]

	pop	esi
	retn


segment _LTEXT
;-------------------------------------------------------------------------------
; restrict access to protected devices
;-------------------------------------------------------------------------------
	jmp	short HookedGetDDB
	jmp	[OrgGetDDB]

HookedGetDDB:
	cmp	ax,SICE_Device_ID
	jz	.check

	cmp	ax,SIWVID_Device_ID
	jz	.check

	cmp	ax,SIWDEBUG_Device_ID
	jz	.check

	or	eax,eax
	jnz	.passdown

	call	IsProtectedDeviceName
	jnc	.passdown

.check:
	call	IsAccessAllowed
	jnc	.passdown

	xor	ecx,ecx
	retn

.passdown:
	jmp	[OrgGetDDB]


segment _LDATA
	align 4
OrgGetDDB: dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; restrict access to protected devices
;-------------------------------------------------------------------------------
	jmp	short HookedGetVxDName
	jmp	[OrgGetVxDName]

HookedGetVxDName:
	call	IsAccessAllowed
	jnc	.passdown

	push	ebx
	mov	ebx,[esp+12]
	mov	eax,[esp+8]
	mov	byte [ebx+9],0
	call	[pDword2HexString]
	pop	ebx

	xor	edx,edx
	xor	eax,eax
	retn

.passdown:
	jmp	[OrgGetVxDName]


segment _LDATA
	align 4
OrgGetVxDName: dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; edi: oDeviceName (must be 8 chars, space filled, case sensitive)
;
; stc if protected
;-------------------------------------------------------------------------------
IsProtectedDeviceName:
	push	eax
	push	ecx

	mov	ecx,ProtectedDeviceNames-8

@@
	add	ecx,byte 8
	mov	eax,[ecx]
	or	eax,eax
	jz	.ok

	cmp	[edi],eax
	jnz	@B

	mov	eax,[ecx+4]
	cmp	[edi+4],eax
	jnz	@B

	pop	ecx
	pop	eax
	stc
	retn

.ok:
	pop	ecx
	pop	eax
	clc
	retn


segment _LDATA
ProtectedDeviceNames:
	db 'SICE    '
	db 'SIWVID  '
	db 'SIWDEBUG'
	db 'FILEVXD '
	db 'REGVXD  '
	db ICEDUMP_DEVICE_NAME
	dd 0,0


segment _LTEXT
;-------------------------------------------------------------------------------
; stc if not
;-------------------------------------------------------------------------------
IsAccessAllowed:
	push	ecx
	push	edi

	VMMCall	Get_Cur_Thread_Handle
	mov	ecx,[TDS]
	mov	ecx,[edi+ecx]			; ecx: AppInfo
	jecxz	.no

	test	byte [ecx+AppInfo.Rights],PROTECT_ALLOW_ICE_MASK
	jz	.no

	mov	edi,[edi+TCB_ClientPtr]		; edi: CRS
	test	byte [edi+CRS.EFlags+2],2	; V86 mode client?
	jz	.yes

	push	ecx
	movzx	ecx,word [edi+CRS.CS]
	shl	ecx,4
	movzx	edi,word [edi+CRS.EIP]
	add	edi,ecx
	pop	ecx

	sub	edi,[ecx+AppInfo.Start]
	jb	.no

	cmp	edi,[ecx+AppInfo.Size]
	jae	.no

.yes:
	pop	edi
	pop	ecx
	clc
	retn

.no:
	pop	edi
	pop	ecx
	stc
	retn


;-------------------------------------------------------------------------------
; of course no debugger is present ;-)
;-------------------------------------------------------------------------------
	jmp	short HookedTestDebugInstalled
	jmp	[OrgTestDebugInstalled]

HookedTestDebugInstalled:
	cmp	eax,eax		; clear Z
	retn


segment _LDATA
	align 4
OrgTestDebugInstalled: dd 0


PROTECT_ALLOW_ICE_BIT	EQU 0
PROTECT_ALLOW_ICE_MASK	EQU 1 << PROTECT_ALLOW_ICE_BIT

;PROTECT_ALLOW_LDT_BIT	EQU 1
;PROTECT_ALLOW_LDT_MASK	EQU 1 << PROTECT_ALLOW_LDT_BIT

struc AppInfo
.Rights:	resd 1
.Start:		resd 1
.Size:		resd 1
.Count:		resd 1
endstruc


segment _LTEXT
;-------------------------------------------------------------------------------
; ebx: VMCB
; esi: HighLinearPSP
; dx: AppPSP
;
; check if thread/V86 app is allowed access to protected services
; (DLDR,DLOG,ICEDUMP)
;-------------------------------------------------------------------------------
	jmp	short HookedDOSMGRBeginV86App
	jmp	[OrgDOSMGRBeginV86App]

HookedDOSMGRBeginV86App:
	push	eax
	push	edi

	VMMCall	Get_Cur_Thread_Handle
	mov	eax,[TDS]
	mov	eax,[edi+eax]		; eax: AppInfo
	or	eax,eax			; only one app/thread is allowed
	jz	@F

	inc	dword [eax+AppInfo.Count]
	jmp	short .exit

@@
	mov	eax,[esi-8]
	xor	eax,'dldr'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	cmp	byte [esi-4],0
	jz	.allow

@@
	mov	eax,[esi-8]
	xor	eax,'dlog'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	cmp	byte [esi-4],0
	jz	.allow

@@
	mov	eax,[esi-8]
	xor	eax,'iced'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	@F

	mov	eax,[esi-4]
	xor	eax,'ump'
	and	eax,0xFFDFDFDF		; stricmp ;-)
	jz	.allow

@@
.exit:
	pop	edi
	pop	eax
	jmp	[OrgDOSMGRBeginV86App]

.allow:
	push	ecx
	push	edx
	VMMCall	_HeapAllocate, dword AppInfo_size, byte HEAPZEROINIT
	pop	edx
	pop	ecx
	or	eax,eax
	jnz	@F

	debug_start debugflags, ICEDUMP_DEBUG_PROTECT
	Trace_Out "ICEDUMP: BeginV86App: failed to allocate AppInfo in thread, R0TCB: #edi"
	debug_end

	jmp	short .exit

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

	mov	eax,[eax]			; eax: DosAppInfo

	mov	dword [eax+AppInfo.Rights],PROTECT_ALLOW_ICE_MASK

	movzx	edi,dx				; first paragraph of image
	shl	edi,4				; start address of image
	mov	[eax+AppInfo.Start],edi

	movzx	edi,word [esi-16+3]		; image size in paragraphs
	shl	edi,4				; image size in bytes
	mov	[eax+AppInfo.Size],edi

	mov	dword [eax+AppInfo.Count],1

	jmp	short .exit


segment _LDATA
	align 4
OrgDOSMGRBeginV86App: dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; ebx: VMCB
; esi: HighLinearPSP
; dx: AppPSP
; cx: AppExitCode
;-------------------------------------------------------------------------------
	jmp	short HookedDOSMGREndV86App
	jmp	[OrgDOSMGREndV86App]

HookedDOSMGREndV86App:
	push	eax
	push	edi

	VMMCall	Get_Cur_Thread_Handle
	mov	eax,[TDS]
	mov	eax,[edi+eax]			; eax: DosAppInfo
	or	eax,eax
	jz	.exit

	dec	dword [eax+AppInfo.Count]
	jnz	.exit

	call	ProtectThreadNotExecuteable

.exit:
	pop	edi
	pop	eax
	jmp	[OrgDOSMGREndV86App]


segment _LDATA
	align 4
OrgDOSMGREndV86App: dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; bx: device ID (0, 0xFFFF: use device name)
; edx: device name
;-------------------------------------------------------------------------------
	jmp	short HookedVXDLDRUnloadDevice
	jmp	[OrgVXDLDRUnloadDevice]

HookedVXDLDRUnloadDevice:
	push	edi

	cmp	bx,SICE_Device_ID
	jz	.check

	cmp	bx,SIWVID_Device_ID
	jz	.check

	cmp	bx,SIWDEBUG_Device_ID
	jz	.check

	or	bx,bx
	jz	@F

	cmp	bx,0xFFFF
	jnz	.passdown

@@
	mov	edi,edx
	call	IsProtectedDeviceName
	jnc	.passdown

.check:
	call	IsAccessAllowed
	jnc	.passdown

	pop	edi
	mov	eax,8
	stc
	retn

.passdown:
	pop	edi
	jmp	[OrgVXDLDRUnloadDevice]


segment _LDATA
	align 4
OrgVXDLDRUnloadDevice: dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; VxD location list? what's that? ;-)
;-------------------------------------------------------------------------------
	jmp	short HookedVMMGetVxDLocationList
	jmp	[OrgVMMGetVxDLocationList]

HookedVMMGetVxDLocationList:
	mov	eax,[NewVxDLocationList]
	mov	ecx,[NewVxDLocationListSize]
	mov	edx,[NewNumberOfDevices]
	retn


segment _LDATA
	align 4
OrgVMMGetVxDLocationList: dd 0
NewVxDLocationList:	dd 0
NewVxDLocationListSize:	dd 0
NewNumberOfDevices:	dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; esp+4: struc DIOCParams
;-------------------------------------------------------------------------------
HookedW32DIOC:
	push	ecx
	push	esi
	push	edi
	push	ebp
	mov	ebp,esp

%define DIOCParams ebp+5*4

	mov	esi,[DIOCParams+DIOCParams.Internal2]
	add	esi,byte 0x0C		; device name
	cmp	esi,byte 0x0C
	jnz	@F

	mov	esi,[DIOCParams+DIOCParams.lpvInBuffer]

@@
	mov	edi,DeviceName
	mov	ecx,8

.strcpy:
	lodsb
	cmp	al,0
	jz	.done

	cmp	al,'a'
	jb	@F

	cmp	al,'z'
	ja	@F

	and	al,0xDF		; upcase

@@
	stosb
	loop	.strcpy

.done:
	mov	al,' '
	rep	stosb		; space filled

	mov	edi,DeviceName
	call	IsProtectedDeviceName

	jnc	.passdown

	VMMCall	Get_Cur_Thread_Handle
	mov	ecx,[TDS]
	mov	ecx,[ecx+edi]	; ecx: AppInfo
	jecxz	.ret

	test	byte [ecx+AppInfo.Rights],PROTECT_ALLOW_ICE_MASK
	jz	.ret

.passdown:
	pop	ebp
	pop	edi
	pop	esi
	pop	ecx
	jmp	[OrgW32DIOC]

.ret:
	mov	eax,[DIOCParams+DIOCParams.Internal1]
	and	dword [eax+0x1C],byte 0

	pop	ebp
	pop	edi
	pop	esi
	pop	ecx

%undef DIOCParams

	retn	0x34


segment _LDATA
	align 4
OrgW32DIOC:	dd 0
DeviceName:	times 9 db 0


segment _LTEXT
;-------------------------------------------------------------------------------
;
;-------------------------------------------------------------------------------
HookedW32SetThreadContext:
	cmp	dword [msgProtectStatus.onoff],'ON'
	jz	@F

	jmp	[OrgW32SetThreadContext]

@@
	push	eax
	push	ebx
	push	esi
	push	edi
	push	ebp

	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	ebp,[edi+TCB_ClientPtr]
	mov	eax,[selKernel32Code]
	mov	ax,[eax]
	arpl	[ebp+CRS.CS],ax
	jnz	@F

	debug_start debugflags, ICEDUMP_DEBUG_PROTECT
	mov	ax,[ebp+CRS.CS]
	Trace_Out "ICEDUMP: CS.RPL adjusted, CS: #ax, R0TCB: #edi"
	debug_end

@@
	pop	ebp
	pop	edi
	pop	esi
	pop	ebx
	pop	eax
	retn	16


segment _LDATA
	align 4
OrgW32SetThreadContext:	dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; in pSetDRx redirect 'call pSaveDRx' to enable/disable LE/GE in DR7
; stc on error
;-------------------------------------------------------------------------------
HookWiniceSaveDRx:

%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
  %else
    %define UNSUPPORTED_VERSION
  %endif
%else
  %define UNSUPPORTED_VERSION
%endif

%ifndef UNSUPPORTED_VERSION

	mov	eax,[pSetDRx]
	add	eax,byte 0x71+4
	mov	dword [eax-4],HookedWiniceSaveDRx
	sub	[eax-4],eax

%else
%error please validate this version
%endif
	
	clc
	retn


;-------------------------------------------------------------------------------
; turn off DR7.GE and DR7.LE
;-------------------------------------------------------------------------------
HookedWiniceSaveDRx:
	and	bx,0xFCFF
	mov	dr7,ebx
	jmp	[pSaveDRx]


;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
UnhookWiniceSaveDRx:

%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
  %else
    %define UNSUPPORTED_VERSION
  %endif
%else
  %define UNSUPPORTED_VERSION
%endif

%ifndef UNSUPPORTED_VERSION

	mov	eax,[pSetDRx]
	add	eax,byte 0x71+4
	push	dword [pSaveDRx]
	pop	dword [eax-4]
	sub	[eax-4],eax

%else
%error please validate this version
%endif

	clc
	retn


;-------------------------------------------------------------------------------
; PROTECT [ON|OFF]
;-------------------------------------------------------------------------------
Parse_Protect:
	call	[pSkipWhiteSpace]	; skip to [ON|OFF]
	jnz	@F

	mov	esi,msgProtectStatus
	call	[pPrintToCommandWindow]

@@
	call	[pIsItOnOrOff]
	jb	near Parser.error

	test	al,al
	jz	@F

	call	ProtectOn

	mov	dword [msgProtectStatus.onoff],'ON'
	jmp	short .ret

@@
	call	ProtectOff
	mov	dword [msgProtectStatus.onoff],'OFF'

.ret:
	popad
	retn


segment _LDATA
msgProtectStatus:	db 'Protection is '
.onoff			db 'OFF',0


segment _LTEXT
;-------------------------------------------------------------------------------
; set Supervisor bit for GDT, SysVM.IDT and SysVM.LDT, handle page faults
;-------------------------------------------------------------------------------
ProtectOn:
	push	ebx

	call	ProtectOnGDT

	VMMCall	Get_Sys_VM_Handle
	call	ProtectOnIDT
	call	ProtectOnLDT

	pop	ebx
	retn


;-------------------------------------------------------------------------------
; set to PTEs covering the GDT to Supervisor
;-------------------------------------------------------------------------------
ProtectOnGDT:
	push	eax
	sgdt	[esp-2]
	pop	eax			; eax: GDT base

	shr	eax,12
	lea	eax,[4*eax+0xFF800000]
	and	byte [eax],~0x04
	retn


;-------------------------------------------------------------------------------
; EBX: VMCB
;-------------------------------------------------------------------------------
ProtectOnIDT:
	mov	eax,[VMCB.oPMIDT]
	mov	eax,[eax+ebx]		; eax: IDT base

	shr	eax,12
	lea	eax,[4*eax+0xFF800000]
	and	byte [eax],~0x04
	retn


;-------------------------------------------------------------------------------
; EBX: VMCB
;-------------------------------------------------------------------------------
ProtectOnLDT:
	push	ecx

	mov	ecx,[VMCB.selLDT]
	movzx	ecx,word [ecx+ebx]
	jecxz	.ret

	push	eax
	sgdt	[esp-2]
	pop	eax			; eax: GDT base

	and	cx,0xFFF8
	movzx	ecx,word [ecx+eax]	; ecx: LDT limit
	shr	ecx,12
	inc	ecx			; ecx: LDT size in pages
	
	mov	eax,[VMCB.oLDT]
	mov	eax,[eax+ebx]		; eax: LDT base
	shr	eax,12			; eax: linear page number of LDT base

@@
	push	eax
	lea	eax,[eax+ecx-1]
	lea	eax,[4*eax+0xFF800000]
	and	byte [eax],~0x02	; read-only
	pop	eax
	loop	@B

.ret:
	pop	ecx
	retn


;-------------------------------------------------------------------------------
; set User bit for GDT, SysVM.IDT and SysVM.LDT
;-------------------------------------------------------------------------------
ProtectOff:
	push	ebx

	call	ProtectOffGDT

	VMMCall	Get_Sys_VM_Handle
	call	ProtectOffIDT
	call	ProtectOffLDT

	pop	ebx
	retn


;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
ProtectOffGDT:
	push	eax
	sgdt	[esp-2]
	pop	eax			; eax: GDT base

	shr	eax,12
	lea	eax,[4*eax+0xFF800000]
	or	byte [eax],0x04
	retn


;-------------------------------------------------------------------------------
; EBX: VMCB
;-------------------------------------------------------------------------------
ProtectOffIDT:
	mov	eax,[VMCB.oPMIDT]
	mov	eax,[eax+ebx]		; eax: IDT base

	shr	eax,12
	lea	eax,[4*eax+0xFF800000]
	or	byte [eax],0x04
	retn


;-------------------------------------------------------------------------------
; EBX: VMCB
;-------------------------------------------------------------------------------
ProtectOffLDT:
	push	ecx

	mov	ecx,[VMCB.selLDT]
	movzx	ecx,word [ecx+ebx]
	jecxz	.ret

	push	eax
	sgdt	[esp-2]
	pop	eax			; eax: GDT base

	and	cx,0xFFF8
	movzx	ecx,word [ecx+eax]	; ecx: LDT limit
	shr	ecx,12
	inc	ecx			; ecx: LDT size in pages
	
	mov	eax,[VMCB.oLDT]
	mov	eax,[eax+ebx]		; eax: LDT base
	shr	eax,12			; eax: linear page number of LDT base

@@
	push	eax
	lea	eax,[eax+ecx-1]
	lea	eax,[4*eax+0xFF800000]
	or	byte [eax],0x02		; read-write
	pop	eax
	loop	@B

.ret:
	pop	ecx
	retn
