Skip to content

Commit

Permalink
gumjs: Add findSymbolByName()/getSymbolByName()
Browse files Browse the repository at this point in the history
Provide direct, native lookups for symbols by name instead of enumerating
all symbols and filtering them in JavaScript. On ELF-based backends,
we can potentially leverage binary search for faster performance.
  • Loading branch information
oleavr committed Dec 16, 2024
1 parent 7bb8909 commit 949f929
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 2 deletions.
25 changes: 24 additions & 1 deletion bindings/gumjs/gumquickmodule.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2023 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2020-2024 Ole André Vadla Ravnås <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
Expand Down Expand Up @@ -51,6 +51,7 @@ static gboolean gum_emit_dependency (const GumDependencyDetails * details,
GumQuickMatchContext * mc);
GUMJS_DECLARE_FUNCTION (gumjs_module_find_base_address)
GUMJS_DECLARE_FUNCTION (gumjs_module_find_export_by_name)
GUMJS_DECLARE_FUNCTION (gumjs_module_find_symbol_by_name)

GUMJS_DECLARE_CONSTRUCTOR (gumjs_module_map_construct)
GUMJS_DECLARE_FINALIZER (gumjs_module_map_finalize)
Expand Down Expand Up @@ -84,6 +85,7 @@ static const JSCFunctionListEntry gumjs_module_entries[] =
gumjs_module_enumerate_dependencies),
JS_CFUNC_DEF ("findBaseAddress", 0, gumjs_module_find_base_address),
JS_CFUNC_DEF ("findExportByName", 0, gumjs_module_find_export_by_name),
JS_CFUNC_DEF ("findSymbolByName", 0, gumjs_module_find_symbol_by_name),
};

static const JSClassDef gumjs_module_map_def =
Expand Down Expand Up @@ -599,6 +601,27 @@ GUMJS_DEFINE_FUNCTION (gumjs_module_find_export_by_name)
return _gum_quick_native_pointer_new (ctx, GSIZE_TO_POINTER (address), core);
}

GUMJS_DEFINE_FUNCTION (gumjs_module_find_symbol_by_name)
{
const gchar * module_name, * symbol_name;
GumQuickScope scope = GUM_QUICK_SCOPE_INIT (core);
GumAddress address;

if (!_gum_quick_args_parse (args, "s?s", &module_name, &symbol_name))
return JS_EXCEPTION;

_gum_quick_scope_suspend (&scope);

address = gum_module_find_symbol_by_name (module_name, symbol_name);

_gum_quick_scope_resume (&scope);

if (address == 0)
return JS_NULL;

return _gum_quick_native_pointer_new (ctx, GSIZE_TO_POINTER (address), core);
}

static gboolean
gum_quick_module_map_get (JSContext * ctx,
JSValueConst val,
Expand Down
31 changes: 30 additions & 1 deletion bindings/gumjs/gumv8module.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2023 Ole André Vadla Ravnås <[email protected]>
* Copyright (C) 2010-2024 Ole André Vadla Ravnås <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
Expand Down Expand Up @@ -85,6 +85,7 @@ static gboolean gum_emit_dependency (const GumDependencyDetails * details,
GumV8MatchContext<GumV8Module> * mc);
GUMJS_DECLARE_FUNCTION (gumjs_module_find_base_address)
GUMJS_DECLARE_FUNCTION (gumjs_module_find_export_by_name)
GUMJS_DECLARE_FUNCTION (gumjs_module_find_symbol_by_name)

GUMJS_DECLARE_CONSTRUCTOR (gumjs_module_map_construct)
GUMJS_DECLARE_GETTER (gumjs_module_map_get_handle)
Expand Down Expand Up @@ -117,6 +118,7 @@ static const GumV8Function gumjs_module_static_functions[] =
{ "_enumerateDependencies", gumjs_module_enumerate_dependencies },
{ "findBaseAddress", gumjs_module_find_base_address },
{ "findExportByName", gumjs_module_find_export_by_name },
{ "findSymbolByName", gumjs_module_find_symbol_by_name },

{ NULL, NULL }
};
Expand Down Expand Up @@ -630,6 +632,33 @@ GUMJS_DEFINE_FUNCTION (gumjs_module_find_export_by_name)
g_free (symbol_name);
}

GUMJS_DEFINE_FUNCTION (gumjs_module_find_symbol_by_name)
{
gchar * module_name, * symbol_name;
if (!_gum_v8_args_parse (args, "s?s", &module_name, &symbol_name))
return;

GumAddress address;
{
ScriptUnlocker unlocker (core);

address = gum_module_find_symbol_by_name (module_name, symbol_name);
}

if (address != 0)
{
info.GetReturnValue ().Set (
_gum_v8_native_pointer_new (GSIZE_TO_POINTER (address), core));
}
else
{
info.GetReturnValue ().SetNull ();
}

g_free (module_name);
g_free (symbol_name);
}

GUMJS_DEFINE_CONSTRUCTOR (gumjs_module_map_construct)
{
if (!info.IsConstructCall ())
Expand Down
23 changes: 23 additions & 0 deletions bindings/gumjs/runtime/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,17 @@ Object.defineProperties(Module, {
return address;
}
},
getSymbolByName: {
enumerable: true,
value: function (moduleName, symbolName) {
const address = Module.findSymbolByName(moduleName, symbolName);
if (address === null) {
const prefix = (moduleName !== null) ? (moduleName + ': ') : '';
throw new Error(prefix + "unable to find symbol '" + symbolName + "'");
}
return address;
}
},
});

Object.defineProperties(Module.prototype, {
Expand Down Expand Up @@ -365,6 +376,18 @@ Object.defineProperties(Module.prototype, {
return Module.getExportByName(this.path, exportName);
}
},
findSymbolByName: {
enumerable: true,
value: function (symbolName) {
return Module.findSymbolByName(this.path, symbolName);
}
},
getSymbolByName: {
enumerable: true,
value: function (symbolName) {
return Module.getSymbolByName(this.path, symbolName);
}
},
});

Object.defineProperties(ModuleMap.prototype, {
Expand Down
41 changes: 41 additions & 0 deletions tests/gumjs/script.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ TESTLIST_BEGIN (script)
TESTENTRY (module_dependencies_can_be_enumerated)
TESTENTRY (module_base_address_can_be_found)
TESTENTRY (module_export_can_be_found_by_name)
TESTENTRY (module_symbol_can_be_found_by_name)
TESTENTRY (module_can_be_loaded)
TESTENTRY (module_can_be_forcibly_initialized)
TESTGROUP_END ()
Expand Down Expand Up @@ -5872,6 +5873,46 @@ TESTCASE (module_export_can_be_found_by_name)
#endif
}

TESTCASE (module_symbol_can_be_found_by_name)
{
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}

COMPILE_AND_LOAD_SCRIPT (
"const sysModuleName = '%s';"

"const allSymbols = Process.getModuleByName(sysModuleName).enumerateSymbols();"
"const sysModuleExport = allSymbols.find(s => !s.address.isNull()).name;"

"const badModuleName = 'nope_' + sysModuleName;"
"const badModuleExport = sysModuleExport + '_does_not_exist';"

"const impl = Module.findSymbolByName(sysModuleName, sysModuleExport);"
"send(impl !== null);"

"send(Module.findSymbolByName(badModuleName, badModuleExport) === null);"

"try {"
"send(Module.getSymbolByName(sysModuleName, sysModuleExport)"
".equals(impl));"

"Module.getSymbolByName(badModuleName, badModuleExport);"
"send('should not get here');"
"} catch (e) {"
"send(/unable to find symbol/.test(e.message));"
"}",
SYSTEM_MODULE_NAME);

EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");

EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
}

TESTCASE (module_can_be_loaded)
{
COMPILE_AND_LOAD_SCRIPT (
Expand Down

0 comments on commit 949f929

Please sign in to comment.