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


global Parse_SuspendX
global Parse_Suspend
global Parse_Resume
global Parse_Kill
global Service_SuspendResumeKill


extern sdata
extern Parser.error
extern Parser.errorMsg
extern Error_V86
extern Error_PM16
extern Error_NoID
extern SetCB
extern SetCBX
extern IsPageCommitted
extern R3TID
extern R3PID
extern R3TCB.SuspendCount
extern R3TCB.R0TCB
extern R3TCB.Flags
extern VWIN32.W32_ResumeThread
extern VWIN32.W32_SuspendThread
extern VWIN32.W32ServiceTable
extern ParseExpression


bits 32


;-------------------------------------------------------------------------------
; SUSPEND <PID>|<TID>
; SUSPENDX <PID>|<TID>
; RESUME <PID>|<TID>
; KILL <PID>
;-------------------------------------------------------------------------------
segment _LTEXT
Parse_SuspendX:
	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_NoID
	call	ParseExpression			; parse <pid>|<tid>
	jb	near Parser.errorMsg

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

	mov	ebp,[dClient_ESI]
	push	dword [ebp]
	pop	dword [.ESI]

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

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

	mov	ebp,[dClient_ESI]		; store pid or tid
	mov	[ebp],eax

; set up registers for service
	push	byte SERVICE_SUSPENDX
	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
.ESI:	dd 0
.CS:	dd 0
.EIP:	dd 0


segment _LTEXT
Parse_Suspend:
	push	byte SERVICE_SUSPEND
	jmp	short @F

Parse_Resume:
	push	byte SERVICE_RESUME
	jmp	short @F

Parse_Kill:
	push	byte SERVICE_KILL

@@
	pop	ebx

	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_NoID
	call	ParseExpression		; parse <pid>|<tid>
	jb	near Parser.errorMsg

	mov	ebp,[dClient_ESI]	; store pid or tid
	mov	[ebp],eax

	mov	ebp,[dClient_EAX]	; set up service
	mov	[ebp],ebx

	call	SetCB

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

	popad
	retn


Service_SuspendResumeKill:
	mov	eax,[ebp+CRS.ESI]	; get xID
	cmp	eax,0xF0000000
	jb	.is_dbase

	mov	ecx,[dK32XOR]		; ECX: obfuscator
	mov	ecx,[ecx]
	cmp	ecx,byte 0		; is obfuscator initialized?
	mov	ebx,.Obf_Error
	jz	.error

	xor	eax,ecx			; EAX: Thread or Process Database

.is_dbase:
	push	eax			; save it for later use
	shr	eax,12			; convert it to page number
	mov	ebx,.Mem_Error
	call	IsPageCommitted		; can we access the corresponding page?
	pop	eax			; back to linear address
	jz	.error

	mov	ebx,.Obj_Error
;	cmp	eax,0x80000000		; PDB/TDB are above private arena
;	jb	.error
	bt	eax,31
	jnc	.error

	xor	ecx,ecx

	mov	bl,[R3TID]
	cmp	[eax],bl
	jnz	@F

	mov	ebx,.Kill_Error
	cmp	dword [ebp+CRS.EAX],byte SERVICE_KILL
	jz	.error
	jmp	short .handle_thread

@@
	mov	bl,[R3PID]
	cmp	[eax],bl
	mov	ebx,.Obj_Error
	jnz	.error

	cmp	dword [ebp+CRS.EAX],byte SERVICE_KILL
	jnz	@F

	call	KillProcess
	jc	.error

	jmp	short .return

@@
	mov	ecx,[eax+0x50]		; get ThreadList
	mov	ecx,[ecx]		; get Thread_Ref pointer

.more_threads:
	mov	eax,[ecx+8]		; get Thread Database
	mov	ecx,[ecx]		; prefetch next Thread_Ref

.handle_thread:
	call	SuspendResumeThread
	jnc	.continue

	push	dword .Sch_Error
	VMMCall	_Trace_Out_Service

.continue:
	jecxz	.return			; was it last/only thread to deal with?
	jmp	short .more_threads

.error:
	push	ebx
	VMMCall	_Trace_Out_Service

.return:
	cmp	dword [ebp+CRS.EAX],byte SERVICE_SUSPENDX
	jnz	.done

; restore client EAX/ESI/CS/EIP
	push	dword [Parse_SuspendX.EAX]
	pop	dword [ebp+CRS.EAX]

	push	dword [Parse_SuspendX.ESI]
	pop	dword [ebp+CRS.ESI]

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

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

.done:
	popfd
	popad
	retn


segment _LDATA
	align 4
;------------------------------------------------------------------------------
.Sch_Error db 'failed to suspend/resume thread #EAX.',CRLF0
.Kill_Error db 'cannot kill object #EAX, it is not a process object.',CRLF0
.Obj_Error db 'object #EAX is not a valid thread or process object.',CRLF0
.Obf_Error db 'obfuscator is not yet initialized.',CRLF0
.Mem_Error db 'page #EAX is not committed. verify command line.',CRLF0
;------------------------------------------------------------------------------


segment _LTEXT

;------------------------------------------------------------------------------
; in:  EAX: thread, EBP: Client registers
; out: clc: ok, stc: error
;------------------------------------------------------------------------------
segment _LTEXT
SuspendResumeThread:        
	pushad
	cmp	eax,0xF0000000		; check if it's ThreadId
	jb	.is_dbase

	mov	esi,[dK32XOR]
	xor	eax,[esi]		; EAX = Thread Database

.is_dbase:
	mov	edx,.sfClient		; get fake client regs
	mov	edi,[VWIN32.W32ServiceTable]
	mov	ebx,[R3TCB.SuspendCount]
	mov	ecx,[R3TCB.R0TCB]

.select:
	cmp	dword [ebp+CRS.EAX],byte SERVICE_SUSPENDX
	jz	.suspendx

	cmp	dword [ebp+CRS.EAX],byte SERVICE_SUSPEND
	jz	.suspend

	cmp	dword [ebp+CRS.EAX],byte SERVICE_RESUME
	jz	.resume

.error:
	stc
	popad
	retn

.success:
	clc
	popad
	retn

.suspend:
	test	byte [ebp+CRS.CS],3	; skip this test if called from ring-0
	jz	.suspendx

	push	edi
	VMMCall	Get_Cur_Thread_Handle
	cmp	edi,[eax+ecx]		; do not suspend the current one
	pop	edi
	jz	.error

.suspendx:
;	cmp	dword [eax+ebx],byte 0	; test suspension counter
;	jnz	.success		; already suspended?

	mov	esi,[R3TCB.Flags]
	test	byte [eax+esi],0x40	; thread created suspended?
	jnz	.s_update_r3

	mov	esi,[R3TCB.Flags]
	test	byte [eax+esi+3],0x10
	jnz	.error

	push	ebx			; save suspension counter index
	push	eax			; save TDB, EAX will get trashed

	push	dword [eax+ecx]		; ring0TCB
	push	byte 0			; fake VM handle
	push	edx			; Client Regs        
	mov	esi,[VWIN32.W32_SuspendThread]
	lea	esi,[edi+esi*8+8]
	call	[esi]

	cmp	eax,byte -1
	pop	eax			; restore EAX
	pop	ebx			; restore suspension counter index
	jz	.error

.s_update_r3:
	inc	dword [eax+ebx]		; increment suspension counter

	jmp	short .success

.resume:
	cmp	dword [eax+ebx],byte 0	; test suspension counter
	jz	.success		; already running?

	mov	esi,[R3TCB.Flags]
	test	byte [eax+esi],0x40	; thread created suspended?
	jnz	.r_update_r3

	mov	esi,[R3TCB.Flags]
	test	byte [eax+esi+3],0x10
	jnz	near .error

	push	ebx
	push	eax

	push	dword [eax+ecx]		; Ring0 Thread handle
	push	byte 0			; fake VM handle
	push	edx			; fake Client Registers     
	mov	esi,[VWIN32.W32_ResumeThread]
	lea	esi,[edi+esi*8+8]
	call	[esi]

	cmp	eax,byte -1		; error?
	pop	eax
	pop	ebx
	jz	near .error

.r_update_r3:
	dec	dword [eax+ebx]		; decrement suspension counter

	clc
	popad
	retn


segment _LDATA
	align 4
.sfClient TIMES 8 db 'SGER'


segment _LTEXT
KillProcess:
	mov	ecx,eax
	VxDCall	VWIN32_GetCurrentProcessHandle
	test	eax,eax
	jnz	@F

	mov	ebx,.Err_NoCurrProc
	stc
	retn

@@
	cmp	eax,ecx			; don't kill current process
	jnz	@F

	mov	ebx,.Err_Current
	stc
	retn

@@
	mov	ecx,[ecx+0x50]		; get ThreadList
	mov	ecx,[ecx]		; get Thread_Ref pointer
	mov	ecx,[ecx+8]		; get Thread Database
	add	ecx,[R3TCB.R0TCB]
	mov	edi,[ecx]
	VxDCall	VWIN32_TerminateApp
	clc
	retn


segment _LDATA
.Err_NoCurrProc	db 'no current process',CRLF0
.Err_Current	db 'cannot kill current process',CRLF0
