Skip to content

Commit

Permalink
thread: Support hardware breakpoints and watchpoints
Browse files Browse the repository at this point in the history
So they can be set and unset.

Co-authored-by: Håvard Sørbø <havard@hsorbo.no>
  • Loading branch information
oleavr and hsorbo committed Sep 6, 2024
1 parent d641cd1 commit dfecbc4
Show file tree
Hide file tree
Showing 27 changed files with 2,013 additions and 243 deletions.
26 changes: 3 additions & 23 deletions bindings/gumjs/gumquickprocess.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,14 @@ void
_gum_quick_process_init (GumQuickProcess * self,
JSValue ns,
GumQuickModule * module,
GumQuickThread * thread,
GumQuickCore * core)
{
JSContext * ctx = core->ctx;
JSValue obj;

self->module = module;
self->thread = thread;
self->core = core;

self->main_module_value = JS_UNINITIALIZED;
Expand Down Expand Up @@ -308,31 +310,9 @@ gum_emit_thread (const GumThreadDetails * details,
GumQuickMatchContext * mc)
{
JSContext * ctx = mc->ctx;
GumQuickCore * core = mc->parent->core;
JSValue thread, result;

thread = JS_NewObject (ctx);

JS_DefinePropertyValue (ctx, thread,
GUM_QUICK_CORE_ATOM (core, id),
JS_NewInt64 (ctx, details->id),
JS_PROP_C_W_E);
if (details->name != NULL)
{
JS_DefinePropertyValue (ctx, thread,
GUM_QUICK_CORE_ATOM (core, name),
JS_NewString (ctx, details->name),
JS_PROP_C_W_E);
}
JS_DefinePropertyValue (ctx, thread,
GUM_QUICK_CORE_ATOM (core, state),
_gum_quick_thread_state_new (ctx, details->state),
JS_PROP_C_W_E);
JS_DefinePropertyValue (ctx, thread,
GUM_QUICK_CORE_ATOM (core, context),
_gum_quick_cpu_context_new (ctx, (GumCpuContext *) &details->cpu_context,
GUM_CPU_CONTEXT_READONLY, core, NULL),
JS_PROP_C_W_E);
thread = _gum_quick_thread_new (ctx, details, mc->parent->thread);

result = JS_Call (ctx, mc->on_match, JS_UNDEFINED, 1, &thread);

Expand Down
6 changes: 5 additions & 1 deletion bindings/gumjs/gumquickprocess.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2020-2024 Ole André Vadla Ravnås <oleavr@nowsecure.com>
* Copyright (C) 2023 Francesco Tamagni <mrmacete@protonmail.ch>
* Copyright (C) 2024 Håvard Sørbø <havard@hsorbo.no>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
Expand All @@ -10,6 +11,7 @@

#include "gumquickcore.h"
#include "gumquickmodule.h"
#include "gumquickthread.h"

G_BEGIN_DECLS

Expand All @@ -19,6 +21,7 @@ typedef struct _GumQuickExceptionHandler GumQuickExceptionHandler;
struct _GumQuickProcess
{
GumQuickModule * module;
GumQuickThread * thread;
GumQuickCore * core;

JSValue main_module_value;
Expand All @@ -30,7 +33,8 @@ struct _GumQuickProcess
};

G_GNUC_INTERNAL void _gum_quick_process_init (GumQuickProcess * self,
JSValue ns, GumQuickModule * module, GumQuickCore * core);
JSValue ns, GumQuickModule * module, GumQuickThread * thread,
GumQuickCore * core);
G_GNUC_INTERNAL void _gum_quick_process_flush (GumQuickProcess * self);
G_GNUC_INTERNAL void _gum_quick_process_dispose (GumQuickProcess * self);
G_GNUC_INTERNAL void _gum_quick_process_finalize (GumQuickProcess * self);
Expand Down
13 changes: 7 additions & 6 deletions bindings/gumjs/gumquickscript.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ struct _GumQuickScript
GumQuickKernel kernel;
GumQuickMemory memory;
GumQuickModule module;
GumQuickProcess process;
GumQuickThread thread;
GumQuickProcess process;
GumQuickFile file;
GumQuickChecksum checksum;
GumQuickStream stream;
Expand Down Expand Up @@ -478,8 +478,9 @@ gum_quick_script_create_context (GumQuickScript * self,
_gum_quick_kernel_init (&self->kernel, global_obj, core);
_gum_quick_memory_init (&self->memory, global_obj, core);
_gum_quick_module_init (&self->module, global_obj, core);
_gum_quick_process_init (&self->process, global_obj, &self->module, core);
_gum_quick_thread_init (&self->thread, global_obj, core);
_gum_quick_process_init (&self->process, global_obj, &self->module,
&self->thread, core);
_gum_quick_file_init (&self->file, global_obj, core);
_gum_quick_checksum_init (&self->checksum, global_obj, core);
_gum_quick_stream_init (&self->stream, global_obj, core);
Expand Down Expand Up @@ -548,8 +549,8 @@ gum_quick_script_destroy_context (GumQuickScript * self)
_gum_quick_stream_dispose (&self->stream);
_gum_quick_checksum_dispose (&self->checksum);
_gum_quick_file_dispose (&self->file);
_gum_quick_thread_dispose (&self->thread);
_gum_quick_process_dispose (&self->process);
_gum_quick_thread_dispose (&self->thread);
_gum_quick_module_dispose (&self->module);
_gum_quick_memory_dispose (&self->memory);
_gum_quick_kernel_dispose (&self->kernel);
Expand Down Expand Up @@ -591,8 +592,8 @@ gum_quick_script_destroy_context (GumQuickScript * self)
_gum_quick_stream_finalize (&self->stream);
_gum_quick_checksum_finalize (&self->checksum);
_gum_quick_file_finalize (&self->file);
_gum_quick_thread_finalize (&self->thread);
_gum_quick_process_finalize (&self->process);
_gum_quick_thread_finalize (&self->thread);
_gum_quick_module_finalize (&self->module);
_gum_quick_memory_finalize (&self->memory);
_gum_quick_kernel_finalize (&self->kernel);
Expand Down Expand Up @@ -1139,9 +1140,9 @@ _gum_quick_script_make_worker (GumQuickScript * self,
_gum_quick_kernel_init (&worker->kernel, global_obj, core);
_gum_quick_memory_init (&worker->memory, global_obj, core);
_gum_quick_module_init (&worker->module, global_obj, core);
_gum_quick_process_init (&worker->process, global_obj, &worker->module,
core);
_gum_quick_thread_init (&worker->thread, global_obj, core);
_gum_quick_process_init (&worker->process, global_obj, &worker->module,
&worker->thread, core);
_gum_quick_file_init (&worker->file, global_obj, core);
_gum_quick_checksum_init (&worker->checksum, global_obj, core);
_gum_quick_stream_init (&worker->stream, global_obj, core);
Expand Down
224 changes: 219 additions & 5 deletions bindings/gumjs/gumquickthread.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2020-2024 Ole André Vadla Ravnås <oleavr@nowsecure.com>
* Copyright (C) 2024 DaVinci <nstefanclaudel13@gmail.com>
* Copyright (C) 2024 Håvard Sørbø <havard@hsorbo.no>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
Expand All @@ -18,12 +19,35 @@ enum _GumBacktracerType
GUMJS_DECLARE_FUNCTION (gumjs_thread_backtrace)
GUMJS_DECLARE_FUNCTION (gumjs_thread_sleep)

static const JSCFunctionListEntry gumjs_thread_entries[] =
GUMJS_DECLARE_CONSTRUCTOR (gumjs_thread_construct)
GUMJS_DECLARE_FUNCTION (gumjs_thread_set_hardware_breakpoint)
GUMJS_DECLARE_FUNCTION (gumjs_thread_unset_hardware_breakpoint)
GUMJS_DECLARE_FUNCTION (gumjs_thread_set_hardware_watchpoint)
GUMJS_DECLARE_FUNCTION (gumjs_thread_unset_hardware_watchpoint)

static const JSClassDef gumjs_thread_def =
{
.class_name = "Thread",
};

static const JSCFunctionListEntry gumjs_thread_module_entries[] =
{
JS_CFUNC_DEF ("_backtrace", 0, gumjs_thread_backtrace),
JS_CFUNC_DEF ("sleep", 0, gumjs_thread_sleep),
};

static const JSCFunctionListEntry gumjs_thread_entries[] =
{
JS_CFUNC_DEF ("setHardwareBreakpoint", 0,
gumjs_thread_set_hardware_breakpoint),
JS_CFUNC_DEF ("unsetHardwareBreakpoint", 0,
gumjs_thread_unset_hardware_breakpoint),
JS_CFUNC_DEF ("setHardwareWatchpoint", 0,
gumjs_thread_set_hardware_watchpoint),
JS_CFUNC_DEF ("unsetHardwareWatchpoint", 0,
gumjs_thread_unset_hardware_watchpoint),
};

static const JSCFunctionListEntry gumjs_backtracer_entries[] =
{
JS_PROP_INT32_DEF ("ACCURATE", GUM_BACKTRACER_ACCURATE, JS_PROP_C_W_E),
Expand All @@ -36,16 +60,23 @@ _gum_quick_thread_init (GumQuickThread * self,
GumQuickCore * core)
{
JSContext * ctx = core->ctx;
JSValue obj;
JSValue obj, proto, ctor;

self->core = core;

_gum_quick_core_store_module_data (core, "thread", self);

obj = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj, gumjs_thread_entries,
_gum_quick_create_class (ctx, &gumjs_thread_def, core, &self->thread_class,
&proto);
ctor = JS_NewCFunction2 (ctx, gumjs_thread_construct,
gumjs_thread_def.class_name, 0, JS_CFUNC_constructor, 0);
JS_SetConstructor (ctx, ctor, proto);
JS_SetPropertyFunctionList (ctx, ctor, gumjs_thread_module_entries,
G_N_ELEMENTS (gumjs_thread_module_entries));
JS_SetPropertyFunctionList (ctx, proto, gumjs_thread_entries,
G_N_ELEMENTS (gumjs_thread_entries));
JS_DefinePropertyValueStr (ctx, ns, "Thread", obj, JS_PROP_C_W_E);
JS_DefinePropertyValueStr (ctx, ns, gumjs_thread_def.class_name, ctor,
JS_PROP_C_W_E);

obj = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj, gumjs_backtracer_entries,
Expand Down Expand Up @@ -159,3 +190,186 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_sleep)

return JS_UNDEFINED;
}

JSValue
_gum_quick_thread_new (JSContext * ctx,
const GumThreadDetails * details,
GumQuickThread * parent)
{
GumQuickCore * core = parent->core;
JSValue thread;

thread = JS_NewObjectClass (ctx, parent->thread_class);

JS_SetOpaque (thread, GSIZE_TO_POINTER (details->id));

JS_DefinePropertyValue (ctx, thread,
GUM_QUICK_CORE_ATOM (core, id),
JS_NewInt64 (ctx, details->id),
JS_PROP_C_W_E);
if (details->name != NULL)
{
JS_DefinePropertyValue (ctx, thread,
GUM_QUICK_CORE_ATOM (core, name),
JS_NewString (ctx, details->name),
JS_PROP_C_W_E);
}
JS_DefinePropertyValue (ctx, thread,
GUM_QUICK_CORE_ATOM (core, state),
_gum_quick_thread_state_new (ctx, details->state),
JS_PROP_C_W_E);
JS_DefinePropertyValue (ctx, thread,
GUM_QUICK_CORE_ATOM (core, context),
_gum_quick_cpu_context_new (ctx, (GumCpuContext *) &details->cpu_context,
GUM_CPU_CONTEXT_READONLY, core, NULL),
JS_PROP_C_W_E);

return thread;
}

static gboolean
gum_thread_get (JSContext * ctx,
JSValueConst val,
GumQuickCore * core,
GumThreadId * thread_id)
{
return _gum_quick_unwrap (ctx, val,
gumjs_get_parent_module (core)->thread_class, core,
(gpointer *) thread_id);
}

GUMJS_DEFINE_CONSTRUCTOR (gumjs_thread_construct)
{
JSValue wrapper = JS_NULL;
GumThreadId thread_id;
JSValue proto;

if (!_gum_quick_args_parse (args, "Z", &thread_id))
return JS_EXCEPTION;

proto = JS_GetProperty (ctx, new_target,
GUM_QUICK_CORE_ATOM (core, prototype));
wrapper = JS_NewObjectProtoClass (ctx, proto,
gumjs_get_parent_module (core)->thread_class);
JS_FreeValue (ctx, proto);
if (JS_IsException (wrapper))
return JS_EXCEPTION;

JS_SetOpaque (wrapper, GSIZE_TO_POINTER (thread_id));

return wrapper;
}

GUMJS_DEFINE_FUNCTION (gumjs_thread_set_hardware_breakpoint)
{
GumThreadId thread_id;
guint breakpoint_id;
gpointer address;
GError * error;

if (!gum_thread_get (ctx, this_val, core, &thread_id))
return JS_EXCEPTION;

if (!_gum_quick_args_parse (args, "up", &breakpoint_id, &address))
return JS_EXCEPTION;

error = NULL;
gum_thread_set_hardware_breakpoint (thread_id, breakpoint_id,
GUM_ADDRESS (address), &error);
if (error != NULL)
return _gum_quick_throw_error (ctx, &error);

return JS_UNDEFINED;
}

GUMJS_DEFINE_FUNCTION (gumjs_thread_unset_hardware_breakpoint)
{
GumThreadId thread_id;
guint breakpoint_id;
GError * error;

if (!gum_thread_get (ctx, this_val, core, &thread_id))
return JS_EXCEPTION;

if (!_gum_quick_args_parse (args, "u", &breakpoint_id))
return JS_EXCEPTION;

error = NULL;
gum_thread_unset_hardware_breakpoint (thread_id, breakpoint_id, &error);
if (error != NULL)
return _gum_quick_throw_error (ctx, &error);

return JS_UNDEFINED;
}

GUMJS_DEFINE_FUNCTION (gumjs_thread_set_hardware_watchpoint)
{
GumThreadId thread_id;
guint watchpoint_id;
gpointer address;
gsize size;
const gchar * conditions_str;
GumWatchConditions conditions;
const gchar * ch;
GError * error;

if (!gum_thread_get (ctx, this_val, core, &thread_id))
return JS_EXCEPTION;

if (!_gum_quick_args_parse (args, "upZs", &watchpoint_id, &address, &size,
&conditions_str))
return JS_EXCEPTION;

conditions = 0;
for (ch = conditions_str; *ch != '\0'; ch++)
{
switch (*ch)
{
case 'r':
conditions |= GUM_WATCH_READ;
break;
case 'w':
conditions |= GUM_WATCH_WRITE;
break;
default:
goto invalid_conditions;
}
}
if (conditions == 0)
goto invalid_conditions;

error = NULL;
gum_thread_set_hardware_watchpoint (thread_id, watchpoint_id,
GUM_ADDRESS (address), size, conditions, &error);
if (error != NULL)
return _gum_quick_throw_error (ctx, &error);

return JS_UNDEFINED;

invalid_conditions:
{
_gum_quick_throw_literal (ctx,
"expected a string specifying watch conditions, e.g. 'rw'");
return JS_EXCEPTION;
}
}

GUMJS_DEFINE_FUNCTION (gumjs_thread_unset_hardware_watchpoint)
{
GumThreadId thread_id;
guint watchpoint_id;
GError * error;

if (!gum_thread_get (ctx, this_val, core, &thread_id))
return JS_EXCEPTION;

if (!_gum_quick_args_parse (args, "u", &watchpoint_id))
return JS_EXCEPTION;

error = NULL;
gum_thread_unset_hardware_watchpoint (thread_id, watchpoint_id, &error);
if (error != NULL)
return _gum_quick_throw_error (ctx, &error);

return JS_UNDEFINED;
}
Loading

0 comments on commit dfecbc4

Please sign in to comment.