diff --git a/bindings/gumjs/gumquickprocess.c b/bindings/gumjs/gumquickprocess.c index 82051f88ed..b22a625cc6 100644 --- a/bindings/gumjs/gumquickprocess.c +++ b/bindings/gumjs/gumquickprocess.c @@ -82,9 +82,7 @@ struct _GumQuickFindRangeByAddressContext struct _GumQuickRunOnThreadContext { GumQuickCore * core; - GumQuickScope scope; JSValue user_func; - gboolean sync; }; static void gumjs_free_main_module_value (GumQuickProcess * self); @@ -122,6 +120,9 @@ static gboolean gum_quick_exception_handler_on_exception ( GumExceptionDetails * details, GumQuickExceptionHandler * handler); static void gum_js_process_run_cb (const GumCpuContext * cpu_context, gpointer user_data); +static void gum_quick_flush_stalker (GumQuickProcess * self, + GumQuickScope * scope); +static gboolean gum_quick_flush_stalker_callback (GumQuickProcess * self); static const JSCFunctionListEntry gumjs_process_entries[] = { @@ -159,6 +160,7 @@ _gum_quick_process_init (GumQuickProcess * self, self->module = module; self->core = core; self->main_module_value = JS_UNINITIALIZED; + self->stalker = NULL; _gum_quick_core_store_module_data (core, "process", self); @@ -186,6 +188,8 @@ _gum_quick_process_flush (GumQuickProcess * self) void _gum_quick_process_dispose (GumQuickProcess * self) { + g_assert (self->flush_timer == NULL); + g_clear_pointer (&self->exception_handler, gum_quick_exception_handler_free); gumjs_free_main_module_value (self); } @@ -638,58 +642,108 @@ gum_quick_exception_handler_on_exception (GumExceptionDetails * details, GUMJS_DEFINE_FUNCTION (gumjs_process_run_on_thread) { + GumQuickProcess * self = gumjs_get_parent_module (core); GumQuickScope scope = GUM_QUICK_SCOPE_INIT (core); GumThreadId thread_id; JSValue user_func; - GumQuickRunOnThreadContext sync_ctx; - GumStalker * stalker; - gboolean success; + GumQuickRunOnThreadContext context; + gboolean run; if (!_gum_quick_args_parse (args, "ZF", &thread_id, &user_func)) return JS_EXCEPTION; - if (thread_id == 0) - return JS_UNDEFINED; - _gum_quick_scope_suspend (&scope); - sync_ctx.core = core; - sync_ctx.scope = scope; - sync_ctx.user_func = JS_DupValue (core->ctx, user_func); - sync_ctx.sync = FALSE; + context.core = core; + context.user_func = JS_DupValue (core->ctx, user_func); - stalker = gum_stalker_new (); + if (self->stalker == NULL) + self->stalker = gum_stalker_new (); + + run = gum_stalker_run_on_thread (self->stalker, thread_id, + gum_js_process_run_cb, &context); - success = gum_stalker_run_on_thread (stalker, thread_id, - gum_js_process_run_cb, &sync_ctx); _gum_quick_scope_resume (&scope); + gum_quick_flush_stalker (self, &scope); - while (gum_stalker_garbage_collect (stalker)) - g_usleep (10000); + if (!run) + goto error; - g_object_unref (stalker); + return JS_UNDEFINED; - if (success) - { - return JS_UNDEFINED; - } - else - { - _gum_quick_throw_literal (ctx, "Failed to run on thread"); - return JS_EXCEPTION; - } +error: + _gum_quick_throw_literal (ctx, "failed to run on thread"); + return JS_EXCEPTION; } static void gum_js_process_run_cb (const GumCpuContext * cpu_context, gpointer user_data) { - GumQuickRunOnThreadContext * sync_ctx = - (GumQuickRunOnThreadContext *) user_data; + GumQuickRunOnThreadContext * context = user_data; + GumQuickCore * core = context->core; + JSValue user_func = context->user_func; + GumQuickScope scope; - _gum_quick_scope_call (&sync_ctx->scope, sync_ctx->user_func, JS_UNDEFINED, 0, + _gum_quick_scope_enter (&scope, core); + + _gum_quick_scope_call (&scope, user_func, JS_UNDEFINED, 0, NULL); - if (!sync_ctx->sync) - JS_FreeValue (sync_ctx->core->ctx, sync_ctx->user_func); + JS_FreeValue (core->ctx, user_func); + + _gum_quick_scope_leave (&scope); +} + +static void +gum_quick_flush_stalker (GumQuickProcess * self, + GumQuickScope * scope) +{ + GumQuickCore * core = self->core; + GSource * source; + + if (!gum_stalker_garbage_collect (self->stalker)) + goto error; + + if (self->flush_timer != NULL) + goto error; + + source = g_timeout_source_new (10); + g_source_set_callback (source, + (GSourceFunc) gum_quick_flush_stalker_callback, self, NULL); + self->flush_timer = source; + + _gum_quick_core_pin (core); + _gum_quick_scope_suspend (scope); + + g_source_attach (source, + gum_script_scheduler_get_js_context (core->scheduler)); + g_source_unref (source); + + _gum_quick_scope_resume (scope); + return; + +error: + g_object_unref (self->stalker); + self->stalker = NULL; +} + +static gboolean +gum_quick_flush_stalker_callback (GumQuickProcess * self) +{ + gboolean pending_garbage; + + pending_garbage = gum_stalker_garbage_collect (self->stalker); + if (!pending_garbage) + { + GumQuickCore * core = self->core; + GumQuickScope scope; + + _gum_quick_scope_enter (&scope, core); + _gum_quick_core_unpin (core); + self->flush_timer = NULL; + _gum_quick_scope_leave (&scope); + } + + return pending_garbage; } diff --git a/bindings/gumjs/gumquickprocess.h b/bindings/gumjs/gumquickprocess.h index e8811e756e..67221f31a6 100644 --- a/bindings/gumjs/gumquickprocess.h +++ b/bindings/gumjs/gumquickprocess.h @@ -23,6 +23,9 @@ struct _GumQuickProcess JSValue main_module_value; GumQuickExceptionHandler * exception_handler; + + GumStalker * stalker; + GSource * flush_timer; }; G_GNUC_INTERNAL void _gum_quick_process_init (GumQuickProcess * self, diff --git a/bindings/gumjs/gumv8process.cpp b/bindings/gumjs/gumv8process.cpp index f449cec829..1b669b9e9a 100644 --- a/bindings/gumjs/gumv8process.cpp +++ b/bindings/gumjs/gumv8process.cpp @@ -66,10 +66,7 @@ struct GumV8FindModuleByNameContext struct GumV8RunOnThreadContext { GumV8Core * core; - Isolate * isolate; - Local context; Local user_func; - MaybeLocal ret; }; GUMJS_DECLARE_GETTER (gumjs_process_get_main_module) @@ -103,6 +100,9 @@ static gboolean gum_v8_exception_handler_on_exception ( GumExceptionDetails * details, GumV8ExceptionHandler * handler); static void gum_js_process_run_cb (const GumCpuContext * cpu_context, gpointer user_data); +static void gum_v8_flush_stalker (GumV8Process * self); +static gboolean gum_v8_flush_stalker_callback (GumV8Process * self); + const gchar * gum_v8_script_exception_type_to_string (GumExceptionType type); @@ -142,6 +142,8 @@ _gum_v8_process_init (GumV8Process * self, self->module = module; self->core = core; + self->stalker = NULL; + auto process_module = External::New (isolate, self); auto process = _gum_v8_create_module ("Process", scope, isolate); @@ -180,6 +182,8 @@ _gum_v8_process_flush (GumV8Process * self) void _gum_v8_process_dispose (GumV8Process * self) { + g_assert (self->flush_timer == NULL); + g_clear_pointer (&self->exception_handler, gum_v8_exception_handler_free); delete self->main_module_value; @@ -530,51 +534,102 @@ GUMJS_DEFINE_FUNCTION (gumjs_process_run_on_thread) { GumThreadId thread_id; Local user_func; - GumV8RunOnThreadContext sync_ctx; - GumStalker * stalker; - gboolean success; + GumV8RunOnThreadContext context; + gboolean run; auto isolate = core->isolate; - auto context = isolate->GetCurrentContext (); if (!_gum_v8_args_parse (args, "ZF", &thread_id, &user_func)) return; - if (thread_id == 0) - return; - - stalker = gum_stalker_new (); + if (module->stalker == NULL) + module->stalker = gum_stalker_new (); { ScriptUnlocker unlocker (core); - sync_ctx.core = core; - sync_ctx.isolate = isolate; - sync_ctx.context = context; - sync_ctx.user_func = Local::New (isolate, user_func); + context.core = core; + context.user_func = Local::New (isolate, user_func); - success = gum_stalker_run_on_thread (stalker, thread_id, - gum_js_process_run_cb, &sync_ctx); + run = gum_stalker_run_on_thread (module->stalker, thread_id, + gum_js_process_run_cb, &context); } - while (gum_stalker_garbage_collect (stalker)) - g_usleep (10000); + gum_v8_flush_stalker (module); - g_object_unref (stalker); + if (!run) + goto error; - if (!success) - _gum_v8_throw_ascii_literal (isolate, "Failed to run on thread"); + return; + +error: + _gum_v8_throw_ascii_literal (isolate, "failed to run on thread"); } static void gum_js_process_run_cb (const GumCpuContext * cpu_context, gpointer user_data) { - GumV8RunOnThreadContext * sync_ctx = (GumV8RunOnThreadContext *) user_data; + GumV8RunOnThreadContext * context = (GumV8RunOnThreadContext *) user_data; + auto core = context->core; + auto isolate = core->isolate; + auto ctx = isolate->GetCurrentContext (); - ScriptScope scope (sync_ctx->core->script); - auto isolate = sync_ctx->isolate; - auto context = sync_ctx->context; + ScriptScope scope (core->script); auto recv = Undefined (isolate); - sync_ctx->ret = sync_ctx->user_func->Call (context, recv, 0, nullptr); + auto result = context->user_func->Call (ctx, recv, 0, nullptr); + (void) result; +} + +static void +gum_v8_flush_stalker (GumV8Process * self) +{ + GumV8Core * core = self->core; + + if (!gum_stalker_garbage_collect (self->stalker)) + goto error; + + if (self->flush_timer != NULL) + goto error; + + { + auto source = g_timeout_source_new (10); + g_source_set_callback (source, + (GSourceFunc) gum_v8_flush_stalker_callback, self, NULL); + self->flush_timer = source; + + _gum_v8_core_pin (core); + + { + ScriptUnlocker unlocker (core); + + g_source_attach (source, + gum_script_scheduler_get_js_context (core->scheduler)); + g_source_unref (source); + } + } + + return; + +error: + g_object_unref (self->stalker); + self->stalker = NULL; +} + +static gboolean +gum_v8_flush_stalker_callback (GumV8Process * self) +{ + gboolean pending_garbage; + + pending_garbage = gum_stalker_garbage_collect (self->stalker); + if (!pending_garbage) + { + GumV8Core * core = self->core; + + ScriptScope scope (core->script); + _gum_v8_core_unpin (core); + self->flush_timer = NULL; + } + + return pending_garbage; } diff --git a/bindings/gumjs/gumv8process.h b/bindings/gumjs/gumv8process.h index 03547e96df..4483dd6220 100644 --- a/bindings/gumjs/gumv8process.h +++ b/bindings/gumjs/gumv8process.h @@ -20,6 +20,9 @@ struct GumV8Process v8::Global * main_module_value; GumV8ExceptionHandler * exception_handler; + + GumStalker * stalker; + GSource * flush_timer; }; G_GNUC_INTERNAL void _gum_v8_process_init (GumV8Process * self,