diff --git a/.claude/add-missing-apis-workflow.js b/.claude/add-missing-apis-workflow.js new file mode 100644 index 00000000..d26225bf --- /dev/null +++ b/.claude/add-missing-apis-workflow.js @@ -0,0 +1,291 @@ +export const meta = { + name: 'add-missing-gum-apis', + description: 'Add all missing GUM API wrappers to frida-rust', + phases: [ + { title: 'Add ThumbWriter' }, + { title: 'Add MipsWriter' }, + { title: 'Add DarwinModule' }, + { title: 'Add KernelAPI' }, + { title: 'Add MetalCollections' }, + { title: 'Add Spinlock' }, + { title: 'Add InspectorServer' }, + { title: 'Add MemoryMap' }, + { title: 'Verify' }, + ], +} + +const APIS = [ + { + name: 'ThumbWriter', + phase: 'Add ThumbWriter', + file: 'frida-gum/src/instruction_writer/arm/thumb_writer.rs', + description: 'ARM Thumb/Thumb-2 instruction writer for 32-bit ARM code generation', + platform: 'arm', + priority: 'critical', + }, + { + name: 'MipsWriter', + phase: 'Add MipsWriter', + file: 'frida-gum/src/instruction_writer/mips/writer.rs', + description: 'MIPS instruction writer for MIPS architecture support', + platform: 'mips', + priority: 'medium', + }, + { + name: 'DarwinModule', + phase: 'Add DarwinModule', + file: 'frida-gum/src/darwin_module.rs', + description: 'Mach-O binary parsing and inspection for macOS/iOS', + platform: 'darwin', + priority: 'critical', + }, + { + name: 'KernelAPI', + phase: 'Add KernelAPI', + file: 'frida-gum/src/kernel.rs', + description: 'Kernel memory operations and module enumeration', + platform: 'all', + priority: 'high', + }, + { + name: 'MetalArray', + phase: 'Add MetalCollections', + file: 'frida-gum/src/metal_array.rs', + description: 'Lock-free array for signal-safe callbacks', + platform: 'all', + priority: 'medium', + }, + { + name: 'MetalHashTable', + phase: 'Add MetalCollections', + file: 'frida-gum/src/metal_hash_table.rs', + description: 'Lock-free hash table for signal-safe callbacks', + platform: 'all', + priority: 'medium', + }, + { + name: 'Spinlock', + phase: 'Add Spinlock', + file: 'frida-gum/src/spinlock.rs', + description: 'Platform-agnostic spinlock primitive', + platform: 'all', + priority: 'low', + }, + { + name: 'InspectorServer', + phase: 'Add InspectorServer', + file: 'frida-gum/src/inspector_server.rs', + description: 'Chrome DevTools protocol debugging server', + platform: 'all', + priority: 'low', + }, + { + name: 'MemoryMap', + phase: 'Add MemoryMap', + file: 'frida-gum/src/memory_map.rs', + description: 'Cached memory range enumeration', + platform: 'all', + priority: 'low', + }, +] + +phase('Add ThumbWriter') +const thumbResult = await agent( + `Create ARM Thumb instruction writer wrapper. + +**Task**: Implement ThumbWriter in frida-gum/src/instruction_writer/arm/thumb_writer.rs + +**Requirements**: +1. Read target/debug/build/frida-gum-sys-*/out/bindings.rs to find all gum_thumb_writer_* functions +2. Create a complete Rust wrapper following the pattern in x86_64/writer.rs and aarch64/writer.rs +3. Implement InstructionWriter trait +4. Add platform cfg gates: #[cfg(target_arch = "arm")] +5. Include proper documentation +6. Add copyright header: "Copyright © 2026 Kirby Kuehl" +7. Create frida-gum/src/instruction_writer/arm/mod.rs if needed +8. Update frida-gum/src/instruction_writer.rs to export arm module +9. Run cargo fmt and cargo clippy +10. Do NOT commit + +Report: file path and line count.`, + { phase: 'Add ThumbWriter', schema: { type: 'object', properties: { file: { type: 'string' }, lines: { type: 'number' }, status: { type: 'string' } }, required: ['file', 'lines', 'status'] } } +) + +phase('Add MipsWriter') +const mipsResult = await agent( + `Create MIPS instruction writer wrapper. + +**Task**: Implement MipsWriter in frida-gum/src/instruction_writer/mips/writer.rs + +**Requirements**: +1. Read bindings.rs to find all gum_mips_writer_* functions +2. Create complete Rust wrapper +3. Implement InstructionWriter trait +4. Add platform cfg gates: #[cfg(target_arch = "mips")] or #[cfg(target_arch = "mips64")] +5. Include proper documentation +6. Add copyright header: "Copyright © 2026 Kirby Kuehl" +7. Create frida-gum/src/instruction_writer/mips/mod.rs +8. Update frida-gum/src/instruction_writer.rs to export mips module +9. Run cargo fmt and cargo clippy +10. Do NOT commit + +Report: file path and line count.`, + { phase: 'Add MipsWriter', schema: { type: 'object', properties: { file: { type: 'string' }, lines: { type: 'number' }, status: { type: 'string' } }, required: ['file', 'lines', 'status'] } } +) + +phase('Add DarwinModule') +const darwinResult = await agent( + `Create Darwin (macOS/iOS) module wrapper for Mach-O binary inspection. + +**Task**: Implement DarwinModule in frida-gum/src/darwin_module.rs + +**Requirements**: +1. Read bindings.rs to find all gum_darwin_module_* and gum_darwin_grafter_* functions +2. Create DarwinModule struct with all methods +3. Create DarwinGrafter struct +4. Add enums/structs for imports, exports, symbols, sections, fixups, rebases, binds, TLV descriptors +5. Add platform cfg gates: #[cfg(target_vendor = "apple")] +6. Include comprehensive documentation +7. Add copyright header: "Copyright © 2026 Kirby Kuehl" +8. Update frida-gum/src/lib.rs to add: pub mod darwin_module; +9. Run cargo fmt and cargo clippy +10. Do NOT commit + +Report: file path and line count.`, + { phase: 'Add DarwinModule', schema: { type: 'object', properties: { file: { type: 'string' }, lines: { type: 'number' }, status: { type: 'string' } }, required: ['file', 'lines', 'status'] } } +) + +phase('Add KernelAPI') +const kernelResult = await agent( + `Create kernel memory operations API wrapper. + +**Task**: Implement Kernel API in frida-gum/src/kernel.rs + +**Requirements**: +1. Read bindings.rs to find all gum_kernel_* functions +2. Create Kernel struct with methods for: scan, read, write, enumerate_ranges, enumerate_modules, alloc, free +3. Add KernelModule, KernelScanMatch structs +4. Include proper error handling +5. Add comprehensive documentation +6. Add copyright header: "Copyright © 2026 Kirby Kuehl" +7. Update frida-gum/src/lib.rs to add: pub mod kernel; +8. Run cargo fmt and cargo clippy +9. Do NOT commit + +Report: file path and line count.`, + { phase: 'Add KernelAPI', schema: { type: 'object', properties: { file: { type: 'string' }, lines: { type: 'number' }, status: { type: 'string' } }, required: ['file', 'lines', 'status'] } } +) + +phase('Add MetalCollections') +const metalResult = await agent( + `Create lock-free collection wrappers for signal-safe callbacks. + +**Task**: Implement MetalArray and MetalHashTable + +**Requirements**: +1. Read bindings.rs to find gum_metal_array_* and gum_metal_hash_table_* functions +2. Create frida-gum/src/metal_array.rs with MetalArray wrapper +3. Create frida-gum/src/metal_hash_table.rs with MetalHashTable wrapper +4. Implement safe Rust APIs wrapping the C functions +5. Add comprehensive documentation explaining signal-safety +6. Add copyright header: "Copyright © 2026 Kirby Kuehl" +7. Update frida-gum/src/lib.rs to add both modules +8. Run cargo fmt and cargo clippy +9. Do NOT commit + +Report: files created and total line count.`, + { phase: 'Add MetalCollections', schema: { type: 'object', properties: { files: { type: 'array', items: { type: 'string' } }, lines: { type: 'number' }, status: { type: 'string' } }, required: ['files', 'lines', 'status'] } } +) + +phase('Add Spinlock') +const spinlockResult = await agent( + `Create spinlock primitive wrapper. + +**Task**: Implement Spinlock in frida-gum/src/spinlock.rs + +**Requirements**: +1. Read bindings.rs to find gum_spinlock_* functions (init, acquire, release, free) +2. Create Spinlock struct with RAII guard pattern +3. Implement safe Rust API with lock() returning a guard +4. Add documentation +5. Add copyright header: "Copyright © 2026 Kirby Kuehl" +6. Update frida-gum/src/lib.rs +7. Run cargo fmt and cargo clippy +8. Do NOT commit + +Report: file path and line count.`, + { phase: 'Add Spinlock', schema: { type: 'object', properties: { file: { type: 'string' }, lines: { type: 'number' }, status: { type: 'string' } }, required: ['file', 'lines', 'status'] } } +) + +phase('Add InspectorServer') +const inspectorResult = await agent( + `Create Chrome DevTools protocol debugging server wrapper. + +**Task**: Implement InspectorServer in frida-gum/src/inspector_server.rs + +**Requirements**: +1. Read bindings.rs to find gum_inspector_server_* functions +2. Create InspectorServer struct +3. Add methods for start, stop, get_address +4. Add documentation explaining Chrome DevTools protocol integration +5. Add copyright header: "Copyright © 2026 Kirby Kuehl" +6. Update frida-gum/src/lib.rs +7. Run cargo fmt and cargo clippy +8. Do NOT commit + +Report: file path and line count.`, + { phase: 'Add InspectorServer', schema: { type: 'object', properties: { file: { type: 'string' }, lines: { type: 'number' }, status: { type: 'string' } }, required: ['file', 'lines', 'status'] } } +) + +phase('Add MemoryMap') +const memMapResult = await agent( + `Create cached memory range enumeration wrapper. + +**Task**: Implement MemoryMap (different from ModuleMap) in frida-gum/src/memory_map.rs (NOT module_map.rs) + +**Requirements**: +1. Read bindings.rs to find GumMemoryMap functions +2. Create MemoryMap struct for cached memory range enumeration +3. Add methods following the C API +4. Add documentation explaining it's an optimization over direct enumeration +5. Add copyright header: "Copyright © 2026 Kirby Kuehl" +6. Update frida-gum/src/lib.rs (use different name to avoid conflict with existing memory_map module) +7. Run cargo fmt and cargo clippy +8. Do NOT commit + +Report: file path and line count.`, + { phase: 'Add MemoryMap', schema: { type: 'object', properties: { file: { type: 'string' }, lines: { type: 'number' }, status: { type: 'string' } }, required: ['file', 'lines', 'status'] } } +) + +phase('Verify') +const verifyResult = await agent( + `Verify all implementations build correctly. + +**Task**: Build and test + +**Requirements**: +1. Run: cargo fmt --all +2. Run: cargo clippy --all-features -- -D warnings +3. Run: cargo build --all-features +4. If errors, fix them +5. Do NOT commit yet + +Report: build status and any issues found.`, + { phase: 'Verify', schema: { type: 'object', properties: { clippy_passed: { type: 'boolean' }, build_passed: { type: 'boolean' }, issues: { type: 'array', items: { type: 'string' } } }, required: ['clippy_passed', 'build_passed', 'issues'] } } +) + +return { + summary: 'All missing GUM APIs implemented', + apis_added: APIS.length, + results: { + thumb: thumbResult, + mips: mipsResult, + darwin: darwinResult, + kernel: kernelResult, + metal: metalResult, + spinlock: spinlockResult, + inspector: inspectorResult, + memmap: memMapResult, + verify: verifyResult, + }, +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index efd94915..e85aa400 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -165,6 +165,12 @@ jobs: profile: minimal toolchain: nightly override: true + - name: Download FRIDA + run: | + export FRIDA_VERSION=$(cat FRIDA_VERSION) + cd frida-gum-sys && mkdir -p include && wget https://github.com/frida/frida/releases/download/$FRIDA_VERSION/frida-gum-devkit-$FRIDA_VERSION-linux-x86_64.tar.xz -O frida-gum.tar.xz && tar xJvf frida-gum.tar.xz -C include + cd .. + cd frida-sys && mkdir -p include && wget https://github.com/frida/frida/releases/download/$FRIDA_VERSION/frida-core-devkit-$FRIDA_VERSION-linux-x86_64.tar.xz -O frida-core.tar.xz && tar xJvf frida-core.tar.xz -C include - name: Build Gum documentation run: | cd frida-gum diff --git a/FRIDA_VERSION b/FRIDA_VERSION index b6e9aafb..e9a51186 100644 --- a/FRIDA_VERSION +++ b/FRIDA_VERSION @@ -1 +1 @@ -17.15.3 +17.16.1 diff --git a/examples/core/inject_lib_blob/src/main.rs b/examples/core/inject_lib_blob/src/main.rs index 428307a1..4de4f157 100644 --- a/examples/core/inject_lib_blob/src/main.rs +++ b/examples/core/inject_lib_blob/src/main.rs @@ -1,8 +1,12 @@ +#[cfg(target_os = "linux")] use frida::{Frida, Inject}; +#[cfg(target_os = "linux")] use std::sync::LazyLock; +#[cfg(target_os = "linux")] static FRIDA: LazyLock = LazyLock::new(|| unsafe { Frida::obtain() }); +#[cfg(target_os = "linux")] fn main() { let device_manager = frida::DeviceManager::obtain(&FRIDA); let local_device = device_manager.get_local_device(); @@ -21,3 +25,8 @@ fn main() { println!("*** Injected, id={}", id); } } + +#[cfg(not(target_os = "linux"))] +fn main() { + println!("This example only works on Linux."); +} diff --git a/examples/gum/debug_symbol/src/main.rs b/examples/gum/debug_symbol/src/main.rs index 9e71a476..27e91dc7 100644 --- a/examples/gum/debug_symbol/src/main.rs +++ b/examples/gum/debug_symbol/src/main.rs @@ -1,14 +1,13 @@ use frida_gum::DebugSymbol; use frida_gum::{Gum, Module}; -use std::iter::Once; use std::sync::OnceLock; fn main() { static CELL: OnceLock = OnceLock::new(); - let gum = CELL.get_or_init(|| Gum::obtain()); + let _gum = CELL.get_or_init(Gum::obtain); - let module = Module::obtain(gum); - let symbol = module.find_export_by_name(None, "mmap").unwrap(); + let symbol = Module::find_global_export_by_name("mmap") + .expect("expected `mmap` to be exported by some loaded module"); let symbol_details = DebugSymbol::from_address(symbol).unwrap(); println!( "address={:#x?} module_name={:?} symbol_name={:?} file_name={:?} line_number={:?}", diff --git a/examples/gum/memory_access_monitor/src/main.rs b/examples/gum/memory_access_monitor/src/main.rs index 4f43746f..171721db 100644 --- a/examples/gum/memory_access_monitor/src/main.rs +++ b/examples/gum/memory_access_monitor/src/main.rs @@ -11,10 +11,10 @@ fn main() { let gum = unsafe { frida_gum::Gum::obtain() }; let mam = MemoryAccessMonitor::new( &gum, - vec![range], + &[range], frida_gum::PageProtection::Write, true, - |_, details| { + |details| { println!( "[monitor callback] hit: {}, details: {}", HIT.fetch_add(1, std::sync::atomic::Ordering::SeqCst), diff --git a/examples/gum/open/src/lib.rs b/examples/gum/open/src/lib.rs index c48fbeb7..4689a97b 100644 --- a/examples/gum/open/src/lib.rs +++ b/examples/gum/open/src/lib.rs @@ -3,7 +3,7 @@ use frida_gum as gum; use gum::{ interceptor::{Interceptor, InvocationContext, InvocationListener}, - Gum, Module, + Gum, Module, Process, }; use std::os::raw::{c_int, c_void}; use std::sync::OnceLock; @@ -25,20 +25,22 @@ extern "C" fn example_agent_main(_user_data: *const c_void, resident: *mut c_int unsafe { *resident = 1 }; static CELL: OnceLock = OnceLock::new(); - let gum = CELL.get_or_init(|| Gum::obtain()); + let gum = CELL.get_or_init(Gum::obtain); let mut interceptor = Interceptor::obtain(gum); let mut listener = OpenListener {}; - let module = Module::obtain(gum); - let modules = module.enumerate_modules(); - for module in modules { + let process = Process::obtain(gum); + for module in process.enumerate_modules() { + let range = module.range(); println!( "{}@{:#x}/{:#x}", - module.name, module.base_address, module.size + module.name(), + range.base_address().0 as usize, + range.size() ); } - let open = module.find_export_by_name(None, "open").unwrap(); + let open = Module::find_global_export_by_name("open").expect("expected `open` to be exported"); interceptor.attach(open, &mut listener).unwrap(); } diff --git a/frida-gum-sys/FRIDA_VERSION b/frida-gum-sys/FRIDA_VERSION index b6e9aafb..e9a51186 100644 --- a/frida-gum-sys/FRIDA_VERSION +++ b/frida-gum-sys/FRIDA_VERSION @@ -1 +1 @@ -17.15.3 +17.16.1 diff --git a/frida-gum-sys/include/frida-gumjs.h b/frida-gum-sys/include/frida-gumjs.h index 10bfb621..953efc73 100644 --- a/frida-gum-sys/include/frida-gumjs.h +++ b/frida-gum-sys/include/frida-gumjs.h @@ -2502,6 +2502,7 @@ #define g_output_stream_writev_all_finish _frida_g_output_stream_writev_all_finish #define g_output_stream_writev_async _frida_g_output_stream_writev_async #define g_output_stream_writev_finish _frida_g_output_stream_writev_finish +#define g_panic _frida_g_panic #define g_param_spec_boolean _frida_g_param_spec_boolean #define g_param_spec_boxed _frida_g_param_spec_boxed #define g_param_spec_char _frida_g_param_spec_char @@ -2942,6 +2943,7 @@ #define g_set_application_name _frida_g_set_application_name #define g_set_error _frida_g_set_error #define g_set_error_literal _frida_g_set_error_literal +#define g_set_panic_handler _frida_g_set_panic_handler #define g_set_prgname _frida_g_set_prgname #define g_set_print_handler _frida_g_set_print_handler #define g_set_printerr_handler _frida_g_set_printerr_handler @@ -4686,6 +4688,7 @@ #define json_generator_set_indent_char _frida_json_generator_set_indent_char #define json_generator_set_pretty _frida_json_generator_set_pretty #define json_generator_set_root _frida_json_generator_set_root +#define json_generator_take_root _frida_json_generator_take_root #define json_generator_to_data _frida_json_generator_to_data #define json_generator_to_file _frida_json_generator_to_file #define json_generator_to_gstring _frida_json_generator_to_gstring @@ -4792,6 +4795,7 @@ #define json_parser_get_current_line _frida_json_parser_get_current_line #define json_parser_get_current_pos _frida_json_parser_get_current_pos #define json_parser_get_root _frida_json_parser_get_root +#define json_parser_get_strict _frida_json_parser_get_strict #define json_parser_get_type _frida_json_parser_get_type #define json_parser_has_assignment _frida_json_parser_has_assignment #define json_parser_load_from_data _frida_json_parser_load_from_data @@ -4802,6 +4806,7 @@ #define json_parser_load_from_stream_finish _frida_json_parser_load_from_stream_finish #define json_parser_new _frida_json_parser_new #define json_parser_new_immutable _frida_json_parser_new_immutable +#define json_parser_set_strict _frida_json_parser_set_strict #define json_parser_steal_root _frida_json_parser_steal_root #define json_path_compile _frida_json_path_compile #define json_path_error_get_type _frida_json_path_error_get_type @@ -4835,13 +4840,21 @@ #define json_reader_read_member _frida_json_reader_read_member #define json_reader_set_root _frida_json_reader_set_root #define json_scanner_destroy _frida_json_scanner_destroy -#define json_scanner_error _frida_json_scanner_error +#define json_scanner_dup_identifier _frida_json_scanner_dup_identifier +#define json_scanner_dup_string_value _frida_json_scanner_dup_string_value +#define json_scanner_get_current_line _frida_json_scanner_get_current_line +#define json_scanner_get_current_position _frida_json_scanner_get_current_position +#define json_scanner_get_current_token _frida_json_scanner_get_current_token +#define json_scanner_get_float_value _frida_json_scanner_get_float_value +#define json_scanner_get_identifier _frida_json_scanner_get_identifier +#define json_scanner_get_int64_value _frida_json_scanner_get_int64_value #define json_scanner_get_next_token _frida_json_scanner_get_next_token +#define json_scanner_get_string_value _frida_json_scanner_get_string_value #define json_scanner_input_text _frida_json_scanner_input_text #define json_scanner_new _frida_json_scanner_new #define json_scanner_peek_next_token _frida_json_scanner_peek_next_token -#define json_scanner_scope_add_symbol _frida_json_scanner_scope_add_symbol -#define json_scanner_unexp_token _frida_json_scanner_unexp_token +#define json_scanner_set_msg_handler _frida_json_scanner_set_msg_handler +#define json_scanner_unknown_token _frida_json_scanner_unknown_token #define json_serializable_default_deserialize_property _frida_json_serializable_default_deserialize_property #define json_serializable_default_serialize_property _frida_json_serializable_default_serialize_property #define json_serializable_deserialize_property _frida_json_serializable_deserialize_property @@ -4888,7 +4901,8 @@ #define __GUM_SCRIPT_BACKEND_H__ /* - * Copyright (C) 2010-2022 Ole André Vadla Ravnås + * Copyright (C) 2010-2026 Ole André Vadla Ravnås + * Copyright (C) 2026 Thanos Petsas * * Licence: wxWindows Library Licence, Version 3.1 */ @@ -5281,6 +5295,7 @@ #define g_macro__has_attribute___const__ G_GNUC_CHECK_VERSION (2, 4) #define g_macro__has_attribute___unused__ G_GNUC_CHECK_VERSION (2, 4) #define g_macro__has_attribute___no_instrument_function__ G_GNUC_CHECK_VERSION (2, 4) +#define g_macro__has_attribute_weak G_GNUC_CHECK_VERSION (2, 7) #define g_macro__has_attribute_fallthrough G_GNUC_CHECK_VERSION (6, 0) #define g_macro__has_attribute___deprecated__ G_GNUC_CHECK_VERSION (3, 1) #define g_macro__has_attribute_may_alias G_GNUC_CHECK_VERSION (3, 3) @@ -5747,6 +5762,12 @@ #define G_GNUC_NO_INSTRUMENT #endif +#if g_macro__has_attribute(weak) +#define G_GNUC_WEAK __attribute__((weak)) +#else +#define G_GNUC_WEAK +#endif + /** * G_GNUC_FALLTHROUGH: * @@ -10681,6 +10702,7 @@ typedef void (*GThreadGarbageHandler) (gpointer data); typedef struct _GThreadCallbacks GThreadCallbacks; typedef struct _GThread GThread; +typedef struct _GSystemThread GSystemThread; typedef union _GMutex GMutex; typedef struct _GRecMutex GRecMutex; @@ -10820,6 +10842,20 @@ gpointer g_thread_join (GThread *thread); GLIB_AVAILABLE_IN_ALL void g_thread_yield (void); +GLIB_AVAILABLE_IN_2_68 +GSystemThread * _g_system_thread_create (gulong stack_size, + const char *name, + GThreadFunc func, + gpointer data); +GLIB_AVAILABLE_IN_2_68 +void _g_system_thread_detach (GSystemThread *thread); +GLIB_AVAILABLE_IN_2_68 +void _g_system_thread_wait (GSystemThread *thread); +GLIB_AVAILABLE_IN_2_68 +void _g_system_thread_exit (void); +GLIB_AVAILABLE_IN_2_68 +void _g_system_thread_set_name (const gchar *name); + GLIB_AVAILABLE_IN_2_32 void g_mutex_init (GMutex *mutex); @@ -15115,6 +15151,9 @@ struct _GPollFD #ifdef G_POLLFD_KQUEUE gpointer handle; #endif +#ifdef G_OS_NONE + gpointer user_data; +#endif }; /** @@ -17584,6 +17623,7 @@ gint g_io_channel_unix_get_fd (GIOChannel *channel); GLIB_VAR GSourceFuncs g_io_watch_funcs; #define G_KQUEUE_WAKEUP_HANDLE -42 +#define G_WAIT_WAKEUP_HANDLE -43 #ifdef G_OS_WIN32 @@ -19916,6 +19956,17 @@ GPrintFunc g_set_printerr_handler (GPrintFunc func); #endif /* !G_DISABLE_CHECKS */ +#define G_PANIC_MISSING_IMPLEMENTATION() \ + g_panic ("Missing implementation for: %s", G_STRFUNC) + +typedef void (*GPanicFunc) (const gchar *message, gpointer data); +GLIB_AVAILABLE_IN_2_68 +void g_panic (const gchar * format, + ...); +GLIB_AVAILABLE_IN_2_68 +void g_set_panic_handler (GPanicFunc func, + gpointer user_data); + G_END_DECLS #endif /* __G_MESSAGES_H__ */ @@ -24954,6 +25005,45 @@ const gchar * glib_check_version (guint required_major, G_END_DECLS #endif /* __G_VERSION_H__ */ +/* + * Copyright © 2025 Ole André Vadla Ravnås + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef __G_WAIT_H__ +#define __G_WAIT_H__ + +#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION) +#error "Only can be included directly." +#endif + + +#define G_WAIT_INFINITE (-1) + +G_BEGIN_DECLS + +GLIB_AVAILABLE_IN_2_68 +void g_wait_sleep (gpointer token, gint64 timeout_us); +GLIB_AVAILABLE_IN_2_68 +void g_wait_wake (gpointer token); +GLIB_AVAILABLE_IN_2_68 +gboolean g_wait_is_set (gpointer token); + +G_END_DECLS + +#endif #ifdef G_PLATFORM_WIN32 #include @@ -25576,7 +25666,7 @@ void g_thread_foreach (GFunc thread_func, #endif #define g_static_mutex_get_mutex g_static_mutex_get_mutex_impl GLIB_DEPRECATED_MACRO_IN_2_32 -#ifndef G_OS_WIN32 +#if !defined (G_OS_WIN32) && !defined (G_OS_NONE) #define G_STATIC_MUTEX_INIT { NULL, PTHREAD_MUTEX_INITIALIZER } GLIB_DEPRECATED_MACRO_IN_2_32_FOR(g_mutex_init) #else #define G_STATIC_MUTEX_INIT { NULL } GLIB_DEPRECATED_MACRO_IN_2_32_FOR(g_mutex_init) @@ -59028,6 +59118,12 @@ gboolean g_task_had_error (GTask *task); GIO_AVAILABLE_IN_2_44 gboolean g_task_get_completed (GTask *task); +/*< private >*/ +#ifndef __GTK_DOC_IGNORE__ +/* Debugging API, not part of the public API */ +void g_task_print_alive_tasks (void); +#endif /* !__GTK_DOC_IGNORE__ */ + G_END_DECLS #endif /* __G_TASK_H__ */ @@ -61672,7 +61768,7 @@ G_END_DECLS #endif /* __G_IO_H__ */ /* - * Copyright (C) 2008-2025 Ole André Vadla Ravnås + * Copyright (C) 2008-2026 Ole André Vadla Ravnås * * Licence: wxWindows Library Licence, Version 3.1 */ @@ -61681,8 +61777,8 @@ G_END_DECLS #define __GUM_H__ /* - * Copyright (C) 2008-2023 Ole André Vadla Ravnås - * Copyright (C) 2023 Håvard Sørbø + * Copyright (C) 2008-2026 Ole André Vadla Ravnås + * Copyright (C) 2023-2026 Håvard Sørbø * Copyright (C) 2024 Yannis Juglaret * * Licence: wxWindows Library Licence, Version 3.1 @@ -61691,5160 +61787,1549 @@ G_END_DECLS #ifndef __GUMDEFS_H__ #define __GUMDEFS_H__ +#ifndef CAPSTONE_ENGINE_H +#define CAPSTONE_ENGINE_H -/* This file is generated by glib-mkenums, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ +/* Capstone Disassembly Engine */ +/* By Nguyen Anh Quynh , 2013-2016 */ -#ifndef __GUM_ENUM_TYPES_H__ -#define __GUM_ENUM_TYPES_H__ +#ifdef __cplusplus +extern "C" { +#endif +#include -G_BEGIN_DECLS +#if defined(CAPSTONE_HAS_OSXKERNEL) +#include +#else +#include +#include +#endif -/* Enumerations from "gumdarwingrafter.h" */ -GType gum_darwin_grafter_flags_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_DARWIN_GRAFTER_FLAGS (gum_darwin_grafter_flags_get_type ()) +/* Capstone Disassembly Engine */ +/* By Axel Souchet & Nguyen Anh Quynh, 2014 */ -/* Enumerations from "gumdarwinmodule.h" */ -GType gum_darwin_module_flags_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_DARWIN_MODULE_FLAGS (gum_darwin_module_flags_get_type ()) +#ifndef CAPSTONE_PLATFORM_H +#define CAPSTONE_PLATFORM_H -/* Enumerations from "gumdefs.h" */ -GType gum_error_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ERROR (gum_error_get_type ()) -GType gum_cpu_type_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_CPU_TYPE (gum_cpu_type_get_type ()) -GType gum_memory_access_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_MEMORY_ACCESS (gum_memory_access_get_type ()) -/* Enumerations from "gumelfmodule.h" */ -GType gum_elf_type_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_TYPE (gum_elf_type_get_type ()) -GType gum_elf_osabi_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_OSABI (gum_elf_osabi_get_type ()) -GType gum_elf_machine_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_MACHINE (gum_elf_machine_get_type ()) -GType gum_elf_source_mode_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_SOURCE_MODE (gum_elf_source_mode_get_type ()) -GType gum_elf_section_type_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_SECTION_TYPE (gum_elf_section_type_get_type ()) -GType gum_elf_section_flags_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_SECTION_FLAGS (gum_elf_section_flags_get_type ()) -GType gum_elf_dynamic_tag_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_DYNAMIC_TAG (gum_elf_dynamic_tag_get_type ()) -GType gum_elf_shdr_index_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_SHDR_INDEX (gum_elf_shdr_index_get_type ()) -GType gum_elf_symbol_type_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_SYMBOL_TYPE (gum_elf_symbol_type_get_type ()) -GType gum_elf_symbol_bind_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_SYMBOL_BIND (gum_elf_symbol_bind_get_type ()) -GType gum_elf_ia32_relocation_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_IA32_RELOCATION (gum_elf_ia32_relocation_get_type ()) -GType gum_elf_x64_relocation_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_X64_RELOCATION (gum_elf_x64_relocation_get_type ()) -GType gum_elf_arm_relocation_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_ARM_RELOCATION (gum_elf_arm_relocation_get_type ()) -GType gum_elf_arm64_relocation_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_ARM64_RELOCATION (gum_elf_arm64_relocation_get_type ()) -GType gum_elf_mips_relocation_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ELF_MIPS_RELOCATION (gum_elf_mips_relocation_get_type ()) +// handle C99 issue (for pre-2013 VisualStudio) +#if !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__) && (defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64)) +// MSVC -/* Enumerations from "guminterceptor.h" */ -GType gum_attach_flags_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ATTACH_FLAGS (gum_attach_flags_get_type ()) -GType gum_attach_return_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_ATTACH_RETURN (gum_attach_return_get_type ()) -GType gum_replace_return_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_REPLACE_RETURN (gum_replace_return_get_type ()) +// stdbool.h +#if (_MSC_VER < 1800) || defined(_KERNEL_MODE) +// this system does not have stdbool.h +#ifndef __cplusplus +typedef unsigned char bool; +#define false 0 +#define true 1 +#endif // __cplusplus -/* Enumerations from "gummodule.h" */ -GType gum_import_type_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_IMPORT_TYPE (gum_import_type_get_type ()) -GType gum_export_type_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_EXPORT_TYPE (gum_export_type_get_type ()) -GType gum_symbol_type_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_SYMBOL_TYPE (gum_symbol_type_get_type ()) -GType gum_dependency_type_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_DEPENDENCY_TYPE (gum_dependency_type_get_type ()) +#else +// VisualStudio 2013+ -> C99 is supported +#include +#endif // (_MSC_VER < 1800) || defined(_KERNEL_MODE) -/* Enumerations from "gumprocess.h" */ -GType gum_teardown_requirement_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_TEARDOWN_REQUIREMENT (gum_teardown_requirement_get_type ()) -GType gum_code_signing_policy_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_CODE_SIGNING_POLICY (gum_code_signing_policy_get_type ()) -GType gum_modify_thread_flags_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_MODIFY_THREAD_FLAGS (gum_modify_thread_flags_get_type ()) -GType gum_thread_flags_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_THREAD_FLAGS (gum_thread_flags_get_type ()) -GType gum_thread_state_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_THREAD_STATE (gum_thread_state_get_type ()) -GType gum_watch_conditions_get_type (void) G_GNUC_CONST; -#define GUM_TYPE_WATCH_CONDITIONS (gum_watch_conditions_get_type ()) -G_END_DECLS +#else +// not MSVC -> C99 is supported +#include +#endif // !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__) && (defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64)) -#endif /* __GUM_ENUM_TYPES_H__ */ -/* Generated data ends here */ +// handle inttypes.h / stdint.h compatibility +#if defined(_WIN32_WCE) && (_WIN32_WCE < 0x800) +#include "windowsce/stdint.h" +#endif // defined(_WIN32_WCE) && (_WIN32_WCE < 0x800) +#if defined(CAPSTONE_HAS_OSXKERNEL) || (defined(_MSC_VER) && (_MSC_VER <= 1700 || defined(_KERNEL_MODE))) +// this system does not have inttypes.h -#if !defined (GUM_STATIC) && defined (G_OS_WIN32) -# ifdef GUM_EXPORTS -# define GUM_API __declspec(dllexport) -# else -# define GUM_API __declspec(dllimport) -# endif -#else -# define GUM_API +#if defined(_MSC_VER) && (_MSC_VER <= 1600 || defined(_KERNEL_MODE)) +// this system does not have stdint.h +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef signed long long int64_t; +typedef unsigned long long uint64_t; +#endif // defined(_MSC_VER) && (_MSC_VER <= 1600 || defined(_KERNEL_MODE)) + +#if defined(_MSC_VER) && (_MSC_VER < 1600 || defined(_KERNEL_MODE)) +#define INT8_MIN (-127i8 - 1) +#define INT16_MIN (-32767i16 - 1) +#define INT32_MIN (-2147483647i32 - 1) +#define INT64_MIN (-9223372036854775807i64 - 1) +#define INT8_MAX 127i8 +#define INT16_MAX 32767i16 +#define INT32_MAX 2147483647i32 +#define INT64_MAX 9223372036854775807i64 +#define UINT8_MAX 0xffui8 +#define UINT16_MAX 0xffffui16 +#define UINT32_MAX 0xffffffffui32 +#define UINT64_MAX 0xffffffffffffffffui64 +#endif // defined(_MSC_VER) && (_MSC_VER < 1600 || defined(_KERNEL_MODE)) + +#ifdef CAPSTONE_HAS_OSXKERNEL +// this system has stdint.h +#include #endif -G_BEGIN_DECLS +#define __PRI_8_LENGTH_MODIFIER__ "hh" +#define __PRI_64_LENGTH_MODIFIER__ "ll" -#define GUM_ERROR gum_error_quark () +#define PRId8 __PRI_8_LENGTH_MODIFIER__ "d" +#define PRIi8 __PRI_8_LENGTH_MODIFIER__ "i" +#define PRIo8 __PRI_8_LENGTH_MODIFIER__ "o" +#define PRIu8 __PRI_8_LENGTH_MODIFIER__ "u" +#define PRIx8 __PRI_8_LENGTH_MODIFIER__ "x" +#define PRIX8 __PRI_8_LENGTH_MODIFIER__ "X" -typedef enum { - GUM_ERROR_FAILED, - GUM_ERROR_NOT_FOUND, - GUM_ERROR_EXISTS, - GUM_ERROR_PERMISSION_DENIED, - GUM_ERROR_INVALID_ARGUMENT, - GUM_ERROR_NOT_SUPPORTED, - GUM_ERROR_INVALID_DATA, -} GumError; +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" -typedef guint64 GumAddress; -#define GUM_ADDRESS(a) ((GumAddress) (guintptr) (a)) -#define GUM_TYPE_ADDRESS (gum_address_get_type ()) -typedef guint GumOS; -typedef guint GumCallingConvention; -typedef guint GumAbiType; -typedef guint GumCpuFeatures; -typedef guint GumInstructionEncoding; -typedef guint GumArgType; -typedef struct _GumArgument GumArgument; -typedef guint GumBranchHint; -typedef struct _GumIA32CpuContext GumIA32CpuContext; -typedef struct _GumX64CpuContext GumX64CpuContext; -typedef struct _GumArmCpuContext GumArmCpuContext; -typedef union _GumArmVectorReg GumArmVectorReg; -typedef struct _GumArm64CpuContext GumArm64CpuContext; -typedef union _GumArm64VectorReg GumArm64VectorReg; -typedef struct _GumMipsCpuContext GumMipsCpuContext; -typedef guint GumRelocationScenario; +#if defined(_MSC_VER) && _MSC_VER <= 1700 +#define PRId32 "ld" +#define PRIi32 "li" +#define PRIo32 "lo" +#define PRIu32 "lu" +#define PRIx32 "lx" +#define PRIX32 "lX" +#else // OSX +#define PRId32 "d" +#define PRIi32 "i" +#define PRIo32 "o" +#define PRIu32 "u" +#define PRIx32 "x" +#define PRIX32 "X" +#endif // defined(_MSC_VER) && _MSC_VER <= 1700 + +#if defined(_MSC_VER) && _MSC_VER <= 1700 +// redefine functions from inttypes.h used in cstool +#define strtoull _strtoui64 +#endif + +#define PRId64 __PRI_64_LENGTH_MODIFIER__ "d" +#define PRIi64 __PRI_64_LENGTH_MODIFIER__ "i" +#define PRIo64 __PRI_64_LENGTH_MODIFIER__ "o" +#define PRIu64 __PRI_64_LENGTH_MODIFIER__ "u" +#define PRIx64 __PRI_64_LENGTH_MODIFIER__ "x" +#define PRIX64 __PRI_64_LENGTH_MODIFIER__ "X" -#if defined (_M_IX86) || defined (__i386__) -# define GUM_NATIVE_CPU GUM_CPU_IA32 -# define GUM_DEFAULT_CS_ARCH CS_ARCH_X86 -# define gum_cs_arch_register_native cs_arch_register_x86 -/** - * GUM_DEFAULT_CS_MODE: (skip) - */ -# define GUM_DEFAULT_CS_MODE CS_MODE_32 -typedef GumIA32CpuContext GumCpuContext; -#elif defined (_M_X64) || defined (__x86_64__) -# define GUM_NATIVE_CPU GUM_CPU_AMD64 -# define GUM_DEFAULT_CS_ARCH CS_ARCH_X86 -# define gum_cs_arch_register_native cs_arch_register_x86 -/** - * GUM_DEFAULT_CS_MODE: (skip) - */ -# define GUM_DEFAULT_CS_MODE CS_MODE_64 -typedef GumX64CpuContext GumCpuContext; -#elif defined (_M_ARM) || defined (__arm__) -# define GUM_NATIVE_CPU GUM_CPU_ARM -# define GUM_DEFAULT_CS_ARCH CS_ARCH_ARM -# define gum_cs_arch_register_native cs_arch_register_arm -/** - * GUM_DEFAULT_CS_MODE: (skip) - */ -# define GUM_DEFAULT_CS_MODE \ - ((cs_mode) (CS_MODE_ARM | CS_MODE_V8 | GUM_DEFAULT_CS_ENDIAN)) -# define GUM_PSR_T_BIT 0x20 -typedef GumArmCpuContext GumCpuContext; -#elif defined (_M_ARM64) || defined (__aarch64__) -# define GUM_NATIVE_CPU GUM_CPU_ARM64 -# define GUM_DEFAULT_CS_ARCH CS_ARCH_ARM64 -# define gum_cs_arch_register_native cs_arch_register_arm64 -/** - * GUM_DEFAULT_CS_MODE: (skip) - */ -# define GUM_DEFAULT_CS_MODE GUM_DEFAULT_CS_ENDIAN -typedef GumArm64CpuContext GumCpuContext; -#elif defined (__mips__) -# define GUM_NATIVE_CPU GUM_CPU_MIPS -# define GUM_DEFAULT_CS_ARCH CS_ARCH_MIPS -# define gum_cs_arch_register_native cs_arch_register_mips -# if GLIB_SIZEOF_VOID_P == 4 -/** - * GUM_DEFAULT_CS_MODE: (skip) - */ -# define GUM_DEFAULT_CS_MODE ((cs_mode) \ - (CS_MODE_MIPS32 | GUM_DEFAULT_CS_ENDIAN)) -# else -/** - * GUM_DEFAULT_CS_MODE: (skip) - */ -# define GUM_DEFAULT_CS_MODE ((cs_mode) \ - (CS_MODE_MIPS64 | GUM_DEFAULT_CS_ENDIAN)) -# endif -typedef GumMipsCpuContext GumCpuContext; #else -# error Unsupported architecture. +// this system has inttypes.h by default +#include +#endif // defined(CAPSTONE_HAS_OSXKERNEL) || (defined(_MSC_VER) && (_MSC_VER <= 1700 || defined(_KERNEL_MODE))) + +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#pragma warning(disable:4100) +#define CAPSTONE_API __cdecl +#ifdef CAPSTONE_SHARED +#define CAPSTONE_EXPORT __declspec(dllexport) +#else // defined(CAPSTONE_STATIC) +#define CAPSTONE_EXPORT #endif -/* - * The only non-legacy big-endian configuration on 32-bit ARM systems is BE8. - * In this configuration, whilst the data is in big-endian, the code stream is - * still in little-endian. Since Capstone is disassembling the code stream, it - * should work in little-endian even on BE8 systems. On big-endian 64-bit ARM - * systems, the code stream is likewise in little-endian. - */ -#if G_BYTE_ORDER == G_LITTLE_ENDIAN || \ - defined (__arm__) || \ - defined (_M_ARM64) || \ - defined (__aarch64__) -# define GUM_DEFAULT_CS_ENDIAN CS_MODE_LITTLE_ENDIAN #else -# define GUM_DEFAULT_CS_ENDIAN CS_MODE_BIG_ENDIAN +#define CAPSTONE_API +#if (defined(__GNUC__) || defined(__IBMC__)) && !defined(CAPSTONE_STATIC) +#define CAPSTONE_EXPORT __attribute__((visibility("default"))) +#else // defined(CAPSTONE_STATIC) +#define CAPSTONE_EXPORT #endif -#ifdef G_OS_WIN32 -# define GUM_NATIVE_ABI GUM_ABI_WINDOWS -# define GUM_NATIVE_ABI_IS_WINDOWS 1 -# define GUM_NATIVE_ABI_IS_UNIX 0 +#endif + +#if (defined(__GNUC__) || defined(__IBMC__)) +#define CAPSTONE_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define CAPSTONE_DEPRECATED __declspec(deprecated) #else -# define GUM_NATIVE_ABI GUM_ABI_UNIX -# define GUM_NATIVE_ABI_IS_WINDOWS 0 -# define GUM_NATIVE_ABI_IS_UNIX 1 +#pragma message("WARNING: You need to implement CAPSTONE_DEPRECATED for this compiler") +#define CAPSTONE_DEPRECATED #endif -enum _GumOS -{ - GUM_OS_WINDOWS, - GUM_OS_MACOS, - GUM_OS_LINUX, - GUM_OS_IOS, - GUM_OS_WATCHOS, - GUM_OS_TVOS, - GUM_OS_ANDROID, - GUM_OS_FREEBSD, - GUM_OS_QNX -}; +// Capstone API version +#define CS_API_MAJOR 5 +#define CS_API_MINOR 0 -enum _GumCallingConvention -{ - GUM_CALL_CAPI, - GUM_CALL_SYSAPI -}; +// Version for bleeding edge code of the Github's "next" branch. +// Use this if you want the absolutely latest development code. +// This version number will be bumped up whenever we have a new major change. +#define CS_NEXT_VERSION 5 -enum _GumAbiType -{ - GUM_ABI_UNIX, - GUM_ABI_WINDOWS -}; +// Capstone package version +#define CS_VERSION_MAJOR CS_API_MAJOR +#define CS_VERSION_MINOR CS_API_MINOR +#define CS_VERSION_EXTRA 1 -typedef enum { - GUM_CPU_INVALID, - GUM_CPU_IA32, - GUM_CPU_AMD64, - GUM_CPU_ARM, - GUM_CPU_ARM64, - GUM_CPU_MIPS -} GumCpuType; +/// Macro for meta programming. +/// Meant for projects using Capstone and need to support multiple +/// versions of it. +/// These macros replace several instances of the old "ARM64" with +/// the new "AArch64" name depending on the CS version. +#if CS_NEXT_VERSION < 6 +#define CS_AARCH64(x) ARM64##x +#else +#define CS_AARCH64(x) AArch64##x +#endif -enum _GumCpuFeatures -{ - GUM_CPU_AVX2 = 1 << 0, - GUM_CPU_CET_SS = 1 << 1, - GUM_CPU_THUMB_INTERWORK = 1 << 2, - GUM_CPU_VFP2 = 1 << 3, - GUM_CPU_VFP3 = 1 << 4, - GUM_CPU_VFPD32 = 1 << 5, - GUM_CPU_PTRAUTH = 1 << 6, -}; +#if CS_NEXT_VERSION < 6 +#define CS_AARCH64pre(x) x##ARM64 +#else +#define CS_AARCH64pre(x) x##AARCH64 +#endif -typedef enum { - GUM_MEMORY_ACCESS_OPEN, - GUM_MEMORY_ACCESS_EXCLUSIVE, -} GumMemoryAccess; +#if CS_NEXT_VERSION < 6 +#define CS_AARCH64CC(x) ARM64_CC##x +#else +#define CS_AARCH64CC(x) AArch64CC##x +#endif -enum _GumInstructionEncoding -{ - GUM_INSTRUCTION_DEFAULT, - GUM_INSTRUCTION_SPECIAL -}; +#if CS_NEXT_VERSION < 6 +#define CS_AARCH64_VL_(x) ARM64_VAS_##x +#else +#define CS_AARCH64_VL_(x) AArch64Layout_VL_##x +#endif -enum _GumArgType -{ - GUM_ARG_ADDRESS, - GUM_ARG_REGISTER -}; +#if CS_NEXT_VERSION < 6 +#define CS_aarch64_ arm64 +#else +#define CS_aarch64_ aarch64 +#endif -struct _GumArgument -{ - GumArgType type; +#if CS_NEXT_VERSION < 6 +#define CS_aarch64(x) arm64##x +#else +#define CS_aarch64(x) aarch64##x +#endif - union - { - GumAddress address; - gint reg; - } value; -}; +#if CS_NEXT_VERSION < 6 +#define CS_aarch64_op() cs_arm64_op +#define CS_aarch64_reg() arm64_reg +#define CS_aarch64_cc() arm64_cc +#define CS_cs_aarch64() cs_arm64 +#define CS_aarch64_extender() arm64_extender +#define CS_aarch64_shifter() arm64_shifter +#define CS_aarch64_vas() arm64_vas +#else +#define CS_aarch64_op() cs_aarch64_op +#define CS_aarch64_reg() aarch64_reg +#define CS_aarch64_cc() AArch64CC_CondCode +#define CS_cs_aarch64() cs_aarch64 +#define CS_aarch64_extender() aarch64_extender +#define CS_aarch64_shifter() aarch64_shifter +#define CS_aarch64_vas() AArch64Layout_VectorLayout +#endif -enum _GumBranchHint -{ - GUM_NO_HINT, - GUM_LIKELY, - GUM_UNLIKELY -}; - -struct _GumIA32CpuContext -{ - guint32 eip; +/// Macro to create combined version which can be compared to +/// result of cs_version() API. +#define CS_MAKE_VERSION(major, minor) ((major << 8) + minor) - guint32 edi; - guint32 esi; - guint32 ebp; - guint32 esp; - guint32 ebx; - guint32 edx; - guint32 ecx; - guint32 eax; -}; +/// Maximum size of an instruction mnemonic string. +#define CS_MNEMONIC_SIZE 32 -struct _GumX64CpuContext -{ - guint64 rip; +// Handle using with all API +typedef size_t csh; - guint64 r15; - guint64 r14; - guint64 r13; - guint64 r12; - guint64 r11; - guint64 r10; - guint64 r9; - guint64 r8; +/// Architecture type +typedef enum cs_arch { + CS_ARCH_ARM = 0, ///< ARM architecture (including Thumb, Thumb-2) + CS_ARCH_ARM64, ///< ARM-64, also called AArch64 + CS_ARCH_MIPS, ///< Mips architecture + CS_ARCH_X86, ///< X86 architecture (including x86 & x86-64) + CS_ARCH_PPC, ///< PowerPC architecture + CS_ARCH_SPARC, ///< Sparc architecture + CS_ARCH_SYSZ, ///< SystemZ architecture + CS_ARCH_XCORE, ///< XCore architecture + CS_ARCH_M68K, ///< 68K architecture + CS_ARCH_TMS320C64X, ///< TMS320C64x architecture + CS_ARCH_M680X, ///< 680X architecture + CS_ARCH_EVM, ///< Ethereum architecture + CS_ARCH_MOS65XX, ///< MOS65XX architecture (including MOS6502) + CS_ARCH_WASM, ///< WebAssembly architecture + CS_ARCH_BPF, ///< Berkeley Packet Filter architecture (including eBPF) + CS_ARCH_RISCV, ///< RISCV architecture + CS_ARCH_SH, ///< SH architecture + CS_ARCH_TRICORE, ///< TriCore architecture + CS_ARCH_MAX, + CS_ARCH_ALL = 0xFFFF, // All architectures - for cs_support() +} cs_arch; - guint64 rdi; - guint64 rsi; - guint64 rbp; - guint64 rsp; - guint64 rbx; - guint64 rdx; - guint64 rcx; - guint64 rax; -}; +// Support value to verify diet mode of the engine. +// If cs_support(CS_SUPPORT_DIET) return True, the engine was compiled +// in diet mode. +#define CS_SUPPORT_DIET (CS_ARCH_ALL + 1) -union _GumArmVectorReg -{ - guint8 q[16]; - gdouble d[2]; - gfloat s[4]; -}; +// Support value to verify X86 reduce mode of the engine. +// If cs_support(CS_SUPPORT_X86_REDUCE) return True, the engine was compiled +// in X86 reduce mode. +#define CS_SUPPORT_X86_REDUCE (CS_ARCH_ALL + 2) -struct _GumArmCpuContext -{ - guint32 pc; - guint32 sp; - guint32 cpsr; +/// Mode type +typedef enum cs_mode { + CS_MODE_LITTLE_ENDIAN = 0, ///< little-endian mode (default mode) + CS_MODE_ARM = 0, ///< 32-bit ARM + CS_MODE_16 = 1 << 1, ///< 16-bit mode (X86) + CS_MODE_32 = 1 << 2, ///< 32-bit mode (X86) + CS_MODE_64 = 1 << 3, ///< 64-bit mode (X86, PPC) + CS_MODE_THUMB = 1 << 4, ///< ARM's Thumb mode, including Thumb-2 + CS_MODE_MCLASS = 1 << 5, ///< ARM's Cortex-M series + CS_MODE_V8 = 1 << 6, ///< ARMv8 A32 encodings for ARM + CS_MODE_MICRO = 1 << 4, ///< MicroMips mode (MIPS) + CS_MODE_MIPS3 = 1 << 5, ///< Mips III ISA + CS_MODE_MIPS32R6 = 1 << 6, ///< Mips32r6 ISA + CS_MODE_MIPS2 = 1 << 7, ///< Mips II ISA + CS_MODE_V9 = 1 << 4, ///< SparcV9 mode (Sparc) + CS_MODE_QPX = 1 << 4, ///< Quad Processing eXtensions mode (PPC) + CS_MODE_SPE = 1 << 5, ///< Signal Processing Engine mode (PPC) + CS_MODE_BOOKE = 1 << 6, ///< Book-E mode (PPC) + CS_MODE_PS = 1 << 7, ///< Paired-singles mode (PPC) + CS_MODE_M68K_000 = 1 << 1, ///< M68K 68000 mode + CS_MODE_M68K_010 = 1 << 2, ///< M68K 68010 mode + CS_MODE_M68K_020 = 1 << 3, ///< M68K 68020 mode + CS_MODE_M68K_030 = 1 << 4, ///< M68K 68030 mode + CS_MODE_M68K_040 = 1 << 5, ///< M68K 68040 mode + CS_MODE_M68K_060 = 1 << 6, ///< M68K 68060 mode + CS_MODE_BIG_ENDIAN = 1U << 31, ///< big-endian mode + CS_MODE_MIPS32 = CS_MODE_32, ///< Mips32 ISA (Mips) + CS_MODE_MIPS64 = CS_MODE_64, ///< Mips64 ISA (Mips) + CS_MODE_M680X_6301 = 1 << 1, ///< M680X Hitachi 6301,6303 mode + CS_MODE_M680X_6309 = 1 << 2, ///< M680X Hitachi 6309 mode + CS_MODE_M680X_6800 = 1 << 3, ///< M680X Motorola 6800,6802 mode + CS_MODE_M680X_6801 = 1 << 4, ///< M680X Motorola 6801,6803 mode + CS_MODE_M680X_6805 = 1 << 5, ///< M680X Motorola/Freescale 6805 mode + CS_MODE_M680X_6808 = 1 << 6, ///< M680X Motorola/Freescale/NXP 68HC08 mode + CS_MODE_M680X_6809 = 1 << 7, ///< M680X Motorola 6809 mode + CS_MODE_M680X_6811 = 1 << 8, ///< M680X Motorola/Freescale/NXP 68HC11 mode + CS_MODE_M680X_CPU12 = 1 << 9, ///< M680X Motorola/Freescale/NXP CPU12 + ///< used on M68HC12/HCS12 + CS_MODE_M680X_HCS08 = 1 << 10, ///< M680X Freescale/NXP HCS08 mode + CS_MODE_BPF_CLASSIC = 0, ///< Classic BPF mode (default) + CS_MODE_BPF_EXTENDED = 1 << 0, ///< Extended BPF mode + CS_MODE_RISCV32 = 1 << 0, ///< RISCV RV32G + CS_MODE_RISCV64 = 1 << 1, ///< RISCV RV64G + CS_MODE_RISCVC = 1 << 2, ///< RISCV compressed instructure mode + CS_MODE_MOS65XX_6502 = 1 << 1, ///< MOS65XXX MOS 6502 + CS_MODE_MOS65XX_65C02 = 1 << 2, ///< MOS65XXX WDC 65c02 + CS_MODE_MOS65XX_W65C02 = 1 << 3, ///< MOS65XXX WDC W65c02 + CS_MODE_MOS65XX_65816 = 1 << 4, ///< MOS65XXX WDC 65816, 8-bit m/x + CS_MODE_MOS65XX_65816_LONG_M = (1 << 5), ///< MOS65XXX WDC 65816, 16-bit m, 8-bit x + CS_MODE_MOS65XX_65816_LONG_X = (1 << 6), ///< MOS65XXX WDC 65816, 8-bit m, 16-bit x + CS_MODE_MOS65XX_65816_LONG_MX = CS_MODE_MOS65XX_65816_LONG_M | CS_MODE_MOS65XX_65816_LONG_X, + CS_MODE_SH2 = 1 << 1, ///< SH2 + CS_MODE_SH2A = 1 << 2, ///< SH2A + CS_MODE_SH3 = 1 << 3, ///< SH3 + CS_MODE_SH4 = 1 << 4, ///< SH4 + CS_MODE_SH4A = 1 << 5, ///< SH4A + CS_MODE_SHFPU = 1 << 6, ///< w/ FPU + CS_MODE_SHDSP = 1 << 7, ///< w/ DSP + CS_MODE_TRICORE_110 = 1 << 1, ///< Tricore 1.1 + CS_MODE_TRICORE_120 = 1 << 2, ///< Tricore 1.2 + CS_MODE_TRICORE_130 = 1 << 3, ///< Tricore 1.3 + CS_MODE_TRICORE_131 = 1 << 4, ///< Tricore 1.3.1 + CS_MODE_TRICORE_160 = 1 << 5, ///< Tricore 1.6 + CS_MODE_TRICORE_161 = 1 << 6, ///< Tricore 1.6.1 + CS_MODE_TRICORE_162 = 1 << 7, ///< Tricore 1.6.2 +} cs_mode; - guint32 r8; - guint32 r9; - guint32 r10; - guint32 r11; - guint32 r12; +typedef void* (CAPSTONE_API *cs_malloc_t)(size_t size); +typedef void* (CAPSTONE_API *cs_calloc_t)(size_t nmemb, size_t size); +typedef void* (CAPSTONE_API *cs_realloc_t)(void *ptr, size_t size); +typedef void (CAPSTONE_API *cs_free_t)(void *ptr); +typedef int (CAPSTONE_API *cs_vsnprintf_t)(char *str, size_t size, const char *format, va_list ap); - GumArmVectorReg v[16]; - guint32 _padding; +/// User-defined dynamic memory related functions: malloc/calloc/realloc/free/vsnprintf() +/// By default, Capstone uses system's malloc(), calloc(), realloc(), free() & vsnprintf(). +typedef struct cs_opt_mem { + cs_malloc_t malloc; + cs_calloc_t calloc; + cs_realloc_t realloc; + cs_free_t free; + cs_vsnprintf_t vsnprintf; +} cs_opt_mem; - guint32 r[8]; - guint32 lr; -}; +/// Customize mnemonic for instructions with alternative name. +/// To reset existing customized instruction to its default mnemonic, +/// call cs_option(CS_OPT_MNEMONIC) again with the same @id and NULL value +/// for @mnemonic. +typedef struct cs_opt_mnem { + /// ID of instruction to be customized. + unsigned int id; + /// Customized instruction mnemonic. + const char *mnemonic; +} cs_opt_mnem; -union _GumArm64VectorReg -{ - guint8 q[16]; - gdouble d; - gfloat s; - guint16 h; - guint8 b; -}; +/// Runtime option for the disassembled engine +typedef enum cs_opt_type { + CS_OPT_INVALID = 0, ///< No option specified + CS_OPT_SYNTAX, ///< Assembly output syntax + CS_OPT_DETAIL, ///< Break down instruction structure into details + CS_OPT_MODE, ///< Change engine's mode at run-time + CS_OPT_MEM, ///< User-defined dynamic memory related functions + CS_OPT_SKIPDATA, ///< Skip data when disassembling. Then engine is in SKIPDATA mode. + CS_OPT_SKIPDATA_SETUP, ///< Setup user-defined function for SKIPDATA option + CS_OPT_MNEMONIC, ///< Customize instruction mnemonic + CS_OPT_UNSIGNED, ///< print immediate operands in unsigned form + CS_OPT_NO_BRANCH_OFFSET, ///< ARM, prints branch immediates without offset. +} cs_opt_type; -struct _GumArm64CpuContext -{ - guint64 pc; - guint64 sp; - guint64 nzcv; +/// Runtime option value (associated with option type above) +typedef enum cs_opt_value { + CS_OPT_OFF = 0, ///< Turn OFF an option - default for CS_OPT_DETAIL, CS_OPT_SKIPDATA, CS_OPT_UNSIGNED. + CS_OPT_ON = 3, ///< Turn ON an option (CS_OPT_DETAIL, CS_OPT_SKIPDATA). + CS_OPT_SYNTAX_DEFAULT = 0, ///< Default asm syntax (CS_OPT_SYNTAX). + CS_OPT_SYNTAX_INTEL, ///< X86 Intel asm syntax - default on X86 (CS_OPT_SYNTAX). + CS_OPT_SYNTAX_ATT, ///< X86 ATT asm syntax (CS_OPT_SYNTAX). + CS_OPT_SYNTAX_NOREGNAME, ///< Prints register name with only number (CS_OPT_SYNTAX) + CS_OPT_SYNTAX_MASM, ///< X86 Intel Masm syntax (CS_OPT_SYNTAX). + CS_OPT_SYNTAX_MOTOROLA, ///< MOS65XX use $ as hex prefix +} cs_opt_value; - guint64 x[29]; - guint64 fp; - guint64 lr; +/// Common instruction operand types - to be consistent across all architectures. +typedef enum cs_op_type { + CS_OP_INVALID = 0, ///< uninitialized/invalid operand. + CS_OP_REG, ///< Register operand. + CS_OP_IMM, ///< Immediate operand. + CS_OP_FP, ///< Floating-Point operand. + CS_OP_MEM = + 0x80, ///< Memory operand. Can be ORed with another operand type. +} cs_op_type; - GumArm64VectorReg v[32]; -}; +/// Common instruction operand access types - to be consistent across all architectures. +/// It is possible to combine access types, for example: CS_AC_READ | CS_AC_WRITE +typedef enum cs_ac_type { + CS_AC_INVALID = 0, ///< Uninitialized/invalid access type. + CS_AC_READ = 1 << 0, ///< Operand read from memory or register. + CS_AC_WRITE = 1 << 1, ///< Operand write to memory or register. +} cs_ac_type; -struct _GumMipsCpuContext -{ - /* - * This structure represents the register state pushed onto the stack by the - * trampoline which allows us to vector from the original minimal assembly - * hook to architecture agnostic C code inside frida-gum. These registers are - * natively sized. Even if some have not been expanded to 64-bits from the - * MIPS32 architecture MIPS can only perform aligned data access and as such - * pushing zero extended values is simpler than attempting to push minimally - * sized data types. - */ - gsize pc; +/// Common instruction groups - to be consistent across all architectures. +typedef enum cs_group_type { + CS_GRP_INVALID = 0, ///< uninitialized/invalid group. + CS_GRP_JUMP, ///< all jump instructions (conditional+direct+indirect jumps) + CS_GRP_CALL, ///< all call instructions + CS_GRP_RET, ///< all return instructions + CS_GRP_INT, ///< all interrupt instructions (int+syscall) + CS_GRP_IRET, ///< all interrupt return instructions + CS_GRP_PRIVILEGE, ///< all privileged instructions + CS_GRP_BRANCH_RELATIVE, ///< all relative branching instructions +} cs_group_type; - gsize gp; - gsize sp; - gsize fp; - gsize ra; +/** + User-defined callback function for SKIPDATA option. + See tests/test_skipdata.c for sample code demonstrating this API. - gsize hi; - gsize lo; + @code: the input buffer containing code to be disassembled. + This is the same buffer passed to cs_disasm(). + @code_size: size (in bytes) of the above @code buffer. + @offset: the position of the currently-examining byte in the input + buffer @code mentioned above. + @user_data: user-data passed to cs_option() via @user_data field in + cs_opt_skipdata struct below. - gsize at; + @return: return number of bytes to skip, or 0 to immediately stop disassembling. +*/ +typedef size_t (CAPSTONE_API *cs_skipdata_cb_t)(const uint8_t *code, size_t code_size, size_t offset, void *user_data); - gsize v0; - gsize v1; +/// User-customized setup for SKIPDATA option +typedef struct cs_opt_skipdata { + /// Capstone considers data to skip as special "instructions". + /// User can specify the string for this instruction's "mnemonic" here. + /// By default (if @mnemonic is NULL), Capstone use ".byte". + const char *mnemonic; - gsize a0; - gsize a1; - gsize a2; - gsize a3; + /// User-defined callback function to be called when Capstone hits data. + /// If the returned value from this callback is positive (>0), Capstone + /// will skip exactly that number of bytes & continue. Otherwise, if + /// the callback returns 0, Capstone stops disassembling and returns + /// immediately from cs_disasm() + /// NOTE: if this callback pointer is NULL, Capstone would skip a number + /// of bytes depending on architectures, as following: + /// Arm: 2 bytes (Thumb mode) or 4 bytes. + /// Arm64: 4 bytes. + /// Mips: 4 bytes. + /// M680x: 1 byte. + /// PowerPC: 4 bytes. + /// Sparc: 4 bytes. + /// SystemZ: 2 bytes. + /// X86: 1 bytes. + /// XCore: 2 bytes. + /// EVM: 1 bytes. + /// RISCV: 4 bytes. + /// WASM: 1 bytes. + /// MOS65XX: 1 bytes. + /// BPF: 8 bytes. + /// TriCore: 2 bytes. + cs_skipdata_cb_t callback; // default value is NULL - gsize t0; - gsize t1; - gsize t2; - gsize t3; - gsize t4; - gsize t5; - gsize t6; - gsize t7; - gsize t8; - gsize t9; + /// User-defined data to be passed to @callback function pointer. + void *user_data; +} cs_opt_skipdata; - gsize s0; - gsize s1; - gsize s2; - gsize s3; - gsize s4; - gsize s5; - gsize s6; - gsize s7; - gsize k0; - gsize k1; -}; +#ifndef CAPSTONE_ARM_H +#define CAPSTONE_ARM_H -enum _GumRelocationScenario -{ - GUM_SCENARIO_OFFLINE, - GUM_SCENARIO_ONLINE -}; +/* Capstone Disassembly Engine */ +/* By Nguyen Anh Quynh , 2013-2015 */ -#ifndef __arm__ -# if GLIB_SIZEOF_VOID_P == 8 -# define GUM_CPU_CONTEXT_XAX(c) ((c)->rax) -# define GUM_CPU_CONTEXT_XCX(c) ((c)->rcx) -# define GUM_CPU_CONTEXT_XDX(c) ((c)->rdx) -# define GUM_CPU_CONTEXT_XBX(c) ((c)->rbx) -# define GUM_CPU_CONTEXT_XSP(c) ((c)->rsp) -# define GUM_CPU_CONTEXT_XBP(c) ((c)->rbp) -# define GUM_CPU_CONTEXT_XSI(c) ((c)->rsi) -# define GUM_CPU_CONTEXT_XDI(c) ((c)->rdi) -# define GUM_CPU_CONTEXT_XIP(c) ((c)->rip) -# define GUM_CPU_CONTEXT_OFFSET_XAX (G_STRUCT_OFFSET (GumCpuContext, rax)) -# define GUM_CPU_CONTEXT_OFFSET_XCX (G_STRUCT_OFFSET (GumCpuContext, rcx)) -# define GUM_CPU_CONTEXT_OFFSET_XDX (G_STRUCT_OFFSET (GumCpuContext, rdx)) -# define GUM_CPU_CONTEXT_OFFSET_XBX (G_STRUCT_OFFSET (GumCpuContext, rbx)) -# define GUM_CPU_CONTEXT_OFFSET_XSP (G_STRUCT_OFFSET (GumCpuContext, rsp)) -# define GUM_CPU_CONTEXT_OFFSET_XBP (G_STRUCT_OFFSET (GumCpuContext, rbp)) -# define GUM_CPU_CONTEXT_OFFSET_XSI (G_STRUCT_OFFSET (GumCpuContext, rsi)) -# define GUM_CPU_CONTEXT_OFFSET_XDI (G_STRUCT_OFFSET (GumCpuContext, rdi)) -# define GUM_CPU_CONTEXT_OFFSET_XIP (G_STRUCT_OFFSET (GumCpuContext, rip)) -# else -# define GUM_CPU_CONTEXT_XAX(c) ((c)->eax) -# define GUM_CPU_CONTEXT_XCX(c) ((c)->ecx) -# define GUM_CPU_CONTEXT_XDX(c) ((c)->edx) -# define GUM_CPU_CONTEXT_XBX(c) ((c)->ebx) -# define GUM_CPU_CONTEXT_XSP(c) ((c)->esp) -# define GUM_CPU_CONTEXT_XBP(c) ((c)->ebp) -# define GUM_CPU_CONTEXT_XSI(c) ((c)->esi) -# define GUM_CPU_CONTEXT_XDI(c) ((c)->edi) -# define GUM_CPU_CONTEXT_XIP(c) ((c)->eip) -# define GUM_CPU_CONTEXT_OFFSET_XAX (G_STRUCT_OFFSET (GumCpuContext, eax)) -# define GUM_CPU_CONTEXT_OFFSET_XCX (G_STRUCT_OFFSET (GumCpuContext, ecx)) -# define GUM_CPU_CONTEXT_OFFSET_XDX (G_STRUCT_OFFSET (GumCpuContext, edx)) -# define GUM_CPU_CONTEXT_OFFSET_XBX (G_STRUCT_OFFSET (GumCpuContext, ebx)) -# define GUM_CPU_CONTEXT_OFFSET_XSP (G_STRUCT_OFFSET (GumCpuContext, esp)) -# define GUM_CPU_CONTEXT_OFFSET_XBP (G_STRUCT_OFFSET (GumCpuContext, ebp)) -# define GUM_CPU_CONTEXT_OFFSET_XSI (G_STRUCT_OFFSET (GumCpuContext, esi)) -# define GUM_CPU_CONTEXT_OFFSET_XDI (G_STRUCT_OFFSET (GumCpuContext, edi)) -# define GUM_CPU_CONTEXT_OFFSET_XIP (G_STRUCT_OFFSET (GumCpuContext, eip)) -# endif +#ifdef __cplusplus +extern "C" { #endif -#define GUM_MAX_PATH 260 -#define GUM_MAX_TYPE_NAME 16 -#define GUM_MAX_SYMBOL_NAME 2048 - -#define GUM_MAX_THREADS 768 -#define GUM_MAX_CALL_DEPTH 32 -#define GUM_MAX_BACKTRACE_DEPTH 16 -#define GUM_MAX_WORST_CASE_INFO_SIZE 128 -#define GUM_MAX_LISTENERS_PER_FUNCTION 2 -#define GUM_MAX_LISTENER_DATA 1024 +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif -#define GUM_MAX_THREAD_RANGES 2 +/// ARM shift type +typedef enum arm_shifter { + ARM_SFT_INVALID = 0, + ARM_SFT_ASR, ///< shift with immediate const + ARM_SFT_LSL, ///< shift with immediate const + ARM_SFT_LSR, ///< shift with immediate const + ARM_SFT_ROR, ///< shift with immediate const + ARM_SFT_RRX, ///< shift with immediate const + ARM_SFT_ASR_REG, ///< shift with register + ARM_SFT_LSL_REG, ///< shift with register + ARM_SFT_LSR_REG, ///< shift with register + ARM_SFT_ROR_REG, ///< shift with register + ARM_SFT_RRX_REG, ///< shift with register +} arm_shifter; -#if defined (HAVE_I386) -# if GLIB_SIZEOF_VOID_P == 8 -# define GUM_CPU_MODE CS_MODE_64 -# define GUM_X86_THUNK -# else -# define GUM_CPU_MODE CS_MODE_32 -# define GUM_X86_THUNK GUM_FASTCALL -# endif -#else -# if G_BYTE_ORDER == G_LITTLE_ENDIAN -# define GUM_CPU_MODE CS_MODE_LITTLE_ENDIAN -# else -# define GUM_CPU_MODE CS_MODE_BIG_ENDIAN -# endif -#endif -#if !defined (G_OS_WIN32) && GLIB_SIZEOF_VOID_P == 8 -# define GUM_X86_THUNK_REG_ARG0 GUM_X86_XDI -# define GUM_X86_THUNK_REG_ARG1 GUM_X86_XSI -#else -# define GUM_X86_THUNK_REG_ARG0 GUM_X86_XCX -# define GUM_X86_THUNK_REG_ARG1 GUM_X86_XDX -#endif -#define GUM_RED_ZONE_SIZE 128 +/// ARM condition code +typedef enum arm_cc { + ARM_CC_INVALID = 0, + ARM_CC_EQ, ///< Equal Equal + ARM_CC_NE, ///< Not equal Not equal, or unordered + ARM_CC_HS, ///< Carry set >, ==, or unordered + ARM_CC_LO, ///< Carry clear Less than + ARM_CC_MI, ///< Minus, negative Less than + ARM_CC_PL, ///< Plus, positive or zero >, ==, or unordered + ARM_CC_VS, ///< Overflow Unordered + ARM_CC_VC, ///< No overflow Not unordered + ARM_CC_HI, ///< Unsigned higher Greater than, or unordered + ARM_CC_LS, ///< Unsigned lower or same Less than or equal + ARM_CC_GE, ///< Greater than or equal Greater than or equal + ARM_CC_LT, ///< Less than Less than, or unordered + ARM_CC_GT, ///< Greater than Greater than + ARM_CC_LE, ///< Less than or equal <, ==, or unordered + ARM_CC_AL ///< Always (unconditional) Always (unconditional) +} arm_cc; -#if defined (_M_IX86) || defined (__i386__) -# ifdef _MSC_VER -# define GUM_CDECL __cdecl -# define GUM_STDCALL __stdcall -# define GUM_FASTCALL __fastcall -# else -# define GUM_CDECL __attribute__ ((cdecl)) -# define GUM_STDCALL __attribute__ ((stdcall)) -# define GUM_FASTCALL __attribute__ ((fastcall)) -# endif -#else -# define GUM_CDECL -# define GUM_STDCALL -# define GUM_FASTCALL -#endif +typedef enum arm_sysreg { + /// Special registers for MSR + ARM_SYSREG_INVALID = 0, -#ifdef _MSC_VER -# define GUM_NOINLINE __declspec (noinline) -#else -# define GUM_NOINLINE __attribute__ ((noinline)) -#endif + // SPSR* registers can be OR combined + ARM_SYSREG_SPSR_C = 1, + ARM_SYSREG_SPSR_X = 2, + ARM_SYSREG_SPSR_S = 4, + ARM_SYSREG_SPSR_F = 8, -#define GUM_ALIGN_POINTER(t, p, b) \ - ((t) GSIZE_TO_POINTER (((GPOINTER_TO_SIZE (p) + ((gsize) (b - 1))) & \ - ~((gsize) (b - 1))))) -#define GUM_ALIGN_SIZE(s, b) \ - ((((gsize) s) + ((gsize) (b - 1))) & ~((gsize) (b - 1))) + // CPSR* registers can be OR combined + ARM_SYSREG_CPSR_C = 16, + ARM_SYSREG_CPSR_X = 32, + ARM_SYSREG_CPSR_S = 64, + ARM_SYSREG_CPSR_F = 128, -#define GUM_FUNCPTR_TO_POINTER(f) (GSIZE_TO_POINTER (f)) -#define GUM_POINTER_TO_FUNCPTR(t, p) ((t) GPOINTER_TO_SIZE (p)) + // independent registers + ARM_SYSREG_APSR = 256, + ARM_SYSREG_APSR_G, + ARM_SYSREG_APSR_NZCVQ, + ARM_SYSREG_APSR_NZCVQG, -#define GUM_INT2_MASK 0x00000003U -#define GUM_INT3_MASK 0x00000007U -#define GUM_INT4_MASK 0x0000000fU -#define GUM_INT5_MASK 0x0000001fU -#define GUM_INT6_MASK 0x0000003fU -#define GUM_INT8_MASK 0x000000ffU -#define GUM_INT10_MASK 0x000003ffU -#define GUM_INT11_MASK 0x000007ffU -#define GUM_INT12_MASK 0x00000fffU -#define GUM_INT14_MASK 0x00003fffU -#define GUM_INT16_MASK 0x0000ffffU -#define GUM_INT18_MASK 0x0003ffffU -#define GUM_INT19_MASK 0x0007ffffU -#define GUM_INT24_MASK 0x00ffffffU -#define GUM_INT26_MASK 0x03ffffffU -#define GUM_INT28_MASK 0x0fffffffU -#define GUM_INT32_MASK 0xffffffffU - -#define GUM_IS_WITHIN_UINT7_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (0) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (127)) -#define GUM_IS_WITHIN_UINT8_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (0) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (255)) -#define GUM_IS_WITHIN_INT8_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-128) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (127)) -#define GUM_IS_WITHIN_INT11_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-1024) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (1023)) -#define GUM_IS_WITHIN_INT14_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-8192) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (8191)) -#define GUM_IS_WITHIN_INT16_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-32768) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (32767)) -#define GUM_IS_WITHIN_INT18_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-131072) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (131071)) -#define GUM_IS_WITHIN_INT19_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-262144) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (262143)) -#define GUM_IS_WITHIN_INT20_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-524288) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (524287)) -#define GUM_IS_WITHIN_INT21_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-1048576) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (1048575)) -#define GUM_IS_WITHIN_INT24_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-8388608) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (8388607)) -#define GUM_IS_WITHIN_INT26_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-33554432) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (33554431)) -#define GUM_IS_WITHIN_INT28_RANGE(i) \ - (((gint64) (i)) >= G_GINT64_CONSTANT (-134217728) && \ - ((gint64) (i)) <= G_GINT64_CONSTANT (134217727)) -#define GUM_IS_WITHIN_INT32_RANGE(i) \ - (((gint64) (i)) >= (gint64) G_MININT32 && \ - ((gint64) (i)) <= (gint64) G_MAXINT32) - -#ifdef G_NORETURN -# define GUM_NORETURN G_NORETURN -#else -# define GUM_NORETURN -#endif - -GUM_API GQuark gum_error_quark (void); - -GUM_API GUM_NORETURN void gum_panic (const gchar * format, ...) - G_ANALYZER_NORETURN; + ARM_SYSREG_IAPSR, + ARM_SYSREG_IAPSR_G, + ARM_SYSREG_IAPSR_NZCVQG, + ARM_SYSREG_IAPSR_NZCVQ, -GUM_API GumCpuFeatures gum_query_cpu_features (void); + ARM_SYSREG_EAPSR, + ARM_SYSREG_EAPSR_G, + ARM_SYSREG_EAPSR_NZCVQG, + ARM_SYSREG_EAPSR_NZCVQ, -GUM_API gpointer gum_cpu_context_get_nth_argument (GumCpuContext * self, - guint n); -GUM_API void gum_cpu_context_replace_nth_argument (GumCpuContext * self, - guint n, gpointer value); -GUM_API gpointer gum_cpu_context_get_return_value (GumCpuContext * self); -GUM_API void gum_cpu_context_replace_return_value (GumCpuContext * self, - gpointer value); + ARM_SYSREG_XPSR, + ARM_SYSREG_XPSR_G, + ARM_SYSREG_XPSR_NZCVQG, + ARM_SYSREG_XPSR_NZCVQ, -GUM_API GType gum_address_get_type (void) G_GNUC_CONST; + ARM_SYSREG_IPSR, + ARM_SYSREG_EPSR, + ARM_SYSREG_IEPSR, -G_END_DECLS + ARM_SYSREG_MSP, + ARM_SYSREG_PSP, + ARM_SYSREG_PRIMASK, + ARM_SYSREG_BASEPRI, + ARM_SYSREG_BASEPRI_MAX, + ARM_SYSREG_FAULTMASK, + ARM_SYSREG_CONTROL, + ARM_SYSREG_MSPLIM, + ARM_SYSREG_PSPLIM, + ARM_SYSREG_MSP_NS, + ARM_SYSREG_PSP_NS, + ARM_SYSREG_MSPLIM_NS, + ARM_SYSREG_PSPLIM_NS, + ARM_SYSREG_PRIMASK_NS, + ARM_SYSREG_BASEPRI_NS, + ARM_SYSREG_FAULTMASK_NS, + ARM_SYSREG_CONTROL_NS, + ARM_SYSREG_SP_NS, -#endif + // Banked Registers + ARM_SYSREG_R8_USR, + ARM_SYSREG_R9_USR, + ARM_SYSREG_R10_USR, + ARM_SYSREG_R11_USR, + ARM_SYSREG_R12_USR, + ARM_SYSREG_SP_USR, + ARM_SYSREG_LR_USR, + ARM_SYSREG_R8_FIQ, + ARM_SYSREG_R9_FIQ, + ARM_SYSREG_R10_FIQ, + ARM_SYSREG_R11_FIQ, + ARM_SYSREG_R12_FIQ, + ARM_SYSREG_SP_FIQ, + ARM_SYSREG_LR_FIQ, + ARM_SYSREG_LR_IRQ, + ARM_SYSREG_SP_IRQ, + ARM_SYSREG_LR_SVC, + ARM_SYSREG_SP_SVC, + ARM_SYSREG_LR_ABT, + ARM_SYSREG_SP_ABT, + ARM_SYSREG_LR_UND, + ARM_SYSREG_SP_UND, + ARM_SYSREG_LR_MON, + ARM_SYSREG_SP_MON, + ARM_SYSREG_ELR_HYP, + ARM_SYSREG_SP_HYP, -/* - * Copyright (C) 2016-2023 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ + ARM_SYSREG_SPSR_FIQ, + ARM_SYSREG_SPSR_IRQ, + ARM_SYSREG_SPSR_SVC, + ARM_SYSREG_SPSR_ABT, + ARM_SYSREG_SPSR_UND, + ARM_SYSREG_SPSR_MON, + ARM_SYSREG_SPSR_HYP, +} arm_sysreg; -#ifndef __GUM_API_RESOLVER_H__ -#define __GUM_API_RESOLVER_H__ +/// The memory barrier constants map directly to the 4-bit encoding of +/// the option field for Memory Barrier operations. +typedef enum arm_mem_barrier { + ARM_MB_INVALID = 0, + ARM_MB_RESERVED_0, + ARM_MB_OSHLD, + ARM_MB_OSHST, + ARM_MB_OSH, + ARM_MB_RESERVED_4, + ARM_MB_NSHLD, + ARM_MB_NSHST, + ARM_MB_NSH, + ARM_MB_RESERVED_8, + ARM_MB_ISHLD, + ARM_MB_ISHST, + ARM_MB_ISH, + ARM_MB_RESERVED_12, + ARM_MB_LD, + ARM_MB_ST, + ARM_MB_SY, +} arm_mem_barrier; +/// Operand type for instruction's operands +typedef enum arm_op_type { + ARM_OP_INVALID = 0, ///< = CS_OP_INVALID (Uninitialized). + ARM_OP_REG, ///< = CS_OP_REG (Register operand). + ARM_OP_IMM, ///< = CS_OP_IMM (Immediate operand). + ARM_OP_MEM, ///< = CS_OP_MEM (Memory operand). + ARM_OP_FP, ///< = CS_OP_FP (Floating-Point operand). + ARM_OP_CIMM = 64, ///< C-Immediate (coprocessor registers) + ARM_OP_PIMM, ///< P-Immediate (coprocessor registers) + ARM_OP_SETEND, ///< operand for SETEND instruction + ARM_OP_SYSREG, ///< MSR/MRS special register operand +} arm_op_type; -G_BEGIN_DECLS +/// Operand type for SETEND instruction +typedef enum arm_setend_type { + ARM_SETEND_INVALID = 0, ///< Uninitialized. + ARM_SETEND_BE, ///< BE operand. + ARM_SETEND_LE, ///< LE operand +} arm_setend_type; -#define GUM_API_SIZE_NONE -1 +typedef enum arm_cpsmode_type { + ARM_CPSMODE_INVALID = 0, + ARM_CPSMODE_IE = 2, + ARM_CPSMODE_ID = 3 +} arm_cpsmode_type; -#define GUM_TYPE_API_RESOLVER (gum_api_resolver_get_type ()) -G_DECLARE_INTERFACE (GumApiResolver, gum_api_resolver, GUM, API_RESOLVER, - GObject) +/// Operand type for SETEND instruction +typedef enum arm_cpsflag_type { + ARM_CPSFLAG_INVALID = 0, + ARM_CPSFLAG_F = 1, + ARM_CPSFLAG_I = 2, + ARM_CPSFLAG_A = 4, + ARM_CPSFLAG_NONE = 16, ///< no flag +} arm_cpsflag_type; -typedef struct _GumApiDetails GumApiDetails; +/// Data type for elements of vector instructions. +typedef enum arm_vectordata_type { + ARM_VECTORDATA_INVALID = 0, -typedef gboolean (* GumFoundApiFunc) (const GumApiDetails * details, - gpointer user_data); + // Integer type + ARM_VECTORDATA_I8, + ARM_VECTORDATA_I16, + ARM_VECTORDATA_I32, + ARM_VECTORDATA_I64, -struct _GumApiResolverInterface -{ - GTypeInterface parent; + // Signed integer type + ARM_VECTORDATA_S8, + ARM_VECTORDATA_S16, + ARM_VECTORDATA_S32, + ARM_VECTORDATA_S64, - void (* enumerate_matches) (GumApiResolver * self, const gchar * query, - GumFoundApiFunc func, gpointer user_data, GError ** error); -}; + // Unsigned integer type + ARM_VECTORDATA_U8, + ARM_VECTORDATA_U16, + ARM_VECTORDATA_U32, + ARM_VECTORDATA_U64, -struct _GumApiDetails -{ - const gchar * name; - GumAddress address; - gssize size; -}; + // Data type for VMUL/VMULL + ARM_VECTORDATA_P8, -GUM_API GumApiResolver * gum_api_resolver_make (const gchar * type); + // Floating type + ARM_VECTORDATA_F16, + ARM_VECTORDATA_F32, + ARM_VECTORDATA_F64, -GUM_API void gum_api_resolver_enumerate_matches (GumApiResolver * self, - const gchar * query, GumFoundApiFunc func, gpointer user_data, - GError ** error); + // Convert float <-> float + ARM_VECTORDATA_F16F64, // f16.f64 + ARM_VECTORDATA_F64F16, // f64.f16 + ARM_VECTORDATA_F32F16, // f32.f16 + ARM_VECTORDATA_F16F32, // f32.f16 + ARM_VECTORDATA_F64F32, // f64.f32 + ARM_VECTORDATA_F32F64, // f32.f64 -G_END_DECLS + // Convert integer <-> float + ARM_VECTORDATA_S32F32, // s32.f32 + ARM_VECTORDATA_U32F32, // u32.f32 + ARM_VECTORDATA_F32S32, // f32.s32 + ARM_VECTORDATA_F32U32, // f32.u32 + ARM_VECTORDATA_F64S16, // f64.s16 + ARM_VECTORDATA_F32S16, // f32.s16 + ARM_VECTORDATA_F64S32, // f64.s32 + ARM_VECTORDATA_S16F64, // s16.f64 + ARM_VECTORDATA_S16F32, // s16.f64 + ARM_VECTORDATA_S32F64, // s32.f64 + ARM_VECTORDATA_U16F64, // u16.f64 + ARM_VECTORDATA_U16F32, // u16.f32 + ARM_VECTORDATA_U32F64, // u32.f64 + ARM_VECTORDATA_F64U16, // f64.u16 + ARM_VECTORDATA_F32U16, // f32.u16 + ARM_VECTORDATA_F64U32, // f64.u32 + ARM_VECTORDATA_F16U16, // f16.u16 + ARM_VECTORDATA_U16F16, // u16.f16 + ARM_VECTORDATA_F16U32, // f16.u32 + ARM_VECTORDATA_U32F16, // u32.f16 +} arm_vectordata_type; -#endif -/* - * Copyright (C) 2008-2022 Ole André Vadla Ravnås - * Copyright (C) 2021 Francesco Tamagni - * - * Licence: wxWindows Library Licence, Version 3.1 - */ +/// ARM registers +typedef enum arm_reg { + ARM_REG_INVALID = 0, + ARM_REG_APSR, + ARM_REG_APSR_NZCV, + ARM_REG_CPSR, + ARM_REG_FPEXC, + ARM_REG_FPINST, + ARM_REG_FPSCR, + ARM_REG_FPSCR_NZCV, + ARM_REG_FPSID, + ARM_REG_ITSTATE, + ARM_REG_LR, + ARM_REG_PC, + ARM_REG_SP, + ARM_REG_SPSR, + ARM_REG_D0, + ARM_REG_D1, + ARM_REG_D2, + ARM_REG_D3, + ARM_REG_D4, + ARM_REG_D5, + ARM_REG_D6, + ARM_REG_D7, + ARM_REG_D8, + ARM_REG_D9, + ARM_REG_D10, + ARM_REG_D11, + ARM_REG_D12, + ARM_REG_D13, + ARM_REG_D14, + ARM_REG_D15, + ARM_REG_D16, + ARM_REG_D17, + ARM_REG_D18, + ARM_REG_D19, + ARM_REG_D20, + ARM_REG_D21, + ARM_REG_D22, + ARM_REG_D23, + ARM_REG_D24, + ARM_REG_D25, + ARM_REG_D26, + ARM_REG_D27, + ARM_REG_D28, + ARM_REG_D29, + ARM_REG_D30, + ARM_REG_D31, + ARM_REG_FPINST2, + ARM_REG_MVFR0, + ARM_REG_MVFR1, + ARM_REG_MVFR2, + ARM_REG_Q0, + ARM_REG_Q1, + ARM_REG_Q2, + ARM_REG_Q3, + ARM_REG_Q4, + ARM_REG_Q5, + ARM_REG_Q6, + ARM_REG_Q7, + ARM_REG_Q8, + ARM_REG_Q9, + ARM_REG_Q10, + ARM_REG_Q11, + ARM_REG_Q12, + ARM_REG_Q13, + ARM_REG_Q14, + ARM_REG_Q15, + ARM_REG_R0, + ARM_REG_R1, + ARM_REG_R2, + ARM_REG_R3, + ARM_REG_R4, + ARM_REG_R5, + ARM_REG_R6, + ARM_REG_R7, + ARM_REG_R8, + ARM_REG_R9, + ARM_REG_R10, + ARM_REG_R11, + ARM_REG_R12, + ARM_REG_S0, + ARM_REG_S1, + ARM_REG_S2, + ARM_REG_S3, + ARM_REG_S4, + ARM_REG_S5, + ARM_REG_S6, + ARM_REG_S7, + ARM_REG_S8, + ARM_REG_S9, + ARM_REG_S10, + ARM_REG_S11, + ARM_REG_S12, + ARM_REG_S13, + ARM_REG_S14, + ARM_REG_S15, + ARM_REG_S16, + ARM_REG_S17, + ARM_REG_S18, + ARM_REG_S19, + ARM_REG_S20, + ARM_REG_S21, + ARM_REG_S22, + ARM_REG_S23, + ARM_REG_S24, + ARM_REG_S25, + ARM_REG_S26, + ARM_REG_S27, + ARM_REG_S28, + ARM_REG_S29, + ARM_REG_S30, + ARM_REG_S31, -#ifndef __GUM_BACKTRACER_H__ -#define __GUM_BACKTRACER_H__ + ARM_REG_ENDING, // <-- mark the end of the list or registers -/* - * Copyright (C) 2008-2010 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ + // alias registers + ARM_REG_R13 = ARM_REG_SP, + ARM_REG_R14 = ARM_REG_LR, + ARM_REG_R15 = ARM_REG_PC, -#ifndef __GUM_RETURN_ADDRESS_H__ -#define __GUM_RETURN_ADDRESS_H__ + ARM_REG_SB = ARM_REG_R9, + ARM_REG_SL = ARM_REG_R10, + ARM_REG_FP = ARM_REG_R11, + ARM_REG_IP = ARM_REG_R12, +} arm_reg; +/// Instruction's operand referring to memory +/// This is associated with ARM_OP_MEM operand type above +typedef struct arm_op_mem { + arm_reg base; ///< base register + arm_reg index; ///< index register + int scale; ///< scale for index register (can be 1, or -1) + int disp; ///< displacement/offset value + /// left-shift on index register, or 0 if irrelevant + /// NOTE: this value can also be fetched via operand.shift.value + int lshift; +} arm_op_mem; -typedef struct _GumReturnAddressDetails GumReturnAddressDetails; -typedef gpointer GumReturnAddress; -typedef struct _GumReturnAddressArray GumReturnAddressArray; +/// Instruction operand +typedef struct cs_arm_op { + int vector_index; ///< Vector Index for some vector operands (or -1 if irrelevant) -struct _GumReturnAddressDetails -{ - GumReturnAddress address; - gchar module_name[GUM_MAX_PATH + 1]; - gchar function_name[GUM_MAX_SYMBOL_NAME + 1]; - gchar file_name[GUM_MAX_PATH + 1]; - guint line_number; - guint column; -}; + struct { + arm_shifter type; + unsigned int value; + } shift; -struct _GumReturnAddressArray -{ - guint len; - GumReturnAddress items[GUM_MAX_BACKTRACE_DEPTH]; -}; + arm_op_type type; ///< operand type -G_BEGIN_DECLS + union { + int reg; ///< register value for REG/SYSREG operand + int32_t imm; ///< immediate value for C-IMM, P-IMM or IMM operand + double fp; ///< floating point value for FP operand + arm_op_mem mem; ///< base/index/scale/disp value for MEM operand + arm_setend_type setend; ///< SETEND instruction's operand type + }; -GUM_API gboolean gum_return_address_details_from_address ( - GumReturnAddress address, GumReturnAddressDetails * details); + /// in some instructions, an operand can be subtracted or added to + /// the base register, + /// if TRUE, this operand is subtracted. otherwise, it is added. + bool subtracted; -GUM_API gboolean gum_return_address_array_is_equal ( - const GumReturnAddressArray * array1, - const GumReturnAddressArray * array2); + /// How is this operand accessed? (READ, WRITE or READ|WRITE) + /// This field is combined of cs_ac_type. + /// NOTE: this field is irrelevant if engine is compiled in DIET mode. + uint8_t access; -G_END_DECLS + /// Neon lane index for NEON instructions (or -1 if irrelevant) + int8_t neon_lane; +} cs_arm_op; -#endif +/// Instruction structure +typedef struct cs_arm { + bool usermode; ///< User-mode registers to be loaded (for LDM/STM instructions) + int vector_size; ///< Scalar size for vector instructions + arm_vectordata_type vector_data; ///< Data type for elements of vector instructions + arm_cpsmode_type cps_mode; ///< CPS mode for CPS instruction + arm_cpsflag_type cps_flag; ///< CPS mode for CPS instruction + arm_cc cc; ///< conditional code for this insn + bool update_flags; ///< does this insn update flags? + bool writeback; ///< does this insn write-back? + bool post_index; ///< only set if writeback is 'True', if 'False' pre-index, otherwise post. + arm_mem_barrier mem_barrier; ///< Option for some memory barrier instructions -G_BEGIN_DECLS + /// Number of operands of this instruction, + /// or 0 when instruction has no operand. + uint8_t op_count; -#define GUM_TYPE_BACKTRACER (gum_backtracer_get_type ()) -G_DECLARE_INTERFACE (GumBacktracer, gum_backtracer, GUM, BACKTRACER, GObject) + cs_arm_op operands[36]; ///< operands for this instruction. +} cs_arm; -struct _GumBacktracerInterface -{ - GTypeInterface parent; +/// ARM instruction +typedef enum arm_insn { + ARM_INS_INVALID = 0, - void (* generate) (GumBacktracer * self, const GumCpuContext * cpu_context, - GumReturnAddressArray * return_addresses, guint limit); -}; - -GUM_API GumBacktracer * gum_backtracer_make_accurate (void); -GUM_API GumBacktracer * gum_backtracer_make_fuzzy (void); - -GUM_API void gum_backtracer_generate (GumBacktracer * self, - const GumCpuContext * cpu_context, - GumReturnAddressArray * return_addresses); -GUM_API void gum_backtracer_generate_with_limit (GumBacktracer * self, - const GumCpuContext * cpu_context, - GumReturnAddressArray * return_addresses, guint limit); - -G_END_DECLS + ARM_INS_ADC, + ARM_INS_ADD, + ARM_INS_ADDW, + ARM_INS_ADR, + ARM_INS_AESD, + ARM_INS_AESE, + ARM_INS_AESIMC, + ARM_INS_AESMC, + ARM_INS_AND, + ARM_INS_ASR, + ARM_INS_B, + ARM_INS_BFC, + ARM_INS_BFI, + ARM_INS_BIC, + ARM_INS_BKPT, + ARM_INS_BL, + ARM_INS_BLX, + ARM_INS_BLXNS, + ARM_INS_BX, + ARM_INS_BXJ, + ARM_INS_BXNS, + ARM_INS_CBNZ, + ARM_INS_CBZ, + ARM_INS_CDP, + ARM_INS_CDP2, + ARM_INS_CLREX, + ARM_INS_CLZ, + ARM_INS_CMN, + ARM_INS_CMP, + ARM_INS_CPS, + ARM_INS_CRC32B, + ARM_INS_CRC32CB, + ARM_INS_CRC32CH, + ARM_INS_CRC32CW, + ARM_INS_CRC32H, + ARM_INS_CRC32W, + ARM_INS_CSDB, + ARM_INS_DBG, + ARM_INS_DCPS1, + ARM_INS_DCPS2, + ARM_INS_DCPS3, + ARM_INS_DFB, + ARM_INS_DMB, + ARM_INS_DSB, + ARM_INS_EOR, + ARM_INS_ERET, + ARM_INS_ESB, + ARM_INS_FADDD, + ARM_INS_FADDS, + ARM_INS_FCMPZD, + ARM_INS_FCMPZS, + ARM_INS_FCONSTD, + ARM_INS_FCONSTS, + ARM_INS_FLDMDBX, + ARM_INS_FLDMIAX, + ARM_INS_FMDHR, + ARM_INS_FMDLR, + ARM_INS_FMSTAT, + ARM_INS_FSTMDBX, + ARM_INS_FSTMIAX, + ARM_INS_FSUBD, + ARM_INS_FSUBS, + ARM_INS_HINT, + ARM_INS_HLT, + ARM_INS_HVC, + ARM_INS_ISB, + ARM_INS_IT, + ARM_INS_LDA, + ARM_INS_LDAB, + ARM_INS_LDAEX, + ARM_INS_LDAEXB, + ARM_INS_LDAEXD, + ARM_INS_LDAEXH, + ARM_INS_LDAH, + ARM_INS_LDC, + ARM_INS_LDC2, + ARM_INS_LDC2L, + ARM_INS_LDCL, + ARM_INS_LDM, + ARM_INS_LDMDA, + ARM_INS_LDMDB, + ARM_INS_LDMIB, + ARM_INS_LDR, + ARM_INS_LDRB, + ARM_INS_LDRBT, + ARM_INS_LDRD, + ARM_INS_LDREX, + ARM_INS_LDREXB, + ARM_INS_LDREXD, + ARM_INS_LDREXH, + ARM_INS_LDRH, + ARM_INS_LDRHT, + ARM_INS_LDRSB, + ARM_INS_LDRSBT, + ARM_INS_LDRSH, + ARM_INS_LDRSHT, + ARM_INS_LDRT, + ARM_INS_LSL, + ARM_INS_LSR, + ARM_INS_MCR, + ARM_INS_MCR2, + ARM_INS_MCRR, + ARM_INS_MCRR2, + ARM_INS_MLA, + ARM_INS_MLS, + ARM_INS_MOV, + ARM_INS_MOVS, + ARM_INS_MOVT, + ARM_INS_MOVW, + ARM_INS_MRC, + ARM_INS_MRC2, + ARM_INS_MRRC, + ARM_INS_MRRC2, + ARM_INS_MRS, + ARM_INS_MSR, + ARM_INS_MUL, + ARM_INS_MVN, + ARM_INS_NEG, + ARM_INS_NOP, + ARM_INS_ORN, + ARM_INS_ORR, + ARM_INS_PKHBT, + ARM_INS_PKHTB, + ARM_INS_PLD, + ARM_INS_PLDW, + ARM_INS_PLI, + ARM_INS_POP, + ARM_INS_PUSH, + ARM_INS_QADD, + ARM_INS_QADD16, + ARM_INS_QADD8, + ARM_INS_QASX, + ARM_INS_QDADD, + ARM_INS_QDSUB, + ARM_INS_QSAX, + ARM_INS_QSUB, + ARM_INS_QSUB16, + ARM_INS_QSUB8, + ARM_INS_RBIT, + ARM_INS_REV, + ARM_INS_REV16, + ARM_INS_REVSH, + ARM_INS_RFEDA, + ARM_INS_RFEDB, + ARM_INS_RFEIA, + ARM_INS_RFEIB, + ARM_INS_ROR, + ARM_INS_RRX, + ARM_INS_RSB, + ARM_INS_RSC, + ARM_INS_SADD16, + ARM_INS_SADD8, + ARM_INS_SASX, + ARM_INS_SBC, + ARM_INS_SBFX, + ARM_INS_SDIV, + ARM_INS_SEL, + ARM_INS_SETEND, + ARM_INS_SETPAN, + ARM_INS_SEV, + ARM_INS_SEVL, + ARM_INS_SG, + ARM_INS_SHA1C, + ARM_INS_SHA1H, + ARM_INS_SHA1M, + ARM_INS_SHA1P, + ARM_INS_SHA1SU0, + ARM_INS_SHA1SU1, + ARM_INS_SHA256H, + ARM_INS_SHA256H2, + ARM_INS_SHA256SU0, + ARM_INS_SHA256SU1, + ARM_INS_SHADD16, + ARM_INS_SHADD8, + ARM_INS_SHASX, + ARM_INS_SHSAX, + ARM_INS_SHSUB16, + ARM_INS_SHSUB8, + ARM_INS_SMC, + ARM_INS_SMLABB, + ARM_INS_SMLABT, + ARM_INS_SMLAD, + ARM_INS_SMLADX, + ARM_INS_SMLAL, + ARM_INS_SMLALBB, + ARM_INS_SMLALBT, + ARM_INS_SMLALD, + ARM_INS_SMLALDX, + ARM_INS_SMLALTB, + ARM_INS_SMLALTT, + ARM_INS_SMLATB, + ARM_INS_SMLATT, + ARM_INS_SMLAWB, + ARM_INS_SMLAWT, + ARM_INS_SMLSD, + ARM_INS_SMLSDX, + ARM_INS_SMLSLD, + ARM_INS_SMLSLDX, + ARM_INS_SMMLA, + ARM_INS_SMMLAR, + ARM_INS_SMMLS, + ARM_INS_SMMLSR, + ARM_INS_SMMUL, + ARM_INS_SMMULR, + ARM_INS_SMUAD, + ARM_INS_SMUADX, + ARM_INS_SMULBB, + ARM_INS_SMULBT, + ARM_INS_SMULL, + ARM_INS_SMULTB, + ARM_INS_SMULTT, + ARM_INS_SMULWB, + ARM_INS_SMULWT, + ARM_INS_SMUSD, + ARM_INS_SMUSDX, + ARM_INS_SRSDA, + ARM_INS_SRSDB, + ARM_INS_SRSIA, + ARM_INS_SRSIB, + ARM_INS_SSAT, + ARM_INS_SSAT16, + ARM_INS_SSAX, + ARM_INS_SSUB16, + ARM_INS_SSUB8, + ARM_INS_STC, + ARM_INS_STC2, + ARM_INS_STC2L, + ARM_INS_STCL, + ARM_INS_STL, + ARM_INS_STLB, + ARM_INS_STLEX, + ARM_INS_STLEXB, + ARM_INS_STLEXD, + ARM_INS_STLEXH, + ARM_INS_STLH, + ARM_INS_STM, + ARM_INS_STMDA, + ARM_INS_STMDB, + ARM_INS_STMIB, + ARM_INS_STR, + ARM_INS_STRB, + ARM_INS_STRBT, + ARM_INS_STRD, + ARM_INS_STREX, + ARM_INS_STREXB, + ARM_INS_STREXD, + ARM_INS_STREXH, + ARM_INS_STRH, + ARM_INS_STRHT, + ARM_INS_STRT, + ARM_INS_SUB, + ARM_INS_SUBS, + ARM_INS_SUBW, + ARM_INS_SVC, + ARM_INS_SWP, + ARM_INS_SWPB, + ARM_INS_SXTAB, + ARM_INS_SXTAB16, + ARM_INS_SXTAH, + ARM_INS_SXTB, + ARM_INS_SXTB16, + ARM_INS_SXTH, + ARM_INS_TBB, + ARM_INS_TBH, + ARM_INS_TEQ, + ARM_INS_TRAP, + ARM_INS_TSB, + ARM_INS_TST, + ARM_INS_TT, + ARM_INS_TTA, + ARM_INS_TTAT, + ARM_INS_TTT, + ARM_INS_UADD16, + ARM_INS_UADD8, + ARM_INS_UASX, + ARM_INS_UBFX, + ARM_INS_UDF, + ARM_INS_UDIV, + ARM_INS_UHADD16, + ARM_INS_UHADD8, + ARM_INS_UHASX, + ARM_INS_UHSAX, + ARM_INS_UHSUB16, + ARM_INS_UHSUB8, + ARM_INS_UMAAL, + ARM_INS_UMLAL, + ARM_INS_UMULL, + ARM_INS_UQADD16, + ARM_INS_UQADD8, + ARM_INS_UQASX, + ARM_INS_UQSAX, + ARM_INS_UQSUB16, + ARM_INS_UQSUB8, + ARM_INS_USAD8, + ARM_INS_USADA8, + ARM_INS_USAT, + ARM_INS_USAT16, + ARM_INS_USAX, + ARM_INS_USUB16, + ARM_INS_USUB8, + ARM_INS_UXTAB, + ARM_INS_UXTAB16, + ARM_INS_UXTAH, + ARM_INS_UXTB, + ARM_INS_UXTB16, + ARM_INS_UXTH, + ARM_INS_VABA, + ARM_INS_VABAL, + ARM_INS_VABD, + ARM_INS_VABDL, + ARM_INS_VABS, + ARM_INS_VACGE, + ARM_INS_VACGT, + ARM_INS_VACLE, + ARM_INS_VACLT, + ARM_INS_VADD, + ARM_INS_VADDHN, + ARM_INS_VADDL, + ARM_INS_VADDW, + ARM_INS_VAND, + ARM_INS_VBIC, + ARM_INS_VBIF, + ARM_INS_VBIT, + ARM_INS_VBSL, + ARM_INS_VCADD, + ARM_INS_VCEQ, + ARM_INS_VCGE, + ARM_INS_VCGT, + ARM_INS_VCLE, + ARM_INS_VCLS, + ARM_INS_VCLT, + ARM_INS_VCLZ, + ARM_INS_VCMLA, + ARM_INS_VCMP, + ARM_INS_VCMPE, + ARM_INS_VCNT, + ARM_INS_VCVT, + ARM_INS_VCVTA, + ARM_INS_VCVTB, + ARM_INS_VCVTM, + ARM_INS_VCVTN, + ARM_INS_VCVTP, + ARM_INS_VCVTR, + ARM_INS_VCVTT, + ARM_INS_VDIV, + ARM_INS_VDUP, + ARM_INS_VEOR, + ARM_INS_VEXT, + ARM_INS_VFMA, + ARM_INS_VFMS, + ARM_INS_VFNMA, + ARM_INS_VFNMS, + ARM_INS_VHADD, + ARM_INS_VHSUB, + ARM_INS_VINS, + ARM_INS_VJCVT, + ARM_INS_VLD1, + ARM_INS_VLD2, + ARM_INS_VLD3, + ARM_INS_VLD4, + ARM_INS_VLDMDB, + ARM_INS_VLDMIA, + ARM_INS_VLDR, + ARM_INS_VLLDM, + ARM_INS_VLSTM, + ARM_INS_VMAX, + ARM_INS_VMAXNM, + ARM_INS_VMIN, + ARM_INS_VMINNM, + ARM_INS_VMLA, + ARM_INS_VMLAL, + ARM_INS_VMLS, + ARM_INS_VMLSL, + ARM_INS_VMOV, + ARM_INS_VMOVL, + ARM_INS_VMOVN, + ARM_INS_VMOVX, + ARM_INS_VMRS, + ARM_INS_VMSR, + ARM_INS_VMUL, + ARM_INS_VMULL, + ARM_INS_VMVN, + ARM_INS_VNEG, + ARM_INS_VNMLA, + ARM_INS_VNMLS, + ARM_INS_VNMUL, + ARM_INS_VORN, + ARM_INS_VORR, + ARM_INS_VPADAL, + ARM_INS_VPADD, + ARM_INS_VPADDL, + ARM_INS_VPMAX, + ARM_INS_VPMIN, + ARM_INS_VPOP, + ARM_INS_VPUSH, + ARM_INS_VQABS, + ARM_INS_VQADD, + ARM_INS_VQDMLAL, + ARM_INS_VQDMLSL, + ARM_INS_VQDMULH, + ARM_INS_VQDMULL, + ARM_INS_VQMOVN, + ARM_INS_VQMOVUN, + ARM_INS_VQNEG, + ARM_INS_VQRDMLAH, + ARM_INS_VQRDMLSH, + ARM_INS_VQRDMULH, + ARM_INS_VQRSHL, + ARM_INS_VQRSHRN, + ARM_INS_VQRSHRUN, + ARM_INS_VQSHL, + ARM_INS_VQSHLU, + ARM_INS_VQSHRN, + ARM_INS_VQSHRUN, + ARM_INS_VQSUB, + ARM_INS_VRADDHN, + ARM_INS_VRECPE, + ARM_INS_VRECPS, + ARM_INS_VREV16, + ARM_INS_VREV32, + ARM_INS_VREV64, + ARM_INS_VRHADD, + ARM_INS_VRINTA, + ARM_INS_VRINTM, + ARM_INS_VRINTN, + ARM_INS_VRINTP, + ARM_INS_VRINTR, + ARM_INS_VRINTX, + ARM_INS_VRINTZ, + ARM_INS_VRSHL, + ARM_INS_VRSHR, + ARM_INS_VRSHRN, + ARM_INS_VRSQRTE, + ARM_INS_VRSQRTS, + ARM_INS_VRSRA, + ARM_INS_VRSUBHN, + ARM_INS_VSDOT, + ARM_INS_VSELEQ, + ARM_INS_VSELGE, + ARM_INS_VSELGT, + ARM_INS_VSELVS, + ARM_INS_VSHL, + ARM_INS_VSHLL, + ARM_INS_VSHR, + ARM_INS_VSHRN, + ARM_INS_VSLI, + ARM_INS_VSQRT, + ARM_INS_VSRA, + ARM_INS_VSRI, + ARM_INS_VST1, + ARM_INS_VST2, + ARM_INS_VST3, + ARM_INS_VST4, + ARM_INS_VSTMDB, + ARM_INS_VSTMIA, + ARM_INS_VSTR, + ARM_INS_VSUB, + ARM_INS_VSUBHN, + ARM_INS_VSUBL, + ARM_INS_VSUBW, + ARM_INS_VSWP, + ARM_INS_VTBL, + ARM_INS_VTBX, + ARM_INS_VTRN, + ARM_INS_VTST, + ARM_INS_VUDOT, + ARM_INS_VUZP, + ARM_INS_VZIP, + ARM_INS_WFE, + ARM_INS_WFI, + ARM_INS_YIELD, -#endif -/* - * Copyright (C) 2017-2023 Ole André Vadla Ravnås - * Copyright (C) 2024 Francesco Tamagni - * - * Licence: wxWindows Library Licence, Version 3.1 - */ + ARM_INS_ENDING, // <-- mark the end of the list of instructions +} arm_insn; -#ifndef __GUM_CLOAK_H__ -#define __GUM_CLOAK_H__ +/// Group of ARM instructions +typedef enum arm_insn_group { + ARM_GRP_INVALID = 0, ///< = CS_GRP_INVALID -/* - * Copyright (C) 2008-2024 Ole André Vadla Ravnås - * Copyright (C) 2008 Christian Berentsen - * - * Licence: wxWindows Library Licence, Version 3.1 - */ + // Generic groups + // all jump instructions (conditional+direct+indirect jumps) + ARM_GRP_JUMP, ///< = CS_GRP_JUMP + ARM_GRP_CALL, ///< = CS_GRP_CALL + ARM_GRP_INT = 4, ///< = CS_GRP_INT + ARM_GRP_PRIVILEGE = 6, ///< = CS_GRP_PRIVILEGE + ARM_GRP_BRANCH_RELATIVE, ///< = CS_GRP_BRANCH_RELATIVE -#ifndef __GUM_MEMORY_H__ -#define __GUM_MEMORY_H__ - - -#define GUM_TYPE_MATCH_PATTERN (gum_match_pattern_get_type ()) -#define GUM_TYPE_MEMORY_RANGE (gum_memory_range_get_type ()) -#define GUM_MEMORY_RANGE_INCLUDES(r, a) ((a) >= (r)->base_address && \ - (a) < ((r)->base_address + (r)->size)) - -#define GUM_PAGE_RW ((GumPageProtection) (GUM_PAGE_READ | GUM_PAGE_WRITE)) -#define GUM_PAGE_RX ((GumPageProtection) (GUM_PAGE_READ | GUM_PAGE_EXECUTE)) -#define GUM_PAGE_RWX ((GumPageProtection) (GUM_PAGE_READ | GUM_PAGE_WRITE | \ - GUM_PAGE_EXECUTE)) - -G_BEGIN_DECLS - -typedef guint GumPtrauthSupport; -typedef guint GumRwxSupport; -typedef guint GumMemoryOperation; -typedef guint GumPageProtection; -typedef struct _GumAddressSpec GumAddressSpec; -typedef struct _GumRangeDetails GumRangeDetails; -typedef struct _GumMemoryRange GumMemoryRange; -typedef struct _GumFileMapping GumFileMapping; -typedef struct _GumMatchPattern GumMatchPattern; - -typedef gboolean (* GumMemoryIsNearFunc) (gpointer memory, gpointer address); - -enum _GumPtrauthSupport -{ - GUM_PTRAUTH_INVALID, - GUM_PTRAUTH_UNSUPPORTED, - GUM_PTRAUTH_SUPPORTED -}; - -enum _GumRwxSupport -{ - GUM_RWX_NONE, - GUM_RWX_ALLOCATIONS_ONLY, - GUM_RWX_FULL -}; - -enum _GumMemoryOperation -{ - GUM_MEMOP_INVALID, - GUM_MEMOP_READ, - GUM_MEMOP_WRITE, - GUM_MEMOP_EXECUTE -}; - -enum _GumPageProtection -{ - GUM_PAGE_NO_ACCESS = 0, - GUM_PAGE_READ = (1 << 0), - GUM_PAGE_WRITE = (1 << 1), - GUM_PAGE_EXECUTE = (1 << 2), -}; - -struct _GumAddressSpec -{ - gpointer near_address; - gsize max_distance; -}; - -struct _GumRangeDetails -{ - const GumMemoryRange * range; - GumPageProtection protection; - const GumFileMapping * file; -}; - -struct _GumMemoryRange -{ - GumAddress base_address; - gsize size; -}; - -struct _GumFileMapping -{ - const gchar * path; - guint64 offset; - gsize size; -}; - -typedef gboolean (* GumFoundRangeFunc) (const GumRangeDetails * details, - gpointer user_data); -typedef void (* GumMemoryPatchApplyFunc) (gpointer mem, gpointer user_data); -typedef gboolean (* GumMemoryScanMatchFunc) (GumAddress address, gsize size, - gpointer user_data); - -GUM_API void gum_internal_heap_ref (void); -GUM_API void gum_internal_heap_unref (void); - -GUM_API gpointer gum_sign_code_pointer (gpointer value); -GUM_API gpointer gum_strip_code_pointer (gpointer value); -GUM_API GumAddress gum_sign_code_address (GumAddress value); -GUM_API GumAddress gum_strip_code_address (GumAddress value); -GUM_API GumPtrauthSupport gum_query_ptrauth_support (void); -GUM_API guint gum_query_page_size (void); -GUM_API gboolean gum_query_is_rwx_supported (void); -GUM_API GumRwxSupport gum_query_rwx_support (void); -GUM_API gboolean gum_memory_is_readable (gconstpointer address, gsize len); -GUM_API gboolean gum_memory_query_protection (gconstpointer address, - GumPageProtection * prot); -GUM_API guint8 * gum_memory_read (gconstpointer address, gsize len, - gsize * n_bytes_read); -GUM_API gboolean gum_memory_write (gpointer address, const guint8 * bytes, - gsize len); -GUM_API gboolean gum_memory_patch_code (gpointer address, gsize size, - GumMemoryPatchApplyFunc apply, gpointer apply_data); -GUM_API gboolean gum_memory_mark_code (gpointer address, gsize size); - -GUM_API void gum_memory_scan (const GumMemoryRange * range, - const GumMatchPattern * pattern, GumMemoryScanMatchFunc func, - gpointer user_data); - -GUM_API GType gum_match_pattern_get_type (void) G_GNUC_CONST; -GUM_API GumMatchPattern * gum_match_pattern_new_from_string ( - const gchar * pattern_str); -GUM_API GumMatchPattern * gum_match_pattern_ref (GumMatchPattern * pattern); -GUM_API void gum_match_pattern_unref (GumMatchPattern * pattern); -GUM_API guint gum_match_pattern_get_size (const GumMatchPattern * pattern); -GUM_API GPtrArray * gum_match_pattern_get_tokens ( - const GumMatchPattern * pattern); - -GUM_API void gum_ensure_code_readable (gconstpointer address, gsize size); - -GUM_API void gum_mprotect (gpointer address, gsize size, - GumPageProtection prot); -GUM_API gboolean gum_try_mprotect (gpointer address, gsize size, - GumPageProtection prot); - -GUM_API void gum_clear_cache (gpointer address, gsize size); - -#define gum_new(struct_type, n_structs) \ - ((struct_type *) gum_malloc (n_structs * sizeof (struct_type))) -#define gum_new0(struct_type, n_structs) \ - ((struct_type *) gum_malloc0 (n_structs * sizeof (struct_type))) - -GUM_API guint gum_peek_private_memory_usage (void); - -GUM_API gpointer gum_malloc (gsize size); -GUM_API gpointer gum_malloc0 (gsize size); -GUM_API gsize gum_malloc_usable_size (gconstpointer mem); -GUM_API gpointer gum_calloc (gsize count, gsize size); -GUM_API gpointer gum_realloc (gpointer mem, gsize size); -GUM_API gpointer gum_memalign (gsize alignment, gsize size); -GUM_API gpointer gum_memdup (gconstpointer mem, gsize byte_size); -GUM_API void gum_free (gpointer mem); - -GUM_API gpointer gum_alloc_n_pages (guint n_pages, GumPageProtection prot); -GUM_API gpointer gum_try_alloc_n_pages (guint n_pages, GumPageProtection prot); -GUM_API gpointer gum_alloc_n_pages_near (guint n_pages, GumPageProtection prot, - const GumAddressSpec * spec); -GUM_API gpointer gum_try_alloc_n_pages_near (guint n_pages, - GumPageProtection prot, const GumAddressSpec * spec); -GUM_API void gum_query_page_allocation_range (gconstpointer mem, guint size, - GumMemoryRange * range); -GUM_API void gum_free_pages (gpointer mem); - -GUM_API gpointer gum_memory_allocate (gpointer address, gsize size, - gsize alignment, GumPageProtection prot); -GUM_API gpointer gum_memory_allocate_near (const GumAddressSpec * spec, - gsize size, gsize alignment, GumPageProtection prot); -GUM_API gboolean gum_memory_free (gpointer address, gsize size); -GUM_API gboolean gum_memory_release (gpointer address, gsize size); -GUM_API gboolean gum_memory_recommit (gpointer address, gsize size, - GumPageProtection prot); -GUM_API gboolean gum_memory_discard (gpointer address, gsize size); -GUM_API gboolean gum_memory_decommit (gpointer address, gsize size); - -GUM_API gboolean gum_address_spec_is_satisfied_by (const GumAddressSpec * spec, - gconstpointer address); - -GUM_API GType gum_memory_range_get_type (void) G_GNUC_CONST; -GUM_API GumMemoryRange * gum_memory_range_copy (const GumMemoryRange * range); -GUM_API void gum_memory_range_free (GumMemoryRange * range); + // Architecture-specific groups + ARM_GRP_CRYPTO = 128, + ARM_GRP_DATABARRIER, + ARM_GRP_DIVIDE, + ARM_GRP_FPARMV8, + ARM_GRP_MULTPRO, + ARM_GRP_NEON, + ARM_GRP_T2EXTRACTPACK, + ARM_GRP_THUMB2DSP, + ARM_GRP_TRUSTZONE, + ARM_GRP_V4T, + ARM_GRP_V5T, + ARM_GRP_V5TE, + ARM_GRP_V6, + ARM_GRP_V6T2, + ARM_GRP_V7, + ARM_GRP_V8, + ARM_GRP_VFP2, + ARM_GRP_VFP3, + ARM_GRP_VFP4, + ARM_GRP_ARM, + ARM_GRP_MCLASS, + ARM_GRP_NOTMCLASS, + ARM_GRP_THUMB, + ARM_GRP_THUMB1ONLY, + ARM_GRP_THUMB2, + ARM_GRP_PREV8, + ARM_GRP_FPVMLX, + ARM_GRP_MULOPS, + ARM_GRP_CRC, + ARM_GRP_DPVFP, + ARM_GRP_V6M, + ARM_GRP_VIRTUALIZATION, -G_END_DECLS + ARM_GRP_ENDING, +} arm_insn_group; +#ifdef __cplusplus +} #endif -/* - * Copyright (C) 2008-2025 Ole André Vadla Ravnås - * Copyright (C) 2020-2024 Francesco Tamagni - * Copyright (C) 2023 Grant Douglas - * Copyright (C) 2024 Håvard Sørbø - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_PROCESS_H__ -#define __GUM_PROCESS_H__ - -/* - * Copyright (C) 2008-2025 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_MODULE_H__ -#define __GUM_MODULE_H__ - - -G_BEGIN_DECLS - -#define GUM_TYPE_MODULE (gum_module_get_type ()) -G_DECLARE_INTERFACE (GumModule, gum_module, GUM, MODULE, GObject) - -typedef struct _GumImportDetails GumImportDetails; -typedef struct _GumExportDetails GumExportDetails; -typedef struct _GumSymbolDetails GumSymbolDetails; -typedef struct _GumSymbolSection GumSymbolSection; -typedef struct _GumSectionDetails GumSectionDetails; -typedef struct _GumDependencyDetails GumDependencyDetails; - -typedef gboolean (* GumFoundImportFunc) (const GumImportDetails * details, - gpointer user_data); -typedef gboolean (* GumFoundExportFunc) (const GumExportDetails * details, - gpointer user_data); -typedef gboolean (* GumFoundSymbolFunc) (const GumSymbolDetails * details, - gpointer user_data); -typedef gboolean (* GumFoundSectionFunc) (const GumSectionDetails * details, - gpointer user_data); -typedef gboolean (* GumFoundDependencyFunc) ( - const GumDependencyDetails * details, gpointer user_data); -typedef GumAddress (* GumResolveExportFunc) (const char * module_name, - const char * symbol_name, gpointer user_data); - -typedef enum { - GUM_IMPORT_UNKNOWN, - GUM_IMPORT_FUNCTION, - GUM_IMPORT_VARIABLE -} GumImportType; - -typedef enum { - GUM_EXPORT_FUNCTION = 1, - GUM_EXPORT_VARIABLE -} GumExportType; - -typedef enum { - /* Common */ - GUM_SYMBOL_UNKNOWN, - GUM_SYMBOL_SECTION, - - /* Mach-O */ - GUM_SYMBOL_UNDEFINED, - GUM_SYMBOL_ABSOLUTE, - GUM_SYMBOL_PREBOUND_UNDEFINED, - GUM_SYMBOL_INDIRECT, - - /* ELF */ - GUM_SYMBOL_OBJECT, - GUM_SYMBOL_FUNCTION, - GUM_SYMBOL_FILE, - GUM_SYMBOL_COMMON, - GUM_SYMBOL_TLS, -} GumSymbolType; - -struct _GumModuleInterface -{ - GTypeInterface parent; - - const gchar * (* get_name) (GumModule * self); - const gchar * (* get_path) (GumModule * self); - const GumMemoryRange * (* get_range) (GumModule * self); - void (* ensure_initialized) (GumModule * self); - void (* enumerate_imports) (GumModule * self, GumFoundImportFunc func, - gpointer user_data); - void (* enumerate_exports) (GumModule * self, GumFoundExportFunc func, - gpointer user_data); - void (* enumerate_symbols) (GumModule * self, GumFoundSymbolFunc func, - gpointer user_data); - void (* enumerate_ranges) (GumModule * self, GumPageProtection prot, - GumFoundRangeFunc func, gpointer user_data); - void (* enumerate_sections) (GumModule * self, GumFoundSectionFunc func, - gpointer user_data); - void (* enumerate_dependencies) (GumModule * self, - GumFoundDependencyFunc func, gpointer user_data); - GumAddress (* find_export_by_name) (GumModule * self, - const gchar * symbol_name); - GumAddress (* find_symbol_by_name) (GumModule * self, - const gchar * symbol_name); -}; - -struct _GumImportDetails -{ - GumImportType type; - const gchar * name; - const gchar * module; - GumAddress address; - GumAddress slot; -}; - -struct _GumExportDetails -{ - GumExportType type; - const gchar * name; - GumAddress address; -}; - -struct _GumSymbolDetails -{ - gboolean is_global; - GumSymbolType type; - const GumSymbolSection * section; - const gchar * name; - GumAddress address; - gssize size; -}; - -struct _GumSymbolSection -{ - const gchar * id; - GumPageProtection protection; -}; - -struct _GumSectionDetails -{ - const gchar * id; - const gchar * name; - GumAddress address; - gsize size; -}; - -typedef enum { - GUM_DEPENDENCY_REGULAR, - GUM_DEPENDENCY_WEAK, - GUM_DEPENDENCY_REEXPORT, - GUM_DEPENDENCY_UPWARD, -} GumDependencyType; - -struct _GumDependencyDetails -{ - const gchar * name; - GumDependencyType type; -}; - -GUM_API GumModule * gum_module_load (const gchar * module_name, - GError ** error); - -GUM_API const gchar * gum_module_get_name (GumModule * self); -GUM_API const gchar * gum_module_get_path (GumModule * self); -GUM_API const GumMemoryRange * gum_module_get_range (GumModule * self); - -GUM_API void gum_module_ensure_initialized (GumModule * self); -GUM_API void gum_module_enumerate_imports (GumModule * self, - GumFoundImportFunc func, gpointer user_data); -GUM_API void gum_module_enumerate_exports (GumModule * self, - GumFoundExportFunc func, gpointer user_data); -GUM_API void gum_module_enumerate_symbols (GumModule * self, - GumFoundSymbolFunc func, gpointer user_data); -GUM_API void gum_module_enumerate_ranges (GumModule * self, - GumPageProtection prot, GumFoundRangeFunc func, gpointer user_data); -GUM_API void gum_module_enumerate_sections (GumModule * self, - GumFoundSectionFunc func, gpointer user_data); -GUM_API void gum_module_enumerate_dependencies (GumModule * self, - GumFoundDependencyFunc func, gpointer user_data); -GUM_API GumAddress gum_module_find_export_by_name (GumModule * self, - const gchar * symbol_name); -GUM_API GumAddress gum_module_find_global_export_by_name ( - const gchar * symbol_name); -GUM_API GumAddress gum_module_find_symbol_by_name (GumModule * self, - const gchar * symbol_name); - -GUM_API const gchar * gum_symbol_type_to_string (GumSymbolType type); - -G_END_DECLS #endif +#ifndef CAPSTONE_ARM64_H +#define CAPSTONE_ARM64_H -#define GUM_THREAD_ID_INVALID ((GumThreadId) -1) -#define GUM_TYPE_THREAD_DETAILS (gum_thread_details_get_type ()) - -G_BEGIN_DECLS - -typedef guint GumProcessId; -typedef gsize GumThreadId; -typedef struct _GumThreadDetails GumThreadDetails; -typedef struct _GumThreadEntrypoint GumThreadEntrypoint; -typedef struct _GumMallocRangeDetails GumMallocRangeDetails; - -typedef enum { - GUM_TEARDOWN_REQUIREMENT_FULL, - GUM_TEARDOWN_REQUIREMENT_MINIMAL -} GumTeardownRequirement; - -typedef enum { - GUM_CODE_SIGNING_OPTIONAL, - GUM_CODE_SIGNING_REQUIRED -} GumCodeSigningPolicy; +/* Capstone Disassembly Engine */ +/* By Nguyen Anh Quynh , 2013-2015 */ -typedef enum { - GUM_MODIFY_THREAD_FLAGS_NONE = 0, - GUM_MODIFY_THREAD_FLAGS_ABORT_SAFELY = (1 << 0), -} GumModifyThreadFlags; +#ifdef __cplusplus +extern "C" { +#endif -typedef enum { - GUM_THREAD_FLAGS_NAME = (1 << 0), - GUM_THREAD_FLAGS_STATE = (1 << 1), - GUM_THREAD_FLAGS_CPU_CONTEXT = (1 << 2), - GUM_THREAD_FLAGS_ENTRYPOINT_ROUTINE = (1 << 3), - GUM_THREAD_FLAGS_ENTRYPOINT_PARAMETER = (1 << 4), - GUM_THREAD_FLAGS_NONE = 0, - GUM_THREAD_FLAGS_ALL = GUM_THREAD_FLAGS_NAME | - GUM_THREAD_FLAGS_STATE | - GUM_THREAD_FLAGS_CPU_CONTEXT | - GUM_THREAD_FLAGS_ENTRYPOINT_ROUTINE | - GUM_THREAD_FLAGS_ENTRYPOINT_PARAMETER, -} GumThreadFlags; +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif -typedef enum { - GUM_THREAD_RUNNING = 1, - GUM_THREAD_STOPPED, - GUM_THREAD_WAITING, - GUM_THREAD_UNINTERRUPTIBLE, - GUM_THREAD_HALTED -} GumThreadState; +/// ARM64 shift type +typedef enum arm64_shifter { + ARM64_SFT_INVALID = 0, + ARM64_SFT_LSL = 1, + ARM64_SFT_MSL = 2, + ARM64_SFT_LSR = 3, + ARM64_SFT_ASR = 4, + ARM64_SFT_ROR = 5, +} arm64_shifter; -struct _GumThreadEntrypoint -{ - GumAddress routine; - GumAddress parameter; -}; +/// ARM64 extender type +typedef enum arm64_extender { + ARM64_EXT_INVALID = 0, + ARM64_EXT_UXTB = 1, + ARM64_EXT_UXTH = 2, + ARM64_EXT_UXTW = 3, + ARM64_EXT_UXTX = 4, + ARM64_EXT_SXTB = 5, + ARM64_EXT_SXTH = 6, + ARM64_EXT_SXTW = 7, + ARM64_EXT_SXTX = 8, +} arm64_extender; -struct _GumThreadDetails -{ - GumThreadFlags flags; - GumThreadId id; - const gchar * name; - GumThreadState state; - GumCpuContext cpu_context; - GumThreadEntrypoint entrypoint; -}; +/// ARM64 condition code +typedef enum arm64_cc { + ARM64_CC_INVALID = 0, + ARM64_CC_EQ = 1, ///< Equal + ARM64_CC_NE = 2, ///< Not equal: Not equal, or unordered + ARM64_CC_HS = 3, ///< Unsigned higher or same: >, ==, or unordered + ARM64_CC_LO = 4, ///< Unsigned lower or same: Less than + ARM64_CC_MI = 5, ///< Minus, negative: Less than + ARM64_CC_PL = 6, ///< Plus, positive or zero: >, ==, or unordered + ARM64_CC_VS = 7, ///< Overflow: Unordered + ARM64_CC_VC = 8, ///< No overflow: Ordered + ARM64_CC_HI = 9, ///< Unsigned higher: Greater than, or unordered + ARM64_CC_LS = 10, ///< Unsigned lower or same: Less than or equal + ARM64_CC_GE = 11, ///< Greater than or equal: Greater than or equal + ARM64_CC_LT = 12, ///< Less than: Less than, or unordered + ARM64_CC_GT = 13, ///< Signed greater than: Greater than + ARM64_CC_LE = 14, ///< Signed less than or equal: <, ==, or unordered + ARM64_CC_AL = 15, ///< Always (unconditional): Always (unconditional) + ARM64_CC_NV = 16, ///< Always (unconditional): Always (unconditional) + //< Note the NV exists purely to disassemble 0b1111. Execution is "always". +} arm64_cc; -typedef enum { - GUM_WATCH_READ = (1 << 0), - GUM_WATCH_WRITE = (1 << 1), -} GumWatchConditions; - -struct _GumMallocRangeDetails -{ - const GumMemoryRange * range; -}; - -typedef void (* GumModifyThreadFunc) (GumThreadId thread_id, - GumCpuContext * cpu_context, gpointer user_data); -typedef gboolean (* GumFoundThreadFunc) (const GumThreadDetails * details, - gpointer user_data); -typedef gboolean (* GumFoundModuleFunc) (GumModule * module, - gpointer user_data); -typedef gboolean (* GumFoundMallocRangeFunc) ( - const GumMallocRangeDetails * details, gpointer user_data); - -GUM_API GumOS gum_process_get_native_os (void); -GUM_API GumTeardownRequirement gum_process_get_teardown_requirement (void); -GUM_API void gum_process_set_teardown_requirement ( - GumTeardownRequirement requirement); -GUM_API GumCodeSigningPolicy gum_process_get_code_signing_policy (void); -GUM_API void gum_process_set_code_signing_policy (GumCodeSigningPolicy policy); -GUM_API gboolean gum_process_is_debugger_attached (void); -GUM_API GumProcessId gum_process_get_id (void); -GUM_API GumThreadId gum_process_get_current_thread_id (void); -GUM_API gboolean gum_process_has_thread (GumThreadId thread_id); -GUM_API gboolean gum_process_modify_thread (GumThreadId thread_id, - GumModifyThreadFunc func, gpointer user_data, GumModifyThreadFlags flags); -GUM_API void gum_process_enumerate_threads (GumFoundThreadFunc func, - gpointer user_data, GumThreadFlags flags); -GUM_API GumModule * gum_process_get_main_module (void); -GUM_API GumModule * gum_process_get_libc_module (void); -GUM_API GumModule * gum_process_find_module_by_name (const gchar * name); -GUM_API GumModule * gum_process_find_module_by_address (GumAddress address); -GUM_API void gum_process_enumerate_modules (GumFoundModuleFunc func, - gpointer user_data); -GUM_API void gum_process_enumerate_ranges (GumPageProtection prot, - GumFoundRangeFunc func, gpointer user_data); -GUM_API void gum_process_enumerate_malloc_ranges ( - GumFoundMallocRangeFunc func, gpointer user_data); -GUM_API guint gum_thread_try_get_ranges (GumMemoryRange * ranges, - guint max_length); -GUM_API gint gum_thread_get_system_error (void); -GUM_API void gum_thread_set_system_error (gint value); -GUM_API gboolean gum_thread_suspend (GumThreadId thread_id, GError ** error); -GUM_API gboolean gum_thread_resume (GumThreadId thread_id, GError ** error); -GUM_API gboolean gum_thread_set_hardware_breakpoint (GumThreadId thread_id, - guint breakpoint_id, GumAddress address, GError ** error); -GUM_API gboolean gum_thread_unset_hardware_breakpoint (GumThreadId thread_id, - guint breakpoint_id, GError ** error); -GUM_API gboolean gum_thread_set_hardware_watchpoint (GumThreadId thread_id, - guint watchpoint_id, GumAddress address, gsize size, GumWatchConditions wc, - GError ** error); -GUM_API gboolean gum_thread_unset_hardware_watchpoint (GumThreadId thread_id, - guint watchpoint_id, GError ** error); - -GUM_API const gchar * gum_code_signing_policy_to_string ( - GumCodeSigningPolicy policy); - -GUM_API GType gum_thread_details_get_type (void) G_GNUC_CONST; -GUM_API GumThreadDetails * gum_thread_details_copy ( - const GumThreadDetails * details); -GUM_API void gum_thread_details_free (GumThreadDetails * details); - -G_END_DECLS - -#endif - -G_BEGIN_DECLS - -typedef struct _GumCloak GumCloak; - -typedef gboolean (* GumCloakFoundThreadFunc) (GumThreadId id, - gpointer user_data); -typedef gboolean (* GumCloakFoundRangeFunc) (const GumMemoryRange * range, - gpointer user_data); -typedef gboolean (* GumCloakFoundFDFunc) (gint fd, gpointer user_data); -typedef void (* GumCloakLockedFunc) (gpointer user_data); - -GUM_API void gum_cloak_add_thread (GumThreadId id); -GUM_API void gum_cloak_remove_thread (GumThreadId id); -GUM_API gboolean gum_cloak_has_thread (GumThreadId id); -GUM_API void gum_cloak_enumerate_threads (GumCloakFoundThreadFunc func, - gpointer user_data); - -GUM_API void gum_cloak_add_range (const GumMemoryRange * range); -GUM_API void gum_cloak_remove_range (const GumMemoryRange * range); -GUM_API gboolean gum_cloak_has_range_containing (GumAddress address); -GUM_API GArray * gum_cloak_clip_range (const GumMemoryRange * range); -GUM_API void gum_cloak_enumerate_ranges (GumCloakFoundRangeFunc func, - gpointer user_data); - -GUM_API void gum_cloak_add_file_descriptor (gint fd); -GUM_API void gum_cloak_remove_file_descriptor (gint fd); -GUM_API gboolean gum_cloak_has_file_descriptor (gint fd); -GUM_API void gum_cloak_enumerate_file_descriptors (GumCloakFoundFDFunc func, - gpointer user_data); - -GUM_API void gum_cloak_with_lock_held (GumCloakLockedFunc func, - gpointer user_data); -GUM_API gboolean gum_cloak_is_locked (void); - -G_END_DECLS - -#endif -/* - * Copyright (C) 2010-2021 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_CODE_ALLOCATOR_H__ -#define __GUM_CODE_ALLOCATOR_H__ - - -#define GUM_TYPE_CODE_SLICE (gum_code_slice_get_type ()) -#define GUM_TYPE_CODE_DEFLECTOR (gum_code_deflector_get_type ()) - -G_BEGIN_DECLS - -typedef struct _GumCodeAllocator GumCodeAllocator; -typedef struct _GumCodeSlice GumCodeSlice; -typedef struct _GumCodeDeflector GumCodeDeflector; - -struct _GumCodeAllocator -{ - gsize slice_size; - gsize pages_per_batch; - gsize slices_per_batch; - gsize pages_metadata_size; - - GSList * uncommitted_pages; - GHashTable * dirty_pages; - GList * free_slices; - - GSList * dispatchers; -}; - -struct _GumCodeSlice -{ - gpointer data; - guint size; - - /*< private >*/ - gint ref_count; -}; - -struct _GumCodeDeflector -{ - gpointer return_address; - gpointer target; - gpointer trampoline; - - /*< private >*/ - gint ref_count; -}; - -GUM_API void gum_code_allocator_init (GumCodeAllocator * allocator, - gsize slice_size); -GUM_API void gum_code_allocator_free (GumCodeAllocator * allocator); - -GUM_API GumCodeSlice * gum_code_allocator_alloc_slice (GumCodeAllocator * self); -GUM_API GumCodeSlice * gum_code_allocator_try_alloc_slice_near ( - GumCodeAllocator * self, const GumAddressSpec * spec, gsize alignment); -GUM_API void gum_code_allocator_commit (GumCodeAllocator * self); -GUM_API GType gum_code_slice_get_type (void) G_GNUC_CONST; -GUM_API GumCodeSlice * gum_code_slice_ref (GumCodeSlice * slice); -GUM_API void gum_code_slice_unref (GumCodeSlice * slice); - -GUM_API GumCodeDeflector * gum_code_allocator_alloc_deflector ( - GumCodeAllocator * self, const GumAddressSpec * caller, - gpointer return_address, gpointer target, gboolean dedicated); -GUM_API GType gum_code_deflector_get_type (void) G_GNUC_CONST; -GUM_API GumCodeDeflector * gum_code_deflector_ref ( - GumCodeDeflector * deflector); -GUM_API void gum_code_deflector_unref (GumCodeDeflector * deflector); - -G_END_DECLS - -#endif -/* - * Copyright (C) 2016-2019 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_CODE_SEGMENT_H__ -#define __GUM_CODE_SEGMENT_H__ - - -G_BEGIN_DECLS - -typedef struct _GumCodeSegment GumCodeSegment; - -GUM_API gboolean gum_code_segment_is_supported (void); - -GUM_API GumCodeSegment * gum_code_segment_new (gsize size, - const GumAddressSpec * spec); -GUM_API void gum_code_segment_free (GumCodeSegment * segment); - -GUM_API gpointer gum_code_segment_get_address (GumCodeSegment * self); -GUM_API gsize gum_code_segment_get_size (GumCodeSegment * self); -GUM_API gsize gum_code_segment_get_virtual_size (GumCodeSegment * self); - -GUM_API void gum_code_segment_realize (GumCodeSegment * self); -GUM_API void gum_code_segment_map (GumCodeSegment * self, gsize source_offset, - gsize source_size, gpointer target_address); - -GUM_API gboolean gum_code_segment_mark (gpointer code, gsize size, - GError ** error); - -G_END_DECLS - -#endif -/* - * Copyright (C) 2021-2022 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_DARWIN_GRAFTER_H__ -#define __GUM_DARWIN_GRAFTER_H__ - - -G_BEGIN_DECLS - -typedef enum { - GUM_DARWIN_GRAFTER_FLAGS_NONE = 0, - GUM_DARWIN_GRAFTER_FLAGS_INGEST_FUNCTION_STARTS = (1 << 0), - GUM_DARWIN_GRAFTER_FLAGS_INGEST_IMPORTS = (1 << 1), - GUM_DARWIN_GRAFTER_FLAGS_TRANSFORM_LAZY_BINDS = (1 << 2), -} GumDarwinGrafterFlags; - -#define GUM_TYPE_DARWIN_GRAFTER (gum_darwin_grafter_get_type ()) -G_DECLARE_FINAL_TYPE (GumDarwinGrafter, gum_darwin_grafter, GUM, DARWIN_GRAFTER, - GObject) - -GUM_API GumDarwinGrafter * gum_darwin_grafter_new_from_file ( - const gchar * path, GumDarwinGrafterFlags flags); - -GUM_API void gum_darwin_grafter_add (GumDarwinGrafter * self, - guint32 code_offset); - -GUM_API gboolean gum_darwin_grafter_graft (GumDarwinGrafter * self, - GError ** error); - -G_END_DECLS - -#endif -/* - * Copyright (C) 2015-2023 Ole André Vadla Ravnås - * Copyright (C) 2023 Fabian Freyer - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_DARWIN_MODULE_H__ -#define __GUM_DARWIN_MODULE_H__ - - -G_BEGIN_DECLS - -#define GUM_TYPE_DARWIN_MODULE (gum_darwin_module_get_type ()) -G_DECLARE_FINAL_TYPE (GumDarwinModule, gum_darwin_module, GUM, DARWIN_MODULE, - GObject) - -#define GUM_TYPE_DARWIN_MODULE_IMAGE (gum_darwin_module_image_get_type ()) - -#define GUM_DARWIN_PORT_NULL 0 -#define GUM_DARWIN_EXPORT_KIND_MASK 3 - -typedef guint GumDarwinModuleFiletype; -typedef gint GumDarwinCpuType; -typedef gint GumDarwinCpuSubtype; - -typedef struct _GumDarwinModuleImage GumDarwinModuleImage; - -typedef struct _GumDarwinModuleImageSegment GumDarwinModuleImageSegment; -typedef struct _GumDarwinSectionDetails GumDarwinSectionDetails; -typedef struct _GumDarwinChainedFixupsDetails GumDarwinChainedFixupsDetails; -typedef struct _GumDarwinRebaseDetails GumDarwinRebaseDetails; -typedef struct _GumDarwinBindDetails GumDarwinBindDetails; -typedef struct _GumDarwinThreadedItem GumDarwinThreadedItem; -typedef struct _GumDarwinTlvParameters GumDarwinTlvParameters; -typedef struct _GumDarwinTlvDescriptorDetails GumDarwinTlvDescriptorDetails; -typedef struct _GumDarwinInitPointersDetails GumDarwinInitPointersDetails; -typedef struct _GumDarwinInitOffsetsDetails GumDarwinInitOffsetsDetails; -typedef struct _GumDarwinTermPointersDetails GumDarwinTermPointersDetails; -typedef struct _GumDarwinFunctionStartsDetails GumDarwinFunctionStartsDetails; -typedef struct _GumDarwinSegment GumDarwinSegment; -typedef struct _GumDarwinExportDetails GumDarwinExportDetails; -typedef struct _GumDarwinSymbolDetails GumDarwinSymbolDetails; - -typedef guint8 GumDarwinRebaseType; -typedef guint8 GumDarwinBindType; -typedef guint8 GumDarwinThreadedItemType; -typedef gint GumDarwinBindOrdinal; -typedef guint8 GumDarwinBindSymbolFlags; -typedef guint8 GumDarwinExportSymbolKind; -typedef guint8 GumDarwinExportSymbolFlags; - -typedef guint GumDarwinPort; -typedef gint GumDarwinPageProtection; - -typedef gboolean (* GumFoundDarwinExportFunc) ( - const GumDarwinExportDetails * details, gpointer user_data); -typedef gboolean (* GumFoundDarwinSymbolFunc) ( - const GumDarwinSymbolDetails * details, gpointer user_data); -typedef gboolean (* GumFoundDarwinSectionFunc) ( - const GumDarwinSectionDetails * details, gpointer user_data); -typedef gboolean (* GumFoundDarwinChainedFixupsFunc) ( - const GumDarwinChainedFixupsDetails * details, gpointer user_data); -typedef gboolean (* GumFoundDarwinRebaseFunc) ( - const GumDarwinRebaseDetails * details, gpointer user_data); -typedef gboolean (* GumFoundDarwinBindFunc) ( - const GumDarwinBindDetails * details, gpointer user_data); - -typedef gboolean (* GumFoundDarwinTlvDescriptorFunc) ( - const GumDarwinTlvDescriptorDetails * details, gpointer user_data); - -typedef gboolean (* GumFoundDarwinInitPointersFunc) ( - const GumDarwinInitPointersDetails * details, gpointer user_data); -typedef gboolean (* GumFoundDarwinInitOffsetsFunc) ( - const GumDarwinInitOffsetsDetails * details, gpointer user_data); -typedef gboolean (* GumFoundDarwinTermPointersFunc) ( - const GumDarwinTermPointersDetails * details, gpointer user_data); -typedef gboolean (* GumFoundDarwinFunctionStartsFunc) ( - const GumDarwinFunctionStartsDetails * details, gpointer user_data); - -typedef struct _GumDyldInfoCommand GumDyldInfoCommand; -typedef struct _GumSymtabCommand GumSymtabCommand; -typedef struct _GumDysymtabCommand GumDysymtabCommand; - -typedef enum { - GUM_DARWIN_MODULE_FLAGS_NONE = 0, - GUM_DARWIN_MODULE_FLAGS_HEADER_ONLY = (1 << 0), -} GumDarwinModuleFlags; - -typedef struct _GumChainedFixupsHeader GumChainedFixupsHeader; -typedef struct _GumChainedStartsInImage GumChainedStartsInImage; -typedef struct _GumChainedStartsInSegment GumChainedStartsInSegment; - -typedef guint32 GumChainedImportFormat; -typedef guint32 GumChainedSymbolFormat; -typedef guint16 GumChainedPtrFormat; - -typedef struct _GumChainedImport GumChainedImport; -typedef struct _GumChainedImportAddend GumChainedImportAddend; -typedef struct _GumChainedImportAddend64 GumChainedImportAddend64; - -typedef struct _GumChainedPtr64Rebase GumChainedPtr64Rebase; -typedef struct _GumChainedPtr64Bind GumChainedPtr64Bind; -typedef struct _GumChainedPtrArm64eRebase GumChainedPtrArm64eRebase; -typedef struct _GumChainedPtrArm64eBind GumChainedPtrArm64eBind; -typedef struct _GumChainedPtrArm64eBind24 GumChainedPtrArm64eBind24; -typedef struct _GumChainedPtrArm64eAuthRebase GumChainedPtrArm64eAuthRebase; -typedef struct _GumChainedPtrArm64eAuthBind GumChainedPtrArm64eAuthBind; -typedef struct _GumChainedPtrArm64eAuthBind24 GumChainedPtrArm64eAuthBind24; - -struct _GumDarwinModule -{ - GObject parent; - - GumDarwinModuleFiletype filetype; - gchar * name; - gchar * uuid; - - GumDarwinPort task; - gboolean is_local; - gboolean is_kernel; - GumCpuType cpu_type; - GumPtrauthSupport ptrauth_support; - gsize pointer_size; - GumAddress base_address; - gchar * source_path; - GBytes * source_blob; - GumDarwinModuleFlags flags; - - GumDarwinModuleImage * image; - - const GumDyldInfoCommand * info; - const GumSymtabCommand * symtab; - const GumDysymtabCommand * dysymtab; - - GumAddress preferred_address; - - GArray * segments; - GArray * text_ranges; - gsize text_size; - - const guint8 * rebases; - const guint8 * rebases_end; - gpointer rebases_malloc_data; - - const guint8 * binds; - const guint8 * binds_end; - gpointer binds_malloc_data; - - const guint8 * lazy_binds; - const guint8 * lazy_binds_end; - gpointer lazy_binds_malloc_data; - - const guint8 * exports; - const guint8 * exports_end; - gpointer exports_malloc_data; - - GArray * dependencies; - GPtrArray * reexports; -}; - -enum _GumDarwinModuleFiletype -{ - GUM_DARWIN_MODULE_FILETYPE_OBJECT = 1, - GUM_DARWIN_MODULE_FILETYPE_EXECUTE, - GUM_DARWIN_MODULE_FILETYPE_FVMLIB, - GUM_DARWIN_MODULE_FILETYPE_CORE, - GUM_DARWIN_MODULE_FILETYPE_PRELOAD, - GUM_DARWIN_MODULE_FILETYPE_DYLIB, - GUM_DARWIN_MODULE_FILETYPE_DYLINKER, - GUM_DARWIN_MODULE_FILETYPE_BUNDLE, - GUM_DARWIN_MODULE_FILETYPE_DYLIB_STUB, - GUM_DARWIN_MODULE_FILETYPE_DSYM, - GUM_DARWIN_MODULE_FILETYPE_KEXT_BUNDLE, - GUM_DARWIN_MODULE_FILETYPE_FILESET, -}; - -enum _GumDarwinCpuArchType -{ - GUM_DARWIN_CPU_ARCH_ABI64 = 0x01000000, - GUM_DARWIN_CPU_ARCH_ABI64_32 = 0x02000000, -}; - -enum _GumDarwinCpuType -{ - GUM_DARWIN_CPU_X86 = 7, - GUM_DARWIN_CPU_X86_64 = 7 | GUM_DARWIN_CPU_ARCH_ABI64, - GUM_DARWIN_CPU_ARM = 12, - GUM_DARWIN_CPU_ARM64 = 12 | GUM_DARWIN_CPU_ARCH_ABI64, - GUM_DARWIN_CPU_ARM64_32 = 12 | GUM_DARWIN_CPU_ARCH_ABI64_32, -}; - -enum _GumDarwinCpuSubtype -{ - GUM_DARWIN_CPU_SUBTYPE_ARM64E = 2, - - GUM_DARWIN_CPU_SUBTYPE_MASK = 0x00ffffff, -}; - -struct _GumDarwinModuleImage -{ - gpointer data; - guint64 size; - gconstpointer linkedit; - - guint64 source_offset; - guint64 source_size; - guint64 shared_offset; - guint64 shared_size; - GArray * shared_segments; - - GBytes * bytes; - gpointer malloc_data; -}; - -struct _GumDarwinModuleImageSegment -{ - guint64 offset; - guint64 size; - GumDarwinPageProtection protection; -}; - -struct _GumDarwinSectionDetails -{ - gchar segment_name[17]; - gchar section_name[17]; - GumAddress vm_address; - guint64 size; - GumDarwinPageProtection protection; - guint32 file_offset; - guint32 flags; -}; - -struct _GumDarwinChainedFixupsDetails -{ - GumAddress vm_address; - guint64 file_offset; - guint32 size; -}; - -struct _GumDarwinRebaseDetails -{ - const GumDarwinSegment * segment; - guint64 offset; - GumDarwinRebaseType type; - GumAddress slide; -}; - -struct _GumDarwinBindDetails -{ - const GumDarwinSegment * segment; - guint64 offset; - GumDarwinBindType type; - GumDarwinBindOrdinal library_ordinal; - const gchar * symbol_name; - GumDarwinBindSymbolFlags symbol_flags; - gint64 addend; - guint16 threaded_table_size; -}; - -struct _GumDarwinThreadedItem -{ - gboolean is_authenticated; - GumDarwinThreadedItemType type; - guint16 delta; - guint8 key; - gboolean has_address_diversity; - guint16 diversity; - - guint16 bind_ordinal; - - GumAddress rebase_address; -}; - -struct _GumDarwinTlvParameters -{ - guint num_descriptors; - guint descriptors_offset; - guint data_offset; - gsize data_size; - gsize bss_size; -}; - -struct _GumDarwinTlvDescriptorDetails -{ - guint64 file_offset; - GumAddress thunk; - guint64 key; - gsize offset; -}; - -struct _GumDarwinInitPointersDetails -{ - GumAddress address; - guint64 count; -}; - -struct _GumDarwinInitOffsetsDetails -{ - GumAddress address; - guint64 count; -}; - -struct _GumDarwinTermPointersDetails -{ - GumAddress address; - guint64 count; -}; - -struct _GumDarwinFunctionStartsDetails -{ - GumAddress vm_address; - guint64 file_offset; - guint32 size; -}; - -struct _GumDarwinSegment -{ - gchar name[17]; - GumAddress vm_address; - guint64 vm_size; - guint64 file_offset; - guint64 file_size; - GumDarwinPageProtection protection; -}; - -struct _GumDarwinExportDetails -{ - const gchar * name; - guint64 flags; - - union - { - struct - { - guint64 offset; - }; - - struct - { - guint64 stub; - guint64 resolver; - }; - - struct - { - gint reexport_library_ordinal; - const gchar * reexport_symbol; - }; - }; -}; - -struct _GumDarwinSymbolDetails -{ - const gchar * name; - GumAddress address; - - /* These map 1:1 to their struct nlist / nlist_64 equivalents. */ - guint8 type; - guint8 section; - guint16 description; -}; - -enum _GumDarwinRebaseType -{ - GUM_DARWIN_REBASE_POINTER = 1, - GUM_DARWIN_REBASE_TEXT_ABSOLUTE32, - GUM_DARWIN_REBASE_TEXT_PCREL32, -}; - -enum _GumDarwinBindType -{ - GUM_DARWIN_BIND_POINTER = 1, - GUM_DARWIN_BIND_TEXT_ABSOLUTE32, - GUM_DARWIN_BIND_TEXT_PCREL32, - GUM_DARWIN_BIND_THREADED_TABLE, - GUM_DARWIN_BIND_THREADED_ITEMS, -}; - -enum _GumDarwinThreadedItemType -{ - GUM_DARWIN_THREADED_REBASE, - GUM_DARWIN_THREADED_BIND -}; - -enum _GumDarwinBindOrdinal -{ - GUM_DARWIN_BIND_SELF = 0, - GUM_DARWIN_BIND_MAIN_EXECUTABLE = -1, - GUM_DARWIN_BIND_FLAT_LOOKUP = -2, - GUM_DARWIN_BIND_WEAK_LOOKUP = -3, -}; - -enum _GumDarwinBindSymbolFlags -{ - GUM_DARWIN_BIND_WEAK_IMPORT = 0x1, - GUM_DARWIN_BIND_NON_WEAK_DEFINITION = 0x8, -}; - -enum _GumDarwinExportSymbolKind -{ - GUM_DARWIN_EXPORT_REGULAR, - GUM_DARWIN_EXPORT_THREAD_LOCAL, - GUM_DARWIN_EXPORT_ABSOLUTE -}; - -enum _GumDarwinExportSymbolFlags -{ - GUM_DARWIN_EXPORT_WEAK_DEFINITION = 0x04, - GUM_DARWIN_EXPORT_REEXPORT = 0x08, - GUM_DARWIN_EXPORT_STUB_AND_RESOLVER = 0x10, -}; - -#ifdef _MSC_VER -# pragma warning (push) -# pragma warning (disable: 4214) -#endif - -struct _GumChainedFixupsHeader -{ - guint32 fixups_version; - guint32 starts_offset; - guint32 imports_offset; - guint32 symbols_offset; - guint32 imports_count; - GumChainedImportFormat imports_format; - GumChainedSymbolFormat symbols_format; -}; - -enum _GumChainedImportFormat -{ - GUM_CHAINED_IMPORT = 1, - GUM_CHAINED_IMPORT_ADDEND = 2, - GUM_CHAINED_IMPORT_ADDEND64 = 3, -}; - -struct _GumChainedImport -{ - guint32 lib_ordinal : 8, - weak_import : 1, - name_offset : 23; -}; - -struct _GumChainedImportAddend -{ - guint32 lib_ordinal : 8, - weak_import : 1, - name_offset : 23; - gint32 addend; -}; - -struct _GumChainedImportAddend64 -{ - guint64 lib_ordinal : 16, - weak_import : 1, - reserved : 15, - name_offset : 32; - guint64 addend; -}; - -struct _GumChainedStartsInImage -{ - guint32 seg_count; - guint32 seg_info_offset[1]; -}; - -struct _GumChainedStartsInSegment -{ - guint32 size; - guint16 page_size; - GumChainedPtrFormat pointer_format; - guint64 segment_offset; - guint32 max_valid_pointer; - guint16 page_count; - guint16 page_start[1]; -}; - -enum _GumChainedPtrStart -{ - GUM_CHAINED_PTR_START_NONE = 0xffff, - GUM_CHAINED_PTR_START_MULTI = 0x8000, - GUM_CHAINED_PTR_START_LAST = 0x8000, -}; - -enum _GumChainedPtrFormat -{ - GUM_CHAINED_PTR_ARM64E = 1, - GUM_CHAINED_PTR_64 = 2, - GUM_CHAINED_PTR_32 = 3, - GUM_CHAINED_PTR_32_CACHE = 4, - GUM_CHAINED_PTR_32_FIRMWARE = 5, - GUM_CHAINED_PTR_64_OFFSET = 6, - GUM_CHAINED_PTR_ARM64E_OFFSET = 7, - GUM_CHAINED_PTR_ARM64E_KERNEL = 7, - GUM_CHAINED_PTR_64_KERNEL_CACHE = 8, - GUM_CHAINED_PTR_ARM64E_USERLAND = 9, - GUM_CHAINED_PTR_ARM64E_FIRMWARE = 10, - GUM_CHAINED_PTR_X86_64_KERNEL_CACHE = 11, - GUM_CHAINED_PTR_ARM64E_USERLAND24 = 12, -}; - -struct _GumChainedPtr64Rebase -{ - guint64 target : 36, - high8 : 8, - reserved : 7, - next : 12, - bind : 1; -}; - -struct _GumChainedPtr64Bind -{ - guint64 ordinal : 24, - addend : 8, - reserved : 19, - next : 12, - bind : 1; -}; - -struct _GumChainedPtrArm64eRebase -{ - guint64 target : 43, - high8 : 8, - next : 11, - bind : 1, - auth : 1; -}; - -struct _GumChainedPtrArm64eBind -{ - guint64 ordinal : 16, - zero : 16, - addend : 19, - next : 11, - bind : 1, - auth : 1; -}; - -struct _GumChainedPtrArm64eBind24 -{ - guint64 ordinal : 24, - zero : 8, - addend : 19, - next : 11, - bind : 1, - auth : 1; -}; - -struct _GumChainedPtrArm64eAuthRebase -{ - guint64 target : 32, - diversity : 16, - addr_div : 1, - key : 2, - next : 11, - bind : 1, - auth : 1; -}; - -struct _GumChainedPtrArm64eAuthBind -{ - guint64 ordinal : 16, - zero : 16, - diversity : 16, - addr_div : 1, - key : 2, - next : 11, - bind : 1, - auth : 1; -}; - -struct _GumChainedPtrArm64eAuthBind24 -{ - guint64 ordinal : 24, - zero : 8, - diversity : 16, - addr_div : 1, - key : 2, - next : 11, - bind : 1, - auth : 1; -}; - -#ifdef _MSC_VER -# pragma warning (pop) -#endif - -GUM_API GumDarwinModule * gum_darwin_module_new_from_file (const gchar * path, - GumCpuType cpu_type, GumPtrauthSupport ptrauth_support, - GumDarwinModuleFlags flags, GError ** error); -GUM_API GumDarwinModule * gum_darwin_module_new_from_blob (GBytes * blob, - GumCpuType cpu_type, GumPtrauthSupport ptrauth_support, - GumDarwinModuleFlags flags, GError ** error); -GUM_API GumDarwinModule * gum_darwin_module_new_from_memory (const gchar * name, - GumDarwinPort task, GumAddress base_address, GumDarwinModuleFlags flags, - GError ** error); - -GUM_API gboolean gum_darwin_module_load (GumDarwinModule * self, - GError ** error); - -GUM_API gboolean gum_darwin_module_resolve_export (GumDarwinModule * self, - const gchar * symbol, GumDarwinExportDetails * details); -GUM_API GumAddress gum_darwin_module_resolve_symbol_address ( - GumDarwinModule * self, const gchar * symbol); -GUM_API gboolean gum_darwin_module_get_lacks_exports_for_reexports ( - GumDarwinModule * self); -GUM_API void gum_darwin_module_enumerate_imports (GumDarwinModule * self, - GumFoundImportFunc func, GumResolveExportFunc resolver, gpointer user_data); -GUM_API void gum_darwin_module_enumerate_exports (GumDarwinModule * self, - GumFoundDarwinExportFunc func, gpointer user_data); -GUM_API void gum_darwin_module_enumerate_symbols (GumDarwinModule * self, - GumFoundDarwinSymbolFunc func, gpointer user_data); -GUM_API GumAddress gum_darwin_module_get_slide (GumDarwinModule * self); -GUM_API const GumDarwinSegment * gum_darwin_module_get_nth_segment ( - GumDarwinModule * self, gsize index); -GUM_API void gum_darwin_module_enumerate_sections (GumDarwinModule * self, - GumFoundDarwinSectionFunc func, gpointer user_data); -GUM_API gboolean gum_darwin_module_is_address_in_text_section ( - GumDarwinModule * self, GumAddress address); -GUM_API void gum_darwin_module_enumerate_chained_fixups (GumDarwinModule * self, - GumFoundDarwinChainedFixupsFunc func, gpointer user_data); -GUM_API void gum_darwin_module_enumerate_rebases (GumDarwinModule * self, - GumFoundDarwinRebaseFunc func, gpointer user_data); -GUM_API void gum_darwin_module_enumerate_binds (GumDarwinModule * self, - GumFoundDarwinBindFunc func, gpointer user_data); -GUM_API void gum_darwin_module_enumerate_lazy_binds (GumDarwinModule * self, - GumFoundDarwinBindFunc func, gpointer user_data); -GUM_API void gum_darwin_module_query_tlv_parameters (GumDarwinModule * self, - GumDarwinTlvParameters * params); -GUM_API void gum_darwin_module_enumerate_tlv_descriptors ( - GumDarwinModule * self, GumFoundDarwinTlvDescriptorFunc func, - gpointer user_data); -GUM_API void gum_darwin_module_enumerate_init_pointers (GumDarwinModule * self, - GumFoundDarwinInitPointersFunc func, gpointer user_data); -GUM_API void gum_darwin_module_enumerate_init_offsets (GumDarwinModule * self, - GumFoundDarwinInitOffsetsFunc func, gpointer user_data); -GUM_API void gum_darwin_module_enumerate_term_pointers (GumDarwinModule * self, - GumFoundDarwinTermPointersFunc func, gpointer user_data); -GUM_API void gum_darwin_module_enumerate_dependencies (GumDarwinModule * self, - GumFoundDependencyFunc func, gpointer user_data); -GUM_API void gum_darwin_module_enumerate_function_starts ( - GumDarwinModule * self, GumFoundDarwinFunctionStartsFunc func, - gpointer user_data); -GUM_API const gchar * gum_darwin_module_get_dependency_by_ordinal ( - GumDarwinModule * self, gint ordinal); -GUM_API gboolean gum_darwin_module_ensure_image_loaded (GumDarwinModule * self, - GError ** error); - -GUM_API void gum_darwin_threaded_item_parse (guint64 value, - GumDarwinThreadedItem * result); - -GUM_API GType gum_darwin_module_image_get_type (void) G_GNUC_CONST; -GUM_API GumDarwinModuleImage * gum_darwin_module_image_new (void); -GUM_API GumDarwinModuleImage * gum_darwin_module_image_dup ( - const GumDarwinModuleImage * other); -GUM_API void gum_darwin_module_image_free (GumDarwinModuleImage * image); - -G_END_DECLS - -#endif -/* - * Copyright (C) 2010-2025 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_ELF_MODULE_H__ -#define __GUM_ELF_MODULE_H__ - - -G_BEGIN_DECLS - -#define GUM_ELF_TYPE_MODULE (gum_elf_module_get_type ()) -G_DECLARE_FINAL_TYPE (GumElfModule, gum_elf_module, GUM_ELF, MODULE, GObject) - -typedef enum { - GUM_ELF_NONE, - GUM_ELF_REL, - GUM_ELF_EXEC, - GUM_ELF_DYN, - GUM_ELF_CORE, -} GumElfType; - -typedef enum { - GUM_ELF_OS_SYSV, - GUM_ELF_OS_HPUX, - GUM_ELF_OS_NETBSD, - GUM_ELF_OS_LINUX, - GUM_ELF_OS_SOLARIS = 6, - GUM_ELF_OS_AIX, - GUM_ELF_OS_IRIX, - GUM_ELF_OS_FREEBSD, - GUM_ELF_OS_TRU64, - GUM_ELF_OS_MODESTO, - GUM_ELF_OS_OPENBSD, - GUM_ELF_OS_ARM_AEABI = 64, - GUM_ELF_OS_ARM = 97, - GUM_ELF_OS_STANDALONE = 255, -} GumElfOSABI; - -typedef enum { - GUM_ELF_MACHINE_NONE, - GUM_ELF_MACHINE_M32, - GUM_ELF_MACHINE_SPARC, - GUM_ELF_MACHINE_386, - GUM_ELF_MACHINE_68K, - GUM_ELF_MACHINE_88K, - GUM_ELF_MACHINE_IAMCU, - GUM_ELF_MACHINE_860, - GUM_ELF_MACHINE_MIPS, - GUM_ELF_MACHINE_S370, - GUM_ELF_MACHINE_MIPS_RS3_LE, - - GUM_ELF_MACHINE_PARISC = 15, - - GUM_ELF_MACHINE_VPP500 = 17, - GUM_ELF_MACHINE_SPARC32PLUS, - GUM_ELF_MACHINE_960, - GUM_ELF_MACHINE_PPC, - GUM_ELF_MACHINE_PPC64, - GUM_ELF_MACHINE_S390, - GUM_ELF_MACHINE_SPU, - - GUM_ELF_MACHINE_V800 = 36, - GUM_ELF_MACHINE_FR20, - GUM_ELF_MACHINE_RH32, - GUM_ELF_MACHINE_RCE, - GUM_ELF_MACHINE_ARM, - GUM_ELF_MACHINE_FAKE_ALPHA, - GUM_ELF_MACHINE_SH, - GUM_ELF_MACHINE_SPARCV9, - GUM_ELF_MACHINE_TRICORE, - GUM_ELF_MACHINE_ARC, - GUM_ELF_MACHINE_H8_300, - GUM_ELF_MACHINE_H8_300H, - GUM_ELF_MACHINE_H8S, - GUM_ELF_MACHINE_H8_500, - GUM_ELF_MACHINE_IA_64, - GUM_ELF_MACHINE_MIPS_X, - GUM_ELF_MACHINE_COLDFIRE, - GUM_ELF_MACHINE_68HC12, - GUM_ELF_MACHINE_MMA, - GUM_ELF_MACHINE_PCP, - GUM_ELF_MACHINE_NCPU, - GUM_ELF_MACHINE_NDR1, - GUM_ELF_MACHINE_STARCORE, - GUM_ELF_MACHINE_ME16, - GUM_ELF_MACHINE_ST100, - GUM_ELF_MACHINE_TINYJ, - GUM_ELF_MACHINE_X86_64, - GUM_ELF_MACHINE_PDSP, - GUM_ELF_MACHINE_PDP10, - GUM_ELF_MACHINE_PDP11, - GUM_ELF_MACHINE_FX66, - GUM_ELF_MACHINE_ST9PLUS, - GUM_ELF_MACHINE_ST7, - GUM_ELF_MACHINE_68HC16, - GUM_ELF_MACHINE_68HC11, - GUM_ELF_MACHINE_68HC08, - GUM_ELF_MACHINE_68HC05, - GUM_ELF_MACHINE_SVX, - GUM_ELF_MACHINE_ST19, - GUM_ELF_MACHINE_VAX, - GUM_ELF_MACHINE_CRIS, - GUM_ELF_MACHINE_JAVELIN, - GUM_ELF_MACHINE_FIREPATH, - GUM_ELF_MACHINE_ZSP, - GUM_ELF_MACHINE_MMIX, - GUM_ELF_MACHINE_HUANY, - GUM_ELF_MACHINE_PRISM, - GUM_ELF_MACHINE_AVR, - GUM_ELF_MACHINE_FR30, - GUM_ELF_MACHINE_D10V, - GUM_ELF_MACHINE_D30V, - GUM_ELF_MACHINE_V850, - GUM_ELF_MACHINE_M32R, - GUM_ELF_MACHINE_MN10300, - GUM_ELF_MACHINE_MN10200, - GUM_ELF_MACHINE_PJ, - GUM_ELF_MACHINE_OPENRISC, - GUM_ELF_MACHINE_ARC_COMPACT, - GUM_ELF_MACHINE_XTENSA, - GUM_ELF_MACHINE_VIDEOCORE, - GUM_ELF_MACHINE_TMM_GPP, - GUM_ELF_MACHINE_NS32K, - GUM_ELF_MACHINE_TPC, - GUM_ELF_MACHINE_SNP1K, - GUM_ELF_MACHINE_ST200, - GUM_ELF_MACHINE_IP2K, - GUM_ELF_MACHINE_MAX, - GUM_ELF_MACHINE_CR, - GUM_ELF_MACHINE_F2MC16, - GUM_ELF_MACHINE_MSP430, - GUM_ELF_MACHINE_BLACKFIN, - GUM_ELF_MACHINE_SE_C33, - GUM_ELF_MACHINE_SEP, - GUM_ELF_MACHINE_ARCA, - GUM_ELF_MACHINE_UNICORE, - GUM_ELF_MACHINE_EXCESS, - GUM_ELF_MACHINE_DXP, - GUM_ELF_MACHINE_ALTERA_NIOS2, - GUM_ELF_MACHINE_CRX, - GUM_ELF_MACHINE_XGATE, - GUM_ELF_MACHINE_C166, - GUM_ELF_MACHINE_M16C, - GUM_ELF_MACHINE_DSPIC30F, - GUM_ELF_MACHINE_CE, - GUM_ELF_MACHINE_M32C, - - GUM_ELF_MACHINE_TSK3000 = 131, - GUM_ELF_MACHINE_RS08, - GUM_ELF_MACHINE_SHARC, - GUM_ELF_MACHINE_ECOG2, - GUM_ELF_MACHINE_SCORE7, - GUM_ELF_MACHINE_DSP24, - GUM_ELF_MACHINE_VIDEOCORE3, - GUM_ELF_MACHINE_LATTICEMICO32, - GUM_ELF_MACHINE_SE_C17, - GUM_ELF_MACHINE_TI_C6000, - GUM_ELF_MACHINE_TI_C2000, - GUM_ELF_MACHINE_TI_C5500, - GUM_ELF_MACHINE_TI_ARP32, - GUM_ELF_MACHINE_TI_PRU, - - GUM_ELF_MACHINE_MMDSP_PLUS = 160, - GUM_ELF_MACHINE_CYPRESS_M8C, - GUM_ELF_MACHINE_R32C, - GUM_ELF_MACHINE_TRIMEDIA, - GUM_ELF_MACHINE_QDSP6, - GUM_ELF_MACHINE_8051, - GUM_ELF_MACHINE_STXP7X, - GUM_ELF_MACHINE_NDS32, - GUM_ELF_MACHINE_ECOG1X, - GUM_ELF_MACHINE_MAXQ30, - GUM_ELF_MACHINE_XIMO16, - GUM_ELF_MACHINE_MANIK, - GUM_ELF_MACHINE_CRAYNV2, - GUM_ELF_MACHINE_RX, - GUM_ELF_MACHINE_METAG, - GUM_ELF_MACHINE_MCST_ELBRUS, - GUM_ELF_MACHINE_ECOG16, - GUM_ELF_MACHINE_CR16, - GUM_ELF_MACHINE_ETPU, - GUM_ELF_MACHINE_SLE9X, - GUM_ELF_MACHINE_L10M, - GUM_ELF_MACHINE_K10M, - - GUM_ELF_MACHINE_AARCH64 = 183, - - GUM_ELF_MACHINE_AVR32 = 185, - GUM_ELF_MACHINE_STM8, - GUM_ELF_MACHINE_TILE64, - GUM_ELF_MACHINE_TILEPRO, - GUM_ELF_MACHINE_MICROBLAZE, - GUM_ELF_MACHINE_CUDA, - GUM_ELF_MACHINE_TILEGX, - GUM_ELF_MACHINE_CLOUDSHIELD, - GUM_ELF_MACHINE_COREA_1ST, - GUM_ELF_MACHINE_COREA_2ND, - GUM_ELF_MACHINE_ARCV2, - GUM_ELF_MACHINE_OPEN8, - GUM_ELF_MACHINE_RL78, - GUM_ELF_MACHINE_VIDEOCORE5, - GUM_ELF_MACHINE_78KOR, - GUM_ELF_MACHINE_56800EX, - GUM_ELF_MACHINE_BA1, - GUM_ELF_MACHINE_BA2, - GUM_ELF_MACHINE_XCORE, - GUM_ELF_MACHINE_MCHP_PIC, - - GUM_ELF_MACHINE_KM32 = 210, - GUM_ELF_MACHINE_KMX32, - GUM_ELF_MACHINE_EMX16, - GUM_ELF_MACHINE_EMX8, - GUM_ELF_MACHINE_KVARC, - GUM_ELF_MACHINE_CDP, - GUM_ELF_MACHINE_COGE, - GUM_ELF_MACHINE_COOL, - GUM_ELF_MACHINE_NORC, - GUM_ELF_MACHINE_CSR_KALIMBA, - GUM_ELF_MACHINE_Z80, - GUM_ELF_MACHINE_VISIUM, - GUM_ELF_MACHINE_FT32, - GUM_ELF_MACHINE_MOXIE, - GUM_ELF_MACHINE_AMDGPU, - - GUM_ELF_MACHINE_RISCV = 243, - - GUM_ELF_MACHINE_BPF = 247, - - GUM_ELF_MACHINE_CSKY = 252, - - GUM_ELF_MACHINE_ALPHA = 0x9026, -} GumElfMachine; - -typedef enum { - GUM_ELF_SOURCE_MODE_OFFLINE, - GUM_ELF_SOURCE_MODE_ONLINE, -} GumElfSourceMode; - -typedef enum { - GUM_ELF_SECTION_NULL, - GUM_ELF_SECTION_PROGBITS, - GUM_ELF_SECTION_SYMTAB, - GUM_ELF_SECTION_STRTAB, - GUM_ELF_SECTION_RELA, - GUM_ELF_SECTION_HASH, - GUM_ELF_SECTION_DYNAMIC, - GUM_ELF_SECTION_NOTE, - GUM_ELF_SECTION_NOBITS, - GUM_ELF_SECTION_REL, - GUM_ELF_SECTION_SHLIB, - GUM_ELF_SECTION_DYNSYM, - GUM_ELF_SECTION_INIT_ARRAY = 14, - GUM_ELF_SECTION_FINI_ARRAY, - GUM_ELF_SECTION_PREINIT_ARRAY, - GUM_ELF_SECTION_GROUP, - GUM_ELF_SECTION_SYMTAB_SHNDX, - GUM_ELF_SECTION_RELR, - GUM_ELF_SECTION_NUM, - GUM_ELF_SECTION_GNU_ATTRIBUTES = 0x6ffffff5, - GUM_ELF_SECTION_GNU_HASH = 0x6ffffff6, - GUM_ELF_SECTION_GNU_LIBLIST = 0x6ffffff7, - GUM_ELF_SECTION_CHECKSUM = 0x6ffffff8, - GUM_ELF_SECTION_SUNW_MOVE = 0x6ffffffa, - GUM_ELF_SECTION_SUNW_COMDAT = 0x6ffffffb, - GUM_ELF_SECTION_SUNW_SYMINFO = 0x6ffffffc, - GUM_ELF_SECTION_GNU_VERDEF = 0x6ffffffd, - GUM_ELF_SECTION_GNU_VERNEED = 0x6ffffffe, - GUM_ELF_SECTION_GNU_VERSYM = 0x6fffffff, -} GumElfSectionType; - -typedef enum { - GUM_ELF_SECTION_FLAG_WRITE = (1U << 0), - GUM_ELF_SECTION_FLAG_ALLOC = (1U << 1), - GUM_ELF_SECTION_FLAG_EXECINSTR = (1U << 2), - GUM_ELF_SECTION_FLAG_MERGE = (1U << 4), - GUM_ELF_SECTION_FLAG_STRINGS = (1U << 5), - GUM_ELF_SECTION_FLAG_INFO_LINK = (1U << 6), - GUM_ELF_SECTION_FLAG_LINK_ORDER = (1U << 7), - GUM_ELF_SECTION_FLAG_OS_NONCONFORMING = (1U << 8), - GUM_ELF_SECTION_FLAG_GROUP = (1U << 9), - GUM_ELF_SECTION_FLAG_TLS = (1U << 10), - GUM_ELF_SECTION_FLAG_COMPRESSED = (1U << 11), - GUM_ELF_SECTION_FLAG_GNU_RETAIN = (1U << 21), - GUM_ELF_SECTION_FLAG_ORDERED = (1U << 30), - GUM_ELF_SECTION_FLAG_EXCLUDE = (1U << 31), -} GumElfSectionFlags; - -#define GUM_ELF_SECTION_MASK_OS 0x0ff00000 -#define GUM_ELF_SECTION_MASK_PROCESSOR 0xf0000000 - -typedef enum { - GUM_ELF_DYNAMIC_NULL, - GUM_ELF_DYNAMIC_NEEDED, - GUM_ELF_DYNAMIC_PLTRELSZ, - GUM_ELF_DYNAMIC_PLTGOT, - GUM_ELF_DYNAMIC_HASH, - GUM_ELF_DYNAMIC_STRTAB, - GUM_ELF_DYNAMIC_SYMTAB, - GUM_ELF_DYNAMIC_RELA, - GUM_ELF_DYNAMIC_RELASZ, - GUM_ELF_DYNAMIC_RELAENT, - GUM_ELF_DYNAMIC_STRSZ, - GUM_ELF_DYNAMIC_SYMENT, - GUM_ELF_DYNAMIC_INIT, - GUM_ELF_DYNAMIC_FINI, - GUM_ELF_DYNAMIC_SONAME, - GUM_ELF_DYNAMIC_RPATH, - GUM_ELF_DYNAMIC_SYMBOLIC, - GUM_ELF_DYNAMIC_REL, - GUM_ELF_DYNAMIC_RELSZ, - GUM_ELF_DYNAMIC_RELENT, - GUM_ELF_DYNAMIC_PLTREL, - GUM_ELF_DYNAMIC_DEBUG, - GUM_ELF_DYNAMIC_TEXTREL, - GUM_ELF_DYNAMIC_JMPREL, - GUM_ELF_DYNAMIC_BIND_NOW, - GUM_ELF_DYNAMIC_INIT_ARRAY, - GUM_ELF_DYNAMIC_FINI_ARRAY, - GUM_ELF_DYNAMIC_INIT_ARRAYSZ, - GUM_ELF_DYNAMIC_FINI_ARRAYSZ, - GUM_ELF_DYNAMIC_RUNPATH, - GUM_ELF_DYNAMIC_FLAGS, - GUM_ELF_DYNAMIC_ENCODING = 32, - GUM_ELF_DYNAMIC_PREINIT_ARRAY = 32, - GUM_ELF_DYNAMIC_PREINIT_ARRAYSZ, - GUM_ELF_DYNAMIC_MAXPOSTAGS, - - GUM_ELF_DYNAMIC_LOOS = 0x6000000d, - GUM_ELF_DYNAMIC_SUNW_AUXILIARY = 0x6000000d, - GUM_ELF_DYNAMIC_SUNW_RTLDINF = 0x6000000e, - GUM_ELF_DYNAMIC_SUNW_FILTER = 0x6000000f, - GUM_ELF_DYNAMIC_SUNW_CAP = 0x60000010, - GUM_ELF_DYNAMIC_SUNW_ASLR = 0x60000023, - GUM_ELF_DYNAMIC_HIOS = 0x6ffff000, - - GUM_ELF_DYNAMIC_VALRNGLO = 0x6ffffd00, - GUM_ELF_DYNAMIC_GNU_PRELINKED = 0x6ffffdf5, - GUM_ELF_DYNAMIC_GNU_CONFLICTSZ = 0x6ffffdf6, - GUM_ELF_DYNAMIC_GNU_LIBLISTSZ = 0x6ffffdf7, - GUM_ELF_DYNAMIC_CHECKSUM = 0x6ffffdf8, - GUM_ELF_DYNAMIC_PLTPADSZ = 0x6ffffdf9, - GUM_ELF_DYNAMIC_MOVEENT = 0x6ffffdfa, - GUM_ELF_DYNAMIC_MOVESZ = 0x6ffffdfb, - GUM_ELF_DYNAMIC_FEATURE = 0x6ffffdfc, - GUM_ELF_DYNAMIC_FEATURE_1 = 0x6ffffdfc, - GUM_ELF_DYNAMIC_POSFLAG_1 = 0x6ffffdfd, - - GUM_ELF_DYNAMIC_SYMINSZ = 0x6ffffdfe, - GUM_ELF_DYNAMIC_SYMINENT = 0x6ffffdff, - GUM_ELF_DYNAMIC_VALRNGHI = 0x6ffffdff, - - GUM_ELF_DYNAMIC_ADDRRNGLO = 0x6ffffe00, - GUM_ELF_DYNAMIC_GNU_HASH = 0x6ffffef5, - GUM_ELF_DYNAMIC_TLSDESC_PLT = 0x6ffffef6, - GUM_ELF_DYNAMIC_TLSDESC_GOT = 0x6ffffef7, - GUM_ELF_DYNAMIC_GNU_CONFLICT = 0x6ffffef8, - GUM_ELF_DYNAMIC_GNU_LIBLIST = 0x6ffffef9, - GUM_ELF_DYNAMIC_CONFIG = 0x6ffffefa, - GUM_ELF_DYNAMIC_DEPAUDIT = 0x6ffffefb, - GUM_ELF_DYNAMIC_AUDIT = 0x6ffffefc, - GUM_ELF_DYNAMIC_PLTPAD = 0x6ffffefd, - GUM_ELF_DYNAMIC_MOVETAB = 0x6ffffefe, - GUM_ELF_DYNAMIC_SYMINFO = 0x6ffffeff, - GUM_ELF_DYNAMIC_ADDRRNGHI = 0x6ffffeff, - - GUM_ELF_DYNAMIC_VERSYM = 0x6ffffff0, - GUM_ELF_DYNAMIC_RELACOUNT = 0x6ffffff9, - GUM_ELF_DYNAMIC_RELCOUNT = 0x6ffffffa, - GUM_ELF_DYNAMIC_FLAGS_1 = 0x6ffffffb, - GUM_ELF_DYNAMIC_VERDEF = 0x6ffffffc, - GUM_ELF_DYNAMIC_VERDEFNUM = 0x6ffffffd, - GUM_ELF_DYNAMIC_VERNEED = 0x6ffffffe, - GUM_ELF_DYNAMIC_VERNEEDNUM = 0x6fffffff, - - GUM_ELF_DYNAMIC_LOPROC = 0x70000000, - - GUM_ELF_DYNAMIC_ARM_SYMTABSZ = 0x70000001, - GUM_ELF_DYNAMIC_ARM_PREEMPTMAP = 0x70000002, - - GUM_ELF_DYNAMIC_SPARC_REGISTER = 0x70000001, - GUM_ELF_DYNAMIC_DEPRECATED_SPARC_REGISTER = 0x7000001, - - GUM_ELF_DYNAMIC_MIPS_RLD_VERSION = 0x70000001, - GUM_ELF_DYNAMIC_MIPS_TIME_STAMP = 0x70000002, - GUM_ELF_DYNAMIC_MIPS_ICHECKSUM = 0x70000003, - GUM_ELF_DYNAMIC_MIPS_IVERSION = 0x70000004, - GUM_ELF_DYNAMIC_MIPS_FLAGS = 0x70000005, - GUM_ELF_DYNAMIC_MIPS_BASE_ADDRESS = 0x70000006, - GUM_ELF_DYNAMIC_MIPS_CONFLICT = 0x70000008, - GUM_ELF_DYNAMIC_MIPS_LIBLIST = 0x70000009, - GUM_ELF_DYNAMIC_MIPS_LOCAL_GOTNO = 0x7000000a, - GUM_ELF_DYNAMIC_MIPS_CONFLICTNO = 0x7000000b, - GUM_ELF_DYNAMIC_MIPS_LIBLISTNO = 0x70000010, - GUM_ELF_DYNAMIC_MIPS_SYMTABNO = 0x70000011, - GUM_ELF_DYNAMIC_MIPS_UNREFEXTNO = 0x70000012, - GUM_ELF_DYNAMIC_MIPS_GOTSYM = 0x70000013, - GUM_ELF_DYNAMIC_MIPS_HIPAGENO = 0x70000014, - GUM_ELF_DYNAMIC_MIPS_RLD_MAP = 0x70000016, - GUM_ELF_DYNAMIC_MIPS_DELTA_CLASS = 0x70000017, - GUM_ELF_DYNAMIC_MIPS_DELTA_CLASS_NO = 0x70000018, - GUM_ELF_DYNAMIC_MIPS_DELTA_INSTANCE = 0x70000019, - GUM_ELF_DYNAMIC_MIPS_DELTA_INSTANCE_NO = 0x7000001a, - GUM_ELF_DYNAMIC_MIPS_DELTA_RELOC = 0x7000001b, - GUM_ELF_DYNAMIC_MIPS_DELTA_RELOC_NO = 0x7000001c, - GUM_ELF_DYNAMIC_MIPS_DELTA_SYM = 0x7000001d, - GUM_ELF_DYNAMIC_MIPS_DELTA_SYM_NO = 0x7000001e, - GUM_ELF_DYNAMIC_MIPS_DELTA_CLASSSYM = 0x70000020, - GUM_ELF_DYNAMIC_MIPS_DELTA_CLASSSYM_NO = 0x70000021, - GUM_ELF_DYNAMIC_MIPS_CXX_FLAGS = 0x70000022, - GUM_ELF_DYNAMIC_MIPS_PIXIE_INIT = 0x70000023, - GUM_ELF_DYNAMIC_MIPS_SYMBOL_LIB = 0x70000024, - GUM_ELF_DYNAMIC_MIPS_LOCALPAGE_GOTIDX = 0x70000025, - GUM_ELF_DYNAMIC_MIPS_LOCAL_GOTIDX = 0x70000026, - GUM_ELF_DYNAMIC_MIPS_HIDDEN_GOTIDX = 0x70000027, - GUM_ELF_DYNAMIC_MIPS_PROTECTED_GOTIDX = 0x70000028, - GUM_ELF_DYNAMIC_MIPS_OPTIONS = 0x70000029, - GUM_ELF_DYNAMIC_MIPS_INTERFACE = 0x7000002a, - GUM_ELF_DYNAMIC_MIPS_DYNSTR_ALIGN = 0x7000002b, - GUM_ELF_DYNAMIC_MIPS_INTERFACE_SIZE = 0x7000002c, - GUM_ELF_DYNAMIC_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d, - GUM_ELF_DYNAMIC_MIPS_PERF_SUFFIX = 0x7000002e, - GUM_ELF_DYNAMIC_MIPS_COMPACT_SIZE = 0x7000002f, - GUM_ELF_DYNAMIC_MIPS_GP_VALUE = 0x70000030, - GUM_ELF_DYNAMIC_MIPS_AUX_DYNAMIC = 0x70000031, - GUM_ELF_DYNAMIC_MIPS_PLTGOT = 0x70000032, - GUM_ELF_DYNAMIC_MIPS_RLD_OBJ_UPDATE = 0x70000033, - GUM_ELF_DYNAMIC_MIPS_RWPLT = 0x70000034, - GUM_ELF_DYNAMIC_MIPS_RLD_MAP_REL = 0x70000035, - - GUM_ELF_DYNAMIC_PPC_GOT = 0x70000000, - GUM_ELF_DYNAMIC_PPC_TLSOPT = 0x70000001, - - GUM_ELF_DYNAMIC_PPC64_GLINK = 0x70000000, - GUM_ELF_DYNAMIC_PPC64_OPD = 0x70000001, - GUM_ELF_DYNAMIC_PPC64_OPDSZ = 0x70000002, - GUM_ELF_DYNAMIC_PPC64_TLSOPT = 0x70000003, - - GUM_ELF_DYNAMIC_AUXILIARY = 0x7ffffffd, - GUM_ELF_DYNAMIC_USED = 0x7ffffffe, - GUM_ELF_DYNAMIC_FILTER = 0x7fffffff, - - GUM_ELF_DYNAMIC_HIPROC = 0x7fffffff, -} GumElfDynamicTag; - -typedef enum { - GUM_ELF_SHDR_INDEX_UNDEF, - GUM_ELF_SHDR_INDEX_BEFORE = 0xff00, - GUM_ELF_SHDR_INDEX_AFTER = 0xff01, - GUM_ELF_SHDR_INDEX_ABS = 0xfff1, - GUM_ELF_SHDR_INDEX_COMMON = 0xfff2, - GUM_ELF_SHDR_INDEX_XINDEX = 0xffff, -} GumElfShdrIndex; - -typedef enum { - GUM_ELF_SYMBOL_NOTYPE, - GUM_ELF_SYMBOL_OBJECT, - GUM_ELF_SYMBOL_FUNC, - GUM_ELF_SYMBOL_SECTION, - GUM_ELF_SYMBOL_FILE, - GUM_ELF_SYMBOL_COMMON, - GUM_ELF_SYMBOL_TLS, - GUM_ELF_SYMBOL_NUM, - GUM_ELF_SYMBOL_LOOS = 10, - GUM_ELF_SYMBOL_GNU_IFUNC = 10, - GUM_ELF_SYMBOL_HIOS = 12, - GUM_ELF_SYMBOL_LOPROC, - GUM_ELF_SYMBOL_SPARC_REGISTER = 13, - GUM_ELF_SYMBOL_HIPROC = 15, -} GumElfSymbolType; - -typedef enum { - GUM_ELF_BIND_LOCAL, - GUM_ELF_BIND_GLOBAL, - GUM_ELF_BIND_WEAK, - - GUM_ELF_BIND_LOOS = 10, - GUM_ELF_BIND_GNU_UNIQUE = 10, - GUM_ELF_BIND_HIOS = 12, - - GUM_ELF_BIND_LOPROC, - GUM_ELF_BIND_HIPROC = 15, -} GumElfSymbolBind; - -typedef enum { - GUM_ELF_IA32_NONE, - GUM_ELF_IA32_32, - GUM_ELF_IA32_PC32, - GUM_ELF_IA32_GOT32, - GUM_ELF_IA32_PLT32, - GUM_ELF_IA32_COPY, - GUM_ELF_IA32_GLOB_DAT, - GUM_ELF_IA32_JMP_SLOT, - GUM_ELF_IA32_RELATIVE, - GUM_ELF_IA32_GOTOFF, - GUM_ELF_IA32_GOTPC, - GUM_ELF_IA32_32PLT, - GUM_ELF_IA32_TLS_TPOFF = 14, - GUM_ELF_IA32_TLS_IE, - GUM_ELF_IA32_TLS_GOTIE, - GUM_ELF_IA32_TLS_LE, - GUM_ELF_IA32_TLS_GD, - GUM_ELF_IA32_TLS_LDM, - GUM_ELF_IA32_16, - GUM_ELF_IA32_PC16, - GUM_ELF_IA32_8, - GUM_ELF_IA32_PC8, - GUM_ELF_IA32_TLS_GD_32, - GUM_ELF_IA32_TLS_GD_PUSH, - GUM_ELF_IA32_TLS_GD_CALL, - GUM_ELF_IA32_TLS_GD_POP, - GUM_ELF_IA32_TLS_LDM_32, - GUM_ELF_IA32_TLS_LDM_PUSH, - GUM_ELF_IA32_TLS_LDM_CALL, - GUM_ELF_IA32_TLS_LDM_POP, - GUM_ELF_IA32_TLS_LDO_32, - GUM_ELF_IA32_TLS_IE_32, - GUM_ELF_IA32_TLS_LE_32, - GUM_ELF_IA32_TLS_DTPMOD32, - GUM_ELF_IA32_TLS_DTPOFF32, - GUM_ELF_IA32_TLS_TPOFF32, - GUM_ELF_IA32_SIZE32, - GUM_ELF_IA32_TLS_GOTDESC, - GUM_ELF_IA32_TLS_DESC_CALL, - GUM_ELF_IA32_TLS_DESC, - GUM_ELF_IA32_IRELATIVE, - GUM_ELF_IA32_GOT32X, -} GumElfIA32Relocation; - -typedef enum { - GUM_ELF_X64_NONE, - GUM_ELF_X64_64, - GUM_ELF_X64_PC32, - GUM_ELF_X64_GOT32, - GUM_ELF_X64_PLT32, - GUM_ELF_X64_COPY, - GUM_ELF_X64_GLOB_DAT, - GUM_ELF_X64_JUMP_SLOT, - GUM_ELF_X64_RELATIVE, - GUM_ELF_X64_GOTPCREL, - GUM_ELF_X64_32, - GUM_ELF_X64_32S, - GUM_ELF_X64_16, - GUM_ELF_X64_PC16, - GUM_ELF_X64_8, - GUM_ELF_X64_PC8, - GUM_ELF_X64_DTPMOD64, - GUM_ELF_X64_DTPOFF64, - GUM_ELF_X64_TPOFF64, - GUM_ELF_X64_TLSGD, - GUM_ELF_X64_TLSLD, - GUM_ELF_X64_DTPOFF32, - GUM_ELF_X64_GOTTPOFF, - GUM_ELF_X64_TPOFF32, - GUM_ELF_X64_PC64, - GUM_ELF_X64_GOTOFF64, - GUM_ELF_X64_GOTPC32, - GUM_ELF_X64_GOT64, - GUM_ELF_X64_GOTPCREL64, - GUM_ELF_X64_GOTPC64, - GUM_ELF_X64_GOTPLT64, - GUM_ELF_X64_PLTOFF64, - GUM_ELF_X64_SIZE32, - GUM_ELF_X64_SIZE64, - GUM_ELF_X64_GOTPC32_TLSDESC, - GUM_ELF_X64_TLSDESC_CALL, - GUM_ELF_X64_TLSDESC, - GUM_ELF_X64_IRELATIVE, - GUM_ELF_X64_RELATIVE64, - GUM_ELF_X64_GOTPCRELX = 41, - GUM_ELF_X64_REX_GOTPCRELX, -} GumElfX64Relocation; - -typedef enum { - GUM_ELF_ARM_NONE, - GUM_ELF_ARM_PC24, - GUM_ELF_ARM_ABS32, - GUM_ELF_ARM_REL32, - GUM_ELF_ARM_PC13, - GUM_ELF_ARM_ABS16, - GUM_ELF_ARM_ABS12, - GUM_ELF_ARM_THM_ABS5, - GUM_ELF_ARM_ABS8, - GUM_ELF_ARM_SBREL32, - GUM_ELF_ARM_THM_PC22, - GUM_ELF_ARM_THM_PC8, - GUM_ELF_ARM_AMP_VCALL9, - GUM_ELF_ARM_SWI24, - GUM_ELF_ARM_TLS_DESC = 13, - GUM_ELF_ARM_THM_SWI8, - GUM_ELF_ARM_XPC25, - GUM_ELF_ARM_THM_XPC22, - GUM_ELF_ARM_TLS_DTPMOD32, - GUM_ELF_ARM_TLS_DTPOFF32, - GUM_ELF_ARM_TLS_TPOFF32, - GUM_ELF_ARM_COPY, - GUM_ELF_ARM_GLOB_DAT, - GUM_ELF_ARM_JUMP_SLOT, - GUM_ELF_ARM_RELATIVE, - GUM_ELF_ARM_GOTOFF, - GUM_ELF_ARM_GOTPC, - GUM_ELF_ARM_GOT32, - GUM_ELF_ARM_PLT32, - GUM_ELF_ARM_CALL, - GUM_ELF_ARM_JUMP24, - GUM_ELF_ARM_THM_JUMP24, - GUM_ELF_ARM_BASE_ABS, - GUM_ELF_ARM_ALU_PCREL_7_0, - GUM_ELF_ARM_ALU_PCREL_15_8, - GUM_ELF_ARM_ALU_PCREL_23_15, - GUM_ELF_ARM_LDR_SBREL_11_0, - GUM_ELF_ARM_ALU_SBREL_19_12, - GUM_ELF_ARM_ALU_SBREL_27_20, - GUM_ELF_ARM_TARGET1, - GUM_ELF_ARM_SBREL31, - GUM_ELF_ARM_V4BX, - GUM_ELF_ARM_TARGET2, - GUM_ELF_ARM_PREL31, - GUM_ELF_ARM_MOVW_ABS_NC, - GUM_ELF_ARM_MOVT_ABS, - GUM_ELF_ARM_MOVW_PREL_NC, - GUM_ELF_ARM_MOVT_PREL, - GUM_ELF_ARM_THM_MOVW_ABS_NC, - GUM_ELF_ARM_THM_MOVT_ABS, - GUM_ELF_ARM_THM_MOVW_PREL_NC, - GUM_ELF_ARM_THM_MOVT_PREL, - GUM_ELF_ARM_THM_JUMP19, - GUM_ELF_ARM_THM_JUMP6, - GUM_ELF_ARM_THM_ALU_PREL_11_0, - GUM_ELF_ARM_THM_PC12, - GUM_ELF_ARM_ABS32_NOI, - GUM_ELF_ARM_REL32_NOI, - GUM_ELF_ARM_ALU_PC_G0_NC, - GUM_ELF_ARM_ALU_PC_G0, - GUM_ELF_ARM_ALU_PC_G1_NC, - GUM_ELF_ARM_ALU_PC_G1, - GUM_ELF_ARM_ALU_PC_G2, - GUM_ELF_ARM_LDR_PC_G1, - GUM_ELF_ARM_LDR_PC_G2, - GUM_ELF_ARM_LDRS_PC_G0, - GUM_ELF_ARM_LDRS_PC_G1, - GUM_ELF_ARM_LDRS_PC_G2, - GUM_ELF_ARM_LDC_PC_G0, - GUM_ELF_ARM_LDC_PC_G1, - GUM_ELF_ARM_LDC_PC_G2, - GUM_ELF_ARM_ALU_SB_G0_NC, - GUM_ELF_ARM_ALU_SB_G0, - GUM_ELF_ARM_ALU_SB_G1_NC, - GUM_ELF_ARM_ALU_SB_G1, - GUM_ELF_ARM_ALU_SB_G2, - GUM_ELF_ARM_LDR_SB_G0, - GUM_ELF_ARM_LDR_SB_G1, - GUM_ELF_ARM_LDR_SB_G2, - GUM_ELF_ARM_LDRS_SB_G0, - GUM_ELF_ARM_LDRS_SB_G1, - GUM_ELF_ARM_LDRS_SB_G2, - GUM_ELF_ARM_LDC_SB_G0, - GUM_ELF_ARM_LDC_SB_G1, - GUM_ELF_ARM_LDC_SB_G2, - GUM_ELF_ARM_MOVW_BREL_NC, - GUM_ELF_ARM_MOVT_BREL, - GUM_ELF_ARM_MOVW_BREL, - GUM_ELF_ARM_THM_MOVW_BREL_NC, - GUM_ELF_ARM_THM_MOVT_BREL, - GUM_ELF_ARM_THM_MOVW_BREL, - GUM_ELF_ARM_TLS_GOTDESC, - GUM_ELF_ARM_TLS_CALL, - GUM_ELF_ARM_TLS_DESCSEQ, - GUM_ELF_ARM_THM_TLS_CALL, - GUM_ELF_ARM_PLT32_ABS, - GUM_ELF_ARM_GOT_ABS, - GUM_ELF_ARM_GOT_PREL, - GUM_ELF_ARM_GOT_BREL12, - GUM_ELF_ARM_GOTOFF12, - GUM_ELF_ARM_GOTRELAX, - GUM_ELF_ARM_GNU_VTENTRY, - GUM_ELF_ARM_GNU_VTINHERIT, - GUM_ELF_ARM_THM_PC11, - GUM_ELF_ARM_THM_PC9, - GUM_ELF_ARM_TLS_GD32, - GUM_ELF_ARM_TLS_LDM32, - GUM_ELF_ARM_TLS_LDO32, - GUM_ELF_ARM_TLS_IE32, - GUM_ELF_ARM_TLS_LE32, - GUM_ELF_ARM_TLS_LDO12, - GUM_ELF_ARM_TLS_LE12, - GUM_ELF_ARM_TLS_IE12GP, - GUM_ELF_ARM_ME_TOO = 128, - GUM_ELF_ARM_THM_TLS_DESCSEQ, - GUM_ELF_ARM_THM_TLS_DESCSEQ16 = 129, - GUM_ELF_ARM_THM_TLS_DESCSEQ32, - GUM_ELF_ARM_THM_GOT_BREL12, - GUM_ELF_ARM_IRELATIVE = 160, - GUM_ELF_ARM_RXPC25 = 249, - GUM_ELF_ARM_RSBREL32, - GUM_ELF_ARM_THM_RPC22, - GUM_ELF_ARM_RREL32, - GUM_ELF_ARM_RABS22, - GUM_ELF_ARM_RPC24, - GUM_ELF_ARM_RBASE, -} GumElfArmRelocation; - -typedef enum { - GUM_ELF_ARM64_NONE, - GUM_ELF_ARM64_P32_ABS32, - GUM_ELF_ARM64_P32_COPY = 180, - GUM_ELF_ARM64_P32_GLOB_DAT, - GUM_ELF_ARM64_P32_JUMP_SLOT, - GUM_ELF_ARM64_P32_RELATIVE, - GUM_ELF_ARM64_P32_TLS_DTPMOD, - GUM_ELF_ARM64_P32_TLS_DTPREL, - GUM_ELF_ARM64_P32_TLS_TPREL, - GUM_ELF_ARM64_P32_TLSDESC, - GUM_ELF_ARM64_P32_IRELATIVE, - GUM_ELF_ARM64_ABS64 = 257, - GUM_ELF_ARM64_ABS32, - GUM_ELF_ARM64_ABS16, - GUM_ELF_ARM64_PREL64, - GUM_ELF_ARM64_PREL32, - GUM_ELF_ARM64_PREL16, - GUM_ELF_ARM64_MOVW_UABS_G0, - GUM_ELF_ARM64_MOVW_UABS_G0_NC, - GUM_ELF_ARM64_MOVW_UABS_G1, - GUM_ELF_ARM64_MOVW_UABS_G1_NC, - GUM_ELF_ARM64_MOVW_UABS_G2, - GUM_ELF_ARM64_MOVW_UABS_G2_NC, - GUM_ELF_ARM64_MOVW_UABS_G3, - GUM_ELF_ARM64_MOVW_SABS_G0, - GUM_ELF_ARM64_MOVW_SABS_G1, - GUM_ELF_ARM64_MOVW_SABS_G2, - GUM_ELF_ARM64_LD_PREL_LO19, - GUM_ELF_ARM64_ADR_PREL_LO21, - GUM_ELF_ARM64_ADR_PREL_PG_HI21, - GUM_ELF_ARM64_ADR_PREL_PG_HI21_NC, - GUM_ELF_ARM64_ADD_ABS_LO12_NC, - GUM_ELF_ARM64_LDST8_ABS_LO12_NC, - GUM_ELF_ARM64_TSTBR14, - GUM_ELF_ARM64_CONDBR19, - GUM_ELF_ARM64_JUMP26 = 282, - GUM_ELF_ARM64_CALL26, - GUM_ELF_ARM64_LDST16_ABS_LO12_NC, - GUM_ELF_ARM64_LDST32_ABS_LO12_NC, - GUM_ELF_ARM64_LDST64_ABS_LO12_NC, - GUM_ELF_ARM64_MOVW_PREL_G0, - GUM_ELF_ARM64_MOVW_PREL_G0_NC, - GUM_ELF_ARM64_MOVW_PREL_G1, - GUM_ELF_ARM64_MOVW_PREL_G1_NC, - GUM_ELF_ARM64_MOVW_PREL_G2, - GUM_ELF_ARM64_MOVW_PREL_G2_NC, - GUM_ELF_ARM64_MOVW_PREL_G3, - GUM_ELF_ARM64_LDST128_ABS_LO12_NC = 299, - GUM_ELF_ARM64_MOVW_GOTOFF_G0, - GUM_ELF_ARM64_MOVW_GOTOFF_G0_NC, - GUM_ELF_ARM64_MOVW_GOTOFF_G1, - GUM_ELF_ARM64_MOVW_GOTOFF_G1_NC, - GUM_ELF_ARM64_MOVW_GOTOFF_G2, - GUM_ELF_ARM64_MOVW_GOTOFF_G2_NC, - GUM_ELF_ARM64_MOVW_GOTOFF_G3, - GUM_ELF_ARM64_GOTREL64, - GUM_ELF_ARM64_GOTREL32, - GUM_ELF_ARM64_GOT_LD_PREL19, - GUM_ELF_ARM64_LD64_GOTOFF_LO15, - GUM_ELF_ARM64_ADR_GOT_PAGE, - GUM_ELF_ARM64_LD64_GOT_LO12_NC, - GUM_ELF_ARM64_LD64_GOTPAGE_LO15, - GUM_ELF_ARM64_TLSGD_ADR_PREL21 = 512, - GUM_ELF_ARM64_TLSGD_ADR_PAGE21, - GUM_ELF_ARM64_TLSGD_ADD_LO12_NC, - GUM_ELF_ARM64_TLSGD_MOVW_G1, - GUM_ELF_ARM64_TLSGD_MOVW_G0_NC, - GUM_ELF_ARM64_TLSLD_ADR_PREL21, - GUM_ELF_ARM64_TLSLD_ADR_PAGE21, - GUM_ELF_ARM64_TLSLD_ADD_LO12_NC, - GUM_ELF_ARM64_TLSLD_MOVW_G1, - GUM_ELF_ARM64_TLSLD_MOVW_G0_NC, - GUM_ELF_ARM64_TLSLD_LD_PREL19, - GUM_ELF_ARM64_TLSLD_MOVW_DTPREL_G2, - GUM_ELF_ARM64_TLSLD_MOVW_DTPREL_G1, - GUM_ELF_ARM64_TLSLD_MOVW_DTPREL_G1_NC, - GUM_ELF_ARM64_TLSLD_MOVW_DTPREL_G0, - GUM_ELF_ARM64_TLSLD_MOVW_DTPREL_G0_NC, - GUM_ELF_ARM64_TLSLD_ADD_DTPREL_HI12, - GUM_ELF_ARM64_TLSLD_ADD_DTPREL_LO12, - GUM_ELF_ARM64_TLSLD_ADD_DTPREL_LO12_NC, - GUM_ELF_ARM64_TLSLD_LDST8_DTPREL_LO12, - GUM_ELF_ARM64_TLSLD_LDST8_DTPREL_LO12_NC, - GUM_ELF_ARM64_TLSLD_LDST16_DTPREL_LO12, - GUM_ELF_ARM64_TLSLD_LDST16_DTPREL_LO12_NC, - GUM_ELF_ARM64_TLSLD_LDST32_DTPREL_LO12, - GUM_ELF_ARM64_TLSLD_LDST32_DTPREL_LO12_NC, - GUM_ELF_ARM64_TLSLD_LDST64_DTPREL_LO12, - GUM_ELF_ARM64_TLSLD_LDST64_DTPREL_LO12_NC, - GUM_ELF_ARM64_TLSIE_MOVW_GOTTPREL_G1, - GUM_ELF_ARM64_TLSIE_MOVW_GOTTPREL_G0_NC, - GUM_ELF_ARM64_TLSIE_ADR_GOTTPREL_PAGE21, - GUM_ELF_ARM64_TLSIE_LD64_GOTTPREL_LO12_NC, - GUM_ELF_ARM64_TLSIE_LD_GOTTPREL_PREL19, - GUM_ELF_ARM64_TLSLE_MOVW_TPREL_G2, - GUM_ELF_ARM64_TLSLE_MOVW_TPREL_G1, - GUM_ELF_ARM64_TLSLE_MOVW_TPREL_G1_NC, - GUM_ELF_ARM64_TLSLE_MOVW_TPREL_G0, - GUM_ELF_ARM64_TLSLE_MOVW_TPREL_G0_NC, - GUM_ELF_ARM64_TLSLE_ADD_TPREL_HI12, - GUM_ELF_ARM64_TLSLE_ADD_TPREL_LO12, - GUM_ELF_ARM64_TLSLE_ADD_TPREL_LO12_NC, - GUM_ELF_ARM64_TLSLE_LDST8_TPREL_LO12, - GUM_ELF_ARM64_TLSLE_LDST8_TPREL_LO12_NC, - GUM_ELF_ARM64_TLSLE_LDST16_TPREL_LO12, - GUM_ELF_ARM64_TLSLE_LDST16_TPREL_LO12_NC, - GUM_ELF_ARM64_TLSLE_LDST32_TPREL_LO12, - GUM_ELF_ARM64_TLSLE_LDST32_TPREL_LO12_NC, - GUM_ELF_ARM64_TLSLE_LDST64_TPREL_LO12, - GUM_ELF_ARM64_TLSLE_LDST64_TPREL_LO12_NC, - GUM_ELF_ARM64_TLSDESC_LD_PREL19, - GUM_ELF_ARM64_TLSDESC_ADR_PREL21, - GUM_ELF_ARM64_TLSDESC_ADR_PAGE21, - GUM_ELF_ARM64_TLSDESC_LD64_LO12, - GUM_ELF_ARM64_TLSDESC_ADD_LO12, - GUM_ELF_ARM64_TLSDESC_OFF_G1, - GUM_ELF_ARM64_TLSDESC_OFF_G0_NC, - GUM_ELF_ARM64_TLSDESC_LDR, - GUM_ELF_ARM64_TLSDESC_ADD, - GUM_ELF_ARM64_TLSDESC_CALL, - GUM_ELF_ARM64_TLSLE_LDST128_TPREL_LO12, - GUM_ELF_ARM64_TLSLE_LDST128_TPREL_LO12_NC, - GUM_ELF_ARM64_TLSLD_LDST128_DTPREL_LO12, - GUM_ELF_ARM64_TLSLD_LDST128_DTPREL_LO12_NC, - GUM_ELF_ARM64_COPY = 1024, - GUM_ELF_ARM64_GLOB_DAT, - GUM_ELF_ARM64_JUMP_SLOT, - GUM_ELF_ARM64_RELATIVE, - GUM_ELF_ARM64_TLS_DTPMOD, - GUM_ELF_ARM64_TLS_DTPREL, - GUM_ELF_ARM64_TLS_TPREL, - GUM_ELF_ARM64_TLSDESC, - GUM_ELF_ARM64_IRELATIVE, -} GumElfArm64Relocation; - -typedef enum { - GUM_ELF_MIPS_NONE, - GUM_ELF_MIPS_16, - GUM_ELF_MIPS_32, - GUM_ELF_MIPS_REL32, - GUM_ELF_MIPS_26, - GUM_ELF_MIPS_HI16, - GUM_ELF_MIPS_LO16, - GUM_ELF_MIPS_GPREL16, - GUM_ELF_MIPS_LITERAL, - GUM_ELF_MIPS_GOT16, - GUM_ELF_MIPS_PC16, - GUM_ELF_MIPS_CALL16, - GUM_ELF_MIPS_GPREL32, - GUM_ELF_MIPS_SHIFT5 = 16, - GUM_ELF_MIPS_SHIFT6, - GUM_ELF_MIPS_64, - GUM_ELF_MIPS_GOT_DISP, - GUM_ELF_MIPS_GOT_PAGE, - GUM_ELF_MIPS_GOT_OFST, - GUM_ELF_MIPS_GOT_HI16, - GUM_ELF_MIPS_GOT_LO16, - GUM_ELF_MIPS_SUB, - GUM_ELF_MIPS_INSERT_A, - GUM_ELF_MIPS_INSERT_B, - GUM_ELF_MIPS_DELETE, - GUM_ELF_MIPS_HIGHER, - GUM_ELF_MIPS_HIGHEST, - GUM_ELF_MIPS_CALL_HI16, - GUM_ELF_MIPS_CALL_LO16, - GUM_ELF_MIPS_SCN_DISP, - GUM_ELF_MIPS_REL16, - GUM_ELF_MIPS_ADD_IMMEDIATE, - GUM_ELF_MIPS_PJUMP, - GUM_ELF_MIPS_RELGOT, - GUM_ELF_MIPS_JALR, - GUM_ELF_MIPS_TLS_DTPMOD32, - GUM_ELF_MIPS_TLS_DTPREL32, - GUM_ELF_MIPS_TLS_DTPMOD64, - GUM_ELF_MIPS_TLS_DTPREL64, - GUM_ELF_MIPS_TLS_GD, - GUM_ELF_MIPS_TLS_LDM, - GUM_ELF_MIPS_TLS_DTPREL_HI16, - GUM_ELF_MIPS_TLS_DTPREL_LO16, - GUM_ELF_MIPS_TLS_GOTTPREL, - GUM_ELF_MIPS_TLS_TPREL32, - GUM_ELF_MIPS_TLS_TPREL64, - GUM_ELF_MIPS_TLS_TPREL_HI16, - GUM_ELF_MIPS_TLS_TPREL_LO16, - GUM_ELF_MIPS_GLOB_DAT, - GUM_ELF_MIPS_COPY = 126, - GUM_ELF_MIPS_JUMP_SLOT, -} GumElfMipsRelocation; - -typedef struct _GumElfSegmentDetails GumElfSegmentDetails; -typedef struct _GumElfSectionDetails GumElfSectionDetails; -typedef struct _GumElfRelocationDetails GumElfRelocationDetails; -typedef struct _GumElfDynamicEntryDetails GumElfDynamicEntryDetails; -typedef struct _GumElfSymbolDetails GumElfSymbolDetails; - -typedef struct _GumElfNoteHeader GumElfNoteHeader; - -typedef gboolean (* GumFoundElfSegmentFunc) ( - const GumElfSegmentDetails * details, gpointer user_data); -typedef gboolean (* GumFoundElfSectionFunc) ( - const GumElfSectionDetails * details, gpointer user_data); -typedef gboolean (* GumFoundElfRelocationFunc) ( - const GumElfRelocationDetails * details, gpointer user_data); -typedef gboolean (* GumFoundElfDynamicEntryFunc) ( - const GumElfDynamicEntryDetails * details, gpointer user_data); -typedef gboolean (* GumFoundElfSymbolFunc) (const GumElfSymbolDetails * details, - gpointer user_data); - -struct _GumElfSegmentDetails -{ - GumAddress vm_address; - guint64 vm_size; - guint64 file_offset; - guint64 file_size; - GumPageProtection protection; -}; - -struct _GumElfSectionDetails -{ - const gchar * id; - const gchar * name; - GumElfSectionType type; - guint64 flags; - GumAddress address; - guint64 offset; - gsize size; - guint32 link; - guint32 info; - guint64 alignment; - guint64 entry_size; - GumPageProtection protection; -}; - -struct _GumElfRelocationDetails -{ - GumAddress address; - guint32 type; - const GumElfSymbolDetails * symbol; - gint64 addend; - const GumElfSectionDetails * parent; -}; - -struct _GumElfDynamicEntryDetails -{ - GumElfDynamicTag tag; - guint64 val; -}; - -struct _GumElfSymbolDetails -{ - const gchar * name; - GumAddress address; - gsize size; - GumElfSymbolType type; - GumElfSymbolBind bind; - guint16 shdr_index; - const GumElfSectionDetails * section; -}; - -struct _GumElfNoteHeader -{ - guint32 name_size; - guint32 desc_size; - guint32 type; -}; - -GUM_API GumElfModule * gum_elf_module_new_from_file (const gchar * path, - GError ** error); -GUM_API GumElfModule * gum_elf_module_new_from_blob (GBytes * blob, - GError ** error); -GUM_API GumElfModule * gum_elf_module_new_from_memory (const gchar * path, - GumAddress base_address, GError ** error); - -GUM_API gboolean gum_elf_module_load (GumElfModule * self, GError ** error); - -GUM_API GumElfType gum_elf_module_get_etype (GumElfModule * self); -GUM_API guint gum_elf_module_get_pointer_size (GumElfModule * self); -GUM_API gint gum_elf_module_get_byte_order (GumElfModule * self); -GUM_API GumElfOSABI gum_elf_module_get_os_abi (GumElfModule * self); -GUM_API guint8 gum_elf_module_get_os_abi_version (GumElfModule * self); -GUM_API GumElfMachine gum_elf_module_get_machine (GumElfModule * self); -GUM_API GumAddress gum_elf_module_get_base_address (GumElfModule * self); -GUM_API GumAddress gum_elf_module_get_preferred_address (GumElfModule * self); -GUM_API guint64 gum_elf_module_get_mapped_size (GumElfModule * self); -GUM_API GumAddress gum_elf_module_get_entrypoint (GumElfModule * self); -GUM_API const gchar * gum_elf_module_get_interpreter (GumElfModule * self); -GUM_API const gchar * gum_elf_module_get_source_path (GumElfModule * self); -GUM_API GBytes * gum_elf_module_get_source_blob (GumElfModule * self); -GUM_API GumElfSourceMode gum_elf_module_get_source_mode (GumElfModule * self); -GUM_API gconstpointer gum_elf_module_get_file_data (GumElfModule * self, - gsize * size); - -GUM_API void gum_elf_module_enumerate_segments (GumElfModule * self, - GumFoundElfSegmentFunc func, gpointer user_data); -GUM_API void gum_elf_module_enumerate_sections (GumElfModule * self, - GumFoundElfSectionFunc func, gpointer user_data); -GUM_API void gum_elf_module_enumerate_relocations (GumElfModule * self, - GumFoundElfRelocationFunc func, gpointer user_data); -GUM_API void gum_elf_module_enumerate_dynamic_entries (GumElfModule * self, - GumFoundElfDynamicEntryFunc func, gpointer user_data); -GUM_API void gum_elf_module_enumerate_imports (GumElfModule * self, - GumFoundImportFunc func, gpointer user_data); -GUM_API void gum_elf_module_enumerate_exports (GumElfModule * self, - GumFoundExportFunc func, gpointer user_data); -GUM_API void gum_elf_module_enumerate_dynamic_symbols (GumElfModule * self, - GumFoundElfSymbolFunc func, gpointer user_data); -GUM_API void gum_elf_module_enumerate_symbols (GumElfModule * self, - GumFoundElfSymbolFunc func, gpointer user_data); -GUM_API void gum_elf_module_enumerate_dependencies (GumElfModule * self, - GumFoundDependencyFunc func, gpointer user_data); - -GUM_API GumAddress gum_elf_module_translate_to_offline (GumElfModule * self, - GumAddress online_address); -GUM_API GumAddress gum_elf_module_translate_to_online (GumElfModule * self, - GumAddress offline_address); - -G_END_DECLS - -#endif -/* - * Copyright (C) 2009-2022 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_EVENT_H__ -#define __GUM_EVENT_H__ - - -G_BEGIN_DECLS - -typedef guint GumEventType; - -typedef union _GumEvent GumEvent; - -typedef struct _GumAnyEvent GumAnyEvent; -typedef struct _GumCallEvent GumCallEvent; -typedef struct _GumRetEvent GumRetEvent; -typedef struct _GumExecEvent GumExecEvent; -typedef struct _GumBlockEvent GumBlockEvent; -typedef struct _GumCompileEvent GumCompileEvent; - -enum _GumEventType -{ - GUM_NOTHING = 0, - GUM_CALL = 1 << 0, - GUM_RET = 1 << 1, - GUM_EXEC = 1 << 2, - GUM_BLOCK = 1 << 3, - GUM_COMPILE = 1 << 4, -}; - -struct _GumAnyEvent -{ - GumEventType type; -}; - -struct _GumCallEvent -{ - GumEventType type; - - gpointer location; - gpointer target; - gint depth; -}; - -struct _GumRetEvent -{ - GumEventType type; - - gpointer location; - gpointer target; - gint depth; -}; - -struct _GumExecEvent -{ - GumEventType type; - - gpointer location; -}; - -struct _GumBlockEvent -{ - GumEventType type; - - gpointer start; - gpointer end; -}; - -struct _GumCompileEvent -{ - GumEventType type; - - gpointer start; - gpointer end; -}; - -union _GumEvent -{ - GumEventType type; - - GumAnyEvent any; - GumCallEvent call; - GumRetEvent ret; - GumExecEvent exec; - GumBlockEvent block; - GumCompileEvent compile; -}; - -G_END_DECLS - -#endif -/* - * Copyright (C) 2009-2022 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_EVENT_SINK_H__ -#define __GUM_EVENT_SINK_H__ - - -G_BEGIN_DECLS - -#define GUM_TYPE_EVENT_SINK (gum_event_sink_get_type ()) -G_DECLARE_INTERFACE (GumEventSink, gum_event_sink, GUM, EVENT_SINK, GObject) - -#define GUM_TYPE_DEFAULT_EVENT_SINK (gum_default_event_sink_get_type ()) -G_DECLARE_FINAL_TYPE (GumDefaultEventSink, gum_default_event_sink, GUM, - DEFAULT_EVENT_SINK, GObject) - -#define GUM_TYPE_CALLBACK_EVENT_SINK (gum_callback_event_sink_get_type ()) -G_DECLARE_FINAL_TYPE (GumCallbackEventSink, gum_callback_event_sink, GUM, - CALLBACK_EVENT_SINK, GObject) - -typedef void (* GumEventSinkCallback) (const GumEvent * event, - GumCpuContext * cpu_context, gpointer user_data); - -struct _GumEventSinkInterface -{ - GTypeInterface parent; - - GumEventType (* query_mask) (GumEventSink * self); - void (* start) (GumEventSink * self); - void (* process) (GumEventSink * self, const GumEvent * event, - GumCpuContext * cpu_context); - void (* flush) (GumEventSink * self); - void (* stop) (GumEventSink * self); -}; - -GUM_API GumEventType gum_event_sink_query_mask (GumEventSink * self); -GUM_API void gum_event_sink_start (GumEventSink * self); -GUM_API void gum_event_sink_process (GumEventSink * self, - const GumEvent * event, GumCpuContext * cpu_context); -GUM_API void gum_event_sink_flush (GumEventSink * self); -GUM_API void gum_event_sink_stop (GumEventSink * self); - -GUM_API GumEventSink * gum_event_sink_make_default (void); -GUM_API GumEventSink * gum_event_sink_make_from_callback (GumEventType mask, - GumEventSinkCallback callback, gpointer data, GDestroyNotify data_destroy); - -G_END_DECLS - -#endif -/* - * Copyright (C) 2015-2024 Ole André Vadla Ravnås - * Copyright (C) 2020 Francesco Tamagni - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_EXCEPTOR_H__ -#define __GUM_EXCEPTOR_H__ - -#include - -G_BEGIN_DECLS - -#define GUM_TYPE_EXCEPTOR (gum_exceptor_get_type ()) -G_DECLARE_FINAL_TYPE (GumExceptor, gum_exceptor, GUM, EXCEPTOR, GObject) - -#if defined (G_OS_WIN32) || defined (__APPLE__) -# define GUM_NATIVE_SETJMP(env) setjmp (env) -# define GUM_NATIVE_LONGJMP longjmp -# ifndef GUM_GIR_COMPILATION - typedef jmp_buf GumExceptorNativeJmpBuf; -# endif -#else -# define GUM_NATIVE_SETJMP(env) sigsetjmp (env, TRUE) -# define GUM_NATIVE_LONGJMP siglongjmp -# ifndef GUM_GIR_COMPILATION - typedef sigjmp_buf GumExceptorNativeJmpBuf; -# endif -#endif -#ifdef GUM_GIR_COMPILATION -typedef int GumExceptorNativeJmpBuf; -#endif - -typedef struct _GumExceptionDetails GumExceptionDetails; -typedef guint GumExceptionType; -typedef struct _GumExceptionMemoryDetails GumExceptionMemoryDetails; -typedef gboolean (* GumExceptionHandler) (GumExceptionDetails * details, - gpointer user_data); - -typedef struct _GumExceptorScope GumExceptorScope; - -enum _GumExceptionType -{ - GUM_EXCEPTION_ABORT = 1, - GUM_EXCEPTION_ACCESS_VIOLATION, - GUM_EXCEPTION_GUARD_PAGE, - GUM_EXCEPTION_ILLEGAL_INSTRUCTION, - GUM_EXCEPTION_STACK_OVERFLOW, - GUM_EXCEPTION_ARITHMETIC, - GUM_EXCEPTION_BREAKPOINT, - GUM_EXCEPTION_SINGLE_STEP, - GUM_EXCEPTION_SYSTEM -}; - -struct _GumExceptionMemoryDetails -{ - GumMemoryOperation operation; - gpointer address; -}; - -struct _GumExceptionDetails -{ - GumThreadId thread_id; - GumExceptionType type; - gpointer address; - GumExceptionMemoryDetails memory; - GumCpuContext context; - gpointer native_context; -}; - -struct _GumExceptorScope -{ - GumExceptionDetails exception; - - /*< private */ - gboolean exception_occurred; - gpointer padding[2]; - GumExceptorNativeJmpBuf env; -#ifdef __ANDROID__ - sigset_t mask; -#endif - - GumExceptorScope * next; -}; - -GUM_API void gum_exceptor_disable (void); - -GUM_API GumExceptor * gum_exceptor_obtain (void); - -GUM_API void gum_exceptor_reset (GumExceptor * self); - -GUM_API void gum_exceptor_add (GumExceptor * self, GumExceptionHandler func, - gpointer user_data); -GUM_API void gum_exceptor_remove (GumExceptor * self, GumExceptionHandler func, - gpointer user_data); - -#if defined (_MSC_VER) && defined (HAVE_I386) && GLIB_SIZEOF_VOID_P == 8 -/* - * On MSVC/x86_64 setjmp() is actually an intrinsic that calls _setjmp() with a - * a hidden second argument specifying the frame pointer. This makes sense when - * the longjmp() is guaranteed to happen from code we control, but is not - * reliable otherwise. - */ -# define gum_exceptor_try(self, scope) ( \ - _gum_exceptor_prepare_try (self, scope), \ - ((int (*) (jmp_buf env, void * frame_pointer)) _setjmp) ( \ - (scope)->env, NULL) == 0) -#else -# define gum_exceptor_try(self, scope) ( \ - _gum_exceptor_prepare_try (self, scope), \ - GUM_NATIVE_SETJMP ((scope)->env) == 0) -#endif -GUM_API gboolean gum_exceptor_catch (GumExceptor * self, - GumExceptorScope * scope); -GUM_API gboolean gum_exceptor_has_scope (GumExceptor * self, - GumThreadId thread_id); - -GUM_API gchar * gum_exception_details_to_string ( - const GumExceptionDetails * details); - -GUM_API void _gum_exceptor_prepare_try (GumExceptor * self, - GumExceptorScope * scope); - -G_END_DECLS - -#endif -/* - * Copyright (C) 2009 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_FUNCTION_H__ -#define __GUM_FUNCTION_H__ - -G_BEGIN_DECLS - -typedef struct _GumFunctionDetails GumFunctionDetails; - -struct _GumFunctionDetails -{ - const gchar * name; - gpointer address; - gint num_arguments; -}; - -G_END_DECLS - -#endif -/* - * Copyright (C) 2008-2025 Ole André Vadla Ravnås - * Copyright (C) 2008 Christian Berentsen - * Copyright (C) 2024 Francesco Tamagni - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_INTERCEPTOR_H__ -#define __GUM_INTERCEPTOR_H__ - -/* - * Copyright (C) 2008-2022 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_INVOCATION_LISTENER_H__ -#define __GUM_INVOCATION_LISTENER_H__ - -/* - * Copyright (C) 2008-2022 Ole André Vadla Ravnås - * - * Licence: wxWindows Library Licence, Version 3.1 - */ - -#ifndef __GUM_INVOCATION_CONTEXT_H__ -#define __GUM_INVOCATION_CONTEXT_H__ - - - -#define GUM_IC_GET_THREAD_DATA(context, data_type) \ - ((data_type *) gum_invocation_context_get_listener_thread_data (context, \ - sizeof (data_type))) -#define GUM_IC_GET_FUNC_DATA(context, data_type) \ - ((data_type) gum_invocation_context_get_listener_function_data (context)) -#define GUM_IC_GET_INVOCATION_DATA(context, data_type) \ - ((data_type *) \ - gum_invocation_context_get_listener_invocation_data (context,\ - sizeof (data_type))) - -#define GUM_IC_GET_REPLACEMENT_DATA(ctx, data_type) \ - ((data_type) gum_invocation_context_get_replacement_data (ctx)) - -typedef struct _GumInvocationBackend GumInvocationBackend; -typedef struct _GumInvocationContext GumInvocationContext; -typedef guint GumPointCut; - -struct _GumInvocationBackend -{ - GumPointCut (* get_point_cut) (GumInvocationContext * context); - - GumThreadId (* get_thread_id) (GumInvocationContext * context); - guint (* get_depth) (GumInvocationContext * context); - - gpointer (* get_listener_thread_data) (GumInvocationContext * context, - gsize required_size); - gpointer (* get_listener_function_data) (GumInvocationContext * context); - gpointer (* get_listener_invocation_data) ( - GumInvocationContext * context, gsize required_size); - - gpointer (* get_replacement_data) (GumInvocationContext * context); - - gpointer state; - gpointer data; -}; - -struct _GumInvocationContext -{ - gpointer function; - GumCpuContext * cpu_context; - gint system_error; - - /*< private */ - GumInvocationBackend * backend; -}; - -enum _GumPointCut -{ - GUM_POINT_ENTER, - GUM_POINT_LEAVE -}; - -G_BEGIN_DECLS - -GUM_API GumPointCut gum_invocation_context_get_point_cut ( - GumInvocationContext * context); - -GUM_API gpointer gum_invocation_context_get_nth_argument ( - GumInvocationContext * context, guint n); -GUM_API void gum_invocation_context_replace_nth_argument ( - GumInvocationContext * context, guint n, gpointer value); -GUM_API gpointer gum_invocation_context_get_return_value ( - GumInvocationContext * context); -GUM_API void gum_invocation_context_replace_return_value ( - GumInvocationContext * context, gpointer value); - -GUM_API gpointer gum_invocation_context_get_return_address ( - GumInvocationContext * context); - -GUM_API guint gum_invocation_context_get_thread_id ( - GumInvocationContext * context); -GUM_API guint gum_invocation_context_get_depth ( - GumInvocationContext * context); - -GUM_API gpointer gum_invocation_context_get_listener_thread_data ( - GumInvocationContext * context, gsize required_size); -GUM_API gpointer gum_invocation_context_get_listener_function_data ( - GumInvocationContext * context); -GUM_API gpointer gum_invocation_context_get_listener_invocation_data ( - GumInvocationContext * context, gsize required_size); - -GUM_API gpointer gum_invocation_context_get_replacement_data ( - GumInvocationContext * context); - -G_END_DECLS - -#endif - -G_BEGIN_DECLS - -#define GUM_TYPE_INVOCATION_LISTENER (gum_invocation_listener_get_type ()) -G_DECLARE_INTERFACE (GumInvocationListener, gum_invocation_listener, GUM, - INVOCATION_LISTENER, GObject) - -typedef void (* GumInvocationCallback) (GumInvocationContext * context, - gpointer user_data); - -struct _GumInvocationListenerInterface -{ - GTypeInterface parent; - - void (* on_enter) (GumInvocationListener * self, - GumInvocationContext * context); - void (* on_leave) (GumInvocationListener * self, - GumInvocationContext * context); -}; - -GUM_API GumInvocationListener * gum_make_call_listener ( - GumInvocationCallback on_enter, GumInvocationCallback on_leave, - gpointer data, GDestroyNotify data_destroy); -GUM_API GumInvocationListener * gum_make_probe_listener ( - GumInvocationCallback on_hit, gpointer data, GDestroyNotify data_destroy); - -GUM_API void gum_invocation_listener_on_enter (GumInvocationListener * self, - GumInvocationContext * context); -GUM_API void gum_invocation_listener_on_leave (GumInvocationListener * self, - GumInvocationContext * context); - -G_END_DECLS - -#endif - -#ifndef CAPSTONE_ENGINE_H -#define CAPSTONE_ENGINE_H - -/* Capstone Disassembly Engine */ -/* By Nguyen Anh Quynh , 2013-2016 */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#if defined(CAPSTONE_HAS_OSXKERNEL) -#include -#else -#include -#include -#endif - -/* Capstone Disassembly Engine */ -/* By Axel Souchet & Nguyen Anh Quynh, 2014 */ - -#ifndef CAPSTONE_PLATFORM_H -#define CAPSTONE_PLATFORM_H - - -// handle C99 issue (for pre-2013 VisualStudio) -#if !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__) && (defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64)) -// MSVC - -// stdbool.h -#if (_MSC_VER < 1800) || defined(_KERNEL_MODE) -// this system does not have stdbool.h -#ifndef __cplusplus -typedef unsigned char bool; -#define false 0 -#define true 1 -#endif // __cplusplus - -#else -// VisualStudio 2013+ -> C99 is supported -#include -#endif // (_MSC_VER < 1800) || defined(_KERNEL_MODE) - -#else -// not MSVC -> C99 is supported -#include -#endif // !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__) && (defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64)) - - -// handle inttypes.h / stdint.h compatibility -#if defined(_WIN32_WCE) && (_WIN32_WCE < 0x800) -#include "windowsce/stdint.h" -#endif // defined(_WIN32_WCE) && (_WIN32_WCE < 0x800) - -#if defined(CAPSTONE_HAS_OSXKERNEL) || (defined(_MSC_VER) && (_MSC_VER <= 1700 || defined(_KERNEL_MODE))) -// this system does not have inttypes.h - -#if defined(_MSC_VER) && (_MSC_VER <= 1600 || defined(_KERNEL_MODE)) -// this system does not have stdint.h -typedef signed char int8_t; -typedef signed short int16_t; -typedef signed int int32_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef signed long long int64_t; -typedef unsigned long long uint64_t; -#endif // defined(_MSC_VER) && (_MSC_VER <= 1600 || defined(_KERNEL_MODE)) - -#if defined(_MSC_VER) && (_MSC_VER < 1600 || defined(_KERNEL_MODE)) -#define INT8_MIN (-127i8 - 1) -#define INT16_MIN (-32767i16 - 1) -#define INT32_MIN (-2147483647i32 - 1) -#define INT64_MIN (-9223372036854775807i64 - 1) -#define INT8_MAX 127i8 -#define INT16_MAX 32767i16 -#define INT32_MAX 2147483647i32 -#define INT64_MAX 9223372036854775807i64 -#define UINT8_MAX 0xffui8 -#define UINT16_MAX 0xffffui16 -#define UINT32_MAX 0xffffffffui32 -#define UINT64_MAX 0xffffffffffffffffui64 -#endif // defined(_MSC_VER) && (_MSC_VER < 1600 || defined(_KERNEL_MODE)) - -#ifdef CAPSTONE_HAS_OSXKERNEL -// this system has stdint.h -#include -#endif - -#define __PRI_8_LENGTH_MODIFIER__ "hh" -#define __PRI_64_LENGTH_MODIFIER__ "ll" - -#define PRId8 __PRI_8_LENGTH_MODIFIER__ "d" -#define PRIi8 __PRI_8_LENGTH_MODIFIER__ "i" -#define PRIo8 __PRI_8_LENGTH_MODIFIER__ "o" -#define PRIu8 __PRI_8_LENGTH_MODIFIER__ "u" -#define PRIx8 __PRI_8_LENGTH_MODIFIER__ "x" -#define PRIX8 __PRI_8_LENGTH_MODIFIER__ "X" - -#define PRId16 "hd" -#define PRIi16 "hi" -#define PRIo16 "ho" -#define PRIu16 "hu" -#define PRIx16 "hx" -#define PRIX16 "hX" - -#if defined(_MSC_VER) && _MSC_VER <= 1700 -#define PRId32 "ld" -#define PRIi32 "li" -#define PRIo32 "lo" -#define PRIu32 "lu" -#define PRIx32 "lx" -#define PRIX32 "lX" -#else // OSX -#define PRId32 "d" -#define PRIi32 "i" -#define PRIo32 "o" -#define PRIu32 "u" -#define PRIx32 "x" -#define PRIX32 "X" -#endif // defined(_MSC_VER) && _MSC_VER <= 1700 - -#if defined(_MSC_VER) && _MSC_VER <= 1700 -// redefine functions from inttypes.h used in cstool -#define strtoull _strtoui64 -#endif - -#define PRId64 __PRI_64_LENGTH_MODIFIER__ "d" -#define PRIi64 __PRI_64_LENGTH_MODIFIER__ "i" -#define PRIo64 __PRI_64_LENGTH_MODIFIER__ "o" -#define PRIu64 __PRI_64_LENGTH_MODIFIER__ "u" -#define PRIx64 __PRI_64_LENGTH_MODIFIER__ "x" -#define PRIX64 __PRI_64_LENGTH_MODIFIER__ "X" - -#else -// this system has inttypes.h by default -#include -#endif // defined(CAPSTONE_HAS_OSXKERNEL) || (defined(_MSC_VER) && (_MSC_VER <= 1700 || defined(_KERNEL_MODE))) - -#endif - -#ifdef _MSC_VER -#pragma warning(disable:4201) -#pragma warning(disable:4100) -#define CAPSTONE_API __cdecl -#ifdef CAPSTONE_SHARED -#define CAPSTONE_EXPORT __declspec(dllexport) -#else // defined(CAPSTONE_STATIC) -#define CAPSTONE_EXPORT -#endif -#else -#define CAPSTONE_API -#if (defined(__GNUC__) || defined(__IBMC__)) && !defined(CAPSTONE_STATIC) -#define CAPSTONE_EXPORT __attribute__((visibility("default"))) -#else // defined(CAPSTONE_STATIC) -#define CAPSTONE_EXPORT -#endif -#endif - -#if (defined(__GNUC__) || defined(__IBMC__)) -#define CAPSTONE_DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) -#define CAPSTONE_DEPRECATED __declspec(deprecated) -#else -#pragma message("WARNING: You need to implement CAPSTONE_DEPRECATED for this compiler") -#define CAPSTONE_DEPRECATED -#endif - -// Capstone API version -#define CS_API_MAJOR 5 -#define CS_API_MINOR 0 - -// Version for bleeding edge code of the Github's "next" branch. -// Use this if you want the absolutely latest development code. -// This version number will be bumped up whenever we have a new major change. -#define CS_NEXT_VERSION 5 - -// Capstone package version -#define CS_VERSION_MAJOR CS_API_MAJOR -#define CS_VERSION_MINOR CS_API_MINOR -#define CS_VERSION_EXTRA 1 - -/// Macro for meta programming. -/// Meant for projects using Capstone and need to support multiple -/// versions of it. -/// These macros replace several instances of the old "ARM64" with -/// the new "AArch64" name depending on the CS version. -#if CS_NEXT_VERSION < 6 -#define CS_AARCH64(x) ARM64##x -#else -#define CS_AARCH64(x) AArch64##x -#endif - -#if CS_NEXT_VERSION < 6 -#define CS_AARCH64pre(x) x##ARM64 -#else -#define CS_AARCH64pre(x) x##AARCH64 -#endif - -#if CS_NEXT_VERSION < 6 -#define CS_AARCH64CC(x) ARM64_CC##x -#else -#define CS_AARCH64CC(x) AArch64CC##x -#endif - -#if CS_NEXT_VERSION < 6 -#define CS_AARCH64_VL_(x) ARM64_VAS_##x -#else -#define CS_AARCH64_VL_(x) AArch64Layout_VL_##x -#endif - -#if CS_NEXT_VERSION < 6 -#define CS_aarch64_ arm64 -#else -#define CS_aarch64_ aarch64 -#endif - -#if CS_NEXT_VERSION < 6 -#define CS_aarch64(x) arm64##x -#else -#define CS_aarch64(x) aarch64##x -#endif - -#if CS_NEXT_VERSION < 6 -#define CS_aarch64_op() cs_arm64_op -#define CS_aarch64_reg() arm64_reg -#define CS_aarch64_cc() arm64_cc -#define CS_cs_aarch64() cs_arm64 -#define CS_aarch64_extender() arm64_extender -#define CS_aarch64_shifter() arm64_shifter -#define CS_aarch64_vas() arm64_vas -#else -#define CS_aarch64_op() cs_aarch64_op -#define CS_aarch64_reg() aarch64_reg -#define CS_aarch64_cc() AArch64CC_CondCode -#define CS_cs_aarch64() cs_aarch64 -#define CS_aarch64_extender() aarch64_extender -#define CS_aarch64_shifter() aarch64_shifter -#define CS_aarch64_vas() AArch64Layout_VectorLayout -#endif - -/// Macro to create combined version which can be compared to -/// result of cs_version() API. -#define CS_MAKE_VERSION(major, minor) ((major << 8) + minor) - -/// Maximum size of an instruction mnemonic string. -#define CS_MNEMONIC_SIZE 32 - -// Handle using with all API -typedef size_t csh; - -/// Architecture type -typedef enum cs_arch { - CS_ARCH_ARM = 0, ///< ARM architecture (including Thumb, Thumb-2) - CS_ARCH_ARM64, ///< ARM-64, also called AArch64 - CS_ARCH_MIPS, ///< Mips architecture - CS_ARCH_X86, ///< X86 architecture (including x86 & x86-64) - CS_ARCH_PPC, ///< PowerPC architecture - CS_ARCH_SPARC, ///< Sparc architecture - CS_ARCH_SYSZ, ///< SystemZ architecture - CS_ARCH_XCORE, ///< XCore architecture - CS_ARCH_M68K, ///< 68K architecture - CS_ARCH_TMS320C64X, ///< TMS320C64x architecture - CS_ARCH_M680X, ///< 680X architecture - CS_ARCH_EVM, ///< Ethereum architecture - CS_ARCH_MOS65XX, ///< MOS65XX architecture (including MOS6502) - CS_ARCH_WASM, ///< WebAssembly architecture - CS_ARCH_BPF, ///< Berkeley Packet Filter architecture (including eBPF) - CS_ARCH_RISCV, ///< RISCV architecture - CS_ARCH_SH, ///< SH architecture - CS_ARCH_TRICORE, ///< TriCore architecture - CS_ARCH_MAX, - CS_ARCH_ALL = 0xFFFF, // All architectures - for cs_support() -} cs_arch; - -// Support value to verify diet mode of the engine. -// If cs_support(CS_SUPPORT_DIET) return True, the engine was compiled -// in diet mode. -#define CS_SUPPORT_DIET (CS_ARCH_ALL + 1) - -// Support value to verify X86 reduce mode of the engine. -// If cs_support(CS_SUPPORT_X86_REDUCE) return True, the engine was compiled -// in X86 reduce mode. -#define CS_SUPPORT_X86_REDUCE (CS_ARCH_ALL + 2) - -/// Mode type -typedef enum cs_mode { - CS_MODE_LITTLE_ENDIAN = 0, ///< little-endian mode (default mode) - CS_MODE_ARM = 0, ///< 32-bit ARM - CS_MODE_16 = 1 << 1, ///< 16-bit mode (X86) - CS_MODE_32 = 1 << 2, ///< 32-bit mode (X86) - CS_MODE_64 = 1 << 3, ///< 64-bit mode (X86, PPC) - CS_MODE_THUMB = 1 << 4, ///< ARM's Thumb mode, including Thumb-2 - CS_MODE_MCLASS = 1 << 5, ///< ARM's Cortex-M series - CS_MODE_V8 = 1 << 6, ///< ARMv8 A32 encodings for ARM - CS_MODE_MICRO = 1 << 4, ///< MicroMips mode (MIPS) - CS_MODE_MIPS3 = 1 << 5, ///< Mips III ISA - CS_MODE_MIPS32R6 = 1 << 6, ///< Mips32r6 ISA - CS_MODE_MIPS2 = 1 << 7, ///< Mips II ISA - CS_MODE_V9 = 1 << 4, ///< SparcV9 mode (Sparc) - CS_MODE_QPX = 1 << 4, ///< Quad Processing eXtensions mode (PPC) - CS_MODE_SPE = 1 << 5, ///< Signal Processing Engine mode (PPC) - CS_MODE_BOOKE = 1 << 6, ///< Book-E mode (PPC) - CS_MODE_PS = 1 << 7, ///< Paired-singles mode (PPC) - CS_MODE_M68K_000 = 1 << 1, ///< M68K 68000 mode - CS_MODE_M68K_010 = 1 << 2, ///< M68K 68010 mode - CS_MODE_M68K_020 = 1 << 3, ///< M68K 68020 mode - CS_MODE_M68K_030 = 1 << 4, ///< M68K 68030 mode - CS_MODE_M68K_040 = 1 << 5, ///< M68K 68040 mode - CS_MODE_M68K_060 = 1 << 6, ///< M68K 68060 mode - CS_MODE_BIG_ENDIAN = 1U << 31, ///< big-endian mode - CS_MODE_MIPS32 = CS_MODE_32, ///< Mips32 ISA (Mips) - CS_MODE_MIPS64 = CS_MODE_64, ///< Mips64 ISA (Mips) - CS_MODE_M680X_6301 = 1 << 1, ///< M680X Hitachi 6301,6303 mode - CS_MODE_M680X_6309 = 1 << 2, ///< M680X Hitachi 6309 mode - CS_MODE_M680X_6800 = 1 << 3, ///< M680X Motorola 6800,6802 mode - CS_MODE_M680X_6801 = 1 << 4, ///< M680X Motorola 6801,6803 mode - CS_MODE_M680X_6805 = 1 << 5, ///< M680X Motorola/Freescale 6805 mode - CS_MODE_M680X_6808 = 1 << 6, ///< M680X Motorola/Freescale/NXP 68HC08 mode - CS_MODE_M680X_6809 = 1 << 7, ///< M680X Motorola 6809 mode - CS_MODE_M680X_6811 = 1 << 8, ///< M680X Motorola/Freescale/NXP 68HC11 mode - CS_MODE_M680X_CPU12 = 1 << 9, ///< M680X Motorola/Freescale/NXP CPU12 - ///< used on M68HC12/HCS12 - CS_MODE_M680X_HCS08 = 1 << 10, ///< M680X Freescale/NXP HCS08 mode - CS_MODE_BPF_CLASSIC = 0, ///< Classic BPF mode (default) - CS_MODE_BPF_EXTENDED = 1 << 0, ///< Extended BPF mode - CS_MODE_RISCV32 = 1 << 0, ///< RISCV RV32G - CS_MODE_RISCV64 = 1 << 1, ///< RISCV RV64G - CS_MODE_RISCVC = 1 << 2, ///< RISCV compressed instructure mode - CS_MODE_MOS65XX_6502 = 1 << 1, ///< MOS65XXX MOS 6502 - CS_MODE_MOS65XX_65C02 = 1 << 2, ///< MOS65XXX WDC 65c02 - CS_MODE_MOS65XX_W65C02 = 1 << 3, ///< MOS65XXX WDC W65c02 - CS_MODE_MOS65XX_65816 = 1 << 4, ///< MOS65XXX WDC 65816, 8-bit m/x - CS_MODE_MOS65XX_65816_LONG_M = (1 << 5), ///< MOS65XXX WDC 65816, 16-bit m, 8-bit x - CS_MODE_MOS65XX_65816_LONG_X = (1 << 6), ///< MOS65XXX WDC 65816, 8-bit m, 16-bit x - CS_MODE_MOS65XX_65816_LONG_MX = CS_MODE_MOS65XX_65816_LONG_M | CS_MODE_MOS65XX_65816_LONG_X, - CS_MODE_SH2 = 1 << 1, ///< SH2 - CS_MODE_SH2A = 1 << 2, ///< SH2A - CS_MODE_SH3 = 1 << 3, ///< SH3 - CS_MODE_SH4 = 1 << 4, ///< SH4 - CS_MODE_SH4A = 1 << 5, ///< SH4A - CS_MODE_SHFPU = 1 << 6, ///< w/ FPU - CS_MODE_SHDSP = 1 << 7, ///< w/ DSP - CS_MODE_TRICORE_110 = 1 << 1, ///< Tricore 1.1 - CS_MODE_TRICORE_120 = 1 << 2, ///< Tricore 1.2 - CS_MODE_TRICORE_130 = 1 << 3, ///< Tricore 1.3 - CS_MODE_TRICORE_131 = 1 << 4, ///< Tricore 1.3.1 - CS_MODE_TRICORE_160 = 1 << 5, ///< Tricore 1.6 - CS_MODE_TRICORE_161 = 1 << 6, ///< Tricore 1.6.1 - CS_MODE_TRICORE_162 = 1 << 7, ///< Tricore 1.6.2 -} cs_mode; - -typedef void* (CAPSTONE_API *cs_malloc_t)(size_t size); -typedef void* (CAPSTONE_API *cs_calloc_t)(size_t nmemb, size_t size); -typedef void* (CAPSTONE_API *cs_realloc_t)(void *ptr, size_t size); -typedef void (CAPSTONE_API *cs_free_t)(void *ptr); -typedef int (CAPSTONE_API *cs_vsnprintf_t)(char *str, size_t size, const char *format, va_list ap); - - -/// User-defined dynamic memory related functions: malloc/calloc/realloc/free/vsnprintf() -/// By default, Capstone uses system's malloc(), calloc(), realloc(), free() & vsnprintf(). -typedef struct cs_opt_mem { - cs_malloc_t malloc; - cs_calloc_t calloc; - cs_realloc_t realloc; - cs_free_t free; - cs_vsnprintf_t vsnprintf; -} cs_opt_mem; - -/// Customize mnemonic for instructions with alternative name. -/// To reset existing customized instruction to its default mnemonic, -/// call cs_option(CS_OPT_MNEMONIC) again with the same @id and NULL value -/// for @mnemonic. -typedef struct cs_opt_mnem { - /// ID of instruction to be customized. - unsigned int id; - /// Customized instruction mnemonic. - const char *mnemonic; -} cs_opt_mnem; - -/// Runtime option for the disassembled engine -typedef enum cs_opt_type { - CS_OPT_INVALID = 0, ///< No option specified - CS_OPT_SYNTAX, ///< Assembly output syntax - CS_OPT_DETAIL, ///< Break down instruction structure into details - CS_OPT_MODE, ///< Change engine's mode at run-time - CS_OPT_MEM, ///< User-defined dynamic memory related functions - CS_OPT_SKIPDATA, ///< Skip data when disassembling. Then engine is in SKIPDATA mode. - CS_OPT_SKIPDATA_SETUP, ///< Setup user-defined function for SKIPDATA option - CS_OPT_MNEMONIC, ///< Customize instruction mnemonic - CS_OPT_UNSIGNED, ///< print immediate operands in unsigned form - CS_OPT_NO_BRANCH_OFFSET, ///< ARM, prints branch immediates without offset. -} cs_opt_type; - -/// Runtime option value (associated with option type above) -typedef enum cs_opt_value { - CS_OPT_OFF = 0, ///< Turn OFF an option - default for CS_OPT_DETAIL, CS_OPT_SKIPDATA, CS_OPT_UNSIGNED. - CS_OPT_ON = 3, ///< Turn ON an option (CS_OPT_DETAIL, CS_OPT_SKIPDATA). - CS_OPT_SYNTAX_DEFAULT = 0, ///< Default asm syntax (CS_OPT_SYNTAX). - CS_OPT_SYNTAX_INTEL, ///< X86 Intel asm syntax - default on X86 (CS_OPT_SYNTAX). - CS_OPT_SYNTAX_ATT, ///< X86 ATT asm syntax (CS_OPT_SYNTAX). - CS_OPT_SYNTAX_NOREGNAME, ///< Prints register name with only number (CS_OPT_SYNTAX) - CS_OPT_SYNTAX_MASM, ///< X86 Intel Masm syntax (CS_OPT_SYNTAX). - CS_OPT_SYNTAX_MOTOROLA, ///< MOS65XX use $ as hex prefix -} cs_opt_value; - -/// Common instruction operand types - to be consistent across all architectures. -typedef enum cs_op_type { - CS_OP_INVALID = 0, ///< uninitialized/invalid operand. - CS_OP_REG, ///< Register operand. - CS_OP_IMM, ///< Immediate operand. - CS_OP_FP, ///< Floating-Point operand. - CS_OP_MEM = - 0x80, ///< Memory operand. Can be ORed with another operand type. -} cs_op_type; - -/// Common instruction operand access types - to be consistent across all architectures. -/// It is possible to combine access types, for example: CS_AC_READ | CS_AC_WRITE -typedef enum cs_ac_type { - CS_AC_INVALID = 0, ///< Uninitialized/invalid access type. - CS_AC_READ = 1 << 0, ///< Operand read from memory or register. - CS_AC_WRITE = 1 << 1, ///< Operand write to memory or register. -} cs_ac_type; - -/// Common instruction groups - to be consistent across all architectures. -typedef enum cs_group_type { - CS_GRP_INVALID = 0, ///< uninitialized/invalid group. - CS_GRP_JUMP, ///< all jump instructions (conditional+direct+indirect jumps) - CS_GRP_CALL, ///< all call instructions - CS_GRP_RET, ///< all return instructions - CS_GRP_INT, ///< all interrupt instructions (int+syscall) - CS_GRP_IRET, ///< all interrupt return instructions - CS_GRP_PRIVILEGE, ///< all privileged instructions - CS_GRP_BRANCH_RELATIVE, ///< all relative branching instructions -} cs_group_type; - -/** - User-defined callback function for SKIPDATA option. - See tests/test_skipdata.c for sample code demonstrating this API. - - @code: the input buffer containing code to be disassembled. - This is the same buffer passed to cs_disasm(). - @code_size: size (in bytes) of the above @code buffer. - @offset: the position of the currently-examining byte in the input - buffer @code mentioned above. - @user_data: user-data passed to cs_option() via @user_data field in - cs_opt_skipdata struct below. - - @return: return number of bytes to skip, or 0 to immediately stop disassembling. -*/ -typedef size_t (CAPSTONE_API *cs_skipdata_cb_t)(const uint8_t *code, size_t code_size, size_t offset, void *user_data); - -/// User-customized setup for SKIPDATA option -typedef struct cs_opt_skipdata { - /// Capstone considers data to skip as special "instructions". - /// User can specify the string for this instruction's "mnemonic" here. - /// By default (if @mnemonic is NULL), Capstone use ".byte". - const char *mnemonic; - - /// User-defined callback function to be called when Capstone hits data. - /// If the returned value from this callback is positive (>0), Capstone - /// will skip exactly that number of bytes & continue. Otherwise, if - /// the callback returns 0, Capstone stops disassembling and returns - /// immediately from cs_disasm() - /// NOTE: if this callback pointer is NULL, Capstone would skip a number - /// of bytes depending on architectures, as following: - /// Arm: 2 bytes (Thumb mode) or 4 bytes. - /// Arm64: 4 bytes. - /// Mips: 4 bytes. - /// M680x: 1 byte. - /// PowerPC: 4 bytes. - /// Sparc: 4 bytes. - /// SystemZ: 2 bytes. - /// X86: 1 bytes. - /// XCore: 2 bytes. - /// EVM: 1 bytes. - /// RISCV: 4 bytes. - /// WASM: 1 bytes. - /// MOS65XX: 1 bytes. - /// BPF: 8 bytes. - /// TriCore: 2 bytes. - cs_skipdata_cb_t callback; // default value is NULL - - /// User-defined data to be passed to @callback function pointer. - void *user_data; -} cs_opt_skipdata; - - -#ifndef CAPSTONE_ARM_H -#define CAPSTONE_ARM_H - -/* Capstone Disassembly Engine */ -/* By Nguyen Anh Quynh , 2013-2015 */ - -#ifdef __cplusplus -extern "C" { -#endif - - -#ifdef _MSC_VER -#pragma warning(disable:4201) -#endif - -/// ARM shift type -typedef enum arm_shifter { - ARM_SFT_INVALID = 0, - ARM_SFT_ASR, ///< shift with immediate const - ARM_SFT_LSL, ///< shift with immediate const - ARM_SFT_LSR, ///< shift with immediate const - ARM_SFT_ROR, ///< shift with immediate const - ARM_SFT_RRX, ///< shift with immediate const - ARM_SFT_ASR_REG, ///< shift with register - ARM_SFT_LSL_REG, ///< shift with register - ARM_SFT_LSR_REG, ///< shift with register - ARM_SFT_ROR_REG, ///< shift with register - ARM_SFT_RRX_REG, ///< shift with register -} arm_shifter; - -/// ARM condition code -typedef enum arm_cc { - ARM_CC_INVALID = 0, - ARM_CC_EQ, ///< Equal Equal - ARM_CC_NE, ///< Not equal Not equal, or unordered - ARM_CC_HS, ///< Carry set >, ==, or unordered - ARM_CC_LO, ///< Carry clear Less than - ARM_CC_MI, ///< Minus, negative Less than - ARM_CC_PL, ///< Plus, positive or zero >, ==, or unordered - ARM_CC_VS, ///< Overflow Unordered - ARM_CC_VC, ///< No overflow Not unordered - ARM_CC_HI, ///< Unsigned higher Greater than, or unordered - ARM_CC_LS, ///< Unsigned lower or same Less than or equal - ARM_CC_GE, ///< Greater than or equal Greater than or equal - ARM_CC_LT, ///< Less than Less than, or unordered - ARM_CC_GT, ///< Greater than Greater than - ARM_CC_LE, ///< Less than or equal <, ==, or unordered - ARM_CC_AL ///< Always (unconditional) Always (unconditional) -} arm_cc; - -typedef enum arm_sysreg { - /// Special registers for MSR - ARM_SYSREG_INVALID = 0, - - // SPSR* registers can be OR combined - ARM_SYSREG_SPSR_C = 1, - ARM_SYSREG_SPSR_X = 2, - ARM_SYSREG_SPSR_S = 4, - ARM_SYSREG_SPSR_F = 8, - - // CPSR* registers can be OR combined - ARM_SYSREG_CPSR_C = 16, - ARM_SYSREG_CPSR_X = 32, - ARM_SYSREG_CPSR_S = 64, - ARM_SYSREG_CPSR_F = 128, - - // independent registers - ARM_SYSREG_APSR = 256, - ARM_SYSREG_APSR_G, - ARM_SYSREG_APSR_NZCVQ, - ARM_SYSREG_APSR_NZCVQG, - - ARM_SYSREG_IAPSR, - ARM_SYSREG_IAPSR_G, - ARM_SYSREG_IAPSR_NZCVQG, - ARM_SYSREG_IAPSR_NZCVQ, - - ARM_SYSREG_EAPSR, - ARM_SYSREG_EAPSR_G, - ARM_SYSREG_EAPSR_NZCVQG, - ARM_SYSREG_EAPSR_NZCVQ, - - ARM_SYSREG_XPSR, - ARM_SYSREG_XPSR_G, - ARM_SYSREG_XPSR_NZCVQG, - ARM_SYSREG_XPSR_NZCVQ, - - ARM_SYSREG_IPSR, - ARM_SYSREG_EPSR, - ARM_SYSREG_IEPSR, - - ARM_SYSREG_MSP, - ARM_SYSREG_PSP, - ARM_SYSREG_PRIMASK, - ARM_SYSREG_BASEPRI, - ARM_SYSREG_BASEPRI_MAX, - ARM_SYSREG_FAULTMASK, - ARM_SYSREG_CONTROL, - ARM_SYSREG_MSPLIM, - ARM_SYSREG_PSPLIM, - ARM_SYSREG_MSP_NS, - ARM_SYSREG_PSP_NS, - ARM_SYSREG_MSPLIM_NS, - ARM_SYSREG_PSPLIM_NS, - ARM_SYSREG_PRIMASK_NS, - ARM_SYSREG_BASEPRI_NS, - ARM_SYSREG_FAULTMASK_NS, - ARM_SYSREG_CONTROL_NS, - ARM_SYSREG_SP_NS, - - // Banked Registers - ARM_SYSREG_R8_USR, - ARM_SYSREG_R9_USR, - ARM_SYSREG_R10_USR, - ARM_SYSREG_R11_USR, - ARM_SYSREG_R12_USR, - ARM_SYSREG_SP_USR, - ARM_SYSREG_LR_USR, - ARM_SYSREG_R8_FIQ, - ARM_SYSREG_R9_FIQ, - ARM_SYSREG_R10_FIQ, - ARM_SYSREG_R11_FIQ, - ARM_SYSREG_R12_FIQ, - ARM_SYSREG_SP_FIQ, - ARM_SYSREG_LR_FIQ, - ARM_SYSREG_LR_IRQ, - ARM_SYSREG_SP_IRQ, - ARM_SYSREG_LR_SVC, - ARM_SYSREG_SP_SVC, - ARM_SYSREG_LR_ABT, - ARM_SYSREG_SP_ABT, - ARM_SYSREG_LR_UND, - ARM_SYSREG_SP_UND, - ARM_SYSREG_LR_MON, - ARM_SYSREG_SP_MON, - ARM_SYSREG_ELR_HYP, - ARM_SYSREG_SP_HYP, - - ARM_SYSREG_SPSR_FIQ, - ARM_SYSREG_SPSR_IRQ, - ARM_SYSREG_SPSR_SVC, - ARM_SYSREG_SPSR_ABT, - ARM_SYSREG_SPSR_UND, - ARM_SYSREG_SPSR_MON, - ARM_SYSREG_SPSR_HYP, -} arm_sysreg; - -/// The memory barrier constants map directly to the 4-bit encoding of -/// the option field for Memory Barrier operations. -typedef enum arm_mem_barrier { - ARM_MB_INVALID = 0, - ARM_MB_RESERVED_0, - ARM_MB_OSHLD, - ARM_MB_OSHST, - ARM_MB_OSH, - ARM_MB_RESERVED_4, - ARM_MB_NSHLD, - ARM_MB_NSHST, - ARM_MB_NSH, - ARM_MB_RESERVED_8, - ARM_MB_ISHLD, - ARM_MB_ISHST, - ARM_MB_ISH, - ARM_MB_RESERVED_12, - ARM_MB_LD, - ARM_MB_ST, - ARM_MB_SY, -} arm_mem_barrier; - -/// Operand type for instruction's operands -typedef enum arm_op_type { - ARM_OP_INVALID = 0, ///< = CS_OP_INVALID (Uninitialized). - ARM_OP_REG, ///< = CS_OP_REG (Register operand). - ARM_OP_IMM, ///< = CS_OP_IMM (Immediate operand). - ARM_OP_MEM, ///< = CS_OP_MEM (Memory operand). - ARM_OP_FP, ///< = CS_OP_FP (Floating-Point operand). - ARM_OP_CIMM = 64, ///< C-Immediate (coprocessor registers) - ARM_OP_PIMM, ///< P-Immediate (coprocessor registers) - ARM_OP_SETEND, ///< operand for SETEND instruction - ARM_OP_SYSREG, ///< MSR/MRS special register operand -} arm_op_type; - -/// Operand type for SETEND instruction -typedef enum arm_setend_type { - ARM_SETEND_INVALID = 0, ///< Uninitialized. - ARM_SETEND_BE, ///< BE operand. - ARM_SETEND_LE, ///< LE operand -} arm_setend_type; - -typedef enum arm_cpsmode_type { - ARM_CPSMODE_INVALID = 0, - ARM_CPSMODE_IE = 2, - ARM_CPSMODE_ID = 3 -} arm_cpsmode_type; - -/// Operand type for SETEND instruction -typedef enum arm_cpsflag_type { - ARM_CPSFLAG_INVALID = 0, - ARM_CPSFLAG_F = 1, - ARM_CPSFLAG_I = 2, - ARM_CPSFLAG_A = 4, - ARM_CPSFLAG_NONE = 16, ///< no flag -} arm_cpsflag_type; - -/// Data type for elements of vector instructions. -typedef enum arm_vectordata_type { - ARM_VECTORDATA_INVALID = 0, - - // Integer type - ARM_VECTORDATA_I8, - ARM_VECTORDATA_I16, - ARM_VECTORDATA_I32, - ARM_VECTORDATA_I64, - - // Signed integer type - ARM_VECTORDATA_S8, - ARM_VECTORDATA_S16, - ARM_VECTORDATA_S32, - ARM_VECTORDATA_S64, - - // Unsigned integer type - ARM_VECTORDATA_U8, - ARM_VECTORDATA_U16, - ARM_VECTORDATA_U32, - ARM_VECTORDATA_U64, - - // Data type for VMUL/VMULL - ARM_VECTORDATA_P8, - - // Floating type - ARM_VECTORDATA_F16, - ARM_VECTORDATA_F32, - ARM_VECTORDATA_F64, - - // Convert float <-> float - ARM_VECTORDATA_F16F64, // f16.f64 - ARM_VECTORDATA_F64F16, // f64.f16 - ARM_VECTORDATA_F32F16, // f32.f16 - ARM_VECTORDATA_F16F32, // f32.f16 - ARM_VECTORDATA_F64F32, // f64.f32 - ARM_VECTORDATA_F32F64, // f32.f64 - - // Convert integer <-> float - ARM_VECTORDATA_S32F32, // s32.f32 - ARM_VECTORDATA_U32F32, // u32.f32 - ARM_VECTORDATA_F32S32, // f32.s32 - ARM_VECTORDATA_F32U32, // f32.u32 - ARM_VECTORDATA_F64S16, // f64.s16 - ARM_VECTORDATA_F32S16, // f32.s16 - ARM_VECTORDATA_F64S32, // f64.s32 - ARM_VECTORDATA_S16F64, // s16.f64 - ARM_VECTORDATA_S16F32, // s16.f64 - ARM_VECTORDATA_S32F64, // s32.f64 - ARM_VECTORDATA_U16F64, // u16.f64 - ARM_VECTORDATA_U16F32, // u16.f32 - ARM_VECTORDATA_U32F64, // u32.f64 - ARM_VECTORDATA_F64U16, // f64.u16 - ARM_VECTORDATA_F32U16, // f32.u16 - ARM_VECTORDATA_F64U32, // f64.u32 - ARM_VECTORDATA_F16U16, // f16.u16 - ARM_VECTORDATA_U16F16, // u16.f16 - ARM_VECTORDATA_F16U32, // f16.u32 - ARM_VECTORDATA_U32F16, // u32.f16 -} arm_vectordata_type; - -/// ARM registers -typedef enum arm_reg { - ARM_REG_INVALID = 0, - ARM_REG_APSR, - ARM_REG_APSR_NZCV, - ARM_REG_CPSR, - ARM_REG_FPEXC, - ARM_REG_FPINST, - ARM_REG_FPSCR, - ARM_REG_FPSCR_NZCV, - ARM_REG_FPSID, - ARM_REG_ITSTATE, - ARM_REG_LR, - ARM_REG_PC, - ARM_REG_SP, - ARM_REG_SPSR, - ARM_REG_D0, - ARM_REG_D1, - ARM_REG_D2, - ARM_REG_D3, - ARM_REG_D4, - ARM_REG_D5, - ARM_REG_D6, - ARM_REG_D7, - ARM_REG_D8, - ARM_REG_D9, - ARM_REG_D10, - ARM_REG_D11, - ARM_REG_D12, - ARM_REG_D13, - ARM_REG_D14, - ARM_REG_D15, - ARM_REG_D16, - ARM_REG_D17, - ARM_REG_D18, - ARM_REG_D19, - ARM_REG_D20, - ARM_REG_D21, - ARM_REG_D22, - ARM_REG_D23, - ARM_REG_D24, - ARM_REG_D25, - ARM_REG_D26, - ARM_REG_D27, - ARM_REG_D28, - ARM_REG_D29, - ARM_REG_D30, - ARM_REG_D31, - ARM_REG_FPINST2, - ARM_REG_MVFR0, - ARM_REG_MVFR1, - ARM_REG_MVFR2, - ARM_REG_Q0, - ARM_REG_Q1, - ARM_REG_Q2, - ARM_REG_Q3, - ARM_REG_Q4, - ARM_REG_Q5, - ARM_REG_Q6, - ARM_REG_Q7, - ARM_REG_Q8, - ARM_REG_Q9, - ARM_REG_Q10, - ARM_REG_Q11, - ARM_REG_Q12, - ARM_REG_Q13, - ARM_REG_Q14, - ARM_REG_Q15, - ARM_REG_R0, - ARM_REG_R1, - ARM_REG_R2, - ARM_REG_R3, - ARM_REG_R4, - ARM_REG_R5, - ARM_REG_R6, - ARM_REG_R7, - ARM_REG_R8, - ARM_REG_R9, - ARM_REG_R10, - ARM_REG_R11, - ARM_REG_R12, - ARM_REG_S0, - ARM_REG_S1, - ARM_REG_S2, - ARM_REG_S3, - ARM_REG_S4, - ARM_REG_S5, - ARM_REG_S6, - ARM_REG_S7, - ARM_REG_S8, - ARM_REG_S9, - ARM_REG_S10, - ARM_REG_S11, - ARM_REG_S12, - ARM_REG_S13, - ARM_REG_S14, - ARM_REG_S15, - ARM_REG_S16, - ARM_REG_S17, - ARM_REG_S18, - ARM_REG_S19, - ARM_REG_S20, - ARM_REG_S21, - ARM_REG_S22, - ARM_REG_S23, - ARM_REG_S24, - ARM_REG_S25, - ARM_REG_S26, - ARM_REG_S27, - ARM_REG_S28, - ARM_REG_S29, - ARM_REG_S30, - ARM_REG_S31, - - ARM_REG_ENDING, // <-- mark the end of the list or registers - - // alias registers - ARM_REG_R13 = ARM_REG_SP, - ARM_REG_R14 = ARM_REG_LR, - ARM_REG_R15 = ARM_REG_PC, - - ARM_REG_SB = ARM_REG_R9, - ARM_REG_SL = ARM_REG_R10, - ARM_REG_FP = ARM_REG_R11, - ARM_REG_IP = ARM_REG_R12, -} arm_reg; - -/// Instruction's operand referring to memory -/// This is associated with ARM_OP_MEM operand type above -typedef struct arm_op_mem { - arm_reg base; ///< base register - arm_reg index; ///< index register - int scale; ///< scale for index register (can be 1, or -1) - int disp; ///< displacement/offset value - /// left-shift on index register, or 0 if irrelevant - /// NOTE: this value can also be fetched via operand.shift.value - int lshift; -} arm_op_mem; - -/// Instruction operand -typedef struct cs_arm_op { - int vector_index; ///< Vector Index for some vector operands (or -1 if irrelevant) - - struct { - arm_shifter type; - unsigned int value; - } shift; - - arm_op_type type; ///< operand type - - union { - int reg; ///< register value for REG/SYSREG operand - int32_t imm; ///< immediate value for C-IMM, P-IMM or IMM operand - double fp; ///< floating point value for FP operand - arm_op_mem mem; ///< base/index/scale/disp value for MEM operand - arm_setend_type setend; ///< SETEND instruction's operand type - }; - - /// in some instructions, an operand can be subtracted or added to - /// the base register, - /// if TRUE, this operand is subtracted. otherwise, it is added. - bool subtracted; - - /// How is this operand accessed? (READ, WRITE or READ|WRITE) - /// This field is combined of cs_ac_type. - /// NOTE: this field is irrelevant if engine is compiled in DIET mode. - uint8_t access; - - /// Neon lane index for NEON instructions (or -1 if irrelevant) - int8_t neon_lane; -} cs_arm_op; - -/// Instruction structure -typedef struct cs_arm { - bool usermode; ///< User-mode registers to be loaded (for LDM/STM instructions) - int vector_size; ///< Scalar size for vector instructions - arm_vectordata_type vector_data; ///< Data type for elements of vector instructions - arm_cpsmode_type cps_mode; ///< CPS mode for CPS instruction - arm_cpsflag_type cps_flag; ///< CPS mode for CPS instruction - arm_cc cc; ///< conditional code for this insn - bool update_flags; ///< does this insn update flags? - bool writeback; ///< does this insn write-back? - bool post_index; ///< only set if writeback is 'True', if 'False' pre-index, otherwise post. - arm_mem_barrier mem_barrier; ///< Option for some memory barrier instructions - - /// Number of operands of this instruction, - /// or 0 when instruction has no operand. - uint8_t op_count; - - cs_arm_op operands[36]; ///< operands for this instruction. -} cs_arm; - -/// ARM instruction -typedef enum arm_insn { - ARM_INS_INVALID = 0, - - ARM_INS_ADC, - ARM_INS_ADD, - ARM_INS_ADDW, - ARM_INS_ADR, - ARM_INS_AESD, - ARM_INS_AESE, - ARM_INS_AESIMC, - ARM_INS_AESMC, - ARM_INS_AND, - ARM_INS_ASR, - ARM_INS_B, - ARM_INS_BFC, - ARM_INS_BFI, - ARM_INS_BIC, - ARM_INS_BKPT, - ARM_INS_BL, - ARM_INS_BLX, - ARM_INS_BLXNS, - ARM_INS_BX, - ARM_INS_BXJ, - ARM_INS_BXNS, - ARM_INS_CBNZ, - ARM_INS_CBZ, - ARM_INS_CDP, - ARM_INS_CDP2, - ARM_INS_CLREX, - ARM_INS_CLZ, - ARM_INS_CMN, - ARM_INS_CMP, - ARM_INS_CPS, - ARM_INS_CRC32B, - ARM_INS_CRC32CB, - ARM_INS_CRC32CH, - ARM_INS_CRC32CW, - ARM_INS_CRC32H, - ARM_INS_CRC32W, - ARM_INS_CSDB, - ARM_INS_DBG, - ARM_INS_DCPS1, - ARM_INS_DCPS2, - ARM_INS_DCPS3, - ARM_INS_DFB, - ARM_INS_DMB, - ARM_INS_DSB, - ARM_INS_EOR, - ARM_INS_ERET, - ARM_INS_ESB, - ARM_INS_FADDD, - ARM_INS_FADDS, - ARM_INS_FCMPZD, - ARM_INS_FCMPZS, - ARM_INS_FCONSTD, - ARM_INS_FCONSTS, - ARM_INS_FLDMDBX, - ARM_INS_FLDMIAX, - ARM_INS_FMDHR, - ARM_INS_FMDLR, - ARM_INS_FMSTAT, - ARM_INS_FSTMDBX, - ARM_INS_FSTMIAX, - ARM_INS_FSUBD, - ARM_INS_FSUBS, - ARM_INS_HINT, - ARM_INS_HLT, - ARM_INS_HVC, - ARM_INS_ISB, - ARM_INS_IT, - ARM_INS_LDA, - ARM_INS_LDAB, - ARM_INS_LDAEX, - ARM_INS_LDAEXB, - ARM_INS_LDAEXD, - ARM_INS_LDAEXH, - ARM_INS_LDAH, - ARM_INS_LDC, - ARM_INS_LDC2, - ARM_INS_LDC2L, - ARM_INS_LDCL, - ARM_INS_LDM, - ARM_INS_LDMDA, - ARM_INS_LDMDB, - ARM_INS_LDMIB, - ARM_INS_LDR, - ARM_INS_LDRB, - ARM_INS_LDRBT, - ARM_INS_LDRD, - ARM_INS_LDREX, - ARM_INS_LDREXB, - ARM_INS_LDREXD, - ARM_INS_LDREXH, - ARM_INS_LDRH, - ARM_INS_LDRHT, - ARM_INS_LDRSB, - ARM_INS_LDRSBT, - ARM_INS_LDRSH, - ARM_INS_LDRSHT, - ARM_INS_LDRT, - ARM_INS_LSL, - ARM_INS_LSR, - ARM_INS_MCR, - ARM_INS_MCR2, - ARM_INS_MCRR, - ARM_INS_MCRR2, - ARM_INS_MLA, - ARM_INS_MLS, - ARM_INS_MOV, - ARM_INS_MOVS, - ARM_INS_MOVT, - ARM_INS_MOVW, - ARM_INS_MRC, - ARM_INS_MRC2, - ARM_INS_MRRC, - ARM_INS_MRRC2, - ARM_INS_MRS, - ARM_INS_MSR, - ARM_INS_MUL, - ARM_INS_MVN, - ARM_INS_NEG, - ARM_INS_NOP, - ARM_INS_ORN, - ARM_INS_ORR, - ARM_INS_PKHBT, - ARM_INS_PKHTB, - ARM_INS_PLD, - ARM_INS_PLDW, - ARM_INS_PLI, - ARM_INS_POP, - ARM_INS_PUSH, - ARM_INS_QADD, - ARM_INS_QADD16, - ARM_INS_QADD8, - ARM_INS_QASX, - ARM_INS_QDADD, - ARM_INS_QDSUB, - ARM_INS_QSAX, - ARM_INS_QSUB, - ARM_INS_QSUB16, - ARM_INS_QSUB8, - ARM_INS_RBIT, - ARM_INS_REV, - ARM_INS_REV16, - ARM_INS_REVSH, - ARM_INS_RFEDA, - ARM_INS_RFEDB, - ARM_INS_RFEIA, - ARM_INS_RFEIB, - ARM_INS_ROR, - ARM_INS_RRX, - ARM_INS_RSB, - ARM_INS_RSC, - ARM_INS_SADD16, - ARM_INS_SADD8, - ARM_INS_SASX, - ARM_INS_SBC, - ARM_INS_SBFX, - ARM_INS_SDIV, - ARM_INS_SEL, - ARM_INS_SETEND, - ARM_INS_SETPAN, - ARM_INS_SEV, - ARM_INS_SEVL, - ARM_INS_SG, - ARM_INS_SHA1C, - ARM_INS_SHA1H, - ARM_INS_SHA1M, - ARM_INS_SHA1P, - ARM_INS_SHA1SU0, - ARM_INS_SHA1SU1, - ARM_INS_SHA256H, - ARM_INS_SHA256H2, - ARM_INS_SHA256SU0, - ARM_INS_SHA256SU1, - ARM_INS_SHADD16, - ARM_INS_SHADD8, - ARM_INS_SHASX, - ARM_INS_SHSAX, - ARM_INS_SHSUB16, - ARM_INS_SHSUB8, - ARM_INS_SMC, - ARM_INS_SMLABB, - ARM_INS_SMLABT, - ARM_INS_SMLAD, - ARM_INS_SMLADX, - ARM_INS_SMLAL, - ARM_INS_SMLALBB, - ARM_INS_SMLALBT, - ARM_INS_SMLALD, - ARM_INS_SMLALDX, - ARM_INS_SMLALTB, - ARM_INS_SMLALTT, - ARM_INS_SMLATB, - ARM_INS_SMLATT, - ARM_INS_SMLAWB, - ARM_INS_SMLAWT, - ARM_INS_SMLSD, - ARM_INS_SMLSDX, - ARM_INS_SMLSLD, - ARM_INS_SMLSLDX, - ARM_INS_SMMLA, - ARM_INS_SMMLAR, - ARM_INS_SMMLS, - ARM_INS_SMMLSR, - ARM_INS_SMMUL, - ARM_INS_SMMULR, - ARM_INS_SMUAD, - ARM_INS_SMUADX, - ARM_INS_SMULBB, - ARM_INS_SMULBT, - ARM_INS_SMULL, - ARM_INS_SMULTB, - ARM_INS_SMULTT, - ARM_INS_SMULWB, - ARM_INS_SMULWT, - ARM_INS_SMUSD, - ARM_INS_SMUSDX, - ARM_INS_SRSDA, - ARM_INS_SRSDB, - ARM_INS_SRSIA, - ARM_INS_SRSIB, - ARM_INS_SSAT, - ARM_INS_SSAT16, - ARM_INS_SSAX, - ARM_INS_SSUB16, - ARM_INS_SSUB8, - ARM_INS_STC, - ARM_INS_STC2, - ARM_INS_STC2L, - ARM_INS_STCL, - ARM_INS_STL, - ARM_INS_STLB, - ARM_INS_STLEX, - ARM_INS_STLEXB, - ARM_INS_STLEXD, - ARM_INS_STLEXH, - ARM_INS_STLH, - ARM_INS_STM, - ARM_INS_STMDA, - ARM_INS_STMDB, - ARM_INS_STMIB, - ARM_INS_STR, - ARM_INS_STRB, - ARM_INS_STRBT, - ARM_INS_STRD, - ARM_INS_STREX, - ARM_INS_STREXB, - ARM_INS_STREXD, - ARM_INS_STREXH, - ARM_INS_STRH, - ARM_INS_STRHT, - ARM_INS_STRT, - ARM_INS_SUB, - ARM_INS_SUBS, - ARM_INS_SUBW, - ARM_INS_SVC, - ARM_INS_SWP, - ARM_INS_SWPB, - ARM_INS_SXTAB, - ARM_INS_SXTAB16, - ARM_INS_SXTAH, - ARM_INS_SXTB, - ARM_INS_SXTB16, - ARM_INS_SXTH, - ARM_INS_TBB, - ARM_INS_TBH, - ARM_INS_TEQ, - ARM_INS_TRAP, - ARM_INS_TSB, - ARM_INS_TST, - ARM_INS_TT, - ARM_INS_TTA, - ARM_INS_TTAT, - ARM_INS_TTT, - ARM_INS_UADD16, - ARM_INS_UADD8, - ARM_INS_UASX, - ARM_INS_UBFX, - ARM_INS_UDF, - ARM_INS_UDIV, - ARM_INS_UHADD16, - ARM_INS_UHADD8, - ARM_INS_UHASX, - ARM_INS_UHSAX, - ARM_INS_UHSUB16, - ARM_INS_UHSUB8, - ARM_INS_UMAAL, - ARM_INS_UMLAL, - ARM_INS_UMULL, - ARM_INS_UQADD16, - ARM_INS_UQADD8, - ARM_INS_UQASX, - ARM_INS_UQSAX, - ARM_INS_UQSUB16, - ARM_INS_UQSUB8, - ARM_INS_USAD8, - ARM_INS_USADA8, - ARM_INS_USAT, - ARM_INS_USAT16, - ARM_INS_USAX, - ARM_INS_USUB16, - ARM_INS_USUB8, - ARM_INS_UXTAB, - ARM_INS_UXTAB16, - ARM_INS_UXTAH, - ARM_INS_UXTB, - ARM_INS_UXTB16, - ARM_INS_UXTH, - ARM_INS_VABA, - ARM_INS_VABAL, - ARM_INS_VABD, - ARM_INS_VABDL, - ARM_INS_VABS, - ARM_INS_VACGE, - ARM_INS_VACGT, - ARM_INS_VACLE, - ARM_INS_VACLT, - ARM_INS_VADD, - ARM_INS_VADDHN, - ARM_INS_VADDL, - ARM_INS_VADDW, - ARM_INS_VAND, - ARM_INS_VBIC, - ARM_INS_VBIF, - ARM_INS_VBIT, - ARM_INS_VBSL, - ARM_INS_VCADD, - ARM_INS_VCEQ, - ARM_INS_VCGE, - ARM_INS_VCGT, - ARM_INS_VCLE, - ARM_INS_VCLS, - ARM_INS_VCLT, - ARM_INS_VCLZ, - ARM_INS_VCMLA, - ARM_INS_VCMP, - ARM_INS_VCMPE, - ARM_INS_VCNT, - ARM_INS_VCVT, - ARM_INS_VCVTA, - ARM_INS_VCVTB, - ARM_INS_VCVTM, - ARM_INS_VCVTN, - ARM_INS_VCVTP, - ARM_INS_VCVTR, - ARM_INS_VCVTT, - ARM_INS_VDIV, - ARM_INS_VDUP, - ARM_INS_VEOR, - ARM_INS_VEXT, - ARM_INS_VFMA, - ARM_INS_VFMS, - ARM_INS_VFNMA, - ARM_INS_VFNMS, - ARM_INS_VHADD, - ARM_INS_VHSUB, - ARM_INS_VINS, - ARM_INS_VJCVT, - ARM_INS_VLD1, - ARM_INS_VLD2, - ARM_INS_VLD3, - ARM_INS_VLD4, - ARM_INS_VLDMDB, - ARM_INS_VLDMIA, - ARM_INS_VLDR, - ARM_INS_VLLDM, - ARM_INS_VLSTM, - ARM_INS_VMAX, - ARM_INS_VMAXNM, - ARM_INS_VMIN, - ARM_INS_VMINNM, - ARM_INS_VMLA, - ARM_INS_VMLAL, - ARM_INS_VMLS, - ARM_INS_VMLSL, - ARM_INS_VMOV, - ARM_INS_VMOVL, - ARM_INS_VMOVN, - ARM_INS_VMOVX, - ARM_INS_VMRS, - ARM_INS_VMSR, - ARM_INS_VMUL, - ARM_INS_VMULL, - ARM_INS_VMVN, - ARM_INS_VNEG, - ARM_INS_VNMLA, - ARM_INS_VNMLS, - ARM_INS_VNMUL, - ARM_INS_VORN, - ARM_INS_VORR, - ARM_INS_VPADAL, - ARM_INS_VPADD, - ARM_INS_VPADDL, - ARM_INS_VPMAX, - ARM_INS_VPMIN, - ARM_INS_VPOP, - ARM_INS_VPUSH, - ARM_INS_VQABS, - ARM_INS_VQADD, - ARM_INS_VQDMLAL, - ARM_INS_VQDMLSL, - ARM_INS_VQDMULH, - ARM_INS_VQDMULL, - ARM_INS_VQMOVN, - ARM_INS_VQMOVUN, - ARM_INS_VQNEG, - ARM_INS_VQRDMLAH, - ARM_INS_VQRDMLSH, - ARM_INS_VQRDMULH, - ARM_INS_VQRSHL, - ARM_INS_VQRSHRN, - ARM_INS_VQRSHRUN, - ARM_INS_VQSHL, - ARM_INS_VQSHLU, - ARM_INS_VQSHRN, - ARM_INS_VQSHRUN, - ARM_INS_VQSUB, - ARM_INS_VRADDHN, - ARM_INS_VRECPE, - ARM_INS_VRECPS, - ARM_INS_VREV16, - ARM_INS_VREV32, - ARM_INS_VREV64, - ARM_INS_VRHADD, - ARM_INS_VRINTA, - ARM_INS_VRINTM, - ARM_INS_VRINTN, - ARM_INS_VRINTP, - ARM_INS_VRINTR, - ARM_INS_VRINTX, - ARM_INS_VRINTZ, - ARM_INS_VRSHL, - ARM_INS_VRSHR, - ARM_INS_VRSHRN, - ARM_INS_VRSQRTE, - ARM_INS_VRSQRTS, - ARM_INS_VRSRA, - ARM_INS_VRSUBHN, - ARM_INS_VSDOT, - ARM_INS_VSELEQ, - ARM_INS_VSELGE, - ARM_INS_VSELGT, - ARM_INS_VSELVS, - ARM_INS_VSHL, - ARM_INS_VSHLL, - ARM_INS_VSHR, - ARM_INS_VSHRN, - ARM_INS_VSLI, - ARM_INS_VSQRT, - ARM_INS_VSRA, - ARM_INS_VSRI, - ARM_INS_VST1, - ARM_INS_VST2, - ARM_INS_VST3, - ARM_INS_VST4, - ARM_INS_VSTMDB, - ARM_INS_VSTMIA, - ARM_INS_VSTR, - ARM_INS_VSUB, - ARM_INS_VSUBHN, - ARM_INS_VSUBL, - ARM_INS_VSUBW, - ARM_INS_VSWP, - ARM_INS_VTBL, - ARM_INS_VTBX, - ARM_INS_VTRN, - ARM_INS_VTST, - ARM_INS_VUDOT, - ARM_INS_VUZP, - ARM_INS_VZIP, - ARM_INS_WFE, - ARM_INS_WFI, - ARM_INS_YIELD, - - ARM_INS_ENDING, // <-- mark the end of the list of instructions -} arm_insn; - -/// Group of ARM instructions -typedef enum arm_insn_group { - ARM_GRP_INVALID = 0, ///< = CS_GRP_INVALID - - // Generic groups - // all jump instructions (conditional+direct+indirect jumps) - ARM_GRP_JUMP, ///< = CS_GRP_JUMP - ARM_GRP_CALL, ///< = CS_GRP_CALL - ARM_GRP_INT = 4, ///< = CS_GRP_INT - ARM_GRP_PRIVILEGE = 6, ///< = CS_GRP_PRIVILEGE - ARM_GRP_BRANCH_RELATIVE, ///< = CS_GRP_BRANCH_RELATIVE - - // Architecture-specific groups - ARM_GRP_CRYPTO = 128, - ARM_GRP_DATABARRIER, - ARM_GRP_DIVIDE, - ARM_GRP_FPARMV8, - ARM_GRP_MULTPRO, - ARM_GRP_NEON, - ARM_GRP_T2EXTRACTPACK, - ARM_GRP_THUMB2DSP, - ARM_GRP_TRUSTZONE, - ARM_GRP_V4T, - ARM_GRP_V5T, - ARM_GRP_V5TE, - ARM_GRP_V6, - ARM_GRP_V6T2, - ARM_GRP_V7, - ARM_GRP_V8, - ARM_GRP_VFP2, - ARM_GRP_VFP3, - ARM_GRP_VFP4, - ARM_GRP_ARM, - ARM_GRP_MCLASS, - ARM_GRP_NOTMCLASS, - ARM_GRP_THUMB, - ARM_GRP_THUMB1ONLY, - ARM_GRP_THUMB2, - ARM_GRP_PREV8, - ARM_GRP_FPVMLX, - ARM_GRP_MULOPS, - ARM_GRP_CRC, - ARM_GRP_DPVFP, - ARM_GRP_V6M, - ARM_GRP_VIRTUALIZATION, - - ARM_GRP_ENDING, -} arm_insn_group; - -#ifdef __cplusplus -} -#endif - -#endif -#ifndef CAPSTONE_ARM64_H -#define CAPSTONE_ARM64_H - -/* Capstone Disassembly Engine */ -/* By Nguyen Anh Quynh , 2013-2015 */ - -#ifdef __cplusplus -extern "C" { -#endif - - -#ifdef _MSC_VER -#pragma warning(disable : 4201) -#endif - -/// ARM64 shift type -typedef enum arm64_shifter { - ARM64_SFT_INVALID = 0, - ARM64_SFT_LSL = 1, - ARM64_SFT_MSL = 2, - ARM64_SFT_LSR = 3, - ARM64_SFT_ASR = 4, - ARM64_SFT_ROR = 5, -} arm64_shifter; - -/// ARM64 extender type -typedef enum arm64_extender { - ARM64_EXT_INVALID = 0, - ARM64_EXT_UXTB = 1, - ARM64_EXT_UXTH = 2, - ARM64_EXT_UXTW = 3, - ARM64_EXT_UXTX = 4, - ARM64_EXT_SXTB = 5, - ARM64_EXT_SXTH = 6, - ARM64_EXT_SXTW = 7, - ARM64_EXT_SXTX = 8, -} arm64_extender; - -/// ARM64 condition code -typedef enum arm64_cc { - ARM64_CC_INVALID = 0, - ARM64_CC_EQ = 1, ///< Equal - ARM64_CC_NE = 2, ///< Not equal: Not equal, or unordered - ARM64_CC_HS = 3, ///< Unsigned higher or same: >, ==, or unordered - ARM64_CC_LO = 4, ///< Unsigned lower or same: Less than - ARM64_CC_MI = 5, ///< Minus, negative: Less than - ARM64_CC_PL = 6, ///< Plus, positive or zero: >, ==, or unordered - ARM64_CC_VS = 7, ///< Overflow: Unordered - ARM64_CC_VC = 8, ///< No overflow: Ordered - ARM64_CC_HI = 9, ///< Unsigned higher: Greater than, or unordered - ARM64_CC_LS = 10, ///< Unsigned lower or same: Less than or equal - ARM64_CC_GE = 11, ///< Greater than or equal: Greater than or equal - ARM64_CC_LT = 12, ///< Less than: Less than, or unordered - ARM64_CC_GT = 13, ///< Signed greater than: Greater than - ARM64_CC_LE = 14, ///< Signed less than or equal: <, ==, or unordered - ARM64_CC_AL = 15, ///< Always (unconditional): Always (unconditional) - ARM64_CC_NV = 16, ///< Always (unconditional): Always (unconditional) - //< Note the NV exists purely to disassemble 0b1111. Execution is "always". -} arm64_cc; - -/// System registers -typedef enum arm64_sysreg { - // System registers for MRS - ARM64_SYSREG_INVALID = 0, +/// System registers +typedef enum arm64_sysreg { + // System registers for MRS + ARM64_SYSREG_INVALID = 0, ARM64_SYSREG_ACCDATA_EL1 = 0xC685, ARM64_SYSREG_ACTLR_EL1 = 0xC081, @@ -78642,4143 +75127,7927 @@ typedef enum x86_insn { X86_INS_XSTORE, X86_INS_XTEST, - X86_INS_ENDING, // mark the end of the list of insn -} x86_insn; + X86_INS_ENDING, // mark the end of the list of insn +} x86_insn; + +/// Group of X86 instructions +typedef enum x86_insn_group { + X86_GRP_INVALID = 0, ///< = CS_GRP_INVALID + + // Generic groups + // all jump instructions (conditional+direct+indirect jumps) + X86_GRP_JUMP, ///< = CS_GRP_JUMP + // all call instructions + X86_GRP_CALL, ///< = CS_GRP_CALL + // all return instructions + X86_GRP_RET, ///< = CS_GRP_RET + // all interrupt instructions (int+syscall) + X86_GRP_INT, ///< = CS_GRP_INT + // all interrupt return instructions + X86_GRP_IRET, ///< = CS_GRP_IRET + // all privileged instructions + X86_GRP_PRIVILEGE, ///< = CS_GRP_PRIVILEGE + // all relative branching instructions + X86_GRP_BRANCH_RELATIVE, ///< = CS_GRP_BRANCH_RELATIVE + + // Architecture-specific groups + X86_GRP_VM = 128, ///< all virtualization instructions (VT-x + AMD-V) + X86_GRP_3DNOW, + X86_GRP_AES, + X86_GRP_ADX, + X86_GRP_AVX, + X86_GRP_AVX2, + X86_GRP_AVX512, + X86_GRP_BMI, + X86_GRP_BMI2, + X86_GRP_CMOV, + X86_GRP_F16C, + X86_GRP_FMA, + X86_GRP_FMA4, + X86_GRP_FSGSBASE, + X86_GRP_HLE, + X86_GRP_MMX, + X86_GRP_MODE32, + X86_GRP_MODE64, + X86_GRP_RTM, + X86_GRP_SHA, + X86_GRP_SSE1, + X86_GRP_SSE2, + X86_GRP_SSE3, + X86_GRP_SSE41, + X86_GRP_SSE42, + X86_GRP_SSE4A, + X86_GRP_SSSE3, + X86_GRP_PCLMUL, + X86_GRP_XOP, + X86_GRP_CDI, + X86_GRP_ERI, + X86_GRP_TBM, + X86_GRP_16BITMODE, + X86_GRP_NOT64BITMODE, + X86_GRP_SGX, + X86_GRP_DQI, + X86_GRP_BWI, + X86_GRP_PFI, + X86_GRP_VLX, + X86_GRP_SMAP, + X86_GRP_NOVLX, + X86_GRP_FPU, + + X86_GRP_ENDING +} x86_insn_group; + +#ifdef __cplusplus +} +#endif + +#endif +#ifndef CAPSTONE_XCORE_H +#define CAPSTONE_XCORE_H + +/* Capstone Disassembly Engine */ +/* By Nguyen Anh Quynh , 2014-2015 */ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +/// Operand type for instruction's operands +typedef enum xcore_op_type { + XCORE_OP_INVALID = 0, ///< = CS_OP_INVALID (Uninitialized). + XCORE_OP_REG, ///< = CS_OP_REG (Register operand). + XCORE_OP_IMM, ///< = CS_OP_IMM (Immediate operand). + XCORE_OP_MEM, ///< = CS_OP_MEM (Memory operand). +} xcore_op_type; + +/// XCore registers +typedef enum xcore_reg { + XCORE_REG_INVALID = 0, + + XCORE_REG_CP, + XCORE_REG_DP, + XCORE_REG_LR, + XCORE_REG_SP, + XCORE_REG_R0, + XCORE_REG_R1, + XCORE_REG_R2, + XCORE_REG_R3, + XCORE_REG_R4, + XCORE_REG_R5, + XCORE_REG_R6, + XCORE_REG_R7, + XCORE_REG_R8, + XCORE_REG_R9, + XCORE_REG_R10, + XCORE_REG_R11, + + // pseudo registers + XCORE_REG_PC, ///< pc + + // internal thread registers + // see The-XMOS-XS1-Architecture(X7879A).pdf + XCORE_REG_SCP, ///< save pc + XCORE_REG_SSR, //< save status + XCORE_REG_ET, //< exception type + XCORE_REG_ED, //< exception data + XCORE_REG_SED, //< save exception data + XCORE_REG_KEP, //< kernel entry pointer + XCORE_REG_KSP, //< kernel stack pointer + XCORE_REG_ID, //< thread ID + + XCORE_REG_ENDING, // <-- mark the end of the list of registers +} xcore_reg; + +/// Instruction's operand referring to memory +/// This is associated with XCORE_OP_MEM operand type above +typedef struct xcore_op_mem { + uint8_t base; ///< base register, can be safely interpreted as + ///< a value of type `xcore_reg`, but it is only + ///< one byte wide + uint8_t index; ///< index register, same conditions apply here + int32_t disp; ///< displacement/offset value + int direct; ///< +1: forward, -1: backward +} xcore_op_mem; + +/// Instruction operand +typedef struct cs_xcore_op { + xcore_op_type type; ///< operand type + union { + xcore_reg reg; ///< register value for REG operand + int32_t imm; ///< immediate value for IMM operand + xcore_op_mem mem; ///< base/disp value for MEM operand + }; +} cs_xcore_op; + +/// Instruction structure +typedef struct cs_xcore { + /// Number of operands of this instruction, + /// or 0 when instruction has no operand. + uint8_t op_count; + cs_xcore_op operands[8]; ///< operands for this instruction. +} cs_xcore; + +/// XCore instruction +typedef enum xcore_insn { + XCORE_INS_INVALID = 0, + + XCORE_INS_ADD, + XCORE_INS_ANDNOT, + XCORE_INS_AND, + XCORE_INS_ASHR, + XCORE_INS_BAU, + XCORE_INS_BITREV, + XCORE_INS_BLA, + XCORE_INS_BLAT, + XCORE_INS_BL, + XCORE_INS_BF, + XCORE_INS_BT, + XCORE_INS_BU, + XCORE_INS_BRU, + XCORE_INS_BYTEREV, + XCORE_INS_CHKCT, + XCORE_INS_CLRE, + XCORE_INS_CLRPT, + XCORE_INS_CLRSR, + XCORE_INS_CLZ, + XCORE_INS_CRC8, + XCORE_INS_CRC32, + XCORE_INS_DCALL, + XCORE_INS_DENTSP, + XCORE_INS_DGETREG, + XCORE_INS_DIVS, + XCORE_INS_DIVU, + XCORE_INS_DRESTSP, + XCORE_INS_DRET, + XCORE_INS_ECALLF, + XCORE_INS_ECALLT, + XCORE_INS_EDU, + XCORE_INS_EEF, + XCORE_INS_EET, + XCORE_INS_EEU, + XCORE_INS_ENDIN, + XCORE_INS_ENTSP, + XCORE_INS_EQ, + XCORE_INS_EXTDP, + XCORE_INS_EXTSP, + XCORE_INS_FREER, + XCORE_INS_FREET, + XCORE_INS_GETD, + XCORE_INS_GET, + XCORE_INS_GETN, + XCORE_INS_GETR, + XCORE_INS_GETSR, + XCORE_INS_GETST, + XCORE_INS_GETTS, + XCORE_INS_INCT, + XCORE_INS_INIT, + XCORE_INS_INPW, + XCORE_INS_INSHR, + XCORE_INS_INT, + XCORE_INS_IN, + XCORE_INS_KCALL, + XCORE_INS_KENTSP, + XCORE_INS_KRESTSP, + XCORE_INS_KRET, + XCORE_INS_LADD, + XCORE_INS_LD16S, + XCORE_INS_LD8U, + XCORE_INS_LDA16, + XCORE_INS_LDAP, + XCORE_INS_LDAW, + XCORE_INS_LDC, + XCORE_INS_LDW, + XCORE_INS_LDIVU, + XCORE_INS_LMUL, + XCORE_INS_LSS, + XCORE_INS_LSUB, + XCORE_INS_LSU, + XCORE_INS_MACCS, + XCORE_INS_MACCU, + XCORE_INS_MJOIN, + XCORE_INS_MKMSK, + XCORE_INS_MSYNC, + XCORE_INS_MUL, + XCORE_INS_NEG, + XCORE_INS_NOT, + XCORE_INS_OR, + XCORE_INS_OUTCT, + XCORE_INS_OUTPW, + XCORE_INS_OUTSHR, + XCORE_INS_OUTT, + XCORE_INS_OUT, + XCORE_INS_PEEK, + XCORE_INS_REMS, + XCORE_INS_REMU, + XCORE_INS_RETSP, + XCORE_INS_SETCLK, + XCORE_INS_SET, + XCORE_INS_SETC, + XCORE_INS_SETD, + XCORE_INS_SETEV, + XCORE_INS_SETN, + XCORE_INS_SETPSC, + XCORE_INS_SETPT, + XCORE_INS_SETRDY, + XCORE_INS_SETSR, + XCORE_INS_SETTW, + XCORE_INS_SETV, + XCORE_INS_SEXT, + XCORE_INS_SHL, + XCORE_INS_SHR, + XCORE_INS_SSYNC, + XCORE_INS_ST16, + XCORE_INS_ST8, + XCORE_INS_STW, + XCORE_INS_SUB, + XCORE_INS_SYNCR, + XCORE_INS_TESTCT, + XCORE_INS_TESTLCL, + XCORE_INS_TESTWCT, + XCORE_INS_TSETMR, + XCORE_INS_START, + XCORE_INS_WAITEF, + XCORE_INS_WAITET, + XCORE_INS_WAITEU, + XCORE_INS_XOR, + XCORE_INS_ZEXT, + + XCORE_INS_ENDING, // <-- mark the end of the list of instructions +} xcore_insn; + +/// Group of XCore instructions +typedef enum xcore_insn_group { + XCORE_GRP_INVALID = 0, ///< = CS_GRP_INVALID + + // Generic groups + // all jump instructions (conditional+direct+indirect jumps) + XCORE_GRP_JUMP, ///< = CS_GRP_JUMP + + XCORE_GRP_ENDING, // <-- mark the end of the list of groups +} xcore_insn_group; + +#ifdef __cplusplus +} +#endif + +#endif +/* Capstone Disassembly Engine */ +/* TMS320C64x Backend by Fotis Loukos 2016 */ + +#ifndef CAPSTONE_TMS320C64X_H +#define CAPSTONE_TMS320C64X_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +typedef enum tms320c64x_op_type { + TMS320C64X_OP_INVALID = 0, ///< = CS_OP_INVALID (Uninitialized). + TMS320C64X_OP_REG, ///< = CS_OP_REG (Register operand). + TMS320C64X_OP_IMM, ///< = CS_OP_IMM (Immediate operand). + TMS320C64X_OP_MEM, ///< = CS_OP_MEM (Memory operand). + TMS320C64X_OP_REGPAIR = 64, ///< Register pair for double word ops +} tms320c64x_op_type; + +typedef enum tms320c64x_mem_disp { + TMS320C64X_MEM_DISP_INVALID = 0, + TMS320C64X_MEM_DISP_CONSTANT, + TMS320C64X_MEM_DISP_REGISTER, +} tms320c64x_mem_disp; + +typedef enum tms320c64x_mem_dir { + TMS320C64X_MEM_DIR_INVALID = 0, + TMS320C64X_MEM_DIR_FW, + TMS320C64X_MEM_DIR_BW, +} tms320c64x_mem_dir; + +typedef enum tms320c64x_mem_mod { + TMS320C64X_MEM_MOD_INVALID = 0, + TMS320C64X_MEM_MOD_NO, + TMS320C64X_MEM_MOD_PRE, + TMS320C64X_MEM_MOD_POST, +} tms320c64x_mem_mod; + +typedef struct tms320c64x_op_mem { + unsigned int base; ///< base register + unsigned int disp; ///< displacement/offset value + unsigned int unit; ///< unit of base and offset register + unsigned int scaled; ///< offset scaled + unsigned int disptype; ///< displacement type + unsigned int direction; ///< direction + unsigned int modify; ///< modification +} tms320c64x_op_mem; + +typedef struct cs_tms320c64x_op { + tms320c64x_op_type type; ///< operand type + union { + unsigned int reg; ///< register value for REG operand or first register for REGPAIR operand + int32_t imm; ///< immediate value for IMM operand + tms320c64x_op_mem mem; ///< base/disp value for MEM operand + }; +} cs_tms320c64x_op; + +typedef struct cs_tms320c64x { + uint8_t op_count; + cs_tms320c64x_op operands[8]; ///< operands for this instruction. + struct { + unsigned int reg; + unsigned int zero; + } condition; + struct { + unsigned int unit; + unsigned int side; + unsigned int crosspath; + } funit; + unsigned int parallel; +} cs_tms320c64x; + +typedef enum tms320c64x_reg { + TMS320C64X_REG_INVALID = 0, + + TMS320C64X_REG_AMR, + TMS320C64X_REG_CSR, + TMS320C64X_REG_DIER, + TMS320C64X_REG_DNUM, + TMS320C64X_REG_ECR, + TMS320C64X_REG_GFPGFR, + TMS320C64X_REG_GPLYA, + TMS320C64X_REG_GPLYB, + TMS320C64X_REG_ICR, + TMS320C64X_REG_IER, + TMS320C64X_REG_IERR, + TMS320C64X_REG_ILC, + TMS320C64X_REG_IRP, + TMS320C64X_REG_ISR, + TMS320C64X_REG_ISTP, + TMS320C64X_REG_ITSR, + TMS320C64X_REG_NRP, + TMS320C64X_REG_NTSR, + TMS320C64X_REG_REP, + TMS320C64X_REG_RILC, + TMS320C64X_REG_SSR, + TMS320C64X_REG_TSCH, + TMS320C64X_REG_TSCL, + TMS320C64X_REG_TSR, + TMS320C64X_REG_A0, + TMS320C64X_REG_A1, + TMS320C64X_REG_A2, + TMS320C64X_REG_A3, + TMS320C64X_REG_A4, + TMS320C64X_REG_A5, + TMS320C64X_REG_A6, + TMS320C64X_REG_A7, + TMS320C64X_REG_A8, + TMS320C64X_REG_A9, + TMS320C64X_REG_A10, + TMS320C64X_REG_A11, + TMS320C64X_REG_A12, + TMS320C64X_REG_A13, + TMS320C64X_REG_A14, + TMS320C64X_REG_A15, + TMS320C64X_REG_A16, + TMS320C64X_REG_A17, + TMS320C64X_REG_A18, + TMS320C64X_REG_A19, + TMS320C64X_REG_A20, + TMS320C64X_REG_A21, + TMS320C64X_REG_A22, + TMS320C64X_REG_A23, + TMS320C64X_REG_A24, + TMS320C64X_REG_A25, + TMS320C64X_REG_A26, + TMS320C64X_REG_A27, + TMS320C64X_REG_A28, + TMS320C64X_REG_A29, + TMS320C64X_REG_A30, + TMS320C64X_REG_A31, + TMS320C64X_REG_B0, + TMS320C64X_REG_B1, + TMS320C64X_REG_B2, + TMS320C64X_REG_B3, + TMS320C64X_REG_B4, + TMS320C64X_REG_B5, + TMS320C64X_REG_B6, + TMS320C64X_REG_B7, + TMS320C64X_REG_B8, + TMS320C64X_REG_B9, + TMS320C64X_REG_B10, + TMS320C64X_REG_B11, + TMS320C64X_REG_B12, + TMS320C64X_REG_B13, + TMS320C64X_REG_B14, + TMS320C64X_REG_B15, + TMS320C64X_REG_B16, + TMS320C64X_REG_B17, + TMS320C64X_REG_B18, + TMS320C64X_REG_B19, + TMS320C64X_REG_B20, + TMS320C64X_REG_B21, + TMS320C64X_REG_B22, + TMS320C64X_REG_B23, + TMS320C64X_REG_B24, + TMS320C64X_REG_B25, + TMS320C64X_REG_B26, + TMS320C64X_REG_B27, + TMS320C64X_REG_B28, + TMS320C64X_REG_B29, + TMS320C64X_REG_B30, + TMS320C64X_REG_B31, + TMS320C64X_REG_PCE1, + + TMS320C64X_REG_ENDING, // <-- mark the end of the list of registers + + // Alias registers + TMS320C64X_REG_EFR = TMS320C64X_REG_ECR, + TMS320C64X_REG_IFR = TMS320C64X_REG_ISR, +} tms320c64x_reg; + +typedef enum tms320c64x_insn { + TMS320C64X_INS_INVALID = 0, + + TMS320C64X_INS_ABS, + TMS320C64X_INS_ABS2, + TMS320C64X_INS_ADD, + TMS320C64X_INS_ADD2, + TMS320C64X_INS_ADD4, + TMS320C64X_INS_ADDAB, + TMS320C64X_INS_ADDAD, + TMS320C64X_INS_ADDAH, + TMS320C64X_INS_ADDAW, + TMS320C64X_INS_ADDK, + TMS320C64X_INS_ADDKPC, + TMS320C64X_INS_ADDU, + TMS320C64X_INS_AND, + TMS320C64X_INS_ANDN, + TMS320C64X_INS_AVG2, + TMS320C64X_INS_AVGU4, + TMS320C64X_INS_B, + TMS320C64X_INS_BDEC, + TMS320C64X_INS_BITC4, + TMS320C64X_INS_BNOP, + TMS320C64X_INS_BPOS, + TMS320C64X_INS_CLR, + TMS320C64X_INS_CMPEQ, + TMS320C64X_INS_CMPEQ2, + TMS320C64X_INS_CMPEQ4, + TMS320C64X_INS_CMPGT, + TMS320C64X_INS_CMPGT2, + TMS320C64X_INS_CMPGTU4, + TMS320C64X_INS_CMPLT, + TMS320C64X_INS_CMPLTU, + TMS320C64X_INS_DEAL, + TMS320C64X_INS_DOTP2, + TMS320C64X_INS_DOTPN2, + TMS320C64X_INS_DOTPNRSU2, + TMS320C64X_INS_DOTPRSU2, + TMS320C64X_INS_DOTPSU4, + TMS320C64X_INS_DOTPU4, + TMS320C64X_INS_EXT, + TMS320C64X_INS_EXTU, + TMS320C64X_INS_GMPGTU, + TMS320C64X_INS_GMPY4, + TMS320C64X_INS_LDB, + TMS320C64X_INS_LDBU, + TMS320C64X_INS_LDDW, + TMS320C64X_INS_LDH, + TMS320C64X_INS_LDHU, + TMS320C64X_INS_LDNDW, + TMS320C64X_INS_LDNW, + TMS320C64X_INS_LDW, + TMS320C64X_INS_LMBD, + TMS320C64X_INS_MAX2, + TMS320C64X_INS_MAXU4, + TMS320C64X_INS_MIN2, + TMS320C64X_INS_MINU4, + TMS320C64X_INS_MPY, + TMS320C64X_INS_MPY2, + TMS320C64X_INS_MPYH, + TMS320C64X_INS_MPYHI, + TMS320C64X_INS_MPYHIR, + TMS320C64X_INS_MPYHL, + TMS320C64X_INS_MPYHLU, + TMS320C64X_INS_MPYHSLU, + TMS320C64X_INS_MPYHSU, + TMS320C64X_INS_MPYHU, + TMS320C64X_INS_MPYHULS, + TMS320C64X_INS_MPYHUS, + TMS320C64X_INS_MPYLH, + TMS320C64X_INS_MPYLHU, + TMS320C64X_INS_MPYLI, + TMS320C64X_INS_MPYLIR, + TMS320C64X_INS_MPYLSHU, + TMS320C64X_INS_MPYLUHS, + TMS320C64X_INS_MPYSU, + TMS320C64X_INS_MPYSU4, + TMS320C64X_INS_MPYU, + TMS320C64X_INS_MPYU4, + TMS320C64X_INS_MPYUS, + TMS320C64X_INS_MVC, + TMS320C64X_INS_MVD, + TMS320C64X_INS_MVK, + TMS320C64X_INS_MVKL, + TMS320C64X_INS_MVKLH, + TMS320C64X_INS_NOP, + TMS320C64X_INS_NORM, + TMS320C64X_INS_OR, + TMS320C64X_INS_PACK2, + TMS320C64X_INS_PACKH2, + TMS320C64X_INS_PACKH4, + TMS320C64X_INS_PACKHL2, + TMS320C64X_INS_PACKL4, + TMS320C64X_INS_PACKLH2, + TMS320C64X_INS_ROTL, + TMS320C64X_INS_SADD, + TMS320C64X_INS_SADD2, + TMS320C64X_INS_SADDU4, + TMS320C64X_INS_SADDUS2, + TMS320C64X_INS_SAT, + TMS320C64X_INS_SET, + TMS320C64X_INS_SHFL, + TMS320C64X_INS_SHL, + TMS320C64X_INS_SHLMB, + TMS320C64X_INS_SHR, + TMS320C64X_INS_SHR2, + TMS320C64X_INS_SHRMB, + TMS320C64X_INS_SHRU, + TMS320C64X_INS_SHRU2, + TMS320C64X_INS_SMPY, + TMS320C64X_INS_SMPY2, + TMS320C64X_INS_SMPYH, + TMS320C64X_INS_SMPYHL, + TMS320C64X_INS_SMPYLH, + TMS320C64X_INS_SPACK2, + TMS320C64X_INS_SPACKU4, + TMS320C64X_INS_SSHL, + TMS320C64X_INS_SSHVL, + TMS320C64X_INS_SSHVR, + TMS320C64X_INS_SSUB, + TMS320C64X_INS_STB, + TMS320C64X_INS_STDW, + TMS320C64X_INS_STH, + TMS320C64X_INS_STNDW, + TMS320C64X_INS_STNW, + TMS320C64X_INS_STW, + TMS320C64X_INS_SUB, + TMS320C64X_INS_SUB2, + TMS320C64X_INS_SUB4, + TMS320C64X_INS_SUBAB, + TMS320C64X_INS_SUBABS4, + TMS320C64X_INS_SUBAH, + TMS320C64X_INS_SUBAW, + TMS320C64X_INS_SUBC, + TMS320C64X_INS_SUBU, + TMS320C64X_INS_SWAP4, + TMS320C64X_INS_UNPKHU4, + TMS320C64X_INS_UNPKLU4, + TMS320C64X_INS_XOR, + TMS320C64X_INS_XPND2, + TMS320C64X_INS_XPND4, + // Aliases + TMS320C64X_INS_IDLE, + TMS320C64X_INS_MV, + TMS320C64X_INS_NEG, + TMS320C64X_INS_NOT, + TMS320C64X_INS_SWAP2, + TMS320C64X_INS_ZERO, + + TMS320C64X_INS_ENDING, // <-- mark the end of the list of instructions +} tms320c64x_insn; + +typedef enum tms320c64x_insn_group { + TMS320C64X_GRP_INVALID = 0, ///< = CS_GRP_INVALID + + TMS320C64X_GRP_JUMP, ///< = CS_GRP_JUMP + + TMS320C64X_GRP_FUNIT_D = 128, + TMS320C64X_GRP_FUNIT_L, + TMS320C64X_GRP_FUNIT_M, + TMS320C64X_GRP_FUNIT_S, + TMS320C64X_GRP_FUNIT_NO, + + TMS320C64X_GRP_ENDING, // <-- mark the end of the list of groups +} tms320c64x_insn_group; + +typedef enum tms320c64x_funit { + TMS320C64X_FUNIT_INVALID = 0, + TMS320C64X_FUNIT_D, + TMS320C64X_FUNIT_L, + TMS320C64X_FUNIT_M, + TMS320C64X_FUNIT_S, + TMS320C64X_FUNIT_NO +} tms320c64x_funit; + +#ifdef __cplusplus +} +#endif + +#endif + +#ifndef CAPSTONE_M680X_H +#define CAPSTONE_M680X_H + +/* Capstone Disassembly Engine */ +/* M680X Backend by Wolfgang Schwotzer 2017 */ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +#define M680X_OPERAND_COUNT 9 + +/// M680X registers and special registers +typedef enum m680x_reg { + M680X_REG_INVALID = 0, + + M680X_REG_A, ///< M6800/1/2/3/9, HD6301/9 + M680X_REG_B, ///< M6800/1/2/3/9, HD6301/9 + M680X_REG_E, ///< HD6309 + M680X_REG_F, ///< HD6309 + M680X_REG_0, ///< HD6309 + + M680X_REG_D, ///< M6801/3/9, HD6301/9 + M680X_REG_W, ///< HD6309 + + M680X_REG_CC, ///< M6800/1/2/3/9, M6301/9 + M680X_REG_DP, ///< M6809/M6309 + M680X_REG_MD, ///< M6309 + + M680X_REG_HX, ///< M6808 + M680X_REG_H, ///< M6808 + M680X_REG_X, ///< M6800/1/2/3/9, M6301/9 + M680X_REG_Y, ///< M6809/M6309 + M680X_REG_S, ///< M6809/M6309 + M680X_REG_U, ///< M6809/M6309 + M680X_REG_V, ///< M6309 + + M680X_REG_Q, ///< M6309 + + M680X_REG_PC, ///< M6800/1/2/3/9, M6301/9 + + M680X_REG_TMP2, ///< CPU12 + M680X_REG_TMP3, ///< CPU12 + + M680X_REG_ENDING, ///< <-- mark the end of the list of registers +} m680x_reg; + +/// Operand type for instruction's operands +typedef enum m680x_op_type { + M680X_OP_INVALID = 0, ///< = CS_OP_INVALID (Uninitialized). + M680X_OP_REGISTER, ///< = Register operand. + M680X_OP_IMMEDIATE, ///< = Immediate operand. + M680X_OP_INDEXED, ///< = Indexed addressing operand. + M680X_OP_EXTENDED, ///< = Extended addressing operand. + M680X_OP_DIRECT, ///< = Direct addressing operand. + M680X_OP_RELATIVE, ///< = Relative addressing operand. + M680X_OP_CONSTANT, ///< = constant operand (Displayed as number only). + ///< Used e.g. for a bit index or page number. +} m680x_op_type; + +// Supported bit values for mem.idx.offset_bits +#define M680X_OFFSET_NONE 0 +#define M680X_OFFSET_BITS_5 5 +#define M680X_OFFSET_BITS_8 8 +#define M680X_OFFSET_BITS_9 9 +#define M680X_OFFSET_BITS_16 16 + +// Supported bit flags for mem.idx.flags +// These flags can be combined +#define M680X_IDX_INDIRECT 1 +#define M680X_IDX_NO_COMMA 2 +#define M680X_IDX_POST_INC_DEC 4 + +/// Instruction's operand referring to indexed addressing +typedef struct m680x_op_idx { + m680x_reg base_reg; ///< base register (or M680X_REG_INVALID if + ///< irrelevant) + m680x_reg offset_reg; ///< offset register (or M680X_REG_INVALID if + ///< irrelevant) + int16_t offset; ///< 5-,8- or 16-bit offset. See also offset_bits. + uint16_t offset_addr; ///< = offset addr. if base_reg == M680X_REG_PC. + ///< calculated as offset + PC + uint8_t offset_bits; ///< offset width in bits for indexed addressing + int8_t inc_dec; ///< inc. or dec. value: + ///< 0: no inc-/decrement + ///< 1 .. 8: increment by 1 .. 8 + ///< -1 .. -8: decrement by 1 .. 8 + ///< if flag M680X_IDX_POST_INC_DEC set it is post + ///< inc-/decrement otherwise pre inc-/decrement + uint8_t flags; ///< 8-bit flags (see above) +} m680x_op_idx; + +/// Instruction's memory operand referring to relative addressing (Bcc/LBcc) +typedef struct m680x_op_rel { + uint16_t address; ///< The absolute address. + ///< calculated as PC + offset. PC is the first + ///< address after the instruction. + int16_t offset; ///< the offset/displacement value +} m680x_op_rel; + +/// Instruction's operand referring to extended addressing +typedef struct m680x_op_ext { + uint16_t address; ///< The absolute address + bool indirect; ///< true if extended indirect addressing +} m680x_op_ext; + +/// Instruction operand +typedef struct cs_m680x_op { + m680x_op_type type; + union { + int32_t imm; ///< immediate value for IMM operand + m680x_reg reg; ///< register value for REG operand + m680x_op_idx idx; ///< Indexed addressing operand + m680x_op_rel rel; ///< Relative address. operand (Bcc/LBcc) + m680x_op_ext ext; ///< Extended address + uint8_t direct_addr; ///<, 2013-2018 */ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +/// Instruction structure +typedef struct cs_evm { + unsigned char pop; ///< number of items popped from the stack + unsigned char push; ///< number of items pushed into the stack + unsigned int fee; ///< gas fee for the instruction +} cs_evm; + +/// EVM instruction +typedef enum evm_insn { + EVM_INS_STOP = 0, + EVM_INS_ADD = 1, + EVM_INS_MUL = 2, + EVM_INS_SUB = 3, + EVM_INS_DIV = 4, + EVM_INS_SDIV = 5, + EVM_INS_MOD = 6, + EVM_INS_SMOD = 7, + EVM_INS_ADDMOD = 8, + EVM_INS_MULMOD = 9, + EVM_INS_EXP = 10, + EVM_INS_SIGNEXTEND = 11, + EVM_INS_LT = 16, + EVM_INS_GT = 17, + EVM_INS_SLT = 18, + EVM_INS_SGT = 19, + EVM_INS_EQ = 20, + EVM_INS_ISZERO = 21, + EVM_INS_AND = 22, + EVM_INS_OR = 23, + EVM_INS_XOR = 24, + EVM_INS_NOT = 25, + EVM_INS_BYTE = 26, + EVM_INS_SHA3 = 32, + EVM_INS_ADDRESS = 48, + EVM_INS_BALANCE = 49, + EVM_INS_ORIGIN = 50, + EVM_INS_CALLER = 51, + EVM_INS_CALLVALUE = 52, + EVM_INS_CALLDATALOAD = 53, + EVM_INS_CALLDATASIZE = 54, + EVM_INS_CALLDATACOPY = 55, + EVM_INS_CODESIZE = 56, + EVM_INS_CODECOPY = 57, + EVM_INS_GASPRICE = 58, + EVM_INS_EXTCODESIZE = 59, + EVM_INS_EXTCODECOPY = 60, + EVM_INS_RETURNDATASIZE = 61, + EVM_INS_RETURNDATACOPY = 62, + EVM_INS_BLOCKHASH = 64, + EVM_INS_COINBASE = 65, + EVM_INS_TIMESTAMP = 66, + EVM_INS_NUMBER = 67, + EVM_INS_DIFFICULTY = 68, + EVM_INS_GASLIMIT = 69, + EVM_INS_POP = 80, + EVM_INS_MLOAD = 81, + EVM_INS_MSTORE = 82, + EVM_INS_MSTORE8 = 83, + EVM_INS_SLOAD = 84, + EVM_INS_SSTORE = 85, + EVM_INS_JUMP = 86, + EVM_INS_JUMPI = 87, + EVM_INS_PC = 88, + EVM_INS_MSIZE = 89, + EVM_INS_GAS = 90, + EVM_INS_JUMPDEST = 91, + EVM_INS_PUSH1 = 96, + EVM_INS_PUSH2 = 97, + EVM_INS_PUSH3 = 98, + EVM_INS_PUSH4 = 99, + EVM_INS_PUSH5 = 100, + EVM_INS_PUSH6 = 101, + EVM_INS_PUSH7 = 102, + EVM_INS_PUSH8 = 103, + EVM_INS_PUSH9 = 104, + EVM_INS_PUSH10 = 105, + EVM_INS_PUSH11 = 106, + EVM_INS_PUSH12 = 107, + EVM_INS_PUSH13 = 108, + EVM_INS_PUSH14 = 109, + EVM_INS_PUSH15 = 110, + EVM_INS_PUSH16 = 111, + EVM_INS_PUSH17 = 112, + EVM_INS_PUSH18 = 113, + EVM_INS_PUSH19 = 114, + EVM_INS_PUSH20 = 115, + EVM_INS_PUSH21 = 116, + EVM_INS_PUSH22 = 117, + EVM_INS_PUSH23 = 118, + EVM_INS_PUSH24 = 119, + EVM_INS_PUSH25 = 120, + EVM_INS_PUSH26 = 121, + EVM_INS_PUSH27 = 122, + EVM_INS_PUSH28 = 123, + EVM_INS_PUSH29 = 124, + EVM_INS_PUSH30 = 125, + EVM_INS_PUSH31 = 126, + EVM_INS_PUSH32 = 127, + EVM_INS_DUP1 = 128, + EVM_INS_DUP2 = 129, + EVM_INS_DUP3 = 130, + EVM_INS_DUP4 = 131, + EVM_INS_DUP5 = 132, + EVM_INS_DUP6 = 133, + EVM_INS_DUP7 = 134, + EVM_INS_DUP8 = 135, + EVM_INS_DUP9 = 136, + EVM_INS_DUP10 = 137, + EVM_INS_DUP11 = 138, + EVM_INS_DUP12 = 139, + EVM_INS_DUP13 = 140, + EVM_INS_DUP14 = 141, + EVM_INS_DUP15 = 142, + EVM_INS_DUP16 = 143, + EVM_INS_SWAP1 = 144, + EVM_INS_SWAP2 = 145, + EVM_INS_SWAP3 = 146, + EVM_INS_SWAP4 = 147, + EVM_INS_SWAP5 = 148, + EVM_INS_SWAP6 = 149, + EVM_INS_SWAP7 = 150, + EVM_INS_SWAP8 = 151, + EVM_INS_SWAP9 = 152, + EVM_INS_SWAP10 = 153, + EVM_INS_SWAP11 = 154, + EVM_INS_SWAP12 = 155, + EVM_INS_SWAP13 = 156, + EVM_INS_SWAP14 = 157, + EVM_INS_SWAP15 = 158, + EVM_INS_SWAP16 = 159, + EVM_INS_LOG0 = 160, + EVM_INS_LOG1 = 161, + EVM_INS_LOG2 = 162, + EVM_INS_LOG3 = 163, + EVM_INS_LOG4 = 164, + EVM_INS_CREATE = 240, + EVM_INS_CALL = 241, + EVM_INS_CALLCODE = 242, + EVM_INS_RETURN = 243, + EVM_INS_DELEGATECALL = 244, + EVM_INS_CALLBLACKBOX = 245, + EVM_INS_STATICCALL = 250, + EVM_INS_REVERT = 253, + EVM_INS_SUICIDE = 255, + + EVM_INS_INVALID = 512, + EVM_INS_ENDING, // <-- mark the end of the list of instructions +} evm_insn; + +/// Group of EVM instructions +typedef enum evm_insn_group { + EVM_GRP_INVALID = 0, ///< = CS_GRP_INVALID + + EVM_GRP_JUMP, ///< all jump instructions + + EVM_GRP_MATH = 8, ///< math instructions + EVM_GRP_STACK_WRITE, ///< instructions write to stack + EVM_GRP_STACK_READ, ///< instructions read from stack + EVM_GRP_MEM_WRITE, ///< instructions write to memory + EVM_GRP_MEM_READ, ///< instructions read from memory + EVM_GRP_STORE_WRITE, ///< instructions write to storage + EVM_GRP_STORE_READ, ///< instructions read from storage + EVM_GRP_HALT, ///< instructions halt execution + + EVM_GRP_ENDING, ///< <-- mark the end of the list of groups +} evm_insn_group; + +#ifdef __cplusplus +} +#endif + +#endif +#ifndef CAPSTONE_RISCV_H +#define CAPSTONE_RISCV_H + +/* Capstone Disassembly Engine */ +/* RISC-V Backend By Rodrigo Cortes Porto & + Shawn Chang , HardenedLinux@2018 */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(_MSC_VER) || !defined(_KERNEL_MODE) +#include +#endif + + +// GCC MIPS toolchain has a default macro called "mips" which breaks +// compilation +//#undef riscv + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +//> Operand type for instruction's operands +typedef enum riscv_op_type { + RISCV_OP_INVALID = 0, // = CS_OP_INVALID (Uninitialized). + RISCV_OP_REG, // = CS_OP_REG (Register operand). + RISCV_OP_IMM, // = CS_OP_IMM (Immediate operand). + RISCV_OP_MEM, // = CS_OP_MEM (Memory operand). +} riscv_op_type; + +// Instruction's operand referring to memory +// This is associated with RISCV_OP_MEM operand type above +typedef struct riscv_op_mem { + unsigned int base; // base register + int64_t disp; // displacement/offset value +} riscv_op_mem; + +// Instruction operand +typedef struct cs_riscv_op { + riscv_op_type type; // operand type + union { + unsigned int reg; // register value for REG operand + int64_t imm; // immediate value for IMM operand + riscv_op_mem mem; // base/disp value for MEM operand + }; +} cs_riscv_op; + +// Instruction structure +typedef struct cs_riscv { + // Does this instruction need effective address or not. + bool need_effective_addr; + // Number of operands of this instruction, + // or 0 when instruction has no operand. + uint8_t op_count; + cs_riscv_op operands[8]; // operands for this instruction. +} cs_riscv; + +//> RISCV registers +typedef enum riscv_reg { + RISCV_REG_INVALID = 0, + //> General purpose registers + RISCV_REG_X0, // "zero" + RISCV_REG_ZERO = RISCV_REG_X0, // "zero" + RISCV_REG_X1, // "ra" + RISCV_REG_RA = RISCV_REG_X1, // "ra" + RISCV_REG_X2, // "sp" + RISCV_REG_SP = RISCV_REG_X2, // "sp" + RISCV_REG_X3, // "gp" + RISCV_REG_GP = RISCV_REG_X3, // "gp" + RISCV_REG_X4, // "tp" + RISCV_REG_TP = RISCV_REG_X4, // "tp" + RISCV_REG_X5, // "t0" + RISCV_REG_T0 = RISCV_REG_X5, // "t0" + RISCV_REG_X6, // "t1" + RISCV_REG_T1 = RISCV_REG_X6, // "t1" + RISCV_REG_X7, // "t2" + RISCV_REG_T2 = RISCV_REG_X7, // "t2" + RISCV_REG_X8, // "s0/fp" + RISCV_REG_S0 = RISCV_REG_X8, // "s0" + RISCV_REG_FP = RISCV_REG_X8, // "fp" + RISCV_REG_X9, // "s1" + RISCV_REG_S1 = RISCV_REG_X9, // "s1" + RISCV_REG_X10, // "a0" + RISCV_REG_A0 = RISCV_REG_X10, // "a0" + RISCV_REG_X11, // "a1" + RISCV_REG_A1 = RISCV_REG_X11, // "a1" + RISCV_REG_X12, // "a2" + RISCV_REG_A2 = RISCV_REG_X12, // "a2" + RISCV_REG_X13, // "a3" + RISCV_REG_A3 = RISCV_REG_X13, // "a3" + RISCV_REG_X14, // "a4" + RISCV_REG_A4 = RISCV_REG_X14, // "a4" + RISCV_REG_X15, // "a5" + RISCV_REG_A5 = RISCV_REG_X15, // "a5" + RISCV_REG_X16, // "a6" + RISCV_REG_A6 = RISCV_REG_X16, // "a6" + RISCV_REG_X17, // "a7" + RISCV_REG_A7 = RISCV_REG_X17, // "a7" + RISCV_REG_X18, // "s2" + RISCV_REG_S2 = RISCV_REG_X18, // "s2" + RISCV_REG_X19, // "s3" + RISCV_REG_S3 = RISCV_REG_X19, // "s3" + RISCV_REG_X20, // "s4" + RISCV_REG_S4 = RISCV_REG_X20, // "s4" + RISCV_REG_X21, // "s5" + RISCV_REG_S5 = RISCV_REG_X21, // "s5" + RISCV_REG_X22, // "s6" + RISCV_REG_S6 = RISCV_REG_X22, // "s6" + RISCV_REG_X23, // "s7" + RISCV_REG_S7 = RISCV_REG_X23, // "s7" + RISCV_REG_X24, // "s8" + RISCV_REG_S8 = RISCV_REG_X24, // "s8" + RISCV_REG_X25, // "s9" + RISCV_REG_S9 = RISCV_REG_X25, // "s9" + RISCV_REG_X26, // "s10" + RISCV_REG_S10 = RISCV_REG_X26, // "s10" + RISCV_REG_X27, // "s11" + RISCV_REG_S11 = RISCV_REG_X27, // "s11" + RISCV_REG_X28, // "t3" + RISCV_REG_T3 = RISCV_REG_X28, // "t3" + RISCV_REG_X29, // "t4" + RISCV_REG_T4 = RISCV_REG_X29, // "t4" + RISCV_REG_X30, // "t5" + RISCV_REG_T5 = RISCV_REG_X30, // "t5" + RISCV_REG_X31, // "t6" + RISCV_REG_T6 = RISCV_REG_X31, // "t6" + + //> Floating-point registers + RISCV_REG_F0_32, // "ft0" + RISCV_REG_F0_64, // "ft0" + RISCV_REG_F1_32, // "ft1" + RISCV_REG_F1_64, // "ft1" + RISCV_REG_F2_32, // "ft2" + RISCV_REG_F2_64, // "ft2" + RISCV_REG_F3_32, // "ft3" + RISCV_REG_F3_64, // "ft3" + RISCV_REG_F4_32, // "ft4" + RISCV_REG_F4_64, // "ft4" + RISCV_REG_F5_32, // "ft5" + RISCV_REG_F5_64, // "ft5" + RISCV_REG_F6_32, // "ft6" + RISCV_REG_F6_64, // "ft6" + RISCV_REG_F7_32, // "ft7" + RISCV_REG_F7_64, // "ft7" + RISCV_REG_F8_32, // "fs0" + RISCV_REG_F8_64, // "fs0" + RISCV_REG_F9_32, // "fs1" + RISCV_REG_F9_64, // "fs1" + RISCV_REG_F10_32, // "fa0" + RISCV_REG_F10_64, // "fa0" + RISCV_REG_F11_32, // "fa1" + RISCV_REG_F11_64, // "fa1" + RISCV_REG_F12_32, // "fa2" + RISCV_REG_F12_64, // "fa2" + RISCV_REG_F13_32, // "fa3" + RISCV_REG_F13_64, // "fa3" + RISCV_REG_F14_32, // "fa4" + RISCV_REG_F14_64, // "fa4" + RISCV_REG_F15_32, // "fa5" + RISCV_REG_F15_64, // "fa5" + RISCV_REG_F16_32, // "fa6" + RISCV_REG_F16_64, // "fa6" + RISCV_REG_F17_32, // "fa7" + RISCV_REG_F17_64, // "fa7" + RISCV_REG_F18_32, // "fs2" + RISCV_REG_F18_64, // "fs2" + RISCV_REG_F19_32, // "fs3" + RISCV_REG_F19_64, // "fs3" + RISCV_REG_F20_32, // "fs4" + RISCV_REG_F20_64, // "fs4" + RISCV_REG_F21_32, // "fs5" + RISCV_REG_F21_64, // "fs5" + RISCV_REG_F22_32, // "fs6" + RISCV_REG_F22_64, // "fs6" + RISCV_REG_F23_32, // "fs7" + RISCV_REG_F23_64, // "fs7" + RISCV_REG_F24_32, // "fs8" + RISCV_REG_F24_64, // "fs8" + RISCV_REG_F25_32, // "fs9" + RISCV_REG_F25_64, // "fs9" + RISCV_REG_F26_32, // "fs10" + RISCV_REG_F26_64, // "fs10" + RISCV_REG_F27_32, // "fs11" + RISCV_REG_F27_64, // "fs11" + RISCV_REG_F28_32, // "ft8" + RISCV_REG_F28_64, // "ft8" + RISCV_REG_F29_32, // "ft9" + RISCV_REG_F29_64, // "ft9" + RISCV_REG_F30_32, // "ft10" + RISCV_REG_F30_64, // "ft10" + RISCV_REG_F31_32, // "ft11" + RISCV_REG_F31_64, // "ft11" + + RISCV_REG_ENDING, // <-- mark the end of the list or registers +} riscv_reg; + +//> RISCV instruction +typedef enum riscv_insn { + RISCV_INS_INVALID = 0, + + RISCV_INS_ADD, + RISCV_INS_ADDI, + RISCV_INS_ADDIW, + RISCV_INS_ADDW, + RISCV_INS_AMOADD_D, + RISCV_INS_AMOADD_D_AQ, + RISCV_INS_AMOADD_D_AQ_RL, + RISCV_INS_AMOADD_D_RL, + RISCV_INS_AMOADD_W, + RISCV_INS_AMOADD_W_AQ, + RISCV_INS_AMOADD_W_AQ_RL, + RISCV_INS_AMOADD_W_RL, + RISCV_INS_AMOAND_D, + RISCV_INS_AMOAND_D_AQ, + RISCV_INS_AMOAND_D_AQ_RL, + RISCV_INS_AMOAND_D_RL, + RISCV_INS_AMOAND_W, + RISCV_INS_AMOAND_W_AQ, + RISCV_INS_AMOAND_W_AQ_RL, + RISCV_INS_AMOAND_W_RL, + RISCV_INS_AMOMAXU_D, + RISCV_INS_AMOMAXU_D_AQ, + RISCV_INS_AMOMAXU_D_AQ_RL, + RISCV_INS_AMOMAXU_D_RL, + RISCV_INS_AMOMAXU_W, + RISCV_INS_AMOMAXU_W_AQ, + RISCV_INS_AMOMAXU_W_AQ_RL, + RISCV_INS_AMOMAXU_W_RL, + RISCV_INS_AMOMAX_D, + RISCV_INS_AMOMAX_D_AQ, + RISCV_INS_AMOMAX_D_AQ_RL, + RISCV_INS_AMOMAX_D_RL, + RISCV_INS_AMOMAX_W, + RISCV_INS_AMOMAX_W_AQ, + RISCV_INS_AMOMAX_W_AQ_RL, + RISCV_INS_AMOMAX_W_RL, + RISCV_INS_AMOMINU_D, + RISCV_INS_AMOMINU_D_AQ, + RISCV_INS_AMOMINU_D_AQ_RL, + RISCV_INS_AMOMINU_D_RL, + RISCV_INS_AMOMINU_W, + RISCV_INS_AMOMINU_W_AQ, + RISCV_INS_AMOMINU_W_AQ_RL, + RISCV_INS_AMOMINU_W_RL, + RISCV_INS_AMOMIN_D, + RISCV_INS_AMOMIN_D_AQ, + RISCV_INS_AMOMIN_D_AQ_RL, + RISCV_INS_AMOMIN_D_RL, + RISCV_INS_AMOMIN_W, + RISCV_INS_AMOMIN_W_AQ, + RISCV_INS_AMOMIN_W_AQ_RL, + RISCV_INS_AMOMIN_W_RL, + RISCV_INS_AMOOR_D, + RISCV_INS_AMOOR_D_AQ, + RISCV_INS_AMOOR_D_AQ_RL, + RISCV_INS_AMOOR_D_RL, + RISCV_INS_AMOOR_W, + RISCV_INS_AMOOR_W_AQ, + RISCV_INS_AMOOR_W_AQ_RL, + RISCV_INS_AMOOR_W_RL, + RISCV_INS_AMOSWAP_D, + RISCV_INS_AMOSWAP_D_AQ, + RISCV_INS_AMOSWAP_D_AQ_RL, + RISCV_INS_AMOSWAP_D_RL, + RISCV_INS_AMOSWAP_W, + RISCV_INS_AMOSWAP_W_AQ, + RISCV_INS_AMOSWAP_W_AQ_RL, + RISCV_INS_AMOSWAP_W_RL, + RISCV_INS_AMOXOR_D, + RISCV_INS_AMOXOR_D_AQ, + RISCV_INS_AMOXOR_D_AQ_RL, + RISCV_INS_AMOXOR_D_RL, + RISCV_INS_AMOXOR_W, + RISCV_INS_AMOXOR_W_AQ, + RISCV_INS_AMOXOR_W_AQ_RL, + RISCV_INS_AMOXOR_W_RL, + RISCV_INS_AND, + RISCV_INS_ANDI, + RISCV_INS_AUIPC, + RISCV_INS_BEQ, + RISCV_INS_BGE, + RISCV_INS_BGEU, + RISCV_INS_BLT, + RISCV_INS_BLTU, + RISCV_INS_BNE, + RISCV_INS_CSRRC, + RISCV_INS_CSRRCI, + RISCV_INS_CSRRS, + RISCV_INS_CSRRSI, + RISCV_INS_CSRRW, + RISCV_INS_CSRRWI, + RISCV_INS_C_ADD, + RISCV_INS_C_ADDI, + RISCV_INS_C_ADDI16SP, + RISCV_INS_C_ADDI4SPN, + RISCV_INS_C_ADDIW, + RISCV_INS_C_ADDW, + RISCV_INS_C_AND, + RISCV_INS_C_ANDI, + RISCV_INS_C_BEQZ, + RISCV_INS_C_BNEZ, + RISCV_INS_C_EBREAK, + RISCV_INS_C_FLD, + RISCV_INS_C_FLDSP, + RISCV_INS_C_FLW, + RISCV_INS_C_FLWSP, + RISCV_INS_C_FSD, + RISCV_INS_C_FSDSP, + RISCV_INS_C_FSW, + RISCV_INS_C_FSWSP, + RISCV_INS_C_J, + RISCV_INS_C_JAL, + RISCV_INS_C_JALR, + RISCV_INS_C_JR, + RISCV_INS_C_LD, + RISCV_INS_C_LDSP, + RISCV_INS_C_LI, + RISCV_INS_C_LUI, + RISCV_INS_C_LW, + RISCV_INS_C_LWSP, + RISCV_INS_C_MV, + RISCV_INS_C_NOP, + RISCV_INS_C_OR, + RISCV_INS_C_SD, + RISCV_INS_C_SDSP, + RISCV_INS_C_SLLI, + RISCV_INS_C_SRAI, + RISCV_INS_C_SRLI, + RISCV_INS_C_SUB, + RISCV_INS_C_SUBW, + RISCV_INS_C_SW, + RISCV_INS_C_SWSP, + RISCV_INS_C_UNIMP, + RISCV_INS_C_XOR, + RISCV_INS_DIV, + RISCV_INS_DIVU, + RISCV_INS_DIVUW, + RISCV_INS_DIVW, + RISCV_INS_EBREAK, + RISCV_INS_ECALL, + RISCV_INS_FADD_D, + RISCV_INS_FADD_S, + RISCV_INS_FCLASS_D, + RISCV_INS_FCLASS_S, + RISCV_INS_FCVT_D_L, + RISCV_INS_FCVT_D_LU, + RISCV_INS_FCVT_D_S, + RISCV_INS_FCVT_D_W, + RISCV_INS_FCVT_D_WU, + RISCV_INS_FCVT_LU_D, + RISCV_INS_FCVT_LU_S, + RISCV_INS_FCVT_L_D, + RISCV_INS_FCVT_L_S, + RISCV_INS_FCVT_S_D, + RISCV_INS_FCVT_S_L, + RISCV_INS_FCVT_S_LU, + RISCV_INS_FCVT_S_W, + RISCV_INS_FCVT_S_WU, + RISCV_INS_FCVT_WU_D, + RISCV_INS_FCVT_WU_S, + RISCV_INS_FCVT_W_D, + RISCV_INS_FCVT_W_S, + RISCV_INS_FDIV_D, + RISCV_INS_FDIV_S, + RISCV_INS_FENCE, + RISCV_INS_FENCE_I, + RISCV_INS_FENCE_TSO, + RISCV_INS_FEQ_D, + RISCV_INS_FEQ_S, + RISCV_INS_FLD, + RISCV_INS_FLE_D, + RISCV_INS_FLE_S, + RISCV_INS_FLT_D, + RISCV_INS_FLT_S, + RISCV_INS_FLW, + RISCV_INS_FMADD_D, + RISCV_INS_FMADD_S, + RISCV_INS_FMAX_D, + RISCV_INS_FMAX_S, + RISCV_INS_FMIN_D, + RISCV_INS_FMIN_S, + RISCV_INS_FMSUB_D, + RISCV_INS_FMSUB_S, + RISCV_INS_FMUL_D, + RISCV_INS_FMUL_S, + RISCV_INS_FMV_D_X, + RISCV_INS_FMV_W_X, + RISCV_INS_FMV_X_D, + RISCV_INS_FMV_X_W, + RISCV_INS_FNMADD_D, + RISCV_INS_FNMADD_S, + RISCV_INS_FNMSUB_D, + RISCV_INS_FNMSUB_S, + RISCV_INS_FSD, + RISCV_INS_FSGNJN_D, + RISCV_INS_FSGNJN_S, + RISCV_INS_FSGNJX_D, + RISCV_INS_FSGNJX_S, + RISCV_INS_FSGNJ_D, + RISCV_INS_FSGNJ_S, + RISCV_INS_FSQRT_D, + RISCV_INS_FSQRT_S, + RISCV_INS_FSUB_D, + RISCV_INS_FSUB_S, + RISCV_INS_FSW, + RISCV_INS_JAL, + RISCV_INS_JALR, + RISCV_INS_LB, + RISCV_INS_LBU, + RISCV_INS_LD, + RISCV_INS_LH, + RISCV_INS_LHU, + RISCV_INS_LR_D, + RISCV_INS_LR_D_AQ, + RISCV_INS_LR_D_AQ_RL, + RISCV_INS_LR_D_RL, + RISCV_INS_LR_W, + RISCV_INS_LR_W_AQ, + RISCV_INS_LR_W_AQ_RL, + RISCV_INS_LR_W_RL, + RISCV_INS_LUI, + RISCV_INS_LW, + RISCV_INS_LWU, + RISCV_INS_MRET, + RISCV_INS_MUL, + RISCV_INS_MULH, + RISCV_INS_MULHSU, + RISCV_INS_MULHU, + RISCV_INS_MULW, + RISCV_INS_OR, + RISCV_INS_ORI, + RISCV_INS_REM, + RISCV_INS_REMU, + RISCV_INS_REMUW, + RISCV_INS_REMW, + RISCV_INS_SB, + RISCV_INS_SC_D, + RISCV_INS_SC_D_AQ, + RISCV_INS_SC_D_AQ_RL, + RISCV_INS_SC_D_RL, + RISCV_INS_SC_W, + RISCV_INS_SC_W_AQ, + RISCV_INS_SC_W_AQ_RL, + RISCV_INS_SC_W_RL, + RISCV_INS_SD, + RISCV_INS_SFENCE_VMA, + RISCV_INS_SH, + RISCV_INS_SLL, + RISCV_INS_SLLI, + RISCV_INS_SLLIW, + RISCV_INS_SLLW, + RISCV_INS_SLT, + RISCV_INS_SLTI, + RISCV_INS_SLTIU, + RISCV_INS_SLTU, + RISCV_INS_SRA, + RISCV_INS_SRAI, + RISCV_INS_SRAIW, + RISCV_INS_SRAW, + RISCV_INS_SRET, + RISCV_INS_SRL, + RISCV_INS_SRLI, + RISCV_INS_SRLIW, + RISCV_INS_SRLW, + RISCV_INS_SUB, + RISCV_INS_SUBW, + RISCV_INS_SW, + RISCV_INS_UNIMP, + RISCV_INS_URET, + RISCV_INS_WFI, + RISCV_INS_XOR, + RISCV_INS_XORI, + + RISCV_INS_ENDING, +} riscv_insn; + +//> Group of RISCV instructions +typedef enum riscv_insn_group { + RISCV_GRP_INVALID = 0, ///< = CS_GRP_INVALID + + // Generic groups + // all jump instructions (conditional+direct+indirect jumps) + RISCV_GRP_JUMP, ///< = CS_GRP_JUMP + // all call instructions + RISCV_GRP_CALL, ///< = CS_GRP_CALL + // all return instructions + RISCV_GRP_RET, ///< = CS_GRP_RET + // all interrupt instructions (int+syscall) + RISCV_GRP_INT, ///< = CS_GRP_INT + // all interrupt return instructions + RISCV_GRP_IRET, ///< = CS_GRP_IRET + // all privileged instructions + RISCV_GRP_PRIVILEGE, ///< = CS_GRP_PRIVILEGE + // all relative branching instructions + RISCV_GRP_BRANCH_RELATIVE, ///< = CS_GRP_BRANCH_RELATIVE + + // Architecture-specific groups + RISCV_GRP_ISRV32 = 128, + RISCV_GRP_ISRV64, + RISCV_GRP_HASSTDEXTA, + RISCV_GRP_HASSTDEXTC, + RISCV_GRP_HASSTDEXTD, + RISCV_GRP_HASSTDEXTF, + RISCV_GRP_HASSTDEXTM, + /* + RISCV_GRP_ISRVA, + RISCV_GRP_ISRVC, + RISCV_GRP_ISRVD, + RISCV_GRP_ISRVCD, + RISCV_GRP_ISRVF, + RISCV_GRP_ISRV32C, + RISCV_GRP_ISRV32CF, + RISCV_GRP_ISRVM, + RISCV_GRP_ISRV64A, + RISCV_GRP_ISRV64C, + RISCV_GRP_ISRV64D, + RISCV_GRP_ISRV64F, + RISCV_GRP_ISRV64M, + */ + RISCV_GRP_ENDING, +} riscv_insn_group; + +#ifdef __cplusplus +} +#endif + +#endif + +/* Capstone Disassembly Engine */ +/* By Spike , xwings 2019 */ + +#ifndef CAPSTONE_WASM_H +#define CAPSTONE_WASM_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +typedef enum wasm_op_type { + WASM_OP_INVALID = 0, + WASM_OP_NONE, + WASM_OP_INT7, + WASM_OP_VARUINT32, + WASM_OP_VARUINT64, + WASM_OP_UINT32, + WASM_OP_UINT64, + WASM_OP_IMM, + WASM_OP_BRTABLE, +} wasm_op_type; + +typedef struct cs_wasm_brtable { + uint32_t length; + uint64_t address; + uint32_t default_target; +} cs_wasm_brtable; + +typedef struct cs_wasm_op { + wasm_op_type type; + uint32_t size; + union { + int8_t int7; + uint32_t varuint32; + uint64_t varuint64; + uint32_t uint32; + uint64_t uint64; + uint32_t immediate[2]; + cs_wasm_brtable brtable; + }; +} cs_wasm_op; + +/// Instruction structure +typedef struct cs_wasm { + uint8_t op_count; + cs_wasm_op operands[2]; +} cs_wasm; + +/// WASM instruction +typedef enum wasm_insn { + WASM_INS_UNREACHABLE = 0x0, + WASM_INS_NOP = 0x1, + WASM_INS_BLOCK = 0x2, + WASM_INS_LOOP = 0x3, + WASM_INS_IF = 0x4, + WASM_INS_ELSE = 0x5, + WASM_INS_END = 0xb, + WASM_INS_BR = 0xc, + WASM_INS_BR_IF = 0xd, + WASM_INS_BR_TABLE = 0xe, + WASM_INS_RETURN = 0xf, + WASM_INS_CALL = 0x10, + WASM_INS_CALL_INDIRECT = 0x11, + WASM_INS_DROP = 0x1a, + WASM_INS_SELECT = 0x1b, + WASM_INS_GET_LOCAL = 0x20, + WASM_INS_SET_LOCAL = 0x21, + WASM_INS_TEE_LOCAL = 0x22, + WASM_INS_GET_GLOBAL = 0x23, + WASM_INS_SET_GLOBAL = 0x24, + WASM_INS_I32_LOAD = 0x28, + WASM_INS_I64_LOAD = 0x29, + WASM_INS_F32_LOAD = 0x2a, + WASM_INS_F64_LOAD = 0x2b, + WASM_INS_I32_LOAD8_S = 0x2c, + WASM_INS_I32_LOAD8_U = 0x2d, + WASM_INS_I32_LOAD16_S = 0x2e, + WASM_INS_I32_LOAD16_U = 0x2f, + WASM_INS_I64_LOAD8_S = 0x30, + WASM_INS_I64_LOAD8_U = 0x31, + WASM_INS_I64_LOAD16_S = 0x32, + WASM_INS_I64_LOAD16_U = 0x33, + WASM_INS_I64_LOAD32_S = 0x34, + WASM_INS_I64_LOAD32_U = 0x35, + WASM_INS_I32_STORE = 0x36, + WASM_INS_I64_STORE = 0x37, + WASM_INS_F32_STORE = 0x38, + WASM_INS_F64_STORE = 0x39, + WASM_INS_I32_STORE8 = 0x3a, + WASM_INS_I32_STORE16 = 0x3b, + WASM_INS_I64_STORE8 = 0x3c, + WASM_INS_I64_STORE16 = 0x3d, + WASM_INS_I64_STORE32 = 0x3e, + WASM_INS_CURRENT_MEMORY = 0x3f, + WASM_INS_GROW_MEMORY = 0x40, + WASM_INS_I32_CONST = 0x41, + WASM_INS_I64_CONST = 0x42, + WASM_INS_F32_CONST = 0x43, + WASM_INS_F64_CONST = 0x44, + WASM_INS_I32_EQZ = 0x45, + WASM_INS_I32_EQ = 0x46, + WASM_INS_I32_NE = 0x47, + WASM_INS_I32_LT_S = 0x48, + WASM_INS_I32_LT_U = 0x49, + WASM_INS_I32_GT_S = 0x4a, + WASM_INS_I32_GT_U = 0x4b, + WASM_INS_I32_LE_S = 0x4c, + WASM_INS_I32_LE_U = 0x4d, + WASM_INS_I32_GE_S = 0x4e, + WASM_INS_I32_GE_U = 0x4f, + WASM_INS_I64_EQZ = 0x50, + WASM_INS_I64_EQ = 0x51, + WASM_INS_I64_NE = 0x52, + WASM_INS_I64_LT_S = 0x53, + WASM_INS_I64_LT_U = 0x54, + WASN_INS_I64_GT_S = 0x55, + WASM_INS_I64_GT_U = 0x56, + WASM_INS_I64_LE_S = 0x57, + WASM_INS_I64_LE_U = 0x58, + WASM_INS_I64_GE_S = 0x59, + WASM_INS_I64_GE_U = 0x5a, + WASM_INS_F32_EQ = 0x5b, + WASM_INS_F32_NE = 0x5c, + WASM_INS_F32_LT = 0x5d, + WASM_INS_F32_GT = 0x5e, + WASM_INS_F32_LE = 0x5f, + WASM_INS_F32_GE = 0x60, + WASM_INS_F64_EQ = 0x61, + WASM_INS_F64_NE = 0x62, + WASM_INS_F64_LT = 0x63, + WASM_INS_F64_GT = 0x64, + WASM_INS_F64_LE = 0x65, + WASM_INS_F64_GE = 0x66, + WASM_INS_I32_CLZ = 0x67, + WASM_INS_I32_CTZ = 0x68, + WASM_INS_I32_POPCNT = 0x69, + WASM_INS_I32_ADD = 0x6a, + WASM_INS_I32_SUB = 0x6b, + WASM_INS_I32_MUL = 0x6c, + WASM_INS_I32_DIV_S = 0x6d, + WASM_INS_I32_DIV_U = 0x6e, + WASM_INS_I32_REM_S = 0x6f, + WASM_INS_I32_REM_U = 0x70, + WASM_INS_I32_AND = 0x71, + WASM_INS_I32_OR = 0x72, + WASM_INS_I32_XOR = 0x73, + WASM_INS_I32_SHL = 0x74, + WASM_INS_I32_SHR_S = 0x75, + WASM_INS_I32_SHR_U = 0x76, + WASM_INS_I32_ROTL = 0x77, + WASM_INS_I32_ROTR = 0x78, + WASM_INS_I64_CLZ = 0x79, + WASM_INS_I64_CTZ = 0x7a, + WASM_INS_I64_POPCNT = 0x7b, + WASM_INS_I64_ADD = 0x7c, + WASM_INS_I64_SUB = 0x7d, + WASM_INS_I64_MUL = 0x7e, + WASM_INS_I64_DIV_S = 0x7f, + WASM_INS_I64_DIV_U = 0x80, + WASM_INS_I64_REM_S = 0x81, + WASM_INS_I64_REM_U = 0x82, + WASM_INS_I64_AND = 0x83, + WASM_INS_I64_OR = 0x84, + WASM_INS_I64_XOR = 0x85, + WASM_INS_I64_SHL = 0x86, + WASM_INS_I64_SHR_S = 0x87, + WASM_INS_I64_SHR_U = 0x88, + WASM_INS_I64_ROTL = 0x89, + WASM_INS_I64_ROTR = 0x8a, + WASM_INS_F32_ABS = 0x8b, + WASM_INS_F32_NEG = 0x8c, + WASM_INS_F32_CEIL = 0x8d, + WASM_INS_F32_FLOOR = 0x8e, + WASM_INS_F32_TRUNC = 0x8f, + WASM_INS_F32_NEAREST = 0x90, + WASM_INS_F32_SQRT = 0x91, + WASM_INS_F32_ADD = 0x92, + WASM_INS_F32_SUB = 0x93, + WASM_INS_F32_MUL = 0x94, + WASM_INS_F32_DIV = 0x95, + WASM_INS_F32_MIN = 0x96, + WASM_INS_F32_MAX = 0x97, + WASM_INS_F32_COPYSIGN = 0x98, + WASM_INS_F64_ABS = 0x99, + WASM_INS_F64_NEG = 0x9a, + WASM_INS_F64_CEIL = 0x9b, + WASM_INS_F64_FLOOR = 0x9c, + WASM_INS_F64_TRUNC = 0x9d, + WASM_INS_F64_NEAREST = 0x9e, + WASM_INS_F64_SQRT = 0x9f, + WASM_INS_F64_ADD = 0xa0, + WASM_INS_F64_SUB = 0xa1, + WASM_INS_F64_MUL = 0xa2, + WASM_INS_F64_DIV = 0xa3, + WASM_INS_F64_MIN = 0xa4, + WASM_INS_F64_MAX = 0xa5, + WASM_INS_F64_COPYSIGN = 0xa6, + WASM_INS_I32_WARP_I64 = 0xa7, + WASP_INS_I32_TRUNC_S_F32 = 0xa8, + WASM_INS_I32_TRUNC_U_F32 = 0xa9, + WASM_INS_I32_TRUNC_S_F64 = 0xaa, + WASM_INS_I32_TRUNC_U_F64 = 0xab, + WASM_INS_I64_EXTEND_S_I32 = 0xac, + WASM_INS_I64_EXTEND_U_I32 = 0xad, + WASM_INS_I64_TRUNC_S_F32 = 0xae, + WASM_INS_I64_TRUNC_U_F32 = 0xaf, + WASM_INS_I64_TRUNC_S_F64 = 0xb0, + WASM_INS_I64_TRUNC_U_F64 = 0xb1, + WASM_INS_F32_CONVERT_S_I32 = 0xb2, + WASM_INS_F32_CONVERT_U_I32 = 0xb3, + WASM_INS_F32_CONVERT_S_I64 = 0xb4, + WASM_INS_F32_CONVERT_U_I64 = 0xb5, + WASM_INS_F32_DEMOTE_F64 = 0xb6, + WASM_INS_F64_CONVERT_S_I32 = 0xb7, + WASM_INS_F64_CONVERT_U_I32 = 0xb8, + WASM_INS_F64_CONVERT_S_I64 = 0xb9, + WASM_INS_F64_CONVERT_U_I64 = 0xba, + WASM_INS_F64_PROMOTE_F32 = 0xbb, + WASM_INS_I32_REINTERPRET_F32 = 0xbc, + WASM_INS_I64_REINTERPRET_F64 = 0xbd, + WASM_INS_F32_REINTERPRET_I32 = 0xbe, + WASM_INS_F64_REINTERPRET_I64 = 0xbf, + WASM_INS_INVALID = 512, + WASM_INS_ENDING, +} wasm_insn; + +/// Group of WASM instructions +typedef enum wasm_insn_group { + WASM_GRP_INVALID = 0, ///< = CS_GRP_INVALID + + WASM_GRP_NUMBERIC = 8, + WASM_GRP_PARAMETRIC, + WASM_GRP_VARIABLE, + WASM_GRP_MEMORY, + WASM_GRP_CONTROL, + + WASM_GRP_ENDING, ///< <-- mark the end of the list of groups +} wasm_insn_group; + +#ifdef __cplusplus +} +#endif + +#endif +#ifndef CAPSTONE_MOS65XX_H +#define CAPSTONE_MOS65XX_H + +/* Capstone Disassembly Engine */ +/* By Sebastian Macke , 2019 */ + +#ifndef CAPSTONE_BPF_H +#define CAPSTONE_BPF_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +/// Operand type for instruction's operands +typedef enum bpf_op_type { + BPF_OP_INVALID = 0, + + BPF_OP_REG, + BPF_OP_IMM, + BPF_OP_OFF, + BPF_OP_MEM, + BPF_OP_MMEM, ///< M[k] in cBPF + BPF_OP_MSH, ///< corresponds to cBPF's BPF_MSH mode + BPF_OP_EXT, ///< cBPF's extension (not eBPF) +} bpf_op_type; + +/// BPF registers +typedef enum bpf_reg { + BPF_REG_INVALID = 0, + + ///< cBPF + BPF_REG_A, + BPF_REG_X, + + ///< eBPF + BPF_REG_R0, + BPF_REG_R1, + BPF_REG_R2, + BPF_REG_R3, + BPF_REG_R4, + BPF_REG_R5, + BPF_REG_R6, + BPF_REG_R7, + BPF_REG_R8, + BPF_REG_R9, + BPF_REG_R10, + + BPF_REG_ENDING, +} bpf_reg; + +/// Instruction's operand referring to memory +/// This is associated with BPF_OP_MEM operand type above +typedef struct bpf_op_mem { + bpf_reg base; ///< base register + uint32_t disp; ///< offset value +} bpf_op_mem; + +typedef enum bpf_ext_type { + BPF_EXT_INVALID = 0, + + BPF_EXT_LEN, +} bpf_ext_type; + +/// Instruction operand +typedef struct cs_bpf_op { + bpf_op_type type; + union { + uint8_t reg; ///< register value for REG operand + uint64_t imm; ///< immediate value IMM operand + uint32_t off; ///< offset value, used in jump & call + bpf_op_mem mem; ///< base/disp value for MEM operand + /* cBPF only */ + uint32_t mmem; ///< M[k] in cBPF + uint32_t msh; ///< corresponds to cBPF's BPF_MSH mode + uint32_t ext; ///< cBPF's extension (not eBPF) + }; + + /// How is this operand accessed? (READ, WRITE or READ|WRITE) + /// This field is combined of cs_ac_type. + /// NOTE: this field is irrelevant if engine is compiled in DIET mode. + uint8_t access; +} cs_bpf_op; + +/// Instruction structure +typedef struct cs_bpf { + uint8_t op_count; + cs_bpf_op operands[4]; +} cs_bpf; + +/// BPF instruction +typedef enum cs_bpf_insn { + BPF_INS_INVALID = 0, + + ///< ALU + BPF_INS_ADD, + BPF_INS_SUB, + BPF_INS_MUL, + BPF_INS_DIV, + BPF_INS_OR, + BPF_INS_AND, + BPF_INS_LSH, + BPF_INS_RSH, + BPF_INS_NEG, + BPF_INS_MOD, + BPF_INS_XOR, + BPF_INS_MOV, ///< eBPF only + BPF_INS_ARSH, ///< eBPF only + + ///< ALU64, eBPF only + BPF_INS_ADD64, + BPF_INS_SUB64, + BPF_INS_MUL64, + BPF_INS_DIV64, + BPF_INS_OR64, + BPF_INS_AND64, + BPF_INS_LSH64, + BPF_INS_RSH64, + BPF_INS_NEG64, + BPF_INS_MOD64, + BPF_INS_XOR64, + BPF_INS_MOV64, + BPF_INS_ARSH64, + + ///< Byteswap, eBPF only + BPF_INS_LE16, + BPF_INS_LE32, + BPF_INS_LE64, + BPF_INS_BE16, + BPF_INS_BE32, + BPF_INS_BE64, + + ///< Load + BPF_INS_LDW, ///< eBPF only + BPF_INS_LDH, + BPF_INS_LDB, + BPF_INS_LDDW, ///< eBPF only: load 64-bit imm + BPF_INS_LDXW, ///< eBPF only + BPF_INS_LDXH, ///< eBPF only + BPF_INS_LDXB, ///< eBPF only + BPF_INS_LDXDW, ///< eBPF only + + ///< Store + BPF_INS_STW, ///< eBPF only + BPF_INS_STH, ///< eBPF only + BPF_INS_STB, ///< eBPF only + BPF_INS_STDW, ///< eBPF only + BPF_INS_STXW, ///< eBPF only + BPF_INS_STXH, ///< eBPF only + BPF_INS_STXB, ///< eBPF only + BPF_INS_STXDW, ///< eBPF only + BPF_INS_XADDW, ///< eBPF only + BPF_INS_XADDDW, ///< eBPF only + + ///< Jump + BPF_INS_JMP, + BPF_INS_JEQ, + BPF_INS_JGT, + BPF_INS_JGE, + BPF_INS_JSET, + BPF_INS_JNE, ///< eBPF only + BPF_INS_JSGT, ///< eBPF only + BPF_INS_JSGE, ///< eBPF only + BPF_INS_CALL, ///< eBPF only + BPF_INS_CALLX, ///< eBPF only + BPF_INS_EXIT, ///< eBPF only + BPF_INS_JLT, ///< eBPF only + BPF_INS_JLE, ///< eBPF only + BPF_INS_JSLT, ///< eBPF only + BPF_INS_JSLE, ///< eBPF only + + ///< Return, cBPF only + BPF_INS_RET, + + ///< Misc, cBPF only + BPF_INS_TAX, + BPF_INS_TXA, + + BPF_INS_ENDING, + + // alias instructions + BPF_INS_LD = BPF_INS_LDW, ///< cBPF only + BPF_INS_LDX = BPF_INS_LDXW, ///< cBPF only + BPF_INS_ST = BPF_INS_STW, ///< cBPF only + BPF_INS_STX = BPF_INS_STXW, ///< cBPF only +} cs_bpf_insn; + +/// Group of BPF instructions +typedef enum cs_bpf_insn_group { + BPF_GRP_INVALID = 0, ///< = CS_GRP_INVALID + + BPF_GRP_LOAD, + BPF_GRP_STORE, + BPF_GRP_ALU, + BPF_GRP_JUMP, + BPF_GRP_CALL, ///< eBPF only + BPF_GRP_RETURN, + BPF_GRP_MISC, ///< cBPF only + + BPF_GRP_ENDING, +} cs_bpf_insn_group; + +#ifdef __cplusplus +} +#endif + +#endif +#ifndef CAPSTONE_SH_H +#define CAPSTONE_SH_H + +/* Capstone Disassembly Engine */ +/* By Yoshinori Sato, 2022 */ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +/// SH registers and special registers +typedef enum { + SH_REG_INVALID = 0, + + SH_REG_R0, + SH_REG_R1, + SH_REG_R2, + SH_REG_R3, + SH_REG_R4, + SH_REG_R5, + SH_REG_R6, + SH_REG_R7, + + SH_REG_R8, + SH_REG_R9, + SH_REG_R10, + SH_REG_R11, + SH_REG_R12, + SH_REG_R13, + SH_REG_R14, + SH_REG_R15, + + SH_REG_R0_BANK, + SH_REG_R1_BANK, + SH_REG_R2_BANK, + SH_REG_R3_BANK, + SH_REG_R4_BANK, + SH_REG_R5_BANK, + SH_REG_R6_BANK, + SH_REG_R7_BANK, + + SH_REG_FR0, + SH_REG_FR1, + SH_REG_FR2, + SH_REG_FR3, + SH_REG_FR4, + SH_REG_FR5, + SH_REG_FR6, + SH_REG_FR7, + SH_REG_FR8, + SH_REG_FR9, + SH_REG_FR10, + SH_REG_FR11, + SH_REG_FR12, + SH_REG_FR13, + SH_REG_FR14, + SH_REG_FR15, + + SH_REG_DR0, + SH_REG_DR2, + SH_REG_DR4, + SH_REG_DR6, + SH_REG_DR8, + SH_REG_DR10, + SH_REG_DR12, + SH_REG_DR14, + + SH_REG_XD0, + SH_REG_XD2, + SH_REG_XD4, + SH_REG_XD6, + SH_REG_XD8, + SH_REG_XD10, + SH_REG_XD12, + SH_REG_XD14, + + SH_REG_XF0, + SH_REG_XF1, + SH_REG_XF2, + SH_REG_XF3, + SH_REG_XF4, + SH_REG_XF5, + SH_REG_XF6, + SH_REG_XF7, + SH_REG_XF8, + SH_REG_XF9, + SH_REG_XF10, + SH_REG_XF11, + SH_REG_XF12, + SH_REG_XF13, + SH_REG_XF14, + SH_REG_XF15, + + SH_REG_FV0, + SH_REG_FV4, + SH_REG_FV8, + SH_REG_FV12, + + SH_REG_XMATRX, + + SH_REG_PC, + SH_REG_PR, + SH_REG_MACH, + SH_REG_MACL, + + SH_REG_SR, + SH_REG_GBR, + SH_REG_SSR, + SH_REG_SPC, + SH_REG_SGR, + SH_REG_DBR, + SH_REG_VBR, + SH_REG_TBR, + SH_REG_RS, + SH_REG_RE, + SH_REG_MOD, + + SH_REG_FPUL, + SH_REG_FPSCR, + + SH_REG_DSP_X0, + SH_REG_DSP_X1, + SH_REG_DSP_Y0, + SH_REG_DSP_Y1, + SH_REG_DSP_A0, + SH_REG_DSP_A1, + SH_REG_DSP_A0G, + SH_REG_DSP_A1G, + SH_REG_DSP_M0, + SH_REG_DSP_M1, + SH_REG_DSP_DSR, + + SH_REG_DSP_RSV0, + SH_REG_DSP_RSV1, + SH_REG_DSP_RSV2, + SH_REG_DSP_RSV3, + SH_REG_DSP_RSV4, + SH_REG_DSP_RSV5, + SH_REG_DSP_RSV6, + SH_REG_DSP_RSV7, + SH_REG_DSP_RSV8, + SH_REG_DSP_RSV9, + SH_REG_DSP_RSVA, + SH_REG_DSP_RSVB, + SH_REG_DSP_RSVC, + SH_REG_DSP_RSVD, + SH_REG_DSP_RSVE, + SH_REG_DSP_RSVF, + + SH_REG_ENDING, // <-- mark the end of the list of registers +} sh_reg; + +typedef enum { + SH_OP_INVALID = 0, ///< = CS_OP_INVALID (Uninitialized). + SH_OP_REG, ///< = CS_OP_REG (Register operand). + SH_OP_IMM, ///< = CS_OP_IMM (Immediate operand). + SH_OP_MEM, ///< = CS_OP_MEM (Memory operand). +} sh_op_type; + +typedef enum { + SH_OP_MEM_INVALID = 0, /// <= Invalid + SH_OP_MEM_REG_IND, /// <= Register indirect + SH_OP_MEM_REG_POST, /// <= Register post increment + SH_OP_MEM_REG_PRE, /// <= Register pre decrement + SH_OP_MEM_REG_DISP, /// <= displacement + SH_OP_MEM_REG_R0, /// <= R0 indexed + SH_OP_MEM_GBR_DISP, /// <= GBR based displacement + SH_OP_MEM_GBR_R0, /// <= GBR based R0 indexed + SH_OP_MEM_PCR, /// <= PC relative + SH_OP_MEM_TBR_DISP, /// <= TBR based displaysment +} sh_op_mem_type; + +typedef struct sh_op_mem { + sh_op_mem_type address; /// <= memory address + sh_reg reg; /// <= base register + uint32_t disp; /// <= displacement +} sh_op_mem; + +// SH-DSP instcutions define +typedef enum sh_dsp_insn_type { + SH_INS_DSP_INVALID, + SH_INS_DSP_DOUBLE, + SH_INS_DSP_SINGLE, + SH_INS_DSP_PARALLEL, +} sh_dsp_insn_type; + +typedef enum sh_dsp_insn { + SH_INS_DSP_NOP = 1, + SH_INS_DSP_MOV, + SH_INS_DSP_PSHL, + SH_INS_DSP_PSHA, + SH_INS_DSP_PMULS, + SH_INS_DSP_PCLR_PMULS, + SH_INS_DSP_PSUB_PMULS, + SH_INS_DSP_PADD_PMULS, + SH_INS_DSP_PSUBC, + SH_INS_DSP_PADDC, + SH_INS_DSP_PCMP, + SH_INS_DSP_PABS, + SH_INS_DSP_PRND, + SH_INS_DSP_PSUB, + SH_INS_DSP_PSUBr, + SH_INS_DSP_PADD, + SH_INS_DSP_PAND, + SH_INS_DSP_PXOR, + SH_INS_DSP_POR, + SH_INS_DSP_PDEC, + SH_INS_DSP_PINC, + SH_INS_DSP_PCLR, + SH_INS_DSP_PDMSB, + SH_INS_DSP_PNEG, + SH_INS_DSP_PCOPY, + SH_INS_DSP_PSTS, + SH_INS_DSP_PLDS, + SH_INS_DSP_PSWAP, + SH_INS_DSP_PWAD, + SH_INS_DSP_PWSB, +} sh_dsp_insn; + +typedef enum sh_dsp_operand { + SH_OP_DSP_INVALID, + SH_OP_DSP_REG_PRE, + SH_OP_DSP_REG_IND, + SH_OP_DSP_REG_POST, + SH_OP_DSP_REG_INDEX, + SH_OP_DSP_REG, + SH_OP_DSP_IMM, + +} sh_dsp_operand; + +typedef enum sh_dsp_cc { + SH_DSP_CC_INVALID, + SH_DSP_CC_NONE, + SH_DSP_CC_DCT, + SH_DSP_CC_DCF, +} sh_dsp_cc; + +typedef struct sh_op_dsp { + sh_dsp_insn insn; + sh_dsp_operand operand[2]; + sh_reg r[6]; + sh_dsp_cc cc; + uint8_t imm; + int size; +} sh_op_dsp; + +/// Instruction operand +typedef struct cs_sh_op { + sh_op_type type; + union { + uint64_t imm; ///< immediate value for IMM operand + sh_reg reg; ///< register value for REG operand + sh_op_mem mem; ///< data when operand is targeting memory + sh_op_dsp dsp; ///< dsp instruction + }; +} cs_sh_op; + +/// SH instruction +typedef enum sh_insn { + SH_INS_INVALID, + SH_INS_ADD_r, + SH_INS_ADD, + SH_INS_ADDC, + SH_INS_ADDV, + SH_INS_AND, + SH_INS_BAND, + SH_INS_BANDNOT, + SH_INS_BCLR, + SH_INS_BF, + SH_INS_BF_S, + SH_INS_BLD, + SH_INS_BLDNOT, + SH_INS_BOR, + SH_INS_BORNOT, + SH_INS_BRA, + SH_INS_BRAF, + SH_INS_BSET, + SH_INS_BSR, + SH_INS_BSRF, + SH_INS_BST, + SH_INS_BT, + SH_INS_BT_S, + SH_INS_BXOR, + SH_INS_CLIPS, + SH_INS_CLIPU, + SH_INS_CLRDMXY, + SH_INS_CLRMAC, + SH_INS_CLRS, + SH_INS_CLRT, + SH_INS_CMP_EQ, + SH_INS_CMP_GE, + SH_INS_CMP_GT, + SH_INS_CMP_HI, + SH_INS_CMP_HS, + SH_INS_CMP_PL, + SH_INS_CMP_PZ, + SH_INS_CMP_STR, + SH_INS_DIV0S, + SH_INS_DIV0U, + SH_INS_DIV1, + SH_INS_DIVS, + SH_INS_DIVU, + SH_INS_DMULS_L, + SH_INS_DMULU_L, + SH_INS_DT, + SH_INS_EXTS_B, + SH_INS_EXTS_W, + SH_INS_EXTU_B, + SH_INS_EXTU_W, + SH_INS_FABS, + SH_INS_FADD, + SH_INS_FCMP_EQ, + SH_INS_FCMP_GT, + SH_INS_FCNVDS, + SH_INS_FCNVSD, + SH_INS_FDIV, + SH_INS_FIPR, + SH_INS_FLDI0, + SH_INS_FLDI1, + SH_INS_FLDS, + SH_INS_FLOAT, + SH_INS_FMAC, + SH_INS_FMOV, + SH_INS_FMUL, + SH_INS_FNEG, + SH_INS_FPCHG, + SH_INS_FRCHG, + SH_INS_FSCA, + SH_INS_FSCHG, + SH_INS_FSQRT, + SH_INS_FSRRA, + SH_INS_FSTS, + SH_INS_FSUB, + SH_INS_FTRC, + SH_INS_FTRV, + SH_INS_ICBI, + SH_INS_JMP, + SH_INS_JSR, + SH_INS_JSR_N, + SH_INS_LDBANK, + SH_INS_LDC, + SH_INS_LDRC, + SH_INS_LDRE, + SH_INS_LDRS, + SH_INS_LDS, + SH_INS_LDTLB, + SH_INS_MAC_L, + SH_INS_MAC_W, + SH_INS_MOV, + SH_INS_MOVA, + SH_INS_MOVCA, + SH_INS_MOVCO, + SH_INS_MOVI20, + SH_INS_MOVI20S, + SH_INS_MOVLI, + SH_INS_MOVML, + SH_INS_MOVMU, + SH_INS_MOVRT, + SH_INS_MOVT, + SH_INS_MOVU, + SH_INS_MOVUA, + SH_INS_MUL_L, + SH_INS_MULR, + SH_INS_MULS_W, + SH_INS_MULU_W, + SH_INS_NEG, + SH_INS_NEGC, + SH_INS_NOP, + SH_INS_NOT, + SH_INS_NOTT, + SH_INS_OCBI, + SH_INS_OCBP, + SH_INS_OCBWB, + SH_INS_OR, + SH_INS_PREF, + SH_INS_PREFI, + SH_INS_RESBANK, + SH_INS_ROTCL, + SH_INS_ROTCR, + SH_INS_ROTL, + SH_INS_ROTR, + SH_INS_RTE, + SH_INS_RTS, + SH_INS_RTS_N, + SH_INS_RTV_N, + SH_INS_SETDMX, + SH_INS_SETDMY, + SH_INS_SETRC, + SH_INS_SETS, + SH_INS_SETT, + SH_INS_SHAD, + SH_INS_SHAL, + SH_INS_SHAR, + SH_INS_SHLD, + SH_INS_SHLL, + SH_INS_SHLL16, + SH_INS_SHLL2, + SH_INS_SHLL8, + SH_INS_SHLR, + SH_INS_SHLR16, + SH_INS_SHLR2, + SH_INS_SHLR8, + SH_INS_SLEEP, + SH_INS_STBANK, + SH_INS_STC, + SH_INS_STS, + SH_INS_SUB, + SH_INS_SUBC, + SH_INS_SUBV, + SH_INS_SWAP_B, + SH_INS_SWAP_W, + SH_INS_SYNCO, + SH_INS_TAS, + SH_INS_TRAPA, + SH_INS_TST, + SH_INS_XOR, + SH_INS_XTRCT, + SH_INS_DSP, + SH_INS_ENDING, // <-- mark the end of the list of instructions +} sh_insn; + +/// Instruction structure +typedef struct cs_sh { + sh_insn insn; + uint8_t size; + uint8_t op_count; + cs_sh_op operands[3]; +} cs_sh; + +/// Group of SH instructions +typedef enum sh_insn_group { + SH_GRP_INVALID = 0, ///< CS_GRUP_INVALID + SH_GRP_JUMP, ///< = CS_GRP_JUMP + SH_GRP_CALL, ///< = CS_GRP_CALL + SH_GRP_INT, ///< = CS_GRP_INT + SH_GRP_RET, ///< = CS_GRP_RET + SH_GRP_IRET, ///< = CS_GRP_IRET + SH_GRP_PRIVILEGE, ///< = CS_GRP_PRIVILEGE + SH_GRP_BRANCH_RELATIVE, ///< = CS_GRP_BRANCH_RELATIVE + + SH_GRP_SH1, + SH_GRP_SH2, + SH_GRP_SH2E, + SH_GRP_SH2DSP, + SH_GRP_SH2A, + SH_GRP_SH2AFPU, + SH_GRP_SH3, + SH_GRP_SH3DSP, + SH_GRP_SH4, + SH_GRP_SH4A, + + SH_GRP_ENDING,// <-- mark the end of the list of groups +} sh_insn_group; + +#ifdef __cplusplus +} +#endif + +#endif +#ifndef CAPSTONE_TRICORE_H +#define CAPSTONE_TRICORE_H + +/* Capstone Disassembly Engine */ +/* By Nguyen Anh Quynh , 2014 */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(_MSC_VER) || !defined(_KERNEL_MODE) +#include +#endif + + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +/// Operand type for instruction's operands +typedef enum tricore_op_type { + TRICORE_OP_INVALID = CS_OP_INVALID, ///< CS_OP_INVALID (Uninitialized). + TRICORE_OP_REG = CS_OP_REG, ///< CS_OP_REG (Register operand). + TRICORE_OP_IMM = CS_OP_IMM, ///< CS_OP_IMM (Immediate operand). + TRICORE_OP_MEM = CS_OP_MEM, ///< CS_OP_MEM (Memory operand). +} tricore_op_type; + +/// Instruction's operand referring to memory +/// This is associated with TRICORE_OP_MEM operand type above +typedef struct tricore_op_mem { + uint8_t base; ///< base register + int32_t disp; ///< displacement/offset value +} tricore_op_mem; + +/// Instruction operand +typedef struct cs_tricore_op { + tricore_op_type type; ///< operand type + union { + unsigned int reg; ///< register value for REG operand + int32_t imm; ///< immediate value for IMM operand + tricore_op_mem mem; ///< base/disp value for MEM operand + }; + /// This field is combined of cs_ac_type. + /// NOTE: this field is irrelevant if engine is compiled in DIET mode. + uint8_t access; ///< How is this operand accessed? (READ, WRITE or READ|WRITE) +} cs_tricore_op; + +#define TRICORE_OP_COUNT 8 + +/// Instruction structure +typedef struct cs_tricore { + uint8_t op_count; ///< number of operands of this instruction. + cs_tricore_op + operands[TRICORE_OP_COUNT]; ///< operands for this instruction. + /// TODO: Mark the modified flags register in td files and regenerate inc files + bool update_flags; ///< whether the flags register is updated. +} cs_tricore; + +/// TriCore registers +typedef enum tricore_reg { + // generate content begin + // clang-format off + + TRICORE_REG_INVALID = 0, + TRICORE_REG_FCX = 1, + TRICORE_REG_PC = 2, + TRICORE_REG_PCXI = 3, + TRICORE_REG_PSW = 4, + TRICORE_REG_A0 = 5, + TRICORE_REG_A1 = 6, + TRICORE_REG_A2 = 7, + TRICORE_REG_A3 = 8, + TRICORE_REG_A4 = 9, + TRICORE_REG_A5 = 10, + TRICORE_REG_A6 = 11, + TRICORE_REG_A7 = 12, + TRICORE_REG_A8 = 13, + TRICORE_REG_A9 = 14, + TRICORE_REG_A10 = 15, + TRICORE_REG_A11 = 16, + TRICORE_REG_A12 = 17, + TRICORE_REG_A13 = 18, + TRICORE_REG_A14 = 19, + TRICORE_REG_A15 = 20, + TRICORE_REG_D0 = 21, + TRICORE_REG_D1 = 22, + TRICORE_REG_D2 = 23, + TRICORE_REG_D3 = 24, + TRICORE_REG_D4 = 25, + TRICORE_REG_D5 = 26, + TRICORE_REG_D6 = 27, + TRICORE_REG_D7 = 28, + TRICORE_REG_D8 = 29, + TRICORE_REG_D9 = 30, + TRICORE_REG_D10 = 31, + TRICORE_REG_D11 = 32, + TRICORE_REG_D12 = 33, + TRICORE_REG_D13 = 34, + TRICORE_REG_D14 = 35, + TRICORE_REG_D15 = 36, + TRICORE_REG_E0 = 37, + TRICORE_REG_E2 = 38, + TRICORE_REG_E4 = 39, + TRICORE_REG_E6 = 40, + TRICORE_REG_E8 = 41, + TRICORE_REG_E10 = 42, + TRICORE_REG_E12 = 43, + TRICORE_REG_E14 = 44, + TRICORE_REG_P0 = 45, + TRICORE_REG_P2 = 46, + TRICORE_REG_P4 = 47, + TRICORE_REG_P6 = 48, + TRICORE_REG_P8 = 49, + TRICORE_REG_P10 = 50, + TRICORE_REG_P12 = 51, + TRICORE_REG_P14 = 52, + TRICORE_REG_A0_A1 = 53, + TRICORE_REG_A2_A3 = 54, + TRICORE_REG_A4_A5 = 55, + TRICORE_REG_A6_A7 = 56, + TRICORE_REG_A8_A9 = 57, + TRICORE_REG_A10_A11 = 58, + TRICORE_REG_A12_A13 = 59, + TRICORE_REG_A14_A15 = 60, + TRICORE_REG_ENDING, // 61 + + // clang-format on + // generate content end +} tricore_reg; + +/// TriCore instruction +typedef enum tricore_insn { + TRICORE_INS_INVALID = 0, + // generate content begin + // clang-format off + + TRICORE_INS_XOR_T, + TRICORE_INS_ABSDIFS_B, + TRICORE_INS_ABSDIFS_H, + TRICORE_INS_ABSDIFS, + TRICORE_INS_ABSDIF_B, + TRICORE_INS_ABSDIF_H, + TRICORE_INS_ABSDIF, + TRICORE_INS_ABSS_B, + TRICORE_INS_ABSS_H, + TRICORE_INS_ABSS, + TRICORE_INS_ABS_B, + TRICORE_INS_ABS_H, + TRICORE_INS_ABS, + TRICORE_INS_ADDC, + TRICORE_INS_ADDIH_A, + TRICORE_INS_ADDIH, + TRICORE_INS_ADDI, + TRICORE_INS_ADDSC_AT, + TRICORE_INS_ADDSC_A, + TRICORE_INS_ADDS_BU, + TRICORE_INS_ADDS_B, + TRICORE_INS_ADDS_H, + TRICORE_INS_ADDS_HU, + TRICORE_INS_ADDS_U, + TRICORE_INS_ADDS, + TRICORE_INS_ADDX, + TRICORE_INS_ADD_A, + TRICORE_INS_ADD_B, + TRICORE_INS_ADD_F, + TRICORE_INS_ADD_H, + TRICORE_INS_ADD, + TRICORE_INS_ANDN_T, + TRICORE_INS_ANDN, + TRICORE_INS_AND_ANDN_T, + TRICORE_INS_AND_AND_T, + TRICORE_INS_AND_EQ, + TRICORE_INS_AND_GE_U, + TRICORE_INS_AND_GE, + TRICORE_INS_AND_LT_U, + TRICORE_INS_AND_LT, + TRICORE_INS_AND_NE, + TRICORE_INS_AND_NOR_T, + TRICORE_INS_AND_OR_T, + TRICORE_INS_AND_T, + TRICORE_INS_AND, + TRICORE_INS_BISR, + TRICORE_INS_BMERGE, + TRICORE_INS_BSPLIT, + TRICORE_INS_CACHEA_I, + TRICORE_INS_CACHEA_WI, + TRICORE_INS_CACHEA_W, + TRICORE_INS_CACHEI_I, + TRICORE_INS_CACHEI_WI, + TRICORE_INS_CACHEI_W, + TRICORE_INS_CADDN_A, + TRICORE_INS_CADDN, + TRICORE_INS_CADD_A, + TRICORE_INS_CADD, + TRICORE_INS_CALLA, + TRICORE_INS_CALLI, + TRICORE_INS_CALL, + TRICORE_INS_CLO_B, + TRICORE_INS_CLO_H, + TRICORE_INS_CLO, + TRICORE_INS_CLS_B, + TRICORE_INS_CLS_H, + TRICORE_INS_CLS, + TRICORE_INS_CLZ_B, + TRICORE_INS_CLZ_H, + TRICORE_INS_CLZ, + TRICORE_INS_CMOVN, + TRICORE_INS_CMOV, + TRICORE_INS_CMPSWAP_W, + TRICORE_INS_CMP_F, + TRICORE_INS_CRC32B_W, + TRICORE_INS_CRC32L_W, + TRICORE_INS_CRC32_B, + TRICORE_INS_CRCN, + TRICORE_INS_CSUBN_A, + TRICORE_INS_CSUBN, + TRICORE_INS_CSUB_A, + TRICORE_INS_CSUB, + TRICORE_INS_DEBUG, + TRICORE_INS_DEXTR, + TRICORE_INS_DIFSC_A, + TRICORE_INS_DISABLE, + TRICORE_INS_DIV_F, + TRICORE_INS_DIV_U, + TRICORE_INS_DIV, + TRICORE_INS_DSYNC, + TRICORE_INS_DVADJ, + TRICORE_INS_DVINIT_BU, + TRICORE_INS_DVINIT_B, + TRICORE_INS_DVINIT_HU, + TRICORE_INS_DVINIT_H, + TRICORE_INS_DVINIT_U, + TRICORE_INS_DVINIT, + TRICORE_INS_DVSTEP_U, + TRICORE_INS_DVSTEP, + TRICORE_INS_ENABLE, + TRICORE_INS_EQANY_B, + TRICORE_INS_EQANY_H, + TRICORE_INS_EQZ_A, + TRICORE_INS_EQ_A, + TRICORE_INS_EQ_B, + TRICORE_INS_EQ_H, + TRICORE_INS_EQ_W, + TRICORE_INS_EQ, + TRICORE_INS_EXTR_U, + TRICORE_INS_EXTR, + TRICORE_INS_FCALLA, + TRICORE_INS_FCALLI, + TRICORE_INS_FCALL, + TRICORE_INS_FRET, + TRICORE_INS_FTOHP, + TRICORE_INS_FTOIZ, + TRICORE_INS_FTOI, + TRICORE_INS_FTOQ31Z, + TRICORE_INS_FTOQ31, + TRICORE_INS_FTOUZ, + TRICORE_INS_FTOU, + TRICORE_INS_GE_A, + TRICORE_INS_GE_U, + TRICORE_INS_GE, + TRICORE_INS_HPTOF, + TRICORE_INS_IMASK, + TRICORE_INS_INSERT, + TRICORE_INS_INSN_T, + TRICORE_INS_INS_T, + TRICORE_INS_ISYNC, + TRICORE_INS_ITOF, + TRICORE_INS_IXMAX_U, + TRICORE_INS_IXMAX, + TRICORE_INS_IXMIN_U, + TRICORE_INS_IXMIN, + TRICORE_INS_JA, + TRICORE_INS_JEQ_A, + TRICORE_INS_JEQ, + TRICORE_INS_JGEZ, + TRICORE_INS_JGE_U, + TRICORE_INS_JGE, + TRICORE_INS_JGTZ, + TRICORE_INS_JI, + TRICORE_INS_JLA, + TRICORE_INS_JLEZ, + TRICORE_INS_JLI, + TRICORE_INS_JLTZ, + TRICORE_INS_JLT_U, + TRICORE_INS_JLT, + TRICORE_INS_JL, + TRICORE_INS_JNED, + TRICORE_INS_JNEI, + TRICORE_INS_JNE_A, + TRICORE_INS_JNE, + TRICORE_INS_JNZ_A, + TRICORE_INS_JNZ_T, + TRICORE_INS_JNZ, + TRICORE_INS_JZ_A, + TRICORE_INS_JZ_T, + TRICORE_INS_JZ, + TRICORE_INS_J, + TRICORE_INS_LDLCX, + TRICORE_INS_LDMST, + TRICORE_INS_LDUCX, + TRICORE_INS_LD_A, + TRICORE_INS_LD_BU, + TRICORE_INS_LD_B, + TRICORE_INS_LD_DA, + TRICORE_INS_LD_D, + TRICORE_INS_LD_HU, + TRICORE_INS_LD_H, + TRICORE_INS_LD_Q, + TRICORE_INS_LD_W, + TRICORE_INS_LEA, + TRICORE_INS_LHA, + TRICORE_INS_LOOPU, + TRICORE_INS_LOOP, + TRICORE_INS_LT_A, + TRICORE_INS_LT_B, + TRICORE_INS_LT_BU, + TRICORE_INS_LT_H, + TRICORE_INS_LT_HU, + TRICORE_INS_LT_U, + TRICORE_INS_LT_W, + TRICORE_INS_LT_WU, + TRICORE_INS_LT, + TRICORE_INS_MADDMS_H, + TRICORE_INS_MADDMS_U, + TRICORE_INS_MADDMS, + TRICORE_INS_MADDM_H, + TRICORE_INS_MADDM_Q, + TRICORE_INS_MADDM_U, + TRICORE_INS_MADDM, + TRICORE_INS_MADDRS_H, + TRICORE_INS_MADDRS_Q, + TRICORE_INS_MADDR_H, + TRICORE_INS_MADDR_Q, + TRICORE_INS_MADDSUMS_H, + TRICORE_INS_MADDSUM_H, + TRICORE_INS_MADDSURS_H, + TRICORE_INS_MADDSUR_H, + TRICORE_INS_MADDSUS_H, + TRICORE_INS_MADDSU_H, + TRICORE_INS_MADDS_H, + TRICORE_INS_MADDS_Q, + TRICORE_INS_MADDS_U, + TRICORE_INS_MADDS, + TRICORE_INS_MADD_F, + TRICORE_INS_MADD_H, + TRICORE_INS_MADD_Q, + TRICORE_INS_MADD_U, + TRICORE_INS_MADD, + TRICORE_INS_MAX_B, + TRICORE_INS_MAX_BU, + TRICORE_INS_MAX_H, + TRICORE_INS_MAX_HU, + TRICORE_INS_MAX_U, + TRICORE_INS_MAX, + TRICORE_INS_MFCR, + TRICORE_INS_MIN_B, + TRICORE_INS_MIN_BU, + TRICORE_INS_MIN_H, + TRICORE_INS_MIN_HU, + TRICORE_INS_MIN_U, + TRICORE_INS_MIN, + TRICORE_INS_MOVH_A, + TRICORE_INS_MOVH, + TRICORE_INS_MOVZ_A, + TRICORE_INS_MOV_AA, + TRICORE_INS_MOV_A, + TRICORE_INS_MOV_D, + TRICORE_INS_MOV_U, + TRICORE_INS_MOV, + TRICORE_INS_MSUBADMS_H, + TRICORE_INS_MSUBADM_H, + TRICORE_INS_MSUBADRS_H, + TRICORE_INS_MSUBADR_H, + TRICORE_INS_MSUBADS_H, + TRICORE_INS_MSUBAD_H, + TRICORE_INS_MSUBMS_H, + TRICORE_INS_MSUBMS_U, + TRICORE_INS_MSUBMS, + TRICORE_INS_MSUBM_H, + TRICORE_INS_MSUBM_Q, + TRICORE_INS_MSUBM_U, + TRICORE_INS_MSUBM, + TRICORE_INS_MSUBRS_H, + TRICORE_INS_MSUBRS_Q, + TRICORE_INS_MSUBR_H, + TRICORE_INS_MSUBR_Q, + TRICORE_INS_MSUBS_H, + TRICORE_INS_MSUBS_Q, + TRICORE_INS_MSUBS_U, + TRICORE_INS_MSUBS, + TRICORE_INS_MSUB_F, + TRICORE_INS_MSUB_H, + TRICORE_INS_MSUB_Q, + TRICORE_INS_MSUB_U, + TRICORE_INS_MSUB, + TRICORE_INS_MTCR, + TRICORE_INS_MULMS_H, + TRICORE_INS_MULM_H, + TRICORE_INS_MULM_U, + TRICORE_INS_MULM, + TRICORE_INS_MULR_H, + TRICORE_INS_MULR_Q, + TRICORE_INS_MULS_U, + TRICORE_INS_MULS, + TRICORE_INS_MUL_F, + TRICORE_INS_MUL_H, + TRICORE_INS_MUL_Q, + TRICORE_INS_MUL_U, + TRICORE_INS_MUL, + TRICORE_INS_NAND_T, + TRICORE_INS_NAND, + TRICORE_INS_NEZ_A, + TRICORE_INS_NE_A, + TRICORE_INS_NE, + TRICORE_INS_NOP, + TRICORE_INS_NOR_T, + TRICORE_INS_NOR, + TRICORE_INS_NOT, + TRICORE_INS_ORN_T, + TRICORE_INS_ORN, + TRICORE_INS_OR_ANDN_T, + TRICORE_INS_OR_AND_T, + TRICORE_INS_OR_EQ, + TRICORE_INS_OR_GE_U, + TRICORE_INS_OR_GE, + TRICORE_INS_OR_LT_U, + TRICORE_INS_OR_LT, + TRICORE_INS_OR_NE, + TRICORE_INS_OR_NOR_T, + TRICORE_INS_OR_OR_T, + TRICORE_INS_OR_T, + TRICORE_INS_OR, + TRICORE_INS_PACK, + TRICORE_INS_PARITY, + TRICORE_INS_POPCNT_W, + TRICORE_INS_Q31TOF, + TRICORE_INS_QSEED_F, + TRICORE_INS_RESTORE, + TRICORE_INS_RET, + TRICORE_INS_RFE, + TRICORE_INS_RFM, + TRICORE_INS_RSLCX, + TRICORE_INS_RSTV, + TRICORE_INS_RSUBS_U, + TRICORE_INS_RSUBS, + TRICORE_INS_RSUB, + TRICORE_INS_SAT_BU, + TRICORE_INS_SAT_B, + TRICORE_INS_SAT_HU, + TRICORE_INS_SAT_H, + TRICORE_INS_SELN_A, + TRICORE_INS_SELN, + TRICORE_INS_SEL_A, + TRICORE_INS_SEL, + TRICORE_INS_SHAS, + TRICORE_INS_SHA_B, + TRICORE_INS_SHA_H, + TRICORE_INS_SHA, + TRICORE_INS_SHUFFLE, + TRICORE_INS_SH_ANDN_T, + TRICORE_INS_SH_AND_T, + TRICORE_INS_SH_B, + TRICORE_INS_SH_EQ, + TRICORE_INS_SH_GE_U, + TRICORE_INS_SH_GE, + TRICORE_INS_SH_H, + TRICORE_INS_SH_LT_U, + TRICORE_INS_SH_LT, + TRICORE_INS_SH_NAND_T, + TRICORE_INS_SH_NE, + TRICORE_INS_SH_NOR_T, + TRICORE_INS_SH_ORN_T, + TRICORE_INS_SH_OR_T, + TRICORE_INS_SH_XNOR_T, + TRICORE_INS_SH_XOR_T, + TRICORE_INS_SH, + TRICORE_INS_STLCX, + TRICORE_INS_STUCX, + TRICORE_INS_ST_A, + TRICORE_INS_ST_B, + TRICORE_INS_ST_DA, + TRICORE_INS_ST_D, + TRICORE_INS_ST_H, + TRICORE_INS_ST_Q, + TRICORE_INS_ST_T, + TRICORE_INS_ST_W, + TRICORE_INS_SUBC, + TRICORE_INS_SUBSC_A, + TRICORE_INS_SUBS_BU, + TRICORE_INS_SUBS_B, + TRICORE_INS_SUBS_HU, + TRICORE_INS_SUBS_H, + TRICORE_INS_SUBS_U, + TRICORE_INS_SUBS, + TRICORE_INS_SUBX, + TRICORE_INS_SUB_A, + TRICORE_INS_SUB_B, + TRICORE_INS_SUB_F, + TRICORE_INS_SUB_H, + TRICORE_INS_SUB, + TRICORE_INS_SVLCX, + TRICORE_INS_SWAPMSK_W, + TRICORE_INS_SWAP_A, + TRICORE_INS_SWAP_W, + TRICORE_INS_SYSCALL, + TRICORE_INS_TLBDEMAP, + TRICORE_INS_TLBFLUSH_A, + TRICORE_INS_TLBFLUSH_B, + TRICORE_INS_TLBMAP, + TRICORE_INS_TLBPROBE_A, + TRICORE_INS_TLBPROBE_I, + TRICORE_INS_TRAPSV, + TRICORE_INS_TRAPV, + TRICORE_INS_UNPACK, + TRICORE_INS_UPDFL, + TRICORE_INS_UTOF, + TRICORE_INS_WAIT, + TRICORE_INS_XNOR_T, + TRICORE_INS_XNOR, + TRICORE_INS_XOR_EQ, + TRICORE_INS_XOR_GE_U, + TRICORE_INS_XOR_GE, + TRICORE_INS_XOR_LT_U, + TRICORE_INS_XOR_LT, + TRICORE_INS_XOR_NE, + TRICORE_INS_XOR, + + // clang-format on + // generate content end + TRICORE_INS_ENDING, // <-- mark the end of the list of instructions +} tricore_insn; + +/// Group of TriCore instructions +typedef enum tricore_insn_group { + TRICORE_GRP_INVALID, ///< = CS_GRP_INVALID + /// Generic groups + TRICORE_GRP_CALL, ///< = CS_GRP_CALL + TRICORE_GRP_JUMP, ///< = CS_GRP_JUMP + TRICORE_GRP_ENDING, ///< mark the end of the list of groups +} tricore_insn_group; + +typedef enum tricore_feature_t { + TRICORE_FEATURE_INVALID = 0, + // generate content begin + // clang-format off + + TRICORE_FEATURE_HasV110 = 128, + TRICORE_FEATURE_HasV120_UP, + TRICORE_FEATURE_HasV130_UP, + TRICORE_FEATURE_HasV161, + TRICORE_FEATURE_HasV160_UP, + TRICORE_FEATURE_HasV131_UP, + TRICORE_FEATURE_HasV161_UP, + TRICORE_FEATURE_HasV162, + TRICORE_FEATURE_HasV162_UP, + + // clang-format on + // generate content end + TRICORE_FEATURE_ENDING, ///< mark the end of the list of features +} tricore_feature; + +#ifdef __cplusplus +} +#endif + +#endif + +#define MAX_IMPL_W_REGS 20 +#define MAX_IMPL_R_REGS 20 +#define MAX_NUM_GROUPS 8 + +/// NOTE: All information in cs_detail is only available when CS_OPT_DETAIL = CS_OPT_ON +/// Initialized as memset(., 0, offsetof(cs_detail, ARCH)+sizeof(cs_ARCH)) +/// by ARCH_getInstruction in arch/ARCH/ARCHDisassembler.c +/// if cs_detail changes, in particular if a field is added after the union, +/// then update arch/ARCH/ARCHDisassembler.c accordingly +typedef struct cs_detail { + uint16_t regs_read + [MAX_IMPL_R_REGS]; ///< list of implicit registers read by this insn + uint8_t regs_read_count; ///< number of implicit registers read by this insn + + uint16_t regs_write + [MAX_IMPL_W_REGS]; ///< list of implicit registers modified by this insn + uint8_t regs_write_count; ///< number of implicit registers modified by this insn + + uint8_t groups[MAX_NUM_GROUPS]; ///< list of group this instruction belong to + uint8_t groups_count; ///< number of groups this insn belongs to + + bool writeback; ///< Instruction has writeback operands. + + /// Architecture-specific instruction info + union { + cs_x86 x86; ///< X86 architecture, including 16-bit, 32-bit & 64-bit mode + cs_arm64 arm64; ///< ARM64 architecture (aka AArch64) + cs_arm arm; ///< ARM architecture (including Thumb/Thumb2) + cs_m68k m68k; ///< M68K architecture + cs_mips mips; ///< MIPS architecture + cs_ppc ppc; ///< PowerPC architecture + cs_sparc sparc; ///< Sparc architecture + cs_sysz sysz; ///< SystemZ architecture + cs_xcore xcore; ///< XCore architecture + cs_tms320c64x tms320c64x; ///< TMS320C64x architecture + cs_m680x m680x; ///< M680X architecture + cs_evm evm; ///< Ethereum architecture + cs_mos65xx mos65xx; ///< MOS65XX architecture (including MOS6502) + cs_wasm wasm; ///< Web Assembly architecture + cs_bpf bpf; ///< Berkeley Packet Filter architecture (including eBPF) + cs_riscv riscv; ///< RISCV architecture + cs_sh sh; ///< SH architecture + cs_tricore tricore; ///< TriCore architecture + }; +} cs_detail; + +/// Detail information of disassembled instruction +typedef struct cs_insn { + /// Instruction ID (basically a numeric ID for the instruction mnemonic) + /// Find the instruction id in the '[ARCH]_insn' enum in the header file + /// of corresponding architecture, such as 'arm_insn' in arm.h for ARM, + /// 'x86_insn' in x86.h for X86, etc... + /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF + /// NOTE: in Skipdata mode, "data" instruction has 0 for this id field. + unsigned int id; + + /// Address (EIP) of this instruction + /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF + uint64_t address; + + /// Size of this instruction + /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF + uint16_t size; + + /// Machine bytes of this instruction, with number of bytes indicated by @size above + /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF + uint8_t bytes[24]; + + /// Ascii text of instruction mnemonic + /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF + char mnemonic[CS_MNEMONIC_SIZE]; + + /// Ascii text of instruction operands + /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF + char op_str[160]; + + /// Pointer to cs_detail. + /// NOTE: detail pointer is only valid when both requirements below are met: + /// (1) CS_OP_DETAIL = CS_OPT_ON + /// (2) Engine is not in Skipdata mode (CS_OP_SKIPDATA option set to CS_OPT_ON) + /// + /// NOTE 2: when in Skipdata mode, or when detail mode is OFF, even if this pointer + /// is not NULL, its content is still irrelevant. + cs_detail *detail; +} cs_insn; + + +/// Calculate the offset of a disassembled instruction in its buffer, given its position +/// in its array of disassembled insn +/// NOTE: this macro works with position (>=1), not index +#define CS_INSN_OFFSET(insns, post) (insns[post - 1].address - insns[0].address) + + +/// All type of errors encountered by Capstone API. +/// These are values returned by cs_errno() +typedef enum cs_err { + CS_ERR_OK = 0, ///< No error: everything was fine + CS_ERR_MEM, ///< Out-Of-Memory error: cs_open(), cs_disasm(), cs_disasm_iter() + CS_ERR_ARCH, ///< Unsupported architecture: cs_open() + CS_ERR_HANDLE, ///< Invalid handle: cs_op_count(), cs_op_index() + CS_ERR_CSH, ///< Invalid csh argument: cs_close(), cs_errno(), cs_option() + CS_ERR_MODE, ///< Invalid/unsupported mode: cs_open() + CS_ERR_OPTION, ///< Invalid/unsupported option: cs_option() + CS_ERR_DETAIL, ///< Information is unavailable because detail option is OFF + CS_ERR_MEMSETUP, ///< Dynamic memory management uninitialized (see CS_OPT_MEM) + CS_ERR_VERSION, ///< Unsupported version (bindings) + CS_ERR_DIET, ///< Access irrelevant data in "diet" engine + CS_ERR_SKIPDATA, ///< Access irrelevant data for "data" instruction in SKIPDATA mode + CS_ERR_X86_ATT, ///< X86 AT&T syntax is unsupported (opt-out at compile time) + CS_ERR_X86_INTEL, ///< X86 Intel syntax is unsupported (opt-out at compile time) + CS_ERR_X86_MASM, ///< X86 Masm syntax is unsupported (opt-out at compile time) +} cs_err; + +/** + Return combined API version & major and minor version numbers. + + @major: major number of API version + @minor: minor number of API version + + @return hexical number as (major << 8 | minor), which encodes both + major & minor versions. + NOTE: This returned value can be compared with version number made + with macro CS_MAKE_VERSION + + For example, second API version would return 1 in @major, and 1 in @minor + The return value would be 0x0101 + + NOTE: if you only care about returned value, but not major and minor values, + set both @major & @minor arguments to NULL. +*/ +CAPSTONE_EXPORT +unsigned int CAPSTONE_API cs_version(int *major, int *minor); + +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_arm(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_arm64(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_mips(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_x86(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_ppc(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_sparc(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_sysz(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_xcore(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_m68k(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_tms320c64x(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_m680x(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_evm(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_mos65xx(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_wasm(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_bpf(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_riscv(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_sh(void); +CAPSTONE_EXPORT +void CAPSTONE_API cs_arch_register_tricore(void); + +/** + This API can be used to either ask for archs supported by this library, + or check to see if the library was compile with 'diet' option (or called + in 'diet' mode). + + To check if a particular arch is supported by this library, set @query to + arch mode (CS_ARCH_* value). + To verify if this library supports all the archs, use CS_ARCH_ALL. + + To check if this library is in 'diet' mode, set @query to CS_SUPPORT_DIET. + + @return True if this library supports the given arch, or in 'diet' mode. +*/ +CAPSTONE_EXPORT +bool CAPSTONE_API cs_support(int query); + +/** + Initialize CS handle: this must be done before any usage of CS. + + @arch: architecture type (CS_ARCH_*) + @mode: hardware mode. This is combined of CS_MODE_* + @handle: pointer to handle, which will be updated at return time + + @return CS_ERR_OK on success, or other value on failure (refer to cs_err enum + for detailed error). +*/ +CAPSTONE_EXPORT +cs_err CAPSTONE_API cs_open(cs_arch arch, cs_mode mode, csh *handle); + +/** + Close CS handle: MUST do to release the handle when it is not used anymore. + NOTE: this must be only called when there is no longer usage of Capstone, + not even access to cs_insn array. The reason is the this API releases some + cached memory, thus access to any Capstone API after cs_close() might crash + your application. + + In fact,this API invalidate @handle by ZERO out its value (i.e *handle = 0). + + @handle: pointer to a handle returned by cs_open() + + @return CS_ERR_OK on success, or other value on failure (refer to cs_err enum + for detailed error). +*/ +CAPSTONE_EXPORT +cs_err CAPSTONE_API cs_close(csh *handle); + +/** + Set option for disassembling engine at runtime + + @handle: handle returned by cs_open() + @type: type of option to be set + @value: option value corresponding with @type + + @return: CS_ERR_OK on success, or other value on failure. + Refer to cs_err enum for detailed error. + + NOTE: in the case of CS_OPT_MEM, handle's value can be anything, + so that cs_option(handle, CS_OPT_MEM, value) can (i.e must) be called + even before cs_open() +*/ +CAPSTONE_EXPORT +cs_err CAPSTONE_API cs_option(csh handle, cs_opt_type type, size_t value); + +/** + Report the last error number when some API function fail. + Like glibc's errno, cs_errno might not retain its old value once accessed. + + @handle: handle returned by cs_open() + + @return: error code of cs_err enum type (CS_ERR_*, see above) +*/ +CAPSTONE_EXPORT +cs_err CAPSTONE_API cs_errno(csh handle); + + +/** + Return a string describing given error code. + + @code: error code (see CS_ERR_* above) + + @return: returns a pointer to a string that describes the error code + passed in the argument @code +*/ +CAPSTONE_EXPORT +const char * CAPSTONE_API cs_strerror(cs_err code); + +/** + Disassemble binary code, given the code buffer, size, address and number + of instructions to be decoded. + This API dynamically allocate memory to contain disassembled instruction. + Resulting instructions will be put into @*insn + + NOTE 1: this API will automatically determine memory needed to contain + output disassembled instructions in @insn. + + NOTE 2: caller must free the allocated memory itself to avoid memory leaking. + + NOTE 3: for system with scarce memory to be dynamically allocated such as + OS kernel or firmware, the API cs_disasm_iter() might be a better choice than + cs_disasm(). The reason is that with cs_disasm(), based on limited available + memory, we have to calculate in advance how many instructions to be disassembled, + which complicates things. This is especially troublesome for the case @count=0, + when cs_disasm() runs uncontrollably (until either end of input buffer, or + when it encounters an invalid instruction). + + @handle: handle returned by cs_open() + @code: buffer containing raw binary code to be disassembled. + @code_size: size of the above code buffer. + @address: address of the first instruction in given raw code buffer. + @insn: array of instructions filled in by this API. + NOTE: @insn will be allocated by this function, and should be freed + with cs_free() API. + @count: number of instructions to be disassembled, or 0 to get all of them + + @return: the number of successfully disassembled instructions, + or 0 if this function failed to disassemble the given code + + On failure, call cs_errno() for error code. +*/ +CAPSTONE_EXPORT +size_t CAPSTONE_API cs_disasm(csh handle, + const uint8_t *code, size_t code_size, + uint64_t address, + size_t count, + cs_insn **insn); + +/** + Free memory allocated by cs_malloc() or cs_disasm() (argument @insn) + + @insn: pointer returned by @insn argument in cs_disasm() or cs_malloc() + @count: number of cs_insn structures returned by cs_disasm(), or 1 + to free memory allocated by cs_malloc(). +*/ +CAPSTONE_EXPORT +void CAPSTONE_API cs_free(cs_insn *insn, size_t count); + + +/** + Allocate memory for 1 instruction to be used by cs_disasm_iter(). + + @handle: handle returned by cs_open() + + NOTE: when no longer in use, you can reclaim the memory allocated for + this instruction with cs_free(insn, 1) +*/ +CAPSTONE_EXPORT +cs_insn * CAPSTONE_API cs_malloc(csh handle); + +/** + Fast API to disassemble binary code, given the code buffer, size, address + and number of instructions to be decoded. + This API puts the resulting instruction into a given cache in @insn. + See tests/test_iter.c for sample code demonstrating this API. + + NOTE 1: this API will update @code, @size & @address to point to the next + instruction in the input buffer. Therefore, it is convenient to use + cs_disasm_iter() inside a loop to quickly iterate all the instructions. + While decoding one instruction at a time can also be achieved with + cs_disasm(count=1), some benchmarks shown that cs_disasm_iter() can be 30% + faster on random input. + + NOTE 2: the cache in @insn can be created with cs_malloc() API. + + NOTE 3: for system with scarce memory to be dynamically allocated such as + OS kernel or firmware, this API is recommended over cs_disasm(), which + allocates memory based on the number of instructions to be disassembled. + The reason is that with cs_disasm(), based on limited available memory, + we have to calculate in advance how many instructions to be disassembled, + which complicates things. This is especially troublesome for the case + @count=0, when cs_disasm() runs uncontrollably (until either end of input + buffer, or when it encounters an invalid instruction). + + @handle: handle returned by cs_open() + @code: buffer containing raw binary code to be disassembled + @size: size of above code + @address: address of the first insn in given raw code buffer + @insn: pointer to instruction to be filled in by this API. + + @return: true if this API successfully decode 1 instruction, + or false otherwise. + + On failure, call cs_errno() for error code. +*/ +CAPSTONE_EXPORT +bool CAPSTONE_API cs_disasm_iter(csh handle, + const uint8_t **code, size_t *size, + uint64_t *address, cs_insn *insn); + +/** + Return friendly name of register in a string. + Find the instruction id from header file of corresponding architecture (arm.h for ARM, + x86.h for X86, ...) + + WARN: when in 'diet' mode, this API is irrelevant because engine does not + store register name. + + @handle: handle returned by cs_open() + @reg_id: register id + + @return: string name of the register, or NULL if @reg_id is invalid. +*/ +CAPSTONE_EXPORT +const char * CAPSTONE_API cs_reg_name(csh handle, unsigned int reg_id); + +/** + Return friendly name of an instruction in a string. + Find the instruction id from header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) + + WARN: when in 'diet' mode, this API is irrelevant because the engine does not + store instruction name. + + @handle: handle returned by cs_open() + @insn_id: instruction id + + @return: string name of the instruction, or NULL if @insn_id is invalid. +*/ +CAPSTONE_EXPORT +const char * CAPSTONE_API cs_insn_name(csh handle, unsigned int insn_id); + +/** + Return friendly name of a group id (that an instruction can belong to) + Find the group id from header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) + + WARN: when in 'diet' mode, this API is irrelevant because the engine does not + store group name. + + @handle: handle returned by cs_open() + @group_id: group id + + @return: string name of the group, or NULL if @group_id is invalid. +*/ +CAPSTONE_EXPORT +const char * CAPSTONE_API cs_group_name(csh handle, unsigned int group_id); + +/** + Check if a disassembled instruction belong to a particular group. + Find the group id from header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) + Internally, this simply verifies if @group_id matches any member of insn->groups array. + + NOTE: this API is only valid when detail option is ON (which is OFF by default). + + WARN: when in 'diet' mode, this API is irrelevant because the engine does not + update @groups array. + + @handle: handle returned by cs_open() + @insn: disassembled instruction structure received from cs_disasm() or cs_disasm_iter() + @group_id: group that you want to check if this instruction belong to. + + @return: true if this instruction indeed belongs to the given group, or false otherwise. +*/ +CAPSTONE_EXPORT +bool CAPSTONE_API cs_insn_group(csh handle, const cs_insn *insn, unsigned int group_id); + +/** + Check if a disassembled instruction IMPLICITLY used a particular register. + Find the register id from header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) + Internally, this simply verifies if @reg_id matches any member of insn->regs_read array. + + NOTE: this API is only valid when detail option is ON (which is OFF by default) + + WARN: when in 'diet' mode, this API is irrelevant because the engine does not + update @regs_read array. + + @insn: disassembled instruction structure received from cs_disasm() or cs_disasm_iter() + @reg_id: register that you want to check if this instruction used it. + + @return: true if this instruction indeed implicitly used the given register, or false otherwise. +*/ +CAPSTONE_EXPORT +bool CAPSTONE_API cs_reg_read(csh handle, const cs_insn *insn, unsigned int reg_id); + +/** + Check if a disassembled instruction IMPLICITLY modified a particular register. + Find the register id from header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) + Internally, this simply verifies if @reg_id matches any member of insn->regs_write array. + + NOTE: this API is only valid when detail option is ON (which is OFF by default) + + WARN: when in 'diet' mode, this API is irrelevant because the engine does not + update @regs_write array. + + @insn: disassembled instruction structure received from cs_disasm() or cs_disasm_iter() + @reg_id: register that you want to check if this instruction modified it. + + @return: true if this instruction indeed implicitly modified the given register, or false otherwise. +*/ +CAPSTONE_EXPORT +bool CAPSTONE_API cs_reg_write(csh handle, const cs_insn *insn, unsigned int reg_id); + +/** + Count the number of operands of a given type. + Find the operand type in header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) + + NOTE: this API is only valid when detail option is ON (which is OFF by default) + + @handle: handle returned by cs_open() + @insn: disassembled instruction structure received from cs_disasm() or cs_disasm_iter() + @op_type: Operand type to be found. + + @return: number of operands of given type @op_type in instruction @insn, + or -1 on failure. +*/ +CAPSTONE_EXPORT +int CAPSTONE_API cs_op_count(csh handle, const cs_insn *insn, unsigned int op_type); + +/** + Retrieve the position of operand of given type in .operands[] array. + Later, the operand can be accessed using the returned position. + Find the operand type in header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) + + NOTE: this API is only valid when detail option is ON (which is OFF by default) + + @handle: handle returned by cs_open() + @insn: disassembled instruction structure received from cs_disasm() or cs_disasm_iter() + @op_type: Operand type to be found. + @position: position of the operand to be found. This must be in the range + [1, cs_op_count(handle, insn, op_type)] + + @return: index of operand of given type @op_type in .operands[] array + in instruction @insn, or -1 on failure. +*/ +CAPSTONE_EXPORT +int CAPSTONE_API cs_op_index(csh handle, const cs_insn *insn, unsigned int op_type, + unsigned int position); + +/// Type of array to keep the list of registers +typedef uint16_t cs_regs[64]; + +/** + Retrieve all the registers accessed by an instruction, either explicitly or + implicitly. + + WARN: when in 'diet' mode, this API is irrelevant because engine does not + store registers. + + @handle: handle returned by cs_open() + @insn: disassembled instruction structure returned from cs_disasm() or cs_disasm_iter() + @regs_read: on return, this array contains all registers read by instruction. + @regs_read_count: number of registers kept inside @regs_read array. + @regs_write: on return, this array contains all registers written by instruction. + @regs_write_count: number of registers kept inside @regs_write array. + + @return CS_ERR_OK on success, or other value on failure (refer to cs_err enum + for detailed error). +*/ +CAPSTONE_EXPORT +cs_err CAPSTONE_API cs_regs_access(csh handle, const cs_insn *insn, + cs_regs regs_read, uint8_t *regs_read_count, + cs_regs regs_write, uint8_t *regs_write_count); + +#ifdef __cplusplus +} +#endif + +#endif + +/* This file is generated by glib-mkenums, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ + +#ifndef __GUM_ENUM_TYPES_H__ +#define __GUM_ENUM_TYPES_H__ + + +G_BEGIN_DECLS + +/* Enumerations from "gumdarwingrafter.h" */ +GType gum_darwin_grafter_flags_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_DARWIN_GRAFTER_FLAGS (gum_darwin_grafter_flags_get_type ()) + +/* Enumerations from "gumdarwinmodule.h" */ +GType gum_darwin_module_flags_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_DARWIN_MODULE_FLAGS (gum_darwin_module_flags_get_type ()) + +/* Enumerations from "gumdefs.h" */ +GType gum_error_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ERROR (gum_error_get_type ()) +GType gum_cpu_type_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_CPU_TYPE (gum_cpu_type_get_type ()) +GType gum_memory_access_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_MEMORY_ACCESS (gum_memory_access_get_type ()) +GType gum_relocation_policy_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_RELOCATION_POLICY (gum_relocation_policy_get_type ()) + +/* Enumerations from "gumelfmodule.h" */ +GType gum_elf_type_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_TYPE (gum_elf_type_get_type ()) +GType gum_elf_osabi_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_OSABI (gum_elf_osabi_get_type ()) +GType gum_elf_machine_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_MACHINE (gum_elf_machine_get_type ()) +GType gum_elf_source_mode_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_SOURCE_MODE (gum_elf_source_mode_get_type ()) +GType gum_elf_section_type_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_SECTION_TYPE (gum_elf_section_type_get_type ()) +GType gum_elf_section_flags_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_SECTION_FLAGS (gum_elf_section_flags_get_type ()) +GType gum_elf_dynamic_tag_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_DYNAMIC_TAG (gum_elf_dynamic_tag_get_type ()) +GType gum_elf_shdr_index_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_SHDR_INDEX (gum_elf_shdr_index_get_type ()) +GType gum_elf_symbol_type_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_SYMBOL_TYPE (gum_elf_symbol_type_get_type ()) +GType gum_elf_symbol_bind_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_SYMBOL_BIND (gum_elf_symbol_bind_get_type ()) +GType gum_elf_ia32_relocation_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_IA32_RELOCATION (gum_elf_ia32_relocation_get_type ()) +GType gum_elf_x64_relocation_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_X64_RELOCATION (gum_elf_x64_relocation_get_type ()) +GType gum_elf_arm_relocation_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_ARM_RELOCATION (gum_elf_arm_relocation_get_type ()) +GType gum_elf_arm64_relocation_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_ARM64_RELOCATION (gum_elf_arm64_relocation_get_type ()) +GType gum_elf_mips_relocation_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ELF_MIPS_RELOCATION (gum_elf_mips_relocation_get_type ()) + +/* Enumerations from "gumexceptor.h" */ +GType gum_exceptor_mode_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_EXCEPTOR_MODE (gum_exceptor_mode_get_type ()) + +/* Enumerations from "guminterceptor.h" */ +GType gum_interceptor_scenario_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_INTERCEPTOR_SCENARIO (gum_interceptor_scenario_get_type ()) +GType gum_invocation_ignorability_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_INVOCATION_IGNORABILITY (gum_invocation_ignorability_get_type ()) +GType gum_redirect_write_result_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_REDIRECT_WRITE_RESULT (gum_redirect_write_result_get_type ()) +GType gum_attach_return_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_ATTACH_RETURN (gum_attach_return_get_type ()) +GType gum_replace_return_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_REPLACE_RETURN (gum_replace_return_get_type ()) + +/* Enumerations from "gummodule.h" */ +GType gum_import_type_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_IMPORT_TYPE (gum_import_type_get_type ()) +GType gum_export_type_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_EXPORT_TYPE (gum_export_type_get_type ()) +GType gum_symbol_type_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_SYMBOL_TYPE (gum_symbol_type_get_type ()) +GType gum_dependency_type_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_DEPENDENCY_TYPE (gum_dependency_type_get_type ()) + +/* Enumerations from "gumprocess.h" */ +GType gum_teardown_requirement_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_TEARDOWN_REQUIREMENT (gum_teardown_requirement_get_type ()) +GType gum_code_signing_policy_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_CODE_SIGNING_POLICY (gum_code_signing_policy_get_type ()) +GType gum_modify_thread_flags_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_MODIFY_THREAD_FLAGS (gum_modify_thread_flags_get_type ()) +GType gum_thread_flags_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_THREAD_FLAGS (gum_thread_flags_get_type ()) +GType gum_thread_state_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_THREAD_STATE (gum_thread_state_get_type ()) +GType gum_watch_conditions_get_type (void) G_GNUC_CONST; +#define GUM_TYPE_WATCH_CONDITIONS (gum_watch_conditions_get_type ()) +G_END_DECLS + +#endif /* __GUM_ENUM_TYPES_H__ */ + +/* Generated data ends here */ + + +#if CS_API_MAJOR >= 6 +# define CS_ARCH_ARM64 CS_ARCH_AARCH64 +#endif + +#if !defined (GUM_STATIC) && defined (G_OS_WIN32) +# ifdef GUM_EXPORTS +# define GUM_API __declspec(dllexport) +# else +# define GUM_API __declspec(dllimport) +# endif +#else +# define GUM_API +#endif + +G_BEGIN_DECLS + +#define GUM_ERROR gum_error_quark () + +typedef enum { + GUM_ERROR_FAILED, + GUM_ERROR_NOT_FOUND, + GUM_ERROR_EXISTS, + GUM_ERROR_PERMISSION_DENIED, + GUM_ERROR_INVALID_ARGUMENT, + GUM_ERROR_NOT_SUPPORTED, + GUM_ERROR_INVALID_DATA, +} GumError; + +typedef guint64 GumAddress; +#define GUM_ADDRESS(a) ((GumAddress) (guintptr) (a)) +#define GUM_TYPE_ADDRESS (gum_address_get_type ()) +typedef guint GumOS; +typedef guint GumCallingConvention; +typedef guint GumAbiType; +typedef guint GumCpuFeatures; +typedef guint GumInstructionEncoding; +typedef guint GumArgType; +typedef struct _GumArgument GumArgument; +typedef guint GumBranchHint; +typedef struct _GumIA32CpuContext GumIA32CpuContext; +typedef struct _GumX64CpuContext GumX64CpuContext; +typedef union _GumX86VectorReg GumX86VectorReg; +typedef struct _GumArmCpuContext GumArmCpuContext; +typedef union _GumArmVectorReg GumArmVectorReg; +typedef struct _GumArm64CpuContext GumArm64CpuContext; +typedef union _GumArm64VectorReg GumArm64VectorReg; +typedef struct _GumMipsCpuContext GumMipsCpuContext; +typedef guint GumRelocationScenario; + +#if defined (_M_IX86) || defined (__i386__) +# define GUM_NATIVE_CPU GUM_CPU_IA32 +# define GUM_DEFAULT_CS_ARCH CS_ARCH_X86 +# define gum_cs_arch_register_native cs_arch_register_x86 +/** + * GUM_DEFAULT_CS_MODE: (skip) + */ +# define GUM_DEFAULT_CS_MODE CS_MODE_32 +# define GUM_CPU_CONTEXT_HAS_OUT_OF_LINE_VECTORS 1 +# define GUM_X86_XMM_REG_COUNT 8 +# define GUM_FXSAVE_OFFSET_XMM 160 +typedef GumIA32CpuContext GumCpuContext; +#elif defined (_M_X64) || defined (__x86_64__) +# define GUM_NATIVE_CPU GUM_CPU_AMD64 +# define GUM_DEFAULT_CS_ARCH CS_ARCH_X86 +# define gum_cs_arch_register_native cs_arch_register_x86 +/** + * GUM_DEFAULT_CS_MODE: (skip) + */ +# define GUM_DEFAULT_CS_MODE CS_MODE_64 +# define GUM_CPU_CONTEXT_HAS_OUT_OF_LINE_VECTORS 1 +# define GUM_X86_XMM_REG_COUNT 16 +# define GUM_FXSAVE_OFFSET_XMM 160 +typedef GumX64CpuContext GumCpuContext; +#elif defined (_M_ARM) || defined (__arm__) +# define GUM_NATIVE_CPU GUM_CPU_ARM +# define GUM_DEFAULT_CS_ARCH CS_ARCH_ARM +# define gum_cs_arch_register_native cs_arch_register_arm +/** + * GUM_DEFAULT_CS_MODE: (skip) + */ +# define GUM_DEFAULT_CS_MODE \ + ((cs_mode) (CS_MODE_ARM | CS_MODE_V8 | GUM_DEFAULT_CS_ENDIAN)) +# define GUM_PSR_T_BIT 0x20 +typedef GumArmCpuContext GumCpuContext; +#elif defined (_M_ARM64) || defined (__aarch64__) +# define GUM_NATIVE_CPU GUM_CPU_ARM64 +# define GUM_DEFAULT_CS_ARCH CS_ARCH_ARM64 +# define gum_cs_arch_register_native cs_arch_register_arm64 +/** + * GUM_DEFAULT_CS_MODE: (skip) + */ +# define GUM_DEFAULT_CS_MODE GUM_DEFAULT_CS_ENDIAN +typedef GumArm64CpuContext GumCpuContext; +#elif defined (__mips__) +# define GUM_NATIVE_CPU GUM_CPU_MIPS +# define GUM_DEFAULT_CS_ARCH CS_ARCH_MIPS +# define gum_cs_arch_register_native cs_arch_register_mips +# if GLIB_SIZEOF_VOID_P == 4 +/** + * GUM_DEFAULT_CS_MODE: (skip) + */ +# define GUM_DEFAULT_CS_MODE ((cs_mode) \ + (CS_MODE_MIPS32 | GUM_DEFAULT_CS_ENDIAN)) +# else +/** + * GUM_DEFAULT_CS_MODE: (skip) + */ +# define GUM_DEFAULT_CS_MODE ((cs_mode) \ + (CS_MODE_MIPS64 | GUM_DEFAULT_CS_ENDIAN)) +# endif +typedef GumMipsCpuContext GumCpuContext; +#else +# error Unsupported architecture. +#endif +/* + * The only non-legacy big-endian configuration on 32-bit ARM systems is BE8. + * In this configuration, whilst the data is in big-endian, the code stream is + * still in little-endian. Since Capstone is disassembling the code stream, it + * should work in little-endian even on BE8 systems. On big-endian 64-bit ARM + * systems, the code stream is likewise in little-endian. + */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN || \ + defined (__arm__) || \ + defined (_M_ARM64) || \ + defined (__aarch64__) +# define GUM_DEFAULT_CS_ENDIAN CS_MODE_LITTLE_ENDIAN +#else +# define GUM_DEFAULT_CS_ENDIAN CS_MODE_BIG_ENDIAN +#endif +#ifdef G_OS_WIN32 +# define GUM_NATIVE_ABI GUM_ABI_WINDOWS +# define GUM_NATIVE_ABI_IS_WINDOWS 1 +# define GUM_NATIVE_ABI_IS_UNIX 0 +#else +# define GUM_NATIVE_ABI GUM_ABI_UNIX +# define GUM_NATIVE_ABI_IS_WINDOWS 0 +# define GUM_NATIVE_ABI_IS_UNIX 1 +#endif + +enum _GumOS +{ + GUM_OS_NONE, + GUM_OS_WINDOWS, + GUM_OS_MACOS, + GUM_OS_LINUX, + GUM_OS_IOS, + GUM_OS_WATCHOS, + GUM_OS_TVOS, + GUM_OS_XROS, + GUM_OS_ANDROID, + GUM_OS_FREEBSD, + GUM_OS_QNX +}; + +enum _GumCallingConvention +{ + GUM_CALL_CAPI, + GUM_CALL_SYSAPI +}; + +enum _GumAbiType +{ + GUM_ABI_UNIX, + GUM_ABI_WINDOWS +}; + +typedef enum { + GUM_CPU_INVALID, + GUM_CPU_IA32, + GUM_CPU_AMD64, + GUM_CPU_ARM, + GUM_CPU_ARM64, + GUM_CPU_MIPS +} GumCpuType; + +enum _GumCpuFeatures +{ + GUM_CPU_AVX2 = 1 << 0, + GUM_CPU_AVX512 = 1 << 1, + GUM_CPU_CET_SS = 1 << 2, + GUM_CPU_THUMB_INTERWORK = 1 << 3, + GUM_CPU_VFP2 = 1 << 4, + GUM_CPU_VFP3 = 1 << 5, + GUM_CPU_VFPD32 = 1 << 6, + GUM_CPU_PTRAUTH = 1 << 7, +}; + +typedef enum { + GUM_MEMORY_ACCESS_OPEN, + GUM_MEMORY_ACCESS_EXCLUSIVE, +} GumMemoryAccess; + +enum _GumInstructionEncoding +{ + GUM_INSTRUCTION_DEFAULT, + GUM_INSTRUCTION_SPECIAL +}; + +enum _GumArgType +{ + GUM_ARG_ADDRESS, + GUM_ARG_REGISTER +}; + +struct _GumArgument +{ + GumArgType type; + + union + { + GumAddress address; + gint reg; + } value; +}; + +enum _GumBranchHint +{ + GUM_NO_HINT, + GUM_LIKELY, + GUM_UNLIKELY +}; + +union _GumX86VectorReg +{ + guint8 q[16]; + gdouble d[2]; + gfloat s[4]; +}; + +struct _GumIA32CpuContext +{ + guint32 eip; + + guint32 edi; + guint32 esi; + guint32 ebp; + guint32 esp; + guint32 ebx; + guint32 edx; + guint32 ecx; + guint32 eax; + + GumX86VectorReg * xmm; +}; + +struct _GumX64CpuContext +{ + guint64 rip; + + guint64 r15; + guint64 r14; + guint64 r13; + guint64 r12; + guint64 r11; + guint64 r10; + guint64 r9; + guint64 r8; + + guint64 rdi; + guint64 rsi; + guint64 rbp; + guint64 rsp; + guint64 rbx; + guint64 rdx; + guint64 rcx; + guint64 rax; + + GumX86VectorReg * xmm; +}; + +union _GumArmVectorReg +{ + guint8 q[16]; + gdouble d[2]; + gfloat s[4]; +}; + +struct _GumArmCpuContext +{ + guint32 pc; + guint32 sp; + guint32 cpsr; + + guint32 r8; + guint32 r9; + guint32 r10; + guint32 r11; + guint32 r12; + + GumArmVectorReg v[16]; + + guint32 _padding; + + guint32 r[8]; + guint32 lr; +}; + +union _GumArm64VectorReg +{ + guint8 q[16]; + gdouble d; + gfloat s; + guint16 h; + guint8 b; +}; + +struct _GumArm64CpuContext +{ + guint64 pc; + guint64 sp; + guint64 nzcv; + + guint64 x[29]; + guint64 fp; + guint64 lr; + +#ifndef G_OS_NONE + /* Bare-metal threads can be entered with a near-exhausted kernel stack, so + * there the trampolines stay integer-only and skip the vector registers. */ + GumArm64VectorReg v[32]; +#endif +}; + +struct _GumMipsCpuContext +{ + /* + * This structure represents the register state pushed onto the stack by the + * trampoline which allows us to vector from the original minimal assembly + * hook to architecture agnostic C code inside frida-gum. These registers are + * natively sized. Even if some have not been expanded to 64-bits from the + * MIPS32 architecture MIPS can only perform aligned data access and as such + * pushing zero extended values is simpler than attempting to push minimally + * sized data types. + */ + gsize pc; + + gsize gp; + gsize sp; + gsize fp; + gsize ra; + + gsize hi; + gsize lo; + + gsize at; + + gsize v0; + gsize v1; + + gsize a0; + gsize a1; + gsize a2; + gsize a3; + + gsize t0; + gsize t1; + gsize t2; + gsize t3; + gsize t4; + gsize t5; + gsize t6; + gsize t7; + gsize t8; + gsize t9; + + gsize s0; + gsize s1; + gsize s2; + gsize s3; + gsize s4; + gsize s5; + gsize s6; + gsize s7; + + gsize k0; + gsize k1; +}; + +enum _GumRelocationScenario +{ + GUM_SCENARIO_OFFLINE, + GUM_SCENARIO_ONLINE +}; + +typedef enum { + GUM_RELOCATION_DEFAULT, + GUM_RELOCATION_CHECKED, + GUM_RELOCATION_UNCHECKED, + GUM_RELOCATION_FORCED, +} GumRelocationPolicy; + +#ifndef __arm__ +# if GLIB_SIZEOF_VOID_P == 8 +# define GUM_CPU_CONTEXT_XAX(c) ((c)->rax) +# define GUM_CPU_CONTEXT_XCX(c) ((c)->rcx) +# define GUM_CPU_CONTEXT_XDX(c) ((c)->rdx) +# define GUM_CPU_CONTEXT_XBX(c) ((c)->rbx) +# define GUM_CPU_CONTEXT_XSP(c) ((c)->rsp) +# define GUM_CPU_CONTEXT_XBP(c) ((c)->rbp) +# define GUM_CPU_CONTEXT_XSI(c) ((c)->rsi) +# define GUM_CPU_CONTEXT_XDI(c) ((c)->rdi) +# define GUM_CPU_CONTEXT_XIP(c) ((c)->rip) +# define GUM_CPU_CONTEXT_OFFSET_XAX (G_STRUCT_OFFSET (GumCpuContext, rax)) +# define GUM_CPU_CONTEXT_OFFSET_XCX (G_STRUCT_OFFSET (GumCpuContext, rcx)) +# define GUM_CPU_CONTEXT_OFFSET_XDX (G_STRUCT_OFFSET (GumCpuContext, rdx)) +# define GUM_CPU_CONTEXT_OFFSET_XBX (G_STRUCT_OFFSET (GumCpuContext, rbx)) +# define GUM_CPU_CONTEXT_OFFSET_XSP (G_STRUCT_OFFSET (GumCpuContext, rsp)) +# define GUM_CPU_CONTEXT_OFFSET_XBP (G_STRUCT_OFFSET (GumCpuContext, rbp)) +# define GUM_CPU_CONTEXT_OFFSET_XSI (G_STRUCT_OFFSET (GumCpuContext, rsi)) +# define GUM_CPU_CONTEXT_OFFSET_XDI (G_STRUCT_OFFSET (GumCpuContext, rdi)) +# define GUM_CPU_CONTEXT_OFFSET_XIP (G_STRUCT_OFFSET (GumCpuContext, rip)) +# else +# define GUM_CPU_CONTEXT_XAX(c) ((c)->eax) +# define GUM_CPU_CONTEXT_XCX(c) ((c)->ecx) +# define GUM_CPU_CONTEXT_XDX(c) ((c)->edx) +# define GUM_CPU_CONTEXT_XBX(c) ((c)->ebx) +# define GUM_CPU_CONTEXT_XSP(c) ((c)->esp) +# define GUM_CPU_CONTEXT_XBP(c) ((c)->ebp) +# define GUM_CPU_CONTEXT_XSI(c) ((c)->esi) +# define GUM_CPU_CONTEXT_XDI(c) ((c)->edi) +# define GUM_CPU_CONTEXT_XIP(c) ((c)->eip) +# define GUM_CPU_CONTEXT_OFFSET_XAX (G_STRUCT_OFFSET (GumCpuContext, eax)) +# define GUM_CPU_CONTEXT_OFFSET_XCX (G_STRUCT_OFFSET (GumCpuContext, ecx)) +# define GUM_CPU_CONTEXT_OFFSET_XDX (G_STRUCT_OFFSET (GumCpuContext, edx)) +# define GUM_CPU_CONTEXT_OFFSET_XBX (G_STRUCT_OFFSET (GumCpuContext, ebx)) +# define GUM_CPU_CONTEXT_OFFSET_XSP (G_STRUCT_OFFSET (GumCpuContext, esp)) +# define GUM_CPU_CONTEXT_OFFSET_XBP (G_STRUCT_OFFSET (GumCpuContext, ebp)) +# define GUM_CPU_CONTEXT_OFFSET_XSI (G_STRUCT_OFFSET (GumCpuContext, esi)) +# define GUM_CPU_CONTEXT_OFFSET_XDI (G_STRUCT_OFFSET (GumCpuContext, edi)) +# define GUM_CPU_CONTEXT_OFFSET_XIP (G_STRUCT_OFFSET (GumCpuContext, eip)) +# endif +#endif + +#define GUM_MAX_PATH 260 +#define GUM_MAX_TYPE_NAME 16 +#define GUM_MAX_SYMBOL_NAME 2048 + +#define GUM_MAX_THREADS 768 +#define GUM_MAX_CALL_DEPTH 32 +#define GUM_MAX_BACKTRACE_DEPTH 16 +#define GUM_MAX_WORST_CASE_INFO_SIZE 128 + +#define GUM_MAX_LISTENERS_PER_FUNCTION 2 +#define GUM_MAX_LISTENER_DATA 1024 + +#define GUM_MAX_THREAD_RANGES 2 + +#if defined (HAVE_I386) +# if GLIB_SIZEOF_VOID_P == 8 +# define GUM_CPU_MODE CS_MODE_64 +# define GUM_X86_THUNK +# else +# define GUM_CPU_MODE CS_MODE_32 +# define GUM_X86_THUNK GUM_FASTCALL +# endif +#else +# if G_BYTE_ORDER == G_LITTLE_ENDIAN +# define GUM_CPU_MODE CS_MODE_LITTLE_ENDIAN +# else +# define GUM_CPU_MODE CS_MODE_BIG_ENDIAN +# endif +#endif +#if !defined (G_OS_WIN32) && GLIB_SIZEOF_VOID_P == 8 +# define GUM_X86_THUNK_REG_ARG0 GUM_X86_XDI +# define GUM_X86_THUNK_REG_ARG1 GUM_X86_XSI +#else +# define GUM_X86_THUNK_REG_ARG0 GUM_X86_XCX +# define GUM_X86_THUNK_REG_ARG1 GUM_X86_XDX +#endif +#define GUM_RED_ZONE_SIZE 128 + +#if defined (_M_IX86) || defined (__i386__) +# ifdef _MSC_VER +# define GUM_CDECL __cdecl +# define GUM_STDCALL __stdcall +# define GUM_FASTCALL __fastcall +# else +# define GUM_CDECL __attribute__ ((cdecl)) +# define GUM_STDCALL __attribute__ ((stdcall)) +# define GUM_FASTCALL __attribute__ ((fastcall)) +# endif +#else +# define GUM_CDECL +# define GUM_STDCALL +# define GUM_FASTCALL +#endif + +#ifdef _MSC_VER +# define GUM_NOINLINE __declspec (noinline) +#else +# define GUM_NOINLINE __attribute__ ((noinline)) +#endif + +#define GUM_ALIGN_POINTER(t, p, b) \ + ((t) GSIZE_TO_POINTER (((GPOINTER_TO_SIZE (p) + ((gsize) (b - 1))) & \ + ~((gsize) (b - 1))))) +#define GUM_ALIGN_SIZE(s, b) \ + ((((gsize) s) + ((gsize) (b - 1))) & ~((gsize) (b - 1))) + +#define GUM_FUNCPTR_TO_POINTER(f) (GSIZE_TO_POINTER (f)) +#define GUM_POINTER_TO_FUNCPTR(t, p) ((t) GPOINTER_TO_SIZE (p)) + +#define GUM_INT2_MASK 0x00000003U +#define GUM_INT3_MASK 0x00000007U +#define GUM_INT4_MASK 0x0000000fU +#define GUM_INT5_MASK 0x0000001fU +#define GUM_INT6_MASK 0x0000003fU +#define GUM_INT8_MASK 0x000000ffU +#define GUM_INT10_MASK 0x000003ffU +#define GUM_INT11_MASK 0x000007ffU +#define GUM_INT12_MASK 0x00000fffU +#define GUM_INT14_MASK 0x00003fffU +#define GUM_INT16_MASK 0x0000ffffU +#define GUM_INT18_MASK 0x0003ffffU +#define GUM_INT19_MASK 0x0007ffffU +#define GUM_INT24_MASK 0x00ffffffU +#define GUM_INT26_MASK 0x03ffffffU +#define GUM_INT28_MASK 0x0fffffffU +#define GUM_INT32_MASK 0xffffffffU + +#define GUM_IS_WITHIN_UINT7_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (0) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (127)) +#define GUM_IS_WITHIN_UINT8_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (0) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (255)) +#define GUM_IS_WITHIN_INT8_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-128) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (127)) +#define GUM_IS_WITHIN_INT11_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-1024) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (1023)) +#define GUM_IS_WITHIN_INT14_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-8192) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (8191)) +#define GUM_IS_WITHIN_INT16_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-32768) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (32767)) +#define GUM_IS_WITHIN_INT18_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-131072) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (131071)) +#define GUM_IS_WITHIN_INT19_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-262144) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (262143)) +#define GUM_IS_WITHIN_INT20_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-524288) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (524287)) +#define GUM_IS_WITHIN_INT21_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-1048576) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (1048575)) +#define GUM_IS_WITHIN_INT24_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-8388608) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (8388607)) +#define GUM_IS_WITHIN_INT26_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-33554432) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (33554431)) +#define GUM_IS_WITHIN_INT28_RANGE(i) \ + (((gint64) (i)) >= G_GINT64_CONSTANT (-134217728) && \ + ((gint64) (i)) <= G_GINT64_CONSTANT (134217727)) +#define GUM_IS_WITHIN_INT32_RANGE(i) \ + (((gint64) (i)) >= (gint64) G_MININT32 && \ + ((gint64) (i)) <= (gint64) G_MAXINT32) -/// Group of X86 instructions -typedef enum x86_insn_group { - X86_GRP_INVALID = 0, ///< = CS_GRP_INVALID +#ifdef G_NORETURN +# define GUM_NORETURN G_NORETURN +#else +# define GUM_NORETURN +#endif - // Generic groups - // all jump instructions (conditional+direct+indirect jumps) - X86_GRP_JUMP, ///< = CS_GRP_JUMP - // all call instructions - X86_GRP_CALL, ///< = CS_GRP_CALL - // all return instructions - X86_GRP_RET, ///< = CS_GRP_RET - // all interrupt instructions (int+syscall) - X86_GRP_INT, ///< = CS_GRP_INT - // all interrupt return instructions - X86_GRP_IRET, ///< = CS_GRP_IRET - // all privileged instructions - X86_GRP_PRIVILEGE, ///< = CS_GRP_PRIVILEGE - // all relative branching instructions - X86_GRP_BRANCH_RELATIVE, ///< = CS_GRP_BRANCH_RELATIVE +GUM_API GQuark gum_error_quark (void); - // Architecture-specific groups - X86_GRP_VM = 128, ///< all virtualization instructions (VT-x + AMD-V) - X86_GRP_3DNOW, - X86_GRP_AES, - X86_GRP_ADX, - X86_GRP_AVX, - X86_GRP_AVX2, - X86_GRP_AVX512, - X86_GRP_BMI, - X86_GRP_BMI2, - X86_GRP_CMOV, - X86_GRP_F16C, - X86_GRP_FMA, - X86_GRP_FMA4, - X86_GRP_FSGSBASE, - X86_GRP_HLE, - X86_GRP_MMX, - X86_GRP_MODE32, - X86_GRP_MODE64, - X86_GRP_RTM, - X86_GRP_SHA, - X86_GRP_SSE1, - X86_GRP_SSE2, - X86_GRP_SSE3, - X86_GRP_SSE41, - X86_GRP_SSE42, - X86_GRP_SSE4A, - X86_GRP_SSSE3, - X86_GRP_PCLMUL, - X86_GRP_XOP, - X86_GRP_CDI, - X86_GRP_ERI, - X86_GRP_TBM, - X86_GRP_16BITMODE, - X86_GRP_NOT64BITMODE, - X86_GRP_SGX, - X86_GRP_DQI, - X86_GRP_BWI, - X86_GRP_PFI, - X86_GRP_VLX, - X86_GRP_SMAP, - X86_GRP_NOVLX, - X86_GRP_FPU, +GUM_API GUM_NORETURN void gum_panic (const gchar * format, ...) + G_ANALYZER_NORETURN; - X86_GRP_ENDING -} x86_insn_group; +GUM_API GumCpuFeatures gum_query_cpu_features (void); -#ifdef __cplusplus -} -#endif +GUM_API gpointer gum_cpu_context_get_nth_argument (GumCpuContext * self, + guint n); +GUM_API void gum_cpu_context_replace_nth_argument (GumCpuContext * self, + guint n, gpointer value); +GUM_API gpointer gum_cpu_context_get_return_value (GumCpuContext * self); +GUM_API void gum_cpu_context_replace_return_value (GumCpuContext * self, + gpointer value); -#endif -#ifndef CAPSTONE_XCORE_H -#define CAPSTONE_XCORE_H +#define GUM_TYPE_CPU_CONTEXT (gum_cpu_context_get_type ()) +GUM_API GType gum_cpu_context_get_type (void) G_GNUC_CONST; +GUM_API GumCpuContext * gum_cpu_context_copy ( + const GumCpuContext * cpu_context); +GUM_API void gum_cpu_context_free (GumCpuContext * cpu_context); -/* Capstone Disassembly Engine */ -/* By Nguyen Anh Quynh , 2014-2015 */ +GUM_API GType gum_address_get_type (void) G_GNUC_CONST; + +G_END_DECLS -#ifdef __cplusplus -extern "C" { #endif +/* + * Copyright (C) 2016-2023 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -#ifdef _MSC_VER -#pragma warning(disable:4201) -#endif +#ifndef __GUM_API_RESOLVER_H__ +#define __GUM_API_RESOLVER_H__ -/// Operand type for instruction's operands -typedef enum xcore_op_type { - XCORE_OP_INVALID = 0, ///< = CS_OP_INVALID (Uninitialized). - XCORE_OP_REG, ///< = CS_OP_REG (Register operand). - XCORE_OP_IMM, ///< = CS_OP_IMM (Immediate operand). - XCORE_OP_MEM, ///< = CS_OP_MEM (Memory operand). -} xcore_op_type; -/// XCore registers -typedef enum xcore_reg { - XCORE_REG_INVALID = 0, +G_BEGIN_DECLS - XCORE_REG_CP, - XCORE_REG_DP, - XCORE_REG_LR, - XCORE_REG_SP, - XCORE_REG_R0, - XCORE_REG_R1, - XCORE_REG_R2, - XCORE_REG_R3, - XCORE_REG_R4, - XCORE_REG_R5, - XCORE_REG_R6, - XCORE_REG_R7, - XCORE_REG_R8, - XCORE_REG_R9, - XCORE_REG_R10, - XCORE_REG_R11, +#define GUM_API_SIZE_NONE -1 - // pseudo registers - XCORE_REG_PC, ///< pc +#define GUM_TYPE_API_RESOLVER (gum_api_resolver_get_type ()) +G_DECLARE_INTERFACE (GumApiResolver, gum_api_resolver, GUM, API_RESOLVER, + GObject) - // internal thread registers - // see The-XMOS-XS1-Architecture(X7879A).pdf - XCORE_REG_SCP, ///< save pc - XCORE_REG_SSR, //< save status - XCORE_REG_ET, //< exception type - XCORE_REG_ED, //< exception data - XCORE_REG_SED, //< save exception data - XCORE_REG_KEP, //< kernel entry pointer - XCORE_REG_KSP, //< kernel stack pointer - XCORE_REG_ID, //< thread ID +typedef struct _GumApiDetails GumApiDetails; - XCORE_REG_ENDING, // <-- mark the end of the list of registers -} xcore_reg; +typedef gboolean (* GumFoundApiFunc) (const GumApiDetails * details, + gpointer user_data); -/// Instruction's operand referring to memory -/// This is associated with XCORE_OP_MEM operand type above -typedef struct xcore_op_mem { - uint8_t base; ///< base register, can be safely interpreted as - ///< a value of type `xcore_reg`, but it is only - ///< one byte wide - uint8_t index; ///< index register, same conditions apply here - int32_t disp; ///< displacement/offset value - int direct; ///< +1: forward, -1: backward -} xcore_op_mem; +struct _GumApiResolverInterface +{ + GTypeInterface parent; -/// Instruction operand -typedef struct cs_xcore_op { - xcore_op_type type; ///< operand type - union { - xcore_reg reg; ///< register value for REG operand - int32_t imm; ///< immediate value for IMM operand - xcore_op_mem mem; ///< base/disp value for MEM operand - }; -} cs_xcore_op; + void (* enumerate_matches) (GumApiResolver * self, const gchar * query, + GumFoundApiFunc func, gpointer user_data, GError ** error); +}; -/// Instruction structure -typedef struct cs_xcore { - /// Number of operands of this instruction, - /// or 0 when instruction has no operand. - uint8_t op_count; - cs_xcore_op operands[8]; ///< operands for this instruction. -} cs_xcore; +struct _GumApiDetails +{ + const gchar * name; + GumAddress address; + gssize size; +}; -/// XCore instruction -typedef enum xcore_insn { - XCORE_INS_INVALID = 0, +GUM_API GumApiResolver * gum_api_resolver_make (const gchar * type); - XCORE_INS_ADD, - XCORE_INS_ANDNOT, - XCORE_INS_AND, - XCORE_INS_ASHR, - XCORE_INS_BAU, - XCORE_INS_BITREV, - XCORE_INS_BLA, - XCORE_INS_BLAT, - XCORE_INS_BL, - XCORE_INS_BF, - XCORE_INS_BT, - XCORE_INS_BU, - XCORE_INS_BRU, - XCORE_INS_BYTEREV, - XCORE_INS_CHKCT, - XCORE_INS_CLRE, - XCORE_INS_CLRPT, - XCORE_INS_CLRSR, - XCORE_INS_CLZ, - XCORE_INS_CRC8, - XCORE_INS_CRC32, - XCORE_INS_DCALL, - XCORE_INS_DENTSP, - XCORE_INS_DGETREG, - XCORE_INS_DIVS, - XCORE_INS_DIVU, - XCORE_INS_DRESTSP, - XCORE_INS_DRET, - XCORE_INS_ECALLF, - XCORE_INS_ECALLT, - XCORE_INS_EDU, - XCORE_INS_EEF, - XCORE_INS_EET, - XCORE_INS_EEU, - XCORE_INS_ENDIN, - XCORE_INS_ENTSP, - XCORE_INS_EQ, - XCORE_INS_EXTDP, - XCORE_INS_EXTSP, - XCORE_INS_FREER, - XCORE_INS_FREET, - XCORE_INS_GETD, - XCORE_INS_GET, - XCORE_INS_GETN, - XCORE_INS_GETR, - XCORE_INS_GETSR, - XCORE_INS_GETST, - XCORE_INS_GETTS, - XCORE_INS_INCT, - XCORE_INS_INIT, - XCORE_INS_INPW, - XCORE_INS_INSHR, - XCORE_INS_INT, - XCORE_INS_IN, - XCORE_INS_KCALL, - XCORE_INS_KENTSP, - XCORE_INS_KRESTSP, - XCORE_INS_KRET, - XCORE_INS_LADD, - XCORE_INS_LD16S, - XCORE_INS_LD8U, - XCORE_INS_LDA16, - XCORE_INS_LDAP, - XCORE_INS_LDAW, - XCORE_INS_LDC, - XCORE_INS_LDW, - XCORE_INS_LDIVU, - XCORE_INS_LMUL, - XCORE_INS_LSS, - XCORE_INS_LSUB, - XCORE_INS_LSU, - XCORE_INS_MACCS, - XCORE_INS_MACCU, - XCORE_INS_MJOIN, - XCORE_INS_MKMSK, - XCORE_INS_MSYNC, - XCORE_INS_MUL, - XCORE_INS_NEG, - XCORE_INS_NOT, - XCORE_INS_OR, - XCORE_INS_OUTCT, - XCORE_INS_OUTPW, - XCORE_INS_OUTSHR, - XCORE_INS_OUTT, - XCORE_INS_OUT, - XCORE_INS_PEEK, - XCORE_INS_REMS, - XCORE_INS_REMU, - XCORE_INS_RETSP, - XCORE_INS_SETCLK, - XCORE_INS_SET, - XCORE_INS_SETC, - XCORE_INS_SETD, - XCORE_INS_SETEV, - XCORE_INS_SETN, - XCORE_INS_SETPSC, - XCORE_INS_SETPT, - XCORE_INS_SETRDY, - XCORE_INS_SETSR, - XCORE_INS_SETTW, - XCORE_INS_SETV, - XCORE_INS_SEXT, - XCORE_INS_SHL, - XCORE_INS_SHR, - XCORE_INS_SSYNC, - XCORE_INS_ST16, - XCORE_INS_ST8, - XCORE_INS_STW, - XCORE_INS_SUB, - XCORE_INS_SYNCR, - XCORE_INS_TESTCT, - XCORE_INS_TESTLCL, - XCORE_INS_TESTWCT, - XCORE_INS_TSETMR, - XCORE_INS_START, - XCORE_INS_WAITEF, - XCORE_INS_WAITET, - XCORE_INS_WAITEU, - XCORE_INS_XOR, - XCORE_INS_ZEXT, +GUM_API void gum_api_resolver_enumerate_matches (GumApiResolver * self, + const gchar * query, GumFoundApiFunc func, gpointer user_data, + GError ** error); - XCORE_INS_ENDING, // <-- mark the end of the list of instructions -} xcore_insn; +G_END_DECLS -/// Group of XCore instructions -typedef enum xcore_insn_group { - XCORE_GRP_INVALID = 0, ///< = CS_GRP_INVALID +#endif +/* + * Copyright (C) 2008-2022 Ole André Vadla Ravnås + * Copyright (C) 2021 Francesco Tamagni + * + * Licence: wxWindows Library Licence, Version 3.1 + */ - // Generic groups - // all jump instructions (conditional+direct+indirect jumps) - XCORE_GRP_JUMP, ///< = CS_GRP_JUMP +#ifndef __GUM_BACKTRACER_H__ +#define __GUM_BACKTRACER_H__ - XCORE_GRP_ENDING, // <-- mark the end of the list of groups -} xcore_insn_group; +/* + * Copyright (C) 2008-2010 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -#ifdef __cplusplus -} -#endif +#ifndef __GUM_RETURN_ADDRESS_H__ +#define __GUM_RETURN_ADDRESS_H__ -#endif -/* Capstone Disassembly Engine */ -/* TMS320C64x Backend by Fotis Loukos 2016 */ -#ifndef CAPSTONE_TMS320C64X_H -#define CAPSTONE_TMS320C64X_H +typedef struct _GumReturnAddressDetails GumReturnAddressDetails; +typedef gpointer GumReturnAddress; +typedef struct _GumReturnAddressArray GumReturnAddressArray; + +struct _GumReturnAddressDetails +{ + GumReturnAddress address; + gchar module_name[GUM_MAX_PATH + 1]; + gchar function_name[GUM_MAX_SYMBOL_NAME + 1]; + gchar file_name[GUM_MAX_PATH + 1]; + guint line_number; + guint column; +}; + +struct _GumReturnAddressArray +{ + guint len; + GumReturnAddress items[GUM_MAX_BACKTRACE_DEPTH]; +}; + +G_BEGIN_DECLS + +GUM_API gboolean gum_return_address_details_from_address ( + GumReturnAddress address, GumReturnAddressDetails * details); + +GUM_API gboolean gum_return_address_array_is_equal ( + const GumReturnAddressArray * array1, + const GumReturnAddressArray * array2); + +G_END_DECLS -#ifdef __cplusplus -extern "C" { #endif -#include +G_BEGIN_DECLS + +#define GUM_TYPE_BACKTRACER (gum_backtracer_get_type ()) +G_DECLARE_INTERFACE (GumBacktracer, gum_backtracer, GUM, BACKTRACER, GObject) + +struct _GumBacktracerInterface +{ + GTypeInterface parent; + + void (* generate) (GumBacktracer * self, const GumCpuContext * cpu_context, + GumReturnAddressArray * return_addresses, guint limit); +}; + +GUM_API GumBacktracer * gum_backtracer_make_accurate (void); +GUM_API GumBacktracer * gum_backtracer_make_fuzzy (void); + +GUM_API void gum_backtracer_generate (GumBacktracer * self, + const GumCpuContext * cpu_context, + GumReturnAddressArray * return_addresses); +GUM_API void gum_backtracer_generate_with_limit (GumBacktracer * self, + const GumCpuContext * cpu_context, + GumReturnAddressArray * return_addresses, guint limit); + +G_END_DECLS -#ifdef _MSC_VER -#pragma warning(disable:4201) #endif +/* + * Copyright (C) 2026 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -typedef enum tms320c64x_op_type { - TMS320C64X_OP_INVALID = 0, ///< = CS_OP_INVALID (Uninitialized). - TMS320C64X_OP_REG, ///< = CS_OP_REG (Register operand). - TMS320C64X_OP_IMM, ///< = CS_OP_IMM (Immediate operand). - TMS320C64X_OP_MEM, ///< = CS_OP_MEM (Memory operand). - TMS320C64X_OP_REGPAIR = 64, ///< Register pair for double word ops -} tms320c64x_op_type; +#ifndef __GUM_CONTROL_FLOW_GRAPH_H__ +#define __GUM_CONTROL_FLOW_GRAPH_H__ + +/* + * Copyright (C) 2008-2026 Ole André Vadla Ravnås + * Copyright (C) 2008 Christian Berentsen + * Copyright (C) 2025 Francesco Tamagni + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +#ifndef __GUM_MEMORY_H__ +#define __GUM_MEMORY_H__ + + +#define GUM_TYPE_MATCH_PATTERN (gum_match_pattern_get_type ()) +#define GUM_TYPE_MEMORY_RANGE (gum_memory_range_get_type ()) +#define GUM_MEMORY_RANGE_INCLUDES(r, a) ((a) >= (r)->base_address && \ + (a) < ((r)->base_address + (r)->size)) + +#define GUM_PAGE_RW ((GumPageProtection) (GUM_PAGE_READ | GUM_PAGE_WRITE)) +#define GUM_PAGE_RX ((GumPageProtection) (GUM_PAGE_READ | GUM_PAGE_EXECUTE)) +#define GUM_PAGE_RWX ((GumPageProtection) (GUM_PAGE_READ | GUM_PAGE_WRITE | \ + GUM_PAGE_EXECUTE)) + +G_BEGIN_DECLS + +typedef guint GumPtrauthSupport; +typedef guint GumRwxSupport; +typedef guint GumMemoryOperation; +typedef guint GumPageProtection; +typedef struct _GumAddressSpec GumAddressSpec; +typedef struct _GumRangeDetails GumRangeDetails; +typedef struct _GumMemoryRange GumMemoryRange; +typedef struct _GumFileMapping GumFileMapping; +typedef struct _GumMatchPattern GumMatchPattern; +typedef struct _GumPointerMatch GumPointerMatch; + +typedef gboolean (* GumMemoryIsNearFunc) (gpointer memory, gpointer address); + +enum _GumPtrauthSupport +{ + GUM_PTRAUTH_INVALID, + GUM_PTRAUTH_UNSUPPORTED, + GUM_PTRAUTH_SUPPORTED +}; -typedef enum tms320c64x_mem_disp { - TMS320C64X_MEM_DISP_INVALID = 0, - TMS320C64X_MEM_DISP_CONSTANT, - TMS320C64X_MEM_DISP_REGISTER, -} tms320c64x_mem_disp; +enum _GumRwxSupport +{ + GUM_RWX_NONE, + GUM_RWX_ALLOCATIONS_ONLY, + GUM_RWX_FULL +}; -typedef enum tms320c64x_mem_dir { - TMS320C64X_MEM_DIR_INVALID = 0, - TMS320C64X_MEM_DIR_FW, - TMS320C64X_MEM_DIR_BW, -} tms320c64x_mem_dir; +enum _GumMemoryOperation +{ + GUM_MEMOP_INVALID, + GUM_MEMOP_READ, + GUM_MEMOP_WRITE, + GUM_MEMOP_EXECUTE +}; -typedef enum tms320c64x_mem_mod { - TMS320C64X_MEM_MOD_INVALID = 0, - TMS320C64X_MEM_MOD_NO, - TMS320C64X_MEM_MOD_PRE, - TMS320C64X_MEM_MOD_POST, -} tms320c64x_mem_mod; +enum _GumPageProtection +{ + GUM_PAGE_NO_ACCESS = 0, + GUM_PAGE_READ = (1 << 0), + GUM_PAGE_WRITE = (1 << 1), + GUM_PAGE_EXECUTE = (1 << 2), +}; -typedef struct tms320c64x_op_mem { - unsigned int base; ///< base register - unsigned int disp; ///< displacement/offset value - unsigned int unit; ///< unit of base and offset register - unsigned int scaled; ///< offset scaled - unsigned int disptype; ///< displacement type - unsigned int direction; ///< direction - unsigned int modify; ///< modification -} tms320c64x_op_mem; +struct _GumAddressSpec +{ + gpointer near_address; + gsize max_distance; +}; -typedef struct cs_tms320c64x_op { - tms320c64x_op_type type; ///< operand type - union { - unsigned int reg; ///< register value for REG operand or first register for REGPAIR operand - int32_t imm; ///< immediate value for IMM operand - tms320c64x_op_mem mem; ///< base/disp value for MEM operand - }; -} cs_tms320c64x_op; +struct _GumRangeDetails +{ + const GumMemoryRange * range; + GumPageProtection protection; + const GumFileMapping * file; +}; -typedef struct cs_tms320c64x { - uint8_t op_count; - cs_tms320c64x_op operands[8]; ///< operands for this instruction. - struct { - unsigned int reg; - unsigned int zero; - } condition; - struct { - unsigned int unit; - unsigned int side; - unsigned int crosspath; - } funit; - unsigned int parallel; -} cs_tms320c64x; +struct _GumMemoryRange +{ + GumAddress base_address; + gsize size; +}; -typedef enum tms320c64x_reg { - TMS320C64X_REG_INVALID = 0, +struct _GumFileMapping +{ + const gchar * path; + guint64 offset; + gsize size; +}; - TMS320C64X_REG_AMR, - TMS320C64X_REG_CSR, - TMS320C64X_REG_DIER, - TMS320C64X_REG_DNUM, - TMS320C64X_REG_ECR, - TMS320C64X_REG_GFPGFR, - TMS320C64X_REG_GPLYA, - TMS320C64X_REG_GPLYB, - TMS320C64X_REG_ICR, - TMS320C64X_REG_IER, - TMS320C64X_REG_IERR, - TMS320C64X_REG_ILC, - TMS320C64X_REG_IRP, - TMS320C64X_REG_ISR, - TMS320C64X_REG_ISTP, - TMS320C64X_REG_ITSR, - TMS320C64X_REG_NRP, - TMS320C64X_REG_NTSR, - TMS320C64X_REG_REP, - TMS320C64X_REG_RILC, - TMS320C64X_REG_SSR, - TMS320C64X_REG_TSCH, - TMS320C64X_REG_TSCL, - TMS320C64X_REG_TSR, - TMS320C64X_REG_A0, - TMS320C64X_REG_A1, - TMS320C64X_REG_A2, - TMS320C64X_REG_A3, - TMS320C64X_REG_A4, - TMS320C64X_REG_A5, - TMS320C64X_REG_A6, - TMS320C64X_REG_A7, - TMS320C64X_REG_A8, - TMS320C64X_REG_A9, - TMS320C64X_REG_A10, - TMS320C64X_REG_A11, - TMS320C64X_REG_A12, - TMS320C64X_REG_A13, - TMS320C64X_REG_A14, - TMS320C64X_REG_A15, - TMS320C64X_REG_A16, - TMS320C64X_REG_A17, - TMS320C64X_REG_A18, - TMS320C64X_REG_A19, - TMS320C64X_REG_A20, - TMS320C64X_REG_A21, - TMS320C64X_REG_A22, - TMS320C64X_REG_A23, - TMS320C64X_REG_A24, - TMS320C64X_REG_A25, - TMS320C64X_REG_A26, - TMS320C64X_REG_A27, - TMS320C64X_REG_A28, - TMS320C64X_REG_A29, - TMS320C64X_REG_A30, - TMS320C64X_REG_A31, - TMS320C64X_REG_B0, - TMS320C64X_REG_B1, - TMS320C64X_REG_B2, - TMS320C64X_REG_B3, - TMS320C64X_REG_B4, - TMS320C64X_REG_B5, - TMS320C64X_REG_B6, - TMS320C64X_REG_B7, - TMS320C64X_REG_B8, - TMS320C64X_REG_B9, - TMS320C64X_REG_B10, - TMS320C64X_REG_B11, - TMS320C64X_REG_B12, - TMS320C64X_REG_B13, - TMS320C64X_REG_B14, - TMS320C64X_REG_B15, - TMS320C64X_REG_B16, - TMS320C64X_REG_B17, - TMS320C64X_REG_B18, - TMS320C64X_REG_B19, - TMS320C64X_REG_B20, - TMS320C64X_REG_B21, - TMS320C64X_REG_B22, - TMS320C64X_REG_B23, - TMS320C64X_REG_B24, - TMS320C64X_REG_B25, - TMS320C64X_REG_B26, - TMS320C64X_REG_B27, - TMS320C64X_REG_B28, - TMS320C64X_REG_B29, - TMS320C64X_REG_B30, - TMS320C64X_REG_B31, - TMS320C64X_REG_PCE1, +struct _GumPointerMatch +{ + GumAddress address; + gsize value; +}; - TMS320C64X_REG_ENDING, // <-- mark the end of the list of registers +typedef gboolean (* GumFoundRangeFunc) (const GumRangeDetails * details, + gpointer user_data); +typedef void (* GumMemoryPatchApplyFunc) (gpointer mem, gpointer user_data); +typedef void (* GumMemoryPatchPagesApplyFunc) (gpointer mem, + gpointer target_page, guint n_pages, gpointer user_data); +typedef gboolean (* GumMemoryScanMatchFunc) (GumAddress address, gsize size, + gpointer user_data); - // Alias registers - TMS320C64X_REG_EFR = TMS320C64X_REG_ECR, - TMS320C64X_REG_IFR = TMS320C64X_REG_ISR, -} tms320c64x_reg; +GUM_API void gum_internal_heap_ref (void); +GUM_API void gum_internal_heap_unref (void); -typedef enum tms320c64x_insn { - TMS320C64X_INS_INVALID = 0, +GUM_API gpointer gum_sign_code_pointer (gpointer value); +GUM_API gpointer gum_strip_code_pointer (gpointer value); +GUM_API GumAddress gum_sign_code_address (GumAddress value); +GUM_API GumAddress gum_strip_code_address (GumAddress value); +GUM_API GumPtrauthSupport gum_query_ptrauth_support (void); +GUM_API guint gum_query_page_size (void); +GUM_API gboolean gum_query_is_rwx_supported (void); +GUM_API GumRwxSupport gum_query_rwx_support (void); +GUM_API gboolean gum_memory_is_readable (gconstpointer address, gsize len); +GUM_API gboolean gum_memory_query_protection (gconstpointer address, + GumPageProtection * prot); +GUM_API guint8 * gum_memory_read (gconstpointer address, gsize len, + gsize * n_bytes_read); +GUM_API gboolean gum_memory_write (gpointer address, const guint8 * bytes, + gsize len); +GUM_API gboolean gum_memory_patch_code (gpointer address, gsize size, + GumMemoryPatchApplyFunc apply, gpointer apply_data); +GUM_API gboolean gum_memory_patch_code_pages (GPtrArray * sorted_addresses, + gboolean coalesce, GumMemoryPatchPagesApplyFunc apply, + gpointer apply_data); +GUM_API gboolean gum_memory_can_remap_writable (void); +GUM_API gpointer gum_memory_try_remap_writable_pages (gpointer first_page, + guint n_pages); +GUM_API void gum_memory_dispose_writable_pages (gpointer first_page, + guint n_pages); +GUM_API gboolean gum_memory_mark_code (gpointer address, gsize size); - TMS320C64X_INS_ABS, - TMS320C64X_INS_ABS2, - TMS320C64X_INS_ADD, - TMS320C64X_INS_ADD2, - TMS320C64X_INS_ADD4, - TMS320C64X_INS_ADDAB, - TMS320C64X_INS_ADDAD, - TMS320C64X_INS_ADDAH, - TMS320C64X_INS_ADDAW, - TMS320C64X_INS_ADDK, - TMS320C64X_INS_ADDKPC, - TMS320C64X_INS_ADDU, - TMS320C64X_INS_AND, - TMS320C64X_INS_ANDN, - TMS320C64X_INS_AVG2, - TMS320C64X_INS_AVGU4, - TMS320C64X_INS_B, - TMS320C64X_INS_BDEC, - TMS320C64X_INS_BITC4, - TMS320C64X_INS_BNOP, - TMS320C64X_INS_BPOS, - TMS320C64X_INS_CLR, - TMS320C64X_INS_CMPEQ, - TMS320C64X_INS_CMPEQ2, - TMS320C64X_INS_CMPEQ4, - TMS320C64X_INS_CMPGT, - TMS320C64X_INS_CMPGT2, - TMS320C64X_INS_CMPGTU4, - TMS320C64X_INS_CMPLT, - TMS320C64X_INS_CMPLTU, - TMS320C64X_INS_DEAL, - TMS320C64X_INS_DOTP2, - TMS320C64X_INS_DOTPN2, - TMS320C64X_INS_DOTPNRSU2, - TMS320C64X_INS_DOTPRSU2, - TMS320C64X_INS_DOTPSU4, - TMS320C64X_INS_DOTPU4, - TMS320C64X_INS_EXT, - TMS320C64X_INS_EXTU, - TMS320C64X_INS_GMPGTU, - TMS320C64X_INS_GMPY4, - TMS320C64X_INS_LDB, - TMS320C64X_INS_LDBU, - TMS320C64X_INS_LDDW, - TMS320C64X_INS_LDH, - TMS320C64X_INS_LDHU, - TMS320C64X_INS_LDNDW, - TMS320C64X_INS_LDNW, - TMS320C64X_INS_LDW, - TMS320C64X_INS_LMBD, - TMS320C64X_INS_MAX2, - TMS320C64X_INS_MAXU4, - TMS320C64X_INS_MIN2, - TMS320C64X_INS_MINU4, - TMS320C64X_INS_MPY, - TMS320C64X_INS_MPY2, - TMS320C64X_INS_MPYH, - TMS320C64X_INS_MPYHI, - TMS320C64X_INS_MPYHIR, - TMS320C64X_INS_MPYHL, - TMS320C64X_INS_MPYHLU, - TMS320C64X_INS_MPYHSLU, - TMS320C64X_INS_MPYHSU, - TMS320C64X_INS_MPYHU, - TMS320C64X_INS_MPYHULS, - TMS320C64X_INS_MPYHUS, - TMS320C64X_INS_MPYLH, - TMS320C64X_INS_MPYLHU, - TMS320C64X_INS_MPYLI, - TMS320C64X_INS_MPYLIR, - TMS320C64X_INS_MPYLSHU, - TMS320C64X_INS_MPYLUHS, - TMS320C64X_INS_MPYSU, - TMS320C64X_INS_MPYSU4, - TMS320C64X_INS_MPYU, - TMS320C64X_INS_MPYU4, - TMS320C64X_INS_MPYUS, - TMS320C64X_INS_MVC, - TMS320C64X_INS_MVD, - TMS320C64X_INS_MVK, - TMS320C64X_INS_MVKL, - TMS320C64X_INS_MVKLH, - TMS320C64X_INS_NOP, - TMS320C64X_INS_NORM, - TMS320C64X_INS_OR, - TMS320C64X_INS_PACK2, - TMS320C64X_INS_PACKH2, - TMS320C64X_INS_PACKH4, - TMS320C64X_INS_PACKHL2, - TMS320C64X_INS_PACKL4, - TMS320C64X_INS_PACKLH2, - TMS320C64X_INS_ROTL, - TMS320C64X_INS_SADD, - TMS320C64X_INS_SADD2, - TMS320C64X_INS_SADDU4, - TMS320C64X_INS_SADDUS2, - TMS320C64X_INS_SAT, - TMS320C64X_INS_SET, - TMS320C64X_INS_SHFL, - TMS320C64X_INS_SHL, - TMS320C64X_INS_SHLMB, - TMS320C64X_INS_SHR, - TMS320C64X_INS_SHR2, - TMS320C64X_INS_SHRMB, - TMS320C64X_INS_SHRU, - TMS320C64X_INS_SHRU2, - TMS320C64X_INS_SMPY, - TMS320C64X_INS_SMPY2, - TMS320C64X_INS_SMPYH, - TMS320C64X_INS_SMPYHL, - TMS320C64X_INS_SMPYLH, - TMS320C64X_INS_SPACK2, - TMS320C64X_INS_SPACKU4, - TMS320C64X_INS_SSHL, - TMS320C64X_INS_SSHVL, - TMS320C64X_INS_SSHVR, - TMS320C64X_INS_SSUB, - TMS320C64X_INS_STB, - TMS320C64X_INS_STDW, - TMS320C64X_INS_STH, - TMS320C64X_INS_STNDW, - TMS320C64X_INS_STNW, - TMS320C64X_INS_STW, - TMS320C64X_INS_SUB, - TMS320C64X_INS_SUB2, - TMS320C64X_INS_SUB4, - TMS320C64X_INS_SUBAB, - TMS320C64X_INS_SUBABS4, - TMS320C64X_INS_SUBAH, - TMS320C64X_INS_SUBAW, - TMS320C64X_INS_SUBC, - TMS320C64X_INS_SUBU, - TMS320C64X_INS_SWAP4, - TMS320C64X_INS_UNPKHU4, - TMS320C64X_INS_UNPKLU4, - TMS320C64X_INS_XOR, - TMS320C64X_INS_XPND2, - TMS320C64X_INS_XPND4, - // Aliases - TMS320C64X_INS_IDLE, - TMS320C64X_INS_MV, - TMS320C64X_INS_NEG, - TMS320C64X_INS_NOT, - TMS320C64X_INS_SWAP2, - TMS320C64X_INS_ZERO, +GUM_API void gum_memory_scan (const GumMemoryRange * range, + const GumMatchPattern * pattern, GumMemoryScanMatchFunc func, + gpointer user_data); +GUM_API GArray * gum_memory_find_pointers (const GumMemoryRange * ranges, + guint n_ranges, const gsize * values, guint n_values, gsize mask); - TMS320C64X_INS_ENDING, // <-- mark the end of the list of instructions -} tms320c64x_insn; +GUM_API GType gum_match_pattern_get_type (void) G_GNUC_CONST; +GUM_API GumMatchPattern * gum_match_pattern_new_from_string ( + const gchar * pattern_str); +GUM_API GumMatchPattern * gum_match_pattern_ref (GumMatchPattern * pattern); +GUM_API void gum_match_pattern_unref (GumMatchPattern * pattern); +GUM_API guint gum_match_pattern_get_size (const GumMatchPattern * pattern); +GUM_API GPtrArray * gum_match_pattern_get_tokens ( + const GumMatchPattern * pattern); -typedef enum tms320c64x_insn_group { - TMS320C64X_GRP_INVALID = 0, ///< = CS_GRP_INVALID +GUM_API void gum_ensure_code_readable (gconstpointer address, gsize size); - TMS320C64X_GRP_JUMP, ///< = CS_GRP_JUMP +GUM_API void gum_mprotect (gpointer address, gsize size, + GumPageProtection prot); +GUM_API gboolean gum_try_mprotect (gpointer address, gsize size, + GumPageProtection prot); - TMS320C64X_GRP_FUNIT_D = 128, - TMS320C64X_GRP_FUNIT_L, - TMS320C64X_GRP_FUNIT_M, - TMS320C64X_GRP_FUNIT_S, - TMS320C64X_GRP_FUNIT_NO, +GUM_API void gum_clear_cache (gpointer address, gsize size); - TMS320C64X_GRP_ENDING, // <-- mark the end of the list of groups -} tms320c64x_insn_group; +#define gum_new(struct_type, n_structs) \ + ((struct_type *) gum_malloc (n_structs * sizeof (struct_type))) +#define gum_new0(struct_type, n_structs) \ + ((struct_type *) gum_malloc0 (n_structs * sizeof (struct_type))) -typedef enum tms320c64x_funit { - TMS320C64X_FUNIT_INVALID = 0, - TMS320C64X_FUNIT_D, - TMS320C64X_FUNIT_L, - TMS320C64X_FUNIT_M, - TMS320C64X_FUNIT_S, - TMS320C64X_FUNIT_NO -} tms320c64x_funit; +GUM_API guint gum_peek_private_memory_usage (void); -#ifdef __cplusplus -} -#endif +GUM_API gpointer gum_malloc (gsize size); +GUM_API gpointer gum_malloc0 (gsize size); +GUM_API gsize gum_malloc_usable_size (gconstpointer mem); +GUM_API gpointer gum_calloc (gsize count, gsize size); +GUM_API gpointer gum_realloc (gpointer mem, gsize size); +GUM_API gpointer gum_memalign (gsize alignment, gsize size); +GUM_API gpointer gum_memdup (gconstpointer mem, gsize byte_size); +GUM_API void gum_free (gpointer mem); -#endif +GUM_API gpointer gum_memory_allocate (gpointer address, gsize size, + gsize alignment, GumPageProtection prot); +GUM_API gpointer gum_memory_allocate_near (const GumAddressSpec * spec, + gsize size, gsize alignment, GumPageProtection prot); +GUM_API gboolean gum_memory_free (gpointer address, gsize size); +GUM_API gboolean gum_memory_release (gpointer address, gsize size); +GUM_API gboolean gum_memory_recommit (gpointer address, gsize size, + GumPageProtection prot); +GUM_API gboolean gum_memory_discard (gpointer address, gsize size); +GUM_API gboolean gum_memory_decommit (gpointer address, gsize size); -#ifndef CAPSTONE_M680X_H -#define CAPSTONE_M680X_H +GUM_API gboolean gum_address_spec_is_satisfied_by (const GumAddressSpec * spec, + gconstpointer address); -/* Capstone Disassembly Engine */ -/* M680X Backend by Wolfgang Schwotzer 2017 */ +GUM_API GType gum_memory_range_get_type (void) G_GNUC_CONST; +GUM_API GumMemoryRange * gum_memory_range_copy (const GumMemoryRange * range); +GUM_API void gum_memory_range_free (GumMemoryRange * range); + +G_END_DECLS -#ifdef __cplusplus -extern "C" { #endif -#ifdef _MSC_VER -#pragma warning(disable:4201) -#endif +G_BEGIN_DECLS -#define M680X_OPERAND_COUNT 9 +#define GUM_CONTROL_FLOW_GRAPH_NO_BLOCK G_MAXUINT -/// M680X registers and special registers -typedef enum m680x_reg { - M680X_REG_INVALID = 0, +typedef struct _GumControlFlowGraph GumControlFlowGraph; - M680X_REG_A, ///< M6800/1/2/3/9, HD6301/9 - M680X_REG_B, ///< M6800/1/2/3/9, HD6301/9 - M680X_REG_E, ///< HD6309 - M680X_REG_F, ///< HD6309 - M680X_REG_0, ///< HD6309 +/** + * GumControlFlowGraphFindRangeFunc: + * @address: a code address to locate + * @range: (out): the contiguous code range covering @address + * @user_data: data passed to gum_control_flow_graph_new() + * + * Resolves the contiguous range of code that @address belongs to. Called for + * the entry, and for every direct branch target that falls outside the ranges + * discovered so far — this is how a function split across multiple ranges (e.g. + * a hot body plus a cold .text.unlikely fragment) gets stitched together. + * + * Returns: %TRUE if a range was found + */ +typedef gboolean (* GumControlFlowGraphFindRangeFunc) (gconstpointer address, + GumMemoryRange * range, gpointer user_data); - M680X_REG_D, ///< M6801/3/9, HD6301/9 - M680X_REG_W, ///< HD6309 +/** + * GumFoundDominatingSiteFunc: + * @site: instruction-aligned address that dominates the target + * @capacity: number of contiguous bytes at @site with no incoming branch and + * within a single range — how much may be overwritten by a redirect + * without another control-flow edge landing inside it + * @user_data: data passed to the dominating-site enumerator + * + * Returns: %TRUE to keep enumerating, %FALSE to stop + */ +typedef gboolean (* GumFoundDominatingSiteFunc) (gconstpointer site, + gsize capacity, gpointer user_data); - M680X_REG_CC, ///< M6800/1/2/3/9, M6301/9 - M680X_REG_DP, ///< M6809/M6309 - M680X_REG_MD, ///< M6309 +GUM_API GumControlFlowGraph * gum_control_flow_graph_new (gconstpointer entry, + cs_arch arch, cs_mode mode, GumControlFlowGraphFindRangeFunc find_range, + gpointer user_data); +/* + * Convenience constructor that resolves ranges via the platform's unwind + * tables and derives the disassembly mode from the native architecture, with + * the low bit selecting Thumb on 32-bit ARM. + */ +GUM_API GumControlFlowGraph * gum_control_flow_graph_new_for_function ( + gconstpointer entry_point); +GUM_API void gum_control_flow_graph_free (GumControlFlowGraph * self); + +GUM_API gboolean gum_control_flow_graph_dominates (GumControlFlowGraph * self, + gconstpointer a, gconstpointer b); + +GUM_API void gum_control_flow_graph_enumerate_dominating_sites ( + GumControlFlowGraph * self, gconstpointer target, + GumFoundDominatingSiteFunc func, gpointer user_data); + +GUM_API guint gum_control_flow_graph_get_num_blocks ( + GumControlFlowGraph * self); +GUM_API guint gum_control_flow_graph_get_entry_block ( + GumControlFlowGraph * self); +GUM_API guint gum_control_flow_graph_find_block_containing ( + GumControlFlowGraph * self, gconstpointer address); +GUM_API void gum_control_flow_graph_get_block_bounds ( + GumControlFlowGraph * self, guint index, GumAddress * start, + GumAddress * end); +GUM_API guint gum_control_flow_graph_get_block_immediate_dominator ( + GumControlFlowGraph * self, guint index); +GUM_API guint gum_control_flow_graph_get_block_successors ( + GumControlFlowGraph * self, guint index, const guint ** successors); +GUM_API guint gum_control_flow_graph_get_block_predecessors ( + GumControlFlowGraph * self, guint index, const guint ** predecessors); + +GUM_API const cs_insn * gum_control_flow_graph_find_instruction_containing ( + GumControlFlowGraph * self, gconstpointer address); - M680X_REG_HX, ///< M6808 - M680X_REG_H, ///< M6808 - M680X_REG_X, ///< M6800/1/2/3/9, M6301/9 - M680X_REG_Y, ///< M6809/M6309 - M680X_REG_S, ///< M6809/M6309 - M680X_REG_U, ///< M6809/M6309 - M680X_REG_V, ///< M6309 +G_END_DECLS - M680X_REG_Q, ///< M6309 +#endif +/* + * Copyright (C) 2017-2023 Ole André Vadla Ravnås + * Copyright (C) 2024 Francesco Tamagni + * + * Licence: wxWindows Library Licence, Version 3.1 + */ - M680X_REG_PC, ///< M6800/1/2/3/9, M6301/9 +#ifndef __GUM_CLOAK_H__ +#define __GUM_CLOAK_H__ - M680X_REG_TMP2, ///< CPU12 - M680X_REG_TMP3, ///< CPU12 +/* + * Copyright (C) 2008-2026 Ole André Vadla Ravnås + * Copyright (C) 2020-2024 Francesco Tamagni + * Copyright (C) 2023 Grant Douglas + * Copyright (C) 2024 Håvard Sørbø + * + * Licence: wxWindows Library Licence, Version 3.1 + */ - M680X_REG_ENDING, ///< <-- mark the end of the list of registers -} m680x_reg; +#ifndef __GUM_PROCESS_H__ +#define __GUM_PROCESS_H__ -/// Operand type for instruction's operands -typedef enum m680x_op_type { - M680X_OP_INVALID = 0, ///< = CS_OP_INVALID (Uninitialized). - M680X_OP_REGISTER, ///< = Register operand. - M680X_OP_IMMEDIATE, ///< = Immediate operand. - M680X_OP_INDEXED, ///< = Indexed addressing operand. - M680X_OP_EXTENDED, ///< = Extended addressing operand. - M680X_OP_DIRECT, ///< = Direct addressing operand. - M680X_OP_RELATIVE, ///< = Relative addressing operand. - M680X_OP_CONSTANT, ///< = constant operand (Displayed as number only). - ///< Used e.g. for a bit index or page number. -} m680x_op_type; +/* + * Copyright (C) 2008-2025 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -// Supported bit values for mem.idx.offset_bits -#define M680X_OFFSET_NONE 0 -#define M680X_OFFSET_BITS_5 5 -#define M680X_OFFSET_BITS_8 8 -#define M680X_OFFSET_BITS_9 9 -#define M680X_OFFSET_BITS_16 16 +#ifndef __GUM_MODULE_H__ +#define __GUM_MODULE_H__ -// Supported bit flags for mem.idx.flags -// These flags can be combined -#define M680X_IDX_INDIRECT 1 -#define M680X_IDX_NO_COMMA 2 -#define M680X_IDX_POST_INC_DEC 4 -/// Instruction's operand referring to indexed addressing -typedef struct m680x_op_idx { - m680x_reg base_reg; ///< base register (or M680X_REG_INVALID if - ///< irrelevant) - m680x_reg offset_reg; ///< offset register (or M680X_REG_INVALID if - ///< irrelevant) - int16_t offset; ///< 5-,8- or 16-bit offset. See also offset_bits. - uint16_t offset_addr; ///< = offset addr. if base_reg == M680X_REG_PC. - ///< calculated as offset + PC - uint8_t offset_bits; ///< offset width in bits for indexed addressing - int8_t inc_dec; ///< inc. or dec. value: - ///< 0: no inc-/decrement - ///< 1 .. 8: increment by 1 .. 8 - ///< -1 .. -8: decrement by 1 .. 8 - ///< if flag M680X_IDX_POST_INC_DEC set it is post - ///< inc-/decrement otherwise pre inc-/decrement - uint8_t flags; ///< 8-bit flags (see above) -} m680x_op_idx; +G_BEGIN_DECLS -/// Instruction's memory operand referring to relative addressing (Bcc/LBcc) -typedef struct m680x_op_rel { - uint16_t address; ///< The absolute address. - ///< calculated as PC + offset. PC is the first - ///< address after the instruction. - int16_t offset; ///< the offset/displacement value -} m680x_op_rel; +#define GUM_TYPE_MODULE (gum_module_get_type ()) +G_DECLARE_INTERFACE (GumModule, gum_module, GUM, MODULE, GObject) -/// Instruction's operand referring to extended addressing -typedef struct m680x_op_ext { - uint16_t address; ///< The absolute address - bool indirect; ///< true if extended indirect addressing -} m680x_op_ext; +typedef struct _GumImportDetails GumImportDetails; +typedef struct _GumExportDetails GumExportDetails; +typedef struct _GumSymbolDetails GumSymbolDetails; +typedef struct _GumSymbolSection GumSymbolSection; +typedef struct _GumSectionDetails GumSectionDetails; +typedef struct _GumDependencyDetails GumDependencyDetails; -/// Instruction operand -typedef struct cs_m680x_op { - m680x_op_type type; - union { - int32_t imm; ///< immediate value for IMM operand - m680x_reg reg; ///< register value for REG operand - m680x_op_idx idx; ///< Indexed addressing operand - m680x_op_rel rel; ///< Relative address. operand (Bcc/LBcc) - m680x_op_ext ext; ///< Extended address - uint8_t direct_addr; ///<, 2013-2018 */ +#define GUM_THREAD_ID_INVALID ((GumThreadId) -1) +#define GUM_TYPE_THREAD_DETAILS (gum_thread_details_get_type ()) + +G_BEGIN_DECLS + +typedef guint GumProcessId; +typedef gsize GumThreadId; +typedef struct _GumThreadDetails GumThreadDetails; +typedef struct _GumThreadEntrypoint GumThreadEntrypoint; +typedef struct _GumMallocRangeDetails GumMallocRangeDetails; + +typedef enum { + GUM_TEARDOWN_REQUIREMENT_FULL, + GUM_TEARDOWN_REQUIREMENT_MINIMAL +} GumTeardownRequirement; + +typedef enum { + GUM_CODE_SIGNING_OPTIONAL, + GUM_CODE_SIGNING_REQUIRED +} GumCodeSigningPolicy; + +typedef enum { + GUM_MODIFY_THREAD_FLAGS_NONE = 0, + GUM_MODIFY_THREAD_FLAGS_ABORT_SAFELY = (1 << 0), +} GumModifyThreadFlags; + +typedef enum { + GUM_THREAD_FLAGS_NAME = (1 << 0), + GUM_THREAD_FLAGS_STATE = (1 << 1), + GUM_THREAD_FLAGS_CPU_CONTEXT = (1 << 2), + GUM_THREAD_FLAGS_ENTRYPOINT_ROUTINE = (1 << 3), + GUM_THREAD_FLAGS_ENTRYPOINT_PARAMETER = (1 << 4), + + GUM_THREAD_FLAGS_NONE = 0, + GUM_THREAD_FLAGS_ALL = GUM_THREAD_FLAGS_NAME | + GUM_THREAD_FLAGS_STATE | + GUM_THREAD_FLAGS_CPU_CONTEXT | + GUM_THREAD_FLAGS_ENTRYPOINT_ROUTINE | + GUM_THREAD_FLAGS_ENTRYPOINT_PARAMETER, +} GumThreadFlags; + +typedef enum { + GUM_THREAD_RUNNING = 1, + GUM_THREAD_STOPPED, + GUM_THREAD_WAITING, + GUM_THREAD_UNINTERRUPTIBLE, + GUM_THREAD_HALTED +} GumThreadState; + +struct _GumThreadEntrypoint +{ + GumAddress routine; + GumAddress parameter; +}; + +struct _GumThreadDetails +{ + GumThreadFlags flags; + GumThreadId id; + const gchar * name; + GumThreadState state; + GumCpuContext cpu_context; + GumThreadEntrypoint entrypoint; +}; + +typedef enum { + GUM_WATCH_READ = (1 << 0), + GUM_WATCH_WRITE = (1 << 1), +} GumWatchConditions; + +struct _GumMallocRangeDetails +{ + const GumMemoryRange * range; +}; + +typedef void (* GumModifyThreadFunc) (GumThreadId thread_id, + GumCpuContext * cpu_context, gpointer user_data); +typedef gboolean (* GumFoundThreadFunc) (const GumThreadDetails * details, + gpointer user_data); +typedef gboolean (* GumFoundModuleFunc) (GumModule * module, + gpointer user_data); +typedef gboolean (* GumFoundMallocRangeFunc) ( + const GumMallocRangeDetails * details, gpointer user_data); + +GUM_API GumOS gum_process_get_native_os (void); +GUM_API GumTeardownRequirement gum_process_get_teardown_requirement (void); +GUM_API void gum_process_set_teardown_requirement ( + GumTeardownRequirement requirement); +GUM_API GumCodeSigningPolicy gum_process_get_code_signing_policy (void); +GUM_API void gum_process_set_code_signing_policy (GumCodeSigningPolicy policy); +GUM_API gboolean gum_process_is_debugger_attached (void); +GUM_API GumProcessId gum_process_get_id (void); +GUM_API GumThreadId gum_process_get_current_thread_id (void); +GUM_API gboolean gum_process_has_thread (GumThreadId thread_id); +GUM_API GumThreadDetails * gum_process_find_thread_by_id (GumThreadId thread_id, + GumThreadFlags flags); +GUM_API gboolean gum_process_modify_thread (GumThreadId thread_id, + GumModifyThreadFunc func, gpointer user_data, GumModifyThreadFlags flags); +GUM_API void gum_process_enumerate_threads (GumFoundThreadFunc func, + gpointer user_data, GumThreadFlags flags); +GUM_API GumModule * gum_process_get_main_module (void); +GUM_API GumModule * gum_process_get_libc_module (void); +GUM_API GumModule * gum_process_find_module_by_name (const gchar * name); +GUM_API GumModule * gum_process_find_module_by_address (GumAddress address); +GUM_API gboolean gum_process_find_function_range (gconstpointer address, + GumMemoryRange * range); +GUM_API void gum_process_enumerate_modules (GumFoundModuleFunc func, + gpointer user_data); +GUM_API void gum_process_enumerate_ranges (GumPageProtection prot, + GumFoundRangeFunc func, gpointer user_data); +GUM_API void gum_process_enumerate_malloc_ranges ( + GumFoundMallocRangeFunc func, gpointer user_data); +GUM_API guint gum_thread_try_get_ranges (GumMemoryRange * ranges, + guint max_length); +GUM_API gint gum_thread_get_system_error (void); +GUM_API void gum_thread_set_system_error (gint value); +GUM_API gboolean gum_thread_suspend (GumThreadId thread_id, GError ** error); +GUM_API gboolean gum_thread_resume (GumThreadId thread_id, GError ** error); +GUM_API gboolean gum_thread_set_hardware_breakpoint (GumThreadId thread_id, + guint breakpoint_id, GumAddress address, GError ** error); +GUM_API gboolean gum_thread_unset_hardware_breakpoint (GumThreadId thread_id, + guint breakpoint_id, GError ** error); +GUM_API gboolean gum_thread_set_hardware_watchpoint (GumThreadId thread_id, + guint watchpoint_id, GumAddress address, gsize size, GumWatchConditions wc, + GError ** error); +GUM_API gboolean gum_thread_unset_hardware_watchpoint (GumThreadId thread_id, + guint watchpoint_id, GError ** error); + +GUM_API const gchar * gum_code_signing_policy_to_string ( + GumCodeSigningPolicy policy); + +GUM_API GType gum_thread_details_get_type (void) G_GNUC_CONST; +GUM_API GumThreadDetails * gum_thread_details_copy ( + const GumThreadDetails * details); +GUM_API void gum_thread_details_free (GumThreadDetails * details); + +G_END_DECLS -#ifdef __cplusplus -extern "C" { #endif +G_BEGIN_DECLS + +typedef struct _GumCloak GumCloak; + +typedef gboolean (* GumCloakFoundThreadFunc) (GumThreadId id, + gpointer user_data); +typedef gboolean (* GumCloakFoundRangeFunc) (const GumMemoryRange * range, + gpointer user_data); +typedef gboolean (* GumCloakFoundFDFunc) (gint fd, gpointer user_data); +typedef void (* GumCloakLockedFunc) (gpointer user_data); + +GUM_API void gum_cloak_add_thread (GumThreadId id); +GUM_API void gum_cloak_remove_thread (GumThreadId id); +GUM_API gboolean gum_cloak_has_thread (GumThreadId id); +GUM_API void gum_cloak_enumerate_threads (GumCloakFoundThreadFunc func, + gpointer user_data); + +GUM_API void gum_cloak_add_range (const GumMemoryRange * range); +GUM_API void gum_cloak_remove_range (const GumMemoryRange * range); +GUM_API gboolean gum_cloak_has_range_containing (GumAddress address); +GUM_API GArray * gum_cloak_clip_range (const GumMemoryRange * range); +GUM_API void gum_cloak_enumerate_ranges (GumCloakFoundRangeFunc func, + gpointer user_data); + +GUM_API void gum_cloak_add_file_descriptor (gint fd); +GUM_API void gum_cloak_remove_file_descriptor (gint fd); +GUM_API gboolean gum_cloak_has_file_descriptor (gint fd); +GUM_API void gum_cloak_enumerate_file_descriptors (GumCloakFoundFDFunc func, + gpointer user_data); + +GUM_API void gum_cloak_with_lock_held (GumCloakLockedFunc func, + gpointer user_data); +GUM_API gboolean gum_cloak_is_locked (void); + +G_END_DECLS -#ifdef _MSC_VER -#pragma warning(disable:4201) #endif +/* + * Copyright (C) 2010-2021 Ole André Vadla Ravnås + * Copyright (C) 2025 Francesco Tamagni + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -/// Instruction structure -typedef struct cs_evm { - unsigned char pop; ///< number of items popped from the stack - unsigned char push; ///< number of items pushed into the stack - unsigned int fee; ///< gas fee for the instruction -} cs_evm; +#ifndef __GUM_CODE_ALLOCATOR_H__ +#define __GUM_CODE_ALLOCATOR_H__ -/// EVM instruction -typedef enum evm_insn { - EVM_INS_STOP = 0, - EVM_INS_ADD = 1, - EVM_INS_MUL = 2, - EVM_INS_SUB = 3, - EVM_INS_DIV = 4, - EVM_INS_SDIV = 5, - EVM_INS_MOD = 6, - EVM_INS_SMOD = 7, - EVM_INS_ADDMOD = 8, - EVM_INS_MULMOD = 9, - EVM_INS_EXP = 10, - EVM_INS_SIGNEXTEND = 11, - EVM_INS_LT = 16, - EVM_INS_GT = 17, - EVM_INS_SLT = 18, - EVM_INS_SGT = 19, - EVM_INS_EQ = 20, - EVM_INS_ISZERO = 21, - EVM_INS_AND = 22, - EVM_INS_OR = 23, - EVM_INS_XOR = 24, - EVM_INS_NOT = 25, - EVM_INS_BYTE = 26, - EVM_INS_SHA3 = 32, - EVM_INS_ADDRESS = 48, - EVM_INS_BALANCE = 49, - EVM_INS_ORIGIN = 50, - EVM_INS_CALLER = 51, - EVM_INS_CALLVALUE = 52, - EVM_INS_CALLDATALOAD = 53, - EVM_INS_CALLDATASIZE = 54, - EVM_INS_CALLDATACOPY = 55, - EVM_INS_CODESIZE = 56, - EVM_INS_CODECOPY = 57, - EVM_INS_GASPRICE = 58, - EVM_INS_EXTCODESIZE = 59, - EVM_INS_EXTCODECOPY = 60, - EVM_INS_RETURNDATASIZE = 61, - EVM_INS_RETURNDATACOPY = 62, - EVM_INS_BLOCKHASH = 64, - EVM_INS_COINBASE = 65, - EVM_INS_TIMESTAMP = 66, - EVM_INS_NUMBER = 67, - EVM_INS_DIFFICULTY = 68, - EVM_INS_GASLIMIT = 69, - EVM_INS_POP = 80, - EVM_INS_MLOAD = 81, - EVM_INS_MSTORE = 82, - EVM_INS_MSTORE8 = 83, - EVM_INS_SLOAD = 84, - EVM_INS_SSTORE = 85, - EVM_INS_JUMP = 86, - EVM_INS_JUMPI = 87, - EVM_INS_PC = 88, - EVM_INS_MSIZE = 89, - EVM_INS_GAS = 90, - EVM_INS_JUMPDEST = 91, - EVM_INS_PUSH1 = 96, - EVM_INS_PUSH2 = 97, - EVM_INS_PUSH3 = 98, - EVM_INS_PUSH4 = 99, - EVM_INS_PUSH5 = 100, - EVM_INS_PUSH6 = 101, - EVM_INS_PUSH7 = 102, - EVM_INS_PUSH8 = 103, - EVM_INS_PUSH9 = 104, - EVM_INS_PUSH10 = 105, - EVM_INS_PUSH11 = 106, - EVM_INS_PUSH12 = 107, - EVM_INS_PUSH13 = 108, - EVM_INS_PUSH14 = 109, - EVM_INS_PUSH15 = 110, - EVM_INS_PUSH16 = 111, - EVM_INS_PUSH17 = 112, - EVM_INS_PUSH18 = 113, - EVM_INS_PUSH19 = 114, - EVM_INS_PUSH20 = 115, - EVM_INS_PUSH21 = 116, - EVM_INS_PUSH22 = 117, - EVM_INS_PUSH23 = 118, - EVM_INS_PUSH24 = 119, - EVM_INS_PUSH25 = 120, - EVM_INS_PUSH26 = 121, - EVM_INS_PUSH27 = 122, - EVM_INS_PUSH28 = 123, - EVM_INS_PUSH29 = 124, - EVM_INS_PUSH30 = 125, - EVM_INS_PUSH31 = 126, - EVM_INS_PUSH32 = 127, - EVM_INS_DUP1 = 128, - EVM_INS_DUP2 = 129, - EVM_INS_DUP3 = 130, - EVM_INS_DUP4 = 131, - EVM_INS_DUP5 = 132, - EVM_INS_DUP6 = 133, - EVM_INS_DUP7 = 134, - EVM_INS_DUP8 = 135, - EVM_INS_DUP9 = 136, - EVM_INS_DUP10 = 137, - EVM_INS_DUP11 = 138, - EVM_INS_DUP12 = 139, - EVM_INS_DUP13 = 140, - EVM_INS_DUP14 = 141, - EVM_INS_DUP15 = 142, - EVM_INS_DUP16 = 143, - EVM_INS_SWAP1 = 144, - EVM_INS_SWAP2 = 145, - EVM_INS_SWAP3 = 146, - EVM_INS_SWAP4 = 147, - EVM_INS_SWAP5 = 148, - EVM_INS_SWAP6 = 149, - EVM_INS_SWAP7 = 150, - EVM_INS_SWAP8 = 151, - EVM_INS_SWAP9 = 152, - EVM_INS_SWAP10 = 153, - EVM_INS_SWAP11 = 154, - EVM_INS_SWAP12 = 155, - EVM_INS_SWAP13 = 156, - EVM_INS_SWAP14 = 157, - EVM_INS_SWAP15 = 158, - EVM_INS_SWAP16 = 159, - EVM_INS_LOG0 = 160, - EVM_INS_LOG1 = 161, - EVM_INS_LOG2 = 162, - EVM_INS_LOG3 = 163, - EVM_INS_LOG4 = 164, - EVM_INS_CREATE = 240, - EVM_INS_CALL = 241, - EVM_INS_CALLCODE = 242, - EVM_INS_RETURN = 243, - EVM_INS_DELEGATECALL = 244, - EVM_INS_CALLBLACKBOX = 245, - EVM_INS_STATICCALL = 250, - EVM_INS_REVERT = 253, - EVM_INS_SUICIDE = 255, - EVM_INS_INVALID = 512, - EVM_INS_ENDING, // <-- mark the end of the list of instructions -} evm_insn; +#define GUM_TYPE_CODE_SLICE (gum_code_slice_get_type ()) +#define GUM_TYPE_CODE_DEFLECTOR (gum_code_deflector_get_type ()) -/// Group of EVM instructions -typedef enum evm_insn_group { - EVM_GRP_INVALID = 0, ///< = CS_GRP_INVALID +G_BEGIN_DECLS - EVM_GRP_JUMP, ///< all jump instructions +typedef struct _GumCodeAllocator GumCodeAllocator; +typedef struct _GumCodeSlice GumCodeSlice; +typedef struct _GumCodeDeflector GumCodeDeflector; + +struct _GumCodeAllocator +{ + gsize slice_size; + gsize pages_per_batch; + gsize slices_per_batch; + gsize pages_metadata_size; + + GSList * uncommitted_pages; + GHashTable * dirty_pages; + GList * free_slices; + + GSList * dispatchers; +}; + +struct _GumCodeSlice +{ + gpointer data; + gpointer pc; + guint size; + + /*< private >*/ + gint ref_count; +}; + +struct _GumCodeDeflector +{ + gpointer return_address; + gpointer target; + gpointer trampoline; + + /*< private >*/ + gint ref_count; +}; + +GUM_API void gum_code_allocator_init (GumCodeAllocator * allocator, + gsize slice_size); +GUM_API void gum_code_allocator_free (GumCodeAllocator * allocator); + +GUM_API GumCodeSlice * gum_code_allocator_alloc_slice (GumCodeAllocator * self); +GUM_API GumCodeSlice * gum_code_allocator_try_alloc_slice_near ( + GumCodeAllocator * self, const GumAddressSpec * spec, gsize alignment); +GUM_API void gum_code_allocator_commit (GumCodeAllocator * self); +GUM_API GType gum_code_slice_get_type (void) G_GNUC_CONST; +GUM_API GumCodeSlice * gum_code_slice_ref (GumCodeSlice * slice); +GUM_API void gum_code_slice_unref (GumCodeSlice * slice); + +GUM_API GumCodeDeflector * gum_code_allocator_alloc_deflector ( + GumCodeAllocator * self, const GumAddressSpec * caller, + gpointer return_address, gpointer target, gboolean dedicated); +GUM_API GType gum_code_deflector_get_type (void) G_GNUC_CONST; +GUM_API GumCodeDeflector * gum_code_deflector_ref ( + GumCodeDeflector * deflector); +GUM_API void gum_code_deflector_unref (GumCodeDeflector * deflector); + +G_END_DECLS + +#endif +/* + * Copyright (C) 2016-2019 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +#ifndef __GUM_CODE_SEGMENT_H__ +#define __GUM_CODE_SEGMENT_H__ - EVM_GRP_MATH = 8, ///< math instructions - EVM_GRP_STACK_WRITE, ///< instructions write to stack - EVM_GRP_STACK_READ, ///< instructions read from stack - EVM_GRP_MEM_WRITE, ///< instructions write to memory - EVM_GRP_MEM_READ, ///< instructions read from memory - EVM_GRP_STORE_WRITE, ///< instructions write to storage - EVM_GRP_STORE_READ, ///< instructions read from storage - EVM_GRP_HALT, ///< instructions halt execution - EVM_GRP_ENDING, ///< <-- mark the end of the list of groups -} evm_insn_group; +G_BEGIN_DECLS -#ifdef __cplusplus -} -#endif +typedef struct _GumCodeSegment GumCodeSegment; -#endif -#ifndef CAPSTONE_RISCV_H -#define CAPSTONE_RISCV_H +GUM_API gboolean gum_code_segment_is_supported (void); -/* Capstone Disassembly Engine */ -/* RISC-V Backend By Rodrigo Cortes Porto & - Shawn Chang , HardenedLinux@2018 */ +GUM_API GumCodeSegment * gum_code_segment_new (gsize size, + const GumAddressSpec * spec); +GUM_API void gum_code_segment_free (GumCodeSegment * segment); -#ifdef __cplusplus -extern "C" { -#endif +GUM_API gpointer gum_code_segment_get_address (GumCodeSegment * self); +GUM_API gsize gum_code_segment_get_size (GumCodeSegment * self); +GUM_API gsize gum_code_segment_get_virtual_size (GumCodeSegment * self); -#if !defined(_MSC_VER) || !defined(_KERNEL_MODE) -#include -#endif +GUM_API void gum_code_segment_realize (GumCodeSegment * self); +GUM_API void gum_code_segment_map (GumCodeSegment * self, gsize source_offset, + gsize source_size, gpointer target_address); +GUM_API gboolean gum_code_segment_mark (gpointer code, gsize size, + GError ** error); -// GCC MIPS toolchain has a default macro called "mips" which breaks -// compilation -//#undef riscv +G_END_DECLS -#ifdef _MSC_VER -#pragma warning(disable:4201) #endif +/* + * Copyright (C) 2021-2022 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -//> Operand type for instruction's operands -typedef enum riscv_op_type { - RISCV_OP_INVALID = 0, // = CS_OP_INVALID (Uninitialized). - RISCV_OP_REG, // = CS_OP_REG (Register operand). - RISCV_OP_IMM, // = CS_OP_IMM (Immediate operand). - RISCV_OP_MEM, // = CS_OP_MEM (Memory operand). -} riscv_op_type; +#ifndef __GUM_DARWIN_GRAFTER_H__ +#define __GUM_DARWIN_GRAFTER_H__ -// Instruction's operand referring to memory -// This is associated with RISCV_OP_MEM operand type above -typedef struct riscv_op_mem { - unsigned int base; // base register - int64_t disp; // displacement/offset value -} riscv_op_mem; -// Instruction operand -typedef struct cs_riscv_op { - riscv_op_type type; // operand type - union { - unsigned int reg; // register value for REG operand - int64_t imm; // immediate value for IMM operand - riscv_op_mem mem; // base/disp value for MEM operand - }; -} cs_riscv_op; +G_BEGIN_DECLS -// Instruction structure -typedef struct cs_riscv { - // Does this instruction need effective address or not. - bool need_effective_addr; - // Number of operands of this instruction, - // or 0 when instruction has no operand. - uint8_t op_count; - cs_riscv_op operands[8]; // operands for this instruction. -} cs_riscv; +typedef enum { + GUM_DARWIN_GRAFTER_FLAGS_NONE = 0, + GUM_DARWIN_GRAFTER_FLAGS_INGEST_FUNCTION_STARTS = (1 << 0), + GUM_DARWIN_GRAFTER_FLAGS_INGEST_IMPORTS = (1 << 1), + GUM_DARWIN_GRAFTER_FLAGS_TRANSFORM_LAZY_BINDS = (1 << 2), +} GumDarwinGrafterFlags; -//> RISCV registers -typedef enum riscv_reg { - RISCV_REG_INVALID = 0, - //> General purpose registers - RISCV_REG_X0, // "zero" - RISCV_REG_ZERO = RISCV_REG_X0, // "zero" - RISCV_REG_X1, // "ra" - RISCV_REG_RA = RISCV_REG_X1, // "ra" - RISCV_REG_X2, // "sp" - RISCV_REG_SP = RISCV_REG_X2, // "sp" - RISCV_REG_X3, // "gp" - RISCV_REG_GP = RISCV_REG_X3, // "gp" - RISCV_REG_X4, // "tp" - RISCV_REG_TP = RISCV_REG_X4, // "tp" - RISCV_REG_X5, // "t0" - RISCV_REG_T0 = RISCV_REG_X5, // "t0" - RISCV_REG_X6, // "t1" - RISCV_REG_T1 = RISCV_REG_X6, // "t1" - RISCV_REG_X7, // "t2" - RISCV_REG_T2 = RISCV_REG_X7, // "t2" - RISCV_REG_X8, // "s0/fp" - RISCV_REG_S0 = RISCV_REG_X8, // "s0" - RISCV_REG_FP = RISCV_REG_X8, // "fp" - RISCV_REG_X9, // "s1" - RISCV_REG_S1 = RISCV_REG_X9, // "s1" - RISCV_REG_X10, // "a0" - RISCV_REG_A0 = RISCV_REG_X10, // "a0" - RISCV_REG_X11, // "a1" - RISCV_REG_A1 = RISCV_REG_X11, // "a1" - RISCV_REG_X12, // "a2" - RISCV_REG_A2 = RISCV_REG_X12, // "a2" - RISCV_REG_X13, // "a3" - RISCV_REG_A3 = RISCV_REG_X13, // "a3" - RISCV_REG_X14, // "a4" - RISCV_REG_A4 = RISCV_REG_X14, // "a4" - RISCV_REG_X15, // "a5" - RISCV_REG_A5 = RISCV_REG_X15, // "a5" - RISCV_REG_X16, // "a6" - RISCV_REG_A6 = RISCV_REG_X16, // "a6" - RISCV_REG_X17, // "a7" - RISCV_REG_A7 = RISCV_REG_X17, // "a7" - RISCV_REG_X18, // "s2" - RISCV_REG_S2 = RISCV_REG_X18, // "s2" - RISCV_REG_X19, // "s3" - RISCV_REG_S3 = RISCV_REG_X19, // "s3" - RISCV_REG_X20, // "s4" - RISCV_REG_S4 = RISCV_REG_X20, // "s4" - RISCV_REG_X21, // "s5" - RISCV_REG_S5 = RISCV_REG_X21, // "s5" - RISCV_REG_X22, // "s6" - RISCV_REG_S6 = RISCV_REG_X22, // "s6" - RISCV_REG_X23, // "s7" - RISCV_REG_S7 = RISCV_REG_X23, // "s7" - RISCV_REG_X24, // "s8" - RISCV_REG_S8 = RISCV_REG_X24, // "s8" - RISCV_REG_X25, // "s9" - RISCV_REG_S9 = RISCV_REG_X25, // "s9" - RISCV_REG_X26, // "s10" - RISCV_REG_S10 = RISCV_REG_X26, // "s10" - RISCV_REG_X27, // "s11" - RISCV_REG_S11 = RISCV_REG_X27, // "s11" - RISCV_REG_X28, // "t3" - RISCV_REG_T3 = RISCV_REG_X28, // "t3" - RISCV_REG_X29, // "t4" - RISCV_REG_T4 = RISCV_REG_X29, // "t4" - RISCV_REG_X30, // "t5" - RISCV_REG_T5 = RISCV_REG_X30, // "t5" - RISCV_REG_X31, // "t6" - RISCV_REG_T6 = RISCV_REG_X31, // "t6" - - //> Floating-point registers - RISCV_REG_F0_32, // "ft0" - RISCV_REG_F0_64, // "ft0" - RISCV_REG_F1_32, // "ft1" - RISCV_REG_F1_64, // "ft1" - RISCV_REG_F2_32, // "ft2" - RISCV_REG_F2_64, // "ft2" - RISCV_REG_F3_32, // "ft3" - RISCV_REG_F3_64, // "ft3" - RISCV_REG_F4_32, // "ft4" - RISCV_REG_F4_64, // "ft4" - RISCV_REG_F5_32, // "ft5" - RISCV_REG_F5_64, // "ft5" - RISCV_REG_F6_32, // "ft6" - RISCV_REG_F6_64, // "ft6" - RISCV_REG_F7_32, // "ft7" - RISCV_REG_F7_64, // "ft7" - RISCV_REG_F8_32, // "fs0" - RISCV_REG_F8_64, // "fs0" - RISCV_REG_F9_32, // "fs1" - RISCV_REG_F9_64, // "fs1" - RISCV_REG_F10_32, // "fa0" - RISCV_REG_F10_64, // "fa0" - RISCV_REG_F11_32, // "fa1" - RISCV_REG_F11_64, // "fa1" - RISCV_REG_F12_32, // "fa2" - RISCV_REG_F12_64, // "fa2" - RISCV_REG_F13_32, // "fa3" - RISCV_REG_F13_64, // "fa3" - RISCV_REG_F14_32, // "fa4" - RISCV_REG_F14_64, // "fa4" - RISCV_REG_F15_32, // "fa5" - RISCV_REG_F15_64, // "fa5" - RISCV_REG_F16_32, // "fa6" - RISCV_REG_F16_64, // "fa6" - RISCV_REG_F17_32, // "fa7" - RISCV_REG_F17_64, // "fa7" - RISCV_REG_F18_32, // "fs2" - RISCV_REG_F18_64, // "fs2" - RISCV_REG_F19_32, // "fs3" - RISCV_REG_F19_64, // "fs3" - RISCV_REG_F20_32, // "fs4" - RISCV_REG_F20_64, // "fs4" - RISCV_REG_F21_32, // "fs5" - RISCV_REG_F21_64, // "fs5" - RISCV_REG_F22_32, // "fs6" - RISCV_REG_F22_64, // "fs6" - RISCV_REG_F23_32, // "fs7" - RISCV_REG_F23_64, // "fs7" - RISCV_REG_F24_32, // "fs8" - RISCV_REG_F24_64, // "fs8" - RISCV_REG_F25_32, // "fs9" - RISCV_REG_F25_64, // "fs9" - RISCV_REG_F26_32, // "fs10" - RISCV_REG_F26_64, // "fs10" - RISCV_REG_F27_32, // "fs11" - RISCV_REG_F27_64, // "fs11" - RISCV_REG_F28_32, // "ft8" - RISCV_REG_F28_64, // "ft8" - RISCV_REG_F29_32, // "ft9" - RISCV_REG_F29_64, // "ft9" - RISCV_REG_F30_32, // "ft10" - RISCV_REG_F30_64, // "ft10" - RISCV_REG_F31_32, // "ft11" - RISCV_REG_F31_64, // "ft11" - - RISCV_REG_ENDING, // <-- mark the end of the list or registers -} riscv_reg; +#define GUM_TYPE_DARWIN_GRAFTER (gum_darwin_grafter_get_type ()) +G_DECLARE_FINAL_TYPE (GumDarwinGrafter, gum_darwin_grafter, GUM, DARWIN_GRAFTER, + GObject) -//> RISCV instruction -typedef enum riscv_insn { - RISCV_INS_INVALID = 0, +GUM_API GumDarwinGrafter * gum_darwin_grafter_new_from_file ( + const gchar * path, GumDarwinGrafterFlags flags); - RISCV_INS_ADD, - RISCV_INS_ADDI, - RISCV_INS_ADDIW, - RISCV_INS_ADDW, - RISCV_INS_AMOADD_D, - RISCV_INS_AMOADD_D_AQ, - RISCV_INS_AMOADD_D_AQ_RL, - RISCV_INS_AMOADD_D_RL, - RISCV_INS_AMOADD_W, - RISCV_INS_AMOADD_W_AQ, - RISCV_INS_AMOADD_W_AQ_RL, - RISCV_INS_AMOADD_W_RL, - RISCV_INS_AMOAND_D, - RISCV_INS_AMOAND_D_AQ, - RISCV_INS_AMOAND_D_AQ_RL, - RISCV_INS_AMOAND_D_RL, - RISCV_INS_AMOAND_W, - RISCV_INS_AMOAND_W_AQ, - RISCV_INS_AMOAND_W_AQ_RL, - RISCV_INS_AMOAND_W_RL, - RISCV_INS_AMOMAXU_D, - RISCV_INS_AMOMAXU_D_AQ, - RISCV_INS_AMOMAXU_D_AQ_RL, - RISCV_INS_AMOMAXU_D_RL, - RISCV_INS_AMOMAXU_W, - RISCV_INS_AMOMAXU_W_AQ, - RISCV_INS_AMOMAXU_W_AQ_RL, - RISCV_INS_AMOMAXU_W_RL, - RISCV_INS_AMOMAX_D, - RISCV_INS_AMOMAX_D_AQ, - RISCV_INS_AMOMAX_D_AQ_RL, - RISCV_INS_AMOMAX_D_RL, - RISCV_INS_AMOMAX_W, - RISCV_INS_AMOMAX_W_AQ, - RISCV_INS_AMOMAX_W_AQ_RL, - RISCV_INS_AMOMAX_W_RL, - RISCV_INS_AMOMINU_D, - RISCV_INS_AMOMINU_D_AQ, - RISCV_INS_AMOMINU_D_AQ_RL, - RISCV_INS_AMOMINU_D_RL, - RISCV_INS_AMOMINU_W, - RISCV_INS_AMOMINU_W_AQ, - RISCV_INS_AMOMINU_W_AQ_RL, - RISCV_INS_AMOMINU_W_RL, - RISCV_INS_AMOMIN_D, - RISCV_INS_AMOMIN_D_AQ, - RISCV_INS_AMOMIN_D_AQ_RL, - RISCV_INS_AMOMIN_D_RL, - RISCV_INS_AMOMIN_W, - RISCV_INS_AMOMIN_W_AQ, - RISCV_INS_AMOMIN_W_AQ_RL, - RISCV_INS_AMOMIN_W_RL, - RISCV_INS_AMOOR_D, - RISCV_INS_AMOOR_D_AQ, - RISCV_INS_AMOOR_D_AQ_RL, - RISCV_INS_AMOOR_D_RL, - RISCV_INS_AMOOR_W, - RISCV_INS_AMOOR_W_AQ, - RISCV_INS_AMOOR_W_AQ_RL, - RISCV_INS_AMOOR_W_RL, - RISCV_INS_AMOSWAP_D, - RISCV_INS_AMOSWAP_D_AQ, - RISCV_INS_AMOSWAP_D_AQ_RL, - RISCV_INS_AMOSWAP_D_RL, - RISCV_INS_AMOSWAP_W, - RISCV_INS_AMOSWAP_W_AQ, - RISCV_INS_AMOSWAP_W_AQ_RL, - RISCV_INS_AMOSWAP_W_RL, - RISCV_INS_AMOXOR_D, - RISCV_INS_AMOXOR_D_AQ, - RISCV_INS_AMOXOR_D_AQ_RL, - RISCV_INS_AMOXOR_D_RL, - RISCV_INS_AMOXOR_W, - RISCV_INS_AMOXOR_W_AQ, - RISCV_INS_AMOXOR_W_AQ_RL, - RISCV_INS_AMOXOR_W_RL, - RISCV_INS_AND, - RISCV_INS_ANDI, - RISCV_INS_AUIPC, - RISCV_INS_BEQ, - RISCV_INS_BGE, - RISCV_INS_BGEU, - RISCV_INS_BLT, - RISCV_INS_BLTU, - RISCV_INS_BNE, - RISCV_INS_CSRRC, - RISCV_INS_CSRRCI, - RISCV_INS_CSRRS, - RISCV_INS_CSRRSI, - RISCV_INS_CSRRW, - RISCV_INS_CSRRWI, - RISCV_INS_C_ADD, - RISCV_INS_C_ADDI, - RISCV_INS_C_ADDI16SP, - RISCV_INS_C_ADDI4SPN, - RISCV_INS_C_ADDIW, - RISCV_INS_C_ADDW, - RISCV_INS_C_AND, - RISCV_INS_C_ANDI, - RISCV_INS_C_BEQZ, - RISCV_INS_C_BNEZ, - RISCV_INS_C_EBREAK, - RISCV_INS_C_FLD, - RISCV_INS_C_FLDSP, - RISCV_INS_C_FLW, - RISCV_INS_C_FLWSP, - RISCV_INS_C_FSD, - RISCV_INS_C_FSDSP, - RISCV_INS_C_FSW, - RISCV_INS_C_FSWSP, - RISCV_INS_C_J, - RISCV_INS_C_JAL, - RISCV_INS_C_JALR, - RISCV_INS_C_JR, - RISCV_INS_C_LD, - RISCV_INS_C_LDSP, - RISCV_INS_C_LI, - RISCV_INS_C_LUI, - RISCV_INS_C_LW, - RISCV_INS_C_LWSP, - RISCV_INS_C_MV, - RISCV_INS_C_NOP, - RISCV_INS_C_OR, - RISCV_INS_C_SD, - RISCV_INS_C_SDSP, - RISCV_INS_C_SLLI, - RISCV_INS_C_SRAI, - RISCV_INS_C_SRLI, - RISCV_INS_C_SUB, - RISCV_INS_C_SUBW, - RISCV_INS_C_SW, - RISCV_INS_C_SWSP, - RISCV_INS_C_UNIMP, - RISCV_INS_C_XOR, - RISCV_INS_DIV, - RISCV_INS_DIVU, - RISCV_INS_DIVUW, - RISCV_INS_DIVW, - RISCV_INS_EBREAK, - RISCV_INS_ECALL, - RISCV_INS_FADD_D, - RISCV_INS_FADD_S, - RISCV_INS_FCLASS_D, - RISCV_INS_FCLASS_S, - RISCV_INS_FCVT_D_L, - RISCV_INS_FCVT_D_LU, - RISCV_INS_FCVT_D_S, - RISCV_INS_FCVT_D_W, - RISCV_INS_FCVT_D_WU, - RISCV_INS_FCVT_LU_D, - RISCV_INS_FCVT_LU_S, - RISCV_INS_FCVT_L_D, - RISCV_INS_FCVT_L_S, - RISCV_INS_FCVT_S_D, - RISCV_INS_FCVT_S_L, - RISCV_INS_FCVT_S_LU, - RISCV_INS_FCVT_S_W, - RISCV_INS_FCVT_S_WU, - RISCV_INS_FCVT_WU_D, - RISCV_INS_FCVT_WU_S, - RISCV_INS_FCVT_W_D, - RISCV_INS_FCVT_W_S, - RISCV_INS_FDIV_D, - RISCV_INS_FDIV_S, - RISCV_INS_FENCE, - RISCV_INS_FENCE_I, - RISCV_INS_FENCE_TSO, - RISCV_INS_FEQ_D, - RISCV_INS_FEQ_S, - RISCV_INS_FLD, - RISCV_INS_FLE_D, - RISCV_INS_FLE_S, - RISCV_INS_FLT_D, - RISCV_INS_FLT_S, - RISCV_INS_FLW, - RISCV_INS_FMADD_D, - RISCV_INS_FMADD_S, - RISCV_INS_FMAX_D, - RISCV_INS_FMAX_S, - RISCV_INS_FMIN_D, - RISCV_INS_FMIN_S, - RISCV_INS_FMSUB_D, - RISCV_INS_FMSUB_S, - RISCV_INS_FMUL_D, - RISCV_INS_FMUL_S, - RISCV_INS_FMV_D_X, - RISCV_INS_FMV_W_X, - RISCV_INS_FMV_X_D, - RISCV_INS_FMV_X_W, - RISCV_INS_FNMADD_D, - RISCV_INS_FNMADD_S, - RISCV_INS_FNMSUB_D, - RISCV_INS_FNMSUB_S, - RISCV_INS_FSD, - RISCV_INS_FSGNJN_D, - RISCV_INS_FSGNJN_S, - RISCV_INS_FSGNJX_D, - RISCV_INS_FSGNJX_S, - RISCV_INS_FSGNJ_D, - RISCV_INS_FSGNJ_S, - RISCV_INS_FSQRT_D, - RISCV_INS_FSQRT_S, - RISCV_INS_FSUB_D, - RISCV_INS_FSUB_S, - RISCV_INS_FSW, - RISCV_INS_JAL, - RISCV_INS_JALR, - RISCV_INS_LB, - RISCV_INS_LBU, - RISCV_INS_LD, - RISCV_INS_LH, - RISCV_INS_LHU, - RISCV_INS_LR_D, - RISCV_INS_LR_D_AQ, - RISCV_INS_LR_D_AQ_RL, - RISCV_INS_LR_D_RL, - RISCV_INS_LR_W, - RISCV_INS_LR_W_AQ, - RISCV_INS_LR_W_AQ_RL, - RISCV_INS_LR_W_RL, - RISCV_INS_LUI, - RISCV_INS_LW, - RISCV_INS_LWU, - RISCV_INS_MRET, - RISCV_INS_MUL, - RISCV_INS_MULH, - RISCV_INS_MULHSU, - RISCV_INS_MULHU, - RISCV_INS_MULW, - RISCV_INS_OR, - RISCV_INS_ORI, - RISCV_INS_REM, - RISCV_INS_REMU, - RISCV_INS_REMUW, - RISCV_INS_REMW, - RISCV_INS_SB, - RISCV_INS_SC_D, - RISCV_INS_SC_D_AQ, - RISCV_INS_SC_D_AQ_RL, - RISCV_INS_SC_D_RL, - RISCV_INS_SC_W, - RISCV_INS_SC_W_AQ, - RISCV_INS_SC_W_AQ_RL, - RISCV_INS_SC_W_RL, - RISCV_INS_SD, - RISCV_INS_SFENCE_VMA, - RISCV_INS_SH, - RISCV_INS_SLL, - RISCV_INS_SLLI, - RISCV_INS_SLLIW, - RISCV_INS_SLLW, - RISCV_INS_SLT, - RISCV_INS_SLTI, - RISCV_INS_SLTIU, - RISCV_INS_SLTU, - RISCV_INS_SRA, - RISCV_INS_SRAI, - RISCV_INS_SRAIW, - RISCV_INS_SRAW, - RISCV_INS_SRET, - RISCV_INS_SRL, - RISCV_INS_SRLI, - RISCV_INS_SRLIW, - RISCV_INS_SRLW, - RISCV_INS_SUB, - RISCV_INS_SUBW, - RISCV_INS_SW, - RISCV_INS_UNIMP, - RISCV_INS_URET, - RISCV_INS_WFI, - RISCV_INS_XOR, - RISCV_INS_XORI, - - RISCV_INS_ENDING, -} riscv_insn; +GUM_API void gum_darwin_grafter_add (GumDarwinGrafter * self, + guint32 code_offset); -//> Group of RISCV instructions -typedef enum riscv_insn_group { - RISCV_GRP_INVALID = 0, ///< = CS_GRP_INVALID +GUM_API gboolean gum_darwin_grafter_graft (GumDarwinGrafter * self, + GError ** error); - // Generic groups - // all jump instructions (conditional+direct+indirect jumps) - RISCV_GRP_JUMP, ///< = CS_GRP_JUMP - // all call instructions - RISCV_GRP_CALL, ///< = CS_GRP_CALL - // all return instructions - RISCV_GRP_RET, ///< = CS_GRP_RET - // all interrupt instructions (int+syscall) - RISCV_GRP_INT, ///< = CS_GRP_INT - // all interrupt return instructions - RISCV_GRP_IRET, ///< = CS_GRP_IRET - // all privileged instructions - RISCV_GRP_PRIVILEGE, ///< = CS_GRP_PRIVILEGE - // all relative branching instructions - RISCV_GRP_BRANCH_RELATIVE, ///< = CS_GRP_BRANCH_RELATIVE - - // Architecture-specific groups - RISCV_GRP_ISRV32 = 128, - RISCV_GRP_ISRV64, - RISCV_GRP_HASSTDEXTA, - RISCV_GRP_HASSTDEXTC, - RISCV_GRP_HASSTDEXTD, - RISCV_GRP_HASSTDEXTF, - RISCV_GRP_HASSTDEXTM, - /* - RISCV_GRP_ISRVA, - RISCV_GRP_ISRVC, - RISCV_GRP_ISRVD, - RISCV_GRP_ISRVCD, - RISCV_GRP_ISRVF, - RISCV_GRP_ISRV32C, - RISCV_GRP_ISRV32CF, - RISCV_GRP_ISRVM, - RISCV_GRP_ISRV64A, - RISCV_GRP_ISRV64C, - RISCV_GRP_ISRV64D, - RISCV_GRP_ISRV64F, - RISCV_GRP_ISRV64M, - */ - RISCV_GRP_ENDING, -} riscv_insn_group; +G_END_DECLS -#ifdef __cplusplus -} #endif +/* + * Copyright (C) 2015-2025 Ole André Vadla Ravnås + * Copyright (C) 2023 Fabian Freyer + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -#endif +#ifndef __GUM_DARWIN_MODULE_H__ +#define __GUM_DARWIN_MODULE_H__ -/* Capstone Disassembly Engine */ -/* By Spike , xwings 2019 */ -#ifndef CAPSTONE_WASM_H -#define CAPSTONE_WASM_H +G_BEGIN_DECLS -#ifdef __cplusplus -extern "C" { -#endif +#define GUM_TYPE_DARWIN_MODULE (gum_darwin_module_get_type ()) +G_DECLARE_FINAL_TYPE (GumDarwinModule, gum_darwin_module, GUM, DARWIN_MODULE, + GObject) +#define GUM_TYPE_DARWIN_MODULE_IMAGE (gum_darwin_module_image_get_type ()) -#ifdef _MSC_VER -#pragma warning(disable:4201) -#endif +#define GUM_DARWIN_PORT_NULL 0 +#define GUM_DARWIN_EXPORT_KIND_MASK 3 -typedef enum wasm_op_type { - WASM_OP_INVALID = 0, - WASM_OP_NONE, - WASM_OP_INT7, - WASM_OP_VARUINT32, - WASM_OP_VARUINT64, - WASM_OP_UINT32, - WASM_OP_UINT64, - WASM_OP_IMM, - WASM_OP_BRTABLE, -} wasm_op_type; +typedef guint GumDarwinModuleFiletype; +typedef gint GumDarwinCpuType; +typedef gint GumDarwinCpuSubtype; -typedef struct cs_wasm_brtable { - uint32_t length; - uint64_t address; - uint32_t default_target; -} cs_wasm_brtable; +typedef struct _GumDarwinModuleImage GumDarwinModuleImage; -typedef struct cs_wasm_op { - wasm_op_type type; - uint32_t size; - union { - int8_t int7; - uint32_t varuint32; - uint64_t varuint64; - uint32_t uint32; - uint64_t uint64; - uint32_t immediate[2]; - cs_wasm_brtable brtable; - }; -} cs_wasm_op; +typedef struct _GumDarwinModuleImageSegment GumDarwinModuleImageSegment; +typedef struct _GumDarwinSectionDetails GumDarwinSectionDetails; +typedef struct _GumDarwinChainedFixupsDetails GumDarwinChainedFixupsDetails; +typedef struct _GumDarwinRebaseDetails GumDarwinRebaseDetails; +typedef struct _GumDarwinBindDetails GumDarwinBindDetails; +typedef struct _GumDarwinThreadedItem GumDarwinThreadedItem; +typedef struct _GumDarwinTlvParameters GumDarwinTlvParameters; +typedef struct _GumDarwinTlvDescriptorDetails GumDarwinTlvDescriptorDetails; +typedef struct _GumDarwinInitPointersDetails GumDarwinInitPointersDetails; +typedef struct _GumDarwinInitOffsetsDetails GumDarwinInitOffsetsDetails; +typedef struct _GumDarwinTermPointersDetails GumDarwinTermPointersDetails; +typedef struct _GumDarwinFunctionStartsDetails GumDarwinFunctionStartsDetails; +typedef struct _GumDarwinSegment GumDarwinSegment; +typedef struct _GumDarwinExportDetails GumDarwinExportDetails; +typedef struct _GumDarwinSymbolDetails GumDarwinSymbolDetails; -/// Instruction structure -typedef struct cs_wasm { - uint8_t op_count; - cs_wasm_op operands[2]; -} cs_wasm; +typedef guint8 GumDarwinRebaseType; +typedef guint8 GumDarwinBindType; +typedef guint8 GumDarwinThreadedItemType; +typedef gint GumDarwinBindOrdinal; +typedef guint8 GumDarwinBindSymbolFlags; +typedef guint8 GumDarwinExportSymbolKind; +typedef guint8 GumDarwinExportSymbolFlags; -/// WASM instruction -typedef enum wasm_insn { - WASM_INS_UNREACHABLE = 0x0, - WASM_INS_NOP = 0x1, - WASM_INS_BLOCK = 0x2, - WASM_INS_LOOP = 0x3, - WASM_INS_IF = 0x4, - WASM_INS_ELSE = 0x5, - WASM_INS_END = 0xb, - WASM_INS_BR = 0xc, - WASM_INS_BR_IF = 0xd, - WASM_INS_BR_TABLE = 0xe, - WASM_INS_RETURN = 0xf, - WASM_INS_CALL = 0x10, - WASM_INS_CALL_INDIRECT = 0x11, - WASM_INS_DROP = 0x1a, - WASM_INS_SELECT = 0x1b, - WASM_INS_GET_LOCAL = 0x20, - WASM_INS_SET_LOCAL = 0x21, - WASM_INS_TEE_LOCAL = 0x22, - WASM_INS_GET_GLOBAL = 0x23, - WASM_INS_SET_GLOBAL = 0x24, - WASM_INS_I32_LOAD = 0x28, - WASM_INS_I64_LOAD = 0x29, - WASM_INS_F32_LOAD = 0x2a, - WASM_INS_F64_LOAD = 0x2b, - WASM_INS_I32_LOAD8_S = 0x2c, - WASM_INS_I32_LOAD8_U = 0x2d, - WASM_INS_I32_LOAD16_S = 0x2e, - WASM_INS_I32_LOAD16_U = 0x2f, - WASM_INS_I64_LOAD8_S = 0x30, - WASM_INS_I64_LOAD8_U = 0x31, - WASM_INS_I64_LOAD16_S = 0x32, - WASM_INS_I64_LOAD16_U = 0x33, - WASM_INS_I64_LOAD32_S = 0x34, - WASM_INS_I64_LOAD32_U = 0x35, - WASM_INS_I32_STORE = 0x36, - WASM_INS_I64_STORE = 0x37, - WASM_INS_F32_STORE = 0x38, - WASM_INS_F64_STORE = 0x39, - WASM_INS_I32_STORE8 = 0x3a, - WASM_INS_I32_STORE16 = 0x3b, - WASM_INS_I64_STORE8 = 0x3c, - WASM_INS_I64_STORE16 = 0x3d, - WASM_INS_I64_STORE32 = 0x3e, - WASM_INS_CURRENT_MEMORY = 0x3f, - WASM_INS_GROW_MEMORY = 0x40, - WASM_INS_I32_CONST = 0x41, - WASM_INS_I64_CONST = 0x42, - WASM_INS_F32_CONST = 0x43, - WASM_INS_F64_CONST = 0x44, - WASM_INS_I32_EQZ = 0x45, - WASM_INS_I32_EQ = 0x46, - WASM_INS_I32_NE = 0x47, - WASM_INS_I32_LT_S = 0x48, - WASM_INS_I32_LT_U = 0x49, - WASM_INS_I32_GT_S = 0x4a, - WASM_INS_I32_GT_U = 0x4b, - WASM_INS_I32_LE_S = 0x4c, - WASM_INS_I32_LE_U = 0x4d, - WASM_INS_I32_GE_S = 0x4e, - WASM_INS_I32_GE_U = 0x4f, - WASM_INS_I64_EQZ = 0x50, - WASM_INS_I64_EQ = 0x51, - WASM_INS_I64_NE = 0x52, - WASM_INS_I64_LT_S = 0x53, - WASM_INS_I64_LT_U = 0x54, - WASN_INS_I64_GT_S = 0x55, - WASM_INS_I64_GT_U = 0x56, - WASM_INS_I64_LE_S = 0x57, - WASM_INS_I64_LE_U = 0x58, - WASM_INS_I64_GE_S = 0x59, - WASM_INS_I64_GE_U = 0x5a, - WASM_INS_F32_EQ = 0x5b, - WASM_INS_F32_NE = 0x5c, - WASM_INS_F32_LT = 0x5d, - WASM_INS_F32_GT = 0x5e, - WASM_INS_F32_LE = 0x5f, - WASM_INS_F32_GE = 0x60, - WASM_INS_F64_EQ = 0x61, - WASM_INS_F64_NE = 0x62, - WASM_INS_F64_LT = 0x63, - WASM_INS_F64_GT = 0x64, - WASM_INS_F64_LE = 0x65, - WASM_INS_F64_GE = 0x66, - WASM_INS_I32_CLZ = 0x67, - WASM_INS_I32_CTZ = 0x68, - WASM_INS_I32_POPCNT = 0x69, - WASM_INS_I32_ADD = 0x6a, - WASM_INS_I32_SUB = 0x6b, - WASM_INS_I32_MUL = 0x6c, - WASM_INS_I32_DIV_S = 0x6d, - WASM_INS_I32_DIV_U = 0x6e, - WASM_INS_I32_REM_S = 0x6f, - WASM_INS_I32_REM_U = 0x70, - WASM_INS_I32_AND = 0x71, - WASM_INS_I32_OR = 0x72, - WASM_INS_I32_XOR = 0x73, - WASM_INS_I32_SHL = 0x74, - WASM_INS_I32_SHR_S = 0x75, - WASM_INS_I32_SHR_U = 0x76, - WASM_INS_I32_ROTL = 0x77, - WASM_INS_I32_ROTR = 0x78, - WASM_INS_I64_CLZ = 0x79, - WASM_INS_I64_CTZ = 0x7a, - WASM_INS_I64_POPCNT = 0x7b, - WASM_INS_I64_ADD = 0x7c, - WASM_INS_I64_SUB = 0x7d, - WASM_INS_I64_MUL = 0x7e, - WASM_INS_I64_DIV_S = 0x7f, - WASM_INS_I64_DIV_U = 0x80, - WASM_INS_I64_REM_S = 0x81, - WASM_INS_I64_REM_U = 0x82, - WASM_INS_I64_AND = 0x83, - WASM_INS_I64_OR = 0x84, - WASM_INS_I64_XOR = 0x85, - WASM_INS_I64_SHL = 0x86, - WASM_INS_I64_SHR_S = 0x87, - WASM_INS_I64_SHR_U = 0x88, - WASM_INS_I64_ROTL = 0x89, - WASM_INS_I64_ROTR = 0x8a, - WASM_INS_F32_ABS = 0x8b, - WASM_INS_F32_NEG = 0x8c, - WASM_INS_F32_CEIL = 0x8d, - WASM_INS_F32_FLOOR = 0x8e, - WASM_INS_F32_TRUNC = 0x8f, - WASM_INS_F32_NEAREST = 0x90, - WASM_INS_F32_SQRT = 0x91, - WASM_INS_F32_ADD = 0x92, - WASM_INS_F32_SUB = 0x93, - WASM_INS_F32_MUL = 0x94, - WASM_INS_F32_DIV = 0x95, - WASM_INS_F32_MIN = 0x96, - WASM_INS_F32_MAX = 0x97, - WASM_INS_F32_COPYSIGN = 0x98, - WASM_INS_F64_ABS = 0x99, - WASM_INS_F64_NEG = 0x9a, - WASM_INS_F64_CEIL = 0x9b, - WASM_INS_F64_FLOOR = 0x9c, - WASM_INS_F64_TRUNC = 0x9d, - WASM_INS_F64_NEAREST = 0x9e, - WASM_INS_F64_SQRT = 0x9f, - WASM_INS_F64_ADD = 0xa0, - WASM_INS_F64_SUB = 0xa1, - WASM_INS_F64_MUL = 0xa2, - WASM_INS_F64_DIV = 0xa3, - WASM_INS_F64_MIN = 0xa4, - WASM_INS_F64_MAX = 0xa5, - WASM_INS_F64_COPYSIGN = 0xa6, - WASM_INS_I32_WARP_I64 = 0xa7, - WASP_INS_I32_TRUNC_S_F32 = 0xa8, - WASM_INS_I32_TRUNC_U_F32 = 0xa9, - WASM_INS_I32_TRUNC_S_F64 = 0xaa, - WASM_INS_I32_TRUNC_U_F64 = 0xab, - WASM_INS_I64_EXTEND_S_I32 = 0xac, - WASM_INS_I64_EXTEND_U_I32 = 0xad, - WASM_INS_I64_TRUNC_S_F32 = 0xae, - WASM_INS_I64_TRUNC_U_F32 = 0xaf, - WASM_INS_I64_TRUNC_S_F64 = 0xb0, - WASM_INS_I64_TRUNC_U_F64 = 0xb1, - WASM_INS_F32_CONVERT_S_I32 = 0xb2, - WASM_INS_F32_CONVERT_U_I32 = 0xb3, - WASM_INS_F32_CONVERT_S_I64 = 0xb4, - WASM_INS_F32_CONVERT_U_I64 = 0xb5, - WASM_INS_F32_DEMOTE_F64 = 0xb6, - WASM_INS_F64_CONVERT_S_I32 = 0xb7, - WASM_INS_F64_CONVERT_U_I32 = 0xb8, - WASM_INS_F64_CONVERT_S_I64 = 0xb9, - WASM_INS_F64_CONVERT_U_I64 = 0xba, - WASM_INS_F64_PROMOTE_F32 = 0xbb, - WASM_INS_I32_REINTERPRET_F32 = 0xbc, - WASM_INS_I64_REINTERPRET_F64 = 0xbd, - WASM_INS_F32_REINTERPRET_I32 = 0xbe, - WASM_INS_F64_REINTERPRET_I64 = 0xbf, - WASM_INS_INVALID = 512, - WASM_INS_ENDING, -} wasm_insn; +typedef guint GumDarwinPort; +typedef gint GumDarwinPageProtection; -/// Group of WASM instructions -typedef enum wasm_insn_group { - WASM_GRP_INVALID = 0, ///< = CS_GRP_INVALID +typedef gboolean (* GumFoundDarwinExportFunc) ( + const GumDarwinExportDetails * details, gpointer user_data); +typedef gboolean (* GumFoundDarwinSymbolFunc) ( + const GumDarwinSymbolDetails * details, gpointer user_data); +typedef gboolean (* GumFoundDarwinSectionFunc) ( + const GumDarwinSectionDetails * details, gpointer user_data); +typedef gboolean (* GumFoundDarwinChainedFixupsFunc) ( + const GumDarwinChainedFixupsDetails * details, gpointer user_data); +typedef gboolean (* GumFoundDarwinRebaseFunc) ( + const GumDarwinRebaseDetails * details, gpointer user_data); +typedef gboolean (* GumFoundDarwinBindFunc) ( + const GumDarwinBindDetails * details, gpointer user_data); - WASM_GRP_NUMBERIC = 8, - WASM_GRP_PARAMETRIC, - WASM_GRP_VARIABLE, - WASM_GRP_MEMORY, - WASM_GRP_CONTROL, +typedef gboolean (* GumFoundDarwinTlvDescriptorFunc) ( + const GumDarwinTlvDescriptorDetails * details, gpointer user_data); + +typedef gboolean (* GumFoundDarwinInitPointersFunc) ( + const GumDarwinInitPointersDetails * details, gpointer user_data); +typedef gboolean (* GumFoundDarwinInitOffsetsFunc) ( + const GumDarwinInitOffsetsDetails * details, gpointer user_data); +typedef gboolean (* GumFoundDarwinTermPointersFunc) ( + const GumDarwinTermPointersDetails * details, gpointer user_data); +typedef gboolean (* GumFoundDarwinFunctionStartsFunc) ( + const GumDarwinFunctionStartsDetails * details, gpointer user_data); + +typedef struct _GumDyldInfoCommand GumDyldInfoCommand; +typedef struct _GumSymtabCommand GumSymtabCommand; +typedef struct _GumDysymtabCommand GumDysymtabCommand; + +typedef enum { + GUM_DARWIN_MODULE_FLAGS_NONE = 0, + GUM_DARWIN_MODULE_FLAGS_HEADER_ONLY = (1 << 0), +} GumDarwinModuleFlags; + +typedef struct _GumChainedFixupsHeader GumChainedFixupsHeader; +typedef struct _GumChainedStartsInImage GumChainedStartsInImage; +typedef struct _GumChainedStartsInSegment GumChainedStartsInSegment; + +typedef guint32 GumChainedImportFormat; +typedef guint32 GumChainedSymbolFormat; +typedef guint16 GumChainedPtrFormat; + +typedef struct _GumChainedImport GumChainedImport; +typedef struct _GumChainedImportAddend GumChainedImportAddend; +typedef struct _GumChainedImportAddend64 GumChainedImportAddend64; + +typedef struct _GumChainedPtr64Rebase GumChainedPtr64Rebase; +typedef struct _GumChainedPtr64Bind GumChainedPtr64Bind; +typedef struct _GumChainedPtrArm64eRebase GumChainedPtrArm64eRebase; +typedef struct _GumChainedPtrArm64eBind GumChainedPtrArm64eBind; +typedef struct _GumChainedPtrArm64eBind24 GumChainedPtrArm64eBind24; +typedef struct _GumChainedPtrArm64eAuthRebase GumChainedPtrArm64eAuthRebase; +typedef struct _GumChainedPtrArm64eAuthBind GumChainedPtrArm64eAuthBind; +typedef struct _GumChainedPtrArm64eAuthBind24 GumChainedPtrArm64eAuthBind24; + +struct _GumDarwinModule +{ + GObject parent; + + GumDarwinModuleFiletype filetype; + gchar * name; + gchar * uuid; + gchar * source_version; + + GumDarwinPort task; + gboolean is_local; + gboolean is_kernel; + GumCpuType cpu_type; + GumPtrauthSupport ptrauth_support; + gsize pointer_size; + GumAddress base_address; + gchar * source_path; + GBytes * source_blob; + GumDarwinModuleFlags flags; + + GumDarwinModuleImage * image; + + const GumDyldInfoCommand * info; + const GumSymtabCommand * symtab; + const GumDysymtabCommand * dysymtab; + + GumAddress preferred_address; + + GArray * segments; + GArray * text_ranges; + gsize text_size; + + const guint8 * rebases; + const guint8 * rebases_end; + gpointer rebases_malloc_data; + + const guint8 * binds; + const guint8 * binds_end; + gpointer binds_malloc_data; + + const guint8 * lazy_binds; + const guint8 * lazy_binds_end; + gpointer lazy_binds_malloc_data; + + const guint8 * exports; + const guint8 * exports_end; + gpointer exports_malloc_data; + + GArray * dependencies; + GPtrArray * reexports; +}; + +enum _GumDarwinModuleFiletype +{ + GUM_DARWIN_MODULE_FILETYPE_OBJECT = 1, + GUM_DARWIN_MODULE_FILETYPE_EXECUTE, + GUM_DARWIN_MODULE_FILETYPE_FVMLIB, + GUM_DARWIN_MODULE_FILETYPE_CORE, + GUM_DARWIN_MODULE_FILETYPE_PRELOAD, + GUM_DARWIN_MODULE_FILETYPE_DYLIB, + GUM_DARWIN_MODULE_FILETYPE_DYLINKER, + GUM_DARWIN_MODULE_FILETYPE_BUNDLE, + GUM_DARWIN_MODULE_FILETYPE_DYLIB_STUB, + GUM_DARWIN_MODULE_FILETYPE_DSYM, + GUM_DARWIN_MODULE_FILETYPE_KEXT_BUNDLE, + GUM_DARWIN_MODULE_FILETYPE_FILESET, +}; + +enum _GumDarwinCpuArchType +{ + GUM_DARWIN_CPU_ARCH_ABI64 = 0x01000000, + GUM_DARWIN_CPU_ARCH_ABI64_32 = 0x02000000, +}; + +enum _GumDarwinCpuType +{ + GUM_DARWIN_CPU_X86 = 7, + GUM_DARWIN_CPU_X86_64 = 7 | GUM_DARWIN_CPU_ARCH_ABI64, + GUM_DARWIN_CPU_ARM = 12, + GUM_DARWIN_CPU_ARM64 = 12 | GUM_DARWIN_CPU_ARCH_ABI64, + GUM_DARWIN_CPU_ARM64_32 = 12 | GUM_DARWIN_CPU_ARCH_ABI64_32, +}; + +enum _GumDarwinCpuSubtype +{ + GUM_DARWIN_CPU_SUBTYPE_ARM64E = 2, + + GUM_DARWIN_CPU_SUBTYPE_MASK = 0x00ffffff, +}; + +struct _GumDarwinModuleImage +{ + gpointer data; + guint64 size; + gconstpointer linkedit; + + guint64 source_offset; + guint64 source_size; + guint64 shared_offset; + guint64 shared_size; + GArray * shared_segments; + + GBytes * bytes; + gpointer malloc_data; +}; + +struct _GumDarwinModuleImageSegment +{ + guint64 offset; + guint64 size; + GumDarwinPageProtection protection; +}; + +struct _GumDarwinSectionDetails +{ + gchar segment_name[17]; + gchar section_name[17]; + GumAddress vm_address; + guint64 size; + GumDarwinPageProtection protection; + guint32 file_offset; + guint32 flags; +}; + +struct _GumDarwinChainedFixupsDetails +{ + GumAddress vm_address; + guint64 file_offset; + guint32 size; +}; + +struct _GumDarwinRebaseDetails +{ + const GumDarwinSegment * segment; + guint64 offset; + GumDarwinRebaseType type; + GumAddress slide; +}; - WASM_GRP_ENDING, ///< <-- mark the end of the list of groups -} wasm_insn_group; +struct _GumDarwinBindDetails +{ + const GumDarwinSegment * segment; + guint64 offset; + GumDarwinBindType type; + GumDarwinBindOrdinal library_ordinal; + const gchar * symbol_name; + GumDarwinBindSymbolFlags symbol_flags; + gint64 addend; + guint16 threaded_table_size; +}; -#ifdef __cplusplus -} -#endif +struct _GumDarwinThreadedItem +{ + gboolean is_authenticated; + GumDarwinThreadedItemType type; + guint16 delta; + guint8 key; + gboolean has_address_diversity; + guint16 diversity; -#endif -#ifndef CAPSTONE_MOS65XX_H -#define CAPSTONE_MOS65XX_H + guint16 bind_ordinal; -/* Capstone Disassembly Engine */ -/* By Sebastian Macke , 2019 */ +struct _GumDarwinSymbolDetails +{ + const gchar * name; + GumAddress address; -#ifndef CAPSTONE_BPF_H -#define CAPSTONE_BPF_H + /* These map 1:1 to their struct nlist / nlist_64 equivalents. */ + guint8 type; + guint8 section; + guint16 description; +}; -#ifdef __cplusplus -extern "C" { -#endif +enum _GumDarwinRebaseType +{ + GUM_DARWIN_REBASE_POINTER = 1, + GUM_DARWIN_REBASE_TEXT_ABSOLUTE32, + GUM_DARWIN_REBASE_TEXT_PCREL32, +}; +enum _GumDarwinBindType +{ + GUM_DARWIN_BIND_POINTER = 1, + GUM_DARWIN_BIND_TEXT_ABSOLUTE32, + GUM_DARWIN_BIND_TEXT_PCREL32, + GUM_DARWIN_BIND_THREADED_TABLE, + GUM_DARWIN_BIND_THREADED_ITEMS, +}; -#ifdef _MSC_VER -#pragma warning(disable:4201) -#endif +enum _GumDarwinThreadedItemType +{ + GUM_DARWIN_THREADED_REBASE, + GUM_DARWIN_THREADED_BIND +}; -/// Operand type for instruction's operands -typedef enum bpf_op_type { - BPF_OP_INVALID = 0, +enum _GumDarwinBindOrdinal +{ + GUM_DARWIN_BIND_SELF = 0, + GUM_DARWIN_BIND_MAIN_EXECUTABLE = -1, + GUM_DARWIN_BIND_FLAT_LOOKUP = -2, + GUM_DARWIN_BIND_WEAK_LOOKUP = -3, +}; - BPF_OP_REG, - BPF_OP_IMM, - BPF_OP_OFF, - BPF_OP_MEM, - BPF_OP_MMEM, ///< M[k] in cBPF - BPF_OP_MSH, ///< corresponds to cBPF's BPF_MSH mode - BPF_OP_EXT, ///< cBPF's extension (not eBPF) -} bpf_op_type; +enum _GumDarwinBindSymbolFlags +{ + GUM_DARWIN_BIND_WEAK_IMPORT = 0x1, + GUM_DARWIN_BIND_NON_WEAK_DEFINITION = 0x8, +}; -/// BPF registers -typedef enum bpf_reg { - BPF_REG_INVALID = 0, +enum _GumDarwinExportSymbolKind +{ + GUM_DARWIN_EXPORT_REGULAR, + GUM_DARWIN_EXPORT_THREAD_LOCAL, + GUM_DARWIN_EXPORT_ABSOLUTE +}; - ///< cBPF - BPF_REG_A, - BPF_REG_X, +enum _GumDarwinExportSymbolFlags +{ + GUM_DARWIN_EXPORT_WEAK_DEFINITION = 0x04, + GUM_DARWIN_EXPORT_REEXPORT = 0x08, + GUM_DARWIN_EXPORT_STUB_AND_RESOLVER = 0x10, +}; - ///< eBPF - BPF_REG_R0, - BPF_REG_R1, - BPF_REG_R2, - BPF_REG_R3, - BPF_REG_R4, - BPF_REG_R5, - BPF_REG_R6, - BPF_REG_R7, - BPF_REG_R8, - BPF_REG_R9, - BPF_REG_R10, +#ifdef _MSC_VER +# pragma warning (push) +# pragma warning (disable: 4214) +#endif - BPF_REG_ENDING, -} bpf_reg; +struct _GumChainedFixupsHeader +{ + guint32 fixups_version; + guint32 starts_offset; + guint32 imports_offset; + guint32 symbols_offset; + guint32 imports_count; + GumChainedImportFormat imports_format; + GumChainedSymbolFormat symbols_format; +}; -/// Instruction's operand referring to memory -/// This is associated with BPF_OP_MEM operand type above -typedef struct bpf_op_mem { - bpf_reg base; ///< base register - uint32_t disp; ///< offset value -} bpf_op_mem; +enum _GumChainedImportFormat +{ + GUM_CHAINED_IMPORT = 1, + GUM_CHAINED_IMPORT_ADDEND = 2, + GUM_CHAINED_IMPORT_ADDEND64 = 3, +}; -typedef enum bpf_ext_type { - BPF_EXT_INVALID = 0, +struct _GumChainedImport +{ + guint32 lib_ordinal : 8, + weak_import : 1, + name_offset : 23; +}; - BPF_EXT_LEN, -} bpf_ext_type; +struct _GumChainedImportAddend +{ + guint32 lib_ordinal : 8, + weak_import : 1, + name_offset : 23; + gint32 addend; +}; -/// Instruction operand -typedef struct cs_bpf_op { - bpf_op_type type; - union { - uint8_t reg; ///< register value for REG operand - uint64_t imm; ///< immediate value IMM operand - uint32_t off; ///< offset value, used in jump & call - bpf_op_mem mem; ///< base/disp value for MEM operand - /* cBPF only */ - uint32_t mmem; ///< M[k] in cBPF - uint32_t msh; ///< corresponds to cBPF's BPF_MSH mode - uint32_t ext; ///< cBPF's extension (not eBPF) - }; +struct _GumChainedImportAddend64 +{ + guint64 lib_ordinal : 16, + weak_import : 1, + reserved : 15, + name_offset : 32; + guint64 addend; +}; - /// How is this operand accessed? (READ, WRITE or READ|WRITE) - /// This field is combined of cs_ac_type. - /// NOTE: this field is irrelevant if engine is compiled in DIET mode. - uint8_t access; -} cs_bpf_op; +struct _GumChainedStartsInImage +{ + guint32 seg_count; + guint32 seg_info_offset[1]; +}; -/// Instruction structure -typedef struct cs_bpf { - uint8_t op_count; - cs_bpf_op operands[4]; -} cs_bpf; +struct _GumChainedStartsInSegment +{ + guint32 size; + guint16 page_size; + GumChainedPtrFormat pointer_format; + guint64 segment_offset; + guint32 max_valid_pointer; + guint16 page_count; + guint16 page_start[1]; +}; -/// BPF instruction -typedef enum bpf_insn { - BPF_INS_INVALID = 0, +enum _GumChainedPtrStart +{ + GUM_CHAINED_PTR_START_NONE = 0xffff, + GUM_CHAINED_PTR_START_MULTI = 0x8000, + GUM_CHAINED_PTR_START_LAST = 0x8000, +}; - ///< ALU - BPF_INS_ADD, - BPF_INS_SUB, - BPF_INS_MUL, - BPF_INS_DIV, - BPF_INS_OR, - BPF_INS_AND, - BPF_INS_LSH, - BPF_INS_RSH, - BPF_INS_NEG, - BPF_INS_MOD, - BPF_INS_XOR, - BPF_INS_MOV, ///< eBPF only - BPF_INS_ARSH, ///< eBPF only +enum _GumChainedPtrFormat +{ + GUM_CHAINED_PTR_ARM64E = 1, + GUM_CHAINED_PTR_64 = 2, + GUM_CHAINED_PTR_32 = 3, + GUM_CHAINED_PTR_32_CACHE = 4, + GUM_CHAINED_PTR_32_FIRMWARE = 5, + GUM_CHAINED_PTR_64_OFFSET = 6, + GUM_CHAINED_PTR_ARM64E_OFFSET = 7, + GUM_CHAINED_PTR_ARM64E_KERNEL = 7, + GUM_CHAINED_PTR_64_KERNEL_CACHE = 8, + GUM_CHAINED_PTR_ARM64E_USERLAND = 9, + GUM_CHAINED_PTR_ARM64E_FIRMWARE = 10, + GUM_CHAINED_PTR_X86_64_KERNEL_CACHE = 11, + GUM_CHAINED_PTR_ARM64E_USERLAND24 = 12, +}; + +struct _GumChainedPtr64Rebase +{ + guint64 target : 36, + high8 : 8, + reserved : 7, + next : 12, + bind : 1; +}; - ///< ALU64, eBPF only - BPF_INS_ADD64, - BPF_INS_SUB64, - BPF_INS_MUL64, - BPF_INS_DIV64, - BPF_INS_OR64, - BPF_INS_AND64, - BPF_INS_LSH64, - BPF_INS_RSH64, - BPF_INS_NEG64, - BPF_INS_MOD64, - BPF_INS_XOR64, - BPF_INS_MOV64, - BPF_INS_ARSH64, +struct _GumChainedPtr64Bind +{ + guint64 ordinal : 24, + addend : 8, + reserved : 19, + next : 12, + bind : 1; +}; - ///< Byteswap, eBPF only - BPF_INS_LE16, - BPF_INS_LE32, - BPF_INS_LE64, - BPF_INS_BE16, - BPF_INS_BE32, - BPF_INS_BE64, +struct _GumChainedPtrArm64eRebase +{ + guint64 target : 43, + high8 : 8, + next : 11, + bind : 1, + auth : 1; +}; - ///< Load - BPF_INS_LDW, ///< eBPF only - BPF_INS_LDH, - BPF_INS_LDB, - BPF_INS_LDDW, ///< eBPF only: load 64-bit imm - BPF_INS_LDXW, ///< eBPF only - BPF_INS_LDXH, ///< eBPF only - BPF_INS_LDXB, ///< eBPF only - BPF_INS_LDXDW, ///< eBPF only +struct _GumChainedPtrArm64eBind +{ + guint64 ordinal : 16, + zero : 16, + addend : 19, + next : 11, + bind : 1, + auth : 1; +}; - ///< Store - BPF_INS_STW, ///< eBPF only - BPF_INS_STH, ///< eBPF only - BPF_INS_STB, ///< eBPF only - BPF_INS_STDW, ///< eBPF only - BPF_INS_STXW, ///< eBPF only - BPF_INS_STXH, ///< eBPF only - BPF_INS_STXB, ///< eBPF only - BPF_INS_STXDW, ///< eBPF only - BPF_INS_XADDW, ///< eBPF only - BPF_INS_XADDDW, ///< eBPF only +struct _GumChainedPtrArm64eBind24 +{ + guint64 ordinal : 24, + zero : 8, + addend : 19, + next : 11, + bind : 1, + auth : 1; +}; - ///< Jump - BPF_INS_JMP, - BPF_INS_JEQ, - BPF_INS_JGT, - BPF_INS_JGE, - BPF_INS_JSET, - BPF_INS_JNE, ///< eBPF only - BPF_INS_JSGT, ///< eBPF only - BPF_INS_JSGE, ///< eBPF only - BPF_INS_CALL, ///< eBPF only - BPF_INS_CALLX, ///< eBPF only - BPF_INS_EXIT, ///< eBPF only - BPF_INS_JLT, ///< eBPF only - BPF_INS_JLE, ///< eBPF only - BPF_INS_JSLT, ///< eBPF only - BPF_INS_JSLE, ///< eBPF only +struct _GumChainedPtrArm64eAuthRebase +{ + guint64 target : 32, + diversity : 16, + addr_div : 1, + key : 2, + next : 11, + bind : 1, + auth : 1; +}; - ///< Return, cBPF only - BPF_INS_RET, +struct _GumChainedPtrArm64eAuthBind +{ + guint64 ordinal : 16, + zero : 16, + diversity : 16, + addr_div : 1, + key : 2, + next : 11, + bind : 1, + auth : 1; +}; - ///< Misc, cBPF only - BPF_INS_TAX, - BPF_INS_TXA, +struct _GumChainedPtrArm64eAuthBind24 +{ + guint64 ordinal : 24, + zero : 8, + diversity : 16, + addr_div : 1, + key : 2, + next : 11, + bind : 1, + auth : 1; +}; - BPF_INS_ENDING, +#ifdef _MSC_VER +# pragma warning (pop) +#endif - // alias instructions - BPF_INS_LD = BPF_INS_LDW, ///< cBPF only - BPF_INS_LDX = BPF_INS_LDXW, ///< cBPF only - BPF_INS_ST = BPF_INS_STW, ///< cBPF only - BPF_INS_STX = BPF_INS_STXW, ///< cBPF only -} bpf_insn; +GUM_API GumDarwinModule * gum_darwin_module_new_from_file (const gchar * path, + GumCpuType cpu_type, GumPtrauthSupport ptrauth_support, + GumDarwinModuleFlags flags, GError ** error); +GUM_API GumDarwinModule * gum_darwin_module_new_from_blob (GBytes * blob, + GumCpuType cpu_type, GumPtrauthSupport ptrauth_support, + GumDarwinModuleFlags flags, GError ** error); +GUM_API GumDarwinModule * gum_darwin_module_new_from_memory (const gchar * name, + GumDarwinPort task, GumAddress base_address, GumDarwinModuleFlags flags, + GError ** error); -/// Group of BPF instructions -typedef enum bpf_insn_group { - BPF_GRP_INVALID = 0, ///< = CS_GRP_INVALID +GUM_API gboolean gum_darwin_module_load (GumDarwinModule * self, + GError ** error); - BPF_GRP_LOAD, - BPF_GRP_STORE, - BPF_GRP_ALU, - BPF_GRP_JUMP, - BPF_GRP_CALL, ///< eBPF only - BPF_GRP_RETURN, - BPF_GRP_MISC, ///< cBPF only +GUM_API gboolean gum_darwin_module_resolve_export (GumDarwinModule * self, + const gchar * symbol, GumDarwinExportDetails * details); +GUM_API GumAddress gum_darwin_module_resolve_symbol_address ( + GumDarwinModule * self, const gchar * symbol); +GUM_API gboolean gum_darwin_module_get_lacks_exports_for_reexports ( + GumDarwinModule * self); +GUM_API void gum_darwin_module_enumerate_imports (GumDarwinModule * self, + GumFoundImportFunc func, GumResolveExportFunc resolver, gpointer user_data); +GUM_API void gum_darwin_module_enumerate_exports (GumDarwinModule * self, + GumFoundDarwinExportFunc func, gpointer user_data); +GUM_API void gum_darwin_module_enumerate_symbols (GumDarwinModule * self, + GumFoundDarwinSymbolFunc func, gpointer user_data); +GUM_API GumAddress gum_darwin_module_get_slide (GumDarwinModule * self); +GUM_API const GumDarwinSegment * gum_darwin_module_get_nth_segment ( + GumDarwinModule * self, gsize index); +GUM_API void gum_darwin_module_enumerate_sections (GumDarwinModule * self, + GumFoundDarwinSectionFunc func, gpointer user_data); +GUM_API gboolean gum_darwin_module_is_address_in_text_section ( + GumDarwinModule * self, GumAddress address); +GUM_API void gum_darwin_module_enumerate_chained_fixups (GumDarwinModule * self, + GumFoundDarwinChainedFixupsFunc func, gpointer user_data); +GUM_API void gum_darwin_module_enumerate_rebases (GumDarwinModule * self, + GumFoundDarwinRebaseFunc func, gpointer user_data); +GUM_API void gum_darwin_module_enumerate_binds (GumDarwinModule * self, + GumFoundDarwinBindFunc func, gpointer user_data); +GUM_API void gum_darwin_module_enumerate_lazy_binds (GumDarwinModule * self, + GumFoundDarwinBindFunc func, gpointer user_data); +GUM_API void gum_darwin_module_query_tlv_parameters (GumDarwinModule * self, + GumDarwinTlvParameters * params); +GUM_API void gum_darwin_module_enumerate_tlv_descriptors ( + GumDarwinModule * self, GumFoundDarwinTlvDescriptorFunc func, + gpointer user_data); +GUM_API void gum_darwin_module_enumerate_init_pointers (GumDarwinModule * self, + GumFoundDarwinInitPointersFunc func, gpointer user_data); +GUM_API void gum_darwin_module_enumerate_init_offsets (GumDarwinModule * self, + GumFoundDarwinInitOffsetsFunc func, gpointer user_data); +GUM_API void gum_darwin_module_enumerate_term_pointers (GumDarwinModule * self, + GumFoundDarwinTermPointersFunc func, gpointer user_data); +GUM_API void gum_darwin_module_enumerate_dependencies (GumDarwinModule * self, + GumFoundDependencyFunc func, gpointer user_data); +GUM_API void gum_darwin_module_enumerate_function_starts ( + GumDarwinModule * self, GumFoundDarwinFunctionStartsFunc func, + gpointer user_data); +GUM_API const gchar * gum_darwin_module_get_dependency_by_ordinal ( + GumDarwinModule * self, gint ordinal); +GUM_API gboolean gum_darwin_module_ensure_image_loaded (GumDarwinModule * self, + GError ** error); - BPF_GRP_ENDING, -} bpf_insn_group; +GUM_API void gum_darwin_threaded_item_parse (guint64 value, + GumDarwinThreadedItem * result); -#ifdef __cplusplus -} -#endif +GUM_API GType gum_darwin_module_image_get_type (void) G_GNUC_CONST; +GUM_API GumDarwinModuleImage * gum_darwin_module_image_new (void); +GUM_API GumDarwinModuleImage * gum_darwin_module_image_dup ( + const GumDarwinModuleImage * other); +GUM_API void gum_darwin_module_image_free (GumDarwinModuleImage * image); + +G_END_DECLS #endif -#ifndef CAPSTONE_SH_H -#define CAPSTONE_SH_H +/* + * Copyright (C) 2010-2026 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -/* Capstone Disassembly Engine */ -/* By Yoshinori Sato, 2022 */ +#ifndef __GUM_ELF_MODULE_H__ +#define __GUM_ELF_MODULE_H__ -#ifdef __cplusplus -extern "C" { -#endif +G_BEGIN_DECLS -#ifdef _MSC_VER -#pragma warning(disable:4201) -#endif +#define GUM_ELF_TYPE_MODULE (gum_elf_module_get_type ()) +G_DECLARE_FINAL_TYPE (GumElfModule, gum_elf_module, GUM_ELF, MODULE, GObject) -/// SH registers and special registers typedef enum { - SH_REG_INVALID = 0, + GUM_ELF_NONE, + GUM_ELF_REL, + GUM_ELF_EXEC, + GUM_ELF_DYN, + GUM_ELF_CORE, +} GumElfType; - SH_REG_R0, - SH_REG_R1, - SH_REG_R2, - SH_REG_R3, - SH_REG_R4, - SH_REG_R5, - SH_REG_R6, - SH_REG_R7, +typedef enum { + GUM_ELF_OS_SYSV, + GUM_ELF_OS_HPUX, + GUM_ELF_OS_NETBSD, + GUM_ELF_OS_LINUX, + GUM_ELF_OS_SOLARIS = 6, + GUM_ELF_OS_AIX, + GUM_ELF_OS_IRIX, + GUM_ELF_OS_FREEBSD, + GUM_ELF_OS_TRU64, + GUM_ELF_OS_MODESTO, + GUM_ELF_OS_OPENBSD, + GUM_ELF_OS_ARM_AEABI = 64, + GUM_ELF_OS_ARM = 97, + GUM_ELF_OS_STANDALONE = 255, +} GumElfOSABI; - SH_REG_R8, - SH_REG_R9, - SH_REG_R10, - SH_REG_R11, - SH_REG_R12, - SH_REG_R13, - SH_REG_R14, - SH_REG_R15, +typedef enum { + GUM_ELF_MACHINE_NONE, + GUM_ELF_MACHINE_M32, + GUM_ELF_MACHINE_SPARC, + GUM_ELF_MACHINE_386, + GUM_ELF_MACHINE_68K, + GUM_ELF_MACHINE_88K, + GUM_ELF_MACHINE_IAMCU, + GUM_ELF_MACHINE_860, + GUM_ELF_MACHINE_MIPS, + GUM_ELF_MACHINE_S370, + GUM_ELF_MACHINE_MIPS_RS3_LE, - SH_REG_R0_BANK, - SH_REG_R1_BANK, - SH_REG_R2_BANK, - SH_REG_R3_BANK, - SH_REG_R4_BANK, - SH_REG_R5_BANK, - SH_REG_R6_BANK, - SH_REG_R7_BANK, + GUM_ELF_MACHINE_PARISC = 15, - SH_REG_FR0, - SH_REG_FR1, - SH_REG_FR2, - SH_REG_FR3, - SH_REG_FR4, - SH_REG_FR5, - SH_REG_FR6, - SH_REG_FR7, - SH_REG_FR8, - SH_REG_FR9, - SH_REG_FR10, - SH_REG_FR11, - SH_REG_FR12, - SH_REG_FR13, - SH_REG_FR14, - SH_REG_FR15, + GUM_ELF_MACHINE_VPP500 = 17, + GUM_ELF_MACHINE_SPARC32PLUS, + GUM_ELF_MACHINE_960, + GUM_ELF_MACHINE_PPC, + GUM_ELF_MACHINE_PPC64, + GUM_ELF_MACHINE_S390, + GUM_ELF_MACHINE_SPU, - SH_REG_DR0, - SH_REG_DR2, - SH_REG_DR4, - SH_REG_DR6, - SH_REG_DR8, - SH_REG_DR10, - SH_REG_DR12, - SH_REG_DR14, + GUM_ELF_MACHINE_V800 = 36, + GUM_ELF_MACHINE_FR20, + GUM_ELF_MACHINE_RH32, + GUM_ELF_MACHINE_RCE, + GUM_ELF_MACHINE_ARM, + GUM_ELF_MACHINE_FAKE_ALPHA, + GUM_ELF_MACHINE_SH, + GUM_ELF_MACHINE_SPARCV9, + GUM_ELF_MACHINE_TRICORE, + GUM_ELF_MACHINE_ARC, + GUM_ELF_MACHINE_H8_300, + GUM_ELF_MACHINE_H8_300H, + GUM_ELF_MACHINE_H8S, + GUM_ELF_MACHINE_H8_500, + GUM_ELF_MACHINE_IA_64, + GUM_ELF_MACHINE_MIPS_X, + GUM_ELF_MACHINE_COLDFIRE, + GUM_ELF_MACHINE_68HC12, + GUM_ELF_MACHINE_MMA, + GUM_ELF_MACHINE_PCP, + GUM_ELF_MACHINE_NCPU, + GUM_ELF_MACHINE_NDR1, + GUM_ELF_MACHINE_STARCORE, + GUM_ELF_MACHINE_ME16, + GUM_ELF_MACHINE_ST100, + GUM_ELF_MACHINE_TINYJ, + GUM_ELF_MACHINE_X86_64, + GUM_ELF_MACHINE_PDSP, + GUM_ELF_MACHINE_PDP10, + GUM_ELF_MACHINE_PDP11, + GUM_ELF_MACHINE_FX66, + GUM_ELF_MACHINE_ST9PLUS, + GUM_ELF_MACHINE_ST7, + GUM_ELF_MACHINE_68HC16, + GUM_ELF_MACHINE_68HC11, + GUM_ELF_MACHINE_68HC08, + GUM_ELF_MACHINE_68HC05, + GUM_ELF_MACHINE_SVX, + GUM_ELF_MACHINE_ST19, + GUM_ELF_MACHINE_VAX, + GUM_ELF_MACHINE_CRIS, + GUM_ELF_MACHINE_JAVELIN, + GUM_ELF_MACHINE_FIREPATH, + GUM_ELF_MACHINE_ZSP, + GUM_ELF_MACHINE_MMIX, + GUM_ELF_MACHINE_HUANY, + GUM_ELF_MACHINE_PRISM, + GUM_ELF_MACHINE_AVR, + GUM_ELF_MACHINE_FR30, + GUM_ELF_MACHINE_D10V, + GUM_ELF_MACHINE_D30V, + GUM_ELF_MACHINE_V850, + GUM_ELF_MACHINE_M32R, + GUM_ELF_MACHINE_MN10300, + GUM_ELF_MACHINE_MN10200, + GUM_ELF_MACHINE_PJ, + GUM_ELF_MACHINE_OPENRISC, + GUM_ELF_MACHINE_ARC_COMPACT, + GUM_ELF_MACHINE_XTENSA, + GUM_ELF_MACHINE_VIDEOCORE, + GUM_ELF_MACHINE_TMM_GPP, + GUM_ELF_MACHINE_NS32K, + GUM_ELF_MACHINE_TPC, + GUM_ELF_MACHINE_SNP1K, + GUM_ELF_MACHINE_ST200, + GUM_ELF_MACHINE_IP2K, + GUM_ELF_MACHINE_MAX, + GUM_ELF_MACHINE_CR, + GUM_ELF_MACHINE_F2MC16, + GUM_ELF_MACHINE_MSP430, + GUM_ELF_MACHINE_BLACKFIN, + GUM_ELF_MACHINE_SE_C33, + GUM_ELF_MACHINE_SEP, + GUM_ELF_MACHINE_ARCA, + GUM_ELF_MACHINE_UNICORE, + GUM_ELF_MACHINE_EXCESS, + GUM_ELF_MACHINE_DXP, + GUM_ELF_MACHINE_ALTERA_NIOS2, + GUM_ELF_MACHINE_CRX, + GUM_ELF_MACHINE_XGATE, + GUM_ELF_MACHINE_C166, + GUM_ELF_MACHINE_M16C, + GUM_ELF_MACHINE_DSPIC30F, + GUM_ELF_MACHINE_CE, + GUM_ELF_MACHINE_M32C, - SH_REG_XD0, - SH_REG_XD2, - SH_REG_XD4, - SH_REG_XD6, - SH_REG_XD8, - SH_REG_XD10, - SH_REG_XD12, - SH_REG_XD14, + GUM_ELF_MACHINE_TSK3000 = 131, + GUM_ELF_MACHINE_RS08, + GUM_ELF_MACHINE_SHARC, + GUM_ELF_MACHINE_ECOG2, + GUM_ELF_MACHINE_SCORE7, + GUM_ELF_MACHINE_DSP24, + GUM_ELF_MACHINE_VIDEOCORE3, + GUM_ELF_MACHINE_LATTICEMICO32, + GUM_ELF_MACHINE_SE_C17, + GUM_ELF_MACHINE_TI_C6000, + GUM_ELF_MACHINE_TI_C2000, + GUM_ELF_MACHINE_TI_C5500, + GUM_ELF_MACHINE_TI_ARP32, + GUM_ELF_MACHINE_TI_PRU, - SH_REG_XF0, - SH_REG_XF1, - SH_REG_XF2, - SH_REG_XF3, - SH_REG_XF4, - SH_REG_XF5, - SH_REG_XF6, - SH_REG_XF7, - SH_REG_XF8, - SH_REG_XF9, - SH_REG_XF10, - SH_REG_XF11, - SH_REG_XF12, - SH_REG_XF13, - SH_REG_XF14, - SH_REG_XF15, + GUM_ELF_MACHINE_MMDSP_PLUS = 160, + GUM_ELF_MACHINE_CYPRESS_M8C, + GUM_ELF_MACHINE_R32C, + GUM_ELF_MACHINE_TRIMEDIA, + GUM_ELF_MACHINE_QDSP6, + GUM_ELF_MACHINE_8051, + GUM_ELF_MACHINE_STXP7X, + GUM_ELF_MACHINE_NDS32, + GUM_ELF_MACHINE_ECOG1X, + GUM_ELF_MACHINE_MAXQ30, + GUM_ELF_MACHINE_XIMO16, + GUM_ELF_MACHINE_MANIK, + GUM_ELF_MACHINE_CRAYNV2, + GUM_ELF_MACHINE_RX, + GUM_ELF_MACHINE_METAG, + GUM_ELF_MACHINE_MCST_ELBRUS, + GUM_ELF_MACHINE_ECOG16, + GUM_ELF_MACHINE_CR16, + GUM_ELF_MACHINE_ETPU, + GUM_ELF_MACHINE_SLE9X, + GUM_ELF_MACHINE_L10M, + GUM_ELF_MACHINE_K10M, - SH_REG_FV0, - SH_REG_FV4, - SH_REG_FV8, - SH_REG_FV12, + GUM_ELF_MACHINE_AARCH64 = 183, - SH_REG_XMATRX, + GUM_ELF_MACHINE_AVR32 = 185, + GUM_ELF_MACHINE_STM8, + GUM_ELF_MACHINE_TILE64, + GUM_ELF_MACHINE_TILEPRO, + GUM_ELF_MACHINE_MICROBLAZE, + GUM_ELF_MACHINE_CUDA, + GUM_ELF_MACHINE_TILEGX, + GUM_ELF_MACHINE_CLOUDSHIELD, + GUM_ELF_MACHINE_COREA_1ST, + GUM_ELF_MACHINE_COREA_2ND, + GUM_ELF_MACHINE_ARCV2, + GUM_ELF_MACHINE_OPEN8, + GUM_ELF_MACHINE_RL78, + GUM_ELF_MACHINE_VIDEOCORE5, + GUM_ELF_MACHINE_78KOR, + GUM_ELF_MACHINE_56800EX, + GUM_ELF_MACHINE_BA1, + GUM_ELF_MACHINE_BA2, + GUM_ELF_MACHINE_XCORE, + GUM_ELF_MACHINE_MCHP_PIC, + + GUM_ELF_MACHINE_KM32 = 210, + GUM_ELF_MACHINE_KMX32, + GUM_ELF_MACHINE_EMX16, + GUM_ELF_MACHINE_EMX8, + GUM_ELF_MACHINE_KVARC, + GUM_ELF_MACHINE_CDP, + GUM_ELF_MACHINE_COGE, + GUM_ELF_MACHINE_COOL, + GUM_ELF_MACHINE_NORC, + GUM_ELF_MACHINE_CSR_KALIMBA, + GUM_ELF_MACHINE_Z80, + GUM_ELF_MACHINE_VISIUM, + GUM_ELF_MACHINE_FT32, + GUM_ELF_MACHINE_MOXIE, + GUM_ELF_MACHINE_AMDGPU, - SH_REG_PC, - SH_REG_PR, - SH_REG_MACH, - SH_REG_MACL, + GUM_ELF_MACHINE_RISCV = 243, - SH_REG_SR, - SH_REG_GBR, - SH_REG_SSR, - SH_REG_SPC, - SH_REG_SGR, - SH_REG_DBR, - SH_REG_VBR, - SH_REG_TBR, - SH_REG_RS, - SH_REG_RE, - SH_REG_MOD, + GUM_ELF_MACHINE_BPF = 247, - SH_REG_FPUL, - SH_REG_FPSCR, + GUM_ELF_MACHINE_CSKY = 252, - SH_REG_DSP_X0, - SH_REG_DSP_X1, - SH_REG_DSP_Y0, - SH_REG_DSP_Y1, - SH_REG_DSP_A0, - SH_REG_DSP_A1, - SH_REG_DSP_A0G, - SH_REG_DSP_A1G, - SH_REG_DSP_M0, - SH_REG_DSP_M1, - SH_REG_DSP_DSR, + GUM_ELF_MACHINE_ALPHA = 0x9026, +} GumElfMachine; - SH_REG_DSP_RSV0, - SH_REG_DSP_RSV1, - SH_REG_DSP_RSV2, - SH_REG_DSP_RSV3, - SH_REG_DSP_RSV4, - SH_REG_DSP_RSV5, - SH_REG_DSP_RSV6, - SH_REG_DSP_RSV7, - SH_REG_DSP_RSV8, - SH_REG_DSP_RSV9, - SH_REG_DSP_RSVA, - SH_REG_DSP_RSVB, - SH_REG_DSP_RSVC, - SH_REG_DSP_RSVD, - SH_REG_DSP_RSVE, - SH_REG_DSP_RSVF, +typedef enum { + GUM_ELF_SOURCE_MODE_OFFLINE, + GUM_ELF_SOURCE_MODE_ONLINE, +} GumElfSourceMode; - SH_REG_ENDING, // <-- mark the end of the list of registers -} sh_reg; +typedef enum { + GUM_ELF_SECTION_NULL, + GUM_ELF_SECTION_PROGBITS, + GUM_ELF_SECTION_SYMTAB, + GUM_ELF_SECTION_STRTAB, + GUM_ELF_SECTION_RELA, + GUM_ELF_SECTION_HASH, + GUM_ELF_SECTION_DYNAMIC, + GUM_ELF_SECTION_NOTE, + GUM_ELF_SECTION_NOBITS, + GUM_ELF_SECTION_REL, + GUM_ELF_SECTION_SHLIB, + GUM_ELF_SECTION_DYNSYM, + GUM_ELF_SECTION_INIT_ARRAY = 14, + GUM_ELF_SECTION_FINI_ARRAY, + GUM_ELF_SECTION_PREINIT_ARRAY, + GUM_ELF_SECTION_GROUP, + GUM_ELF_SECTION_SYMTAB_SHNDX, + GUM_ELF_SECTION_RELR, + GUM_ELF_SECTION_NUM, + GUM_ELF_SECTION_GNU_ATTRIBUTES = 0x6ffffff5, + GUM_ELF_SECTION_GNU_HASH = 0x6ffffff6, + GUM_ELF_SECTION_GNU_LIBLIST = 0x6ffffff7, + GUM_ELF_SECTION_CHECKSUM = 0x6ffffff8, + GUM_ELF_SECTION_SUNW_MOVE = 0x6ffffffa, + GUM_ELF_SECTION_SUNW_COMDAT = 0x6ffffffb, + GUM_ELF_SECTION_SUNW_SYMINFO = 0x6ffffffc, + GUM_ELF_SECTION_GNU_VERDEF = 0x6ffffffd, + GUM_ELF_SECTION_GNU_VERNEED = 0x6ffffffe, + GUM_ELF_SECTION_GNU_VERSYM = 0x6fffffff, +} GumElfSectionType; typedef enum { - SH_OP_INVALID = 0, ///< = CS_OP_INVALID (Uninitialized). - SH_OP_REG, ///< = CS_OP_REG (Register operand). - SH_OP_IMM, ///< = CS_OP_IMM (Immediate operand). - SH_OP_MEM, ///< = CS_OP_MEM (Memory operand). -} sh_op_type; + GUM_ELF_SECTION_FLAG_WRITE = (1U << 0), + GUM_ELF_SECTION_FLAG_ALLOC = (1U << 1), + GUM_ELF_SECTION_FLAG_EXECINSTR = (1U << 2), + GUM_ELF_SECTION_FLAG_MERGE = (1U << 4), + GUM_ELF_SECTION_FLAG_STRINGS = (1U << 5), + GUM_ELF_SECTION_FLAG_INFO_LINK = (1U << 6), + GUM_ELF_SECTION_FLAG_LINK_ORDER = (1U << 7), + GUM_ELF_SECTION_FLAG_OS_NONCONFORMING = (1U << 8), + GUM_ELF_SECTION_FLAG_GROUP = (1U << 9), + GUM_ELF_SECTION_FLAG_TLS = (1U << 10), + GUM_ELF_SECTION_FLAG_COMPRESSED = (1U << 11), + GUM_ELF_SECTION_FLAG_GNU_RETAIN = (1U << 21), + GUM_ELF_SECTION_FLAG_ORDERED = (1U << 30), + GUM_ELF_SECTION_FLAG_EXCLUDE = (1U << 31), +} GumElfSectionFlags; + +#define GUM_ELF_SECTION_MASK_OS 0x0ff00000 +#define GUM_ELF_SECTION_MASK_PROCESSOR 0xf0000000 typedef enum { - SH_OP_MEM_INVALID = 0, /// <= Invalid - SH_OP_MEM_REG_IND, /// <= Register indirect - SH_OP_MEM_REG_POST, /// <= Register post increment - SH_OP_MEM_REG_PRE, /// <= Register pre decrement - SH_OP_MEM_REG_DISP, /// <= displacement - SH_OP_MEM_REG_R0, /// <= R0 indexed - SH_OP_MEM_GBR_DISP, /// <= GBR based displacement - SH_OP_MEM_GBR_R0, /// <= GBR based R0 indexed - SH_OP_MEM_PCR, /// <= PC relative - SH_OP_MEM_TBR_DISP, /// <= TBR based displaysment -} sh_op_mem_type; + GUM_ELF_DYNAMIC_NULL, + GUM_ELF_DYNAMIC_NEEDED, + GUM_ELF_DYNAMIC_PLTRELSZ, + GUM_ELF_DYNAMIC_PLTGOT, + GUM_ELF_DYNAMIC_HASH, + GUM_ELF_DYNAMIC_STRTAB, + GUM_ELF_DYNAMIC_SYMTAB, + GUM_ELF_DYNAMIC_RELA, + GUM_ELF_DYNAMIC_RELASZ, + GUM_ELF_DYNAMIC_RELAENT, + GUM_ELF_DYNAMIC_STRSZ, + GUM_ELF_DYNAMIC_SYMENT, + GUM_ELF_DYNAMIC_INIT, + GUM_ELF_DYNAMIC_FINI, + GUM_ELF_DYNAMIC_SONAME, + GUM_ELF_DYNAMIC_RPATH, + GUM_ELF_DYNAMIC_SYMBOLIC, + GUM_ELF_DYNAMIC_REL, + GUM_ELF_DYNAMIC_RELSZ, + GUM_ELF_DYNAMIC_RELENT, + GUM_ELF_DYNAMIC_PLTREL, + GUM_ELF_DYNAMIC_DEBUG, + GUM_ELF_DYNAMIC_TEXTREL, + GUM_ELF_DYNAMIC_JMPREL, + GUM_ELF_DYNAMIC_BIND_NOW, + GUM_ELF_DYNAMIC_INIT_ARRAY, + GUM_ELF_DYNAMIC_FINI_ARRAY, + GUM_ELF_DYNAMIC_INIT_ARRAYSZ, + GUM_ELF_DYNAMIC_FINI_ARRAYSZ, + GUM_ELF_DYNAMIC_RUNPATH, + GUM_ELF_DYNAMIC_FLAGS, + GUM_ELF_DYNAMIC_ENCODING = 32, + GUM_ELF_DYNAMIC_PREINIT_ARRAY = 32, + GUM_ELF_DYNAMIC_PREINIT_ARRAYSZ, + GUM_ELF_DYNAMIC_MAXPOSTAGS, -typedef struct sh_op_mem { - sh_op_mem_type address; /// <= memory address - sh_reg reg; /// <= base register - uint32_t disp; /// <= displacement -} sh_op_mem; + GUM_ELF_DYNAMIC_LOOS = 0x6000000d, + GUM_ELF_DYNAMIC_SUNW_AUXILIARY = 0x6000000d, + GUM_ELF_DYNAMIC_SUNW_RTLDINF = 0x6000000e, + GUM_ELF_DYNAMIC_SUNW_FILTER = 0x6000000f, + GUM_ELF_DYNAMIC_SUNW_CAP = 0x60000010, + GUM_ELF_DYNAMIC_SUNW_ASLR = 0x60000023, + GUM_ELF_DYNAMIC_HIOS = 0x6ffff000, -// SH-DSP instcutions define -typedef enum sh_dsp_insn_type { - SH_INS_DSP_INVALID, - SH_INS_DSP_DOUBLE, - SH_INS_DSP_SINGLE, - SH_INS_DSP_PARALLEL, -} sh_dsp_insn_type; + GUM_ELF_DYNAMIC_VALRNGLO = 0x6ffffd00, + GUM_ELF_DYNAMIC_GNU_PRELINKED = 0x6ffffdf5, + GUM_ELF_DYNAMIC_GNU_CONFLICTSZ = 0x6ffffdf6, + GUM_ELF_DYNAMIC_GNU_LIBLISTSZ = 0x6ffffdf7, + GUM_ELF_DYNAMIC_CHECKSUM = 0x6ffffdf8, + GUM_ELF_DYNAMIC_PLTPADSZ = 0x6ffffdf9, + GUM_ELF_DYNAMIC_MOVEENT = 0x6ffffdfa, + GUM_ELF_DYNAMIC_MOVESZ = 0x6ffffdfb, + GUM_ELF_DYNAMIC_FEATURE = 0x6ffffdfc, + GUM_ELF_DYNAMIC_FEATURE_1 = 0x6ffffdfc, + GUM_ELF_DYNAMIC_POSFLAG_1 = 0x6ffffdfd, -typedef enum sh_dsp_insn { - SH_INS_DSP_NOP = 1, - SH_INS_DSP_MOV, - SH_INS_DSP_PSHL, - SH_INS_DSP_PSHA, - SH_INS_DSP_PMULS, - SH_INS_DSP_PCLR_PMULS, - SH_INS_DSP_PSUB_PMULS, - SH_INS_DSP_PADD_PMULS, - SH_INS_DSP_PSUBC, - SH_INS_DSP_PADDC, - SH_INS_DSP_PCMP, - SH_INS_DSP_PABS, - SH_INS_DSP_PRND, - SH_INS_DSP_PSUB, - SH_INS_DSP_PSUBr, - SH_INS_DSP_PADD, - SH_INS_DSP_PAND, - SH_INS_DSP_PXOR, - SH_INS_DSP_POR, - SH_INS_DSP_PDEC, - SH_INS_DSP_PINC, - SH_INS_DSP_PCLR, - SH_INS_DSP_PDMSB, - SH_INS_DSP_PNEG, - SH_INS_DSP_PCOPY, - SH_INS_DSP_PSTS, - SH_INS_DSP_PLDS, - SH_INS_DSP_PSWAP, - SH_INS_DSP_PWAD, - SH_INS_DSP_PWSB, -} sh_dsp_insn; + GUM_ELF_DYNAMIC_SYMINSZ = 0x6ffffdfe, + GUM_ELF_DYNAMIC_SYMINENT = 0x6ffffdff, + GUM_ELF_DYNAMIC_VALRNGHI = 0x6ffffdff, -typedef enum sh_dsp_operand { - SH_OP_DSP_INVALID, - SH_OP_DSP_REG_PRE, - SH_OP_DSP_REG_IND, - SH_OP_DSP_REG_POST, - SH_OP_DSP_REG_INDEX, - SH_OP_DSP_REG, - SH_OP_DSP_IMM, - -} sh_dsp_operand; + GUM_ELF_DYNAMIC_ADDRRNGLO = 0x6ffffe00, + GUM_ELF_DYNAMIC_GNU_HASH = 0x6ffffef5, + GUM_ELF_DYNAMIC_TLSDESC_PLT = 0x6ffffef6, + GUM_ELF_DYNAMIC_TLSDESC_GOT = 0x6ffffef7, + GUM_ELF_DYNAMIC_GNU_CONFLICT = 0x6ffffef8, + GUM_ELF_DYNAMIC_GNU_LIBLIST = 0x6ffffef9, + GUM_ELF_DYNAMIC_CONFIG = 0x6ffffefa, + GUM_ELF_DYNAMIC_DEPAUDIT = 0x6ffffefb, + GUM_ELF_DYNAMIC_AUDIT = 0x6ffffefc, + GUM_ELF_DYNAMIC_PLTPAD = 0x6ffffefd, + GUM_ELF_DYNAMIC_MOVETAB = 0x6ffffefe, + GUM_ELF_DYNAMIC_SYMINFO = 0x6ffffeff, + GUM_ELF_DYNAMIC_ADDRRNGHI = 0x6ffffeff, -typedef enum sh_dsp_cc { - SH_DSP_CC_INVALID, - SH_DSP_CC_NONE, - SH_DSP_CC_DCT, - SH_DSP_CC_DCF, -} sh_dsp_cc; + GUM_ELF_DYNAMIC_VERSYM = 0x6ffffff0, + GUM_ELF_DYNAMIC_RELACOUNT = 0x6ffffff9, + GUM_ELF_DYNAMIC_RELCOUNT = 0x6ffffffa, + GUM_ELF_DYNAMIC_FLAGS_1 = 0x6ffffffb, + GUM_ELF_DYNAMIC_VERDEF = 0x6ffffffc, + GUM_ELF_DYNAMIC_VERDEFNUM = 0x6ffffffd, + GUM_ELF_DYNAMIC_VERNEED = 0x6ffffffe, + GUM_ELF_DYNAMIC_VERNEEDNUM = 0x6fffffff, -typedef struct sh_op_dsp { - sh_dsp_insn insn; - sh_dsp_operand operand[2]; - sh_reg r[6]; - sh_dsp_cc cc; - uint8_t imm; - int size; -} sh_op_dsp; - -/// Instruction operand -typedef struct cs_sh_op { - sh_op_type type; - union { - uint64_t imm; ///< immediate value for IMM operand - sh_reg reg; ///< register value for REG operand - sh_op_mem mem; ///< data when operand is targeting memory - sh_op_dsp dsp; ///< dsp instruction - }; -} cs_sh_op; + GUM_ELF_DYNAMIC_LOPROC = 0x70000000, -/// SH instruction -typedef enum sh_insn { - SH_INS_INVALID, - SH_INS_ADD_r, - SH_INS_ADD, - SH_INS_ADDC, - SH_INS_ADDV, - SH_INS_AND, - SH_INS_BAND, - SH_INS_BANDNOT, - SH_INS_BCLR, - SH_INS_BF, - SH_INS_BF_S, - SH_INS_BLD, - SH_INS_BLDNOT, - SH_INS_BOR, - SH_INS_BORNOT, - SH_INS_BRA, - SH_INS_BRAF, - SH_INS_BSET, - SH_INS_BSR, - SH_INS_BSRF, - SH_INS_BST, - SH_INS_BT, - SH_INS_BT_S, - SH_INS_BXOR, - SH_INS_CLIPS, - SH_INS_CLIPU, - SH_INS_CLRDMXY, - SH_INS_CLRMAC, - SH_INS_CLRS, - SH_INS_CLRT, - SH_INS_CMP_EQ, - SH_INS_CMP_GE, - SH_INS_CMP_GT, - SH_INS_CMP_HI, - SH_INS_CMP_HS, - SH_INS_CMP_PL, - SH_INS_CMP_PZ, - SH_INS_CMP_STR, - SH_INS_DIV0S, - SH_INS_DIV0U, - SH_INS_DIV1, - SH_INS_DIVS, - SH_INS_DIVU, - SH_INS_DMULS_L, - SH_INS_DMULU_L, - SH_INS_DT, - SH_INS_EXTS_B, - SH_INS_EXTS_W, - SH_INS_EXTU_B, - SH_INS_EXTU_W, - SH_INS_FABS, - SH_INS_FADD, - SH_INS_FCMP_EQ, - SH_INS_FCMP_GT, - SH_INS_FCNVDS, - SH_INS_FCNVSD, - SH_INS_FDIV, - SH_INS_FIPR, - SH_INS_FLDI0, - SH_INS_FLDI1, - SH_INS_FLDS, - SH_INS_FLOAT, - SH_INS_FMAC, - SH_INS_FMOV, - SH_INS_FMUL, - SH_INS_FNEG, - SH_INS_FPCHG, - SH_INS_FRCHG, - SH_INS_FSCA, - SH_INS_FSCHG, - SH_INS_FSQRT, - SH_INS_FSRRA, - SH_INS_FSTS, - SH_INS_FSUB, - SH_INS_FTRC, - SH_INS_FTRV, - SH_INS_ICBI, - SH_INS_JMP, - SH_INS_JSR, - SH_INS_JSR_N, - SH_INS_LDBANK, - SH_INS_LDC, - SH_INS_LDRC, - SH_INS_LDRE, - SH_INS_LDRS, - SH_INS_LDS, - SH_INS_LDTLB, - SH_INS_MAC_L, - SH_INS_MAC_W, - SH_INS_MOV, - SH_INS_MOVA, - SH_INS_MOVCA, - SH_INS_MOVCO, - SH_INS_MOVI20, - SH_INS_MOVI20S, - SH_INS_MOVLI, - SH_INS_MOVML, - SH_INS_MOVMU, - SH_INS_MOVRT, - SH_INS_MOVT, - SH_INS_MOVU, - SH_INS_MOVUA, - SH_INS_MUL_L, - SH_INS_MULR, - SH_INS_MULS_W, - SH_INS_MULU_W, - SH_INS_NEG, - SH_INS_NEGC, - SH_INS_NOP, - SH_INS_NOT, - SH_INS_NOTT, - SH_INS_OCBI, - SH_INS_OCBP, - SH_INS_OCBWB, - SH_INS_OR, - SH_INS_PREF, - SH_INS_PREFI, - SH_INS_RESBANK, - SH_INS_ROTCL, - SH_INS_ROTCR, - SH_INS_ROTL, - SH_INS_ROTR, - SH_INS_RTE, - SH_INS_RTS, - SH_INS_RTS_N, - SH_INS_RTV_N, - SH_INS_SETDMX, - SH_INS_SETDMY, - SH_INS_SETRC, - SH_INS_SETS, - SH_INS_SETT, - SH_INS_SHAD, - SH_INS_SHAL, - SH_INS_SHAR, - SH_INS_SHLD, - SH_INS_SHLL, - SH_INS_SHLL16, - SH_INS_SHLL2, - SH_INS_SHLL8, - SH_INS_SHLR, - SH_INS_SHLR16, - SH_INS_SHLR2, - SH_INS_SHLR8, - SH_INS_SLEEP, - SH_INS_STBANK, - SH_INS_STC, - SH_INS_STS, - SH_INS_SUB, - SH_INS_SUBC, - SH_INS_SUBV, - SH_INS_SWAP_B, - SH_INS_SWAP_W, - SH_INS_SYNCO, - SH_INS_TAS, - SH_INS_TRAPA, - SH_INS_TST, - SH_INS_XOR, - SH_INS_XTRCT, - SH_INS_DSP, - SH_INS_ENDING, // <-- mark the end of the list of instructions -} sh_insn; + GUM_ELF_DYNAMIC_ARM_SYMTABSZ = 0x70000001, + GUM_ELF_DYNAMIC_ARM_PREEMPTMAP = 0x70000002, -/// Instruction structure -typedef struct cs_sh { - sh_insn insn; - uint8_t size; - uint8_t op_count; - cs_sh_op operands[3]; -} cs_sh; + GUM_ELF_DYNAMIC_SPARC_REGISTER = 0x70000001, + GUM_ELF_DYNAMIC_DEPRECATED_SPARC_REGISTER = 0x7000001, -/// Group of SH instructions -typedef enum sh_insn_group { - SH_GRP_INVALID = 0, ///< CS_GRUP_INVALID - SH_GRP_JUMP, ///< = CS_GRP_JUMP - SH_GRP_CALL, ///< = CS_GRP_CALL - SH_GRP_INT, ///< = CS_GRP_INT - SH_GRP_RET, ///< = CS_GRP_RET - SH_GRP_IRET, ///< = CS_GRP_IRET - SH_GRP_PRIVILEGE, ///< = CS_GRP_PRIVILEGE - SH_GRP_BRANCH_RELATIVE, ///< = CS_GRP_BRANCH_RELATIVE + GUM_ELF_DYNAMIC_MIPS_RLD_VERSION = 0x70000001, + GUM_ELF_DYNAMIC_MIPS_TIME_STAMP = 0x70000002, + GUM_ELF_DYNAMIC_MIPS_ICHECKSUM = 0x70000003, + GUM_ELF_DYNAMIC_MIPS_IVERSION = 0x70000004, + GUM_ELF_DYNAMIC_MIPS_FLAGS = 0x70000005, + GUM_ELF_DYNAMIC_MIPS_BASE_ADDRESS = 0x70000006, + GUM_ELF_DYNAMIC_MIPS_CONFLICT = 0x70000008, + GUM_ELF_DYNAMIC_MIPS_LIBLIST = 0x70000009, + GUM_ELF_DYNAMIC_MIPS_LOCAL_GOTNO = 0x7000000a, + GUM_ELF_DYNAMIC_MIPS_CONFLICTNO = 0x7000000b, + GUM_ELF_DYNAMIC_MIPS_LIBLISTNO = 0x70000010, + GUM_ELF_DYNAMIC_MIPS_SYMTABNO = 0x70000011, + GUM_ELF_DYNAMIC_MIPS_UNREFEXTNO = 0x70000012, + GUM_ELF_DYNAMIC_MIPS_GOTSYM = 0x70000013, + GUM_ELF_DYNAMIC_MIPS_HIPAGENO = 0x70000014, + GUM_ELF_DYNAMIC_MIPS_RLD_MAP = 0x70000016, + GUM_ELF_DYNAMIC_MIPS_DELTA_CLASS = 0x70000017, + GUM_ELF_DYNAMIC_MIPS_DELTA_CLASS_NO = 0x70000018, + GUM_ELF_DYNAMIC_MIPS_DELTA_INSTANCE = 0x70000019, + GUM_ELF_DYNAMIC_MIPS_DELTA_INSTANCE_NO = 0x7000001a, + GUM_ELF_DYNAMIC_MIPS_DELTA_RELOC = 0x7000001b, + GUM_ELF_DYNAMIC_MIPS_DELTA_RELOC_NO = 0x7000001c, + GUM_ELF_DYNAMIC_MIPS_DELTA_SYM = 0x7000001d, + GUM_ELF_DYNAMIC_MIPS_DELTA_SYM_NO = 0x7000001e, + GUM_ELF_DYNAMIC_MIPS_DELTA_CLASSSYM = 0x70000020, + GUM_ELF_DYNAMIC_MIPS_DELTA_CLASSSYM_NO = 0x70000021, + GUM_ELF_DYNAMIC_MIPS_CXX_FLAGS = 0x70000022, + GUM_ELF_DYNAMIC_MIPS_PIXIE_INIT = 0x70000023, + GUM_ELF_DYNAMIC_MIPS_SYMBOL_LIB = 0x70000024, + GUM_ELF_DYNAMIC_MIPS_LOCALPAGE_GOTIDX = 0x70000025, + GUM_ELF_DYNAMIC_MIPS_LOCAL_GOTIDX = 0x70000026, + GUM_ELF_DYNAMIC_MIPS_HIDDEN_GOTIDX = 0x70000027, + GUM_ELF_DYNAMIC_MIPS_PROTECTED_GOTIDX = 0x70000028, + GUM_ELF_DYNAMIC_MIPS_OPTIONS = 0x70000029, + GUM_ELF_DYNAMIC_MIPS_INTERFACE = 0x7000002a, + GUM_ELF_DYNAMIC_MIPS_DYNSTR_ALIGN = 0x7000002b, + GUM_ELF_DYNAMIC_MIPS_INTERFACE_SIZE = 0x7000002c, + GUM_ELF_DYNAMIC_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d, + GUM_ELF_DYNAMIC_MIPS_PERF_SUFFIX = 0x7000002e, + GUM_ELF_DYNAMIC_MIPS_COMPACT_SIZE = 0x7000002f, + GUM_ELF_DYNAMIC_MIPS_GP_VALUE = 0x70000030, + GUM_ELF_DYNAMIC_MIPS_AUX_DYNAMIC = 0x70000031, + GUM_ELF_DYNAMIC_MIPS_PLTGOT = 0x70000032, + GUM_ELF_DYNAMIC_MIPS_RLD_OBJ_UPDATE = 0x70000033, + GUM_ELF_DYNAMIC_MIPS_RWPLT = 0x70000034, + GUM_ELF_DYNAMIC_MIPS_RLD_MAP_REL = 0x70000035, - SH_GRP_SH1, - SH_GRP_SH2, - SH_GRP_SH2E, - SH_GRP_SH2DSP, - SH_GRP_SH2A, - SH_GRP_SH2AFPU, - SH_GRP_SH3, - SH_GRP_SH3DSP, - SH_GRP_SH4, - SH_GRP_SH4A, - - SH_GRP_ENDING,// <-- mark the end of the list of groups -} sh_insn_group; + GUM_ELF_DYNAMIC_PPC_GOT = 0x70000000, + GUM_ELF_DYNAMIC_PPC_TLSOPT = 0x70000001, -#ifdef __cplusplus -} -#endif + GUM_ELF_DYNAMIC_PPC64_GLINK = 0x70000000, + GUM_ELF_DYNAMIC_PPC64_OPD = 0x70000001, + GUM_ELF_DYNAMIC_PPC64_OPDSZ = 0x70000002, + GUM_ELF_DYNAMIC_PPC64_TLSOPT = 0x70000003, -#endif -#ifndef CAPSTONE_TRICORE_H -#define CAPSTONE_TRICORE_H + GUM_ELF_DYNAMIC_AUXILIARY = 0x7ffffffd, + GUM_ELF_DYNAMIC_USED = 0x7ffffffe, + GUM_ELF_DYNAMIC_FILTER = 0x7fffffff, -/* Capstone Disassembly Engine */ -/* By Nguyen Anh Quynh , 2014 */ + GUM_ELF_DYNAMIC_HIPROC = 0x7fffffff, +} GumElfDynamicTag; -#ifdef __cplusplus -extern "C" { -#endif +typedef enum { + GUM_ELF_SHDR_INDEX_UNDEF, + GUM_ELF_SHDR_INDEX_BEFORE = 0xff00, + GUM_ELF_SHDR_INDEX_AFTER = 0xff01, + GUM_ELF_SHDR_INDEX_ABS = 0xfff1, + GUM_ELF_SHDR_INDEX_COMMON = 0xfff2, + GUM_ELF_SHDR_INDEX_XINDEX = 0xffff, +} GumElfShdrIndex; -#if !defined(_MSC_VER) || !defined(_KERNEL_MODE) -#include -#endif +typedef enum { + GUM_ELF_SYMBOL_NOTYPE, + GUM_ELF_SYMBOL_OBJECT, + GUM_ELF_SYMBOL_FUNC, + GUM_ELF_SYMBOL_SECTION, + GUM_ELF_SYMBOL_FILE, + GUM_ELF_SYMBOL_COMMON, + GUM_ELF_SYMBOL_TLS, + GUM_ELF_SYMBOL_NUM, + GUM_ELF_SYMBOL_LOOS = 10, + GUM_ELF_SYMBOL_GNU_IFUNC = 10, + GUM_ELF_SYMBOL_HIOS = 12, + GUM_ELF_SYMBOL_LOPROC, + GUM_ELF_SYMBOL_SPARC_REGISTER = 13, + GUM_ELF_SYMBOL_HIPROC = 15, +} GumElfSymbolType; +typedef enum { + GUM_ELF_BIND_LOCAL, + GUM_ELF_BIND_GLOBAL, + GUM_ELF_BIND_WEAK, -#ifdef _MSC_VER -#pragma warning(disable : 4201) -#endif + GUM_ELF_BIND_LOOS = 10, + GUM_ELF_BIND_GNU_UNIQUE = 10, + GUM_ELF_BIND_HIOS = 12, -/// Operand type for instruction's operands -typedef enum tricore_op_type { - TRICORE_OP_INVALID = CS_OP_INVALID, ///< CS_OP_INVALID (Uninitialized). - TRICORE_OP_REG = CS_OP_REG, ///< CS_OP_REG (Register operand). - TRICORE_OP_IMM = CS_OP_IMM, ///< CS_OP_IMM (Immediate operand). - TRICORE_OP_MEM = CS_OP_MEM, ///< CS_OP_MEM (Memory operand). -} tricore_op_type; + GUM_ELF_BIND_LOPROC, + GUM_ELF_BIND_HIPROC = 15, +} GumElfSymbolBind; -/// Instruction's operand referring to memory -/// This is associated with TRICORE_OP_MEM operand type above -typedef struct tricore_op_mem { - uint8_t base; ///< base register - int32_t disp; ///< displacement/offset value -} tricore_op_mem; +typedef enum { + GUM_ELF_IA32_NONE, + GUM_ELF_IA32_32, + GUM_ELF_IA32_PC32, + GUM_ELF_IA32_GOT32, + GUM_ELF_IA32_PLT32, + GUM_ELF_IA32_COPY, + GUM_ELF_IA32_GLOB_DAT, + GUM_ELF_IA32_JMP_SLOT, + GUM_ELF_IA32_RELATIVE, + GUM_ELF_IA32_GOTOFF, + GUM_ELF_IA32_GOTPC, + GUM_ELF_IA32_32PLT, + GUM_ELF_IA32_TLS_TPOFF = 14, + GUM_ELF_IA32_TLS_IE, + GUM_ELF_IA32_TLS_GOTIE, + GUM_ELF_IA32_TLS_LE, + GUM_ELF_IA32_TLS_GD, + GUM_ELF_IA32_TLS_LDM, + GUM_ELF_IA32_16, + GUM_ELF_IA32_PC16, + GUM_ELF_IA32_8, + GUM_ELF_IA32_PC8, + GUM_ELF_IA32_TLS_GD_32, + GUM_ELF_IA32_TLS_GD_PUSH, + GUM_ELF_IA32_TLS_GD_CALL, + GUM_ELF_IA32_TLS_GD_POP, + GUM_ELF_IA32_TLS_LDM_32, + GUM_ELF_IA32_TLS_LDM_PUSH, + GUM_ELF_IA32_TLS_LDM_CALL, + GUM_ELF_IA32_TLS_LDM_POP, + GUM_ELF_IA32_TLS_LDO_32, + GUM_ELF_IA32_TLS_IE_32, + GUM_ELF_IA32_TLS_LE_32, + GUM_ELF_IA32_TLS_DTPMOD32, + GUM_ELF_IA32_TLS_DTPOFF32, + GUM_ELF_IA32_TLS_TPOFF32, + GUM_ELF_IA32_SIZE32, + GUM_ELF_IA32_TLS_GOTDESC, + GUM_ELF_IA32_TLS_DESC_CALL, + GUM_ELF_IA32_TLS_DESC, + GUM_ELF_IA32_IRELATIVE, + GUM_ELF_IA32_GOT32X, +} GumElfIA32Relocation; -/// Instruction operand -typedef struct cs_tricore_op { - tricore_op_type type; ///< operand type - union { - unsigned int reg; ///< register value for REG operand - int32_t imm; ///< immediate value for IMM operand - tricore_op_mem mem; ///< base/disp value for MEM operand - }; - /// This field is combined of cs_ac_type. - /// NOTE: this field is irrelevant if engine is compiled in DIET mode. - uint8_t access; ///< How is this operand accessed? (READ, WRITE or READ|WRITE) -} cs_tricore_op; +typedef enum { + GUM_ELF_X64_NONE, + GUM_ELF_X64_64, + GUM_ELF_X64_PC32, + GUM_ELF_X64_GOT32, + GUM_ELF_X64_PLT32, + GUM_ELF_X64_COPY, + GUM_ELF_X64_GLOB_DAT, + GUM_ELF_X64_JUMP_SLOT, + GUM_ELF_X64_RELATIVE, + GUM_ELF_X64_GOTPCREL, + GUM_ELF_X64_32, + GUM_ELF_X64_32S, + GUM_ELF_X64_16, + GUM_ELF_X64_PC16, + GUM_ELF_X64_8, + GUM_ELF_X64_PC8, + GUM_ELF_X64_DTPMOD64, + GUM_ELF_X64_DTPOFF64, + GUM_ELF_X64_TPOFF64, + GUM_ELF_X64_TLSGD, + GUM_ELF_X64_TLSLD, + GUM_ELF_X64_DTPOFF32, + GUM_ELF_X64_GOTTPOFF, + GUM_ELF_X64_TPOFF32, + GUM_ELF_X64_PC64, + GUM_ELF_X64_GOTOFF64, + GUM_ELF_X64_GOTPC32, + GUM_ELF_X64_GOT64, + GUM_ELF_X64_GOTPCREL64, + GUM_ELF_X64_GOTPC64, + GUM_ELF_X64_GOTPLT64, + GUM_ELF_X64_PLTOFF64, + GUM_ELF_X64_SIZE32, + GUM_ELF_X64_SIZE64, + GUM_ELF_X64_GOTPC32_TLSDESC, + GUM_ELF_X64_TLSDESC_CALL, + GUM_ELF_X64_TLSDESC, + GUM_ELF_X64_IRELATIVE, + GUM_ELF_X64_RELATIVE64, + GUM_ELF_X64_GOTPCRELX = 41, + GUM_ELF_X64_REX_GOTPCRELX, +} GumElfX64Relocation; -#define TRICORE_OP_COUNT 8 +typedef enum { + GUM_ELF_ARM_NONE, + GUM_ELF_ARM_PC24, + GUM_ELF_ARM_ABS32, + GUM_ELF_ARM_REL32, + GUM_ELF_ARM_PC13, + GUM_ELF_ARM_ABS16, + GUM_ELF_ARM_ABS12, + GUM_ELF_ARM_THM_ABS5, + GUM_ELF_ARM_ABS8, + GUM_ELF_ARM_SBREL32, + GUM_ELF_ARM_THM_PC22, + GUM_ELF_ARM_THM_PC8, + GUM_ELF_ARM_AMP_VCALL9, + GUM_ELF_ARM_SWI24, + GUM_ELF_ARM_TLS_DESC = 13, + GUM_ELF_ARM_THM_SWI8, + GUM_ELF_ARM_XPC25, + GUM_ELF_ARM_THM_XPC22, + GUM_ELF_ARM_TLS_DTPMOD32, + GUM_ELF_ARM_TLS_DTPOFF32, + GUM_ELF_ARM_TLS_TPOFF32, + GUM_ELF_ARM_COPY, + GUM_ELF_ARM_GLOB_DAT, + GUM_ELF_ARM_JUMP_SLOT, + GUM_ELF_ARM_RELATIVE, + GUM_ELF_ARM_GOTOFF, + GUM_ELF_ARM_GOTPC, + GUM_ELF_ARM_GOT32, + GUM_ELF_ARM_PLT32, + GUM_ELF_ARM_CALL, + GUM_ELF_ARM_JUMP24, + GUM_ELF_ARM_THM_JUMP24, + GUM_ELF_ARM_BASE_ABS, + GUM_ELF_ARM_ALU_PCREL_7_0, + GUM_ELF_ARM_ALU_PCREL_15_8, + GUM_ELF_ARM_ALU_PCREL_23_15, + GUM_ELF_ARM_LDR_SBREL_11_0, + GUM_ELF_ARM_ALU_SBREL_19_12, + GUM_ELF_ARM_ALU_SBREL_27_20, + GUM_ELF_ARM_TARGET1, + GUM_ELF_ARM_SBREL31, + GUM_ELF_ARM_V4BX, + GUM_ELF_ARM_TARGET2, + GUM_ELF_ARM_PREL31, + GUM_ELF_ARM_MOVW_ABS_NC, + GUM_ELF_ARM_MOVT_ABS, + GUM_ELF_ARM_MOVW_PREL_NC, + GUM_ELF_ARM_MOVT_PREL, + GUM_ELF_ARM_THM_MOVW_ABS_NC, + GUM_ELF_ARM_THM_MOVT_ABS, + GUM_ELF_ARM_THM_MOVW_PREL_NC, + GUM_ELF_ARM_THM_MOVT_PREL, + GUM_ELF_ARM_THM_JUMP19, + GUM_ELF_ARM_THM_JUMP6, + GUM_ELF_ARM_THM_ALU_PREL_11_0, + GUM_ELF_ARM_THM_PC12, + GUM_ELF_ARM_ABS32_NOI, + GUM_ELF_ARM_REL32_NOI, + GUM_ELF_ARM_ALU_PC_G0_NC, + GUM_ELF_ARM_ALU_PC_G0, + GUM_ELF_ARM_ALU_PC_G1_NC, + GUM_ELF_ARM_ALU_PC_G1, + GUM_ELF_ARM_ALU_PC_G2, + GUM_ELF_ARM_LDR_PC_G1, + GUM_ELF_ARM_LDR_PC_G2, + GUM_ELF_ARM_LDRS_PC_G0, + GUM_ELF_ARM_LDRS_PC_G1, + GUM_ELF_ARM_LDRS_PC_G2, + GUM_ELF_ARM_LDC_PC_G0, + GUM_ELF_ARM_LDC_PC_G1, + GUM_ELF_ARM_LDC_PC_G2, + GUM_ELF_ARM_ALU_SB_G0_NC, + GUM_ELF_ARM_ALU_SB_G0, + GUM_ELF_ARM_ALU_SB_G1_NC, + GUM_ELF_ARM_ALU_SB_G1, + GUM_ELF_ARM_ALU_SB_G2, + GUM_ELF_ARM_LDR_SB_G0, + GUM_ELF_ARM_LDR_SB_G1, + GUM_ELF_ARM_LDR_SB_G2, + GUM_ELF_ARM_LDRS_SB_G0, + GUM_ELF_ARM_LDRS_SB_G1, + GUM_ELF_ARM_LDRS_SB_G2, + GUM_ELF_ARM_LDC_SB_G0, + GUM_ELF_ARM_LDC_SB_G1, + GUM_ELF_ARM_LDC_SB_G2, + GUM_ELF_ARM_MOVW_BREL_NC, + GUM_ELF_ARM_MOVT_BREL, + GUM_ELF_ARM_MOVW_BREL, + GUM_ELF_ARM_THM_MOVW_BREL_NC, + GUM_ELF_ARM_THM_MOVT_BREL, + GUM_ELF_ARM_THM_MOVW_BREL, + GUM_ELF_ARM_TLS_GOTDESC, + GUM_ELF_ARM_TLS_CALL, + GUM_ELF_ARM_TLS_DESCSEQ, + GUM_ELF_ARM_THM_TLS_CALL, + GUM_ELF_ARM_PLT32_ABS, + GUM_ELF_ARM_GOT_ABS, + GUM_ELF_ARM_GOT_PREL, + GUM_ELF_ARM_GOT_BREL12, + GUM_ELF_ARM_GOTOFF12, + GUM_ELF_ARM_GOTRELAX, + GUM_ELF_ARM_GNU_VTENTRY, + GUM_ELF_ARM_GNU_VTINHERIT, + GUM_ELF_ARM_THM_PC11, + GUM_ELF_ARM_THM_PC9, + GUM_ELF_ARM_TLS_GD32, + GUM_ELF_ARM_TLS_LDM32, + GUM_ELF_ARM_TLS_LDO32, + GUM_ELF_ARM_TLS_IE32, + GUM_ELF_ARM_TLS_LE32, + GUM_ELF_ARM_TLS_LDO12, + GUM_ELF_ARM_TLS_LE12, + GUM_ELF_ARM_TLS_IE12GP, + GUM_ELF_ARM_ME_TOO = 128, + GUM_ELF_ARM_THM_TLS_DESCSEQ, + GUM_ELF_ARM_THM_TLS_DESCSEQ16 = 129, + GUM_ELF_ARM_THM_TLS_DESCSEQ32, + GUM_ELF_ARM_THM_GOT_BREL12, + GUM_ELF_ARM_IRELATIVE = 160, + GUM_ELF_ARM_RXPC25 = 249, + GUM_ELF_ARM_RSBREL32, + GUM_ELF_ARM_THM_RPC22, + GUM_ELF_ARM_RREL32, + GUM_ELF_ARM_RABS22, + GUM_ELF_ARM_RPC24, + GUM_ELF_ARM_RBASE, +} GumElfArmRelocation; -/// Instruction structure -typedef struct cs_tricore { - uint8_t op_count; ///< number of operands of this instruction. - cs_tricore_op - operands[TRICORE_OP_COUNT]; ///< operands for this instruction. - /// TODO: Mark the modified flags register in td files and regenerate inc files - bool update_flags; ///< whether the flags register is updated. -} cs_tricore; +typedef enum { + GUM_ELF_ARM64_NONE, + GUM_ELF_ARM64_P32_ABS32, + GUM_ELF_ARM64_P32_COPY = 180, + GUM_ELF_ARM64_P32_GLOB_DAT, + GUM_ELF_ARM64_P32_JUMP_SLOT, + GUM_ELF_ARM64_P32_RELATIVE, + GUM_ELF_ARM64_P32_TLS_DTPMOD, + GUM_ELF_ARM64_P32_TLS_DTPREL, + GUM_ELF_ARM64_P32_TLS_TPREL, + GUM_ELF_ARM64_P32_TLSDESC, + GUM_ELF_ARM64_P32_IRELATIVE, + GUM_ELF_ARM64_ABS64 = 257, + GUM_ELF_ARM64_ABS32, + GUM_ELF_ARM64_ABS16, + GUM_ELF_ARM64_PREL64, + GUM_ELF_ARM64_PREL32, + GUM_ELF_ARM64_PREL16, + GUM_ELF_ARM64_MOVW_UABS_G0, + GUM_ELF_ARM64_MOVW_UABS_G0_NC, + GUM_ELF_ARM64_MOVW_UABS_G1, + GUM_ELF_ARM64_MOVW_UABS_G1_NC, + GUM_ELF_ARM64_MOVW_UABS_G2, + GUM_ELF_ARM64_MOVW_UABS_G2_NC, + GUM_ELF_ARM64_MOVW_UABS_G3, + GUM_ELF_ARM64_MOVW_SABS_G0, + GUM_ELF_ARM64_MOVW_SABS_G1, + GUM_ELF_ARM64_MOVW_SABS_G2, + GUM_ELF_ARM64_LD_PREL_LO19, + GUM_ELF_ARM64_ADR_PREL_LO21, + GUM_ELF_ARM64_ADR_PREL_PG_HI21, + GUM_ELF_ARM64_ADR_PREL_PG_HI21_NC, + GUM_ELF_ARM64_ADD_ABS_LO12_NC, + GUM_ELF_ARM64_LDST8_ABS_LO12_NC, + GUM_ELF_ARM64_TSTBR14, + GUM_ELF_ARM64_CONDBR19, + GUM_ELF_ARM64_JUMP26 = 282, + GUM_ELF_ARM64_CALL26, + GUM_ELF_ARM64_LDST16_ABS_LO12_NC, + GUM_ELF_ARM64_LDST32_ABS_LO12_NC, + GUM_ELF_ARM64_LDST64_ABS_LO12_NC, + GUM_ELF_ARM64_MOVW_PREL_G0, + GUM_ELF_ARM64_MOVW_PREL_G0_NC, + GUM_ELF_ARM64_MOVW_PREL_G1, + GUM_ELF_ARM64_MOVW_PREL_G1_NC, + GUM_ELF_ARM64_MOVW_PREL_G2, + GUM_ELF_ARM64_MOVW_PREL_G2_NC, + GUM_ELF_ARM64_MOVW_PREL_G3, + GUM_ELF_ARM64_LDST128_ABS_LO12_NC = 299, + GUM_ELF_ARM64_MOVW_GOTOFF_G0, + GUM_ELF_ARM64_MOVW_GOTOFF_G0_NC, + GUM_ELF_ARM64_MOVW_GOTOFF_G1, + GUM_ELF_ARM64_MOVW_GOTOFF_G1_NC, + GUM_ELF_ARM64_MOVW_GOTOFF_G2, + GUM_ELF_ARM64_MOVW_GOTOFF_G2_NC, + GUM_ELF_ARM64_MOVW_GOTOFF_G3, + GUM_ELF_ARM64_GOTREL64, + GUM_ELF_ARM64_GOTREL32, + GUM_ELF_ARM64_GOT_LD_PREL19, + GUM_ELF_ARM64_LD64_GOTOFF_LO15, + GUM_ELF_ARM64_ADR_GOT_PAGE, + GUM_ELF_ARM64_LD64_GOT_LO12_NC, + GUM_ELF_ARM64_LD64_GOTPAGE_LO15, + GUM_ELF_ARM64_TLSGD_ADR_PREL21 = 512, + GUM_ELF_ARM64_TLSGD_ADR_PAGE21, + GUM_ELF_ARM64_TLSGD_ADD_LO12_NC, + GUM_ELF_ARM64_TLSGD_MOVW_G1, + GUM_ELF_ARM64_TLSGD_MOVW_G0_NC, + GUM_ELF_ARM64_TLSLD_ADR_PREL21, + GUM_ELF_ARM64_TLSLD_ADR_PAGE21, + GUM_ELF_ARM64_TLSLD_ADD_LO12_NC, + GUM_ELF_ARM64_TLSLD_MOVW_G1, + GUM_ELF_ARM64_TLSLD_MOVW_G0_NC, + GUM_ELF_ARM64_TLSLD_LD_PREL19, + GUM_ELF_ARM64_TLSLD_MOVW_DTPREL_G2, + GUM_ELF_ARM64_TLSLD_MOVW_DTPREL_G1, + GUM_ELF_ARM64_TLSLD_MOVW_DTPREL_G1_NC, + GUM_ELF_ARM64_TLSLD_MOVW_DTPREL_G0, + GUM_ELF_ARM64_TLSLD_MOVW_DTPREL_G0_NC, + GUM_ELF_ARM64_TLSLD_ADD_DTPREL_HI12, + GUM_ELF_ARM64_TLSLD_ADD_DTPREL_LO12, + GUM_ELF_ARM64_TLSLD_ADD_DTPREL_LO12_NC, + GUM_ELF_ARM64_TLSLD_LDST8_DTPREL_LO12, + GUM_ELF_ARM64_TLSLD_LDST8_DTPREL_LO12_NC, + GUM_ELF_ARM64_TLSLD_LDST16_DTPREL_LO12, + GUM_ELF_ARM64_TLSLD_LDST16_DTPREL_LO12_NC, + GUM_ELF_ARM64_TLSLD_LDST32_DTPREL_LO12, + GUM_ELF_ARM64_TLSLD_LDST32_DTPREL_LO12_NC, + GUM_ELF_ARM64_TLSLD_LDST64_DTPREL_LO12, + GUM_ELF_ARM64_TLSLD_LDST64_DTPREL_LO12_NC, + GUM_ELF_ARM64_TLSIE_MOVW_GOTTPREL_G1, + GUM_ELF_ARM64_TLSIE_MOVW_GOTTPREL_G0_NC, + GUM_ELF_ARM64_TLSIE_ADR_GOTTPREL_PAGE21, + GUM_ELF_ARM64_TLSIE_LD64_GOTTPREL_LO12_NC, + GUM_ELF_ARM64_TLSIE_LD_GOTTPREL_PREL19, + GUM_ELF_ARM64_TLSLE_MOVW_TPREL_G2, + GUM_ELF_ARM64_TLSLE_MOVW_TPREL_G1, + GUM_ELF_ARM64_TLSLE_MOVW_TPREL_G1_NC, + GUM_ELF_ARM64_TLSLE_MOVW_TPREL_G0, + GUM_ELF_ARM64_TLSLE_MOVW_TPREL_G0_NC, + GUM_ELF_ARM64_TLSLE_ADD_TPREL_HI12, + GUM_ELF_ARM64_TLSLE_ADD_TPREL_LO12, + GUM_ELF_ARM64_TLSLE_ADD_TPREL_LO12_NC, + GUM_ELF_ARM64_TLSLE_LDST8_TPREL_LO12, + GUM_ELF_ARM64_TLSLE_LDST8_TPREL_LO12_NC, + GUM_ELF_ARM64_TLSLE_LDST16_TPREL_LO12, + GUM_ELF_ARM64_TLSLE_LDST16_TPREL_LO12_NC, + GUM_ELF_ARM64_TLSLE_LDST32_TPREL_LO12, + GUM_ELF_ARM64_TLSLE_LDST32_TPREL_LO12_NC, + GUM_ELF_ARM64_TLSLE_LDST64_TPREL_LO12, + GUM_ELF_ARM64_TLSLE_LDST64_TPREL_LO12_NC, + GUM_ELF_ARM64_TLSDESC_LD_PREL19, + GUM_ELF_ARM64_TLSDESC_ADR_PREL21, + GUM_ELF_ARM64_TLSDESC_ADR_PAGE21, + GUM_ELF_ARM64_TLSDESC_LD64_LO12, + GUM_ELF_ARM64_TLSDESC_ADD_LO12, + GUM_ELF_ARM64_TLSDESC_OFF_G1, + GUM_ELF_ARM64_TLSDESC_OFF_G0_NC, + GUM_ELF_ARM64_TLSDESC_LDR, + GUM_ELF_ARM64_TLSDESC_ADD, + GUM_ELF_ARM64_TLSDESC_CALL, + GUM_ELF_ARM64_TLSLE_LDST128_TPREL_LO12, + GUM_ELF_ARM64_TLSLE_LDST128_TPREL_LO12_NC, + GUM_ELF_ARM64_TLSLD_LDST128_DTPREL_LO12, + GUM_ELF_ARM64_TLSLD_LDST128_DTPREL_LO12_NC, + GUM_ELF_ARM64_COPY = 1024, + GUM_ELF_ARM64_GLOB_DAT, + GUM_ELF_ARM64_JUMP_SLOT, + GUM_ELF_ARM64_RELATIVE, + GUM_ELF_ARM64_TLS_DTPMOD, + GUM_ELF_ARM64_TLS_DTPREL, + GUM_ELF_ARM64_TLS_TPREL, + GUM_ELF_ARM64_TLSDESC, + GUM_ELF_ARM64_IRELATIVE, +} GumElfArm64Relocation; + +typedef enum { + GUM_ELF_MIPS_NONE, + GUM_ELF_MIPS_16, + GUM_ELF_MIPS_32, + GUM_ELF_MIPS_REL32, + GUM_ELF_MIPS_26, + GUM_ELF_MIPS_HI16, + GUM_ELF_MIPS_LO16, + GUM_ELF_MIPS_GPREL16, + GUM_ELF_MIPS_LITERAL, + GUM_ELF_MIPS_GOT16, + GUM_ELF_MIPS_PC16, + GUM_ELF_MIPS_CALL16, + GUM_ELF_MIPS_GPREL32, + GUM_ELF_MIPS_SHIFT5 = 16, + GUM_ELF_MIPS_SHIFT6, + GUM_ELF_MIPS_64, + GUM_ELF_MIPS_GOT_DISP, + GUM_ELF_MIPS_GOT_PAGE, + GUM_ELF_MIPS_GOT_OFST, + GUM_ELF_MIPS_GOT_HI16, + GUM_ELF_MIPS_GOT_LO16, + GUM_ELF_MIPS_SUB, + GUM_ELF_MIPS_INSERT_A, + GUM_ELF_MIPS_INSERT_B, + GUM_ELF_MIPS_DELETE, + GUM_ELF_MIPS_HIGHER, + GUM_ELF_MIPS_HIGHEST, + GUM_ELF_MIPS_CALL_HI16, + GUM_ELF_MIPS_CALL_LO16, + GUM_ELF_MIPS_SCN_DISP, + GUM_ELF_MIPS_REL16, + GUM_ELF_MIPS_ADD_IMMEDIATE, + GUM_ELF_MIPS_PJUMP, + GUM_ELF_MIPS_RELGOT, + GUM_ELF_MIPS_JALR, + GUM_ELF_MIPS_TLS_DTPMOD32, + GUM_ELF_MIPS_TLS_DTPREL32, + GUM_ELF_MIPS_TLS_DTPMOD64, + GUM_ELF_MIPS_TLS_DTPREL64, + GUM_ELF_MIPS_TLS_GD, + GUM_ELF_MIPS_TLS_LDM, + GUM_ELF_MIPS_TLS_DTPREL_HI16, + GUM_ELF_MIPS_TLS_DTPREL_LO16, + GUM_ELF_MIPS_TLS_GOTTPREL, + GUM_ELF_MIPS_TLS_TPREL32, + GUM_ELF_MIPS_TLS_TPREL64, + GUM_ELF_MIPS_TLS_TPREL_HI16, + GUM_ELF_MIPS_TLS_TPREL_LO16, + GUM_ELF_MIPS_GLOB_DAT, + GUM_ELF_MIPS_COPY = 126, + GUM_ELF_MIPS_JUMP_SLOT, +} GumElfMipsRelocation; -/// TriCore registers -typedef enum tricore_reg { - // generate content begin - // clang-format off +typedef struct _GumElfSegmentDetails GumElfSegmentDetails; +typedef struct _GumElfSectionDetails GumElfSectionDetails; +typedef struct _GumElfRelocationDetails GumElfRelocationDetails; +typedef struct _GumElfDynamicEntryDetails GumElfDynamicEntryDetails; +typedef struct _GumElfSymbolDetails GumElfSymbolDetails; - TRICORE_REG_INVALID = 0, - TRICORE_REG_FCX = 1, - TRICORE_REG_PC = 2, - TRICORE_REG_PCXI = 3, - TRICORE_REG_PSW = 4, - TRICORE_REG_A0 = 5, - TRICORE_REG_A1 = 6, - TRICORE_REG_A2 = 7, - TRICORE_REG_A3 = 8, - TRICORE_REG_A4 = 9, - TRICORE_REG_A5 = 10, - TRICORE_REG_A6 = 11, - TRICORE_REG_A7 = 12, - TRICORE_REG_A8 = 13, - TRICORE_REG_A9 = 14, - TRICORE_REG_A10 = 15, - TRICORE_REG_A11 = 16, - TRICORE_REG_A12 = 17, - TRICORE_REG_A13 = 18, - TRICORE_REG_A14 = 19, - TRICORE_REG_A15 = 20, - TRICORE_REG_D0 = 21, - TRICORE_REG_D1 = 22, - TRICORE_REG_D2 = 23, - TRICORE_REG_D3 = 24, - TRICORE_REG_D4 = 25, - TRICORE_REG_D5 = 26, - TRICORE_REG_D6 = 27, - TRICORE_REG_D7 = 28, - TRICORE_REG_D8 = 29, - TRICORE_REG_D9 = 30, - TRICORE_REG_D10 = 31, - TRICORE_REG_D11 = 32, - TRICORE_REG_D12 = 33, - TRICORE_REG_D13 = 34, - TRICORE_REG_D14 = 35, - TRICORE_REG_D15 = 36, - TRICORE_REG_E0 = 37, - TRICORE_REG_E2 = 38, - TRICORE_REG_E4 = 39, - TRICORE_REG_E6 = 40, - TRICORE_REG_E8 = 41, - TRICORE_REG_E10 = 42, - TRICORE_REG_E12 = 43, - TRICORE_REG_E14 = 44, - TRICORE_REG_P0 = 45, - TRICORE_REG_P2 = 46, - TRICORE_REG_P4 = 47, - TRICORE_REG_P6 = 48, - TRICORE_REG_P8 = 49, - TRICORE_REG_P10 = 50, - TRICORE_REG_P12 = 51, - TRICORE_REG_P14 = 52, - TRICORE_REG_A0_A1 = 53, - TRICORE_REG_A2_A3 = 54, - TRICORE_REG_A4_A5 = 55, - TRICORE_REG_A6_A7 = 56, - TRICORE_REG_A8_A9 = 57, - TRICORE_REG_A10_A11 = 58, - TRICORE_REG_A12_A13 = 59, - TRICORE_REG_A14_A15 = 60, - TRICORE_REG_ENDING, // 61 +typedef struct _GumElfNoteHeader GumElfNoteHeader; - // clang-format on - // generate content end -} tricore_reg; +typedef gboolean (* GumFoundElfSegmentFunc) ( + const GumElfSegmentDetails * details, gpointer user_data); +typedef gboolean (* GumFoundElfSectionFunc) ( + const GumElfSectionDetails * details, gpointer user_data); +typedef gboolean (* GumFoundElfRelocationFunc) ( + const GumElfRelocationDetails * details, gpointer user_data); +typedef gboolean (* GumFoundElfDynamicEntryFunc) ( + const GumElfDynamicEntryDetails * details, gpointer user_data); +typedef gboolean (* GumFoundElfSymbolFunc) (const GumElfSymbolDetails * details, + gpointer user_data); -/// TriCore instruction -typedef enum tricore_insn { - TRICORE_INS_INVALID = 0, - // generate content begin - // clang-format off +struct _GumElfSegmentDetails +{ + GumAddress vm_address; + guint64 vm_size; + guint64 file_offset; + guint64 file_size; + GumPageProtection protection; +}; - TRICORE_INS_XOR_T, - TRICORE_INS_ABSDIFS_B, - TRICORE_INS_ABSDIFS_H, - TRICORE_INS_ABSDIFS, - TRICORE_INS_ABSDIF_B, - TRICORE_INS_ABSDIF_H, - TRICORE_INS_ABSDIF, - TRICORE_INS_ABSS_B, - TRICORE_INS_ABSS_H, - TRICORE_INS_ABSS, - TRICORE_INS_ABS_B, - TRICORE_INS_ABS_H, - TRICORE_INS_ABS, - TRICORE_INS_ADDC, - TRICORE_INS_ADDIH_A, - TRICORE_INS_ADDIH, - TRICORE_INS_ADDI, - TRICORE_INS_ADDSC_AT, - TRICORE_INS_ADDSC_A, - TRICORE_INS_ADDS_BU, - TRICORE_INS_ADDS_B, - TRICORE_INS_ADDS_H, - TRICORE_INS_ADDS_HU, - TRICORE_INS_ADDS_U, - TRICORE_INS_ADDS, - TRICORE_INS_ADDX, - TRICORE_INS_ADD_A, - TRICORE_INS_ADD_B, - TRICORE_INS_ADD_F, - TRICORE_INS_ADD_H, - TRICORE_INS_ADD, - TRICORE_INS_ANDN_T, - TRICORE_INS_ANDN, - TRICORE_INS_AND_ANDN_T, - TRICORE_INS_AND_AND_T, - TRICORE_INS_AND_EQ, - TRICORE_INS_AND_GE_U, - TRICORE_INS_AND_GE, - TRICORE_INS_AND_LT_U, - TRICORE_INS_AND_LT, - TRICORE_INS_AND_NE, - TRICORE_INS_AND_NOR_T, - TRICORE_INS_AND_OR_T, - TRICORE_INS_AND_T, - TRICORE_INS_AND, - TRICORE_INS_BISR, - TRICORE_INS_BMERGE, - TRICORE_INS_BSPLIT, - TRICORE_INS_CACHEA_I, - TRICORE_INS_CACHEA_WI, - TRICORE_INS_CACHEA_W, - TRICORE_INS_CACHEI_I, - TRICORE_INS_CACHEI_WI, - TRICORE_INS_CACHEI_W, - TRICORE_INS_CADDN_A, - TRICORE_INS_CADDN, - TRICORE_INS_CADD_A, - TRICORE_INS_CADD, - TRICORE_INS_CALLA, - TRICORE_INS_CALLI, - TRICORE_INS_CALL, - TRICORE_INS_CLO_B, - TRICORE_INS_CLO_H, - TRICORE_INS_CLO, - TRICORE_INS_CLS_B, - TRICORE_INS_CLS_H, - TRICORE_INS_CLS, - TRICORE_INS_CLZ_B, - TRICORE_INS_CLZ_H, - TRICORE_INS_CLZ, - TRICORE_INS_CMOVN, - TRICORE_INS_CMOV, - TRICORE_INS_CMPSWAP_W, - TRICORE_INS_CMP_F, - TRICORE_INS_CRC32B_W, - TRICORE_INS_CRC32L_W, - TRICORE_INS_CRC32_B, - TRICORE_INS_CRCN, - TRICORE_INS_CSUBN_A, - TRICORE_INS_CSUBN, - TRICORE_INS_CSUB_A, - TRICORE_INS_CSUB, - TRICORE_INS_DEBUG, - TRICORE_INS_DEXTR, - TRICORE_INS_DIFSC_A, - TRICORE_INS_DISABLE, - TRICORE_INS_DIV_F, - TRICORE_INS_DIV_U, - TRICORE_INS_DIV, - TRICORE_INS_DSYNC, - TRICORE_INS_DVADJ, - TRICORE_INS_DVINIT_BU, - TRICORE_INS_DVINIT_B, - TRICORE_INS_DVINIT_HU, - TRICORE_INS_DVINIT_H, - TRICORE_INS_DVINIT_U, - TRICORE_INS_DVINIT, - TRICORE_INS_DVSTEP_U, - TRICORE_INS_DVSTEP, - TRICORE_INS_ENABLE, - TRICORE_INS_EQANY_B, - TRICORE_INS_EQANY_H, - TRICORE_INS_EQZ_A, - TRICORE_INS_EQ_A, - TRICORE_INS_EQ_B, - TRICORE_INS_EQ_H, - TRICORE_INS_EQ_W, - TRICORE_INS_EQ, - TRICORE_INS_EXTR_U, - TRICORE_INS_EXTR, - TRICORE_INS_FCALLA, - TRICORE_INS_FCALLI, - TRICORE_INS_FCALL, - TRICORE_INS_FRET, - TRICORE_INS_FTOHP, - TRICORE_INS_FTOIZ, - TRICORE_INS_FTOI, - TRICORE_INS_FTOQ31Z, - TRICORE_INS_FTOQ31, - TRICORE_INS_FTOUZ, - TRICORE_INS_FTOU, - TRICORE_INS_GE_A, - TRICORE_INS_GE_U, - TRICORE_INS_GE, - TRICORE_INS_HPTOF, - TRICORE_INS_IMASK, - TRICORE_INS_INSERT, - TRICORE_INS_INSN_T, - TRICORE_INS_INS_T, - TRICORE_INS_ISYNC, - TRICORE_INS_ITOF, - TRICORE_INS_IXMAX_U, - TRICORE_INS_IXMAX, - TRICORE_INS_IXMIN_U, - TRICORE_INS_IXMIN, - TRICORE_INS_JA, - TRICORE_INS_JEQ_A, - TRICORE_INS_JEQ, - TRICORE_INS_JGEZ, - TRICORE_INS_JGE_U, - TRICORE_INS_JGE, - TRICORE_INS_JGTZ, - TRICORE_INS_JI, - TRICORE_INS_JLA, - TRICORE_INS_JLEZ, - TRICORE_INS_JLI, - TRICORE_INS_JLTZ, - TRICORE_INS_JLT_U, - TRICORE_INS_JLT, - TRICORE_INS_JL, - TRICORE_INS_JNED, - TRICORE_INS_JNEI, - TRICORE_INS_JNE_A, - TRICORE_INS_JNE, - TRICORE_INS_JNZ_A, - TRICORE_INS_JNZ_T, - TRICORE_INS_JNZ, - TRICORE_INS_JZ_A, - TRICORE_INS_JZ_T, - TRICORE_INS_JZ, - TRICORE_INS_J, - TRICORE_INS_LDLCX, - TRICORE_INS_LDMST, - TRICORE_INS_LDUCX, - TRICORE_INS_LD_A, - TRICORE_INS_LD_BU, - TRICORE_INS_LD_B, - TRICORE_INS_LD_DA, - TRICORE_INS_LD_D, - TRICORE_INS_LD_HU, - TRICORE_INS_LD_H, - TRICORE_INS_LD_Q, - TRICORE_INS_LD_W, - TRICORE_INS_LEA, - TRICORE_INS_LHA, - TRICORE_INS_LOOPU, - TRICORE_INS_LOOP, - TRICORE_INS_LT_A, - TRICORE_INS_LT_B, - TRICORE_INS_LT_BU, - TRICORE_INS_LT_H, - TRICORE_INS_LT_HU, - TRICORE_INS_LT_U, - TRICORE_INS_LT_W, - TRICORE_INS_LT_WU, - TRICORE_INS_LT, - TRICORE_INS_MADDMS_H, - TRICORE_INS_MADDMS_U, - TRICORE_INS_MADDMS, - TRICORE_INS_MADDM_H, - TRICORE_INS_MADDM_Q, - TRICORE_INS_MADDM_U, - TRICORE_INS_MADDM, - TRICORE_INS_MADDRS_H, - TRICORE_INS_MADDRS_Q, - TRICORE_INS_MADDR_H, - TRICORE_INS_MADDR_Q, - TRICORE_INS_MADDSUMS_H, - TRICORE_INS_MADDSUM_H, - TRICORE_INS_MADDSURS_H, - TRICORE_INS_MADDSUR_H, - TRICORE_INS_MADDSUS_H, - TRICORE_INS_MADDSU_H, - TRICORE_INS_MADDS_H, - TRICORE_INS_MADDS_Q, - TRICORE_INS_MADDS_U, - TRICORE_INS_MADDS, - TRICORE_INS_MADD_F, - TRICORE_INS_MADD_H, - TRICORE_INS_MADD_Q, - TRICORE_INS_MADD_U, - TRICORE_INS_MADD, - TRICORE_INS_MAX_B, - TRICORE_INS_MAX_BU, - TRICORE_INS_MAX_H, - TRICORE_INS_MAX_HU, - TRICORE_INS_MAX_U, - TRICORE_INS_MAX, - TRICORE_INS_MFCR, - TRICORE_INS_MIN_B, - TRICORE_INS_MIN_BU, - TRICORE_INS_MIN_H, - TRICORE_INS_MIN_HU, - TRICORE_INS_MIN_U, - TRICORE_INS_MIN, - TRICORE_INS_MOVH_A, - TRICORE_INS_MOVH, - TRICORE_INS_MOVZ_A, - TRICORE_INS_MOV_AA, - TRICORE_INS_MOV_A, - TRICORE_INS_MOV_D, - TRICORE_INS_MOV_U, - TRICORE_INS_MOV, - TRICORE_INS_MSUBADMS_H, - TRICORE_INS_MSUBADM_H, - TRICORE_INS_MSUBADRS_H, - TRICORE_INS_MSUBADR_H, - TRICORE_INS_MSUBADS_H, - TRICORE_INS_MSUBAD_H, - TRICORE_INS_MSUBMS_H, - TRICORE_INS_MSUBMS_U, - TRICORE_INS_MSUBMS, - TRICORE_INS_MSUBM_H, - TRICORE_INS_MSUBM_Q, - TRICORE_INS_MSUBM_U, - TRICORE_INS_MSUBM, - TRICORE_INS_MSUBRS_H, - TRICORE_INS_MSUBRS_Q, - TRICORE_INS_MSUBR_H, - TRICORE_INS_MSUBR_Q, - TRICORE_INS_MSUBS_H, - TRICORE_INS_MSUBS_Q, - TRICORE_INS_MSUBS_U, - TRICORE_INS_MSUBS, - TRICORE_INS_MSUB_F, - TRICORE_INS_MSUB_H, - TRICORE_INS_MSUB_Q, - TRICORE_INS_MSUB_U, - TRICORE_INS_MSUB, - TRICORE_INS_MTCR, - TRICORE_INS_MULMS_H, - TRICORE_INS_MULM_H, - TRICORE_INS_MULM_U, - TRICORE_INS_MULM, - TRICORE_INS_MULR_H, - TRICORE_INS_MULR_Q, - TRICORE_INS_MULS_U, - TRICORE_INS_MULS, - TRICORE_INS_MUL_F, - TRICORE_INS_MUL_H, - TRICORE_INS_MUL_Q, - TRICORE_INS_MUL_U, - TRICORE_INS_MUL, - TRICORE_INS_NAND_T, - TRICORE_INS_NAND, - TRICORE_INS_NEZ_A, - TRICORE_INS_NE_A, - TRICORE_INS_NE, - TRICORE_INS_NOP, - TRICORE_INS_NOR_T, - TRICORE_INS_NOR, - TRICORE_INS_NOT, - TRICORE_INS_ORN_T, - TRICORE_INS_ORN, - TRICORE_INS_OR_ANDN_T, - TRICORE_INS_OR_AND_T, - TRICORE_INS_OR_EQ, - TRICORE_INS_OR_GE_U, - TRICORE_INS_OR_GE, - TRICORE_INS_OR_LT_U, - TRICORE_INS_OR_LT, - TRICORE_INS_OR_NE, - TRICORE_INS_OR_NOR_T, - TRICORE_INS_OR_OR_T, - TRICORE_INS_OR_T, - TRICORE_INS_OR, - TRICORE_INS_PACK, - TRICORE_INS_PARITY, - TRICORE_INS_POPCNT_W, - TRICORE_INS_Q31TOF, - TRICORE_INS_QSEED_F, - TRICORE_INS_RESTORE, - TRICORE_INS_RET, - TRICORE_INS_RFE, - TRICORE_INS_RFM, - TRICORE_INS_RSLCX, - TRICORE_INS_RSTV, - TRICORE_INS_RSUBS_U, - TRICORE_INS_RSUBS, - TRICORE_INS_RSUB, - TRICORE_INS_SAT_BU, - TRICORE_INS_SAT_B, - TRICORE_INS_SAT_HU, - TRICORE_INS_SAT_H, - TRICORE_INS_SELN_A, - TRICORE_INS_SELN, - TRICORE_INS_SEL_A, - TRICORE_INS_SEL, - TRICORE_INS_SHAS, - TRICORE_INS_SHA_B, - TRICORE_INS_SHA_H, - TRICORE_INS_SHA, - TRICORE_INS_SHUFFLE, - TRICORE_INS_SH_ANDN_T, - TRICORE_INS_SH_AND_T, - TRICORE_INS_SH_B, - TRICORE_INS_SH_EQ, - TRICORE_INS_SH_GE_U, - TRICORE_INS_SH_GE, - TRICORE_INS_SH_H, - TRICORE_INS_SH_LT_U, - TRICORE_INS_SH_LT, - TRICORE_INS_SH_NAND_T, - TRICORE_INS_SH_NE, - TRICORE_INS_SH_NOR_T, - TRICORE_INS_SH_ORN_T, - TRICORE_INS_SH_OR_T, - TRICORE_INS_SH_XNOR_T, - TRICORE_INS_SH_XOR_T, - TRICORE_INS_SH, - TRICORE_INS_STLCX, - TRICORE_INS_STUCX, - TRICORE_INS_ST_A, - TRICORE_INS_ST_B, - TRICORE_INS_ST_DA, - TRICORE_INS_ST_D, - TRICORE_INS_ST_H, - TRICORE_INS_ST_Q, - TRICORE_INS_ST_T, - TRICORE_INS_ST_W, - TRICORE_INS_SUBC, - TRICORE_INS_SUBSC_A, - TRICORE_INS_SUBS_BU, - TRICORE_INS_SUBS_B, - TRICORE_INS_SUBS_HU, - TRICORE_INS_SUBS_H, - TRICORE_INS_SUBS_U, - TRICORE_INS_SUBS, - TRICORE_INS_SUBX, - TRICORE_INS_SUB_A, - TRICORE_INS_SUB_B, - TRICORE_INS_SUB_F, - TRICORE_INS_SUB_H, - TRICORE_INS_SUB, - TRICORE_INS_SVLCX, - TRICORE_INS_SWAPMSK_W, - TRICORE_INS_SWAP_A, - TRICORE_INS_SWAP_W, - TRICORE_INS_SYSCALL, - TRICORE_INS_TLBDEMAP, - TRICORE_INS_TLBFLUSH_A, - TRICORE_INS_TLBFLUSH_B, - TRICORE_INS_TLBMAP, - TRICORE_INS_TLBPROBE_A, - TRICORE_INS_TLBPROBE_I, - TRICORE_INS_TRAPSV, - TRICORE_INS_TRAPV, - TRICORE_INS_UNPACK, - TRICORE_INS_UPDFL, - TRICORE_INS_UTOF, - TRICORE_INS_WAIT, - TRICORE_INS_XNOR_T, - TRICORE_INS_XNOR, - TRICORE_INS_XOR_EQ, - TRICORE_INS_XOR_GE_U, - TRICORE_INS_XOR_GE, - TRICORE_INS_XOR_LT_U, - TRICORE_INS_XOR_LT, - TRICORE_INS_XOR_NE, - TRICORE_INS_XOR, +struct _GumElfSectionDetails +{ + const gchar * id; + const gchar * name; + GumElfSectionType type; + guint64 flags; + GumAddress address; + guint64 offset; + gsize size; + guint32 link; + guint32 info; + guint64 alignment; + guint64 entry_size; + GumPageProtection protection; - // clang-format on - // generate content end - TRICORE_INS_ENDING, // <-- mark the end of the list of instructions -} tricore_insn; + /*< private >*/ + gint ref_count; +}; + +struct _GumElfRelocationDetails +{ + GumAddress address; + guint32 type; + const GumElfSymbolDetails * symbol; + gint64 addend; + const GumElfSectionDetails * parent; +}; -/// Group of TriCore instructions -typedef enum tricore_insn_group { - TRICORE_GRP_INVALID, ///< = CS_GRP_INVALID - /// Generic groups - TRICORE_GRP_CALL, ///< = CS_GRP_CALL - TRICORE_GRP_JUMP, ///< = CS_GRP_JUMP - TRICORE_GRP_ENDING, ///< mark the end of the list of groups -} tricore_insn_group; +struct _GumElfDynamicEntryDetails +{ + GumElfDynamicTag tag; + guint64 val; +}; -typedef enum tricore_feature_t { - TRICORE_FEATURE_INVALID = 0, - // generate content begin - // clang-format off +struct _GumElfSymbolDetails +{ + const gchar * name; + GumAddress address; + gsize size; + GumElfSymbolType type; + GumElfSymbolBind bind; + guint16 shdr_index; + GumElfSectionDetails * section; +}; - TRICORE_FEATURE_HasV110 = 128, - TRICORE_FEATURE_HasV120_UP, - TRICORE_FEATURE_HasV130_UP, - TRICORE_FEATURE_HasV161, - TRICORE_FEATURE_HasV160_UP, - TRICORE_FEATURE_HasV131_UP, - TRICORE_FEATURE_HasV161_UP, - TRICORE_FEATURE_HasV162, - TRICORE_FEATURE_HasV162_UP, +struct _GumElfNoteHeader +{ + guint32 name_size; + guint32 desc_size; + guint32 type; +}; - // clang-format on - // generate content end - TRICORE_FEATURE_ENDING, ///< mark the end of the list of features -} tricore_feature; +GUM_API GumElfModule * gum_elf_module_new_from_file (const gchar * path, + GError ** error); +GUM_API GumElfModule * gum_elf_module_new_from_blob (GBytes * blob, + GError ** error); +GUM_API GumElfModule * gum_elf_module_new_from_memory (const gchar * path, + GumAddress base_address, GError ** error); -#ifdef __cplusplus -} -#endif +GUM_API gboolean gum_elf_module_load (GumElfModule * self, GError ** error); -#endif +GUM_API GumElfType gum_elf_module_get_etype (GumElfModule * self); +GUM_API guint gum_elf_module_get_pointer_size (GumElfModule * self); +GUM_API gint gum_elf_module_get_byte_order (GumElfModule * self); +GUM_API GumElfOSABI gum_elf_module_get_os_abi (GumElfModule * self); +GUM_API guint8 gum_elf_module_get_os_abi_version (GumElfModule * self); +GUM_API GumElfMachine gum_elf_module_get_machine (GumElfModule * self); +GUM_API GumAddress gum_elf_module_get_base_address (GumElfModule * self); +GUM_API GumAddress gum_elf_module_get_preferred_address (GumElfModule * self); +GUM_API guint64 gum_elf_module_get_mapped_size (GumElfModule * self); +GUM_API GumAddress gum_elf_module_get_entrypoint (GumElfModule * self); +GUM_API const gchar * gum_elf_module_get_interpreter (GumElfModule * self); +GUM_API const gchar * gum_elf_module_get_source_path (GumElfModule * self); +GUM_API GBytes * gum_elf_module_get_source_blob (GumElfModule * self); +GUM_API GumElfSourceMode gum_elf_module_get_source_mode (GumElfModule * self); +GUM_API gconstpointer gum_elf_module_get_file_data (GumElfModule * self, + gsize * size); -#define MAX_IMPL_W_REGS 20 -#define MAX_IMPL_R_REGS 20 -#define MAX_NUM_GROUPS 8 +GUM_API void gum_elf_module_enumerate_segments (GumElfModule * self, + GumFoundElfSegmentFunc func, gpointer user_data); +GUM_API void gum_elf_module_enumerate_sections (GumElfModule * self, + GumFoundElfSectionFunc func, gpointer user_data); +GUM_API void gum_elf_module_enumerate_relocations (GumElfModule * self, + GumFoundElfRelocationFunc func, gpointer user_data); +GUM_API void gum_elf_module_enumerate_dynamic_entries (GumElfModule * self, + GumFoundElfDynamicEntryFunc func, gpointer user_data); +GUM_API void gum_elf_module_enumerate_imports (GumElfModule * self, + GumFoundImportFunc func, gpointer user_data); +GUM_API void gum_elf_module_enumerate_exports (GumElfModule * self, + GumFoundExportFunc func, gpointer user_data); +GUM_API void gum_elf_module_enumerate_dynamic_symbols (GumElfModule * self, + GumFoundElfSymbolFunc func, gpointer user_data); +GUM_API void gum_elf_module_enumerate_symbols (GumElfModule * self, + GumFoundElfSymbolFunc func, gpointer user_data); +GUM_API void gum_elf_module_enumerate_dependencies (GumElfModule * self, + GumFoundDependencyFunc func, gpointer user_data); -/// NOTE: All information in cs_detail is only available when CS_OPT_DETAIL = CS_OPT_ON -/// Initialized as memset(., 0, offsetof(cs_detail, ARCH)+sizeof(cs_ARCH)) -/// by ARCH_getInstruction in arch/ARCH/ARCHDisassembler.c -/// if cs_detail changes, in particular if a field is added after the union, -/// then update arch/ARCH/ARCHDisassembler.c accordingly -typedef struct cs_detail { - uint16_t regs_read - [MAX_IMPL_R_REGS]; ///< list of implicit registers read by this insn - uint8_t regs_read_count; ///< number of implicit registers read by this insn +GUM_API GumAddress gum_elf_module_translate_to_offline (GumElfModule * self, + GumAddress online_address); +GUM_API GumAddress gum_elf_module_translate_to_online (GumElfModule * self, + GumAddress offline_address); - uint16_t regs_write - [MAX_IMPL_W_REGS]; ///< list of implicit registers modified by this insn - uint8_t regs_write_count; ///< number of implicit registers modified by this insn +GUM_API GType gum_elf_section_details_get_type (void) G_GNUC_CONST; +GUM_API GumElfSectionDetails * gum_elf_section_details_ref ( + GumElfSectionDetails * details); +GUM_API void gum_elf_section_details_unref (GumElfSectionDetails * details); - uint8_t groups[MAX_NUM_GROUPS]; ///< list of group this instruction belong to - uint8_t groups_count; ///< number of groups this insn belongs to +GUM_API GType gum_elf_symbol_details_get_type (void) G_GNUC_CONST; +GUM_API GumElfSymbolDetails * gum_elf_symbol_details_copy ( + const GumElfSymbolDetails * details); +GUM_API void gum_elf_symbol_details_free (GumElfSymbolDetails * details); - bool writeback; ///< Instruction has writeback operands. +G_END_DECLS - /// Architecture-specific instruction info - union { - cs_x86 x86; ///< X86 architecture, including 16-bit, 32-bit & 64-bit mode - cs_arm64 arm64; ///< ARM64 architecture (aka AArch64) - cs_arm arm; ///< ARM architecture (including Thumb/Thumb2) - cs_m68k m68k; ///< M68K architecture - cs_mips mips; ///< MIPS architecture - cs_ppc ppc; ///< PowerPC architecture - cs_sparc sparc; ///< Sparc architecture - cs_sysz sysz; ///< SystemZ architecture - cs_xcore xcore; ///< XCore architecture - cs_tms320c64x tms320c64x; ///< TMS320C64x architecture - cs_m680x m680x; ///< M680X architecture - cs_evm evm; ///< Ethereum architecture - cs_mos65xx mos65xx; ///< MOS65XX architecture (including MOS6502) - cs_wasm wasm; ///< Web Assembly architecture - cs_bpf bpf; ///< Berkeley Packet Filter architecture (including eBPF) - cs_riscv riscv; ///< RISCV architecture - cs_sh sh; ///< SH architecture - cs_tricore tricore; ///< TriCore architecture - }; -} cs_detail; +#endif +/* + * Copyright (C) 2009-2022 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -/// Detail information of disassembled instruction -typedef struct cs_insn { - /// Instruction ID (basically a numeric ID for the instruction mnemonic) - /// Find the instruction id in the '[ARCH]_insn' enum in the header file - /// of corresponding architecture, such as 'arm_insn' in arm.h for ARM, - /// 'x86_insn' in x86.h for X86, etc... - /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF - /// NOTE: in Skipdata mode, "data" instruction has 0 for this id field. - unsigned int id; +#ifndef __GUM_EVENT_H__ +#define __GUM_EVENT_H__ - /// Address (EIP) of this instruction - /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF - uint64_t address; - /// Size of this instruction - /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF - uint16_t size; +G_BEGIN_DECLS - /// Machine bytes of this instruction, with number of bytes indicated by @size above - /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF - uint8_t bytes[24]; +typedef guint GumEventType; - /// Ascii text of instruction mnemonic - /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF - char mnemonic[CS_MNEMONIC_SIZE]; +typedef union _GumEvent GumEvent; - /// Ascii text of instruction operands - /// This information is available even when CS_OPT_DETAIL = CS_OPT_OFF - char op_str[160]; +typedef struct _GumAnyEvent GumAnyEvent; +typedef struct _GumCallEvent GumCallEvent; +typedef struct _GumRetEvent GumRetEvent; +typedef struct _GumExecEvent GumExecEvent; +typedef struct _GumBlockEvent GumBlockEvent; +typedef struct _GumCompileEvent GumCompileEvent; - /// Pointer to cs_detail. - /// NOTE: detail pointer is only valid when both requirements below are met: - /// (1) CS_OP_DETAIL = CS_OPT_ON - /// (2) Engine is not in Skipdata mode (CS_OP_SKIPDATA option set to CS_OPT_ON) - /// - /// NOTE 2: when in Skipdata mode, or when detail mode is OFF, even if this pointer - /// is not NULL, its content is still irrelevant. - cs_detail *detail; -} cs_insn; +enum _GumEventType +{ + GUM_NOTHING = 0, + GUM_CALL = 1 << 0, + GUM_RET = 1 << 1, + GUM_EXEC = 1 << 2, + GUM_BLOCK = 1 << 3, + GUM_COMPILE = 1 << 4, +}; +struct _GumAnyEvent +{ + GumEventType type; +}; -/// Calculate the offset of a disassembled instruction in its buffer, given its position -/// in its array of disassembled insn -/// NOTE: this macro works with position (>=1), not index -#define CS_INSN_OFFSET(insns, post) (insns[post - 1].address - insns[0].address) +struct _GumCallEvent +{ + GumEventType type; + gpointer location; + gpointer target; + gint depth; +}; -/// All type of errors encountered by Capstone API. -/// These are values returned by cs_errno() -typedef enum cs_err { - CS_ERR_OK = 0, ///< No error: everything was fine - CS_ERR_MEM, ///< Out-Of-Memory error: cs_open(), cs_disasm(), cs_disasm_iter() - CS_ERR_ARCH, ///< Unsupported architecture: cs_open() - CS_ERR_HANDLE, ///< Invalid handle: cs_op_count(), cs_op_index() - CS_ERR_CSH, ///< Invalid csh argument: cs_close(), cs_errno(), cs_option() - CS_ERR_MODE, ///< Invalid/unsupported mode: cs_open() - CS_ERR_OPTION, ///< Invalid/unsupported option: cs_option() - CS_ERR_DETAIL, ///< Information is unavailable because detail option is OFF - CS_ERR_MEMSETUP, ///< Dynamic memory management uninitialized (see CS_OPT_MEM) - CS_ERR_VERSION, ///< Unsupported version (bindings) - CS_ERR_DIET, ///< Access irrelevant data in "diet" engine - CS_ERR_SKIPDATA, ///< Access irrelevant data for "data" instruction in SKIPDATA mode - CS_ERR_X86_ATT, ///< X86 AT&T syntax is unsupported (opt-out at compile time) - CS_ERR_X86_INTEL, ///< X86 Intel syntax is unsupported (opt-out at compile time) - CS_ERR_X86_MASM, ///< X86 Masm syntax is unsupported (opt-out at compile time) -} cs_err; +struct _GumRetEvent +{ + GumEventType type; -/** - Return combined API version & major and minor version numbers. + gpointer location; + gpointer target; + gint depth; +}; - @major: major number of API version - @minor: minor number of API version +struct _GumExecEvent +{ + GumEventType type; - @return hexical number as (major << 8 | minor), which encodes both - major & minor versions. - NOTE: This returned value can be compared with version number made - with macro CS_MAKE_VERSION + gpointer location; +}; - For example, second API version would return 1 in @major, and 1 in @minor - The return value would be 0x0101 +struct _GumBlockEvent +{ + GumEventType type; - NOTE: if you only care about returned value, but not major and minor values, - set both @major & @minor arguments to NULL. -*/ -CAPSTONE_EXPORT -unsigned int CAPSTONE_API cs_version(int *major, int *minor); + gpointer start; + gpointer end; +}; -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_arm(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_arm64(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_mips(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_x86(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_ppc(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_sparc(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_sysz(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_xcore(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_m68k(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_tms320c64x(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_m680x(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_evm(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_mos65xx(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_wasm(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_bpf(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_riscv(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_sh(void); -CAPSTONE_EXPORT -void CAPSTONE_API cs_arch_register_tricore(void); +struct _GumCompileEvent +{ + GumEventType type; -/** - This API can be used to either ask for archs supported by this library, - or check to see if the library was compile with 'diet' option (or called - in 'diet' mode). + gpointer start; + gpointer end; +}; - To check if a particular arch is supported by this library, set @query to - arch mode (CS_ARCH_* value). - To verify if this library supports all the archs, use CS_ARCH_ALL. +union _GumEvent +{ + GumEventType type; - To check if this library is in 'diet' mode, set @query to CS_SUPPORT_DIET. + GumAnyEvent any; + GumCallEvent call; + GumRetEvent ret; + GumExecEvent exec; + GumBlockEvent block; + GumCompileEvent compile; +}; - @return True if this library supports the given arch, or in 'diet' mode. -*/ -CAPSTONE_EXPORT -bool CAPSTONE_API cs_support(int query); +G_END_DECLS -/** - Initialize CS handle: this must be done before any usage of CS. +#endif +/* + * Copyright (C) 2009-2022 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ - @arch: architecture type (CS_ARCH_*) - @mode: hardware mode. This is combined of CS_MODE_* - @handle: pointer to handle, which will be updated at return time +#ifndef __GUM_EVENT_SINK_H__ +#define __GUM_EVENT_SINK_H__ - @return CS_ERR_OK on success, or other value on failure (refer to cs_err enum - for detailed error). -*/ -CAPSTONE_EXPORT -cs_err CAPSTONE_API cs_open(cs_arch arch, cs_mode mode, csh *handle); -/** - Close CS handle: MUST do to release the handle when it is not used anymore. - NOTE: this must be only called when there is no longer usage of Capstone, - not even access to cs_insn array. The reason is the this API releases some - cached memory, thus access to any Capstone API after cs_close() might crash - your application. +G_BEGIN_DECLS - In fact,this API invalidate @handle by ZERO out its value (i.e *handle = 0). +#define GUM_TYPE_EVENT_SINK (gum_event_sink_get_type ()) +G_DECLARE_INTERFACE (GumEventSink, gum_event_sink, GUM, EVENT_SINK, GObject) - @handle: pointer to a handle returned by cs_open() +#define GUM_TYPE_DEFAULT_EVENT_SINK (gum_default_event_sink_get_type ()) +G_DECLARE_FINAL_TYPE (GumDefaultEventSink, gum_default_event_sink, GUM, + DEFAULT_EVENT_SINK, GObject) - @return CS_ERR_OK on success, or other value on failure (refer to cs_err enum - for detailed error). -*/ -CAPSTONE_EXPORT -cs_err CAPSTONE_API cs_close(csh *handle); +#define GUM_TYPE_CALLBACK_EVENT_SINK (gum_callback_event_sink_get_type ()) +G_DECLARE_FINAL_TYPE (GumCallbackEventSink, gum_callback_event_sink, GUM, + CALLBACK_EVENT_SINK, GObject) -/** - Set option for disassembling engine at runtime +typedef void (* GumEventSinkCallback) (const GumEvent * event, + GumCpuContext * cpu_context, gpointer user_data); - @handle: handle returned by cs_open() - @type: type of option to be set - @value: option value corresponding with @type +struct _GumEventSinkInterface +{ + GTypeInterface parent; - @return: CS_ERR_OK on success, or other value on failure. - Refer to cs_err enum for detailed error. + GumEventType (* query_mask) (GumEventSink * self); + void (* start) (GumEventSink * self); + void (* process) (GumEventSink * self, const GumEvent * event, + GumCpuContext * cpu_context); + void (* flush) (GumEventSink * self); + void (* stop) (GumEventSink * self); +}; - NOTE: in the case of CS_OPT_MEM, handle's value can be anything, - so that cs_option(handle, CS_OPT_MEM, value) can (i.e must) be called - even before cs_open() -*/ -CAPSTONE_EXPORT -cs_err CAPSTONE_API cs_option(csh handle, cs_opt_type type, size_t value); +GUM_API GumEventType gum_event_sink_query_mask (GumEventSink * self); +GUM_API void gum_event_sink_start (GumEventSink * self); +GUM_API void gum_event_sink_process (GumEventSink * self, + const GumEvent * event, GumCpuContext * cpu_context); +GUM_API void gum_event_sink_flush (GumEventSink * self); +GUM_API void gum_event_sink_stop (GumEventSink * self); -/** - Report the last error number when some API function fail. - Like glibc's errno, cs_errno might not retain its old value once accessed. +GUM_API GumEventSink * gum_event_sink_make_default (void); +GUM_API GumEventSink * gum_event_sink_make_from_callback (GumEventType mask, + GumEventSinkCallback callback, gpointer data, GDestroyNotify data_destroy); - @handle: handle returned by cs_open() +G_END_DECLS - @return: error code of cs_err enum type (CS_ERR_*, see above) -*/ -CAPSTONE_EXPORT -cs_err CAPSTONE_API cs_errno(csh handle); +#endif +/* + * Copyright (C) 2015-2026 Ole André Vadla Ravnås + * Copyright (C) 2020 Francesco Tamagni + * + * Licence: wxWindows Library Licence, Version 3.1 + */ +#ifndef __GUM_EXCEPTOR_H__ +#define __GUM_EXCEPTOR_H__ -/** - Return a string describing given error code. +#include - @code: error code (see CS_ERR_* above) +G_BEGIN_DECLS - @return: returns a pointer to a string that describes the error code - passed in the argument @code -*/ -CAPSTONE_EXPORT -const char * CAPSTONE_API cs_strerror(cs_err code); +#define GUM_TYPE_EXCEPTOR (gum_exceptor_get_type ()) +G_DECLARE_FINAL_TYPE (GumExceptor, gum_exceptor, GUM, EXCEPTOR, GObject) -/** - Disassemble binary code, given the code buffer, size, address and number - of instructions to be decoded. - This API dynamically allocate memory to contain disassembled instruction. - Resulting instructions will be put into @*insn +typedef enum { + GUM_EXCEPTOR_MODE_FULL, + GUM_EXCEPTOR_MODE_HANDLER_ONLY, + GUM_EXCEPTOR_MODE_OFF +} GumExceptorMode; + +#if defined (GUM_GIR_COMPILATION) + typedef int GumExceptorNativeJmpBuf; +#elif defined (G_OS_WIN32) || defined (G_OS_NONE) || defined (__APPLE__) +# define GUM_NATIVE_SETJMP(env) setjmp (env) +# define GUM_NATIVE_LONGJMP longjmp + typedef jmp_buf GumExceptorNativeJmpBuf; +#else +# define GUM_NATIVE_SETJMP(env) sigsetjmp (env, TRUE) +# define GUM_NATIVE_LONGJMP siglongjmp + typedef sigjmp_buf GumExceptorNativeJmpBuf; +#endif - NOTE 1: this API will automatically determine memory needed to contain - output disassembled instructions in @insn. +typedef struct _GumExceptionDetails GumExceptionDetails; +typedef guint GumExceptionType; +typedef struct _GumExceptionMemoryDetails GumExceptionMemoryDetails; +typedef gboolean (* GumExceptionHandler) (GumExceptionDetails * details, + gpointer user_data); - NOTE 2: caller must free the allocated memory itself to avoid memory leaking. +typedef struct _GumExceptorScope GumExceptorScope; - NOTE 3: for system with scarce memory to be dynamically allocated such as - OS kernel or firmware, the API cs_disasm_iter() might be a better choice than - cs_disasm(). The reason is that with cs_disasm(), based on limited available - memory, we have to calculate in advance how many instructions to be disassembled, - which complicates things. This is especially troublesome for the case @count=0, - when cs_disasm() runs uncontrollably (until either end of input buffer, or - when it encounters an invalid instruction). - - @handle: handle returned by cs_open() - @code: buffer containing raw binary code to be disassembled. - @code_size: size of the above code buffer. - @address: address of the first instruction in given raw code buffer. - @insn: array of instructions filled in by this API. - NOTE: @insn will be allocated by this function, and should be freed - with cs_free() API. - @count: number of instructions to be disassembled, or 0 to get all of them +enum _GumExceptionType +{ + GUM_EXCEPTION_ABORT = 1, + GUM_EXCEPTION_ACCESS_VIOLATION, + GUM_EXCEPTION_GUARD_PAGE, + GUM_EXCEPTION_ILLEGAL_INSTRUCTION, + GUM_EXCEPTION_STACK_OVERFLOW, + GUM_EXCEPTION_ARITHMETIC, + GUM_EXCEPTION_BREAKPOINT, + GUM_EXCEPTION_SINGLE_STEP, + GUM_EXCEPTION_SYSTEM +}; - @return: the number of successfully disassembled instructions, - or 0 if this function failed to disassemble the given code +struct _GumExceptionMemoryDetails +{ + GumMemoryOperation operation; + gpointer address; +}; - On failure, call cs_errno() for error code. -*/ -CAPSTONE_EXPORT -size_t CAPSTONE_API cs_disasm(csh handle, - const uint8_t *code, size_t code_size, - uint64_t address, - size_t count, - cs_insn **insn); +struct _GumExceptionDetails +{ + GumThreadId thread_id; + GumExceptionType type; + gpointer address; + GumExceptionMemoryDetails memory; + GumCpuContext context; + gpointer native_context; +}; -/** - Free memory allocated by cs_malloc() or cs_disasm() (argument @insn) +struct _GumExceptorScope +{ + GumExceptionDetails exception; - @insn: pointer returned by @insn argument in cs_disasm() or cs_malloc() - @count: number of cs_insn structures returned by cs_disasm(), or 1 - to free memory allocated by cs_malloc(). -*/ -CAPSTONE_EXPORT -void CAPSTONE_API cs_free(cs_insn *insn, size_t count); + /*< private */ + gboolean exception_occurred; + gpointer padding[2]; + GumExceptorNativeJmpBuf env; +#ifdef __ANDROID__ + sigset_t mask; +#endif + GumExceptorScope * next; +}; -/** - Allocate memory for 1 instruction to be used by cs_disasm_iter(). +GUM_API void gum_exceptor_set_mode (GumExceptorMode mode); - @handle: handle returned by cs_open() +GUM_API GumExceptor * gum_exceptor_obtain (void); - NOTE: when no longer in use, you can reclaim the memory allocated for - this instruction with cs_free(insn, 1) -*/ -CAPSTONE_EXPORT -cs_insn * CAPSTONE_API cs_malloc(csh handle); +GUM_API void gum_exceptor_reset (GumExceptor * self); -/** - Fast API to disassemble binary code, given the code buffer, size, address - and number of instructions to be decoded. - This API puts the resulting instruction into a given cache in @insn. - See tests/test_iter.c for sample code demonstrating this API. +GUM_API void gum_exceptor_add (GumExceptor * self, GumExceptionHandler func, + gpointer user_data); +GUM_API void gum_exceptor_remove (GumExceptor * self, GumExceptionHandler func, + gpointer user_data); - NOTE 1: this API will update @code, @size & @address to point to the next - instruction in the input buffer. Therefore, it is convenient to use - cs_disasm_iter() inside a loop to quickly iterate all the instructions. - While decoding one instruction at a time can also be achieved with - cs_disasm(count=1), some benchmarks shown that cs_disasm_iter() can be 30% - faster on random input. +#if defined (_MSC_VER) && defined (HAVE_I386) && GLIB_SIZEOF_VOID_P == 8 +/* + * On MSVC/x86_64 setjmp() is actually an intrinsic that calls _setjmp() with a + * a hidden second argument specifying the frame pointer. This makes sense when + * the longjmp() is guaranteed to happen from code we control, but is not + * reliable otherwise. + */ +# define gum_exceptor_try(self, scope) ( \ + _gum_exceptor_prepare_try (self, scope), \ + ((int (*) (jmp_buf env, void * frame_pointer)) _setjmp) ( \ + (scope)->env, NULL) == 0) +#else +# define gum_exceptor_try(self, scope) ( \ + _gum_exceptor_prepare_try (self, scope), \ + GUM_NATIVE_SETJMP ((scope)->env) == 0) +#endif +GUM_API gboolean gum_exceptor_catch (GumExceptor * self, + GumExceptorScope * scope); +GUM_API gboolean gum_exceptor_has_scope (GumExceptor * self, + GumThreadId thread_id); - NOTE 2: the cache in @insn can be created with cs_malloc() API. +GUM_API gchar * gum_exception_details_to_string ( + const GumExceptionDetails * details); - NOTE 3: for system with scarce memory to be dynamically allocated such as - OS kernel or firmware, this API is recommended over cs_disasm(), which - allocates memory based on the number of instructions to be disassembled. - The reason is that with cs_disasm(), based on limited available memory, - we have to calculate in advance how many instructions to be disassembled, - which complicates things. This is especially troublesome for the case - @count=0, when cs_disasm() runs uncontrollably (until either end of input - buffer, or when it encounters an invalid instruction). - - @handle: handle returned by cs_open() - @code: buffer containing raw binary code to be disassembled - @size: size of above code - @address: address of the first insn in given raw code buffer - @insn: pointer to instruction to be filled in by this API. +GUM_API void _gum_exceptor_prepare_try (GumExceptor * self, + GumExceptorScope * scope); - @return: true if this API successfully decode 1 instruction, - or false otherwise. +G_END_DECLS - On failure, call cs_errno() for error code. -*/ -CAPSTONE_EXPORT -bool CAPSTONE_API cs_disasm_iter(csh handle, - const uint8_t **code, size_t *size, - uint64_t *address, cs_insn *insn); +#endif +/* + * Copyright (C) 2009 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -/** - Return friendly name of register in a string. - Find the instruction id from header file of corresponding architecture (arm.h for ARM, - x86.h for X86, ...) +#ifndef __GUM_FUNCTION_H__ +#define __GUM_FUNCTION_H__ - WARN: when in 'diet' mode, this API is irrelevant because engine does not - store register name. +G_BEGIN_DECLS - @handle: handle returned by cs_open() - @reg_id: register id +typedef struct _GumFunctionDetails GumFunctionDetails; - @return: string name of the register, or NULL if @reg_id is invalid. -*/ -CAPSTONE_EXPORT -const char * CAPSTONE_API cs_reg_name(csh handle, unsigned int reg_id); +struct _GumFunctionDetails +{ + const gchar * name; + gpointer address; + gint num_arguments; +}; -/** - Return friendly name of an instruction in a string. - Find the instruction id from header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) +G_END_DECLS - WARN: when in 'diet' mode, this API is irrelevant because the engine does not - store instruction name. +#endif +/* + * Copyright (C) 2008-2026 Ole André Vadla Ravnås + * Copyright (C) 2008 Christian Berentsen + * Copyright (C) 2024 Francesco Tamagni + * + * Licence: wxWindows Library Licence, Version 3.1 + */ - @handle: handle returned by cs_open() - @insn_id: instruction id +#ifndef __GUM_INTERCEPTOR_H__ +#define __GUM_INTERCEPTOR_H__ - @return: string name of the instruction, or NULL if @insn_id is invalid. -*/ -CAPSTONE_EXPORT -const char * CAPSTONE_API cs_insn_name(csh handle, unsigned int insn_id); +/* + * Copyright (C) 2008-2022 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -/** - Return friendly name of a group id (that an instruction can belong to) - Find the group id from header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) +#ifndef __GUM_INVOCATION_LISTENER_H__ +#define __GUM_INVOCATION_LISTENER_H__ - WARN: when in 'diet' mode, this API is irrelevant because the engine does not - store group name. +/* + * Copyright (C) 2008-2022 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ - @handle: handle returned by cs_open() - @group_id: group id +#ifndef __GUM_INVOCATION_CONTEXT_H__ +#define __GUM_INVOCATION_CONTEXT_H__ - @return: string name of the group, or NULL if @group_id is invalid. -*/ -CAPSTONE_EXPORT -const char * CAPSTONE_API cs_group_name(csh handle, unsigned int group_id); -/** - Check if a disassembled instruction belong to a particular group. - Find the group id from header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) - Internally, this simply verifies if @group_id matches any member of insn->groups array. - NOTE: this API is only valid when detail option is ON (which is OFF by default). +#define GUM_IC_GET_THREAD_DATA(context, data_type) \ + ((data_type *) gum_invocation_context_get_listener_thread_data (context, \ + sizeof (data_type))) +#define GUM_IC_GET_FUNC_DATA(context, data_type) \ + ((data_type) gum_invocation_context_get_listener_function_data (context)) +#define GUM_IC_GET_INVOCATION_DATA(context, data_type) \ + ((data_type *) \ + gum_invocation_context_get_listener_invocation_data (context,\ + sizeof (data_type))) - WARN: when in 'diet' mode, this API is irrelevant because the engine does not - update @groups array. +#define GUM_IC_GET_REPLACEMENT_DATA(ctx, data_type) \ + ((data_type) gum_invocation_context_get_replacement_data (ctx)) - @handle: handle returned by cs_open() - @insn: disassembled instruction structure received from cs_disasm() or cs_disasm_iter() - @group_id: group that you want to check if this instruction belong to. +typedef struct _GumInvocationBackend GumInvocationBackend; +typedef struct _GumInvocationContext GumInvocationContext; +typedef guint GumPointCut; - @return: true if this instruction indeed belongs to the given group, or false otherwise. -*/ -CAPSTONE_EXPORT -bool CAPSTONE_API cs_insn_group(csh handle, const cs_insn *insn, unsigned int group_id); +struct _GumInvocationBackend +{ + GumPointCut (* get_point_cut) (GumInvocationContext * context); -/** - Check if a disassembled instruction IMPLICITLY used a particular register. - Find the register id from header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) - Internally, this simply verifies if @reg_id matches any member of insn->regs_read array. + GumThreadId (* get_thread_id) (GumInvocationContext * context); + guint (* get_depth) (GumInvocationContext * context); - NOTE: this API is only valid when detail option is ON (which is OFF by default) + gpointer (* get_listener_thread_data) (GumInvocationContext * context, + gsize required_size); + gpointer (* get_listener_function_data) (GumInvocationContext * context); + gpointer (* get_listener_invocation_data) ( + GumInvocationContext * context, gsize required_size); - WARN: when in 'diet' mode, this API is irrelevant because the engine does not - update @regs_read array. + gpointer (* get_replacement_data) (GumInvocationContext * context); - @insn: disassembled instruction structure received from cs_disasm() or cs_disasm_iter() - @reg_id: register that you want to check if this instruction used it. + gpointer state; + gpointer data; +}; - @return: true if this instruction indeed implicitly used the given register, or false otherwise. -*/ -CAPSTONE_EXPORT -bool CAPSTONE_API cs_reg_read(csh handle, const cs_insn *insn, unsigned int reg_id); +struct _GumInvocationContext +{ + gpointer function; + GumCpuContext * cpu_context; + gint system_error; -/** - Check if a disassembled instruction IMPLICITLY modified a particular register. - Find the register id from header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) - Internally, this simply verifies if @reg_id matches any member of insn->regs_write array. + /*< private */ + GumInvocationBackend * backend; +}; - NOTE: this API is only valid when detail option is ON (which is OFF by default) +enum _GumPointCut +{ + GUM_POINT_ENTER, + GUM_POINT_LEAVE +}; - WARN: when in 'diet' mode, this API is irrelevant because the engine does not - update @regs_write array. +G_BEGIN_DECLS - @insn: disassembled instruction structure received from cs_disasm() or cs_disasm_iter() - @reg_id: register that you want to check if this instruction modified it. +GUM_API GumPointCut gum_invocation_context_get_point_cut ( + GumInvocationContext * context); - @return: true if this instruction indeed implicitly modified the given register, or false otherwise. -*/ -CAPSTONE_EXPORT -bool CAPSTONE_API cs_reg_write(csh handle, const cs_insn *insn, unsigned int reg_id); +GUM_API gpointer gum_invocation_context_get_nth_argument ( + GumInvocationContext * context, guint n); +GUM_API void gum_invocation_context_replace_nth_argument ( + GumInvocationContext * context, guint n, gpointer value); +GUM_API gpointer gum_invocation_context_get_return_value ( + GumInvocationContext * context); +GUM_API void gum_invocation_context_replace_return_value ( + GumInvocationContext * context, gpointer value); -/** - Count the number of operands of a given type. - Find the operand type in header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) +GUM_API gpointer gum_invocation_context_get_return_address ( + GumInvocationContext * context); - NOTE: this API is only valid when detail option is ON (which is OFF by default) +GUM_API guint gum_invocation_context_get_thread_id ( + GumInvocationContext * context); +GUM_API guint gum_invocation_context_get_depth ( + GumInvocationContext * context); - @handle: handle returned by cs_open() - @insn: disassembled instruction structure received from cs_disasm() or cs_disasm_iter() - @op_type: Operand type to be found. +GUM_API gpointer gum_invocation_context_get_listener_thread_data ( + GumInvocationContext * context, gsize required_size); +GUM_API gpointer gum_invocation_context_get_listener_function_data ( + GumInvocationContext * context); +GUM_API gpointer gum_invocation_context_get_listener_invocation_data ( + GumInvocationContext * context, gsize required_size); - @return: number of operands of given type @op_type in instruction @insn, - or -1 on failure. -*/ -CAPSTONE_EXPORT -int CAPSTONE_API cs_op_count(csh handle, const cs_insn *insn, unsigned int op_type); +GUM_API gpointer gum_invocation_context_get_replacement_data ( + GumInvocationContext * context); -/** - Retrieve the position of operand of given type in .operands[] array. - Later, the operand can be accessed using the returned position. - Find the operand type in header file of corresponding architecture (arm.h for ARM, x86.h for X86, ...) +G_END_DECLS - NOTE: this API is only valid when detail option is ON (which is OFF by default) +#endif - @handle: handle returned by cs_open() - @insn: disassembled instruction structure received from cs_disasm() or cs_disasm_iter() - @op_type: Operand type to be found. - @position: position of the operand to be found. This must be in the range - [1, cs_op_count(handle, insn, op_type)] +G_BEGIN_DECLS - @return: index of operand of given type @op_type in .operands[] array - in instruction @insn, or -1 on failure. -*/ -CAPSTONE_EXPORT -int CAPSTONE_API cs_op_index(csh handle, const cs_insn *insn, unsigned int op_type, - unsigned int position); +#define GUM_TYPE_INVOCATION_LISTENER (gum_invocation_listener_get_type ()) +G_DECLARE_INTERFACE (GumInvocationListener, gum_invocation_listener, GUM, + INVOCATION_LISTENER, GObject) -/// Type of array to keep the list of registers -typedef uint16_t cs_regs[64]; +typedef void (* GumInvocationCallback) (GumInvocationContext * context, + gpointer user_data); -/** - Retrieve all the registers accessed by an instruction, either explicitly or - implicitly. +struct _GumInvocationListenerInterface +{ + GTypeInterface parent; - WARN: when in 'diet' mode, this API is irrelevant because engine does not - store registers. + void (* on_enter) (GumInvocationListener * self, + GumInvocationContext * context); + void (* on_leave) (GumInvocationListener * self, + GumInvocationContext * context); +}; - @handle: handle returned by cs_open() - @insn: disassembled instruction structure returned from cs_disasm() or cs_disasm_iter() - @regs_read: on return, this array contains all registers read by instruction. - @regs_read_count: number of registers kept inside @regs_read array. - @regs_write: on return, this array contains all registers written by instruction. - @regs_write_count: number of registers kept inside @regs_write array. +GUM_API GumInvocationListener * gum_make_call_listener ( + GumInvocationCallback on_enter, GumInvocationCallback on_leave, + gpointer data, GDestroyNotify data_destroy); +GUM_API GumInvocationListener * gum_make_probe_listener ( + GumInvocationCallback on_hit, gpointer data, GDestroyNotify data_destroy); - @return CS_ERR_OK on success, or other value on failure (refer to cs_err enum - for detailed error). -*/ -CAPSTONE_EXPORT -cs_err CAPSTONE_API cs_regs_access(csh handle, const cs_insn *insn, - cs_regs regs_read, uint8_t *regs_read_count, - cs_regs regs_write, uint8_t *regs_write_count); +GUM_API void gum_invocation_listener_on_enter (GumInvocationListener * self, + GumInvocationContext * context); +GUM_API void gum_invocation_listener_on_leave (GumInvocationListener * self, + GumInvocationContext * context); -#ifdef __cplusplus -} -#endif +G_END_DECLS #endif + G_BEGIN_DECLS #define GUM_TYPE_INTERCEPTOR (gum_interceptor_get_type ()) @@ -82789,11 +83058,60 @@ typedef GArray GumInvocationStack; typedef guint GumInvocationState; typedef void (* GumInterceptorLockedFunc) (gpointer user_data); -typedef enum +typedef enum { + GUM_INTERCEPTOR_SCENARIO_DEFAULT, + GUM_INTERCEPTOR_SCENARIO_ONLINE, + GUM_INTERCEPTOR_SCENARIO_OFFLINE, +} GumInterceptorScenario; + +typedef enum { + GUM_INVOCATION_IGNORABLE, + GUM_INVOCATION_UNIGNORABLE, +} GumInvocationIgnorability; + +typedef enum _GumRedirectWriteResult { + GUM_REDIRECT_WRITTEN, + GUM_REDIRECT_DECLINED, +} GumRedirectWriteResult; + +typedef struct _GumInterceptorOptions GumInterceptorOptions; +typedef struct _GumAttachOptions GumAttachOptions; +typedef struct _GumReplaceOptions GumReplaceOptions; +typedef struct _GumRedirectWriteDetails GumRedirectWriteDetails; + +typedef GumRedirectWriteResult (* GumWriteRedirectFunc) ( + const GumRedirectWriteDetails * details, gpointer user_data); + +struct _GumInterceptorOptions +{ + gint scratch_register; + GumInterceptorScenario scenario; + GumRelocationPolicy relocation_policy; + GumWriteRedirectFunc write_redirect; + gpointer write_redirect_data; + guint redirect_space_hint; +}; + +struct _GumAttachOptions +{ + GumInterceptorOptions instrumentation; + gpointer listener_function_data; + GumInvocationIgnorability ignorability; +}; + +struct _GumReplaceOptions { - GUM_ATTACH_FLAGS_NONE = 0, - GUM_ATTACH_FLAGS_UNIGNORABLE = (1 << 0), -} GumAttachFlags; + GumInterceptorOptions instrumentation; + gpointer replacement_data; +}; + +struct _GumRedirectWriteDetails +{ + gpointer writer; + gpointer target; + gint scratch_register; + guint capacity; +}; typedef enum { @@ -82815,24 +83133,31 @@ typedef enum GUM_API GumInterceptor * gum_interceptor_obtain (void); +GUM_API void gum_interceptor_set_default_options (GumInterceptor * self, + const GumInterceptorOptions * options); + GUM_API GumAttachReturn gum_interceptor_attach (GumInterceptor * self, - gpointer function_address, GumInvocationListener * listener, - gpointer listener_function_data, GumAttachFlags flags); + gpointer target, GumInvocationListener * listener, + const GumAttachOptions * options); GUM_API void gum_interceptor_detach (GumInterceptor * self, GumInvocationListener * listener); GUM_API GumReplaceReturn gum_interceptor_replace (GumInterceptor * self, gpointer function_address, gpointer replacement_function, - gpointer replacement_data, gpointer * original_function); + gpointer * original_function, const GumReplaceOptions * options); GumReplaceReturn gum_interceptor_replace_fast (GumInterceptor * self, gpointer function_address, gpointer replacement_function, - gpointer * original_function); + gpointer * original_function, const GumInterceptorOptions * options); GUM_API void gum_interceptor_revert (GumInterceptor * self, - gpointer function_address); + gpointer target); GUM_API void gum_interceptor_begin_transaction (GumInterceptor * self); GUM_API void gum_interceptor_end_transaction (GumInterceptor * self); GUM_API gboolean gum_interceptor_flush (GumInterceptor * self); +GUM_API gboolean gum_interceptor_flush_function (GumInterceptor * self, + gconstpointer function_address); +GUM_API gboolean gum_interceptor_flush_listener (GumInterceptor * self, + GumInvocationListener * listener); GUM_API GumInvocationContext * gum_interceptor_get_current_invocation (void); GUM_API GumInvocationContext * gum_interceptor_get_live_replacement_invocation ( @@ -83208,7 +83533,7 @@ G_END_DECLS #endif /* - * Copyright (C) 2025 Ole André Vadla Ravnås + * Copyright (C) 2025-2026 Ole André Vadla Ravnås * * Licence: wxWindows Library Licence, Version 3.1 */ @@ -83223,6 +83548,9 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (GumModuleRegistry, gum_module_registry, GUM, MODULE_REGISTRY, GObject) +GUM_API void gum_module_registry_set_rtld_notifier_offsets ( + const guint * offsets, guint n_offsets); + GUM_API GumModuleRegistry * gum_module_registry_obtain (void); GUM_API void gum_module_registry_enumerate_modules (GumModuleRegistry * self, @@ -83298,7 +83626,7 @@ G_END_DECLS #define __GUM_STALKER_H__ /* - * Copyright (C) 2009-2022 Ole André Vadla Ravnås + * Copyright (C) 2009-2026 Ole André Vadla Ravnås * Copyright (C) 2023 Fabian Freyer * Copyright (C) 2024 Yannis Juglaret * @@ -83652,6 +83980,21 @@ GUM_API gboolean gum_x86_writer_put_fxsave_reg_ptr (GumX86Writer * self, GUM_API gboolean gum_x86_writer_put_fxrstor_reg_ptr (GumX86Writer * self, GumX86Reg reg); +GUM_API gboolean gum_x86_writer_put_vmovdqu64_reg_offset_ptr_zmm ( + GumX86Writer * self, GumX86Reg dst_base, gssize dst_offset, guint src_zmm); +GUM_API gboolean gum_x86_writer_put_vmovdqu64_zmm_reg_offset_ptr ( + GumX86Writer * self, guint dst_zmm, GumX86Reg src_base, gssize src_offset); +GUM_API gboolean gum_x86_writer_put_vextracti64x4_reg_offset_ptr_zmm ( + GumX86Writer * self, GumX86Reg dst_base, gssize dst_offset, guint src_zmm, + guint8 imm); +GUM_API gboolean gum_x86_writer_put_vinserti64x4_zmm_reg_offset_ptr ( + GumX86Writer * self, guint dst_zmm, GumX86Reg src_base, gssize src_offset, + guint8 imm); +GUM_API gboolean gum_x86_writer_put_kmovq_reg_offset_ptr_kreg ( + GumX86Writer * self, GumX86Reg dst_base, gssize dst_offset, guint src_kreg); +GUM_API gboolean gum_x86_writer_put_kmovq_kreg_reg_offset_ptr ( + GumX86Writer * self, guint dst_kreg, GumX86Reg src_base, gssize src_offset); + GUM_API void gum_x86_writer_put_u8 (GumX86Writer * self, guint8 value); GUM_API void gum_x86_writer_put_s8 (GumX86Writer * self, gint8 value); GUM_API void gum_x86_writer_put_bytes (GumX86Writer * self, const guint8 * data, @@ -84011,7 +84354,7 @@ G_END_DECLS /* * Copyright (C) 2014-2023 Ole André Vadla Ravnås * Copyright (C) 2017 Antonio Ken Iannillo - * Copyright (C) 2023 Håvard Sørbø + * Copyright (C) 2023-2026 Håvard Sørbø * Copyright (C) 2023 Fabian Freyer * * Licence: wxWindows Library Licence, Version 3.1 @@ -84208,6 +84551,8 @@ GUM_API void gum_arm64_writer_put_mov_reg_nzcv (GumArm64Writer * self, arm64_reg reg); GUM_API void gum_arm64_writer_put_mov_nzcv_reg (GumArm64Writer * self, arm64_reg reg); +GUM_API gboolean gum_arm64_writer_put_movk_reg_imm (GumArm64Writer * self, + arm64_reg reg, guint16 imm, guint shift); GUM_API gboolean gum_arm64_writer_put_uxtw_reg_reg (GumArm64Writer * self, arm64_reg dst_reg, arm64_reg src_reg); GUM_API gboolean gum_arm64_writer_put_add_reg_reg_imm (GumArm64Writer * self, @@ -84235,6 +84580,8 @@ GUM_API gboolean gum_arm64_writer_put_cmp_reg_reg (GumArm64Writer * self, GUM_API gboolean gum_arm64_writer_put_xpaci_reg (GumArm64Writer * self, arm64_reg reg); +GUM_API gboolean gum_arm64_writer_put_pacia_reg_reg (GumArm64Writer * self, + arm64_reg dst_reg, arm64_reg mod_reg); GUM_API void gum_arm64_writer_put_nop (GumArm64Writer * self); GUM_API void gum_arm64_writer_put_brk_imm (GumArm64Writer * self, guint16 imm); @@ -84516,8 +84863,6 @@ struct _GumCallDetails GUM_API gboolean gum_stalker_is_supported (void); -GUM_API void gum_stalker_activate_experimental_unwind_support (void); - GUM_API GumStalker * gum_stalker_new (void); GUM_API void gum_stalker_exclude (GumStalker * self, @@ -84752,6 +85097,77 @@ GUM_API void gum_tls_key_set_value (GumTlsKey key, gpointer value); G_END_DECLS +#endif +/* + * Copyright (C) 2024-2025 Francesco Tamagni + * Copyright (C) 2024-2026 Ole André Vadla Ravnås + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +#ifndef __GUM_UNWIND_BROKER_H__ +#define __GUM_UNWIND_BROKER_H__ + + +G_BEGIN_DECLS + +#define GUM_TYPE_UNWIND_BROKER (gum_unwind_broker_get_type ()) +G_DECLARE_FINAL_TYPE (GumUnwindBroker, gum_unwind_broker, GUM, UNWIND_BROKER, + GObject) + +#define GUM_TYPE_UNWIND_SECTIONS_PROVIDER \ + (gum_unwind_sections_provider_get_type ()) +G_DECLARE_INTERFACE (GumUnwindSectionsProvider, gum_unwind_sections_provider, + GUM, UNWIND_SECTIONS_PROVIDER, GObject) + +#define GUM_TYPE_UNWIND_PC_TRANSLATOR (gum_unwind_pc_translator_get_type ()) +G_DECLARE_INTERFACE (GumUnwindPcTranslator, gum_unwind_pc_translator, GUM, + UNWIND_PC_TRANSLATOR, GObject) + +struct _GumUnwindSectionsProviderInterface +{ + GTypeInterface parent; + + const GumMemoryRange * (* get_range) (GumUnwindSectionsProvider * self); + gboolean (* fill) (GumUnwindSectionsProvider * self, GumAddress address, + gpointer info); +}; + +struct _GumUnwindPcTranslatorInterface +{ + GTypeInterface parent; + + GumAddress (* translate) (GumUnwindPcTranslator * self, + GumAddress code_address); + gboolean (* install_resume_context) (GumUnwindPcTranslator * self, + gpointer unwind_context, GumAddress real_resume_ip); +}; + +GUM_API GumUnwindBroker * gum_unwind_broker_obtain (void); + +GUM_API void gum_unwind_broker_add_sections_provider (GumUnwindBroker * self, + GumUnwindSectionsProvider * provider); +GUM_API void gum_unwind_broker_remove_sections_provider (GumUnwindBroker * self, + GumUnwindSectionsProvider * provider); + +GUM_API void gum_unwind_broker_add_pc_translator (GumUnwindBroker * self, + GumUnwindPcTranslator * translator); +GUM_API void gum_unwind_broker_remove_pc_translator (GumUnwindBroker * self, + GumUnwindPcTranslator * translator); + +GUM_API const GumMemoryRange * gum_unwind_sections_provider_get_range ( + GumUnwindSectionsProvider * self); +GUM_API gboolean gum_unwind_sections_provider_fill ( + GumUnwindSectionsProvider * self, GumAddress address, gpointer info); + +GUM_API GumAddress gum_unwind_pc_translator_translate ( + GumUnwindPcTranslator * self, GumAddress code_address); +GUM_API gboolean gum_unwind_pc_translator_install_resume_context ( + GumUnwindPcTranslator * self, gpointer unwind_context, + GumAddress real_resume_ip); + +G_END_DECLS + #endif G_BEGIN_DECLS @@ -84773,8 +85189,10 @@ G_END_DECLS /* json-glib.h: Main header * * This file is part of JSON-GLib - * Copyright (C) 2007 OpenedHand Ltd. - * Copyright (C) 2009 Intel Corp. + * + * SPDX-FileCopyrightText: 2007 OpenedHand Ltd. + * SPDX-FileCopyrightText: 2009 Intel Corp. + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -84792,17 +85210,17 @@ G_END_DECLS * Author: * Emmanuele Bassi */ - -#ifndef __JSON_GLIB_H__ -#define __JSON_GLIB_H__ +#pragma once #define __JSON_GLIB_INSIDE__ /* json-types.h - JSON data types * * This file is part of JSON-GLib - * Copyright (C) 2007 OpenedHand Ltd. - * Copyright (C) 2009 Intel Corp. + * + * SPDX-FileCopyrightText: 2007 OpenedHand Ltd. + * SPDX-FileCopyrightText: 2009 Intel Corp. + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -84821,8 +85239,7 @@ G_END_DECLS * Emmanuele Bassi */ -#ifndef __JSON_TYPES_H__ -#define __JSON_TYPES_H__ +#pragma once #if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) #error "Only can be included directly." @@ -84831,7 +85248,9 @@ G_END_DECLS /* json-version-macros.h - JSON-GLib symbol versioning macros * * This file is part of JSON-GLib - * Copyright © 2014 Emmanuele Bassi + * + * SPDX-FileCopyrightText: 2014 Emmanuele Bassi + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -84847,8 +85266,7 @@ G_END_DECLS * License along with this library. If not, see . */ -#ifndef __JSON_VERSION_MACROS_H__ -#define __JSON_VERSION_MACROS_H__ +#pragma once #if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) #error "Only can be included directly." @@ -84857,8 +85275,10 @@ G_END_DECLS /* json-version.h - JSON-GLib versioning information * * This file is part of JSON-GLib - * Copyright (C) 2007 OpenedHand Ltd. - * Copyright (C) 2009 Intel Corp. + * + * SPDX-FileCopyrightText: 2007 OpenedHand Ltd. + * SPDX-FileCopyrightText: 2009 Intel Corp. + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -84877,8 +85297,7 @@ G_END_DECLS * Emmanuele Bassi */ -#ifndef __JSON_VERSION_H__ -#define __JSON_VERSION_H__ +#pragma once #if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) #error "Only can be included directly." @@ -84896,21 +85315,21 @@ G_END_DECLS * * Json minor version component (e.g. 2 if `JSON_VERSION` is "1.2.3") */ -#define JSON_MINOR_VERSION (7) +#define JSON_MINOR_VERSION (10) /** * JSON_MICRO_VERSION: * * Json micro version component (e.g. 3 if `JSON_VERSION` is "1.2.3") */ -#define JSON_MICRO_VERSION (1) +#define JSON_MICRO_VERSION (7) /** * JSON_VERSION * * The version of JSON-GLib. */ -#define JSON_VERSION (1.7.1) +#define JSON_VERSION (1.10.7) /** * JSON_VERSION_S: @@ -84918,7 +85337,7 @@ G_END_DECLS * The version of JSON-GLib, encoded as a string, useful for printing and * concatenation. */ -#define JSON_VERSION_S "1.7.1" +#define JSON_VERSION_S "1.10.7" /** * JSON_ENCODE_VERSION: @@ -84956,12 +85375,27 @@ G_END_DECLS (JSON_MAJOR_VERSION == (major) && JSON_MINOR_VERSION == (minor) && \ JSON_MICRO_VERSION >= (micro))) -#endif /* __JSON_VERSION_H__ */ +#define JSON_STATIC_BUILD + +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(JSON_STATIC_BUILD) +# define _JSON_EXPORT __declspec(dllexport) +# define _JSON_IMPORT __declspec(dllimport) +#elif defined(__GNUC__) && (__GNUC__ >= 4) +# define _JSON_EXPORT __attribute__((__visibility__("default"))) +# define _JSON_IMPORT +#else +# define _JSON_EXPORT +# define _JSON_IMPORT +#endif -#ifndef _JSON_EXTERN -#define _JSON_EXTERN extern +#ifdef JSON_COMPILATION +# define _JSON_API _JSON_EXPORT +#else +# define _JSON_API _JSON_IMPORT #endif +#define _JSON_EXTERN _JSON_API extern + #ifdef JSON_DISABLE_DEPRECATION_WARNINGS #define JSON_DEPRECATED _JSON_EXTERN #define JSON_DEPRECATED_FOR(f) _JSON_EXTERN @@ -85018,6 +85452,15 @@ G_END_DECLS */ #define JSON_VERSION_1_8 (G_ENCODE_VERSION (1, 8)) +/** + * JSON_VERSION_1_10: + * + * The encoded representation of JSON-GLib version "1.10". + * + * Since: 1.10 + */ +#define JSON_VERSION_1_10 (G_ENCODE_VERSION (1, 10)) + /* evaluates to the current stable version; for development cycles, * this means the next stable target */ @@ -85169,7 +85612,20 @@ G_END_DECLS # define JSON_AVAILABLE_IN_1_8 _JSON_EXTERN #endif -#endif /* __JSON_VERSION_MACROS_H__ */ +/* 1.10 */ +#if JSON_VERSION_MIN_REQUIRED >= JSON_VERSION_1_10 +# define JSON_DEPRECATED_IN_1_10 JSON_DEPRECATED +# define JSON_DEPRECATED_IN_1_10_FOR(f) JSON_DEPRECATED_FOR(f) +#else +# define JSON_DEPRECATED_IN_1_10 _JSON_EXTERN +# define JSON_DEPRECATED_IN_1_10_FOR(f) _JSON_EXTERN +#endif + +#if JSON_VERSION_MAX_ALLOWED < JSON_VERSION_1_10 +# define JSON_AVAILABLE_IN_1_10 JSON_UNAVAILABLE(1, 10) +#else +# define JSON_AVAILABLE_IN_1_10 _JSON_EXTERN +#endif G_BEGIN_DECLS @@ -85261,7 +85717,7 @@ typedef enum { * @object: the iterated JSON object * @member_name: the name of the member * @member_node: the value of the member - * @user_data: data passed to the function + * @user_data: (closure): data passed to the function * * The function to be passed to [method@Json.Object.foreach_member]. * @@ -85282,7 +85738,7 @@ typedef void (* JsonObjectForeach) (JsonObject *object, * @array: the iterated JSON array * @index_: the index of the element * @element_node: the value of the element at the given @index_ - * @user_data: data passed to the function + * @user_data: (closure): data passed to the function * * The function to be passed to [method@Json.Array.foreach_element]. * @@ -85674,12 +86130,12 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (JsonNode, json_node_unref) G_END_DECLS -#endif /* __JSON_TYPES_H__ */ - /* json-builder.h: JSON tree builder * * This file is part of JSON-GLib - * Copyright (C) 2010 Luca Bruno + * + * SPDX-FileCopyrightText: 2010 Luca Bruno + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -85698,8 +86154,7 @@ G_END_DECLS * Luca Bruno */ -#ifndef __JSON_BUILDER_H__ -#define __JSON_BUILDER_H__ +#pragma once #if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) #error "Only can be included directly." @@ -85784,13 +86239,13 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (JsonBuilder, g_object_unref) #endif G_END_DECLS - -#endif /* __JSON_BUILDER_H__ */ /* json-generator.h - JSON streams generator * * This file is part of JSON-GLib - * Copyright (C) 2007 OpenedHand Ltd. - * Copyright (C) 2009 Intel Corp. + * + * SPDX-FileCopyrightText: 2007 OpenedHand Ltd. + * SPDX-FileCopyrightText: 2009 Intel Corp. + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -85808,9 +86263,7 @@ G_END_DECLS * Author: * Emmanuele Bassi */ - -#ifndef __JSON_GENERATOR_H__ -#define __JSON_GENERATOR_H__ +#pragma once #if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) #error "Only can be included directly." @@ -85876,6 +86329,9 @@ void json_generator_set_root (JsonGenerator *generator, JsonNode *node); JSON_AVAILABLE_IN_1_0 JsonNode * json_generator_get_root (JsonGenerator *generator); +JSON_AVAILABLE_IN_1_10 +void json_generator_take_root (JsonGenerator *generator, + JsonNode *node); JSON_AVAILABLE_IN_1_4 GString *json_generator_to_gstring (JsonGenerator *generator, @@ -85899,13 +86355,13 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (JsonGenerator, g_object_unref) #endif G_END_DECLS - -#endif /* __JSON_GENERATOR_H__ */ /* json-parser.h - JSON streams parser * * This file is part of JSON-GLib - * Copyright (C) 2007 OpenedHand Ltd. - * Copyright (C) 2009 Intel Corp. + * + * SPDX-FileCopyrightText: 2007 OpenedHand Ltd. + * SPDX-FileCopyrightText: 2009 Intel Corp. + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -85923,9 +86379,7 @@ G_END_DECLS * Author: * Emmanuele Bassi */ - -#ifndef __JSON_PARSER_H__ -#define __JSON_PARSER_H__ +#pragma once #if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) #error "Only can be included directly." @@ -85948,6 +86402,15 @@ G_BEGIN_DECLS */ #define JSON_PARSER_ERROR (json_parser_error_quark ()) +/** + * JSON_PARSER_MAX_RECURSION_DEPTH: + * + * The maximum recursion depth for a JSON tree. + * + * Since: 1.10 + */ +#define JSON_PARSER_MAX_RECURSION_DEPTH (1024) + typedef struct _JsonParser JsonParser; typedef struct _JsonParserPrivate JsonParserPrivate; typedef struct _JsonParserClass JsonParserClass; @@ -85959,8 +86422,6 @@ typedef struct _JsonParserClass JsonParserClass; * @JSON_PARSER_ERROR_MISSING_COMMA: expected comma * @JSON_PARSER_ERROR_MISSING_COLON: expected colon * @JSON_PARSER_ERROR_INVALID_BAREWORD: invalid bareword - * @JSON_PARSER_ERROR_EMPTY_MEMBER_NAME: empty member name (Since: 0.16) - * @JSON_PARSER_ERROR_INVALID_DATA: invalid data (Since: 0.18) * @JSON_PARSER_ERROR_UNKNOWN: unknown error * * Error codes for `JSON_PARSER_ERROR`. @@ -85973,10 +86434,47 @@ typedef enum { JSON_PARSER_ERROR_MISSING_COMMA, JSON_PARSER_ERROR_MISSING_COLON, JSON_PARSER_ERROR_INVALID_BAREWORD, + /** + * JSON_PARSER_ERROR_EMPTY_MEMBER_NAME: + * + * Empty member name. + * + * Since: 0.16 + */ JSON_PARSER_ERROR_EMPTY_MEMBER_NAME, + /** + * JSON_PARSER_ERROR_INVALID_DATA: + * + * Invalid data. + * + * Since: 0.18 + */ JSON_PARSER_ERROR_INVALID_DATA, - - JSON_PARSER_ERROR_UNKNOWN + JSON_PARSER_ERROR_UNKNOWN, + /** + * JSON_PARSER_ERROR_NESTING: + * + * Too many levels of nesting. + * + * Since: 1.10 + */ + JSON_PARSER_ERROR_NESTING, + /** + * JSON_PARSER_ERROR_INVALID_STRUCTURE: + * + * Invalid structure. + * + * Since: 1.10 + */ + JSON_PARSER_ERROR_INVALID_STRUCTURE, + /** + * JSON_PARSER_ERROR_INVALID_ASSIGNMENT: + * + * Invalid assignment. + * + * Since: 1.10 + */ + JSON_PARSER_ERROR_INVALID_ASSIGNMENT } JsonParserError; struct _JsonParser @@ -86049,6 +86547,11 @@ JSON_AVAILABLE_IN_1_0 JsonParser *json_parser_new (void); JSON_AVAILABLE_IN_1_2 JsonParser *json_parser_new_immutable (void); +JSON_AVAILABLE_IN_1_10 +void json_parser_set_strict (JsonParser *parser, + gboolean strict); +JSON_AVAILABLE_IN_1_10 +gboolean json_parser_get_strict (JsonParser *parser); JSON_AVAILABLE_IN_1_0 gboolean json_parser_load_from_file (JsonParser *parser, const gchar *filename, @@ -86096,12 +86599,12 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (JsonParser, g_object_unref) #endif G_END_DECLS - -#endif /* __JSON_PARSER_H__ */ /* json-path.h - JSONPath implementation * * This file is part of JSON-GLib - * Copyright © 2011 Intel Corp. + * + * SPDX-FileCopyrightText: 2011 Intel Corp. + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -86120,8 +86623,7 @@ G_END_DECLS * Emmanuele Bassi */ -#ifndef __JSON_PATH_H__ -#define __JSON_PATH_H__ +#pragma once #if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) #error "Only can be included directly." @@ -86186,12 +86688,12 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (JsonPath, g_object_unref) #endif G_END_DECLS - -#endif /* __JSON_PATH_H__ */ /* json-reader.h - JSON cursor parser * * This file is part of JSON-GLib - * Copyright (C) 2010 Intel Corp. + * + * SPDX-FileCopyrightText: 2010 Intel Corp. + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -86210,8 +86712,7 @@ G_END_DECLS * Emmanuele Bassi */ -#ifndef __JSON_READER_H__ -#define __JSON_READER_H__ +#pragma once #if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) #error "Only can be included directly." @@ -86349,12 +86850,12 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (JsonReader, g_object_unref) #endif G_END_DECLS - -#endif /* __JSON_READER_H__ */ /* json-utils.h - JSON utility API * * This file is part of JSON-GLib - * Copyright 2015 Emmanuele Bassi + * + * SPDX-FileCopyrightText: 2015 Emmanuele Bassi + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -86370,8 +86871,7 @@ G_END_DECLS * License along with this library. If not, see . */ -#ifndef __JSON_UTILS_H__ -#define __JSON_UTILS_H__ +#pragma once #if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) #error "Only can be included directly." @@ -86389,39 +86889,39 @@ char * json_to_string (JsonNode *node, G_END_DECLS -#endif /* __JSON_UTILS_H__ */ - /* This file is generated by glib-mkenums, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ -#ifndef __JSON_ENUM_TYPES_H__ -#define __JSON_ENUM_TYPES_H__ +#pragma once -#if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) -#error "Only can be included directly." -#endif + G_BEGIN_DECLS -G_BEGIN_DECLS /* enumerations from "json-parser.h" */ + JSON_AVAILABLE_IN_1_0 -GType json_parser_error_get_type (void) G_GNUC_CONST; +GType json_parser_error_get_type (void); #define JSON_TYPE_PARSER_ERROR (json_parser_error_get_type()) + /* enumerations from "json-path.h" */ + JSON_AVAILABLE_IN_1_0 -GType json_path_error_get_type (void) G_GNUC_CONST; +GType json_path_error_get_type (void); #define JSON_TYPE_PATH_ERROR (json_path_error_get_type()) + /* enumerations from "json-reader.h" */ + JSON_AVAILABLE_IN_1_0 -GType json_reader_error_get_type (void) G_GNUC_CONST; +GType json_reader_error_get_type (void); #define JSON_TYPE_READER_ERROR (json_reader_error_get_type()) + /* enumerations from "json-types.h" */ + JSON_AVAILABLE_IN_1_0 -GType json_node_type_get_type (void) G_GNUC_CONST; +GType json_node_type_get_type (void); #define JSON_TYPE_NODE_TYPE (json_node_type_get_type()) -G_END_DECLS -#endif /* !__JSON_ENUM_TYPES_H__ */ +G_END_DECLS /* Generated data ends here */ @@ -86429,8 +86929,10 @@ G_END_DECLS /* json-gobject.h - JSON GObject integration * * This file is part of JSON-GLib - * Copyright (C) 2007 OpenedHand Ltd. - * Copyright (C) 2009 Intel Corp. + * + * SPDX-FileCopyrightText: 2007 OpenedHand Ltd. + * SPDX-FileCopyrightText: 2009 Intel Corp. + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -86449,8 +86951,7 @@ G_END_DECLS * Emmanuele Bassi */ -#ifndef __JSON_GOBJECT_H__ -#define __JSON_GOBJECT_H__ +#pragma once G_BEGIN_DECLS @@ -86677,13 +87178,13 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (JsonSerializable, g_object_unref) G_END_DECLS -#endif /* __JSON_GOBJECT_H__ */ - /* json-gvariant.h - JSON GVariant integration * * This file is part of JSON-GLib - * Copyright (C) 2007 OpenedHand Ltd. - * Copyright (C) 2009 Intel Corp. + * + * SPDX-FileCopyrightText: 2007 OpenedHand Ltd. + * SPDX-FileCopyrightText: 2009 Intel Corp. + * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -86702,8 +87203,7 @@ G_END_DECLS * Eduardo Lima Mitev */ -#ifndef __JSON_GVARIANT_H__ -#define __JSON_GVARIANT_H__ +#pragma once #if !defined(__JSON_GLIB_INSIDE__) && !defined(JSON_COMPILATION) #error "Only can be included directly." @@ -86730,12 +87230,8 @@ GVariant * json_gvariant_deserialize_data (const gchar *json, G_END_DECLS -#endif /* __JSON_GVARIANT_H__ */ - #undef __JSON_GLIB_INSIDE__ -#endif /* __JSON_GLIB_H__ */ - G_BEGIN_DECLS #define GUM_TYPE_SCRIPT (gum_script_get_type ()) @@ -86758,6 +87254,8 @@ struct _GumScriptInterface GAsyncReadyCallback callback, gpointer user_data); void (* unload_finish) (GumScript * self, GAsyncResult * result); void (* unload_sync) (GumScript * self, GCancellable * cancellable); + void (* interrupt) (GumScript * self); + void (* terminate) (GumScript * self); void (* set_message_handler) (GumScript * self, GumScriptMessageHandler handler, gpointer data, @@ -86782,6 +87280,8 @@ GUM_API void gum_script_unload (GumScript * self, GCancellable * cancellable, GUM_API void gum_script_unload_finish (GumScript * self, GAsyncResult * result); GUM_API void gum_script_unload_sync (GumScript * self, GCancellable * cancellable); +GUM_API void gum_script_interrupt (GumScript * self); +GUM_API void gum_script_terminate (GumScript * self); GUM_API void gum_script_set_message_handler (GumScript * self, GumScriptMessageHandler handler, gpointer data, @@ -86945,9 +87445,6 @@ GUM_API gboolean gum_script_backend_is_locked (GumScriptBackend * self); GUM_API GumScriptScheduler * gum_script_backend_get_scheduler (void); -GUM_API gchar * gum_script_backend_extract_inline_source_map ( - const gchar * source); - G_END_DECLS #endif diff --git a/frida-gum/src/api_resolver.rs b/frida-gum/src/api_resolver.rs new file mode 100644 index 00000000..4a844a20 --- /dev/null +++ b/frida-gum/src/api_resolver.rs @@ -0,0 +1,167 @@ +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! API resolver for finding functions by pattern matching. +//! +//! The ApiResolver allows you to find APIs using wildcard patterns like: +//! - `"exports:*!open*"` - Find all exports starting with "open" +//! - `"imports:libc.so!*"` - Find all imports from libc.so +//! - `"module:kernel32.dll!CreateFile*"` - Find CreateFile* in kernel32.dll + +use { + crate::NativePointer, + core::ffi::{CStr, c_void}, + cstr_core::CString, + frida_gum_sys as gum_sys, +}; + +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; + +#[cfg(feature = "std")] +use std::{string::String, vec::Vec}; + +/// Details about a resolved API match. +#[derive(Debug, Clone)] +pub struct ApiMatch { + /// Name of the matched API + pub name: String, + /// Address of the matched API + pub address: NativePointer, + /// Size of the matched API (if available) + pub size: Option, +} + +/// API resolver for finding functions by pattern. +pub struct ApiResolver { + resolver: *mut gum_sys::GumApiResolver, +} + +impl ApiResolver { + /// Create a new ApiResolver of the specified type. + /// + /// # Arguments + /// + /// * `resolver_type` - Type of resolver: "module", "objc", or "swift" + /// + /// # Examples + /// + /// ```no_run + /// use frida_gum::ApiResolver; + /// + /// let resolver = ApiResolver::make("module").expect("Failed to create resolver"); + /// ``` + pub fn make(resolver_type: &str) -> Option { + let type_cstr = match CString::new(resolver_type) { + Ok(s) => s, + Err(_) => return None, + }; + + let resolver = unsafe { gum_sys::gum_api_resolver_make(type_cstr.as_ptr().cast()) }; + + if resolver.is_null() { + None + } else { + Some(ApiResolver { resolver }) + } + } + + /// Find all APIs matching the given query pattern. + /// + /// # Arguments + /// + /// * `query` - Pattern to match, e.g., "exports:*!open*" + /// + /// # Returns + /// + /// A vector of matched APIs with their names and addresses. + /// + /// # Pattern Syntax + /// + /// - `exports:*!open*` - All exports starting with "open" + /// - `exports:libc.so!*` - All exports from libc.so + /// - `imports:*!malloc` - All imports named malloc + /// - `imports:mylib!free` - Import "free" in mylib + /// + /// # Examples + /// + /// ```no_run + /// use frida_gum::ApiResolver; + /// + /// let resolver = ApiResolver::make("module").unwrap(); + /// let matches = resolver.enumerate_matches("exports:*!CreateFile*"); + /// for m in matches { + /// println!("{} at {:?}", m.name, m.address); + /// } + /// ``` + pub fn enumerate_matches(&self, query: &str) -> Vec { + let query_cstr = match CString::new(query) { + Ok(s) => s, + Err(_) => return Vec::new(), + }; + + let mut matches = Vec::new(); + + unsafe extern "C" fn callback( + details: *const gum_sys::GumApiDetails, + user_data: gum_sys::gpointer, + ) -> gum_sys::gboolean { + unsafe { + let matches = &mut *(user_data as *mut Vec); + + let name = if !(*details).name.is_null() { + CStr::from_ptr((*details).name) + .to_string_lossy() + .into_owned() + } else { + String::new() + }; + + let address = NativePointer((*details).address as *mut c_void); + + let size = if (*details).size > 0 { + Some((*details).size as usize) + } else { + None + }; + + matches.push(ApiMatch { + name, + address, + size, + }); + + 1 // Continue enumeration + } + } + + let mut error: *mut gum_sys::GError = core::ptr::null_mut(); + unsafe { + gum_sys::gum_api_resolver_enumerate_matches( + self.resolver, + query_cstr.as_ptr().cast(), + Some(callback), + &mut matches as *mut _ as *mut c_void, + &mut error, + ); + if !error.is_null() { + crate::glib_compat::g_error_free(error); + } + } + + matches + } +} + +impl Drop for ApiResolver { + fn drop(&mut self) { + unsafe { frida_gum_sys::g_object_unref(self.resolver as *mut c_void) }; + } +} + +unsafe impl Send for ApiResolver {} +unsafe impl Sync for ApiResolver {} diff --git a/frida-gum/src/cloak.rs b/frida-gum/src/cloak.rs new file mode 100644 index 00000000..1bb49615 --- /dev/null +++ b/frida-gum/src/cloak.rs @@ -0,0 +1,215 @@ +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Cloaking utilities to hide threads, memory ranges, and file descriptors +//! from instrumentation. +//! +//! The Cloak module allows hiding instrumentation-related resources so that +//! they do not appear when enumerating threads, memory ranges, or file +//! descriptors. This is commonly used to prevent self-introspection from +//! detecting Frida's own threads and memory regions. + +use { + crate::{MemoryRange, NativePointer}, + core::ffi::c_void, + frida_gum_sys as gum_sys, +}; + +#[cfg(not(feature = "std"))] +use alloc::{boxed::Box, vec::Vec}; + +#[cfg(feature = "std")] +use std::{boxed::Box, vec::Vec}; + +/// Static interface to Frida's cloak machinery. +pub struct Cloak; + +impl Cloak { + /// Hide the specified thread from enumeration. + pub fn add_thread(id: usize) { + unsafe { gum_sys::gum_cloak_add_thread(id as gum_sys::GumThreadId) }; + } + + /// Stop hiding the specified thread. + pub fn remove_thread(id: usize) { + unsafe { gum_sys::gum_cloak_remove_thread(id as gum_sys::GumThreadId) }; + } + + /// Check whether the specified thread is currently hidden. + pub fn has_thread(id: usize) -> bool { + unsafe { gum_sys::gum_cloak_has_thread(id as gum_sys::GumThreadId) != 0 } + } + + /// Iterate over all currently cloaked threads, invoking the callback for each. + /// + /// Returning `false` from the callback halts iteration. + pub fn enumerate_threads(mut callback: F) + where + F: FnMut(usize) -> bool, + { + unsafe extern "C" fn trampoline( + id: gum_sys::GumThreadId, + user_data: gum_sys::gpointer, + ) -> gum_sys::gboolean + where + F: FnMut(usize) -> bool, + { + unsafe { + let cb = &mut *(user_data as *mut F); + if cb(id as usize) { 1 } else { 0 } + } + } + + unsafe { + gum_sys::gum_cloak_enumerate_threads( + Some(trampoline::), + &mut callback as *mut _ as *mut c_void, + ); + } + } + + /// Hide the specified memory range from enumeration. + pub fn add_range(range: &MemoryRange) { + unsafe { gum_sys::gum_cloak_add_range(&range.memory_range as *const _) }; + } + + /// Stop hiding the specified memory range. + pub fn remove_range(range: &MemoryRange) { + unsafe { gum_sys::gum_cloak_remove_range(&range.memory_range as *const _) }; + } + + /// Check whether any cloaked range contains the given address. + pub fn has_range_containing(address: NativePointer) -> bool { + unsafe { gum_sys::gum_cloak_has_range_containing(address.0 as gum_sys::GumAddress) != 0 } + } + + /// Clip the specified range against currently cloaked ranges. + /// + /// Returns the sub-ranges that remain visible after clipping. If the + /// entire range is hidden, the returned vector is empty. + pub fn clip_range(range: &MemoryRange) -> Vec { + let mut results = Vec::new(); + unsafe { + let array = gum_sys::gum_cloak_clip_range(&range.memory_range as *const _); + if array.is_null() { + return results; + } + + let len = (*array).len as usize; + let data = (*array).data as *const gum_sys::GumMemoryRange; + for i in 0..len { + let r = *data.add(i); + results.push(MemoryRange::new( + NativePointer(r.base_address as *mut c_void), + r.size as usize, + )); + } + crate::glib_compat::g_array_free(array, gum_sys::true_ as _); + } + results + } + + /// Iterate over all currently cloaked memory ranges. + /// + /// Returning `false` from the callback halts iteration. + pub fn enumerate_ranges(mut callback: F) + where + F: FnMut(&MemoryRange) -> bool, + { + unsafe extern "C" fn trampoline( + range: *const gum_sys::GumMemoryRange, + user_data: gum_sys::gpointer, + ) -> gum_sys::gboolean + where + F: FnMut(&MemoryRange) -> bool, + { + unsafe { + let cb = &mut *(user_data as *mut F); + let r = *range; + let memory_range = MemoryRange::new( + NativePointer(r.base_address as *mut c_void), + r.size as usize, + ); + if cb(&memory_range) { 1 } else { 0 } + } + } + + unsafe { + gum_sys::gum_cloak_enumerate_ranges( + Some(trampoline::), + &mut callback as *mut _ as *mut c_void, + ); + } + } + + /// Hide the specified file descriptor from enumeration. + pub fn add_file_descriptor(fd: i32) { + unsafe { gum_sys::gum_cloak_add_file_descriptor(fd) }; + } + + /// Stop hiding the specified file descriptor. + pub fn remove_file_descriptor(fd: i32) { + unsafe { gum_sys::gum_cloak_remove_file_descriptor(fd) }; + } + + /// Check whether the specified file descriptor is currently hidden. + pub fn has_file_descriptor(fd: i32) -> bool { + unsafe { gum_sys::gum_cloak_has_file_descriptor(fd) != 0 } + } + + /// Iterate over all currently cloaked file descriptors. + pub fn enumerate_file_descriptors(mut callback: F) + where + F: FnMut(i32) -> bool, + { + unsafe extern "C" fn trampoline( + fd: gum_sys::gint, + user_data: gum_sys::gpointer, + ) -> gum_sys::gboolean + where + F: FnMut(i32) -> bool, + { + unsafe { + let cb = &mut *(user_data as *mut F); + if cb(fd) { 1 } else { 0 } + } + } + + unsafe { + gum_sys::gum_cloak_enumerate_file_descriptors( + Some(trampoline::), + &mut callback as *mut _ as *mut c_void, + ); + } + } + + /// Run the callback while holding the cloak's internal lock. + /// + /// This guarantees a consistent view of cloaked resources for the duration + /// of the callback. + pub fn with_lock_held(callback: F) { + unsafe extern "C" fn trampoline(user_data: gum_sys::gpointer) { + unsafe { + let cb = Box::from_raw(user_data as *mut F); + cb(); + } + } + + let boxed = Box::new(callback); + unsafe { + gum_sys::gum_cloak_with_lock_held( + Some(trampoline::), + Box::into_raw(boxed) as *mut c_void, + ); + } + } + + /// Check whether the cloak's internal lock is currently held. + pub fn is_locked() -> bool { + unsafe { gum_sys::gum_cloak_is_locked() != 0 } + } +} diff --git a/frida-gum/src/code_allocator.rs b/frida-gum/src/code_allocator.rs new file mode 100644 index 00000000..cdb7b054 --- /dev/null +++ b/frida-gum/src/code_allocator.rs @@ -0,0 +1,264 @@ +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Memory allocator for executable code. +//! +//! The CodeAllocator provides efficient allocation of executable memory, +//! which is useful when generating code at runtime. + +use {crate::NativePointer, frida_gum_sys as gum_sys}; + +/// A slice of executable memory. +pub struct CodeSlice { + slice: *mut gum_sys::GumCodeSlice, +} + +impl CodeSlice { + /// Get the base address of this code slice. + pub fn base(&self) -> NativePointer { + NativePointer(unsafe { (*self.slice).data }) + } + + /// Get the size of this code slice. + pub fn size(&self) -> usize { + unsafe { (*self.slice).size as usize } + } + + /// Get a mutable pointer to the data for writing code. + /// + /// # Safety + /// + /// The caller must ensure they don't write beyond the size of the slice. + pub unsafe fn as_mut_ptr(&mut self) -> *mut u8 { + unsafe { (*self.slice).data as *mut u8 } + } + + /// Get a slice view of the code memory. + /// + /// # Safety + /// + /// The caller must ensure the memory contains valid data. + pub unsafe fn as_slice(&self) -> &[u8] { + unsafe { + core::slice::from_raw_parts( + (*self.slice).data as *const u8, + (*self.slice).size as usize, + ) + } + } + + /// Get a mutable slice view of the code memory. + /// + /// # Safety + /// + /// The caller must ensure proper synchronization if the code is being executed. + pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { + core::slice::from_raw_parts_mut( + (*self.slice).data as *mut u8, + (*self.slice).size as usize, + ) + } + } +} + +impl Clone for CodeSlice { + fn clone(&self) -> Self { + CodeSlice { + slice: unsafe { gum_sys::gum_code_slice_ref(self.slice) }, + } + } +} + +impl Drop for CodeSlice { + fn drop(&mut self) { + unsafe { gum_sys::gum_code_slice_unref(self.slice) }; + } +} + +unsafe impl Send for CodeSlice {} +unsafe impl Sync for CodeSlice {} + +/// A code deflector trampoline allocated by [`CodeAllocator::alloc_deflector`]. +pub struct CodeDeflector { + deflector: *mut gum_sys::GumCodeDeflector, +} + +impl CodeDeflector { + /// The return address this deflector redirects from. + pub fn return_address(&self) -> NativePointer { + NativePointer(unsafe { (*self.deflector).return_address }) + } + + /// The address execution is redirected to. + pub fn target(&self) -> NativePointer { + NativePointer(unsafe { (*self.deflector).target }) + } + + /// The trampoline entry point installed near the caller. + pub fn trampoline(&self) -> NativePointer { + NativePointer(unsafe { (*self.deflector).trampoline }) + } +} + +impl Clone for CodeDeflector { + fn clone(&self) -> Self { + CodeDeflector { + deflector: unsafe { gum_sys::gum_code_deflector_ref(self.deflector) }, + } + } +} + +impl Drop for CodeDeflector { + fn drop(&mut self) { + unsafe { gum_sys::gum_code_deflector_unref(self.deflector) }; + } +} + +unsafe impl Send for CodeDeflector {} +unsafe impl Sync for CodeDeflector {} + +/// Allocator for executable code memory. +/// +/// Provides efficient allocation of memory regions that can contain +/// executable code. This is useful when generating code at runtime. +pub struct CodeAllocator { + allocator: gum_sys::GumCodeAllocator, +} + +impl CodeAllocator { + /// Create a new CodeAllocator with the specified slice size. + /// + /// # Arguments + /// + /// * `slice_size` - Size of each allocated code slice + /// + /// # Examples + /// + /// ```no_run + /// use frida_gum::CodeAllocator; + /// + /// let mut allocator = CodeAllocator::new(1024); + /// let slice = allocator.alloc_slice(); + /// ``` + pub fn new(slice_size: usize) -> Self { + let mut allocator: gum_sys::GumCodeAllocator = unsafe { core::mem::zeroed() }; + unsafe { + gum_sys::gum_code_allocator_init( + &mut allocator as *mut _, + slice_size as gum_sys::gsize, + ); + } + CodeAllocator { allocator } + } + + /// Allocate a code slice. + /// + /// Returns a new code slice of the size specified during allocator creation. + pub fn alloc_slice(&mut self) -> Option { + let slice = unsafe { gum_sys::gum_code_allocator_alloc_slice(&mut self.allocator) }; + if slice.is_null() { + None + } else { + Some(CodeSlice { slice }) + } + } + + /// Try to allocate a code slice within `max_distance` bytes of `near`. + /// + /// # Arguments + /// + /// * `near` - Target address to allocate near. + /// * `max_distance` - Maximum distance in bytes (e.g. `i32::MAX as usize` + /// for x86 short branches). + /// * `alignment` - Alignment requirement (power of two). + pub fn try_alloc_slice_near( + &mut self, + near: NativePointer, + max_distance: usize, + alignment: usize, + ) -> Option { + let spec = gum_sys::GumAddressSpec { + near_address: near.0, + max_distance: max_distance as gum_sys::gsize, + }; + + let slice = unsafe { + gum_sys::gum_code_allocator_try_alloc_slice_near( + &mut self.allocator, + &spec, + alignment as gum_sys::gsize, + ) + }; + + if slice.is_null() { + None + } else { + Some(CodeSlice { slice }) + } + } + + /// Commit any pending allocations. + /// + /// This ensures all allocated slices are fully committed and ready for use. + pub fn commit(&mut self) { + unsafe { gum_sys::gum_code_allocator_commit(&mut self.allocator) }; + } + + /// Allocate a code deflector. + /// + /// A deflector installs a small trampoline near `caller` that redirects + /// execution arriving from `return_address` to `target`. Set `dedicated` + /// to reserve the deflector for a single caller. + /// + /// # Arguments + /// + /// * `caller` - Address to allocate the deflector near, with the maximum + /// distance permitted. + /// * `max_distance` - Maximum distance in bytes from `caller`. + /// * `return_address` - The return address that should be deflected. + /// * `target` - Where execution should be redirected to. + /// * `dedicated` - Whether the deflector is dedicated to a single caller. + pub fn alloc_deflector( + &mut self, + caller: NativePointer, + max_distance: usize, + return_address: NativePointer, + target: NativePointer, + dedicated: bool, + ) -> Option { + let spec = gum_sys::GumAddressSpec { + near_address: caller.0, + max_distance: max_distance as gum_sys::gsize, + }; + + let deflector = unsafe { + gum_sys::gum_code_allocator_alloc_deflector( + &mut self.allocator, + &spec, + return_address.0, + target.0, + i32::from(dedicated), + ) + }; + + if deflector.is_null() { + None + } else { + Some(CodeDeflector { deflector }) + } + } +} + +impl Drop for CodeAllocator { + fn drop(&mut self) { + unsafe { gum_sys::gum_code_allocator_free(&mut self.allocator) }; + } +} + +unsafe impl Send for CodeAllocator {} +unsafe impl Sync for CodeAllocator {} diff --git a/frida-gum/src/code_segment.rs b/frida-gum/src/code_segment.rs new file mode 100644 index 00000000..ca09c043 --- /dev/null +++ b/frida-gum/src/code_segment.rs @@ -0,0 +1,144 @@ +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Allocate executable memory in a way that honours the host's code-signing +//! policy. +//! +//! On platforms where pages cannot simply be mapped writable-and-executable +//! (Windows with CET, Apple with hardened runtime, etc.) Frida implements a +//! shadow-mapping scheme: the segment is allocated read-write, code is +//! written to it, then [`CodeSegment::realize`] makes it executable and +//! [`CodeSegment::map`] exposes it at a target address as read-execute. +//! +//! Use [`CodeSegment::is_supported`] to check availability before relying on +//! this API. + +use { + crate::{NativePointer, error::Error}, + core::ptr, + frida_gum_sys as gum_sys, +}; + +/// A region of executable memory managed by Frida. +pub struct CodeSegment { + inner: *mut gum_sys::GumCodeSegment, +} + +impl CodeSegment { + /// Returns whether the running platform supports `CodeSegment`. + pub fn is_supported() -> bool { + unsafe { gum_sys::gum_code_segment_is_supported() != 0 } + } + + /// Allocate a new segment of at least `size` bytes. + /// + /// Returns `None` if the host does not support code segments or the + /// allocation fails. + pub fn new(size: usize) -> Option { + let inner = unsafe { gum_sys::gum_code_segment_new(size as gum_sys::gsize, ptr::null()) }; + if inner.is_null() { + None + } else { + Some(CodeSegment { inner }) + } + } + + /// Allocate a segment whose pages will live within `max_distance` bytes + /// of `near`. Useful for short-jump trampolines. + pub fn new_near(size: usize, near: NativePointer, max_distance: usize) -> Option { + let spec = gum_sys::GumAddressSpec { + near_address: near.0, + max_distance: max_distance as gum_sys::gsize, + }; + let inner = unsafe { gum_sys::gum_code_segment_new(size as gum_sys::gsize, &spec) }; + if inner.is_null() { + None + } else { + Some(CodeSegment { inner }) + } + } + + /// Get the writable shadow address where code should be staged. + /// + /// Write your instructions here, then call [`Self::realize`] and + /// [`Self::map`] to publish them as executable. + pub fn address(&self) -> NativePointer { + NativePointer(unsafe { gum_sys::gum_code_segment_get_address(self.inner) }) + } + + /// Get the size of the underlying file mapping. + pub fn size(&self) -> usize { + unsafe { gum_sys::gum_code_segment_get_size(self.inner) as usize } + } + + /// Get the size of the address space reserved for the segment. + pub fn virtual_size(&self) -> usize { + unsafe { gum_sys::gum_code_segment_get_virtual_size(self.inner) as usize } + } + + /// Finalize the segment so it becomes executable. + pub fn realize(&self) { + unsafe { gum_sys::gum_code_segment_realize(self.inner) }; + } + + /// Publish a portion of the segment at `target_address` as read-execute. + /// + /// # Safety + /// + /// `target_address` must point to a valid region of at least `source_size` + /// bytes that the caller owns. This typically pairs with memory previously + /// reserved by [`crate::Memory::allocate`] or similar. + pub unsafe fn map( + &self, + source_offset: usize, + source_size: usize, + target_address: NativePointer, + ) { + unsafe { + gum_sys::gum_code_segment_map( + self.inner, + source_offset as gum_sys::gsize, + source_size as gum_sys::gsize, + target_address.0, + ); + } + } + + /// Mark `code` of `size` bytes as executable. + /// + /// Convenience wrapper around `gum_code_segment_mark` for cases where + /// you have already allocated code memory by other means and just need + /// the OS-level "make this executable" call. + /// + /// # Safety + /// + /// `code` must point to a region of at least `size` bytes that the + /// caller owns and that contains valid instructions for the host CPU. + pub unsafe fn mark(code: NativePointer, size: usize) -> Result<(), Error> { + unsafe { + let mut err: *mut gum_sys::GError = ptr::null_mut(); + let ok = gum_sys::gum_code_segment_mark(code.0, size as gum_sys::gsize, &mut err) != 0; + if !err.is_null() { + crate::glib_compat::g_error_free(err); + } + if ok { + Ok(()) + } else { + Err(Error::MemoryAccessError) + } + } + } +} + +impl Drop for CodeSegment { + fn drop(&mut self) { + unsafe { gum_sys::gum_code_segment_free(self.inner) }; + } +} + +unsafe impl Send for CodeSegment {} +unsafe impl Sync for CodeSegment {} diff --git a/frida-gum/src/control_flow_graph.rs b/frida-gum/src/control_flow_graph.rs new file mode 100644 index 00000000..06a7395c --- /dev/null +++ b/frida-gum/src/control_flow_graph.rs @@ -0,0 +1,242 @@ +/* + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Native control-flow graph construction and dominator analysis. +//! +//! A [`ControlFlowGraph`] models the basic blocks of a function and the edges +//! between them, computed directly from machine code. It can be built for a +//! function whose entry point is known ([`ControlFlowGraph::for_function`]), +//! and then queried for block bounds, successors/predecessors, and dominance +//! relationships (using the Cooper-Harvey-Kennedy dominator algorithm). +//! +//! This is particularly useful for deciding *where* it is safe to install a +//! redirect: [`ControlFlowGraph::enumerate_dominating_sites`] reports the +//! instruction-aligned addresses that dominate a target along with how many +//! contiguous bytes may be overwritten there without another control-flow edge +//! landing inside the redirect. + +use {crate::NativePointer, core::ffi::c_void, frida_gum_sys as gum_sys}; + +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +/// A site that dominates a target address, reported by +/// [`ControlFlowGraph::enumerate_dominating_sites`]. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct DominatingSite { + /// Instruction-aligned address that dominates the target. + pub site: NativePointer, + /// Number of contiguous bytes at `site` that may be overwritten by a + /// redirect without another control-flow edge landing inside them. + pub capacity: usize, +} + +/// The start and end (exclusive) addresses of a basic block. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct BlockBounds { + /// First address of the block. + pub start: u64, + /// One past the last address of the block. + pub end: u64, +} + +/// A control-flow graph computed from machine code. +pub struct ControlFlowGraph { + inner: *mut gum_sys::GumControlFlowGraph, +} + +impl ControlFlowGraph { + /// Build a control-flow graph starting at `entry`, decoding instructions + /// with the given capstone architecture and mode. + /// + /// `find_range` is called to resolve the contiguous code range covering an + /// address — for the entry, and for every direct branch target that falls + /// outside the ranges discovered so far. It receives the address to locate + /// and must return `Some(MemoryRange)` for the covering range, or `None` + /// if the address is not part of any known code range. + /// + /// Prefer [`ControlFlowGraph::for_function`] unless you need to supply a + /// custom range resolver (e.g. for code that is not described by the + /// platform's unwind information). + /// + /// # Safety + /// + /// `entry` must point to a valid instruction decodable with `arch`/`mode`, + /// and `find_range` must return ranges that accurately describe readable, + /// executable memory. + pub unsafe fn new( + entry: NativePointer, + arch: gum_sys::cs_arch, + mode: gum_sys::cs_mode, + mut find_range: F, + ) -> Self + where + F: FnMut(NativePointer) -> Option, + { + unsafe extern "C" fn trampoline( + address: gum_sys::gconstpointer, + range: *mut gum_sys::GumMemoryRange, + user_data: gum_sys::gpointer, + ) -> gum_sys::gboolean + where + F: FnMut(NativePointer) -> Option, + { + let find_range = unsafe { &mut *(user_data as *mut F) }; + match find_range(NativePointer(address as *mut c_void)) { + Some(found) => { + unsafe { *range = found.memory_range }; + i32::from(true) + } + None => i32::from(false), + } + } + + Self { + inner: unsafe { + gum_sys::gum_control_flow_graph_new( + entry.0, + arch, + mode, + Some(trampoline::), + &mut find_range as *mut _ as *mut c_void, + ) + }, + } + } + + /// Build a control-flow graph for the function starting at `entry_point`. + /// + /// Ranges are resolved via the platform's unwind information, so this works + /// even on stripped binaries with no symbol table. + /// + /// # Safety + /// + /// `entry_point` must point to the first instruction of a function in a + /// readable, executable memory region. + pub unsafe fn for_function(entry_point: NativePointer) -> Self { + Self { + inner: unsafe { gum_sys::gum_control_flow_graph_new_for_function(entry_point.0) }, + } + } + + /// Whether block dominance holds between two addresses: returns `true` if + /// every path from the entry to `b` passes through `a`. + pub fn dominates(&self, a: NativePointer, b: NativePointer) -> bool { + unsafe { gum_sys::gum_control_flow_graph_dominates(self.inner, a.0, b.0) != 0 } + } + + /// Enumerate the instruction-aligned sites that dominate `target`. + /// + /// The closure is invoked once per [`DominatingSite`]; return `true` to + /// continue enumeration or `false` to stop early. + pub fn enumerate_dominating_sites(&self, target: NativePointer, mut callback: F) + where + F: FnMut(DominatingSite) -> bool, + { + unsafe extern "C" fn trampoline( + site: gum_sys::gconstpointer, + capacity: gum_sys::gsize, + user_data: gum_sys::gpointer, + ) -> gum_sys::gboolean + where + F: FnMut(DominatingSite) -> bool, + { + let callback = unsafe { &mut *(user_data as *mut F) }; + let cont = callback(DominatingSite { + site: NativePointer(site as *mut c_void), + capacity: capacity as usize, + }); + i32::from(cont) + } + + unsafe { + gum_sys::gum_control_flow_graph_enumerate_dominating_sites( + self.inner, + target.0, + Some(trampoline::), + &mut callback as *mut _ as *mut c_void, + ); + } + } + + /// The number of basic blocks in the graph. + pub fn num_blocks(&self) -> u32 { + unsafe { gum_sys::gum_control_flow_graph_get_num_blocks(self.inner) } + } + + /// The index of the entry block. + pub fn entry_block(&self) -> u32 { + unsafe { gum_sys::gum_control_flow_graph_get_entry_block(self.inner) } + } + + /// Find the index of the block containing `address`. + pub fn find_block_containing(&self, address: NativePointer) -> u32 { + unsafe { gum_sys::gum_control_flow_graph_find_block_containing(self.inner, address.0) } + } + + /// Get the start/end bounds of the block at `index`. + pub fn block_bounds(&self, index: u32) -> BlockBounds { + let mut start: gum_sys::GumAddress = 0; + let mut end: gum_sys::GumAddress = 0; + unsafe { + gum_sys::gum_control_flow_graph_get_block_bounds( + self.inner, index, &mut start, &mut end, + ); + } + BlockBounds { start, end } + } + + /// Get the index of the immediate dominator of the block at `index`. + pub fn block_immediate_dominator(&self, index: u32) -> u32 { + unsafe { gum_sys::gum_control_flow_graph_get_block_immediate_dominator(self.inner, index) } + } + + /// Get the successor block indices of the block at `index`. + pub fn block_successors(&self, index: u32) -> Vec { + let mut ptr: *const gum_sys::guint = core::ptr::null(); + let len = unsafe { + gum_sys::gum_control_flow_graph_get_block_successors(self.inner, index, &mut ptr) + }; + unsafe { slice_to_vec(ptr, len) } + } + + /// Get the predecessor block indices of the block at `index`. + pub fn block_predecessors(&self, index: u32) -> Vec { + let mut ptr: *const gum_sys::guint = core::ptr::null(); + let len = unsafe { + gum_sys::gum_control_flow_graph_get_block_predecessors(self.inner, index, &mut ptr) + }; + unsafe { slice_to_vec(ptr, len) } + } + + /// Find the capstone instruction containing `address`, if any. + /// + /// The returned pointer is owned by the graph and is valid only for as long + /// as this [`ControlFlowGraph`] is alive. + pub fn find_instruction_containing( + &self, + address: NativePointer, + ) -> Option<*const gum_sys::cs_insn> { + let insn = unsafe { + gum_sys::gum_control_flow_graph_find_instruction_containing(self.inner, address.0) + }; + if insn.is_null() { None } else { Some(insn) } + } +} + +/// Copy a C array of `guint` (owned by the graph) into an owned `Vec`. +unsafe fn slice_to_vec(ptr: *const gum_sys::guint, len: gum_sys::guint) -> Vec { + if ptr.is_null() || len == 0 { + return Vec::new(); + } + unsafe { core::slice::from_raw_parts(ptr, len as usize).to_vec() } +} + +impl Drop for ControlFlowGraph { + fn drop(&mut self) { + unsafe { gum_sys::gum_control_flow_graph_free(self.inner) }; + } +} diff --git a/frida-gum/src/darwin_module.rs b/frida-gum/src/darwin_module.rs new file mode 100644 index 00000000..a7982c95 --- /dev/null +++ b/frida-gum/src/darwin_module.rs @@ -0,0 +1,369 @@ +/* + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Darwin/Mach-O module inspection — enumerate sections, symbols, exports, imports, binds, rebases. +//! +//! Added in Frida 17.16.0. Only available on macOS (Darwin module API not available on iOS/tvOS/watchOS). + +#![cfg(target_os = "macos")] + +use {core::ffi::c_void, cstr_core::CString, frida_gum_sys as gum_sys, frida_gum_sys::gpointer}; + +#[cfg(not(feature = "std"))] +use alloc::{boxed::Box, string::String, vec::Vec}; + +#[cfg(feature = "std")] +use std::ffi::CStr; + +#[cfg(not(feature = "std"))] +use core::ffi::CStr; + +/// Darwin module segment details. +#[derive(Clone, Debug)] +pub struct DarwinSegment { + pub name: String, + pub vm_address: u64, + pub vm_size: u64, + pub file_offset: u64, + pub file_size: u64, + pub protection: u32, +} + +impl DarwinSegment { + unsafe fn from_raw(details: *const gum_sys::GumDarwinSegment) -> Self { + unsafe { + Self { + name: CStr::from_ptr((*details).name.as_ptr()) + .to_string_lossy() + .into_owned(), + vm_address: (*details).vm_address, + vm_size: (*details).vm_size, + file_offset: (*details).file_offset, + file_size: (*details).file_size, + protection: (*details).protection as u32, + } + } + } +} + +/// Darwin section details. +#[derive(Clone, Debug)] +pub struct DarwinSectionDetails { + pub segment_name: String, + pub section_name: String, + pub vm_address: u64, + pub size: u64, +} + +impl DarwinSectionDetails { + unsafe fn from_raw(details: *const gum_sys::GumDarwinSectionDetails) -> Self { + unsafe { + Self { + segment_name: CStr::from_ptr((*details).segment_name.as_ptr()) + .to_string_lossy() + .into_owned(), + section_name: CStr::from_ptr((*details).section_name.as_ptr()) + .to_string_lossy() + .into_owned(), + vm_address: (*details).vm_address, + size: (*details).size, + } + } + } +} + +/// Darwin symbol details. +#[derive(Clone, Debug)] +pub struct DarwinSymbolDetails { + pub name: String, + pub address: u64, +} + +impl DarwinSymbolDetails { + unsafe fn from_raw(details: *const gum_sys::GumDarwinSymbolDetails) -> Self { + unsafe { + Self { + name: CStr::from_ptr((*details).name) + .to_string_lossy() + .into_owned(), + address: (*details).address, + } + } + } +} + +/// Darwin export details. +#[derive(Clone, Debug)] +pub struct DarwinExportDetails { + pub name: String, + pub flags: u64, + pub offset: u64, +} + +impl DarwinExportDetails { + unsafe fn from_raw(details: *const gum_sys::GumDarwinExportDetails) -> Self { + unsafe { + Self { + name: CStr::from_ptr((*details).name) + .to_string_lossy() + .into_owned(), + flags: (*details).flags, + offset: (*details).__bindgen_anon_1.__bindgen_anon_1.offset, + } + } + } +} + +/// Darwin bind details. +#[derive(Clone, Debug)] +pub struct DarwinBindDetails { + pub segment_index: u16, + pub offset: u64, + pub type_: u8, + pub library_ordinal: i16, + pub symbol_name: String, + pub symbol_flags: i8, + pub addend: i64, +} + +impl DarwinBindDetails { + unsafe fn from_raw(details: *const gum_sys::GumDarwinBindDetails) -> Self { + unsafe { + Self { + segment_index: (*details).segment as u16, + offset: (*details).offset, + type_: (*details).type_, + library_ordinal: (*details).library_ordinal as i16, + symbol_name: CStr::from_ptr((*details).symbol_name) + .to_string_lossy() + .into_owned(), + symbol_flags: (*details).symbol_flags as i8, + addend: (*details).addend, + } + } + } +} + +extern "C" fn enumerate_sections_callout( + details: *const gum_sys::GumDarwinSectionDetails, + user_data: *mut c_void, +) -> gum_sys::gboolean { + let mut f = + unsafe { Box::from_raw(user_data as *mut Box bool>) }; + let r = unsafe { f(DarwinSectionDetails::from_raw(details)) }; + Box::leak(f); + r as gum_sys::gboolean +} + +extern "C" fn enumerate_symbols_callout( + details: *const gum_sys::GumDarwinSymbolDetails, + user_data: *mut c_void, +) -> gum_sys::gboolean { + let mut f = + unsafe { Box::from_raw(user_data as *mut Box bool>) }; + let r = unsafe { f(DarwinSymbolDetails::from_raw(details)) }; + Box::leak(f); + r as gum_sys::gboolean +} + +extern "C" fn enumerate_exports_callout( + details: *const gum_sys::GumDarwinExportDetails, + user_data: *mut c_void, +) -> gum_sys::gboolean { + let mut f = + unsafe { Box::from_raw(user_data as *mut Box bool>) }; + let r = unsafe { f(DarwinExportDetails::from_raw(details)) }; + Box::leak(f); + r as gum_sys::gboolean +} + +extern "C" fn enumerate_binds_callout( + details: *const gum_sys::GumDarwinBindDetails, + user_data: *mut c_void, +) -> gum_sys::gboolean { + let mut f = + unsafe { Box::from_raw(user_data as *mut Box bool>) }; + let r = unsafe { f(DarwinBindDetails::from_raw(details)) }; + Box::leak(f); + r as gum_sys::gboolean +} + +/// A Darwin (Mach-O) module loaded by Frida. +pub struct DarwinModule { + inner: *mut gum_sys::GumDarwinModule, +} + +impl DarwinModule { + /// Open a Mach-O file from disk. + /// + /// Returns `None` if the file cannot be opened or parsed. + pub fn from_file(path: &str) -> Option { + let path = CString::new(path).ok()?; + let mut error: *mut gum_sys::GError = core::ptr::null_mut(); + let ptr = unsafe { + gum_sys::gum_darwin_module_new_from_file( + path.as_ptr(), + gum_sys::GumCpuType_GUM_CPU_INVALID, + gum_sys::_GumPtrauthSupport_GUM_PTRAUTH_INVALID, + 0, + &mut error, + ) + }; + if ptr.is_null() { + None + } else { + Some(Self { inner: ptr }) + } + } + + /// Open a Mach-O module already mapped into memory at `base_address`. + pub fn from_memory(name: &str, base_address: u64) -> Option { + let name = CString::new(name).ok()?; + let mut error: *mut gum_sys::GError = core::ptr::null_mut(); + let ptr = unsafe { + gum_sys::gum_darwin_module_new_from_memory( + name.as_ptr(), + gum_sys::GumCpuType_GUM_CPU_INVALID, + base_address, + gum_sys::_GumPtrauthSupport_GUM_PTRAUTH_INVALID, + &mut error, + ) + }; + if ptr.is_null() { + None + } else { + Some(Self { inner: ptr }) + } + } + + /// Enumerate segments in this module. + pub fn segments(&self) -> Vec { + let mut result = Vec::new(); + unsafe { + let segments_array = (*self.inner).segments; + let n_segments = (*segments_array).len; + let segments = (*segments_array).data as *const gum_sys::GumDarwinSegment; + for i in 0..n_segments { + result.push(DarwinSegment::from_raw(segments.add(i as usize))); + } + } + result + } + + /// Enumerate sections in this module. + pub fn enumerate_sections(&self, mut callback: F) + where + F: FnMut(DarwinSectionDetails) -> bool, + { + let callback: Box bool> = Box::new(&mut callback); + let callback = Box::into_raw(Box::new(callback)); + unsafe { + gum_sys::gum_darwin_module_enumerate_sections( + self.inner, + Some(enumerate_sections_callout), + callback as gpointer, + ); + drop(Box::from_raw(callback)); + } + } + + /// Enumerate symbols in this module. + pub fn enumerate_symbols(&self, mut callback: F) + where + F: FnMut(DarwinSymbolDetails) -> bool, + { + let callback: Box bool> = Box::new(&mut callback); + let callback = Box::into_raw(Box::new(callback)); + unsafe { + gum_sys::gum_darwin_module_enumerate_symbols( + self.inner, + Some(enumerate_symbols_callout), + callback as gpointer, + ); + drop(Box::from_raw(callback)); + } + } + + /// Enumerate exports from this module. + pub fn enumerate_exports(&self, mut callback: F) + where + F: FnMut(DarwinExportDetails) -> bool, + { + let callback: Box bool> = Box::new(&mut callback); + let callback = Box::into_raw(Box::new(callback)); + unsafe { + gum_sys::gum_darwin_module_enumerate_exports( + self.inner, + Some(enumerate_exports_callout), + callback as gpointer, + ); + drop(Box::from_raw(callback)); + } + } + + /// Enumerate bind entries (external symbol references). + pub fn enumerate_binds(&self, mut callback: F) + where + F: FnMut(DarwinBindDetails) -> bool, + { + let callback: Box bool> = Box::new(&mut callback); + let callback = Box::into_raw(Box::new(callback)); + unsafe { + gum_sys::gum_darwin_module_enumerate_binds( + self.inner, + Some(enumerate_binds_callout), + callback as gpointer, + ); + drop(Box::from_raw(callback)); + } + } + + /// Collect all sections into a Vec. + pub fn sections(&self) -> Vec { + let mut result = Vec::new(); + self.enumerate_sections(|section| { + result.push(section); + true + }); + result + } + + /// Collect all symbols into a Vec. + pub fn symbols(&self) -> Vec { + let mut result = Vec::new(); + self.enumerate_symbols(|symbol| { + result.push(symbol); + true + }); + result + } + + /// Collect all exports into a Vec. + pub fn exports(&self) -> Vec { + let mut result = Vec::new(); + self.enumerate_exports(|export| { + result.push(export); + true + }); + result + } + + /// Collect all binds into a Vec. + pub fn binds(&self) -> Vec { + let mut result = Vec::new(); + self.enumerate_binds(|bind| { + result.push(bind); + true + }); + result + } +} + +impl Drop for DarwinModule { + fn drop(&mut self) { + unsafe { gum_sys::g_object_unref(self.inner as *mut c_void) }; + } +} diff --git a/frida-gum/src/debug_symbol.rs b/frida-gum/src/debug_symbol.rs index 79b55460..2b4779c9 100644 --- a/frida-gum/src/debug_symbol.rs +++ b/frida-gum/src/debug_symbol.rs @@ -3,7 +3,10 @@ use { core::{fmt, mem::MaybeUninit, str::Utf8Error}, cstr_core::{CStr, CString}, frida_gum_sys as gum_sys, - gum_sys::{GumDebugSymbolDetails, gum_find_function, gum_symbol_details_from_address}, + gum_sys::{ + GumDebugSymbolDetails, GumSymbolType, gum_find_function, gum_symbol_details_from_address, + gum_symbol_type_to_string, + }, }; pub struct Symbol { @@ -49,6 +52,28 @@ impl Symbol { pub fn line_number(&self) -> u32 { self.gum_debug_symbol_details.line_number } + + /// Column number in file_name (0 when unavailable). + pub fn column(&self) -> u32 { + self.gum_debug_symbol_details.column + } +} + +/// Get a human-readable name for a [`GumSymbolType`]. +/// +/// Wraps `gum_symbol_type_to_string`. Returns `None` if the type is not +/// recognised by Gum. +pub fn symbol_type_to_string(symbol_type: GumSymbolType) -> Option<&'static str> { + unsafe { + let ptr = gum_symbol_type_to_string(symbol_type); + if ptr.is_null() { + None + } else { + // The returned pointer points to a static string in Gum, safe to + // expose with a 'static lifetime. + CStr::from_ptr(ptr.cast()).to_str().ok() + } + } } pub struct DebugSymbol {} @@ -75,7 +100,7 @@ impl DebugSymbol { pub fn find_function>(name: S) -> Option { match CString::new(name.as_ref()) { Ok(name) => { - let address = unsafe { gum_find_function(name.into_raw().cast()) }; + let address = unsafe { gum_find_function(name.as_ptr().cast()) }; if address.is_null() { None } else { @@ -92,4 +117,15 @@ impl DebugSymbol { None => None, } } + + /// Load debug symbols from the module (or symbol file) at `path`. + /// + /// Useful when symbols live in a separate file (e.g. a stripped binary with + /// a companion `.debug`/`.pdb`). Returns `true` if symbols were loaded. + pub fn load_symbols>(path: S) -> bool { + match CString::new(path.as_ref()) { + Ok(path) => unsafe { gum_sys::gum_load_symbols(path.as_ptr().cast()) != 0 }, + Err(_) => false, + } + } } diff --git a/frida-gum/src/elf_module.rs b/frida-gum/src/elf_module.rs new file mode 100644 index 00000000..ca23c068 --- /dev/null +++ b/frida-gum/src/elf_module.rs @@ -0,0 +1,203 @@ +/* + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! ELF module inspection — enumerate dynamic entries and query ELF metadata. +//! +//! Added in Frida 17.15.0. Only available on platforms that load ELF binaries +//! (Linux, Android, FreeBSD). Guarded by `#[cfg(not(target_os = "windows"))]` +//! and `#[cfg(not(target_os = "macos"))]`. + +#![cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))] + +use { + core::ffi::c_void, + cstr_core::CString, + frida_gum_sys as gum_sys, + frida_gum_sys::{GumElfDynamicEntryDetails, gpointer}, +}; + +#[cfg(not(feature = "std"))] +use alloc::{boxed::Box, string::String, vec::Vec}; + +#[cfg(feature = "std")] +use std::ffi::CStr; + +#[cfg(not(feature = "std"))] +use core::ffi::CStr; + +/// ELF dynamic section entry — mirrors `GumElfDynamicEntryDetails`. +#[derive(Clone, Debug)] +pub struct ElfDynamicEntry { + /// The dynamic tag (e.g. `DT_NEEDED`, `DT_SONAME`, etc.) + pub tag: u32, + /// The value associated with the tag (address, offset, or integer). + pub val: u64, +} + +impl ElfDynamicEntry { + fn from_raw(details: *const GumElfDynamicEntryDetails) -> Self { + unsafe { + Self { + tag: (*details).tag, + val: (*details).val, + } + } + } +} + +extern "C" fn enumerate_dynamic_entries_callout( + details: *const GumElfDynamicEntryDetails, + user_data: *mut c_void, +) -> gum_sys::gboolean { + let mut f = unsafe { Box::from_raw(user_data as *mut Box bool>) }; + let r = f(ElfDynamicEntry::from_raw(details)); + Box::leak(f); + r as gum_sys::gboolean +} + +/// An ELF module loaded by Frida. +pub struct ElfModule { + inner: *mut gum_sys::GumElfModule, +} + +impl ElfModule { + /// Open an ELF file from disk. + /// + /// Returns `None` if the file cannot be opened or parsed. + pub fn from_file(path: &str) -> Option { + let path = CString::new(path).ok()?; + let mut error: *mut gum_sys::GError = core::ptr::null_mut(); + let ptr = unsafe { gum_sys::gum_elf_module_new_from_file(path.as_ptr(), &mut error) }; + if ptr.is_null() { + None + } else { + Some(Self { inner: ptr }) + } + } + + /// Open an ELF module already mapped into memory at `base_address`. + pub fn from_memory(path: &str, base_address: u64) -> Option { + let path = CString::new(path).ok()?; + let mut error: *mut gum_sys::GError = core::ptr::null_mut(); + let ptr = unsafe { + gum_sys::gum_elf_module_new_from_memory(path.as_ptr(), base_address, &mut error) + }; + if ptr.is_null() { + None + } else { + Some(Self { inner: ptr }) + } + } + + /// Pointer size of this ELF binary (4 for 32-bit, 8 for 64-bit). + pub fn pointer_size(&self) -> u32 { + unsafe { gum_sys::gum_elf_module_get_pointer_size(self.inner) } + } + + /// Byte order — 0 = little-endian, 1 = big-endian (matches `GLib.ByteOrder`). + pub fn byte_order(&self) -> i32 { + unsafe { gum_sys::gum_elf_module_get_byte_order(self.inner) } + } + + /// OS ABI version field from the ELF header. + pub fn os_abi_version(&self) -> u8 { + unsafe { gum_sys::gum_elf_module_get_os_abi_version(self.inner) } + } + + /// Total mapped memory size of this module. + pub fn mapped_size(&self) -> u64 { + unsafe { gum_sys::gum_elf_module_get_mapped_size(self.inner) } + } + + /// Base (load) address of this module. + pub fn base_address(&self) -> u64 { + unsafe { gum_sys::gum_elf_module_get_base_address(self.inner) } + } + + /// Preferred (link-time) address of this module. + pub fn preferred_address(&self) -> u64 { + unsafe { gum_sys::gum_elf_module_get_preferred_address(self.inner) } + } + + /// Entry point address, if any. + pub fn entrypoint(&self) -> u64 { + unsafe { gum_sys::gum_elf_module_get_entrypoint(self.inner) } + } + + /// Path to the dynamic linker interpreter (`PT_INTERP`), if any. + pub fn interpreter(&self) -> Option { + let ptr = unsafe { gum_sys::gum_elf_module_get_interpreter(self.inner) }; + if ptr.is_null() { + None + } else { + Some( + unsafe { CStr::from_ptr(ptr) } + .to_string_lossy() + .into_owned(), + ) + } + } + + /// File path this module was loaded from. + pub fn source_path(&self) -> Option { + let ptr = unsafe { gum_sys::gum_elf_module_get_source_path(self.inner) }; + if ptr.is_null() { + None + } else { + Some( + unsafe { CStr::from_ptr(ptr) } + .to_string_lossy() + .into_owned(), + ) + } + } + + /// Enumerate the ELF dynamic section entries, calling `callback` for each. + /// + /// Return `true` from the callback to continue, `false` to stop early. + /// + /// # Example + /// ```rust,no_run + /// use frida_gum::elf_module::ElfModule; + /// if let Some(m) = ElfModule::from_file("/lib/x86_64-linux-gnu/libc.so.6") { + /// m.enumerate_dynamic_entries(|entry| { + /// println!("tag={} val={:#x}", entry.tag, entry.val); + /// true + /// }); + /// } + /// ``` + pub fn enumerate_dynamic_entries(&self, mut callback: F) + where + F: FnMut(ElfDynamicEntry) -> bool, + { + let callback: Box bool> = Box::new(&mut callback); + let callback = Box::into_raw(Box::new(callback)); + unsafe { + gum_sys::gum_elf_module_enumerate_dynamic_entries( + self.inner, + Some(enumerate_dynamic_entries_callout), + callback as gpointer, + ); + drop(Box::from_raw(callback)); + } + } + + /// Collect all dynamic entries into a `Vec`. + pub fn dynamic_entries(&self) -> Vec { + let mut result = Vec::new(); + self.enumerate_dynamic_entries(|entry| { + result.push(entry); + true + }); + result + } +} + +impl Drop for ElfModule { + fn drop(&mut self) { + unsafe { gum_sys::g_object_unref(self.inner as *mut c_void) }; + } +} diff --git a/frida-gum/src/exceptor.rs b/frida-gum/src/exceptor.rs new file mode 100644 index 00000000..899acf78 --- /dev/null +++ b/frida-gum/src/exceptor.rs @@ -0,0 +1,238 @@ +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Exception handling and signal interception. +//! +//! The Exceptor allows you to intercept and handle exceptions/signals +//! before they reach the application's normal exception handlers. + +use { + crate::Gum, + core::ffi::{CStr, c_char, c_void}, + frida_gum_sys as gum_sys, +}; + +#[cfg(not(feature = "std"))] +use alloc::{boxed::Box, string::String}; + +#[cfg(feature = "std")] +use std::{boxed::Box, string::String}; + +/// Operating mode for the global exceptor. +#[repr(i32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ExceptorMode { + /// Full handling: signals are caught and dispatched to registered handlers, + /// and Gum's `try_catch` mechanism is also functional. + Full = gum_sys::GumExceptorMode_GUM_EXCEPTOR_MODE_FULL as _, + /// Only registered handlers receive signals; `try_catch` is disabled. + HandlerOnly = gum_sys::GumExceptorMode_GUM_EXCEPTOR_MODE_HANDLER_ONLY as _, + /// The exceptor is fully disabled. + Off = gum_sys::GumExceptorMode_GUM_EXCEPTOR_MODE_OFF as _, +} + +/// Types of exceptions that can be caught. +#[repr(i32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ExceptionType { + /// Abort/terminate signal + Abort = gum_sys::_GumExceptionType_GUM_EXCEPTION_ABORT as _, + /// Access violation / segmentation fault + AccessViolation = gum_sys::_GumExceptionType_GUM_EXCEPTION_ACCESS_VIOLATION as _, + /// Guard page violation + GuardPage = gum_sys::_GumExceptionType_GUM_EXCEPTION_GUARD_PAGE as _, + /// Illegal instruction + IllegalInstruction = gum_sys::_GumExceptionType_GUM_EXCEPTION_ILLEGAL_INSTRUCTION as _, + /// Stack overflow + StackOverflow = gum_sys::_GumExceptionType_GUM_EXCEPTION_STACK_OVERFLOW as _, + /// Arithmetic exception + Arithmetic = gum_sys::_GumExceptionType_GUM_EXCEPTION_ARITHMETIC as _, + /// Breakpoint + Breakpoint = gum_sys::_GumExceptionType_GUM_EXCEPTION_BREAKPOINT as _, + /// Single step + SingleStep = gum_sys::_GumExceptionType_GUM_EXCEPTION_SINGLE_STEP as _, + /// System exception + System = gum_sys::_GumExceptionType_GUM_EXCEPTION_SYSTEM as _, +} + +/// Exception handler interface. +/// +/// Handlers registered with [`Exceptor::add`] are **not** removed automatically +/// when the `Exceptor` is dropped: the underlying exceptor is a process-wide +/// singleton, so dropping this handle does not tear down the C-side registration. +/// Each handler must be explicitly removed with [`Exceptor::remove`] before the +/// data captured by its closure goes out of scope; otherwise the closure leaks +/// and a subsequent exception could invoke a callback over freed state. +pub struct Exceptor { + exceptor: *mut gum_sys::GumExceptor, + _gum: Gum, +} + +impl Exceptor { + /// Obtain the global Exceptor instance. + pub fn obtain(gum: &Gum) -> Exceptor { + Exceptor { + exceptor: unsafe { gum_sys::gum_exceptor_obtain() }, + _gum: gum.clone(), + } + } + + /// Add an exception handler. + /// + /// The handler will be called when an exception occurs. Return `true` from + /// the handler to indicate that the exception was handled and execution + /// should continue, or `false` to pass it to the next handler. + /// + /// # Arguments + /// + /// * `callback` - Function to call when an exception occurs + /// + /// # Returns + /// + /// A handle that can be used to remove the exception handler. + /// + /// # Examples + /// + /// ```no_run + /// use frida_gum::{Gum, Exceptor}; + /// + /// let gum = unsafe { Gum::obtain() }; + /// let mut exceptor = Exceptor::obtain(&gum); + /// + /// let handle = exceptor.add(|details| { + /// // Handle exception + /// println!("Exception caught!"); + /// false // Don't handle, pass to next handler + /// }); + /// + /// // Later, remove the handler + /// exceptor.remove(handle); + /// ``` + pub fn add(&mut self, callback: F) -> ExceptionHandlerHandle + where + F: FnMut(*mut gum_sys::GumExceptionDetails) -> bool + Send + 'static, + { + unsafe extern "C" fn trampoline( + details: *mut gum_sys::GumExceptionDetails, + user_data: gum_sys::gpointer, + ) -> gum_sys::gboolean + where + F: FnMut(*mut gum_sys::GumExceptionDetails) -> bool, + { + unsafe { + let callback = &mut *(user_data as *mut F); + if callback(details) { 1 } else { 0 } + } + } + + let callback = Box::new(callback); + let user_data = Box::into_raw(callback) as *mut _; + + unsafe extern "C" fn cleanup(user_data: *mut c_void) + where + F: FnMut(*mut gum_sys::GumExceptionDetails) -> bool, + { + unsafe { + // Reconstruct and drop the Box with the correct type + let _ = Box::from_raw(user_data as *mut F); + } + } + + unsafe { + gum_sys::gum_exceptor_add(self.exceptor, Some(trampoline::), user_data); + } + + ExceptionHandlerHandle { + func: Some(trampoline::), + user_data, + cleanup: cleanup::, + } + } + + /// Remove a previously added exception handler. + /// + /// # Arguments + /// + /// * `handle` - The handle returned by [`Exceptor::add()`] + pub fn remove(&mut self, handle: ExceptionHandlerHandle) { + unsafe { + gum_sys::gum_exceptor_remove(self.exceptor, handle.func, handle.user_data); + // Clean up the callback using the stored cleanup function + (handle.cleanup)(handle.user_data); + } + } + + /// Reset the exceptor's internal state. + /// + /// Restores Gum's signal-handling primitives. Useful after the host has + /// installed its own signal handlers and you want Frida to take control + /// back. + pub fn reset(&mut self) { + unsafe { gum_sys::gum_exceptor_reset(self.exceptor) }; + } + + /// Check whether the specified thread currently has an active + /// `try_catch` scope (corresponding to `gum_exceptor_try` in the C API). + pub fn has_scope(&self, thread_id: usize) -> bool { + unsafe { + gum_sys::gum_exceptor_has_scope(self.exceptor, thread_id as gum_sys::GumThreadId) != 0 + } + } + + /// Set the global exceptor mode. + /// + /// This affects every Exceptor in the process; pass [`ExceptorMode::Off`] + /// to fully disable Frida's signal handling. + pub fn set_mode(mode: ExceptorMode) { + unsafe { gum_sys::gum_exceptor_set_mode(mode as gum_sys::GumExceptorMode) }; + } + + /// Format the given exception details into a human-readable string. + /// + /// `details` is the pointer handed to an exception handler registered with + /// [`Exceptor::add`]. + /// + /// # Safety + /// + /// `details` must be a valid `GumExceptionDetails` pointer (e.g. the one + /// received by an exception handler). + pub unsafe fn exception_details_to_string( + details: *const gum_sys::GumExceptionDetails, + ) -> String { + unsafe { + let raw = gum_sys::gum_exception_details_to_string(details); + if raw.is_null() { + return String::new(); + } + let owned = CStr::from_ptr(raw as *const c_char) + .to_string_lossy() + .into_owned(); + // gum_exception_details_to_string returns a newly-allocated string + // (transfer-full) that the caller must release. + crate::glib_compat::g_free(raw as *mut c_void); + owned + } + } +} + +impl Drop for Exceptor { + fn drop(&mut self) { + unsafe { frida_gum_sys::g_object_unref(self.exceptor as *mut c_void) }; + } +} + +/// Handle for a registered exception handler. +/// +/// This handle can be used to remove the exception handler via [`Exceptor::remove()`]. +pub struct ExceptionHandlerHandle { + func: gum_sys::GumExceptionHandler, + user_data: *mut c_void, + cleanup: unsafe extern "C" fn(*mut c_void), +} + +unsafe impl Send for Exceptor {} +unsafe impl Sync for Exceptor {} diff --git a/frida-gum/src/glib_compat.rs b/frida-gum/src/glib_compat.rs new file mode 100644 index 00000000..2e02b6a1 --- /dev/null +++ b/frida-gum/src/glib_compat.rs @@ -0,0 +1,85 @@ +/* + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Cross-platform GLib symbol shims. +//! +//! Frida's static devkit exposes GLib helpers under their bare `g_*` names on +//! Windows, macOS, and iOS, but under a `_frida_g_*` prefix on the Linux/FreeBSD +//! devkits (to avoid clashing with a system GLib). `frida-gum-sys` therefore +//! only generates bindings for whichever name the current target's devkit +//! provides. This module papers over that difference so the rest of the crate +//! can call a single stable name regardless of platform (mirroring the existing +//! `_frida_g_get_*_dir` handling in [`crate::process`]). + +use frida_gum_sys::{GArray, GError, GPtrArray, gboolean, gchar, gpointer, guint}; + +#[cfg(any(target_os = "linux", target_os = "freebsd"))] +mod sys { + pub use frida_gum_sys::{ + _frida_g_array_free as g_array_free, _frida_g_error_free as g_error_free, + _frida_g_free as g_free, _frida_g_ptr_array_add as g_ptr_array_add, + _frida_g_ptr_array_free as g_ptr_array_free, + _frida_g_ptr_array_sized_new as g_ptr_array_sized_new, + }; +} + +#[cfg(not(any(target_os = "linux", target_os = "freebsd")))] +mod sys { + pub use frida_gum_sys::{ + g_array_free, g_error_free, g_free, g_ptr_array_add, g_ptr_array_free, + g_ptr_array_sized_new, + }; +} + +/// Free a block previously allocated by GLib. +/// +/// # Safety +/// +/// `mem` must have been returned by a GLib allocation and not yet freed. +pub unsafe fn g_free(mem: gpointer) { + unsafe { sys::g_free(mem) } +} + +/// Free a `GError`. +/// +/// # Safety +/// +/// `error` must be a valid `GError` that has not already been freed. +pub unsafe fn g_error_free(error: *mut GError) { + unsafe { sys::g_error_free(error) } +} + +/// Free a `GArray`, optionally freeing the element segment. +/// +/// # Safety +/// +/// `array` must be a valid `GArray` that has not already been freed. +pub unsafe fn g_array_free(array: *mut GArray, free_segment: gboolean) -> *mut gchar { + unsafe { sys::g_array_free(array, free_segment) } +} + +/// Allocate a `GPtrArray` with space reserved for `reserved_size` elements. +pub fn g_ptr_array_sized_new(reserved_size: guint) -> *mut GPtrArray { + unsafe { sys::g_ptr_array_sized_new(reserved_size) } +} + +/// Append a pointer to a `GPtrArray`. +/// +/// # Safety +/// +/// `array` must be a valid `GPtrArray`. +pub unsafe fn g_ptr_array_add(array: *mut GPtrArray, data: gpointer) { + unsafe { sys::g_ptr_array_add(array, data) } +} + +/// Free a `GPtrArray`, optionally freeing the underlying segment. +/// +/// # Safety +/// +/// `array` must be a valid `GPtrArray` that has not already been freed. +pub unsafe fn g_ptr_array_free(array: *mut GPtrArray, free_seg: gboolean) -> *mut gpointer { + unsafe { sys::g_ptr_array_free(array, free_seg) } +} diff --git a/frida-gum/src/instruction_writer.rs b/frida-gum/src/instruction_writer.rs index 82ae867a..5b5360c4 100644 --- a/frida-gum/src/instruction_writer.rs +++ b/frida-gum/src/instruction_writer.rs @@ -19,6 +19,11 @@ mod x86_64; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub use x86_64::*; +#[cfg(any(target_arch = "mips", target_arch = "mips64"))] +mod mips; +#[cfg(any(target_arch = "mips", target_arch = "mips64"))] +pub use mips::*; + use frida_gum_sys::Insn; pub enum Argument { diff --git a/frida-gum/src/instruction_writer/aarch64/index.rs b/frida-gum/src/instruction_writer/aarch64/index.rs index 3a8e3c89..bfbfecd1 100644 --- a/frida-gum/src/instruction_writer/aarch64/index.rs +++ b/frida-gum/src/instruction_writer/aarch64/index.rs @@ -3,7 +3,7 @@ use frida_gum_sys as gum_sys; #[derive(FromPrimitive)] #[repr(u32)] pub enum IndexMode { - PostAdjust = gum_sys::_GumArm64IndexMode_GUM_INDEX_POST_ADJUST, - SignedOffset = gum_sys::_GumArm64IndexMode_GUM_INDEX_SIGNED_OFFSET, - PreAdjust = gum_sys::_GumArm64IndexMode_GUM_INDEX_PRE_ADJUST, + PostAdjust = gum_sys::_GumArm64IndexMode_GUM_INDEX_POST_ADJUST as _, + SignedOffset = gum_sys::_GumArm64IndexMode_GUM_INDEX_SIGNED_OFFSET as _, + PreAdjust = gum_sys::_GumArm64IndexMode_GUM_INDEX_PRE_ADJUST as _, } diff --git a/frida-gum/src/instruction_writer/aarch64/writer.rs b/frida-gum/src/instruction_writer/aarch64/writer.rs index 46f3ac56..05c78c70 100644 --- a/frida-gum/src/instruction_writer/aarch64/writer.rs +++ b/frida-gum/src/instruction_writer/aarch64/writer.rs @@ -275,7 +275,7 @@ impl Aarch64InstructionWriter { } #[allow(clippy::useless_conversion)] - pub fn put_call_address_with_arguments(&self, address: u64, arguments: &[Argument]) -> bool { + pub fn put_call_address_with_arguments(&self, address: u64, arguments: &[Argument]) { unsafe { let arguments: Vec = arguments .iter() @@ -299,7 +299,6 @@ impl Aarch64InstructionWriter { arguments.len() as u32, arguments.as_ptr(), ); - true } } @@ -307,6 +306,225 @@ impl Aarch64InstructionWriter { pub fn put_bl_imm(&self, address: u64) -> bool { unsafe { gum_sys::gum_arm64_writer_put_bl_imm(self.writer, address) != 0 } } + + /// Insert a `movk` instruction (move with keep). + /// + /// Moves a 16-bit immediate into a register while keeping other bits unchanged. + /// Used for building 64-bit constants across multiple instructions. + /// Added in Frida 17.16.1. + pub fn put_movk_reg_imm(&self, reg: Aarch64Register, shift: u16, imm: u32) -> bool { + unsafe { + gum_sys::gum_arm64_writer_put_movk_reg_imm(self.writer, reg as u32, shift, imm) != 0 + } + } + + /// Insert a `pacia` instruction (Pointer Authentication Code for Instruction address). + /// + /// Signs the value in the first register using the second register as context. + /// Part of ARMv8.3-A Pointer Authentication (PAC) feature. + /// Added in Frida 17.16.1. + /// + /// # Example + /// ```rust,no_run + /// use frida_gum::instruction_writer::{Aarch64InstructionWriter, InstructionWriter, Aarch64Register}; + /// + /// let writer = Aarch64InstructionWriter::new(0x1000); + /// // Sign x0 using x1 as modifier + /// writer.put_pacia_reg_reg(Aarch64Register::X0, Aarch64Register::X1); + /// ``` + pub fn put_pacia_reg_reg(&self, reg: Aarch64Register, modifier: Aarch64Register) -> bool { + unsafe { + gum_sys::gum_arm64_writer_put_pacia_reg_reg(self.writer, reg as u32, modifier as u32) + != 0 + } + } + + /// Insert a `ret` instruction. + pub fn put_ret(&self) { + unsafe { gum_sys::gum_arm64_writer_put_ret(self.writer) } + } + + /// Insert a `ret` instruction with a specific register. + pub fn put_ret_reg(&self, reg: Aarch64Register) -> bool { + unsafe { gum_sys::gum_arm64_writer_put_ret_reg(self.writer, reg as u32) != 0 } + } + + /// Insert a `br` instruction without authentication (for PAC). + pub fn put_br_reg_no_auth(&self, reg: Aarch64Register) -> bool { + unsafe { gum_sys::gum_arm64_writer_put_br_reg_no_auth(self.writer, reg as u32) != 0 } + } + + /// Insert a `blr` (branch with link register) instruction. + pub fn put_blr_reg(&self, reg: Aarch64Register) -> bool { + unsafe { gum_sys::gum_arm64_writer_put_blr_reg(self.writer, reg as u32) != 0 } + } + + /// Insert a `blr` instruction without authentication (for PAC). + pub fn put_blr_reg_no_auth(&self, reg: Aarch64Register) -> bool { + unsafe { gum_sys::gum_arm64_writer_put_blr_reg_no_auth(self.writer, reg as u32) != 0 } + } + + /// Insert a `b` (branch immediate) instruction. + pub fn put_b_imm(&self, target: u64) -> bool { + unsafe { gum_sys::gum_arm64_writer_put_b_imm(self.writer, target) != 0 } + } + + /// Insert a `bl` (branch with link) to a label. + pub fn put_bl_label(&self, label_id: &str) { + unsafe { + gum_sys::gum_arm64_writer_put_bl_label(self.writer, label_id.as_ptr() as *const c_void) + } + } + + /// Insert a `cbz` (compare and branch if zero) instruction. + pub fn put_cbz_reg_imm(&self, reg: Aarch64Register, target: u64) -> bool { + unsafe { gum_sys::gum_arm64_writer_put_cbz_reg_imm(self.writer, reg as u32, target) != 0 } + } + + /// Insert a `cbz` to a label. + pub fn put_cbz_reg_label(&self, reg: Aarch64Register, label_id: &str) { + unsafe { + gum_sys::gum_arm64_writer_put_cbz_reg_label( + self.writer, + reg as u32, + label_id.as_ptr() as *const c_void, + ) + } + } + + /// Insert a `cbnz` (compare and branch if not zero) instruction. + pub fn put_cbnz_reg_imm(&self, reg: Aarch64Register, target: u64) -> bool { + unsafe { gum_sys::gum_arm64_writer_put_cbnz_reg_imm(self.writer, reg as u32, target) != 0 } + } + + /// Insert a `cbnz` to a label. + pub fn put_cbnz_reg_label(&self, reg: Aarch64Register, label_id: &str) { + unsafe { + gum_sys::gum_arm64_writer_put_cbnz_reg_label( + self.writer, + reg as u32, + label_id.as_ptr() as *const c_void, + ) + } + } + + /// Insert a `tbz` (test bit and branch if zero) instruction. + pub fn put_tbz_reg_imm_imm(&self, reg: Aarch64Register, bit: u32, target: u64) -> bool { + unsafe { + gum_sys::gum_arm64_writer_put_tbz_reg_imm_imm(self.writer, reg as u32, bit, target) != 0 + } + } + + /// Insert a `tbz` to a label. + pub fn put_tbz_reg_imm_label(&self, reg: Aarch64Register, bit: u32, label_id: &str) { + unsafe { + gum_sys::gum_arm64_writer_put_tbz_reg_imm_label( + self.writer, + reg as u32, + bit, + label_id.as_ptr() as *const c_void, + ) + } + } + + /// Insert a `tbnz` (test bit and branch if not zero) instruction. + pub fn put_tbnz_reg_imm_imm(&self, reg: Aarch64Register, bit: u32, target: u64) -> bool { + unsafe { + gum_sys::gum_arm64_writer_put_tbnz_reg_imm_imm(self.writer, reg as u32, bit, target) + != 0 + } + } + + /// Insert a `tbnz` to a label. + pub fn put_tbnz_reg_imm_label(&self, reg: Aarch64Register, bit: u32, label_id: &str) { + unsafe { + gum_sys::gum_arm64_writer_put_tbnz_reg_imm_label( + self.writer, + reg as u32, + bit, + label_id.as_ptr() as *const c_void, + ) + } + } + + /// Push all X registers (X0-X29, LR). + pub fn put_push_all_x_registers(&self) { + unsafe { gum_sys::gum_arm64_writer_put_push_all_x_registers(self.writer) } + } + + /// Pop all X registers (X0-X29, LR). + pub fn put_pop_all_x_registers(&self) { + unsafe { gum_sys::gum_arm64_writer_put_pop_all_x_registers(self.writer) } + } + + /// Push all Q (SIMD/FP) registers. + pub fn put_push_all_q_registers(&self) { + unsafe { gum_sys::gum_arm64_writer_put_push_all_q_registers(self.writer) } + } + + /// Pop all Q (SIMD/FP) registers. + pub fn put_pop_all_q_registers(&self) { + unsafe { gum_sys::gum_arm64_writer_put_pop_all_q_registers(self.writer) } + } + + /// Insert an `ldr` with a 32-bit immediate. + pub fn put_ldr_reg_u32(&self, reg: Aarch64Register, val: u32) -> bool { + unsafe { gum_sys::gum_arm64_writer_put_ldr_reg_u32(self.writer, reg as u32, val) != 0 } + } + + /// Insert an `ldr` from register indirect. + pub fn put_ldr_reg_reg(&self, dst: Aarch64Register, src: Aarch64Register) -> bool { + unsafe { + gum_sys::gum_arm64_writer_put_ldr_reg_reg(self.writer, dst as u32, src as u32) != 0 + } + } + + /// Insert a `str` (store register) to register indirect. + pub fn put_str_reg_reg(&self, src: Aarch64Register, dst: Aarch64Register) -> bool { + unsafe { + gum_sys::gum_arm64_writer_put_str_reg_reg(self.writer, src as u32, dst as u32) != 0 + } + } + + /// Insert `mov` from register to NZCV (condition flags). + pub fn put_mov_reg_nzcv(&self, reg: Aarch64Register) { + unsafe { gum_sys::gum_arm64_writer_put_mov_reg_nzcv(self.writer, reg as u32) } + } + + /// Insert `mov` from NZCV (condition flags) to register. + pub fn put_mov_nzcv_reg(&self, reg: Aarch64Register) { + unsafe { gum_sys::gum_arm64_writer_put_mov_nzcv_reg(self.writer, reg as u32) } + } + + /// Insert a `sub` with register. + pub fn put_sub_reg_reg_reg( + &self, + dst: Aarch64Register, + src1: Aarch64Register, + src2: Aarch64Register, + ) -> bool { + unsafe { + gum_sys::gum_arm64_writer_put_sub_reg_reg_reg( + self.writer, + dst as u32, + src1 as u32, + src2 as u32, + ) != 0 + } + } + + /// Insert an `and` with immediate. + pub fn put_and_reg_reg_imm( + &self, + dst: Aarch64Register, + src: Aarch64Register, + imm: u64, + ) -> bool { + unsafe { + gum_sys::gum_arm64_writer_put_and_reg_reg_imm(self.writer, dst as u32, src as u32, imm) + != 0 + } + } } impl Drop for Aarch64InstructionWriter { diff --git a/frida-gum/src/instruction_writer/arm.rs b/frida-gum/src/instruction_writer/arm.rs index e8aeb77b..3833d6bc 100644 --- a/frida-gum/src/instruction_writer/arm.rs +++ b/frida-gum/src/instruction_writer/arm.rs @@ -7,6 +7,16 @@ pub use relocator::*; mod writer; pub use writer::*; +#[cfg(target_arch = "arm")] +mod thumb_condition; +#[cfg(target_arch = "arm")] +pub use thumb_condition::*; + +#[cfg(target_arch = "arm")] +mod thumb_writer; +#[cfg(target_arch = "arm")] +pub use thumb_writer::*; + pub type TargetInstructionWriter = ArmInstructionWriter; pub type TargetRelocator = ArmRelocator; pub type TargetRegister = ArmRegister; diff --git a/frida-gum/src/instruction_writer/arm/thumb_condition.rs b/frida-gum/src/instruction_writer/arm/thumb_condition.rs new file mode 100644 index 00000000..67f06589 --- /dev/null +++ b/frida-gum/src/instruction_writer/arm/thumb_condition.rs @@ -0,0 +1,42 @@ +// Copyright © 2026 Kirby Kuehl +// +// Licence: wxWindows Library Licence, Version 3.1 + +use frida_gum_sys as gum_sys; + +/// ARM condition codes for conditional branch instructions. +#[derive(FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] +#[repr(u32)] +pub enum ArmConditionCode { + Invalid = gum_sys::arm_cc_ARM_CC_INVALID, + /// Equal + Eq = gum_sys::arm_cc_ARM_CC_EQ, + /// Not equal + Ne = gum_sys::arm_cc_ARM_CC_NE, + /// Unsigned higher or same (carry set) + Hs = gum_sys::arm_cc_ARM_CC_HS, + /// Unsigned lower (carry clear) + Lo = gum_sys::arm_cc_ARM_CC_LO, + /// Negative (minus) + Mi = gum_sys::arm_cc_ARM_CC_MI, + /// Positive or zero (plus) + Pl = gum_sys::arm_cc_ARM_CC_PL, + /// Overflow set + Vs = gum_sys::arm_cc_ARM_CC_VS, + /// Overflow clear + Vc = gum_sys::arm_cc_ARM_CC_VC, + /// Unsigned higher + Hi = gum_sys::arm_cc_ARM_CC_HI, + /// Unsigned lower or same + Ls = gum_sys::arm_cc_ARM_CC_LS, + /// Signed greater than or equal + Ge = gum_sys::arm_cc_ARM_CC_GE, + /// Signed less than + Lt = gum_sys::arm_cc_ARM_CC_LT, + /// Signed greater than + Gt = gum_sys::arm_cc_ARM_CC_GT, + /// Signed less than or equal + Le = gum_sys::arm_cc_ARM_CC_LE, + /// Always (unconditional) + Al = gum_sys::arm_cc_ARM_CC_AL, +} diff --git a/frida-gum/src/instruction_writer/arm/thumb_writer.rs b/frida-gum/src/instruction_writer/arm/thumb_writer.rs new file mode 100644 index 00000000..1d981c86 --- /dev/null +++ b/frida-gum/src/instruction_writer/arm/thumb_writer.rs @@ -0,0 +1,700 @@ +// Copyright © 2026 Kirby Kuehl +// +// Licence: wxWindows Library Licence, Version 3.1 + +use { + crate::{ + NativePointer, + instruction_writer::{Argument, ArmConditionCode, ArmRegister, InstructionWriter}, + }, + core::ffi::c_void, + frida_gum_sys as gum_sys, + gum_sys::{GumArgument, arm_reg, gsize, gssize}, +}; + +#[cfg(not(any( + feature = "module-names", + feature = "backtrace", + feature = "memory-access-monitor" +)))] +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +/// The ARM Thumb instruction writer. +/// +/// Provides a safe Rust wrapper around Frida's Thumb instruction writer, +/// which generates ARM Thumb (16-bit/32-bit mixed) instruction sequences. +#[cfg(target_arch = "arm")] +pub struct ThumbWriter { + pub(crate) writer: *mut gum_sys::_GumThumbWriter, + is_from_new: bool, +} + +#[cfg(target_arch = "arm")] +impl InstructionWriter for ThumbWriter { + fn new(code_address: u64) -> Self { + Self { + writer: unsafe { gum_sys::gum_thumb_writer_new(code_address as *mut c_void) }, + is_from_new: true, + } + } + + fn code_offset(&self) -> u64 { + unsafe { (*self.writer).code as u64 } + } + + fn pc(&self) -> u64 { + unsafe { (*self.writer).pc } + } + + fn can_branch_directly_between(&self, source: u64, target: u64) -> bool { + unsafe { + gum_sys::gum_thumb_writer_can_branch_directly_between(self.writer, source, target) != 0 + } + } + + fn put_bytes(&self, bytes: &[u8]) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_bytes(self.writer, bytes.as_ptr(), bytes.len() as u32) + != 0 + } + } + + fn put_label(&self, id: u64) -> bool { + unsafe { gum_sys::gum_thumb_writer_put_label(self.writer, id as *const c_void) != 0 } + } + + fn reset(&self, code_address: u64) { + unsafe { gum_sys::gum_thumb_writer_reset(self.writer, code_address as *mut c_void) } + } + + fn put_branch_address(&self, address: u64) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_branch_address(self.writer, address); + true + } + } + + fn put_nop(&self) { + unsafe { gum_sys::gum_thumb_writer_put_nop(self.writer) } + } + + fn flush(&self) -> bool { + unsafe { gum_sys::gum_thumb_writer_flush(self.writer) != 0 } + } +} + +#[cfg(target_arch = "arm")] +impl ThumbWriter { + pub(crate) fn from_raw(writer: *mut gum_sys::_GumThumbWriter) -> Self { + Self { + writer, + is_from_new: false, + } + } + + /// Get the underlying Frida gum writer object. + pub fn raw_writer(&self) -> *mut gum_sys::_GumThumbWriter { + self.writer + } + + /// Clear (free) the writer's internal state without deallocating the writer struct itself. + pub fn clear(&self) { + unsafe { gum_sys::gum_thumb_writer_clear(self.writer) } + } + + /// Get a pointer to the writer's current write cursor. + pub fn cur(&self) -> NativePointer { + NativePointer(unsafe { gum_sys::gum_thumb_writer_cur(self.writer) }) + } + + /// Get the writer's byte offset from the original code address. + pub fn offset(&self) -> u32 { + unsafe { gum_sys::gum_thumb_writer_offset(self.writer) } + } + + /// Set the target operating system. + pub fn set_target_os(&self, os: gum_sys::GumOS) { + unsafe { gum_sys::gum_thumb_writer_set_target_os(self.writer, os) } + } + + /// Skip a number of bytes in the output stream. + pub fn skip(&self, n_bytes: u32) { + unsafe { gum_sys::gum_thumb_writer_skip(self.writer, n_bytes) } + } + + /// Commit a label at the current position. + pub fn commit_label(&self, id: u64) -> bool { + unsafe { gum_sys::gum_thumb_writer_commit_label(self.writer, id as *const c_void) != 0 } + } + + /// Insert a call to an address with arguments. + pub fn put_call_address_with_arguments(&self, func: u64, arguments: &[Argument]) { + let gum_arguments = Self::convert_arguments(arguments); + unsafe { + gum_sys::gum_thumb_writer_put_call_address_with_arguments_array( + self.writer, + func, + gum_arguments.len() as u32, + gum_arguments.as_ptr(), + ) + } + } + + /// Insert a call to a register with arguments. + pub fn put_call_reg_with_arguments(&self, reg: ArmRegister, arguments: &[Argument]) { + let gum_arguments = Self::convert_arguments(arguments); + unsafe { + gum_sys::gum_thumb_writer_put_call_reg_with_arguments_array( + self.writer, + reg as arm_reg, + gum_arguments.len() as u32, + gum_arguments.as_ptr(), + ) + } + } + + /// Insert a `b` (branch) to an immediate address. + pub fn put_b_imm(&self, target: u64) { + unsafe { gum_sys::gum_thumb_writer_put_b_imm(self.writer, target) } + } + + /// Insert a `b` (branch) to a label. + pub fn put_b_label(&self, label_id: u64) { + unsafe { gum_sys::gum_thumb_writer_put_b_label(self.writer, label_id as *const c_void) } + } + + /// Insert a wide `b` (branch) to a label. + pub fn put_b_label_wide(&self, label_id: u64) { + unsafe { + gum_sys::gum_thumb_writer_put_b_label_wide(self.writer, label_id as *const c_void) + } + } + + /// Insert a `bx` (branch and exchange) to a register. + pub fn put_bx_reg(&self, reg: ArmRegister) { + unsafe { gum_sys::gum_thumb_writer_put_bx_reg(self.writer, reg as arm_reg) } + } + + /// Insert a `bl` (branch with link) to an immediate address. + pub fn put_bl_imm(&self, target: u64) { + unsafe { gum_sys::gum_thumb_writer_put_bl_imm(self.writer, target) } + } + + /// Insert a `bl` (branch with link) to a label. + pub fn put_bl_label(&self, label_id: u64) { + unsafe { gum_sys::gum_thumb_writer_put_bl_label(self.writer, label_id as *const c_void) } + } + + /// Insert a `blx` (branch with link and exchange) to an immediate address. + pub fn put_blx_imm(&self, target: u64) { + unsafe { gum_sys::gum_thumb_writer_put_blx_imm(self.writer, target) } + } + + /// Insert a `blx` (branch with link and exchange) to a register. + pub fn put_blx_reg(&self, reg: ArmRegister) { + unsafe { gum_sys::gum_thumb_writer_put_blx_reg(self.writer, reg as arm_reg) } + } + + /// Insert a `cmp` (compare) register with immediate. + pub fn put_cmp_reg_imm(&self, reg: ArmRegister, imm_value: u8) { + unsafe { gum_sys::gum_thumb_writer_put_cmp_reg_imm(self.writer, reg as arm_reg, imm_value) } + } + + /// Insert a `beq` (branch if equal) to a label. + pub fn put_beq_label(&self, label_id: u64) { + unsafe { gum_sys::gum_thumb_writer_put_beq_label(self.writer, label_id as *const c_void) } + } + + /// Insert a `bne` (branch if not equal) to a label. + pub fn put_bne_label(&self, label_id: u64) { + unsafe { gum_sys::gum_thumb_writer_put_bne_label(self.writer, label_id as *const c_void) } + } + + /// Insert a conditional branch to a label. + pub fn put_b_cond_label(&self, cc: ArmConditionCode, label_id: u64) { + unsafe { + gum_sys::gum_thumb_writer_put_b_cond_label( + self.writer, + cc as u32, + label_id as *const c_void, + ) + } + } + + /// Insert a wide conditional branch to a label. + pub fn put_b_cond_label_wide(&self, cc: ArmConditionCode, label_id: u64) { + unsafe { + gum_sys::gum_thumb_writer_put_b_cond_label_wide( + self.writer, + cc as u32, + label_id as *const c_void, + ) + } + } + + /// Insert a `cbz` (compare and branch if zero) instruction. + pub fn put_cbz_reg_label(&self, reg: ArmRegister, label_id: u64) { + unsafe { + gum_sys::gum_thumb_writer_put_cbz_reg_label( + self.writer, + reg as arm_reg, + label_id as *const c_void, + ) + } + } + + /// Insert a `cbnz` (compare and branch if non-zero) instruction. + pub fn put_cbnz_reg_label(&self, reg: ArmRegister, label_id: u64) { + unsafe { + gum_sys::gum_thumb_writer_put_cbnz_reg_label( + self.writer, + reg as arm_reg, + label_id as *const c_void, + ) + } + } + + /// Insert a `push` instruction for multiple registers. + pub fn put_push_regs(&self, regs: &[ArmRegister]) -> bool { + let reg_array: Vec = regs.iter().map(|&r| r as arm_reg).collect(); + unsafe { + gum_sys::gum_thumb_writer_put_push_regs_array( + self.writer, + reg_array.len() as u32, + reg_array.as_ptr(), + ) != 0 + } + } + + /// Insert a `pop` instruction for multiple registers. + pub fn put_pop_regs(&self, regs: &[ArmRegister]) -> bool { + let reg_array: Vec = regs.iter().map(|&r| r as arm_reg).collect(); + unsafe { + gum_sys::gum_thumb_writer_put_pop_regs_array( + self.writer, + reg_array.len() as u32, + reg_array.as_ptr(), + ) != 0 + } + } + + /// Insert a `vpush` instruction for a range of VFP registers. + pub fn put_vpush_range(&self, first_reg: ArmRegister, last_reg: ArmRegister) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_vpush_range( + self.writer, + first_reg as arm_reg, + last_reg as arm_reg, + ) != 0 + } + } + + /// Insert a `vpop` instruction for a range of VFP registers. + pub fn put_vpop_range(&self, first_reg: ArmRegister, last_reg: ArmRegister) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_vpop_range( + self.writer, + first_reg as arm_reg, + last_reg as arm_reg, + ) != 0 + } + } + + /// Insert a `ldr` (load register) from an address. + pub fn put_ldr_reg_address(&self, reg: ArmRegister, address: u64) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_ldr_reg_address(self.writer, reg as arm_reg, address) != 0 + } + } + + /// Insert a `ldr` (load register) with an immediate 32-bit value. + pub fn put_ldr_reg_u32(&self, reg: ArmRegister, val: u32) -> bool { + unsafe { gum_sys::gum_thumb_writer_put_ldr_reg_u32(self.writer, reg as arm_reg, val) != 0 } + } + + /// Insert a `ldr` (load register) from a register. + pub fn put_ldr_reg_reg(&self, dst_reg: ArmRegister, src_reg: ArmRegister) { + unsafe { + gum_sys::gum_thumb_writer_put_ldr_reg_reg( + self.writer, + dst_reg as arm_reg, + src_reg as arm_reg, + ) + } + } + + /// Insert a `ldr` (load register) from a register with offset. + pub fn put_ldr_reg_reg_offset( + &self, + dst_reg: ArmRegister, + src_reg: ArmRegister, + src_offset: usize, + ) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_ldr_reg_reg_offset( + self.writer, + dst_reg as arm_reg, + src_reg as arm_reg, + src_offset as gsize, + ) != 0 + } + } + + /// Insert a `ldrb` (load byte) from a register. + pub fn put_ldrb_reg_reg(&self, dst_reg: ArmRegister, src_reg: ArmRegister) { + unsafe { + gum_sys::gum_thumb_writer_put_ldrb_reg_reg( + self.writer, + dst_reg as arm_reg, + src_reg as arm_reg, + ) + } + } + + /// Insert a `ldrh` (load halfword) from a register. + pub fn put_ldrh_reg_reg(&self, dst_reg: ArmRegister, src_reg: ArmRegister) { + unsafe { + gum_sys::gum_thumb_writer_put_ldrh_reg_reg( + self.writer, + dst_reg as arm_reg, + src_reg as arm_reg, + ) + } + } + + /// Insert a `vldr` (VFP load) from a register with offset. + pub fn put_vldr_reg_reg_offset( + &self, + dst_reg: ArmRegister, + src_reg: ArmRegister, + src_offset: isize, + ) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_vldr_reg_reg_offset( + self.writer, + dst_reg as arm_reg, + src_reg as arm_reg, + src_offset as gssize, + ) != 0 + } + } + + /// Insert a `ldmia` (load multiple increment after) instruction. + pub fn put_ldmia_reg_mask(&self, reg: ArmRegister, mask: u16) { + unsafe { gum_sys::gum_thumb_writer_put_ldmia_reg_mask(self.writer, reg as arm_reg, mask) } + } + + /// Insert a `str` (store register) to a register. + pub fn put_str_reg_reg(&self, src_reg: ArmRegister, dst_reg: ArmRegister) { + unsafe { + gum_sys::gum_thumb_writer_put_str_reg_reg( + self.writer, + src_reg as arm_reg, + dst_reg as arm_reg, + ) + } + } + + /// Insert a `str` (store register) to a register with offset. + pub fn put_str_reg_reg_offset( + &self, + src_reg: ArmRegister, + dst_reg: ArmRegister, + dst_offset: usize, + ) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_str_reg_reg_offset( + self.writer, + src_reg as arm_reg, + dst_reg as arm_reg, + dst_offset as gsize, + ) != 0 + } + } + + /// Insert a `mov` (move) register to register. + pub fn put_mov_reg_reg(&self, dst_reg: ArmRegister, src_reg: ArmRegister) { + unsafe { + gum_sys::gum_thumb_writer_put_mov_reg_reg( + self.writer, + dst_reg as arm_reg, + src_reg as arm_reg, + ) + } + } + + /// Insert a `mov` (move) immediate 8-bit value to register. + pub fn put_mov_reg_u8(&self, dst_reg: ArmRegister, imm_value: u8) { + unsafe { + gum_sys::gum_thumb_writer_put_mov_reg_u8(self.writer, dst_reg as arm_reg, imm_value) + } + } + + /// Insert a `mrs` (move from special register) from CPSR to register. + pub fn put_mov_reg_cpsr(&self, reg: ArmRegister) { + unsafe { gum_sys::gum_thumb_writer_put_mov_reg_cpsr(self.writer, reg as arm_reg) } + } + + /// Insert a `msr` (move to special register) from register to CPSR. + pub fn put_mov_cpsr_reg(&self, reg: ArmRegister) { + unsafe { gum_sys::gum_thumb_writer_put_mov_cpsr_reg(self.writer, reg as arm_reg) } + } + + /// Insert an `add` (add) immediate to register. + pub fn put_add_reg_imm(&self, dst_reg: ArmRegister, imm_value: isize) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_add_reg_imm( + self.writer, + dst_reg as arm_reg, + imm_value as gssize, + ) != 0 + } + } + + /// Insert an `add` (add) register to register. + pub fn put_add_reg_reg(&self, dst_reg: ArmRegister, src_reg: ArmRegister) { + unsafe { + gum_sys::gum_thumb_writer_put_add_reg_reg( + self.writer, + dst_reg as arm_reg, + src_reg as arm_reg, + ) + } + } + + /// Insert an `add` (add) register to register to register. + pub fn put_add_reg_reg_reg( + &self, + dst_reg: ArmRegister, + left_reg: ArmRegister, + right_reg: ArmRegister, + ) { + unsafe { + gum_sys::gum_thumb_writer_put_add_reg_reg_reg( + self.writer, + dst_reg as arm_reg, + left_reg as arm_reg, + right_reg as arm_reg, + ) + } + } + + /// Insert an `add` (add) register plus immediate to register. + pub fn put_add_reg_reg_imm( + &self, + dst_reg: ArmRegister, + left_reg: ArmRegister, + right_value: isize, + ) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_add_reg_reg_imm( + self.writer, + dst_reg as arm_reg, + left_reg as arm_reg, + right_value as gssize, + ) != 0 + } + } + + /// Insert a `sub` (subtract) immediate from register. + pub fn put_sub_reg_imm(&self, dst_reg: ArmRegister, imm_value: isize) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_sub_reg_imm( + self.writer, + dst_reg as arm_reg, + imm_value as gssize, + ) != 0 + } + } + + /// Insert a `sub` (subtract) register from register. + pub fn put_sub_reg_reg(&self, dst_reg: ArmRegister, src_reg: ArmRegister) { + unsafe { + gum_sys::gum_thumb_writer_put_sub_reg_reg( + self.writer, + dst_reg as arm_reg, + src_reg as arm_reg, + ) + } + } + + /// Insert a `sub` (subtract) register from register to register. + pub fn put_sub_reg_reg_reg( + &self, + dst_reg: ArmRegister, + left_reg: ArmRegister, + right_reg: ArmRegister, + ) { + unsafe { + gum_sys::gum_thumb_writer_put_sub_reg_reg_reg( + self.writer, + dst_reg as arm_reg, + left_reg as arm_reg, + right_reg as arm_reg, + ) + } + } + + /// Insert a `sub` (subtract) register minus immediate to register. + pub fn put_sub_reg_reg_imm( + &self, + dst_reg: ArmRegister, + left_reg: ArmRegister, + right_value: isize, + ) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_sub_reg_reg_imm( + self.writer, + dst_reg as arm_reg, + left_reg as arm_reg, + right_value as gssize, + ) != 0 + } + } + + /// Insert an `and` (bitwise AND) instruction. + pub fn put_and_reg_reg_imm( + &self, + dst_reg: ArmRegister, + left_reg: ArmRegister, + right_value: isize, + ) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_and_reg_reg_imm( + self.writer, + dst_reg as arm_reg, + left_reg as arm_reg, + right_value as gssize, + ) != 0 + } + } + + /// Insert an `orr` (bitwise OR) instruction. + pub fn put_or_reg_reg_imm( + &self, + dst_reg: ArmRegister, + left_reg: ArmRegister, + right_value: isize, + ) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_or_reg_reg_imm( + self.writer, + dst_reg as arm_reg, + left_reg as arm_reg, + right_value as gssize, + ) != 0 + } + } + + /// Insert a `lsl` (logical shift left) instruction. + pub fn put_lsl_reg_reg_imm( + &self, + dst_reg: ArmRegister, + left_reg: ArmRegister, + right_value: u8, + ) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_lsl_reg_reg_imm( + self.writer, + dst_reg as arm_reg, + left_reg as arm_reg, + right_value, + ) != 0 + } + } + + /// Insert a `lsls` (logical shift left, setting flags) instruction. + pub fn put_lsls_reg_reg_imm( + &self, + dst_reg: ArmRegister, + left_reg: ArmRegister, + right_value: u8, + ) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_lsls_reg_reg_imm( + self.writer, + dst_reg as arm_reg, + left_reg as arm_reg, + right_value, + ) != 0 + } + } + + /// Insert a `lsrs` (logical shift right, setting flags) instruction. + pub fn put_lsrs_reg_reg_imm( + &self, + dst_reg: ArmRegister, + left_reg: ArmRegister, + right_value: u8, + ) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_lsrs_reg_reg_imm( + self.writer, + dst_reg as arm_reg, + left_reg as arm_reg, + right_value, + ) != 0 + } + } + + /// Insert a `mrs` (move from system register) instruction. + pub fn put_mrs_reg_reg(&self, dst_reg: ArmRegister, src_reg: u32) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_mrs_reg_reg(self.writer, dst_reg as arm_reg, src_reg) != 0 + } + } + + /// Insert a `msr` (move to system register) instruction. + pub fn put_msr_reg_reg(&self, dst_reg: u32, src_reg: ArmRegister) -> bool { + unsafe { + gum_sys::gum_thumb_writer_put_msr_reg_reg(self.writer, dst_reg, src_reg as arm_reg) != 0 + } + } + + /// Insert a `bkpt` (breakpoint) instruction with immediate. + pub fn put_bkpt_imm(&self, imm: u8) { + unsafe { gum_sys::gum_thumb_writer_put_bkpt_imm(self.writer, imm) } + } + + /// Insert a breakpoint instruction. + pub fn put_breakpoint(&self) { + unsafe { gum_sys::gum_thumb_writer_put_breakpoint(self.writer) } + } + + /// Insert a raw 16-bit instruction. + pub fn put_instruction(&self, insn: u16) { + unsafe { gum_sys::gum_thumb_writer_put_instruction(self.writer, insn) } + } + + /// Insert a raw 32-bit wide instruction. + pub fn put_instruction_wide(&self, upper: u16, lower: u16) { + unsafe { gum_sys::gum_thumb_writer_put_instruction_wide(self.writer, upper, lower) } + } + + /// Helper to convert Argument slice to GumArgument Vec. + fn convert_arguments(arguments: &[Argument]) -> Vec { + arguments + .iter() + .map(|arg| match arg { + Argument::Register(reg) => GumArgument { + type_: gum_sys::_GumArgType_GUM_ARG_REGISTER.try_into().unwrap(), + value: gum_sys::_GumArgument__bindgen_ty_1 { reg: *reg as i32 }, + }, + Argument::Address(addr) => GumArgument { + type_: gum_sys::_GumArgType_GUM_ARG_ADDRESS.try_into().unwrap(), + value: gum_sys::_GumArgument__bindgen_ty_1 { address: *addr }, + }, + }) + .collect() + } +} + +#[cfg(target_arch = "arm")] +impl Drop for ThumbWriter { + fn drop(&mut self) { + if self.is_from_new { + unsafe { gum_sys::gum_thumb_writer_unref(self.writer) } + } + } +} diff --git a/frida-gum/src/instruction_writer/mips.rs b/frida-gum/src/instruction_writer/mips.rs new file mode 100644 index 00000000..99a2f54a --- /dev/null +++ b/frida-gum/src/instruction_writer/mips.rs @@ -0,0 +1,11 @@ +/* + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +mod register; +pub use register::*; + +mod writer; +pub use writer::*; diff --git a/frida-gum/src/instruction_writer/mips/register.rs b/frida-gum/src/instruction_writer/mips/register.rs new file mode 100644 index 00000000..d1020d0a --- /dev/null +++ b/frida-gum/src/instruction_writer/mips/register.rs @@ -0,0 +1,87 @@ +/* + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +use frida_gum_sys as gum_sys; + +#[derive(FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] +#[repr(u32)] +pub enum MipsRegister { + Invalid = gum_sys::mips_reg_MIPS_REG_INVALID as u32, + Pc = gum_sys::mips_reg_MIPS_REG_PC as u32, + + // General purpose registers + R0 = gum_sys::mips_reg_MIPS_REG_0 as u32, + R1 = gum_sys::mips_reg_MIPS_REG_1 as u32, + R2 = gum_sys::mips_reg_MIPS_REG_2 as u32, + R3 = gum_sys::mips_reg_MIPS_REG_3 as u32, + R4 = gum_sys::mips_reg_MIPS_REG_4 as u32, + R5 = gum_sys::mips_reg_MIPS_REG_5 as u32, + R6 = gum_sys::mips_reg_MIPS_REG_6 as u32, + R7 = gum_sys::mips_reg_MIPS_REG_7 as u32, + R8 = gum_sys::mips_reg_MIPS_REG_8 as u32, + R9 = gum_sys::mips_reg_MIPS_REG_9 as u32, + R10 = gum_sys::mips_reg_MIPS_REG_10 as u32, + R11 = gum_sys::mips_reg_MIPS_REG_11 as u32, + R12 = gum_sys::mips_reg_MIPS_REG_12 as u32, + R13 = gum_sys::mips_reg_MIPS_REG_13 as u32, + R14 = gum_sys::mips_reg_MIPS_REG_14 as u32, + R15 = gum_sys::mips_reg_MIPS_REG_15 as u32, + R16 = gum_sys::mips_reg_MIPS_REG_16 as u32, + R17 = gum_sys::mips_reg_MIPS_REG_17 as u32, + R18 = gum_sys::mips_reg_MIPS_REG_18 as u32, + R19 = gum_sys::mips_reg_MIPS_REG_19 as u32, + R20 = gum_sys::mips_reg_MIPS_REG_20 as u32, + R21 = gum_sys::mips_reg_MIPS_REG_21 as u32, + R22 = gum_sys::mips_reg_MIPS_REG_22 as u32, + R23 = gum_sys::mips_reg_MIPS_REG_23 as u32, + R24 = gum_sys::mips_reg_MIPS_REG_24 as u32, + R25 = gum_sys::mips_reg_MIPS_REG_25 as u32, + R26 = gum_sys::mips_reg_MIPS_REG_26 as u32, + R27 = gum_sys::mips_reg_MIPS_REG_27 as u32, + R28 = gum_sys::mips_reg_MIPS_REG_28 as u32, + R29 = gum_sys::mips_reg_MIPS_REG_29 as u32, + R30 = gum_sys::mips_reg_MIPS_REG_30 as u32, + R31 = gum_sys::mips_reg_MIPS_REG_31 as u32, + + // Common register aliases + Zero = gum_sys::mips_reg_MIPS_REG_ZERO as u32, + At = gum_sys::mips_reg_MIPS_REG_AT as u32, + V0 = gum_sys::mips_reg_MIPS_REG_V0 as u32, + V1 = gum_sys::mips_reg_MIPS_REG_V1 as u32, + A0 = gum_sys::mips_reg_MIPS_REG_A0 as u32, + A1 = gum_sys::mips_reg_MIPS_REG_A1 as u32, + A2 = gum_sys::mips_reg_MIPS_REG_A2 as u32, + A3 = gum_sys::mips_reg_MIPS_REG_A3 as u32, + T0 = gum_sys::mips_reg_MIPS_REG_T0 as u32, + T1 = gum_sys::mips_reg_MIPS_REG_T1 as u32, + T2 = gum_sys::mips_reg_MIPS_REG_T2 as u32, + T3 = gum_sys::mips_reg_MIPS_REG_T3 as u32, + T4 = gum_sys::mips_reg_MIPS_REG_T4 as u32, + T5 = gum_sys::mips_reg_MIPS_REG_T5 as u32, + T6 = gum_sys::mips_reg_MIPS_REG_T6 as u32, + T7 = gum_sys::mips_reg_MIPS_REG_T7 as u32, + S0 = gum_sys::mips_reg_MIPS_REG_S0 as u32, + S1 = gum_sys::mips_reg_MIPS_REG_S1 as u32, + S2 = gum_sys::mips_reg_MIPS_REG_S2 as u32, + S3 = gum_sys::mips_reg_MIPS_REG_S3 as u32, + S4 = gum_sys::mips_reg_MIPS_REG_S4 as u32, + S5 = gum_sys::mips_reg_MIPS_REG_S5 as u32, + S6 = gum_sys::mips_reg_MIPS_REG_S6 as u32, + S7 = gum_sys::mips_reg_MIPS_REG_S7 as u32, + T8 = gum_sys::mips_reg_MIPS_REG_T8 as u32, + T9 = gum_sys::mips_reg_MIPS_REG_T9 as u32, + K0 = gum_sys::mips_reg_MIPS_REG_K0 as u32, + K1 = gum_sys::mips_reg_MIPS_REG_K1 as u32, + Gp = gum_sys::mips_reg_MIPS_REG_GP as u32, + Sp = gum_sys::mips_reg_MIPS_REG_SP as u32, + Fp = gum_sys::mips_reg_MIPS_REG_FP as u32, + S8 = gum_sys::mips_reg_MIPS_REG_S8 as u32, + Ra = gum_sys::mips_reg_MIPS_REG_RA as u32, + + // Special registers + Hi = gum_sys::mips_reg_MIPS_REG_HI as u32, + Lo = gum_sys::mips_reg_MIPS_REG_LO as u32, +} diff --git a/frida-gum/src/instruction_writer/mips/writer.rs b/frida-gum/src/instruction_writer/mips/writer.rs new file mode 100644 index 00000000..7ba2a1ad --- /dev/null +++ b/frida-gum/src/instruction_writer/mips/writer.rs @@ -0,0 +1,326 @@ +/* + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +use { + crate::{NativePointer, instruction_writer::InstructionWriter}, + core::ffi::c_void, + frida_gum_sys as gum_sys, + gum_sys::{GumArgument, mips_reg}, +}; + +#[cfg(not(any( + feature = "module-names", + feature = "backtrace", + feature = "memory-access-monitor" +)))] +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +/// The MIPS instruction writer. +#[cfg(any(target_arch = "mips", target_arch = "mips64"))] +pub struct MipsInstructionWriter { + pub(crate) writer: *mut gum_sys::_GumMipsWriter, + is_from_new: bool, +} + +#[cfg(any(target_arch = "mips", target_arch = "mips64"))] +impl InstructionWriter for MipsInstructionWriter { + fn new(code_address: u64) -> Self { + Self { + writer: unsafe { gum_sys::gum_mips_writer_new(code_address as *mut c_void) }, + is_from_new: true, + } + } + + fn code_offset(&self) -> u64 { + unsafe { (*self.writer).code as u64 } + } + + fn pc(&self) -> u64 { + unsafe { (*self.writer).pc } + } + + fn can_branch_directly_between(&self, source: u64, target: u64) -> bool { + unsafe { gum_sys::gum_mips_writer_can_branch_directly_between(source, target) != 0 } + } + + fn put_bytes(&self, bytes: &[u8]) -> bool { + unsafe { + gum_sys::gum_mips_writer_put_bytes(self.writer, bytes.as_ptr(), bytes.len() as u32) != 0 + } + } + + fn put_label(&self, id: u64) -> bool { + unsafe { gum_sys::gum_mips_writer_put_label(self.writer, id as *const c_void) != 0 } + } + + fn reset(&self, code_address: u64) { + unsafe { gum_sys::gum_mips_writer_reset(self.writer, code_address as *mut c_void) } + } + + fn put_branch_address(&self, address: u64) -> bool { + unsafe { gum_sys::gum_mips_writer_put_j_address(self.writer, address) != 0 } + } + + fn put_nop(&self) { + unsafe { gum_sys::gum_mips_writer_put_nop(self.writer) } + } + + fn flush(&self) -> bool { + unsafe { gum_sys::gum_mips_writer_flush(self.writer) != 0 } + } +} + +#[cfg(any(target_arch = "mips", target_arch = "mips64"))] +impl MipsInstructionWriter { + pub(crate) fn from_raw(writer: *mut gum_sys::_GumMipsWriter) -> Self { + Self { + writer, + is_from_new: false, + } + } + + /// Get the underlying frida gum writer object + pub fn raw_writer(&self) -> *mut gum_sys::_GumMipsWriter { + self.writer + } + + /// Clear (free) the writer's internal state without deallocating the + /// writer struct itself. + pub fn clear(&self) { + unsafe { gum_sys::gum_mips_writer_clear(self.writer) }; + } + + /// Get a pointer to the writer's current write cursor. + pub fn cur(&self) -> NativePointer { + NativePointer(unsafe { gum_sys::gum_mips_writer_cur(self.writer) }) + } + + /// Get the writer's byte offset from the original code address. + pub fn offset(&self) -> u32 { + unsafe { gum_sys::gum_mips_writer_offset(self.writer) } + } + + /// Skip `n_bytes` bytes in the instruction stream. + pub fn skip(&self, n_bytes: u32) { + unsafe { gum_sys::gum_mips_writer_skip(self.writer, n_bytes) }; + } + + /// Write a call to the given address with arguments (array form). + pub fn put_call_address_with_arguments_array(&self, func: u64, args: &[GumArgument]) { + unsafe { + gum_sys::gum_mips_writer_put_call_address_with_arguments_array( + self.writer, + func, + args.len() as u32, + args.as_ptr(), + ) + } + } + + /// Write a call to the given register with arguments (array form). + pub fn put_call_reg_with_arguments_array(&self, reg: mips_reg, args: &[GumArgument]) { + unsafe { + gum_sys::gum_mips_writer_put_call_reg_with_arguments_array( + self.writer, + reg, + args.len() as u32, + args.as_ptr(), + ) + } + } + + /// Write a jump to the given address. + pub fn put_j_address(&self, address: u64) -> bool { + unsafe { gum_sys::gum_mips_writer_put_j_address(self.writer, address) != 0 } + } + + /// Write a jump to the given address without a NOP in the delay slot. + pub fn put_j_address_without_nop(&self, address: u64) -> bool { + unsafe { gum_sys::gum_mips_writer_put_j_address_without_nop(self.writer, address) != 0 } + } + + /// Write a jump to the given label. + pub fn put_j_label(&self, label_id: u64) { + unsafe { gum_sys::gum_mips_writer_put_j_label(self.writer, label_id as *const c_void) } + } + + /// Write a jump register instruction. + pub fn put_jr_reg(&self, reg: mips_reg) { + unsafe { gum_sys::gum_mips_writer_put_jr_reg(self.writer, reg) } + } + + /// Write a jump and link to the given address. + pub fn put_jal_address(&self, address: u32) { + unsafe { gum_sys::gum_mips_writer_put_jal_address(self.writer, address) } + } + + /// Write a jump and link register instruction. + pub fn put_jalr_reg(&self, reg: mips_reg) { + unsafe { gum_sys::gum_mips_writer_put_jalr_reg(self.writer, reg) } + } + + /// Write a branch with the given offset. + pub fn put_b_offset(&self, offset: i32) { + unsafe { gum_sys::gum_mips_writer_put_b_offset(self.writer, offset) } + } + + /// Write a branch if equal instruction. + pub fn put_beq_reg_reg_label(&self, right_reg: mips_reg, left_reg: mips_reg, label_id: u64) { + unsafe { + gum_sys::gum_mips_writer_put_beq_reg_reg_label( + self.writer, + right_reg, + left_reg, + label_id as *const c_void, + ) + } + } + + /// Write a return instruction. + pub fn put_ret(&self) { + unsafe { gum_sys::gum_mips_writer_put_ret(self.writer) } + } + + /// Write a load address pseudo-instruction. + pub fn put_la_reg_address(&self, reg: mips_reg, address: u64) { + unsafe { gum_sys::gum_mips_writer_put_la_reg_address(self.writer, reg, address) } + } + + /// Write a load upper immediate instruction. + pub fn put_lui_reg_imm(&self, reg: mips_reg, imm: u32) { + unsafe { gum_sys::gum_mips_writer_put_lui_reg_imm(self.writer, reg, imm) } + } + + /// Write a doubleword shift left logical instruction (MIPS64). + pub fn put_dsll_reg_reg(&self, dst_reg: mips_reg, src_reg: mips_reg, amount: u32) { + unsafe { gum_sys::gum_mips_writer_put_dsll_reg_reg(self.writer, dst_reg, src_reg, amount) } + } + + /// Write an OR immediate instruction. + pub fn put_ori_reg_reg_imm(&self, rt: mips_reg, rs: mips_reg, imm: u32) { + unsafe { gum_sys::gum_mips_writer_put_ori_reg_reg_imm(self.writer, rt, rs, imm) } + } + + /// Write a load doubleword instruction (MIPS64). + pub fn put_ld_reg_reg_offset(&self, dst_reg: mips_reg, src_reg: mips_reg, src_offset: usize) { + unsafe { + gum_sys::gum_mips_writer_put_ld_reg_reg_offset( + self.writer, + dst_reg, + src_reg, + src_offset, + ) + } + } + + /// Write a load word instruction. + pub fn put_lw_reg_reg_offset(&self, dst_reg: mips_reg, src_reg: mips_reg, src_offset: usize) { + unsafe { + gum_sys::gum_mips_writer_put_lw_reg_reg_offset( + self.writer, + dst_reg, + src_reg, + src_offset, + ) + } + } + + /// Write a store word instruction. + pub fn put_sw_reg_reg_offset(&self, src_reg: mips_reg, dst_reg: mips_reg, dst_offset: usize) { + unsafe { + gum_sys::gum_mips_writer_put_sw_reg_reg_offset( + self.writer, + src_reg, + dst_reg, + dst_offset, + ) + } + } + + /// Write a move pseudo-instruction. + pub fn put_move_reg_reg(&self, dst_reg: mips_reg, src_reg: mips_reg) { + unsafe { gum_sys::gum_mips_writer_put_move_reg_reg(self.writer, dst_reg, src_reg) } + } + + /// Write an add unsigned instruction. + pub fn put_addu_reg_reg_reg(&self, dst_reg: mips_reg, left_reg: mips_reg, right_reg: mips_reg) { + unsafe { + gum_sys::gum_mips_writer_put_addu_reg_reg_reg(self.writer, dst_reg, left_reg, right_reg) + } + } + + /// Write an add immediate instruction. + pub fn put_addi_reg_reg_imm(&self, dst_reg: mips_reg, left_reg: mips_reg, imm: i32) { + unsafe { + gum_sys::gum_mips_writer_put_addi_reg_reg_imm(self.writer, dst_reg, left_reg, imm) + } + } + + /// Write an add immediate instruction (single register form). + pub fn put_addi_reg_imm(&self, dst_reg: mips_reg, imm: i32) { + unsafe { gum_sys::gum_mips_writer_put_addi_reg_imm(self.writer, dst_reg, imm) } + } + + /// Write a subtract immediate instruction. + pub fn put_sub_reg_reg_imm(&self, dst_reg: mips_reg, left_reg: mips_reg, imm: i32) { + unsafe { gum_sys::gum_mips_writer_put_sub_reg_reg_imm(self.writer, dst_reg, left_reg, imm) } + } + + /// Write a push register pseudo-instruction. + pub fn put_push_reg(&self, reg: mips_reg) { + unsafe { gum_sys::gum_mips_writer_put_push_reg(self.writer, reg) } + } + + /// Write a pop register pseudo-instruction. + pub fn put_pop_reg(&self, reg: mips_reg) { + unsafe { gum_sys::gum_mips_writer_put_pop_reg(self.writer, reg) } + } + + /// Write a move from HI instruction. + pub fn put_mfhi_reg(&self, reg: mips_reg) { + unsafe { gum_sys::gum_mips_writer_put_mfhi_reg(self.writer, reg) } + } + + /// Write a move from LO instruction. + pub fn put_mflo_reg(&self, reg: mips_reg) { + unsafe { gum_sys::gum_mips_writer_put_mflo_reg(self.writer, reg) } + } + + /// Write a move to HI instruction. + pub fn put_mthi_reg(&self, reg: mips_reg) { + unsafe { gum_sys::gum_mips_writer_put_mthi_reg(self.writer, reg) } + } + + /// Write a move to LO instruction. + pub fn put_mtlo_reg(&self, reg: mips_reg) { + unsafe { gum_sys::gum_mips_writer_put_mtlo_reg(self.writer, reg) } + } + + /// Write a break instruction. + pub fn put_break(&self) { + unsafe { gum_sys::gum_mips_writer_put_break(self.writer) } + } + + /// Write a prologue trampoline. + pub fn put_prologue_trampoline(&self, reg: mips_reg, address: u64) { + unsafe { gum_sys::gum_mips_writer_put_prologue_trampoline(self.writer, reg, address) } + } + + /// Write a raw instruction word. + pub fn put_instruction(&self, insn: u32) { + unsafe { gum_sys::gum_mips_writer_put_instruction(self.writer, insn) } + } +} + +#[cfg(any(target_arch = "mips", target_arch = "mips64"))] +impl Drop for MipsInstructionWriter { + fn drop(&mut self) { + if self.is_from_new { + unsafe { gum_sys::gum_mips_writer_unref(self.writer) } + } + } +} diff --git a/frida-gum/src/instruction_writer/x86_64/writer.rs b/frida-gum/src/instruction_writer/x86_64/writer.rs index dcf90225..4866d197 100644 --- a/frida-gum/src/instruction_writer/x86_64/writer.rs +++ b/frida-gum/src/instruction_writer/x86_64/writer.rs @@ -1,5 +1,8 @@ use { - crate::instruction_writer::{Argument, InstructionWriter, X86BranchCondition, X86Register}, + crate::{ + NativePointer, + instruction_writer::{Argument, InstructionWriter, X86BranchCondition, X86Register}, + }, core::ffi::c_void, frida_gum_sys as gum_sys, gum_sys::{GumArgument, GumBranchHint, gssize}, @@ -80,44 +83,78 @@ impl X86InstructionWriter { self.writer } - pub fn put_leave(&self) -> bool { + /// Clear (free) the writer's internal state without deallocating the + /// writer struct itself. + pub fn clear(&self) { + unsafe { gum_sys::gum_x86_writer_clear(self.writer) }; + } + + /// Get a pointer to the writer's current write cursor. + pub fn cur(&self) -> NativePointer { + NativePointer(unsafe { gum_sys::gum_x86_writer_cur(self.writer) }) + } + + /// Get the writer's byte offset from the original code address. + pub fn offset(&self) -> u32 { + unsafe { gum_sys::gum_x86_writer_offset(self.writer) } + } + + /// Set the target CPU type (IA32, AMD64, etc). + pub fn set_target_cpu(&self, cpu_type: gum_sys::GumCpuType) { + unsafe { gum_sys::gum_x86_writer_set_target_cpu(self.writer, cpu_type) }; + } + + /// Set the target ABI (Unix or Windows). This affects how + /// `put_call_*_with_arguments` lays out arguments. + pub fn set_target_abi(&self, abi_type: gum_sys::GumAbiType) { + unsafe { gum_sys::gum_x86_writer_set_target_abi(self.writer, abi_type) }; + } + + /// Get the register that holds the n-th argument of a function call + /// under the writer's current ABI / CPU configuration. + /// + /// Returns `None` if Frida reports a register that is unknown to this + /// binding (typically an indication that the writer has not been + /// configured with a target ABI). + pub fn get_cpu_register_for_nth_argument(&self, n: u32) -> Option { + let raw = + unsafe { gum_sys::gum_x86_writer_get_cpu_register_for_nth_argument(self.writer, n) }; + num::FromPrimitive::from_u32(raw as u32) + } + + pub fn put_leave(&self) { unsafe { gum_sys::gum_x86_writer_put_leave(self.writer); } - true } - pub fn put_ret(&self) -> bool { + pub fn put_ret(&self) { unsafe { gum_sys::gum_x86_writer_put_ret(self.writer); } - true } - pub fn put_ret_imm(&self, imm: u16) -> bool { + pub fn put_ret_imm(&self, imm: u16) { unsafe { gum_sys::gum_x86_writer_put_ret_imm(self.writer, imm); } - true } pub fn put_jmp_address(&self, address: u64) -> bool { unsafe { gum_sys::gum_x86_writer_put_jmp_address(self.writer, address) != 0 } } - pub fn put_jmp_short_label(&self, id: u64) -> bool { + pub fn put_jmp_short_label(&self, id: u64) { unsafe { gum_sys::gum_x86_writer_put_jmp_short_label(self.writer, id as *const c_void); } - true } /// Insert a `jmp` near to a label. The label is specified by `id`. - pub fn put_jmp_near_label(&self, id: u64) -> bool { + pub fn put_jmp_near_label(&self, id: u64) { unsafe { gum_sys::gum_x86_writer_put_jmp_near_label(self.writer, id as *const c_void); } - true } pub fn put_jmp_reg(&self, reg: X86Register) -> bool { @@ -267,18 +304,16 @@ impl X86InstructionWriter { unsafe { gum_sys::gum_x86_writer_put_mov_reg_u64(self.writer, dst_reg as u32, imm) != 0 } } - pub fn put_mov_reg_address(&self, dst_reg: X86Register, address: u64) -> bool { + pub fn put_mov_reg_address(&self, dst_reg: X86Register, address: u64) { unsafe { gum_sys::gum_x86_writer_put_mov_reg_address(self.writer, dst_reg as u32, address); } - true } - pub fn put_mov_reg_ptr_u32(&self, dst_reg: X86Register, imm: u32) -> bool { + pub fn put_mov_reg_ptr_u32(&self, dst_reg: X86Register, imm: u32) { unsafe { gum_sys::gum_x86_writer_put_mov_reg_ptr_u32(self.writer, dst_reg as u32, imm); } - true } pub fn put_mov_reg_offset_ptr_u32( @@ -297,7 +332,7 @@ impl X86InstructionWriter { } } - pub fn put_mov_reg_ptr_reg(&self, dst_reg: X86Register, src_reg: X86Register) -> bool { + pub fn put_mov_reg_ptr_reg(&self, dst_reg: X86Register, src_reg: X86Register) { unsafe { gum_sys::gum_x86_writer_put_mov_reg_ptr_reg( self.writer, @@ -305,7 +340,6 @@ impl X86InstructionWriter { src_reg as u32, ); } - true } pub fn put_mov_reg_offset_ptr_reg( @@ -324,11 +358,10 @@ impl X86InstructionWriter { } } - pub fn put_mov_reg_reg_ptr(&self, dst_reg: X86Register, src_reg: X86Register) -> bool { + pub fn put_mov_reg_reg_ptr(&self, dst_reg: X86Register, src_reg: X86Register) { unsafe { gum_sys::gum_x86_writer_put_mov_reg_reg_ptr(self.writer, dst_reg as u32, src_reg as u32) } - true } pub fn put_mov_reg_reg_offset_ptr( @@ -384,11 +417,10 @@ impl X86InstructionWriter { } } - pub fn put_push_u32(&self, imm: u32) -> bool { + pub fn put_push_u32(&self, imm: u32) { unsafe { gum_sys::gum_x86_writer_put_push_u32(self.writer, imm); } - true } pub fn put_push_near_ptr(&self, address: u64) -> bool { @@ -413,31 +445,59 @@ impl X86InstructionWriter { #[allow(clippy::useless_conversion)] pub fn put_call_address_with_arguments(&self, address: u64, arguments: &[Argument]) -> bool { unsafe { + #[cfg(target_os = "windows")] let arguments: Vec = arguments .iter() .map(|argument| match argument { Argument::Register(register) => GumArgument { - type_: gum_sys::_GumArgType_GUM_ARG_REGISTER.try_into().unwrap(), + type_: gum_sys::_GumArgType_GUM_ARG_REGISTER as u32, value: gum_sys::_GumArgument__bindgen_ty_1 { reg: *register as i32, }, }, Argument::Address(address) => GumArgument { - type_: gum_sys::_GumArgType_GUM_ARG_ADDRESS.try_into().unwrap(), + type_: gum_sys::_GumArgType_GUM_ARG_ADDRESS as u32, value: gum_sys::_GumArgument__bindgen_ty_1 { address: *address }, }, }) .collect(); - gum_sys::gum_x86_writer_put_call_address_with_arguments_array( + #[cfg(not(target_os = "windows"))] + let arguments: Vec = arguments + .iter() + .map(|argument| match argument { + Argument::Register(register) => GumArgument { + type_: gum_sys::_GumArgType_GUM_ARG_REGISTER, + value: gum_sys::_GumArgument__bindgen_ty_1 { + reg: *register as i32, + }, + }, + Argument::Address(address) => GumArgument { + type_: gum_sys::_GumArgType_GUM_ARG_ADDRESS, + value: gum_sys::_GumArgument__bindgen_ty_1 { address: *address }, + }, + }) + .collect(); + + #[cfg(target_os = "windows")] + let result = gum_sys::gum_x86_writer_put_call_address_with_arguments_array( self.writer, - gum_sys::_GumCallingConvention_GUM_CALL_CAPI - .try_into() - .unwrap(), + gum_sys::_GumCallingConvention_GUM_CALL_CAPI as u32, address, arguments.len() as u32, arguments.as_ptr(), - ) != 0 + ) != 0; + + #[cfg(not(target_os = "windows"))] + let result = gum_sys::gum_x86_writer_put_call_address_with_arguments_array( + self.writer, + gum_sys::_GumCallingConvention_GUM_CALL_CAPI, + address, + arguments.len() as u32, + arguments.as_ptr(), + ) != 0; + + result } } @@ -448,31 +508,59 @@ impl X86InstructionWriter { arguments: &[Argument], ) -> bool { unsafe { + #[cfg(target_os = "windows")] let arguments: Vec = arguments .iter() .map(|argument| match argument { Argument::Register(register) => GumArgument { - type_: gum_sys::_GumArgType_GUM_ARG_REGISTER.try_into().unwrap(), + type_: gum_sys::_GumArgType_GUM_ARG_REGISTER as u32, value: gum_sys::_GumArgument__bindgen_ty_1 { reg: *register as i32, }, }, Argument::Address(address) => GumArgument { - type_: gum_sys::_GumArgType_GUM_ARG_ADDRESS.try_into().unwrap(), + type_: gum_sys::_GumArgType_GUM_ARG_ADDRESS as u32, value: gum_sys::_GumArgument__bindgen_ty_1 { address: *address }, }, }) .collect(); - gum_sys::gum_x86_writer_put_call_address_with_aligned_arguments_array( + #[cfg(not(target_os = "windows"))] + let arguments: Vec = arguments + .iter() + .map(|argument| match argument { + Argument::Register(register) => GumArgument { + type_: gum_sys::_GumArgType_GUM_ARG_REGISTER, + value: gum_sys::_GumArgument__bindgen_ty_1 { + reg: *register as i32, + }, + }, + Argument::Address(address) => GumArgument { + type_: gum_sys::_GumArgType_GUM_ARG_ADDRESS, + value: gum_sys::_GumArgument__bindgen_ty_1 { address: *address }, + }, + }) + .collect(); + + #[cfg(target_os = "windows")] + let result = gum_sys::gum_x86_writer_put_call_address_with_aligned_arguments_array( self.writer, - gum_sys::_GumCallingConvention_GUM_CALL_CAPI - .try_into() - .unwrap(), + gum_sys::_GumCallingConvention_GUM_CALL_CAPI as u32, address, arguments.len() as u32, arguments.as_ptr(), - ) != 0 + ) != 0; + + #[cfg(not(target_os = "windows"))] + let result = gum_sys::gum_x86_writer_put_call_address_with_aligned_arguments_array( + self.writer, + gum_sys::_GumCallingConvention_GUM_CALL_CAPI, + address, + arguments.len() as u32, + arguments.as_ptr(), + ) != 0; + + result } } @@ -481,39 +569,875 @@ impl X86InstructionWriter { gum_sys::gum_x86_writer_put_test_reg_reg(self.writer, reg_a as u32, reg_b as u32) != 0 } } - pub fn put_nop(&self) -> bool { + pub fn put_nop(&self) { unsafe { gum_sys::gum_x86_writer_put_nop(self.writer); } - true } - pub fn put_pushfx(&self) -> bool { + pub fn put_pushfx(&self) { unsafe { gum_sys::gum_x86_writer_put_pushfx(self.writer); } - true } - pub fn put_popfx(&self) -> bool { + pub fn put_popfx(&self) { unsafe { gum_sys::gum_x86_writer_put_popfx(self.writer); } - true } - pub fn put_pushax(&self) -> bool { + pub fn put_pushax(&self) { unsafe { gum_sys::gum_x86_writer_put_pushax(self.writer); } - true } - pub fn put_popax(&self) -> bool { + pub fn put_popax(&self) { unsafe { gum_sys::gum_x86_writer_put_popax(self.writer); } - true + } + + /// Insert a breakpoint instruction (int3). + pub fn put_breakpoint(&self) { + unsafe { + gum_sys::gum_x86_writer_put_breakpoint(self.writer); + } + } + + /// Insert padding bytes (NOP instructions). + pub fn put_padding(&self, n: u32) { + unsafe { + gum_sys::gum_x86_writer_put_padding(self.writer, n); + } + } + + /// Insert NOP instructions with specific encoding for padding. + pub fn put_nop_padding(&self, n: u32) { + unsafe { + gum_sys::gum_x86_writer_put_nop_padding(self.writer, n); + } + } + + /// Insert a single unsigned 8-bit value. + pub fn put_u8(&self, value: u8) { + unsafe { + gum_sys::gum_x86_writer_put_u8(self.writer, value); + } + } + + /// Insert a single signed 8-bit value. + pub fn put_s8(&self, value: i8) { + unsafe { + gum_sys::gum_x86_writer_put_s8(self.writer, value); + } + } + + /// Insert a `call` indirect through an address. + pub fn put_call_indirect(&self, addr: u64) -> bool { + unsafe { gum_sys::gum_x86_writer_put_call_indirect(self.writer, addr) != 0 } + } + + /// Insert a `call` indirect through a label. + pub fn put_call_indirect_label(&self, label_id: u64) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_call_indirect_label(self.writer, label_id as *const c_void) + != 0 + } + } + + /// Insert a `call` near to a label. + pub fn put_call_near_label(&self, label_id: u64) { + unsafe { + gum_sys::gum_x86_writer_put_call_near_label(self.writer, label_id as *const c_void); + } + } + + /// Insert a `call` to a register. + pub fn put_call_reg(&self, reg: X86Register) -> bool { + unsafe { gum_sys::gum_x86_writer_put_call_reg(self.writer, reg as u32) != 0 } + } + + /// Insert a `call` to a register with offset. + pub fn put_call_reg_offset_ptr(&self, reg: X86Register, offset: isize) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_call_reg_offset_ptr( + self.writer, + reg as u32, + offset as gssize, + ) != 0 + } + } + + /// Insert a `call` to an address with arguments array. + /// + /// # Safety + /// + /// The `args` pointer must be valid for `n_args` elements. + pub unsafe fn put_call_address_with_arguments_array( + &self, + conv: gum_sys::GumCallingConvention, + func: u64, + n_args: u32, + args: *const GumArgument, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_call_address_with_arguments_array( + self.writer, + conv, + func, + n_args, + args, + ) != 0 + } + } + + /// Insert a `call` to an address with aligned arguments array. + /// + /// # Safety + /// + /// The `args` pointer must be valid for `n_args` elements. + pub unsafe fn put_call_address_with_aligned_arguments_array( + &self, + conv: gum_sys::GumCallingConvention, + func: u64, + n_args: u32, + args: *const GumArgument, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_call_address_with_aligned_arguments_array( + self.writer, + conv, + func, + n_args, + args, + ) != 0 + } + } + + /// Insert a `call` to a register with arguments. + pub fn put_call_reg_with_arguments(&self, reg: X86Register, arguments: &[Argument]) -> bool { + let gum_arguments = Self::convert_arguments(arguments); + unsafe { + #[cfg(target_os = "windows")] + let result = gum_sys::gum_x86_writer_put_call_reg_with_arguments( + self.writer, + gum_sys::_GumCallingConvention_GUM_CALL_CAPI as u32, + reg as u32, + gum_arguments.len() as u32, + gum_arguments.as_ptr(), + ) != 0; + + #[cfg(not(target_os = "windows"))] + let result = gum_sys::gum_x86_writer_put_call_reg_with_arguments( + self.writer, + gum_sys::_GumCallingConvention_GUM_CALL_CAPI, + reg as u32, + gum_arguments.len() as u32, + gum_arguments.as_ptr(), + ) != 0; + + result + } + } + + /// Insert a `call` to a register with arguments array. + /// + /// # Safety + /// + /// The `args` pointer must be valid for `n_args` elements. + pub unsafe fn put_call_reg_with_arguments_array( + &self, + conv: gum_sys::GumCallingConvention, + reg: u32, + n_args: u32, + args: *const GumArgument, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_call_reg_with_arguments_array( + self.writer, + conv, + reg, + n_args, + args, + ) != 0 + } + } + + /// Insert a `call` to a register with aligned arguments array. + /// + /// # Safety + /// + /// The `args` pointer must be valid for `n_args` elements. + pub unsafe fn put_call_reg_with_aligned_arguments_array( + &self, + conv: gum_sys::GumCallingConvention, + reg: u32, + n_args: u32, + args: *const GumArgument, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_call_reg_with_aligned_arguments_array( + self.writer, + conv, + reg, + n_args, + args, + ) != 0 + } + } + + /// Insert a `call` to a register offset pointer with arguments. + pub fn put_call_reg_offset_ptr_with_arguments( + &self, + reg: X86Register, + offset: isize, + arguments: &[Argument], + ) -> bool { + let gum_arguments = Self::convert_arguments(arguments); + unsafe { + #[cfg(target_os = "windows")] + let result = gum_sys::gum_x86_writer_put_call_reg_offset_ptr_with_arguments( + self.writer, + gum_sys::_GumCallingConvention_GUM_CALL_CAPI as u32, + reg as u32, + offset as gssize, + gum_arguments.len() as u32, + gum_arguments.as_ptr(), + ) != 0; + + #[cfg(not(target_os = "windows"))] + let result = gum_sys::gum_x86_writer_put_call_reg_offset_ptr_with_arguments( + self.writer, + gum_sys::_GumCallingConvention_GUM_CALL_CAPI, + reg as u32, + offset as gssize, + gum_arguments.len() as u32, + gum_arguments.as_ptr(), + ) != 0; + + result + } + } + + /// Insert a `call` to a register offset pointer with arguments array. + /// + /// # Safety + /// + /// The `args` pointer must be valid for `n_args` elements. + pub unsafe fn put_call_reg_offset_ptr_with_arguments_array( + &self, + conv: gum_sys::GumCallingConvention, + reg: u32, + offset: isize, + n_args: u32, + args: *const GumArgument, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_call_reg_offset_ptr_with_arguments_array( + self.writer, + conv, + reg, + offset as gssize, + n_args, + args, + ) != 0 + } + } + + /// Insert a `call` to a register offset pointer with aligned arguments. + pub fn put_call_reg_offset_ptr_with_aligned_arguments( + &self, + reg: X86Register, + offset: isize, + arguments: &[Argument], + ) -> bool { + let gum_arguments = Self::convert_arguments(arguments); + unsafe { + #[cfg(target_os = "windows")] + let result = gum_sys::gum_x86_writer_put_call_reg_offset_ptr_with_aligned_arguments( + self.writer, + gum_sys::_GumCallingConvention_GUM_CALL_CAPI as u32, + reg as u32, + offset as gssize, + gum_arguments.len() as u32, + gum_arguments.as_ptr(), + ) != 0; + + #[cfg(not(target_os = "windows"))] + let result = gum_sys::gum_x86_writer_put_call_reg_offset_ptr_with_aligned_arguments( + self.writer, + gum_sys::_GumCallingConvention_GUM_CALL_CAPI, + reg as u32, + offset as gssize, + gum_arguments.len() as u32, + gum_arguments.as_ptr(), + ) != 0; + + result + } + } + + /// Insert a `call` to a register offset pointer with aligned arguments array. + /// + /// # Safety + /// + /// The `args` pointer must be valid for `n_args` elements. + pub unsafe fn put_call_reg_offset_ptr_with_aligned_arguments_array( + &self, + conv: gum_sys::GumCallingConvention, + reg: u32, + offset: isize, + n_args: u32, + args: *const GumArgument, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_call_reg_offset_ptr_with_aligned_arguments_array( + self.writer, + conv, + reg, + offset as gssize, + n_args, + args, + ) != 0 + } + } + + /// Insert a `call` to a register with aligned arguments. + pub fn put_call_reg_with_aligned_arguments( + &self, + reg: X86Register, + arguments: &[Argument], + ) -> bool { + let gum_arguments = Self::convert_arguments(arguments); + unsafe { + #[cfg(target_os = "windows")] + let result = gum_sys::gum_x86_writer_put_call_reg_with_aligned_arguments( + self.writer, + gum_sys::_GumCallingConvention_GUM_CALL_CAPI as u32, + reg as u32, + gum_arguments.len() as u32, + gum_arguments.as_ptr(), + ) != 0; + + #[cfg(not(target_os = "windows"))] + let result = gum_sys::gum_x86_writer_put_call_reg_with_aligned_arguments( + self.writer, + gum_sys::_GumCallingConvention_GUM_CALL_CAPI, + reg as u32, + gum_arguments.len() as u32, + gum_arguments.as_ptr(), + ) != 0; + + result + } + } + + /// Insert a CLC (clear carry flag) instruction. + pub fn put_clc(&self) { + unsafe { + gum_sys::gum_x86_writer_put_clc(self.writer); + } + } + + /// Insert a STC (set carry flag) instruction. + pub fn put_stc(&self) { + unsafe { + gum_sys::gum_x86_writer_put_stc(self.writer); + } + } + + /// Insert a CLD (clear direction flag) instruction. + pub fn put_cld(&self) { + unsafe { + gum_sys::gum_x86_writer_put_cld(self.writer); + } + } + + /// Insert a STD (set direction flag) instruction. + pub fn put_std(&self) { + unsafe { + gum_sys::gum_x86_writer_put_std(self.writer); + } + } + + /// Insert a CPUID instruction. + pub fn put_cpuid(&self) { + unsafe { + gum_sys::gum_x86_writer_put_cpuid(self.writer); + } + } + + /// Insert a LFENCE (load fence) instruction. + pub fn put_lfence(&self) { + unsafe { + gum_sys::gum_x86_writer_put_lfence(self.writer); + } + } + + /// Insert an RDTSC (read time-stamp counter) instruction. + pub fn put_rdtsc(&self) { + unsafe { + gum_sys::gum_x86_writer_put_rdtsc(self.writer); + } + } + + /// Insert a PAUSE instruction. + pub fn put_pause(&self) { + unsafe { + gum_sys::gum_x86_writer_put_pause(self.writer); + } + } + + /// Insert a LAHF (load flags into AH) instruction. + pub fn put_lahf(&self) { + unsafe { + gum_sys::gum_x86_writer_put_lahf(self.writer); + } + } + + /// Insert a SAHF (store AH into flags) instruction. + pub fn put_sahf(&self) { + unsafe { + gum_sys::gum_x86_writer_put_sahf(self.writer); + } + } + + /// Insert a `cmp` immediate pointer with immediate u32. + pub fn put_cmp_imm_ptr_imm_u32(&self, imm_ptr: u64, imm_value: u32) { + unsafe { + gum_sys::gum_x86_writer_put_cmp_imm_ptr_imm_u32( + self.writer, + imm_ptr as *const c_void, + imm_value, + ); + } + } + + /// Insert a `cmp` register with immediate i32. + pub fn put_cmp_reg_i32(&self, reg: X86Register, imm_value: i32) -> bool { + unsafe { gum_sys::gum_x86_writer_put_cmp_reg_i32(self.writer, reg as u32, imm_value) != 0 } + } + + /// Insert a `cmp` register offset pointer with register. + pub fn put_cmp_reg_offset_ptr_reg( + &self, + reg_a: X86Register, + offset: isize, + reg_b: X86Register, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_cmp_reg_offset_ptr_reg( + self.writer, + reg_a as u32, + offset as gssize, + reg_b as u32, + ) != 0 + } + } + + /// Insert a `cmp` register with register. + pub fn put_cmp_reg_reg(&self, reg_a: X86Register, reg_b: X86Register) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_cmp_reg_reg(self.writer, reg_a as u32, reg_b as u32) != 0 + } + } + + /// Insert a `test` register with immediate u32. + pub fn put_test_reg_u32(&self, reg: X86Register, imm_value: u32) -> bool { + unsafe { gum_sys::gum_x86_writer_put_test_reg_u32(self.writer, reg as u32, imm_value) != 0 } + } + + /// Insert a conditional jump short to the given condition. + pub fn put_jcc_short( + &self, + condition: X86BranchCondition, + target: u64, + hint: GumBranchHint, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_jcc_short( + self.writer, + condition as _, + target as *const c_void, + hint, + ) != 0 + } + } + + /// Insert a conditional jump near to the given condition. + pub fn put_jcc_near( + &self, + condition: X86BranchCondition, + target: u64, + hint: GumBranchHint, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_jcc_near( + self.writer, + condition as _, + target as *const c_void, + hint, + ) != 0 + } + } + + /// Insert a `inc` to a register pointer. + /// + /// # Arguments + /// + /// * `target` - Pointer size: BYTE (0), DWORD (1), or QWORD (2) + /// * `reg` - Register to increment + pub fn put_inc_reg_ptr(&self, target: u32, reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_inc_reg_ptr(self.writer, target, reg as u32); + } + } + + /// Insert a `dec` to a register pointer. + /// + /// # Arguments + /// + /// * `target` - Pointer size: BYTE (0), DWORD (1), or QWORD (2) + /// * `reg` - Register to decrement + pub fn put_dec_reg_ptr(&self, target: u32, reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_dec_reg_ptr(self.writer, target, reg as u32); + } + } + + /// Insert a `lock inc` to an immediate 32-bit pointer. + pub fn put_lock_inc_imm32_ptr(&self, target: u64) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_lock_inc_imm32_ptr(self.writer, target as *mut c_void) != 0 + } + } + + /// Insert a `lock dec` to an immediate 32-bit pointer. + pub fn put_lock_dec_imm32_ptr(&self, target: u64) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_lock_dec_imm32_ptr(self.writer, target as *mut c_void) != 0 + } + } + + /// Insert a `lock cmpxchg` register pointer with register. + pub fn put_lock_cmpxchg_reg_ptr_reg(&self, dst_reg: X86Register, src_reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_lock_cmpxchg_reg_ptr_reg( + self.writer, + dst_reg as u32, + src_reg as u32, + ); + } + } + + /// Insert a `lock xadd` register pointer with register. + pub fn put_lock_xadd_reg_ptr_reg(&self, dst_reg: X86Register, src_reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_lock_xadd_reg_ptr_reg( + self.writer, + dst_reg as u32, + src_reg as u32, + ); + } + } + + /// Insert a `xchg` register with register pointer. + pub fn put_xchg_reg_reg_ptr(&self, left_reg: X86Register, right_reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_xchg_reg_reg_ptr( + self.writer, + left_reg as u32, + right_reg as u32, + ); + } + } + + /// Insert a `push` immediate pointer. + pub fn put_push_imm_ptr(&self, imm_ptr: u64) { + unsafe { + gum_sys::gum_x86_writer_put_push_imm_ptr(self.writer, imm_ptr as *const c_void); + } + } + + /// Insert a `mov` from near pointer to register. + pub fn put_mov_reg_near_ptr(&self, dst_reg: X86Register, src: u64) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_mov_reg_near_ptr(self.writer, dst_reg as u32, src) != 0 + } + } + + /// Insert a `mov` from register to near pointer. + pub fn put_mov_near_ptr_reg(&self, dst: u64, src_reg: X86Register) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_mov_near_ptr_reg(self.writer, dst, src_reg as u32) != 0 + } + } + + /// Insert a `mov` from FS segment register pointer to register. + pub fn put_mov_reg_fs_reg_ptr(&self, dst_reg: X86Register, src_reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_mov_reg_fs_reg_ptr( + self.writer, + dst_reg as u32, + src_reg as u32, + ); + } + } + + /// Insert a `mov` from FS segment u32 pointer to register. + pub fn put_mov_reg_fs_u32_ptr(&self, dst_reg: X86Register, fs_offset: u32) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_mov_reg_fs_u32_ptr(self.writer, dst_reg as u32, fs_offset) + != 0 + } + } + + /// Insert a `mov` from register to FS segment register pointer. + pub fn put_mov_fs_reg_ptr_reg(&self, dst_reg: X86Register, src_reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_mov_fs_reg_ptr_reg( + self.writer, + dst_reg as u32, + src_reg as u32, + ); + } + } + + /// Insert a `mov` from register to FS segment u32 pointer. + pub fn put_mov_fs_u32_ptr_reg(&self, fs_offset: u32, src_reg: X86Register) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_mov_fs_u32_ptr_reg(self.writer, fs_offset, src_reg as u32) + != 0 + } + } + + /// Insert a `mov` from GS segment register pointer to register. + pub fn put_mov_reg_gs_reg_ptr(&self, dst_reg: X86Register, src_reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_mov_reg_gs_reg_ptr( + self.writer, + dst_reg as u32, + src_reg as u32, + ); + } + } + + /// Insert a `mov` from register to GS segment register pointer. + pub fn put_mov_gs_reg_ptr_reg(&self, dst_reg: X86Register, src_reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_mov_gs_reg_ptr_reg( + self.writer, + dst_reg as u32, + src_reg as u32, + ); + } + } + + /// Insert a `mov` from register to GS segment u32 pointer. + pub fn put_mov_gs_u32_ptr_reg(&self, gs_offset: u32, src_reg: X86Register) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_mov_gs_u32_ptr_reg(self.writer, gs_offset, src_reg as u32) + != 0 + } + } + + /// Insert a `movdqu` from XMM0 to EAX offset pointer. + pub fn put_movdqu_eax_offset_ptr_xmm0(&self, offset: i8) { + unsafe { + gum_sys::gum_x86_writer_put_movdqu_eax_offset_ptr_xmm0(self.writer, offset); + } + } + + /// Insert a `movdqu` from ESP offset pointer to XMM0. + pub fn put_movdqu_xmm0_esp_offset_ptr(&self, offset: i8) { + unsafe { + gum_sys::gum_x86_writer_put_movdqu_xmm0_esp_offset_ptr(self.writer, offset); + } + } + + /// Insert a `movq` from XMM0 to EAX offset pointer. + pub fn put_movq_eax_offset_ptr_xmm0(&self, offset: i8) { + unsafe { + gum_sys::gum_x86_writer_put_movq_eax_offset_ptr_xmm0(self.writer, offset); + } + } + + /// Insert a `movq` from ESP offset pointer to XMM0. + pub fn put_movq_xmm0_esp_offset_ptr(&self, offset: i8) { + unsafe { + gum_sys::gum_x86_writer_put_movq_xmm0_esp_offset_ptr(self.writer, offset); + } + } + + /// Insert an `fxsave` to a register pointer. + pub fn put_fxsave_reg_ptr(&self, reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_fxsave_reg_ptr(self.writer, reg as u32); + } + } + + /// Insert an `fxrstor` from a register pointer. + pub fn put_fxrstor_reg_ptr(&self, reg: X86Register) { + unsafe { + gum_sys::gum_x86_writer_put_fxrstor_reg_ptr(self.writer, reg as u32); + } + } + + /// Insert a `kmovq` from k-register to register+offset pointer (AVX-512). + /// + /// Added in Frida 17.16.1. + pub fn put_kmovq_kreg_reg_offset_ptr( + &self, + kreg: u32, + reg: X86Register, + offset: isize, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_kmovq_kreg_reg_offset_ptr( + self.writer, + kreg, + reg as u32, + offset as gum_sys::gssize, + ) != 0 + } + } + + /// Insert a `kmovq` from register+offset pointer to k-register (AVX-512). + /// + /// Added in Frida 17.16.1. + pub fn put_kmovq_reg_offset_ptr_kreg( + &self, + reg: X86Register, + offset: isize, + kreg: u32, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_kmovq_reg_offset_ptr_kreg( + self.writer, + reg as u32, + offset as gum_sys::gssize, + kreg, + ) != 0 + } + } + + /// Insert a `vextracti64x4` from ZMM to register+offset pointer (AVX-512). + /// + /// Extracts 256 bits from a 512-bit ZMM register. + /// Added in Frida 17.16.1. + pub fn put_vextracti64x4_reg_offset_ptr_zmm( + &self, + reg: X86Register, + offset: isize, + zmm: u32, + imm: u8, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_vextracti64x4_reg_offset_ptr_zmm( + self.writer, + reg as u32, + offset as gum_sys::gssize, + zmm, + imm, + ) != 0 + } + } + + /// Insert a `vinserti64x4` from register+offset pointer to ZMM (AVX-512). + /// + /// Inserts 256 bits into a 512-bit ZMM register. + /// Added in Frida 17.16.1. + pub fn put_vinserti64x4_zmm_reg_offset_ptr( + &self, + zmm: u32, + reg: X86Register, + offset: isize, + imm: u8, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_vinserti64x4_zmm_reg_offset_ptr( + self.writer, + zmm, + reg as u32, + offset as gum_sys::gssize, + imm, + ) != 0 + } + } + + /// Insert a `vmovdqu64` from register+offset pointer to ZMM (AVX-512). + /// + /// Unaligned move of 512 bits. + /// Added in Frida 17.16.1. + pub fn put_vmovdqu64_reg_offset_ptr_zmm( + &self, + reg: X86Register, + offset: isize, + zmm: u32, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_vmovdqu64_reg_offset_ptr_zmm( + self.writer, + reg as u32, + offset as gum_sys::gssize, + zmm, + ) != 0 + } + } + + /// Insert a `vmovdqu64` from ZMM to register+offset pointer (AVX-512). + /// + /// Unaligned move of 512 bits. + /// Added in Frida 17.16.1. + pub fn put_vmovdqu64_zmm_reg_offset_ptr( + &self, + zmm: u32, + reg: X86Register, + offset: isize, + ) -> bool { + unsafe { + gum_sys::gum_x86_writer_put_vmovdqu64_zmm_reg_offset_ptr( + self.writer, + zmm, + reg as u32, + offset as gum_sys::gssize, + ) != 0 + } + } + + /// Helper to convert Argument slice to GumArgument Vec. + fn convert_arguments(arguments: &[Argument]) -> Vec { + #[cfg(target_os = "windows")] + return arguments + .iter() + .map(|arg| match arg { + Argument::Register(reg) => GumArgument { + type_: gum_sys::_GumArgType_GUM_ARG_REGISTER as u32, + value: gum_sys::_GumArgument__bindgen_ty_1 { reg: *reg as i32 }, + }, + Argument::Address(addr) => GumArgument { + type_: gum_sys::_GumArgType_GUM_ARG_ADDRESS as u32, + value: gum_sys::_GumArgument__bindgen_ty_1 { address: *addr }, + }, + }) + .collect(); + + #[cfg(not(target_os = "windows"))] + return arguments + .iter() + .map(|arg| match arg { + Argument::Register(reg) => GumArgument { + type_: gum_sys::_GumArgType_GUM_ARG_REGISTER, + value: gum_sys::_GumArgument__bindgen_ty_1 { reg: *reg as i32 }, + }, + Argument::Address(addr) => GumArgument { + type_: gum_sys::_GumArgType_GUM_ARG_ADDRESS, + value: gum_sys::_GumArgument__bindgen_ty_1 { address: *addr }, + }, + }) + .collect(); } } diff --git a/frida-gum/src/interceptor.rs b/frida-gum/src/interceptor.rs index ee617b91..1ad6f2a1 100644 --- a/frida-gum/src/interceptor.rs +++ b/frida-gum/src/interceptor.rs @@ -6,6 +6,7 @@ //! Function hooking engine. //! + use { crate::{Error, Gum, NativePointer, Result}, core::ptr, @@ -68,22 +69,35 @@ impl Interceptor { listener: &mut I, ) -> Result { let listener = invocation_listener_transform(listener); - match unsafe { - gum_sys::gum_interceptor_attach(self.interceptor, f.0, listener, ptr::null()) - } { - gum_sys::GumAttachReturn_GUM_ATTACH_OK => { - Ok(Listener(NativePointer(listener as *mut c_void))) - } - gum_sys::GumAttachReturn_GUM_ATTACH_WRONG_SIGNATURE => { - Err(Error::InterceptorBadSignature) - } - gum_sys::GumAttachReturn_GUM_ATTACH_ALREADY_ATTACHED => { - Err(Error::InterceptorAlreadyAttached) - } - gum_sys::GumAttachReturn_GUM_ATTACH_POLICY_VIOLATION => Err(Error::PolicyViolation), - gum_sys::GumAttachReturn_GUM_ATTACH_WRONG_TYPE => Err(Error::WrongType), - _ => Err(Error::InterceptorError), - } + let ret = unsafe { + gum_sys::gum_interceptor_attach(self.interceptor, f.0, listener, ptr::null_mut()) + }; + attach_return_to_result(ret, listener) + } + + /// Attach a listener to a function, supplying composable [`AttachOptions`]. + /// + /// This is the full form of [`Interceptor::attach`], exposing the + /// instrumentation knobs added in Frida 17.10 (scratch-register selection, + /// scenario, relocation policy, redirect space hint), per-listener data, + /// and invocation ignorability. + /// + /// # Safety + /// + /// The provided address *must* point to the start of a function in a valid + /// memory region. + #[cfg(feature = "invocation-listener")] + #[cfg_attr(docsrs, doc(cfg(feature = "invocation-listener")))] + pub unsafe fn attach_with_options( + &mut self, + f: NativePointer, + listener: &mut I, + options: &AttachOptions, + ) -> Result { + let listener = invocation_listener_transform(listener); + let raw = options.to_raw(); + let ret = unsafe { gum_sys::gum_interceptor_attach(self.interceptor, f.0, listener, &raw) }; + attach_return_to_result(ret, listener) } /// Attach a listener to an instruction address. @@ -99,22 +113,10 @@ impl Interceptor { listener: &mut I, ) -> Result { let listener = probe_listener_transform(listener); - match unsafe { - gum_sys::gum_interceptor_attach(self.interceptor, instr.0, listener, ptr::null()) - } { - gum_sys::GumAttachReturn_GUM_ATTACH_OK => { - Ok(Listener(NativePointer(listener as *mut c_void))) - } - gum_sys::GumAttachReturn_GUM_ATTACH_WRONG_SIGNATURE => { - Err(Error::InterceptorBadSignature) - } - gum_sys::GumAttachReturn_GUM_ATTACH_ALREADY_ATTACHED => { - Err(Error::InterceptorAlreadyAttached) - } - gum_sys::GumAttachReturn_GUM_ATTACH_POLICY_VIOLATION => Err(Error::PolicyViolation), - gum_sys::GumAttachReturn_GUM_ATTACH_WRONG_TYPE => Err(Error::WrongType), - _ => Err(Error::InterceptorError), - } + let ret = unsafe { + gum_sys::gum_interceptor_attach(self.interceptor, instr.0, listener, ptr::null_mut()) + }; + attach_return_to_result(ret, listener) } /// Detach an attached listener. @@ -145,16 +147,27 @@ impl Interceptor { &mut self, function: NativePointer, replacement: NativePointer, - _replacement_data: NativePointer, + replacement_data: NativePointer, ) -> Result { let mut original_function = NativePointer(ptr::null_mut()); unsafe { + let options = gum_sys::GumReplaceOptions { + instrumentation: gum_sys::GumInterceptorOptions { + scratch_register: -1, + scenario: gum_sys::GumInterceptorScenario_GUM_INTERCEPTOR_SCENARIO_DEFAULT, + relocation_policy: gum_sys::GumRelocationPolicy_GUM_RELOCATION_DEFAULT, + write_redirect: None, + write_redirect_data: ptr::null_mut(), + redirect_space_hint: 0, + }, + replacement_data: replacement_data.0, + }; match gum_sys::gum_interceptor_replace( self.interceptor, function.0, replacement.0, - ptr::addr_of_mut!(original_function.0), - ptr::null(), + &mut original_function.0, + &options, ) { gum_sys::GumReplaceReturn_GUM_REPLACE_OK => Ok(original_function), gum_sys::GumReplaceReturn_GUM_REPLACE_WRONG_SIGNATURE => { @@ -190,12 +203,20 @@ impl Interceptor { ) -> Result { let mut original_function = NativePointer(ptr::null_mut()); unsafe { + let options = gum_sys::GumInterceptorOptions { + scratch_register: -1, + scenario: gum_sys::GumInterceptorScenario_GUM_INTERCEPTOR_SCENARIO_DEFAULT, + relocation_policy: gum_sys::GumRelocationPolicy_GUM_RELOCATION_DEFAULT, + write_redirect: None, + write_redirect_data: ptr::null_mut(), + redirect_space_hint: 0, + }; match gum_sys::gum_interceptor_replace_fast( self.interceptor, function.0, replacement.0, - ptr::addr_of_mut!(original_function.0), - ptr::null(), + &mut original_function.0, + &options, ) { gum_sys::GumReplaceReturn_GUM_REPLACE_OK => Ok(original_function), gum_sys::GumReplaceReturn_GUM_REPLACE_WRONG_SIGNATURE => { @@ -237,6 +258,242 @@ impl Interceptor { InvocationContext::from_raw(unsafe { gum_sys::gum_interceptor_get_current_invocation() }) } + /// Retrieve the current thread's invocation stack. + /// + /// The returned pointer is only valid until the current hook returns; it + /// must not be retained or accessed from another thread. + /// + /// # Safety + /// + /// Must be called from within a hook or replacement function. The + /// returned pointer aliases Frida-owned memory whose lifetime is bounded + /// by the current invocation. + pub unsafe fn current_stack() -> *mut gum_sys::GumInvocationStack { + unsafe { gum_sys::gum_interceptor_get_current_stack() } + } + + /// Translate a return address inside Stalker- or Interceptor-generated + /// trampoline code back to its address in the original (pre-instrumented) + /// program. + /// + /// Returns the original address, or the input address unchanged if no + /// translation is available. + /// + /// # Safety + /// + /// `stack` must come from [`Self::current_stack`] or an equivalent live + /// invocation stack pointer. + pub unsafe fn translate( + stack: *mut gum_sys::GumInvocationStack, + return_address: NativePointer, + ) -> NativePointer { + unsafe { + NativePointer(gum_sys::gum_invocation_stack_translate( + stack, + return_address.0, + )) + } + } + + /// Ignore all calls from the current thread. + /// + /// This causes the interceptor to bypass any hooks installed on the current thread + /// until [`Interceptor::unignore_current_thread()`] is called. + pub fn ignore_current_thread(&mut self) { + unsafe { gum_sys::gum_interceptor_ignore_current_thread(self.interceptor) }; + } + + /// Resume intercepting calls from the current thread. + /// + /// Re-enables hook interception on the current thread after a call to + /// [`Interceptor::ignore_current_thread()`]. + pub fn unignore_current_thread(&mut self) { + unsafe { gum_sys::gum_interceptor_unignore_current_thread(self.interceptor) }; + } + + /// Conditionally resume intercepting calls from the current thread. + /// + /// Re-enables hook interception only if the current ignore count is 1 (i.e., only one + /// ignore is active). This is useful when multiple components may be ignoring threads. + /// + /// Returns `true` if the thread was unignored, `false` otherwise. + pub fn maybe_unignore_current_thread(&mut self) -> bool { + unsafe { gum_sys::gum_interceptor_maybe_unignore_current_thread(self.interceptor) != 0 } + } + + /// Ignore all calls from threads other than the current thread. + /// + /// This causes the interceptor to only process hooks on the current thread until + /// [`Interceptor::unignore_other_threads()`] is called. + pub fn ignore_other_threads(&mut self) { + unsafe { gum_sys::gum_interceptor_ignore_other_threads(self.interceptor) }; + } + + /// Resume intercepting calls from all threads. + /// + /// Re-enables hook interception on all threads after a call to + /// [`Interceptor::ignore_other_threads()`]. + pub fn unignore_other_threads(&mut self) { + unsafe { gum_sys::gum_interceptor_unignore_other_threads(self.interceptor) }; + } + + /// Check if the interceptor is currently locked. + /// + /// Returns `true` if the interceptor's internal lock is held, indicating that + /// modifications to hooks are currently prohibited. + pub fn is_locked(&self) -> bool { + unsafe { gum_sys::gum_interceptor_is_locked(self.interceptor) != 0 } + } + + /// Execute a function while holding the interceptor's lock. + /// + /// This ensures exclusive access to the interceptor's internal state during the + /// callback execution. + /// + /// # Safety + /// + /// The callback should not attempt to recursively acquire the lock or perform + /// operations that could deadlock. + pub fn with_lock_held(&mut self, f: F) + where + F: FnOnce(), + { + #[cfg(not(feature = "std"))] + use alloc::boxed::Box; + #[cfg(feature = "std")] + use std::boxed::Box; + + unsafe extern "C" fn trampoline(user_data: gum_sys::gpointer) + where + F: FnOnce(), + { + unsafe { + let callback = Box::from_raw(user_data as *mut F); + callback(); + } + } + + let callback = Box::new(f); + unsafe { + gum_sys::gum_interceptor_with_lock_held( + self.interceptor, + Some(trampoline::), + Box::into_raw(callback) as *mut _, + ) + }; + } + + /// Flush any pending interceptor operations. + /// + /// Forces the interceptor to immediately apply any pending hook modifications. + /// Returns `true` if any modifications were flushed, `false` otherwise. + pub fn flush(&mut self) -> bool { + unsafe { gum_sys::gum_interceptor_flush(self.interceptor) != 0 } + } + + /// Flush pending operations for a single function only. + /// + /// Like [`Interceptor::flush`], but limited to the hooks affecting the + /// function at `function_address`. Returns `true` if any modifications were + /// flushed. + /// + /// # Safety + /// + /// `function_address` must point to a function that has had a hook applied + /// via this interceptor. + pub unsafe fn flush_function(&mut self, function_address: NativePointer) -> bool { + unsafe { + gum_sys::gum_interceptor_flush_function(self.interceptor, function_address.0) != 0 + } + } + + /// Flush pending operations for a single listener only. + /// + /// Like [`Interceptor::flush`], but limited to the hooks owned by the given + /// `listener`. Returns `true` if any modifications were flushed. + #[cfg(feature = "invocation-listener")] + #[cfg_attr(docsrs, doc(cfg(feature = "invocation-listener")))] + pub fn flush_listener(&mut self, listener: &Listener) -> bool { + let Listener(NativePointer(ptr)) = listener; + unsafe { + gum_sys::gum_interceptor_flush_listener( + self.interceptor, + *ptr as *mut gum_sys::GumInvocationListener, + ) != 0 + } + } + + /// Set default options for interceptor operations. + /// + /// Configures global default options that will be used for subsequent attach and + /// replace operations when no explicit options are provided. + /// + /// # Arguments + /// + /// * `scratch_register` - Register to use for temporary operations (-1 for auto) + /// * `scenario` - Interceptor scenario (DEFAULT, ONLINE, OFFLINE) + /// * `relocation_policy` - Code relocation strategy + pub fn set_default_options( + &mut self, + scratch_register: i32, + scenario: gum_sys::GumInterceptorScenario, + relocation_policy: gum_sys::GumRelocationPolicy, + ) { + let options = gum_sys::GumInterceptorOptions { + scratch_register, + scenario, + relocation_policy, + write_redirect: None, + write_redirect_data: ptr::null_mut(), + redirect_space_hint: 0, + }; + unsafe { + gum_sys::gum_interceptor_set_default_options(self.interceptor, &options); + } + } + + /// Detect the minimum size needed to hook a function at the given address. + /// + /// Analyzes the code at the specified address to determine how many bytes are + /// needed to safely install a hook without breaking instruction boundaries. + /// + /// Returns the size in bytes, or 0 if the code cannot be safely hooked. + /// + /// # Safety + /// + /// `address` must point to readable, valid executable code. + pub unsafe fn detect_hook_size(address: NativePointer) -> usize { + unsafe { gum_sys::gum_interceptor_detect_hook_size(address.0, 0, ptr::null_mut()) as usize } + } + + /// Save the current invocation state into `state`. + /// + /// Captures the current interceptor invocation state so it can be restored + /// with [`Interceptor::restore()`]. Useful when temporarily suspending + /// interception. + /// + /// # Safety + /// + /// Must be called from within a hook. `state` must remain accessible (and + /// not move) until [`Interceptor::restore`] is called on the same thread. + pub unsafe fn save(state: &mut gum_sys::GumInvocationState) { + unsafe { + gum_sys::gum_interceptor_save(state); + } + } + + /// Restore a previously saved invocation state. + /// + /// # Safety + /// + /// `state` must have been populated by [`Interceptor::save`] on the same + /// thread, and the corresponding hook must still be on the call stack. + pub unsafe fn restore(state: &mut gum_sys::GumInvocationState) { + unsafe { + gum_sys::gum_interceptor_restore(state); + } + } + /// Begin an [`Interceptor`] transaction. This may improve performance if /// applying many hooks. /// @@ -271,3 +528,131 @@ impl Clone for Listener { Self(NativePointer(unsafe { frida_gum_sys::g_object_ref(*ptr) })) } } + +/// Map a raw `GumAttachReturn` to a [`Result`], wrapping the listener on success. +#[cfg(feature = "invocation-listener")] +fn attach_return_to_result( + ret: gum_sys::GumAttachReturn, + listener: *mut gum_sys::GumInvocationListener, +) -> Result { + match ret { + gum_sys::GumAttachReturn_GUM_ATTACH_OK => { + Ok(Listener(NativePointer(listener as *mut c_void))) + } + gum_sys::GumAttachReturn_GUM_ATTACH_WRONG_SIGNATURE => Err(Error::InterceptorBadSignature), + gum_sys::GumAttachReturn_GUM_ATTACH_ALREADY_ATTACHED => { + Err(Error::InterceptorAlreadyAttached) + } + gum_sys::GumAttachReturn_GUM_ATTACH_POLICY_VIOLATION => Err(Error::PolicyViolation), + gum_sys::GumAttachReturn_GUM_ATTACH_WRONG_TYPE => Err(Error::WrongType), + _ => Err(Error::InterceptorError), + } +} + +/// Whether an attached hook may be ignored on the current thread. +#[cfg(feature = "invocation-listener")] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +pub enum Ignorability { + /// The hook honours `ignore_current_thread` (the default). + #[default] + Ignorable, + /// The hook always fires, even on ignored threads. + Unignorable, +} + +/// Composable options for [`Interceptor::attach_with_options`]. +/// +/// Build with [`AttachOptions::new`] and chain the setters for the knobs you +/// need; unset fields use Frida's defaults. +#[cfg(feature = "invocation-listener")] +#[derive(Debug, Clone)] +pub struct AttachOptions { + scratch_register: i32, + scenario: gum_sys::GumInterceptorScenario, + relocation_policy: gum_sys::GumRelocationPolicy, + redirect_space_hint: u32, + listener_function_data: NativePointer, + ignorability: Ignorability, +} + +#[cfg(feature = "invocation-listener")] +impl Default for AttachOptions { + fn default() -> Self { + Self::new() + } +} + +#[cfg(feature = "invocation-listener")] +impl AttachOptions { + /// Create attach options with Frida's defaults. + pub fn new() -> Self { + Self { + scratch_register: -1, + scenario: gum_sys::GumInterceptorScenario_GUM_INTERCEPTOR_SCENARIO_DEFAULT, + relocation_policy: gum_sys::GumRelocationPolicy_GUM_RELOCATION_DEFAULT, + redirect_space_hint: 0, + listener_function_data: NativePointer(core::ptr::null_mut()), + ignorability: Ignorability::Ignorable, + } + } + + /// Set the scratch register to use for the hook (`-1` selects automatically). + pub fn scratch_register(mut self, register: i32) -> Self { + self.scratch_register = register; + self + } + + /// Set the interceptor scenario (`DEFAULT`, `ONLINE`, `OFFLINE`). + pub fn scenario(mut self, scenario: gum_sys::GumInterceptorScenario) -> Self { + self.scenario = scenario; + self + } + + /// Set the code relocation policy. + pub fn relocation_policy(mut self, policy: gum_sys::GumRelocationPolicy) -> Self { + self.relocation_policy = policy; + self + } + + /// Hint, in bytes, of how much space to reserve for the redirect. + pub fn redirect_space_hint(mut self, hint: u32) -> Self { + self.redirect_space_hint = hint; + self + } + + /// Set the per-listener function data pointer, retrievable from the + /// invocation context as listener function data. + pub fn listener_function_data(mut self, data: NativePointer) -> Self { + self.listener_function_data = data; + self + } + + /// Set whether the hook may be ignored on the current thread. + pub fn ignorability(mut self, ignorability: Ignorability) -> Self { + self.ignorability = ignorability; + self + } + + /// Build the raw `GumAttachOptions` consumed by the C API. + fn to_raw(&self) -> gum_sys::GumAttachOptions { + gum_sys::GumAttachOptions { + instrumentation: gum_sys::GumInterceptorOptions { + scratch_register: self.scratch_register, + scenario: self.scenario, + relocation_policy: self.relocation_policy, + write_redirect: None, + write_redirect_data: ptr::null_mut(), + redirect_space_hint: self.redirect_space_hint, + }, + listener_function_data: self.listener_function_data.0, + ignorability: match self.ignorability { + Ignorability::Ignorable => { + gum_sys::GumInvocationIgnorability_GUM_INVOCATION_IGNORABLE + } + Ignorability::Unignorable => { + gum_sys::GumInvocationIgnorability_GUM_INVOCATION_UNIGNORABLE + } + }, + } + } +} diff --git a/frida-gum/src/interceptor/invocation_listener.rs b/frida-gum/src/interceptor/invocation_listener.rs index 123cc4d3..701c6e8a 100644 --- a/frida-gum/src/interceptor/invocation_listener.rs +++ b/frida-gum/src/interceptor/invocation_listener.rs @@ -27,20 +27,16 @@ unsafe extern "C" fn call_on_enter( user_data: *mut c_void, context: *mut gum_sys::GumInvocationContext, ) { - unsafe { - let listener: &mut I = &mut *(user_data as *mut I); - listener.on_enter(InvocationContext::from_raw(context)); - } + let listener: &mut I = unsafe { &mut *(user_data as *mut I) }; + listener.on_enter(InvocationContext::from_raw(context)); } unsafe extern "C" fn call_on_leave( user_data: *mut c_void, context: *mut gum_sys::GumInvocationContext, ) { - unsafe { - let listener: &mut I = &mut *(user_data as *mut I); - listener.on_leave(InvocationContext::from_raw(context)); - } + let listener: &mut I = unsafe { &mut *(user_data as *mut I) }; + listener.on_leave(InvocationContext::from_raw(context)); } pub(crate) fn invocation_listener_transform( @@ -63,10 +59,8 @@ unsafe extern "C" fn call_on_hit( user_data: *mut c_void, context: *mut gum_sys::GumInvocationContext, ) { - unsafe { - let listener: &mut I = &mut *(user_data as *mut I); - listener.on_hit(InvocationContext::from_raw(context)); - } + let listener: &mut I = unsafe { &mut *(user_data as *mut I) }; + listener.on_hit(InvocationContext::from_raw(context)); } pub(crate) fn probe_listener_transform( @@ -162,6 +156,46 @@ impl<'a> InvocationContext<'a> { } } + /// Get the per-function listener data set via + /// [`crate::interceptor::AttachOptions::listener_function_data`]. + /// + /// Returns `None` if no function data was associated with this listener. + pub fn listener_function_data(&self) -> Option { + let data = + unsafe { gum_sys::gum_invocation_context_get_listener_function_data(self.context) }; + if data.is_null() { + None + } else { + Some(NativePointer(data)) + } + } + + /// Get scratch storage of `size` bytes that is private to this listener and + /// the calling thread, zero-initialised on first access. + /// + /// The returned pointer is valid for the duration of the invocation. + pub fn listener_thread_data(&self, size: usize) -> NativePointer { + NativePointer(unsafe { + gum_sys::gum_invocation_context_get_listener_thread_data( + self.context, + size as gum_sys::gsize, + ) + }) + } + + /// Get scratch storage of `size` bytes that is private to this listener and + /// this single invocation, shared between the enter and leave callbacks. + /// + /// The returned pointer is valid for the duration of the invocation. + pub fn listener_invocation_data(&self, size: usize) -> NativePointer { + NativePointer(unsafe { + gum_sys::gum_invocation_context_get_listener_invocation_data( + self.context, + size as gum_sys::gsize, + ) + }) + } + /// Get the thread ID of the currently executing function. pub fn thread_id(&self) -> u32 { unsafe { gum_sys::gum_invocation_context_get_thread_id(self.context) as u32 } diff --git a/frida-gum/src/kernel.rs b/frida-gum/src/kernel.rs new file mode 100644 index 00000000..6309ee68 --- /dev/null +++ b/frida-gum/src/kernel.rs @@ -0,0 +1,338 @@ +/* + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Kernel-mode memory operations and module enumeration. +//! +//! Provides APIs for interacting with kernel memory on supported platforms. +//! Availability can be checked with [`Kernel::api_is_available`]. + +use { + crate::{MemoryRange, NativePointer, PageProtection, glib_compat::g_free}, + core::ffi::c_void, + frida_gum_sys as gum_sys, +}; + +#[cfg(not(feature = "std"))] +use alloc::{boxed::Box, string::String, vec::Vec}; + +#[cfg(feature = "std")] +use std::ffi::CStr; + +#[cfg(not(feature = "std"))] +use core::ffi::CStr; + +/// Details about a kernel module. +#[derive(Clone, Debug)] +pub struct KernelModuleDetails { + pub name: String, + pub range: MemoryRange, + pub path: String, +} + +impl KernelModuleDetails { + unsafe fn from_raw(details: *const gum_sys::GumKernelModuleDetails) -> Self { + unsafe { + let range_ptr = (*details).range; + let range = MemoryRange::new( + NativePointer((*range_ptr).base_address as *mut c_void), + (*range_ptr).size as usize, + ); + Self { + name: CStr::from_ptr((*details).name) + .to_string_lossy() + .into_owned(), + range, + path: CStr::from_ptr((*details).path) + .to_string_lossy() + .into_owned(), + } + } + } +} + +extern "C" fn enumerate_modules_callout( + details: *const gum_sys::GumKernelModuleDetails, + user_data: *mut c_void, +) -> gum_sys::gboolean { + let mut f = + unsafe { Box::from_raw(user_data as *mut Box bool>) }; + let r = unsafe { f(KernelModuleDetails::from_raw(details)) }; + Box::leak(f); + r as gum_sys::gboolean +} + +extern "C" fn enumerate_ranges_callout( + details: *const gum_sys::_GumRangeDetails, + user_data: *mut c_void, +) -> gum_sys::gboolean { + let mut f = unsafe { Box::from_raw(user_data as *mut Box bool>) }; + let range = unsafe { + let range_ptr = (*details).range; + MemoryRange::new( + NativePointer((*range_ptr).base_address as *mut c_void), + (*range_ptr).size as usize, + ) + }; + let r = f(range); + Box::leak(f); + r as gum_sys::gboolean +} + +extern "C" fn enumerate_module_ranges_callout( + details: *const gum_sys::GumKernelModuleRangeDetails, + user_data: *mut c_void, +) -> gum_sys::gboolean { + let mut f = unsafe { Box::from_raw(user_data as *mut Box bool>) }; + let range = unsafe { + MemoryRange::new( + NativePointer((*details).address as *mut c_void), + (*details).size as usize, + ) + }; + let r = f(range); + Box::leak(f); + r as gum_sys::gboolean +} + +extern "C" fn scan_callout( + address: gum_sys::GumAddress, + size: gum_sys::gsize, + user_data: *mut c_void, +) -> gum_sys::gboolean { + let mut f = + unsafe { Box::from_raw(user_data as *mut Box bool>) }; + let r = f(NativePointer(address as *mut c_void), size as usize); + Box::leak(f); + r as gum_sys::gboolean +} + +/// Static interface to Frida's kernel memory operations. +pub struct Kernel; + +impl Kernel { + /// Check if kernel API is available on this platform. + /// + /// Returns `false` on platforms without kernel API support or + /// when running without sufficient privileges. + pub fn api_is_available() -> bool { + unsafe { gum_sys::gum_kernel_api_is_available() != 0 } + } + + /// Get the kernel page size. + pub fn query_page_size() -> u32 { + unsafe { gum_sys::gum_kernel_query_page_size() } + } + + /// Allocate kernel pages. + /// + /// # Safety + /// + /// This allocates memory in kernel space. Improper use can crash the system. + pub unsafe fn alloc_n_pages(n_pages: u32) -> NativePointer { + let addr = unsafe { gum_sys::gum_kernel_alloc_n_pages(n_pages) }; + NativePointer(addr as *mut c_void) + } + + /// Free kernel pages previously allocated with [`Kernel::alloc_n_pages`]. + /// + /// # Safety + /// + /// The address must have been returned by [`Kernel::alloc_n_pages`] + /// and must not have been freed already. + pub unsafe fn free_pages(mem: NativePointer) { + unsafe { gum_sys::gum_kernel_free_pages(mem.0 as gum_sys::GumAddress) }; + } + + /// Try to change protection on kernel memory. + /// + /// # Safety + /// + /// Modifying kernel memory protection can destabilize the system. + pub unsafe fn try_mprotect( + address: NativePointer, + size: usize, + protection: PageProtection, + ) -> bool { + unsafe { + gum_sys::gum_kernel_try_mprotect( + address.0 as gum_sys::GumAddress, + size as gum_sys::gsize, + protection as u32, + ) != 0 + } + } + + /// Read from kernel memory. + /// + /// Returns the bytes read, or `None` on failure. + /// + /// # Safety + /// + /// The address must point to valid kernel memory. + pub unsafe fn read(address: NativePointer, len: usize) -> Option> { + let mut n_read = 0usize; + let ptr = unsafe { + gum_sys::gum_kernel_read( + address.0 as gum_sys::GumAddress, + len as gum_sys::gsize, + &mut n_read as *mut usize as *mut gum_sys::gsize, + ) + }; + if ptr.is_null() { + None + } else { + let mut buf = Vec::with_capacity(n_read); + unsafe { + core::ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), n_read); + buf.set_len(n_read); + g_free(ptr as gum_sys::gpointer); + } + Some(buf) + } + } + + /// Write to kernel memory. + /// + /// # Safety + /// + /// The address must point to valid writable kernel memory. + pub unsafe fn write(address: NativePointer, bytes: &[u8]) -> bool { + unsafe { + gum_sys::gum_kernel_write( + address.0 as gum_sys::GumAddress, + bytes.as_ptr(), + bytes.len() as gum_sys::gsize, + ) != 0 + } + } + + /// Scan kernel memory for a pattern. + /// + /// Calls `callback` for each match with the address and size. + /// + /// # Safety + /// + /// The range must be valid kernel memory. + pub unsafe fn scan( + range: &MemoryRange, + pattern: &crate::memory_range::MatchPattern, + mut callback: F, + ) where + F: FnMut(NativePointer, usize) -> bool, + { + let gum_range = gum_sys::GumMemoryRange { + base_address: range.base_address().0 as gum_sys::GumAddress, + size: range.size() as gum_sys::gsize, + }; + + let callback: Box bool> = Box::new(&mut callback); + let callback = Box::into_raw(Box::new(callback)); + + unsafe { + gum_sys::gum_kernel_scan( + &gum_range, + pattern.internal, + Some(scan_callout), + callback as *mut c_void, + ); + drop(Box::from_raw(callback)); + } + } + + /// Enumerate kernel memory ranges matching the given protection. + pub fn enumerate_ranges(protection: PageProtection, mut callback: F) + where + F: FnMut(MemoryRange) -> bool, + { + let callback: Box bool> = Box::new(&mut callback); + let callback = Box::into_raw(Box::new(callback)); + unsafe { + gum_sys::gum_kernel_enumerate_ranges( + protection as u32, + Some(enumerate_ranges_callout), + callback as *mut c_void, + ); + drop(Box::from_raw(callback)); + } + } + + /// Enumerate kernel memory ranges belonging to a specific module. + pub fn enumerate_module_ranges( + module_name: &str, + protection: PageProtection, + mut callback: F, + ) where + F: FnMut(MemoryRange) -> bool, + { + use cstr_core::CString; + let module_cstr = match CString::new(module_name) { + Ok(s) => s, + Err(_) => return, + }; + + let callback: Box bool> = Box::new(&mut callback); + let callback = Box::into_raw(Box::new(callback)); + unsafe { + gum_sys::gum_kernel_enumerate_module_ranges( + module_cstr.as_ptr() as *const core::ffi::c_char, + protection as u32, + Some(enumerate_module_ranges_callout), + callback as *mut c_void, + ); + drop(Box::from_raw(callback)); + } + } + + /// Enumerate loaded kernel modules. + pub fn enumerate_modules(mut callback: F) + where + F: FnMut(KernelModuleDetails) -> bool, + { + let callback: Box bool> = Box::new(&mut callback); + let callback = Box::into_raw(Box::new(callback)); + unsafe { + gum_sys::gum_kernel_enumerate_modules( + Some(enumerate_modules_callout), + callback as *mut c_void, + ); + drop(Box::from_raw(callback)); + } + } + + /// Find the kernel base address. + /// + /// Returns 0 if not found. + pub fn find_base_address() -> u64 { + unsafe { gum_sys::gum_kernel_find_base_address() } + } + + /// Set the kernel base address manually. + /// + /// Useful when automatic detection doesn't work. + pub fn set_base_address(base: u64) { + unsafe { gum_sys::gum_kernel_set_base_address(base) }; + } + + /// Collect all kernel modules into a Vec. + pub fn modules() -> Vec { + let mut result = Vec::new(); + Self::enumerate_modules(|module| { + result.push(module); + true + }); + result + } + + /// Collect all kernel ranges with the given protection into a Vec. + pub fn ranges(protection: PageProtection) -> Vec { + let mut result = Vec::new(); + Self::enumerate_ranges(protection, |range| { + result.push(range); + true + }); + result + } +} diff --git a/frida-gum/src/lib.rs b/frida-gum/src/lib.rs index 781c0379..2ee8a98b 100644 --- a/frida-gum/src/lib.rs +++ b/frida-gum/src/lib.rs @@ -78,9 +78,16 @@ pub mod instruction_writer; mod module; pub use module::*; +pub mod elf_module; + mod module_map; pub use module_map::*; +#[cfg(target_os = "macos")] +mod darwin_module; +#[cfg(target_os = "macos")] +pub use darwin_module::*; + mod process; pub use process::*; @@ -95,9 +102,50 @@ mod memory_access_monitor; #[cfg(feature = "memory-access-monitor")] pub use memory_access_monitor::*; +mod memory; +pub use memory::Memory; + +mod kernel; +pub use kernel::*; + mod memory_range; pub use memory_range::*; +mod api_resolver; +pub use api_resolver::*; + +mod exceptor; +pub use exceptor::*; + +mod code_allocator; +pub use code_allocator::*; + +mod code_segment; +pub use code_segment::*; + +mod symbol_util; +pub use symbol_util::*; + +mod cloak; +pub use cloak::*; + +mod glib_compat; + +mod query; +pub use query::*; + +mod control_flow_graph; +pub use control_flow_graph::*; + +mod unwind_broker; +pub use unwind_broker::*; + +mod tls; +pub use tls::*; + +mod registry; +pub use registry::*; + mod range_details; pub use range_details::*; diff --git a/frida-gum/src/memory.rs b/frida-gum/src/memory.rs new file mode 100644 index 00000000..afb5a97b --- /dev/null +++ b/frida-gum/src/memory.rs @@ -0,0 +1,473 @@ +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Memory manipulation utilities. +//! +//! Provides functions for reading, writing, allocating, and patching memory. + +use { + crate::{MemoryRange, NativePointer, PageProtection}, + core::ffi::c_void, + frida_gum_sys as gum_sys, +}; + +#[cfg(not(feature = "std"))] +use alloc::{boxed::Box, vec::Vec}; + +#[cfg(feature = "std")] +use std::{boxed::Box, vec::Vec}; + +/// Memory allocation and manipulation utilities. +pub struct Memory; + +impl Memory { + /// Read memory from the specified address. + /// + /// Returns a `Vec` containing the read bytes, or `None` if the address + /// is unreadable. The returned `Vec` may be shorter than `size` if the + /// read terminated early at an unreadable boundary. + /// + /// # Safety + /// + /// `address` must be a value the caller believes points to memory; the + /// underlying probe is fault-tolerant but undefined memory layouts can + /// still cause crashes on some platforms. + pub unsafe fn read(address: NativePointer, size: usize) -> Option> { + unsafe { + let mut n_read: gum_sys::gsize = 0; + let buf = gum_sys::gum_memory_read(address.0, size as gum_sys::gsize, &mut n_read); + + if buf.is_null() { + return None; + } + + let n = n_read as usize; + let slice = core::slice::from_raw_parts(buf, n); + let owned = slice.to_vec(); + crate::glib_compat::g_free(buf as *mut c_void); + Some(owned) + } + } + + /// Write data to the specified address. + /// + /// Returns true if the write succeeded. + /// + /// # Safety + /// + /// The address must point to valid, writable memory of at least `data.len()` bytes. + pub unsafe fn write(address: NativePointer, data: &[u8]) -> bool { + unsafe { + gum_sys::gum_memory_write( + address.0, + data.as_ptr() as *const _, + data.len() as gum_sys::gsize, + ) != 0 + } + } + + /// Allocate memory with the specified size, alignment, and protection. + /// + /// `alignment` must be a power of two; pass `1` if no specific alignment + /// is required. Returns the address of the allocated memory, or `None` + /// if allocation fails. + pub fn allocate( + size: usize, + alignment: usize, + protection: PageProtection, + ) -> Option { + let ptr = unsafe { + gum_sys::gum_memory_allocate( + core::ptr::null_mut(), + size as gum_sys::gsize, + alignment as gum_sys::gsize, + protection as u32, + ) + }; + + if ptr.is_null() { + None + } else { + Some(NativePointer(ptr)) + } + } + + /// Allocate memory within `max_distance` bytes of `near`. + /// + /// Useful for position-dependent code that requires the new region to be + /// reachable by a short branch from the target address. + /// + /// # Arguments + /// + /// * `near` - Target address to allocate near. + /// * `max_distance` - Maximum distance in bytes; e.g. `i32::MAX as usize` + /// for x86 short branches. + /// * `size` - Size in bytes to allocate. + /// * `alignment` - Alignment requirement (power of two). + /// * `protection` - Memory protection flags. + pub fn allocate_near( + near: NativePointer, + max_distance: usize, + size: usize, + alignment: usize, + protection: PageProtection, + ) -> Option { + let spec = gum_sys::GumAddressSpec { + near_address: near.0, + max_distance: max_distance as gum_sys::gsize, + }; + + let ptr = unsafe { + gum_sys::gum_memory_allocate_near( + &spec, + size as gum_sys::gsize, + alignment as gum_sys::gsize, + protection as u32, + ) + }; + + if ptr.is_null() { + None + } else { + Some(NativePointer(ptr)) + } + } + + /// Free previously allocated memory. + /// + /// Returns true if the free succeeded. + /// + /// # Safety + /// + /// The address must have been allocated with [`Memory::allocate()`] or + /// [`Memory::allocate_near()`], and must not have been freed already. + pub unsafe fn free(address: NativePointer, size: usize) -> bool { + unsafe { gum_sys::gum_memory_free(address.0, size as gum_sys::gsize) != 0 } + } + + /// Release memory back to the system. + /// + /// Like free, but may keep the virtual address space reserved. + /// + /// # Safety + /// + /// The address and size must correspond to a previously allocated region. + pub unsafe fn release(address: NativePointer, size: usize) -> bool { + unsafe { gum_sys::gum_memory_release(address.0, size as gum_sys::gsize) != 0 } + } + + /// Decommit memory pages. + /// + /// Releases the physical memory backing the pages while keeping the virtual + /// address space reserved. + /// + /// # Safety + /// + /// The address and size must correspond to a previously allocated region. + pub unsafe fn decommit(address: NativePointer, size: usize) -> bool { + unsafe { gum_sys::gum_memory_decommit(address.0, size as gum_sys::gsize) != 0 } + } + + /// Recommit previously decommitted memory pages. + /// + /// # Safety + /// + /// The address and size must correspond to a previously decommitted region. + pub unsafe fn recommit( + address: NativePointer, + size: usize, + protection: PageProtection, + ) -> bool { + unsafe { + gum_sys::gum_memory_recommit(address.0, size as gum_sys::gsize, protection as u32) != 0 + } + } + + /// Discard the contents of memory pages. + /// + /// Hints to the OS that the pages' contents can be discarded, potentially + /// improving performance. + /// + /// # Safety + /// + /// The address and size must point to valid memory. + pub unsafe fn discard(address: NativePointer, size: usize) -> bool { + unsafe { gum_sys::gum_memory_discard(address.0, size as gum_sys::gsize) != 0 } + } + + /// Query the protection flags for a memory region. + /// + /// # Safety + /// + /// The address must point to valid memory. + pub unsafe fn query_protection(address: NativePointer) -> Option { + unsafe { + let mut prot: gum_sys::GumPageProtection = 0; + if gum_sys::gum_memory_query_protection(address.0, &mut prot) != 0 { + num::FromPrimitive::from_u32(prot) + } else { + None + } + } + } + + /// Check if memory at the specified address is readable. + /// + /// # Safety + /// + /// The address should be a valid pointer. + pub unsafe fn is_readable(address: NativePointer, size: usize) -> bool { + unsafe { gum_sys::gum_memory_is_readable(address.0, size as gum_sys::gsize) != 0 } + } + + /// Mark a memory region as executable code. + /// + /// This is a hint to the system that the region contains executable code, + /// which may enable certain optimizations. + /// + /// Returns true if successful. + /// + /// # Safety + /// + /// The address and size must point to valid memory with appropriate permissions. + pub unsafe fn mark_code(address: NativePointer, size: usize) -> bool { + unsafe { gum_sys::gum_memory_mark_code(address.0, size as gum_sys::gsize) != 0 } + } + + /// Change the protection of a memory region, aborting on failure. + /// + /// Use [`Memory::try_mprotect`] if you need to handle failure gracefully. + /// + /// # Safety + /// + /// `address`/`size` must describe a region of mapped pages owned by the + /// caller; changing protection on memory in use elsewhere can crash the + /// process. + pub unsafe fn mprotect(address: NativePointer, size: usize, prot: PageProtection) { + unsafe { gum_sys::gum_mprotect(address.0, size as gum_sys::gsize, prot as u32) }; + } + + /// Try to change the protection of a memory region. + /// + /// Returns `true` if the protection was changed successfully. + /// + /// # Safety + /// + /// `address`/`size` must describe a region of mapped pages owned by the + /// caller. + pub unsafe fn try_mprotect(address: NativePointer, size: usize, prot: PageProtection) -> bool { + unsafe { gum_sys::gum_try_mprotect(address.0, size as gum_sys::gsize, prot as u32) != 0 } + } + + /// Flush the CPU instruction cache for a region of freshly written code. + /// + /// Required on architectures with separate instruction/data caches (e.g. + /// ARM/AArch64) after writing code that is about to be executed. + /// + /// # Safety + /// + /// `address`/`size` must describe a region of mapped, executable memory. + pub unsafe fn clear_cache(address: NativePointer, size: usize) { + unsafe { gum_sys::gum_clear_cache(address.0, size as gum_sys::gsize) }; + } + + /// Ensure a region of code is readable, faulting it in if necessary. + /// + /// # Safety + /// + /// `address`/`size` must describe a region of mapped code memory. + pub unsafe fn ensure_code_readable(address: NativePointer, size: usize) { + unsafe { gum_sys::gum_ensure_code_readable(address.0, size as gum_sys::gsize) }; + } + + /// Atomically patch code at the specified address. + /// + /// This function temporarily makes the memory writable, applies the patch, + /// then restores the original protection. This is safer than manually changing + /// permissions and ensures atomicity. + /// + /// # Arguments + /// + /// * `address` - Address to patch + /// * `size` - Size of the region to patch + /// * `apply` - Callback that performs the actual patching + /// + /// Returns true if the patch succeeded. + /// + /// # Safety + /// + /// The address must point to valid code memory. The apply callback must not + /// access memory outside the specified region. + pub unsafe fn patch_code(address: NativePointer, size: usize, apply: F) -> bool + where + F: FnOnce(*mut u8), + { + unsafe { + unsafe extern "C" fn trampoline(mem: gum_sys::gpointer, user_data: gum_sys::gpointer) + where + F: FnOnce(*mut u8), + { + unsafe { + let callback = Box::from_raw(user_data as *mut F); + callback(mem as *mut u8); + } + } + + let callback = Box::new(apply); + let user_data = Box::into_raw(callback) as *mut _; + + gum_sys::gum_memory_patch_code( + address.0, + size as gum_sys::gsize, + Some(trampoline::), + user_data, + ) != 0 + } + } + + /// Atomically patch code spanning multiple pages. + /// + /// Like [`Memory::patch_code`], but for a set of page-aligned addresses. The + /// `apply` callback is invoked with `(mem, target_page, n_pages)` for each + /// contiguous run, where `mem` is a temporarily-writable mirror of + /// `target_page`. When `coalesce` is `true`, adjacent pages are merged into + /// a single callback invocation. + /// + /// Returns `true` if the patch succeeded. + /// + /// # Safety + /// + /// Every entry of `pages` must be a page-aligned address of mapped code + /// memory, and the callback must not write outside the mirrored pages. + pub unsafe fn patch_code_pages(pages: &[NativePointer], coalesce: bool, apply: F) -> bool + where + F: FnMut(*mut u8, *mut u8, u32), + { + unsafe { + unsafe extern "C" fn trampoline( + mem: gum_sys::gpointer, + target_page: gum_sys::gpointer, + n_pages: gum_sys::guint, + user_data: gum_sys::gpointer, + ) where + F: FnMut(*mut u8, *mut u8, u32), + { + let callback = unsafe { &mut *(user_data as *mut F) }; + callback(mem as *mut u8, target_page as *mut u8, n_pages); + } + + // gum_memory_patch_code_pages takes a GPtrArray of page addresses. + let array = crate::glib_compat::g_ptr_array_sized_new(pages.len() as u32); + for page in pages { + crate::glib_compat::g_ptr_array_add(array, page.0); + } + + let mut apply = apply; + let ok = gum_sys::gum_memory_patch_code_pages( + array, + i32::from(coalesce), + Some(trampoline::), + &mut apply as *mut _ as *mut c_void, + ) != 0; + + crate::glib_compat::g_ptr_array_free(array, gum_sys::true_ as _); + ok + } + } + + /// Find all pointers in memory ranges that point to any of the specified values. + /// + /// This is useful for finding references to specific addresses or objects. + /// + /// # Arguments + /// + /// * `ranges` - Memory ranges to search within + /// * `values` - Target values to find pointers to + /// * `mask` - Bit mask to apply when comparing pointers + /// + /// Returns a Vec of addresses where matching pointers were found. + /// + /// # Safety + /// + /// The ranges must point to valid, readable memory. + pub unsafe fn find_pointers( + ranges: &[MemoryRange], + values: &[usize], + mask: usize, + ) -> Vec { + unsafe { + let ranges_raw: Vec = + ranges.iter().map(|r| r.memory_range).collect(); + + let values_gsize: Vec = + values.iter().map(|&v| v as gum_sys::gsize).collect(); + + let result_array = gum_sys::gum_memory_find_pointers( + ranges_raw.as_ptr(), + ranges_raw.len() as u32, + values_gsize.as_ptr(), + values_gsize.len() as u32, + mask as gum_sys::gsize, + ); + + let mut results = Vec::new(); + if !result_array.is_null() { + let len = (*result_array).len as usize; + let data = (*result_array).data as *const u64; + for i in 0..len { + results.push(NativePointer(*data.add(i) as *mut c_void)); + } + crate::glib_compat::g_array_free(result_array, frida_gum_sys::true_ as _); + } + + results + } + } + + /// Check if writable pages can be remapped. + /// + /// Returns true if the system supports remapping writable pages to different addresses. + pub fn can_remap_writable() -> bool { + unsafe { gum_sys::gum_memory_can_remap_writable() != 0 } + } + + /// Remap `n_pages` whole pages starting at `first_page` so they are + /// writable. Returns the base address of the writable mirror, or `None` + /// if remapping is not possible. + /// + /// # Safety + /// + /// `first_page` must be page-aligned, and the range + /// `[first_page, first_page + n_pages * page_size)` must be a valid set + /// of whole pages owned by the caller. + pub unsafe fn try_remap_writable_pages( + first_page: NativePointer, + n_pages: u32, + ) -> Option { + unsafe { + let result = gum_sys::gum_memory_try_remap_writable_pages(first_page.0, n_pages); + if result.is_null() { + None + } else { + Some(NativePointer(result)) + } + } + } + + /// Release a writable mirror previously obtained from + /// [`Memory::try_remap_writable_pages`]. + /// + /// # Safety + /// + /// `first_page` and `n_pages` must match the values that were passed to + /// the corresponding [`Memory::try_remap_writable_pages`] call. + pub unsafe fn dispose_writable_pages(first_page: NativePointer, n_pages: u32) { + unsafe { + gum_sys::gum_memory_dispose_writable_pages(first_page.0, n_pages); + } + } +} diff --git a/frida-gum/src/memory_access_monitor.rs b/frida-gum/src/memory_access_monitor.rs index cd09bb69..1e7adb11 100644 --- a/frida-gum/src/memory_access_monitor.rs +++ b/frida-gum/src/memory_access_monitor.rs @@ -1,72 +1,63 @@ -use super::{memory_range::MemoryRange, range_details::PageProtection}; -use crate::{NativePointer, error::GumResult}; -use core::{ffi::c_void, ptr::null_mut}; -use frida_gum_sys::{ - _GumMemoryOperation_GUM_MEMOP_EXECUTE, _GumMemoryOperation_GUM_MEMOP_INVALID, - _GumMemoryOperation_GUM_MEMOP_READ, _GumMemoryOperation_GUM_MEMOP_WRITE, _GumMemoryRange, - GError, GumMemoryAccessDetails, GumMemoryAccessMonitor, GumPageProtection, false_, - gum_memory_access_monitor_disable, gum_memory_access_monitor_enable, - gum_memory_access_monitor_new, -}; +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ -pub trait CallbackFn: Fn(&mut MemoryAccessMonitor, &MemoryAccessDetails) {} +//! Watch a set of memory ranges and run a callback whenever they are read, +//! written, or executed. -impl CallbackFn for F where F: Fn(&mut MemoryAccessMonitor, &MemoryAccessDetails) {} +use { + crate::{MemoryRange, NativePointer, PageProtection, error::Error}, + core::{ffi::c_void, ptr}, + frida_gum_sys as gum_sys, +}; -pub struct CallbackWrapper -where - F: CallbackFn, -{ - callback: F, -} +#[cfg(not(feature = "std"))] +use alloc::{boxed::Box, vec::Vec}; -extern "C" fn c_callback( - monitor: *mut GumMemoryAccessMonitor, - details: *const GumMemoryAccessDetails, - user_data: *mut c_void, -) where - F: CallbackFn, -{ - let details = unsafe { &*(details as *const GumMemoryAccessDetails) }; - let details = MemoryAccessDetails::from(details); - let mut monitor = MemoryAccessMonitor { monitor }; - let cw: &mut CallbackWrapper = unsafe { &mut *(user_data as *mut _) }; - (cw.callback)(&mut monitor, &details); -} +#[cfg(feature = "std")] +use std::{boxed::Box, vec::Vec}; -#[derive(FromPrimitive)] +/// Kind of memory access that triggered a notification. +#[derive(FromPrimitive, Debug, Copy, Clone, PartialEq, Eq)] #[repr(u32)] pub enum MemoryOperation { - Invalid = _GumMemoryOperation_GUM_MEMOP_INVALID as _, - Read = _GumMemoryOperation_GUM_MEMOP_READ as _, - Write = _GumMemoryOperation_GUM_MEMOP_WRITE as _, - Execute = _GumMemoryOperation_GUM_MEMOP_EXECUTE as _, + /// Operation could not be determined. + Invalid = gum_sys::_GumMemoryOperation_GUM_MEMOP_INVALID as u32, + /// Read access. + Read = gum_sys::_GumMemoryOperation_GUM_MEMOP_READ as u32, + /// Write access. + Write = gum_sys::_GumMemoryOperation_GUM_MEMOP_WRITE as u32, + /// Execute access. + Execute = gum_sys::_GumMemoryOperation_GUM_MEMOP_EXECUTE as u32, } -/// Details about a memory access -/// -/// # Fields -/// -/// * `operation` - The kind of operation that triggered the access -/// * `from` - Address of instruction performing the access as a [`NativePointer`] -/// * `address` - Address being accessed as a [`NativePointer`] -/// * `range_index` - Index of the accessed range in the ranges provided to -/// * `page_index` - Index of the accessed memory page inside the specified range -/// * `pages_completed` - Overall number of pages which have been accessed so far (and are no longer being monitored) -/// * `pages_total` - Overall number of pages that were initially monitored +/// Details about a memory access reported by [`MemoryAccessMonitor`]. +#[derive(Debug, Clone)] pub struct MemoryAccessDetails { + /// Thread that performed the access. + pub thread_id: usize, + /// Kind of access. pub operation: MemoryOperation, + /// Address of the instruction that performed the access. pub from: NativePointer, + /// Address that was accessed. pub address: NativePointer, + /// Index of the matched range in the slice passed to [`MemoryAccessMonitor::new`]. pub range_index: usize, + /// Index of the accessed page within the matched range. pub page_index: usize, + /// Number of pages already accessed (and no longer monitored). pub pages_completed: usize, + /// Total number of pages originally monitored. pub pages_total: usize, } -impl std::fmt::Display for MemoryAccessDetails { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let operation = match self.operation { +impl core::fmt::Display for MemoryAccessDetails { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let op = match self.operation { MemoryOperation::Invalid => "invalid", MemoryOperation::Read => "read", MemoryOperation::Write => "write", @@ -75,7 +66,7 @@ impl std::fmt::Display for MemoryAccessDetails { write!( f, "MemoryAccessDetails {{ operation: {}, from: {:#x}, address: {:#x}, range_index: {}, page_index: {}, pages_completed: {}, pages_total: {} }}", - operation, + op, self.from.0 as usize, self.address.0 as usize, self.range_index, @@ -86,84 +77,125 @@ impl std::fmt::Display for MemoryAccessDetails { } } -impl From<&GumMemoryAccessDetails> for MemoryAccessDetails { - fn from(details: &GumMemoryAccessDetails) -> Self { +impl From<&gum_sys::GumMemoryAccessDetails> for MemoryAccessDetails { + fn from(details: &gum_sys::GumMemoryAccessDetails) -> Self { Self { - operation: num::FromPrimitive::from_u32(details.operation).unwrap(), + thread_id: details.thread_id as usize, + operation: num::FromPrimitive::from_u32(details.operation) + .unwrap_or(MemoryOperation::Invalid), from: NativePointer(details.from), address: NativePointer(details.address), - range_index: details.range_index as _, - page_index: details.page_index as _, - pages_completed: details.pages_completed as _, - pages_total: details.pages_total as _, + range_index: details.range_index as usize, + page_index: details.page_index as usize, + pages_completed: details.pages_completed as usize, + pages_total: details.pages_total as usize, } } } +type Callback = Box; + +/// Watch one or more memory ranges and invoke a callback on access. +/// +/// The monitor takes ownership of the callback closure. Dropping the monitor +/// releases the closure and unregisters Frida's signal/exception hooks. pub struct MemoryAccessMonitor { - monitor: *mut GumMemoryAccessMonitor, + monitor: *mut gum_sys::GumMemoryAccessMonitor, + // Kept boxed and alive for the lifetime of the monitor; the C side + // holds a *mut c_void into this allocation. + _callback: Box, } impl MemoryAccessMonitor { - /// Create a new [`MemoryAccessMonitor`] + /// Create a new monitor watching the supplied ranges. /// /// # Arguments /// - /// * `ranges` - The memory ranges to monitor - /// * `mask` - The page protection mask to monitor - /// * `auto_reset` - Whether to automatically reset the monitor after each access - /// * `callback` - The callback to call when an access occurs + /// * `ranges` - Ranges to watch. + /// * `mask` - Which protection bits to react to (e.g. `Read`, `Write`, + /// `ReadWrite`, `Execute`). + /// * `auto_reset` - If `true`, the watch on a page is rearmed after each + /// access; otherwise each page only fires once. + /// * `callback` - Closure invoked on every access. pub fn new( _gum: &crate::Gum, - ranges: Vec, + ranges: &[MemoryRange], mask: PageProtection, auto_reset: bool, callback: F, ) -> Self where - F: CallbackFn, + F: FnMut(&MemoryAccessDetails) + 'static, { - let mut cw = CallbackWrapper { callback }; - let monitor = unsafe { - let size = std::mem::size_of::<_GumMemoryRange>() * ranges.len(); - let block = std::alloc::alloc(std::alloc::Layout::from_size_align_unchecked( - size, - std::mem::align_of::<_GumMemoryRange>(), - )) as *mut _GumMemoryRange; - // copy ranges into the buffer - for (i, range) in ranges.iter().enumerate() { - std::ptr::write(block.add(i), range.memory_range); + unsafe extern "C" fn trampoline( + _monitor: *mut gum_sys::GumMemoryAccessMonitor, + details: *const gum_sys::GumMemoryAccessDetails, + user_data: gum_sys::gpointer, + ) { + unsafe { + let cb = &mut *(user_data as *mut Callback); + let details = MemoryAccessDetails::from(&*details); + (cb)(&details); } - let num_ranges = ranges.len() as u32; - - gum_memory_access_monitor_new( - block, - num_ranges, - mask as GumPageProtection, - auto_reset as _, - Some(c_callback::), - &mut cw as *mut _ as *mut c_void, + } + + // Frida copies the ranges array internally, so a stack-allocated Vec is fine. + let raw_ranges: Vec = + ranges.iter().map(|r| r.memory_range).collect(); + + let mut boxed: Box = Box::new(Box::new(callback)); + let user_data = &mut *boxed as *mut Callback as *mut c_void; + + let monitor = unsafe { + gum_sys::gum_memory_access_monitor_new( + raw_ranges.as_ptr(), + raw_ranges.len() as u32, + mask as gum_sys::GumPageProtection, + auto_reset as gum_sys::gboolean, + Some(trampoline), + user_data, None, ) }; - Self { monitor } + + MemoryAccessMonitor { + monitor, + _callback: boxed, + } } - /// Enable the monitor - pub fn enable(&self) -> GumResult<()> { - let mut error: *mut GError = null_mut(); - if unsafe { gum_memory_access_monitor_enable(self.monitor, &mut error) } == false_ as _ { - Err(crate::error::Error::MemoryAccessError) - } else { - Ok(()) + /// Arm the monitor. + /// + /// Returns an error if the underlying VM probes could not be installed + /// (e.g. the requested ranges overlap memory that cannot be probed). + pub fn enable(&self) -> Result<(), Error> { + unsafe { + let mut err: *mut gum_sys::GError = ptr::null_mut(); + let ok = gum_sys::gum_memory_access_monitor_enable(self.monitor, &mut err) != 0; + if !err.is_null() { + crate::glib_compat::g_error_free(err); + } + if ok { + Ok(()) + } else { + Err(Error::MemoryAccessError) + } } } - /// Disable the monitor + /// Disarm the monitor without destroying it. pub fn disable(&self) { - if self.monitor.is_null() { - return; + unsafe { gum_sys::gum_memory_access_monitor_disable(self.monitor) }; + } +} + +impl Drop for MemoryAccessMonitor { + fn drop(&mut self) { + unsafe { + gum_sys::gum_memory_access_monitor_disable(self.monitor); + gum_sys::g_object_unref(self.monitor as *mut c_void); } - unsafe { gum_memory_access_monitor_disable(self.monitor) }; } } + +unsafe impl Send for MemoryAccessMonitor {} diff --git a/frida-gum/src/memory_range.rs b/frida-gum/src/memory_range.rs index fa25f06c..c5d35ef6 100644 --- a/frida-gum/src/memory_range.rs +++ b/frida-gum/src/memory_range.rs @@ -4,7 +4,12 @@ * Licence: wxWindows Library Licence, Version 3.1 */ -use {crate::NativePointer, core::ffi::c_void, cstr_core::CString, frida_gum_sys as gum_sys}; +use { + crate::{NativePointer, PageProtection}, + core::ffi::c_void, + cstr_core::CString, + frida_gum_sys as gum_sys, +}; use core::{ fmt::{Debug, Display, LowerHex, UpperHex}, @@ -20,7 +25,9 @@ pub struct MatchPattern { impl MatchPattern { pub fn from_string(pattern: &str) -> Option { - let pattern = CString::new(pattern).unwrap(); + // An interior NUL can't form a valid pattern; report it as `None` + // rather than panicking, matching this function's fallible contract. + let pattern = CString::new(pattern).ok()?; let internal = unsafe { gum_sys::gum_match_pattern_new_from_string(pattern.as_ptr().cast()) }; @@ -30,6 +37,34 @@ impl MatchPattern { None } } + + /// Get the size in bytes of a buffer that this pattern could match. + pub fn size(&self) -> u32 { + unsafe { gum_sys::gum_match_pattern_get_size(self.internal) } + } + + /// Get the raw pointer to the GLib `GPtrArray` of pattern tokens. + /// + /// Exposed for advanced users who need to inspect the compiled pattern + /// structure directly. The returned pointer aliases storage owned by + /// this `MatchPattern`; do not free it or retain it beyond the + /// `MatchPattern`'s lifetime. + /// + /// # Safety + /// + /// The caller must treat the returned pointer as borrowed from `&self` + /// and must not mutate the underlying array. + pub unsafe fn tokens(&self) -> *mut gum_sys::GPtrArray { + unsafe { gum_sys::gum_match_pattern_get_tokens(self.internal) } + } +} + +impl Clone for MatchPattern { + fn clone(&self) -> Self { + MatchPattern { + internal: unsafe { gum_sys::gum_match_pattern_ref(self.internal) }, + } + } } impl Drop for MatchPattern { @@ -38,6 +73,45 @@ impl Drop for MatchPattern { } } +/// Cached map of memory pages matching a given protection. +/// +/// Useful for fast "is this address inside an executable / readable region?" +/// queries — much cheaper than repeatedly enumerating process ranges. +pub struct MemoryMap { + inner: *mut gum_sys::GumMemoryMap, +} + +impl MemoryMap { + /// Build a memory map covering all pages with the given protection. + pub fn new(protection: PageProtection) -> Self { + MemoryMap { + inner: unsafe { gum_sys::gum_memory_map_new(protection as u32) }, + } + } + + /// Test whether the entire range is contained within the cached map. + pub fn contains(&self, range: &MemoryRange) -> bool { + unsafe { gum_sys::gum_memory_map_contains(self.inner, &range.memory_range) != 0 } + } + + /// Re-scan the process and update the cached map. + /// + /// Call this after operations that may alter page protection + /// (`VirtualProtect`, `mprotect`, allocations, etc.). + pub fn update(&self) { + unsafe { gum_sys::gum_memory_map_update(self.inner) }; + } +} + +impl Drop for MemoryMap { + fn drop(&mut self) { + unsafe { gum_sys::g_object_unref(self.inner as *mut c_void) }; + } +} + +unsafe impl Send for MemoryMap {} +unsafe impl Sync for MemoryMap {} + #[allow(dead_code)] pub struct ScanResult { pub address: usize, diff --git a/frida-gum/src/module.rs b/frida-gum/src/module.rs index 9dab9852..86a48420 100644 --- a/frida-gum/src/module.rs +++ b/frida-gum/src/module.rs @@ -23,7 +23,8 @@ use { cstr_core::CString, frida_gum_sys as gum_sys, frida_gum_sys::{ - GumExportDetails, GumModule, GumSectionDetails, GumSymbolDetails, gboolean, gpointer, + GumDependencyDetails, GumExportDetails, GumImportDetails, GumModule, GumSectionDetails, + GumSymbolDetails, gboolean, gpointer, }, }; @@ -75,6 +76,46 @@ pub struct SectionDetails { pub size: usize, } +/// Import type — function, variable, or unknown. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ImportType { + /// The import is a function. + Function, + /// The import is a variable. + Variable, + /// The import type could not be determined. + Unknown, +} + +/// Dependency type — how the module depends on the named library. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum DependencyType { + /// A standard runtime dependency. + Regular, + /// A weak link — the dependency may be absent. + Weak, + /// The dependency is a re-export. + Reexport, + /// An upward (parent) dependency. + Upward, +} + +/// Details about an imported symbol returned by [`Module::enumerate_imports`]. +pub struct ImportDetails { + pub typ: ImportType, + pub name: String, + pub module: Option, + pub address: usize, + pub slot: usize, +} + +/// Details about a module dependency returned by +/// [`Module::enumerate_dependencies`]. +pub struct DependencyDetails { + pub name: String, + pub typ: DependencyType, +} + /// Module export details returned by [`Module::enumerate_exports`]. pub struct ExportDetails { pub typ: ExportType, @@ -209,7 +250,7 @@ impl Module { /// Enumerates exports in module. pub fn enumerate_exports(&self) -> Vec { - let result: Vec = vec![]; + let mut result: Vec = vec![]; unsafe extern "C" fn callback( details: *const GumExportDetails, @@ -233,7 +274,7 @@ impl Module { frida_gum_sys::gum_module_enumerate_exports( self.inner, Some(callback), - &result as *const _ as *mut c_void, + &mut result as *mut _ as *mut c_void, ); } result @@ -241,7 +282,7 @@ impl Module { /// Enumerates symbols in module. pub fn enumerate_symbols(&self) -> Vec { - let result: Vec = vec![]; + let mut result: Vec = vec![]; unsafe extern "C" fn callback( details: *const GumSymbolDetails, user_data: gpointer, @@ -270,7 +311,7 @@ impl Module { frida_gum_sys::gum_module_enumerate_symbols( self.inner, Some(callback), - &result as *const _ as *mut c_void, + &mut result as *mut _ as *mut c_void, ); } result @@ -278,7 +319,7 @@ impl Module { /// Enumerates sections of module. pub fn enumerate_sections(&self) -> Vec { - let result: Vec = vec![]; + let mut result: Vec = vec![]; unsafe extern "C" fn callback( details: *const GumSectionDetails, @@ -312,11 +353,131 @@ impl Module { frida_gum_sys::gum_module_enumerate_sections( self.inner, Some(callback), - &result as *const _ as *mut c_void, + &mut result as *mut _ as *mut c_void, ); } result } + + /// Enumerate the module's imports. + pub fn enumerate_imports(&self) -> Vec { + let mut result: Vec = Vec::new(); + + unsafe extern "C" fn callback( + details: *const GumImportDetails, + user_data: gpointer, + ) -> gboolean { + unsafe { + let res = &mut *(user_data as *mut Vec); + + let name: String = NativePointer((*details).name as *mut _) + .try_into() + .unwrap_or_default(); + + let module = if (*details).module.is_null() { + None + } else { + Some( + NativePointer((*details).module as *mut _) + .try_into() + .unwrap_or_default(), + ) + }; + + let typ = match (*details).type_ as u32 { + x if x == gum_sys::GumImportType_GUM_IMPORT_FUNCTION as u32 => { + ImportType::Function + } + x if x == gum_sys::GumImportType_GUM_IMPORT_VARIABLE as u32 => { + ImportType::Variable + } + _ => ImportType::Unknown, + }; + + res.push(ImportDetails { + typ, + name, + module, + address: (*details).address as usize, + slot: (*details).slot as usize, + }); + 1 + } + } + + unsafe { + frida_gum_sys::gum_module_enumerate_imports( + self.inner, + Some(callback), + &mut result as *mut _ as *mut c_void, + ); + } + result + } + + /// Enumerate the module's dependencies. + pub fn enumerate_dependencies(&self) -> Vec { + let mut result: Vec = Vec::new(); + + unsafe extern "C" fn callback( + details: *const GumDependencyDetails, + user_data: gpointer, + ) -> gboolean { + unsafe { + let res = &mut *(user_data as *mut Vec); + + let name: String = NativePointer((*details).name as *mut _) + .try_into() + .unwrap_or_default(); + + let typ = match (*details).type_ as u32 { + x if x == gum_sys::GumDependencyType_GUM_DEPENDENCY_WEAK as u32 => { + DependencyType::Weak + } + x if x == gum_sys::GumDependencyType_GUM_DEPENDENCY_REEXPORT as u32 => { + DependencyType::Reexport + } + x if x == gum_sys::GumDependencyType_GUM_DEPENDENCY_UPWARD as u32 => { + DependencyType::Upward + } + _ => DependencyType::Regular, + }; + + res.push(DependencyDetails { name, typ }); + 1 + } + } + + unsafe { + frida_gum_sys::gum_module_enumerate_dependencies( + self.inner, + Some(callback), + &mut result as *mut _ as *mut c_void, + ); + } + result + } + + /// Get the version string of the module, if any. + pub fn version(&self) -> Option { + unsafe { + let ptr = gum_sys::gum_module_get_version(self.inner); + if ptr.is_null() { + None + } else { + Some(CStr::from_ptr(ptr).to_string_lossy().to_string()) + } + } + } + + /// Ensure the module is fully initialized. + /// + /// On platforms with lazy module loading (e.g. Linux ld.so), this + /// resolves any pending fixups so subsequent enumeration calls produce + /// stable results. + pub fn ensure_initialized(&self) { + unsafe { gum_sys::gum_module_ensure_initialized(self.inner) }; + } } impl Debug for Module { diff --git a/frida-gum/src/module_map.rs b/frida-gum/src/module_map.rs index bf428786..f96af25f 100644 --- a/frida-gum/src/module_map.rs +++ b/frida-gum/src/module_map.rs @@ -36,22 +36,44 @@ impl ModuleMap { Self::from_raw(unsafe { gum_sys::gum_module_map_new() }) } - /// Create a new [`ModuleMap`] with a filter function - pub fn new_with_filter(filter: &mut dyn FnMut(Module) -> bool) -> Self { - unsafe extern "C" fn module_map_filter( + /// Create a new [`ModuleMap`] with a filter function. + /// + /// The filter is retained by the underlying `GumModuleMap` and re-invoked + /// on every [`ModuleMap::update`]. The closure is boxed and owned by the + /// map; `destroy` is called by Frida when the map is finalized, ensuring + /// the closure is dropped at the right time. No `'static` bound is required + /// because the box is freed before the map's drop — the lifetime is + /// managed explicitly via the `destroy` callback. + pub fn new_with_filter(filter: F) -> Self + where + F: FnMut(Module) -> bool, + { + unsafe extern "C" fn module_map_filter( details: *mut gum_sys::GumModule, callback: *mut c_void, - ) -> i32 { + ) -> i32 + where + F: FnMut(Module) -> bool, + { unsafe { - let callback = &mut *(callback as *mut Box<&mut dyn FnMut(Module) -> bool>); - i32::from((callback)(Module::from_raw(details))) + let callback = &mut *(callback as *mut F); + i32::from(callback(Module::from_raw(details))) } } + + // Frees the boxed closure when the GumModuleMap is finalized. + unsafe extern "C" fn destroy(data: *mut c_void) { + unsafe { + drop(Box::from_raw(data as *mut F)); + } + } + + let filter = Box::into_raw(Box::new(filter)); Self::from_raw(unsafe { gum_sys::gum_module_map_new_filtered( - Some(module_map_filter), - &mut Box::new(filter) as *mut _ as *mut c_void, - None, + Some(module_map_filter::), + filter as *mut c_void, + Some(destroy::), ) }) } @@ -59,21 +81,19 @@ impl ModuleMap { /// Create a new [`ModuleMap`] from a list of names #[cfg(feature = "module-names")] pub fn new_from_names(names: &[&str]) -> Self { - Self::new_with_filter(&mut |details: Module| { - for name in names { - if (name.starts_with('/') && details.path().eq(name)) + // The filter outlives this call (Frida retains it), so capture owned + // copies of the names rather than borrowing the caller's slice. + let names: Vec = names.iter().map(|name| (*name).to_owned()).collect(); + Self::new_with_filter(move |details: Module| { + names.iter().any(|name| { + (name.starts_with('/') && details.path().eq(name)) || (name.contains('/') - && details.name().eq(Path::new(name) + && Path::new(name) .file_name() - .unwrap() - .to_str() - .unwrap())) - || (details.name().eq(name)) - { - return true; - } - } - false + .and_then(|f| f.to_str()) + .is_some_and(|f| details.name().eq(f))) + || details.name().eq(name) + }) }) } /// Find the given address in the [`ModuleMap`] diff --git a/frida-gum/src/process.rs b/frida-gum/src/process.rs index 11df0a07..a74f9dde 100644 --- a/frida-gum/src/process.rs +++ b/frida-gum/src/process.rs @@ -42,6 +42,16 @@ pub enum CodeSigningPolicy { CodeSigningRequired = gum_sys::GumCodeSigningPolicy_GUM_CODE_SIGNING_REQUIRED as u32, } +/// How thoroughly Gum should clean up its state when shutting down. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(i32)] +pub enum TeardownRequirement { + /// Complete teardown of all internal state. + Full = gum_sys::GumTeardownRequirement_GUM_TEARDOWN_REQUIREMENT_FULL as _, + /// Minimal teardown, intended for short-lived processes. + Minimal = gum_sys::GumTeardownRequirement_GUM_TEARDOWN_REQUIREMENT_MINIMAL as _, +} + #[derive(Clone, FromPrimitive, Debug)] #[repr(u32)] pub enum Os { @@ -105,6 +115,99 @@ impl<'a> Process<'a> { unsafe { Module::from_raw(gum_sys::gum_process_get_main_module()) } } + /// Returns the libc / system C runtime module if available. + pub fn libc_module(&self) -> Option { + unsafe { + let module = gum_sys::gum_process_get_libc_module(); + if module.is_null() { + None + } else { + Some(Module::from_raw(module)) + } + } + } + + /// Check whether the specified thread exists. + pub fn has_thread(&self, thread_id: usize) -> bool { + unsafe { gum_sys::gum_process_has_thread(thread_id as gum_sys::GumThreadId) != 0 } + } + + /// Set the process-wide code signing policy. + pub fn set_code_signing_policy(&self, policy: CodeSigningPolicy) { + let raw: gum_sys::GumCodeSigningPolicy = match policy { + CodeSigningPolicy::CodeSigningOptional => { + gum_sys::GumCodeSigningPolicy_GUM_CODE_SIGNING_OPTIONAL + } + CodeSigningPolicy::CodeSigningRequired => { + gum_sys::GumCodeSigningPolicy_GUM_CODE_SIGNING_REQUIRED + } + }; + unsafe { gum_sys::gum_process_set_code_signing_policy(raw) }; + } + + /// Get the current teardown requirement. + pub fn teardown_requirement(&self) -> TeardownRequirement { + let raw = unsafe { gum_sys::gum_process_get_teardown_requirement() }; + if raw == gum_sys::GumTeardownRequirement_GUM_TEARDOWN_REQUIREMENT_FULL { + TeardownRequirement::Full + } else { + TeardownRequirement::Minimal + } + } + + /// Set the process teardown requirement. + pub fn set_teardown_requirement(&self, requirement: TeardownRequirement) { + unsafe { + gum_sys::gum_process_set_teardown_requirement( + requirement as gum_sys::GumTeardownRequirement, + ); + } + } + + /// Suspend the specified thread, run `callback` with its CPU context, + /// then resume. + /// + /// The callback runs synchronously on the calling thread; you may + /// inspect or modify the suspended thread's registers via the supplied + /// `*mut GumCpuContext`. + /// + /// Set `abort_safely` to `true` to allow Frida to terminate the request + /// at safe points if the suspended thread is in a critical region. + /// + /// Returns `true` on success. + pub fn modify_thread(&self, thread_id: usize, mut callback: F, abort_safely: bool) -> bool + where + F: FnMut(usize, *mut gum_sys::GumCpuContext), + { + unsafe extern "C" fn trampoline( + thread_id: gum_sys::GumThreadId, + cpu_context: *mut gum_sys::GumCpuContext, + user_data: gpointer, + ) where + F: FnMut(usize, *mut gum_sys::GumCpuContext), + { + unsafe { + let cb = &mut *(user_data as *mut F); + cb(thread_id as usize, cpu_context); + } + } + + let flags = if abort_safely { + gum_sys::GumModifyThreadFlags_GUM_MODIFY_THREAD_FLAGS_ABORT_SAFELY + } else { + gum_sys::GumModifyThreadFlags_GUM_MODIFY_THREAD_FLAGS_NONE + }; + + unsafe { + gum_sys::gum_process_modify_thread( + thread_id as gum_sys::GumThreadId, + Some(trampoline::), + &mut callback as *mut _ as *mut c_void, + flags, + ) != 0 + } + } + pub fn find_module_by_name(&self, module_name: &str) -> Option { let module_name = CString::new(module_name).unwrap(); unsafe { @@ -128,6 +231,30 @@ impl<'a> Process<'a> { } } + /// Find the contiguous code range of the function containing `address`. + /// + /// Works on stripped binaries without symbol information, by following the + /// platform's unwind data. Returns `None` if no function range could be + /// determined for the address. + /// + /// # Safety + /// + /// `address` must point into executable code (typically the entry or body + /// of a function) in a readable memory region. + pub unsafe fn find_function_range(&self, address: NativePointer) -> Option { + unsafe { + let mut raw: gum_sys::GumMemoryRange = core::mem::zeroed(); + if gum_sys::gum_process_find_function_range(address.0, &mut raw) != 0 { + Some(crate::MemoryRange::new( + NativePointer(raw.base_address as *mut c_void), + raw.size as usize, + )) + } else { + None + } + } + } + /// Returns a string specifying the filesystem path to the current working directory pub fn current_dir(&self) -> String { unsafe { @@ -136,7 +263,11 @@ impl<'a> Process<'a> { #[cfg(not(any(target_os = "linux", target_os = "freebsd")))] let dir = g_get_current_dir(); - CStr::from_ptr(dir).to_string_lossy().to_string() + let owned = CStr::from_ptr(dir).to_string_lossy().to_string(); + // `g_get_current_dir` returns a newly-allocated string (transfer-full); + // unlike `g_get_home_dir`/`g_get_tmp_dir` it must be freed by the caller. + crate::glib_compat::g_free(dir as *mut c_void); + owned } } @@ -206,7 +337,7 @@ impl<'a> Process<'a> { } } - let callback_data = CallbackData { + let mut callback_data = CallbackData { ranges: Vec::new(), protection: protection.clone(), }; @@ -215,13 +346,47 @@ impl<'a> Process<'a> { gum_sys::gum_process_enumerate_ranges( protection as u32, Some(enumerate_ranges_callback), - &callback_data as *const _ as *mut c_void, + &mut callback_data as *mut _ as *mut c_void, ); } callback_data.ranges } + /// Enumerate ranges that the libc allocator is currently using. + /// + /// Each entry is the [`crate::MemoryRange`] of a heap chunk reported by + /// the host's malloc implementation (Windows heap, glibc, jemalloc, etc.). + /// The set of returned ranges is a snapshot — concurrent allocations + /// may invalidate it. + pub fn enumerate_malloc_ranges(&self) -> Vec { + let mut result: Vec = Vec::new(); + + unsafe extern "C" fn callback( + details: *const gum_sys::GumMallocRangeDetails, + user_data: gpointer, + ) -> gboolean { + unsafe { + let res = &mut *(user_data as *mut Vec); + let r = *(*details).range; + res.push(crate::MemoryRange::new( + NativePointer(r.base_address as *mut c_void), + r.size as usize, + )); + 1 + } + } + + unsafe { + gum_sys::gum_process_enumerate_malloc_ranges( + Some(callback), + &mut result as *mut _ as *mut c_void, + ); + } + + result + } + /// Enumerates loaded modules pub fn enumerate_modules(&self) -> Vec { struct CallbackData { @@ -240,14 +405,14 @@ impl<'a> Process<'a> { } } - let callback_data = CallbackData { + let mut callback_data = CallbackData { modules: Vec::new(), }; unsafe { gum_sys::gum_process_enumerate_modules( Some(enumerate_modules_callback), - &callback_data as *const _ as *mut c_void, + &mut callback_data as *mut _ as *mut c_void, ); } @@ -270,12 +435,12 @@ impl<'a> Process<'a> { } } - let callback_data = Vec::new(); + let mut callback_data = Vec::new(); unsafe { gum_sys::gum_process_enumerate_threads( Some(enumerate_threads_callback), - &callback_data as *const _ as *mut c_void, + &mut callback_data as *mut _ as *mut c_void, GumThreadFlags_GUM_THREAD_FLAGS_ALL, ) }; diff --git a/frida-gum/src/query.rs b/frida-gum/src/query.rs new file mode 100644 index 00000000..6a63e4c4 --- /dev/null +++ b/frida-gum/src/query.rs @@ -0,0 +1,106 @@ +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Runtime queries about the host CPU and memory subsystem. +//! +//! Note: GUM constants have platform-dependent types (i32 on Windows, u32 on Linux). +//! The `as u32` casts are necessary on Windows but trigger clippy warnings on Linux. + +#![allow(clippy::unnecessary_cast)] + +use {bitflags::bitflags, frida_gum_sys as gum_sys}; + +bitflags! { + /// Bit flags describing optional CPU features detected at runtime. + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub struct CpuFeatures: u32 { + /// AVX2 extensions (Intel/AMD). + const AVX2 = gum_sys::_GumCpuFeatures_GUM_CPU_AVX2 as u32; + /// Control-flow Enforcement Technology — Shadow Stack. + const CET_SS = gum_sys::_GumCpuFeatures_GUM_CPU_CET_SS as u32; + /// ARM Thumb interworking support. + const THUMB_INTERWORK = gum_sys::_GumCpuFeatures_GUM_CPU_THUMB_INTERWORK as u32; + /// ARM VFPv2. + const VFP2 = gum_sys::_GumCpuFeatures_GUM_CPU_VFP2 as u32; + /// ARM VFPv3. + const VFP3 = gum_sys::_GumCpuFeatures_GUM_CPU_VFP3 as u32; + /// 32 double-precision floating-point registers. + const VFPD32 = gum_sys::_GumCpuFeatures_GUM_CPU_VFPD32 as u32; + /// Pointer authentication (ARMv8.3-A). + const PTRAUTH = gum_sys::_GumCpuFeatures_GUM_CPU_PTRAUTH as u32; + } +} + +/// Pointer authentication support level (ARMv8.3-A). +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum PtrauthSupport { + /// Could not be determined. + Invalid = gum_sys::_GumPtrauthSupport_GUM_PTRAUTH_INVALID as u32, + /// Pointer authentication is not supported. + Unsupported = gum_sys::_GumPtrauthSupport_GUM_PTRAUTH_UNSUPPORTED as u32, + /// Pointer authentication is supported. + Supported = gum_sys::_GumPtrauthSupport_GUM_PTRAUTH_SUPPORTED as u32, +} + +/// Level of support for pages mapped read-write-execute. +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum RwxSupport { + /// RWX pages are not allowed at all. + None = gum_sys::_GumRwxSupport_GUM_RWX_NONE as u32, + /// RWX is only permitted for newly allocated pages. + AllocationsOnly = gum_sys::_GumRwxSupport_GUM_RWX_ALLOCATIONS_ONLY as u32, + /// Full RWX including existing pages. + Full = gum_sys::_GumRwxSupport_GUM_RWX_FULL as u32, +} + +/// Static interface to Frida's runtime queries. +pub struct Query; + +impl Query { + /// Get the CPU feature bits detected at runtime. + pub fn cpu_features() -> CpuFeatures { + CpuFeatures::from_bits_truncate(unsafe { gum_sys::gum_query_cpu_features() }) + } + + /// Get the host's pointer authentication support level. + pub fn ptrauth_support() -> PtrauthSupport { + let raw = unsafe { gum_sys::gum_query_ptrauth_support() }; + match raw { + x if x == gum_sys::_GumPtrauthSupport_GUM_PTRAUTH_SUPPORTED as u32 => { + PtrauthSupport::Supported + } + x if x == gum_sys::_GumPtrauthSupport_GUM_PTRAUTH_UNSUPPORTED as u32 => { + PtrauthSupport::Unsupported + } + _ => PtrauthSupport::Invalid, + } + } + + /// Get the system page size. + pub fn page_size() -> u32 { + unsafe { gum_sys::gum_query_page_size() } + } + + /// Check whether RWX memory is supported on this host. + pub fn is_rwx_supported() -> bool { + unsafe { gum_sys::gum_query_is_rwx_supported() != 0 } + } + + /// Get the RWX support level on this host. + pub fn rwx_support() -> RwxSupport { + let raw = unsafe { gum_sys::gum_query_rwx_support() }; + match raw { + x if x == gum_sys::_GumRwxSupport_GUM_RWX_FULL as u32 => RwxSupport::Full, + x if x == gum_sys::_GumRwxSupport_GUM_RWX_ALLOCATIONS_ONLY as u32 => { + RwxSupport::AllocationsOnly + } + _ => RwxSupport::None, + } + } +} diff --git a/frida-gum/src/range_details.rs b/frida-gum/src/range_details.rs index fe8b1d68..3b63c5e9 100644 --- a/frida-gum/src/range_details.rs +++ b/frida-gum/src/range_details.rs @@ -34,6 +34,8 @@ pub enum PageProtection { Execute = gum_sys::_GumPageProtection_GUM_PAGE_EXECUTE as u32, ReadWrite = gum_sys::_GumPageProtection_GUM_PAGE_READ as u32 | gum_sys::_GumPageProtection_GUM_PAGE_WRITE as u32, + WriteExecute = gum_sys::_GumPageProtection_GUM_PAGE_WRITE as u32 + | gum_sys::_GumPageProtection_GUM_PAGE_EXECUTE as u32, ReadExecute = gum_sys::_GumPageProtection_GUM_PAGE_READ as u32 | gum_sys::_GumPageProtection_GUM_PAGE_EXECUTE as u32, ReadWriteExecute = gum_sys::_GumPageProtection_GUM_PAGE_READ as u32 @@ -49,6 +51,7 @@ impl fmt::Display for PageProtection { PageProtection::Write => write!(fmt, "-w-"), PageProtection::Execute => write!(fmt, "--e"), PageProtection::ReadWrite => write!(fmt, "rw-"), + PageProtection::WriteExecute => write!(fmt, "-we"), PageProtection::ReadExecute => write!(fmt, "r-e"), PageProtection::ReadWriteExecute => write!(fmt, "rwe"), } @@ -146,7 +149,11 @@ impl<'a> RangeDetails<'a> { unsafe { Self { range: MemoryRange::from_raw((*range_details).range), - protection: num::FromPrimitive::from_u32((*range_details).protection).unwrap(), + // Frida only reports combinations of R/W/X (0..=7); fall back to + // NoAccess rather than panicking inside this FFI callback if an + // unexpected value is ever encountered. + protection: num::FromPrimitive::from_u32((*range_details).protection) + .unwrap_or(PageProtection::NoAccess), file: FileMapping::from_raw((*range_details).file), phantom: PhantomData, } diff --git a/frida-gum/src/registry.rs b/frida-gum/src/registry.rs new file mode 100644 index 00000000..a8956cac --- /dev/null +++ b/frida-gum/src/registry.rs @@ -0,0 +1,162 @@ +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Locked snapshots of process-wide module and thread registries. +//! +//! [`ModuleRegistry`] and [`ThreadRegistry`] expose Frida's internal lookup +//! tables. Unlike [`crate::Process::enumerate_modules`] / +//! [`crate::Process::enumerate_threads`], a registry can be **locked** so +//! that successive operations observe a consistent snapshot. + +use { + crate::{Module, Thread}, + core::ffi::c_void, + frida_gum_sys as gum_sys, +}; + +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +/// Process-wide module registry. +/// +/// Calls to [`Self::lock`] / [`Self::unlock`] guarantee the snapshot +/// observed by [`Self::enumerate_modules`] is stable across invocations. +pub struct ModuleRegistry { + inner: *mut gum_sys::GumModuleRegistry, +} + +impl ModuleRegistry { + /// Get the singleton registry instance. + pub fn obtain() -> Self { + ModuleRegistry { + inner: unsafe { gum_sys::gum_module_registry_obtain() }, + } + } + + /// Acquire the registry's read/write lock. + /// + /// While the lock is held, the module list cannot be modified by Frida + /// or by other callers of [`Self::lock`]. + pub fn lock(&self) { + unsafe { gum_sys::gum_module_registry_lock(self.inner) }; + } + + /// Release a lock previously acquired with [`Self::lock`]. + /// + /// # Safety + /// + /// Must be paired one-for-one with [`Self::lock`]. + pub unsafe fn unlock(&self) { + unsafe { + gum_sys::gum_module_registry_unlock(self.inner); + } + } + + /// Run `f` while holding the registry lock. + pub fn with_lock_held(&self, f: impl FnOnce(&Self) -> R) -> R { + self.lock(); + let result = f(self); + unsafe { self.unlock() }; + result + } + + /// Enumerate all modules known to the registry. + pub fn enumerate_modules(&self) -> Vec { + let mut result: Vec = Vec::new(); + + unsafe extern "C" fn callback( + module: *mut gum_sys::GumModule, + user_data: gum_sys::gpointer, + ) -> gum_sys::gboolean { + unsafe { + let res = &mut *(user_data as *mut Vec); + res.push(Module::from_raw(module)); + 1 + } + } + + unsafe { + gum_sys::gum_module_registry_enumerate_modules( + self.inner, + Some(callback), + &mut result as *mut _ as *mut c_void, + ); + } + + result + } +} + +unsafe impl Send for ModuleRegistry {} +unsafe impl Sync for ModuleRegistry {} + +/// Process-wide thread registry. +pub struct ThreadRegistry { + inner: *mut gum_sys::GumThreadRegistry, +} + +impl ThreadRegistry { + /// Get the singleton registry instance. + pub fn obtain() -> Self { + ThreadRegistry { + inner: unsafe { gum_sys::gum_thread_registry_obtain() }, + } + } + + /// Acquire the registry's lock. + pub fn lock(&self) { + unsafe { gum_sys::gum_thread_registry_lock(self.inner) }; + } + + /// Release the registry's lock. + /// + /// # Safety + /// + /// Must be paired one-for-one with [`Self::lock`]. + pub unsafe fn unlock(&self) { + unsafe { + gum_sys::gum_thread_registry_unlock(self.inner); + } + } + + /// Run `f` while holding the registry lock. + pub fn with_lock_held(&self, f: impl FnOnce(&Self) -> R) -> R { + self.lock(); + let result = f(self); + unsafe { self.unlock() }; + result + } + + /// Enumerate all threads known to the registry. + pub fn enumerate_threads(&self) -> Vec { + let mut result: Vec = Vec::new(); + + unsafe extern "C" fn callback( + details: *const gum_sys::GumThreadDetails, + user_data: gum_sys::gpointer, + ) -> gum_sys::gboolean { + unsafe { + let res = &mut *(user_data as *mut Vec); + res.push(Thread::from_raw(details)); + 1 + } + } + + unsafe { + gum_sys::gum_thread_registry_enumerate_threads( + self.inner, + Some(callback), + &mut result as *mut _ as *mut c_void, + ); + } + + result + } +} + +unsafe impl Send for ThreadRegistry {} +unsafe impl Sync for ThreadRegistry {} diff --git a/frida-gum/src/stalker.rs b/frida-gum/src/stalker.rs index 3e94dbdd..6b67983d 100644 --- a/frida-gum/src/stalker.rs +++ b/frida-gum/src/stalker.rs @@ -295,6 +295,269 @@ impl Stalker { unsafe { gum_sys::gum_stalker_deactivate(self.stalker) } } + /// Add a call probe at the specified target address. + /// + /// The probe will be invoked whenever the target function is called. + /// Returns a probe ID that can be used to remove the probe later. + /// + /// # Safety + /// + /// The target address must point to a valid function entry point. + pub fn add_call_probe(&mut self, target_address: NativePointer, callback: F) -> u32 + where + F: FnMut(*mut gum_sys::GumCallDetails) + 'static, + { + #[cfg(not(feature = "std"))] + use alloc::boxed::Box; + + #[cfg(feature = "std")] + use std::boxed::Box; + + unsafe extern "C" fn trampoline( + details: *mut gum_sys::GumCallDetails, + user_data: gum_sys::gpointer, + ) where + F: FnMut(*mut gum_sys::GumCallDetails), + { + unsafe { + let callback = &mut *(user_data as *mut F); + callback(details); + } + } + + unsafe extern "C" fn destroy(user_data: gum_sys::gpointer) + where + F: FnMut(*mut gum_sys::GumCallDetails), + { + unsafe { + // Free the Box that holds the callback closure once Frida releases it. + let _ = Box::from_raw(user_data as *mut F); + } + } + + let callback = Box::new(callback); + let user_data = Box::into_raw(callback) as *mut _; + + unsafe { + gum_sys::gum_stalker_add_call_probe( + self.stalker, + target_address.0, + Some(trampoline::), + user_data, + Some(destroy::), + ) + } + } + + /// Remove a previously added call probe. + /// + /// # Arguments + /// + /// * `id` - The probe ID returned by [`Stalker::add_call_probe()`] + pub fn remove_call_probe(&mut self, id: u32) { + unsafe { gum_sys::gum_stalker_remove_call_probe(self.stalker, id) }; + } + + /// Invalidate the compiled code for the specified address. + /// + /// Forces the Stalker to recompile the code at the given address the next time + /// it is encountered. Useful after modifying code or needing fresh instrumentation. + pub fn invalidate(&mut self, address: NativePointer) { + unsafe { gum_sys::gum_stalker_invalidate(self.stalker, address.0) }; + } + + /// Invalidate compiled code for a specific thread. + /// + /// Like [`Stalker::invalidate()`], but only affects the specified thread. + /// + /// # Arguments + /// + /// * `thread_id` - The thread ID to invalidate code for + /// * `address` - The address to invalidate + pub fn invalidate_for_thread(&mut self, thread_id: usize, address: NativePointer) { + use frida_gum_sys::GumThreadId; + unsafe { + gum_sys::gum_stalker_invalidate_for_thread( + self.stalker, + thread_id as GumThreadId, + address.0, + ) + }; + } + + /// Force recompilation of the code at the specified address. + /// + /// Similar to [`Stalker::invalidate()`], but immediately recompiles the code + /// rather than waiting for the next execution. + pub fn recompile(&mut self, address: NativePointer) { + unsafe { gum_sys::gum_stalker_recompile(self.stalker, address.0) }; + } + + /// Prefetch and compile code at the specified address. + /// + /// Pre-compiles code before it is executed to reduce latency when it is first encountered. + /// + /// # Arguments + /// + /// * `address` - The address to prefetch + /// * `recycle_count` - Number of times to allow recycling the compiled code + pub fn prefetch(&mut self, address: NativePointer, recycle_count: i32) { + unsafe { gum_sys::gum_stalker_prefetch(self.stalker, address.0, recycle_count) }; + } + + /// Prefetch code based on a backpatch notification. + /// + /// This is typically used internally by the Stalker but can be called explicitly + /// to optimize performance for known code patterns. + /// + /// # Safety + /// + /// The backpatch pointer must be valid and point to a valid GumBackpatch structure. + pub unsafe fn prefetch_backpatch(&mut self, backpatch: *const gum_sys::GumBackpatch) { + unsafe { + gum_sys::gum_stalker_prefetch_backpatch(self.stalker, backpatch); + } + } + + /// Schedule a closure to run on the specified thread. + /// + /// Returns `true` if the request was successfully posted. The closure + /// runs asynchronously inside the target thread's context; if posting + /// fails, the closure is still dropped via Frida's destroy notifier. + /// + /// # Arguments + /// + /// * `thread_id` - The thread ID to run the function on + /// * `func` - The function to execute. It receives a pointer to the + /// target thread's CPU context. + pub fn run_on_thread(&mut self, thread_id: usize, func: F) -> bool + where + F: FnOnce(*const gum_sys::GumCpuContext) + 'static, + { + #[cfg(not(feature = "std"))] + use alloc::boxed::Box; + + #[cfg(feature = "std")] + use std::boxed::Box; + + use frida_gum_sys::GumThreadId; + + struct CallbackState { + callback: Option, + } + + unsafe extern "C" fn trampoline( + cpu_context: *const gum_sys::GumCpuContext, + user_data: gum_sys::gpointer, + ) where + F: FnOnce(*const gum_sys::GumCpuContext), + { + unsafe { + let state = &mut *(user_data as *mut CallbackState); + if let Some(callback) = state.callback.take() { + callback(cpu_context); + } + } + } + + unsafe extern "C" fn destroy(user_data: gum_sys::gpointer) + where + F: FnOnce(*const gum_sys::GumCpuContext), + { + unsafe { + let _ = Box::from_raw(user_data as *mut CallbackState); + } + } + + let state = Box::new(CallbackState { + callback: Some(func), + }); + let user_data = Box::into_raw(state) as *mut _; + + unsafe { + gum_sys::gum_stalker_run_on_thread( + self.stalker, + thread_id as GumThreadId, + Some(trampoline::), + user_data, + Some(destroy::), + ) != 0 + } + } + + /// Execute a function on the specified thread synchronously. + /// + /// Like [`Stalker::run_on_thread()`], but blocks until the function completes. + /// Returns `true` if the operation succeeded. + /// + /// # Arguments + /// + /// * `thread_id` - The thread ID to run the function on + /// * `func` - The function to execute + pub fn run_on_thread_sync(&mut self, thread_id: usize, func: F) -> bool + where + F: FnOnce(*const gum_sys::GumCpuContext) + 'static, + { + #[cfg(not(feature = "std"))] + use alloc::boxed::Box; + + #[cfg(feature = "std")] + use std::boxed::Box; + + use frida_gum_sys::GumThreadId; + + unsafe extern "C" fn trampoline( + cpu_context: *const gum_sys::GumCpuContext, + user_data: gum_sys::gpointer, + ) where + F: FnOnce(*const gum_sys::GumCpuContext), + { + unsafe { + let callback = Box::from_raw(user_data as *mut F); + callback(cpu_context); + } + } + + let callback = Box::new(func); + let user_data = Box::into_raw(callback) as *mut _; + + let result = unsafe { + gum_sys::gum_stalker_run_on_thread_sync( + self.stalker, + thread_id as GumThreadId, + Some(trampoline::), + user_data, + ) != 0 + }; + + // If the operation failed, we need to clean up the Box ourselves + if !result { + unsafe { + let _ = Box::from_raw(user_data as *mut F); + } + } + + result + } + + /// Get the source address from a backpatch notification. + /// + /// # Safety + /// + /// The backpatch pointer must be valid. + pub unsafe fn backpatch_get_from(backpatch: *const gum_sys::GumBackpatch) -> NativePointer { + unsafe { NativePointer(gum_sys::gum_stalker_backpatch_get_from(backpatch)) } + } + + /// Get the destination address from a backpatch notification. + /// + /// # Safety + /// + /// The backpatch pointer must be valid. + pub unsafe fn backpatch_get_to(backpatch: *const gum_sys::GumBackpatch) -> NativePointer { + unsafe { NativePointer(gum_sys::gum_stalker_backpatch_get_to(backpatch)) } + } + #[cfg(feature = "stalker-observer")] #[cfg_attr(docsrs, doc(cfg(feature = "stalker-observer")))] pub fn set_observer(&mut self, observer: &mut O) { diff --git a/frida-gum/src/stalker/event_sink.rs b/frida-gum/src/stalker/event_sink.rs index cda732d8..fbf93dae 100644 --- a/frida-gum/src/stalker/event_sink.rs +++ b/frida-gum/src/stalker/event_sink.rs @@ -107,43 +107,33 @@ pub trait EventSink { } unsafe extern "C" fn call_start(user_data: *mut c_void) { - unsafe { - let event_sink: &mut S = &mut *(user_data as *mut S); - event_sink.start(); - } + let event_sink: &mut S = unsafe { &mut *(user_data as *mut S) }; + event_sink.start(); } unsafe extern "C" fn call_process( user_data: *mut c_void, event: *const frida_gum_sys::GumEvent, ) { - unsafe { - let event_sink: &mut S = &mut *(user_data as *mut S); - event_sink.process(&(*event).into()); - } + let event_sink: &mut S = unsafe { &mut *(user_data as *mut S) }; + event_sink.process(&unsafe { *event }.into()); } unsafe extern "C" fn call_flush(user_data: *mut c_void) { - unsafe { - let event_sink: &mut S = &mut *(user_data as *mut S); - event_sink.flush(); - } + let event_sink: &mut S = unsafe { &mut *(user_data as *mut S) }; + event_sink.flush(); } unsafe extern "C" fn call_stop(user_data: *mut c_void) { - unsafe { - let event_sink: &mut S = &mut *(user_data as *mut S); - event_sink.stop(); - } + let event_sink: &mut S = unsafe { &mut *(user_data as *mut S) }; + event_sink.stop(); } unsafe extern "C" fn call_query_mask( user_data: *mut c_void, ) -> frida_gum_sys::GumEventType { - unsafe { - let event_sink: &mut S = &mut *(user_data as *mut S); - event_sink.query_mask() as u32 - } + let event_sink: &mut S = unsafe { &mut *(user_data as *mut S) }; + event_sink.query_mask() as u32 } pub(crate) fn event_sink_transform( diff --git a/frida-gum/src/stalker/observer.rs b/frida-gum/src/stalker/observer.rs index 30396903..9a324bbd 100644 --- a/frida-gum/src/stalker/observer.rs +++ b/frida-gum/src/stalker/observer.rs @@ -23,10 +23,8 @@ unsafe extern "C" fn notify_backpatch( backpatch: *const gum_sys::GumBackpatch, size: gum_sys::gsize, ) { - unsafe { - let stalker_observer: &mut S = &mut *(user_data as *mut S); - stalker_observer.notify_backpatch(backpatch, size); - } + let stalker_observer: &mut S = unsafe { &mut *(user_data as *mut S) }; + stalker_observer.notify_backpatch(backpatch, size); } unsafe extern "C" fn switch_callback( @@ -36,10 +34,10 @@ unsafe extern "C" fn switch_callback( from_insn: gum_sys::gpointer, target: *mut gum_sys::gpointer, ) { - unsafe { - let stalker_observer: &mut S = &mut *(user_data as *mut S); - stalker_observer.switch_callback(from_address, start_address, from_insn, &mut *target); - } + let stalker_observer: &mut S = unsafe { &mut *(user_data as *mut S) }; + stalker_observer.switch_callback(from_address, start_address, from_insn, unsafe { + &mut *target + }); } pub(crate) fn stalker_observer_transform( diff --git a/frida-gum/src/stalker/transformer.rs b/frida-gum/src/stalker/transformer.rs index 8353a03b..06799380 100644 --- a/frida-gum/src/stalker/transformer.rs +++ b/frida-gum/src/stalker/transformer.rs @@ -40,6 +40,16 @@ unsafe extern "C" fn put_callout_destroy(user_data: *mut c_void) { } } +/// Memory-access category reported by [`StalkerIterator::memory_access`]. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(i32)] +pub enum MemoryAccess { + /// Other code is allowed to access the page concurrently. + Open = frida_gum_sys::GumMemoryAccess_GUM_MEMORY_ACCESS_OPEN as _, + /// The instruction has exclusive access to the page. + Exclusive = frida_gum_sys::GumMemoryAccess_GUM_MEMORY_ACCESS_EXCLUSIVE as _, +} + impl<'a> StalkerIterator<'a> { fn from_raw(iterator: *mut frida_gum_sys::GumStalkerIterator) -> StalkerIterator<'a> { StalkerIterator { @@ -69,6 +79,28 @@ impl<'a> StalkerIterator<'a> { pub fn put_chaining_return(&self) { unsafe { frida_gum_sys::gum_stalker_iterator_put_chaining_return(self.iterator) }; } + + /// Get the underlying capstone handle used to decode instructions. + /// + /// The handle is owned by the iterator and is valid only for the lifetime + /// of the current transformer callback. Pass it to capstone's `cs_*` APIs + /// to inspect instructions in greater depth than the default `Insn` view. + pub fn capstone_handle(&self) -> frida_gum_sys::csh { + unsafe { frida_gum_sys::gum_stalker_iterator_get_capstone(self.iterator) } + } + + /// Get the memory-access category of the current instruction. + /// + /// `Exclusive` means the instruction is operating on a region that is + /// guaranteed not to race with other code; `Open` is the default. + pub fn memory_access(&self) -> MemoryAccess { + let raw = unsafe { frida_gum_sys::gum_stalker_iterator_get_memory_access(self.iterator) }; + if raw == frida_gum_sys::GumMemoryAccess_GUM_MEMORY_ACCESS_EXCLUSIVE { + MemoryAccess::Exclusive + } else { + MemoryAccess::Open + } + } } use frida_gum_sys::cs_insn; @@ -116,6 +148,22 @@ impl<'a> Instruction<'a> { pub fn instr(&self) -> &Insn { &self.instr } + + /// Get the capstone handle for decoding (see + /// [`StalkerIterator::capstone_handle`]). + pub fn capstone_handle(&self) -> frida_gum_sys::csh { + unsafe { frida_gum_sys::gum_stalker_iterator_get_capstone(self.parent) } + } + + /// Get the memory-access category of this instruction. + pub fn memory_access(&self) -> MemoryAccess { + let raw = unsafe { frida_gum_sys::gum_stalker_iterator_get_memory_access(self.parent) }; + if raw == frida_gum_sys::GumMemoryAccess_GUM_MEMORY_ACCESS_EXCLUSIVE { + MemoryAccess::Exclusive + } else { + MemoryAccess::Open + } + } } impl<'a> Iterator for StalkerIterator<'a> { diff --git a/frida-gum/src/symbol_util.rs b/frida-gum/src/symbol_util.rs new file mode 100644 index 00000000..5fabeeb7 --- /dev/null +++ b/frida-gum/src/symbol_util.rs @@ -0,0 +1,171 @@ +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Symbol resolution and manipulation utilities. +//! +//! The SymbolUtil module provides functions for resolving symbols by name, +//! finding symbol information by address, and locating functions. + +use { + crate::NativePointer, + core::ffi::{CStr, c_void}, + cstr_core::CString, + frida_gum_sys as gum_sys, +}; + +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; + +#[cfg(feature = "std")] +use std::{string::String, vec::Vec}; + +/// Symbol resolution utilities. +pub struct SymbolUtil; + +impl SymbolUtil { + /// Get the name of the symbol at the specified address. + /// + /// Returns the symbol name if found, or None if no symbol exists at that address. + /// + /// # Examples + /// + /// ```no_run + /// use frida_gum::{SymbolUtil, NativePointer}; + /// + /// let addr = NativePointer(std::ptr::null_mut()); + /// if let Some(name) = SymbolUtil::name_from_address(addr) { + /// println!("Symbol: {}", name); + /// } + /// ``` + pub fn name_from_address(address: NativePointer) -> Option { + let name_ptr = unsafe { gum_sys::gum_symbol_name_from_address(address.0) }; + + if name_ptr.is_null() { + None + } else { + let name = unsafe { + CStr::from_ptr(name_ptr.cast()) + .to_string_lossy() + .into_owned() + }; + unsafe { crate::glib_compat::g_free(name_ptr as *mut c_void) }; + Some(name) + } + } + + /// Find a function by name. + /// + /// Returns the address of the function if found, or None if not found. + /// + /// # Examples + /// + /// ```no_run + /// use frida_gum::SymbolUtil; + /// + /// if let Some(addr) = SymbolUtil::find_function("malloc") { + /// println!("malloc is at {:?}", addr); + /// } + /// ``` + pub fn find_function(name: &str) -> Option { + let name_cstr = match CString::new(name) { + Ok(s) => s, + Err(_) => return None, + }; + + let ptr = unsafe { gum_sys::gum_find_function(name_cstr.as_ptr().cast()) }; + + if ptr.is_null() { + None + } else { + Some(NativePointer(ptr)) + } + } + + /// Find all functions with the specified name. + /// + /// This is useful when multiple functions have the same name (e.g., across + /// different modules or due to name mangling). + /// + /// # Examples + /// + /// ```no_run + /// use frida_gum::SymbolUtil; + /// + /// let addrs = SymbolUtil::find_functions_named("malloc"); + /// for addr in addrs { + /// println!("Found malloc at {:?}", addr); + /// } + /// ``` + pub fn find_functions_named(name: &str) -> Vec { + let name_cstr = match CString::new(name) { + Ok(s) => s, + Err(_) => return Vec::new(), + }; + + let array = unsafe { gum_sys::gum_find_functions_named(name_cstr.as_ptr().cast()) }; + + let mut results = Vec::new(); + if !array.is_null() { + unsafe { + let len = (*array).len as usize; + let data = (*array).data as *const gum_sys::gpointer; + for i in 0..len { + let ptr = *data.add(i); + if !ptr.is_null() { + results.push(NativePointer(ptr)); + } + } + crate::glib_compat::g_array_free(array, gum_sys::true_ as _); + } + } + + results + } + + /// Find all functions matching a pattern. + /// + /// The pattern can include wildcards: + /// - `*` matches any sequence of characters + /// - `?` matches any single character + /// + /// # Examples + /// + /// ```no_run + /// use frida_gum::SymbolUtil; + /// + /// // Find all malloc-related functions + /// let addrs = SymbolUtil::find_functions_matching("*alloc*"); + /// for addr in addrs { + /// println!("Found function at {:?}", addr); + /// } + /// ``` + pub fn find_functions_matching(pattern: &str) -> Vec { + let pattern_cstr = match CString::new(pattern) { + Ok(s) => s, + Err(_) => return Vec::new(), + }; + + let array = unsafe { gum_sys::gum_find_functions_matching(pattern_cstr.as_ptr().cast()) }; + + let mut results = Vec::new(); + if !array.is_null() { + unsafe { + let len = (*array).len as usize; + let data = (*array).data as *const gum_sys::gpointer; + for i in 0..len { + let ptr = *data.add(i); + if !ptr.is_null() { + results.push(NativePointer(ptr)); + } + } + crate::glib_compat::g_array_free(array, gum_sys::true_ as _); + } + } + + results + } +} diff --git a/frida-gum/src/thread.rs b/frida-gum/src/thread.rs index 14e43956..ca93a276 100644 --- a/frida-gum/src/thread.rs +++ b/frida-gum/src/thread.rs @@ -1,5 +1,6 @@ -use core::ffi::CStr; +use core::ffi::{CStr, c_void}; use core::fmt::{self, Debug}; +use core::ptr; use bitflags::bitflags; use frida_gum_sys::{ @@ -14,6 +15,14 @@ use frida_gum_sys::{ use frida_gum_sys::{GumThreadDetails, GumThreadState_GUM_THREAD_RUNNING}; use num::FromPrimitive; +#[cfg(not(feature = "std"))] +use alloc::{string::String, vec::Vec}; + +#[cfg(feature = "std")] +use std::string::String; + +use crate::MemoryRange; + #[cfg(feature = "backtrace")] use crate::Backtracer; use crate::{CpuContext, CpuContextAccess, NativePointer}; @@ -113,6 +122,195 @@ impl Drop for Thread { } } +/// OS-level thread operations. +/// +/// These wrap Frida's process-wide thread manipulation primitives. They do +/// not require an existing [`Thread`] instance because they operate on raw +/// thread IDs. +pub struct ThreadOps; + +impl ThreadOps { + /// Get the calling thread's last system error code. + /// + /// On Windows this is `GetLastError()`; on POSIX systems this is `errno`. + pub fn get_system_error() -> i32 { + unsafe { gum_sys::gum_thread_get_system_error() } + } + + /// Set the calling thread's last system error code. + pub fn set_system_error(value: i32) { + unsafe { gum_sys::gum_thread_set_system_error(value) }; + } + + /// Try to retrieve up to `max` ranges associated with the calling thread. + /// + /// Returns the ranges actually populated (which may be fewer than `max`). + pub fn try_get_ranges(max: u32) -> Vec { + let mut buffer: Vec = Vec::with_capacity(max as usize); + let count = unsafe { + let n = gum_sys::gum_thread_try_get_ranges(buffer.as_mut_ptr(), max); + buffer.set_len(n as usize); + n as usize + }; + let mut out = Vec::with_capacity(count); + for raw in buffer.iter().take(count) { + out.push(MemoryRange::new( + NativePointer(raw.base_address as *mut c_void), + raw.size as usize, + )); + } + out + } + + /// Suspend the specified thread. + /// + /// Returns `true` on success. On failure, `error_message` (if any) will + /// contain a description of what went wrong. + pub fn suspend(thread_id: usize) -> Result<(), ThreadError> { + unsafe { + let mut err: *mut gum_sys::GError = ptr::null_mut(); + let ok = gum_sys::gum_thread_suspend(thread_id as GumThreadId, &mut err) != 0; + check_error(ok, err) + } + } + + /// Resume the specified thread. + pub fn resume(thread_id: usize) -> Result<(), ThreadError> { + unsafe { + let mut err: *mut gum_sys::GError = ptr::null_mut(); + let ok = gum_sys::gum_thread_resume(thread_id as GumThreadId, &mut err) != 0; + check_error(ok, err) + } + } + + /// Set a hardware breakpoint at `address` on the specified thread. + /// + /// `breakpoint_id` selects which of the CPU's breakpoint registers to + /// use (typically 0..=3 on x86 and 0..=15 on AArch64). + pub fn set_hardware_breakpoint( + thread_id: usize, + breakpoint_id: u32, + address: NativePointer, + ) -> Result<(), ThreadError> { + unsafe { + let mut err: *mut gum_sys::GError = ptr::null_mut(); + let ok = gum_sys::gum_thread_set_hardware_breakpoint( + thread_id as GumThreadId, + breakpoint_id, + address.0 as gum_sys::GumAddress, + &mut err, + ) != 0; + check_error(ok, err) + } + } + + /// Clear a previously installed hardware breakpoint. + pub fn unset_hardware_breakpoint( + thread_id: usize, + breakpoint_id: u32, + ) -> Result<(), ThreadError> { + unsafe { + let mut err: *mut gum_sys::GError = ptr::null_mut(); + let ok = gum_sys::gum_thread_unset_hardware_breakpoint( + thread_id as GumThreadId, + breakpoint_id, + &mut err, + ) != 0; + check_error(ok, err) + } + } + + /// Set a hardware watchpoint at `address` of `size` bytes. + /// + /// `conditions` controls whether reads, writes, or both fire the + /// watchpoint. + pub fn set_hardware_watchpoint( + thread_id: usize, + watchpoint_id: u32, + address: NativePointer, + size: usize, + conditions: WatchConditions, + ) -> Result<(), ThreadError> { + unsafe { + let mut err: *mut gum_sys::GError = ptr::null_mut(); + let ok = gum_sys::gum_thread_set_hardware_watchpoint( + thread_id as GumThreadId, + watchpoint_id, + address.0 as gum_sys::GumAddress, + size as gum_sys::gsize, + conditions.bits(), + &mut err, + ) != 0; + check_error(ok, err) + } + } + + /// Clear a previously installed hardware watchpoint. + pub fn unset_hardware_watchpoint( + thread_id: usize, + watchpoint_id: u32, + ) -> Result<(), ThreadError> { + unsafe { + let mut err: *mut gum_sys::GError = ptr::null_mut(); + let ok = gum_sys::gum_thread_unset_hardware_watchpoint( + thread_id as GumThreadId, + watchpoint_id, + &mut err, + ) != 0; + check_error(ok, err) + } + } +} + +bitflags! { + /// Conditions under which a hardware watchpoint fires. + /// + /// Backed by the binding's own `GumWatchConditions` type so the flag values + /// and `bits()` match the FFI parameter on every platform (bindgen types + /// this enum as `i32` on MSVC and `u32` on clang targets). + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct WatchConditions: gum_sys::GumWatchConditions { + /// Fire on reads. + const READ = gum_sys::GumWatchConditions_GUM_WATCH_READ; + /// Fire on writes. + const WRITE = gum_sys::GumWatchConditions_GUM_WATCH_WRITE; + } +} + +/// Error returned by [`ThreadOps`] when an operation fails. +#[derive(Debug)] +pub struct ThreadError { + /// Optional human-readable description of the failure. + pub message: Option, +} + +unsafe fn check_error(ok: bool, err: *mut gum_sys::GError) -> Result<(), ThreadError> { + unsafe { + if ok { + if !err.is_null() { + crate::glib_compat::g_error_free(err); + } + return Ok(()); + } + let message = if !err.is_null() { + let msg = if !(*err).message.is_null() { + Some( + CStr::from_ptr((*err).message) + .to_string_lossy() + .into_owned(), + ) + } else { + None + }; + crate::glib_compat::g_error_free(err); + msg + } else { + None + }; + Err(ThreadError { message }) + } +} + impl Debug for Thread { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Thread") diff --git a/frida-gum/src/tls.rs b/frida-gum/src/tls.rs new file mode 100644 index 00000000..28865d04 --- /dev/null +++ b/frida-gum/src/tls.rs @@ -0,0 +1,62 @@ +/* + * Copyright © 2020-2021 Keegan Saunders + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Thread-local storage keys. +//! +//! Provides a Rust wrapper around Frida's thread-local storage API. Each +//! [`TlsKey`] manages an OS-level TLS slot whose lifetime is tied to the +//! Rust value: dropping the key releases the slot. + +use {crate::NativePointer, frida_gum_sys as gum_sys}; + +/// A thread-local storage key. +/// +/// The slot is freed when the key is dropped. Cloning a key is intentionally +/// not provided — the underlying TLS slot has unique ownership. +pub struct TlsKey { + key: gum_sys::GumTlsKey, +} + +impl TlsKey { + /// Allocate a new TLS slot. + pub fn new() -> Self { + TlsKey { + key: unsafe { gum_sys::gum_tls_key_new() }, + } + } + + /// Read the value of this slot for the current thread. + pub fn get(&self) -> NativePointer { + NativePointer(unsafe { gum_sys::gum_tls_key_get_value(self.key) }) + } + + /// Write a value into this slot for the current thread. + pub fn set(&self, value: NativePointer) { + unsafe { gum_sys::gum_tls_key_set_value(self.key, value.0) }; + } + + /// Get the raw `GumTlsKey` (typically `pthread_key_t` on Unix or `DWORD` + /// on Windows). Provided for FFI use. + pub fn raw(&self) -> gum_sys::GumTlsKey { + self.key + } +} + +impl Drop for TlsKey { + fn drop(&mut self) { + unsafe { gum_sys::gum_tls_key_free(self.key) }; + } +} + +unsafe impl Send for TlsKey {} +unsafe impl Sync for TlsKey {} + +impl Default for TlsKey { + fn default() -> Self { + Self::new() + } +} diff --git a/frida-gum/src/unwind_broker.rs b/frida-gum/src/unwind_broker.rs new file mode 100644 index 00000000..c11ee945 --- /dev/null +++ b/frida-gum/src/unwind_broker.rs @@ -0,0 +1,162 @@ +/* + * Copyright © 2026 Kirby Kuehl + * + * Licence: wxWindows Library Licence, Version 3.1 + */ + +//! Stack-unwinding broker for dynamically generated code. +//! +//! The [`UnwindBroker`] is a process-wide singleton that lets unwinders make +//! sense of code that has no static unwind information — for example, the +//! trampolines and relocated code produced by the [`crate::stalker::Stalker`] +//! and [`crate::interceptor::Interceptor`]. Frida registers its own providers +//! and translators automatically; this wrapper exposes the broker so that +//! additional [`GumUnwindSectionsProvider`]/[`GumUnwindPcTranslator`] +//! implementations can be (de)registered, and the provider/translator vtable +//! entries can be invoked directly. +//! +//! [`GumUnwindSectionsProvider`]: gum_sys::GumUnwindSectionsProvider +//! [`GumUnwindPcTranslator`]: gum_sys::GumUnwindPcTranslator +//! +//! Implementing a *custom* provider or translator from Rust requires a C-side +//! GObject that conforms to the interface vtable; the devkit ships no generic +//! constructor for these, so the registration methods here take raw +//! `*mut Gum…` handles obtained elsewhere (e.g. from Frida internals or a +//! bespoke `frida-gum-sys` shim). + +use { + crate::{MemoryRange, NativePointer}, + core::ffi::c_void, + frida_gum_sys as gum_sys, +}; + +/// Process-wide broker coordinating unwind information for generated code. +pub struct UnwindBroker { + inner: *mut gum_sys::GumUnwindBroker, +} + +impl UnwindBroker { + /// Obtain the global unwind broker. + pub fn obtain() -> Self { + Self { + inner: unsafe { gum_sys::gum_unwind_broker_obtain() }, + } + } + + /// Register a sections provider with the broker. + /// + /// # Safety + /// + /// `provider` must be a valid `GumUnwindSectionsProvider` that outlives its + /// registration (remove it with + /// [`UnwindBroker::remove_sections_provider`] before it is destroyed). + pub unsafe fn add_sections_provider(&self, provider: *mut gum_sys::GumUnwindSectionsProvider) { + unsafe { gum_sys::gum_unwind_broker_add_sections_provider(self.inner, provider) }; + } + + /// Deregister a previously added sections provider. + /// + /// # Safety + /// + /// `provider` must have been registered with + /// [`UnwindBroker::add_sections_provider`] on this broker. + pub unsafe fn remove_sections_provider( + &self, + provider: *mut gum_sys::GumUnwindSectionsProvider, + ) { + unsafe { gum_sys::gum_unwind_broker_remove_sections_provider(self.inner, provider) }; + } + + /// Register a program-counter translator with the broker. + /// + /// # Safety + /// + /// `translator` must be a valid `GumUnwindPcTranslator` that outlives its + /// registration (remove it with [`UnwindBroker::remove_pc_translator`] + /// before it is destroyed). + pub unsafe fn add_pc_translator(&self, translator: *mut gum_sys::GumUnwindPcTranslator) { + unsafe { gum_sys::gum_unwind_broker_add_pc_translator(self.inner, translator) }; + } + + /// Deregister a previously added program-counter translator. + /// + /// # Safety + /// + /// `translator` must have been registered with + /// [`UnwindBroker::add_pc_translator`] on this broker. + pub unsafe fn remove_pc_translator(&self, translator: *mut gum_sys::GumUnwindPcTranslator) { + unsafe { gum_sys::gum_unwind_broker_remove_pc_translator(self.inner, translator) }; + } +} + +impl Drop for UnwindBroker { + fn drop(&mut self) { + unsafe { gum_sys::g_object_unref(self.inner as *mut c_void) }; + } +} + +/// Query the memory range covered by a sections provider. +/// +/// # Safety +/// +/// `provider` must be a valid `GumUnwindSectionsProvider`. +pub unsafe fn sections_provider_range( + provider: *mut gum_sys::GumUnwindSectionsProvider, +) -> Option { + let range = unsafe { gum_sys::gum_unwind_sections_provider_get_range(provider) }; + if range.is_null() { + None + } else { + Some(MemoryRange::from_raw(range)) + } +} + +/// Fill `info` with the unwind sections covering `address`. +/// +/// Returns `true` if the provider supplied sections for the address. +/// +/// # Safety +/// +/// `provider` must be a valid `GumUnwindSectionsProvider` and `info` must point +/// to storage of the layout the provider expects for the host platform. +pub unsafe fn sections_provider_fill( + provider: *mut gum_sys::GumUnwindSectionsProvider, + address: u64, + info: NativePointer, +) -> bool { + unsafe { gum_sys::gum_unwind_sections_provider_fill(provider, address, info.0) != 0 } +} + +/// Translate a code address through a PC translator. +/// +/// # Safety +/// +/// `translator` must be a valid `GumUnwindPcTranslator`. +pub unsafe fn pc_translator_translate( + translator: *mut gum_sys::GumUnwindPcTranslator, + code_address: u64, +) -> u64 { + unsafe { gum_sys::gum_unwind_pc_translator_translate(translator, code_address) } +} + +/// Install a resume context for a translated address. +/// +/// Returns `true` if the translator installed the context. +/// +/// # Safety +/// +/// `translator` must be a valid `GumUnwindPcTranslator` and `unwind_context` +/// must point to a valid host unwind context. +pub unsafe fn pc_translator_install_resume_context( + translator: *mut gum_sys::GumUnwindPcTranslator, + unwind_context: NativePointer, + real_resume_ip: u64, +) -> bool { + unsafe { + gum_sys::gum_unwind_pc_translator_install_resume_context( + translator, + unwind_context.0, + real_resume_ip, + ) != 0 + } +} diff --git a/frida-sys/FRIDA_VERSION b/frida-sys/FRIDA_VERSION index b6e9aafb..e9a51186 100644 --- a/frida-sys/FRIDA_VERSION +++ b/frida-sys/FRIDA_VERSION @@ -1 +1 @@ -17.15.3 +17.16.1 diff --git a/frida/src/script.rs b/frida/src/script.rs index 478fd7da..d64a8c63 100644 --- a/frida/src/script.rs +++ b/frida/src/script.rs @@ -147,7 +147,7 @@ unsafe extern "C" fn call_on_message( } } } - } + } // end unsafe } fn on_message(cb_handler: &mut CallbackHandler, message: Message) { diff --git a/frida/src/variant.rs b/frida/src/variant.rs index 5314f6ac..d3caad76 100644 --- a/frida/src/variant.rs +++ b/frida/src/variant.rs @@ -19,6 +19,9 @@ pub enum Variant { /// Array of Maps MapList(Vec>), + /// Array of strings (e.g. the `argv` process parameter added in Frida 17.13). + StringList(Vec), + /// GVariant type signatures we don't decode (e.g. "ay" byte arrays /// returned by frida for process icons). The string holds the original /// type signature so callers can identify and skip. @@ -52,6 +55,7 @@ impl Variant { "t" => Self::Int64(frida_sys::g_variant_get_uint64(variant) as i64), "a{sv}" => Self::Map(sv_array_to_map(variant)), "aa{sv}" => Self::MapList(asv_array_to_maplist(variant)), + "as" => Self::StringList(string_array_to_vec(variant)), // Don't panic on unknown signatures; preserve the sig so callers // can `Variant::Unsupported(sig)` and decide. Frida on Windows // packs process icons as "ay" which we have no use for. @@ -93,6 +97,14 @@ impl Variant { }; Some(l) } + + /// Get the string-list value of a variant, if any (e.g. `argv`). + pub fn get_string_list(&self) -> Option<&[String]> { + let Self::StringList(l) = self else { + return None; + }; + Some(l) + } } impl std::fmt::Debug for Variant { @@ -103,6 +115,7 @@ impl std::fmt::Debug for Variant { Self::Boolean(b) => b.fmt(f), Self::Map(m) => m.fmt(f), Self::MapList(l) => l.fmt(f), + Self::StringList(l) => l.fmt(f), Self::Unsupported(sig) => write!(f, ""), } } @@ -135,6 +148,21 @@ unsafe fn sv_array_to_map(variant: *mut frida_sys::GVariant) -> HashMap Vec { + unsafe { + let mut ret = Vec::new(); + let mut iter: frida_sys::GVariantIter = std::mem::MaybeUninit::zeroed().assume_init(); + let mut value: *const i8 = std::ptr::null(); + + frida_sys::g_variant_iter_init(&mut iter, variant); + let s = CString::new("s").unwrap(); + while frida_sys::g_variant_iter_loop(&mut iter, s.as_ptr(), &mut value) != 0 { + ret.push(CStr::from_ptr(value.cast()).to_string_lossy().into_owned()); + } + ret + } +} + unsafe fn asv_array_to_maplist(variant: *mut frida_sys::GVariant) -> Vec> { unsafe { let mut ret = Vec::new();