%include "util.mac"
%include "vxdn.inc"
%include "icedump.inc"
%include "wiat.inc"
%include "common.inc"
%include "options.inc"

_Dr0			equ	0
_Dr1			equ	4
_Dr2			equ	8
_Dr3			equ	12
_Dr6			equ	16	
_Dr7			equ	20

bak_Dr0			equ	24	
bak_Dr1			equ	28
bak_Dr2			equ	32
bak_Dr3			equ	36
bak_Dr6			equ	40
bak_Dr7			equ	44
SingleStepPending	equ	48
ISR_Thief_Slot		equ	52

global Parse_Step
global Service_Step
global StepThreadNotExecutable
global StepThreadInit
global FreeProtectedVectors


extern SetCBX
extern SetCB
extern sdata
extern Parser.error
extern Parser.errorMsg
extern Error_V86
extern Error_PM16
extern Error_PMR0
extern ParseExpression


bits 32

segment _LDATA
	align 4
stpage:			dd 0
stblock:		dd 0
stpdb:			dd 0
StOldInt1:		dd 0
StOldInt3:		dd 0
StOldInt1_idt:		dd 0
StInitPf:		dd 0
bpxByte:		db 0
protIdt:		dd 0
DbAccessCount:		dd 0
ShadowIdt:		dd 0
BackupIdt:		dd 0
IdtAccessCount:		dd 0
ProtectedISRs:		dd 0
ISRheap:		dd 0

stpfcount:		dd 0
stpinitcount:		dd 0
eip_r_thread:		db 'ICEDUMP: Stepping halted,  r0tcb:         ',13,10,0
stfaultlogout:		db 'ICEDUMP: fault count:           pinit step count:         ',13,10,0
DebugCounter:		db 'ICEDUMP:          #db single step or bpm passed down',13,10,0
IllegalIDTAccess:	db 'ICEDUMP: IDT access count:          ',13,10,0
IsrHazard		db 'ICEDUMP: Geek wants to overwrite or read ISR:         ',13,10,0


segment _LTEXT

;--------------------------------------------------------------------------------------------------
;Protection in case the program wants to rob us of our #db handler
;I was thinking that i could maybe reload idt with a copy of the current one, and then spoof the idt
;entry the stepped program is reading, if i do i can then hook corresponding interrupt and manage
;it to fool program that it has control over the vector it was tampering with
;(i belive Intel would call that emulation :))
;--------------------------------------------------------------------------------------------------

IdtAccess:
	call	UnprotectIdt
	mov	edx,ebx		;ebx is pagefault = IDT+x where x is RVA of attempted hijack vector
	sub	edx,[protIdt]
	shr	edx,3		;eax = Vector! :)
	cmp	edx,1
	jnz	@F

	Trace_Out "ICEDUMP: Geek poking at INT: 01 be careful!!"
	jmp	SetBreakPoint

@@
	inc	dword [IdtAccessCount]
	push	byte 0
	push	byte 0
	sidt	[esp-2]
	pop	eax
	push	dword [ShadowIdt]
	lidt	[esp-2]		;reload idt with clone 
	add	esp,8

	call	IsThisMyThread
	test	eax,eax
	jz	@F

	mov	dword [eax+ISR_Thief_Slot],edx	;save vector number in thread database
;	mov	dword [eax+SingleStepPending],0	;single step cant be pending if it already stepped no?
	call	set_pages_present	

@@
	call	hook_isr1_idt	
	or	dword [esp+0x34],0x10100	;set trap flag and resume flag

	pop	es
	pop	ds
	popad
	popfd
	retn	


;------------------------------------------------------------------------
;Second phase of protection, load back old idt
;------------------------------------------------------------------------

idtprotectstep:
	pushfd
	pushad
	push	ds
	push	es
	mov	ax,ss
	mov	ds,ax
	mov	es,ax
	
	and	dword [esp+0x34],0xfffffeff		;clear trap flag 
	or	dword [esp+0x34],0x10000		;resume flag, 
	
	call	unhook_isr1_idt

	call	IsThisMyThread	
	test	eax,eax
	jz	@F

	mov	ebx,eax
	mov	eax,[ebx+ISR_Thief_Slot]		;get ISR to tamper with
	mov	dword [ebx+ISR_Thief_Slot],0		;clear slot for less confusion	
	call	set_pages_not_present			;pages disappear again

@@
	mov	eax,cr3		;flush tlb's out with soap!
	mov	cr3,eax		;never forget to flush (annoyed me for an hour or more :~)
	
	nop			;this nop could have been code to rebuild damaged vectors
				;but its not (yet)

	push	byte 0
	push	byte 0
	sidt	[esp-2]
	pop	eax
	push	dword [protIdt]
	lidt	[esp-2]
	add	esp,8

	call	hook_single_step
	call	ProtectIdt		;reprotect idt, god i hope this works

	pop	es
	pop	ds
	popad
	popfd
	iretd


;---------------------------------------------------------------------------------
;Idt was protected :) phew that was close
;---------------------------------------------------------------------------------


;------------------------------------------------------------------------
;This is invoked if the program wants to read or write to a handler itself
;they are all hooked and their corresponding procedures are placed on a 
;protected page, if (like stupid pelocknt) the program wants to overwrite
;an ISR softice will pop up and let user decide if and how they want to procede.
;a clone of the original idt is always saved so if anytime we choose to exit
;we get the one back that we had when we started.
;INT 1 is not protected by this mechanism as it is considered 
;critical, a manual decision always has to be taken when it comes to it
;since we break before the program gets a chance to read the vector from IDT.
;INT 0xE should have been handled the same way, but that causes problems
;since kernel32 seems to want to poke at it now and then, i left it
;out because of this, this means there is no protection at all for INT 0xE
;I doubt a protection will choose INT 0xE to get ring 0
;anyway, most people who do thoose things are dumb enough to disregard the
;error code and crash themselves all day and then give up anyway :)
;an idea would be to let the program abuse interrupt freely and simply on any
;access to idt simply shift the intterrupt back x number of bytes and place
;a winice popup routine there to let us trace/step or whatever the ISR they 
;stole from us, blah blah blah i doubt you even care about my ideas :/
;------------------------------------------------------------------------        
TryReadWriteToVector:
	call	UnprotectVectors
	mov	eax,ebx
	sub	eax,[ProtectedISRs]
	sub	eax,0x80
	shr	eax,7

	mov	ebx,IsrHazard
	add	ebx,46
	push	ebx
	push	eax
	call	Dword2Hex
	sub	ebx,46
	push	ebx
	VMMCall	_Trace_Out_Service	
	jmp	SetBreakPoint
;------------------------------------------------------------------------
;
;------------------------------------------------------------------------


;-------------------------------------------------------------------------------
;Main Routine the PF handler
;A pagefaul occurs, the handler validates if the fault occured within the EIP 
;range, if it did we set a breakpoint, if not we pass down the fault OR
;we mark pages present again, then let the program single step one instruction,
;and then let the single step handler enumerate the fault, if it still doesnt
;qualify we simple mark pages not present again and repeat the whole thing
;page faults will also occur if program tries to access idt, in thoose cases 
;we pop up winice and stop stepping, user can decide if she/he wants to continue
;anyway, only int 0x1 and 0xE are considered critical, others can usually
;be allowed and stepping will still work
;(setting write protect bit in CR0 (and clear write bit in pte) might help protect against ring0 
;access to IDT but im not sure if i dare do that just yet :))
;-------------------------------------------------------------------------------
StPfHandler:
	pushfd
	pushad
	push	ds
	push	es
	mov	ax,ss
	mov	ds,ax
	mov	es,ax
	mov	ebx,cr2

	mov	eax,ebx
	and	eax,0xfffff000			;round off low eip to even page
	cmp	eax,[protIdt]
	jz	near IdtAccess			;is program trying to access IDT???

	cmp	eax,[ProtectedISRs]		;is it access attempt at my ISR stubs??
	jz	TryReadWriteToVector

	mov	eax,cr3				;flush tlb's
	mov	cr3,eax	
	
	call	IsThisMyThread			;thread is owned by whom?
	test	eax,eax
	jz	near PassDown

	call	set_pages_not_present		;if my threads clear present bits always!!
	mov	eax,[stpage]			;get page ranges
	and	eax,0xfffff000			;round off low eip to even page
	cmp	ebx,eax
	jb	near PassDown			;if the fault isnt ours we pass it down

	add	eax,[stblock]	
	cmp	ebx,eax
	jae	near PassDown			;this is not my fault, leave it

ValidateEip:
	mov	ebx,[ebp+0x24]			;get eip and check if fault is in range
	cmp	ebx,[stpage]
	jb	SingleStepMyFault		;if below, pass down to single step

	mov	eax,[stpage]
	and	eax,0xfffff000			;round off low eip to even page
	add	eax,[stblock]		
	cmp	ebx,eax
	jae	SingleStepMyFault		;if above, pass down to single step

	mov	ebx,cr2
	push	ebx
	call	validate_page_acessed		;if page not accessed yet, pass down to single step
	test	eax,eax
	jz	SingleStepMyFault

	Trace_Out "ICEDUMP: EIP in range"

SetBreakPoint:	
	and	dword [ebp+0x2c],0xfffffeff	;clear trap flag and resume flag
	call	UnprotectIdt			;disable protection
	call	Deactivate_Step			;remove all handlers
	call	set_pages_present		;mark pages present
	call	hook_breakpoint			;borrow int 3

	inc	dword [stpfcount]		;counting faults
	pop	es
	pop	ds

	mov	ebx,[ebp+0x24]			;get eip (again?:=)
	sub	eax,eax
	mov	al,[ebx]
	mov	[ss:bpxByte],al			;save byte (yes i know you (owl) want me to use thread specific data
	mov	byte [ebx],0xcc			;but for gods sake its ONE byte and it only happens ONCE!!!)

	popad
	popfd
	retn					;give the fault back to windows and let breakpoint occur

SingleStepMyFault:				;set up the single stepper
	call	set_pages_present		;mark pages present
	call	IsThisMyThread			;is my thread? if so returns thread database into which we write
	test	eax,eax				;a check flag, Single step pending, for our INT 1 handler to recognize
	jz	NoBase

	mov	dword [eax+SingleStepPending],1	;flag mentioned above :))

NoBase:						;something odd has happened if there is no thread database!
	or	dword [ebp+0x2c],0x10100	;set trap flag and resume flag

	mov	ebx,cr2				;whats my fault again?
	push	ebx
	call	validate_page_acessed		;has it been accessed?	
	test	eax,eax
	jz	PassDownForPagein		;if not we pass down for paging in

	inc	dword [stpfcount]		;if it has we return to faulting instruction
	pop	es
	pop	ds
	popad
	popfd
	retn


PassDownForPagein:				;this fault has been passed down to be single stepped so
	inc	dword [stpinitcount]		;that windows can bring a page for it and fill it with the things
						;that should be on it

PassDown:					;pass the fault down the chain

						;pass down the fault and let windows deal with it
	pop	es
	pop	ds
	popad
	popfd
	jmp	[ss:StOldPfH]
;-------------------------------------------------------------------------------------------
;pagefaults should have been handled more or less properly
;------------------------------------------------------------------------------------------        


;-------------------------------------------------------------------------------
;Step one instruction with pages set to present
;this stepper is also used for pages that are being paged in by the OS
;in thoose cases we simply single step and the mark pages not present again
;-------------------------------------------------------------------------------  

StStepInstruction:
	pushfd
	pushad

	push	ds
	push	es
	mov	ax,ss
	mov	ds,ax
	mov	es,ax
	
	mov	eax,cr3					;cycle cr3 and flush translation buffers
	mov	cr3,eax
	
	call	IsThisMyThread				;if this isnt my thread it should not be handled
	test	eax,eax					
	jz	near PassDownToRealInt1

	mov	ebx,eax
	cmp	dword [ebx+SingleStepPending],1		;is Single Step pending for this thread atm?
	jnz	near PassDownToRealInt1_		;if not we pass it down, thread may be single stepping itself

	mov	dword [ebx+SingleStepPending],0		;or setting breakpoints

							;Single step was pending, this means that thread has indeed taken
							;a step and needs to have its page present bits cleared and so on
							
	and	dword [esp+0x34],0xfffffeff		;clear trap flag	
	or	dword [esp+0x34],0x10000		;set resume flag

examine_fault:						;we need to examine the fault
	call	set_pages_present			;mark pages present again, they are already present but better make sure

	mov	eax,dword [esp+0x2c]			;get eip
	mov	ebx,[stpage]				;to examine if fault is mine to pop winice up on

	cmp	eax,ebx
	jb      near Dont_popup

	and	ebx,0xfffff000				;round off low eip to even page
	add	ebx,[stblock]
	cmp	eax,ebx					
	jae     near Dont_popup

;-------------------------------------------------------------------------------------------
;collect useless fault/trace data print that and then pop up winice
;this code below will almost never execute, only on range being on a page boundry just brought
;in almost all cases the winice popup will be handled by breakpoint (see breakpoint being set above :)
;-------------------------------------------------------------------------------------------
	Trace_Out "ICEDUMP: EIP in range"

	call	UnprotectIdt
	call	Deactivate_Step			;disable all my things

        VMMCall  Get_Cur_Thread_Handle			;who are we?
        mov      ebx,eip_r_thread
        add      ebx,0x22
        push     ebx
        push     edi
        call     Dword2Hex
        sub      ebx,0x22
        push     ebx
        VMMCall _Trace_Out_Service        		;we tell user who we are

        pop	es
        pop     ds
        popad
        popfd
        call	[ss:pWiniceMain]
	iretd	

Dont_popup:
	call	set_pages_not_present			;we dont pop up so we mark pages not present again
							;and thread will fault again and we repeat and so on
	pop	es
	pop	ds
	popad
	popfd
	iretd

PassDownToRealInt1_:        
	call	set_pages_not_present

PassDownToRealInt1:
							;if fault wasnt in my thread or if Single Step wasnt
	inc	dword [DbAccessCount]			;pending the fault is passed on to real INT 1 handler
	pop	es
	pop	ds
	popad
	popfd
	jmp	[ss:SavedSingleStep]
;-------------------------------------------------------------------------------------------
;
;------------------------------------------------------------------------------------------        


;-------------------------------------------------------------------------------------------
;Test if the thread is one of mine and if so it returns the thread database
;this is my little utility function :))))))))))))))))
;------------------------------------------------------------------------------------------        
        
IsThisMyThread:
	pushad	
	VMMCall Get_Cur_Thread_Handle
	mov	ebx,edi
	mov	esi,[StThreadDb] 
	mov	ecx,0x80	
	sub	eax,eax
_SeekThreadOwner:
	cmp	ebx,[esi+eax*8]
	jz	YesItsMine
	inc	eax
	loop	_SeekThreadOwner
	popad
	sub	eax,eax
	ret	
YesItsMine:	
	lea	eax,[esi+eax*8]
  	mov	eax,[eax+4]	
	mov	[esp+0x1c],eax
	popad
	ret        

;-------------------------------------------------------------------------------------------
;
;------------------------------------------------------------------------------------------        











;-------------------------------------------------------------------------------
;Breakpoint popup routine
;
;If the Pagefault handler finds that EIP is in the desired range AND that the 
;page currently faulting is indeed paged in, a breakpoint is set and handled by
;this handler, this is the way that winice is almost always popped up
;since its not common that a page is initilized by execution (in programs that unpack
;themselves i mean, otherwise its quite common :)
;-------------------------------------------------------------------------------

stbreakpoint:
	pushfd
	pushad
	push	ds
	push	es
	mov	ax,ss
	mov	ds,ax
	mov	es,ax

	call	restore_breakpoint	       ;give back int 3
        
        VMMCall Get_Cur_Thread_Handle		;same as the things in the int 1 handler above
        mov      ebx,eip_r_thread		;for printing to user etc
        add      ebx,0x22
        push     ebx
        push     edi
        call     Dword2Hex
        sub      ebx,0x22
        push     ebx
        VMMCall _Trace_Out_Service        
        

        dec	dword [esp+0x2c]		;decrease EIP so we can restore breakpoint byte
        mov	ebx,[esp+0x2c]			;get eip (the decreased one :)	
        pop	es
        pop     ds
        mov	al,[ss:bpxByte]			;write back byte (again im sorry for the static data)
        mov	byte [ebx],al			;write! 
        popad
        popfd
        call	[ss:pWiniceMain]
	iretd	
;-------------------------------------------------------------------------------------------
;
;------------------------------------------------------------------------------------------        

	








































     
;-------------------------------------------------------------------------------
; STEP <Low EIP> <Block Length> 
;-------------------------------------------------------------------------------
Parse_Step:
        
        cmp     byte [StepActive],1
        jnz     step_not_active
                   
        call    unhook_page_fault
        call    restore_singlestep
        cmp     byte [StepActive],0
        jmp     step_deactivate
        
        step_not_active:
	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_BadlowEIP
	call	ParseExpression			; parse <EIP low>
	jb	near Parser.errorMsg

        cmp     eax,0xc0000000
        jae     near OutOfRange
        
        cmp     eax,0x400000
        jb      near OutOfRange  

	mov	[stpage],eax

	call	[pSkipWhiteSpace]		; skip to <block length>
	mov	eax,[stpage]
	jz	@F

	mov	edi,Error_Badblocksize
	call	ParseExpression			; parse <block length>
	jb	near Parser.errorMsg
        and     eax,0xfffff000                 

@@
        cmp     eax,0x1000
        jb      near Error_Step
        cmp     eax,0x400000
        ja      near Error_Step
        mov     [stblock],eax

        push    byte HEAPZEROINIT
        push    dword 0x1000
        VMMCall _HeapAllocate
        test    eax,eax
        jz      near Failed_to_allocate_thread_db
        add     esp,8
        mov     [StThreadDb],eax               ;create a thread database where all thread handles are stored
;       Trace_Out "ICEDUMP: Created thread information database"  

        VxDCall	VWIN32_GetCurrentProcessHandle
        mov     [stpdb],eax
        

	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]

        call    SaveSingleStep

        
        push	byte SERVICE_STEP
	mov	ebp,[dClient_EAX]
	pop	dword [ebp]
	
	call	SetCBX
	
        mov	ebp,[fExecuteMoreCommands]	; set internal Winice flag to 0
	mov	byte [ebp],0	
	
	mov     byte [StepActive],1
      
        popad
        retn

OutOfRange:
        Trace_Out "Low eip is not in allowed range 0x400000 - 0xC000000"
        popad
        retn


Error_Step:
        Trace_Out "block must be between 0x1000 and 0x400000"
        popad
        retn
        
Failed_to_allocate_thread_db:
        mov     dword [stpdb],0
        mov     byte [ServStepActive],0
        mov     byte [StepActive],0
        Trace_Out "Failed to allocate thread information database"
	popad
       	retn       
        
        
        

step_deactivate:
        push	byte SERVICE_STEP
	mov	ebp,[dClient_EAX]
	pop	dword [ebp]
	
	call	SetCB
	
        mov	ebp,[fExecuteMoreCommands]	; set internal Winice flag to 0
	mov	byte [ebp],0	

        popad
        retn




;-------------------------------------------------------------------------------
;Set up PF environment for STEP (or switch context to remove if not popup occurs)
;-------------------------------------------------------------------------------


Service_Step:
        cmp     byte [ServStepActive],1
        jnz     Step_not_yet_active 
        call    Deactivate_Step
        mov     dword [stpfcount],0      ;reset fault counter
        mov     dword [stpinitcount],0   ;reset step counter
        jmp     EndService
        
Step_not_yet_active:
        VxDCall	VWIN32_GetCurrentProcessHandle
	mov      [stpdb],eax		;in case i need to know what process im in

        VMMCall Get_Cur_Thread_Handle
        VMMCall _GetCurrentContext   
        mov     [StContext],eax		;or maybe what context im in
        
        mov     ebx,edi             ;save thread handle
        mov     edi,[StThreadDb]
                
        mov     dword [edi],ebx   ;store handle in database
        
        push     byte HEAPZEROINIT
        push     dword 0x100
        VMMCall  _HeapAllocate   
   	mov	 [edi+4],eax
        add	 esp,8
        mov	 ebx,eax
        mov	 eax,dr0
        mov	 [ebx],eax
        mov	 eax,dr1
        mov	 [ebx+4],eax
        mov	 eax,dr2
        mov	 [ebx+8],eax
        mov	 eax,dr3
        mov	 [ebx+0xc],eax
        mov	 eax,dr6
        mov	 [ebx+0x10],eax
        mov	 eax,dr7
        mov	 [ebx+0x14],eax

        
        
;        Trace_Out "ICEDUMP: Created thread specific data chunk"         
        

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

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

	push	dword [_EIP]
	pop	dword [ebp+CRS.EIP]
	
        mov     byte [ServStepActive],1

	cmp	dword [ProtectedISRs],0
	jnz	AlreadyExists
	call	MakeSpaceForProtectedVectors
AlreadyExists:

	call	ProtectIdt		;why call this twice here?, because i was tired lazy and needed the
	call	UnprotectIdt		;protIdt to initialize so i can clone it later
	call    StHookPageFault		;hook pm faults because its safer
	call	hook_single_step	;not hook pm fault because i dont trust that :)
        call	CloneIDT		;this also builds the protected ISR's
	call	ProtectIdt
EndService:	
	popfd
	popad
       	retn

;-------------------------------------------------------------------------------
;Remove all my things except protected vectors, thoose stay until icedump unloads
;-------------------------------------------------------------------------------
Deactivate_Step:
	call	 UnprotectVectors
	call	 FreeClonedIDT
        call     unhook_page_fault
        call     restore_singlestep

	mov	 ebx,DebugCounter
	add	 ebx,9
	push	 ebx
	push	 dword [DbAccessCount]
        call     Dword2Hex
        sub	 ebx,9
        push     ebx
        VMMCall _Trace_Out_Service        
	
	mov	 ebx,IllegalIDTAccess
	add	 ebx,27
	push	 ebx
	push	 dword [IdtAccessCount]
        call     Dword2Hex
        sub	 ebx,27
        push     ebx
        VMMCall _Trace_Out_Service   	
	
        mov      ebx,stfaultlogout
        add      ebx,0x16
        push     ebx
        push     dword [stpfcount]
        call     Dword2Hex
        add      ebx,0x1c
        push     ebx
        push     dword [stpinitcount]
        call     Dword2Hex
        sub      ebx,0x32
        push     ebx
        VMMCall _Trace_Out_Service
        
        mov      dword [stpfcount],0      ;reset fault counter
        mov      dword [stpinitcount],0   ;reset step counter
      
        mov	ecx,0x80
        sub	eax,eax
        mov	esi,[StThreadDb]
ReleaseAllthreadDatas:
        inc	eax
        mov	ebx,[esi+eax*4]
        test	ebx,ebx
      	jz	DontFreeThreadSlot
      	pushad
        push    byte 0
        push    ebx
        VMMCall	_HeapFree
        add     esp,8      
;        Trace_Out "ICEDUMP: Destroyed thread specific data chunk"               
      	popad
      	mov	dword [esi+eax*4],0
      	dec	eax
      	mov	dword [esi+eax*4],0
	inc	eax
DontFreeThreadSlot:
	inc	eax
	loop	ReleaseAllthreadDatas      
      
        push    byte 0
        push    dword [StThreadDb]
        VMMCall	_HeapFree
        add     esp,8        
;        Trace_Out "ICEDUMP: Freed thread information database"        
        test    eax,eax
        jnz     did_not_fail_to_free_thread_database
        Trace_Out "ICEDUMP: Failed to free thread information database"
did_not_fail_to_free_thread_database:        

        VMMCall Get_Cur_Thread_Handle                    
        VMMCall _GetCurrentContext                       
        mov     [StOffcontext],eax
        mov     ebx,eax
        mov     eax,[StContext]
        cmp     eax,ebx
        jz      DontSwitchContextIfTheyMatch
       
        push    ebx
        VMMCall _ContextSwitch                           
        add     esp,4

        call    set_pages_present    

        push    dword [StOffcontext]
        VMMCall _ContextSwitch                           
        add     esp,4
        mov     byte [ServStepActive],0

        jmp     PageTablesRestored
        
DontSwitchContextIfTheyMatch:
        call    set_pages_present  
    
PageTablesRestored:
;        Trace_Out "ICEDUMP: Stepping stopped handlers uninstalled"      
        mov     dword [stpdb],0
        mov     byte  [ServStepActive],0
        mov     byte  [StepActive],0
        mov     dword [StThreadDb],0
	mov	dword [DbAccessCount],0
	mov	dword [IdtAccessCount],0        
     	ret

;------------------------------------------------------------------------------------
;
;------------------------------------------------------------------------------------






;----------------------------------------------------------------------------------------
;Thread exit proc, look up thread, am i active? is it mine? is it the last in my process? 
;----------------------------------------------------------------------------------------   

StepThreadNotExecutable:
        pushad                ;edi is r0tcb
        
        cmp      byte  [StepActive],1
        jnz      near DoNothingWithPageTables
        
        mov      ecx,0x100    ;max thread count 100
        mov      eax,[StThreadDb]   ;get thread log             
SeekOutThreadHandles1:               
        cmp      edi,dword [eax]
        jz       OneOfTheStepThreadsDied
        add      eax,4
        loop     SeekOutThreadHandles1
        jmp      DoNothingWithPageTables
                

OneOfTheStepThreadsDied:
        mov      dword [eax],0            ;clear this thread slot        
   	mov	 ebx,eax
       	mov	 eax,[eax+4]
       	test	 eax,eax
       	jz	 NotFreeNothing
        push     byte 0
        push     eax
        VMMCall	 _HeapFree
        add	 esp,8
;        Trace_Out "ICEDUMP: Destroyed thread specific data chunk"        
NotFreeNothing:
 	mov	 dword [ebx+4],0
 	       
        mov      ebx,threaddied
        add      ebx,0x1c   
   
        push     ebx
        push     edi
        call     Dword2Hex
        
        sub      ebx,0x1c
        push     ebx
        VMMCall _Trace_Out_Service

        mov      ecx,0x80
        mov      eax,[StThreadDb]   ;get thread log
SeekOutThreadHandles2:               

        cmp      dword [eax],0
        jnz      near DoNothingWithPageTables
        add      eax,8
        loop     SeekOutThreadHandles2
        
ProcessLastThreadDied:
        call     Deactivate_Step        

        popad
        ret
        
;-------------------------------------------------------------------------------
;Thread init proc, am i active? is stepped thread making itself multithreaded??
;------------------------------------------------------------------------------- 

StepThreadInit:
        pushad

        cmp      byte  [StepActive],1
        jnz      near DoNothingWithPageTables

        
        VMMCall _GetCurrentContext 
        cmp      [StContext],eax
        jnz      near DoNothingWithPageTables
        
        
        mov      ecx,0x80
        mov      eax,[StThreadDb]   ;get thread log
SeekOutThreadHandles3:               
        cmp      dword [eax],0
        jz       EmptySlotLocated
        add      eax,8
        loop     SeekOutThreadHandles3

EmptySlotLocated:
        mov      dword [eax],edi      ;save r0tcb in slot  
   	mov	 ebx,eax
        push     byte HEAPZEROINIT
        push     dword 0x100
        VMMCall  _HeapAllocate   
   	mov	 [ebx+4],eax
        add	 esp,8
        mov	 ebx,eax
        mov	 eax,dr0
        mov	 [ebx],eax
        mov	 eax,dr1
        mov	 [ebx+4],eax
        mov	 eax,dr2
        mov	 [ebx+8],eax
        mov	 eax,dr3
        mov	 [ebx+0xc],eax
        mov	 eax,dr6
        mov	 [ebx+0x10],eax
        mov	 eax,dr7
        mov	 [ebx+0x14],eax       
        
        
;        Trace_Out "ICEDUMP: Created thread specific data chunk"         
        
        mov      ebx,threadborn
        add      ebx,0x1c   
   
        push     ebx
        push     edi
        call     Dword2Hex
        
        sub      ebx,0x1c
        push     ebx
        VMMCall _Trace_Out_Service

        popad
        ret

DoNothingWithPageTables:
        popad
        ret
        

;------------------------------------------------------------------------------------
;Clear user bit in pte to protect idt from read/write access from user page
;------------------------------------------------------------------------------------

	
ProtectIdt:
	push	 eax
        push     eax
        push     eax
        sidt     [esp-2]
        pop      eax
        add      esp,4       
        mov	 dword [protIdt],eax
        and      eax,0x0fffff000
        shr      eax,0xa         ;round off to get entry relative to PTE's
	add      eax,0x0ff800000  ;Get address of PTE by adding ff800000
	and	 dword [eax],11111111111111111111111111111011b		
	pop      eax
	ret
	
UnprotectIdt:
	push	 eax
  	mov      eax,[protIdt]      
        and      eax,0x0fffff000
        shr      eax,0xa         ;round off to get entry relative to PTE's
	add      eax,0x0ff800000  ;Get address of PTE by adding ff800000
	or	 dword [eax],100b	
	pop	eax
	ret        

;------------------------------------------------------------------------------------
;Setting user bit make idt normal again (not protected)
;------------------------------------------------------------------------------------



;-------------------------------------------------------------------------------------
;Get variables like page block pte slot low and clear pte bits and hook pf
;------------------------------------------------------------------------------------
StHookPageFault:
        mov      eax,[stpage]
       
        and      eax,0x0fffff000
        shr      eax,0xa         ;round off to get entry relative to PTE's

        add      eax,0x0ff800000  ;Get address of PTE by adding ff800000
        mov      [stpteslot],eax
        
        
        mov      eax,[stblock]
        shr      eax,0xc
        test     eax,eax
        jnz      at_least_one_page
        inc      eax
at_least_one_page:
        mov      [stpcount],eax
       
        call     hook_page_fault
        call     set_pages_not_present
        
        mov      eax,cr3
        mov      cr3,eax              ;flush tlb's
        ret
;-------------------------------------------------------------------------------
;Simply hook or unhook pf handler
;-------------------------------------------------------------------------------

hook_page_fault:
        mov     esi,dword StPfHandler
	mov	eax,0xe
	VMMCall	Hook_PM_Fault
	mov	[StOldPfH],esi
        ret
        
        
unhook_page_fault:
        mov     esi,dword StPfHandler
	mov	eax,0xe
	VMMCall	Unhook_PM_Fault		
        mov     esi,[StOldPfH]
	mov	eax,0xe
	VMMCall	Hook_PM_Fault
        ret

;-------------------------------------------------------------------------------
;Setting all pages that have been accessed to present in pte's
;-------------------------------------------------------------------------------

set_pages_present:
        push     ecx
        push     eax
        mov      ecx,[stpcount]
        mov      eax,[stpteslot]      ;get address of PTE entry for bpr'd page
        
mark_present:
        test     dword [eax],100000b
        jz       not_accessed_Set		;dont modify non existant
        or       dword [eax],1b            ;set present bit
        
not_accessed_Set:
        add      eax,4
        loop     mark_present
        pop      eax
        pop      ecx
        ret
        
;-------------------------------------------------------------------------------
;Setting all pages that have been accessed to NOT present in pte's
;-------------------------------------------------------------------------------

set_pages_not_present:
        push     ecx
        push     eax
        mov      ecx,[stpcount]
        mov      eax,[stpteslot]      ;get address of PTE entry for bpr'd page
        
        mark_not_present:
        test     dword [eax],100000b
        jz       not_accessed_unSet ;dont modify non existant
        and      dword [eax],1111111111111111111111111111110b ;clear present bit and make page fault again when touched

not_accessed_unSet:
        add      eax,4
        loop     mark_not_present
        pop      eax
        pop      ecx
        ret

;-------------------------------------------------------------------------------
;look at accessed bit for a certain page
;-------------------------------------------------------------------------------
validate_page_acessed:
        mov      eax,[esp+4]
        and      eax,0x0fffff000 
        shr      eax,0x0a        
        add      eax,0x0ff800000 
        test     dword [eax],100000b         
        jz       page_not_a
        mov      eax,1
        ret      4             
        page_not_a:
        sub      eax,eax          
        ret      4

;-------------------------------------------------------------------------------
;look at present bit for a certain page (if it has been paged in by init stepper)
;-------------------------------------------------------------------------------        

validate_page_present:
        mov      eax,[esp+4]
        and      eax,0x0fffff000 
        shr      eax,0x0a        
        add      eax,0x0ff800000 
        test     dword [eax],1b         
        jz       page_not_p
        mov      eax,1
        ret      4             
        page_not_p:
        sub      eax,eax          
        ret      4

;-------------------------------------------------------------------------------------
;Hook interrupt 1 single step (we need to let insrtuctions execute on not present page
;-------------------------------------------------------------------------------------  

hook_single_step:
	push	 ebx
        push     eax
        push     eax
        sidt     [esp-2]
        pop      eax
        add      esp,4
        
        add      eax,8
        mov      bx,[eax+6]
        shl      ebx,10h
        mov      bx,[eax]
        mov      [StOldInt1],ebx
        
        mov      ebx,StStepInstruction
        mov      [eax],bx
        shr      ebx,10h
        mov      [eax+6],bx
        pop	 ebx
        ret


unhook_singlestep:
	push	 ebx
        push     eax
        push     eax
        sidt     [esp-2]
        pop      eax
        add      esp,4
        
        add      eax,8        
        
        mov      ebx,[StOldInt1]
        mov      [eax],bx
        shr      ebx,10h
        mov      [eax+6],bx     
	pop	 ebx
        ret

;----------------------------------------------------
;we save int 1 for emergencies
;----------------------------------------------------
SaveSingleStep:
	push	 ebx
        push     eax
        push     eax
        sidt     [esp-2]
        pop      eax
        add      esp,4
        
        add      eax,8
        mov      bx,[eax+6]
        shl      ebx,10h
        mov      bx,[eax]
        mov      [SavedSingleStep],ebx
        pop	 ebx
        ret

restore_singlestep:
	push	 ebx
        push     eax
        push     eax
        sidt     [esp-2]
        pop      eax
        add      esp,4
        
        add      eax,8        
        
        mov      ebx,[SavedSingleStep]
        mov      [eax],bx
        shr      ebx,10h
        mov      [eax+6],bx     
      	pop	 ebx
        ret

;---------------------------------------------------------------------
;We use int 3 for popping up winice
;---------------------------------------------------------------------

 hook_breakpoint:
 	push	 ebx
        push     eax
        push     eax
        sidt     [esp-2]
        pop      eax
        add      esp,4
        
        add      eax,8*3
        mov      bx,[eax+6]
        shl      ebx,10h
        mov      bx,[eax]
        mov      [StOldInt3],ebx
        
        mov      ebx,stbreakpoint
        mov      [eax],bx
        shr      ebx,10h
        mov      [eax+6],bx
        pop	 ebx
        ret
        
 restore_breakpoint:
 	push	 ebx
        push     eax
        push     eax
        sidt     [esp-2]
        pop      eax
        add      esp,4
        
        add      eax,8*3        
        
        mov      ebx,[StOldInt3]
        mov      [eax],bx
        shr      ebx,10h
        mov      [eax+6],bx
        pop	 ebx     
        ret
        
;-------------------------------------------------------------------------------
;we also use it to pop up winice if idt access is attempted (sorry for using two 
;different ones for the same thing, but it just felt safer this way)
;-------------------------------------------------------------------------------    
 
hook_isr1_idt:
	push	 ebx
        push     eax
        push     eax
        sidt     [esp-2]
        pop      eax
        add      esp,4
        
        add      eax,8
        mov      bx,[eax+6]
        shl      ebx,10h
        mov      bx,[eax]
        mov      [StOldInt1_idt],ebx
        
        mov      ebx,idtprotectstep
        mov      [eax],bx
        shr      ebx,10h
        mov      [eax+6],bx
        pop	 ebx
        ret
        
unhook_isr1_idt:
	push	 ebx
        push     eax
        push     eax
        sidt     [esp-2]
        pop      eax
        add      esp,4
        
        add      eax,8
        
        mov      ebx,[StOldInt1_idt]
        mov      [eax],bx
        shr      ebx,10h
        mov      [eax+6],bx
        pop	 ebx     
        ret

;---------------------------------------------------------------------
;
;---------------------------------------------------------------------

CloneIDT:
	pushad
        push     byte HEAPZEROINIT
        push     dword 0x1000
        VMMCall  _HeapAllocate  	
	mov	 [ShadowIdt],eax	;create a copy of IDT to reload with on IDT access
	add	 esp,8
	mov	 esi,[protIdt]
	mov	 ecx,0x400
	mov	 edi,eax
	rep	 movsd
	
        push     byte HEAPZEROINIT
        push     dword 0x1000
        VMMCall  _HeapAllocate  	
	mov	 [BackupIdt],eax	;also create a backup IDT in case the other two get damaged
	add	 esp,8			;it happens so easily for me
	mov	 esi,[protIdt]
	mov	 ecx,0x400
	mov	 edi,eax
	rep	 movsd

	call	 HookAllVectors
	call	 ProtectVectors
	popad
	ret

FreeClonedIDT:
	pushad
	mov	 edi,[protIdt]
	mov	 ecx,0x400
	mov	 esi,[BackupIdt]	;put back backup idt in case someone (or me) ruined it :~
	rep	 movsd
        push    byte 0
        push	dword [ShadowIdt]	;free the shadow
        VMMCall	_HeapFree
	add	esp,8
        
        push    byte 0			
        push	dword [BackupIdt]	;free the backup
        VMMCall	_HeapFree
	add	esp,8
        
	popad
	ret


;---------------------------------------------------------------------
;Allocate a page for ISR stubs, not heapalloc becuase it appears not safe
;this page stays the same as long as icedump doesnt unload
;---------------------------------------------------------------------

MakeSpaceForProtectedVectors:
	pushad
	push	byte 0xf
	push	byte 0
	push	dword 0xffffffff
	push	byte 0
	push	byte 0
	push	byte 1
	push	byte 1
	VMMCall	_PageAllocate
	mov	[ProtectedISRs],eax
	add	esp,7*4
	popad
	retn


;-------------------------------------------------------------------------------------------
;Freeing the protected vectors are done when icedump unloads (extern called by icedump.asm)
;-------------------------------------------------------------------------------------------

FreeProtectedVectors:
	pushad
	push	dword [ProtectedISRs]
	VMMCall	_PageFree
	add	esp,byte 4
	mov	dword [ProtectedISRs],0
	popad
	retn


;------------------------------------------------------------------------------------------------------
;This is the protection of the ISR's on the hook page for all of them (not 0xE because i dont know how)
;------------------------------------------------------------------------------------------------------
ProtectVectors:
	push	eax
	mov	eax,[ProtectedISRs]
	and	eax,0x0fffff000
	shr	eax,0xa		;round off to get entry relative to PTE's
	add	eax,0x0ff800000	;Get address of PTE by adding ff800000
	and	dword [eax],11111111111111111111111111111011b
	pop	eax
	retn


UnprotectVectors:
	push	eax
	mov	eax,[ProtectedISRs]
	and	eax,0x0fffff000
	shr	eax,0xa		;round off to get entry relative to PTE's
	add	eax,0x0ff800000	;Get address of PTE by adding ff800000
	or	dword [eax],100b
	pop	eax
	retn
;------------------------------------------------------------------------------------
;
;------------------------------------------------------------------------------------	


;------------------------------------------------------------------------------------
;All ISR's are placed on a protected page for us to filter what the program can and
;can not do to them, the program is as always free to reroute all interrupts
;except int 1 and int 14
;------------------------------------------------------------------------------------
HookAllVectors:
	pushad
	
	mov	edi,[ProtectedISRs]
	mov	ecx,0xe
	sub	edx,edx

HookAllVectors_:	
	add	edi,0x80		;space for winice popup routines if need be :)
	lea	ebx,[edi+7]
	mov	dword [edi],0x0025ff36	;jmp dword ptr ss:x
	mov	[edi+3],ebx		;where is ptr

	push	edx
	call	GetIdtVector
	mov	[ebx],eax		;what does ptr contain? 
	
	push	edi
	push	edx
	call	SetIdtVector
	inc	edx

	loop	HookAllVectors_

	popad
	retn


;---------------------------------------------------------------------
;Get a vector or set a vector, 
;---------------------------------------------------------------------

GetIdtVector:
	push	ebx
	mov	ebx,[esp+8]	
	shl	ebx,3		;vector rva
	mov	eax,[protIdt]
	add	eax,ebx

	mov	bx,[eax+6]
	shl	ebx,10h
	mov	bx,[eax]
	mov	eax,ebx	
	pop	ebx
	retn	4


SetIdtVector:
	push	ebx
	mov	ebx,[esp+0xc]
	mov	eax,[esp+0x8]
	shl	eax,3
	add	eax,[protIdt]

	mov	[eax],bx
	shr	ebx,10h
	mov	[eax+6],bx
	pop	ebx
	retn	8
;------------------------------------------------------------------------------------
;
;------------------------------------------------------------------------------------


segment _LDATA
	align 4
stpteslot:	dd 0
stpcount:	dd 0
StOldPfH:	dd 0
OpfIdt:		dd 0

_EAX:		dd 0
_CS:		dd 0
_EIP:		dd 0

SavedSingleStep:	dd 0
StOffcontext:		dd 0
StContext:		dd 0
StThreadDb:		dd 0
StThreadCount:		dd 0
StepActive		db 0
ServStepActive		db 0

threaddied:		db 'ICEDUMP: Thread died r0tcb:          ',13,10,0
threadborn:		db 'ICEDUMP: Thread born r0tcb:          ',13,10,0
Error_BadlowEIP:	db 'invalid EIP low',0
Error_Badblocksize:	db 'invalid block size',0
