From 0198f7e29d0c3fa07002fac878130a6d842c3e64 Mon Sep 17 00:00:00 2001 From: DaVinci Date: Sun, 13 Aug 2023 01:07:12 +0300 Subject: [PATCH] gumjs: Add `limit` option to Thread.backtrace() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ole André Vadla Ravnås --- bindings/gumjs/gumquickthread.c | 32 +++++++++++++++-------- bindings/gumjs/gumv8thread.cpp | 45 ++++++++++++++++++++++----------- bindings/gumjs/runtime/core.js | 18 +++++++++++++ tests/gumjs/script.c | 20 +++++++++++++++ 4 files changed, 89 insertions(+), 26 deletions(-) diff --git a/bindings/gumjs/gumquickthread.c b/bindings/gumjs/gumquickthread.c index f6d6cb89a2..ae0e80ee41 100644 --- a/bindings/gumjs/gumquickthread.c +++ b/bindings/gumjs/gumquickthread.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2020 Ole André Vadla Ravnås + * Copyright (C) 2020-2024 Ole André Vadla Ravnås + * Copyright (C) 2024 DaVinci * * Licence: wxWindows Library Licence, Version 3.1 */ @@ -19,7 +20,7 @@ GUMJS_DECLARE_FUNCTION (gumjs_thread_sleep) static const JSCFunctionListEntry gumjs_thread_entries[] = { - JS_CFUNC_DEF ("backtrace", 0, gumjs_thread_backtrace), + JS_CFUNC_DEF ("_backtrace", 0, gumjs_thread_backtrace), JS_CFUNC_DEF ("sleep", 0, gumjs_thread_sleep), }; @@ -74,21 +75,22 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_backtrace) { JSValue result; GumQuickThread * self; - GumCpuContext * cpu_context = NULL; - gint selector = GUM_BACKTRACER_ACCURATE; + GumCpuContext * cpu_context; + gint type; + guint limit; GumBacktracer * backtracer; GumReturnAddressArray ret_addrs; guint i; self = gumjs_get_parent_module (core); - if (!_gum_quick_args_parse (args, "|C?i", &cpu_context, &selector)) + if (!_gum_quick_args_parse (args, "C?iu", &cpu_context, &type, &limit)) return JS_EXCEPTION; - if (selector != GUM_BACKTRACER_ACCURATE && selector != GUM_BACKTRACER_FUZZY) - goto invalid_selector; + if (type != GUM_BACKTRACER_ACCURATE && type != GUM_BACKTRACER_FUZZY) + goto invalid_type; - if (selector == GUM_BACKTRACER_ACCURATE) + if (type == GUM_BACKTRACER_ACCURATE) { if (self->accurate_backtracer == NULL) self->accurate_backtracer = gum_backtracer_make_accurate (); @@ -103,7 +105,15 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_backtrace) if (backtracer == NULL) goto not_available; - gum_backtracer_generate (backtracer, cpu_context, &ret_addrs); + if (limit != 0) + { + gum_backtracer_generate_with_limit (backtracer, cpu_context, &ret_addrs, + limit); + } + else + { + gum_backtracer_generate (backtracer, cpu_context, &ret_addrs); + } result = JS_NewArray (ctx); @@ -116,13 +126,13 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_backtrace) return result; -invalid_selector: +invalid_type: { return _gum_quick_throw_literal (ctx, "invalid backtracer enum value"); } not_available: { - return _gum_quick_throw_literal (ctx, (selector == GUM_BACKTRACER_ACCURATE) + return _gum_quick_throw_literal (ctx, (type == GUM_BACKTRACER_ACCURATE) ? "backtracer not yet available for this platform; " "please try Thread.backtrace(context, Backtracer.FUZZY)" : "backtracer not yet available for this platform; " diff --git a/bindings/gumjs/gumv8thread.cpp b/bindings/gumjs/gumv8thread.cpp index 158ba85bac..84c41ed8e8 100644 --- a/bindings/gumjs/gumv8thread.cpp +++ b/bindings/gumjs/gumv8thread.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2010-2022 Ole André Vadla Ravnås + * Copyright (C) 2010-2024 Ole André Vadla Ravnås + * Copyright (C) 2024 DaVinci * * Licence: wxWindows Library Licence, Version 3.1 */ @@ -17,7 +18,7 @@ GUMJS_DECLARE_FUNCTION (gumjs_thread_sleep) static const GumV8Function gumjs_thread_functions[] = { - { "backtrace", gumjs_thread_backtrace }, + { "_backtrace", gumjs_thread_backtrace }, { "sleep", gumjs_thread_sleep }, { NULL, NULL } @@ -87,22 +88,28 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_backtrace) auto context = isolate->GetCurrentContext (); GumCpuContext * cpu_context = NULL; - Local selector; - if (!_gum_v8_args_parse (args, "|C?V", &cpu_context, &selector)) + Local raw_type; + guint limit; + if (!_gum_v8_args_parse (args, "C?Vu", &cpu_context, &raw_type, &limit)) return; + if (!raw_type->IsSymbol ()) + { + _gum_v8_throw_ascii_literal (isolate, "invalid backtracer value"); + return; + } + Local type = raw_type.As (); gboolean accurate = TRUE; - if (!selector.IsEmpty ()) + if (type->StrictEquals ( + Local::New (isolate, *module->fuzzy_enum_value))) { - if ((*module->fuzzy_enum_value) == selector) - { - accurate = FALSE; - } - else if ((*module->accurate_enum_value) != selector) - { - _gum_v8_throw_ascii_literal (isolate, "invalid backtracer enum value"); - return; - } + accurate = FALSE; + } + else if (!type->StrictEquals ( + Local::New (isolate, *module->accurate_enum_value))) + { + _gum_v8_throw_ascii_literal (isolate, "invalid backtracer enum value"); + return; } GumBacktracer * backtracer; @@ -129,7 +136,15 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_backtrace) } GumReturnAddressArray ret_addrs; - gum_backtracer_generate (backtracer, cpu_context, &ret_addrs); + if (limit != 0) + { + gum_backtracer_generate_with_limit (backtracer, cpu_context, &ret_addrs, + limit); + } + else + { + gum_backtracer_generate (backtracer, cpu_context, &ret_addrs); + } auto result = Array::New (isolate, ret_addrs.len); for (guint i = 0; i != ret_addrs.len; i++) diff --git a/bindings/gumjs/runtime/core.js b/bindings/gumjs/runtime/core.js index b17fdc1666..f1c1417e74 100644 --- a/bindings/gumjs/runtime/core.js +++ b/bindings/gumjs/runtime/core.js @@ -485,6 +485,24 @@ if (Process.findRangeByAddress === undefined) { }); } +Object.defineProperties(Thread, { + backtrace: { + enumerable: true, + value: function (cpuContext = null, backtracerOrOptions = {}) { + const options = (typeof backtracerOrOptions === 'object') + ? backtracerOrOptions + : { backtracer: backtracerOrOptions }; + + const { + backtracer = Backtracer.ACCURATE, + limit = 0, + } = options; + + return Thread._backtrace(cpuContext, backtracer, limit); + } + }, +}); + if (globalThis.Interceptor !== undefined) { Object.defineProperties(Interceptor, { attach: { diff --git a/tests/gumjs/script.c b/tests/gumjs/script.c index c74bd77bf4..a76cfd62bc 100644 --- a/tests/gumjs/script.c +++ b/tests/gumjs/script.c @@ -29,6 +29,8 @@ TESTLIST_BEGIN (script) TESTENTRY (rpc_can_be_performed) TESTENTRY (message_can_be_logged) TESTENTRY (thread_can_be_forced_to_sleep) + TESTENTRY (thread_backtrace_can_be_captured_with_specific_backtracer) + TESTENTRY (thread_backtrace_can_be_captured_with_limit) TESTENTRY (timeout_can_be_scheduled) TESTENTRY (timeout_can_be_cancelled) TESTENTRY (interval_can_be_scheduled) @@ -6355,6 +6357,24 @@ TESTCASE (thread_can_be_forced_to_sleep) g_timer_destroy (timer); } +TESTCASE (thread_backtrace_can_be_captured_with_specific_backtracer) +{ + COMPILE_AND_LOAD_SCRIPT ( + "const a = Thread.backtrace(null, { backtracer: Backtracer.ACCURATE });" + "const b = Thread.backtrace(null, { backtracer: Backtracer.FUZZY });" + "send(JSON.stringify(a) !== JSON.stringify(b));"); + EXPECT_SEND_MESSAGE_WITH ("true"); + EXPECT_NO_MESSAGES (); +} + +TESTCASE (thread_backtrace_can_be_captured_with_limit) +{ + COMPILE_AND_LOAD_SCRIPT ( + "send(Thread.backtrace(null, { limit: 2 }).length);"); + EXPECT_SEND_MESSAGE_WITH ("2"); + EXPECT_NO_MESSAGES (); +} + TESTCASE (timeout_can_be_scheduled) { COMPILE_AND_LOAD_SCRIPT (