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


global GetAPIs
global GetK32Info
global IsPageCommitted
global IsPagePresent
global SaveRegs
global RestoreRegs
global ParseAddress
global ParseExpression
global GetFileName1
global GetFileName2
global IsThreadKernel32
global IsThreadWin32
global FindKernel32Thread
global FindWin32Thread
global GetVideoMem

global API.oGetCurrentProcessID
global API.oFindWindowA
global API.oSendMessageA
global API.oOpenProcess
global API.oResumeThread
global API.oSuspendThread
global API.oExitThread
;global API.oExitProcess
;global API.oTerminateThread
;global API.oTerminateProcess
global API.oORD_0017
global API.oGetTickCount
global API.oVirtualAlloc
global API.oVirtualFree
global API.oGetLastError
global API.oGlobalAlloc
global API.oGlobalLock
global API.oGlobalUnlock
global API.oGlobalFree
global API.oOpenClipboard
global API.oGetClipboardData
global API.oSetClipboardData
global API.oCloseClipboard
global API.oEmptyClipboard

global R3PID
global R3TID
global R3TCB.Flags
global R3TCB.R0TCB
global R3TCB.TDBX
global R3TCB.SuspendCount
global oMTEList

global Error_V86
global Error_PM16
global Error_PMR0
global Error_NoID


extern sdata
extern entryPMR0
extern wcrs
extern VWIN32.TDS
extern TDBX.pPDB


bits 32


segment _LTEXT
;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
GetAPIs:
	push	esi
	push	ecx
	push	eax

	or	ecx,byte -1

.loop:
	inc	ecx
	cmp	dword [8*ecx+API+4],byte 0
	jnz	@F

	pop	eax
	pop	ecx
	pop	esi

	clc
	retn

@@
	cmp	dword [8*ecx+API],byte 0
	jnz	.loop

	mov	esi,[8*ecx+API+4]
	
	call	ParseAddress
	jnb	@F

	pop	eax
	pop	ecx
	pop	esi

	stc
	retn

@@
	mov     [8*ecx+API],eax
	jmp	short .loop


segment _LDATA
	align 4
API:
.oGetCurrentProcessID:	dd 0, .GetCurrentProcessID
.oFindWindowA:		dd 0, .FindWindowA
.oSendMessageA:		dd 0, .SendMessageA
.oOpenProcess:		dd 0, .OpenProcess
.oResumeThread:		dd 0, .ResumeThread
.oSuspendThread:	dd 0, .SuspendThread
.oExitThread:		dd 0, .ExitThread
;.oExitProcess:		dd 0, .ExitProcess
;.oTerminateThread:	dd 0, .TerminateThread
;.oTerminateProcess:	dd 0, .TerminateProcess
.oORD_0017:		dd 0, .ORD_0017
.oGetTickCount:		dd 0, .GetTickCount
.oVirtualAlloc:		dd 0, .VirtualAlloc
.oVirtualFree:		dd 0, .VirtualFree
.oGetLastError:		dd 0, .GetLastError
.oGlobalAlloc:		dd 0, .GlobalAlloc
.oGlobalLock:		dd 0, .GlobalLock
.oGlobalUnlock:		dd 0, .GlobalUnlock
.oGlobalFree:		dd 0, .GlobalFree
.oOpenClipboard:	dd 0, .OpenClipboard
.oGetClipboardData:	dd 0, .GetClipboardData
.oSetClipboardData:	dd 0, .SetClipboardData
.oCloseClipboard:	dd 0, .CloseClipboard
.oEmptyClipboard:	dd 0, .EmptyClipboard

; null record, do not remove
			dd 0, 0

.GetCurrentProcessID:	db 'GetCurrentProcessID',0
.FindWindowA:		db 'FindWindowA',0
.SendMessageA:		db 'SendMessageA',0
.OpenProcess:		db 'OpenProcess',0
.ResumeThread:		db 'ResumeThread',0
.SuspendThread:		db 'SuspendThread',0
.ExitThread:		db 'ExitThread',0
;.ExitProcess:		db 'ExitProcess',0
;.TerminateThread:	db 'TerminateThread',0
;.TerminateProcess:	db 'TerminateProcess',0
.ORD_0017:		db 'KERNEL32!ORD_0017',0
.GetTickCount:		db 'GetTickCount',0
.VirtualAlloc:		db 'VirtualAlloc',0
.VirtualFree:		db 'VirtualFree',0
.GetLastError:		db 'GetLastError',0
.GlobalAlloc:		db 'GlobalAlloc',0
.GlobalLock:		db 'GlobalLock',0
.GlobalUnlock:		db 'GlobalUnlock',0
.GlobalFree:		db 'GlobalFree',0
.OpenClipboard:		db 'OpenClipboard',0
.GetClipboardData:	db 'GetClipboardData',0
.SetClipboardData:	db 'SetClipboardData',0
.CloseClipboard:	db 'CloseClipboard',0
.EmptyClipboard:	db 'EmptyClipboard',0


segment _LTEXT
;-------------------------------------------------------------------------------
; stc on error
;-------------------------------------------------------------------------------
GetK32Info:
	push	eax
	push	ecx
	push	edi

; get the IDs first
	cmp	dword [R3PID],byte 0
	jnz	.flags

	mov	ecx,64
	mov	edi,[API.oOpenProcess]
	mov	al,0x38			; cmp byte [eax],PID

.1:
	repnz	scasb
	jecxz	.error

	cmp	byte [edi-2],0x80	; cmp byte [eax],PID
	jz	.1a

	cmp	byte [edi-2],0x83	; cmp dword [eax],PID
	jnz	.1

.1a:
	cmp	word [edi+3],0x576A	; push 0x57
	jnz	.1

	movzx	eax,byte [edi]
	mov	[R3PID],eax
	inc	al
	mov	[R3TID],eax

; get R3TCB.SuspendCount and R3TCB.Flags
.flags:
	cmp	dword [R3TCB.Flags],byte 0
	jnz	.r0tcb

	mov	ecx,64
	mov	edi,[API.oResumeThread]
	mov	al,0x8B			; mov eax,[edi+R3TCB.SuspendCount]

.2:
	repnz	scasb
	jecxz	.3

	cmp	byte [edi],0x87		; mov eax,[edi+R3TCB.SuspendCount]
	jnz	.2

	cmp	word [edi+5],0xC085	; test eax,eax
	jnz	.2

	push	dword [edi+1]
	pop	dword [R3TCB.SuspendCount]

	movzx	eax,byte [edi+11]
	mov	[R3TCB.Flags],eax
	jmp	short .5

.error:
	pop	edi
	pop	ecx
	pop	eax

	stc
	retn

.3:
	mov	ecx,64
	mov	edi,[API.oResumeThread]
	mov	al,0x8D			; lea edi,[eax+R3TCB.SuspendCount]

.4:
	repnz	scasb
	jecxz	.error

	cmp	byte [edi],0xB8		; lea edi,[eax+R3TCB.SuspendCount]
	jnz	.4

	cmp	word [edi+5],0x0F8B	; mov ecx,[edi]
	jnz	.4

	push	dword [edi+1]
	pop	dword [R3TCB.SuspendCount]

	movzx	eax,byte [edi+13]
	mov	[R3TCB.Flags],eax
.5:

; get R3TCB.R0TCB
.r0tcb:
	cmp	dword [R3TCB.R0TCB],byte 0
	jnz	.omtelist

	mov	ecx,64
	mov	edi,[API.oSuspendThread]
	mov	al,0x83			; cmp eax,0xFFFFFFFF

.6:
	repnz	scasb
	jecxz	.error

	cmp	word [edi],0xFFF8	; cmp eax,0xFFFFFFFF
	jnz	.6

	dec	edi
	mov	eax,[edi-4]		; get call's destination
	add	edi,eax

	mov	ecx,64
	mov	al,0x8B			; mov eax,[eax+R3TCB.R0TCB]

.7:
	repnz	scasb
	jecxz	.error

	cmp	byte [edi],0x40		; mov eax,[eax+R3TCB.R0TCB]
	jnz	.7

	cmp	word [edi+2],0xC085	; test eax,eax
	jnz	.7

	movzx	eax,byte [edi+1]
	mov	[R3TCB.R0TCB],eax
	add	eax,byte 4
	mov	[R3TCB.TDBX],eax

	mov	eax,[wVMMVersion]
	cmp	word [eax],0x45A	; wme?
	jb	.omtelist

	add	dword [R3TCB.TDBX],byte 0x30

.omtelist:
	cmp	dword [oMTEList],byte 0
	jnz	.success

	mov	ecx,48
	mov	edi,[API.oORD_0017]
	mov	al,0x8B			; mov ecx,oMTEList

.8:
	repnz	scasb
	jecxz	.error2

	cmp	byte [edi],0x0D		; mov ecx,oMTEList
	jnz	.8

	push	dword [edi+1]
	pop	dword [oMTEList]

.success:
	pop	edi
	pop	ecx
	pop	eax

	clc
	retn

.error2:
	pop	edi
	pop	ecx
	pop	eax

	stc
	retn


segment _LDATA
	align 4
R3PID:		dd 0
R3TID:		dd 0

R3TCB:
.Flags:		dd 0
.R0TCB:		dd 0
.TDBX:		dd 0
.SuspendCount:	dd 0

oMTEList:	dd 0


segment _LTEXT
;-------------------------------------------------------------------------------
; in:  EAX: page number,
; out: ZF set on error, cleared otherwise
;-------------------------------------------------------------------------------
; the following piece of code directly accesses the page tables and directories
; to figure out if a specific page has been committed or not. credits go to
; LiuTaoTao for the short code to calculate the offsets into the tables.
;-------------------------------------------------------------------------------
IsPageCommitted:
	push	edi
	push	edx
	mov	edi,0xFF800000		; linear start address of page tables
	lea	edx,[edi+4*eax]		; EAX: current page number
	shr	edx,12			; EDX: current directory number
	test	byte [edi+4*edx+1],0x2	; is page directory committed?
	jz	@F

	test	byte [edi+4*eax+1],0x2	; is page committed?

@@
	pop	edx
	pop	edi
	retn


;-------------------------------------------------------------------------------
; in:  EAX: page number,
; out: ZF set on error, cleared otherwise
;-------------------------------------------------------------------------------
IsPagePresent:
	push	edi
	push	edx
	mov	edi,0xFF800000		; linear start address of page tables
	lea	edx,[edi+4*eax]		; EAX: current page number
	shr	edx,12			; EDX: current directory number
	test	byte [edi+4*edx],0x1	; is page directory present?
	jz	@F

	test	byte [edi+4*eax],0x1	; is page present?

@@
	pop	edx
	pop	edi
	retn


;-------------------------------------------------------------------------------
SaveRegs:
	push	edi
	push	esi
	push	ecx

	mov	esi,[dClient_EAX]
	mov	edi,wcrs+WCRS.dClient_EAX
	mov	ecx,(WCRS_size-WCRS.dClient_EAX)/4
	rep	movsd

	pop	ecx
	pop	esi
	pop	edi
	retn


;-------------------------------------------------------------------------------
RestoreRegs:
	push	edi
	push	esi
	push	ecx

	mov	edi,[dClient_EAX]
	mov	esi,wcrs+WCRS.dClient_EAX
	mov	ecx,(WCRS_size-WCRS.dClient_EAX)/4
	rep	movsd

	pop	ecx
	pop	esi
	pop	edi
	retn


;-------------------------------------------------------------------------------
ParseAddress:
	push	eax
	mov	eax,[fExp2Int_08]
	mov	byte [eax],0
	mov	eax,[fExp2Int_10]
	mov	byte [eax],0
	mov	eax,[fExp2Int_04]
	mov	byte [eax],1
	pop	eax
	call	[pExpression2Integer]
	retn


;-------------------------------------------------------------------------------
ParseExpression:
	push	eax
	mov	eax,[fExp2Int_40]
	mov	byte [eax],1
	pop	eax
	call	[pExpression2Integer]
	push	eax
	mov	eax,[fExp2Int_40]
	mov	byte [eax],0
	pop	eax
	retn


;-------------------------------------------------------------------------------
; get file name for process based on taskdb/hmodule info (thread must run in
; the System VM)
;
; eax: PDB
;
; edi: ptr to file extension
; stc on error
;-------------------------------------------------------------------------------
GetFileName1:
	push	eax
	push	ebx
	push	ecx
	push	edx

	VMMCall	Get_Sys_VM_Handle

	movzx	eax,word [eax+0x38]	; eax: PDB.W16TDB
	VMMCall	_SelectorMapFlat, ebx, eax, byte 0
	cmp	eax,byte -1
	jz	.error

	movzx	eax,word [eax+0x1E]	; eax: TaskDB.hMod
	VMMCall	_SelectorMapFlat, ebx, eax, byte 0
	cmp	eax,byte -1
	jz	.error

	movzx	edi,word [eax+0x0A]	; eax: hMod.OFSTRUCT
	lea	edi,[eax+edi+9]

	mov	ecx,MAX_PATH
	xor	al,al
	repne	scasb
	jne	.error

	sub	edi,byte 1+4
	mov	eax,[edi]
	xor	eax,'.exe'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	.error

	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
	clc
	retn

.error:
	xor	edi,edi
	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
	stc
	retn


;-------------------------------------------------------------------------------
; get file name for process based on cmdline
;
; eax: PDB
;
; edi: ptr to file extension
; stc on error
;-------------------------------------------------------------------------------
GetFileName2:
	push	eax
	push	ecx

; find '.exe"' or '.exe\0' or '.exe ' in cmdline
	mov	eax,[eax+0x40]		; eax: PDB.pEDB
	mov	edi,[eax+8]		; edi: EDB.pCmdLine
	or	ecx,byte -1
	xor	al,al
	repne	scasb

	neg	ecx
	dec	ecx
	sub	edi,ecx			; edi: EDB.pCmdLine
	dec	ecx			; ecx: strlen(cmdline)

.next:
	mov	al,'.'
	repne	scasb
	jne	.error

	mov	eax,[edi]
	xor	eax,'exe'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jz	@F			; detects 'exe\0' and 'exe '

	rol	eax,8
	cmp	eax,byte '"' & 0xDF	; detects 'exe"'
	jnz	.next

@@
	dec	edi
	pop	ecx
	pop	eax
	clc
	retn

.error:
	pop	ecx
	pop	eax
	stc
	retn


;-------------------------------------------------------------------------------
; EDI: R0TCB
; EBP: Client Registers
;
; clc: kernel32
;-------------------------------------------------------------------------------
IsThreadKernel32:
	push	ecx
	push	edi

	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
	call	GetFileName1
	jnc	@F

	mov	eax,ecx
	call	GetFileName1
	jc	.ret

@@
	mov	eax,[edi-7]
	xor	eax,'krnl'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	.ret

	mov	eax,[edi-3]
	xor	eax,'386.'
	and	eax,0xDFDFDFDF		; stricmp ;-)
	jnz	.ret

	pop	edi
	pop	ecx
	clc
	retn

.ret:
	pop	edi
	pop	ecx
	stc
	retn


;-------------------------------------------------------------------------------
; EDI: R0TCB
; EBP: Client Registers
;
; clc: win32
;-------------------------------------------------------------------------------
IsThreadWin32:
	push	eax
	test	byte [ebp+CRS.EFlags+2],2	; is client in V86 mode?
	jnz	.non_win32

	mov	eax,[selKernel32Code]		; is it win32?
	movzx	eax,word [eax]
	cmp	ax,[ebp+CRS.CS]
	jnz	.non_win32

	mov	eax,[selKernel32Data]		; is it win32?
	movzx	eax,word [eax]
	cmp	ax,[ebp+CRS.SS]
	jnz	.non_win32

	pop	eax
	clc
	retn

.non_win32:
	pop	eax
	stc
	retn


;-------------------------------------------------------------------------------
;
; clc: EAX holds R0TCB of a kernel32 thread
;-------------------------------------------------------------------------------
FindKernel32Thread:
	push	ebx
	push	edi
	push	ebp

	VMMCall	Get_Sys_VM_Handle
	VMMCall	Get_Initial_Thread_Handle
	mov     ebx,edi

@@
	mov	ebp,[edi+TCB_ClientPtr]
	call	IsThreadKernel32
	jnc	@F

	VMMCall	Get_Next_Thread_Handle
	cmp	ebx,edi
	jnz	@B

	pop	ebp
	pop	edi
	pop	ebx
	stc
	retn

@@
	mov	eax,edi
	pop	ebp
	pop	edi
	pop	ebx
	retn


;-------------------------------------------------------------------------------
;
; clc: EAX holds R0TCB of a win32 thread
;-------------------------------------------------------------------------------
FindWin32Thread:
	push	ebx
	push	edi
	push	ebp

	VMMCall	Get_Sys_VM_Handle
	VMMCall	Get_Initial_Thread_Handle
	mov     ebx,edi

@@
	mov	ebp,[edi+TCB_ClientPtr]
	call	IsThreadWin32
	jnc	@F

	VMMCall	Get_Next_Thread_Handle
	cmp	ebx,edi
	jnz	@B

	pop	ebp
	pop	edi
	pop	ebx
	stc
	retn

@@
	mov	eax,edi
	pop	ebp
	pop	edi
	pop	ebx
	retn


;-------------------------------------------------------------------------------
; eax: linear address of the video memory as used by winice
;-------------------------------------------------------------------------------
GetVideoMem:
	mov	eax,[oVideoMem]
	mov	eax,[eax]

%if WINICE_VERSION_MAJOR = 3
  %if WINICE_VERSION_MINOR = 22 || WINICE_VERSION_MINOR = 23 || WINICE_VERSION_MINOR = 24
	push	ebx
	mov	ebx,[oLinAddrPhysical_0_MAXPHYS]
	add	eax,[ebx]
	pop	ebx
  %endif
%else
%endif

	retn


segment _LDATA
;-------------------------------------------------------------------------------
; global error messages (used at more than one place)
;-------------------------------------------------------------------------------
Error_V86:	db 'EFLAGS.VM=1, only win32 clients are supported.',0
Error_PM16:	db 'CS.D=0, only win32 clients are supported.',0
Error_PMR0:	db 'CS.DPL=0, only win32 clients are supported.',0
Error_NoID:	db 'specify PID/TID.',0
