Skip to content
Permalink
6873f1504e
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
7970 lines (6891 sloc) 225 KB
/*
* Copyright (C) 2010-2020 Ole André Vadla Ravnås <oleavr@nowsecure.com>
* Copyright (C) 2015 Marc Hartmayer <hello@hartmayer.com>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
#include "script-fixture.c"
TESTLIST_BEGIN (script)
TESTENTRY (invalid_script_should_return_null)
TESTENTRY (strict_mode_should_be_enforced)
TESTENTRY (array_buffer_can_be_created)
TESTENTRY (message_can_be_sent)
TESTENTRY (message_can_be_sent_with_data)
TESTENTRY (message_can_be_received)
TESTENTRY (message_can_be_received_with_data)
TESTENTRY (recv_may_specify_desired_message_type)
TESTENTRY (recv_can_be_waited_for_from_an_application_thread)
TESTENTRY (recv_can_be_waited_for_from_two_application_threads)
TESTENTRY (recv_can_be_waited_for_from_our_js_thread)
TESTENTRY (recv_wait_in_an_application_thread_should_throw_on_unload)
TESTENTRY (recv_wait_in_our_js_thread_should_throw_on_unload)
TESTENTRY (recv_wait_should_not_leak)
TESTENTRY (rpc_can_be_performed)
TESTENTRY (message_can_be_logged)
TESTENTRY (thread_can_be_forced_to_sleep)
TESTENTRY (timeout_can_be_scheduled)
TESTENTRY (timeout_can_be_cancelled)
TESTENTRY (interval_can_be_scheduled)
TESTENTRY (interval_can_be_cancelled)
TESTENTRY (callback_can_be_scheduled)
TESTENTRY (callback_can_be_scheduled_from_a_scheduled_callback)
TESTENTRY (callback_can_be_cancelled)
TESTENTRY (callback_can_be_scheduled_on_next_tick)
TESTENTRY (timer_cancellation_apis_should_be_forgiving)
TESTGROUP_BEGIN ("WeakRef")
TESTENTRY (weak_callback_is_triggered_on_gc)
TESTENTRY (weak_callback_is_triggered_on_unload)
TESTENTRY (weak_callback_is_triggered_on_unbind)
TESTENTRY (weak_callback_should_not_be_exclusive)
TESTGROUP_END ()
TESTGROUP_BEGIN ("Interceptor")
TESTENTRY (argument_can_be_read)
TESTENTRY (argument_can_be_replaced)
TESTENTRY (return_value_can_be_read)
TESTENTRY (return_value_can_be_replaced)
TESTENTRY (return_address_can_be_read)
TESTENTRY (register_can_be_read)
TESTENTRY (register_can_be_written)
TESTENTRY (system_error_can_be_read_from_interceptor_listener)
TESTENTRY (system_error_can_be_read_from_replacement_function)
TESTENTRY (system_error_can_be_replaced_from_interceptor_listener)
TESTENTRY (system_error_can_be_replaced_from_replacement_function)
TESTENTRY (invocations_are_bound_on_tls_object)
TESTENTRY (invocations_provide_thread_id)
TESTENTRY (invocations_provide_call_depth)
#if !defined (HAVE_QNX) && !defined (HAVE_MIPS)
TESTENTRY (invocations_provide_context_for_backtrace)
#endif
TESTENTRY (invocations_provide_context_serializable_to_json)
TESTENTRY (listener_can_be_detached)
TESTENTRY (listener_can_be_detached_by_destruction_mid_call)
TESTENTRY (all_listeners_can_be_detached)
TESTENTRY (function_can_be_replaced)
TESTENTRY (function_can_be_replaced_and_called_immediately)
TESTENTRY (function_can_be_reverted)
TESTENTRY (replaced_function_should_have_invocation_context)
TESTENTRY (instructions_can_be_probed)
TESTENTRY (interceptor_should_support_native_pointer_values)
TESTENTRY (interceptor_handles_invalid_arguments)
TESTGROUP_END ()
TESTGROUP_BEGIN ("Interceptor/Performance")
TESTENTRY (interceptor_on_enter_performance)
TESTENTRY (interceptor_on_leave_performance)
TESTENTRY (interceptor_on_enter_and_leave_performance)
TESTGROUP_END ()
TESTGROUP_BEGIN ("Memory")
TESTENTRY (pointer_can_be_read)
TESTENTRY (pointer_can_be_read_legacy_style)
TESTENTRY (pointer_can_be_written)
TESTENTRY (pointer_can_be_written_legacy_style)
TESTENTRY (memory_can_be_allocated)
TESTENTRY (memory_can_be_copied)
TESTENTRY (memory_can_be_duped)
TESTENTRY (memory_can_be_protected)
TESTENTRY (code_can_be_patched)
TESTENTRY (s8_can_be_read)
TESTENTRY (s8_can_be_written)
TESTENTRY (u8_can_be_read)
TESTENTRY (u8_can_be_written)
TESTENTRY (s16_can_be_read)
TESTENTRY (s16_can_be_written)
TESTENTRY (u16_can_be_read)
TESTENTRY (u16_can_be_written)
TESTENTRY (s32_can_be_read)
TESTENTRY (s32_can_be_written)
TESTENTRY (u32_can_be_read)
TESTENTRY (u32_can_be_written)
TESTENTRY (s64_can_be_read)
TESTENTRY (s64_can_be_written)
TESTENTRY (u64_can_be_read)
TESTENTRY (u64_can_be_written)
TESTENTRY (short_can_be_read)
TESTENTRY (short_can_be_written)
TESTENTRY (ushort_can_be_read)
TESTENTRY (ushort_can_be_written)
TESTENTRY (int_can_be_read)
TESTENTRY (int_can_be_written)
TESTENTRY (uint_can_be_read)
TESTENTRY (uint_can_be_written)
TESTENTRY (long_can_be_read)
TESTENTRY (long_can_be_written)
TESTENTRY (ulong_can_be_read)
TESTENTRY (ulong_can_be_written)
TESTENTRY (float_can_be_read)
TESTENTRY (float_can_be_written)
TESTENTRY (double_can_be_read)
TESTENTRY (double_can_be_written)
TESTENTRY (byte_array_can_be_read)
TESTENTRY (byte_array_can_be_written)
TESTENTRY (c_string_can_be_read)
TESTENTRY (utf8_string_can_be_read)
TESTENTRY (utf8_string_can_be_written)
TESTENTRY (utf8_string_can_be_allocated)
TESTENTRY (utf16_string_can_be_read)
TESTENTRY (utf16_string_can_be_written)
TESTENTRY (utf16_string_can_be_allocated)
#ifdef HAVE_WINDOWS
TESTENTRY (ansi_string_can_be_read_in_code_page_936)
TESTENTRY (ansi_string_can_be_read_in_code_page_1252)
TESTENTRY (ansi_string_can_be_written_in_code_page_936)
TESTENTRY (ansi_string_can_be_written_in_code_page_1252)
TESTENTRY (ansi_string_can_be_allocated_in_code_page_936)
TESTENTRY (ansi_string_can_be_allocated_in_code_page_1252)
#endif
TESTENTRY (invalid_read_results_in_exception)
TESTENTRY (invalid_write_results_in_exception)
TESTENTRY (invalid_read_write_execute_results_in_exception)
TESTENTRY (memory_can_be_scanned)
TESTENTRY (memory_can_be_scanned_synchronously)
TESTENTRY (memory_scan_should_be_interruptible)
TESTENTRY (memory_scan_handles_unreadable_memory)
TESTENTRY (memory_access_can_be_monitored)
TESTENTRY (memory_access_can_be_monitored_one_range)
TESTGROUP_END ()
TESTENTRY (frida_version_is_available)
TESTENTRY (frida_heap_size_can_be_queried)
TESTGROUP_BEGIN ("Process")
TESTENTRY (process_arch_is_available)
TESTENTRY (process_platform_is_available)
TESTENTRY (process_page_size_is_available)
TESTENTRY (process_pointer_size_is_available)
TESTENTRY (process_should_support_nested_signal_handling)
#ifndef HAVE_QNX
TESTENTRY (process_debugger_status_is_available)
#endif
TESTENTRY (process_id_is_available)
TESTENTRY (process_current_thread_id_is_available)
TESTENTRY (process_threads_can_be_enumerated)
TESTENTRY (process_threads_can_be_enumerated_legacy_style)
TESTENTRY (process_modules_can_be_enumerated)
TESTENTRY (process_modules_can_be_enumerated_legacy_style)
TESTENTRY (process_module_can_be_looked_up_from_address)
TESTENTRY (process_module_can_be_looked_up_from_name)
TESTENTRY (process_ranges_can_be_enumerated)
TESTENTRY (process_ranges_can_be_enumerated_legacy_style)
TESTENTRY (process_ranges_can_be_enumerated_with_neighbors_coalesced)
TESTENTRY (process_range_can_be_looked_up_from_address)
TESTENTRY (process_system_ranges_can_be_enumerated)
#ifdef HAVE_DARWIN
TESTENTRY (process_malloc_ranges_can_be_enumerated)
TESTENTRY (process_malloc_ranges_can_be_enumerated_legacy_style)
#endif
TESTGROUP_END ()
TESTGROUP_BEGIN ("Module")
#ifndef HAVE_QNX
TESTENTRY (module_imports_can_be_enumerated)
TESTENTRY (module_imports_can_be_enumerated_legacy_style)
#endif
TESTENTRY (module_exports_can_be_enumerated)
TESTENTRY (module_exports_can_be_enumerated_legacy_style)
TESTENTRY (module_exports_enumeration_performance)
TESTENTRY (module_symbols_can_be_enumerated)
TESTENTRY (module_symbols_can_be_enumerated_legacy_style)
TESTENTRY (module_ranges_can_be_enumerated)
TESTENTRY (module_ranges_can_be_enumerated_legacy_style)
TESTENTRY (module_base_address_can_be_found)
TESTENTRY (module_export_can_be_found_by_name)
TESTENTRY (module_can_be_loaded)
TESTENTRY (module_can_be_forcibly_initialized)
TESTGROUP_END ()
TESTGROUP_BEGIN ("ModuleMap")
TESTENTRY (module_map_values_should_have_module_prototype)
TESTGROUP_END ()
TESTGROUP_BEGIN ("ApiResolver")
TESTENTRY (api_resolver_can_be_used_to_find_functions)
TESTENTRY (api_resolver_can_be_used_to_find_functions_legacy_style)
TESTGROUP_END ()
TESTGROUP_BEGIN ("Socket")
TESTENTRY (socket_connection_can_be_established)
TESTENTRY (socket_connection_can_be_established_with_tls)
TESTENTRY (socket_connection_should_not_leak_on_error)
TESTENTRY (socket_type_can_be_inspected)
TESTENTRY (socket_endpoints_can_be_inspected)
TESTGROUP_END ()
TESTGROUP_BEGIN ("Stream")
#ifdef G_OS_UNIX
TESTENTRY (unix_fd_can_be_read_from)
TESTENTRY (unix_fd_can_be_written_to)
#endif
TESTGROUP_END ()
TESTGROUP_BEGIN ("Hexdump")
TESTENTRY (basic_hexdump_functionality_is_available)
TESTENTRY (hexdump_supports_native_pointer_conforming_object)
TESTGROUP_END ()
TESTGROUP_BEGIN ("NativePointer")
TESTENTRY (native_pointer_provides_is_null)
TESTENTRY (native_pointer_provides_arithmetic_operations)
TESTENTRY (native_pointer_provides_uint32_conversion_functionality)
TESTENTRY (native_pointer_provides_ptrauth_functionality)
TESTENTRY (native_pointer_to_match_pattern)
TESTENTRY (native_pointer_can_be_constructed_from_64bit_value)
TESTENTRY (native_pointer_should_be_serializable_to_json)
TESTGROUP_END ()
TESTGROUP_BEGIN ("ArrayBuffer")
TESTENTRY (array_buffer_can_wrap_memory_region)
TESTENTRY (array_buffer_can_be_unwrapped)
TESTGROUP_END ()
TESTGROUP_BEGIN ("UInt64")
TESTENTRY (uint64_provides_arithmetic_operations)
TESTENTRY (uint64_can_be_constructed_from_a_large_number)
TESTENTRY (uint64_can_be_converted_to_a_large_number)
TESTGROUP_END ()
TESTGROUP_BEGIN ("Int64")
TESTENTRY (int64_provides_arithmetic_operations)
TESTGROUP_END ()
TESTGROUP_BEGIN ("NativeFunction")
TESTENTRY (native_function_can_be_invoked)
TESTENTRY (native_function_can_be_intercepted_when_thread_is_ignored)
TESTENTRY (native_function_should_implement_call_and_apply)
TESTENTRY (native_function_crash_results_in_exception)
TESTENTRY (nested_native_function_crash_is_handled_gracefully)
TESTENTRY (variadic_native_function_can_be_invoked)
TESTENTRY (
variadic_native_function_args_smaller_than_int_should_be_promoted)
TESTENTRY (variadic_native_function_float_args_should_be_promoted_to_double)
TESTENTRY (native_function_is_a_native_pointer)
TESTGROUP_END ()
TESTGROUP_BEGIN ("SystemFunction")
TESTENTRY (system_function_can_be_invoked)
TESTENTRY (system_function_should_implement_call_and_apply)
TESTENTRY (system_function_is_a_native_pointer)
TESTGROUP_END ()
TESTGROUP_BEGIN ("NativeCallback")
TESTENTRY (native_callback_can_be_invoked)
TESTENTRY (native_callback_is_a_native_pointer)
TESTENTRY (native_callback_memory_should_be_eagerly_reclaimed)
TESTENTRY (native_callback_should_be_kept_alive_during_calls)
TESTGROUP_END ()
TESTGROUP_BEGIN ("DebugSymbol")
TESTENTRY (address_can_be_resolved_to_symbol)
TESTENTRY (name_can_be_resolved_to_symbol)
TESTENTRY (function_can_be_found_by_name)
TESTENTRY (functions_can_be_found_by_name)
TESTENTRY (functions_can_be_found_by_matching)
TESTGROUP_END ()
TESTGROUP_BEGIN ("CModule")
#ifdef HAVE_TINYCC
TESTENTRY (cmodule_can_be_defined)
TESTENTRY (cmodule_symbols_can_be_provided)
TESTENTRY (cmodule_should_report_parsing_errors)
TESTENTRY (cmodule_should_report_linking_errors)
TESTENTRY (cmodule_should_provide_lifecycle_hooks)
TESTENTRY (cmodule_can_be_used_with_interceptor_attach)
TESTENTRY (cmodule_can_be_used_with_interceptor_replace)
TESTENTRY (cmodule_can_be_used_with_stalker_events)
TESTENTRY (cmodule_can_be_used_with_stalker_transform)
TESTENTRY (cmodule_can_be_used_with_stalker_callout)
TESTENTRY (cmodule_can_be_used_with_stalker_call_probe)
TESTENTRY (cmodule_can_be_used_with_module_map)
TESTENTRY (cmodule_should_provide_some_builtin_string_functions)
TESTENTRY (cmodule_should_support_floating_point)
TESTENTRY (cmodule_should_support_varargs)
TESTENTRY (cmodule_should_support_global_callbacks)
TESTENTRY (cmodule_should_provide_access_to_cpu_registers)
TESTENTRY (cmodule_should_provide_access_to_system_error)
#else
TESTENTRY (cmodule_constructor_should_throw_not_available)
#endif
TESTGROUP_END ()
TESTGROUP_BEGIN ("Instruction")
TESTENTRY (instruction_can_be_parsed)
TESTENTRY (instruction_can_be_generated)
TESTENTRY (instruction_can_be_relocated)
TESTGROUP_END ()
TESTGROUP_BEGIN ("File")
TESTENTRY (file_can_be_written_to)
TESTGROUP_END ()
TESTGROUP_BEGIN ("Database")
TESTENTRY (inline_sqlite_database_can_be_queried)
TESTENTRY (external_sqlite_database_can_be_queried)
TESTENTRY (external_sqlite_database_can_be_opened_with_flags)
TESTGROUP_END ()
TESTGROUP_BEGIN ("Stalker")
#if defined (HAVE_I386) || defined (HAVE_ARM) || defined (HAVE_ARM64)
TESTENTRY (execution_can_be_traced)
TESTENTRY (execution_can_be_traced_with_custom_transformer)
TESTENTRY (execution_can_be_traced_with_faulty_transformer)
TESTENTRY (execution_can_be_traced_during_immediate_native_function_call)
TESTENTRY (execution_can_be_traced_during_scheduled_native_function_call)
TESTENTRY (execution_can_be_traced_after_native_function_call_from_hook)
#endif
#if defined (HAVE_I386) || defined (HAVE_ARM64)
TESTENTRY (call_can_be_probed)
#endif
TESTENTRY (stalker_events_can_be_parsed)
TESTGROUP_END ()
TESTENTRY (script_can_be_compiled_to_bytecode)
TESTENTRY (script_can_be_reloaded)
TESTENTRY (script_should_not_leak_if_destroyed_before_load)
TESTENTRY (script_memory_usage)
TESTENTRY (source_maps_should_be_supported_for_our_runtime)
TESTENTRY (source_maps_should_be_supported_for_user_scripts)
TESTENTRY (types_handle_invalid_construction)
TESTENTRY (globals_can_be_dynamically_generated)
TESTENTRY (exceptions_can_be_handled)
TESTENTRY (debugger_can_be_enabled)
TESTENTRY (objc_api_is_embedded)
TESTENTRY (java_api_is_embedded)
TESTLIST_END ()
typedef struct _GumInvokeTargetContext GumInvokeTargetContext;
typedef struct _TestTrigger TestTrigger;
struct _GumInvokeTargetContext
{
GumScript * script;
guint repeat_duration;
volatile gint started;
volatile gint finished;
};
struct _TestTrigger
{
volatile gboolean ready;
volatile gboolean fired;
GMutex mutex;
GCond cond;
};
static gboolean ignore_thread (GumInterceptor * interceptor);
static gboolean unignore_thread (GumInterceptor * interceptor);
static gint gum_assert_variadic_uint8_values_are_sane (gpointer a, gpointer b,
gpointer c, gpointer d, ...);
static gint gum_clobber_system_error (gint value);
static gint gum_get_answer_to_life_universe_and_everything (void);
static gint gum_toupper (gchar * str, gint limit);
static gint64 gum_classify_timestamp (gint64 timestamp);
static guint64 gum_square (guint64 value);
static gint gum_sum (gint count, ...);
static gint gum_add_pointers_and_float_fixed (gpointer a, gpointer b, float c);
static gint gum_add_pointers_and_float_variadic (gpointer a, ...);
static gboolean on_incoming_connection (GSocketService * service,
GSocketConnection * connection, GObject * source_object,
gpointer user_data);
static void on_read_ready (GObject * source_object, GAsyncResult * res,
gpointer user_data);
#if defined (HAVE_I386) || defined (HAVE_ARM) || defined (HAVE_ARM64)
static gpointer run_stalked_through_hooked_function (gpointer data);
static gpointer run_stalked_through_target_function (gpointer data);
#endif
static gpointer sleeping_dummy (gpointer data);
static gpointer invoke_target_function_int_worker (gpointer data);
static gpointer invoke_target_function_trigger (gpointer data);
static void measure_target_function_int_overhead (void);
static int compare_measurements (gconstpointer element_a,
gconstpointer element_b);
static gboolean check_exception_handling_testable (void);
static void on_script_message (GumScript * script, const gchar * message,
GBytes * data, gpointer user_data);
static void on_incoming_debug_message (GumInspectorServer * server,
const gchar * message, gpointer user_data);
static void on_outgoing_debug_message (const gchar * message,
gpointer user_data);
static int target_function_int (int arg);
static const guint8 * target_function_base_plus_offset (const guint8 * base,
int offset);
static const gchar * target_function_string (const gchar * arg);
static void target_function_callbacks (const gint value,
void (* first) (const gint * value), void (* second) (const gint * value));
static void target_function_trigger (TestTrigger * trigger);
static int target_function_nested_a (int arg);
static int target_function_nested_b (int arg);
static int target_function_nested_c (int arg);
gint gum_script_dummy_global_to_trick_optimizer = 0;
TESTCASE (instruction_can_be_parsed)
{
COMPILE_AND_LOAD_SCRIPT (
"var first = Instruction.parse(" GUM_PTR_CONST ");"
"var second = Instruction.parse(first.next);"
"send(typeof first.toString());"
"send(typeof second.toString());"
"send(second.toString().indexOf(\"[object\") !== 0);"
"send(first.address.toInt32() !== 0);"
"send(first.size > 0);"
"send(typeof first.mnemonic);"
"send(typeof first.opStr);"
"send(JSON.stringify(first) !== \"{}\");",
target_function_int);
EXPECT_SEND_MESSAGE_WITH ("\"string\"");
EXPECT_SEND_MESSAGE_WITH ("\"string\"");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("\"string\"");
EXPECT_SEND_MESSAGE_WITH ("\"string\"");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_NO_MESSAGES ();
if (!gum_process_is_debugger_attached () && !RUNNING_ON_VALGRIND)
{
COMPILE_AND_LOAD_SCRIPT ("Instruction.parse(ptr(\"0x1\"));");
EXPECT_ERROR_MESSAGE_WITH (ANY_LINE_NUMBER,
"Error: access violation accessing 0x1");
}
#if defined (HAVE_I386) && GLIB_SIZEOF_VOID_P == 8
COMPILE_AND_LOAD_SCRIPT (
"var code = Memory.alloc(Process.pageSize);"
"var cw = new X86Writer(code, { pc: ptr(0x1000) });"
"send(cw.pc);"
"send(cw.offset);"
"cw.putU8(0xab);" /* stosd */
"send(cw.pc);"
"send(cw.offset);"
"send(cw.code.equals(cw.base.add(1)));"
"cw.putMovRegU32('eax', 42);"
"cw.putCallRegOffsetPtr('rax', 12);"
"cw.flush();"
"var stosd = Instruction.parse(code);"
"send(stosd.mnemonic);"
"send(stosd.regsRead);"
"send(stosd.regsWritten);"
"send(stosd.groups);"
"var mov = Instruction.parse(stosd.next);"
"send(mov.mnemonic);"
"var operands = mov.operands;"
"send(operands.length);"
"send(operands[0].type);"
"send(operands[0].value);"
"send(operands[0].size);"
"send(operands[1].type);"
"send(operands[1].value);"
"send(operands[1].size);"
"send(mov.regsRead);"
"send(mov.regsWritten);"
"send(mov.groups);"
"var call = Instruction.parse(mov.next);"
"send(call.mnemonic);"
"operands = call.operands;"
"send(operands[0].type);"
"var memProps = Object.keys(operands[0].value);"
"memProps.sort();"
"send(memProps);"
"send(operands[0].value.base);"
"send(operands[0].value.scale);"
"send(operands[0].value.disp);"
"send(call.groups);");
EXPECT_SEND_MESSAGE_WITH ("\"0x1000\"");
EXPECT_SEND_MESSAGE_WITH ("0");
EXPECT_SEND_MESSAGE_WITH ("\"0x1001\"");
EXPECT_SEND_MESSAGE_WITH ("1");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("\"stosd\"");
EXPECT_SEND_MESSAGE_WITH ("[\"eax\",\"rdi\",\"rflags\"]");
EXPECT_SEND_MESSAGE_WITH ("[\"rdi\"]");
EXPECT_SEND_MESSAGE_WITH ("[]");
EXPECT_SEND_MESSAGE_WITH ("\"mov\"");
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_SEND_MESSAGE_WITH ("\"reg\"");
EXPECT_SEND_MESSAGE_WITH ("\"eax\"");
EXPECT_SEND_MESSAGE_WITH ("4");
EXPECT_SEND_MESSAGE_WITH ("\"imm\"");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("4");
EXPECT_SEND_MESSAGE_WITH ("[]");
EXPECT_SEND_MESSAGE_WITH ("[]");
EXPECT_SEND_MESSAGE_WITH ("[]");
EXPECT_SEND_MESSAGE_WITH ("\"call\"");
EXPECT_SEND_MESSAGE_WITH ("\"mem\"");
EXPECT_SEND_MESSAGE_WITH ("[\"base\",\"disp\",\"scale\"]");
EXPECT_SEND_MESSAGE_WITH ("\"rax\"");
EXPECT_SEND_MESSAGE_WITH ("1");
EXPECT_SEND_MESSAGE_WITH ("12");
EXPECT_SEND_MESSAGE_WITH ("[\"call\",\"mode64\"]");
#elif defined (HAVE_ARM)
COMPILE_AND_LOAD_SCRIPT (
"var code = Memory.alloc(Process.pageSize);"
"var tw = new ThumbWriter(code);"
"tw.putLdrRegU32('r0', 42);"
"tw.putBlImm(code.add(64));"
/* sxtb.w r3, r7, ror 16 */
"tw.putInstruction(0xfa4f); tw.putInstruction(0xf3a7);"
/* vdup.8 d3, d7[1] */
"tw.putInstruction(0xffb3); tw.putInstruction(0x3c07);"
"tw.flush();"
"var ldr = Instruction.parse(code.or(1));"
"send(ldr.mnemonic);"
"var operands = ldr.operands;"
"send(operands.length);"
"send(operands[0].type);"
"send(operands[0].value);"
"send(operands[1].type);"
"send(operands[1].value.base);"
"send(operands[1].value.scale);"
"var disp = operands[1].value.disp;"
"send(ldr.address.add(4 + disp).readU32());"
"var bl = Instruction.parse(ldr.next);"
"send(bl.mnemonic);"
"operands = bl.operands;"
"send(operands[0].type);"
"send(ptr(operands[0].value).equals(code.add(64)));"
"var sxtb = Instruction.parse(bl.next);"
"send(sxtb.mnemonic);"
"operands = sxtb.operands;"
"send(typeof operands[0].shift);"
"send(operands[1].shift.type);"
"send(operands[1].shift.value);"
"var vdup = Instruction.parse(sxtb.next);"
"send(vdup.mnemonic);"
"operands = vdup.operands;"
"send(typeof operands[0].vectorIndex);"
"send(operands[1].vectorIndex);"
"var aw = new ArmWriter(code);"
"aw.putInstruction(0xe00380f7);" /* strd r8, sb, [r3], -r7 */
"aw.flush();"
"var strdeq = Instruction.parse(code);"
"send(strdeq.mnemonic);"
"operands = strdeq.operands;"
"send(operands[0].subtracted);"
"send(operands[1].subtracted);"
"send(operands[2].subtracted);"
"send(operands[3].subtracted);");
EXPECT_SEND_MESSAGE_WITH ("\"ldr\"");
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_SEND_MESSAGE_WITH ("\"reg\"");
EXPECT_SEND_MESSAGE_WITH ("\"r0\"");
EXPECT_SEND_MESSAGE_WITH ("\"mem\"");
EXPECT_SEND_MESSAGE_WITH ("\"pc\"");
EXPECT_SEND_MESSAGE_WITH ("1");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("\"bl\"");
EXPECT_SEND_MESSAGE_WITH ("\"imm\"");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("\"sxtb.w\"");
EXPECT_SEND_MESSAGE_WITH ("\"undefined\"");
EXPECT_SEND_MESSAGE_WITH ("\"ror\"");
EXPECT_SEND_MESSAGE_WITH ("16");
EXPECT_SEND_MESSAGE_WITH ("\"vdup.8\"");
EXPECT_SEND_MESSAGE_WITH ("\"undefined\"");
EXPECT_SEND_MESSAGE_WITH ("1");
EXPECT_SEND_MESSAGE_WITH ("\"strd\"");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("true");
#elif defined (HAVE_ARM64)
COMPILE_AND_LOAD_SCRIPT (
"var code = Memory.alloc(Process.pageSize);"
"var cw = new Arm64Writer(code);"
"cw.putLdrRegU64('x0', 42);"
"cw.putStrRegRegOffset('x0', 'x7', 32);"
"cw.putInstruction(0xcb422020);" /* sub x0, x1, x2, lsr #8 */
"cw.putInstruction(0x8b230841);" /* add x1, x2, w3, uxtb #2 */
"cw.putInstruction(0x4ee28420);" /* add.2d v0, v1, v2 */
"cw.putInstruction(0x9eae00e5);" /* fmov.d x5, v7[1] */
"cw.flush();"
"var ldr = Instruction.parse(code);"
"send(ldr.mnemonic);"
"var operands = ldr.operands;"
"send(operands.length);"
"send(operands[0].type);"
"send(operands[0].value);"
"send(operands[1].type);"
"send(ptr(operands[1].value).readU64().valueOf());"
"var str = Instruction.parse(ldr.next);"
"send(str.mnemonic);"
"operands = str.operands;"
"send(operands[1].type);"
"var memProps = Object.keys(operands[1].value);"
"memProps.sort();"
"send(memProps);"
"send(operands[1].value.base);"
"send(operands[1].value.disp);"
"var sub = Instruction.parse(str.next);"
"send(sub.mnemonic);"
"operands = sub.operands;"
"send(typeof operands[0].shift);"
"send(typeof operands[1].shift);"
"send(operands[2].shift.type);"
"send(operands[2].shift.value);"
"var add = Instruction.parse(sub.next);"
"send(add.mnemonic);"
"operands = add.operands;"
"send(typeof operands[0].ext);"
"send(typeof operands[1].ext);"
"send(operands[2].ext);"
"var vadd = Instruction.parse(add.next);"
"send(vadd.mnemonic);"
"operands = vadd.operands;"
"send(operands[0].vas);"
"send(operands[1].vas);"
"send(operands[2].vas);"
"var fmov = Instruction.parse(vadd.next);"
"send(fmov.mnemonic);"
"operands = fmov.operands;"
"send(typeof operands[0].vectorIndex);"
"send(operands[1].vectorIndex);");
EXPECT_SEND_MESSAGE_WITH ("\"ldr\"");
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_SEND_MESSAGE_WITH ("\"reg\"");
EXPECT_SEND_MESSAGE_WITH ("\"x0\"");
EXPECT_SEND_MESSAGE_WITH ("\"imm\"");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("\"str\"");
EXPECT_SEND_MESSAGE_WITH ("\"mem\"");
EXPECT_SEND_MESSAGE_WITH ("[\"base\",\"disp\"]");
EXPECT_SEND_MESSAGE_WITH ("\"x7\"");
EXPECT_SEND_MESSAGE_WITH ("32");
EXPECT_SEND_MESSAGE_WITH ("\"sub\"");
EXPECT_SEND_MESSAGE_WITH ("\"undefined\"");
EXPECT_SEND_MESSAGE_WITH ("\"undefined\"");
EXPECT_SEND_MESSAGE_WITH ("\"lsr\"");
EXPECT_SEND_MESSAGE_WITH ("8");
EXPECT_SEND_MESSAGE_WITH ("\"add\"");
EXPECT_SEND_MESSAGE_WITH ("\"undefined\"");
EXPECT_SEND_MESSAGE_WITH ("\"undefined\"");
EXPECT_SEND_MESSAGE_WITH ("\"uxtb\"");
EXPECT_SEND_MESSAGE_WITH ("\"add\"");
EXPECT_SEND_MESSAGE_WITH ("\"2d\"");
EXPECT_SEND_MESSAGE_WITH ("\"2d\"");
EXPECT_SEND_MESSAGE_WITH ("\"2d\"");
EXPECT_SEND_MESSAGE_WITH ("\"fmov\"");
EXPECT_SEND_MESSAGE_WITH ("\"undefined\"");
EXPECT_SEND_MESSAGE_WITH ("1");
#endif
}
TESTCASE (instruction_can_be_generated)
{
#if defined (HAVE_I386)
COMPILE_AND_LOAD_SCRIPT (
"var callback = new NativeCallback(function (a, b) {"
" return a * b;"
"}, 'int', ['int', 'int']);"
"var page = Memory.alloc(Process.pageSize);"
"Memory.patchCode(page, 64, function (code) {"
"var cw = new X86Writer(code, { pc: page });"
"cw.putMovRegU32('eax', 42);"
"var stackAlignOffset = Process.pointerSize;"
"cw.putSubRegImm('xsp', stackAlignOffset);"
"cw.putCallAddressWithArguments(callback, ['eax', 7]);"
"cw.putAddRegImm('xsp', stackAlignOffset);"
"cw.putJmpShortLabel('badger');"
"cw.putMovRegU32('eax', 43);"
"cw.putLabel('badger');"
"cw.putRet();"
"cw.flush();"
"send(cw.offset > 30);"
"});"
"var f = new NativeFunction(page, 'int', []);"
"send(f());");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("294");
EXPECT_NO_MESSAGES ();
COMPILE_AND_LOAD_SCRIPT (
"var code = Memory.alloc(16);"
"var cw = new X86Writer(code);"
"cw.putMovRegU32('rax', 42);");
EXPECT_ERROR_MESSAGE_WITH (ANY_LINE_NUMBER, "Error: invalid argument");
#endif
}
TESTCASE (instruction_can_be_relocated)
{
#if defined (HAVE_I386)
COMPILE_AND_LOAD_SCRIPT (
"var page = Memory.alloc(Process.pageSize);"
"var impl1 = page.add(0);"
"var impl2 = page.add(64);"
"Memory.patchCode(impl1, 16, function (code) {"
"var cw = new X86Writer(code, { pc: impl1 });"
"cw.putMovRegU32('eax', 42);"
"cw.putRet();"
"cw.flush();"
"});"
"Memory.patchCode(impl2, 16, function (code) {"
"var cw = new X86Writer(code, { pc: impl2 });"
"var rl = new X86Relocator(impl1, cw);"
"send(rl.input);"
"send(rl.readOne());"
"send(rl.input.toString());"
"send(rl.writeOne());"
"send(rl.eob);"
"send(rl.eoi);"
"send(rl.readOne());"
"send(rl.input.toString());"
"send(rl.writeOne());"
"send(rl.readOne());"
"send(rl.eob);"
"send(rl.eoi);"
"cw.flush();"
"});"
"var f = new NativeFunction(impl2, 'int', []);"
"send(f());");
EXPECT_SEND_MESSAGE_WITH ("null");
EXPECT_SEND_MESSAGE_WITH ("5");
EXPECT_SEND_MESSAGE_WITH ("\"mov eax, 0x2a\"");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("6");
EXPECT_SEND_MESSAGE_WITH ("\"ret\"");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("0");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_NO_MESSAGES ();
#endif
}
TESTCASE (address_can_be_resolved_to_symbol)
{
#ifdef HAVE_ANDROID
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
#endif
COMPILE_AND_LOAD_SCRIPT (
"var sym = DebugSymbol.fromAddress(" GUM_PTR_CONST ");"
"send(sym.name);"
"send(sym.toString().indexOf(sym.name) !== -1);"
"send(JSON.stringify(sym) !== \"{}\");",
target_function_int);
EXPECT_SEND_MESSAGE_WITH ("\"target_function_int\"");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_NO_MESSAGES ();
}
TESTCASE (name_can_be_resolved_to_symbol)
{
gchar * expected;
#ifdef HAVE_ANDROID
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
#endif
COMPILE_AND_LOAD_SCRIPT (
"send(DebugSymbol.fromName(\"target_function_int\").address);");
expected = g_strdup_printf ("\"0x%" G_GINT64_MODIFIER "x\"",
GUM_ADDRESS (target_function_int));
EXPECT_SEND_MESSAGE_WITH (expected);
g_free (expected);
EXPECT_NO_MESSAGES ();
}
TESTCASE (function_can_be_found_by_name)
{
#ifdef HAVE_ANDROID
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
#endif
COMPILE_AND_LOAD_SCRIPT ("send("
"!DebugSymbol.getFunctionByName(\"g_thread_new\").isNull()"
");"
"send("
"DebugSymbol.getFunctionByName(\"g_thread_!@#$\")"
");");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_ERROR_MESSAGE_WITH (ANY_LINE_NUMBER,
"Error: unable to find function with name 'g_thread_!@#$'");
EXPECT_NO_MESSAGES ();
}
TESTCASE (functions_can_be_found_by_name)
{
#ifdef HAVE_ANDROID
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
#endif
COMPILE_AND_LOAD_SCRIPT ("send("
"DebugSymbol.findFunctionsNamed(\"g_thread_new\").length >= 1"
");");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_NO_MESSAGES ();
}
TESTCASE (functions_can_be_found_by_matching)
{
#ifdef HAVE_ANDROID
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
#endif
COMPILE_AND_LOAD_SCRIPT ("send("
"DebugSymbol.findFunctionsMatching(\"gum_symbol_details_from*\")"
".length >= 1"
");");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_NO_MESSAGES ();
}
TESTCASE (native_function_can_be_invoked)
{
gchar str[7];
COMPILE_AND_LOAD_SCRIPT (
"var f = new NativeFunction(" GUM_PTR_CONST ", 'int', []);"
"send(f());",
gum_get_answer_to_life_universe_and_everything);
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_NO_MESSAGES ();
strcpy (str, "badger");
COMPILE_AND_LOAD_SCRIPT (
"var toupper = new NativeFunction(" GUM_PTR_CONST ", "
"'int', ['pointer', 'int']);"
"send(toupper(" GUM_PTR_CONST ", 3));"
"send(toupper(" GUM_PTR_CONST ", -1));",
gum_toupper, str, str);
EXPECT_SEND_MESSAGE_WITH ("3");
EXPECT_SEND_MESSAGE_WITH ("-6");
EXPECT_NO_MESSAGES ();
g_assert_cmpstr (str, ==, "BADGER");
COMPILE_AND_LOAD_SCRIPT (
"var sum = new NativeFunction(" GUM_PTR_CONST ", "
"'int', ['pointer', 'pointer', 'float']);"
"send(sum(ptr(3), ptr(4), 42.0));",
gum_add_pointers_and_float_fixed);
EXPECT_SEND_MESSAGE_WITH ("49");
EXPECT_NO_MESSAGES ();
#ifdef HAVE_WINDOWS
COMPILE_AND_LOAD_SCRIPT (
"var impl = Module.getExportByName(\"user32.dll\", \"GetKeyState\");"
"var f = new NativeFunction(impl, 'int16', ['int']);"
"var result = f(0x41);"
"send(typeof result);");
EXPECT_SEND_MESSAGE_WITH ("\"number\"");
EXPECT_NO_MESSAGES ();
#endif
COMPILE_AND_LOAD_SCRIPT (
"var classify = new NativeFunction(" GUM_PTR_CONST ", "
"'int64', ['int64']);"
"send(classify(int64(\"-42\")));"
"send(classify(int64(\"0\")));"
"send(classify(int64(\"42\")));",
gum_classify_timestamp);
EXPECT_SEND_MESSAGE_WITH ("\"-1\"");
EXPECT_SEND_MESSAGE_WITH ("\"0\"");
EXPECT_SEND_MESSAGE_WITH ("\"1\"");
EXPECT_NO_MESSAGES ();
COMPILE_AND_LOAD_SCRIPT (
"var square = new NativeFunction(" GUM_PTR_CONST ", "
"'uint64', ['uint64']);"
"send(square(uint64(\"2\")));"
"send(square(uint64(\"4\")));"
"send(square(uint64(\"6\")));",
gum_square);
EXPECT_SEND_MESSAGE_WITH ("\"4\"");
EXPECT_SEND_MESSAGE_WITH ("\"16\"");
EXPECT_SEND_MESSAGE_WITH ("\"36\"");
EXPECT_NO_MESSAGES ();
}
TESTCASE (native_function_can_be_intercepted_when_thread_is_ignored)
{
GumInterceptor * interceptor;
GMainContext * js_context;
GSource * source;
interceptor = gum_interceptor_obtain ();
js_context = gum_script_scheduler_get_js_context (
gum_script_backend_get_scheduler ());
source = g_idle_source_new ();
g_source_set_callback (source, (GSourceFunc) ignore_thread,
g_object_ref (interceptor), g_object_unref);
g_source_attach (source, js_context);
g_source_unref (source);
COMPILE_AND_LOAD_SCRIPT (
"var impl = " GUM_PTR_CONST ";"
"Interceptor.attach(impl, {"
" onEnter: function (args) {"
" send('>');"
" },"
" onLeave: function (retval) {"
" send('<');"
" }"
"});"
"Interceptor.flush();"
"var f = new NativeFunction(impl, 'int', ['int']);"
"send(f(42));",
target_function_nested_a);
EXPECT_SEND_MESSAGE_WITH ("\">\"");
EXPECT_SEND_MESSAGE_WITH ("\"<\"");
EXPECT_SEND_MESSAGE_WITH ("16855020");
EXPECT_NO_MESSAGES ();
source = g_idle_source_new ();
g_source_set_callback (source, (GSourceFunc) unignore_thread,
g_object_ref (interceptor), g_object_unref);
g_source_attach (source, js_context);
g_source_unref (source);
g_object_unref (interceptor);
}
static gboolean
ignore_thread (GumInterceptor * interceptor)
{
gum_interceptor_ignore_current_thread (interceptor);
return FALSE;
}
static gboolean
unignore_thread (GumInterceptor * interceptor)
{
gum_interceptor_unignore_current_thread (interceptor);
return FALSE;
}
TESTCASE (native_function_should_implement_call_and_apply)
{
COMPILE_AND_LOAD_SCRIPT (
"var f = new NativeFunction(" GUM_PTR_CONST ", 'int', []);"
"send(f.call());"
"send(f.call(f));"
"send(f.apply(f));"
"send(f.apply(f, undefined));"
"send(f.apply(f, null));"
"send(f.apply(f, []));",
gum_get_answer_to_life_universe_and_everything);
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_NO_MESSAGES ();
COMPILE_AND_LOAD_SCRIPT (
"var f = new NativeFunction(" GUM_PTR_CONST ", 'int', ['int']);"
"send(NativeFunction.prototype.call(f, 42));"
"send(NativeFunction.prototype.apply(f, [42]));"
"send(f.call(undefined, 42));"
"send(f.apply(undefined, [42]));"
"send(f.call(null, 42));"
"send(f.apply(null, [42]));"
"send(f.call(f, 42));"
"send(f.apply(f, [42]));"
"send(f.call(ptr(" GUM_PTR_CONST "), 42));"
"send(f.apply(ptr(" GUM_PTR_CONST "), [42]));",
target_function_int, target_function_nested_a, target_function_nested_a);
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("16855020");
EXPECT_SEND_MESSAGE_WITH ("16855020");
EXPECT_NO_MESSAGES ();
COMPILE_AND_LOAD_SCRIPT (
"var f = new NativeFunction(" GUM_PTR_CONST ", 'pointer', "
" ['pointer', 'int']);"
"send(f.call(null, ptr(4), 3));"
"send(f.apply(null, [ptr(4), 3]));",
target_function_base_plus_offset);
EXPECT_SEND_MESSAGE_WITH ("\"0x7\"");
EXPECT_SEND_MESSAGE_WITH ("\"0x7\"");
EXPECT_NO_MESSAGES ();
}
TESTCASE (native_function_crash_results_in_exception)
{
if (!check_exception_handling_testable ())
return;
COMPILE_AND_LOAD_SCRIPT (
"var targetWithString = new NativeFunction(" GUM_PTR_CONST ", 'pointer', "
"['pointer'], {"
"abi: 'default',"
"scheduling: 'exclusive',"
"exceptions: 'steal',"
"});"
"try {"
" targetWithString(NULL);"
"} catch (e) {"
" send(e.type);"
"}",
target_function_string);
EXPECT_SEND_MESSAGE_WITH ("\"access-violation\"");
}
TESTCASE (nested_native_function_crash_is_handled_gracefully)
{
if (!check_exception_handling_testable ())
return;
COMPILE_AND_LOAD_SCRIPT (
"var targetWithCallback = new NativeFunction(" GUM_PTR_CONST ", "
"'pointer', ['int', 'pointer', 'pointer']);"
"var callback = new NativeCallback(function (value) {"
" send(value.readInt());"
"}, 'void', ['pointer']);"
"try {"
" targetWithCallback(42, callback, NULL);"
"} catch (e) {"
" send(e.type);"
"}",
target_function_callbacks);
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("\"access-violation\"");
EXPECT_NO_MESSAGES ();
}
TESTCASE (variadic_native_function_can_be_invoked)
{
COMPILE_AND_LOAD_SCRIPT (
"var sum = new NativeFunction(" GUM_PTR_CONST ", "
"'int', ['int', '...', 'int']);"
"send(sum(0));"
"send(sum(1, 1));"
"send(sum(3, 1, 2, 3));",
gum_sum);
EXPECT_SEND_MESSAGE_WITH ("0");
EXPECT_SEND_MESSAGE_WITH ("1");
EXPECT_SEND_MESSAGE_WITH ("6");
EXPECT_NO_MESSAGES ();
}
TESTCASE (variadic_native_function_args_smaller_than_int_should_be_promoted)
{
COMPILE_AND_LOAD_SCRIPT (
"var f = new NativeFunction(" GUM_PTR_CONST ", 'int', "
"['pointer', 'pointer', 'pointer', 'pointer', '...', "
"'uint8', 'pointer', 'uint8']);"
"var val = NULL.not();"
"send(f(val, val, val, val, 13, val, 37));",
gum_assert_variadic_uint8_values_are_sane);
EXPECT_SEND_MESSAGE_WITH ("42");
}
static gint
gum_assert_variadic_uint8_values_are_sane (gpointer a,
gpointer b,
gpointer c,
gpointer d,
...)
{
va_list args;
gint e;
gint g;
va_start (args, d);
e = va_arg (args, gint);
va_arg (args, gpointer);
g = va_arg (args, gint);
va_end (args);
g_assert_cmphex (e, ==, 13);
g_assert_cmphex (g, ==, 37);
return 42;
}
TESTCASE (variadic_native_function_float_args_should_be_promoted_to_double)
{
COMPILE_AND_LOAD_SCRIPT (
"var sum = new NativeFunction(" GUM_PTR_CONST ", "
"'int', ['pointer', '...', 'pointer', 'float']);"
"send(sum(ptr(3), NULL));"
"send(sum(ptr(3), ptr(4), 42.0, NULL));"
"send(sum(ptr(3), ptr(4), 42.0, ptr(100), 200.0, NULL));",
gum_add_pointers_and_float_variadic);
EXPECT_SEND_MESSAGE_WITH ("3");
EXPECT_SEND_MESSAGE_WITH ("49");
EXPECT_SEND_MESSAGE_WITH ("349");
EXPECT_NO_MESSAGES ();
}
TESTCASE (native_function_is_a_native_pointer)
{
COMPILE_AND_LOAD_SCRIPT (
"var toupper = new NativeFunction(" GUM_PTR_CONST ", "
"'int', ['pointer', 'int']);"
"send(toupper instanceof NativePointer);"
"send(toupper.toString() === " GUM_PTR_CONST ".toString());",
gum_toupper, gum_toupper);
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
}
TESTCASE (system_function_can_be_invoked)
{
#ifdef HAVE_WINDOWS
COMPILE_AND_LOAD_SCRIPT (
"var f = new SystemFunction(" GUM_PTR_CONST ", 'int', ['int']);"
"var result = f(13);"
"send(result.value);"
"send(result.lastError);"
"result = f(37);"
"send(result.value);"
"send(result.lastError);", gum_clobber_system_error);
#else
COMPILE_AND_LOAD_SCRIPT (
"var f = new SystemFunction(" GUM_PTR_CONST ", 'int', ['int']);"
"var result = f(13);"
"send(result.value);"
"send(result.errno);"
"result = f(37);"
"send(result.value);"
"send(result.errno);", gum_clobber_system_error);
#endif
EXPECT_SEND_MESSAGE_WITH ("26");
EXPECT_SEND_MESSAGE_WITH ("13");
EXPECT_SEND_MESSAGE_WITH ("74");
EXPECT_SEND_MESSAGE_WITH ("37");
EXPECT_NO_MESSAGES ();
}
TESTCASE (system_function_should_implement_call_and_apply)
{
COMPILE_AND_LOAD_SCRIPT (
"var f = new SystemFunction(" GUM_PTR_CONST ", 'int', []);"
"send(f.call().value);"
"send(f.call(f).value);"
"send(f.apply(f).value);"
"send(f.apply(f, undefined).value);"
"send(f.apply(f, null).value);"
"send(f.apply(f, []).value);",
gum_get_answer_to_life_universe_and_everything);
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_SEND_MESSAGE_WITH ("42");
EXPECT_NO_MESSAGES ();
COMPILE_AND_LOAD_SCRIPT (
"var f = new SystemFunction(" GUM_PTR_CONST ", 'int', ['int']);"
"send(SystemFunction.prototype.call(f, 42).value);"
"send(SystemFunction.prototype.apply(f, [42]).value);"
"send(f.call(undefined, 42).value);"
"send(f.apply(undefined, [42]).value);"
"send(f.call(null, 42).value);"
"send(f.apply(null, [42]).value);"
"send(f.call(f, 42).value);"
"send(f.apply(f, [42]).value);"
"send(f.call(ptr(" GUM_PTR_CONST "), 42).value);"
"send(f.apply(ptr(" GUM_PTR_CONST "), [42]).value);",
target_function_int, target_function_nested_a, target_function_nested_a);
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("1890");
EXPECT_SEND_MESSAGE_WITH ("16855020");
EXPECT_SEND_MESSAGE_WITH ("16855020");
EXPECT_NO_MESSAGES ();
COMPILE_AND_LOAD_SCRIPT (
"var f = new SystemFunction(" GUM_PTR_CONST ", 'pointer', "
" ['pointer', 'int']);"
"send(f.call(null, ptr(4), 3).value);"
"send(f.apply(null, [ptr(4), 3]).value);",
target_function_base_plus_offset);
EXPECT_SEND_MESSAGE_WITH ("\"0x7\"");
EXPECT_SEND_MESSAGE_WITH ("\"0x7\"");
EXPECT_NO_MESSAGES ();
}
TESTCASE (system_function_is_a_native_pointer)
{
COMPILE_AND_LOAD_SCRIPT (
"var toupper = new SystemFunction(" GUM_PTR_CONST ", "
"'int', ['pointer', 'int']);"
"send(toupper instanceof NativePointer);"
"send(toupper.toString() === " GUM_PTR_CONST ".toString());",
gum_toupper, gum_toupper);
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
}
static gint
gum_clobber_system_error (gint value)
{
#ifdef HAVE_WINDOWS
SetLastError (value);
#else
errno = value;
#endif
return value * 2;
}
TESTCASE (native_callback_can_be_invoked)
{
gint (* toupper_impl) (gchar * str, gint limit);
gchar str[7];
COMPILE_AND_LOAD_SCRIPT (
"var toupper = new NativeCallback(function (str, limit) {"
" var count = 0;"
" while (count < limit || limit === -1) {"
" var p = str.add(count);"
" var b = p.readU8();"
" if (b === 0)"
" break;"
" p.writeU8(String.fromCharCode(b).toUpperCase().charCodeAt(0));"
" count++;"
" }"
" return (limit === -1) ? -count : count;"
"}, 'int', ['pointer', 'int']);"
"gc();"
"send(toupper);");
toupper_impl = EXPECT_SEND_MESSAGE_WITH_POINTER ();
g_assert_nonnull (toupper_impl);
strcpy (str, "badger");
g_assert_cmpint (toupper_impl (str, 3), ==, 3);
g_assert_cmpstr (str, ==, "BADger");
g_assert_cmpint (toupper_impl (str, -1), ==, -6);
g_assert_cmpstr (str, ==, "BADGER");
}
TESTCASE (native_callback_is_a_native_pointer)
{
COMPILE_AND_LOAD_SCRIPT (
"var cb = new NativeCallback(function () {}, 'void', []);"
"send(cb instanceof NativePointer);");
EXPECT_SEND_MESSAGE_WITH ("true");
}
TESTCASE (native_callback_memory_should_be_eagerly_reclaimed)
{
guint usage_before, usage_after;
gboolean difference_is_less_than_2x;
COMPILE_AND_LOAD_SCRIPT (
"var iterationsRemaining = null;"
"recv('start', onStartRequest);"
"function onStartRequest(message) {"
" iterationsRemaining = message.iterations;"
" processNext();"
"}"
"function processNext() {"
" var cb = new NativeCallback(function () {}, 'void', []);"
" if (--iterationsRemaining === 0) {"
" recv('start', onStartRequest);"
" gc();"
" send('done');"
" } else {"
" setTimeout(processNext, 0);"
" }"
"}");
EXPECT_NO_MESSAGES ();
PUSH_TIMEOUT (20000);
POST_MESSAGE ("{\"type\":\"start\",\"iterations\":5000}");
EXPECT_SEND_MESSAGE_WITH ("\"done\"");
EXPECT_NO_MESSAGES ();
usage_before = gum_peek_private_memory_usage ();
POST_MESSAGE ("{\"type\":\"start\",\"iterations\":5000}");
EXPECT_SEND_MESSAGE_WITH ("\"done\"");
EXPECT_NO_MESSAGES ();
usage_after = gum_peek_private_memory_usage ();
POP_TIMEOUT ();
difference_is_less_than_2x = usage_after < usage_before * 2;
if (!difference_is_less_than_2x)
{
g_printerr ("\n\n"
"Oops, memory usage is not looking good:\n"
"\tusage before: %u\n"
"\t vs after: %u\n\n",
usage_before, usage_after);
g_assert_true (difference_is_less_than_2x);
}
}
TESTCASE (native_callback_should_be_kept_alive_during_calls)
{
void (* cb) (void);
COMPILE_AND_LOAD_SCRIPT (
"let cb = new NativeCallback(() => {"
"cb = null;"
"gc();"
"send('returning');"
"}, 'void', []);"
"WeakRef.bind(cb, () => { send('dead'); });"
GUM_PTR_CONST ".writePointer(cb);",
&cb);
EXPECT_NO_MESSAGES ();
cb ();
EXPECT_SEND_MESSAGE_WITH ("\"returning\"");
EXPECT_SEND_MESSAGE_WITH ("\"dead\"");
EXPECT_NO_MESSAGES ();
}
#ifdef G_OS_UNIX
#define GUM_TEMP_FAILURE_RETRY(expression) \
({ \
gssize __result; \
\
do __result = (gssize) (expression); \
while (__result == -EINTR); \
\
__result; \
})
TESTCASE (unix_fd_can_be_read_from)
{
gint fds[2];
const guint8 message[7] = { 0x13, 0x37, 0xca, 0xfe, 0xba, 0xbe, 0xff };
gssize res;
g_assert_cmpint (socketpair (AF_UNIX, SOCK_STREAM, 0, fds), ==, 0);
COMPILE_AND_LOAD_SCRIPT (
"var stream = new UnixInputStream(%d, { autoClose: false });"
"stream.read(1337)"
".then(function (buf) {"
"send(buf.byteLength, buf);"
"});",
fds[0]);
EXPECT_NO_MESSAGES ();
res = GUM_TEMP_FAILURE_RETRY (write (fds[1], message, 1));
g_assert_cmpint (res, ==, 1);
EXPECT_SEND_MESSAGE_WITH_PAYLOAD_AND_DATA ("1", "13");
EXPECT_NO_MESSAGES ();
COMPILE_AND_LOAD_SCRIPT (
"var stream = new UnixInputStream(%d, { autoClose: false });"
"stream.readAll(7)"
".then(function (buf) {"
"send(buf.byteLength, buf);"
"});",
fds[0]);
EXPECT_NO_MESSAGES ();
res = GUM_TEMP_FAILURE_RETRY (write (fds[1], message, 4));
g_assert_cmpint (res, ==, 4);
g_usleep (G_USEC_PER_SEC / 20);
EXPECT_NO_MESSAGES ();
res = GUM_TEMP_FAILURE_RETRY (write (fds[1], message + 4, 3));
g_assert_cmpint (res, ==, 3);
EXPECT_SEND_MESSAGE_WITH_PAYLOAD_AND_DATA ("7", "13 37 ca fe ba be ff");
EXPECT_NO_MESSAGES ();
res = GUM_TEMP_FAILURE_RETRY (write (fds[1], message, 2));
g_assert_cmpint (res, ==, 2);
close (fds[1]);
COMPILE_AND_LOAD_SCRIPT (
"var stream = new UnixInputStream(%d, { autoClose: false });"
"stream.readAll(7)"
".catch(function (error) {"
"send(error.toString(), error.partialData);"
"});",
fds[0]);
EXPECT_SEND_MESSAGE_WITH ("\"Error: short read\"", "13 37");
EXPECT_NO_MESSAGES ();
COMPILE_AND_LOAD_SCRIPT (
"var stream = new UnixInputStream(%d, { autoClose: false });"
"stream.close()"
".then(function (success) {"
"send(success);"
"stream.read(1337)"
".catch(function (error) {"
"send(error.toString());"
"});"
"});",
fds[0]);
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("\"Error: stream is already closed\"");
EXPECT_NO_MESSAGES ();
COMPILE_AND_LOAD_SCRIPT (
"var stream = new UnixInputStream(%d, { autoClose: false });"
"stream.close()"
".then(function (success) {"
"send(success);"
"stream.close()"
".then(function (success) {"
"send(success);"
"});"
"});",
fds[0]);
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_NO_MESSAGES ();
close (fds[0]);
}
TESTCASE (unix_fd_can_be_written_to)
{
gint fds[2];
guint8 buffer[8];
sig_t original_sigpipe_handler;
if (gum_process_is_debugger_attached ())
{
g_print ("<skipping, debugger is attached> ");
return;
}
original_sigpipe_handler = signal (SIGPIPE, SIG_IGN);
g_assert_cmpint (socketpair (AF_UNIX, SOCK_STREAM, 0, fds), ==, 0);
COMPILE_AND_LOAD_SCRIPT (
"var stream = new UnixOutputStream(%d, { autoClose: false });"
"stream.write([0x13])"
".then(function (size) {"
"send(size);"
"});",
fds[0]);
EXPECT_SEND_MESSAGE_WITH ("1");
EXPECT_NO_MESSAGES ();
g_assert_cmpint (read (fds[1], buffer, sizeof (buffer)), ==, 1);
g_assert_cmphex (buffer[0], ==, 0x13);
COMPILE_AND_LOAD_SCRIPT (
"var stream = new UnixOutputStream(%d, { autoClose: false });"
"stream.writeAll([0x13, 0x37, 0xca, 0xfe, 0xba, 0xbe, 0xff])"
".then(function (size) {"
"send(size);"
"});",
fds[0]);
EXPECT_SEND_MESSAGE_WITH ("7");
EXPECT_NO_MESSAGES ();
g_assert_cmpint (read (fds[1], buffer, sizeof (buffer)), ==, 7);
g_assert_cmphex (buffer[0], ==, 0x13);
g_assert_cmphex (buffer[1], ==, 0x37);
g_assert_cmphex (buffer[2], ==, 0xca);
g_assert_cmphex (buffer[3], ==, 0xfe);
g_assert_cmphex (buffer[4], ==, 0xba);
g_assert_cmphex (buffer[5], ==, 0xbe);
g_assert_cmphex (buffer[6], ==, 0xff);
close (fds[1]);
COMPILE_AND_LOAD_SCRIPT (
"var stream = new UnixOutputStream(%d, { autoClose: false });"
"stream.writeAll([0x13, 0x37, 0xca, 0xfe, 0xba, 0xbe, 0xff])"
".catch(function (error) {"
"send(error.partialSize);"
"});",
fds[0]);
EXPECT_SEND_MESSAGE_WITH ("0");
EXPECT_NO_MESSAGES ();
close (fds[0]);
signal (SIGPIPE, original_sigpipe_handler);
}
#endif
TESTCASE (basic_hexdump_functionality_is_available)
{
COMPILE_AND_LOAD_SCRIPT (
"var str = Memory.allocUtf8String(\"Hello hex world! w00t\");"
"var buf = str.readByteArray(22);"
"send(hexdump(buf));");
EXPECT_SEND_MESSAGE_WITH ("\""
" 0 1 2 3 4 5 6 7 8 9 A B C D E F "
"0123456789ABCDEF\\n"
"00000000 48 65 6c 6c 6f 20 68 65 78 20 77 6f 72 6c 64 21 "
"Hello hex world!\\n"
"00000010 20 77 30 30 74 00 "
" w00t.\"");
COMPILE_AND_LOAD_SCRIPT (
"var str = Memory.allocUtf8String(\"Hello hex world! w00t\");"
"send(hexdump(str, { address: uint64('0x100000000'), length: 22 }));");
EXPECT_SEND_MESSAGE_WITH ("\""
" 0 1 2 3 4 5 6 7 8 9 A B C D E F "
"0123456789ABCDEF\\n"
"100000000 48 65 6c 6c 6f 20 68 65 78 20 77 6f 72 6c 64 21 "
"Hello hex world!\\n"
"100000010 20 77 30 30 74 00 "
" w00t.\"");
}
TESTCASE (hexdump_supports_native_pointer_conforming_object)
{
const gchar * message = "Hello hex world!";
COMPILE_AND_LOAD_SCRIPT (
"var obj = { handle: " GUM_PTR_CONST " };"
"send(hexdump(obj, { address: NULL, length: 16 }));", message);
EXPECT_SEND_MESSAGE_WITH ("\""
" 0 1 2 3 4 5 6 7 8 9 A B C D E F "
"0123456789ABCDEF\\n"
"00000000 48 65 6c 6c 6f 20 68 65 78 20 77 6f 72 6c 64 21 "
"Hello hex world!\"");
}
TESTCASE (native_pointer_provides_is_null)
{
COMPILE_AND_LOAD_SCRIPT (
"send(ptr(\"0\").isNull());"
"send(ptr(\"1337\").isNull());");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("false");
}
TESTCASE (native_pointer_provides_arithmetic_operations)
{
COMPILE_AND_LOAD_SCRIPT (
"send(ptr(3).add(4).toInt32());"
"send(ptr(7).sub(4).toInt32());"
"send(ptr(6).and(3).toInt32());"
"send(ptr(6).or(3).toInt32());"
"send(ptr(6).xor(3).toInt32());"
"send(ptr(63).shr(4).toInt32());"
"send(ptr(1).shl(3).toInt32());"
"send(ptr(0).not().toInt32());");
EXPECT_SEND_MESSAGE_WITH ("7");
EXPECT_SEND_MESSAGE_WITH ("3");
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_SEND_MESSAGE_WITH ("7");
EXPECT_SEND_MESSAGE_WITH ("5");
EXPECT_SEND_MESSAGE_WITH ("3");
EXPECT_SEND_MESSAGE_WITH ("8");
EXPECT_SEND_MESSAGE_WITH ("-1");
}
TESTCASE (native_pointer_provides_uint32_conversion_functionality)
{
COMPILE_AND_LOAD_SCRIPT ("send(ptr(1).toUInt32());");
EXPECT_SEND_MESSAGE_WITH ("1");
}
TESTCASE (native_pointer_provides_ptrauth_functionality)
{
#ifdef HAVE_PTRAUTH
COMPILE_AND_LOAD_SCRIPT (
"var original = ptr(1);"
"var a = original.sign();"
"send(a.equals(original));"
"send(a.strip().equals(original));"
"send(original.sign('ia').equals(a));"
"send(original.sign('ib').equals(a));"
"send(original.sign('da').equals(a));"
"send(original.sign('db').equals(a));"
"var b = original.sign('ia', ptr(1337));"
"send(b.equals(a));"
"var c = original.sign('ia', 1337);"
"send(c.equals(b));"
"var d = original.sign('ia', ptr(1337).blend(42));"
"send(d.equals(b));"
"try {"
"original.sign('x');"
"} catch (e) {"
"send(e.message);"
"}");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("\"invalid key\"");
#else
COMPILE_AND_LOAD_SCRIPT (
"var original = ptr(1);"
"send(original.sign() === original);"
"send(original.strip() === original);"
"send(original.blend(42) === original);");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
#endif
EXPECT_NO_MESSAGES ();
}
TESTCASE (native_pointer_to_match_pattern)
{
const gchar * extra_checks;
#if GLIB_SIZEOF_VOID_P == 4
extra_checks = "";
#else
extra_checks = "send(ptr(\"0xa1b2c3d4e5f6a7b8\").toMatchPattern());";
#endif
COMPILE_AND_LOAD_SCRIPT (
"send(ptr(\"0x0\").toMatchPattern());"
"send(ptr(\"0xa\").toMatchPattern());"
"send(ptr(\"0xa1b\").toMatchPattern());"
"send(ptr(\"0xa1b2\").toMatchPattern());"
"send(ptr(\"0xa1b2c3\").toMatchPattern());"
"send(ptr(\"0xa1b2c3d4\").toMatchPattern());"
"%s",
extra_checks);
#if GLIB_SIZEOF_VOID_P == 4
# if G_BYTE_ORDER == G_LITTLE_ENDIAN
EXPECT_SEND_MESSAGE_WITH ("\"00 00 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"0a 00 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"1b 0a 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"b2 a1 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"c3 b2 a1 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"d4 c3 b2 a1\"");
# else
EXPECT_SEND_MESSAGE_WITH ("\"00 00 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"00 00 00 0a\"");
EXPECT_SEND_MESSAGE_WITH ("\"00 00 0a 1b\"");
EXPECT_SEND_MESSAGE_WITH ("\"00 00 a1 b2\"");
EXPECT_SEND_MESSAGE_WITH ("\"00 a1 b2 c3\"");
EXPECT_SEND_MESSAGE_WITH ("\"a1 b2 c3 d4\"");
# endif
#else
# if G_BYTE_ORDER == G_LITTLE_ENDIAN
EXPECT_SEND_MESSAGE_WITH ("\"00 00 00 00 00 00 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"0a 00 00 00 00 00 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"1b 0a 00 00 00 00 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"b2 a1 00 00 00 00 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"c3 b2 a1 00 00 00 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"d4 c3 b2 a1 00 00 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"b8 a7 f6 e5 d4 c3 b2 a1\"");
# else
EXPECT_SEND_MESSAGE_WITH ("\"00 00 00 00 00 00 00 00\"");
EXPECT_SEND_MESSAGE_WITH ("\"00 00 00 00 00 00 00 0a\"");
EXPECT_SEND_MESSAGE_WITH ("\"00 00 00 00 00 00 0a 1b\"");
EXPECT_SEND_MESSAGE_WITH ("\"00 00 00 00 00 00 a1 b2\"");
EXPECT_SEND_MESSAGE_WITH ("\"00 00 00 00 00 a1 b2 c3\"");
EXPECT_SEND_MESSAGE_WITH ("\"00 00 00 00 a1 b2 c3 d4\"");
EXPECT_SEND_MESSAGE_WITH ("\"a1 b2 c3 d4 e5 f6 a7 b8\"");
# endif
#endif
}
TESTCASE (native_pointer_can_be_constructed_from_64bit_value)
{
COMPILE_AND_LOAD_SCRIPT (
"send(ptr(uint64(0x1ffffffff)).equals(ptr(0x1ffffffff)));"
"send(ptr(int64(0x2ffffffff)).equals(ptr(0x2ffffffff)));");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
#if GLIB_SIZEOF_VOID_P == 4
COMPILE_AND_LOAD_SCRIPT (
"send(ptr(int64(-150450112)).equals(ptr('0xf7085040')));");
EXPECT_SEND_MESSAGE_WITH ("true");
#elif GLIB_SIZEOF_VOID_P == 8
COMPILE_AND_LOAD_SCRIPT (
"send(ptr(int64(-1)).equals(ptr('0xffffffffffffffff')));");
EXPECT_SEND_MESSAGE_WITH ("true");
#endif
}
TESTCASE (native_pointer_should_be_serializable_to_json)
{
COMPILE_AND_LOAD_SCRIPT ("send(ptr(1).toJSON());");
EXPECT_SEND_MESSAGE_WITH ("\"0x1\"");
}
TESTCASE (array_buffer_can_wrap_memory_region)
{
guint8 val[2] = { 13, 37 };
COMPILE_AND_LOAD_SCRIPT (
"var val = new Uint8Array(ArrayBuffer.wrap(" GUM_PTR_CONST ", 2));"
"send(val.length);"
"send(val[0]);"
"send(val[1]);"
"val[0] = 42;"
"val[1] = 24;",
val);
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_SEND_MESSAGE_WITH ("13");
EXPECT_SEND_MESSAGE_WITH ("37");
g_assert_cmpint (val[0], ==, 42);
g_assert_cmpint (val[1], ==, 24);
COMPILE_AND_LOAD_SCRIPT (
"var val = new Uint8Array(ArrayBuffer.wrap(" GUM_PTR_CONST ", 0));"
"send(val.length);"
"send(typeof val[0]);",
val);
EXPECT_SEND_MESSAGE_WITH ("0");
EXPECT_SEND_MESSAGE_WITH ("\"undefined\"");
COMPILE_AND_LOAD_SCRIPT (
"var val = new Uint8Array(ArrayBuffer.wrap(NULL, 0));"
"send(val.length);"
"send(typeof val[0]);");
EXPECT_SEND_MESSAGE_WITH ("0");
EXPECT_SEND_MESSAGE_WITH ("\"undefined\"");
}
TESTCASE (array_buffer_can_be_unwrapped)
{
gchar str[5 + 1];
COMPILE_AND_LOAD_SCRIPT (
"var toupper = new NativeFunction(" GUM_PTR_CONST ", "
"'int', ['pointer', 'int']);"
"var buf = new ArrayBuffer(2 + 1);"
"var bytes = new Uint8Array(buf);"
"bytes[0] = 'h'.charCodeAt(0);"
"bytes[1] = 'i'.charCodeAt(0);"
"send(toupper(buf.unwrap(), -1));"
"send(bytes[0]);"
"send(bytes[1]);",
gum_toupper, str);
EXPECT_SEND_MESSAGE_WITH ("-2");
EXPECT_SEND_MESSAGE_WITH ("72");
EXPECT_SEND_MESSAGE_WITH ("73");
EXPECT_NO_MESSAGES ();
strcpy (str, "snake");
COMPILE_AND_LOAD_SCRIPT (
"var toupper = new NativeFunction(" GUM_PTR_CONST ", "
"'int', ['pointer', 'int']);"
"var buf = ArrayBuffer.wrap(" GUM_PTR_CONST ", 5 + 1);"
"send(toupper(buf.unwrap(), -1));",
gum_toupper, str);
EXPECT_SEND_MESSAGE_WITH ("-5");
EXPECT_NO_MESSAGES ();
g_assert_cmpstr (str, ==, "SNAKE");
}
TESTCASE (uint64_provides_arithmetic_operations)
{
COMPILE_AND_LOAD_SCRIPT (
"send(uint64(3).add(4).toNumber());"
"send(uint64(7).sub(4).toNumber());"
"send(uint64(6).and(3).toNumber());"
"send(uint64(6).or(3).toNumber());"
"send(uint64(6).xor(3).toNumber());"
"send(uint64(63).shr(4).toNumber());"
"send(uint64(1).shl(3).toNumber());"
"send(uint64(0).not().toString());");
EXPECT_SEND_MESSAGE_WITH ("7");
EXPECT_SEND_MESSAGE_WITH ("3");
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_SEND_MESSAGE_WITH ("7");
EXPECT_SEND_MESSAGE_WITH ("5");
EXPECT_SEND_MESSAGE_WITH ("3");
EXPECT_SEND_MESSAGE_WITH ("8");
EXPECT_SEND_MESSAGE_WITH ("\"18446744073709551615\"");
}
TESTCASE (uint64_can_be_constructed_from_a_large_number)
{
COMPILE_AND_LOAD_SCRIPT ("send(uint64(Math.pow(2, 63)).toString(16));");
EXPECT_SEND_MESSAGE_WITH ("\"8000000000000000\"");
}
TESTCASE (uint64_can_be_converted_to_a_large_number)
{
COMPILE_AND_LOAD_SCRIPT (
"var a = Math.pow(2, 63);"
"var b = uint64(a).toNumber();"
"send(b === a);");
EXPECT_SEND_MESSAGE_WITH ("true");
}
TESTCASE (int64_provides_arithmetic_operations)
{
COMPILE_AND_LOAD_SCRIPT (
"send(int64(3).add(4).toNumber());"
"send(int64(7).sub(4).toNumber());"
"send(int64(6).and(3).toNumber());"
"send(int64(6).or(3).toNumber());"
"send(int64(6).xor(3).toNumber());"
"send(int64(63).shr(4).toNumber());"
"send(int64(1).shl(3).toNumber());"
"send(int64(0).not().toNumber());");
EXPECT_SEND_MESSAGE_WITH ("7");
EXPECT_SEND_MESSAGE_WITH ("3");
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_SEND_MESSAGE_WITH ("7");
EXPECT_SEND_MESSAGE_WITH ("5");
EXPECT_SEND_MESSAGE_WITH ("3");
EXPECT_SEND_MESSAGE_WITH ("8");
EXPECT_SEND_MESSAGE_WITH ("-1");
}
static gint
gum_get_answer_to_life_universe_and_everything (void)
{
return 42;
}
static gint
gum_toupper (gchar * str,
gint limit)
{
gint count = 0;
gchar * c;
for (c = str; *c != '\0' && (count < limit || limit == -1); c++, count++)
{
*c = g_ascii_toupper (*c);
}
return (limit == -1) ? -count : count;
}
static gint64
gum_classify_timestamp (gint64 timestamp)
{
if (timestamp < 0)
return -1;
else if (timestamp > 0)
return 1;
else
return 0;
}
static guint64
gum_square (guint64 value)
{
return value * value;
}
static gint
gum_sum (gint count,
...)
{
gint total = 0;
va_list args;
gint i;
va_start (args, count);
for (i = 0; i != count; i++)
total += va_arg (args, gint);
va_end (args);
return total;
}
static gint
gum_add_pointers_and_float_fixed (gpointer a,
gpointer b,
float c)
{
return GPOINTER_TO_SIZE (a) + GPOINTER_TO_SIZE (b) + (int) c;
}
static gint
gum_add_pointers_and_float_variadic (gpointer a,
...)
{
gint total = GPOINTER_TO_SIZE (a);
va_list args;
gpointer p;
va_start (args, a);
while ((p = va_arg (args, gpointer)) != NULL)
{
total += GPOINTER_TO_SIZE (p);
total += (int) va_arg (args, double); /* float is promoted to double */
}
va_end (args);
return total;
}
TESTCASE (file_can_be_written_to)
{
gchar d00d[4] = { 0x64, 0x30, 0x30, 0x64 };
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
COMPILE_AND_LOAD_SCRIPT (
"var log = new File(\"/tmp/script-test.log\", 'a');"
"log.write(\"Hello \");"
"log.write(" GUM_PTR_CONST ".readByteArray(4));"
"log.write(\"!\\n\");"
"log.close();",
d00d);
EXPECT_NO_MESSAGES ();
}
TESTCASE (inline_sqlite_database_can_be_queried)
{
COMPILE_AND_LOAD_SCRIPT (
"var db = SqliteDatabase.openInline('"
"H4sIAMMIT1kAA+3ZsU7DMBAG4HMC7VChROpQut0IqGJhYCWJDAq4LbhGoqNRDYqgpIo"
"CO8y8JM/AC+CKFNhgLfo/+U7n0/kBTp5cqKJ2fFNWc1vzAcUkBB0xE1HYxIrwsdHUYX"
"P/TUj7m+nWcjhy5A8AAAAAAADA//W8Ldq9fl+8dGp7fe8WrlyscphpmRjJJkmV5M8e7"
"xQzzkdGnkjN5zofJnrKZ3LKySQb8IOdOzbyyvBo7ONSqQHbW/f14Lt7Z/1S7+uh1Hn2"
"c/rJ1rbiVI3T3b8s8QAAAAAAAACw3pZ/80H0RtG7TwAAAAAAAACwnuKgRT0RxMdVMbN"
"teu0edkSLukLQaen2Hj8AoNOJGgAwAAA="
"');\n"
/* 1: bindInteger() */
"var s = db.prepare('SELECT name, age FROM people WHERE age = ?');\n"
"s.bindInteger(1, 42);\n"
"send(s.step());\n"
"send(s.step());\n"
"s.reset();\n"
"s.bindInteger(1, 7);\n"
"send(s.step());\n"
/* 2: bindFloat() */
"s = db.prepare('SELECT name FROM people WHERE karma <= ?');\n"
"s.bindFloat(1, 117.5);\n"
"send(s.step());\n"
"send(s.step());\n"
/* 3: bindText() */
"s = db.prepare('SELECT age FROM people WHERE name = ?');\n"
"s.bindText(1, 'Joe');\n"
"send(s.step());\n"
/* 4: bindBlob() */
"s = db.prepare('SELECT name FROM people WHERE avatar = ?');\n"
"s.bindBlob(1, [0x13, 0x37]);\n"
"send(s.step());\n"
"send(s.step());\n"
/* 5: bindNull() */
"s = db.prepare('INSERT INTO people VALUES (?, ?, ?, ?, ?)');\n"
"s.bindInteger(1, 3);\n"
"s.bindText(2, 'Alice');\n"
"s.bindInteger(3, 40);\n"
"s.bindInteger(4, 150);\n"
"s.bindNull(5);\n"
"send(s.step());\n"
"s = db.prepare('SELECT * FROM people WHERE name = \"Alice\"');\n"
"send(s.step());\n"
"send(s.step());\n"
/* 6: blob column */
"s = db.prepare('SELECT avatar FROM people WHERE name = ?');\n"
"s.bindText(1, 'Frida');\n"
"send('avatar', s.step()[0]);\n"
"send(s.step());\n"
"s.reset();\n"
"s.bindText(1, 'Joe');\n"
"send(s.step());\n"
"send(s.step());\n");
/* 1: bindInteger() */
EXPECT_SEND_MESSAGE_WITH ("[\"Joe\",42]");
EXPECT_SEND_MESSAGE_WITH ("null");
EXPECT_SEND_MESSAGE_WITH ("[\"Frida\",7]");
/* 2: bindFloat() */
EXPECT_SEND_MESSAGE_WITH ("[\"Joe\"]");
EXPECT_SEND_MESSAGE_WITH ("null");
/* 3: bindText() */
EXPECT_SEND_MESSAGE_WITH ("[42]");
/* 4: bindBlob() */
EXPECT_SEND_MESSAGE_WITH ("[\"Frida\"]");
EXPECT_SEND_MESSAGE_WITH ("null");
/* 5: bindNull() */
EXPECT_SEND_MESSAGE_WITH ("null");
EXPECT_SEND_MESSAGE_WITH ("[3,\"Alice\",40,150,null]");
EXPECT_SEND_MESSAGE_WITH ("null");
/* 6: blob column */
EXPECT_SEND_MESSAGE_WITH_PAYLOAD_AND_DATA ("\"avatar\"", "13 37");
EXPECT_SEND_MESSAGE_WITH ("null");
EXPECT_SEND_MESSAGE_WITH ("[null]");
EXPECT_SEND_MESSAGE_WITH ("null");
EXPECT_NO_MESSAGES ();
}
TESTCASE (external_sqlite_database_can_be_queried)
{
TestScriptMessageItem * item;
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
COMPILE_AND_LOAD_SCRIPT (
"var db = SqliteDatabase.open('/tmp/gum-test.db');\n"
"db.exec(\""
"PRAGMA foreign_keys=OFF;"
"BEGIN TRANSACTION;"
"CREATE TABLE people ("
"id INTEGER PRIMARY KEY ASC,"
"name TEXT NOT NULL,"
"age INTEGER NOT NULL,"
"karma NUMERIC NOT NULL,"
"avatar BLOB"
");"
"INSERT INTO people VALUES (1, 'Joe', 42, 117, NULL);"
"INSERT INTO people VALUES (2, 'Frida', 7, 140, X'1337');"
"COMMIT;"
"\");\n"
"send(db.dump());\n"
"db.close();\n");
item = test_script_fixture_pop_message (fixture);
g_print ("%s\n", item->message);
test_script_message_item_free (item);
}
TESTCASE (external_sqlite_database_can_be_opened_with_flags)
{
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
COMPILE_AND_LOAD_SCRIPT (
"var db = null;\n"
"try {\n"
"db = SqliteDatabase.open('/tmp/gum-test-dont-create.db',"
"{ flags: ['readwrite'] });\n"
"send('fail');\n"
"db.close();\n"
"} catch (e) {\n"
"send('not exists');\n"
"}\n"
"try {\n"
"db = SqliteDatabase.open('/tmp/gum-test-dont-create2.db',"
"{ flags: ['readonly'] });\n"
"send('fail');\n"
"db.close();\n"
"} catch (e) {\n"
"send('not exists again');\n"
"}\n"
"try {\n"
"db = SqliteDatabase.open('/tmp/gum-test-dont-write.db',"
"{ flags: ['readonly', 'create'] });\n"
"send('fail');\n"
"db.close();\n"
"} catch (e) {\n"
"send('invalid flags');\n"
"}\n"
"db = SqliteDatabase.open('/tmp/gum-test-can-write.db',"
"{ flags: ['readwrite', 'create'] });\n"
"try {\n"
"db.exec(\""
"PRAGMA foreign_keys=OFF;"
"BEGIN TRANSACTION;"
"CREATE TABLE people ("
"id INTEGER PRIMARY KEY ASC,"
"name TEXT NOT NULL,"
"age INTEGER NOT NULL,"
"karma NUMERIC NOT NULL,"
"avatar BLOB"
");"
"INSERT INTO people VALUES (1, 'Joe', 42, 117, NULL);"
"INSERT INTO people VALUES (2, 'Frida', 7, 140, X'1337');"
"COMMIT;"
"\");\n"
"send('can write');\n"
"} catch (e) {\n"
"send('fail');\n"
"}\n"
"db.close();\n");
EXPECT_SEND_MESSAGE_WITH ("\"not exists\"");
EXPECT_SEND_MESSAGE_WITH ("\"not exists again\"");
EXPECT_SEND_MESSAGE_WITH ("\"invalid flags\"");
EXPECT_SEND_MESSAGE_WITH ("\"can write\"");
EXPECT_NO_MESSAGES ();
}
TESTCASE (socket_connection_can_be_established)
{
#ifdef HAVE_ANDROID
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
#endif
PUSH_TIMEOUT (10000);
COMPILE_AND_LOAD_SCRIPT (
"Socket.listen({"
" backlog: 1,"
"})"
".then(function (listener) {"
" listener.accept()"
" .then(function (client) {"
" return client.input.readAll(5)"
" .then(function (data) {"
" send('server read', data);"
" client.close();"
" listener.close();"
" });"
" })"
" .catch(function (error) {"
" send('error: ' + error.message);"
" });"
""
" return Socket.connect({"
" family: 'ipv4',"
" host: 'localhost',"
" port: listener.port,"
" })"
" .then(function (connection) {"
" return connection.setNoDelay(true)"
" .then(function () {"
" return connection.output.writeAll([0x31, 0x33, 0x33, 0x37, 0x0a])"
" .then(function () {"
" return connection.close();"
" });"
" });"
" })"
" .catch(function (error) {"
" send('error: ' + error.message);"
" });"
"})"
".catch(function (error) {"
" send('error: ' + error.message);"
"});");
EXPECT_SEND_MESSAGE_WITH_PAYLOAD_AND_DATA ("\"server read\"",
"31 33 33 37 0a");
#ifdef G_OS_UNIX
{
const gchar * tmp_dir;
#ifdef HAVE_ANDROID
tmp_dir = "/data/local/tmp";
#else
tmp_dir = g_get_tmp_dir ();
#endif
COMPILE_AND_LOAD_SCRIPT (
"var getpid = new NativeFunction("
" Module.getExportByName(null, 'getpid'), 'int', []);"
"var unlink = new NativeFunction("
" Module.getExportByName(null, 'unlink'), 'int', ['pointer']);"
""
"Socket.listen({"
" type: 'path',"
" path: '%s/frida-gum-test-listener-' + getpid(),"
" backlog: 1,"
"})"
".then(function (listener) {"
" listener.accept()"
" .then(function (client) {"
" return client.input.readAll(5)"
" .then(function (data) {"
" send('server read', data);"
" client.close();"
" listener.close();"
" });"
" })"
" .catch(function (error) {"
" send('error: ' + error.message);"
" });"
""
" return Socket.connect({"
" type: 'path',"
" path: listener.path,"
" })"
" .then(function (connection) {"
" unlink(Memory.allocUtf8String(listener.path));"
" return connection.output.writeAll([0x31, 0x33, 0x33, 0x37, 0x0a])"
" .then(function () {"
" return connection.close();"
" });"
" })"
" .catch(function (error) {"
" send('error: ' + error.message);"
" });"
"})"
".catch(function (error) {"
" send('error: ' + error.message);"
"});", tmp_dir);
EXPECT_SEND_MESSAGE_WITH_PAYLOAD_AND_DATA ("\"server read\"",
"31 33 33 37 0a");
}
#endif
}
TESTCASE (socket_connection_can_be_established_with_tls)
{
gboolean done;
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
PUSH_TIMEOUT (10000);
COMPILE_AND_LOAD_SCRIPT (
"Socket.connect({"
" family: 'ipv4',"
" host: 'www.google.com',"
" port: 443,"
" tls: true,"
"})"
".then(function (connection) {"
" return connection.setNoDelay(true)"
" .then(function () {"
" var request = ["
" 'GET / HTTP/1.1',"
" 'Connection: close',"
" 'Host: www.google.com',"
" 'Accept: text/html',"
" 'User-Agent: Frida/" FRIDA_VERSION "',"
" '',"
" '',"
" ].join('\\r\\n');"
" var rawRequest = [];"
" for (var i = 0; i !== request.length; i++)"
" rawRequest.push(request.charCodeAt(i));"
" send('request', rawRequest);"
" return connection.output.writeAll(rawRequest)"
" .then(function () {"
" return connection.input.read(128 * 1024);"
" })"
" .then(function (data) {"
" send('response', data);"
" });"
" });"
"})"
".catch(function (error) {"
" send('error: ' + error.message);"
"});");
g_printerr ("\n\n");
done = FALSE;
while (!done)
{
TestScriptMessageItem * item;
item = test_script_fixture_pop_message (fixture);
if (item->raw_data != NULL)
{
gboolean is_request;
const guint8 * raw_chunk;
gsize size;
gchar * chunk;
is_request = strstr (item->message, "\"request\"") != NULL;
raw_chunk = g_bytes_get_data (item->raw_data, &size);
chunk = g_strndup ((const gchar *) raw_chunk, size);
g_printerr ("*** %s %" G_GSIZE_MODIFIER "u bytes\n%s",
is_request ? "Sent" : "Received",
size,
chunk);
g_free (chunk);
done = !is_request;
}
else
{
g_printerr ("Got: %s\n", item->message);
}
test_script_message_item_free (item);
}
}
TESTCASE (socket_connection_should_not_leak_on_error)
{
if (!g_test_slow ())
{
g_print("<skipping, run in slow mode> ");
return;
}
PUSH_TIMEOUT (5000);
COMPILE_AND_LOAD_SCRIPT (
"var tries = 0;"
"var port = 28300;"
"var firstErrorMessage = null;"
""
"tryNext();"
""
"function tryNext() {"
" tries++;"
" if (tries === 200) {"
" send('done');"
" return;"
" }"
""
" Socket.connect({"
" family: 'ipv4',"
" host: 'localhost',"
" port: port,"
" })"
" .then(function (connection) {"
" console.log('success');"
" tries--;"
" port++;"
" tryNext();"
" })"
" .catch(function (error) {"
" if (firstErrorMessage === null) {"
" firstErrorMessage = error.message;"
" } else if (error.message !== firstErrorMessage) {"
" send('Expected \"' + firstErrorMessage + '\" but got \"' +"
" error.message + '\"');"
" return;"
" }"
" console.log('tries=' + tries + ' error=\"' + error.message + '\"');"
" tryNext();"
" });"
"}");
EXPECT_SEND_MESSAGE_WITH ("\"done\"");
}
TESTCASE (socket_type_can_be_inspected)
{
int fd;
struct sockaddr_in addr = { 0, };
const guint port = 39876;
fd = socket (AF_INET, SOCK_STREAM, 0);
COMPILE_AND_LOAD_SCRIPT ("send(Socket.type(%d));", fd);
EXPECT_SEND_MESSAGE_WITH ("\"tcp\"");
addr.sin_family = AF_INET;
addr.sin_port = GUINT16_TO_BE (port);
addr.sin_addr.s_addr = INADDR_ANY;
bind (fd, (struct sockaddr *) &addr, sizeof (addr));
COMPILE_AND_LOAD_SCRIPT ("send(Socket.type(%d));", fd);
EXPECT_SEND_MESSAGE_WITH ("\"tcp\"");
GUM_CLOSE_SOCKET (fd);
fd = socket (AF_INET, SOCK_DGRAM, 0);
COMPILE_AND_LOAD_SCRIPT ("send(Socket.type(%d));", fd);
EXPECT_SEND_MESSAGE_WITH ("\"udp\"");
GUM_CLOSE_SOCKET (fd);
fd = socket (AF_INET6, SOCK_STREAM, 0);
if (fd != -1)
{
COMPILE_AND_LOAD_SCRIPT ("send(Socket.type(%d));", fd);
EXPECT_SEND_MESSAGE_WITH ("\"tcp6\"");
GUM_CLOSE_SOCKET (fd);
}
fd = socket (AF_INET6, SOCK_DGRAM, 0);
if (fd != -1)
{
COMPILE_AND_LOAD_SCRIPT ("send(Socket.type(%d));", fd);
EXPECT_SEND_MESSAGE_WITH ("\"udp6\"");
GUM_CLOSE_SOCKET (fd);
}
COMPILE_AND_LOAD_SCRIPT ("send(Socket.type(-1));");
EXPECT_SEND_MESSAGE_WITH ("null");
#ifndef HAVE_WINDOWS
fd = socket (AF_UNIX, SOCK_STREAM, 0);
COMPILE_AND_LOAD_SCRIPT ("send(Socket.type(%d));", fd);
EXPECT_SEND_MESSAGE_WITH ("\"unix:stream\"");
close (fd);
fd = socket (AF_UNIX, SOCK_DGRAM, 0);
COMPILE_AND_LOAD_SCRIPT ("send(Socket.type(%d));", fd);
EXPECT_SEND_MESSAGE_WITH ("\"unix:dgram\"");
close (fd);
fd = open ("/etc/hosts", O_RDONLY);
g_assert_cmpint (fd, >=, 0);
COMPILE_AND_LOAD_SCRIPT ("send(Socket.type(%d));", fd);
EXPECT_SEND_MESSAGE_WITH ("null");
close (fd);
#endif
}
TESTCASE (socket_endpoints_can_be_inspected)
{
GSocketFamily family[] = { G_SOCKET_FAMILY_IPV4, G_SOCKET_FAMILY_IPV6 };
guint i;
GMainContext * context;
int fd;
context = g_main_context_get_thread_default ();
for (i = 0; i != G_N_ELEMENTS (family); i++)
{
GSocket * sock;
GSocketService * service;
GInetAddress * loopback;
GSocketAddress * listen_address, * server_address, * client_address;
guint16 server_port, client_port;
sock = g_socket_new (family[i], G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP,
NULL);
if (sock == NULL)
continue;
fd = g_socket_get_fd (sock);
service = g_socket_service_new ();
g_signal_connect (service, "incoming", G_CALLBACK (on_incoming_connection),
NULL);
loopback = g_inet_address_new_loopback (family[i]);
listen_address = g_inet_socket_address_new (loopback, 0);
if (!g_socket_listener_add_address (G_SOCKET_LISTENER (service),
listen_address, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, NULL,
&server_address, NULL))
goto skip_unsupported_family;
server_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (
server_address));
g_socket_service_start (service);
COMPILE_AND_LOAD_SCRIPT ("send(Socket.peerAddress(%d));", fd);
EXPECT_SEND_MESSAGE_WITH ("null");
while (g_main_context_pending (context))
g_main_context_iteration (context, FALSE);
g_assert_true (g_socket_connect (sock, server_address, NULL, NULL));
g_object_get (sock, "local-address", &client_address, NULL);
client_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (
client_address));
while (g_main_context_pending (context))
g_main_context_iteration (context, FALSE);
COMPILE_AND_LOAD_SCRIPT (
"var addr = Socket.localAddress(%d);"
"send([typeof addr.ip, addr.port]);", fd);
EXPECT_SEND_MESSAGE_WITH ("[\"string\",%u]", client_port);
COMPILE_AND_LOAD_SCRIPT (
"var addr = Socket.peerAddress(%d);"
"send([typeof addr.ip, addr.port]);", fd);
EXPECT_SEND_MESSAGE_WITH ("[\"string\",%u]", server_port);
g_socket_close (sock, NULL);
g_socket_service_stop (service);
while (g_main_context_pending (context))
g_main_context_iteration (context, FALSE);
g_object_unref (client_address);
g_object_unref (server_address);
skip_unsupported_family:
g_object_unref (listen_address);
g_object_unref (loopback);
g_object_unref (service);
g_object_unref (sock);
}
#ifdef HAVE_DARWIN
{
struct sockaddr_un address;
socklen_t len;
fd = socket (AF_UNIX, SOCK_STREAM, 0);
address.sun_family = AF_UNIX;
strcpy (address.sun_path, "/tmp/gum-script-test");
unlink (address.sun_path);
address.sun_len = sizeof (address) - sizeof (address.sun_path) +
strlen (address.sun_path) + 1;
len = address.sun_len;
bind (fd, (struct sockaddr *) &address, len);
COMPILE_AND_LOAD_SCRIPT ("send(Socket.localAddress(%d));", fd);
EXPECT_SEND_MESSAGE_WITH ("{\"path\":\"\"}");
close (fd);
unlink (address.sun_path);
}
#endif
}
static gboolean
on_incoming_connection (GSocketService * service,
GSocketConnection * connection,
GObject * source_object,
gpointer user_data)
{
GInputStream * input;
void * buf;
input = g_io_stream_get_input_stream (G_IO_STREAM (connection));
buf = g_malloc (1);
g_input_stream_read_async (input, buf, 1, G_PRIORITY_DEFAULT, NULL,
on_read_ready, g_object_ref (connection));
return TRUE;
}
static void
on_read_ready (GObject * source_object,
GAsyncResult * res,
gpointer user_data)
{
GSocketConnection * connection = user_data;
GError * error = NULL;
g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, &error);
g_clear_error (&error);
g_io_stream_close_async (G_IO_STREAM (connection), G_PRIORITY_LOW, NULL,
NULL, NULL);
g_object_unref (connection);
}
#if defined (HAVE_I386) || defined (HAVE_ARM) || defined (HAVE_ARM64)
#include "stalkerdummychannel.h"
TESTCASE (execution_can_be_traced)
{
GumThreadId test_thread_id;
test_thread_id = gum_process_get_current_thread_id ();
COMPILE_AND_LOAD_SCRIPT (
"Stalker.queueDrainInterval = 0;"
"var testsRange = Process.getModuleByName('%s');"
"Stalker.exclude(testsRange);"
"Stalker.follow(%" G_GSIZE_FORMAT ", {"
" events: {"
" call: true,"
" ret: false,"
" exec: false"
" },"
" onReceive: function (events) {"
" send('onReceive: ' + (events.byteLength > 0));"
" },"
" onCallSummary: function (summary) {"
" send('onCallSummary: ' + (Object.keys(summary).length > 0));"
" }"
"});"
"recv('stop', function (message) {"
" Stalker.unfollow(%" G_GSIZE_FORMAT ");"
" Stalker.flush();"
"});",
GUM_TESTS_MODULE_NAME,
test_thread_id,
test_thread_id);
EXPECT_NO_MESSAGES ();
POST_MESSAGE ("{\"type\":\"stop\"}");
EXPECT_SEND_MESSAGE_WITH ("\"onCallSummary: true\"");
EXPECT_SEND_MESSAGE_WITH ("\"onReceive: true\"");
}
TESTCASE (execution_can_be_traced_with_custom_transformer)
{
GumThreadId test_thread_id;
test_thread_id = gum_process_get_current_thread_id ();
COMPILE_AND_LOAD_SCRIPT (
"var testsRange = Process.getModuleByName('%s');"
"Stalker.exclude(testsRange);"
"var instructionsSeen = 0;"
"Stalker.follow(%" G_GSIZE_FORMAT ", {"
" transform: function (iterator) {"
" var instruction;"
" while ((instruction = iterator.next()) !== null) {"
" if (instructionsSeen === 0) {"
" iterator.putCallout(onBeforeFirstInstruction);"
" }"
" iterator.keep();"
" instructionsSeen++;"
" }"
" }"
"});"
"function onBeforeFirstInstruction (context) {"
" console.log(JSON.stringify(context, null, 2));"
"}"
"recv('stop', function (message) {"
" Stalker.unfollow(%" G_GSIZE_FORMAT ");"
" send(instructionsSeen > 0);"
"});",
GUM_TESTS_MODULE_NAME,
test_thread_id,
test_thread_id);
g_usleep (1);
EXPECT_NO_MESSAGES ();
POST_MESSAGE ("{\"type\":\"stop\"}");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_NO_MESSAGES ();
}
TESTCASE (execution_can_be_traced_with_faulty_transformer)
{
GumThreadId test_thread_id;
test_thread_id = gum_process_get_current_thread_id ();
COMPILE_AND_LOAD_SCRIPT (
"var testsRange = Process.getModuleByName('%s');"
"Stalker.exclude(testsRange);"
"Stalker.follow(%" G_GSIZE_FORMAT ", {"
" transform: function (iterator) {"
" throw new Error('oh no I am buggy');"
" }"
"});",
GUM_TESTS_MODULE_NAME,
test_thread_id);
g_usleep (1);
EXPECT_ERROR_MESSAGE_WITH (ANY_LINE_NUMBER, "Error: oh no I am buggy");
EXPECT_NO_MESSAGES ();
g_assert (
!gum_stalker_is_following_me (gum_script_get_stalker (fixture->script)));
}
TESTCASE (execution_can_be_traced_during_immediate_native_function_call)
{
COMPILE_AND_LOAD_SCRIPT (
"Stalker.queueDrainInterval = 0;"
"var testsRange = Process.getModuleByName('%s');"
"Stalker.exclude(testsRange);"
"var a = new NativeFunction(" GUM_PTR_CONST ", 'int', ['int'], "
"{ traps: 'all', exceptions: 'propagate' });"
"Stalker.follow({"
" events: {"
" call: true,"
" },"
" onCallSummary: function (summary) {"
" var key = a.strip().toString();"
" send(key in summary);"
" send(summary[key]);"
" }"
"});"
"a(42);"
"a(42);"
"Stalker.unfollow();"
"Stalker.flush();",
GUM_TESTS_MODULE_NAME,
target_function_nested_a);
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_NO_MESSAGES ();
}
TESTCASE (execution_can_be_traced_during_scheduled_native_function_call)
{
COMPILE_AND_LOAD_SCRIPT (
"Stalker.queueDrainInterval = 0;"
"var testsRange = Process.getModuleByName('%s');"
"Stalker.exclude(testsRange);"
"var a = new NativeFunction(" GUM_PTR_CONST ", 'int', ['int'], "
"{ traps: 'all' });"
"Stalker.follow({"
" events: {"
" call: true,"
" },"
" onCallSummary: function (summary) {"
" var key = a.strip().toString();"
" send(key in summary);"
" send(summary[key]);"
" }"
"});"
"setImmediate(function () {"
"a(42);"
"a(42);"
"Stalker.unfollow();"
"Stalker.flush();"
"});",
GUM_TESTS_MODULE_NAME,
target_function_nested_a);
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_NO_MESSAGES ();
}
TESTCASE (execution_can_be_traced_after_native_function_call_from_hook)
{
StalkerDummyChannel channel;
GThread * thread;
GumThreadId thread_id;
sdc_init (&channel);
thread = g_thread_new ("stalker-test-target",
run_stalked_through_hooked_function, &channel);
thread_id = sdc_await_thread_id (&channel);
COMPILE_AND_LOAD_SCRIPT (
"Stalker.queueDrainInterval = 0;"
"var testsRange = Process.getModuleByName('%s');"
"Stalker.exclude(testsRange);"
"var targetThreadId = %" G_GSIZE_FORMAT ";"
"var targetFuncInt = " GUM_PTR_CONST ";"
"var targetFuncNestedA = new NativeFunction(" GUM_PTR_CONST ", 'int', "
"['int'], { traps: 'all' });"
"Interceptor.attach(targetFuncInt, function () {"
" targetFuncNestedA(1337);"
"});"
"Stalker.follow(targetThreadId, {"
" events: {"
" call: true,"
" },"
" onCallSummary: function (summary) {"
" [targetFuncInt, targetFuncNestedA].forEach(function (target) {"
" var key = target.strip().toString();"
" send(key in summary);"
" send(summary[key]);"
" });"
" }"
"});"
"recv('stop', function (message) {"
" Stalker.unfollow(targetThreadId);"
" Stalker.flush();"
"});"
"send('ready');",
GUM_TESTS_MODULE_NAME,
thread_id,
target_function_int,
target_function_nested_a);
EXPECT_SEND_MESSAGE_WITH ("\"ready\"");
EXPECT_NO_MESSAGES ();
sdc_put_follow_confirmation (&channel);
sdc_await_run_confirmation (&channel);
POST_MESSAGE ("{\"type\":\"stop\"}");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("1");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("2");
EXPECT_NO_MESSAGES ();
sdc_put_finish_confirmation (&channel);
g_thread_join (thread);
sdc_finalize (&channel);
}
static gpointer
run_stalked_through_hooked_function (gpointer data)
{
StalkerDummyChannel * channel = data;
sdc_put_thread_id (channel, gum_process_get_current_thread_id ());
sdc_await_follow_confirmation (channel);
target_function_int (42);
target_function_nested_a (1338);
sdc_put_run_confirmation (channel);
sdc_await_finish_confirmation (channel);
return NULL;
}
TESTCASE (call_can_be_probed)
{
StalkerDummyChannel channel;
GThread * thread;
GumThreadId thread_id;
sdc_init (&channel);
thread = g_thread_new ("stalker-test-target",
run_stalked_through_target_function, &channel);
thread_id = sdc_await_thread_id (&channel);
COMPILE_AND_LOAD_SCRIPT (
"var targetThreadId = %" G_GSIZE_FORMAT ";"
"Stalker.addCallProbe(" GUM_PTR_CONST ", function (args) {"
" send(args[0].toInt32());"
"});"
"Stalker.follow(targetThreadId);"
"recv('stop', function (message) {"
" Stalker.unfollow(targetThreadId);"
"});"
"send('ready');",
thread_id,
target_function_int);
EXPECT_SEND_MESSAGE_WITH ("\"ready\"");
EXPECT_NO_MESSAGES ();
sdc_put_follow_confirmation (&channel);
EXPECT_SEND_MESSAGE_WITH ("1337");
POST_MESSAGE ("{\"type\":\"stop\"}");
g_thread_join (thread);
sdc_finalize (&channel);
}
static gpointer
run_stalked_through_target_function (gpointer data)
{
StalkerDummyChannel * channel = data;
sdc_put_thread_id (channel, gum_process_get_current_thread_id ());
sdc_await_follow_confirmation (channel);
target_function_int (1337);
return NULL;
}
#endif
TESTCASE (stalker_events_can_be_parsed)
{
GumEvent ev;
ev.type = GUM_CALL;
ev.call.location = GSIZE_TO_POINTER (7);
ev.call.target = GSIZE_TO_POINTER (12);
ev.call.depth = 42;
COMPILE_AND_LOAD_SCRIPT ("send(Stalker.parse(" GUM_PTR_CONST ".readByteArray("
"%" G_GSIZE_FORMAT ")));", &ev, sizeof (ev));
EXPECT_SEND_MESSAGE_WITH ("[[\"call\",\"0x7\",\"0xc\",42]]");
COMPILE_AND_LOAD_SCRIPT ("send(Stalker.parse(new ArrayBuffer(0)));");
EXPECT_SEND_MESSAGE_WITH ("[]");
COMPILE_AND_LOAD_SCRIPT ("send(Stalker.parse(new ArrayBuffer(1)));");
EXPECT_ERROR_MESSAGE_WITH (ANY_LINE_NUMBER, "Error: invalid buffer shape");
COMPILE_AND_LOAD_SCRIPT ("send(Stalker.parse(new ArrayBuffer(%" G_GSIZE_FORMAT
")));", sizeof (GumEvent));
EXPECT_ERROR_MESSAGE_WITH (ANY_LINE_NUMBER, "Error: invalid event type");
}
TESTCASE (frida_version_is_available)
{
COMPILE_AND_LOAD_SCRIPT ("send(typeof Frida.version);");
EXPECT_SEND_MESSAGE_WITH ("\"string\"");
}
TESTCASE (frida_heap_size_can_be_queried)
{
COMPILE_AND_LOAD_SCRIPT ("send(typeof Frida.heapSize);");
EXPECT_SEND_MESSAGE_WITH ("\"number\"");
}
TESTCASE (process_arch_is_available)
{
COMPILE_AND_LOAD_SCRIPT ("send(Process.arch);");
#if defined (HAVE_I386)
# if GLIB_SIZEOF_VOID_P == 4
EXPECT_SEND_MESSAGE_WITH ("\"ia32\"");
# else
EXPECT_SEND_MESSAGE_WITH ("\"x64\"");
# endif
#elif defined (HAVE_ARM)
EXPECT_SEND_MESSAGE_WITH ("\"arm\"");
#elif defined (HAVE_ARM64)
EXPECT_SEND_MESSAGE_WITH ("\"arm64\"");
#endif
}
TESTCASE (process_platform_is_available)
{
COMPILE_AND_LOAD_SCRIPT ("send(Process.platform);");
#if defined (HAVE_LINUX)
EXPECT_SEND_MESSAGE_WITH ("\"linux\"");
#elif defined (HAVE_DARWIN)
EXPECT_SEND_MESSAGE_WITH ("\"darwin\"");
#elif defined (HAVE_WINDOWS)
EXPECT_SEND_MESSAGE_WITH ("\"windows\"");
#endif
}
TESTCASE (process_page_size_is_available)
{
COMPILE_AND_LOAD_SCRIPT ("send(Process.pageSize);");
EXPECT_SEND_MESSAGE_WITH ("%d", gum_query_page_size ());
}
TESTCASE (process_pointer_size_is_available)
{
COMPILE_AND_LOAD_SCRIPT ("send(Process.pointerSize);");
EXPECT_SEND_MESSAGE_WITH (G_STRINGIFY (GLIB_SIZEOF_VOID_P));
}
TESTCASE (process_should_support_nested_signal_handling)
{
#ifdef HAVE_LINUX
gpointer page;
page = gum_alloc_n_pages (1, GUM_PAGE_NO_ACCESS);
COMPILE_AND_LOAD_SCRIPT ("Process.setExceptionHandler(function (details) {"
"Memory.protect(" GUM_PTR_CONST ", Process.pageSize, 'rw-');"
"try {"
"ptr(42).readU8();"
"} catch (e) {"
"send('error');"
"};"
"return true;"
"});", page);
*((guint8 *) page) = 1;
EXPECT_SEND_MESSAGE_WITH ("\"error\"");
gum_free_pages ((gpointer) page);
#else
g_print ("<skipping, only supported on Linux for now> ");
#endif
}
TESTCASE (process_debugger_status_is_available)
{
COMPILE_AND_LOAD_SCRIPT ("send(Process.isDebuggerAttached());");
if (gum_process_is_debugger_attached ())
EXPECT_SEND_MESSAGE_WITH ("true");
else
EXPECT_SEND_MESSAGE_WITH ("false");
}
TESTCASE (process_id_is_available)
{
TestScriptMessageItem * item;
gint pid;
COMPILE_AND_LOAD_SCRIPT ("send(Process.id);");
item = test_script_fixture_pop_message (fixture);
pid = 0;
sscanf (item->message, "{\"type\":\"send\",\"payload\":%d}", &pid);
g_assert_cmpint (pid, ==, gum_process_get_id ());
test_script_message_item_free (item);
}
TESTCASE (process_current_thread_id_is_available)
{
COMPILE_AND_LOAD_SCRIPT ("send(typeof Process.getCurrentThreadId());");
EXPECT_SEND_MESSAGE_WITH ("\"number\"");
}
TESTCASE (process_threads_can_be_enumerated)
{
#ifdef HAVE_LINUX
if (!check_exception_handling_testable ())
return;
#endif
#ifdef HAVE_MIPS
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
#endif
COMPILE_AND_LOAD_SCRIPT (
"var threads = Process.enumerateThreads();"
"send(threads.length > 0);");
EXPECT_SEND_MESSAGE_WITH ("true");
}
TESTCASE (process_threads_can_be_enumerated_legacy_style)
{
gboolean done = FALSE;
GThread * thread_a, * thread_b;
#ifdef HAVE_LINUX
if (!check_exception_handling_testable ())
return;
#endif
#if defined (HAVE_ANDROID) || defined (HAVE_MIPS)
if (!g_test_slow ())
{
g_print ("<skipping, run in slow mode> ");
return;
}
#endif
COMPILE_AND_LOAD_SCRIPT (
"Process.enumerateThreads({"
"onMatch: function (thread) {"
" send('onMatch');"
" return 'stop';"
"},"
"onComplete: function () {"
" send('onComplete');"
"}"
"});");
EXPECT_SEND_MESSAGE_WITH ("\"onMatch\"");
EXPECT_SEND_MESSAGE_WITH ("\"onComplete\"");
thread_a = g_thread_new ("script-test-sleeping-dummy-a", sleeping_dummy,
&done);
thread_b = g_thread_new ("script-test-sleeping-dummy-b", sleeping_dummy,
&done);
COMPILE_AND_LOAD_SCRIPT ("send(Process.enumerateThreadsSync().length >= 2);");
EXPECT_SEND_MESSAGE_WITH ("true");
done = TRUE;
g_thread_join (thread_b);
g_thread_join (thread_a);
}
static gpointer
sleeping_dummy (gpointer data)
{
volatile gboolean * done = (gboolean *) data;
while (!(*done))
g_thread_yield ();
return NULL;
}
TESTCASE (process_modules_can_be_enumerated)
{
COMPILE_AND_LOAD_SCRIPT (
"var modules = Process.enumerateModules();"
"send(modules.length > 0);"
"var m = modules[0];"
"send(typeof m.name === 'string');"
"send(typeof m.path === 'string');"
"send(m.base instanceof NativePointer);"
"send(typeof m.size === 'number');"
"send(JSON.stringify(m) !== \"{}\");");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
}
TESTCASE (process_modules_can_be_enumerated_legacy_style)
{
COMPILE_AND_LOAD_SCRIPT (
"Process.enumerateModules({"
"onMatch: function (module) {"
" send('onMatch');"
" return 'stop';"
"},"
"onComplete: function () {"
" send('onComplete');"
"}"
"});");
EXPECT_SEND_MESSAGE_WITH ("\"onMatch\"");
EXPECT_SEND_MESSAGE_WITH ("\"onComplete\"");
COMPILE_AND_LOAD_SCRIPT ("send(Process.enumerateModulesSync().length > 1);");
EXPECT_SEND_MESSAGE_WITH ("true");
}
TESTCASE (process_module_can_be_looked_up_from_address)
{
#ifndef HAVE_LINUX
GModule * m;
gpointer f;
gboolean found;
m = g_module_open (SYSTEM_MODULE_NAME, G_MODULE_BIND_LAZY);
found = g_module_symbol (m, SYSTEM_MODULE_EXPORT, &f);
g_assert_true (found);
g_module_close (m);
COMPILE_AND_LOAD_SCRIPT (
"send(Process.findModuleByAddress(" GUM_PTR_CONST ".strip()) !== null);",
f);
EXPECT_SEND_MESSAGE_WITH ("true");
COMPILE_AND_LOAD_SCRIPT (
"send(Object.keys(Process.getModuleByAddress(" GUM_PTR_CONST
".strip())).length > 0);",
f);
EXPECT_SEND_MESSAGE_WITH ("true");
#endif
COMPILE_AND_LOAD_SCRIPT (
"var someModule = Process.enumerateModules()[1];"
"var foundModule = Process.findModuleByAddress(someModule.base);"
"send(foundModule !== null);"
"send(foundModule.name === someModule.name);");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_NO_MESSAGES ();
COMPILE_AND_LOAD_SCRIPT (
"var map = new ModuleMap();"
"var someModule = Process.enumerateModules()[1];"
"send(map.has(someModule.base));"
"send(map.has(ptr(1)));"
"var foundModule = map.find(someModule.base);"
"send(foundModule !== null);"
"send(foundModule.name === someModule.name);"
"send(map.find(ptr(1)));"
"map.update();"
"foundModule = map.get(someModule.base);"
"send(foundModule.name === someModule.name);"
"try {"
" map.get(ptr(1));"
"} catch (e) {"
" send(e.message);"
"}"
"send(map.findName(someModule.base) === someModule.name);"
"send(map.findName(ptr(1)));"
"send(map.getName(someModule.base) === someModule.name);"
"try {"
" map.getName(ptr(1));"
"} catch (e) {"
" send(e.message);"
"}"
"send(map.findPath(someModule.base) === someModule.path);"
"send(map.findPath(ptr(1)));"
"send(map.getPath(someModule.base) === someModule.path);"
"try {"
" map.getPath(ptr(1));"
"} catch (e) {"
" send(e.message);"
"}");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("false");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("null");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("\"unable to find module containing 0x1\"");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("null");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("\"unable to find module containing 0x1\"");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("null");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_SEND_MESSAGE_WITH ("\"unable to find module containing 0x1\"");
EXPECT_NO_MESSAGES ();
#ifdef HAVE_DARWIN
COMPILE_AND_LOAD_SCRIPT (
"var systemModule = Process.enumerateModules()"
" .filter(function (m) {"
" return m.path.indexOf('/System/') === 0;"
" })[0];"
"var map = new ModuleMap(function (module) {"
" return module.path.indexOf('/System/') === -1;"
"});"
"var foundModule = map.find(systemModule.base);"
"send(foundModule === null);");
EXPECT_SEND_MESSAGE_WITH ("true");
EXPECT_NO_MESSAGES ();
#endif
}
TESTCASE (process_module_can_be_looked_up_from_name)
{
COMPILE_AND_LOAD_SCRIPT (
"send(Process.findModuleByName('%s') !== null);",
SYSTEM_MODULE_NAME);
EXPECT_SEND_MESSAGE_WITH ("true");
COMPILE_AND_LOAD_SCRIPT (
"send(Object.keys(Process.getModuleByName('%s')).length > 0);",
SYSTEM_MODULE_NAME);
EXPECT_SEND_MESSAGE_WITH ("true");
}
TESTCASE (process_ranges_can_be_enumerated)
{
COMPILE_AND_LOAD_SCRIPT (
"var ranges = Process.enumerateRanges('--x');"
"send(ranges.length > 0);");
EXPECT_SEND_MESSAGE_WITH ("true");
}
TESTCASE (process_ranges_can_be_enumerated_legacy_style)
{
COMPILE_AND_LOAD_SCRIPT (
"Process.enumerateRanges('--x', {"
"onMatch: function (range) {"
" send('onMatch');"
" return 'stop';"
"},"
"onComplete: function () {"
" send('onComplete');"
"}"
"});");
EXPECT_SEND_MESSAGE_WITH ("\"onMatch\"");
EXPECT_SEND_MESSAGE_WITH ("\"onComplete\"");
COMPILE_AND_LOAD_SCRIPT (
"send(Process.enumerateRangesSync('--x').length > 1);");
EXPECT_SEND_MESSAGE_WITH ("true");
}
TESTCASE (process_ranges_can_be_enumerated_with_neighbors_coalesced)
{
COMPILE_AND_LOAD_SCRIPT (
"var a = Process.enumerateRanges('--x');"
"var b = Process.enumerateRanges({"
"protection: '--x',"
"coalesce: true"
"});"
"send(b.length <= a.length);");
EXPECT_SEND_MESSAGE_WITH ("true");
}
TESTCASE (process_range_can_be_looked_up_from_address)
{
GModule * m;
gpointer f;
gboolean found;
m = g_module_open (SYSTEM_MODULE_NAME, G_MODULE_BIND_LAZY);
found = g_module_symbol (m, SYSTEM_MODULE_EXPORT, &f);
g_assert_true (found);
g_module_close (m);
COMPILE_AND_LOAD_SCRIPT (
"send(Process.findRangeByAddress(" GUM_PTR_CONST ".strip()) !== null);",