Permalink
Cannot retrieve contributors at this time
7970 lines (6891 sloc)
225 KB
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?
frida-gum/tests/gumjs/script.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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);", | |