Blob Blame History Raw
/********************************************************/
/*  ntapi: Native API core library                      */
/*  Copyright (C) 2013--2021  SysDeer Technologies, LLC */
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
/********************************************************/

#include <psxtypes/psxtypes.h>
#include <ntapi/nt_object.h>
#include <ntapi/nt_debug.h>
#include "ntapi_impl.h"

static int32_t __tt_debug_break_process_fail(void * hthread, int32_t status)
{
	__ntapi->zw_terminate_thread(
		hthread,status);

	return status;
}

struct __dbg_hoppla {
	void *		caller;
	void *		cx;
	intptr_t	dx;
	intptr_t	r9;
	intptr_t	r8;
};

int32_t __stdcall __ntapi_tt_debug_break_process(
	__in	void *		hprocess,
	__out	void **		hthread,
	__out	nt_cid *	cid)
{
	int32_t			status;
	nt_thread_params	tparams;
	nt_thread_context	context;
	nt_user_stack		spinfo;
	struct __dbg_hoppla	hoppla;
	uintptr_t		sptop;
	uintptr_t		spreg;
	int32_t			(__stdcall *dbg_break_point)(void *);

	/* interrupt & return */
	dbg_break_point = pe_get_procedure_address(
		pe_get_ntdll_module_handle(),
		"DbgBreakPoint");

	/* thread params */
	__ntapi->tt_aligned_block_memset(
		&tparams,0,
		sizeof(tparams));

	__ntapi->tt_aligned_block_memset(
		&spinfo,0,
		sizeof(spinfo));

	tparams.start              = dbg_break_point;
	tparams.hprocess           = hprocess;
	tparams.stack_size_commit  = 0x1000;
	tparams.stack_size_reserve = 0x1000;
	tparams.stack_info         = &spinfo;
	tparams.creation_flags     = NT_CREATE_SUSPENDED;

	if ((status = __ntapi->tt_create_thread(&tparams)))
		return status;

	/* context */
	__ntapi->tt_aligned_block_memset(
		&context,0,
		sizeof(context));

	context.uc_context_flags = NT_CONTEXT_JUST_EVERYTHING;

	if ((status = __ntapi->zw_get_context_thread(
			tparams.hthread,
			&context)))
		return __tt_debug_break_process_fail(
			tparams.hthread,
			status);

	/* return address:=) */
	hoppla.caller = __ntapi->zw_terminate_thread;
	hoppla.cx     = NT_CURRENT_THREAD_HANDLE;
	hoppla.dx     = NT_STATUS_BREAKPOINT;
	hoppla.r8     = 0;
	hoppla.r9     = 0;

	sptop = (uintptr_t)tparams.stack_info->expandable_stack_base;
	spreg = context.STACK_POINTER_REGISTER;

	if (sptop - spreg < sizeof(hoppla))
		spreg -= sizeof(hoppla);

	if ((status = __ntapi->zw_write_virtual_memory(
			hprocess,
			(void *)spreg,
			(char *)&hoppla,
			sizeof(hoppla),0)))
		return __tt_debug_break_process_fail(
			tparams.hthread,
			status);

	/* (fast call args not needed on x86) */
	context.STACK_POINTER_REGISTER = spreg;
	context.FAST_CALL_ARG0         = (uintptr_t)hoppla.cx;
	context.FAST_CALL_ARG1         = hoppla.dx;

	if ((status = __ntapi->zw_set_context_thread(
			tparams.hthread,
			&context)))
		return __tt_debug_break_process_fail(
			tparams.hthread,
			status);

	/* at last... */
	if ((status = __ntapi->zw_resume_thread(tparams.hthread,0)))
		return __tt_debug_break_process_fail(
			tparams.hthread,
			status);

	/* yay */
	*hthread        = tparams.hthread;
	cid->thread_id  = tparams.cid.thread_id;
	cid->process_id = tparams.cid.process_id;

	return NT_STATUS_SUCCESS;
}