mirror of
https://github.com/frida/frida-core.git
synced 2026-11-08 08:05:58 -10:00
Compare commits
No files matched your search
@@ -451,7 +451,10 @@ jobs:
|
||||
cd build
|
||||
cp -a tests/frida-tests tests/labrats lib/agent/frida-agent.so /tmp/pkg/
|
||||
tar -C /tmp/pkg -cf /tmp/runner.tar .
|
||||
/opt/sabrelite/run.sh /tmp/runner.tar /opt/frida/frida-tests
|
||||
/opt/sabrelite/run.sh /tmp/runner.tar \
|
||||
"cp /opt/frida/frida-tests /opt/frida/frida-agent.so /tmp/ && \
|
||||
ln -sP /opt/frida/labrats /tmp/labrats && \
|
||||
/tmp/frida-tests"
|
||||
|
||||
# This job is used to check that the Linux helpers are built correctly for all
|
||||
# supported architectures. We do this because the binary artifacts are checked
|
||||
|
||||
@@ -43,7 +43,7 @@ platform-dependent (e.g. `linux-x86_64`).
|
||||
## Build Requirements
|
||||
|
||||
- **Vala compiler**: Must be the Frida-optimized fork from
|
||||
https://github.com/frida/vala (checks for `-frida` suffix
|
||||
https://github.com/frida-forks/vala (checks for `-frida` suffix
|
||||
in version)
|
||||
- **C/C++ standards**: gnu99/c99 for C, c++17 for C++
|
||||
- **Go >= 1.24**: Required for compiler backend
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PYTHON ?= $(shell which python3 >/dev/null && echo python3 || echo python)
|
||||
PYTHON ?= $(shell command -v python3 >/dev/null && echo python3 || echo python)
|
||||
|
||||
all $(MAKECMDGOALS):
|
||||
@$(PYTHON) \
|
||||
@@ -8,7 +8,7 @@ all $(MAKECMDGOALS):
|
||||
$(MAKECMDGOALS)
|
||||
|
||||
git-submodules:
|
||||
@if [ ! -f releng/meson/meson.py ]; then \
|
||||
@if [ ! -f releng/meson/meson.py ] || git submodule status 2>/dev/null | grep -q '^-'; then \
|
||||
git submodule update --init --recursive --depth 1; \
|
||||
fi
|
||||
-include git-submodules
|
||||
|
||||
+15
-11
@@ -19,7 +19,7 @@ import traceback
|
||||
from typing import Any, Literal, Mapping, Optional, Sequence, Tuple
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
REPO_ROOT = Path(__file__).absolute().parent.parent
|
||||
NINJA = os.environ.get("NINJA", "ninja")
|
||||
|
||||
|
||||
@@ -592,8 +592,10 @@ def load_meson_options(top_builddir: Path,
|
||||
subprojects: set[str]) -> Sequence[str]:
|
||||
from mesonbuild import coredata
|
||||
|
||||
return [f"-D{adapt_key(k, role)}={v.value}" for k, v in coredata.load(top_builddir).options.items()
|
||||
if option_should_be_forwarded(k, v, role, subprojects)]
|
||||
optstore = coredata.load(top_builddir).optstore
|
||||
|
||||
return [f"-D{adapt_key(k, role)}={v.value}" for k, v in optstore.options.items()
|
||||
if option_should_be_forwarded(k, v, optstore, role, subprojects)]
|
||||
|
||||
|
||||
def adapt_key(k: "OptionKey", role: Role) -> "OptionKey":
|
||||
@@ -603,44 +605,46 @@ def adapt_key(k: "OptionKey", role: Role) -> "OptionKey":
|
||||
|
||||
|
||||
def option_should_be_forwarded(k: "OptionKey",
|
||||
v: "coredata.UserOption[Any]",
|
||||
v: "options.UserOption[Any]",
|
||||
optstore: "options.OptionStore",
|
||||
role: Role,
|
||||
subprojects: set[str]) -> bool:
|
||||
from mesonbuild import coredata
|
||||
from mesonbuild import options
|
||||
from mesonbuild.mesonlib import MachineChoice
|
||||
|
||||
our_project_id = "frida-core" if role == "subproject" else ""
|
||||
is_for_us = k.subproject == our_project_id
|
||||
is_for_child = k.subproject in subprojects
|
||||
|
||||
if k.is_project():
|
||||
if optstore.is_project_option(k):
|
||||
if is_for_us:
|
||||
if k.name == "compiler_backend":
|
||||
return False
|
||||
tokens = k.name.split("_")
|
||||
if tokens[0] in {"helper", "agent"} and tokens[-1] in {"modern", "legacy"}:
|
||||
return False
|
||||
if k.subproject and k.machine is not coredata.MachineChoice.HOST:
|
||||
if k.subproject and k.machine is not MachineChoice.HOST:
|
||||
return False
|
||||
return is_for_us or is_for_child
|
||||
|
||||
if coredata.CoreData.is_per_machine_option(k):
|
||||
if optstore.is_per_machine_option(k):
|
||||
# The compat build generates its own machine files, so forwarding the
|
||||
# parent's build-machine options would inject a foreign toolchain. In a
|
||||
# native compat build that conjures a compiler-less build machine and
|
||||
# breaks any native: true target.
|
||||
return False
|
||||
|
||||
if k.is_builtin():
|
||||
if optstore.is_builtin_option(k):
|
||||
if k.name in {"buildtype", "genvslite"}:
|
||||
return False
|
||||
if not str(v.value):
|
||||
return False
|
||||
if not is_for_child and role == "subproject" and k.subproject in {"", our_project_id}:
|
||||
if not coredata.BUILTIN_OPTIONS[k.as_root()].yielding:
|
||||
if not options.BUILTIN_OPTIONS[k.evolve(subproject=None, machine=MachineChoice.HOST)].yielding:
|
||||
return k.subproject == our_project_id
|
||||
return True
|
||||
|
||||
if k.module == "python":
|
||||
if k.get_module_prefix() == "python":
|
||||
if k.name == "install_env" and v.value == "prefix":
|
||||
return False
|
||||
if not str(v.value):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -z "$PYTHON" ] && PYTHON=$(which python3 >/dev/null && echo python3 || echo python)
|
||||
[ -z "$PYTHON" ] && PYTHON=$(command -v python3 >/dev/null && echo python3 || echo python)
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
srcroot=$(pwd)
|
||||
|
||||
if [ ! -f releng/meson/meson.py ]; then
|
||||
if [ ! -f releng/meson/meson.py ] || git submodule status 2>/dev/null | grep -q '^-'; then
|
||||
git submodule update --init --recursive --depth 1 || exit $?
|
||||
fi
|
||||
|
||||
|
||||
+394
-35
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida.Agent {
|
||||
public void main (string agent_parameters, ref Frida.UnloadPolicy unload_policy, void * injector_state) {
|
||||
if (Runner.shared_instance == null)
|
||||
@@ -78,6 +80,7 @@ namespace Frida.Agent {
|
||||
private ExitMonitor? exit_monitor;
|
||||
private Gum.Interceptor interceptor;
|
||||
private Gum.Exceptor? exceptor;
|
||||
private bool code_memory_delegated = false;
|
||||
|
||||
private uint child_gating_subscriber_count = 0;
|
||||
private ForkMonitor? fork_monitor;
|
||||
@@ -100,6 +103,9 @@ namespace Frida.Agent {
|
||||
private SpawnMonitor? spawn_monitor;
|
||||
private ThreadSuspendMonitor? thread_suspend_monitor;
|
||||
private UnwindSitter? unwind_sitter;
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
private NativeCloakMirror? native_cloak_mirror;
|
||||
#endif
|
||||
|
||||
private delegate void CompletionNotify ();
|
||||
|
||||
@@ -122,12 +128,37 @@ namespace Frida.Agent {
|
||||
{
|
||||
Gum.MemoryRange? mapped_range = null;
|
||||
|
||||
#if WINDOWS
|
||||
var windows_state = (WindowsInjectorState *) opaque_injector_state;
|
||||
if (windows_state != null) {
|
||||
if (windows_state.mapped_range.size != 0)
|
||||
mapped_range = windows_state.mapped_range;
|
||||
Gum.Cloak.add_range (windows_state.worker_range);
|
||||
if (windows_state.dbghelp_range.size != 0) {
|
||||
Gum.Cloak.add_range (windows_state.dbghelp_range);
|
||||
Gum.Cloak.add_range (windows_state.symsrv_range);
|
||||
Gum.Windows.use_dbghelp_modules ((void *) windows_state.dbghelp_range.base_address,
|
||||
(void *) windows_state.symsrv_range.base_address);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DARWIN
|
||||
var injector_state = (DarwinInjectorState *) opaque_injector_state;
|
||||
if (injector_state != null)
|
||||
mapped_range = injector_state.mapped_range;
|
||||
#endif
|
||||
|
||||
#if LINUX
|
||||
var linux_state = (LinuxInjectorState *) opaque_injector_state;
|
||||
if (linux_state != null && linux_state.mapped_size != 0) {
|
||||
mapped_range = Gum.MemoryRange () {
|
||||
base_address = linux_state.mapped_base,
|
||||
size = linux_state.mapped_size,
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PROSPERO
|
||||
var prospero_state = (ProsperoInjectorState *) opaque_injector_state;
|
||||
if (prospero_state != null)
|
||||
@@ -199,6 +230,15 @@ namespace Frida.Agent {
|
||||
return;
|
||||
} else {
|
||||
release_shared_instance ();
|
||||
#if WINDOWS
|
||||
if (windows_state != null) {
|
||||
Gum.Cloak.remove_range (windows_state.worker_range);
|
||||
if (windows_state.dbghelp_range.size != 0) {
|
||||
Gum.Cloak.remove_range (windows_state.dbghelp_range);
|
||||
Gum.Cloak.remove_range (windows_state.symsrv_range);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ignore_scope = null;
|
||||
@@ -285,25 +325,12 @@ namespace Frida.Agent {
|
||||
}
|
||||
|
||||
~Runner () {
|
||||
var interceptor = this.interceptor;
|
||||
interceptor.begin_transaction ();
|
||||
remove_instrumentation ();
|
||||
|
||||
disable_child_gating ();
|
||||
|
||||
exceptor = null;
|
||||
|
||||
exit_monitor = null;
|
||||
|
||||
interceptor.end_transaction ();
|
||||
|
||||
interceptor.begin_transaction ();
|
||||
|
||||
thread_suspend_monitor = null;
|
||||
unwind_sitter = null;
|
||||
if (code_memory_delegated)
|
||||
Gum.Memory.set_hooks (null);
|
||||
|
||||
invalidate_dbus_context ();
|
||||
|
||||
interceptor.end_transaction ();
|
||||
}
|
||||
|
||||
private void run (owned FileDescriptorTablePadder padder) throws Error {
|
||||
@@ -326,6 +353,9 @@ namespace Frida.Agent {
|
||||
bool enable_unwind_broker = true;
|
||||
bool enable_exit_monitor = true;
|
||||
bool enable_thread_suspend_monitor = true;
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
bool in_emulated_realm = false;
|
||||
#endif
|
||||
foreach (unowned string option in tokens[1:]) {
|
||||
if (option == "eternal")
|
||||
ensure_eternalized ();
|
||||
@@ -341,38 +371,47 @@ namespace Frida.Agent {
|
||||
enable_exit_monitor = false;
|
||||
else if (option == "thread-suspend-monitor:off")
|
||||
enable_thread_suspend_monitor = false;
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
else if (option == "realm:emulated")
|
||||
in_emulated_realm = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (exceptor_mode != FULL)
|
||||
Gum.Exceptor.set_mode (exceptor_mode);
|
||||
|
||||
{
|
||||
var interceptor = Gum.Interceptor.obtain ();
|
||||
interceptor.begin_transaction ();
|
||||
|
||||
if (enable_exit_monitor)
|
||||
exit_monitor = new ExitMonitor (this, main_context);
|
||||
|
||||
if (enable_thread_suspend_monitor)
|
||||
thread_suspend_monitor = new ThreadSuspendMonitor (this);
|
||||
|
||||
if (enable_unwind_broker)
|
||||
unwind_sitter = new UnwindSitter (this);
|
||||
|
||||
this.interceptor = interceptor;
|
||||
this.exceptor = Gum.Exceptor.obtain ();
|
||||
|
||||
interceptor.end_transaction ();
|
||||
}
|
||||
interceptor = Gum.Interceptor.obtain ();
|
||||
|
||||
try {
|
||||
yield setup_connection_with_transport_uri (transport_uri);
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
if (in_emulated_realm)
|
||||
yield connect_to_native_realm ();
|
||||
#endif
|
||||
} catch (Error e) {
|
||||
start_error = e;
|
||||
main_loop.quit ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!can_allocate_code ())
|
||||
delegate_code_memory_to_controller ();
|
||||
|
||||
interceptor.begin_transaction ();
|
||||
|
||||
if (enable_exit_monitor)
|
||||
exit_monitor = new ExitMonitor (this, main_context);
|
||||
|
||||
if (enable_thread_suspend_monitor)
|
||||
thread_suspend_monitor = new ThreadSuspendMonitor (this);
|
||||
|
||||
if (enable_unwind_broker)
|
||||
unwind_sitter = new UnwindSitter (this);
|
||||
|
||||
exceptor = Gum.Exceptor.obtain ();
|
||||
|
||||
interceptor.end_transaction ();
|
||||
|
||||
Gum.ScriptBackend.get_scheduler ().push_job_on_js_thread (Priority.DEFAULT, () => {
|
||||
schedule_idle (start.callback);
|
||||
});
|
||||
@@ -381,6 +420,77 @@ namespace Frida.Agent {
|
||||
padder = null;
|
||||
}
|
||||
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
private async void connect_to_native_realm () throws Error {
|
||||
try {
|
||||
NativeCloak cloak = yield connection.get_proxy (null, ObjectPath.NATIVE_CLOAK, DO_NOT_LOAD_PROPERTIES,
|
||||
null);
|
||||
native_cloak_mirror = new NativeCloakMirror (cloak);
|
||||
native_cloak_mirror.activate ();
|
||||
|
||||
NativeThreadRegistry registry = yield connection.get_proxy (null, ObjectPath.NATIVE_THREAD_REGISTRY,
|
||||
DO_NOT_LOAD_PROPERTIES, null);
|
||||
Gum.ThreadRegistry.set_provider (new NativeThreadProvider (registry));
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private static bool can_allocate_code () {
|
||||
size_t page_size = Gum.query_page_size ();
|
||||
|
||||
void * page = Gum.Memory.allocate (null, page_size, page_size, READ | EXECUTE);
|
||||
if (page == null)
|
||||
return false;
|
||||
|
||||
Gum.Memory.free (page, page_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void delegate_code_memory_to_controller () {
|
||||
var hooks = Gum.MemoryHooks () {
|
||||
allocate = allocate_via_controller,
|
||||
protect = protect_via_controller,
|
||||
user_data = this,
|
||||
};
|
||||
Gum.Memory.set_hooks (hooks);
|
||||
|
||||
code_memory_delegated = true;
|
||||
}
|
||||
|
||||
private static void * allocate_via_controller (Gum.AddressSpec? spec, size_t size, size_t alignment,
|
||||
Gum.PageProtection prot, void * user_data) {
|
||||
unowned Runner self = (Runner) user_data;
|
||||
|
||||
uint64 near_address = 0;
|
||||
uint64 max_distance = 0;
|
||||
if (spec != null) {
|
||||
near_address = (uint64) spec.near_address;
|
||||
max_distance = spec.max_distance;
|
||||
}
|
||||
|
||||
try {
|
||||
return (void *) self.controller.allocate_memory (get_process_id (), near_address, max_distance, size,
|
||||
alignment, prot, null);
|
||||
} catch (GLib.Error e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool protect_via_controller (void * address, size_t size, Gum.PageProtection prot,
|
||||
void * user_data) {
|
||||
unowned Runner self = (Runner) user_data;
|
||||
|
||||
try {
|
||||
self.controller.protect_memory (get_process_id (), (uint64) address, size, prot, null);
|
||||
return true;
|
||||
} catch (GLib.Error e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void keep_running_eternalized () {
|
||||
agent_gthread = new Thread<bool> ("frida-eternal-agent", () => {
|
||||
var ignore_scope = new ThreadIgnoreScope (FRIDA_THREAD);
|
||||
@@ -1042,6 +1152,10 @@ namespace Frida.Agent {
|
||||
if (unloading)
|
||||
throw new Error.INVALID_OPERATION ("Agent is already unloading");
|
||||
unloading = true;
|
||||
|
||||
if (code_memory_delegated && !is_eternal)
|
||||
yield remove_instrumentation_while_delegated ();
|
||||
|
||||
perform_unload.begin ();
|
||||
}
|
||||
|
||||
@@ -1087,6 +1201,34 @@ namespace Frida.Agent {
|
||||
}
|
||||
}
|
||||
|
||||
private async void remove_instrumentation_while_delegated () {
|
||||
yield close_all_sessions ();
|
||||
|
||||
remove_instrumentation ();
|
||||
}
|
||||
|
||||
private void remove_instrumentation () {
|
||||
var interceptor = this.interceptor;
|
||||
interceptor.begin_transaction ();
|
||||
|
||||
disable_child_gating ();
|
||||
|
||||
exceptor = null;
|
||||
|
||||
exit_monitor = null;
|
||||
|
||||
interceptor.end_transaction ();
|
||||
|
||||
interceptor.begin_transaction ();
|
||||
|
||||
thread_suspend_monitor = null;
|
||||
unwind_sitter = null;
|
||||
|
||||
interceptor.end_transaction ();
|
||||
|
||||
Gum.shutdown ();
|
||||
}
|
||||
|
||||
private void ensure_eternalized () {
|
||||
if (!_is_eternal) {
|
||||
_is_eternal = true;
|
||||
@@ -1295,7 +1437,7 @@ namespace Frida.Agent {
|
||||
|
||||
private void on_connection_closed (DBusConnection connection, bool remote_peer_vanished, GLib.Error? error) {
|
||||
bool closed_by_us = !remote_peer_vanished && error == null;
|
||||
if (!closed_by_us)
|
||||
if (!closed_by_us && !code_memory_delegated)
|
||||
unload.begin (null);
|
||||
|
||||
Promise<bool> operation = null;
|
||||
@@ -1367,6 +1509,8 @@ namespace Frida.Agent {
|
||||
private Socket? emulated_socket;
|
||||
private BridgeState? emulated_bridge_state;
|
||||
private Thread<void>? emulated_worker;
|
||||
private NativeCloakService? native_cloak;
|
||||
private NativeThreadRegistryService? native_thread_registry;
|
||||
|
||||
private async AgentSessionProvider? try_get_emulated_provider (Cancellable? cancellable) throws IOError {
|
||||
if (get_emulated_request == null)
|
||||
@@ -1406,6 +1550,8 @@ namespace Frida.Agent {
|
||||
emulated_agent = nb_api.load_library_ext (emulated_agent_path, RTLD_LAZY, classloader_namespace);
|
||||
} else {
|
||||
emulated_agent = nb_api.load_library (emulated_agent_path, RTLD_LAZY);
|
||||
if (emulated_agent == null && nb_api.load_library_ext != null)
|
||||
emulated_agent = nb_api.load_library_ext (emulated_agent_path, RTLD_LAZY, null);
|
||||
}
|
||||
if (emulated_agent == null)
|
||||
throw new Error.NOT_SUPPORTED ("Process is not using emulation");
|
||||
@@ -1445,6 +1591,7 @@ namespace Frida.Agent {
|
||||
* code being discarded, which seems like an edge-case the translator doesn't handle.
|
||||
*/
|
||||
parameters.append ("|exceptor:off|exit-monitor:off");
|
||||
parameters.append ("|realm:emulated");
|
||||
|
||||
emulated_bridge_state = new BridgeState (parameters.str);
|
||||
|
||||
@@ -1453,6 +1600,13 @@ namespace Frida.Agent {
|
||||
var connection = yield new DBusConnection (stream, ServerGuid.HOST_SESSION_SERVICE,
|
||||
AUTHENTICATION_SERVER | AUTHENTICATION_ALLOW_ANONYMOUS, null, cancellable);
|
||||
|
||||
native_cloak = new NativeCloakService ();
|
||||
connection.register_object (ObjectPath.NATIVE_CLOAK, (NativeCloak) native_cloak);
|
||||
|
||||
native_thread_registry = new NativeThreadRegistryService ();
|
||||
connection.register_object (ObjectPath.NATIVE_THREAD_REGISTRY,
|
||||
(NativeThreadRegistry) native_thread_registry);
|
||||
|
||||
AgentSessionProvider provider = yield connection.get_proxy (null, ObjectPath.AGENT_SESSION_PROVIDER,
|
||||
DO_NOT_LOAD_PROPERTIES, cancellable);
|
||||
|
||||
@@ -1500,6 +1654,16 @@ namespace Frida.Agent {
|
||||
emulated_worker = null;
|
||||
}
|
||||
|
||||
if (native_thread_registry != null) {
|
||||
native_thread_registry.close ();
|
||||
native_thread_registry = null;
|
||||
}
|
||||
|
||||
if (native_cloak != null) {
|
||||
native_cloak.close ();
|
||||
native_cloak = null;
|
||||
}
|
||||
|
||||
emulated_socket = null;
|
||||
|
||||
if (emulated_agent != null) {
|
||||
@@ -1660,6 +1824,201 @@ namespace Frida.Agent {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ANDROID && (X86 || X86_64)
|
||||
private sealed class NativeCloakService : Object, NativeCloak, Gum.Cloak.Observer {
|
||||
private MainContext main_context = MainContext.ref_thread_default ();
|
||||
private bool observing = false;
|
||||
|
||||
public uint[] activate (Cancellable? cancellable) {
|
||||
Gum.Cloak.add_observer (this);
|
||||
observing = true;
|
||||
|
||||
uint[] threads = {};
|
||||
Gum.Cloak.enumerate_threads (id => {
|
||||
threads += (uint) id;
|
||||
return true;
|
||||
});
|
||||
return threads;
|
||||
}
|
||||
|
||||
public void close () {
|
||||
if (!observing)
|
||||
return;
|
||||
|
||||
Gum.Cloak.remove_observer (this);
|
||||
observing = false;
|
||||
}
|
||||
|
||||
public void thread_added (Gum.ThreadId id) {
|
||||
schedule (() => {
|
||||
thread_cloaked ((uint) id);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
public void thread_removed (Gum.ThreadId id) {
|
||||
schedule (() => {
|
||||
thread_uncloaked ((uint) id);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
private void schedule (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
source.attach (main_context);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NativeThreadRegistryService : Object, NativeThreadRegistry {
|
||||
private unowned Gum.ThreadRegistry? registry;
|
||||
private ulong added_handler = 0;
|
||||
private ulong removed_handler = 0;
|
||||
private ulong renamed_handler = 0;
|
||||
|
||||
public NativeThreadInfo[] activate (Cancellable? cancellable) {
|
||||
registry = Gum.ThreadRegistry.obtain ();
|
||||
|
||||
NativeThreadInfo[] threads = {};
|
||||
|
||||
registry.lock ();
|
||||
|
||||
added_handler = registry.thread_added.connect (on_thread_added);
|
||||
removed_handler = registry.thread_removed.connect (on_thread_removed);
|
||||
renamed_handler = registry.thread_renamed.connect (on_thread_renamed);
|
||||
|
||||
registry.enumerate_threads (thread => {
|
||||
threads += NativeThreadInfo ((uint) thread.id, thread.name ?? "");
|
||||
return true;
|
||||
});
|
||||
|
||||
registry.unlock ();
|
||||
|
||||
return threads;
|
||||
}
|
||||
|
||||
public void deactivate (Cancellable? cancellable) {
|
||||
close ();
|
||||
}
|
||||
|
||||
public void close () {
|
||||
if (registry == null)
|
||||
return;
|
||||
|
||||
registry.disconnect (added_handler);
|
||||
registry.disconnect (removed_handler);
|
||||
registry.disconnect (renamed_handler);
|
||||
registry = null;
|
||||
}
|
||||
|
||||
private void on_thread_added (Gum.ThreadDetails thread) {
|
||||
thread_added (NativeThreadInfo ((uint) thread.id, thread.name ?? ""));
|
||||
}
|
||||
|
||||
private void on_thread_removed (Gum.ThreadDetails thread) {
|
||||
thread_removed ((uint) thread.id);
|
||||
}
|
||||
|
||||
private void on_thread_renamed (Gum.ThreadDetails thread, string? previous_name) {
|
||||
thread_renamed ((uint) thread.id, thread.name ?? "");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
private sealed class NativeCloakMirror : Object {
|
||||
public NativeCloak native_cloak {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public NativeCloakMirror (NativeCloak native_cloak) {
|
||||
Object (native_cloak: native_cloak);
|
||||
}
|
||||
|
||||
public void activate () throws GLib.Error {
|
||||
native_cloak.thread_cloaked.connect (on_thread_cloaked);
|
||||
native_cloak.thread_uncloaked.connect (on_thread_uncloaked);
|
||||
|
||||
foreach (uint id in native_cloak.activate (null))
|
||||
on_thread_cloaked (id);
|
||||
}
|
||||
|
||||
private void on_thread_cloaked (uint id) {
|
||||
if (!Gum.Cloak.has_thread (id))
|
||||
Gum.Cloak.add_thread (id);
|
||||
}
|
||||
|
||||
private void on_thread_uncloaked (uint id) {
|
||||
Gum.Cloak.remove_thread (id);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NativeThreadProvider : Object, Gum.ThreadProvider {
|
||||
public NativeThreadRegistry native_registry {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private unowned Gum.ThreadRegistry registry;
|
||||
private ulong added_handler = 0;
|
||||
private ulong removed_handler = 0;
|
||||
private ulong renamed_handler = 0;
|
||||
|
||||
public NativeThreadProvider (NativeThreadRegistry native_registry) {
|
||||
Object (native_registry: native_registry);
|
||||
}
|
||||
|
||||
public void activate (Gum.ThreadRegistry registry) {
|
||||
this.registry = registry;
|
||||
|
||||
added_handler = native_registry.thread_added.connect (add_thread);
|
||||
removed_handler = native_registry.thread_removed.connect (on_thread_removed);
|
||||
renamed_handler = native_registry.thread_renamed.connect (on_thread_renamed);
|
||||
|
||||
NativeThreadInfo[] threads;
|
||||
try {
|
||||
threads = native_registry.activate (null);
|
||||
} catch (GLib.Error e) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var thread in threads)
|
||||
add_thread (thread);
|
||||
}
|
||||
|
||||
public void deactivate () {
|
||||
native_registry.disconnect (added_handler);
|
||||
native_registry.disconnect (removed_handler);
|
||||
native_registry.disconnect (renamed_handler);
|
||||
|
||||
try {
|
||||
native_registry.deactivate (null);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private void add_thread (NativeThreadInfo thread) {
|
||||
var details = Gum.ThreadDetails ();
|
||||
details.id = thread.id;
|
||||
if (thread.name != "") {
|
||||
details.name = thread.name;
|
||||
details.flags = NAME;
|
||||
}
|
||||
|
||||
registry.register (details);
|
||||
}
|
||||
|
||||
private void on_thread_removed (uint id) {
|
||||
registry.unregister (id);
|
||||
}
|
||||
|
||||
private void on_thread_renamed (uint id, string name) {
|
||||
registry.rename (id, (name != "") ? name : null);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private sealed class LiveAgentSession : BaseAgentSession {
|
||||
public uint registration_id {
|
||||
get;
|
||||
|
||||
@@ -25,6 +25,9 @@ if build_agent
|
||||
if host_os_family != 'windows'
|
||||
extra_vala_args += '--pkg=posix'
|
||||
endif
|
||||
if host_os_family == 'windows'
|
||||
extra_vala_args += '--pkg=frida-gum-windows-1.0'
|
||||
endif
|
||||
if host_os_family == 'darwin'
|
||||
extra_vala_args += '--pkg=frida-gum-darwin-1.0'
|
||||
endif
|
||||
@@ -93,8 +96,6 @@ if build_agent
|
||||
install: assets_mode == 'installed' and not need_universal_agent,
|
||||
install_dir: asset_dir,
|
||||
)
|
||||
|
||||
agent_dep = declare_dependency(link_with: agent)
|
||||
endif
|
||||
|
||||
if need_universal_agent
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#include "frida-base.h"
|
||||
|
||||
GBytes *
|
||||
frida_make_bytes_with_owner (GType t_type,
|
||||
GBoxedCopyFunc t_dup_func,
|
||||
GDestroyNotify t_destroy_func,
|
||||
void * data,
|
||||
gsize size,
|
||||
gpointer owner)
|
||||
{
|
||||
return g_bytes_new_with_free_func (data, size, t_destroy_func, owner);
|
||||
}
|
||||
@@ -1,797 +0,0 @@
|
||||
namespace Frida {
|
||||
public sealed class BufferBuilder : Object {
|
||||
public ByteOrder byte_order {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public uint pointer_size {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public size_t offset {
|
||||
get {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
private ByteArray buffer = new ByteArray ();
|
||||
private size_t cursor = 0;
|
||||
|
||||
private uint64 base_address = 0;
|
||||
private Gee.List<LabelRef>? label_refs;
|
||||
private Gee.Map<string, uint>? label_defs;
|
||||
|
||||
public BufferBuilder (ByteOrder byte_order = HOST, uint pointer_size = (uint) sizeof (size_t)) {
|
||||
Object (
|
||||
byte_order: byte_order,
|
||||
pointer_size: pointer_size
|
||||
);
|
||||
}
|
||||
|
||||
public unowned BufferBuilder seek (size_t offset) {
|
||||
if (buffer.len < offset) {
|
||||
size_t n = offset - buffer.len;
|
||||
Memory.set (get_pointer (offset - n, n), 0, n);
|
||||
}
|
||||
cursor = offset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder skip (size_t n) {
|
||||
seek (cursor + n);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder align (size_t n) {
|
||||
size_t remainder = cursor % n;
|
||||
if (remainder != 0)
|
||||
skip (n - remainder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_pointer (uint64 val) {
|
||||
write_pointer (cursor, val);
|
||||
cursor += pointer_size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_pointer_to_label (string name) {
|
||||
if (label_refs == null)
|
||||
label_refs = new Gee.ArrayList<LabelRef> ();
|
||||
label_refs.add (new LabelRef (name, cursor));
|
||||
return skip (pointer_size);
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_pointer_to_label_if (bool present, string name) {
|
||||
if (present)
|
||||
append_pointer_to_label (name);
|
||||
else
|
||||
append_pointer (0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_label (string name) throws Error {
|
||||
if (label_defs == null)
|
||||
label_defs = new Gee.HashMap<string, uint> ();
|
||||
if (label_defs.has_key (name))
|
||||
throw new Error.INVALID_ARGUMENT ("Label '%s' already exists", name);
|
||||
label_defs[name] = (uint) cursor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_size (uint64 val) {
|
||||
return append_pointer (val);
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_int8 (int8 val) {
|
||||
write_int8 (cursor, val);
|
||||
cursor += (uint) sizeof (int8);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_uint8 (uint8 val) {
|
||||
write_uint8 (cursor, val);
|
||||
cursor += (uint) sizeof (uint8);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_int16 (int16 val) {
|
||||
write_int16 (cursor, val);
|
||||
cursor += (uint) sizeof (int16);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_uint16 (uint16 val) {
|
||||
write_uint16 (cursor, val);
|
||||
cursor += (uint) sizeof (uint16);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_int32 (int32 val) {
|
||||
write_int32 (cursor, val);
|
||||
cursor += (uint) sizeof (int32);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_uint32 (uint32 val) {
|
||||
write_uint32 (cursor, val);
|
||||
cursor += (uint) sizeof (uint32);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_int64 (int64 val) {
|
||||
write_int64 (cursor, val);
|
||||
cursor += (uint) sizeof (int64);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_uint64 (uint64 val) {
|
||||
write_uint64 (cursor, val);
|
||||
cursor += (uint) sizeof (uint64);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_float (float val) {
|
||||
write_float (cursor, val);
|
||||
cursor += (uint) sizeof (float);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_double (double val) {
|
||||
write_double (cursor, val);
|
||||
cursor += (uint) sizeof (double);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_string (string val, StringTerminator terminator = NUL) {
|
||||
uint size = val.length;
|
||||
if (terminator == NUL)
|
||||
size++;
|
||||
Memory.copy (get_pointer (cursor, size), val, size);
|
||||
cursor += size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_bytes (Bytes bytes) {
|
||||
return append_data (bytes.get_data ());
|
||||
}
|
||||
|
||||
public unowned BufferBuilder append_data (uint8[] data) {
|
||||
write_data (cursor, data);
|
||||
cursor += data.length;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_pointer (size_t offset, uint64 val) {
|
||||
if (pointer_size == 4)
|
||||
write_uint32 (offset, (uint32) val);
|
||||
else
|
||||
write_uint64 (offset, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_size (size_t offset, uint64 val) {
|
||||
return write_pointer (offset, val);
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_int8 (size_t offset, int8 val) {
|
||||
*((int8 *) get_pointer (offset, sizeof (int8))) = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_uint8 (size_t offset, uint8 val) {
|
||||
*get_pointer (offset, sizeof (uint8)) = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_int16 (size_t offset, int16 val) {
|
||||
int16 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((int16 *) get_pointer (offset, sizeof (int16))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_uint16 (size_t offset, uint16 val) {
|
||||
uint16 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((uint16 *) get_pointer (offset, sizeof (uint16))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_int32 (size_t offset, int32 val) {
|
||||
int32 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((int32 *) get_pointer (offset, sizeof (int32))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_uint32 (size_t offset, uint32 val) {
|
||||
uint32 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((uint32 *) get_pointer (offset, sizeof (uint32))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_int64 (size_t offset, int64 val) {
|
||||
int64 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((int64 *) get_pointer (offset, sizeof (int64))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_uint64 (size_t offset, uint64 val) {
|
||||
uint64 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((uint64 *) get_pointer (offset, sizeof (uint64))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_float (size_t offset, float val) {
|
||||
return write_uint32 (offset, *((uint32 *) &val));
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_double (size_t offset, double val) {
|
||||
return write_uint64 (offset, *((uint64 *) &val));
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_string (size_t offset, string val) {
|
||||
uint size = val.length + 1;
|
||||
Memory.copy (get_pointer (offset, size), val, size);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_bytes (size_t offset, Bytes bytes) {
|
||||
return write_data (offset, bytes.get_data ());
|
||||
}
|
||||
|
||||
public unowned BufferBuilder write_data (size_t offset, uint8[] data) {
|
||||
Memory.copy (get_pointer (offset, data.length), data, data.length);
|
||||
return this;
|
||||
}
|
||||
|
||||
private uint8 * get_pointer (size_t offset, size_t n) {
|
||||
size_t minimum_size = offset + n;
|
||||
if (buffer.len < minimum_size)
|
||||
buffer.set_size ((uint) minimum_size);
|
||||
|
||||
return (uint8 *) buffer.data + offset;
|
||||
}
|
||||
|
||||
public Bytes try_build (uint64 base_address = 0) throws Error {
|
||||
this.base_address = base_address;
|
||||
|
||||
if (label_refs != null) {
|
||||
foreach (LabelRef r in label_refs)
|
||||
write_pointer (r.offset, address_of (r.name));
|
||||
}
|
||||
|
||||
return ByteArray.free_to_bytes ((owned) buffer);
|
||||
}
|
||||
|
||||
public Bytes build (uint64 base_address = 0) {
|
||||
try {
|
||||
return try_build (base_address);
|
||||
} catch (Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
public uint64 address_of (string label) throws Error {
|
||||
if (label_defs == null || !label_defs.has_key (label))
|
||||
throw new Error.INVALID_OPERATION ("Label '%s' not defined", label);
|
||||
size_t offset = label_defs[label];
|
||||
return base_address + offset;
|
||||
}
|
||||
|
||||
private class LabelRef {
|
||||
public string name;
|
||||
public size_t offset;
|
||||
|
||||
public LabelRef (string name, size_t offset) {
|
||||
this.name = name;
|
||||
this.offset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum StringTerminator {
|
||||
NONE,
|
||||
NUL
|
||||
}
|
||||
|
||||
public sealed class Buffer : Object {
|
||||
public Bytes bytes {
|
||||
get {
|
||||
if (_bytes == null)
|
||||
_bytes = new Bytes.static (_data);
|
||||
return _bytes;
|
||||
}
|
||||
}
|
||||
|
||||
public size_t size {
|
||||
get {
|
||||
return _data.length;
|
||||
}
|
||||
}
|
||||
|
||||
public ByteOrder byte_order {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public uint pointer_size {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private Bytes? _bytes;
|
||||
private unowned uint8[] _data;
|
||||
|
||||
public Buffer (Bytes bytes, ByteOrder byte_order = HOST, uint pointer_size = (uint) sizeof (size_t)) {
|
||||
Object (
|
||||
byte_order: byte_order,
|
||||
pointer_size: pointer_size
|
||||
);
|
||||
|
||||
reset_bytes (bytes);
|
||||
}
|
||||
|
||||
public Buffer.from_data (uint8[] data, ByteOrder byte_order = HOST, uint pointer_size = (uint) sizeof (size_t)) {
|
||||
Object (
|
||||
byte_order: byte_order,
|
||||
pointer_size: pointer_size
|
||||
);
|
||||
|
||||
reset_data (data);
|
||||
}
|
||||
|
||||
public void reset_bytes (Bytes bytes) {
|
||||
_bytes = bytes;
|
||||
_data = bytes.get_data ();
|
||||
}
|
||||
|
||||
public void reset_data (uint8[] data) {
|
||||
_bytes = null;
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public unowned uint8[] peek_data () {
|
||||
return _data;
|
||||
}
|
||||
|
||||
public uint64 read_pointer (size_t offset) {
|
||||
return (pointer_size == 4)
|
||||
? read_uint32 (offset)
|
||||
: read_uint64 (offset);
|
||||
}
|
||||
|
||||
public void write_pointer (size_t offset, uint64 val) {
|
||||
if (pointer_size == 4)
|
||||
write_uint32 (offset, (uint32) val);
|
||||
else
|
||||
write_uint64 (offset, val);
|
||||
}
|
||||
|
||||
public int8 read_int8 (size_t offset) {
|
||||
return *((int8 *) get_pointer (offset, sizeof (int8)));
|
||||
}
|
||||
|
||||
public unowned Buffer write_int8 (size_t offset, int8 val) {
|
||||
*((int8 *) get_pointer (offset, sizeof (int8))) = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public uint8 read_uint8 (size_t offset) {
|
||||
return *get_pointer (offset, sizeof (uint8));
|
||||
}
|
||||
|
||||
public unowned Buffer write_uint8 (size_t offset, uint8 val) {
|
||||
*((uint8 *) get_pointer (offset, sizeof (uint8))) = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int16 read_int16 (size_t offset) {
|
||||
int16 val = *((int16 *) get_pointer (offset, sizeof (int16)));
|
||||
return (byte_order == BIG_ENDIAN)
|
||||
? int16.from_big_endian (val)
|
||||
: int16.from_little_endian (val);
|
||||
}
|
||||
|
||||
public unowned Buffer write_int16 (size_t offset, int16 val) {
|
||||
int16 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((int16 *) get_pointer (offset, sizeof (int16))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public uint16 read_uint16 (size_t offset) {
|
||||
uint16 val = *((uint16 *) get_pointer (offset, sizeof (uint16)));
|
||||
return (byte_order == BIG_ENDIAN)
|
||||
? uint16.from_big_endian (val)
|
||||
: uint16.from_little_endian (val);
|
||||
}
|
||||
|
||||
public unowned Buffer write_uint16 (size_t offset, uint16 val) {
|
||||
uint16 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((uint16 *) get_pointer (offset, sizeof (uint16))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int32 read_int32 (size_t offset) {
|
||||
int32 val = *((int32 *) get_pointer (offset, sizeof (int32)));
|
||||
return (byte_order == BIG_ENDIAN)
|
||||
? int32.from_big_endian (val)
|
||||
: int32.from_little_endian (val);
|
||||
}
|
||||
|
||||
public unowned Buffer write_int32 (size_t offset, int32 val) {
|
||||
int32 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((int32 *) get_pointer (offset, sizeof (int32))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public uint32 read_uint32 (size_t offset) {
|
||||
uint32 val = *((uint32 *) get_pointer (offset, sizeof (uint32)));
|
||||
return (byte_order == BIG_ENDIAN)
|
||||
? uint32.from_big_endian (val)
|
||||
: uint32.from_little_endian (val);
|
||||
}
|
||||
|
||||
public unowned Buffer write_uint32 (size_t offset, uint32 val) {
|
||||
uint32 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((uint32 *) get_pointer (offset, sizeof (uint32))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int64 read_int64 (size_t offset) {
|
||||
int64 val = *((int64 *) get_pointer (offset, sizeof (int64)));
|
||||
return (byte_order == BIG_ENDIAN)
|
||||
? int64.from_big_endian (val)
|
||||
: int64.from_little_endian (val);
|
||||
}
|
||||
|
||||
public unowned Buffer write_int64 (size_t offset, int64 val) {
|
||||
int64 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((int64 *) get_pointer (offset, sizeof (int64))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public uint64 read_uint64 (size_t offset) {
|
||||
uint64 val = *((uint64 *) get_pointer (offset, sizeof (uint64)));
|
||||
return (byte_order == BIG_ENDIAN)
|
||||
? uint64.from_big_endian (val)
|
||||
: uint64.from_little_endian (val);
|
||||
}
|
||||
|
||||
public unowned Buffer write_uint64 (size_t offset, uint64 val) {
|
||||
uint64 target_val = (byte_order == BIG_ENDIAN)
|
||||
? val.to_big_endian ()
|
||||
: val.to_little_endian ();
|
||||
*((uint64 *) get_pointer (offset, sizeof (uint64))) = target_val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float read_float (size_t offset) {
|
||||
uint32 bits = read_uint32 (offset);
|
||||
return *((float *) &bits);
|
||||
}
|
||||
|
||||
public double read_double (size_t offset) {
|
||||
uint64 bits = read_uint64 (offset);
|
||||
return *((double *) &bits);
|
||||
}
|
||||
|
||||
public unowned string read_string (size_t offset) throws Error {
|
||||
string * start = (string *) get_pointer (offset, sizeof (char));
|
||||
size_t max_length = _data.length - offset;
|
||||
string * end = memchr (start, 0, max_length);
|
||||
if (end == null)
|
||||
throw new Error.PROTOCOL ("Missing null character");
|
||||
return start;
|
||||
}
|
||||
|
||||
[CCode (cname = "memchr", cheader_filename = "string.h")]
|
||||
private extern static string * memchr (string * s, int c, size_t n);
|
||||
|
||||
public string read_fixed_string (size_t offset, size_t size) throws Error {
|
||||
string * start = (string *) get_pointer (offset, size);
|
||||
size_t max_length = size_t.min (size, _data.length - offset);
|
||||
string * end = memchr (start, 0, max_length);
|
||||
size_t n;
|
||||
if (end != null)
|
||||
n = end - start;
|
||||
else
|
||||
n = size;
|
||||
return start->substring (0, (long) n);
|
||||
}
|
||||
|
||||
public unowned Buffer write_string (size_t offset, string val) {
|
||||
uint size = val.length + 1;
|
||||
Memory.copy (get_pointer (offset, size), val, size);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Bytes read_bytes (size_t offset, size_t size) {
|
||||
if (_bytes == null)
|
||||
return make_bytes_with_owner ((uint8 *) _data + offset, _data.length - size, this);
|
||||
return _bytes[offset:offset + size];
|
||||
}
|
||||
|
||||
public unowned uint8[] read_data (size_t offset, size_t size) {
|
||||
unowned uint8 * ptr = get_pointer (offset, size);
|
||||
return ((uint8[]) ptr)[:size];
|
||||
}
|
||||
|
||||
public unowned Buffer write_bytes (size_t offset, Bytes bytes) {
|
||||
size_t size = bytes.get_size ();
|
||||
Memory.copy (get_pointer (offset, size), bytes.get_data (), size);
|
||||
return this;
|
||||
}
|
||||
|
||||
private uint8 * get_pointer (size_t offset, size_t n) {
|
||||
size_t minimum_size = offset + n;
|
||||
assert (_data.length >= minimum_size);
|
||||
return (uint8 *) _data + offset;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BufferReader {
|
||||
public Buffer buffer {
|
||||
get;
|
||||
}
|
||||
|
||||
public size_t offset {
|
||||
get;
|
||||
}
|
||||
|
||||
public size_t available {
|
||||
get {
|
||||
return _size - _offset;
|
||||
}
|
||||
}
|
||||
|
||||
private size_t _size;
|
||||
|
||||
public BufferReader (Buffer buffer) {
|
||||
reset (buffer);
|
||||
}
|
||||
|
||||
public void reset (Buffer buffer) {
|
||||
_buffer = buffer;
|
||||
_size = buffer.size;
|
||||
_offset = 0;
|
||||
}
|
||||
|
||||
public void reset_at (Buffer buffer, size_t offset) throws Error {
|
||||
_buffer = buffer;
|
||||
_size = buffer.size;
|
||||
seek (offset);
|
||||
}
|
||||
|
||||
public unowned BufferReader seek (size_t offset) throws Error {
|
||||
if (offset > _size)
|
||||
throw new Error.PROTOCOL ("Malformed buffer: truncated");
|
||||
_offset = offset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferReader skip (size_t n) throws Error {
|
||||
check_available (n);
|
||||
_offset += n;
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned BufferReader align (size_t alignment) throws Error {
|
||||
size_t mask = alignment - 1;
|
||||
size_t rem = _offset & mask;
|
||||
if (rem != 0)
|
||||
skip (alignment - rem);
|
||||
return this;
|
||||
}
|
||||
|
||||
public uint64 peek_pointer () throws Error {
|
||||
var pointer_size = _buffer.pointer_size;
|
||||
check_available (pointer_size);
|
||||
return _buffer.read_pointer (_offset);
|
||||
}
|
||||
|
||||
public uint64 read_pointer () throws Error {
|
||||
var pointer_size = _buffer.pointer_size;
|
||||
check_available (pointer_size);
|
||||
var ptr = _buffer.read_pointer (_offset);
|
||||
_offset += pointer_size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
public int8 peek_int8 () throws Error {
|
||||
check_available (sizeof (int8));
|
||||
return _buffer.read_int8 (_offset);
|
||||
}
|
||||
|
||||
public int8 read_int8 () throws Error {
|
||||
check_available (sizeof (int8));
|
||||
var val = _buffer.read_int8 (_offset);
|
||||
_offset += sizeof (int8);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint8 peek_uint8 () throws Error {
|
||||
check_available (sizeof (uint8));
|
||||
return _buffer.read_uint8 (_offset);
|
||||
}
|
||||
|
||||
public uint8 read_uint8 () throws Error {
|
||||
check_available (sizeof (uint8));
|
||||
var val = _buffer.read_uint8 (_offset);
|
||||
_offset += sizeof (uint8);
|
||||
return val;
|
||||
}
|
||||
|
||||
public int16 peek_int16 () throws Error {
|
||||
check_available (sizeof (int16));
|
||||
return _buffer.read_int16 (_offset);
|
||||
}
|
||||
|
||||
public int16 read_int16 () throws Error {
|
||||
check_available (sizeof (int16));
|
||||
var val = _buffer.read_int16 (_offset);
|
||||
_offset += sizeof (int16);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint16 peek_uint16 () throws Error {
|
||||
check_available (sizeof (uint16));
|
||||
return _buffer.read_uint16 (_offset);
|
||||
}
|
||||
|
||||
public uint16 read_uint16 () throws Error {
|
||||
check_available (sizeof (uint16));
|
||||
var val = _buffer.read_uint16 (_offset);
|
||||
_offset += sizeof (uint16);
|
||||
return val;
|
||||
}
|
||||
|
||||
public int32 peek_int32 () throws Error {
|
||||
check_available (sizeof (int32));
|
||||
return _buffer.read_int32 (_offset);
|
||||
}
|
||||
|
||||
public int32 read_int32 () throws Error {
|
||||
check_available (sizeof (int32));
|
||||
var val = _buffer.read_int32 (_offset);
|
||||
_offset += sizeof (int32);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint32 peek_uint32 () throws Error {
|
||||
check_available (sizeof (uint32));
|
||||
return _buffer.read_uint32 (_offset);
|
||||
}
|
||||
|
||||
public uint32 read_uint32 () throws Error {
|
||||
check_available (sizeof (uint32));
|
||||
var val = _buffer.read_uint32 (_offset);
|
||||
_offset += sizeof (uint32);
|
||||
return val;
|
||||
}
|
||||
|
||||
public int64 peek_int64 () throws Error {
|
||||
check_available (sizeof (int64));
|
||||
return _buffer.read_int64 (_offset);
|
||||
}
|
||||
|
||||
public int64 read_int64 () throws Error {
|
||||
check_available (sizeof (int64));
|
||||
var val = _buffer.read_int64 (_offset);
|
||||
_offset += sizeof (int64);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint64 peek_uint64 () throws Error {
|
||||
check_available (sizeof (uint64));
|
||||
return _buffer.read_uint64 (_offset);
|
||||
}
|
||||
|
||||
public uint64 read_uint64 () throws Error {
|
||||
check_available (sizeof (uint64));
|
||||
var val = _buffer.read_uint64 (_offset);
|
||||
_offset += sizeof (uint64);
|
||||
return val;
|
||||
}
|
||||
|
||||
public float peek_float () throws Error {
|
||||
check_available (sizeof (float));
|
||||
return _buffer.read_float (_offset);
|
||||
}
|
||||
|
||||
public float read_float () throws Error {
|
||||
check_available (sizeof (float));
|
||||
var val = _buffer.read_float (_offset);
|
||||
_offset += sizeof (float);
|
||||
return val;
|
||||
}
|
||||
|
||||
public double peek_double () throws Error {
|
||||
check_available (sizeof (double));
|
||||
return _buffer.read_double (_offset);
|
||||
}
|
||||
|
||||
public double read_double () throws Error {
|
||||
check_available (sizeof (double));
|
||||
var val = _buffer.read_double (_offset);
|
||||
_offset += sizeof (double);
|
||||
return val;
|
||||
}
|
||||
|
||||
public unowned string peek_string () throws Error {
|
||||
check_available (1);
|
||||
return _buffer.read_string (_offset);
|
||||
}
|
||||
|
||||
public unowned string read_string () throws Error {
|
||||
check_available (1);
|
||||
unowned string val = _buffer.read_string (_offset);
|
||||
_offset += val.length + 1;
|
||||
return val;
|
||||
}
|
||||
|
||||
public string peek_fixed_string (size_t size) throws Error {
|
||||
check_available (size);
|
||||
return _buffer.read_fixed_string (_offset, size);
|
||||
}
|
||||
|
||||
public string read_fixed_string (size_t size) throws Error {
|
||||
check_available (size);
|
||||
var val = _buffer.read_fixed_string (_offset, size);
|
||||
_offset += size;
|
||||
return val;
|
||||
}
|
||||
|
||||
public unowned uint8[] peek_data (size_t size) throws Error {
|
||||
check_available (size);
|
||||
return _buffer.read_data (_offset, size);
|
||||
}
|
||||
|
||||
public unowned uint8[] read_data (size_t size) throws Error {
|
||||
check_available (size);
|
||||
unowned uint8[] slice = _buffer.read_data (_offset, size);
|
||||
_offset += size;
|
||||
return slice;
|
||||
}
|
||||
|
||||
public Bytes read_bytes (size_t size) throws Error {
|
||||
check_available (size);
|
||||
var val = _buffer.read_bytes (_offset, size);
|
||||
_offset += size;
|
||||
return val;
|
||||
}
|
||||
|
||||
public unowned uint8[] read_remaining_data () throws Error {
|
||||
return read_data (available);
|
||||
}
|
||||
|
||||
private void check_available (size_t n) throws Error {
|
||||
if (available < n)
|
||||
throw new Error.PROTOCOL ("Malformed buffer: truncated");
|
||||
}
|
||||
}
|
||||
|
||||
public extern Bytes make_bytes_with_owner<T> (void * data, size_t size, owned T? owner = null);
|
||||
}
|
||||
+14
-2
@@ -1,9 +1,13 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
public sealed class ChannelRegistry : Object {
|
||||
public signal void channel_closed (ChannelId id);
|
||||
|
||||
private Gee.Map<ChannelId?, weak IOStream> channels =
|
||||
new Gee.HashMap<ChannelId?, weak IOStream> (ChannelId.hash, ChannelId.equal);
|
||||
private Gee.Map<ChannelId?, IOStream> unlinked_channels =
|
||||
new Gee.HashMap<ChannelId?, IOStream> (ChannelId.hash, ChannelId.equal);
|
||||
|
||||
~ChannelRegistry () {
|
||||
clear ();
|
||||
@@ -16,25 +20,33 @@ namespace Frida {
|
||||
channel_stream.abandon ();
|
||||
}
|
||||
channels.clear ();
|
||||
unlinked_channels.clear ();
|
||||
}
|
||||
|
||||
public void register (ChannelId id, IOStream stream) {
|
||||
stream.set_data ("channel-registry-entry", new Entry (this, id));
|
||||
lock (channels)
|
||||
lock (channels) {
|
||||
channels[id] = stream;
|
||||
unlinked_channels[id] = stream;
|
||||
}
|
||||
}
|
||||
|
||||
public IOStream link (ChannelId id) throws Error {
|
||||
IOStream? stream;
|
||||
lock (channels)
|
||||
IOStream? unlinked_stream = null;
|
||||
lock (channels) {
|
||||
stream = channels[id];
|
||||
unlinked_channels.unset (id, out unlinked_stream);
|
||||
}
|
||||
if (stream == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid channel ID");
|
||||
return stream;
|
||||
}
|
||||
|
||||
public void unlink (ChannelId id) {
|
||||
IOStream? unlinked_stream = null;
|
||||
lock (channels) {
|
||||
unlinked_channels.unset (id, out unlinked_stream);
|
||||
if (!channels.unset (id))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
// We need to tease out GDBus' private MainContext as libnice needs to know the MainContext up front :(
|
||||
public async MainContext get_dbus_context () {
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#include "frida-darwin.h"
|
||||
|
||||
gpointer
|
||||
_frida_dispatch_retain (gpointer object)
|
||||
{
|
||||
dispatch_retain (object);
|
||||
return object;
|
||||
}
|
||||
|
||||
void
|
||||
_frida_xpc_connection_set_event_handler (xpc_connection_t connection, FridaXpcHandler handler, gpointer user_data)
|
||||
{
|
||||
xpc_connection_set_event_handler (connection, ^(xpc_object_t object)
|
||||
{
|
||||
handler (object, user_data);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
_frida_xpc_connection_send_message_with_reply (xpc_connection_t connection, xpc_object_t message, dispatch_queue_t replyq,
|
||||
FridaXpcHandler handler, gpointer user_data, GDestroyNotify notify)
|
||||
{
|
||||
xpc_connection_send_message_with_reply (connection, message, replyq, ^(xpc_object_t object)
|
||||
{
|
||||
handler (object, user_data);
|
||||
if (notify != NULL)
|
||||
notify (user_data);
|
||||
});
|
||||
}
|
||||
|
||||
gchar *
|
||||
_frida_xpc_object_to_string (xpc_object_t object)
|
||||
{
|
||||
gchar * result;
|
||||
char * str;
|
||||
|
||||
str = xpc_copy_description (object);
|
||||
result = g_strdup (str);
|
||||
free (str);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_frida_xpc_dictionary_apply (xpc_object_t dict, FridaXpcDictionaryApplier applier, gpointer user_data)
|
||||
{
|
||||
return xpc_dictionary_apply (dict, ^bool (const char * key, xpc_object_t val)
|
||||
{
|
||||
return applier (key, val, user_data);
|
||||
});
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef __FRIDA_DARWIN_H__
|
||||
#define __FRIDA_DARWIN_H__
|
||||
|
||||
#ifdef HAVE_MACOS
|
||||
|
||||
#include <glib.h>
|
||||
#include <xpc/xpc.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef void (* FridaXpcHandler) (xpc_object_t object, gpointer user_data);
|
||||
typedef gboolean (* FridaXpcDictionaryApplier) (const gchar * key, xpc_object_t val, gpointer user_data);
|
||||
|
||||
gpointer _frida_dispatch_retain (gpointer object);
|
||||
|
||||
void _frida_xpc_connection_set_event_handler (xpc_connection_t connection, FridaXpcHandler handler, gpointer user_data);
|
||||
void _frida_xpc_connection_send_message_with_reply (xpc_connection_t connection, xpc_object_t message, dispatch_queue_t replyq,
|
||||
FridaXpcHandler handler, gpointer user_data, GDestroyNotify notify);
|
||||
gchar * _frida_xpc_object_to_string (xpc_object_t object);
|
||||
gboolean _frida_xpc_dictionary_apply (xpc_object_t dict, FridaXpcDictionaryApplier applier, gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,126 +0,0 @@
|
||||
namespace Frida.FS {
|
||||
public void mkdirp (File dir, Cancellable? cancellable = null) throws Error {
|
||||
try {
|
||||
dir.make_directory_with_parents (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (!(e is IOError.EXISTS))
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void rmtree (File dir, Cancellable? cancellable = null) throws Error {
|
||||
rmtree_worker (dir, 0, cancellable);
|
||||
}
|
||||
|
||||
private void rmtree_worker (File dir, uint depth, Cancellable? cancellable = null) throws Error {
|
||||
FileEnumerator enumerator;
|
||||
try {
|
||||
enumerator = dir.enumerate_children (FileAttribute.STANDARD_NAME, NOFOLLOW_SYMLINKS, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (depth == 0 && e is IOError.NOT_FOUND)
|
||||
return;
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
FileInfo? info;
|
||||
File? child;
|
||||
while (enumerator.iterate (out info, out child, cancellable) && info != null) {
|
||||
if (info == null)
|
||||
continue;
|
||||
if (info.get_file_type () == DIRECTORY)
|
||||
rmtree (child, cancellable);
|
||||
else
|
||||
child.delete (cancellable);
|
||||
}
|
||||
|
||||
dir.delete (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (!(e is IOError.EXISTS))
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async void rmtree_async (File dir, Cancellable? cancellable = null) throws Error, IOError {
|
||||
yield rmtree_async_worker (dir, 0, cancellable);
|
||||
}
|
||||
|
||||
private async void rmtree_async_worker (File dir, uint depth, Cancellable? cancellable = null) throws Error, IOError {
|
||||
int io_priority = Priority.DEFAULT;
|
||||
|
||||
FileEnumerator enumerator;
|
||||
try {
|
||||
enumerator = yield dir.enumerate_children_async (FileAttribute.STANDARD_NAME, NOFOLLOW_SYMLINKS, io_priority,
|
||||
cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (depth == 0 && e is IOError.NOT_FOUND)
|
||||
return;
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
var batch = yield enumerator.next_files_async (1000, io_priority, cancellable);
|
||||
if (batch.is_empty ())
|
||||
break;
|
||||
foreach (FileInfo info in batch) {
|
||||
File child = dir.get_child (info.get_name ());
|
||||
if (info.get_file_type () == DIRECTORY)
|
||||
yield rmtree_async_worker (child, depth + 1, cancellable);
|
||||
else
|
||||
yield child.delete_async (io_priority, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
yield dir.delete_async (io_priority, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async string read_all_text (File f, Cancellable? cancellable) throws Error, IOError {
|
||||
Bytes b = yield read_all_bytes (f, cancellable);
|
||||
string text = (string) b.get_data ();
|
||||
if (!text.validate ())
|
||||
throw new Error.PROTOCOL ("Unable to read '%s': invalid UTF-8", f.get_parse_name ());
|
||||
return text;
|
||||
}
|
||||
|
||||
public async void write_all_text (File f, string t, Cancellable? cancellable) throws Error, IOError {
|
||||
yield write_all_bytes (f, new Bytes (t.data), cancellable);
|
||||
}
|
||||
|
||||
public Bytes read_all_bytes_sync (File f) throws Error {
|
||||
try {
|
||||
uint8[] contents;
|
||||
f.load_contents (null, out contents, null);
|
||||
return new Bytes.take ((owned) contents);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.NOT_FOUND)
|
||||
throw new Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Bytes read_all_bytes (File f, Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
return yield f.load_bytes_async (cancellable, null);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CANCELLED)
|
||||
throw (IOError) e;
|
||||
if (e is IOError.NOT_FOUND)
|
||||
throw new Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async void write_all_bytes (File f, Bytes b, Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
yield f.replace_contents_bytes_async (b, null, false, FileCreateFlags.NONE, cancellable, null);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CANCELLED)
|
||||
throw (IOError) e;
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,15 @@ namespace Frida {
|
||||
return fd;
|
||||
}
|
||||
|
||||
public static FileDescriptor with_size (string name, size_t size) {
|
||||
assert (is_supported ());
|
||||
|
||||
var fd = new FileDescriptor (memfd_create (name, 0));
|
||||
int res = Posix.ftruncate (fd.handle, (Posix.off_t) size);
|
||||
assert (res == 0);
|
||||
return fd;
|
||||
}
|
||||
|
||||
private int memfd_create (string name, uint flags) {
|
||||
return Linux.syscall (LinuxSyscall.MEMFD_CREATE, name, flags);
|
||||
}
|
||||
|
||||
+3
-17
@@ -1,21 +1,16 @@
|
||||
base_sources = [
|
||||
'asset-location.vala',
|
||||
'promise.vala',
|
||||
'session.vala',
|
||||
'session-glue.c',
|
||||
'channel.vala',
|
||||
'service.vala',
|
||||
'stream.vala',
|
||||
'rpc.vala',
|
||||
'p2p.vala',
|
||||
'p2p-glue.c',
|
||||
'dbus.vala',
|
||||
'promise.vala',
|
||||
'fs.vala',
|
||||
'socket.vala',
|
||||
'socket-glue.c',
|
||||
'buffer.vala',
|
||||
'buffer-glue.c',
|
||||
'value.vala',
|
||||
'xnu-syscalls' / 'xnu-syscalls.vala',
|
||||
'xnu-syscalls' / 'xnu-syscalls-glue.c',
|
||||
]
|
||||
@@ -29,13 +24,6 @@ else
|
||||
extra_vala_args += '--pkg=posix'
|
||||
endif
|
||||
|
||||
if host_os == 'macos'
|
||||
base_sources += [
|
||||
'xpc.vala',
|
||||
'frida-darwin.c',
|
||||
]
|
||||
endif
|
||||
|
||||
if host_os_family == 'darwin'
|
||||
base_sources += 'corefoundation.vala'
|
||||
extra_vala_args += '--pkg=darwin-xnu'
|
||||
@@ -94,8 +82,6 @@ endif
|
||||
if host_os_family == 'darwin'
|
||||
base_vala_args += [
|
||||
'--pkg=corefoundation',
|
||||
'--pkg=darwin-gcd',
|
||||
'--pkg=darwin-xpc',
|
||||
]
|
||||
endif
|
||||
if host_os_family == 'linux'
|
||||
@@ -114,7 +100,7 @@ base = static_library('frida-base-' + api_version, base_sources,
|
||||
vala_header: 'frida-base.h',
|
||||
vala_gir: base_gir_name,
|
||||
vala_vapi: f'frida-base-@[email protected]',
|
||||
dependencies: [glib_dep, gobject_dep, gio_dep, libsoup_dep, nice_dep, usrsctp_dep, gee_dep, json_glib_dep, gum_dep, openssl_dep] + extra_deps,
|
||||
dependencies: [glib_dep, gobject_dep, gio_dep, libsoup_dep, nice_dep, usrsctp_dep, gee_dep, json_glib_dep, gum_dep, openssl_dep, efni_dep] + extra_deps,
|
||||
install: have_in_tree_payloads,
|
||||
install_dir: [true, header_install_dir, true, false],
|
||||
)
|
||||
@@ -122,7 +108,7 @@ base = static_library('frida-base-' + api_version, base_sources,
|
||||
base_dep = declare_dependency(
|
||||
include_directories: include_directories('.'),
|
||||
link_with: base,
|
||||
dependencies: [nice_dep, json_glib_dep, gum_dep] + extra_deps,
|
||||
dependencies: [nice_dep, json_glib_dep, gum_dep, efni_dep] + extra_deps,
|
||||
)
|
||||
|
||||
base_build_dir = meson.current_build_dir()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
#if HAVE_NICE
|
||||
namespace Frida {
|
||||
namespace PeerConnection {
|
||||
|
||||
+58
-124
@@ -1,19 +1,18 @@
|
||||
namespace Frida {
|
||||
public sealed class Promise<T> {
|
||||
private Impl<T> impl;
|
||||
private Efni.Promise<T> impl;
|
||||
private Future<T>? _future;
|
||||
|
||||
public Future<T> future {
|
||||
get {
|
||||
return impl;
|
||||
if (_future == null)
|
||||
_future = new FutureImpl<T> (impl.future);
|
||||
return _future;
|
||||
}
|
||||
}
|
||||
|
||||
public Promise () {
|
||||
impl = new Impl<T> ();
|
||||
}
|
||||
|
||||
~Promise () {
|
||||
impl.abandon ();
|
||||
impl = new Efni.Promise<T> ();
|
||||
}
|
||||
|
||||
public void resolve (T result) {
|
||||
@@ -24,149 +23,84 @@ namespace Frida {
|
||||
impl.reject (error);
|
||||
}
|
||||
|
||||
private class Impl<T> : Object, Future<T> {
|
||||
private class FutureImpl<T> : Object, Future<T> {
|
||||
private Efni.Future<T> inner;
|
||||
|
||||
public bool ready {
|
||||
get {
|
||||
return _ready;
|
||||
return inner.ready;
|
||||
}
|
||||
}
|
||||
private bool _ready = false;
|
||||
|
||||
public T? value {
|
||||
get {
|
||||
return _value;
|
||||
return inner.value;
|
||||
}
|
||||
}
|
||||
private T? _value;
|
||||
|
||||
public GLib.Error? error {
|
||||
get {
|
||||
return _error;
|
||||
return inner.error;
|
||||
}
|
||||
}
|
||||
private GLib.Error? _error;
|
||||
|
||||
private Gee.ArrayQueue<CompletionFuncEntry>? on_complete;
|
||||
private Gee.ArrayQueue<ThenHandlerEntry<T>>? on_then;
|
||||
public FutureImpl (Efni.Future<T> inner) {
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
public async T wait_async (Cancellable? cancellable) throws Frida.Error, IOError {
|
||||
if (_ready)
|
||||
return get_result ();
|
||||
public async T wait_async (Cancellable? cancellable) throws Error, IOError {
|
||||
if (!inner.ready) {
|
||||
bool resumed = false;
|
||||
|
||||
var entry = new CompletionFuncEntry (wait_async.callback);
|
||||
if (on_complete == null)
|
||||
on_complete = new Gee.ArrayQueue<CompletionFuncEntry> ();
|
||||
on_complete.offer (entry);
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
if (!resumed) {
|
||||
resumed = true;
|
||||
wait_async.callback ();
|
||||
}
|
||||
return Source.REMOVE;
|
||||
});
|
||||
cancel_source.attach (MainContext.get_thread_default ());
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
on_complete.remove (entry);
|
||||
wait_async.callback ();
|
||||
return false;
|
||||
});
|
||||
cancel_source.attach (MainContext.get_thread_default ());
|
||||
inner.then (f => {
|
||||
if (!resumed) {
|
||||
resumed = true;
|
||||
wait_async.callback ();
|
||||
}
|
||||
});
|
||||
|
||||
yield;
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
cancel_source.destroy ();
|
||||
|
||||
cancellable.set_error_if_cancelled ();
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
|
||||
return get_result ();
|
||||
}
|
||||
|
||||
public void then (owned FutureCompletionHandler<T> handler) {
|
||||
if (_ready) {
|
||||
schedule (() => {
|
||||
handler (this);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
return;
|
||||
inner.then (f => {
|
||||
handler (this);
|
||||
});
|
||||
}
|
||||
|
||||
public T get_result () throws Error, IOError {
|
||||
GLib.Error? e = inner.error;
|
||||
if (e != null) {
|
||||
if (e is Error)
|
||||
throw (Error) e;
|
||||
|
||||
if (e is Efni.Error)
|
||||
throw error_from_efni ((Efni.Error) e);
|
||||
|
||||
if (e is IOError.CANCELLED)
|
||||
throw (IOError) e;
|
||||
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
if (on_then == null)
|
||||
on_then = new Gee.ArrayQueue<ThenHandlerEntry<T>> ();
|
||||
on_then.offer (new ThenHandlerEntry<T> ((owned) handler));
|
||||
}
|
||||
|
||||
public T get_result () throws Frida.Error, IOError {
|
||||
if (error != null) {
|
||||
if (error is Frida.Error)
|
||||
throw (Frida.Error) error;
|
||||
|
||||
if (error is IOError.CANCELLED)
|
||||
throw (IOError) error;
|
||||
|
||||
throw new Frida.Error.TRANSPORT ("%s", error.message);
|
||||
}
|
||||
|
||||
return _value;
|
||||
}
|
||||
|
||||
internal void resolve (T value) {
|
||||
assert (!_ready);
|
||||
|
||||
_value = value;
|
||||
transition_to_ready ();
|
||||
}
|
||||
|
||||
internal void reject (GLib.Error error) {
|
||||
assert (!_ready);
|
||||
|
||||
_error = error;
|
||||
transition_to_ready ();
|
||||
}
|
||||
|
||||
internal void abandon () {
|
||||
if (!_ready) {
|
||||
reject (new Frida.Error.INVALID_OPERATION ("Promise abandoned"));
|
||||
}
|
||||
}
|
||||
|
||||
internal void transition_to_ready () {
|
||||
_ready = true;
|
||||
|
||||
if (on_complete != null || on_then != null) {
|
||||
schedule (() => {
|
||||
if (on_complete != null) {
|
||||
CompletionFuncEntry? entry;
|
||||
while ((entry = on_complete.poll ()) != null)
|
||||
entry.func ();
|
||||
on_complete = null;
|
||||
}
|
||||
|
||||
if (on_then != null) {
|
||||
ThenHandlerEntry? entry;
|
||||
while ((entry = on_then.poll ()) != null)
|
||||
entry.handler (this);
|
||||
on_then = null;
|
||||
}
|
||||
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void schedule (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
}
|
||||
|
||||
private class CompletionFuncEntry {
|
||||
public SourceFunc func;
|
||||
|
||||
public CompletionFuncEntry (owned SourceFunc func) {
|
||||
this.func = (owned) func;
|
||||
}
|
||||
}
|
||||
|
||||
private class ThenHandlerEntry<T> {
|
||||
public FutureCompletionHandler<T> handler;
|
||||
|
||||
public ThenHandlerEntry (owned FutureCompletionHandler<T> handler) {
|
||||
this.handler = (owned) handler;
|
||||
return inner.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,9 +109,9 @@ namespace Frida {
|
||||
public abstract bool ready { get; }
|
||||
public abstract T? value { get; }
|
||||
public abstract GLib.Error? error { get; }
|
||||
public abstract async T wait_async (Cancellable? cancellable) throws Frida.Error, IOError;
|
||||
public abstract async T wait_async (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract void then (owned FutureCompletionHandler<T> handler);
|
||||
public abstract T get_result () throws Frida.Error, IOError;
|
||||
public abstract T get_result () throws Error, IOError;
|
||||
}
|
||||
|
||||
public delegate void FutureCompletionHandler<T> (Future<T> future);
|
||||
|
||||
+91
-50
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
[DBus (name = "re.frida.HostSession17")]
|
||||
public interface HostSession : Object {
|
||||
@@ -124,8 +126,43 @@ namespace Frida {
|
||||
|
||||
public abstract async void acknowledge_spawn (HostChildInfo info, SpawnStartState start_state,
|
||||
Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public abstract uint64 allocate_memory (uint pid, uint64 near_address, uint64 max_distance, uint64 size,
|
||||
uint64 alignment, Gum.PageProtection prot, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract void protect_memory (uint pid, uint64 address, uint64 size, Gum.PageProtection prot,
|
||||
Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[DBus (name = "re.frida.NativeThreadRegistry17")]
|
||||
public interface NativeThreadRegistry : Object {
|
||||
public abstract NativeThreadInfo[] activate (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract void deactivate (Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public signal void thread_added (NativeThreadInfo thread);
|
||||
public signal void thread_removed (uint id);
|
||||
public signal void thread_renamed (uint id, string name);
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.NativeCloak17")]
|
||||
public interface NativeCloak : Object {
|
||||
public abstract uint[] activate (Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public signal void thread_cloaked (uint id);
|
||||
public signal void thread_uncloaked (uint id);
|
||||
}
|
||||
|
||||
public struct NativeThreadInfo {
|
||||
public uint id;
|
||||
public string name;
|
||||
|
||||
public NativeThreadInfo (uint id, string name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[DBus (name = "re.frida.AgentMessageSink17")]
|
||||
public interface AgentMessageSink : Object {
|
||||
public abstract async void post_messages (AgentMessage[] messages, uint batch_id,
|
||||
@@ -1133,6 +1170,15 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
#if WINDOWS
|
||||
public struct WindowsInjectorState {
|
||||
public Gum.MemoryRange mapped_range;
|
||||
public Gum.MemoryRange worker_range;
|
||||
public Gum.MemoryRange dbghelp_range;
|
||||
public Gum.MemoryRange symsrv_range;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DARWIN
|
||||
public struct DarwinInjectorState {
|
||||
public Gum.MemoryRange? mapped_range;
|
||||
@@ -1143,6 +1189,8 @@ namespace Frida {
|
||||
public struct LinuxInjectorState {
|
||||
public int frida_ctrlfd;
|
||||
public int agent_ctrlfd;
|
||||
public uint64 mapped_base;
|
||||
public size_t mapped_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1281,6 +1329,9 @@ namespace Frida {
|
||||
if (e is Frida.Error)
|
||||
throw (Frida.Error) e;
|
||||
|
||||
if (e is Efni.Error)
|
||||
throw error_from_efni ((Efni.Error) e);
|
||||
|
||||
if (e is IOError.CANCELLED)
|
||||
throw (IOError) e;
|
||||
|
||||
@@ -1294,6 +1345,9 @@ namespace Frida {
|
||||
if (e is Frida.Error)
|
||||
throw (Frida.Error) e;
|
||||
|
||||
if (e is Efni.Error)
|
||||
throw error_from_efni ((Efni.Error) e);
|
||||
|
||||
if (e is IOError.CANCELLED)
|
||||
throw (IOError) e;
|
||||
|
||||
@@ -1306,6 +1360,41 @@ namespace Frida {
|
||||
throw new Frida.Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
namespace Marshal {
|
||||
public T enum_from_nick<T> (string nick) throws Error {
|
||||
try {
|
||||
return Efni.Marshal.enum_from_nick<T> (nick);
|
||||
} catch (Efni.Error e) {
|
||||
throw error_from_efni (e);
|
||||
}
|
||||
}
|
||||
|
||||
public string enum_to_nick<T> (int val) {
|
||||
return Efni.Marshal.enum_to_nick<T> (val);
|
||||
}
|
||||
}
|
||||
|
||||
public static Frida.Error error_from_efni (Efni.Error e) {
|
||||
unowned string m = e.message;
|
||||
if (e is Efni.Error.SERVER_NOT_RUNNING)
|
||||
return new Frida.Error.SERVER_NOT_RUNNING ("%s", m);
|
||||
if (e is Efni.Error.INVALID_ARGUMENT)
|
||||
return new Frida.Error.INVALID_ARGUMENT ("%s", m);
|
||||
if (e is Efni.Error.INVALID_OPERATION)
|
||||
return new Frida.Error.INVALID_OPERATION ("%s", m);
|
||||
if (e is Efni.Error.PERMISSION_DENIED)
|
||||
return new Frida.Error.PERMISSION_DENIED ("%s", m);
|
||||
if (e is Efni.Error.ADDRESS_IN_USE)
|
||||
return new Frida.Error.ADDRESS_IN_USE ("%s", m);
|
||||
if (e is Efni.Error.TIMED_OUT)
|
||||
return new Frida.Error.TIMED_OUT ("%s", m);
|
||||
if (e is Efni.Error.NOT_SUPPORTED)
|
||||
return new Frida.Error.NOT_SUPPORTED ("%s", m);
|
||||
if (e is Efni.Error.PROTOCOL)
|
||||
return new Frida.Error.PROTOCOL ("%s", m);
|
||||
return new Frida.Error.TRANSPORT ("%s", m);
|
||||
}
|
||||
|
||||
public struct HostApplicationInfo {
|
||||
public string identifier;
|
||||
public string name;
|
||||
@@ -2743,6 +2832,8 @@ namespace Frida {
|
||||
public const string AGENT_SESSION_PROVIDER = "/re/frida/AgentSessionProvider";
|
||||
public const string AGENT_SESSION = "/re/frida/AgentSession";
|
||||
public const string AGENT_CONTROLLER = "/re/frida/AgentController";
|
||||
public const string NATIVE_THREAD_REGISTRY = "/re/frida/NativeThreadRegistry";
|
||||
public const string NATIVE_CLOAK = "/re/frida/NativeCloak";
|
||||
public const string AGENT_MESSAGE_SINK = "/re/frida/AgentMessageSink";
|
||||
public const string GADGET_SESSION = "/re/frida/GadgetSession";
|
||||
public const string CHANNEL = "/re/frida/Channel";
|
||||
@@ -2768,54 +2859,4 @@ namespace Frida {
|
||||
return SERVICE + "/" + id.handle;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Marshal {
|
||||
public static T enum_from_nick<T> (string nick) throws Error {
|
||||
var klass = (EnumClass) typeof (T).class_ref ();
|
||||
var v = klass.get_value_by_nick (nick);
|
||||
if (v == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid %s", klass.get_type ().name ());
|
||||
return (T) v.value;
|
||||
}
|
||||
|
||||
public static string enum_to_nick<T> (int val) {
|
||||
var klass = (EnumClass) typeof (T).class_ref ();
|
||||
unowned EnumValue? v = klass.get_value (val);
|
||||
if (v == null)
|
||||
return "unknown-0x%x".printf (val);
|
||||
return v.value_nick;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Numeric {
|
||||
public uint int64_hash (int64? val) {
|
||||
uint64 v = (uint64) val.abs ();
|
||||
return (uint) ((v >> 32) ^ (v & 0xffffffffU));
|
||||
}
|
||||
|
||||
public bool int64_equal (int64? val_a, int64? val_b) {
|
||||
int64 a = val_a;
|
||||
int64 b = val_b;
|
||||
return a == b;
|
||||
}
|
||||
|
||||
public uint uint64_hash (uint64? val) {
|
||||
uint64 v = val;
|
||||
return (uint) ((v >> 32) ^ (v & 0xffffffffU));
|
||||
}
|
||||
|
||||
public bool uint64_equal (uint64? val_a, uint64? val_b) {
|
||||
uint64 a = val_a;
|
||||
uint64 b = val_b;
|
||||
return a == b;
|
||||
}
|
||||
|
||||
public uint bytes_hash (Bytes b) {
|
||||
return b.hash ();
|
||||
}
|
||||
|
||||
public static bool bytes_equal (Bytes a, Bytes b) {
|
||||
return a.compare (b) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,5 @@
|
||||
#include "frida-base.h"
|
||||
|
||||
#ifdef HAVE_WINDOWS
|
||||
# include <winsock2.h>
|
||||
#else
|
||||
# include <netinet/in.h>
|
||||
# include <netinet/tcp.h>
|
||||
#endif
|
||||
|
||||
void
|
||||
frida_unix_socket_tune_buffer_sizes (gint fd)
|
||||
{
|
||||
#ifndef HAVE_WINDOWS
|
||||
/* The defaults are typically as low as 4K. */
|
||||
const int buffer_size = 256 * 1024;
|
||||
|
||||
setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof (buffer_size));
|
||||
setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof (buffer_size));
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
frida_tcp_enable_nodelay (GSocket * socket)
|
||||
{
|
||||
g_socket_set_option (socket, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
_frida_version_string (void)
|
||||
{
|
||||
|
||||
+27
-86
@@ -1,99 +1,22 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
public const uint16 DEFAULT_CONTROL_PORT = 27042;
|
||||
public const uint16 DEFAULT_CLUSTER_PORT = 27052;
|
||||
|
||||
public SocketConnectable parse_control_address (string? address, uint16 port = 0) throws Error {
|
||||
return parse_socket_address (address, port, "127.0.0.1", DEFAULT_CONTROL_PORT);
|
||||
return parse_frida_address (address, port, DEFAULT_CONTROL_PORT);
|
||||
}
|
||||
|
||||
public SocketConnectable parse_cluster_address (string? address, uint16 port = 0) throws Error {
|
||||
return parse_socket_address (address, port, "127.0.0.1", DEFAULT_CLUSTER_PORT);
|
||||
return parse_frida_address (address, port, DEFAULT_CLUSTER_PORT);
|
||||
}
|
||||
|
||||
public SocketConnectable parse_socket_address (string? address, uint16 port, string default_address,
|
||||
uint16 default_port) throws Error {
|
||||
if (address == null)
|
||||
address = default_address;
|
||||
if (port == 0)
|
||||
port = default_port;
|
||||
|
||||
#if !WINDOWS
|
||||
if (address.has_prefix ("unix:")) {
|
||||
string path = address.substring (5);
|
||||
|
||||
UnixSocketAddressType type = UnixSocketAddress.abstract_names_supported ()
|
||||
? UnixSocketAddressType.ABSTRACT
|
||||
: UnixSocketAddressType.PATH;
|
||||
|
||||
return new UnixSocketAddress.with_type (path, -1, type);
|
||||
}
|
||||
#endif
|
||||
|
||||
private SocketConnectable parse_frida_address (string? address, uint16 port, uint16 default_port) throws Error {
|
||||
try {
|
||||
return NetworkAddress.parse (address, port);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
namespace UnixSocket {
|
||||
public extern void tune_buffer_sizes (int fd);
|
||||
}
|
||||
|
||||
namespace Tcp {
|
||||
public extern void enable_nodelay (Socket socket);
|
||||
}
|
||||
|
||||
namespace Udp {
|
||||
public size_t recv (uint8[] data, DatagramBased source, Cancellable? cancellable, out InetSocketAddress remote_address)
|
||||
throws Error, IOError {
|
||||
var v = InputVector ();
|
||||
v.buffer = data;
|
||||
v.size = data.length;
|
||||
|
||||
InputVector[] vectors = { v };
|
||||
|
||||
var m = InputMessage ();
|
||||
remote_address = null;
|
||||
m.address = &remote_address;
|
||||
m.vectors = vectors;
|
||||
m.num_vectors = vectors.length;
|
||||
|
||||
InputMessage[] messages = { m };
|
||||
|
||||
try {
|
||||
source.receive_messages (messages, 0, 0, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
return messages[0].bytes_received;
|
||||
}
|
||||
|
||||
public void send (uint8[] data, DatagramBased sink, Cancellable? cancellable) throws Error, IOError {
|
||||
send_to (data, null, sink, cancellable);
|
||||
}
|
||||
|
||||
public void send_to (uint8[] data, InetSocketAddress? dest_addr, DatagramBased sink, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var v = OutputVector ();
|
||||
v.buffer = data;
|
||||
v.size = data.length;
|
||||
|
||||
OutputVector[] vectors = { v };
|
||||
|
||||
var m = OutputMessage ();
|
||||
m.address = dest_addr;
|
||||
m.vectors = vectors;
|
||||
m.num_vectors = vectors.length;
|
||||
|
||||
OutputMessage[] messages = { m };
|
||||
|
||||
try {
|
||||
sink.send_messages (messages, 0, 0, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
return Efni.parse_socket_address (address, port, "127.0.0.1", default_port);
|
||||
} catch (Efni.Error e) {
|
||||
throw error_from_efni (e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1117,6 +1040,9 @@ namespace Frida {
|
||||
construct {
|
||||
websocket.max_incoming_payload_size = (256 * 1024) + 1; // XXX: There's an off-by-one error in libsoup
|
||||
|
||||
if (websocket.get_class ().find_property ("max-total-message-size") != null)
|
||||
websocket.set ("max-total-message-size", websocket.max_incoming_payload_size);
|
||||
|
||||
websocket.closed.connect (on_closed);
|
||||
websocket.message.connect (on_message);
|
||||
}
|
||||
@@ -1166,7 +1092,7 @@ namespace Frida {
|
||||
|
||||
if (recv_queue.len == 0) {
|
||||
#if HAVE_SOUP_WEBSOCKET_BACKPRESSURE
|
||||
websocket.resume_input ();
|
||||
resume_websocket_input ();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1183,6 +1109,21 @@ namespace Frida {
|
||||
return n;
|
||||
}
|
||||
|
||||
#if HAVE_SOUP_WEBSOCKET_BACKPRESSURE
|
||||
private void resume_websocket_input () {
|
||||
if (main_context.is_owner ()) {
|
||||
websocket.resume_input ();
|
||||
} else {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
websocket.resume_input ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public override ssize_t write (uint8[] buffer) {
|
||||
with_state_lock (() => {
|
||||
send_queue.append (buffer);
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
namespace Frida {
|
||||
public abstract class VirtualStream : IOStream {
|
||||
public override InputStream input_stream {
|
||||
get {
|
||||
return _input_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public override OutputStream output_stream {
|
||||
get {
|
||||
return _output_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public IOCondition pending_io {
|
||||
get {
|
||||
lock (state)
|
||||
return _pending_io;
|
||||
}
|
||||
}
|
||||
|
||||
protected State state = OPEN;
|
||||
|
||||
private VirtualInputStream _input_stream;
|
||||
private VirtualOutputStream _output_stream;
|
||||
|
||||
private IOCondition _pending_io;
|
||||
|
||||
private Gee.Map<unowned Source, IOCondition> sources = new Gee.HashMap<unowned Source, IOCondition> ();
|
||||
|
||||
protected Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
protected MainContext main_context;
|
||||
|
||||
protected enum State {
|
||||
CREATED,
|
||||
OPENING,
|
||||
OPEN,
|
||||
CLOSED
|
||||
}
|
||||
|
||||
protected delegate void WorkFunc ();
|
||||
|
||||
construct {
|
||||
_input_stream = new VirtualInputStream (this);
|
||||
_output_stream = new VirtualOutputStream (this);
|
||||
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
state = query_initial_state ();
|
||||
_pending_io = query_events ();
|
||||
}
|
||||
|
||||
public override void dispose () {
|
||||
_output_stream.detach ();
|
||||
_input_stream.detach ();
|
||||
|
||||
io_cancellable.cancel ();
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
protected virtual State query_initial_state () {
|
||||
return OPEN;
|
||||
}
|
||||
|
||||
protected abstract IOCondition query_events ();
|
||||
|
||||
public override bool close (GLib.Cancellable? cancellable) throws IOError {
|
||||
_close ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, GLib.Cancellable? cancellable) throws IOError {
|
||||
_close ();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void _close () {
|
||||
if (main_context.is_owner ()) {
|
||||
do_close ();
|
||||
} else {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
do_close ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
}
|
||||
}
|
||||
|
||||
private void do_close () {
|
||||
if (state == CLOSED)
|
||||
return;
|
||||
|
||||
handle_close ();
|
||||
}
|
||||
|
||||
protected abstract void handle_close ();
|
||||
|
||||
public virtual void shutdown_read () throws IOError {
|
||||
}
|
||||
|
||||
public virtual void shutdown_write () throws IOError {
|
||||
}
|
||||
|
||||
public abstract ssize_t read (uint8[] buffer) throws IOError;
|
||||
|
||||
public abstract ssize_t write (uint8[] buffer) throws IOError;
|
||||
|
||||
protected void with_state_lock (WorkFunc func) {
|
||||
lock (state)
|
||||
func ();
|
||||
}
|
||||
|
||||
public void register_source (Source source, IOCondition condition) {
|
||||
lock (state)
|
||||
sources[source] = condition | IOCondition.ERR | IOCondition.HUP;
|
||||
}
|
||||
|
||||
public void unregister_source (Source source) {
|
||||
lock (state)
|
||||
sources.unset (source);
|
||||
}
|
||||
|
||||
protected virtual void update_pending_io () {
|
||||
lock (state) {
|
||||
_pending_io = query_events ();
|
||||
|
||||
foreach (var entry in sources.entries) {
|
||||
unowned Source source = entry.key;
|
||||
IOCondition c = entry.value;
|
||||
if ((_pending_io & c) != 0)
|
||||
source.set_ready_time (0);
|
||||
}
|
||||
}
|
||||
|
||||
notify_property ("pending-io");
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class VirtualInputStream : InputStream, PollableInputStream {
|
||||
private weak VirtualStream stream;
|
||||
|
||||
public VirtualInputStream (VirtualStream stream) {
|
||||
Object ();
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
internal void detach () {
|
||||
stream = null;
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable) throws IOError {
|
||||
if (stream != null)
|
||||
stream.shutdown_read ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
|
||||
return close (cancellable);
|
||||
}
|
||||
|
||||
public override ssize_t read (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
if (stream == null)
|
||||
return 0;
|
||||
|
||||
if (!is_readable ()) {
|
||||
bool done = false;
|
||||
var mutex = Mutex ();
|
||||
var cond = Cond ();
|
||||
|
||||
ulong io_handler = stream.notify["pending-io"].connect ((obj, pspec) => {
|
||||
if (is_readable ()) {
|
||||
mutex.lock ();
|
||||
done = true;
|
||||
cond.signal ();
|
||||
mutex.unlock ();
|
||||
}
|
||||
});
|
||||
ulong cancellation_handler = 0;
|
||||
if (cancellable != null) {
|
||||
cancellation_handler = cancellable.connect (() => {
|
||||
mutex.lock ();
|
||||
done = true;
|
||||
cond.signal ();
|
||||
mutex.unlock ();
|
||||
});
|
||||
}
|
||||
|
||||
if (!is_readable ()) {
|
||||
mutex.lock ();
|
||||
while (!done)
|
||||
cond.wait (mutex);
|
||||
mutex.unlock ();
|
||||
}
|
||||
|
||||
if (cancellation_handler != 0)
|
||||
cancellable.disconnect (cancellation_handler);
|
||||
stream.disconnect (io_handler);
|
||||
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
|
||||
return stream.read (buffer);
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_readable () {
|
||||
if (stream == null)
|
||||
return true;
|
||||
return (stream.pending_io & IOCondition.IN) != 0;
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (this, new VirtualIOSource (stream, IOCondition.IN), cancellable);
|
||||
}
|
||||
|
||||
public ssize_t read_nonblocking_fn (uint8[] buffer) throws GLib.Error {
|
||||
if (stream == null)
|
||||
return 0;
|
||||
return stream.read (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class VirtualOutputStream : OutputStream, PollableOutputStream {
|
||||
private weak VirtualStream? stream;
|
||||
|
||||
public VirtualOutputStream (VirtualStream stream) {
|
||||
Object ();
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
internal void detach () {
|
||||
stream = null;
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable) throws IOError {
|
||||
if (stream != null)
|
||||
stream.shutdown_write ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
|
||||
return close (cancellable);
|
||||
}
|
||||
|
||||
public override bool flush (GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool flush_async (int io_priority, GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ssize_t write (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
return stream.write (buffer);
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_writable () {
|
||||
if (stream == null)
|
||||
return false;
|
||||
return (stream.pending_io & IOCondition.OUT) != 0;
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (this, new VirtualIOSource (stream, IOCondition.OUT), cancellable);
|
||||
}
|
||||
|
||||
public ssize_t write_nonblocking_fn (uint8[]? buffer) throws GLib.Error {
|
||||
if (stream == null)
|
||||
throw new IOError.CLOSED ("Virtual is closed");
|
||||
return stream.write (buffer);
|
||||
}
|
||||
|
||||
public PollableReturn writev_nonblocking_fn (OutputVector[] vectors, out size_t bytes_written) throws GLib.Error {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
private class VirtualIOSource : Source {
|
||||
public VirtualStream? stream;
|
||||
public IOCondition condition;
|
||||
|
||||
public VirtualIOSource (VirtualStream stream, IOCondition condition) {
|
||||
this.stream = stream;
|
||||
this.condition = condition;
|
||||
|
||||
if (stream != null)
|
||||
stream.register_source (this, condition);
|
||||
}
|
||||
|
||||
~VirtualIOSource () {
|
||||
if (stream != null)
|
||||
stream.unregister_source (this);
|
||||
}
|
||||
|
||||
protected override bool prepare (out int timeout) {
|
||||
timeout = -1;
|
||||
return is_ready ();
|
||||
}
|
||||
|
||||
protected override bool check () {
|
||||
return is_ready ();
|
||||
}
|
||||
|
||||
private bool is_ready () {
|
||||
IOCondition pending_io = (stream != null) ? stream.pending_io : IOCondition.IN;
|
||||
return (pending_io & condition) != 0;
|
||||
}
|
||||
|
||||
protected override bool dispatch (SourceFunc? callback) {
|
||||
set_ready_time (-1);
|
||||
|
||||
if (callback == null)
|
||||
return Source.REMOVE;
|
||||
|
||||
return callback ();
|
||||
}
|
||||
|
||||
protected static bool closure_callback (Closure closure) {
|
||||
var return_value = Value (typeof (bool));
|
||||
|
||||
closure.invoke (ref return_value, {});
|
||||
|
||||
return return_value.get_boolean ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,432 +0,0 @@
|
||||
namespace Frida {
|
||||
public interface ObjectBuilder : Object {
|
||||
public abstract unowned ObjectBuilder begin_dictionary ();
|
||||
public abstract unowned ObjectBuilder set_member_name (string name);
|
||||
public abstract unowned ObjectBuilder end_dictionary ();
|
||||
|
||||
public abstract unowned ObjectBuilder begin_array ();
|
||||
public abstract unowned ObjectBuilder end_array ();
|
||||
|
||||
public abstract unowned ObjectBuilder add_null_value ();
|
||||
public abstract unowned ObjectBuilder add_bool_value (bool val);
|
||||
public abstract unowned ObjectBuilder add_int64_value (int64 val);
|
||||
public abstract unowned ObjectBuilder add_uint64_value (uint64 val);
|
||||
public abstract unowned ObjectBuilder add_data_value (Bytes val);
|
||||
public abstract unowned ObjectBuilder add_string_value (string val);
|
||||
public abstract unowned ObjectBuilder add_uuid_value (uint8[] val);
|
||||
public abstract unowned ObjectBuilder add_raw_value (Bytes val);
|
||||
|
||||
public abstract Bytes build ();
|
||||
}
|
||||
|
||||
public interface ObjectReader : Object {
|
||||
public abstract bool has_member (string name) throws Error;
|
||||
public abstract unowned ObjectReader read_member (string name) throws Error;
|
||||
public abstract unowned ObjectReader end_member ();
|
||||
|
||||
public abstract uint count_elements () throws Error;
|
||||
public abstract unowned ObjectReader read_element (uint index) throws Error;
|
||||
public abstract unowned ObjectReader end_element () throws Error;
|
||||
|
||||
public abstract bool get_bool_value () throws Error;
|
||||
public abstract uint8 get_uint8_value () throws Error;
|
||||
public abstract uint16 get_uint16_value () throws Error;
|
||||
public abstract int64 get_int64_value () throws Error;
|
||||
public abstract uint64 get_uint64_value () throws Error;
|
||||
public abstract Bytes get_data_value () throws Error;
|
||||
public abstract unowned string get_string_value () throws Error;
|
||||
public abstract unowned string get_uuid_value () throws Error;
|
||||
}
|
||||
|
||||
public sealed class VariantReader : Object, ObjectReader {
|
||||
public Variant root_object {
|
||||
get {
|
||||
return scopes.peek_head ().val;
|
||||
}
|
||||
}
|
||||
|
||||
public Variant current_object {
|
||||
get {
|
||||
return scopes.peek_tail ().val;
|
||||
}
|
||||
}
|
||||
|
||||
private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();
|
||||
|
||||
public VariantReader (Variant v) {
|
||||
push_scope (v);
|
||||
}
|
||||
|
||||
public string[] list_members () throws Error {
|
||||
var scope = peek_scope ();
|
||||
if (scope.dict == null)
|
||||
throw new Error.PROTOCOL ("Dictionary expected, but at %s", scope.val.print (true));
|
||||
|
||||
var members = new Gee.ArrayList<string> ();
|
||||
foreach (var item in scope.val) {
|
||||
string k;
|
||||
Variant v;
|
||||
item.get ("{sv}", out k, out v);
|
||||
|
||||
members.add (k);
|
||||
}
|
||||
return members.to_array ();
|
||||
}
|
||||
|
||||
public bool has_member (string name) throws Error {
|
||||
var scope = peek_scope ();
|
||||
if (scope.dict == null)
|
||||
throw new Error.PROTOCOL ("Dictionary expected, but at %s", scope.val.print (true));
|
||||
return scope.dict.contains (name);
|
||||
}
|
||||
|
||||
public unowned ObjectReader read_member (string name) throws Error {
|
||||
var scope = peek_scope ();
|
||||
if (scope.dict == null)
|
||||
throw new Error.PROTOCOL ("Dictionary expected, but at %s", scope.val.print (true));
|
||||
|
||||
Variant? v = scope.dict.lookup_value (name, null);
|
||||
if (v == null)
|
||||
throw new Error.PROTOCOL ("Key '%s' not found in dictionary: %s", name, scope.val.print (true));
|
||||
|
||||
push_scope (v);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectReader end_member () {
|
||||
pop_scope ();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public uint count_elements () throws Error {
|
||||
var scope = peek_scope ();
|
||||
scope.check_array ();
|
||||
return (uint) scope.val.n_children ();
|
||||
}
|
||||
|
||||
public unowned ObjectReader read_element (uint index) throws Error {
|
||||
var scope = peek_scope ();
|
||||
scope.check_array ();
|
||||
push_scope (scope.val.get_child_value (index).get_variant ());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectReader end_element () throws Error {
|
||||
pop_scope ();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public bool get_bool_value () throws Error {
|
||||
return peek_scope ().get_value (VariantType.BOOLEAN).get_boolean ();
|
||||
}
|
||||
|
||||
public uint8 get_uint8_value () throws Error {
|
||||
return peek_scope ().get_value (VariantType.BYTE).get_byte ();
|
||||
}
|
||||
|
||||
public uint16 get_uint16_value () throws Error {
|
||||
return peek_scope ().get_value (VariantType.UINT16).get_uint16 ();
|
||||
}
|
||||
|
||||
public int64 get_int64_value () throws Error {
|
||||
return peek_scope ().get_value (VariantType.INT64).get_int64 ();
|
||||
}
|
||||
|
||||
public uint64 get_uint64_value () throws Error {
|
||||
return peek_scope ().get_value (VariantType.UINT64).get_uint64 ();
|
||||
}
|
||||
|
||||
public Bytes get_data_value () throws Error {
|
||||
return peek_scope ().get_value (new VariantType.array (VariantType.BYTE)).get_data_as_bytes ();
|
||||
}
|
||||
|
||||
public unowned string get_string_value () throws Error {
|
||||
return peek_scope ().get_value (VariantType.STRING).get_string ();
|
||||
}
|
||||
|
||||
public unowned string get_uuid_value () throws Error {
|
||||
return peek_scope ().get_value (VariantType.STRING).get_string (); // TODO: Use a tuple to avoid ambiguity.
|
||||
}
|
||||
|
||||
private void push_scope (Variant v) {
|
||||
scopes.offer_tail (new Scope (v));
|
||||
}
|
||||
|
||||
private Scope peek_scope () {
|
||||
return scopes.peek_tail ();
|
||||
}
|
||||
|
||||
private Scope pop_scope () {
|
||||
return scopes.poll_tail ();
|
||||
}
|
||||
|
||||
private class Scope {
|
||||
public Variant val;
|
||||
public VariantDict? dict;
|
||||
public bool is_array = false;
|
||||
|
||||
public Scope (Variant v) {
|
||||
val = v;
|
||||
|
||||
VariantType t = v.get_type ();
|
||||
if (t.equal (VariantType.VARDICT))
|
||||
dict = new VariantDict (v);
|
||||
else if (t.is_subtype_of (VariantType.ARRAY))
|
||||
is_array = true;
|
||||
}
|
||||
|
||||
public Variant get_value (VariantType expected_type) throws Error {
|
||||
if (!val.get_type ().equal (expected_type)) {
|
||||
throw new Error.PROTOCOL ("Expected type '%s', got '%s'",
|
||||
(string) expected_type.peek_string (),
|
||||
(string) val.get_type ().peek_string ());
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public void check_array () throws Error {
|
||||
if (!is_array)
|
||||
throw new Error.PROTOCOL ("Array expected, but at %s", val.print (true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class JsonObjectBuilder : Object, ObjectBuilder {
|
||||
private Json.Builder builder = new Json.Builder ();
|
||||
private Gee.Map<string, Bytes> raw_values = new Gee.HashMap<string, Bytes> ();
|
||||
|
||||
public unowned ObjectBuilder begin_dictionary () {
|
||||
builder.begin_object ();
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder set_member_name (string name) {
|
||||
builder.set_member_name (name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder end_dictionary () {
|
||||
builder.end_object ();
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder begin_array () {
|
||||
builder.begin_array ();
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder end_array () {
|
||||
builder.end_array ();
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_null_value () {
|
||||
builder.add_null_value ();
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_bool_value (bool val) {
|
||||
builder.add_boolean_value (val);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_int64_value (int64 val) {
|
||||
builder.add_int_value (val);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_uint64_value (uint64 val) {
|
||||
builder.add_int_value ((int64) val);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_data_value (Bytes val) {
|
||||
builder.add_string_value (Base64.encode (val.get_data ()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_string_value (string val) {
|
||||
builder.add_string_value (val);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_uuid_value (uint8[] val) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_raw_value (Bytes val) {
|
||||
string uuid = Uuid.string_random ();
|
||||
builder.add_string_value (uuid);
|
||||
raw_values[uuid] = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Bytes build () {
|
||||
string json = Json.to_string (builder.get_root (), false);
|
||||
|
||||
foreach (var e in raw_values.entries) {
|
||||
unowned string uuid = e.key;
|
||||
Bytes val = e.value;
|
||||
|
||||
unowned string raw_str = (string) val.get_data ();
|
||||
string str = raw_str[:(long) val.get_size ()];
|
||||
|
||||
json = json.replace ("\"" + uuid + "\"", str);
|
||||
}
|
||||
|
||||
return new Bytes (json.data);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class JsonObjectReader : Object, ObjectReader {
|
||||
private Json.Reader reader;
|
||||
|
||||
public JsonObjectReader (string json) throws Error {
|
||||
try {
|
||||
reader = make_json_reader (json);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public bool has_member (string name) throws Error {
|
||||
bool result = reader.read_member (name);
|
||||
reader.end_member ();
|
||||
return result;
|
||||
}
|
||||
|
||||
public unowned ObjectReader read_member (string name) throws Error {
|
||||
if (!reader.read_member (name))
|
||||
throw_dict_access_error ();
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectReader end_member () {
|
||||
reader.end_member ();
|
||||
return this;
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private void throw_dict_access_error () throws Error {
|
||||
GLib.Error e = reader.get_error ();
|
||||
reader.end_member ();
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
public uint count_elements () throws Error {
|
||||
int n = reader.count_elements ();
|
||||
if (n == -1)
|
||||
throw_array_access_error ();
|
||||
return n;
|
||||
}
|
||||
|
||||
public unowned ObjectReader read_element (uint index) throws Error {
|
||||
if (!reader.read_element (index)) {
|
||||
GLib.Error e = reader.get_error ();
|
||||
reader.end_element ();
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectReader end_element () throws Error {
|
||||
reader.end_element ();
|
||||
return this;
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private void throw_array_access_error () throws Error {
|
||||
GLib.Error e = reader.get_error ();
|
||||
reader.end_element ();
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
public bool get_bool_value () throws Error {
|
||||
bool v = reader.get_boolean_value ();
|
||||
if (!v)
|
||||
maybe_throw_value_access_error ();
|
||||
return v;
|
||||
}
|
||||
|
||||
public uint8 get_uint8_value () throws Error {
|
||||
int64 v = get_int64_value ();
|
||||
if (v < 0 || v > uint8.MAX)
|
||||
throw new Error.PROTOCOL ("Invalid uint8");
|
||||
return (uint8) v;
|
||||
}
|
||||
|
||||
public uint16 get_uint16_value () throws Error {
|
||||
int64 v = get_int64_value ();
|
||||
if (v < 0 || v > uint16.MAX)
|
||||
throw new Error.PROTOCOL ("Invalid uint16");
|
||||
return (uint16) v;
|
||||
}
|
||||
|
||||
public int64 get_int64_value () throws Error {
|
||||
int64 v = reader.get_int_value ();
|
||||
if (v == 0)
|
||||
maybe_throw_value_access_error ();
|
||||
return v;
|
||||
}
|
||||
|
||||
public uint64 get_uint64_value () throws Error {
|
||||
int64 v = get_int64_value ();
|
||||
if (v < 0)
|
||||
throw new Error.PROTOCOL ("Invalid uint64");
|
||||
return v;
|
||||
}
|
||||
|
||||
public Bytes get_data_value () throws Error {
|
||||
return new Bytes (Base64.decode (get_string_value ()));
|
||||
}
|
||||
|
||||
public unowned string get_string_value () throws Error {
|
||||
unowned string? v = reader.get_string_value ();
|
||||
if (v == null)
|
||||
maybe_throw_value_access_error ();
|
||||
return v;
|
||||
}
|
||||
|
||||
public unowned string get_uuid_value () throws Error {
|
||||
return get_string_value ();
|
||||
}
|
||||
|
||||
private void maybe_throw_value_access_error () throws Error {
|
||||
GLib.Error? e = reader.get_error ();
|
||||
if (e == null)
|
||||
return;
|
||||
reader.end_member ();
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs a Json.Reader for the given JSON string, working around the
|
||||
* json_node_copy() bug in unpatched json-glib (GNOME bugzilla #774684).
|
||||
*
|
||||
* Json.Reader's set_root() copies the supplied node via json_node_copy().
|
||||
* For object/array nodes that copy is a thin shell that shares the original
|
||||
* JsonObject/JsonArray via refcount, but the members inside the shared
|
||||
* container have their parent pointers set to the *original* root, not the
|
||||
* shell. If the original is then dropped, json_reader_end_member()'s walk
|
||||
* via json_node_get_parent() touches a freed node — which on builds with
|
||||
* glib checks enabled (e.g. Fedora) trips JSON_NODE_IS_VALID and aborts.
|
||||
*
|
||||
* We keep the original root alive by attaching it to the reader as object
|
||||
* data; the destroy notify drops our reference when the reader is finalized.
|
||||
*/
|
||||
public Json.Reader make_json_reader (string json) throws GLib.Error {
|
||||
return make_json_reader_taking_node (Json.from_string (json));
|
||||
}
|
||||
|
||||
public Json.Reader make_json_reader_taking_node (owned Json.Node root) {
|
||||
var reader = new Json.Reader (root);
|
||||
reader.set_data<Json.Node> ("frida-json-root", (owned) root);
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
namespace Frida {
|
||||
public sealed class XpcClient : Object {
|
||||
public signal void message (Darwin.Xpc.Object obj);
|
||||
|
||||
public State state {
|
||||
get {
|
||||
return _state;
|
||||
}
|
||||
}
|
||||
|
||||
public Darwin.Xpc.Connection connection {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Darwin.GCD.DispatchQueue queue {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private State _state;
|
||||
private string? close_reason;
|
||||
private MainContext main_context;
|
||||
|
||||
public enum State {
|
||||
OPEN,
|
||||
CLOSING,
|
||||
CLOSED,
|
||||
}
|
||||
|
||||
public static XpcClient make_for_mach_service (string name, Darwin.GCD.DispatchQueue queue) {
|
||||
return new XpcClient (Darwin.Xpc.Connection.create_mach_service (name, queue, 0), queue);
|
||||
}
|
||||
|
||||
public XpcClient (Darwin.Xpc.Connection connection, Darwin.GCD.DispatchQueue queue) {
|
||||
Object (connection: connection, queue: queue);
|
||||
}
|
||||
|
||||
construct {
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
connection.set_event_handler (on_event);
|
||||
connection.activate ();
|
||||
}
|
||||
|
||||
public override void dispose () {
|
||||
if (close_reason != null) {
|
||||
change_state (CLOSED);
|
||||
} else {
|
||||
change_state (CLOSING);
|
||||
this.ref ();
|
||||
connection.cancel ();
|
||||
}
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
public async Darwin.Xpc.Object request (Darwin.Xpc.Object message, Cancellable? cancellable) throws Error, IOError {
|
||||
Darwin.Xpc.Object? reply = null;
|
||||
connection.send_message_with_reply (message, queue, r => {
|
||||
schedule_on_frida_thread (() => {
|
||||
reply = r;
|
||||
request.callback ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
});
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
connection.cancel ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
|
||||
if (reply.type == Darwin.Xpc.Error.TYPE) {
|
||||
var e = (Darwin.Xpc.Error) reply;
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION));
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
public void post (Darwin.Xpc.Object message) throws Error, IOError {
|
||||
connection.send_message (message);
|
||||
}
|
||||
|
||||
private void change_state (State new_state) {
|
||||
_state = new_state;
|
||||
notify_property ("state");
|
||||
}
|
||||
|
||||
private void on_event (Darwin.Xpc.Object obj) {
|
||||
schedule_on_frida_thread (() => {
|
||||
if (obj.type == Darwin.Xpc.Error.TYPE) {
|
||||
var e = (Darwin.Xpc.Error) obj;
|
||||
close_reason = e.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION);
|
||||
switch (state) {
|
||||
case OPEN:
|
||||
change_state (CLOSED);
|
||||
break;
|
||||
case CLOSING:
|
||||
change_state (CLOSED);
|
||||
unref ();
|
||||
break;
|
||||
case CLOSED:
|
||||
assert_not_reached ();
|
||||
}
|
||||
} else {
|
||||
message (obj);
|
||||
}
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
private void schedule_on_frida_thread (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
source.attach (main_context);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class XpcObjectReader {
|
||||
public Darwin.Xpc.Object root_object {
|
||||
get {
|
||||
return scopes.peek_head ().object;
|
||||
}
|
||||
}
|
||||
|
||||
public Darwin.Xpc.Object current_object {
|
||||
get {
|
||||
return scopes.peek_tail ().object;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate int TranslateErrorFunc (string domain, int code, string description);
|
||||
|
||||
private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();
|
||||
|
||||
public XpcObjectReader (Darwin.Xpc.Object obj) {
|
||||
push_scope (obj);
|
||||
}
|
||||
|
||||
public bool has_member (string name) throws Error {
|
||||
return peek_scope ().get_dictionary ().get_value (name) != null;
|
||||
}
|
||||
|
||||
public bool try_read_member (string name) throws Error {
|
||||
var scope = peek_scope ();
|
||||
var dict = scope.get_dictionary ();
|
||||
var val = dict.get_value (name);
|
||||
if (val == null)
|
||||
return false;
|
||||
|
||||
push_scope (val);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public unowned XpcObjectReader read_member (string name) throws Error {
|
||||
var scope = peek_scope ();
|
||||
var dict = scope.get_dictionary ();
|
||||
var val = dict.get_value (name);
|
||||
if (val == null)
|
||||
throw new Error.PROTOCOL ("Key '%s' not found in dictionary: %s", name, scope.object.to_string ());
|
||||
|
||||
push_scope (val);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned XpcObjectReader end_member () {
|
||||
pop_scope ();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public size_t count_elements () throws Error {
|
||||
return peek_scope ().get_array ().count;
|
||||
}
|
||||
|
||||
public unowned XpcObjectReader read_element (size_t index) throws Error {
|
||||
push_scope (peek_scope ().get_array ().get_value (index));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned XpcObjectReader end_element () throws Error {
|
||||
pop_scope ();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public bool get_bool_value () throws Error {
|
||||
return peek_scope ().get_object<Darwin.Xpc.Bool> (Darwin.Xpc.Bool.TYPE).get_value ();
|
||||
}
|
||||
|
||||
public int64 get_int64_value () throws Error {
|
||||
return peek_scope ().get_object<Darwin.Xpc.Int64> (Darwin.Xpc.Int64.TYPE).get_value ();
|
||||
}
|
||||
|
||||
public uint64 get_uint64_value () throws Error {
|
||||
return peek_scope ().get_object<Darwin.Xpc.UInt64> (Darwin.Xpc.UInt64.TYPE).get_value ();
|
||||
}
|
||||
|
||||
public unowned uint8[] get_data_value () throws Error {
|
||||
var data = peek_scope ().get_object<Darwin.Xpc.Data> (Darwin.Xpc.Data.TYPE);
|
||||
unowned uint8[] buf = (uint8[]) data.get_bytes_ptr ();
|
||||
buf.length = (int) data.get_length ();
|
||||
return buf;
|
||||
}
|
||||
|
||||
public unowned string get_string_value () throws Error {
|
||||
return peek_scope ().get_object<Darwin.Xpc.String> (Darwin.Xpc.String.TYPE).get_string_ptr ();
|
||||
}
|
||||
|
||||
public unowned uint8[] get_uuid_value () throws Error {
|
||||
return peek_scope ().get_object<Darwin.Xpc.Uuid> (Darwin.Xpc.Uuid.TYPE).get_bytes ()[:16];
|
||||
}
|
||||
|
||||
public unowned string get_error_description () throws Error {
|
||||
var error = peek_scope ().get_object<Darwin.Xpc.Error> (Darwin.Xpc.Error.TYPE);
|
||||
return error.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION);
|
||||
}
|
||||
|
||||
public void check_nserror (TranslateErrorFunc translate_error) throws Error {
|
||||
if (!has_member ("error"))
|
||||
return;
|
||||
|
||||
read_member ("error");
|
||||
|
||||
unowned string domain = read_member ("domain").get_string_value ();
|
||||
end_member ();
|
||||
|
||||
var code = (int) read_member ("code").get_int64_value ();
|
||||
end_member ();
|
||||
|
||||
string description = read_member ("userInfo").read_member ("NSLocalizedDescription").get_string_value ();
|
||||
if (description.has_suffix ("."))
|
||||
description = description[:description.length - 1];
|
||||
|
||||
throw (Error) new GLib.Error.literal (
|
||||
Quark.from_string ("frida-error-quark"),
|
||||
translate_error (domain, code, description),
|
||||
description);
|
||||
}
|
||||
|
||||
public unowned Darwin.Xpc.Object get_object_value (Darwin.Xpc.Type expected_type) throws Error {
|
||||
return peek_scope ().get_object<Darwin.Xpc.Object> (expected_type);
|
||||
}
|
||||
|
||||
private void push_scope (Darwin.Xpc.Object obj) {
|
||||
scopes.offer_tail (new Scope (obj));
|
||||
}
|
||||
|
||||
private Scope peek_scope () {
|
||||
return scopes.peek_tail ();
|
||||
}
|
||||
|
||||
private Scope pop_scope () {
|
||||
return scopes.poll_tail ();
|
||||
}
|
||||
|
||||
private class Scope {
|
||||
public Darwin.Xpc.Object object;
|
||||
private unowned Darwin.Xpc.Type type;
|
||||
|
||||
public Scope (Darwin.Xpc.Object obj) {
|
||||
object = obj;
|
||||
type = obj.type;
|
||||
}
|
||||
|
||||
public unowned T get_object<T> (Darwin.Xpc.Type expected_type) throws Error {
|
||||
if (type != expected_type)
|
||||
throw new Error.PROTOCOL ("Expected type '%s', got '%s'", expected_type.name, type.name);
|
||||
return object;
|
||||
}
|
||||
|
||||
public unowned Darwin.Xpc.Array get_array () throws Error {
|
||||
return get_object<Darwin.Xpc.Array> (Darwin.Xpc.Array.TYPE);
|
||||
}
|
||||
|
||||
public unowned Darwin.Xpc.Dictionary get_dictionary () throws Error {
|
||||
return get_object<Darwin.Xpc.Dictionary> (Darwin.Xpc.Dictionary.TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida.Gadget {
|
||||
private sealed class Config : Object, Json.Serializable {
|
||||
public Object interaction {
|
||||
|
||||
@@ -8,7 +8,6 @@ extra_link_args = []
|
||||
if host_os in ['ios', 'tvos']
|
||||
extra_vala_args += [
|
||||
'--pkg=corefoundation',
|
||||
'--pkg=darwin-gcd',
|
||||
'--pkg=darwin-systemconfiguration',
|
||||
]
|
||||
extra_link_args += '-Wl,-framework,SystemConfiguration'
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
public abstract class BaseAgentSession : Object, AgentSession {
|
||||
public signal void closed ();
|
||||
|
||||
+10
-16
@@ -253,7 +253,13 @@ static ssize_t frida_write_nointr (int fd, const void * buf, size_t count);
|
||||
static int frida_close_nointr (int fd);
|
||||
static off_t frida_lseek_nointr (int fd, off_t offset, int whence);
|
||||
|
||||
#ifndef FRIDA_STDIO_OPAQUE_FILE
|
||||
#if defined (HAVE_QNX)
|
||||
G_GNUC_INTERNAL FILE _Stdin, _Stdout, _Stderr;
|
||||
|
||||
G_GNUC_INTERNAL FILE * stdin = &_Stdin;
|
||||
G_GNUC_INTERNAL FILE * stdout = &_Stdout;
|
||||
G_GNUC_INTERNAL FILE * stderr = &_Stderr;
|
||||
#elif !defined (FRIDA_STDIO_OPAQUE_FILE)
|
||||
G_GNUC_INTERNAL FILE __sF[3];
|
||||
|
||||
G_GNUC_INTERNAL FILE * stdin = &__sF[0];
|
||||
@@ -308,25 +314,13 @@ frida_libc_shim_init (void)
|
||||
G_UNLOCK (frida_stdio);
|
||||
|
||||
f0 = frida_file_new (0, FALSE, _IOFBF);
|
||||
#ifndef FRIDA_STDIO_OPAQUE_FILE
|
||||
frida_file_bind_slot (&__sF[0], f0);
|
||||
#else
|
||||
frida_file_bind_slot ((FILE *) &frida_stdio[0], f0);
|
||||
#endif
|
||||
frida_file_bind_slot (stdin, f0);
|
||||
|
||||
f1 = frida_file_new (1, FALSE, _IOLBF);
|
||||
#ifndef FRIDA_STDIO_OPAQUE_FILE
|
||||
frida_file_bind_slot (&__sF[1], f1);
|
||||
#else
|
||||
frida_file_bind_slot ((FILE *) &frida_stdio[1], f1);
|
||||
#endif
|
||||
frida_file_bind_slot (stdout, f1);
|
||||
|
||||
f2 = frida_file_new (2, FALSE, _IONBF);
|
||||
#ifndef FRIDA_STDIO_OPAQUE_FILE
|
||||
frida_file_bind_slot (&__sF[2], f2);
|
||||
#else
|
||||
frida_file_bind_slot ((FILE *) &frida_stdio[2], f2);
|
||||
#endif
|
||||
frida_file_bind_slot (stderr, f2);
|
||||
|
||||
frida_libc_shim_initialized = TRUE;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
public sealed class PortalClient : Object, AgentSessionProvider {
|
||||
public signal void resume ();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
public extern void libc_shim_prepare_to_deinit ();
|
||||
#if DARWIN
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
public sealed class ScriptEngine : Object {
|
||||
public signal void message_from_script (AgentScriptId script_id, string json, Bytes? data);
|
||||
|
||||
@@ -34,7 +34,7 @@ struct _FridaCreateArgs
|
||||
|
||||
static FridaDyldUnwindSections * frida_get_cached_sections (GumAddress range_start, GumAddress range_end);
|
||||
static FridaDyldUnwindSections * frida_create_cached_sections (FridaCreateArgs * args);
|
||||
static gboolean frida_fill_info (const GumDarwinSectionDetails * details, FridaFillInfoContext * ctx);
|
||||
static gboolean frida_fill_info (const GumMachoSectionDetails * details, FridaFillInfoContext * ctx);
|
||||
|
||||
void
|
||||
_frida_unwind_sitter_fill_unwind_sections (GumAddress invader_start, GumAddress invader_end, void * info)
|
||||
@@ -70,7 +70,7 @@ frida_create_cached_sections (FridaCreateArgs * args)
|
||||
gsize page_size;
|
||||
gpointer header;
|
||||
GumPageProtection prot;
|
||||
GumDarwinModule * module;
|
||||
GumMachoModule * module;
|
||||
FridaFillInfoContext ctx;
|
||||
|
||||
page_size = gum_query_page_size ();
|
||||
@@ -87,14 +87,14 @@ frida_create_cached_sections (FridaCreateArgs * args)
|
||||
cached_sections = g_slice_new0 (FridaDyldUnwindSections);
|
||||
cached_sections->mh = header;
|
||||
|
||||
module = gum_darwin_module_new_from_memory ("Frida", mach_task_self (), GPOINTER_TO_SIZE (header),
|
||||
GUM_DARWIN_MODULE_FLAGS_NONE, NULL);
|
||||
module = gum_macho_module_new_from_memory ("Frida", mach_task_self (), GPOINTER_TO_SIZE (header),
|
||||
GUM_MACHO_MODULE_FLAGS_NONE, NULL);
|
||||
if (module == NULL)
|
||||
return cached_sections;
|
||||
|
||||
ctx.info = cached_sections;
|
||||
ctx.missing_info = 2;
|
||||
gum_darwin_module_enumerate_sections (module, (GumFoundDarwinSectionFunc) frida_fill_info, &ctx);
|
||||
gum_macho_module_enumerate_sections (module, (GumFoundMachoSectionFunc) frida_fill_info, &ctx);
|
||||
|
||||
g_object_unref (module);
|
||||
|
||||
@@ -102,7 +102,7 @@ frida_create_cached_sections (FridaCreateArgs * args)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_fill_info (const GumDarwinSectionDetails * details, FridaFillInfoContext * ctx)
|
||||
frida_fill_info (const GumMachoSectionDetails * details, FridaFillInfoContext * ctx)
|
||||
{
|
||||
if (strcmp ("__TEXT", details->segment_name) != 0)
|
||||
return TRUE;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "pipe-glue.h"
|
||||
|
||||
#include <efni.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
@@ -66,7 +67,7 @@ _frida_pipe_transport_create_backend (gchar ** local_address, gchar ** remote_ad
|
||||
|
||||
fcntl (fd, F_SETFD, FD_CLOEXEC);
|
||||
setsockopt (fd, SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, sizeof (no_sigpipe));
|
||||
frida_unix_socket_tune_buffer_sizes (fd);
|
||||
efni_unix_socket_tune_buffer_sizes (fd);
|
||||
}
|
||||
|
||||
status = fileport_makeport (sockets[0], &local_wrapper);
|
||||
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
public sealed class PipeTransport : Object {
|
||||
public string local_address {
|
||||
@@ -157,7 +159,7 @@ namespace Frida {
|
||||
UnixSocketAddressType type = UnixSocketAddress.abstract_names_supported ()
|
||||
? UnixSocketAddressType.ABSTRACT
|
||||
: UnixSocketAddressType.PATH;
|
||||
var server_address = new UnixSocketAddress.with_type (path, -1, type);
|
||||
var server_address = new UnixSocketAddress.with_type (path.to_utf8 (), type);
|
||||
|
||||
if (role == "server") {
|
||||
var socket = new Socket (SocketFamily.UNIX, SocketType.STREAM, SocketProtocol.DEFAULT);
|
||||
|
||||
+61
-49
@@ -14,7 +14,7 @@ if not meson.get_compiler('vala').version().endswith('-frida')
|
||||
Frida currently relies on features not yet upstream. Grab the Frida-optimized
|
||||
Vala sources from:
|
||||
|
||||
https://github.com/frida/vala
|
||||
https://github.com/frida-forks/vala
|
||||
|
||||
and compile them with Meson.''')
|
||||
endif
|
||||
@@ -349,9 +349,13 @@ glib_options = [
|
||||
'selinux=disabled',
|
||||
'xattr=false',
|
||||
'libmount=disabled',
|
||||
'sysprof=disabled',
|
||||
'tests=false',
|
||||
'nls=disabled',
|
||||
]
|
||||
if host_os_family in ['darwin', 'qnx', 'none'] or host_os == 'android' or libc == 'uclibc'
|
||||
glib_options += 'iconv=external'
|
||||
endif
|
||||
if optimize_for_prod
|
||||
glib_options += [
|
||||
'glib_debug=disabled',
|
||||
@@ -408,13 +412,27 @@ json_glib_dep = dependency('json-glib-1.0', version: '>=1.8', default_options: [
|
||||
'introspection=disabled',
|
||||
'gtk_doc=disabled',
|
||||
'tests=false',
|
||||
'nls=disabled',
|
||||
])
|
||||
efni_dep = dependency('efni-1.0', default_options: ['default_library=static'])
|
||||
efni_libs = []
|
||||
if efni_dep.type_name() == 'internal'
|
||||
efni_libs += subproject('efni').get_variable('efni')
|
||||
endif
|
||||
efni_vapidir = efni_dep.get_variable('vapidir', default_value: '')
|
||||
if efni_vapidir != ''
|
||||
vala_flags += '--vapidir=' + efni_vapidir
|
||||
endif
|
||||
foreach pkg : efni_dep.get_variable('vala_packages', default_value: '').split()
|
||||
vala_flags += '--pkg=' + pkg
|
||||
endforeach
|
||||
libsoup_dep = dependency('libsoup-3.0', default_options: [
|
||||
'default_library=static',
|
||||
'sqlite:default_library=static',
|
||||
'gssapi=disabled',
|
||||
'ntlm=disabled',
|
||||
'brotli=disabled',
|
||||
'zstd=disabled',
|
||||
'tls_check=false',
|
||||
'introspection=disabled',
|
||||
'vapi=disabled',
|
||||
@@ -428,7 +446,13 @@ if libsoup_dep.type_name() == 'internal' \
|
||||
vala_flags += '--define=HAVE_SOUP_WEBSOCKET_BACKPRESSURE'
|
||||
endif
|
||||
brotlidec_dep = dependency('libbrotlidec', default_options: ['default_library=static'])
|
||||
capstone_dep = dependency('capstone', default_options: ['default_library=static'])
|
||||
capstone_dep = dependency('capstone', default_options: [
|
||||
'default_library=static',
|
||||
'archs=all',
|
||||
'use_arch_registration=true',
|
||||
'x86_att_disable=true',
|
||||
'cli=disabled',
|
||||
])
|
||||
if capstone_dep.type_name() != 'internal' \
|
||||
and not cc.has_function('cs_arch_register_x86', dependencies: [capstone_dep])
|
||||
add_project_arguments(
|
||||
@@ -442,9 +466,9 @@ endif
|
||||
quickjs_dep = dependency('quickjs', required: get_option('barebone_backend'), default_options: [
|
||||
'default_library=static',
|
||||
'libc=false',
|
||||
'bignum=true',
|
||||
'atomics=disabled',
|
||||
'stack_check=disabled',
|
||||
'stack_check=' + ((host_os_family == 'none') ? 'enabled' : 'disabled'),
|
||||
'stack_mode=' + ((host_os_family == 'none') ? 'optimize' : 'default'),
|
||||
])
|
||||
lzfse_dep = dependency('lzfse', required: false, default_options: ['default_library=static'])
|
||||
lzfse_from_system = false
|
||||
@@ -480,6 +504,7 @@ endif
|
||||
|
||||
if host_os == 'android'
|
||||
minizip_dep = dependency('minizip', required: false, default_options: [
|
||||
'default_library=static',
|
||||
'zlib=enabled',
|
||||
'lzma=disabled',
|
||||
])
|
||||
@@ -573,7 +598,11 @@ if compiler_backend_opt.allowed()
|
||||
have_compiler_backend = true
|
||||
cdata.set('HAVE_COMPILER_BACKEND', 1)
|
||||
compiler_backend_mode = lines[1]
|
||||
go_config = lines[2]
|
||||
go_config = configure_file(
|
||||
input: 'tools' / 'cgo' / 'go-config.json.in',
|
||||
output: 'go-config.json',
|
||||
configuration: {'GO_CONFIG': lines[2]},
|
||||
)
|
||||
|
||||
if compiler_backend_mode == 'c-archive'
|
||||
vala_flags += '--define=COMPILER_BACKEND_LINKED'
|
||||
@@ -697,54 +726,32 @@ if host_os_family == 'windows' and cc.get_argument_syntax() != 'msvc'
|
||||
openssl_options += 'asm=disabled'
|
||||
endif
|
||||
openssl_dep = dependency('openssl',
|
||||
required: have_fruity_backend or get_option('connectivity').enabled(),
|
||||
required: get_option('connectivity').enabled(),
|
||||
modules: cc.get_argument_syntax() == 'msvc' ? ['OpenSSL::SSL'] : [],
|
||||
default_options: openssl_options,
|
||||
)
|
||||
|
||||
idun_libs = []
|
||||
have_idund_blob = false
|
||||
if have_fruity_backend
|
||||
backend_deps_private += dependency('libnghttp2', default_options: ['default_library=static'])
|
||||
backend_reqs_private += 'libnghttp2'
|
||||
|
||||
if host_os != 'macos'
|
||||
backend_deps_private += dependency('libngtcp2', default_options: ['default_library=static'])
|
||||
backend_reqs_private += 'libngtcp2'
|
||||
|
||||
ngtcp2_crypto_quictls_dep = dependency('libngtcp2_crypto_quictls', required: false, default_options: ['default_library=static'])
|
||||
if ngtcp2_crypto_quictls_dep.found()
|
||||
backend_deps_private += ngtcp2_crypto_quictls_dep
|
||||
backend_reqs_private += 'libngtcp2_crypto_quictls'
|
||||
else
|
||||
backend_deps_private += dependency('libngtcp2_crypto_ossl', default_options: ['default_library=static'])
|
||||
backend_reqs_private += 'libngtcp2_crypto_ossl'
|
||||
vala_flags += '--define=HAVE_NGTCP2_CRYPTO_OSSL'
|
||||
idun_dep = dependency('idun-1.0', default_options: ['default_library=static'])
|
||||
backend_reqs_private += 'idun-1.0'
|
||||
if idun_dep.type_name() == 'internal'
|
||||
idun_sp = subproject('idun')
|
||||
idun_libs += idun_sp.get_variable('idun')
|
||||
if assets_mode == 'embedded' and idun_sp.get_variable('build_daemon')
|
||||
idund_raw = idun_sp.get_variable('idund')
|
||||
have_idund_blob = true
|
||||
vala_flags += '--define=HAVE_IDUND_BLOB'
|
||||
endif
|
||||
|
||||
if have_frida_glib
|
||||
libusb_dep = dependency('libusb-1.0', default_options: ['default_library=static'])
|
||||
else
|
||||
subproject('libusb', default_options: ['default_library=static'])
|
||||
libusb_dep = dependency('libusb-1.0', default_options: ['default_library=static'])
|
||||
endif
|
||||
backend_deps_private += libusb_dep
|
||||
backend_reqs_private += 'libusb-1.0'
|
||||
|
||||
lwip_options = [
|
||||
'default_library=static',
|
||||
'ipv4=disabled',
|
||||
'ipv6=enabled',
|
||||
'dns=disabled',
|
||||
'arp=disabled',
|
||||
'ethernet=enabled',
|
||||
]
|
||||
if optimize_for_prod
|
||||
lwip_options += 'lwip_debug=disabled'
|
||||
else
|
||||
lwip_options += 'lwip_debug=enabled'
|
||||
endif
|
||||
backend_deps_private += dependency('lwip', default_options: lwip_options)
|
||||
backend_reqs_private += 'lwip'
|
||||
endif
|
||||
idun_vapidir = idun_dep.get_variable('vapidir', default_value: '')
|
||||
if idun_vapidir != ''
|
||||
vala_flags += '--vapidir=' + idun_vapidir
|
||||
endif
|
||||
foreach pkg : idun_dep.get_variable('vala_packages', default_value: '').split()
|
||||
vala_flags += '--pkg=' + pkg
|
||||
endforeach
|
||||
endif
|
||||
|
||||
if quickjs_dep.found()
|
||||
@@ -774,8 +781,12 @@ endif
|
||||
|
||||
if host_os == 'android'
|
||||
termux_elf_cleaner = find_program('termux-elf-cleaner', native: true)
|
||||
libselinux_dep = dependency('libselinux', version: '>=3.0', default_options: ['default_library=static'])
|
||||
libsepol_dep = dependency('libsepol', version: '>=3.0', default_options: ['default_library=static'])
|
||||
selinux_options = [
|
||||
'default_library=static',
|
||||
'regex=disabled',
|
||||
]
|
||||
libselinux_dep = dependency('libselinux', version: '>=3.0', default_options: selinux_options)
|
||||
libsepol_dep = dependency('libsepol', version: '>=3.0', default_options: selinux_options)
|
||||
backend_deps_private += [libselinux_dep, libsepol_dep]
|
||||
backend_reqs_private += ['libselinux', 'libsepol']
|
||||
else
|
||||
@@ -979,12 +990,13 @@ compile_js_agent = files('tools' / 'compile-js-agent.py')
|
||||
|
||||
mapper_opt = get_option('mapper')
|
||||
if mapper_opt.auto()
|
||||
have_mapper = host_os_family == 'darwin'
|
||||
have_mapper = host_os_family in ['windows', 'darwin', 'linux']
|
||||
else
|
||||
have_mapper = mapper_opt.enabled()
|
||||
endif
|
||||
if have_mapper
|
||||
cdata.set('HAVE_MAPPER', 1)
|
||||
vala_flags += '--define=HAVE_MAPPER'
|
||||
endif
|
||||
|
||||
configure_file(
|
||||
|
||||
+1
-1
Submodule releng updated: 1089fe24c2...164a6a4117.
@@ -359,5 +359,11 @@
|
||||
<true/>
|
||||
<key>com.apple.private.amfi.can-execute-cdhash</key>
|
||||
<true/>
|
||||
<key>com.apple.private.FairPlayIOKitUserClient.access</key>
|
||||
<true/>
|
||||
<key>com.apple.security.iokit-user-client-class</key>
|
||||
<array>
|
||||
<string>com_apple_driver_FairPlayIOKitUserClient</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida.Server {
|
||||
private static Application application;
|
||||
|
||||
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaAdb", gir_version = "1.0")]
|
||||
namespace Frida {
|
||||
public sealed class AdbDeviceTracker : Object {
|
||||
@@ -813,7 +815,7 @@ namespace Frida {
|
||||
else if (socket_type == "local" || socket_type == "localfilesystem") {
|
||||
connectable = new UnixSocketAddress (socket_address);
|
||||
} else if (socket_type == "localabstract" && UnixSocketAddress.abstract_names_supported ()) {
|
||||
connectable = new UnixSocketAddress.with_type (socket_address, socket_address.length,
|
||||
connectable = new UnixSocketAddress.with_type (socket_address.to_utf8 (),
|
||||
ABSTRACT_PADDED);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
public sealed class AgentContainer : Object, AgentSessionProvider {
|
||||
public DBusConnection connection {
|
||||
@@ -9,9 +11,7 @@ namespace Frida {
|
||||
[CCode (has_target = false)]
|
||||
private delegate void AgentMainFunc (string data, ref UnloadPolicy unload_policy, void * injector_state);
|
||||
private AgentMainFunc main_impl;
|
||||
#if LINUX
|
||||
private FileDescriptor agent_ctrlfd_for_peer;
|
||||
#else
|
||||
#if !LINUX
|
||||
private PipeTransport transport;
|
||||
#endif
|
||||
private string transport_address;
|
||||
@@ -38,8 +38,7 @@ namespace Frida {
|
||||
if (Posix.socketpair (Posix.AF_UNIX, Posix.SOCK_STREAM, 0, agent_ctrlfds) != 0)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to allocate socketpair");
|
||||
var agent_ctrlfd = new FileDescriptor (agent_ctrlfds[0]);
|
||||
container.agent_ctrlfd_for_peer = new FileDescriptor (agent_ctrlfds[1]);
|
||||
container.transport_address = "";
|
||||
container.transport_address = "socket:%d".printf (agent_ctrlfds[1]);
|
||||
|
||||
try {
|
||||
Socket socket = new Socket.from_fd (agent_ctrlfd.handle);
|
||||
@@ -114,18 +113,10 @@ namespace Frida {
|
||||
|
||||
private bool run () {
|
||||
UnloadPolicy unload_policy = IMMEDIATE;
|
||||
void * injector_state = null;
|
||||
|
||||
#if LINUX
|
||||
var s = LinuxInjectorState ();
|
||||
s.frida_ctrlfd = -1;
|
||||
s.agent_ctrlfd = agent_ctrlfd_for_peer.steal ();
|
||||
injector_state = &s;
|
||||
#endif
|
||||
|
||||
string agent_parameters = transport_address + "|exit-monitor:off|thread-suspend-monitor:off";
|
||||
|
||||
main_impl (agent_parameters, ref unload_policy, injector_state);
|
||||
main_impl (agent_parameters, ref unload_policy, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
public sealed class AndroidHelperClient : Object {
|
||||
public AndroidHelperTransport transport {
|
||||
|
||||
Binary file not shown.
@@ -18,7 +18,6 @@ import android.graphics.drawable.Drawable;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.util.Base64;
|
||||
import android.util.Base64OutputStream;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@@ -982,10 +981,10 @@ public class HelperBackend {
|
||||
icon.setBounds(0, 0, width, height);
|
||||
icon.draw(canvas);
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
bitmap.compress(CompressFormat.PNG, 100, new Base64OutputStream(output, Base64.NO_WRAP));
|
||||
ByteArrayOutputStream png = new ByteArrayOutputStream();
|
||||
bitmap.compress(CompressFormat.PNG, 100, png);
|
||||
|
||||
return output.toString();
|
||||
return Base64.encodeToString(png.toByteArray(), Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
private Map<String, List<RunningAppProcessInfo>> getAppProcesses() {
|
||||
|
||||
+1
-1
@@ -158,7 +158,7 @@ if is_shared
|
||||
else
|
||||
pkg_kwargs += {
|
||||
'libraries': core_public_lib,
|
||||
'requires_private': ['gmodule-2.0', 'gee-0.8', 'libsoup-3.0', 'frida-gum-1.0', 'frida-gumjs-inspector-1.0', 'libbrotlidec'] + backend_reqs_private,
|
||||
'requires_private': ['gmodule-2.0', 'gee-0.8', 'libsoup-3.0', 'efni-1.0', 'frida-gum-1.0', 'frida-gumjs-inspector-1.0', 'libbrotlidec'] + backend_reqs_private,
|
||||
'libraries_private': backend_libs_private,
|
||||
}
|
||||
endif
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class AgentConnection : Object, AsyncInitable {
|
||||
@@ -40,9 +42,9 @@ namespace Frida.Barebone {
|
||||
private Allocation config_allocation;
|
||||
private Allocation? agent_stack;
|
||||
|
||||
private Promise<bool>? listening;
|
||||
private Efni.Promise<bool>? listening;
|
||||
|
||||
private Gee.Map<uint16, Promise<Variant>> pending_requests = new Gee.HashMap<uint16, Promise<Variant>> ();
|
||||
private Gee.Map<uint16, Efni.Promise<Variant>> pending_requests = new Gee.HashMap<uint16, Efni.Promise<Variant>> ();
|
||||
private uint16 next_request_id = 1;
|
||||
|
||||
private const size_t AGENT_STACK_SIZE = 512 * 1024;
|
||||
@@ -71,11 +73,11 @@ namespace Frida.Barebone {
|
||||
this.kernel_notes = kernel_notes;
|
||||
}
|
||||
|
||||
public async void open (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void open (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
try {
|
||||
yield init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
Efni.throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +86,7 @@ namespace Frida.Barebone {
|
||||
* transport with it, such as the Linux kernel module exposing /dev/frida.
|
||||
*/
|
||||
public static async AgentConnection open_resident (IOStream stream, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var connection = new AgentConnection.resident ();
|
||||
|
||||
connection.adopt_hostlink_streams (stream);
|
||||
@@ -105,7 +107,7 @@ namespace Frida.Barebone {
|
||||
private const uint8 TRANSPORT_KIND_PIPE_VSOCK = 4;
|
||||
private const uint8 TRANSPORT_KIND_PIPE_GOLDFISH = 5;
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
progress ("Preparing the agent", 0.0);
|
||||
|
||||
var transport_tag = yield resolve_transport (cancellable);
|
||||
@@ -173,11 +175,11 @@ namespace Frida.Barebone {
|
||||
KernelFlavor flavor;
|
||||
if (kind == XNU) {
|
||||
if (kernel_base == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Missing kernel_base");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Missing kernel_base");
|
||||
flavor = new XnuKernelFlavor (machine, kernel_base, symbols);
|
||||
} else if (kind == LINUX) {
|
||||
if (kernel_base == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Missing kernel_base");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Missing kernel_base");
|
||||
flavor = new LinuxKernelFlavor (machine, kernel_base, symbols);
|
||||
} else if (kind == WIN9X) {
|
||||
flavor = new Win9xKernelFlavor (machine, symbols);
|
||||
@@ -193,7 +195,7 @@ namespace Frida.Barebone {
|
||||
try {
|
||||
elf = new Gum.ElfModule.from_blob (agent_config.image);
|
||||
} catch (Gum.Error e) {
|
||||
throw new Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
}
|
||||
|
||||
var raw_elf = debugger.make_buffer (new Bytes (elf.get_file_data ()));
|
||||
@@ -240,7 +242,7 @@ namespace Frida.Barebone {
|
||||
return true;
|
||||
});
|
||||
if (start_address == 0)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid agent: no _start symbol found");
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Invalid agent: no _start symbol found");
|
||||
|
||||
XnuLayout? xnu_layout = null;
|
||||
bool xnu_reads_its_own_processes = symbols.has_key ("proc_pid")
|
||||
@@ -351,14 +353,14 @@ namespace Frida.Barebone {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void wait_to_be_greeted (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void wait_to_be_greeted (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (listening == null)
|
||||
return;
|
||||
|
||||
var timeout = new TimeoutSource (GREETING_TIMEOUT_MS);
|
||||
timeout.set_callback (() => {
|
||||
if (!listening.future.ready)
|
||||
listening.reject (new Error.TIMED_OUT ("The agent never announced itself"));
|
||||
listening.reject (new Efni.Error.TIMED_OUT ("The agent never announced itself"));
|
||||
return false;
|
||||
});
|
||||
timeout.attach (MainContext.get_thread_default ());
|
||||
@@ -370,7 +372,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async Variant resolve_transport (Cancellable? cancellable) throws Error, IOError {
|
||||
private async Variant resolve_transport (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (agent_config.transport is BareboneHostlinkTransportConfig)
|
||||
return yield connect_virtio_transport ((BareboneHostlinkTransportConfig) agent_config.transport, cancellable);
|
||||
if (agent_config.transport is BareboneVsockTransportConfig) {
|
||||
@@ -407,11 +409,11 @@ namespace Frida.Barebone {
|
||||
new Variant.variant (new Variant.string (config.socket_path))
|
||||
});
|
||||
}
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported transport config");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unsupported transport config");
|
||||
}
|
||||
|
||||
private async void connect_serial_transport (BareboneSerialTransportConfig config,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
try {
|
||||
#if WINDOWS
|
||||
adopt_hostlink_streams (WindowsNamedPipe.open_client (config.path));
|
||||
@@ -421,20 +423,20 @@ namespace Frida.Barebone {
|
||||
cancellable));
|
||||
#endif
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("Unable to reach the serial port of the guest: %s", e.message);
|
||||
throw new Efni.Error.TRANSPORT ("Unable to reach the serial port of the guest: %s", e.message);
|
||||
}
|
||||
|
||||
listening = new Promise<bool> ();
|
||||
listening = new Efni.Promise<bool> ();
|
||||
}
|
||||
|
||||
private async void open_pipe_listener (string path, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void open_pipe_listener (string path, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
FileUtils.unlink (path);
|
||||
var service = new SocketService ();
|
||||
try {
|
||||
SocketAddress effective;
|
||||
service.add_address (new UnixSocketAddress (path), STREAM, DEFAULT, null, out effective);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("Unable to listen on %s: %s", path, e.message);
|
||||
throw new Efni.Error.TRANSPORT ("Unable to listen on %s: %s", path, e.message);
|
||||
}
|
||||
service.incoming.connect ((connection) => {
|
||||
if (pipe_stream == null) {
|
||||
@@ -451,7 +453,7 @@ namespace Frida.Barebone {
|
||||
pipe_service = service;
|
||||
}
|
||||
|
||||
private async void establish_hostlink (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void establish_hostlink (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (pipe_listener_wanted) {
|
||||
while (pipe_stream == null) {
|
||||
pipe_accept_handler = establish_hostlink.callback;
|
||||
@@ -482,7 +484,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async Variant connect_virtio_transport (BareboneHostlinkTransportConfig config, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var qmp = yield QmpClient.open (config.qmp, 0, cancellable);
|
||||
var link = yield qmp.open_hostlink (config.bus, cancellable);
|
||||
this.qmp = qmp;
|
||||
@@ -520,7 +522,7 @@ namespace Frida.Barebone {
|
||||
output = hostlink.get_output_stream ();
|
||||
}
|
||||
|
||||
private void rebase_layout (Layout layout, uint64 preferred_base, KernelRelocation reloc) throws Error {
|
||||
private void rebase_layout (Layout layout, uint64 preferred_base, KernelRelocation reloc) throws Efni.Error {
|
||||
foreach (var s in layout.symbols)
|
||||
s.offset = reloc.runtime_offset (preferred_base + s.offset);
|
||||
foreach (var m in layout.modules) {
|
||||
@@ -559,7 +561,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async void give_the_memory_back (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void give_the_memory_back (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var timeout = new TimeoutSource (LEAVE_INTERVAL_MS);
|
||||
timeout.set_callback (give_the_memory_back.callback);
|
||||
timeout.attach (MainContext.get_thread_default ());
|
||||
@@ -583,7 +585,7 @@ namespace Frida.Barebone {
|
||||
yield flavor.settle (cancellable);
|
||||
}
|
||||
|
||||
private async void wait_for_agent_to_leave (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void wait_for_agent_to_leave (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (left_flag == 0)
|
||||
return;
|
||||
|
||||
@@ -615,13 +617,13 @@ namespace Frida.Barebone {
|
||||
// same data. The process receives the code as it is, thus only the writable half needs a new
|
||||
// location, in memory that both sides can address.
|
||||
public async void place_winnt_user_agent (uint pid, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var response = yield execute_command (Command.PLACE_AGENT_IN_PROCESS,
|
||||
new Variant.uint32 (pid), cancellable);
|
||||
if (!response.is_of_type (VariantType.UINT32))
|
||||
throw new Error.PROTOCOL ("Invalid place_agent_in_process response format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid place_agent_in_process response format");
|
||||
if (response.get_uint32 () == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to place the agent in the process");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to place the agent in the process");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -629,7 +631,7 @@ namespace Frida.Barebone {
|
||||
* copy reports having woken up in.
|
||||
*/
|
||||
public async uint inject_agent_into_process (uint pid, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (kernel_kind == WINNT) {
|
||||
yield place_winnt_user_agent (pid, cancellable);
|
||||
return yield start_winnt_agent_in_process (pid, cancellable);
|
||||
@@ -641,12 +643,12 @@ namespace Frida.Barebone {
|
||||
// The copy cannot answer from the thread that starts it. Thus ask again until it reports its
|
||||
// process id.
|
||||
private async uint start_winnt_agent_in_process (uint pid, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
for (uint attempt = 0; attempt != INJECT_MAX_ATTEMPTS; attempt++) {
|
||||
var response = yield execute_command (Command.START_AGENT_IN_PROCESS,
|
||||
new Variant.uint32 (pid), cancellable);
|
||||
if (!response.is_of_type (VariantType.UINT32))
|
||||
throw new Error.PROTOCOL ("Invalid start_agent_in_process response format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid start_agent_in_process response format");
|
||||
|
||||
uint reached = response.get_uint32 ();
|
||||
if (reached != 0)
|
||||
@@ -659,19 +661,19 @@ namespace Frida.Barebone {
|
||||
timeout.destroy ();
|
||||
}
|
||||
|
||||
throw new Error.TIMED_OUT ("Timed out while starting the agent in the process");
|
||||
throw new Efni.Error.TIMED_OUT ("Timed out while starting the agent in the process");
|
||||
}
|
||||
|
||||
public signal void spawn_added (uint pid, string command_line, uint holder_pid);
|
||||
|
||||
public async Application[] enumerate_applications (string[] identifiers, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var selected = new VariantBuilder (new VariantType ("as"));
|
||||
foreach (unowned string identifier in identifiers)
|
||||
selected.add ("s", identifier);
|
||||
var response = yield execute_command (Command.ENUMERATE_APPLICATIONS, selected.end (), cancellable);
|
||||
if (!response.check_format_string ("a(sss)", false))
|
||||
throw new Error.PROTOCOL ("Invalid enumerate_applications response format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid enumerate_applications response format");
|
||||
|
||||
var applications = new Application[response.n_children ()];
|
||||
for (size_t i = 0; i != applications.length; i++) {
|
||||
@@ -686,11 +688,11 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Shortcut[] enumerate_shortcuts (uint helper_pid, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var response = yield execute_command (Command.ENUMERATE_SHORTCUTS,
|
||||
new Variant.boolean (false), cancellable, helper_pid);
|
||||
if (!response.check_format_string ("a(ssss)", false))
|
||||
throw new Error.PROTOCOL ("Invalid enumerate_shortcuts response format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid enumerate_shortcuts response format");
|
||||
|
||||
var shortcuts = new Shortcut[response.n_children ()];
|
||||
for (size_t i = 0; i != shortcuts.length; i++) {
|
||||
@@ -737,59 +739,59 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
public async void gate_spawns (bool on, Cancellable? cancellable) throws Error, IOError {
|
||||
public async void gate_spawns (bool on, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield execute_command (Command.GATE_SPAWNS, new Variant.boolean (on), cancellable);
|
||||
}
|
||||
|
||||
public async void arm_named_spawn (string name, Cancellable? cancellable) throws Error, IOError {
|
||||
public async void arm_named_spawn (string name, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield execute_command (Command.ARM_NAMED_SPAWN, new Variant.string (name), cancellable);
|
||||
}
|
||||
|
||||
public async void disarm_named_spawn (string name, Cancellable? cancellable) throws Error, IOError {
|
||||
public async void disarm_named_spawn (string name, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield execute_command (Command.DISARM_NAMED_SPAWN, new Variant.string (name), cancellable);
|
||||
}
|
||||
|
||||
public async uint spawn_process (uint helper_pid, string command_line, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
return yield ask_for_a_spawn (helper_pid, new Variant.string (command_line), cancellable);
|
||||
}
|
||||
|
||||
// A kernel that starts programs itself takes the words as they are, since a line to be
|
||||
// split again is what Windows asks for and nothing else does.
|
||||
public async uint spawn_program (string[] words, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
return yield ask_for_a_spawn (0, new Variant.strv (words), cancellable);
|
||||
}
|
||||
|
||||
private async uint ask_for_a_spawn (uint helper_pid, Variant payload, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var response = yield execute_command (Command.SPAWN_PROCESS, payload, cancellable,
|
||||
helper_pid);
|
||||
if (!response.is_of_type (VariantType.UINT32))
|
||||
throw new Error.PROTOCOL ("Invalid spawn_process response format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid spawn_process response format");
|
||||
|
||||
return response.get_uint32 ();
|
||||
}
|
||||
|
||||
public async void resume_process (uint helper_pid, uint pid, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield execute_command (Command.RESUME_PROCESS, new Variant.uint32 (pid), cancellable,
|
||||
helper_pid);
|
||||
}
|
||||
|
||||
public async void detach_from_process (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
public async void detach_from_process (uint pid, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield execute_command (Command.DETACH_FROM_PROCESS, new Variant.uint32 (pid), cancellable);
|
||||
}
|
||||
|
||||
public async uint inject_into_process (uint pid, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
// The agent cannot answer from the context that does the work. Thus it reports the result at
|
||||
// the next request.
|
||||
for (uint attempt = 0; attempt != INJECT_MAX_ATTEMPTS; attempt++) {
|
||||
var response = yield execute_command (Command.INJECT_INTO_PROCESS,
|
||||
new Variant.uint32 (pid), cancellable);
|
||||
if (!response.is_of_type (VariantType.UINT32))
|
||||
throw new Error.PROTOCOL ("Invalid inject_into_process response format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid inject_into_process response format");
|
||||
|
||||
uint reached = response.get_uint32 ();
|
||||
if (reached != 0)
|
||||
@@ -802,11 +804,11 @@ namespace Frida.Barebone {
|
||||
timeout.destroy ();
|
||||
}
|
||||
|
||||
throw new Error.TIMED_OUT ("Timed out while injecting into process");
|
||||
throw new Efni.Error.TIMED_OUT ("Timed out while injecting into process");
|
||||
}
|
||||
|
||||
public async HostProcessInfo[] enumerate_processes (Scope scope, uint[] pids, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
bool include_icons = scope == FULL;
|
||||
var selected = new VariantBuilder (new VariantType ("au"));
|
||||
foreach (uint pid in pids)
|
||||
@@ -814,7 +816,7 @@ namespace Frida.Barebone {
|
||||
var request = new Variant.tuple ({ new Variant.boolean (include_icons), selected.end () });
|
||||
var response = yield execute_command (Command.ENUMERATE_PROCESSES, request, cancellable);
|
||||
if (!response.check_format_string ("a(usssusuxaay)", false))
|
||||
throw new Error.PROTOCOL ("Invalid enumerate_processes response format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid enumerate_processes response format");
|
||||
|
||||
var processes = new HostProcessInfo[response.n_children ()];
|
||||
for (size_t i = 0; i != processes.length; i++) {
|
||||
@@ -1016,30 +1018,30 @@ namespace Frida.Barebone {
|
||||
private const uint32 MAX_ICON_DIMENSION = 256;
|
||||
|
||||
public async AgentScriptId create_script (string source, uint destination, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var payload = new Variant ("s", source);
|
||||
var response = yield execute_command (Command.CREATE_SCRIPT, payload, cancellable, destination);
|
||||
if (!response.check_format_string ("u", false))
|
||||
throw new Error.PROTOCOL ("Invalid create_script response format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid create_script response format");
|
||||
uint32 script_handle;
|
||||
response.get ("u", out script_handle);
|
||||
return AgentScriptId (script_handle);
|
||||
}
|
||||
|
||||
public async void load_script (AgentScriptId script_id, uint destination, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var payload = new Variant ("u", script_id.handle);
|
||||
yield execute_command (Command.LOAD_SCRIPT, payload, cancellable, destination);
|
||||
}
|
||||
|
||||
public async void destroy_script (AgentScriptId script_id, uint destination, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var payload = new Variant ("u", script_id.handle);
|
||||
yield execute_command (Command.DESTROY_SCRIPT, payload, cancellable, destination);
|
||||
}
|
||||
|
||||
public async void post_script_message (AgentScriptId script_id, string message, Bytes? data, uint destination,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var payload = new Variant ("(us@ay)", script_id.handle, message,
|
||||
new Variant.from_bytes (new VariantType ("ay"), data ?? new Bytes (new uint8[0]), true));
|
||||
yield execute_command (Command.POST_SCRIPT_MESSAGE, payload, cancellable, destination);
|
||||
@@ -1047,26 +1049,26 @@ namespace Frida.Barebone {
|
||||
|
||||
// Zero selects the agent in the kernel. A different value selects a process that has a copy.
|
||||
private async Variant execute_command (Command command, Variant payload, Cancellable? cancellable,
|
||||
uint destination = 0) throws Error, IOError {
|
||||
uint destination = 0) throws Efni.Error, IOError {
|
||||
uint16 request_id = next_request_id++;
|
||||
|
||||
Bytes frame = frame_message (command, request_id, destination, payload);
|
||||
|
||||
var promise = new Promise<Variant> ();
|
||||
var promise = new Efni.Promise<Variant> ();
|
||||
pending_requests[request_id] = promise;
|
||||
|
||||
try {
|
||||
yield write_frame (frame, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
pending_requests.unset (request_id);
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
throw new Efni.Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
var timeout_source = new TimeoutSource (COMMAND_TIMEOUT_MS);
|
||||
timeout_source.set_callback (() => {
|
||||
Promise<Variant>? p;
|
||||
Efni.Promise<Variant>? p;
|
||||
if (pending_requests.unset (request_id, out p))
|
||||
p.reject (new Error.TIMED_OUT ("The %s command timed out", command.to_nick ()));
|
||||
p.reject (new Efni.Error.TIMED_OUT ("The %s command timed out", command.to_nick ()));
|
||||
return Source.REMOVE;
|
||||
});
|
||||
timeout_source.attach (MainContext.get_thread_default ());
|
||||
@@ -1074,29 +1076,29 @@ namespace Frida.Barebone {
|
||||
Variant response = null;
|
||||
try {
|
||||
response = yield promise.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
if (!(e is Error.TIMED_OUT))
|
||||
} catch (Efni.Error e) {
|
||||
if (!(e is Efni.Error.TIMED_OUT))
|
||||
throw e;
|
||||
string? fault = yield describe_agent_fault (cancellable);
|
||||
if (fault == null)
|
||||
throw e;
|
||||
throw new Error.TIMED_OUT ("%s; %s", e.message, fault);
|
||||
throw new Efni.Error.TIMED_OUT ("%s; %s", e.message, fault);
|
||||
} finally {
|
||||
timeout_source.destroy ();
|
||||
}
|
||||
|
||||
if (!response.check_format_string ("(bv)", false))
|
||||
throw new Error.PROTOCOL ("Malformed reply from agent");
|
||||
throw new Efni.Error.PROTOCOL ("Malformed reply from agent");
|
||||
bool succeeded;
|
||||
Variant detail;
|
||||
response.get ("(bv)", out succeeded, out detail);
|
||||
if (!succeeded)
|
||||
throw new Error.NOT_SUPPORTED ("%s", detail.get_string ());
|
||||
throw new Efni.Error.NOT_SUPPORTED ("%s", detail.get_string ());
|
||||
|
||||
return detail;
|
||||
}
|
||||
|
||||
private async string? describe_agent_fault (Cancellable? cancellable) throws Error, IOError {
|
||||
private async string? describe_agent_fault (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (fault_record == 0)
|
||||
return null;
|
||||
|
||||
@@ -1143,7 +1145,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void process_incoming_messages () {
|
||||
reading = new Promise<bool> ();
|
||||
reading = new Efni.Promise<bool> ();
|
||||
try {
|
||||
while (true) {
|
||||
size_t header_size = 4;
|
||||
@@ -1171,7 +1173,7 @@ namespace Frida.Barebone {
|
||||
if (byte_order != ByteOrder.HOST)
|
||||
message = message.byteswap ();
|
||||
if (!message.check_format_string ("(yquv)", false))
|
||||
throw new Error.PROTOCOL ("Invalid message format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid message format");
|
||||
|
||||
uint8 command_code;
|
||||
uint16 request_id;
|
||||
@@ -1184,7 +1186,7 @@ namespace Frida.Barebone {
|
||||
listening.resolve (true);
|
||||
} else if (command_code == Command.SPAWN_ADDED) {
|
||||
if (!payload.check_format_string ("(us)", false))
|
||||
throw new Error.PROTOCOL ("Invalid spawn added payload format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid spawn added payload format");
|
||||
|
||||
uint32 pid;
|
||||
unowned string command_line;
|
||||
@@ -1193,7 +1195,7 @@ namespace Frida.Barebone {
|
||||
spawn_added (pid, command_line, destination);
|
||||
} else if (command_code == Command.SCRIPT_MESSAGE) {
|
||||
if (!payload.check_format_string ("(usay)", false))
|
||||
throw new Error.PROTOCOL ("Invalid script message payload format");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid script message payload format");
|
||||
|
||||
uint32 script_handle;
|
||||
unowned string json;
|
||||
@@ -1218,7 +1220,7 @@ namespace Frida.Barebone {
|
||||
result = yield protect_memory (payload, io_cancellable);
|
||||
else
|
||||
result = yield patch_code (payload, io_cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
result = (command_code == Command.REMAP_WRITABLE_PAGES)
|
||||
? new Variant.uint64 (0)
|
||||
: new Variant.boolean (false);
|
||||
@@ -1228,7 +1230,7 @@ namespace Frida.Barebone {
|
||||
|
||||
yield send_reply (request_id, result);
|
||||
} else if (command_code == Command.REPLY) {
|
||||
Promise<Variant>? promise;
|
||||
Efni.Promise<Variant>? promise;
|
||||
if (pending_requests.unset (request_id, out promise))
|
||||
promise.resolve (payload);
|
||||
}
|
||||
@@ -1239,9 +1241,9 @@ namespace Frida.Barebone {
|
||||
reading.resolve (true);
|
||||
}
|
||||
|
||||
private Promise<bool>? reading;
|
||||
private Efni.Promise<bool>? reading;
|
||||
|
||||
private async void fill_until_n_bytes_available (size_t minimum) throws Error, IOError {
|
||||
private async void fill_until_n_bytes_available (size_t minimum) throws Efni.Error, IOError {
|
||||
size_t available = input.get_available ();
|
||||
while (available < minimum) {
|
||||
if (input.get_buffer_size () < minimum)
|
||||
@@ -1252,17 +1254,17 @@ namespace Frida.Barebone {
|
||||
n = yield input.fill_async ((ssize_t) (input.get_buffer_size () - available), Priority.DEFAULT,
|
||||
io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("Connection closed");
|
||||
throw new Efni.Error.TRANSPORT ("Connection closed");
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
throw new Error.TRANSPORT ("Connection closed");
|
||||
throw new Efni.Error.TRANSPORT ("Connection closed");
|
||||
|
||||
available += n;
|
||||
}
|
||||
}
|
||||
|
||||
private async Variant remap_writable_pages (Variant payload, Cancellable? cancellable) throws Error, IOError {
|
||||
private async Variant remap_writable_pages (Variant payload, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var physical_addresses = new Gee.ArrayList<uint64?> ();
|
||||
for (size_t i = 0; i != payload.n_children (); i++) {
|
||||
uint64 va = payload.get_child_value (i).get_uint64 ();
|
||||
@@ -1274,7 +1276,7 @@ namespace Frida.Barebone {
|
||||
return new Variant.uint64 (allocation.virtual_address);
|
||||
}
|
||||
|
||||
private async Variant protect_memory (Variant payload, Cancellable? cancellable) throws Error, IOError {
|
||||
private async Variant protect_memory (Variant payload, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 address;
|
||||
uint64 size;
|
||||
uint32 prot;
|
||||
@@ -1289,7 +1291,7 @@ namespace Frida.Barebone {
|
||||
// CTRR/KTRR locks kernel text read-only against the guest CPU, so the agent cannot patch it
|
||||
// even through a writable alias. The physical-memory bridge writes the backing store directly,
|
||||
// which the lock does not cover, letting us land hooks in kernel and kext text.
|
||||
private async Variant patch_code (Variant payload, Cancellable? cancellable) throws Error, IOError {
|
||||
private async Variant patch_code (Variant payload, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 va;
|
||||
Variant bytes_value;
|
||||
payload.get ("(t@ay)", out va, out bytes_value);
|
||||
@@ -1319,7 +1321,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void write_frame (Bytes frame, Cancellable? cancellable) throws GLib.Error {
|
||||
var ours = new Promise<bool> ();
|
||||
var ours = new Efni.Promise<bool> ();
|
||||
var theirs = write_turn;
|
||||
write_turn = ours;
|
||||
|
||||
@@ -1340,7 +1342,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private Promise<bool>? write_turn;
|
||||
private Efni.Promise<bool>? write_turn;
|
||||
|
||||
private Bytes frame_message (Command command, uint16 request_id, uint destination, Variant payload) {
|
||||
var message = new Variant ("(yquv)", (uint8) command, request_id, destination, payload);
|
||||
@@ -1380,7 +1382,7 @@ namespace Frida.Barebone {
|
||||
READY = 131;
|
||||
|
||||
public string to_nick () {
|
||||
return Marshal.enum_to_nick<Command> (this);
|
||||
return Efni.Marshal.enum_to_nick<Command> (this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +1,36 @@
|
||||
## Getting the SDK and the devkit
|
||||
|
||||
The agent is built against the bare soft-float SDK, which carries picolibc and the
|
||||
compiler runtime, and against a GumJS devkit built for the same flavor. Which flavor
|
||||
is the target's, listed under Targets below; this walks through the XNU one. No
|
||||
prebuilt bundles are published for these, so build both:
|
||||
compiler runtime, and against a GumJS devkit built for the same machine. Which machine
|
||||
is the target's, listed under Targets below; this walks through the XNU one. The SDK
|
||||
is published prebuilt, and the devkit is built from frida-gum:
|
||||
|
||||
machine=none-arm64-softfloat_nopic
|
||||
machine=none-arm64-softfloat_pic
|
||||
|
||||
releng/deps.py build --bundle=sdk --host=$machine
|
||||
mkdir -p ~/sdk-$machine
|
||||
tar -C ~/sdk-$machine -xf deps/sdk-$machine.tar.xz
|
||||
sdk=~/sdk-$machine
|
||||
releng/deps.py sync sdk $machine $sdk
|
||||
|
||||
gum=~/src/frida-gum
|
||||
mkdir -p $gum/build/$machine
|
||||
(cd $gum/build/$machine \
|
||||
&& FRIDA_DEPS=$HOME/src/frida-core/deps CC=clang CXX=clang++ AR=llvm-ar \
|
||||
RANLIB=llvm-ranlib NM=llvm-nm STRIP=llvm-strip \
|
||||
../../configure --host=$machine --enable-gumjs \
|
||||
--with-devkits=gumjs \
|
||||
&& python3 ../../releng/meson/meson.py configure -Ddevkit_symbol_scope=original \
|
||||
&& ninja)
|
||||
&& CC=clang CXX=clang++ ../../configure --host=$machine --enable-gumjs \
|
||||
--with-devkits=gumjs --with-devkit-symbol-scope=original \
|
||||
&& make)
|
||||
|
||||
## Building
|
||||
|
||||
sdk=~/sdk-$machine
|
||||
export GUMJS_DEVKIT_DIR=$gum/build/$machine/bindings/gumjs/devkit
|
||||
export CC="clang -I$sdk/include -I$sdk/include/glib-2.0 -I$sdk/lib/glib-2.0/include \
|
||||
-I$sdk/include/capstone -I$sdk/include/json-glib-1.0"
|
||||
export RUSTFLAGS="-L native=$sdk/lib"
|
||||
|
||||
RUSTC_BOOTSTRAP=1 cargo build --release --features xnu \
|
||||
--target aarch64-unknown-none-softfloat --target-dir target-xnu \
|
||||
-Z build-std=core,alloc,compiler_builtins
|
||||
--target ./aarch64-unknown-none-pic.json -Z json-target-spec \
|
||||
--target-dir target-xnu -Z build-std=core,alloc,compiler_builtins
|
||||
|
||||
Rust names no bare-metal target for the Windows guests, so the agent spells them
|
||||
out and they are asked for by path:
|
||||
|
||||
RUSTC_BOOTSTRAP=1 cargo build --release --features win9x \
|
||||
--target ./i686-unknown-none-pic.json -Z json-target-spec \
|
||||
--target-dir target-win9x -Z build-std=core,alloc,compiler_builtins
|
||||
Every flavor names its target by path, as Rust has no built-in one for a
|
||||
position-independent bare-metal image. The others differ only in feature, machine
|
||||
and target spec, listed under Targets below.
|
||||
|
||||
## Targets
|
||||
|
||||
@@ -58,7 +50,7 @@ and NT's 64-bit half is called in the Microsoft ABI.
|
||||
|
||||
| Flavor | Machine | Rust target |
|
||||
|---------|-----------------------------------|----------------------------------|
|
||||
| `xnu` | `none-arm64-softfloat_nopic` | `aarch64-unknown-none-softfloat` |
|
||||
| `xnu` | `none-arm64-softfloat_pic` | `aarch64-unknown-none-pic.json` |
|
||||
| `win9x` | `none-x86-softfloat_pic` | `i686-unknown-none-pic.json` |
|
||||
| `winnt` | `none-x86-softfloat_pic` | `i686-unknown-none-pic.json` |
|
||||
| `winnt` | `none-x86_64-softfloat_pic_msabi` | `x86_64-unknown-none-pic.json` |
|
||||
@@ -72,7 +64,9 @@ half of Gum's platform backend (`gum_xnu.rs` / `gum_windows.rs` / `gum_linux.rs`
|
||||
## Development loop
|
||||
|
||||
export FRIDA_BAREBONE_CONFIG=$PWD/etc/xnu.json
|
||||
cargo build --release --features xnu --target aarch64-unknown-none-softfloat \
|
||||
RUSTC_BOOTSTRAP=1 cargo build --release --features xnu \
|
||||
--target ./aarch64-unknown-none-pic.json -Z json-target-spec \
|
||||
--target-dir target-xnu -Z build-std=core,alloc,compiler_builtins \
|
||||
&& make -C ~/src/frida-python \
|
||||
&& killall -9 qemu-system-aarch64 && sleep 2 \
|
||||
&& frida -D barebone -p 0
|
||||
|
||||
@@ -11,21 +11,22 @@ how it is told about the kernel around it, and how the host reaches it.
|
||||
## Building
|
||||
|
||||
Three things, in order. First an SDK for this kernel -- picolibc, GLib, QuickJS, capstone and the
|
||||
rest, built for `macos-arm64-kernel`:
|
||||
rest, built for `macos-arm64e-kernel` and published prebuilt:
|
||||
|
||||
cd frida-core
|
||||
python3 -m releng.deps build --host macos-arm64-kernel
|
||||
releng/deps.py sync sdk macos-arm64e-kernel ~/sdk-macos-arm64e-kernel
|
||||
|
||||
That fetches the same sources every other flavour is built from, six of them pinned to a commit
|
||||
that says what a Darwin kernel is not; see below. Then a GumJS devkit against it:
|
||||
It is built from the same sources every other flavour is, six of them pinned to a commit that
|
||||
says what a Darwin kernel is not; see below. Then a GumJS devkit against it:
|
||||
|
||||
cd frida-gum
|
||||
./configure --host=macos-arm64-kernel --enable-gumjs --disable-v8 --with-devkits=gumjs
|
||||
./configure --host=macos-arm64e-kernel --enable-gumjs --with-devkits=gumjs \
|
||||
--with-devkit-symbol-scope=original
|
||||
make
|
||||
|
||||
Then the kext itself:
|
||||
|
||||
make FRIDA_SDK=.../deps/sdk-macos-arm64-kernel \
|
||||
make FRIDA_SDK=~/sdk-macos-arm64e-kernel \
|
||||
GUMJS_DEVKIT_DIR=.../frida-gum/build/bindings/gumjs/devkit
|
||||
|
||||
which builds the agent as a static library for the kernel's ABI, `frida-kext.c` against the Kernel
|
||||
|
||||
@@ -29,7 +29,9 @@ use crate::bindings::{
|
||||
#[cfg(any(feature = "linux", feature = "linux-injected"))]
|
||||
use crate::gum_modules::enumerate_symbols_in_range;
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
use crate::gum_windows::enumerate_exports_in_range;
|
||||
use crate::gum_windows::{enumerate_exports_in_range, enumerate_imports_in_module};
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
use crate::bindings::{GumFoundImportFunc, GumImportDetails, GumImportType_GUM_IMPORT_FUNCTION};
|
||||
#[cfg(any(feature = "xnu-core", feature = "linux-injected"))]
|
||||
use crate::gum_injected::enumerate_exports_in_range;
|
||||
|
||||
@@ -184,6 +186,11 @@ extern "C" fn gum_native_module_iface_init(g_iface: gpointer, _iface_data: gpoin
|
||||
(*iface).get_path = Some(crate::signed_to_be_called_back(gum_native_module_get_path, 0));
|
||||
(*iface).get_range = Some(crate::signed_to_be_called_back(gum_native_module_get_range, 0));
|
||||
(*iface).enumerate_exports = Some(crate::signed_to_be_called_back(gum_native_module_enumerate_exports, 0));
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
{
|
||||
(*iface).enumerate_imports =
|
||||
Some(crate::signed_to_be_called_back(gum_native_module_enumerate_imports, 0));
|
||||
}
|
||||
(*iface).find_export_by_name = Some(crate::signed_to_be_called_back(gum_native_module_find_export_by_name, 0));
|
||||
#[cfg(any(feature = "linux", feature = "linux-injected"))]
|
||||
{
|
||||
@@ -317,6 +324,32 @@ unsafe extern "C" fn gum_native_module_enumerate_exports(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
unsafe extern "C" fn gum_native_module_enumerate_imports(
|
||||
self_: *mut GumModule,
|
||||
func: GumFoundImportFunc,
|
||||
user_data: gpointer,
|
||||
) {
|
||||
unsafe {
|
||||
let module = self_ as *mut GumNativeModule;
|
||||
let base = (*module).range.base_address;
|
||||
|
||||
let mut on_import = |library: *const gchar, name: *const gchar, slot: u64, address: u64| {
|
||||
let details = GumImportDetails {
|
||||
type_: GumImportType_GUM_IMPORT_FUNCTION,
|
||||
name,
|
||||
module: library,
|
||||
address,
|
||||
slot,
|
||||
};
|
||||
|
||||
func.unwrap()(&details as *const GumImportDetails, user_data) != 0
|
||||
};
|
||||
|
||||
enumerate_imports_in_module(base, &mut on_import);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "linux", feature = "linux-injected"))]
|
||||
unsafe extern "C" fn gum_native_module_enumerate_symbols(
|
||||
self_: *mut GumModule,
|
||||
@@ -368,6 +401,9 @@ const SYMBOL_IS_FUNCTION: u8 = 2;
|
||||
/// it returns false.
|
||||
pub(crate) type FoundExportCallback<'a> = dyn FnMut(*const gchar, u64) -> bool + 'a;
|
||||
|
||||
#[cfg(any(feature = "win9x", feature = "winnt"))]
|
||||
pub(crate) type FoundImportCallback<'a> = dyn FnMut(*const gchar, *const gchar, u64, u64) -> bool + 'a;
|
||||
|
||||
// Code slabs the agent allocates are normal writable RAM, so Gum can keep a real writable alias to
|
||||
// them across the slab's lifetime. Pre-existing kernel text is write-protected — CTRR-locked on
|
||||
// Apple silicon, STRICT_KERNEL_RWX on Linux — and can only be modified through a separate path, so
|
||||
@@ -469,6 +505,11 @@ mod symbolication {
|
||||
return crate::xnu_mapped::export_named(name);
|
||||
}
|
||||
|
||||
#[cfg(feature = "linux-injected")]
|
||||
if crate::kernel::in_copy() {
|
||||
return crate::kernel::export_named(name);
|
||||
}
|
||||
|
||||
let table = unsafe { &*ptr::addr_of!(crate::SYMBOL_TABLE) };
|
||||
|
||||
match table.find_symbol_by_name(name) {
|
||||
|
||||
@@ -23,7 +23,10 @@ use crate::{
|
||||
#[cfg(feature = "xnu-core")]
|
||||
use crate::bindings::{g_object_unref, gum_barebone_register_module};
|
||||
#[cfg(feature = "linux-injected")]
|
||||
use crate::bindings::gum_barebone_unregister_thread;
|
||||
use crate::bindings::{
|
||||
GError, GumBareboneStreamOps, g_io_error_from_errno, g_io_error_quark, g_set_error_literal,
|
||||
g_strerror, gint, gssize, gum_barebone_unregister_thread,
|
||||
};
|
||||
#[cfg(not(feature = "xnu-kext"))]
|
||||
use crate::bindings::{
|
||||
g_variant_get_boolean, g_variant_new,
|
||||
@@ -176,6 +179,51 @@ fn remap_agent_pages_through_host(first_page: gpointer, n_pages: guint) -> gpoin
|
||||
#[cfg(all(feature = "linux-injected", not(target_arch = "arm")))]
|
||||
const GUM_PAGE_RWX: u32 = 7;
|
||||
|
||||
#[cfg(feature = "linux-injected")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gum_barebone_query_stream_ops() -> *const GumBareboneStreamOps {
|
||||
if kernel::in_copy() { &STREAM_OPS } else { ptr::null() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "linux-injected")]
|
||||
static STREAM_OPS: GumBareboneStreamOps = GumBareboneStreamOps {
|
||||
poll: Some(crate::linux::user::frida_poll),
|
||||
read: Some(read_descriptor),
|
||||
write: Some(write_descriptor),
|
||||
close: Some(close_descriptor),
|
||||
};
|
||||
|
||||
#[cfg(feature = "linux-injected")]
|
||||
unsafe extern "C" fn read_descriptor(handle: gpointer, buffer: gpointer, count: gsize,
|
||||
error: *mut *mut GError) -> gssize {
|
||||
transferred(crate::linux::user::read(handle as i32, buffer as *mut u8, count as usize), error)
|
||||
}
|
||||
|
||||
#[cfg(feature = "linux-injected")]
|
||||
unsafe extern "C" fn write_descriptor(handle: gpointer, buffer: gconstpointer, count: gsize,
|
||||
error: *mut *mut GError) -> gssize {
|
||||
transferred(crate::linux::user::write(handle as i32, buffer as *const u8, count as usize),
|
||||
error)
|
||||
}
|
||||
|
||||
#[cfg(feature = "linux-injected")]
|
||||
unsafe extern "C" fn close_descriptor(handle: gpointer, error: *mut *mut GError) -> gboolean {
|
||||
(transferred(crate::linux::user::close(handle as i32), error) >= 0) as gboolean
|
||||
}
|
||||
|
||||
#[cfg(feature = "linux-injected")]
|
||||
fn transferred(result: isize, error: *mut *mut GError) -> gssize {
|
||||
if result < 0 {
|
||||
let errno = -result as gint;
|
||||
unsafe {
|
||||
g_set_error_literal(error, g_io_error_quark(), g_io_error_from_errno(errno) as gint,
|
||||
g_strerror(errno))
|
||||
};
|
||||
return -1;
|
||||
}
|
||||
result as gssize
|
||||
}
|
||||
|
||||
unsafe fn what_changed(first_page: u64, shadow: *const u8, total: usize) -> Option<(usize, usize)> {
|
||||
let live = unsafe { core::slice::from_raw_parts(first_page as *const u8, total) };
|
||||
let ours = unsafe { core::slice::from_raw_parts(shadow, total) };
|
||||
@@ -937,3 +985,47 @@ pub(crate) unsafe fn enumerate_exports_in_range(
|
||||
pub extern "C" fn gum_load_symbols(_path: *const gchar) -> gboolean {
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(feature = "linux-injected")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gum_thread_get_system_error() -> gint {
|
||||
match errno_slot() {
|
||||
Some(slot) => unsafe { *slot },
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "linux-injected")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gum_thread_set_system_error(value: gint) {
|
||||
if let Some(slot) = errno_slot() {
|
||||
unsafe { *slot = value };
|
||||
}
|
||||
}
|
||||
|
||||
// errno is thread-local behind a function that hands back its address:
|
||||
// __errno_location on glibc and musl, __errno on Bionic.
|
||||
#[cfg(feature = "linux-injected")]
|
||||
fn errno_slot() -> Option<*mut gint> {
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
if !crate::kernel::in_copy() {
|
||||
return None;
|
||||
}
|
||||
|
||||
static LOCATE: AtomicUsize = AtomicUsize::new(0);
|
||||
let mut locate = LOCATE.load(Ordering::Relaxed);
|
||||
if locate == 0 {
|
||||
locate = crate::kernel::export_named("__errno_location") as usize;
|
||||
if locate == 0 {
|
||||
locate = crate::kernel::export_named("__errno") as usize;
|
||||
}
|
||||
if locate == 0 {
|
||||
return None;
|
||||
}
|
||||
LOCATE.store(locate, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
let locate: unsafe extern "C" fn() -> *mut gint = unsafe { core::mem::transmute(locate) };
|
||||
Some(unsafe { locate() })
|
||||
}
|
||||
@@ -3,26 +3,240 @@
|
||||
|
||||
use crate::{
|
||||
bindings::{
|
||||
_GumPageProtection_GUM_PAGE_EXECUTE, _GumRwxSupport_GUM_RWX_FULL, GumMemoryRange,
|
||||
GumModuleRegistry, GumPageProtection, GumRwxSupport, g_object_unref, gboolean, gpointer,
|
||||
gsize, guint, gum_barebone_register_module, gum_mprotect, GumFoundRangeFunc,
|
||||
GumFoundThreadFunc, GumModifyThreadFlags, GumModifyThreadFunc, GumRangeDetails,
|
||||
GumThreadDetails, GumThreadFlags, GumThreadId, GumThreadRegistry,
|
||||
_GumPageProtection_GUM_PAGE_EXECUTE, _GumRwxSupport_GUM_RWX_FULL, GError,
|
||||
GIOErrorEnum_G_IO_ERROR_BROKEN_PIPE, GIOErrorEnum_G_IO_ERROR_FAILED,
|
||||
GIOErrorEnum_G_IO_ERROR_INVALID_ARGUMENT, GIOErrorEnum_G_IO_ERROR_NOT_FOUND,
|
||||
GIOErrorEnum_G_IO_ERROR_PERMISSION_DENIED, GumBareboneStreamOps, GumMemoryRange,
|
||||
GumModuleRegistry, GumPageProtection, GumRwxSupport, g_free, g_io_error_quark, g_malloc,
|
||||
g_object_unref, g_set_error_literal, g_utf8_to_utf16, g_utf16_to_utf8, gboolean, gchar,
|
||||
gint, gpointer, gsize, gssize, guint, gum_barebone_register_module, gum_mprotect,
|
||||
GumFoundRangeFunc, GumFoundThreadFunc, GumModifyThreadFlags, GumModifyThreadFunc,
|
||||
GumRangeDetails, GumThreadDetails, GumThreadFlags, GumThreadId, GumThreadRegistry,
|
||||
gconstpointer, gum_thread_details_copy,
|
||||
},
|
||||
gum::{self, FoundExportCallback},
|
||||
gum::{self, FoundExportCallback, FoundImportCallback},
|
||||
kernel,
|
||||
};
|
||||
use crate::bindings::{GumCpuContext, GumThreadFlags_GUM_THREAD_FLAGS_CPU_CONTEXT};
|
||||
use alloc::ffi::CString;
|
||||
#[cfg(not(feature = "winnt"))]
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::ptr;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
macro_rules! windows_fn {
|
||||
($($argument:ty),* $(,)?) => { unsafe extern "stdcall" fn($($argument),*) };
|
||||
($($argument:ty),* $(,)? => $result:ty) => {
|
||||
unsafe extern "stdcall" fn($($argument),*) -> $result
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
macro_rules! windows_fn {
|
||||
($($argument:ty),* $(,)?) => { unsafe extern "win64" fn($($argument),*) };
|
||||
($($argument:ty),* $(,)? => $result:ty) => {
|
||||
unsafe extern "win64" fn($($argument),*) -> $result
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
macro_rules! windows_fn {
|
||||
($($argument:ty),* $(,)?) => { unsafe extern "C" fn($($argument),*) };
|
||||
($($argument:ty),* $(,)? => $result:ty) => {
|
||||
unsafe extern "C" fn($($argument),*) -> $result
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use windows_fn;
|
||||
|
||||
pub struct FileApi {
|
||||
pub read_file: windows_fn!(usize, *mut u8, u32, *mut u32, usize => i32),
|
||||
pub write_file: windows_fn!(usize, *const u8, u32, *mut u32, usize => i32),
|
||||
pub close_handle: windows_fn!(usize => i32),
|
||||
pub format_message: windows_fn!(u32, usize, u32, u32, *mut u8, u32, usize => u32),
|
||||
}
|
||||
|
||||
pub struct TextApi {
|
||||
pub multi_byte_to_wide_char: windows_fn!(u32, u32, *const u8, i32, *mut u16, i32 => i32),
|
||||
pub wide_char_to_multi_byte:
|
||||
windows_fn!(u32, u32, *const u16, i32, *mut u8, i32, usize, usize => i32),
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gum_barebone_query_platform() -> *const crate::bindings::gchar {
|
||||
c"windows".as_ptr() as *const crate::bindings::gchar
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gum_thread_get_system_error() -> crate::bindings::gint {
|
||||
kernel::thread_error() as crate::bindings::gint
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gum_thread_set_system_error(value: crate::bindings::gint) {
|
||||
kernel::set_thread_error(value as u32)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gum_barebone_ansi_string_to_utf8(str_ansi: *const gchar, length: gint) -> *mut gchar {
|
||||
let ansi = unsafe { bytes_of(str_ansi as *const u8, length) };
|
||||
match kernel::text_api() {
|
||||
Some(api) => ansi_to_utf8(api, ansi),
|
||||
None => handed_to_glib(ansi.iter().map(|&byte| byte as char).collect::<String>().as_bytes()),
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gum_barebone_ansi_string_from_utf8(str_utf8: *const gchar) -> *mut gchar {
|
||||
match kernel::text_api() {
|
||||
Some(api) => ansi_from_utf8(api, str_utf8),
|
||||
None => {
|
||||
let utf8 = unsafe { core::ffi::CStr::from_ptr(str_utf8) }.to_str().unwrap();
|
||||
let latin1: Vec<u8> =
|
||||
utf8.chars().map(|c| if (c as u32) <= 0xff { c as u8 } else { b'?' }).collect();
|
||||
handed_to_glib(&latin1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn bytes_of<'a>(str: *const u8, length: gint) -> &'a [u8] {
|
||||
let length = if length < 0 {
|
||||
unsafe { core::ffi::CStr::from_ptr(str as *const core::ffi::c_char) }.to_bytes().len()
|
||||
} else {
|
||||
length as usize
|
||||
};
|
||||
unsafe { core::slice::from_raw_parts(str, length) }
|
||||
}
|
||||
|
||||
fn ansi_to_utf8(api: &TextApi, ansi: &[u8]) -> *mut gchar {
|
||||
let count = unsafe {
|
||||
(api.multi_byte_to_wide_char)(CP_ACP, 0, ansi.as_ptr(), ansi.len() as i32,
|
||||
ptr::null_mut(), 0)
|
||||
};
|
||||
let mut wide = alloc::vec![0u16; count as usize];
|
||||
unsafe {
|
||||
(api.multi_byte_to_wide_char)(CP_ACP, 0, ansi.as_ptr(), ansi.len() as i32,
|
||||
wide.as_mut_ptr(), count);
|
||||
g_utf16_to_utf8(wide.as_ptr(), count as _, ptr::null_mut(), ptr::null_mut(),
|
||||
ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
fn ansi_from_utf8(api: &TextApi, utf8: *const gchar) -> *mut gchar {
|
||||
unsafe {
|
||||
let wide = g_utf8_to_utf16(utf8, -1, ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
|
||||
let size = (api.wide_char_to_multi_byte)(CP_ACP, 0, wide, -1, ptr::null_mut(), 0, 0, 0);
|
||||
let ansi = g_malloc(size as gsize) as *mut u8;
|
||||
(api.wide_char_to_multi_byte)(CP_ACP, 0, wide, -1, ansi, size, 0, 0);
|
||||
g_free(wide as gpointer);
|
||||
ansi as *mut gchar
|
||||
}
|
||||
}
|
||||
|
||||
fn handed_to_glib(bytes: &[u8]) -> *mut gchar {
|
||||
unsafe {
|
||||
let copy = g_malloc((bytes.len() + 1) as gsize) as *mut u8;
|
||||
ptr::copy_nonoverlapping(bytes.as_ptr(), copy, bytes.len());
|
||||
*copy.add(bytes.len()) = 0;
|
||||
copy as *mut gchar
|
||||
}
|
||||
}
|
||||
|
||||
const CP_ACP: u32 = 0;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gum_barebone_query_stream_ops() -> *const GumBareboneStreamOps {
|
||||
if kernel::in_copy() { &STREAM_OPS } else { ptr::null() }
|
||||
}
|
||||
|
||||
static STREAM_OPS: GumBareboneStreamOps = GumBareboneStreamOps {
|
||||
poll: Some(kernel::poll_handles),
|
||||
read: Some(read_handle),
|
||||
write: Some(write_handle),
|
||||
close: Some(close_handle),
|
||||
};
|
||||
|
||||
unsafe extern "C" fn read_handle(handle: gpointer, buffer: gpointer, count: gsize,
|
||||
error: *mut *mut GError) -> gssize {
|
||||
let mut transferred = 0u32;
|
||||
let succeeded = unsafe {
|
||||
(kernel::file_api().read_file)(handle as usize, buffer as *mut u8, count as u32,
|
||||
&mut transferred, 0)
|
||||
};
|
||||
if succeeded == 0 {
|
||||
return failed(error);
|
||||
}
|
||||
transferred as gssize
|
||||
}
|
||||
|
||||
unsafe extern "C" fn write_handle(handle: gpointer, buffer: gconstpointer, count: gsize,
|
||||
error: *mut *mut GError) -> gssize {
|
||||
let mut transferred = 0u32;
|
||||
let succeeded = unsafe {
|
||||
(kernel::file_api().write_file)(handle as usize, buffer as *const u8, count as u32,
|
||||
&mut transferred, 0)
|
||||
};
|
||||
if succeeded == 0 {
|
||||
return failed(error);
|
||||
}
|
||||
transferred as gssize
|
||||
}
|
||||
|
||||
unsafe extern "C" fn close_handle(handle: gpointer, error: *mut *mut GError) -> gboolean {
|
||||
let succeeded = unsafe { (kernel::file_api().close_handle)(handle as usize) };
|
||||
if succeeded == 0 {
|
||||
failed(error);
|
||||
return 0;
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
fn failed(error: *mut *mut GError) -> gssize {
|
||||
let code = kernel::thread_error();
|
||||
let message = CString::new(describe_error(code)).unwrap();
|
||||
unsafe {
|
||||
g_set_error_literal(error, g_io_error_quark(), io_error_from_win32(code) as gint,
|
||||
message.as_ptr())
|
||||
};
|
||||
-1
|
||||
}
|
||||
|
||||
fn describe_error(code: u32) -> String {
|
||||
let mut ansi = [0u8; 512];
|
||||
let length = unsafe {
|
||||
(kernel::file_api().format_message)(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code, 0,
|
||||
ansi.as_mut_ptr(), ansi.len() as u32, 0)
|
||||
};
|
||||
let utf8 = ansi_to_utf8(kernel::text_api().unwrap(), &ansi[..length as usize]);
|
||||
let message = unsafe { core::ffi::CStr::from_ptr(utf8) }.to_str().unwrap().trim_end();
|
||||
let described = String::from(message);
|
||||
unsafe { g_free(utf8 as gpointer) };
|
||||
described
|
||||
}
|
||||
|
||||
fn io_error_from_win32(code: u32) -> u32 {
|
||||
match code {
|
||||
ERROR_FILE_NOT_FOUND | ERROR_PATH_NOT_FOUND => GIOErrorEnum_G_IO_ERROR_NOT_FOUND,
|
||||
ERROR_ACCESS_DENIED => GIOErrorEnum_G_IO_ERROR_PERMISSION_DENIED,
|
||||
ERROR_INVALID_HANDLE | ERROR_INVALID_PARAMETER => GIOErrorEnum_G_IO_ERROR_INVALID_ARGUMENT,
|
||||
ERROR_BROKEN_PIPE => GIOErrorEnum_G_IO_ERROR_BROKEN_PIPE,
|
||||
_ => GIOErrorEnum_G_IO_ERROR_FAILED,
|
||||
}
|
||||
}
|
||||
|
||||
const FORMAT_MESSAGE_IGNORE_INSERTS: u32 = 0x200;
|
||||
const FORMAT_MESSAGE_FROM_SYSTEM: u32 = 0x1000;
|
||||
const ERROR_FILE_NOT_FOUND: u32 = 2;
|
||||
const ERROR_PATH_NOT_FOUND: u32 = 3;
|
||||
const ERROR_ACCESS_DENIED: u32 = 5;
|
||||
const ERROR_INVALID_HANDLE: u32 = 6;
|
||||
const ERROR_INVALID_PARAMETER: u32 = 87;
|
||||
const ERROR_BROKEN_PIPE: u32 = 109;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gum_barebone_query_stack_size() -> crate::bindings::gsize {
|
||||
if crate::on_js_thread() {
|
||||
@@ -653,6 +867,14 @@ const INTEGER_OVERFLOW: u32 = 0xc000_0095;
|
||||
const BREAKPOINT: u32 = 0x8000_0003;
|
||||
const SINGLE_STEP: u32 = 0x8000_0004;
|
||||
|
||||
pub(crate) unsafe fn enumerate_imports_in_module(base: u64, callback: &mut FoundImportCallback) {
|
||||
if crate::running_in_a_process() {
|
||||
kernel::enumerate_imports(base as _, &mut |library, name, slot, address| {
|
||||
callback(library as *const crate::bindings::gchar, name as *const crate::bindings::gchar, slot, address)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enumerate_exports_in_range(
|
||||
start_address: u64,
|
||||
end_address: u64,
|
||||
|
||||
@@ -1120,13 +1120,25 @@ unsafe fn init_gum() {
|
||||
// do. A copy in a process would take it from the half that uses it.
|
||||
#[cfg(any(feature = "blob", feature = "xnu-core"))]
|
||||
pub(crate) unsafe fn init_gum_without_exceptor() {
|
||||
unsafe { init_gum_with_exceptor(false) };
|
||||
unsafe {
|
||||
init_gum_with_exceptor(false);
|
||||
warm_async_io();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn warm_async_io() {
|
||||
unsafe {
|
||||
let task = bindings::g_task_new(core::ptr::null_mut(), core::ptr::null_mut(), None,
|
||||
core::ptr::null_mut());
|
||||
bindings::g_object_unref(task as bindings::gpointer);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn init_gum_with_exceptor(exceptor: bool) {
|
||||
unsafe {
|
||||
bindings::g_set_panic_handler(Some(signed_to_be_called_back(frida_panic_handler, 0)), ptr::null_mut());
|
||||
bindings::gum_init_embedded();
|
||||
bindings::glib_enable_io_features();
|
||||
if exceptor {
|
||||
bindings::gum_exceptor_obtain();
|
||||
}
|
||||
@@ -1380,11 +1392,11 @@ pub(crate) fn watch_for_work(main_context: *mut GMainContext, ready: fn() -> boo
|
||||
#[cfg(any(feature = "blob", feature = "xnu-core"))]
|
||||
static mut WORK_SOURCE: *mut GSource = ptr::null_mut();
|
||||
|
||||
#[cfg(any(feature = "winnt", feature = "linux-injected"))]
|
||||
#[cfg(any(feature = "win9x", feature = "winnt", feature = "linux-injected"))]
|
||||
static mut WAKEUP_POLLFD: bindings::GPollFD =
|
||||
bindings::GPollFD { fd: -1, events: 0, revents: 0, user_data: ptr::null_mut() };
|
||||
|
||||
#[cfg(any(feature = "winnt", feature = "linux-injected"))]
|
||||
#[cfg(any(feature = "win9x", feature = "winnt", feature = "linux-injected"))]
|
||||
pub(crate) fn watch_a_descriptor(context: *mut GMainContext, fd: i32,
|
||||
poll: unsafe extern "C" fn(*mut bindings::GPollFD, u32, i32) -> i32) {
|
||||
const G_IO_IN: u16 = 1;
|
||||
|
||||
@@ -25,14 +25,6 @@ impl Arena {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn said(&self) -> &str {
|
||||
let bytes =
|
||||
unsafe { core::slice::from_raw_parts((self.begins + SAID) as *const u8, SAID_SIZE) };
|
||||
let length = bytes.iter().position(|byte| *byte == 0).unwrap_or(SAID_SIZE);
|
||||
|
||||
core::str::from_utf8(&bytes[..length]).unwrap_or("")
|
||||
}
|
||||
|
||||
pub fn page_size(&self) -> u32 {
|
||||
self.word(PAGE_SIZE).load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use core::ffi::{c_int, c_void};
|
||||
use core::ptr;
|
||||
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
|
||||
#[cfg(not(target_arch = "x86"))]
|
||||
use crate::bindings::{
|
||||
@@ -857,31 +856,10 @@ pub fn serve_register_requests() {
|
||||
|
||||
pub fn report_what_the_copies_hit() {
|
||||
for placed in unsafe { placements() }.values() {
|
||||
let Some(hit) = what_it_says(placed) else {
|
||||
if what_it_says(placed).is_none() {
|
||||
continue;
|
||||
};
|
||||
bump_to(placed.arena + super::arena::PROGRESS, NOTHING_MORE);
|
||||
|
||||
let arena = Arena::at(placed.arena);
|
||||
let said = arena.said();
|
||||
if hit == super::user::SPOKE {
|
||||
native::log(&alloc::format!("copy in {}: {}\n", pid_reported_by(placed), said));
|
||||
} else if hit == super::user::PANICKED {
|
||||
native::log(&alloc::format!("copy in {} gave up: {}\n", pid_reported_by(placed), said));
|
||||
} else {
|
||||
let kind = read_address(placed.arena + super::arena::FAULT_KIND);
|
||||
native::log(&alloc::format!(
|
||||
"copy in {} died on signal {} ({}) at {:#x}, pc {}, lr {}, image at {:#x}, {}\n",
|
||||
pid_reported_by(placed),
|
||||
kind as u32,
|
||||
(kind >> 32) as u32,
|
||||
read_address(placed.arena + super::arena::FAULT_ADDRESS),
|
||||
relative_to_image(read_address(placed.arena + super::arena::FAULT_PC), placed),
|
||||
relative_to_image(read_address(placed.arena + super::arena::FAULT_LR), placed),
|
||||
placed.base,
|
||||
said
|
||||
));
|
||||
}
|
||||
bump_to(placed.arena + super::arena::PROGRESS, NOTHING_MORE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,16 +877,6 @@ fn bump_to(address: usize, value: u32) {
|
||||
unsafe { (address as *mut u32).write_volatile(value) };
|
||||
}
|
||||
|
||||
fn relative_to_image(address: u64, placed: &Placement) -> String {
|
||||
let base = placed.base as u64;
|
||||
let size = crate::own_range().1 as u64;
|
||||
if address >= base && address - base < size {
|
||||
alloc::format!("image+{:#x}", address - base)
|
||||
} else {
|
||||
alloc::format!("{:#x}", address)
|
||||
}
|
||||
}
|
||||
|
||||
const NOTHING_MORE: u32 = 0;
|
||||
|
||||
fn watch_the_threads() {
|
||||
@@ -1134,10 +1102,6 @@ fn wake_the_copy(placed: &Placement) {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_address(address: usize) -> u64 {
|
||||
unsafe { (address as *const u64).read_volatile() }
|
||||
}
|
||||
|
||||
fn read_word(address: usize) -> u32 {
|
||||
unsafe { (address as *const u32).read_volatile() }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use core::ffi::c_void;
|
||||
use core::ffi::{CStr, c_void};
|
||||
use core::ptr;
|
||||
|
||||
use crate::bindings::{
|
||||
@@ -112,6 +112,22 @@ pub fn enumerate_exports_in_range(from: u64, to: u64, found: &mut FoundExportCal
|
||||
}
|
||||
}
|
||||
|
||||
pub fn export_named(wanted: &str) -> u64 {
|
||||
let mut found: u64 = 0;
|
||||
for image in mapped_images() {
|
||||
enumerate_exports_in_range(image.base, image.base + image.size, &mut |name, address| {
|
||||
if !name.is_null() && unsafe { CStr::from_ptr(name as *const _) }.to_bytes() == wanted.as_bytes() {
|
||||
found = address;
|
||||
}
|
||||
found == 0
|
||||
});
|
||||
if found != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
found
|
||||
}
|
||||
|
||||
pub fn enumerate_symbols_in_range(from: u64, to: u64, found: &mut FoundSymbolCallback<'_>) {
|
||||
let Some(image) = mapped_images().into_iter().find(|image| image.base == from) else {
|
||||
return;
|
||||
|
||||
@@ -595,7 +595,7 @@ struct TimeSpec {
|
||||
nanoseconds: i64,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn frida_poll(ufds: *mut crate::bindings::GPollFD, nfds: u32,
|
||||
pub(crate) unsafe extern "C" fn frida_poll(ufds: *mut crate::bindings::GPollFD, nfds: u32,
|
||||
timeout: i32) -> i32 {
|
||||
const MAX_FDS: usize = 16;
|
||||
let fds = unsafe { core::slice::from_raw_parts_mut(ufds, nfds as usize) };
|
||||
@@ -1204,6 +1204,18 @@ pub fn names_in(directory: &core::ffi::CStr) -> Vec<String> {
|
||||
names
|
||||
}
|
||||
|
||||
pub(crate) fn read(fd: i32, buffer: *mut u8, count: usize) -> isize {
|
||||
syscall(READ, fd as usize, buffer as usize, count, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn write(fd: i32, buffer: *const u8, count: usize) -> isize {
|
||||
syscall(WRITE, fd as usize, buffer as usize, count, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn close(fd: i32) -> isize {
|
||||
syscall(CLOSE, fd as usize, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub fn contents_of(path: &core::ffi::CStr) -> Vec<u8> {
|
||||
let file = syscall(OPENAT, WORKING_DIRECTORY, path.as_ptr() as usize, READ_ONLY, 0, 0, 0);
|
||||
if file < 0 {
|
||||
|
||||
+124
-12
@@ -192,7 +192,11 @@ fn block_until_signalled_or_timed_out(semaphore: u32, timeout_us: u64) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poke_loop_wakeup() {}
|
||||
pub fn poke_loop_wakeup() {
|
||||
if in_copy() {
|
||||
crate::win9x_user::poke_loop_wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wake(token: *const u8) {
|
||||
(primitives().wake)(token)
|
||||
@@ -220,7 +224,8 @@ pub fn inject_agent(pid: u32) -> u32 {
|
||||
0xff, 0x74, 0x24, 0x04,
|
||||
0xba, 0, 0, 0, 0,
|
||||
0xff, 0xd2,
|
||||
0xeb, 0xfe,
|
||||
0x59,
|
||||
0xc3,
|
||||
];
|
||||
payload[5..9].copy_from_slice(&entry.to_le_bytes());
|
||||
|
||||
@@ -244,6 +249,7 @@ pub fn inject_agent(pid: u32) -> u32 {
|
||||
unsafe {
|
||||
targets().insert(observed, Target {
|
||||
arena: injection.arena,
|
||||
thread: injection.thread,
|
||||
stack: injection.stack,
|
||||
image_base,
|
||||
})
|
||||
@@ -532,15 +538,15 @@ pub fn detach_from_process(pid: u32) -> bool {
|
||||
};
|
||||
let arena = target.arena;
|
||||
|
||||
unsafe { DEPARTING_THREAD = target.thread };
|
||||
unsafe { ((arena + STOP_REQUEST) as *mut u32).write_volatile(1) };
|
||||
if !await_flag(arena + WORKER_STOPPED) || !await_flag(arena + MAIN_STOPPED) {
|
||||
wake_copy(arena);
|
||||
if !await_flag(arena + WORKER_STOPPED) || !await_flag(arena + MAIN_STOPPED)
|
||||
|| !await_departure()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// A thread sets the flag before it leaves. Thus wait for the time it needs to run its last
|
||||
// instructions and return into KERNEL32.
|
||||
wait(core::ptr::addr_of!(TEARDOWN_TOKEN), Some(TEARDOWN_GRACE_US), &mut || false);
|
||||
|
||||
forget_hold(arena);
|
||||
|
||||
unsafe {
|
||||
@@ -556,13 +562,26 @@ pub fn detach_from_process(pid: u32) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn await_departure() -> bool {
|
||||
for _ in 0..INJECTION_ATTEMPTS {
|
||||
if unsafe { (&raw const DEPARTING_THREAD).read_volatile() } == 0 {
|
||||
return true;
|
||||
}
|
||||
wait(core::ptr::addr_of!(DEPARTURE_TOKEN), Some(INJECTION_POLL_US), &mut || false);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
struct Target {
|
||||
arena: u32,
|
||||
thread: u32,
|
||||
stack: u32,
|
||||
image_base: u32,
|
||||
}
|
||||
|
||||
static mut TEARDOWN_TOKEN: u8 = 0;
|
||||
static mut DEPARTING_THREAD: u32 = 0;
|
||||
static mut DEPARTURE_TOKEN: u8 = 0;
|
||||
|
||||
pub fn arena_for_pid(pid: u32) -> Option<u64> {
|
||||
unsafe { targets().get(&pid).map(|t| t.arena as u64) }
|
||||
@@ -784,12 +803,13 @@ pub fn inject(process: u32, payload: &[u8]) -> Injection {
|
||||
|
||||
let database = unsafe { ((arena + THREAD_DATABASE) as *const u32).read_volatile() };
|
||||
let thread = unsafe { (database as *const u32).byte_add(TDB_CONTROL_BLOCK).read() };
|
||||
redirect_to_trampoline(arena, thread);
|
||||
let start_stack = redirect_to_trampoline(arena, thread);
|
||||
|
||||
resume_thread(arena + RESUME_STUB_OFFSET, arena);
|
||||
if !await_flag(arena + RUNNING_FLAG) {
|
||||
return Injection { arena, thread: 0, stack };
|
||||
}
|
||||
free_shared(start_stack);
|
||||
unsafe {
|
||||
((arena + GATING) as *mut u32).write_volatile(spawns_are_gated() as u32);
|
||||
((arena + GO_FLAG) as *mut u32).write_volatile(1);
|
||||
@@ -1030,18 +1050,24 @@ fn write_trampoline(trampoline: u32, entry: u32, stack_top: u32) {
|
||||
0x50,
|
||||
0xba, 0, 0, 0, 0,
|
||||
0xff, 0xd2,
|
||||
0x64, 0xa1, 0x18, 0x00, 0x00, 0x00,
|
||||
0x89, 0x60, 0x58,
|
||||
0x89, 0x60, 0x04,
|
||||
0xc7, 0x40, 0x08, 0, 0, 0, 0,
|
||||
0x6a, 0x00,
|
||||
0xba, 0, 0, 0, 0,
|
||||
0xff, 0xd2,
|
||||
];
|
||||
let stack_bottom = stack_top + 0x40 - INJECTION_STACK_SIZE as u32;
|
||||
code[5..9].copy_from_slice(&stack_top.to_le_bytes());
|
||||
code[11..15].copy_from_slice(&entry.to_le_bytes());
|
||||
code[20..24].copy_from_slice(&kernel32_export(b"ExitThread").to_le_bytes());
|
||||
code[32..36].copy_from_slice(&stack_bottom.to_le_bytes());
|
||||
code[39..43].copy_from_slice(&kernel32_export(b"ExitThread").to_le_bytes());
|
||||
|
||||
unsafe { core::ptr::copy_nonoverlapping(code.as_ptr(), trampoline as *mut u8, code.len()) };
|
||||
}
|
||||
|
||||
fn redirect_to_trampoline(arena: u32, thread: u32) {
|
||||
fn redirect_to_trampoline(arena: u32, thread: u32) -> u32 {
|
||||
let mut context = [0u8; CONTEXT_SIZE];
|
||||
let base = context.as_mut_ptr();
|
||||
unsafe {
|
||||
@@ -1065,6 +1091,8 @@ fn redirect_to_trampoline(arena: u32, thread: u32) {
|
||||
base.add(CONTEXT_ESP).cast::<u32>().write_unaligned(top);
|
||||
__VWIN32_Set_Thread_Context(thread, base);
|
||||
}
|
||||
|
||||
stack
|
||||
}
|
||||
|
||||
fn await_flag(address: u32) -> bool {
|
||||
@@ -1128,6 +1156,26 @@ pub fn in_copy() -> bool {
|
||||
core::ptr::eq(primitives(), &crate::win9x_user::USER)
|
||||
}
|
||||
|
||||
pub fn thread_error() -> u32 {
|
||||
if in_copy() { crate::win9x_user::last_error() } else { 0 }
|
||||
}
|
||||
|
||||
pub fn set_thread_error(value: u32) {
|
||||
if in_copy() {
|
||||
crate::win9x_user::set_last_error(value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_api() -> &'static crate::gum_windows::FileApi {
|
||||
crate::win9x_user::file_api()
|
||||
}
|
||||
|
||||
pub fn text_api() -> Option<&'static crate::gum_windows::TextApi> {
|
||||
if in_copy() { Some(crate::win9x_user::text_api()) } else { None }
|
||||
}
|
||||
|
||||
pub(crate) use crate::win9x_user::poll_handles;
|
||||
|
||||
fn primitives() -> &'static Primitives {
|
||||
unsafe { ACTIVE }
|
||||
}
|
||||
@@ -1462,6 +1510,48 @@ pub(crate) fn enumerate_exports(base: u32, found: &mut dyn FnMut(*const u8, u64)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enumerate_imports(base: u32,
|
||||
found: &mut dyn FnMut(*const u8, *const u8, u64, u64) -> bool) {
|
||||
let headers = base + read_u32(base + DOS_HEADERS_OFFSET);
|
||||
let directory_rva = read_u32(headers + IMPORT_DIRECTORY_OFFSET);
|
||||
if directory_rva == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut descriptor = base + directory_rva;
|
||||
loop {
|
||||
let library_rva = read_u32(descriptor + IMPORT_NAME_OFFSET);
|
||||
if library_rva == 0 {
|
||||
return;
|
||||
}
|
||||
let library = (base + library_rva) as *const u8;
|
||||
let thunks = base + read_u32(descriptor + IMPORT_THUNKS_OFFSET);
|
||||
let lookups = match read_u32(descriptor + IMPORT_LOOKUPS_OFFSET) {
|
||||
0 => thunks,
|
||||
rva => base + rva,
|
||||
};
|
||||
|
||||
let mut index = 0;
|
||||
loop {
|
||||
let lookup = read_u32(lookups + index * 4);
|
||||
if lookup == 0 {
|
||||
break;
|
||||
}
|
||||
let slot = thunks + index * 4;
|
||||
let imported_by_name = lookup & IMPORT_BY_ORDINAL == 0;
|
||||
if imported_by_name {
|
||||
let name = (base + lookup + IMPORT_HINT_SIZE) as *const u8;
|
||||
if !found(library, name, slot as u64, read_u32(slot) as u64) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
descriptor += IMPORT_DESCRIPTOR_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kernel32_export(wanted: &[u8]) -> u32 {
|
||||
let base = unsafe { core::ptr::addr_of!(_KERNEL32_Base).read() };
|
||||
let headers = base + read_u32(base + DOS_HEADERS_OFFSET);
|
||||
@@ -1512,6 +1602,13 @@ fn read_u8(address: u32) -> u8 {
|
||||
}
|
||||
|
||||
const EXPORT_DIRECTORY_OFFSET: u32 = 0x78;
|
||||
const IMPORT_DIRECTORY_OFFSET: u32 = 0x80;
|
||||
const IMPORT_DESCRIPTOR_SIZE: u32 = 20;
|
||||
const IMPORT_LOOKUPS_OFFSET: u32 = 0;
|
||||
const IMPORT_NAME_OFFSET: u32 = 12;
|
||||
const IMPORT_THUNKS_OFFSET: u32 = 16;
|
||||
const IMPORT_BY_ORDINAL: u32 = 0x8000_0000;
|
||||
const IMPORT_HINT_SIZE: u32 = 2;
|
||||
|
||||
pub(crate) const MEM_RESERVE: u32 = 0x0000_2000;
|
||||
pub(crate) const MEM_RELEASE: u32 = 0x0000_8000;
|
||||
@@ -1553,7 +1650,6 @@ const PATCH_SIZE: usize = 2;
|
||||
const PE_SIGNATURE_OFFSET: usize = 0x3c;
|
||||
const PE_ENTRY_POINT_OFFSET: usize = 0x28;
|
||||
const FRAME_BUFFER_SIZE: usize = 0x4000;
|
||||
const TEARDOWN_GRACE_US: u64 = 500_000;
|
||||
const TDB_CONTROL_BLOCK: usize = 0x5c;
|
||||
pub(crate) const PARKED_SLEEP_MS: u32 = 200;
|
||||
const STUB_OFFSET: u32 = 0x100;
|
||||
@@ -2291,6 +2387,10 @@ extern "C" fn frida_win9x_on_control(message: u32, thread: u32) {
|
||||
tell_the_copy_of_a_departure(thread);
|
||||
}
|
||||
|
||||
if message == DESTROY_THREAD && thread == unsafe { DEPARTING_THREAD } {
|
||||
unsafe { (&raw mut DEPARTING_THREAD).write_volatile(0) };
|
||||
}
|
||||
|
||||
let told = match message {
|
||||
THREAD_INIT => unsafe { THREAD_APPEARED },
|
||||
DESTROY_THREAD => unsafe { THREAD_VANISHED },
|
||||
@@ -2436,6 +2536,10 @@ extern "C" fn frida_win9x_on_fault(fault: u32, frame: *mut u32) -> u32 {
|
||||
)
|
||||
};
|
||||
if handled == 0 {
|
||||
if fault == PAGE_FAULT && page_is_absent(registers) {
|
||||
return unsafe { FAULT_CHAIN[fault as usize] };
|
||||
}
|
||||
|
||||
report_unhandled_fault(fault, cpu_context.eip);
|
||||
cpu_context.eip = frida_win9x_park as *const () as u32;
|
||||
}
|
||||
@@ -2472,6 +2576,12 @@ fn recovery_stack(frame: *mut u32) -> u32 {
|
||||
unsafe { frame.add(THUNK_FRAME_ESP).read() }
|
||||
}
|
||||
|
||||
fn page_is_absent(registers: *const u32) -> bool {
|
||||
let error_code = unsafe { registers.byte_add(FAULT_FRAME_ERROR_CODE).read() };
|
||||
|
||||
error_code & PAGE_FAULT_PRESENT == 0
|
||||
}
|
||||
|
||||
fn report_unhandled_fault(fault: u32, eip: u32) {
|
||||
let address = faulting_address();
|
||||
|
||||
@@ -2524,8 +2634,10 @@ const SIMD_FLOATING_POINT: u32 = 19;
|
||||
|
||||
const THUNK_FRAME_EBP: usize = 2;
|
||||
const THUNK_FRAME_ESP: usize = 3;
|
||||
const FAULT_FRAME_ERROR_CODE: usize = 0x20;
|
||||
const FAULT_FRAME_EIP: usize = 0x24;
|
||||
const FAULT_FRAME_CS: usize = 0x28;
|
||||
const PAGE_FAULT_PRESENT: u32 = 1 << 0;
|
||||
const PARK_SLICE_US: u64 = 1_000_000;
|
||||
|
||||
fn faulting_address() -> u32 {
|
||||
|
||||
@@ -8,6 +8,7 @@ use alloc::vec::Vec;
|
||||
use core::ffi::c_void;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::gum_windows::{FileApi, TextApi};
|
||||
use crate::kernel::{MemoryRegion, ThreadEntry};
|
||||
use crate::win9x::*;
|
||||
|
||||
@@ -48,9 +49,6 @@ pub extern "C" fn frida_win9x_user_main(arena: u32) {
|
||||
|
||||
stop_hearing_about_faults();
|
||||
unsafe { ((arena + MAIN_STOPPED) as *mut u32).write_volatile(1) };
|
||||
loop {
|
||||
unsafe { sleep(PARKED_SLEEP_MS) };
|
||||
}
|
||||
}
|
||||
|
||||
// The group that the user-mode half runs on. The order is the order in Primitives.
|
||||
@@ -166,6 +164,97 @@ fn copy_has_work() -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
static LOOP_WAKE_EVENT: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
|
||||
|
||||
fn loop_wake_event() -> u32 {
|
||||
let existing = LOOP_WAKE_EVENT.load(Ordering::Acquire);
|
||||
if existing != 0 {
|
||||
return existing;
|
||||
}
|
||||
|
||||
let create_event: unsafe extern "stdcall" fn(u32, u32, u32, u32) -> u32 =
|
||||
unsafe { core::mem::transmute(user_api().create_event as usize) };
|
||||
let created = unsafe { create_event(0, 0, 0, 0) };
|
||||
|
||||
match LOOP_WAKE_EVENT.compare_exchange(0, created, Ordering::AcqRel, Ordering::Acquire) {
|
||||
Ok(_) => created,
|
||||
Err(raced) => {
|
||||
let close_handle: unsafe extern "stdcall" fn(u32) -> u32 =
|
||||
unsafe { core::mem::transmute(user_api().close_handle as usize) };
|
||||
unsafe { close_handle(created) };
|
||||
raced
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn poke_loop_wakeup() {
|
||||
let set_event: unsafe extern "stdcall" fn(u32) -> u32 =
|
||||
unsafe { core::mem::transmute(user_api().set_event as usize) };
|
||||
unsafe { set_event(loop_wake_event()) };
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn poll_handles(ufds: *mut crate::bindings::GPollFD, nfds: u32,
|
||||
timeout: i32) -> i32 {
|
||||
const MAX_HANDLES: usize = 16;
|
||||
let fds = unsafe { core::slice::from_raw_parts_mut(ufds, nfds as usize) };
|
||||
|
||||
let wake = loop_wake_event();
|
||||
|
||||
let mut handles = [0u32; MAX_HANDLES];
|
||||
let mut origin = [usize::MAX; MAX_HANDLES];
|
||||
handles[0] = wake;
|
||||
let mut count = 1usize;
|
||||
let mut ready = 0i32;
|
||||
|
||||
for (index, fd) in fds.iter_mut().enumerate() {
|
||||
fd.revents = 0;
|
||||
|
||||
if fd.fd == G_WAIT_WAKEUP_HANDLE {
|
||||
if !fd.user_data.is_null() {
|
||||
if unsafe { (fd.user_data as *const i32).read_volatile() } != 0 {
|
||||
fd.revents = POLL_IN;
|
||||
ready += 1;
|
||||
}
|
||||
let token = (fd.user_data as usize + GWAKEUP_TOKEN) as *mut *mut c_void;
|
||||
unsafe { token.write(fd.user_data) };
|
||||
}
|
||||
} else if fd.fd != -1 && fd.fd as u32 != wake && count < MAX_HANDLES {
|
||||
handles[count] = fd.fd as u32;
|
||||
origin[count] = index;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ready == 0 {
|
||||
let wait_for_multiple_objects_ex: unsafe extern "stdcall" fn(u32, *const u32, u32, u32, u32) -> u32 =
|
||||
unsafe { core::mem::transmute(user_api().wait_for_multiple_objects_ex as usize) };
|
||||
let timeout_ms = if timeout < 0 { INFINITE } else { timeout as u32 };
|
||||
let status = unsafe {
|
||||
wait_for_multiple_objects_ex(count as u32, handles.as_ptr(), 0, timeout_ms, 1)
|
||||
};
|
||||
if (status as usize) < count {
|
||||
let slot = origin[status as usize];
|
||||
if slot != usize::MAX {
|
||||
fds[slot].revents = POLL_IN;
|
||||
ready += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for fd in fds.iter() {
|
||||
if fd.fd == G_WAIT_WAKEUP_HANDLE && !fd.user_data.is_null() {
|
||||
let token = (fd.user_data as usize + GWAKEUP_TOKEN) as *mut *mut c_void;
|
||||
unsafe { token.write(core::ptr::null_mut()) };
|
||||
}
|
||||
}
|
||||
|
||||
ready
|
||||
}
|
||||
|
||||
const G_WAIT_WAKEUP_HANDLE: i32 = -43;
|
||||
const GWAKEUP_TOKEN: usize = 4;
|
||||
const POLL_IN: u16 = 1;
|
||||
|
||||
fn serve_the_copy() {
|
||||
let arena = unsafe { ARENA };
|
||||
|
||||
@@ -224,6 +313,7 @@ unsafe extern "C" fn user_worker(parameter: *mut c_void, _wait_result: i32) {
|
||||
crate::gum_windows::watch_the_loader();
|
||||
crate::glib::own_the_loop();
|
||||
crate::watch_for_work(context, copy_has_work, serve_the_copy);
|
||||
crate::watch_a_descriptor(context, loop_wake_event() as i32, poll_handles);
|
||||
|
||||
while unsafe { ((arena + STOP_REQUEST) as *const u32).read_volatile() } == 0 {
|
||||
unsafe { crate::dispatch_pending_work(context) };
|
||||
@@ -331,19 +421,11 @@ const GUM_PAGE_WRITE: u32 = 0x2;
|
||||
const GUM_PAGE_EXECUTE: u32 = 0x4;
|
||||
|
||||
pub unsafe extern "stdcall" fn on_module_load(name: *const u8) -> u32 {
|
||||
unsafe {
|
||||
let n = (ARENA + 0xb0) as *mut u32;
|
||||
n.write_volatile(n.read_volatile() + 1);
|
||||
}
|
||||
let original: extern "stdcall" fn(*const u8) -> u32 =
|
||||
unsafe { core::mem::transmute(crate::gum_windows::loader_load()) };
|
||||
|
||||
let handle = original(name);
|
||||
note_module(handle, name);
|
||||
unsafe {
|
||||
let n = (ARENA + 0xb4) as *mut u32;
|
||||
n.write_volatile(n.read_volatile() + 1);
|
||||
}
|
||||
|
||||
handle
|
||||
}
|
||||
@@ -514,13 +596,20 @@ fn resolve_user_api() {
|
||||
create_event: kernel32_export(b"CreateEventA"),
|
||||
set_event: kernel32_export(b"SetEvent"),
|
||||
wait_for_single_object_ex: kernel32_export(b"WaitForSingleObjectEx"),
|
||||
wait_for_multiple_objects_ex: kernel32_export(b"WaitForMultipleObjectsEx"),
|
||||
close_handle: kernel32_export(b"CloseHandle"),
|
||||
set_file_pointer: kernel32_export(b"SetFilePointer"),
|
||||
create_file: kernel32_export(b"CreateFileA"),
|
||||
read_file: kernel32_export(b"ReadFile"),
|
||||
write_file: kernel32_export(b"WriteFile"),
|
||||
find_first_file: kernel32_export(b"FindFirstFileA"),
|
||||
find_next_file: kernel32_export(b"FindNextFileA"),
|
||||
find_close: kernel32_export(b"FindClose"),
|
||||
get_last_error: kernel32_export(b"GetLastError"),
|
||||
set_last_error: kernel32_export(b"SetLastError"),
|
||||
format_message: kernel32_export(b"FormatMessageA"),
|
||||
multi_byte_to_wide_char: kernel32_export(b"MultiByteToWideChar"),
|
||||
wide_char_to_multi_byte: kernel32_export(b"WideCharToMultiByte"),
|
||||
set_unhandled_exception_filter: kernel32_export(b"SetUnhandledExceptionFilter"),
|
||||
tls_alloc: kernel32_export(b"TlsAlloc"),
|
||||
tls_get_value: kernel32_export(b"TlsGetValue"),
|
||||
@@ -533,6 +622,50 @@ fn user_api() -> &'static UserApi {
|
||||
unsafe { &*core::ptr::addr_of!(USER_API) }
|
||||
}
|
||||
|
||||
pub fn last_error() -> u32 {
|
||||
let get_last_error: unsafe extern "stdcall" fn() -> u32 =
|
||||
unsafe { core::mem::transmute(user_api().get_last_error as usize) };
|
||||
unsafe { get_last_error() }
|
||||
}
|
||||
|
||||
pub fn set_last_error(value: u32) {
|
||||
let set_last_error: unsafe extern "stdcall" fn(u32) =
|
||||
unsafe { core::mem::transmute(user_api().set_last_error as usize) };
|
||||
unsafe { set_last_error(value) }
|
||||
}
|
||||
|
||||
pub fn file_api() -> &'static FileApi {
|
||||
unsafe {
|
||||
if (*core::ptr::addr_of!(FILE_API)).is_none() {
|
||||
let api = user_api();
|
||||
FILE_API = Some(FileApi {
|
||||
read_file: core::mem::transmute(api.read_file as usize),
|
||||
write_file: core::mem::transmute(api.write_file as usize),
|
||||
close_handle: core::mem::transmute(api.close_handle as usize),
|
||||
format_message: core::mem::transmute(api.format_message as usize),
|
||||
});
|
||||
}
|
||||
(*core::ptr::addr_of!(FILE_API)).as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
static mut FILE_API: Option<FileApi> = None;
|
||||
|
||||
pub fn text_api() -> &'static TextApi {
|
||||
unsafe {
|
||||
if (*core::ptr::addr_of!(TEXT_API)).is_none() {
|
||||
let api = user_api();
|
||||
TEXT_API = Some(TextApi {
|
||||
multi_byte_to_wide_char: core::mem::transmute(api.multi_byte_to_wide_char as usize),
|
||||
wide_char_to_multi_byte: core::mem::transmute(api.wide_char_to_multi_byte as usize),
|
||||
});
|
||||
}
|
||||
(*core::ptr::addr_of!(TEXT_API)).as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
static mut TEXT_API: Option<TextApi> = None;
|
||||
|
||||
struct UserApi {
|
||||
sleep: u32,
|
||||
get_tick_count: u32,
|
||||
@@ -553,13 +686,20 @@ struct UserApi {
|
||||
create_event: u32,
|
||||
set_event: u32,
|
||||
wait_for_single_object_ex: u32,
|
||||
wait_for_multiple_objects_ex: u32,
|
||||
close_handle: u32,
|
||||
set_file_pointer: u32,
|
||||
create_file: u32,
|
||||
read_file: u32,
|
||||
write_file: u32,
|
||||
find_first_file: u32,
|
||||
find_next_file: u32,
|
||||
find_close: u32,
|
||||
get_last_error: u32,
|
||||
set_last_error: u32,
|
||||
format_message: u32,
|
||||
multi_byte_to_wide_char: u32,
|
||||
wide_char_to_multi_byte: u32,
|
||||
set_unhandled_exception_filter: u32,
|
||||
tls_alloc: u32,
|
||||
tls_get_value: u32,
|
||||
@@ -586,13 +726,20 @@ static mut USER_API: UserApi = UserApi {
|
||||
create_event: 0,
|
||||
set_event: 0,
|
||||
wait_for_single_object_ex: 0,
|
||||
wait_for_multiple_objects_ex: 0,
|
||||
close_handle: 0,
|
||||
set_file_pointer: 0,
|
||||
create_file: 0,
|
||||
read_file: 0,
|
||||
write_file: 0,
|
||||
find_first_file: 0,
|
||||
find_next_file: 0,
|
||||
find_close: 0,
|
||||
get_last_error: 0,
|
||||
set_last_error: 0,
|
||||
format_message: 0,
|
||||
multi_byte_to_wide_char: 0,
|
||||
wide_char_to_multi_byte: 0,
|
||||
set_unhandled_exception_filter: 0,
|
||||
tls_alloc: 0,
|
||||
tls_get_value: 0,
|
||||
|
||||
@@ -28,31 +28,7 @@ type ThreadChangeRoutine = unsafe extern "win64" fn(usize, usize, u8);
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
type ThreadChangeRoutine = unsafe extern "C" fn(usize, usize, u8);
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
macro_rules! windows_fn {
|
||||
($($argument:ty),* $(,)?) => { unsafe extern "stdcall" fn($($argument),*) };
|
||||
($($argument:ty),* $(,)? => $result:ty) => {
|
||||
unsafe extern "stdcall" fn($($argument),*) -> $result
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
macro_rules! windows_fn {
|
||||
($($argument:ty),* $(,)?) => { unsafe extern "win64" fn($($argument),*) };
|
||||
($($argument:ty),* $(,)? => $result:ty) => {
|
||||
unsafe extern "win64" fn($($argument),*) -> $result
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
macro_rules! windows_fn {
|
||||
($($argument:ty),* $(,)?) => { unsafe extern "C" fn($($argument),*) };
|
||||
($($argument:ty),* $(,)? => $result:ty) => {
|
||||
unsafe extern "C" fn($($argument),*) -> $result
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use windows_fn;
|
||||
pub(crate) use crate::gum_windows::windows_fn;
|
||||
|
||||
pub const MODULE_DIRECTORY: &str = "/WINDOWS/system32/";
|
||||
|
||||
@@ -3447,6 +3423,26 @@ pub fn in_copy() -> bool {
|
||||
core::ptr::eq(primitives(), &crate::winnt_user::USER)
|
||||
}
|
||||
|
||||
pub fn thread_error() -> u32 {
|
||||
if in_copy() { crate::winnt_user::last_error() } else { 0 }
|
||||
}
|
||||
|
||||
pub fn set_thread_error(value: u32) {
|
||||
if in_copy() {
|
||||
crate::winnt_user::set_last_error(value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_api() -> &'static crate::gum_windows::FileApi {
|
||||
crate::winnt_user::file_api()
|
||||
}
|
||||
|
||||
pub fn text_api() -> Option<&'static crate::gum_windows::TextApi> {
|
||||
if in_copy() { Some(crate::winnt_user::text_api()) } else { None }
|
||||
}
|
||||
|
||||
pub(crate) use crate::winnt_user::poll_handles;
|
||||
|
||||
fn primitives() -> &'static Primitives {
|
||||
unsafe { ACTIVE }
|
||||
}
|
||||
@@ -3749,19 +3745,83 @@ pub(crate) fn enumerate_exports(base: usize, found: &mut dyn FnMut(*const u8, u6
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enumerate_imports(base: usize,
|
||||
found: &mut dyn FnMut(*const u8, *const u8, u64, u64) -> bool) {
|
||||
unsafe {
|
||||
let directories = data_directories(base);
|
||||
let directory_rva = read_u32(directories + IMPORT_DIRECTORY_INDEX * 8) as usize;
|
||||
if directory_rva == 0 {
|
||||
return;
|
||||
}
|
||||
let wide = is_pe32_plus(base);
|
||||
let thunk_size = if wide { 8 } else { 4 };
|
||||
let by_ordinal: u64 = if wide { 1 << 63 } else { 1 << 31 };
|
||||
|
||||
let mut descriptor = base + directory_rva;
|
||||
loop {
|
||||
let library_rva = read_u32(descriptor + IMPORT_NAME_OFFSET) as usize;
|
||||
if library_rva == 0 {
|
||||
return;
|
||||
}
|
||||
let library = (base + library_rva) as *const u8;
|
||||
let thunks = base + read_u32(descriptor + IMPORT_THUNKS_OFFSET) as usize;
|
||||
let lookups = match read_u32(descriptor + IMPORT_LOOKUPS_OFFSET) as usize {
|
||||
0 => thunks,
|
||||
rva => base + rva,
|
||||
};
|
||||
|
||||
let mut index = 0;
|
||||
loop {
|
||||
let lookup = read_thunk(lookups + index * thunk_size, wide);
|
||||
if lookup == 0 {
|
||||
break;
|
||||
}
|
||||
let slot = thunks + index * thunk_size;
|
||||
if lookup & by_ordinal == 0 {
|
||||
let name = (base + lookup as usize + IMPORT_HINT_SIZE) as *const u8;
|
||||
if !found(library, name, slot as u64, read_thunk(slot, wide)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
descriptor += IMPORT_DESCRIPTOR_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn read_thunk(address: usize, wide: bool) -> u64 {
|
||||
unsafe {
|
||||
if wide {
|
||||
(address as *const u64).read()
|
||||
} else {
|
||||
read_u32(address) as u64
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn export_directory(base: usize) -> usize {
|
||||
unsafe { base + read_u32(data_directories(base)) as usize }
|
||||
}
|
||||
|
||||
unsafe fn is_pe32_plus(base: usize) -> bool {
|
||||
unsafe {
|
||||
let headers = base + read_u32(base + PE_HEADERS_OFFSET) as usize;
|
||||
let magic = (headers + OPTIONAL_HEADER_OFFSET) as *const u16;
|
||||
let directories = headers
|
||||
((headers + OPTIONAL_HEADER_OFFSET) as *const u16).read() == PE32_PLUS_MAGIC
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn data_directories(base: usize) -> usize {
|
||||
unsafe {
|
||||
let headers = base + read_u32(base + PE_HEADERS_OFFSET) as usize;
|
||||
headers
|
||||
+ OPTIONAL_HEADER_OFFSET
|
||||
+ if magic.read() == PE32_PLUS_MAGIC {
|
||||
+ if is_pe32_plus(base) {
|
||||
DATA_DIRECTORIES_OFFSET_64
|
||||
} else {
|
||||
DATA_DIRECTORIES_OFFSET_32
|
||||
};
|
||||
|
||||
base + read_u32(directories) as usize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3840,6 +3900,12 @@ const PE32_PLUS_MAGIC: u16 = 0x20b;
|
||||
const DATA_DIRECTORIES_OFFSET_32: usize = 0x60;
|
||||
const DATA_DIRECTORIES_OFFSET_64: usize = 0x70;
|
||||
const EXPORT_NAME_COUNT_OFFSET: usize = 0x18;
|
||||
const IMPORT_DIRECTORY_INDEX: usize = 1;
|
||||
const IMPORT_DESCRIPTOR_SIZE: usize = 20;
|
||||
const IMPORT_LOOKUPS_OFFSET: usize = 0;
|
||||
const IMPORT_NAME_OFFSET: usize = 12;
|
||||
const IMPORT_THUNKS_OFFSET: usize = 16;
|
||||
const IMPORT_HINT_SIZE: usize = 2;
|
||||
const EXPORT_FUNCTIONS_OFFSET: usize = 0x1c;
|
||||
const EXPORT_NAMES_OFFSET: usize = 0x20;
|
||||
const EXPORT_ORDINALS_OFFSET: usize = 0x24;
|
||||
|
||||
@@ -8,6 +8,7 @@ use alloc::vec::Vec;
|
||||
use core::ffi::c_void;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::gum_windows::{FileApi, TextApi};
|
||||
use crate::kernel::MemoryRegion;
|
||||
use crate::winnt::{
|
||||
BLOCK_PROCESS_ID, BLOCK_THREAD_ID, CURRENT_PROCESS, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE,
|
||||
@@ -788,6 +789,68 @@ fn user_api() -> &'static UserApi {
|
||||
unsafe { (*core::ptr::addr_of!(USER_API)).as_ref().unwrap() }
|
||||
}
|
||||
|
||||
pub fn last_error() -> u32 {
|
||||
unsafe { (error_api().get_last_error)() }
|
||||
}
|
||||
|
||||
pub fn set_last_error(value: u32) {
|
||||
unsafe { (error_api().set_last_error)(value) }
|
||||
}
|
||||
|
||||
fn error_api() -> &'static ErrorApi {
|
||||
unsafe {
|
||||
if (*core::ptr::addr_of!(ERROR_API)).is_none() {
|
||||
let library = module_base(peb(), b"kernel32.dll");
|
||||
ERROR_API = Some(ErrorApi {
|
||||
get_last_error: core::mem::transmute(export(library, b"GetLastError")),
|
||||
set_last_error: core::mem::transmute(export(library, b"SetLastError")),
|
||||
});
|
||||
}
|
||||
(*core::ptr::addr_of!(ERROR_API)).as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
struct ErrorApi {
|
||||
get_last_error: windows_fn!(=> u32),
|
||||
set_last_error: windows_fn!(u32),
|
||||
}
|
||||
|
||||
static mut ERROR_API: Option<ErrorApi> = None;
|
||||
|
||||
pub fn file_api() -> &'static FileApi {
|
||||
unsafe {
|
||||
if (*core::ptr::addr_of!(FILE_API)).is_none() {
|
||||
let library = module_base(peb(), b"kernel32.dll");
|
||||
FILE_API = Some(FileApi {
|
||||
read_file: core::mem::transmute(export(library, b"ReadFile")),
|
||||
write_file: core::mem::transmute(export(library, b"WriteFile")),
|
||||
close_handle: core::mem::transmute(export(library, b"CloseHandle")),
|
||||
format_message: core::mem::transmute(export(library, b"FormatMessageA")),
|
||||
});
|
||||
}
|
||||
(*core::ptr::addr_of!(FILE_API)).as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
static mut FILE_API: Option<FileApi> = None;
|
||||
|
||||
pub fn text_api() -> &'static TextApi {
|
||||
unsafe {
|
||||
if (*core::ptr::addr_of!(TEXT_API)).is_none() {
|
||||
let library = module_base(peb(), b"kernel32.dll");
|
||||
TEXT_API = Some(TextApi {
|
||||
multi_byte_to_wide_char: core::mem::transmute(
|
||||
export(library, b"MultiByteToWideChar")),
|
||||
wide_char_to_multi_byte: core::mem::transmute(
|
||||
export(library, b"WideCharToMultiByte")),
|
||||
});
|
||||
}
|
||||
(*core::ptr::addr_of!(TEXT_API)).as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
static mut TEXT_API: Option<TextApi> = None;
|
||||
|
||||
struct UserApi {
|
||||
allocate_heap: windows_fn!(usize, u32, usize => *mut u8),
|
||||
free_heap: windows_fn!(usize, u32, *mut u8 => u8),
|
||||
@@ -969,7 +1032,7 @@ fn serve_the_copy() {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn poll_handles(ufds: *mut crate::bindings::GPollFD, nfds: u32,
|
||||
pub(crate) unsafe extern "C" fn poll_handles(ufds: *mut crate::bindings::GPollFD, nfds: u32,
|
||||
timeout: i32) -> i32 {
|
||||
const MAX_HANDLES: usize = 16;
|
||||
let fds = unsafe { core::slice::from_raw_parts_mut(ufds, nfds as usize) };
|
||||
|
||||
@@ -3,7 +3,7 @@ use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::bindings::{
|
||||
GumDarwinExportDetails, GumDarwinModule, GumMemoryRange, GumModuleRegistry, GumAddress,
|
||||
GumMachoExportDetails, GumMachoModule, GumMemoryRange, GumModuleRegistry, GumAddress,
|
||||
gboolean, g_object_unref, gpointer, gsize,
|
||||
};
|
||||
use crate::gum;
|
||||
@@ -33,7 +33,7 @@ pub fn enumerate_exports_in_range(from: u64, to: u64,
|
||||
|
||||
let mut asking = Asking { found, base: image.base };
|
||||
unsafe {
|
||||
crate::bindings::gum_darwin_module_enumerate_exports(module,
|
||||
crate::bindings::gum_macho_module_enumerate_exports(module,
|
||||
Some(crate::signed_to_be_called_back(note_an_export, 0)),
|
||||
&mut asking as *mut Asking<'_, '_> as gpointer);
|
||||
g_object_unref(module as gpointer);
|
||||
@@ -45,7 +45,7 @@ struct Asking<'a, 'b> {
|
||||
base: u64,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn note_an_export(details: *const GumDarwinExportDetails, asking: gpointer)
|
||||
unsafe extern "C" fn note_an_export(details: *const GumMachoExportDetails, asking: gpointer)
|
||||
-> gboolean
|
||||
{
|
||||
let asking = unsafe { &mut *(asking as *mut Asking<'_, '_>) };
|
||||
@@ -146,10 +146,10 @@ fn where_the_loader_keeps_its_list() -> Option<u64> {
|
||||
(list != 0).then_some(list)
|
||||
}
|
||||
|
||||
fn read_the_image(base: u64, name: &str) -> Option<*mut GumDarwinModule> {
|
||||
fn read_the_image(base: u64, name: &str) -> Option<*mut GumMachoModule> {
|
||||
let named = alloc::ffi::CString::new(name).ok()?;
|
||||
let module = unsafe {
|
||||
crate::bindings::gum_darwin_module_new_from_memory(named.as_ptr(), NO_TASK_TO_ASK,
|
||||
crate::bindings::gum_macho_module_new_from_memory(named.as_ptr(), NO_TASK_TO_ASK,
|
||||
base as GumAddress, 0, core::ptr::null_mut())
|
||||
};
|
||||
|
||||
@@ -163,7 +163,7 @@ fn how_much_of_it_there_is(base: u64, name: &str) -> u64 {
|
||||
|
||||
let mut size = 0;
|
||||
for step in 0.. {
|
||||
let segment = unsafe { crate::bindings::gum_darwin_module_get_nth_segment(module, step) };
|
||||
let segment = unsafe { crate::bindings::gum_macho_module_get_nth_segment(module, step) };
|
||||
if segment.is_null() {
|
||||
break;
|
||||
}
|
||||
|
||||
+13
-13
@@ -8,7 +8,7 @@ namespace Frida.Barebone {
|
||||
public abstract Gum.PageProtection granted_protection ();
|
||||
|
||||
public abstract async Allocation allocate (size_t size, size_t alignment, Cancellable? cancellable)
|
||||
throws Error, IOError;
|
||||
throws Efni.Error, IOError;
|
||||
}
|
||||
|
||||
public interface Allocation : Object {
|
||||
@@ -20,11 +20,11 @@ namespace Frida.Barebone {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async void deallocate (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void deallocate (Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
}
|
||||
|
||||
internal async void protect_unless_already (Machine machine, Allocator allocator, uint64 address, size_t size,
|
||||
Gum.PageProtection prot, Cancellable? cancellable) throws Error, IOError {
|
||||
Gum.PageProtection prot, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if ((allocator.granted_protection () & prot) == prot)
|
||||
return;
|
||||
|
||||
@@ -55,9 +55,9 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Allocation allocate (size_t size, size_t alignment, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (size > _size)
|
||||
throw new Error.NOT_SUPPORTED ("Reserved region is too small");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Reserved region is too small");
|
||||
|
||||
return new FixedAllocation (address, size);
|
||||
}
|
||||
@@ -84,7 +84,7 @@ namespace Frida.Barebone {
|
||||
this._size = size;
|
||||
}
|
||||
|
||||
public async void deallocate (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void deallocate (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,8 +105,8 @@ namespace Frida.Barebone {
|
||||
this._page_size = page_size;
|
||||
}
|
||||
|
||||
public async Allocation allocate (size_t size, size_t alignment, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("To enable this feature, specify an allocator in your FRIDA_BAREBONE_CONFIG");
|
||||
public async Allocation allocate (size_t size, size_t alignment, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("To enable this feature, specify an allocator in your FRIDA_BAREBONE_CONFIG");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace Frida.Barebone {
|
||||
this.cursor = config.physical_base.address;
|
||||
}
|
||||
|
||||
public async Allocation allocate (size_t size, size_t alignment, Cancellable? cancellable) throws Error, IOError {
|
||||
public async Allocation allocate (size_t size, size_t alignment, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 address_pa = cursor;
|
||||
|
||||
size_t vm_size = round_size_up (size, _page_size);
|
||||
@@ -168,7 +168,7 @@ namespace Frida.Barebone {
|
||||
page_allocation = allocation;
|
||||
}
|
||||
|
||||
public async void deallocate (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void deallocate (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
// TODO: Add to freelist.
|
||||
yield page_allocation.deallocate (cancellable);
|
||||
}
|
||||
@@ -208,14 +208,14 @@ namespace Frida.Barebone {
|
||||
// A kernel allocator gives blocks with an alignment of less than a page, and the caller can
|
||||
// change the protection and write whole pages. Thus allocate enough for every page the
|
||||
// result spans, give out full pages, and keep the start of the block for the release.
|
||||
public async Allocation allocate (size_t size, size_t alignment, Cancellable? cancellable) throws Error, IOError {
|
||||
public async Allocation allocate (size_t size, size_t alignment, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
size_t aligned_size = (size + alignment - 1) & ~(alignment - 1);
|
||||
size_t padded_size = aligned_size + alignment - 1;
|
||||
|
||||
uint64 block = yield machine.invoke (alloc_function,
|
||||
resolve_arguments (alloc_arguments, padded_size, 0), cancellable);
|
||||
if (block == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to allocate %zu bytes in the target", padded_size);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to allocate %zu bytes in the target", padded_size);
|
||||
|
||||
uint64 address = (block + alignment - 1) & ~((uint64) alignment - 1);
|
||||
|
||||
@@ -274,7 +274,7 @@ namespace Frida.Barebone {
|
||||
this.free_arguments = free_arguments;
|
||||
}
|
||||
|
||||
public async void deallocate (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void deallocate (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield machine.invoke (free_function,
|
||||
resolve_arguments (free_arguments, block_size, block), cancellable);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class AndroidEmulatorStubClient : GDB.Client {
|
||||
@@ -6,19 +8,19 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public static new async AndroidEmulatorStubClient open (IOStream stream, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var client = new AndroidEmulatorStubClient (stream);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
Efni.throw_api_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
protected override async void detect_corellium (Cancellable? cancellable) throws Error, IOError {
|
||||
protected override async void detect_corellium (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class ArmMachine : Object, Machine {
|
||||
@@ -84,14 +86,14 @@ namespace Frida.Barebone {
|
||||
Object (debugger: debugger);
|
||||
}
|
||||
|
||||
public async size_t query_page_size (Cancellable? cancellable) throws Error, IOError {
|
||||
public async size_t query_page_size (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
return 4096;
|
||||
}
|
||||
|
||||
public async uint query_exception_level (Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint query_exception_level (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
DebuggerException? exception = debugger.exception;
|
||||
if (exception == null)
|
||||
throw new Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
DebuggerThread thread = exception.thread;
|
||||
|
||||
var cpsr = yield thread.read_register ("cpsr", cancellable);
|
||||
@@ -100,7 +102,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async void enumerate_ranges (Gum.PageProtection prot, FoundRangeFunc func, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
Gee.List<RangeDetails> ranges = yield collect_ranges_using_mmu (cancellable);
|
||||
foreach (RangeDetails r in coalesce_ranges (ranges)) {
|
||||
if ((r.protection & prot) != prot)
|
||||
@@ -110,7 +112,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async Gee.List<RangeDetails> collect_ranges_using_mmu (Cancellable? cancellable) throws Error, IOError {
|
||||
private async Gee.List<RangeDetails> collect_ranges_using_mmu (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var result = new Gee.ArrayList<RangeDetails> ();
|
||||
|
||||
MMUParameters p = yield MMUParameters.load (debugger, cancellable);
|
||||
@@ -132,7 +134,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void collect_ranges_in_l1 (uint64 table_address, uint first_index, uint end_index, MMUParameters p,
|
||||
Gee.List<RangeDetails> ranges, Cancellable? cancellable) throws Error, IOError {
|
||||
Gee.List<RangeDetails> ranges, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Buffer entries = yield read_physical_buffer (table_address + (first_index * L1_ENTRY_SIZE),
|
||||
(end_index - first_index) * L1_ENTRY_SIZE, cancellable);
|
||||
|
||||
@@ -158,7 +160,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void collect_ranges_in_l2 (uint64 table_address, uint64 base_va, MMUParameters p,
|
||||
Gee.List<RangeDetails> ranges, Cancellable? cancellable) throws Error, IOError {
|
||||
Gee.List<RangeDetails> ranges, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Buffer entries = yield read_physical_buffer (table_address, L2_NUM_ENTRIES * L2_ENTRY_SIZE, cancellable);
|
||||
|
||||
for (uint i = 0; i != L2_NUM_ENTRIES; i++) {
|
||||
@@ -202,7 +204,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public override async void write_virtual (uint64 va, uint8[] data, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (kernel_page_table == 0) {
|
||||
yield debugger.write_byte_array (va, new Bytes (data), cancellable);
|
||||
return;
|
||||
@@ -229,7 +231,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async bool write_through_kernel_table (uint64 va, uint8[] data, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
size_t offset = 0;
|
||||
while (offset < data.length) {
|
||||
uint64 cur_va = va + offset;
|
||||
@@ -247,7 +249,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void locate_kernel_page_table (MMUParameters p, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (kernel_page_table == 0 || kernel_page_table_pa != 0)
|
||||
return;
|
||||
|
||||
@@ -257,7 +259,7 @@ namespace Frida.Barebone {
|
||||
kernel_page_table_pa = table;
|
||||
}
|
||||
|
||||
public override async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Error, IOError {
|
||||
public override async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
MMUParameters p = yield MMUParameters.load (debugger, cancellable);
|
||||
|
||||
bool was_running = yield enter_physical_addressing (debugger, cancellable);
|
||||
@@ -274,13 +276,13 @@ namespace Frida.Barebone {
|
||||
throw_if_failed (failure);
|
||||
|
||||
if (pa == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Address 0x%x is not mapped", (uint) va);
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Address 0x%x is not mapped", (uint) va);
|
||||
|
||||
return pa;
|
||||
}
|
||||
|
||||
private async uint64 table_describing (uint64 va, MMUParameters p, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (kernel_page_table_pa != 0) {
|
||||
uint64 slot = kernel_page_table_pa + (((va >> L1_SHIFT) & (L1_NUM_ENTRIES - 1)) * L1_ENTRY_SIZE);
|
||||
uint32 entry = (yield read_physical_buffer (slot, L1_ENTRY_SIZE, cancellable)).read_uint32 (0);
|
||||
@@ -292,7 +294,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async uint64? translate_in_table (uint64 table_pa, uint64 va, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint64 l1_slot = table_pa + (((va >> L1_SHIFT) & (L1_NUM_ENTRIES - 1)) * L1_ENTRY_SIZE);
|
||||
uint32 l1 = (yield read_physical_buffer (l1_slot, L1_ENTRY_SIZE, cancellable)).read_uint32 (0);
|
||||
|
||||
@@ -317,7 +319,7 @@ namespace Frida.Barebone {
|
||||
return (l2 & SMALL_PAGE_MASK) | (va & 0xfff);
|
||||
}
|
||||
|
||||
private async Buffer read_physical_buffer (uint64 pa, size_t size, Cancellable? cancellable) throws Error, IOError {
|
||||
private async Buffer read_physical_buffer (uint64 pa, size_t size, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
return yield debugger.read_buffer (pa, size, cancellable);
|
||||
}
|
||||
|
||||
@@ -327,15 +329,15 @@ namespace Frida.Barebone {
|
||||
public uint n;
|
||||
public bool afe;
|
||||
|
||||
public static async MMUParameters load (Debugger debugger, Cancellable? cancellable) throws Error, IOError {
|
||||
public static async MMUParameters load (Debugger debugger, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
DebuggerException? exception = debugger.exception;
|
||||
if (exception == null)
|
||||
throw new Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
DebuggerThread thread = exception.thread;
|
||||
|
||||
uint64 ttbcr = yield thread.read_register ("ttbcr", cancellable);
|
||||
if ((ttbcr & TTBCR_EAE) != 0)
|
||||
throw new Error.NOT_SUPPORTED ("LPAE translation tables are not supported; please open a PR");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("LPAE translation tables are not supported; please open a PR");
|
||||
|
||||
uint64 sctlr;
|
||||
try {
|
||||
@@ -344,7 +346,7 @@ namespace Frida.Barebone {
|
||||
sctlr = yield thread.read_register ("sctlr_el1", cancellable);
|
||||
}
|
||||
if ((sctlr & SCTLR_M) == 0)
|
||||
throw new Error.NOT_SUPPORTED ("The MMU is off");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("The MMU is off");
|
||||
|
||||
var p = new MMUParameters ();
|
||||
p.n = (uint) (ttbcr & TTBCR_N_MASK);
|
||||
@@ -367,7 +369,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Allocation allocate_pages (Gee.List<uint64?> physical_addresses, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
MMUParameters p = yield MMUParameters.load (debugger, cancellable);
|
||||
|
||||
uint num_pages = physical_addresses.size;
|
||||
@@ -387,12 +389,12 @@ namespace Frida.Barebone {
|
||||
throw_if_failed (failure);
|
||||
|
||||
if (allocation == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to find a free run of page table entries");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to find a free run of page table entries");
|
||||
|
||||
return allocation;
|
||||
}
|
||||
|
||||
private async bool scan_for_run (Run run, MMUParameters p, Cancellable? cancellable) throws Error, IOError {
|
||||
private async bool scan_for_run (Run run, MMUParameters p, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint boundary = p.split_index;
|
||||
|
||||
if (boundary != L1_NUM_ENTRIES) {
|
||||
@@ -410,7 +412,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async bool scan_for_run_in_l1 (Run run, uint64 table_address, uint first_index, uint end_index,
|
||||
MMUParameters p, Cancellable? cancellable) throws Error, IOError {
|
||||
MMUParameters p, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Buffer l1 = yield read_physical_buffer (table_address + (first_index * L1_ENTRY_SIZE),
|
||||
(end_index - first_index) * L1_ENTRY_SIZE, cancellable);
|
||||
|
||||
@@ -441,7 +443,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async Allocation occupy (Run run, Gee.List<uint64?> physical_addresses, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var displaced = new Gee.ArrayList<DisplacedEntries> ();
|
||||
|
||||
uint next_page = 0;
|
||||
@@ -463,7 +465,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async void protect_pages (uint64 virtual_address, size_t size, Gum.PageProtection prot,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
MMUParameters p = yield MMUParameters.load (debugger, cancellable);
|
||||
|
||||
uint64 start_va = page_start (virtual_address, SMALL_PAGE_SIZE);
|
||||
@@ -517,9 +519,9 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async uint64 skip_section (uint64 page_va, uint64 end_va, uint32 l1_entry, Gum.PageProtection prot,
|
||||
MMUParameters p, Cancellable? cancellable) throws Error, IOError {
|
||||
MMUParameters p, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if ((l1_entry & L1_KIND_MASK) != L1_KIND_SECTION) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to change protection of the unmapped page at 0x%"
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to change protection of the unmapped page at 0x%"
|
||||
+ uint64.FORMAT_MODIFIER + "x", page_va);
|
||||
}
|
||||
|
||||
@@ -527,7 +529,7 @@ namespace Frida.Barebone {
|
||||
| (((l1_entry >> SECTION_APX_SHIFT) & 1) << AP_HIGH_SHIFT);
|
||||
Gum.PageProtection current = protection_from_ap (ap, (l1_entry & SECTION_XN) != 0, p.afe);
|
||||
if ((current & prot) != prot) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to change protection of the section mapping at 0x%"
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to change protection of the section mapping at 0x%"
|
||||
+ uint64.FORMAT_MODIFIER + "x", page_va);
|
||||
}
|
||||
|
||||
@@ -561,7 +563,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void write_physical_buffer (uint64 pa, Bytes data, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield debugger.write_byte_array (pa, data, cancellable);
|
||||
}
|
||||
|
||||
@@ -649,7 +651,7 @@ namespace Frida.Barebone {
|
||||
this.machine = machine;
|
||||
}
|
||||
|
||||
public async void deallocate (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void deallocate (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
bool was_running = yield enter_physical_addressing (machine.debugger, cancellable);
|
||||
GLib.Error? failure = null;
|
||||
try {
|
||||
@@ -664,11 +666,11 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Gee.List<uint64?> scan_ranges (Gee.List<Gum.MemoryRange?> ranges, MatchPattern pattern, uint max_matches,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Not yet supported");
|
||||
}
|
||||
|
||||
public void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Error {
|
||||
public void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Efni.Error {
|
||||
Gum.ElfArmRelocation type = (Gum.ElfArmRelocation) r.type;
|
||||
size_t offset = (size_t) r.address;
|
||||
switch (type) {
|
||||
@@ -682,14 +684,14 @@ namespace Frida.Barebone {
|
||||
relocated.write_int32 (offset, (int32) diff);
|
||||
break;
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported relocation type: %s",
|
||||
Marshal.enum_to_nick<Gum.ElfArmRelocation> (type));
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unsupported relocation type: %s",
|
||||
Efni.Marshal.enum_to_nick<Gum.ElfArmRelocation> (type));
|
||||
}
|
||||
}
|
||||
|
||||
public async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (args.length > NUM_ARGS_IN_REGS)
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported number of arguments; please open a PR");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unsupported number of arguments; please open a PR");
|
||||
|
||||
bool was_running = debugger.state != STOPPED;
|
||||
if (was_running)
|
||||
@@ -718,7 +720,7 @@ namespace Frida.Barebone {
|
||||
int64 deadline = get_monotonic_time () + LANDING_TIMEOUT_USEC;
|
||||
do {
|
||||
if (get_monotonic_time () >= deadline)
|
||||
throw new Error.TIMED_OUT ("Timed out waiting for the call to return");
|
||||
throw new Efni.Error.TIMED_OUT ("Timed out waiting for the call to return");
|
||||
|
||||
ex = yield debugger.continue_until_exception (cancellable);
|
||||
} while (ex.breakpoint != bp);
|
||||
@@ -741,7 +743,7 @@ namespace Frida.Barebone {
|
||||
return cpsr & ~CPSR_THUMB_BIT;
|
||||
}
|
||||
|
||||
public async CallFrame load_call_frame (DebuggerThread thread, uint arity, Cancellable? cancellable) throws Error, IOError {
|
||||
public async CallFrame load_call_frame (DebuggerThread thread, uint arity, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var regs = yield thread.read_registers (cancellable);
|
||||
|
||||
Buffer? stack = null;
|
||||
@@ -844,7 +846,7 @@ namespace Frida.Barebone {
|
||||
stack_state = MODIFIED;
|
||||
}
|
||||
|
||||
public async void commit (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void commit (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (regs.get_data<bool> ("dirty"))
|
||||
yield thread.write_registers (regs, cancellable);
|
||||
|
||||
@@ -876,7 +878,7 @@ namespace Frida.Barebone {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public async void destroy (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void destroy (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (state == DESTROYED)
|
||||
return;
|
||||
|
||||
@@ -892,16 +894,16 @@ namespace Frida.Barebone {
|
||||
yield debugger.resume (cancellable);
|
||||
}
|
||||
|
||||
public async void enable (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void enable (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (state == ENABLED)
|
||||
return;
|
||||
if (state != DISABLED)
|
||||
throw new Error.INVALID_OPERATION ("Invalid operation");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Invalid operation");
|
||||
yield debugger.write_byte_array (target, new_target_code, cancellable);
|
||||
state = ENABLED;
|
||||
}
|
||||
|
||||
public async void disable (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void disable (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (state != ENABLED)
|
||||
return;
|
||||
yield debugger.write_byte_array (target, old_target_code, cancellable);
|
||||
@@ -918,7 +920,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async InlineHook create_inline_hook (uint64 target, uint64 handler, Allocator allocator,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 target_address = address_from_funcptr (target);
|
||||
bool target_is_thumb = (target & THUMB_BIT) != 0;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class Arm64Machine : Object, Machine {
|
||||
@@ -74,7 +76,7 @@ namespace Frida.Barebone {
|
||||
Object (debugger: debugger);
|
||||
}
|
||||
|
||||
public async size_t query_page_size (Cancellable? cancellable) throws Error, IOError {
|
||||
public async size_t query_page_size (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if ("parallels" in debugger.features)
|
||||
return SMALLEST_GRANULE;
|
||||
|
||||
@@ -86,10 +88,10 @@ namespace Frida.Barebone {
|
||||
return p.granule;
|
||||
}
|
||||
|
||||
public async uint query_exception_level (Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint query_exception_level (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
DebuggerException? exception = debugger.exception;
|
||||
if (exception == null)
|
||||
throw new Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
DebuggerThread thread = exception.thread;
|
||||
|
||||
var cpsr = yield thread.read_register ("cpsr", cancellable);
|
||||
@@ -98,7 +100,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async void enumerate_ranges (Gum.PageProtection prot, FoundRangeFunc func, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
Gee.List<RangeDetails> ranges = ("corellium" in debugger.features)
|
||||
? yield collect_ranges_using_corellium (cancellable)
|
||||
: yield collect_ranges_using_mmu (cancellable);
|
||||
@@ -110,7 +112,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async Gee.List<RangeDetails> collect_ranges_using_mmu (Cancellable? cancellable) throws Error, IOError {
|
||||
private async Gee.List<RangeDetails> collect_ranges_using_mmu (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var result = new Gee.ArrayList<RangeDetails> ();
|
||||
|
||||
MMUParameters p = yield load_mmu_parameters (cancellable);
|
||||
@@ -129,7 +131,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void collect_ranges_in_table (uint64 table_address, uint level, uint64 upper_bits, MMUParameters p,
|
||||
Gee.List<RangeDetails> ranges, Cancellable? cancellable) throws Error, IOError {
|
||||
Gee.List<RangeDetails> ranges, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint max_entries = compute_max_entries (level, p);
|
||||
Buffer entries = yield read_physical_buffer (table_address, max_entries * Descriptor.SIZE, cancellable);
|
||||
uint shift = address_shift_at_level (level, p.granule);
|
||||
@@ -155,7 +157,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async Gee.List<RangeDetails> collect_ranges_using_corellium (Cancellable? cancellable) throws Error, IOError {
|
||||
private async Gee.List<RangeDetails> collect_ranges_using_corellium (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var result = new Gee.ArrayList<RangeDetails> ();
|
||||
|
||||
string pt = yield debugger.run_remote_command ("pt", cancellable);
|
||||
@@ -168,7 +170,7 @@ namespace Frida.Barebone {
|
||||
|
||||
string[] range_tokens = range.split ("-");
|
||||
if (range_tokens.length != 2)
|
||||
throw new Error.PROTOCOL ("Unexpected Corellium response; please file a bug");
|
||||
throw new Efni.Error.PROTOCOL ("Unexpected Corellium response; please file a bug");
|
||||
|
||||
uint64 base_va = uint64.parse (range_tokens[0], 16);
|
||||
uint64 end_va = uint64.parse (range_tokens[1], 16) + 1;
|
||||
@@ -184,7 +186,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Allocation allocate_pages (Gee.List<uint64?> physical_addresses, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
MMUParameters p = yield load_mmu_parameters (cancellable);
|
||||
|
||||
bool was_running = yield begin_physical_addressing (cancellable);
|
||||
@@ -200,14 +202,14 @@ namespace Frida.Barebone {
|
||||
throw_if_failed (failure);
|
||||
|
||||
if (allocation == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to insert page table mapping; please file a bug");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to insert page table mapping; please file a bug");
|
||||
|
||||
return allocation;
|
||||
}
|
||||
|
||||
private async Allocation? maybe_insert_descriptor_in_table (Gee.List<uint64?> physical_addresses,
|
||||
uint64 table_address, uint level, uint64 upper_bits, MMUParameters p, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint max_entries = compute_max_entries (level, p);
|
||||
uint shift = address_shift_at_level (level, p.granule);
|
||||
|
||||
@@ -286,7 +288,7 @@ namespace Frida.Barebone {
|
||||
// A kernel using permission indirection reads an index out of those bits instead, leaving
|
||||
// AP to say nothing at all, so both kinds of page are granted by sampling one the kernel
|
||||
// already made that way and replaying its exact attribute encoding.
|
||||
public async void learn_permission_templates (uint64 code_va, Cancellable? cancellable) throws Error, IOError {
|
||||
public async void learn_permission_templates (uint64 code_va, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
code_template_descriptor = yield read_level3_descriptor (code_va, cancellable);
|
||||
code_template_known = true;
|
||||
|
||||
@@ -295,7 +297,7 @@ namespace Frida.Barebone {
|
||||
data_template_known = true;
|
||||
}
|
||||
|
||||
public override async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Error, IOError {
|
||||
public override async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
MMUParameters p = yield load_mmu_parameters (cancellable);
|
||||
uint64 descriptor = yield read_level3_descriptor (va, cancellable);
|
||||
uint64 page_mask = (1ULL << inpage_bits_for_granule (p.granule)) - 1;
|
||||
@@ -303,7 +305,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public override async Bytes read_virtual (uint64 va, size_t size, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (physical_memory == null)
|
||||
return yield debugger.read_byte_array (va, size, cancellable);
|
||||
|
||||
@@ -314,7 +316,7 @@ namespace Frida.Barebone {
|
||||
uint64 cur_va = va + offset;
|
||||
uint64 pa = yield translate_address (cur_va, cancellable);
|
||||
if (!physical_memory.contains (pa))
|
||||
throw new Error.NOT_SUPPORTED ("Address 0x%" + uint64.FORMAT_MODIFIER + "x is a device, not memory", cur_va);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Address 0x%" + uint64.FORMAT_MODIFIER + "x is a device, not memory", cur_va);
|
||||
size_t chunk = size_t.min ((size_t) (p.granule - (cur_va & (p.granule - 1))), size - offset);
|
||||
taken.append (physical_memory.read (pa, chunk));
|
||||
offset += chunk;
|
||||
@@ -323,7 +325,7 @@ namespace Frida.Barebone {
|
||||
return ByteArray.free_to_bytes ((owned) taken);
|
||||
}
|
||||
|
||||
public override async void write_virtual (uint64 va, uint8[] data, Cancellable? cancellable) throws Error, IOError {
|
||||
public override async void write_virtual (uint64 va, uint8[] data, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (physical_memory == null) {
|
||||
yield debugger.write_byte_array (va, new Bytes (data), cancellable);
|
||||
return;
|
||||
@@ -340,13 +342,13 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async Buffer read_physical_buffer (uint64 pa, size_t size, Cancellable? cancellable) throws Error, IOError {
|
||||
private async Buffer read_physical_buffer (uint64 pa, size_t size, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (physical_memory != null && physical_memory.contains (pa))
|
||||
return debugger.make_buffer (new Bytes (physical_memory.read (pa, size)));
|
||||
return yield debugger.read_buffer (pa, size, cancellable);
|
||||
}
|
||||
|
||||
private async void write_physical_buffer (uint64 pa, Bytes data, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void write_physical_buffer (uint64 pa, Bytes data, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (physical_memory != null && physical_memory.contains (pa)) {
|
||||
physical_memory.write (pa, data.get_data ());
|
||||
return;
|
||||
@@ -354,13 +356,13 @@ namespace Frida.Barebone {
|
||||
yield debugger.write_byte_array (pa, data, cancellable);
|
||||
}
|
||||
|
||||
public async uint8[] read_physical (uint64 pa, size_t size, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint8[] read_physical (uint64 pa, size_t size, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (physical_memory != null && physical_memory.contains (pa))
|
||||
return physical_memory.read (pa, size);
|
||||
return yield read_physical_via_stub (pa, size, cancellable);
|
||||
}
|
||||
|
||||
public async void write_physical (uint64 pa, uint8[] data, Cancellable? cancellable) throws Error, IOError {
|
||||
public async void write_physical (uint64 pa, uint8[] data, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (physical_memory != null && physical_memory.contains (pa)) {
|
||||
physical_memory.write (pa, data);
|
||||
return;
|
||||
@@ -376,7 +378,7 @@ namespace Frida.Barebone {
|
||||
throw_if_failed (failure);
|
||||
}
|
||||
|
||||
public async uint8[] read_physical_via_stub (uint64 pa, size_t size, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint8[] read_physical_via_stub (uint64 pa, size_t size, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
bool was_running = yield enter_physical_addressing (debugger, cancellable);
|
||||
uint8[]? data = null;
|
||||
GLib.Error? failure = null;
|
||||
@@ -391,7 +393,7 @@ namespace Frida.Barebone {
|
||||
return data;
|
||||
}
|
||||
|
||||
public async uint64 read_level3_descriptor (uint64 va, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint64 read_level3_descriptor (uint64 va, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
MMUParameters p = yield load_mmu_parameters (cancellable);
|
||||
bool was_running = yield begin_physical_addressing (cancellable);
|
||||
uint64 descriptor = 0;
|
||||
@@ -416,12 +418,12 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public override async void protect_pages_leaving_the_flush (uint64 virtual_address, size_t size,
|
||||
Gum.PageProtection prot, Cancellable? cancellable) throws Error, IOError {
|
||||
Gum.PageProtection prot, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield write_page_protection (virtual_address, size, prot, cancellable);
|
||||
}
|
||||
|
||||
public async void protect_pages (uint64 virtual_address, size_t size, Gum.PageProtection prot, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (!mmu_registers_available) {
|
||||
yield set_pages_protection (virtual_address, size, prot, cancellable);
|
||||
return;
|
||||
@@ -436,7 +438,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void ensure_level3_coverage (uint64 virtual_address, size_t size, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (code_allocator == null)
|
||||
return;
|
||||
|
||||
@@ -469,7 +471,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async Gee.List<BlockMapping> locate_blocks (uint64 start_va, uint64 end_va, MMUParameters p,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var blocks = new Gee.ArrayList<BlockMapping> ();
|
||||
|
||||
bool was_running = yield begin_physical_addressing (cancellable);
|
||||
@@ -496,7 +498,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async BlockMapping? locate_block (uint64 va, MMUParameters p, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint64 table_pa = p.tt1;
|
||||
uint level = p.first_level;
|
||||
while (true) {
|
||||
@@ -522,7 +524,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void split_block (BlockMapping block, uint64 table_pa, MMUParameters p, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint sub_level = block.level + 1;
|
||||
uint64 sub_size = (sub_level == 3)
|
||||
? (uint64) 1 << inpage_bits_for_granule (p.granule)
|
||||
@@ -545,7 +547,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async uint64 resolve_physical_address (uint64 va, MMUParameters p, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint64 table_pa = p.tt1;
|
||||
uint level = p.first_level;
|
||||
while (true) {
|
||||
@@ -565,7 +567,7 @@ namespace Frida.Barebone {
|
||||
return (raw & INT48_MASK & ~(span - 1)) | (va & (span - 1));
|
||||
}
|
||||
if (desc.kind != TABLE)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to resolve physical address for 0x%" + uint64.FORMAT_MODIFIER + "x", va);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to resolve physical address for 0x%" + uint64.FORMAT_MODIFIER + "x", va);
|
||||
|
||||
table_pa = desc.target_address;
|
||||
level++;
|
||||
@@ -576,9 +578,9 @@ namespace Frida.Barebone {
|
||||
// clearing write before adding execute; the helpers take a page-aligned base and a page
|
||||
// count, and handle the TLB and cache maintenance themselves.
|
||||
private async void set_pages_protection (uint64 virtual_address, size_t size, Gum.PageProtection prot,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (set_memory_x == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Kernel set_memory_* helpers are unavailable");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Kernel set_memory_* helpers are unavailable");
|
||||
|
||||
uint64 aligned = virtual_address & ~(uint64) 0xfff;
|
||||
uint num_pages = (uint) (((virtual_address + size + 0xfff) & ~(uint64) 0xfff) - aligned) / 4096;
|
||||
@@ -594,7 +596,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void write_page_protection (uint64 virtual_address, size_t size, Gum.PageProtection prot,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
MMUParameters p = yield load_mmu_parameters (cancellable);
|
||||
|
||||
bool was_running = yield begin_physical_addressing (cancellable);
|
||||
@@ -613,7 +615,7 @@ namespace Frida.Barebone {
|
||||
throw_if_failed (failure);
|
||||
}
|
||||
|
||||
private async void flush_translations (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void flush_translations (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (flush_stub == null) {
|
||||
flush_stub = yield code_allocator.allocate (code_allocator.page_size, code_allocator.page_size,
|
||||
cancellable);
|
||||
@@ -631,7 +633,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void grant_execution (uint64 virtual_address, size_t size, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield ensure_level3_coverage (virtual_address, size, cancellable);
|
||||
|
||||
MMUParameters p = yield load_mmu_parameters (cancellable);
|
||||
@@ -654,7 +656,7 @@ namespace Frida.Barebone {
|
||||
|
||||
// The bridge reads and writes guest physical memory directly, so the stub's
|
||||
// physical-addressing mode, which requires a halted target, is unnecessary.
|
||||
private async bool begin_physical_addressing (Cancellable? cancellable) throws Error, IOError {
|
||||
private async bool begin_physical_addressing (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (physical_memory != null)
|
||||
return false;
|
||||
|
||||
@@ -662,21 +664,21 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void end_physical_addressing (bool was_running, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (physical_memory != null)
|
||||
return;
|
||||
|
||||
yield leave_physical_addressing (debugger, was_running, cancellable);
|
||||
}
|
||||
|
||||
private async MMUParameters load_mmu_parameters (Cancellable? cancellable) throws Error, IOError {
|
||||
private async MMUParameters load_mmu_parameters (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (cached_mmu_parameters == null)
|
||||
cached_mmu_parameters = yield MMUParameters.load (debugger, cancellable);
|
||||
return cached_mmu_parameters;
|
||||
}
|
||||
|
||||
private async void perform_protect_pages (uint64 start_va, uint num_pages, Gum.PageProtection prot, MMUParameters p,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var table_cache = new TableWalkCache ();
|
||||
uint pages_processed = 0;
|
||||
|
||||
@@ -708,7 +710,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void protect_pages_in_table (uint64 current_va, uint batch_size, Gum.PageProtection prot,
|
||||
uint64 table_pa, MMUParameters p, Cancellable? cancellable) throws Error, IOError {
|
||||
uint64 table_pa, MMUParameters p, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint index_bits = num_address_bits_at_level (3, p);
|
||||
uint index_shift = inpage_bits_for_granule (p.granule);
|
||||
uint index_mask = (1U << index_bits) - 1;
|
||||
@@ -734,7 +736,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async uint64 find_level3_table (uint64 va, MMUParameters p, TableWalkCache cache, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
TableWalkResult? cached = cache.lookup (va, p);
|
||||
if (cached != null)
|
||||
return yield walk_to_level3 (va, cached.table_pa, cached.level, p, cache, cancellable);
|
||||
@@ -743,7 +745,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async uint64 walk_to_level3 (uint64 va, uint64 start_table_pa, uint start_level, MMUParameters p,
|
||||
TableWalkCache cache, Cancellable? cancellable) throws Error, IOError {
|
||||
TableWalkCache cache, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 table_pa = start_table_pa;
|
||||
uint level = start_level;
|
||||
|
||||
@@ -760,7 +762,7 @@ namespace Frida.Barebone {
|
||||
Descriptor desc = Descriptor.parse (raw, level, p.granule);
|
||||
|
||||
if (desc.kind != TABLE)
|
||||
throw new Error.NOT_SUPPORTED ("Walk failed at level %u", level);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Walk failed at level %u", level);
|
||||
|
||||
table_pa = desc.target_address;
|
||||
level++;
|
||||
@@ -770,7 +772,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void clear_pxn_table_bits_for_va (uint64 va, MMUParameters p, TableWalkCache cache, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint64 table_pa = p.tt1;
|
||||
for (uint level = p.first_level; level != 3; level++) {
|
||||
uint entries = compute_max_entries (level, p);
|
||||
@@ -847,7 +849,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Gee.List<uint64?> scan_ranges (Gee.List<Gum.MemoryRange?> ranges, MatchPattern pattern, uint max_matches,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
unowned uint8[] scanner_blob = Data.Barebone.get_memory_scanner_arm64_elf_blob ().data;
|
||||
|
||||
var raw_scanner = new Bytes.static (scanner_blob);
|
||||
@@ -856,7 +858,7 @@ namespace Frida.Barebone {
|
||||
try {
|
||||
scanner = new Gum.ElfModule.from_blob (raw_scanner);
|
||||
} catch (Gum.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
|
||||
size_t vm_size = (size_t) scanner.mapped_size;
|
||||
@@ -895,7 +897,7 @@ namespace Frida.Barebone {
|
||||
|
||||
RangeDetails? stack_range = writable_ranges.first_match (r => r.contains_virtual_address (old_sp));
|
||||
if (stack_range == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to find stack memory range");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to find stack memory range");
|
||||
|
||||
uint64 module_pa;
|
||||
size_t logical_page_size = 4096;
|
||||
@@ -950,7 +952,7 @@ namespace Frida.Barebone {
|
||||
DebuggerBreakpoint bp = yield debugger.add_breakpoint (SOFT, landing_zone_va, 4, cancellable);
|
||||
DebuggerException ex = yield debugger.continue_until_exception (cancellable);
|
||||
if (ex.breakpoint != bp)
|
||||
throw new Error.NOT_SUPPORTED ("Breakpoint did not trigger; please file a bug");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Breakpoint did not trigger; please file a bug");
|
||||
yield bp.remove (cancellable);
|
||||
|
||||
var num_matches = (uint) yield thread.read_register ("x0", cancellable);
|
||||
@@ -972,7 +974,7 @@ namespace Frida.Barebone {
|
||||
return matches;
|
||||
}
|
||||
|
||||
public void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Error {
|
||||
public void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Efni.Error {
|
||||
Gum.ElfArm64Relocation type = (Gum.ElfArm64Relocation) r.type;
|
||||
switch (type) {
|
||||
case ABS64:
|
||||
@@ -986,14 +988,14 @@ namespace Frida.Barebone {
|
||||
relocated.write_int32 ((size_t) r.address, (int32) diff);
|
||||
break;
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported relocation type: %s",
|
||||
Marshal.enum_to_nick<Gum.ElfArm64Relocation> (type));
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unsupported relocation type: %s",
|
||||
Efni.Marshal.enum_to_nick<Gum.ElfArm64Relocation> (type));
|
||||
}
|
||||
}
|
||||
|
||||
public async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (args.length > 8)
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported number of arguments; please open a PR");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unsupported number of arguments; please open a PR");
|
||||
|
||||
bool was_running = debugger.state != STOPPED;
|
||||
if (was_running)
|
||||
@@ -1044,7 +1046,7 @@ namespace Frida.Barebone {
|
||||
return retval;
|
||||
}
|
||||
|
||||
public async void execute (uint64 start, uint64 end, Cancellable? cancellable) throws Error, IOError {
|
||||
public async void execute (uint64 start, uint64 end, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
bool was_running = debugger.state != STOPPED;
|
||||
if (was_running)
|
||||
yield debugger.stop (cancellable);
|
||||
@@ -1071,13 +1073,13 @@ namespace Frida.Barebone {
|
||||
// reschedule onto another core, and the stub identifies threads by core; nothing else reaches
|
||||
// these addresses (a cold landing zone, or the scheduler with a predicate), so whichever
|
||||
// thread stops there is ours.
|
||||
public async DebuggerThread run_until_pc (uint64 address, Cancellable? cancellable) throws Error, IOError {
|
||||
public async DebuggerThread run_until_pc (uint64 address, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
DebuggerBreakpoint bp = yield debugger.add_breakpoint (SOFT, address, 4, cancellable);
|
||||
DebuggerException ex = null;
|
||||
int64 deadline = get_monotonic_time () + LANDING_TIMEOUT_USEC;
|
||||
do {
|
||||
if (get_monotonic_time () >= deadline)
|
||||
throw new Error.TIMED_OUT ("Timed out waiting for the call to return");
|
||||
throw new Efni.Error.TIMED_OUT ("Timed out waiting for the call to return");
|
||||
|
||||
ex = yield debugger.continue_until_exception (cancellable);
|
||||
} while (ex.breakpoint != bp);
|
||||
@@ -1089,7 +1091,7 @@ namespace Frida.Barebone {
|
||||
// points at that core's per-processor data. Handing it the register of the core we began
|
||||
// on would make every later access reach the wrong processor.
|
||||
private async void restore_registers (DebuggerThread origin, DebuggerThread landed,
|
||||
Gee.Map<string, Variant> saved, Cancellable? cancellable) throws Error, IOError {
|
||||
Gee.Map<string, Variant> saved, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var restored = saved;
|
||||
if (landed.id != origin.id) {
|
||||
restored = new Gee.HashMap<string, Variant> ();
|
||||
@@ -1100,7 +1102,7 @@ namespace Frida.Barebone {
|
||||
yield landed.write_registers (restored, cancellable);
|
||||
}
|
||||
|
||||
public async CallFrame load_call_frame (DebuggerThread thread, uint arity, Cancellable? cancellable) throws Error, IOError {
|
||||
public async CallFrame load_call_frame (DebuggerThread thread, uint arity, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var regs = yield thread.read_registers (cancellable);
|
||||
|
||||
Buffer? stack = null;
|
||||
@@ -1203,7 +1205,7 @@ namespace Frida.Barebone {
|
||||
stack_state = MODIFIED;
|
||||
}
|
||||
|
||||
public async void commit (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void commit (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (regs.get_data<bool> ("dirty"))
|
||||
yield thread.write_registers (regs, cancellable);
|
||||
|
||||
@@ -1221,7 +1223,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async InlineHook create_inline_hook (uint64 target, uint64 handler, Allocator allocator, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
size_t page_size = allocator.page_size;
|
||||
var allocation = yield allocator.allocate (page_size, page_size, cancellable);
|
||||
uint64 code_va = allocation.virtual_address;
|
||||
@@ -1335,7 +1337,7 @@ namespace Frida.Barebone {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public async void destroy (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void destroy (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (state == DESTROYED)
|
||||
return;
|
||||
|
||||
@@ -1352,16 +1354,16 @@ namespace Frida.Barebone {
|
||||
yield debugger.resume (cancellable);
|
||||
}
|
||||
|
||||
public async void enable (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void enable (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (state == ENABLED)
|
||||
return;
|
||||
if (state != DISABLED)
|
||||
throw new Error.INVALID_OPERATION ("Invalid operation");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Invalid operation");
|
||||
yield debugger.write_byte_array (target, new_target_code, cancellable);
|
||||
state = ENABLED;
|
||||
}
|
||||
|
||||
public async void disable (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void disable (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (state != ENABLED)
|
||||
return;
|
||||
yield debugger.write_byte_array (target, old_target_code, cancellable);
|
||||
@@ -1379,10 +1381,10 @@ namespace Frida.Barebone {
|
||||
public bool sprr_enabled;
|
||||
public uint64 sprr_perm;
|
||||
|
||||
public static async MMUParameters load (Debugger debugger, Cancellable? cancellable) throws Error, IOError {
|
||||
public static async MMUParameters load (Debugger debugger, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
DebuggerException? exception = debugger.exception;
|
||||
if (exception == null)
|
||||
throw new Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
DebuggerThread thread = exception.thread;
|
||||
|
||||
MMURegisters regs = yield MMURegisters.read (thread, cancellable);
|
||||
@@ -1422,7 +1424,7 @@ namespace Frida.Barebone {
|
||||
public uint64 sprr_config;
|
||||
public uint64 sprr_perm;
|
||||
|
||||
public static async MMURegisters read (DebuggerThread thread, Cancellable? cancellable) throws Error, IOError {
|
||||
public static async MMURegisters read (DebuggerThread thread, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var regs = new MMURegisters ();
|
||||
|
||||
Debugger client = thread.debugger;
|
||||
@@ -1447,14 +1449,14 @@ namespace Frida.Barebone {
|
||||
// The stub serves no system register, and the monitor command that would
|
||||
// print them aborts the VM whenever another processor is somewhere it
|
||||
// cannot be read from, so leave the caller to manage without them.
|
||||
throw new Error.NOT_SUPPORTED ("Parallels serves no system register");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Parallels serves no system register");
|
||||
} else {
|
||||
regs.tcr = yield thread.read_register ("tcr_el1", cancellable);
|
||||
regs.ttbr1 = yield thread.read_register ("ttbr1_el1", cancellable);
|
||||
try {
|
||||
regs.sprr_config = yield thread.read_register ("sprr_config_el1", cancellable);
|
||||
regs.sprr_perm = yield thread.read_register ("sprr_el1br1_el1", cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1587,9 +1589,9 @@ namespace Frida.Barebone {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public async void deallocate (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void deallocate (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (old_descriptors == null)
|
||||
throw new Error.INVALID_OPERATION ("Already deallocated");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Already deallocated");
|
||||
Bytes d = old_descriptors;
|
||||
old_descriptors = null;
|
||||
bool was_running = yield enter_physical_addressing (debugger, cancellable);
|
||||
@@ -1610,7 +1612,7 @@ namespace Frida.Barebone {
|
||||
64K = 65536
|
||||
}
|
||||
|
||||
private static Granule granule_from_tg1 (uint tg1) throws Error {
|
||||
private static Granule granule_from_tg1 (uint tg1) throws Efni.Error {
|
||||
switch (tg1) {
|
||||
case 1:
|
||||
return 16K;
|
||||
@@ -1619,7 +1621,7 @@ namespace Frida.Barebone {
|
||||
case 3:
|
||||
return 64K;
|
||||
default:
|
||||
throw new Error.PROTOCOL ("Invalid TG1 value");
|
||||
throw new Efni.Error.PROTOCOL ("Invalid TG1 value");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1733,7 +1735,7 @@ namespace Frida.Barebone {
|
||||
return prot;
|
||||
}
|
||||
|
||||
private uint64 apply_protection_bits (uint64 descriptor, Gum.PageProtection prot, MMUParameters p) throws Error {
|
||||
private uint64 apply_protection_bits (uint64 descriptor, Gum.PageProtection prot, MMUParameters p) throws Efni.Error {
|
||||
bool want_exec = (prot & Gum.PageProtection.EXECUTE) != 0;
|
||||
if (want_exec && code_template_known)
|
||||
return apply_code_template (descriptor, p);
|
||||
@@ -1786,7 +1788,7 @@ namespace Frida.Barebone {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static uint find_sprr_index_for_permissions (Gum.PageProtection prot, MMUParameters p) throws Error {
|
||||
private static uint find_sprr_index_for_permissions (Gum.PageProtection prot, MMUParameters p) throws Efni.Error {
|
||||
bool want_read = (prot & Gum.PageProtection.READ) != 0;
|
||||
bool want_write = (prot & Gum.PageProtection.WRITE) != 0;
|
||||
bool want_exec = (prot & Gum.PageProtection.EXECUTE) != 0;
|
||||
@@ -1819,7 +1821,7 @@ namespace Frida.Barebone {
|
||||
return i;
|
||||
}
|
||||
|
||||
throw new Error.INVALID_ARGUMENT ("No suitable SPRR slot found for protection 0x%x", prot);
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("No suitable SPRR slot found for protection 0x%x", prot);
|
||||
}
|
||||
|
||||
private static uint64 apply_sprr_index_to_descriptor (uint64 descriptor, uint sprr_index) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class UnknownMachine : Object, Machine {
|
||||
@@ -18,43 +20,43 @@ namespace Frida.Barebone {
|
||||
Object (debugger: debugger);
|
||||
}
|
||||
|
||||
public async size_t query_page_size (Cancellable? cancellable) throws Error, IOError {
|
||||
public async size_t query_page_size (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
return 4096;
|
||||
}
|
||||
|
||||
public async uint query_exception_level (Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
public async uint query_exception_level (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Not yet supported");
|
||||
}
|
||||
|
||||
public async void enumerate_ranges (Gum.PageProtection prot, FoundRangeFunc func, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
}
|
||||
|
||||
public async Allocation allocate_pages (Gee.List<uint64?> physical_addresses, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Not yet supported");
|
||||
}
|
||||
|
||||
public async void protect_pages (uint64 virtual_address, size_t size, Gum.PageProtection prot,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Not yet supported");
|
||||
}
|
||||
|
||||
public async Gee.List<uint64?> scan_ranges (Gee.List<Gum.MemoryRange?> ranges, MatchPattern pattern,
|
||||
uint max_matches, Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
uint max_matches, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Not yet supported");
|
||||
}
|
||||
|
||||
public void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Error {
|
||||
throw_not_supported ();
|
||||
public void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Efni.Error {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Not yet supported");
|
||||
}
|
||||
|
||||
public async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
public async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Not yet supported");
|
||||
}
|
||||
|
||||
public async CallFrame load_call_frame (DebuggerThread thread, uint arity, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var regs = yield thread.read_registers (cancellable);
|
||||
|
||||
return new UnknownCallFrame (thread, regs);
|
||||
@@ -101,7 +103,7 @@ namespace Frida.Barebone {
|
||||
public void force_return () {
|
||||
}
|
||||
|
||||
public async void commit (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void commit (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (regs.get_data<bool> ("dirty"))
|
||||
yield thread.write_registers (regs, cancellable);
|
||||
}
|
||||
@@ -116,8 +118,8 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async InlineHook create_inline_hook (uint64 target, uint64 handler, Allocator allocator, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Not yet supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class IA32Machine : Object, Machine {
|
||||
@@ -31,7 +33,7 @@ namespace Frida.Barebone {
|
||||
page_tables = new X86PageTables (debugger);
|
||||
}
|
||||
|
||||
public async size_t query_page_size (Cancellable? cancellable) throws Error, IOError {
|
||||
public async size_t query_page_size (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
return 4096;
|
||||
}
|
||||
|
||||
@@ -43,10 +45,10 @@ namespace Frida.Barebone {
|
||||
private const uint64 USER_PRIVILEGE = 3;
|
||||
private const int64 LANDING_TIMEOUT_USEC = 60000000;
|
||||
|
||||
public async uint query_exception_level (Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint query_exception_level (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
DebuggerException? exception = debugger.exception;
|
||||
if (exception == null)
|
||||
throw new Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
DebuggerThread thread = exception.thread;
|
||||
|
||||
var cs = yield thread.read_register ("cs", cancellable);
|
||||
@@ -55,7 +57,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async void enumerate_ranges (Gum.PageProtection prot, FoundRangeFunc func, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
foreach (RangeDetails r in coalesce_ranges (yield page_tables.collect_ranges (cancellable))) {
|
||||
if ((r.protection & prot) != prot)
|
||||
continue;
|
||||
@@ -65,19 +67,19 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Allocation allocate_pages (Gee.List<uint64?> physical_addresses, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
return yield page_tables.map (physical_addresses, cancellable);
|
||||
}
|
||||
|
||||
public async void protect_pages (uint64 virtual_address, size_t size, Gum.PageProtection prot,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield page_tables.protect (virtual_address, size, prot, cancellable);
|
||||
}
|
||||
|
||||
// Read the memory and do the match here. A script asks about small ranges, thus a scanner in
|
||||
// the guest would cost more round trips than it saves.
|
||||
public async Gee.List<uint64?> scan_ranges (Gee.List<Gum.MemoryRange?> ranges, MatchPattern pattern, uint max_matches,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var matches = new Gee.ArrayList<uint64?> ();
|
||||
|
||||
foreach (Gum.MemoryRange r in ranges) {
|
||||
@@ -90,7 +92,7 @@ namespace Frida.Barebone {
|
||||
Bytes chunk;
|
||||
try {
|
||||
chunk = yield debugger.read_byte_array (cursor, chunk_size, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
cursor += chunk_size;
|
||||
continue;
|
||||
}
|
||||
@@ -113,7 +115,7 @@ namespace Frida.Barebone {
|
||||
// keep the overlap at the boundaries small.
|
||||
private const size_t SCAN_CHUNK_SIZE = 256 * 1024;
|
||||
|
||||
public void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Error {
|
||||
public void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Efni.Error {
|
||||
Gum.ElfIA32Relocation type = (Gum.ElfIA32Relocation) r.type;
|
||||
switch (type) {
|
||||
case NONE:
|
||||
@@ -132,12 +134,12 @@ namespace Frida.Barebone {
|
||||
// Both ends of the displacement move by the same amount.
|
||||
break;
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported relocation type: %s",
|
||||
Marshal.enum_to_nick<Gum.ElfIA32Relocation> (type));
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unsupported relocation type: %s",
|
||||
Efni.Marshal.enum_to_nick<Gum.ElfIA32Relocation> (type));
|
||||
}
|
||||
}
|
||||
|
||||
public async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
bool was_running = debugger.state != STOPPED;
|
||||
if (was_running)
|
||||
yield debugger.stop (cancellable);
|
||||
@@ -178,7 +180,7 @@ namespace Frida.Barebone {
|
||||
int64 deadline = get_monotonic_time () + LANDING_TIMEOUT_USEC;
|
||||
while (true) {
|
||||
if (get_monotonic_time () >= deadline)
|
||||
throw new Error.TIMED_OUT ("Timed out waiting for the call to return");
|
||||
throw new Efni.Error.TIMED_OUT ("Timed out waiting for the call to return");
|
||||
|
||||
ex = yield debugger.continue_until_exception (cancellable);
|
||||
if (ex.breakpoint != bp)
|
||||
@@ -200,7 +202,7 @@ namespace Frida.Barebone {
|
||||
return retval;
|
||||
}
|
||||
|
||||
public async CallFrame load_call_frame (DebuggerThread thread, uint arity, Cancellable? cancellable) throws Error, IOError {
|
||||
public async CallFrame load_call_frame (DebuggerThread thread, uint arity, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var regs = yield thread.read_registers (cancellable);
|
||||
|
||||
uint64 original_esp = regs["esp"].get_uint64 ();
|
||||
@@ -289,7 +291,7 @@ namespace Frida.Barebone {
|
||||
stack_state = MODIFIED;
|
||||
}
|
||||
|
||||
public async void commit (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void commit (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (regs.get_data<bool> ("dirty"))
|
||||
yield thread.write_registers (regs, cancellable);
|
||||
|
||||
@@ -307,11 +309,11 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async InlineHook create_inline_hook (uint64 target, uint64 handler, Allocator allocator, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
return yield X86InlineHook.create (target, handler, IA32, allocator, debugger, cancellable);
|
||||
}
|
||||
|
||||
public override async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Error, IOError {
|
||||
public override async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
return yield page_tables.translate (va, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public enum CallingConvention {
|
||||
@@ -71,17 +73,17 @@ namespace Frida.Barebone {
|
||||
page_tables = new X86PageTables (debugger);
|
||||
}
|
||||
|
||||
public async size_t query_page_size (Cancellable? cancellable) throws Error, IOError {
|
||||
public async size_t query_page_size (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
return 4096;
|
||||
}
|
||||
|
||||
private const uint64 CODE_SELECTOR_PRIVILEGE = 3;
|
||||
private const uint64 USER_PRIVILEGE = 3;
|
||||
|
||||
public async uint query_exception_level (Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint query_exception_level (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
DebuggerException? exception = debugger.exception;
|
||||
if (exception == null)
|
||||
throw new Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
DebuggerThread thread = exception.thread;
|
||||
|
||||
var cs = yield thread.read_register ("cs", cancellable);
|
||||
@@ -90,7 +92,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async void enumerate_ranges (Gum.PageProtection prot, FoundRangeFunc func, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
foreach (RangeDetails r in coalesce_ranges (yield page_tables.collect_ranges (cancellable))) {
|
||||
if ((r.protection & prot) != prot)
|
||||
continue;
|
||||
@@ -100,12 +102,12 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Allocation allocate_pages (Gee.List<uint64?> physical_addresses, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
return yield page_tables.map (physical_addresses, cancellable);
|
||||
}
|
||||
|
||||
public async void protect_pages (uint64 virtual_address, size_t size, Gum.PageProtection prot,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (set_memory_x != 0) {
|
||||
yield set_pages_protection (virtual_address, size, prot, cancellable);
|
||||
return;
|
||||
@@ -115,7 +117,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void set_pages_protection (uint64 virtual_address, size_t size, Gum.PageProtection prot,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 aligned = virtual_address & ~(uint64) 0xfff;
|
||||
uint num_pages = (uint) (((virtual_address + size + 0xfff) & ~(uint64) 0xfff) - aligned) / 4096;
|
||||
|
||||
@@ -130,11 +132,11 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Gee.List<uint64?> scan_ranges (Gee.List<Gum.MemoryRange?> ranges, MatchPattern pattern, uint max_matches,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Not yet supported");
|
||||
}
|
||||
|
||||
public void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Error {
|
||||
public void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Efni.Error {
|
||||
Gum.ElfX64Relocation type = (Gum.ElfX64Relocation) r.type;
|
||||
switch (type) {
|
||||
case NONE:
|
||||
@@ -157,8 +159,8 @@ namespace Frida.Barebone {
|
||||
// Both ends of the displacement move by the same amount.
|
||||
break;
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported relocation type: %s",
|
||||
Marshal.enum_to_nick<Gum.ElfX64Relocation> (type));
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unsupported relocation type: %s",
|
||||
Efni.Marshal.enum_to_nick<Gum.ElfX64Relocation> (type));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,9 +169,9 @@ namespace Frida.Barebone {
|
||||
return true;
|
||||
}
|
||||
|
||||
public async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (args.length > arg_reg_names.length)
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported number of arguments; please open a PR");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unsupported number of arguments; please open a PR");
|
||||
|
||||
bool was_running = debugger.state != STOPPED;
|
||||
if (was_running)
|
||||
@@ -206,7 +208,7 @@ namespace Frida.Barebone {
|
||||
int64 deadline = get_monotonic_time () + LANDING_TIMEOUT_USEC;
|
||||
while (true) {
|
||||
if (get_monotonic_time () >= deadline)
|
||||
throw new Error.TIMED_OUT ("Timed out waiting for the call to return");
|
||||
throw new Efni.Error.TIMED_OUT ("Timed out waiting for the call to return");
|
||||
|
||||
ex = yield debugger.continue_until_exception (cancellable);
|
||||
if (ex.breakpoint != bp)
|
||||
@@ -227,7 +229,7 @@ namespace Frida.Barebone {
|
||||
return retval;
|
||||
}
|
||||
|
||||
public async CallFrame load_call_frame (DebuggerThread thread, uint arity, Cancellable? cancellable) throws Error, IOError {
|
||||
public async CallFrame load_call_frame (DebuggerThread thread, uint arity, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var regs = yield thread.read_registers (cancellable);
|
||||
|
||||
uint64 original_rsp = regs["rsp"].get_uint64 ();
|
||||
@@ -353,7 +355,7 @@ namespace Frida.Barebone {
|
||||
stack_state = MODIFIED;
|
||||
}
|
||||
|
||||
public async void commit (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void commit (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (regs.get_data<bool> ("dirty"))
|
||||
yield thread.write_registers (regs, cancellable);
|
||||
|
||||
@@ -371,11 +373,11 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async InlineHook create_inline_hook (uint64 target, uint64 handler, Allocator allocator, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
return yield X86InlineHook.create (target, handler, AMD64, allocator, debugger, cancellable);
|
||||
}
|
||||
|
||||
public override async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Error, IOError {
|
||||
public override async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
return yield page_tables.translate (va, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
public sealed class BareboneHostSessionBackend : Object, HostSessionBackend {
|
||||
private BareboneHostSessionProvider? provider;
|
||||
@@ -62,128 +64,132 @@ namespace Frida {
|
||||
|
||||
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, owned ConnectingFunc connecting,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != null)
|
||||
throw new Error.INVALID_OPERATION ("Already created");
|
||||
try {
|
||||
if (host_session != null)
|
||||
throw new Error.INVALID_OPERATION ("Already created");
|
||||
|
||||
BareboneConfig? config = null;
|
||||
if (options != null) {
|
||||
Value? val = options.map["config"];
|
||||
if (val != null) {
|
||||
config = (BareboneConfig) val.get_object ();
|
||||
config.check ();
|
||||
BareboneConfig? config = null;
|
||||
if (options != null) {
|
||||
Value? val = options.map["config"];
|
||||
if (val != null) {
|
||||
config = (BareboneConfig) val.get_object ();
|
||||
config.check ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unowned string? config_path = Environment.get_variable ("FRIDA_BAREBONE_CONFIG");
|
||||
if (config == null && config_path != null) {
|
||||
unowned string? config_path = Environment.get_variable ("FRIDA_BAREBONE_CONFIG");
|
||||
if (config == null && config_path != null) {
|
||||
try {
|
||||
var config_data = yield FS.read_all_text (File.new_for_path (config_path), cancellable);
|
||||
var cfg = (BareboneConfig) Json.gobject_from_data (typeof (BareboneConfig), config_data);
|
||||
cfg.check ();
|
||||
config = cfg;
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to load %s: %s", config_path, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (config == null)
|
||||
config = new BareboneConfig ();
|
||||
|
||||
var resident_agent = config.agent as BareboneResidentAgentConfig;
|
||||
if (resident_agent != null) {
|
||||
host_session = yield attach_to_resident_agent (resident_agent.transport, cancellable);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
}
|
||||
|
||||
SocketConnectable connectable;
|
||||
try {
|
||||
var config_data = yield FS.read_all_text (File.new_for_path (config_path), cancellable);
|
||||
var cfg = (BareboneConfig) Json.gobject_from_data (typeof (BareboneConfig), config_data);
|
||||
cfg.check ();
|
||||
config = cfg;
|
||||
BareboneConnectionConfig c = config.connection;
|
||||
connectable = NetworkAddress.parse (c.host, c.port);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to load %s: %s", config_path, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (config == null)
|
||||
config = new BareboneConfig ();
|
||||
connecting ("Connecting to the debugger", 0.0);
|
||||
|
||||
var resident_agent = config.agent as BareboneResidentAgentConfig;
|
||||
if (resident_agent != null) {
|
||||
host_session = yield attach_to_resident_agent (resident_agent.transport, cancellable);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
if (config.connection.flavor == BareboneStubFlavor.VIRTUALBOX_CONSOLE) {
|
||||
var console_debugger = yield Barebone.VirtualBoxDebugger.open (config.connection.host,
|
||||
config.connection.port, cancellable);
|
||||
host_session = yield establish (config, console_debugger, (owned) connecting, cancellable);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
}
|
||||
|
||||
SocketConnectable connectable;
|
||||
try {
|
||||
BareboneConnectionConfig c = config.connection;
|
||||
connectable = NetworkAddress.parse (c.host, c.port);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to load %s: %s", config_path, e.message);
|
||||
}
|
||||
|
||||
connecting ("Connecting to the debugger", 0.0);
|
||||
|
||||
if (config.connection.flavor == BareboneStubFlavor.VIRTUALBOX_CONSOLE) {
|
||||
var console_debugger = yield Barebone.VirtualBoxDebugger.open (config.connection.host,
|
||||
config.connection.port, cancellable);
|
||||
host_session = yield establish (config, console_debugger, (owned) connecting, cancellable);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
}
|
||||
|
||||
IOStream stream;
|
||||
try {
|
||||
var client = new SocketClient ();
|
||||
var connection = yield client.connect_async (connectable, cancellable);
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
stream = connection;
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("The specified GDB remote stub cannot be reached: %s", e.message);
|
||||
}
|
||||
|
||||
#if WINDOWS || MACOS || (LINUX && !ANDROID)
|
||||
if (config.connection.flavor == BareboneStubFlavor.ANDROID_EMULATOR) {
|
||||
connecting ("Instrumenting the emulator", 0.05);
|
||||
emulator_instrumentation = yield EmulatorInstrumentation.apply (hub, config, cancellable);
|
||||
}
|
||||
#endif
|
||||
|
||||
Debugger debugger;
|
||||
switch (config.connection.flavor) {
|
||||
case BareboneStubFlavor.VZ:
|
||||
debugger = yield Barebone.VzStubClient.open (stream, cancellable);
|
||||
break;
|
||||
case BareboneStubFlavor.PARALLELS:
|
||||
debugger = yield Barebone.ParallelsStubClient.open (stream, cancellable);
|
||||
break;
|
||||
case BareboneStubFlavor.VIRTUALBOX:
|
||||
debugger = yield Barebone.VirtualBoxStubClient.open (stream, cancellable);
|
||||
break;
|
||||
case BareboneStubFlavor.ANDROID_EMULATOR:
|
||||
debugger = yield Barebone.AndroidEmulatorStubClient.open (stream, cancellable);
|
||||
break;
|
||||
default:
|
||||
debugger = yield GDB.Client.open (stream, cancellable);
|
||||
break;
|
||||
}
|
||||
|
||||
#if WINDOWS
|
||||
if (emulator_instrumentation != null) {
|
||||
var client = debugger as GDB.Client;
|
||||
if (client != null)
|
||||
emulator_instrumentation.adopt_breakpoints (client);
|
||||
}
|
||||
#endif
|
||||
|
||||
try {
|
||||
host_session = yield establish (config, debugger, (owned) connecting, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (debugger.state == STOPPED) {
|
||||
try {
|
||||
yield debugger.resume (cancellable);
|
||||
} catch (GLib.Error ee) {
|
||||
}
|
||||
return host_session;
|
||||
}
|
||||
|
||||
IOStream stream;
|
||||
try {
|
||||
yield debugger.close (cancellable);
|
||||
} catch (IOError ee) {
|
||||
var client = new SocketClient ();
|
||||
var connection = yield client.connect_async (connectable, cancellable);
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
stream = connection;
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("The specified GDB remote stub cannot be reached: %s", e.message);
|
||||
}
|
||||
|
||||
#if WINDOWS || MACOS || (LINUX && !ANDROID)
|
||||
if (config.connection.flavor == BareboneStubFlavor.ANDROID_EMULATOR) {
|
||||
connecting ("Instrumenting the emulator", 0.05);
|
||||
emulator_instrumentation = yield EmulatorInstrumentation.apply (hub, config, cancellable);
|
||||
}
|
||||
#endif
|
||||
|
||||
Debugger debugger;
|
||||
switch (config.connection.flavor) {
|
||||
case BareboneStubFlavor.VZ:
|
||||
debugger = yield Barebone.VzStubClient.open (stream, cancellable);
|
||||
break;
|
||||
case BareboneStubFlavor.PARALLELS:
|
||||
debugger = yield Barebone.ParallelsStubClient.open (stream, cancellable);
|
||||
break;
|
||||
case BareboneStubFlavor.VIRTUALBOX:
|
||||
debugger = yield Barebone.VirtualBoxStubClient.open (stream, cancellable);
|
||||
break;
|
||||
case BareboneStubFlavor.ANDROID_EMULATOR:
|
||||
debugger = yield Barebone.AndroidEmulatorStubClient.open (stream, cancellable);
|
||||
break;
|
||||
default:
|
||||
debugger = yield GDB.Client.open (stream, cancellable);
|
||||
break;
|
||||
}
|
||||
|
||||
#if WINDOWS
|
||||
if (emulator_instrumentation != null) {
|
||||
var client = debugger as GDB.Client;
|
||||
if (client != null)
|
||||
emulator_instrumentation.adopt_breakpoints (client);
|
||||
}
|
||||
#endif
|
||||
|
||||
try {
|
||||
host_session = yield establish (config, debugger, (owned) connecting, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (debugger.state == STOPPED) {
|
||||
try {
|
||||
yield debugger.resume (cancellable);
|
||||
} catch (GLib.Error ee) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
yield debugger.close (cancellable);
|
||||
} catch (IOError ee) {
|
||||
}
|
||||
throw_api_error (e);
|
||||
}
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
}
|
||||
|
||||
private async BareboneHostSession establish (BareboneConfig config, Debugger debugger, owned ConnectingFunc connecting,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Error, Efni.Error, IOError {
|
||||
// The arm64 MMU system registers (TTBR1_EL1/TCR_EL1) are exposed by some stubs and not
|
||||
// others (the Android emulator's does not); detect it from the advertised register set,
|
||||
// which selects host page-table walking versus the kernel's set_memory_* helpers.
|
||||
@@ -226,7 +232,7 @@ namespace Frida {
|
||||
size_t page_size;
|
||||
try {
|
||||
page_size = yield machine.query_page_size (cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
page_size = 0;
|
||||
}
|
||||
|
||||
@@ -341,7 +347,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async BareboneHostSession attach_to_resident_agent (BareboneResidentTransportConfig transport,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Error, Efni.Error, IOError {
|
||||
#if WINDOWS
|
||||
throw new Error.NOT_SUPPORTED ("Resident agents are not available on this OS");
|
||||
#else
|
||||
@@ -654,51 +660,55 @@ namespace Frida {
|
||||
|
||||
public async HostApplicationInfo[] enumerate_applications (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
try {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
|
||||
var query = ApplicationQueryOptions._deserialize (options);
|
||||
var query = ApplicationQueryOptions._deserialize (options);
|
||||
|
||||
if (android_emulator_pid != 0) {
|
||||
var helper = yield get_android_helper (cancellable);
|
||||
return yield helper.enumerate_applications (query, cancellable);
|
||||
if (android_emulator_pid != 0) {
|
||||
var helper = yield get_android_helper (cancellable);
|
||||
return yield helper.enumerate_applications (query, cancellable);
|
||||
}
|
||||
|
||||
var selected = new Gee.HashSet<string> ();
|
||||
query.enumerate_selected_identifiers (identifier => {
|
||||
selected.add (identifier);
|
||||
});
|
||||
|
||||
var applications = yield list_applications (selected.to_array (), cancellable);
|
||||
|
||||
var running = new Gee.HashMap<string, uint> ();
|
||||
foreach (HostProcessInfo p in yield connection.enumerate_processes (Scope.METADATA, {},
|
||||
cancellable)) {
|
||||
var path = p.parameters["path"];
|
||||
if (path != null)
|
||||
running[basename_of (path.get_string ()).down ()] = p.pid;
|
||||
}
|
||||
|
||||
var scope = query.scope;
|
||||
var result = new HostApplicationInfo[applications.length];
|
||||
for (int i = 0; i != applications.length; i++) {
|
||||
var app = applications[i];
|
||||
var parameters = make_parameters_dict ();
|
||||
if (scope != MINIMAL)
|
||||
parameters["path"] = app.path;
|
||||
|
||||
uint pid;
|
||||
if (!running.unset (basename_of (app.path).down (), out pid))
|
||||
pid = 0;
|
||||
|
||||
var name = (app.description != "") ? app.description : basename_of (app.path);
|
||||
result[i] = HostApplicationInfo (app.identifier, name, pid, (owned) parameters);
|
||||
}
|
||||
return result;
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
var selected = new Gee.HashSet<string> ();
|
||||
query.enumerate_selected_identifiers (identifier => {
|
||||
selected.add (identifier);
|
||||
});
|
||||
|
||||
var applications = yield list_applications (selected.to_array (), cancellable);
|
||||
|
||||
var running = new Gee.HashMap<string, uint> ();
|
||||
foreach (HostProcessInfo p in yield connection.enumerate_processes (Scope.METADATA, {},
|
||||
cancellable)) {
|
||||
var path = p.parameters["path"];
|
||||
if (path != null)
|
||||
running[basename_of (path.get_string ()).down ()] = p.pid;
|
||||
}
|
||||
|
||||
var scope = query.scope;
|
||||
var result = new HostApplicationInfo[applications.length];
|
||||
for (int i = 0; i != applications.length; i++) {
|
||||
var app = applications[i];
|
||||
var parameters = make_parameters_dict ();
|
||||
if (scope != MINIMAL)
|
||||
parameters["path"] = app.path;
|
||||
|
||||
uint pid;
|
||||
if (!running.unset (basename_of (app.path).down (), out pid))
|
||||
pid = 0;
|
||||
|
||||
var name = (app.description != "") ? app.description : basename_of (app.path);
|
||||
result[i] = HostApplicationInfo (app.identifier, name, pid, (owned) parameters);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Barebone.AgentConnection.Application[] list_applications (
|
||||
string[] identifiers, Cancellable? cancellable) throws Error, IOError {
|
||||
string[] identifiers, Cancellable? cancellable) throws Error, Efni.Error, IOError {
|
||||
var listed = new Gee.ArrayList<Barebone.AgentConnection.Application> ();
|
||||
listed.add_all_array (yield connection.enumerate_applications (identifiers, cancellable));
|
||||
|
||||
@@ -738,19 +748,23 @@ namespace Frida {
|
||||
|
||||
public async HostProcessInfo[] enumerate_processes (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
var query = ProcessQueryOptions._deserialize (options);
|
||||
uint[] pids = {};
|
||||
query.enumerate_selected_pids (pid => {
|
||||
pids += pid;
|
||||
});
|
||||
var processes = yield connection.enumerate_processes (query.scope, pids, cancellable);
|
||||
try {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
var query = ProcessQueryOptions._deserialize (options);
|
||||
uint[] pids = {};
|
||||
query.enumerate_selected_pids (pid => {
|
||||
pids += pid;
|
||||
});
|
||||
var processes = yield connection.enumerate_processes (query.scope, pids, cancellable);
|
||||
|
||||
if (query.scope == FULL && android_emulator_pid != 0)
|
||||
yield attach_app_icons (processes, cancellable);
|
||||
if (query.scope == FULL && android_emulator_pid != 0)
|
||||
yield attach_app_icons (processes, cancellable);
|
||||
|
||||
return processes;
|
||||
return processes;
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
private async void attach_app_icons (HostProcessInfo[] processes, Cancellable? cancellable) throws IOError {
|
||||
@@ -759,6 +773,8 @@ namespace Frida {
|
||||
icons = yield resolve_app_icons (cancellable);
|
||||
} catch (Error e) {
|
||||
return;
|
||||
} catch (Efni.Error e) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i != processes.length; i++) {
|
||||
@@ -768,7 +784,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private async Gee.HashMap<string, Variant> resolve_app_icons (Cancellable? cancellable) throws Error, IOError {
|
||||
private async Gee.HashMap<string, Variant> resolve_app_icons (Cancellable? cancellable) throws Error, Efni.Error, IOError {
|
||||
if (android_app_icons == null) {
|
||||
var helper = yield get_android_helper (cancellable);
|
||||
|
||||
@@ -788,7 +804,7 @@ namespace Frida {
|
||||
return android_app_icons;
|
||||
}
|
||||
|
||||
private async AndroidHelperClient get_android_helper (Cancellable? cancellable) throws Error, IOError {
|
||||
private async AndroidHelperClient get_android_helper (Cancellable? cancellable) throws Error, Efni.Error, IOError {
|
||||
while (android_helper_request != null) {
|
||||
try {
|
||||
return yield android_helper_request.future.wait_async (cancellable);
|
||||
@@ -854,15 +870,23 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
yield start_gating (cancellable);
|
||||
try {
|
||||
yield start_gating (cancellable);
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
yield start_gating (cancellable);
|
||||
try {
|
||||
yield start_gating (cancellable);
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
private async void start_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void start_gating (Cancellable? cancellable) throws Error, Efni.Error, IOError {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
|
||||
@@ -872,12 +896,16 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
try {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
|
||||
yield connection.gate_spawns (false, cancellable);
|
||||
watchdog.clear ();
|
||||
pending_spawn.clear ();
|
||||
yield connection.gate_spawns (false, cancellable);
|
||||
watchdog.clear ();
|
||||
pending_spawn.clear ();
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
public async HostSpawnInfo[] enumerate_pending_spawn (Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -894,23 +922,27 @@ namespace Frida {
|
||||
|
||||
public async uint spawn (string program, HostSpawnOptions options, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
try {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
|
||||
if (android_emulator_pid != 0 && program[0] != '/')
|
||||
return yield spawn_android_app (program, options, cancellable);
|
||||
if (android_emulator_pid != 0 && program[0] != '/')
|
||||
return yield spawn_android_app (program, options, cancellable);
|
||||
|
||||
if (connection.spawns_by_itself)
|
||||
return yield connection.spawn_program (words_of (program, options), cancellable);
|
||||
if (connection.spawns_by_itself)
|
||||
return yield connection.spawn_program (words_of (program, options), cancellable);
|
||||
|
||||
uint helper = yield acquire_spawn_helper (cancellable);
|
||||
uint helper = yield acquire_spawn_helper (cancellable);
|
||||
|
||||
return yield connection.spawn_process (helper,
|
||||
command_line_of (yield file_of (program, cancellable), options), cancellable);
|
||||
return yield connection.spawn_process (helper,
|
||||
command_line_of (yield file_of (program, cancellable), options), cancellable);
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
private async uint spawn_android_app (string package, HostSpawnOptions options, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Error, Efni.Error, IOError {
|
||||
var entrypoint = PackageEntrypoint.parse (package, options);
|
||||
|
||||
var helper = yield get_android_helper (cancellable);
|
||||
@@ -963,28 +995,32 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async void resume (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
if (connection.spawns_by_itself) {
|
||||
pending_spawn.unset (pid);
|
||||
watchdog.cancel (pid);
|
||||
try {
|
||||
if (connection.spawns_by_itself) {
|
||||
pending_spawn.unset (pid);
|
||||
watchdog.cancel (pid);
|
||||
|
||||
yield connection.resume_process (0, pid, cancellable);
|
||||
yield connection.resume_process (0, pid, cancellable);
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
uint holder = spawn_helper_pid;
|
||||
|
||||
HeldSpawn? held = pending_spawn[pid];
|
||||
if (held != null) {
|
||||
holder = held.holder_pid;
|
||||
pending_spawn.unset (pid);
|
||||
watchdog.cancel (pid);
|
||||
}
|
||||
|
||||
if (holder == 0)
|
||||
throw new Error.INVALID_ARGUMENT ("Process %u was not spawned by us", pid);
|
||||
|
||||
yield connection.resume_process (holder, pid, cancellable);
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
uint holder = spawn_helper_pid;
|
||||
|
||||
HeldSpawn? held = pending_spawn[pid];
|
||||
if (held != null) {
|
||||
holder = held.holder_pid;
|
||||
pending_spawn.unset (pid);
|
||||
watchdog.cancel (pid);
|
||||
}
|
||||
|
||||
if (holder == 0)
|
||||
throw new Error.INVALID_ARGUMENT ("Process %u was not spawned by us", pid);
|
||||
|
||||
yield connection.resume_process (holder, pid, cancellable);
|
||||
}
|
||||
|
||||
public async void kill (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -993,34 +1029,38 @@ namespace Frida {
|
||||
|
||||
public async AgentSessionId attach (uint pid, HashTable<string, Variant> options, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
if (pid != 0) {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
try {
|
||||
if (pid != 0) {
|
||||
if (connection == null)
|
||||
throw_not_supported ();
|
||||
|
||||
yield acquire_injected_agent (pid, cancellable);
|
||||
yield acquire_injected_agent (pid, cancellable);
|
||||
}
|
||||
|
||||
var opts = SessionOptions._deserialize (options);
|
||||
if (opts.realm == EMULATED)
|
||||
throw new Error.NOT_SUPPORTED ("Emulated realm is not supported on Barebone targets");
|
||||
|
||||
var session_id = AgentSessionId.generate ();
|
||||
|
||||
MainContext dbus_context = yield get_dbus_context ();
|
||||
|
||||
BareboneAgentSession session;
|
||||
if (connection != null) {
|
||||
session = new RemoteBareboneAgentSession (connection, pid, session_id, opts.persist_timeout,
|
||||
dbus_context);
|
||||
} else {
|
||||
if (services == null)
|
||||
throw new Error.NOT_SUPPORTED ("Barebone target has neither an agent nor a debugger stub");
|
||||
session = new LocalBareboneAgentSession (services, session_id, opts.persist_timeout, dbus_context);
|
||||
}
|
||||
agent_sessions[session_id] = session;
|
||||
session.closed.connect (on_agent_session_closed);
|
||||
|
||||
return session_id;
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
var opts = SessionOptions._deserialize (options);
|
||||
if (opts.realm == EMULATED)
|
||||
throw new Error.NOT_SUPPORTED ("Emulated realm is not supported on Barebone targets");
|
||||
|
||||
var session_id = AgentSessionId.generate ();
|
||||
|
||||
MainContext dbus_context = yield get_dbus_context ();
|
||||
|
||||
BareboneAgentSession session;
|
||||
if (connection != null) {
|
||||
session = new RemoteBareboneAgentSession (connection, pid, session_id, opts.persist_timeout,
|
||||
dbus_context);
|
||||
} else {
|
||||
if (services == null)
|
||||
throw new Error.NOT_SUPPORTED ("Barebone target has neither an agent nor a debugger stub");
|
||||
session = new LocalBareboneAgentSession (services, session_id, opts.persist_timeout, dbus_context);
|
||||
}
|
||||
agent_sessions[session_id] = session;
|
||||
session.closed.connect (on_agent_session_closed);
|
||||
|
||||
return session_id;
|
||||
}
|
||||
|
||||
public async void reattach (AgentSessionId id, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -1126,7 +1166,7 @@ namespace Frida {
|
||||
return (end != -1) ? line[:end] : line;
|
||||
}
|
||||
|
||||
private async uint acquire_spawn_helper (Cancellable? cancellable) throws Error, IOError {
|
||||
private async uint acquire_spawn_helper (Cancellable? cancellable) throws Error, Efni.Error, IOError {
|
||||
if (spawn_helper_pid != 0)
|
||||
return spawn_helper_pid;
|
||||
|
||||
@@ -1148,7 +1188,7 @@ namespace Frida {
|
||||
return pid;
|
||||
}
|
||||
|
||||
private async string file_of (string program, Cancellable? cancellable) throws Error, IOError {
|
||||
private async string file_of (string program, Cancellable? cancellable) throws Error, Efni.Error, IOError {
|
||||
if (connection.spawns_by_itself || program.contains ("\\") || program.contains (":"))
|
||||
return program;
|
||||
|
||||
@@ -1190,7 +1230,7 @@ namespace Frida {
|
||||
return "\"" + token + "\"";
|
||||
}
|
||||
|
||||
private async void acquire_injected_agent (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void acquire_injected_agent (uint pid, Cancellable? cancellable) throws Error, Efni.Error, IOError {
|
||||
uint users = injected_agents[pid];
|
||||
if (users != 0) {
|
||||
injected_agents[pid] = users + 1;
|
||||
@@ -1247,19 +1287,23 @@ namespace Frida {
|
||||
|
||||
public override async AgentScriptId create_script (string source, HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
check_open ();
|
||||
try {
|
||||
check_open ();
|
||||
|
||||
var opts = ScriptOptions._deserialize (options);
|
||||
if (opts.runtime == V8)
|
||||
throw new Error.INVALID_ARGUMENT ("The V8 runtime is not supported by the Barebone backend");
|
||||
var opts = ScriptOptions._deserialize (options);
|
||||
if (opts.runtime == V8)
|
||||
throw new Error.INVALID_ARGUMENT ("The V8 runtime is not supported by the Barebone backend");
|
||||
|
||||
var id = AgentScriptId (next_script_id++);
|
||||
var id = AgentScriptId (next_script_id++);
|
||||
|
||||
var script = BareboneScript.create (id, source, services);
|
||||
scripts[id] = script;
|
||||
script.message.connect (on_message_from_script);
|
||||
var script = BareboneScript.create (id, source, services);
|
||||
scripts[id] = script;
|
||||
script.message.connect (on_message_from_script);
|
||||
|
||||
return id;
|
||||
return id;
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
public override async void destroy_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -1347,47 +1391,63 @@ namespace Frida {
|
||||
|
||||
public override async AgentScriptId create_script (string source, HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
check_open ();
|
||||
try {
|
||||
check_open ();
|
||||
|
||||
var opts = ScriptOptions._deserialize (options);
|
||||
if (opts.runtime == V8)
|
||||
throw new Error.INVALID_ARGUMENT ("The V8 runtime is not supported by the Barebone backend");
|
||||
var opts = ScriptOptions._deserialize (options);
|
||||
if (opts.runtime == V8)
|
||||
throw new Error.INVALID_ARGUMENT ("The V8 runtime is not supported by the Barebone backend");
|
||||
|
||||
var script_id = yield connection.create_script (source, pid, cancellable);
|
||||
own_scripts.add (script_id);
|
||||
var script_id = yield connection.create_script (source, pid, cancellable);
|
||||
own_scripts.add (script_id);
|
||||
|
||||
return script_id;
|
||||
return script_id;
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
public override async void destroy_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
|
||||
check_open ();
|
||||
try {
|
||||
check_open ();
|
||||
|
||||
yield connection.destroy_script (script_id, pid, cancellable);
|
||||
own_scripts.remove (script_id);
|
||||
yield connection.destroy_script (script_id, pid, cancellable);
|
||||
own_scripts.remove (script_id);
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
public override async void load_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
|
||||
check_open ();
|
||||
try {
|
||||
check_open ();
|
||||
|
||||
yield connection.load_script (script_id, pid, cancellable);
|
||||
yield connection.load_script (script_id, pid, cancellable);
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
public override async void post_messages (AgentMessage[] messages, uint batch_id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
transmitter.check_okay_to_receive ();
|
||||
try {
|
||||
transmitter.check_okay_to_receive ();
|
||||
|
||||
foreach (var m in messages) {
|
||||
switch (m.kind) {
|
||||
case SCRIPT:
|
||||
yield connection.post_script_message (m.script_id, m.text,
|
||||
m.has_data ? new Bytes (m.data) : null, pid, cancellable);
|
||||
break;
|
||||
case DEBUGGER:
|
||||
break;
|
||||
foreach (var m in messages) {
|
||||
switch (m.kind) {
|
||||
case SCRIPT:
|
||||
yield connection.post_script_message (m.script_id, m.text,
|
||||
m.has_data ? new Bytes (m.data) : null, pid, cancellable);
|
||||
break;
|
||||
case DEBUGGER:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transmitter.notify_rx_batch_id (batch_id);
|
||||
transmitter.notify_rx_batch_id (batch_id);
|
||||
} catch (Efni.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
// Each copy numbers its scripts from one, thus take only what comes from our own process.
|
||||
|
||||
@@ -23,11 +23,11 @@ namespace Frida.Barebone {
|
||||
private Callback console_log_callback;
|
||||
|
||||
public async CModule.from_blob (Bytes blob, Machine machine, Allocator allocator, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
try {
|
||||
elf = new Gum.ElfModule.from_blob (blob);
|
||||
} catch (Gum.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
|
||||
var raw_elf = new Bytes.static (elf.get_file_data ());
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class Callback : Object {
|
||||
@@ -10,7 +12,7 @@ namespace Frida.Barebone {
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
public async Callback (uint64 code, CallbackHandler handler, Machine machine, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
this.code = code;
|
||||
this.handler = handler;
|
||||
this.machine = machine;
|
||||
@@ -26,7 +28,7 @@ namespace Frida.Barebone {
|
||||
debugger.notify["state"].disconnect (on_gdb_state_changed);
|
||||
}
|
||||
|
||||
public async void destroy (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void destroy (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield breakpoint.remove (cancellable);
|
||||
}
|
||||
|
||||
@@ -44,7 +46,7 @@ namespace Frida.Barebone {
|
||||
handle_invocation.begin (exception.thread);
|
||||
}
|
||||
|
||||
private async void handle_invocation (DebuggerThread thread) throws Error, IOError {
|
||||
private async void handle_invocation (DebuggerThread thread) throws Efni.Error, IOError {
|
||||
uint arity = handler.arity;
|
||||
|
||||
var frame = yield machine.load_call_frame (thread, arity, io_cancellable);
|
||||
@@ -69,6 +71,6 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public abstract async uint64 handle_invocation (uint64[] args, CallFrame frame, Cancellable? cancellable)
|
||||
throws Error, IOError;
|
||||
throws Efni.Error, IOError;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
private class ConsoleLogHandler : Object, CallbackHandler {
|
||||
@@ -14,7 +16,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async uint64 handle_invocation (uint64[] args, CallFrame frame, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var message = args[0];
|
||||
var len = (long) args[1];
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public async Allocation inject_elf (Gum.ElfModule elf, Bytes raw_elf, size_t page_size, Machine machine, Allocator allocator,
|
||||
owned UploadProgressFunc on_upload_progress, Cancellable? cancellable) throws Error, IOError {
|
||||
owned UploadProgressFunc on_upload_progress, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
size_t vm_size = (size_t) elf.mapped_size;
|
||||
|
||||
uint num_pages = (uint) (vm_size / page_size);
|
||||
@@ -21,7 +21,7 @@ namespace Frida.Barebone {
|
||||
return true;
|
||||
});
|
||||
if (text_size == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect text segment");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to detect text segment");
|
||||
|
||||
var allocation = yield allocator.allocate (num_pages * page_size, page_size, cancellable);
|
||||
try {
|
||||
@@ -39,14 +39,14 @@ namespace Frida.Barebone {
|
||||
(num_pages * page_size) - (size_t) data_base, READ | WRITE, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
yield allocation.deallocate (cancellable);
|
||||
throw_api_error (e);
|
||||
Efni.throw_api_error (e);
|
||||
}
|
||||
|
||||
return allocation;
|
||||
}
|
||||
|
||||
private async void upload (Machine machine, uint64 va, Bytes image, owned UploadProgressFunc on_progress,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
unowned uint8[] data = image.get_data ();
|
||||
size_t slice_size = size_t.max (data.length / UPLOAD_PROGRESS_STEPS, MIN_UPLOAD_SLICE);
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Frida.Barebone {
|
||||
private const size_t MIN_UPLOAD_SLICE = 256 * 1024;
|
||||
|
||||
private async void clear_tail_beyond_file (Machine machine, uint64 base_va, size_t written,
|
||||
size_t mapped, Cancellable? cancellable) throws Error, IOError {
|
||||
size_t mapped, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (written >= mapped)
|
||||
return;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
private sealed class EmulatorInstrumentation : InternalAgent {
|
||||
private LocalConnection local_connection;
|
||||
|
||||
+14
-12
@@ -1,10 +1,12 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone.Img4 {
|
||||
public Payload parse (Bytes blob) throws Error {
|
||||
public Payload parse (Bytes blob) throws Efni.Error {
|
||||
BufferReader r = new BufferReader (new Buffer (blob, BIG_ENDIAN));
|
||||
|
||||
if (r.read_uint8 () != Der.SEQUENCE)
|
||||
throw new Error.PROTOCOL ("IMG4/IM4P must start with DER SEQUENCE");
|
||||
throw new Efni.Error.PROTOCOL ("IMG4/IM4P must start with DER SEQUENCE");
|
||||
size_t seq_len = Der.read_length (r);
|
||||
size_t seq_end = r.offset + seq_len;
|
||||
|
||||
@@ -12,13 +14,13 @@ namespace Frida.Barebone.Img4 {
|
||||
|
||||
if (first_magic == "IMG4") {
|
||||
if (r.read_uint8 () != Der.SEQUENCE)
|
||||
throw new Error.PROTOCOL ("Expected SEQUENCE for IM4P");
|
||||
throw new Efni.Error.PROTOCOL ("Expected SEQUENCE for IM4P");
|
||||
size_t im4p_len = Der.read_length (r);
|
||||
seq_end = r.offset + im4p_len;
|
||||
if (Der.read_string (r) != "IM4P")
|
||||
throw new Error.PROTOCOL ("Missing IM4P magic");
|
||||
throw new Efni.Error.PROTOCOL ("Missing IM4P magic");
|
||||
} else if (first_magic != "IM4P") {
|
||||
throw new Error.PROTOCOL ("Not an IMG4/IM4P container (magic='%s')", first_magic);
|
||||
throw new Efni.Error.PROTOCOL ("Not an IMG4/IM4P container (magic='%s')", first_magic);
|
||||
}
|
||||
|
||||
string kind = Der.read_string (r);
|
||||
@@ -60,14 +62,14 @@ namespace Frida.Barebone.Img4 {
|
||||
private const uint8 IA5_STRING = 0x16;
|
||||
private const uint8 SEQUENCE = 0x30;
|
||||
|
||||
private size_t read_length (BufferReader r) throws Error {
|
||||
private size_t read_length (BufferReader r) throws Efni.Error {
|
||||
uint8 b = r.read_uint8 ();
|
||||
if ((b & 0x80) == 0)
|
||||
return b;
|
||||
|
||||
uint8 nlen = (uint8) (b & 0x7f);
|
||||
if (nlen == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Indefinite length not supported in IMG4");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Indefinite length not supported in IMG4");
|
||||
|
||||
size_t len = 0;
|
||||
for (uint8 i = 0; i != nlen; i++)
|
||||
@@ -75,24 +77,24 @@ namespace Frida.Barebone.Img4 {
|
||||
return len;
|
||||
}
|
||||
|
||||
private Bytes read_octet_string (BufferReader r) throws Error {
|
||||
private Bytes read_octet_string (BufferReader r) throws Efni.Error {
|
||||
uint8 tag = r.read_uint8 ();
|
||||
if (tag != Der.OCTET_STRING)
|
||||
throw new Error.PROTOCOL ("Expected OCTET STRING, got tag 0x%02x".printf (tag));
|
||||
throw new Efni.Error.PROTOCOL ("Expected OCTET STRING, got tag 0x%02x".printf (tag));
|
||||
size_t len = read_length (r);
|
||||
return r.read_bytes (len);
|
||||
}
|
||||
|
||||
private string read_string (BufferReader r) throws Error {
|
||||
private string read_string (BufferReader r) throws Efni.Error {
|
||||
uint8 tag = r.read_uint8 ();
|
||||
if (tag != Der.IA5_STRING && tag != Der.UTF8_STRING)
|
||||
throw new Error.PROTOCOL ("Unexpected string tag 0x%02x", tag);
|
||||
throw new Efni.Error.PROTOCOL ("Unexpected string tag 0x%02x", tag);
|
||||
size_t len = read_length (r);
|
||||
return r.read_fixed_string (len);
|
||||
}
|
||||
}
|
||||
|
||||
private Bytes maybe_decompress_lzfse (Bytes src) throws Error {
|
||||
private Bytes maybe_decompress_lzfse (Bytes src) throws Efni.Error {
|
||||
if (LZFSE.is_lzfse (src))
|
||||
return LZFSE.decode (src);
|
||||
return src;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class Interceptor : Object {
|
||||
@@ -46,7 +48,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async void attach (uint64 target, BreakpointInvocationListener listener, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint64 address = machine.address_from_funcptr (target);
|
||||
|
||||
BreakpointEntry? entry = breakpoint_entries[address];
|
||||
@@ -65,18 +67,18 @@ namespace Frida.Barebone {
|
||||
machine.breakpoint_size_from_funcptr (target), cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
breakpoint_entries.unset (address);
|
||||
throw_api_error (e);
|
||||
Efni.throw_api_error (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void attach_inline (uint64 target, InlineInvocationListener listener, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (listener.kind != PROBE)
|
||||
throw new Error.NOT_SUPPORTED ("Only probe-style hooks are currently supported for inline hooks");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Only probe-style hooks are currently supported for inline hooks");
|
||||
|
||||
if (inline_hooks.has_key (target))
|
||||
throw new Error.INVALID_ARGUMENT ("Only one probe per target is currently supported for inline hooks");
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Only one probe per target is currently supported for inline hooks");
|
||||
var hook = yield machine.create_inline_hook (target, listener.on_enter, allocator, cancellable);
|
||||
inline_hooks[target] = hook;
|
||||
|
||||
@@ -84,13 +86,13 @@ namespace Frida.Barebone {
|
||||
yield hook.enable (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
inline_hooks.unset (target);
|
||||
throw_api_error (e);
|
||||
Efni.throw_api_error (e);
|
||||
}
|
||||
|
||||
hook.set_data ("listener", listener);
|
||||
}
|
||||
|
||||
public async void detach (InvocationListener listener, Cancellable? cancellable) throws Error, IOError {
|
||||
public async void detach (InvocationListener listener, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
BreakpointInvocationListener? bpl = listener as BreakpointInvocationListener;
|
||||
if (bpl != null) {
|
||||
foreach (var e in breakpoint_entries.entries.to_array ()) {
|
||||
@@ -137,7 +139,7 @@ namespace Frida.Barebone {
|
||||
handle_breakpoint_hit.begin (bp, exception.thread);
|
||||
}
|
||||
|
||||
private async void handle_breakpoint_hit (DebuggerBreakpoint bp, DebuggerThread thread) throws Error, IOError {
|
||||
private async void handle_breakpoint_hit (DebuggerBreakpoint bp, DebuggerThread thread) throws Efni.Error, IOError {
|
||||
uint64 address = bp.address;
|
||||
|
||||
BreakpointEntry? entry = breakpoint_entries[address];
|
||||
@@ -153,7 +155,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async void handle_invocation (BreakpointEntry entry, DebuggerBreakpoint bp, DebuggerThread thread) throws Error, IOError {
|
||||
private async void handle_invocation (BreakpointEntry entry, DebuggerBreakpoint bp, DebuggerThread thread) throws Efni.Error, IOError {
|
||||
unowned string tid = thread.id;
|
||||
CallStack? call_stack = call_stacks[tid];
|
||||
if (call_stack == null) {
|
||||
@@ -193,7 +195,7 @@ namespace Frida.Barebone {
|
||||
yield continue_from_breakpoint (bp, thread);
|
||||
}
|
||||
|
||||
private async void handle_return (CallStack call_stack, DebuggerBreakpoint bp, DebuggerThread thread) throws Error, IOError {
|
||||
private async void handle_return (CallStack call_stack, DebuggerBreakpoint bp, DebuggerThread thread) throws Efni.Error, IOError {
|
||||
var frame = yield machine.load_call_frame (thread, 0, io_cancellable);
|
||||
|
||||
uint64 return_address = machine.address_from_funcptr (frame.return_address);
|
||||
@@ -220,7 +222,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async void continue_from_breakpoint (DebuggerBreakpoint bp, DebuggerThread thread) throws Error, IOError {
|
||||
private async void continue_from_breakpoint (DebuggerBreakpoint bp, DebuggerThread thread) throws Efni.Error, IOError {
|
||||
yield bp.disable (io_cancellable);
|
||||
yield thread.step (io_cancellable);
|
||||
yield bp.enable (io_cancellable);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
/**
|
||||
@@ -5,8 +7,8 @@ namespace Frida.Barebone {
|
||||
* quiescent state beforehand, and how it is let go afterwards.
|
||||
*/
|
||||
internal interface KernelFlavor : Object {
|
||||
public abstract async void prepare (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void settle (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void prepare (Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
public abstract async void settle (Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
|
||||
public abstract bool stays_attached { get; }
|
||||
}
|
||||
@@ -22,10 +24,10 @@ namespace Frida.Barebone {
|
||||
this.machine = machine;
|
||||
}
|
||||
|
||||
public async void prepare (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void prepare (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
}
|
||||
|
||||
public async void settle (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void settle (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield machine.debugger.resume (cancellable);
|
||||
}
|
||||
}
|
||||
@@ -42,18 +44,18 @@ namespace Frida.Barebone {
|
||||
private SymbolInfo? panic;
|
||||
|
||||
public XnuKernelFlavor (Machine machine, uint64 kernel_base, Gee.Map<string, SymbolInfo> symbols)
|
||||
throws Error {
|
||||
throws Efni.Error {
|
||||
this.machine = machine;
|
||||
this.kernel_base = kernel_base;
|
||||
|
||||
thread_block = symbols["thread_block"];
|
||||
if (thread_block == null)
|
||||
throw new Error.NOT_SUPPORTED ("Missing symbol for thread_block");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Missing symbol for thread_block");
|
||||
|
||||
panic = symbols["panic"];
|
||||
}
|
||||
|
||||
public async void prepare (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void prepare (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 thread_block_address = kernel_base + thread_block.offset;
|
||||
|
||||
var arm64 = machine as Arm64Machine;
|
||||
@@ -71,7 +73,7 @@ namespace Frida.Barebone {
|
||||
yield arm64.learn_permission_templates (thread_block_address, cancellable);
|
||||
}
|
||||
|
||||
public async void settle (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void settle (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
// The vphone research kernel panics on any synchronous exception taken while a
|
||||
@@ -87,7 +89,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async void run_until_thread_block (uint64 address, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void run_until_thread_block (uint64 address, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
var bp = yield debugger.add_breakpoint (SOFT, address, 4, cancellable);
|
||||
|
||||
@@ -115,19 +117,19 @@ namespace Frida.Barebone {
|
||||
Numeric.uint64_hash, Numeric.uint64_equal);
|
||||
|
||||
public LinuxKernelFlavor (Machine machine, uint64 kernel_base,
|
||||
Gee.Map<string, SymbolInfo> symbols) throws Error {
|
||||
Gee.Map<string, SymbolInfo> symbols) throws Efni.Error {
|
||||
this.machine = machine;
|
||||
this.kernel_base = kernel_base;
|
||||
this.symbols = symbols;
|
||||
|
||||
schedule = symbols["schedule"];
|
||||
if (schedule == null)
|
||||
throw new Error.NOT_SUPPORTED ("Missing symbol for schedule");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Missing symbol for schedule");
|
||||
|
||||
panic = symbols["panic"];
|
||||
}
|
||||
|
||||
public async void prepare (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void prepare (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 schedule_address = kernel_base + schedule.offset;
|
||||
|
||||
var arm64 = machine as Arm64Machine;
|
||||
@@ -157,7 +159,7 @@ namespace Frida.Barebone {
|
||||
arm64.set_memory_x = symbol_address ("set_memory_x");
|
||||
arm64.set_memory_nx = symbol_address ("set_memory_nx");
|
||||
if (arm64.set_memory_x == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Missing set_memory_* symbols for kernel-API page protection");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Missing set_memory_* symbols for kernel-API page protection");
|
||||
}
|
||||
|
||||
yield machine.enter_exception_level (1, 1000, cancellable);
|
||||
@@ -181,7 +183,7 @@ namespace Frida.Barebone {
|
||||
yield relax_cfi_enforcement (cancellable);
|
||||
}
|
||||
|
||||
private async void keep_current_runnable (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void keep_current_runnable (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 wake_up_process = symbol_address ("wake_up_process");
|
||||
if (wake_up_process == 0)
|
||||
return;
|
||||
@@ -193,7 +195,7 @@ namespace Frida.Barebone {
|
||||
yield machine.invoke (wake_up_process, { current }, cancellable);
|
||||
}
|
||||
|
||||
private async void relax_cfi_enforcement (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void relax_cfi_enforcement (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
foreach (unowned string name in CFI_CHECK_ENTRY_POINTS) {
|
||||
uint64 entry = symbol_address (name);
|
||||
if (entry == 0)
|
||||
@@ -204,7 +206,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async uint64 read_current_task (Cancellable? cancellable) throws Error, IOError {
|
||||
private async uint64 read_current_task (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (machine is X64Machine)
|
||||
return yield read_per_cpu_task ("current_task", cancellable);
|
||||
if (symbols["__entry_task"] != null)
|
||||
@@ -212,7 +214,7 @@ namespace Frida.Barebone {
|
||||
return yield read_current_from_stack (cancellable);
|
||||
}
|
||||
|
||||
private async bool task_is_idle (uint64 task, Cancellable? cancellable) throws Error, IOError {
|
||||
private async bool task_is_idle (uint64 task, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (task == symbol_address ("init_task"))
|
||||
return true;
|
||||
if (symbols["idle_threads"] == null)
|
||||
@@ -220,7 +222,7 @@ namespace Frida.Barebone {
|
||||
return task == yield read_per_cpu_task ("idle_threads", cancellable);
|
||||
}
|
||||
|
||||
private async uint64 read_per_cpu_task (string symbol, Cancellable? cancellable) throws Error, IOError {
|
||||
private async uint64 read_per_cpu_task (string symbol, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 variable = (machine is X64Machine) ? percpu_offset (symbol) : symbol_address (symbol);
|
||||
uint64 cpu = uint64.parse (machine.debugger.exception.thread.id, 16) - 1;
|
||||
var slot = yield machine.read_virtual (symbol_address ("__per_cpu_offset") + cpu * 8, 8,
|
||||
@@ -231,7 +233,7 @@ namespace Frida.Barebone {
|
||||
return read_u64 (task.get_data (), 0);
|
||||
}
|
||||
|
||||
private async uint64 read_current_from_stack (Cancellable? cancellable) throws Error, IOError {
|
||||
private async uint64 read_current_from_stack (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 sp = yield machine.debugger.exception.thread.read_register ("sp", cancellable);
|
||||
uint64 thread_info = sp & ~(ARM64_THREAD_SIZE - 1);
|
||||
var task = yield machine.read_virtual (thread_info + ARM64_THREAD_INFO_TASK_OFFSET, 8, cancellable);
|
||||
@@ -255,7 +257,7 @@ namespace Frida.Barebone {
|
||||
return (sym != null) ? sym.offset : 0;
|
||||
}
|
||||
|
||||
public async void settle (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void settle (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield machine.debugger.resume (cancellable);
|
||||
}
|
||||
|
||||
@@ -263,7 +265,7 @@ namespace Frida.Barebone {
|
||||
// from: sleepable, on a stack of its own, holding nothing. The CPU idle loop reaches the
|
||||
// scheduler with interrupts masked, and injecting from the idle task deadlocks a sleeping
|
||||
// callee (module_alloc, execmem_alloc), so those hits are skipped and a real task awaited.
|
||||
private async void run_until_schedule (uint64 address, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void run_until_schedule (uint64 address, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
SymbolInfo? system_state = symbols["system_state"];
|
||||
uint64 system_state_address = (system_state != null) ? kernel_base + system_state.offset : 0;
|
||||
|
||||
@@ -300,7 +302,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void park_at (uint64 address, uint64 system_state_address, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
unowned string pc_name = (machine is X64Machine) ? "rip" : "eip";
|
||||
|
||||
@@ -333,7 +335,7 @@ namespace Frida.Barebone {
|
||||
yield machine.write_virtual (address, original.get_data (), cancellable);
|
||||
|
||||
if (!parked) {
|
||||
throw new Error.TIMED_OUT ("Timed out waiting for a task to reach 0x%" +
|
||||
throw new Efni.Error.TIMED_OUT ("Timed out waiting for a task to reach 0x%" +
|
||||
uint64.FORMAT_MODIFIER + "x", address);
|
||||
}
|
||||
}
|
||||
@@ -347,7 +349,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async bool system_is_running (uint64 system_state_address, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (system_state_address == 0)
|
||||
return true;
|
||||
var data = (yield machine.read_virtual (system_state_address, 4, cancellable)).get_data ();
|
||||
@@ -359,7 +361,7 @@ namespace Frida.Barebone {
|
||||
|
||||
// Linux on arm64 runs with FIQ permanently masked, so only the IRQ mask distinguishes a
|
||||
// sleepable task (interrupts on) from the idle loop and interrupt-context reschedules.
|
||||
private async bool interrupts_masked (DebuggerThread thread, Cancellable? cancellable) throws Error, IOError {
|
||||
private async bool interrupts_masked (DebuggerThread thread, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (machine is IA32Machine || machine is X64Machine) {
|
||||
uint64 eflags = yield thread.read_register ("eflags", cancellable);
|
||||
return (eflags & INTERRUPT_ENABLE_BIT) == 0;
|
||||
@@ -392,24 +394,24 @@ namespace Frida.Barebone {
|
||||
private Machine machine;
|
||||
private uint64 yield_point;
|
||||
|
||||
public Win9xKernelFlavor (Machine machine, Gee.Map<string, SymbolInfo> symbols) throws Error {
|
||||
public Win9xKernelFlavor (Machine machine, Gee.Map<string, SymbolInfo> symbols) throws Efni.Error {
|
||||
this.machine = machine;
|
||||
|
||||
SymbolInfo? get_system_time = symbols["Get_System_Time"];
|
||||
if (get_system_time == null)
|
||||
throw new Error.NOT_SUPPORTED ("Missing symbol for Get_System_Time");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Missing symbol for Get_System_Time");
|
||||
yield_point = get_system_time.offset;
|
||||
}
|
||||
|
||||
public async void prepare (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void prepare (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield run_until_yield_point (cancellable);
|
||||
}
|
||||
|
||||
public async void settle (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void settle (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield machine.debugger.resume (cancellable);
|
||||
}
|
||||
|
||||
private async void run_until_yield_point (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void run_until_yield_point (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
var bp = yield debugger.add_breakpoint (SOFT, yield_point, 1, cancellable);
|
||||
|
||||
@@ -423,7 +425,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async bool stopped_in_ring_zero (Debugger debugger, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint64 cs = yield debugger.exception.thread.read_register ("cs", cancellable);
|
||||
|
||||
return (cs & RING_MASK) == 0;
|
||||
@@ -442,20 +444,20 @@ namespace Frida.Barebone {
|
||||
private uint64 yield_point;
|
||||
private SymbolInfo? bug_check;
|
||||
|
||||
public WinNtKernelFlavor (Machine machine, Gee.Map<string, SymbolInfo> symbols) throws Error {
|
||||
public WinNtKernelFlavor (Machine machine, Gee.Map<string, SymbolInfo> symbols) throws Efni.Error {
|
||||
this.machine = machine;
|
||||
|
||||
// A thread in this system service is at PASSIVE_LEVEL on its own kernel stack, which the
|
||||
// agent needs to start.
|
||||
SymbolInfo? wait_for_single_object = symbols["NtWaitForSingleObject"];
|
||||
if (wait_for_single_object == null)
|
||||
throw new Error.NOT_SUPPORTED ("Missing symbol for NtWaitForSingleObject");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Missing symbol for NtWaitForSingleObject");
|
||||
yield_point = wait_for_single_object.offset;
|
||||
|
||||
bug_check = symbols["KeBugCheckEx"];
|
||||
}
|
||||
|
||||
public async void prepare (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void prepare (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var arm64 = machine as Arm64Machine;
|
||||
if (bug_check != null && arm64 != null)
|
||||
arm64.call_landing_zone = bug_check.offset;
|
||||
@@ -469,11 +471,11 @@ namespace Frida.Barebone {
|
||||
yield run_until_yield_point (cancellable);
|
||||
}
|
||||
|
||||
public async void settle (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void settle (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield machine.debugger.resume (cancellable);
|
||||
}
|
||||
|
||||
private async void run_until_yield_point (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void run_until_yield_point (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
var bp = yield debugger.add_breakpoint (SOFT, yield_point, breakpoint_size_for (debugger), cancellable);
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
/**
|
||||
@@ -34,7 +36,7 @@ namespace Frida.Barebone {
|
||||
private const uint32 LC_FILESET_ENTRY = 0x80000035U;
|
||||
|
||||
public static async KernelRelocation compute (Machine machine, Bytes kernelcache_blob,
|
||||
uint64 loaded_kext_summaries, Cancellable? cancellable) throws Error, IOError {
|
||||
uint64 loaded_kext_summaries, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var debugger = machine.debugger;
|
||||
Buffer image = debugger.make_buffer (kernelcache_blob);
|
||||
Gee.List<FilesetEntry> static_entries = parse_fileset_entries (image);
|
||||
@@ -79,16 +81,16 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private static FilesetEntry find_entry (Gee.List<FilesetEntry> entries, string name) throws Error {
|
||||
private static FilesetEntry find_entry (Gee.List<FilesetEntry> entries, string name) throws Efni.Error {
|
||||
foreach (var entry in entries) {
|
||||
if (entry.name == name)
|
||||
return entry;
|
||||
}
|
||||
throw new Error.NOT_SUPPORTED ("Kernelcache is missing the %s fileset entry", name);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Kernelcache is missing the %s fileset entry", name);
|
||||
}
|
||||
|
||||
private static async void add_fileset_entry (KernelRelocation reloc, Debugger debugger, Buffer image,
|
||||
FilesetEntry entry, uint64 runtime_header, Cancellable? cancellable) throws Error, IOError {
|
||||
FilesetEntry entry, uint64 runtime_header, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Buffer runtime_image = yield read_mach_header (debugger, runtime_header, cancellable);
|
||||
var static_segments = parse_segments (image, (size_t) entry.fileoff);
|
||||
var runtime_segments = parse_segments (runtime_image, 0);
|
||||
@@ -111,7 +113,7 @@ namespace Frida.Barebone {
|
||||
* name -> runtime-header map from which to translate that kext's independently scattered segments.
|
||||
*/
|
||||
private static async Gee.Map<string, uint64?> read_loaded_kext_headers (Debugger debugger,
|
||||
KernelRelocation reloc, uint64 loaded_kext_summaries, Cancellable? cancellable) throws Error, IOError {
|
||||
KernelRelocation reloc, uint64 loaded_kext_summaries, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 list = (yield debugger.read_buffer (reloc.translate (loaded_kext_summaries), 8, cancellable))
|
||||
.read_uint64 (0);
|
||||
uint32 count = (yield debugger.read_buffer (list, 16, cancellable)).read_uint32 (8);
|
||||
@@ -125,21 +127,21 @@ namespace Frida.Barebone {
|
||||
return result;
|
||||
}
|
||||
|
||||
public uint64 translate (uint64 static_address) throws Error {
|
||||
public uint64 translate (uint64 static_address) throws Efni.Error {
|
||||
foreach (var e in entries) {
|
||||
if (static_address >= e.static_base && static_address < e.static_base + e.size)
|
||||
return e.runtime_base + (static_address - e.static_base);
|
||||
}
|
||||
throw new Error.INVALID_ARGUMENT ("Address 0x%" + uint64.FORMAT_MODIFIER + "x is outside the kernelcache",
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Address 0x%" + uint64.FORMAT_MODIFIER + "x is outside the kernelcache",
|
||||
static_address);
|
||||
}
|
||||
|
||||
public uint32 runtime_offset (uint64 static_address) throws Error {
|
||||
public uint32 runtime_offset (uint64 static_address) throws Efni.Error {
|
||||
return (uint32) (translate (static_address) - reference_base);
|
||||
}
|
||||
|
||||
private static async Buffer read_mach_header (Debugger debugger, uint64 address, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
Buffer head = yield debugger.read_buffer (address, 32, cancellable);
|
||||
return yield debugger.read_buffer (address, 32 + head.read_uint32 (20), cancellable);
|
||||
}
|
||||
@@ -151,7 +153,7 @@ namespace Frida.Barebone {
|
||||
* only the first 16 bytes of each — every byte touched is guaranteed mapped.
|
||||
*/
|
||||
private static async uint64 find_collection_header (Machine machine, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var debugger = machine.debugger;
|
||||
|
||||
uint64 vbar = yield debugger.exception.thread.read_register ("vbar_el1", cancellable);
|
||||
@@ -175,11 +177,11 @@ namespace Frida.Barebone {
|
||||
return found;
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to locate the runtime kernel collection header");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to locate the runtime kernel collection header");
|
||||
}
|
||||
|
||||
private static async uint64 find_header_in (Machine machine, uint64 base_va, uint64 size,
|
||||
size_t page_size, Cancellable? cancellable) throws Error, IOError {
|
||||
size_t page_size, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var debugger = machine.debugger;
|
||||
|
||||
for (uint64 offset = 0; offset + 16 <= size; offset += CHUNK_SIZE) {
|
||||
@@ -188,7 +190,7 @@ namespace Frida.Barebone {
|
||||
Bytes taken;
|
||||
try {
|
||||
taken = yield machine.read_virtual (base_va + offset, chunk, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -203,7 +205,7 @@ namespace Frida.Barebone {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static Gee.List<FilesetEntry> parse_fileset_entries (Buffer image) throws Error {
|
||||
private static Gee.List<FilesetEntry> parse_fileset_entries (Buffer image) throws Efni.Error {
|
||||
var result = new Gee.ArrayList<FilesetEntry> ();
|
||||
uint32 ncmds = image.read_uint32 (16);
|
||||
size_t off = 32;
|
||||
@@ -222,7 +224,7 @@ namespace Frida.Barebone {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Gee.List<SegmentInfo> parse_segments (Buffer image, size_t header_offset) throws Error {
|
||||
private static Gee.List<SegmentInfo> parse_segments (Buffer image, size_t header_offset) throws Efni.Error {
|
||||
var result = new Gee.ArrayList<SegmentInfo> ();
|
||||
uint32 ncmds = image.read_uint32 (header_offset + 16);
|
||||
size_t off = header_offset + 32;
|
||||
@@ -241,7 +243,7 @@ namespace Frida.Barebone {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string read_lc_string (Buffer image, size_t offset) throws Error {
|
||||
private static string read_lc_string (Buffer image, size_t offset) throws Efni.Error {
|
||||
var builder = new StringBuilder ();
|
||||
for (size_t i = 0; ; i++) {
|
||||
uint8 c = image.read_uint8 (offset + i);
|
||||
|
||||
+17
-15
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class Layout : Object {
|
||||
@@ -25,16 +27,16 @@ namespace Frida.Barebone {
|
||||
Object (modules: new Gee.ArrayList<ModuleInfo> (), symbols: new Gee.ArrayList<SymbolInfo> ());
|
||||
}
|
||||
|
||||
public static Gum.DarwinModule parse_kernelcache (Bytes kerncache) throws Error {
|
||||
public static Gum.MachoModule parse_kernelcache (Bytes kerncache) throws Efni.Error {
|
||||
try {
|
||||
return new Gum.DarwinModule.from_blob (kerncache, ARM64, Gum.PtrauthSupport.SUPPORTED);
|
||||
return new Gum.MachoModule.from_blob (kerncache, ARM64, Gum.PtrauthSupport.SUPPORTED);
|
||||
} catch (Gum.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public static Layout load_from_module (Gum.DarwinModule mod, Bytes kerncache, uint64 kernel_base, ByteOrder byte_order,
|
||||
uint pointer_size) throws Error {
|
||||
public static Layout load_from_module (Gum.MachoModule mod, Bytes kerncache, uint64 kernel_base, ByteOrder byte_order,
|
||||
uint pointer_size) throws Efni.Error {
|
||||
var blob = new Blob (kerncache, byte_order, pointer_size);
|
||||
|
||||
var modules = compute_module_list (mod, blob, kernel_base);
|
||||
@@ -43,14 +45,14 @@ namespace Frida.Barebone {
|
||||
return new Layout (modules, symbols);
|
||||
}
|
||||
|
||||
private static Gee.List<ModuleInfo> compute_module_list (Gum.DarwinModule mod, Blob blob, uint64 kernel_base) throws Error {
|
||||
private static Gee.List<ModuleInfo> compute_module_list (Gum.MachoModule mod, Blob blob, uint64 kernel_base) throws Efni.Error {
|
||||
var fileset = compute_fileset_module_list (blob, mod.source_version);
|
||||
if (fileset != null)
|
||||
return fileset;
|
||||
|
||||
Buffer? kmod_info = null;
|
||||
Buffer? kmod_start = null;
|
||||
Error? pending_error = null;
|
||||
Efni.Error? pending_error = null;
|
||||
mod.enumerate_sections (s => {
|
||||
if (s.segment_name == "__PRELINK_INFO") {
|
||||
try {
|
||||
@@ -58,7 +60,7 @@ namespace Frida.Barebone {
|
||||
kmod_info = blob.slice (s.file_offset, (size_t) s.size, "__kmod_info");
|
||||
else if (s.section_name == "__kmod_start")
|
||||
kmod_start = blob.slice (s.file_offset, (size_t) s.size, "__kmod_start");
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
pending_error = e;
|
||||
return false;
|
||||
}
|
||||
@@ -107,7 +109,7 @@ namespace Frida.Barebone {
|
||||
while (start_pointers.available != 0)
|
||||
start_offsets.add ((uint) chained_pointer_to_vm_offset (start_pointers.read_pointer (), kernel_base));
|
||||
if (start_offsets.size != kexts.size + 1)
|
||||
throw new Error.PROTOCOL ("Unexpected __kmod_start length");
|
||||
throw new Efni.Error.PROTOCOL ("Unexpected __kmod_start length");
|
||||
|
||||
var n = kexts.size;
|
||||
for (var i = 0; i != n; i++) {
|
||||
@@ -129,7 +131,7 @@ namespace Frida.Barebone {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Gee.List<ModuleInfo>? compute_fileset_module_list (Blob blob, string? kernel_version) throws Error {
|
||||
private static Gee.List<ModuleInfo>? compute_fileset_module_list (Blob blob, string? kernel_version) throws Efni.Error {
|
||||
var header = blob.slice (0, 32, "Mach-O header");
|
||||
if (header.read_uint32 (0) != MH_MAGIC_64 || header.read_uint32 (12) != MH_FILESET)
|
||||
return null;
|
||||
@@ -178,7 +180,7 @@ namespace Frida.Barebone {
|
||||
return modules;
|
||||
}
|
||||
|
||||
private static string read_lc_string (Buffer image, size_t offset) throws Error {
|
||||
private static string read_lc_string (Buffer image, size_t offset) throws Efni.Error {
|
||||
var builder = new StringBuilder ();
|
||||
for (size_t i = 0; ; i++) {
|
||||
uint8 c = image.read_uint8 (offset + i);
|
||||
@@ -199,7 +201,7 @@ namespace Frida.Barebone {
|
||||
return target - (size_t) (kernel_base & CHAINED_PTR_TARGET_MASK);
|
||||
}
|
||||
|
||||
private static Gee.List<SymbolInfo> compute_symbol_list (Gum.DarwinModule mod) {
|
||||
private static Gee.List<SymbolInfo> compute_symbol_list (Gum.MachoModule mod) {
|
||||
var symbols = new Gee.ArrayList<SymbolInfo> ();
|
||||
mod.enumerate_symbols (s => {
|
||||
symbols.add (new SymbolInfo () {
|
||||
@@ -251,13 +253,13 @@ namespace Frida.Barebone {
|
||||
pointer_size = ptr_size;
|
||||
}
|
||||
|
||||
public Buffer slice (size_t offset, size_t n, string label) throws Error {
|
||||
public Buffer slice (size_t offset, size_t n, string label) throws Efni.Error {
|
||||
if (offset >= size)
|
||||
throw new Error.PROTOCOL ("Offset out of bounds while parsing %s", label);
|
||||
throw new Efni.Error.PROTOCOL ("Offset out of bounds while parsing %s", label);
|
||||
|
||||
size_t end = offset + n;
|
||||
if (end > size)
|
||||
throw new Error.PROTOCOL ("Size out of bounds while parsing %s", label);
|
||||
throw new Efni.Error.PROTOCOL ("Size out of bounds while parsing %s", label);
|
||||
|
||||
return new Buffer (bytes[offset:end], byte_order, pointer_size);
|
||||
}
|
||||
|
||||
+12
-10
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
/**
|
||||
@@ -6,7 +8,7 @@ namespace Frida.Barebone {
|
||||
* image in memory says where it landed, and every symbol moves by the same distance.
|
||||
*/
|
||||
public static async LinuxLayout collect_linux_layout (Machine machine,
|
||||
Gee.List<SymbolInfo> symbols, Cancellable? cancellable) throws Error, IOError {
|
||||
Gee.List<SymbolInfo> symbols, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
|
||||
uint64 linked_base = base_of (symbols);
|
||||
uint64 running_base = yield find_running_kernel (machine, linked_base, symbols, cancellable);
|
||||
@@ -75,7 +77,7 @@ namespace Frida.Barebone {
|
||||
* address its symbols were linked for.
|
||||
*/
|
||||
private static async uint64 find_running_kernel (Machine machine, uint64 linked_base,
|
||||
Gee.List<SymbolInfo> symbols, Cancellable? cancellable) throws Error, IOError {
|
||||
Gee.List<SymbolInfo> symbols, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
// A guest idling in a shell is executing userspace, whose addresses say nothing about
|
||||
// where the kernel is and cannot be walked with the kernel's tables.
|
||||
yield machine.enter_exception_level (1, ENTER_KERNEL_TIMEOUT_MS, cancellable);
|
||||
@@ -125,22 +127,22 @@ namespace Frida.Barebone {
|
||||
IMAGE_MAGIC.length, cancellable);
|
||||
if (Memory.cmp (header.get_data (), IMAGE_MAGIC.data, IMAGE_MAGIC.length) == 0)
|
||||
return candidate;
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
}
|
||||
|
||||
candidate -= KERNEL_ALIGNMENT;
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to find the running kernel; is the guest in kernel mode?");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to find the running kernel; is the guest in kernel mode?");
|
||||
}
|
||||
|
||||
private static async uint64 find_relocated_kernel (Machine machine, uint64 linked_base,
|
||||
Gee.List<SymbolInfo> symbols, Cancellable? cancellable) throws Error, IOError {
|
||||
Gee.List<SymbolInfo> symbols, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
uint64 banner = address_of (symbols, KERNEL_BANNER_SYMBOL);
|
||||
if (banner == 0)
|
||||
throw new Error.NOT_SUPPORTED ("System.map names no %s to anchor relocation", KERNEL_BANNER_SYMBOL);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("System.map names no %s to anchor relocation", KERNEL_BANNER_SYMBOL);
|
||||
|
||||
uint64 banner_offset = banner - linked_base;
|
||||
uint64 span = span_of (symbols, linked_base);
|
||||
@@ -161,11 +163,11 @@ namespace Frida.Barebone {
|
||||
return landing;
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to find the relocated kernel; is the guest in kernel mode?");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to find the relocated kernel; is the guest in kernel mode?");
|
||||
}
|
||||
|
||||
private static async uint64 read_program_counter (Debugger debugger, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var registers = yield debugger.exception.thread.read_registers (cancellable);
|
||||
return registers.has_key ("rip")
|
||||
? registers["rip"].get_uint64 ()
|
||||
@@ -173,11 +175,11 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async bool banner_present_at (Debugger debugger, uint64 address, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
try {
|
||||
var head = yield debugger.read_byte_array (address, KERNEL_BANNER.length, cancellable);
|
||||
return Memory.cmp (head.get_data (), KERNEL_BANNER.data, KERNEL_BANNER.length) == 0;
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@ namespace Frida.Barebone.LZFSE {
|
||||
return d[0] == 'b' && d[1] == 'v' && d[2] == 'x';
|
||||
}
|
||||
|
||||
public Bytes decode (Bytes compressed) throws Error {
|
||||
public Bytes decode (Bytes compressed) throws Efni.Error {
|
||||
size_t est_max = compressed.get_size () * 15;
|
||||
uint8[] dst_buf = new uint8[est_max];
|
||||
|
||||
size_t written = decode_buffer (dst_buf, compressed.get_data ());
|
||||
if (written == 0)
|
||||
throw new Error.PROTOCOL ("liblzfse returned 0 bytes");
|
||||
throw new Efni.Error.PROTOCOL ("liblzfse returned 0 bytes");
|
||||
|
||||
return new Bytes (dst_buf[:written]);
|
||||
}
|
||||
|
||||
+36
-34
@@ -1,8 +1,10 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public interface PhysicalMemory : Object {
|
||||
public abstract uint8[] read (uint64 pa, size_t size) throws Error;
|
||||
public abstract void write (uint64 pa, uint8[] data) throws Error;
|
||||
public abstract uint8[] read (uint64 pa, size_t size) throws Efni.Error;
|
||||
public abstract void write (uint64 pa, uint8[] data) throws Efni.Error;
|
||||
public abstract bool contains (uint64 pa);
|
||||
}
|
||||
|
||||
@@ -20,20 +22,20 @@ namespace Frida.Barebone {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async size_t query_page_size (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async size_t query_page_size (Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
|
||||
public virtual async Bytes read_virtual (uint64 va, size_t size, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
return yield debugger.read_byte_array (va, size, cancellable);
|
||||
}
|
||||
|
||||
public virtual async void write_virtual (uint64 va, uint8[] data, Cancellable? cancellable) throws Error, IOError {
|
||||
public virtual async void write_virtual (uint64 va, uint8[] data, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield debugger.write_byte_array (va, new Bytes (data), cancellable);
|
||||
}
|
||||
|
||||
public abstract async uint query_exception_level (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async uint query_exception_level (Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
|
||||
public async void enter_exception_level (uint level, uint timeout, Cancellable? cancellable) throws Error, IOError {
|
||||
public async void enter_exception_level (uint level, uint timeout, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var timer = new Timer ();
|
||||
|
||||
do {
|
||||
@@ -51,31 +53,31 @@ namespace Frida.Barebone {
|
||||
yield debugger.stop (cancellable);
|
||||
} while ((uint) (timer.elapsed () * 1000.0) < timeout);
|
||||
|
||||
throw new Error.TIMED_OUT ("Timed out while trying to get target to exception level %u", level);
|
||||
throw new Efni.Error.TIMED_OUT ("Timed out while trying to get target to exception level %u", level);
|
||||
}
|
||||
|
||||
public abstract async void enumerate_ranges (Gum.PageProtection prot, FoundRangeFunc func, Cancellable? cancellable)
|
||||
throws Error, IOError;
|
||||
throws Efni.Error, IOError;
|
||||
|
||||
public virtual async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Address translation is not implemented for this architecture");
|
||||
public virtual async uint64 translate_address (uint64 va, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Address translation is not implemented for this architecture");
|
||||
}
|
||||
|
||||
public abstract async Allocation allocate_pages (Gee.List<uint64?> physical_addresses, Cancellable? cancellable)
|
||||
throws Error, IOError;
|
||||
throws Efni.Error, IOError;
|
||||
|
||||
public virtual async void protect_pages_leaving_the_flush (uint64 virtual_address, size_t size,
|
||||
Gum.PageProtection prot, Cancellable? cancellable) throws Error, IOError {
|
||||
Gum.PageProtection prot, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield protect_pages (virtual_address, size, prot, cancellable);
|
||||
}
|
||||
|
||||
public abstract async void protect_pages (uint64 virtual_address, size_t size, Gum.PageProtection prot,
|
||||
Cancellable? cancellable) throws Error, IOError;
|
||||
Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
|
||||
public abstract async Gee.List<uint64?> scan_ranges (Gee.List<Gum.MemoryRange?> ranges, MatchPattern pattern,
|
||||
uint max_matches, Cancellable? cancellable) throws Error, IOError;
|
||||
uint max_matches, Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
|
||||
public Bytes relocate (Gum.ElfModule elf, Bytes raw_elf, uint64 base_va) throws Error {
|
||||
public Bytes relocate (Gum.ElfModule elf, Bytes raw_elf, uint64 base_va) throws Efni.Error {
|
||||
uint64 file_start = uint64.MAX;
|
||||
uint64 file_end = 0;
|
||||
elf.enumerate_segments (s => {
|
||||
@@ -92,7 +94,7 @@ namespace Frida.Barebone {
|
||||
bool position_independent = elf.etype == Gum.ElfType.DYN;
|
||||
|
||||
var relocated_buf = debugger.make_buffer (new Bytes (raw_elf[(size_t) file_start:(size_t) file_end].get_data ()));
|
||||
Error? pending_error = null;
|
||||
Efni.Error? pending_error = null;
|
||||
elf.enumerate_relocations (r => {
|
||||
unowned string parent_section = (r.parent != null) ? r.parent.name : "";
|
||||
if (parent_section.has_prefix (".rela.debug_"))
|
||||
@@ -104,7 +106,7 @@ namespace Frida.Barebone {
|
||||
|
||||
try {
|
||||
apply_relocation (r, base_va, relocated_buf);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
pending_error = e;
|
||||
return false;
|
||||
}
|
||||
@@ -126,7 +128,7 @@ namespace Frida.Barebone {
|
||||
return section != ".rela.dyn" && section != ".rel.dyn";
|
||||
}
|
||||
|
||||
public abstract void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Error;
|
||||
public abstract void apply_relocation (Gum.ElfRelocationDetails r, uint64 base_va, Buffer relocated) throws Efni.Error;
|
||||
|
||||
// Code that uses displacements in the instructions needs no relocation, and its relocation
|
||||
// section holds types that this code does not know. Code that uses absolute addresses needs
|
||||
@@ -135,16 +137,16 @@ namespace Frida.Barebone {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async uint64 invoke (uint64 impl, uint64[] args, Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
|
||||
public abstract async CallFrame load_call_frame (DebuggerThread thread, uint arity, Cancellable? cancellable)
|
||||
throws Error, IOError;
|
||||
throws Efni.Error, IOError;
|
||||
|
||||
public abstract uint64 address_from_funcptr (uint64 ptr);
|
||||
public abstract size_t breakpoint_size_from_funcptr (uint64 ptr);
|
||||
|
||||
public abstract async InlineHook create_inline_hook (uint64 target, uint64 handler, Allocator allocator,
|
||||
Cancellable? cancellable) throws Error, IOError;
|
||||
Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
}
|
||||
|
||||
public delegate bool FoundRangeFunc (RangeDetails details);
|
||||
@@ -197,7 +199,7 @@ namespace Frida.Barebone {
|
||||
DEVICE;
|
||||
|
||||
public string to_nick () {
|
||||
return Marshal.enum_to_nick<MappingType> (this);
|
||||
return Efni.Marshal.enum_to_nick<MappingType> (this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,13 +219,13 @@ namespace Frida.Barebone {
|
||||
|
||||
public abstract void force_return ();
|
||||
|
||||
public abstract async void commit (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void commit (Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
}
|
||||
|
||||
public interface InlineHook : Object {
|
||||
public abstract async void destroy (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void enable (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void disable (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void destroy (Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
public abstract async void enable (Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
public abstract async void disable (Cancellable? cancellable) throws Efni.Error, IOError;
|
||||
}
|
||||
|
||||
internal enum AddressingMode {
|
||||
@@ -235,7 +237,7 @@ namespace Frida.Barebone {
|
||||
// arrives while the next command is pending, and that command consumes it instead — leaving
|
||||
// its caller with an empty response and the stub still in the old mode. Vala rejects a yield
|
||||
// inside a finally block, so callers hold on to any failure across the restore.
|
||||
internal static async bool halt_guest (Debugger debugger, Cancellable? cancellable) throws Error, IOError {
|
||||
internal static async bool halt_guest (Debugger debugger, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
bool was_running = debugger.state != STOPPED;
|
||||
if (was_running)
|
||||
yield debugger.stop (cancellable);
|
||||
@@ -244,13 +246,13 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
internal static async void resume_guest (Debugger debugger, bool was_running, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (was_running)
|
||||
yield debugger.resume (cancellable);
|
||||
}
|
||||
|
||||
internal static async bool enter_physical_addressing (Debugger debugger, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
bool was_running = yield halt_guest (debugger, cancellable);
|
||||
|
||||
yield set_addressing_mode (debugger, PHYSICAL, cancellable);
|
||||
@@ -259,23 +261,23 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
internal static async void leave_physical_addressing (Debugger debugger, bool was_running, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield set_addressing_mode (debugger, VIRTUAL, cancellable);
|
||||
|
||||
yield resume_guest (debugger, was_running, cancellable);
|
||||
}
|
||||
|
||||
internal static async void set_addressing_mode (Debugger debugger, AddressingMode mode, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield debugger.set_physical_memory_mode (mode == PHYSICAL, cancellable);
|
||||
}
|
||||
|
||||
internal static void throw_if_failed (GLib.Error? failure) throws Error, IOError {
|
||||
internal static void throw_if_failed (GLib.Error? failure) throws Efni.Error, IOError {
|
||||
if (failure == null)
|
||||
return;
|
||||
if (failure is IOError)
|
||||
throw (IOError) failure;
|
||||
throw (Error) failure;
|
||||
throw (Efni.Error) failure;
|
||||
}
|
||||
|
||||
internal static Gee.List<RangeDetails> coalesce_ranges (Gee.List<RangeDetails> ranges) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public class MatchPattern {
|
||||
@@ -15,7 +17,7 @@ namespace Frida.Barebone {
|
||||
return true;
|
||||
}
|
||||
|
||||
public MatchPattern.from_string (string pattern) throws Error {
|
||||
public MatchPattern.from_string (string pattern) throws Efni.Error {
|
||||
string[] parts = pattern.replace (" ", "").split (":", 2);
|
||||
|
||||
unowned string match_str = parts[0];
|
||||
@@ -86,7 +88,7 @@ namespace Frida.Barebone {
|
||||
return t;
|
||||
}
|
||||
|
||||
private static uint8 parse_xdigit_value (char ch) throws Error {
|
||||
private static uint8 parse_xdigit_value (char ch) throws Efni.Error {
|
||||
int v = ch.xdigit_value ();
|
||||
if (v == -1)
|
||||
throw_invalid_pattern ();
|
||||
@@ -94,8 +96,8 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private static void throw_invalid_pattern () throws Error {
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid pattern");
|
||||
private static void throw_invalid_pattern () throws Efni.Error {
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Invalid pattern");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +230,7 @@ namespace Frida.Barebone {
|
||||
builder
|
||||
.align (pointer_size)
|
||||
.append_label ("matches");
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
|
||||
Generated
+4
-4
@@ -8,7 +8,7 @@
|
||||
"name": "frida-barebone-script-runtime",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@types/frida-gum": "^19.12.0",
|
||||
"@types/frida-gum": "^19.13.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
@@ -439,9 +439,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/frida-gum": {
|
||||
"version": "19.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.12.0.tgz",
|
||||
"integrity": "sha512-fqXtwFA8AwE89tSdNHlaY+hNWI5j62bcO1xk5w0NSuHBIH6KRxYzIK6rp/JP7TXtRKU2ewBwx81FrHTfGF0q7w==",
|
||||
"version": "19.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.13.0.tgz",
|
||||
"integrity": "sha512-tbmEZj9JUt3S6FqFE4XJPi/WhQuUU0/sM1IWS1KDKqvy6077hwNjG1v4MC3wpk+FO/WzfZ8G1gAKqAKJosfj1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/frida-gum": "^19.12.0",
|
||||
"@types/frida-gum": "^19.13.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class ParallelsStubClient : GDB.Client {
|
||||
@@ -8,24 +10,24 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public static new async ParallelsStubClient open (IOStream stream, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var client = new ParallelsStubClient (stream);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
Efni.throw_api_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
protected override async void detect_vendor_features (Cancellable? cancellable) throws Error, IOError {
|
||||
protected override async void detect_vendor_features (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
supported_features.add ("parallels");
|
||||
supported_features.add ("protected-code");
|
||||
}
|
||||
|
||||
protected override async void enable_extensions (Cancellable? cancellable) throws Error, IOError {
|
||||
protected override async void enable_extensions (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
string info = yield query_property ("HostInfo", cancellable);
|
||||
var host = GDB.Client.PropertyDictionary.parse (info);
|
||||
|
||||
|
||||
+11
-9
@@ -1,7 +1,9 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public static async uint64 find_export (Machine machine, uint64 image, string wanted, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
foreach (Export e in yield enumerate_exports (machine, image, cancellable)) {
|
||||
if (e.name == wanted)
|
||||
return image + e.rva;
|
||||
@@ -13,7 +15,7 @@ namespace Frida.Barebone {
|
||||
// Read the full export directory once, because its tables and its strings are adjacent. Thus
|
||||
// a module with one thousand exports needs one transfer.
|
||||
public static async Gee.List<Export> enumerate_exports (Machine machine, uint64 image, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var exports = new Gee.ArrayList<Export> ();
|
||||
|
||||
Debugger debugger = machine.debugger;
|
||||
@@ -21,7 +23,7 @@ namespace Frida.Barebone {
|
||||
Buffer headers;
|
||||
try {
|
||||
headers = debugger.make_buffer (yield debugger.read_byte_array (image, HEADERS_SIZE, cancellable));
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
return exports;
|
||||
}
|
||||
if (headers.read_uint16 (0) != DOS_SIGNATURE)
|
||||
@@ -84,13 +86,13 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public static async uint32 read_image_size (Machine machine, uint64 image, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
Buffer headers;
|
||||
try {
|
||||
headers = debugger.make_buffer (yield debugger.read_byte_array (image, HEADERS_SIZE, cancellable));
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
return 0;
|
||||
}
|
||||
if (headers.read_uint16 (0) != DOS_SIGNATURE)
|
||||
@@ -106,10 +108,10 @@ namespace Frida.Barebone {
|
||||
// otherwise one page at a time, letting the absent pages read as zero, because no name or
|
||||
// table entry has that value.
|
||||
private static async Bytes read_present_pages (Debugger debugger, uint64 address, size_t size,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
try {
|
||||
return yield debugger.read_byte_array (address, size, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
}
|
||||
|
||||
var result = new uint8[size];
|
||||
@@ -126,14 +128,14 @@ namespace Frida.Barebone {
|
||||
.get_data ();
|
||||
Memory.copy ((uint8 *) result + offset, page, chunk);
|
||||
present++;
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
}
|
||||
|
||||
offset += chunk;
|
||||
}
|
||||
|
||||
if (present == 0)
|
||||
throw new Error.NOT_SUPPORTED (
|
||||
throw new Efni.Error.NOT_SUPPORTED (
|
||||
"Unable to read any page of 0x%" + uint64.FORMAT_MODIFIER + "x", address);
|
||||
|
||||
return new Bytes.take ((owned) result);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class QmpClient : Object, AsyncInitable {
|
||||
@@ -22,9 +24,9 @@ namespace Frida.Barebone {
|
||||
private OutputStream output;
|
||||
|
||||
private bool is_connected = false;
|
||||
private Promise<bool> close_request = new Promise<bool> ();
|
||||
private Efni.Promise<bool> close_request = new Efni.Promise<bool> ();
|
||||
|
||||
private Gee.Map<uint, Promise<Json.Node>> pending_requests = new Gee.HashMap<uint, Promise<Json.Node>> ();
|
||||
private Gee.Map<uint, Efni.Promise<Json.Node>> pending_requests = new Gee.HashMap<uint, Efni.Promise<Json.Node>> ();
|
||||
private uint next_request_id = 1;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
@@ -41,27 +43,27 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public static async QmpClient open (string? address = null, uint16 port = 0, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var client = new QmpClient (address, port);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
Efni.throw_api_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
{
|
||||
SocketConnectable connectable;
|
||||
if (address.has_prefix ("unix:")) {
|
||||
connectable = new UnixSocketAddress.with_type (address.substring (5), -1,
|
||||
connectable = new UnixSocketAddress.with_type (address.substring (5).to_utf8 (),
|
||||
UnixSocketAddressType.PATH);
|
||||
} else {
|
||||
string endpoint = address.has_prefix ("tcp:") ? address.substring (4) : address;
|
||||
connectable = parse_socket_address (endpoint, port, "localhost", 4444);
|
||||
connectable = Efni.parse_socket_address (endpoint, port, "localhost", 4444);
|
||||
}
|
||||
|
||||
SocketConnection socket_connection;
|
||||
@@ -69,7 +71,7 @@ namespace Frida.Barebone {
|
||||
var client = new SocketClient ();
|
||||
socket_connection = yield client.connect_async (connectable, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("Unable to connect to QMP server: %s", e.message);
|
||||
throw new Efni.Error.TRANSPORT ("Unable to connect to QMP server: %s", e.message);
|
||||
}
|
||||
|
||||
var socket = socket_connection.socket;
|
||||
@@ -89,7 +91,7 @@ namespace Frida.Barebone {
|
||||
|
||||
string? greeting_line = yield input.read_line_async (Priority.DEFAULT, cancellable);
|
||||
if (greeting_line == null)
|
||||
throw new Error.TRANSPORT ("Connection closed during QMP greeting");
|
||||
throw new Efni.Error.TRANSPORT ("Connection closed during QMP greeting");
|
||||
|
||||
process_incoming_messages.begin ();
|
||||
started_message_processing = true;
|
||||
@@ -102,22 +104,22 @@ namespace Frida.Barebone {
|
||||
if (!started_message_processing)
|
||||
close_request.resolve (true);
|
||||
|
||||
if (e is Error)
|
||||
throw_api_error (e);
|
||||
if (e is Efni.Error)
|
||||
Efni.throw_api_error (e);
|
||||
else
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
throw new Efni.Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
#if WINDOWS
|
||||
private static uint16 pick_free_port () throws Error {
|
||||
private static uint16 pick_free_port () throws Efni.Error {
|
||||
try {
|
||||
var listener = new SocketListener ();
|
||||
uint16 port = listener.add_any_inet_port (null);
|
||||
listener.close ();
|
||||
return port;
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("Unable to reserve a port: %s", e.message);
|
||||
throw new Efni.Error.TRANSPORT ("Unable to reserve a port: %s", e.message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -127,12 +129,12 @@ namespace Frida.Barebone {
|
||||
|
||||
try {
|
||||
yield close_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
public async void wait_until_hostlink_is_open (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void wait_until_hostlink_is_open (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
while (!hostlink_is_open) {
|
||||
hostlink_open_handler = wait_until_hostlink_is_open.callback;
|
||||
yield;
|
||||
@@ -159,7 +161,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Hostlink open_hostlink (string? preferred_bus = null, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint64 mmio = 0;
|
||||
uint irq = 0;
|
||||
string bus = preferred_bus;
|
||||
@@ -194,7 +196,7 @@ namespace Frida.Barebone {
|
||||
string parent;
|
||||
try {
|
||||
parent = yield get_qom_property_string ("/machine/peripheral/" + controller, "parent_bus", cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -213,7 +215,7 @@ namespace Frida.Barebone {
|
||||
Cancellable? cancellable) throws IOError {
|
||||
try {
|
||||
yield delete_device (device, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -221,7 +223,7 @@ namespace Frida.Barebone {
|
||||
try {
|
||||
yield remove_chardev (chardev, cancellable);
|
||||
return;
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
if (!("busy" in e.message))
|
||||
return;
|
||||
}
|
||||
@@ -234,7 +236,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async void delete_device (string id, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void delete_device (string id, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var args = new Json.Builder ();
|
||||
args
|
||||
.begin_object ()
|
||||
@@ -244,7 +246,7 @@ namespace Frida.Barebone {
|
||||
yield execute_command ("device_del", args.get_root (), cancellable);
|
||||
}
|
||||
|
||||
private async void remove_chardev (string id, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void remove_chardev (string id, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var args = new Json.Builder ();
|
||||
args
|
||||
.begin_object ()
|
||||
@@ -255,7 +257,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async IOStream plug_hostlink (string chardev, string device, string bus,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
#if WINDOWS
|
||||
uint16 hostlink_tcp_port = pick_free_port ();
|
||||
|
||||
@@ -268,7 +270,7 @@ namespace Frida.Barebone {
|
||||
Tcp.enable_nodelay (conn.socket);
|
||||
return conn;
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("Unable to connect to the hostlink: %s", e.message);
|
||||
throw new Efni.Error.TRANSPORT ("Unable to connect to the hostlink: %s", e.message);
|
||||
}
|
||||
#else
|
||||
string socket_path = Path.build_filename (Environment.get_tmp_dir (),
|
||||
@@ -281,7 +283,7 @@ namespace Frida.Barebone {
|
||||
try {
|
||||
return yield client.connect_async (new UnixSocketAddress (socket_path), cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("Unable to connect to %s: %s", socket_path, e.message);
|
||||
throw new Efni.Error.TRANSPORT ("Unable to connect to %s: %s", socket_path, e.message);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -292,22 +294,22 @@ namespace Frida.Barebone {
|
||||
public uint irq;
|
||||
}
|
||||
|
||||
private async int64 get_qom_property_int (string path, string property, Cancellable? cancellable) throws Error, IOError {
|
||||
private async int64 get_qom_property_int (string path, string property, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var val = yield get_qom_property (path, property, cancellable);
|
||||
if (val.get_value_type () != typeof (int64))
|
||||
throw new Error.PROTOCOL ("Expected '%s' property on %s to be an integer", property, path);
|
||||
throw new Efni.Error.PROTOCOL ("Expected '%s' property on %s to be an integer", property, path);
|
||||
return val.get_int ();
|
||||
}
|
||||
|
||||
private async string get_qom_property_string (string path, string property, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var val = yield get_qom_property (path, property, cancellable);
|
||||
if (val.get_value_type () != typeof (string))
|
||||
throw new Error.PROTOCOL ("Expected '%s' property on %s to be a string", property, path);
|
||||
throw new Efni.Error.PROTOCOL ("Expected '%s' property on %s to be a string", property, path);
|
||||
return val.get_string ();
|
||||
}
|
||||
|
||||
private async Json.Node get_qom_property (string path, string property, Cancellable? cancellable) throws Error, IOError {
|
||||
private async Json.Node get_qom_property (string path, string property, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var args = new Json.Builder ();
|
||||
args
|
||||
.begin_object ()
|
||||
@@ -320,7 +322,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void add_listening_chardev (string id, string endpoint, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var args = new Json.Builder ();
|
||||
args
|
||||
.begin_object ()
|
||||
@@ -365,7 +367,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void add_serial_port (string chardev, string bus, string name, string id, uint nr, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var args = new Json.Builder ();
|
||||
args
|
||||
.begin_object ()
|
||||
@@ -386,7 +388,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Json.Node execute_command (string command, Json.Node? arguments = null, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
check_connected ();
|
||||
|
||||
Request request = begin_request (command, arguments);
|
||||
@@ -395,7 +397,7 @@ namespace Frida.Barebone {
|
||||
yield output.write_all_async (request.json.data, Priority.DEFAULT, cancellable, null);
|
||||
} catch (GLib.Error e) {
|
||||
cancel_request (request);
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
throw new Efni.Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
return yield join_request (request, cancellable);
|
||||
@@ -404,7 +406,7 @@ namespace Frida.Barebone {
|
||||
private Request begin_request (string command, Json.Node? arguments) {
|
||||
uint id = next_request_id++;
|
||||
|
||||
var promise = new Promise<Json.Node> ();
|
||||
var promise = new Efni.Promise<Json.Node> ();
|
||||
pending_requests[id] = promise;
|
||||
|
||||
string json = build_request (command, id, arguments);
|
||||
@@ -438,12 +440,12 @@ namespace Frida.Barebone {
|
||||
return Json.to_string (message, false) + "\n";
|
||||
}
|
||||
|
||||
private async Json.Node join_request (Request request, Cancellable? cancellable) throws Error, IOError {
|
||||
private async Json.Node join_request (Request request, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var timeout_source = new TimeoutSource (REQUEST_TIMEOUT_MS);
|
||||
timeout_source.set_callback (() => {
|
||||
Promise<Json.Node>? p;
|
||||
Efni.Promise<Json.Node>? p;
|
||||
if (pending_requests.unset (request.id, out p))
|
||||
p.reject (new Error.TIMED_OUT ("QMP command timed out"));
|
||||
p.reject (new Efni.Error.TIMED_OUT ("QMP command timed out"));
|
||||
return Source.REMOVE;
|
||||
});
|
||||
timeout_source.attach (MainContext.get_thread_default ());
|
||||
@@ -460,14 +462,14 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private class Request {
|
||||
public Promise<Json.Node> promise;
|
||||
public Efni.Promise<Json.Node> promise;
|
||||
public string json;
|
||||
public uint id;
|
||||
}
|
||||
|
||||
private void check_connected () throws Error {
|
||||
private void check_connected () throws Efni.Error {
|
||||
if (!is_connected)
|
||||
throw new Error.INVALID_OPERATION ("QMP client is not connected");
|
||||
throw new Efni.Error.INVALID_OPERATION ("QMP client is not connected");
|
||||
}
|
||||
|
||||
private async void process_incoming_messages () {
|
||||
@@ -484,7 +486,7 @@ namespace Frida.Barebone {
|
||||
is_connected = false;
|
||||
|
||||
foreach (var promise in pending_requests.values)
|
||||
promise.reject (new Error.TRANSPORT ("QMP connection closed"));
|
||||
promise.reject (new Efni.Error.TRANSPORT ("QMP connection closed"));
|
||||
pending_requests.clear ();
|
||||
}
|
||||
|
||||
@@ -503,7 +505,7 @@ namespace Frida.Barebone {
|
||||
close_request.resolve (true);
|
||||
}
|
||||
|
||||
private void handle_message (owned Json.Node message) throws Error {
|
||||
private void handle_message (owned Json.Node message) throws Efni.Error {
|
||||
var r = make_json_reader_taking_node ((owned) message);
|
||||
|
||||
bool is_response = r.read_member ("id");
|
||||
@@ -512,7 +514,7 @@ namespace Frida.Barebone {
|
||||
id = (uint) r.get_int_value ();
|
||||
GLib.Error? e = r.get_error ();
|
||||
if (e != null)
|
||||
throw new Error.PROTOCOL ("Malformed message: %s", e.message);
|
||||
throw new Efni.Error.PROTOCOL ("Malformed message: %s", e.message);
|
||||
}
|
||||
r.end_member ();
|
||||
|
||||
@@ -522,8 +524,8 @@ namespace Frida.Barebone {
|
||||
handle_event (r);
|
||||
}
|
||||
|
||||
private void handle_response (uint request_id, Json.Reader r) throws Error {
|
||||
Promise<Json.Node>? promise;
|
||||
private void handle_response (uint request_id, Json.Reader r) throws Efni.Error {
|
||||
Efni.Promise<Json.Node>? promise;
|
||||
if (!pending_requests.unset (request_id, out promise))
|
||||
return;
|
||||
|
||||
@@ -531,10 +533,10 @@ namespace Frida.Barebone {
|
||||
r.read_member ("desc");
|
||||
string? description = r.get_string_value ();
|
||||
if (description == null)
|
||||
throw new Error.PROTOCOL ("Malformed message: %s", r.get_error ().message);
|
||||
throw new Efni.Error.PROTOCOL ("Malformed message: %s", r.get_error ().message);
|
||||
r.end_member ();
|
||||
|
||||
promise.reject (new Error.NOT_SUPPORTED ("%s", description));
|
||||
promise.reject (new Efni.Error.NOT_SUPPORTED ("%s", description));
|
||||
return;
|
||||
}
|
||||
r.end_member ();
|
||||
@@ -543,17 +545,17 @@ namespace Frida.Barebone {
|
||||
promise.resolve (r.get_current_node ());
|
||||
}
|
||||
|
||||
private void handle_event (Json.Reader r) throws Error {
|
||||
private void handle_event (Json.Reader r) throws Efni.Error {
|
||||
r.read_member ("event");
|
||||
string? name = r.get_string_value ();
|
||||
if (name == null)
|
||||
throw new Error.PROTOCOL ("Malformed event message: missing 'event' property");
|
||||
throw new Efni.Error.PROTOCOL ("Malformed event message: missing 'event' property");
|
||||
r.end_member ();
|
||||
|
||||
Json.Node? data = null;
|
||||
if (r.read_member ("data")) {
|
||||
if (!r.is_object ())
|
||||
throw new Error.PROTOCOL ("Malformed event message: 'data' must be an object");
|
||||
throw new Efni.Error.PROTOCOL ("Malformed event message: 'data' must be an object");
|
||||
data = r.get_current_node ();
|
||||
}
|
||||
r.end_member ();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
private sealed class RustModule : Object {
|
||||
@@ -23,7 +25,7 @@ namespace Frida.Barebone {
|
||||
private Callback console_log_callback;
|
||||
|
||||
public async RustModule.from_string (string str, Gee.Map<string, uint64?> symbols, Gee.List<string> dependencies,
|
||||
Machine machine, Allocator allocator, Cancellable? cancellable) throws Error, IOError {
|
||||
Machine machine, Allocator allocator, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var assets = yield new CompilationAssets (str, symbols, dependencies, machine, cancellable);
|
||||
|
||||
int exit_status;
|
||||
@@ -73,15 +75,15 @@ namespace Frida.Barebone {
|
||||
yield tool.communicate_utf8_async (null, cancellable, out output, null);
|
||||
exit_status = tool.get_exit_status ();
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
if (exit_status != 0)
|
||||
throw new Error.INVALID_ARGUMENT ("Compilation failed: %s", output.chomp ());
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Compilation failed: %s", output.chomp ());
|
||||
|
||||
try {
|
||||
elf = new Gum.ElfModule.from_file (assets.output_elf.get_path ());
|
||||
} catch (Gum.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
|
||||
var raw_elf = new Bytes.static (elf.get_file_data ());
|
||||
@@ -144,7 +146,7 @@ namespace Frida.Barebone {
|
||||
public File output_elf;
|
||||
|
||||
public async CompilationAssets (string code, Gee.Map<string, uint64?> symbols, Gee.List<string> dependencies,
|
||||
Machine machine, Cancellable? cancellable) throws Error, IOError {
|
||||
Machine machine, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
try {
|
||||
int io_priority = Priority.DEFAULT;
|
||||
|
||||
@@ -171,14 +173,14 @@ namespace Frida.Barebone {
|
||||
|
||||
yield write_text_file (workdir, "module.lds", make_linker_script (code, symbols), cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PERMISSION_DENIED ("%s", e.message);
|
||||
throw new Efni.Error.PERMISSION_DENIED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
~CompilationAssets () {
|
||||
try {
|
||||
FS.rmtree (workdir);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+73
-71
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
namespace Frida {
|
||||
private sealed class BareboneScript : Object {
|
||||
public signal void message (string json, Bytes? data);
|
||||
@@ -81,7 +83,7 @@ namespace Frida {
|
||||
private const uint64 MAX_ASSET_SIZE = 100 * 1024 * 1024;
|
||||
private const uint32 MAX_JS_BYTE_ARRAY_LENGTH = 100 * 1024 * 1024;
|
||||
|
||||
public static BareboneScript create (AgentScriptId id, string source, Barebone.Services services) throws Error {
|
||||
public static BareboneScript create (AgentScriptId id, string source, Barebone.Services services) throws Efni.Error {
|
||||
var script = new BareboneScript (id, services);
|
||||
|
||||
unowned string runtime_js = (string) Frida.Data.Barebone.get_script_runtime_js_blob ().data;
|
||||
@@ -380,7 +382,7 @@ namespace Frida {
|
||||
foreach (var listener in invocation_listeners.to_array ()) {
|
||||
try {
|
||||
yield interceptor.detach (listener, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
}
|
||||
}
|
||||
invocation_listeners.clear ();
|
||||
@@ -460,7 +462,7 @@ namespace Frida {
|
||||
perform_pending_io ();
|
||||
}
|
||||
|
||||
private void add_program (string source, string name) throws Error {
|
||||
private void add_program (string source, string name) throws Efni.Error {
|
||||
unowned string package_marker = "📦\n";
|
||||
unowned string delimiter_marker = "\n✄\n";
|
||||
unowned string alias_marker = "↻ ";
|
||||
@@ -544,8 +546,8 @@ namespace Frida {
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private static void throw_malformed_package () throws Error {
|
||||
throw new Error.INVALID_ARGUMENT ("Malformed package");
|
||||
private static void throw_malformed_package () throws Efni.Error {
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Malformed package");
|
||||
}
|
||||
|
||||
private string * normalize_module_name (QuickJS.Context ctx, string base_name, string name) {
|
||||
@@ -600,10 +602,10 @@ namespace Frida {
|
||||
try {
|
||||
Asset? asset = assets[module_name];
|
||||
if (asset == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Could not load module '%s'", module_name);
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Could not load module '%s'", module_name);
|
||||
|
||||
val = compile_module (asset);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
throw_js_error (error_message_to_js (e.message));
|
||||
return null;
|
||||
}
|
||||
@@ -614,7 +616,7 @@ namespace Frida {
|
||||
return mod;
|
||||
}
|
||||
|
||||
private QuickJS.Value compile_module (Asset asset) throws Error {
|
||||
private QuickJS.Value compile_module (Asset asset) throws Efni.Error {
|
||||
var val = ctx.eval (asset.data, asset.data.length, asset.name,
|
||||
QuickJS.EvalType.MODULE |
|
||||
QuickJS.EvalFlag.STRICT |
|
||||
@@ -622,13 +624,13 @@ namespace Frida {
|
||||
|
||||
if (val.is_exception ()) {
|
||||
JSError e = catch_js_error ();
|
||||
throw new Error.INVALID_ARGUMENT ("Could not parse '%s' line %u: %s", asset.name, e.line, e.message);
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Could not parse '%s' line %u: %s", asset.name, e.line, e.message);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private QuickJS.Value compile_script (string source, string name) throws Error {
|
||||
private QuickJS.Value compile_script (string source, string name) throws Efni.Error {
|
||||
var val = ctx.eval (source, source.length, name,
|
||||
QuickJS.EvalType.GLOBAL |
|
||||
QuickJS.EvalFlag.STRICT |
|
||||
@@ -636,7 +638,7 @@ namespace Frida {
|
||||
|
||||
if (val.is_exception ()) {
|
||||
JSError e = catch_js_error ();
|
||||
throw new Error.INVALID_ARGUMENT ("Script(line %u): %s", e.line, e.message);
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Script(line %u): %s", e.line, e.message);
|
||||
}
|
||||
|
||||
return val;
|
||||
@@ -673,7 +675,7 @@ namespace Frida {
|
||||
args += v;
|
||||
}
|
||||
|
||||
var promise = new Promise<uint64?> ();
|
||||
var promise = new Efni.Promise<uint64?> ();
|
||||
script->do_invoke.begin (impl, args, promise);
|
||||
|
||||
uint64? retval = script->process_events_until_ready (promise);
|
||||
@@ -683,7 +685,7 @@ namespace Frida {
|
||||
return ctx.make_biguint64 (retval);
|
||||
}
|
||||
|
||||
private async void do_invoke (uint64 impl, uint64[] args, Promise<uint64?> promise) {
|
||||
private async void do_invoke (uint64 impl, uint64[] args, Efni.Promise<uint64?> promise) {
|
||||
try {
|
||||
uint64 retval = yield services.machine.invoke (impl, args, io_cancellable);
|
||||
|
||||
@@ -713,7 +715,7 @@ namespace Frida {
|
||||
|
||||
var handler = new NativeCallbackHandler (script, wrapper, method, arity, scope);
|
||||
|
||||
var promise = new Promise<Barebone.Callback> ();
|
||||
var promise = new Efni.Promise<Barebone.Callback> ();
|
||||
script->do_install_native_callback.begin (code, handler, promise);
|
||||
|
||||
Barebone.Callback? callback = script->process_events_until_ready (promise);
|
||||
@@ -726,7 +728,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async void do_install_native_callback (uint64 code, Barebone.CallbackHandler handler,
|
||||
Promise<Barebone.Callback> promise) {
|
||||
Efni.Promise<Barebone.Callback> promise) {
|
||||
try {
|
||||
var callback = yield new Barebone.Callback (code, handler, services.machine, io_cancellable);
|
||||
|
||||
@@ -864,7 +866,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async uint64 handle_invocation (uint64[] args, Barebone.CallFrame frame, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var scope = new ValueScope (script);
|
||||
unowned QuickJS.Context ctx = scope.ctx;
|
||||
|
||||
@@ -1103,7 +1105,7 @@ namespace Frida {
|
||||
return QuickJS.Exception;
|
||||
}
|
||||
|
||||
var promise = new Promise<Barebone.Allocation> ();
|
||||
var promise = new Efni.Promise<Barebone.Allocation> ();
|
||||
script->do_memory_alloc.begin (size, promise);
|
||||
|
||||
Barebone.Allocation? allocation = script->process_events_until_ready (promise);
|
||||
@@ -1115,7 +1117,7 @@ namespace Frida {
|
||||
return script->make_native_pointer (allocation.virtual_address);
|
||||
}
|
||||
|
||||
private async void do_memory_alloc (size_t size, Promise<Barebone.Allocation> promise) {
|
||||
private async void do_memory_alloc (size_t size, Efni.Promise<Barebone.Allocation> promise) {
|
||||
try {
|
||||
var allocator = services.allocator;
|
||||
size_t page_size = allocator.page_size;
|
||||
@@ -1148,7 +1150,7 @@ namespace Frida {
|
||||
if (!script->unparse_page_protection (argv[2], out prot))
|
||||
return QuickJS.Exception;
|
||||
|
||||
var promise = new Promise<bool?> ();
|
||||
var promise = new Efni.Promise<bool?> ();
|
||||
script->do_memory_protect.begin (address, size, prot, promise);
|
||||
|
||||
bool? result = script->process_events_until_ready (promise);
|
||||
@@ -1158,7 +1160,7 @@ namespace Frida {
|
||||
return ctx.make_bool (result);
|
||||
}
|
||||
|
||||
private async void do_memory_protect (uint64 address, size_t size, Gum.PageProtection prot, Promise<bool?> promise) {
|
||||
private async void do_memory_protect (uint64 address, size_t size, Gum.PageProtection prot, Efni.Promise<bool?> promise) {
|
||||
try {
|
||||
yield services.machine.protect_pages (address, size, prot, io_cancellable);
|
||||
|
||||
@@ -1226,7 +1228,7 @@ namespace Frida {
|
||||
if (args == null)
|
||||
return QuickJS.Exception;
|
||||
|
||||
var promise = new Promise<QuickJS.Value?> ();
|
||||
var promise = new Efni.Promise<QuickJS.Value?> ();
|
||||
script->do_memory_scan_sync.begin (args, promise);
|
||||
|
||||
QuickJS.Value? matches = script->process_events_until_ready (promise);
|
||||
@@ -1236,7 +1238,7 @@ namespace Frida {
|
||||
return matches;
|
||||
}
|
||||
|
||||
private async void do_memory_scan_sync (ScanArgs args, Promise<QuickJS.Value?> promise) {
|
||||
private async void do_memory_scan_sync (ScanArgs args, Efni.Promise<QuickJS.Value?> promise) {
|
||||
try {
|
||||
var raw_matches =
|
||||
yield services.machine.scan_ranges (args.ranges, args.pattern, args.max_matches, io_cancellable);
|
||||
@@ -1294,7 +1296,7 @@ namespace Frida {
|
||||
return null;
|
||||
try {
|
||||
args.pattern = new Barebone.MatchPattern.from_string (raw_pattern);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
script.throw_js_error (error_message_to_js (e.message));
|
||||
return null;
|
||||
}
|
||||
@@ -1337,7 +1339,7 @@ namespace Frida {
|
||||
return QuickJS.Exception;
|
||||
}
|
||||
|
||||
var promise = new Promise<QuickJS.Value?> ();
|
||||
var promise = new Efni.Promise<QuickJS.Value?> ();
|
||||
script->do_process_enumerate_ranges.begin (prot, promise);
|
||||
|
||||
QuickJS.Value? ranges = script->process_events_until_ready (promise);
|
||||
@@ -1347,7 +1349,7 @@ namespace Frida {
|
||||
return ranges;
|
||||
}
|
||||
|
||||
private async void do_process_enumerate_ranges (Gum.PageProtection prot, Promise<QuickJS.Value?> promise) {
|
||||
private async void do_process_enumerate_ranges (Gum.PageProtection prot, Efni.Promise<QuickJS.Value?> promise) {
|
||||
try {
|
||||
var ranges = ctx.make_array ();
|
||||
|
||||
@@ -1473,7 +1475,7 @@ namespace Frida {
|
||||
|
||||
try {
|
||||
script->services.interceptor.breakpoint_kind = BreakpointKind.from_nick (kind);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
script->throw_js_error (error_message_to_js (e.message));
|
||||
return QuickJS.Exception;
|
||||
}
|
||||
@@ -1540,7 +1542,7 @@ namespace Frida {
|
||||
return QuickJS.Exception;
|
||||
}
|
||||
|
||||
var promise = new Promise<Barebone.Interceptor> ();
|
||||
var promise = new Efni.Promise<Barebone.Interceptor> ();
|
||||
script->do_interceptor_attach.begin (target, listener, promise);
|
||||
|
||||
Barebone.Interceptor? result = script->process_events_until_ready (promise);
|
||||
@@ -1551,7 +1553,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async void do_interceptor_attach (uint64 target, Barebone.InvocationListener listener,
|
||||
Promise<Barebone.Interceptor> promise) {
|
||||
Efni.Promise<Barebone.Interceptor> promise) {
|
||||
try {
|
||||
var interceptor = services.interceptor;
|
||||
|
||||
@@ -1676,7 +1678,7 @@ namespace Frida {
|
||||
if (listener == null)
|
||||
return QuickJS.Undefined;
|
||||
|
||||
var promise = new Promise<Barebone.Interceptor> ();
|
||||
var promise = new Efni.Promise<Barebone.Interceptor> ();
|
||||
script->do_invocation_listener_detach.begin (listener, promise);
|
||||
|
||||
Barebone.Interceptor? result = script->process_events_until_ready (promise);
|
||||
@@ -1690,7 +1692,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async void do_invocation_listener_detach (Barebone.InvocationListener listener,
|
||||
Promise<Barebone.Interceptor> promise) {
|
||||
Efni.Promise<Barebone.Interceptor> promise) {
|
||||
try {
|
||||
var interceptor = services.interceptor;
|
||||
|
||||
@@ -1843,7 +1845,7 @@ namespace Frida {
|
||||
if (!script->unparse_bytes (argv[0], out blob))
|
||||
return QuickJS.Exception;
|
||||
|
||||
var promise = new Promise<Barebone.CModule> ();
|
||||
var promise = new Efni.Promise<Barebone.CModule> ();
|
||||
script->load_c_module.begin (blob, promise);
|
||||
|
||||
Barebone.CModule? mod = script->process_events_until_ready (promise);
|
||||
@@ -1865,7 +1867,7 @@ namespace Frida {
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private async void load_c_module (Bytes blob, Promise<Barebone.CModule> promise) {
|
||||
private async void load_c_module (Bytes blob, Efni.Promise<Barebone.CModule> promise) {
|
||||
try {
|
||||
var mod = yield new Barebone.CModule.from_blob (blob, services.machine, services.allocator, io_cancellable);
|
||||
|
||||
@@ -1945,7 +1947,7 @@ namespace Frida {
|
||||
return QuickJS.Exception;
|
||||
}
|
||||
|
||||
var promise = new Promise<Barebone.RustModule> ();
|
||||
var promise = new Efni.Promise<Barebone.RustModule> ();
|
||||
script->load_rust_module.begin (source, symbols, dependencies, promise);
|
||||
|
||||
Barebone.RustModule? mod = script->process_events_until_ready (promise);
|
||||
@@ -1971,7 +1973,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async void load_rust_module (string source, Gee.Map<string, uint64?> symbols, Gee.List<string> dependencies,
|
||||
Promise<Barebone.RustModule> promise) {
|
||||
Efni.Promise<Barebone.RustModule> promise) {
|
||||
try {
|
||||
var mod = yield new Barebone.RustModule.from_string (source, symbols, dependencies, services.machine,
|
||||
services.allocator, io_cancellable);
|
||||
@@ -2040,7 +2042,7 @@ namespace Frida {
|
||||
private static QuickJS.Value on_gdb_continue (QuickJS.Context ctx, QuickJS.Value this_val, QuickJS.Value[] argv) {
|
||||
BareboneScript * script = ctx.get_opaque ();
|
||||
|
||||
var promise = new Promise<Debugger> ();
|
||||
var promise = new Efni.Promise<Debugger> ();
|
||||
script->do_gdb_continue.begin (promise);
|
||||
|
||||
Debugger? client = script->process_events_until_ready (promise);
|
||||
@@ -2050,7 +2052,7 @@ namespace Frida {
|
||||
return QuickJS.Undefined;
|
||||
}
|
||||
|
||||
private async void do_gdb_continue (Promise<Debugger> promise) {
|
||||
private async void do_gdb_continue (Efni.Promise<Debugger> promise) {
|
||||
try {
|
||||
yield debugger.resume (io_cancellable);
|
||||
|
||||
@@ -2063,7 +2065,7 @@ namespace Frida {
|
||||
private static QuickJS.Value on_gdb_stop (QuickJS.Context ctx, QuickJS.Value this_val, QuickJS.Value[] argv) {
|
||||
BareboneScript * script = ctx.get_opaque ();
|
||||
|
||||
var promise = new Promise<Debugger> ();
|
||||
var promise = new Efni.Promise<Debugger> ();
|
||||
script->do_gdb_stop.begin (promise);
|
||||
|
||||
Debugger? client = script->process_events_until_ready (promise);
|
||||
@@ -2073,7 +2075,7 @@ namespace Frida {
|
||||
return QuickJS.Undefined;
|
||||
}
|
||||
|
||||
private async void do_gdb_stop (Promise<Debugger> promise) {
|
||||
private async void do_gdb_stop (Efni.Promise<Debugger> promise) {
|
||||
try {
|
||||
yield debugger.stop (io_cancellable);
|
||||
|
||||
@@ -2088,7 +2090,7 @@ namespace Frida {
|
||||
|
||||
try {
|
||||
script->debugger.restart ();
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
script->throw_js_error (error_message_to_js (e.message));
|
||||
return QuickJS.Exception;
|
||||
}
|
||||
@@ -2476,12 +2478,12 @@ namespace Frida {
|
||||
private delegate BufferBuilder? GdbWriteUnparseFunc (QuickJS.Value val, BufferBuilder builder);
|
||||
|
||||
private Bytes? read_memory (uint64 address, uint size) {
|
||||
var promise = new Promise<Bytes> ();
|
||||
var promise = new Efni.Promise<Bytes> ();
|
||||
do_read_memory.begin (address, size, promise);
|
||||
return process_events_until_ready<Bytes> (promise);
|
||||
}
|
||||
|
||||
private async void do_read_memory (uint64 address, uint size, Promise<Bytes> promise) {
|
||||
private async void do_read_memory (uint64 address, uint size, Efni.Promise<Bytes> promise) {
|
||||
try {
|
||||
Bytes bytes = yield debugger.read_byte_array (address, size, io_cancellable);
|
||||
|
||||
@@ -2492,12 +2494,12 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private bool write_memory (uint64 address, Bytes bytes) {
|
||||
var promise = new Promise<Debugger> ();
|
||||
var promise = new Efni.Promise<Debugger> ();
|
||||
do_write_memory.begin (address, bytes, promise);
|
||||
return process_events_until_ready<Debugger> (promise) != null;
|
||||
}
|
||||
|
||||
private async void do_write_memory (uint64 address, Bytes bytes, Promise<Debugger> promise) {
|
||||
private async void do_write_memory (uint64 address, Bytes bytes, Efni.Promise<Debugger> promise) {
|
||||
try {
|
||||
yield debugger.write_byte_array (address, bytes, io_cancellable);
|
||||
|
||||
@@ -2522,7 +2524,7 @@ namespace Frida {
|
||||
if (!script->unparse_uint (argv[2], out size))
|
||||
return QuickJS.Exception;
|
||||
|
||||
var promise = new Promise<DebuggerBreakpoint> ();
|
||||
var promise = new Efni.Promise<DebuggerBreakpoint> ();
|
||||
script->do_gdb_add_breakpoint.begin (kind, address, size, promise);
|
||||
|
||||
DebuggerBreakpoint? bp = script->process_events_until_ready (promise);
|
||||
@@ -2533,7 +2535,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async void do_gdb_add_breakpoint (BreakpointKind kind, uint64 address, uint size,
|
||||
Promise<DebuggerBreakpoint> promise) {
|
||||
Efni.Promise<DebuggerBreakpoint> promise) {
|
||||
try {
|
||||
DebuggerBreakpoint bp = yield debugger.add_breakpoint (kind, address, size, io_cancellable);
|
||||
|
||||
@@ -2550,7 +2552,7 @@ namespace Frida {
|
||||
if (!script->unparse_string (argv[0], out command))
|
||||
return QuickJS.Exception;
|
||||
|
||||
var promise = new Promise<string> ();
|
||||
var promise = new Efni.Promise<string> ();
|
||||
script->do_gdb_run_remote_command.begin (command, promise);
|
||||
|
||||
string? result = script->process_events_until_ready (promise);
|
||||
@@ -2560,7 +2562,7 @@ namespace Frida {
|
||||
return ctx.make_string (result);
|
||||
}
|
||||
|
||||
private async void do_gdb_run_remote_command (string command, Promise<string> promise) {
|
||||
private async void do_gdb_run_remote_command (string command, Efni.Promise<string> promise) {
|
||||
try {
|
||||
string result = yield debugger.run_remote_command (command, io_cancellable);
|
||||
|
||||
@@ -2577,7 +2579,7 @@ namespace Frida {
|
||||
if (!script->unparse_string (argv[0], out command))
|
||||
return QuickJS.Exception;
|
||||
|
||||
var promise = new Promise<Debugger> ();
|
||||
var promise = new Efni.Promise<Debugger> ();
|
||||
script->do_gdb_execute.begin (command, promise);
|
||||
|
||||
Debugger? result = script->process_events_until_ready (promise);
|
||||
@@ -2587,14 +2589,14 @@ namespace Frida {
|
||||
return QuickJS.Undefined;
|
||||
}
|
||||
|
||||
private GDB.Client gdb_client () throws Error {
|
||||
private GDB.Client gdb_client () throws Efni.Error {
|
||||
var client = debugger as GDB.Client;
|
||||
if (client == null)
|
||||
throw new Error.NOT_SUPPORTED ("Raw packets are specific to the GDB remote protocol");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Raw packets are specific to the GDB remote protocol");
|
||||
return client;
|
||||
}
|
||||
|
||||
private async void do_gdb_execute (string command, Promise<Debugger> promise) {
|
||||
private async void do_gdb_execute (string command, Efni.Promise<Debugger> promise) {
|
||||
try {
|
||||
yield gdb_client ().execute_simple (command, io_cancellable);
|
||||
|
||||
@@ -2611,7 +2613,7 @@ namespace Frida {
|
||||
if (!script->unparse_string (argv[0], out request))
|
||||
return QuickJS.Exception;
|
||||
|
||||
var promise = new Promise<GDB.Client.Packet> ();
|
||||
var promise = new Efni.Promise<GDB.Client.Packet> ();
|
||||
script->do_gdb_query.begin (request, promise);
|
||||
|
||||
GDB.Client.Packet? response = script->process_events_until_ready (promise);
|
||||
@@ -2621,7 +2623,7 @@ namespace Frida {
|
||||
return ctx.make_string (response.payload);
|
||||
}
|
||||
|
||||
private async void do_gdb_query (string request, Promise<GDB.Client.Packet> promise) {
|
||||
private async void do_gdb_query (string request, Efni.Promise<GDB.Client.Packet> promise) {
|
||||
try {
|
||||
GDB.Client.Packet packet = yield gdb_client ().query_simple (request, io_cancellable);
|
||||
|
||||
@@ -2659,7 +2661,7 @@ namespace Frida {
|
||||
BareboneScript * script = ctx.get_opaque ();
|
||||
DebuggerThread * thread = this_val.get_opaque (gdb_thread_class);
|
||||
|
||||
var promise = new Promise<DebuggerThread> ();
|
||||
var promise = new Efni.Promise<DebuggerThread> ();
|
||||
script->do_gdb_thread_step.begin (thread, promise);
|
||||
|
||||
DebuggerThread? result = script->process_events_until_ready (promise);
|
||||
@@ -2669,7 +2671,7 @@ namespace Frida {
|
||||
return QuickJS.Undefined;
|
||||
}
|
||||
|
||||
private async void do_gdb_thread_step (DebuggerThread thread, Promise<DebuggerThread> promise) {
|
||||
private async void do_gdb_thread_step (DebuggerThread thread, Efni.Promise<DebuggerThread> promise) {
|
||||
try {
|
||||
yield thread.step (io_cancellable);
|
||||
|
||||
@@ -2686,7 +2688,7 @@ namespace Frida {
|
||||
|
||||
try {
|
||||
thread->step_and_continue ();
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
script->throw_js_error (error_message_to_js (e.message));
|
||||
return QuickJS.Exception;
|
||||
}
|
||||
@@ -2699,7 +2701,7 @@ namespace Frida {
|
||||
BareboneScript * script = ctx.get_opaque ();
|
||||
DebuggerThread * thread = this_val.get_opaque (gdb_thread_class);
|
||||
|
||||
var promise = new Promise<Gee.Map<string, Variant>> ();
|
||||
var promise = new Efni.Promise<Gee.Map<string, Variant>> ();
|
||||
script->do_gdb_thread_read_registers.begin (thread, promise);
|
||||
|
||||
Gee.Map<string, Variant> regs = script->process_events_until_ready (promise);
|
||||
@@ -2709,7 +2711,7 @@ namespace Frida {
|
||||
return script->make_cpu_context (regs);
|
||||
}
|
||||
|
||||
private async void do_gdb_thread_read_registers (DebuggerThread thread, Promise<Gee.Map<string, Variant>> promise) {
|
||||
private async void do_gdb_thread_read_registers (DebuggerThread thread, Efni.Promise<Gee.Map<string, Variant>> promise) {
|
||||
try {
|
||||
Gee.Map<string, Variant> regs = yield thread.read_registers (io_cancellable);
|
||||
|
||||
@@ -2728,7 +2730,7 @@ namespace Frida {
|
||||
if (!script->unparse_string (argv[0], out name))
|
||||
return QuickJS.Exception;
|
||||
|
||||
var promise = new Promise<uint64?> ();
|
||||
var promise = new Efni.Promise<uint64?> ();
|
||||
script->do_gdb_thread_read_register.begin (thread, name, promise);
|
||||
|
||||
uint64? val = script->process_events_until_ready (promise);
|
||||
@@ -2738,7 +2740,7 @@ namespace Frida {
|
||||
return script->make_native_pointer (val);
|
||||
}
|
||||
|
||||
private async void do_gdb_thread_read_register (DebuggerThread thread, string name, Promise<uint64?> promise) {
|
||||
private async void do_gdb_thread_read_register (DebuggerThread thread, string name, Efni.Promise<uint64?> promise) {
|
||||
try {
|
||||
uint64 val = yield thread.read_register (name, io_cancellable);
|
||||
|
||||
@@ -2761,7 +2763,7 @@ namespace Frida {
|
||||
if (!script->unparse_uint64 (argv[1], out val))
|
||||
return QuickJS.Exception;
|
||||
|
||||
var promise = new Promise<DebuggerThread> ();
|
||||
var promise = new Efni.Promise<DebuggerThread> ();
|
||||
script->do_gdb_thread_write_register.begin (thread, name, val, promise);
|
||||
|
||||
DebuggerThread? result = script->process_events_until_ready (promise);
|
||||
@@ -2771,7 +2773,7 @@ namespace Frida {
|
||||
return QuickJS.Undefined;
|
||||
}
|
||||
|
||||
private async void do_gdb_thread_write_register (DebuggerThread thread, string name, uint64 val, Promise<DebuggerThread> promise) {
|
||||
private async void do_gdb_thread_write_register (DebuggerThread thread, string name, uint64 val, Efni.Promise<DebuggerThread> promise) {
|
||||
try {
|
||||
yield thread.write_register (name, val, io_cancellable);
|
||||
|
||||
@@ -2828,7 +2830,7 @@ namespace Frida {
|
||||
BareboneScript * script = ctx.get_opaque ();
|
||||
DebuggerBreakpoint * bp = this_val.get_opaque (gdb_breakpoint_class);
|
||||
|
||||
var promise = new Promise<DebuggerBreakpoint> ();
|
||||
var promise = new Efni.Promise<DebuggerBreakpoint> ();
|
||||
script->do_gdb_breakpoint_enable.begin (bp, promise);
|
||||
|
||||
DebuggerBreakpoint? result = script->process_events_until_ready (promise);
|
||||
@@ -2838,7 +2840,7 @@ namespace Frida {
|
||||
return QuickJS.Undefined;
|
||||
}
|
||||
|
||||
private async void do_gdb_breakpoint_enable (DebuggerBreakpoint bp, Promise<DebuggerBreakpoint> promise) {
|
||||
private async void do_gdb_breakpoint_enable (DebuggerBreakpoint bp, Efni.Promise<DebuggerBreakpoint> promise) {
|
||||
try {
|
||||
yield bp.enable (io_cancellable);
|
||||
|
||||
@@ -2852,7 +2854,7 @@ namespace Frida {
|
||||
BareboneScript * script = ctx.get_opaque ();
|
||||
DebuggerBreakpoint * bp = this_val.get_opaque (gdb_breakpoint_class);
|
||||
|
||||
var promise = new Promise<DebuggerBreakpoint> ();
|
||||
var promise = new Efni.Promise<DebuggerBreakpoint> ();
|
||||
script->do_gdb_breakpoint_disable.begin (bp, promise);
|
||||
|
||||
DebuggerBreakpoint? result = script->process_events_until_ready (promise);
|
||||
@@ -2862,7 +2864,7 @@ namespace Frida {
|
||||
return QuickJS.Undefined;
|
||||
}
|
||||
|
||||
private async void do_gdb_breakpoint_disable (DebuggerBreakpoint bp, Promise<DebuggerBreakpoint> promise) {
|
||||
private async void do_gdb_breakpoint_disable (DebuggerBreakpoint bp, Efni.Promise<DebuggerBreakpoint> promise) {
|
||||
try {
|
||||
yield bp.disable (io_cancellable);
|
||||
|
||||
@@ -2876,7 +2878,7 @@ namespace Frida {
|
||||
BareboneScript * script = ctx.get_opaque ();
|
||||
DebuggerBreakpoint * bp = this_val.get_opaque (gdb_breakpoint_class);
|
||||
|
||||
var promise = new Promise<DebuggerBreakpoint> ();
|
||||
var promise = new Efni.Promise<DebuggerBreakpoint> ();
|
||||
script->do_gdb_breakpoint_remove.begin (bp, promise);
|
||||
|
||||
DebuggerBreakpoint? result = script->process_events_until_ready (promise);
|
||||
@@ -2886,7 +2888,7 @@ namespace Frida {
|
||||
return QuickJS.Undefined;
|
||||
}
|
||||
|
||||
private async void do_gdb_breakpoint_remove (DebuggerBreakpoint bp, Promise<DebuggerBreakpoint> promise) {
|
||||
private async void do_gdb_breakpoint_remove (DebuggerBreakpoint bp, Efni.Promise<DebuggerBreakpoint> promise) {
|
||||
try {
|
||||
yield bp.remove (io_cancellable);
|
||||
|
||||
@@ -2896,7 +2898,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private T? process_events_until_ready<T> (Promise<T> promise) {
|
||||
private T? process_events_until_ready<T> (Efni.Promise<T> promise) {
|
||||
var future = promise.future;
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
while (!future.ready)
|
||||
@@ -3174,8 +3176,8 @@ namespace Frida {
|
||||
return false;
|
||||
|
||||
try {
|
||||
result = (int) Marshal.enum_from_nick<T> (nick);
|
||||
} catch (Error e) {
|
||||
result = (int) Efni.Marshal.enum_from_nick<T> (nick);
|
||||
} catch (Efni.Error e) {
|
||||
throw_js_error (error_message_to_js (e.message));
|
||||
return false;
|
||||
} finally {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class VirtualBoxConsole : Object {
|
||||
@@ -20,13 +22,13 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public static async VirtualBoxConsole open (string host, uint16 port, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
SocketConnection connection;
|
||||
try {
|
||||
var client = new SocketClient ();
|
||||
connection = yield client.connect_async (NetworkAddress.parse (host, port), cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("Unable to reach the VirtualBox debugger console: %s", e.message);
|
||||
throw new Efni.Error.TRANSPORT ("Unable to reach the VirtualBox debugger console: %s", e.message);
|
||||
}
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
@@ -37,7 +39,7 @@ namespace Frida.Barebone {
|
||||
return console;
|
||||
}
|
||||
|
||||
public async string run (string command, Cancellable? cancellable) throws Error, IOError {
|
||||
public async string run (string command, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield send (command, cancellable);
|
||||
string reply = yield read_until (PROMPT, cancellable);
|
||||
|
||||
@@ -55,21 +57,21 @@ namespace Frida.Barebone {
|
||||
string message = (message_end != -1)
|
||||
? answer[message_start:message_end]
|
||||
: answer[message_start:];
|
||||
throw new Error.NOT_SUPPORTED ("%s", message.strip ());
|
||||
throw new Efni.Error.NOT_SUPPORTED ("%s", message.strip ());
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
private async void send (string line, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void send (string line, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
try {
|
||||
yield output.write_all_async ((line + "\n").data, Priority.DEFAULT, cancellable, null);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("Unable to drive the VirtualBox debugger console: %s", e.message);
|
||||
throw new Efni.Error.TRANSPORT ("Unable to drive the VirtualBox debugger console: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
private async string read_until (string needle, Cancellable? cancellable) throws Error, IOError {
|
||||
private async string read_until (string needle, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var text = new StringBuilder (pending);
|
||||
pending = "";
|
||||
|
||||
@@ -79,10 +81,10 @@ namespace Frida.Barebone {
|
||||
try {
|
||||
n = yield input.read_async (buffer, Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("The VirtualBox debugger console went away: %s", e.message);
|
||||
throw new Efni.Error.TRANSPORT ("The VirtualBox debugger console went away: %s", e.message);
|
||||
}
|
||||
if (n <= 0)
|
||||
throw new Error.TRANSPORT ("The VirtualBox debugger console closed the connection");
|
||||
throw new Efni.Error.TRANSPORT ("The VirtualBox debugger console closed the connection");
|
||||
|
||||
for (ssize_t i = 0; i != n; i++) {
|
||||
uint8 b = buffer[i];
|
||||
@@ -104,7 +106,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Bytes read_memory (uint64 address, size_t size, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint64 start = address & ~((uint64) WORD_SIZE - 1);
|
||||
size_t span = (size_t) (address - start) + size;
|
||||
size_t words = (span + WORD_SIZE - 1) / WORD_SIZE;
|
||||
@@ -127,14 +129,14 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
if (raw.len < span)
|
||||
throw new Error.INVALID_ARGUMENT ("Short read at %s", format_location (address));
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Short read at %s", format_location (address));
|
||||
|
||||
size_t offset = (size_t) (address - start);
|
||||
return new Bytes (raw.data[offset:offset + size]);
|
||||
}
|
||||
|
||||
public async void write_memory (uint64 address, Bytes bytes, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (!physical_addressing && bytes.get_size () >= MIN_BYTES_PER_STAGED_WRITE)
|
||||
yield write_memory_from_file (address, bytes, cancellable);
|
||||
else
|
||||
@@ -142,7 +144,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void write_memory_from_file (uint64 address, Bytes bytes, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
unowned uint8[] data = bytes.get_data ();
|
||||
size_t whole = data.length - (data.length % STAGED_WRITE_CHUNK_SIZE);
|
||||
|
||||
@@ -156,13 +158,13 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void write_memory_from_staging_file (uint64 address, uint8[] data,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
string path;
|
||||
try {
|
||||
FileUtils.close (FileUtils.open_tmp ("frida-barebone-XXXXXX", out path));
|
||||
FileUtils.set_data (path, data);
|
||||
} catch (FileError e) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to stage a guest memory write: %s", e.message);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to stage a guest memory write: %s", e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -173,7 +175,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void write_memory_word_by_word (uint64 address, Bytes bytes, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
unowned uint8[] data = bytes.get_data ();
|
||||
|
||||
var batch = new StringBuilder ();
|
||||
@@ -205,7 +207,7 @@ namespace Frida.Barebone {
|
||||
yield run_many (batch.str, cancellable);
|
||||
}
|
||||
|
||||
private async string run_many (string commands, Cancellable? cancellable) throws Error, IOError {
|
||||
private async string run_many (string commands, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
string marker = "frida-console-%u".printf (++sequence);
|
||||
yield send (commands + marker, cancellable);
|
||||
string answer = yield read_until (marker, cancellable);
|
||||
@@ -215,7 +217,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Gee.List<Gee.Map<string, uint64?>> read_registers (Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var cpus = parse_registers (yield run ("r all", cancellable));
|
||||
cpu_count = cpus.size;
|
||||
|
||||
@@ -223,7 +225,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Gee.List<Gee.Map<string, uint64?>> read_core_registers (Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (cpu_count == 0)
|
||||
return yield read_registers (cancellable);
|
||||
|
||||
@@ -273,7 +275,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async uint64? read_one_register (uint cpu, string name, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
string answer = yield run ("r cpu%u.%s".printf (cpu, name), cancellable);
|
||||
|
||||
string prefix = "cpu%u.%s=0x".printf (cpu, name);
|
||||
@@ -290,20 +292,20 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async void write_register (uint cpu, string name, uint64 val, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield run ("r cpu%u.%s = %s".printf (cpu, name, format_address (val)), cancellable);
|
||||
}
|
||||
|
||||
public async void resume (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void resume (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield run ("g", cancellable);
|
||||
}
|
||||
|
||||
public async void halt (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void halt (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield run ("stop", cancellable);
|
||||
yield settle (cancellable);
|
||||
}
|
||||
|
||||
private async void settle (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void settle (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var buffer = new uint8[READ_CHUNK_SIZE];
|
||||
|
||||
while (true) {
|
||||
@@ -339,7 +341,7 @@ namespace Frida.Barebone {
|
||||
pending = "";
|
||||
}
|
||||
|
||||
public async Gee.List<LoadedImage> read_loaded_images (Cancellable? cancellable) throws Error, IOError {
|
||||
public async Gee.List<LoadedImage> read_loaded_images (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield run ("detect", cancellable);
|
||||
string answer = yield run ("lm", cancellable);
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class VirtualBoxDebugger : Object, Debugger {
|
||||
@@ -52,7 +54,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public static async VirtualBoxDebugger open (string host, uint16 port, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var console = yield VirtualBoxConsole.open (host, port, cancellable);
|
||||
|
||||
var debugger = new VirtualBoxDebugger (console);
|
||||
@@ -65,14 +67,14 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
internal async Gee.Map<string, uint64?> read_registers (uint cpu, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (full_registers == null)
|
||||
full_registers = yield console.read_registers (cancellable);
|
||||
return full_registers[(int) cpu];
|
||||
}
|
||||
|
||||
internal async uint64? read_register (uint cpu, string name, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (name in VirtualBoxConsole.CORE_REGISTERS) {
|
||||
if (core_registers == null)
|
||||
core_registers = yield console.read_core_registers (cancellable);
|
||||
@@ -87,7 +89,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
internal async void write_register (uint cpu, string name, uint64 val, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield console.write_register (cpu, name, val, cancellable);
|
||||
forget_registers ();
|
||||
}
|
||||
@@ -97,7 +99,7 @@ namespace Frida.Barebone {
|
||||
full_registers = null;
|
||||
}
|
||||
|
||||
private async void note_stopped (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void note_stopped (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
full_registers = null;
|
||||
core_registers = yield console.read_core_registers (cancellable);
|
||||
|
||||
@@ -145,27 +147,27 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Bytes read_byte_array (uint64 address, size_t size, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
return yield console.read_memory (address, size, cancellable);
|
||||
}
|
||||
|
||||
public async void write_byte_array (uint64 address, Bytes bytes, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield console.write_memory (address, bytes, cancellable);
|
||||
}
|
||||
|
||||
public async Buffer read_buffer (uint64 address, size_t size, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
return make_buffer (yield read_byte_array (address, size, cancellable));
|
||||
}
|
||||
|
||||
public async void resume (Cancellable? cancellable = null) throws Error, IOError {
|
||||
public async void resume (Cancellable? cancellable = null) throws Efni.Error, IOError {
|
||||
forget_registers ();
|
||||
yield console.resume (cancellable);
|
||||
change_state (RUNNING, null);
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable = null) throws Error, IOError {
|
||||
public async void stop (Cancellable? cancellable = null) throws Efni.Error, IOError {
|
||||
if (_state == STOPPED)
|
||||
return;
|
||||
|
||||
@@ -173,12 +175,12 @@ namespace Frida.Barebone {
|
||||
yield note_stopped (cancellable);
|
||||
}
|
||||
|
||||
public void restart () throws Error {
|
||||
throw new Error.NOT_SUPPORTED ("The VirtualBox debugger console cannot restart the guest");
|
||||
public void restart () throws Efni.Error {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("The VirtualBox debugger console cannot restart the guest");
|
||||
}
|
||||
|
||||
public async DebuggerException continue_until_exception (Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield release_parked_processors (cancellable);
|
||||
yield resume (cancellable);
|
||||
|
||||
@@ -196,7 +198,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
private async void release_parked_processors (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void release_parked_processors (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (_state != STOPPED || breakpoints.is_empty)
|
||||
return;
|
||||
|
||||
@@ -237,7 +239,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async DebuggerBreakpoint add_breakpoint (BreakpointKind kind, uint64 address, size_t size,
|
||||
Cancellable? cancellable = null) throws Error, IOError {
|
||||
Cancellable? cancellable = null) throws Efni.Error, IOError {
|
||||
var breakpoint = new VirtualBoxBreakpoint (kind, address, size, console);
|
||||
yield breakpoint.enable (cancellable);
|
||||
breakpoints[address] = breakpoint;
|
||||
@@ -248,16 +250,16 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async void set_physical_memory_mode (bool enabled, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
console.physical_addressing = enabled;
|
||||
}
|
||||
|
||||
public async string run_remote_command (string command, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
return yield console.run (command, cancellable);
|
||||
}
|
||||
|
||||
public async void detach (Cancellable? cancellable = null) throws Error, IOError {
|
||||
public async void detach (Cancellable? cancellable = null) throws Efni.Error, IOError {
|
||||
yield console.resume (cancellable);
|
||||
change_state (RUNNING, null);
|
||||
}
|
||||
@@ -265,7 +267,7 @@ namespace Frida.Barebone {
|
||||
public async void close (Cancellable? cancellable = null) throws IOError {
|
||||
try {
|
||||
yield console.resume (cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
}
|
||||
change_state (CLOSED, null);
|
||||
}
|
||||
@@ -314,28 +316,28 @@ namespace Frida.Barebone {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public async void step (Cancellable? cancellable = null) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("VirtualBox cannot single-step the guest");
|
||||
public async void step (Cancellable? cancellable = null) throws Efni.Error, IOError {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("VirtualBox cannot single-step the guest");
|
||||
}
|
||||
|
||||
public void step_and_continue () throws Error {
|
||||
throw new Error.NOT_SUPPORTED ("The VirtualBox debugger console steps synchronously");
|
||||
public void step_and_continue () throws Efni.Error {
|
||||
throw new Efni.Error.NOT_SUPPORTED ("The VirtualBox debugger console steps synchronously");
|
||||
}
|
||||
|
||||
public async uint64 read_register (string name, Cancellable? cancellable = null) throws Error, IOError {
|
||||
public async uint64 read_register (string name, Cancellable? cancellable = null) throws Efni.Error, IOError {
|
||||
uint64? val = yield owner.read_register (cpu, name, cancellable);
|
||||
if (val == null)
|
||||
throw new Error.NOT_SUPPORTED ("Register “%s” is not exposed by VirtualBox", name);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Register “%s” is not exposed by VirtualBox", name);
|
||||
return val;
|
||||
}
|
||||
|
||||
public async void write_register (string name, uint64 val, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield owner.write_register (cpu, name, val, cancellable);
|
||||
}
|
||||
|
||||
public async Gee.Map<string, Variant> read_registers (Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var raw = yield owner.read_registers (cpu, cancellable);
|
||||
|
||||
var registers = new Gee.HashMap<string, Variant> ();
|
||||
@@ -346,7 +348,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async void write_registers (Gee.Map<string, Variant> regs, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var current = yield owner.read_registers (cpu, cancellable);
|
||||
|
||||
foreach (var e in regs.entries) {
|
||||
@@ -393,7 +395,7 @@ namespace Frida.Barebone {
|
||||
this.console = console;
|
||||
}
|
||||
|
||||
public async void enable (Cancellable? cancellable = null) throws Error, IOError {
|
||||
public async void enable (Cancellable? cancellable = null) throws Efni.Error, IOError {
|
||||
if (displaced != null)
|
||||
return;
|
||||
|
||||
@@ -401,7 +403,7 @@ namespace Frida.Barebone {
|
||||
yield console.write_memory (address, new Bytes (SPIN), cancellable);
|
||||
}
|
||||
|
||||
public async void disable (Cancellable? cancellable = null) throws Error, IOError {
|
||||
public async void disable (Cancellable? cancellable = null) throws Efni.Error, IOError {
|
||||
if (displaced == null)
|
||||
return;
|
||||
|
||||
@@ -409,7 +411,7 @@ namespace Frida.Barebone {
|
||||
displaced = null;
|
||||
}
|
||||
|
||||
public async void remove (Cancellable? cancellable = null) throws Error, IOError {
|
||||
public async void remove (Cancellable? cancellable = null) throws Efni.Error, IOError {
|
||||
yield disable (cancellable);
|
||||
|
||||
removed ();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public sealed class VirtualBoxStubClient : GDB.Client {
|
||||
@@ -12,13 +14,13 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public static new async VirtualBoxStubClient open (IOStream stream, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var client = new VirtualBoxStubClient (stream);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
Efni.throw_api_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
@@ -36,12 +38,12 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void prepare_connection (Cancellable? cancellable) throws Error, IOError {
|
||||
protected override async void prepare_connection (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield wait_until_quiet (cancellable);
|
||||
set_max_packet_size (MAX_PACKET_SIZE);
|
||||
}
|
||||
|
||||
private async void wait_until_quiet (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void wait_until_quiet (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
while (get_monotonic_time () - last_packet_arrival_time < QUIET_PERIOD_USEC) {
|
||||
var source = new TimeoutSource (QUIET_POLL_MSEC);
|
||||
source.set_callback (wait_until_quiet.callback);
|
||||
@@ -52,7 +54,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void enable_extensions (Cancellable? cancellable) throws Error, IOError {
|
||||
protected override async void enable_extensions (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
connected = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
/**
|
||||
@@ -27,11 +29,11 @@ namespace Frida.Barebone {
|
||||
private const uint64 MAPPING_ALIGNMENT = 0x4000;
|
||||
private const uint MAX_DISCOVERY_ATTEMPTS = 10;
|
||||
|
||||
public static VzPhysicalMemory open (uint pid) throws Error {
|
||||
public static VzPhysicalMemory open (uint pid) throws Efni.Error {
|
||||
var memory = new VzPhysicalMemory ();
|
||||
|
||||
if (Gum.Darwin.task_for_pid (Gum.Darwin.mach_task_self (), (int) pid, out memory.task) != Gum.Darwin.Status.SUCCESS) {
|
||||
throw new Error.PERMISSION_DENIED (
|
||||
throw new Efni.Error.PERMISSION_DENIED (
|
||||
"Unable to acquire the task port for the Virtualization daemon (pid %u); " +
|
||||
"frida-core needs root or the com.apple.system-task-ports entitlement", pid);
|
||||
}
|
||||
@@ -40,20 +42,20 @@ namespace Frida.Barebone {
|
||||
try {
|
||||
memory.discover ();
|
||||
return memory;
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
if (attempt == MAX_DISCOVERY_ATTEMPTS - 1)
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void discover () throws Error {
|
||||
private void discover () throws Efni.Error {
|
||||
uint64 vptr = (resolve_hv_vm_vtable () + 16) & VPTR_MASK;
|
||||
foreach (uint64 instance in find_vptr_candidates (vptr)) {
|
||||
if (try_load_shared_ram (instance))
|
||||
return;
|
||||
}
|
||||
throw new Error.NOT_SUPPORTED ("Unable to locate a populated Hv::Vm guest-RAM map in the daemon");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to locate a populated Hv::Vm guest-RAM map in the daemon");
|
||||
}
|
||||
|
||||
private bool try_load_shared_ram (uint64 instance) {
|
||||
@@ -111,17 +113,17 @@ namespace Frida.Barebone {
|
||||
entries = loaded;
|
||||
}
|
||||
|
||||
private static uint64 resolve_hv_vm_vtable () throws Error {
|
||||
private static uint64 resolve_hv_vm_vtable () throws Efni.Error {
|
||||
Gum.Module module;
|
||||
try {
|
||||
module = Gum.Module.load ("/System/Library/Frameworks/Hypervisor.framework/Hypervisor");
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to load Hypervisor.framework: %s", e.message);
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to load Hypervisor.framework: %s", e.message);
|
||||
}
|
||||
|
||||
Gum.Address vtable = module.find_symbol_by_name ("_ZTVN2Hv2VmE");
|
||||
if (vtable == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to resolve the Hv::Vm vtable symbol");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to resolve the Hv::Vm vtable symbol");
|
||||
return vtable;
|
||||
}
|
||||
|
||||
@@ -155,18 +157,18 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
public uint8[] read (uint64 pa, size_t size) throws Error {
|
||||
public uint8[] read (uint64 pa, size_t size) throws Efni.Error {
|
||||
SharedRamEntry entry = entry_for (pa, size);
|
||||
uint8[]? data = Gum.Darwin.read (task, entry.host_va + (pa - entry.guest_pa), size);
|
||||
if (data == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to read guest physical memory at PA 0x%" + uint64.FORMAT_MODIFIER + "x", pa);
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Unable to read guest physical memory at PA 0x%" + uint64.FORMAT_MODIFIER + "x", pa);
|
||||
return data;
|
||||
}
|
||||
|
||||
public void write (uint64 pa, uint8[] data) throws Error {
|
||||
public void write (uint64 pa, uint8[] data) throws Efni.Error {
|
||||
SharedRamEntry entry = entry_for (pa, data.length);
|
||||
if (!Gum.Darwin.write (task, entry.host_va + (pa - entry.guest_pa), data))
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to write guest physical memory at PA 0x%" + uint64.FORMAT_MODIFIER + "x", pa);
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Unable to write guest physical memory at PA 0x%" + uint64.FORMAT_MODIFIER + "x", pa);
|
||||
}
|
||||
|
||||
public bool contains (uint64 pa) {
|
||||
@@ -183,14 +185,14 @@ namespace Frida.Barebone {
|
||||
return false;
|
||||
}
|
||||
|
||||
private SharedRamEntry entry_for (uint64 pa, size_t length) throws Error {
|
||||
private SharedRamEntry entry_for (uint64 pa, size_t length) throws Efni.Error {
|
||||
SharedRamEntry? entry = covering_entry (pa, length);
|
||||
if (entry == null) {
|
||||
refresh ();
|
||||
entry = covering_entry (pa, length);
|
||||
}
|
||||
if (entry == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Guest PA 0x%" + uint64.FORMAT_MODIFIER + "x is not backed by guest RAM", pa);
|
||||
throw new Efni.Error.INVALID_ARGUMENT ("Guest PA 0x%" + uint64.FORMAT_MODIFIER + "x is not backed by guest RAM", pa);
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
/**
|
||||
@@ -20,24 +22,24 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public static new async VzStubClient open (IOStream stream, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var client = new VzStubClient (stream);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
Efni.throw_api_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
protected override async void prepare_connection (Cancellable? cancellable) throws Error, IOError {
|
||||
protected override async void prepare_connection (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield halt (cancellable, HALT_TIMEOUT_MSEC);
|
||||
yield start_no_ack_mode (cancellable);
|
||||
}
|
||||
|
||||
protected override async void detect_vendor_features (Cancellable? cancellable) throws Error, IOError {
|
||||
protected override async void detect_vendor_features (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var vcont = yield query_simple ("vCont?", cancellable);
|
||||
unowned string payload = vcont.payload;
|
||||
if (payload.length > 0 && payload[0] != 'E')
|
||||
@@ -48,7 +50,7 @@ namespace Frida.Barebone {
|
||||
supported_features.add ("vf-phy-mem-mode");
|
||||
}
|
||||
|
||||
protected override async void enable_extensions (Cancellable? cancellable) throws Error, IOError {
|
||||
protected override async void enable_extensions (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield execute_simple ("QThreadSuffixSupported", cancellable);
|
||||
supported_features.add ("thread-suffix");
|
||||
yield execute_simple ("QListThreadsInStopReply", cancellable);
|
||||
@@ -56,7 +58,7 @@ namespace Frida.Barebone {
|
||||
yield discover_target (cancellable);
|
||||
}
|
||||
|
||||
private async void discover_target (Cancellable? cancellable) throws Error, IOError {
|
||||
private async void discover_target (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
string info = yield query_property ("HostInfo", cancellable);
|
||||
var host = GDB.Client.PropertyDictionary.parse (info);
|
||||
arch = arch_from_cpu_type (GDB.Protocol.parse_uint (host.get_string ("cputype"), 10));
|
||||
@@ -100,7 +102,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public override async Bytes read_byte_array (uint64 address, size_t size, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var result = new uint8[size];
|
||||
|
||||
size_t offset = 0;
|
||||
@@ -117,7 +119,7 @@ namespace Frida.Barebone {
|
||||
|
||||
Bytes chunk = response.payload_bytes;
|
||||
if (chunk.get_size () != chunk_size) {
|
||||
throw new Error.INVALID_ARGUMENT (
|
||||
throw new Efni.Error.INVALID_ARGUMENT (
|
||||
"Unable to read from 0x%" + uint64.FORMAT_MODIFIER + "x: invalid address", address);
|
||||
}
|
||||
|
||||
|
||||
+20
-18
@@ -1,13 +1,15 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public static async Win9xLayout collect_win9x_layout (Machine machine, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var modules = new Gee.ArrayList<ModuleInfo> ();
|
||||
var symbols = new Gee.ArrayList<SymbolInfo> ();
|
||||
|
||||
var blocks = yield find_descriptor_blocks (machine, cancellable);
|
||||
if (blocks.is_empty) {
|
||||
throw new Error.NOT_SUPPORTED (
|
||||
throw new Efni.Error.NOT_SUPPORTED (
|
||||
"Unable to find any VxD; the guest is probably still booting, as none of its " +
|
||||
"memory is paged in yet");
|
||||
}
|
||||
@@ -86,7 +88,7 @@ namespace Frida.Barebone {
|
||||
// therefore be indistinguishable from noise when sweeping. Sweep only far enough to
|
||||
// find VMM itself, then follow it.
|
||||
private static async Gee.List<DeviceDescriptorBlock> find_descriptor_blocks (Machine machine,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var blocks = new Gee.ArrayList<DeviceDescriptorBlock> ();
|
||||
|
||||
uint64 address = yield find_kernel_descriptor_block (machine, cancellable);
|
||||
@@ -97,7 +99,7 @@ namespace Frida.Barebone {
|
||||
Bytes raw;
|
||||
try {
|
||||
raw = yield machine.debugger.read_byte_array (address, DDB_SIZE, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -115,7 +117,7 @@ namespace Frida.Barebone {
|
||||
|
||||
// VMM is the first VxD in the system arena, thus examine only the start of it.
|
||||
private static async uint64 find_kernel_descriptor_block (Machine machine, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
foreach (Gum.MemoryRange run in yield find_resident_runs (machine, SYSTEM_ARENA_BASE,
|
||||
VMM_SEARCH_SPAN, cancellable)) {
|
||||
foreach (DeviceDescriptorBlock candidate in yield harvest_candidates (machine, run.base_address,
|
||||
@@ -131,14 +133,14 @@ namespace Frida.Barebone {
|
||||
// Windows maps the page tables into all contexts, thus one read gives the present pages. The
|
||||
// machine would walk the full address space, which costs more than the sweep.
|
||||
private static async Gee.List<Gum.MemoryRange?> find_resident_runs (Machine machine, uint64 base_va,
|
||||
uint64 span, Cancellable? cancellable) throws Error, IOError {
|
||||
uint64 span, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
Buffer entries;
|
||||
try {
|
||||
entries = debugger.make_buffer (yield debugger.read_byte_array (
|
||||
PAGE_TABLES_BASE + (base_va >> 22) * PAGE_SIZE, PAGE_SIZE, cancellable));
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
return yield collect_runs_from_machine (machine, base_va, span, cancellable);
|
||||
}
|
||||
|
||||
@@ -165,7 +167,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async Gee.List<Gum.MemoryRange?> collect_runs_from_machine (Machine machine, uint64 base_va,
|
||||
uint64 span, Cancellable? cancellable) throws Error, IOError {
|
||||
uint64 span, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 limit = base_va + span;
|
||||
|
||||
var runs = new Gee.ArrayList<Gum.MemoryRange?> ();
|
||||
@@ -179,7 +181,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async Gee.List<DeviceDescriptorBlock> harvest_candidates (Machine machine, uint64 base_va,
|
||||
uint64 size, Cancellable? cancellable) throws Error, IOError {
|
||||
uint64 size, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var candidates = new Gee.ArrayList<DeviceDescriptorBlock> ();
|
||||
|
||||
Debugger debugger = machine.debugger;
|
||||
@@ -192,7 +194,7 @@ namespace Frida.Barebone {
|
||||
Bytes chunk;
|
||||
try {
|
||||
chunk = yield debugger.read_byte_array (chunk_start, readable_size, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -211,7 +213,7 @@ namespace Frida.Barebone {
|
||||
// the descriptor block is the VxD proper. Statically linked VxDs live inside VMM32.VXD and
|
||||
// have no image of their own to ask about.
|
||||
private static async Gee.List<LoaderImage> read_loader_images (Machine machine,
|
||||
Gee.List<DeviceDescriptorBlock> blocks, Cancellable? cancellable) throws Error, IOError {
|
||||
Gee.List<DeviceDescriptorBlock> blocks, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var images = new Gee.ArrayList<LoaderImage> ();
|
||||
|
||||
DeviceDescriptorBlock? loader = null;
|
||||
@@ -283,7 +285,7 @@ namespace Frida.Barebone {
|
||||
// Two things only KERNEL32 knows: the value it XORs process ids with, and the table its
|
||||
// modules are named through. Read both out of its own code rather than hardcoding them.
|
||||
private static async void add_kernel32_symbols (Machine machine, Gee.List<SymbolInfo> symbols,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 kernel32 = yield find_kernel32 (machine, cancellable);
|
||||
if (kernel32 == 0)
|
||||
return;
|
||||
@@ -311,7 +313,7 @@ namespace Frida.Barebone {
|
||||
// GetCurrentProcessId() pushes the process database pointer and tail-calls a helper whose
|
||||
// first instruction loads the obfuscator.
|
||||
private static async uint64 find_process_id_obfuscator (Machine machine, uint64 kernel32,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
uint64 getter = yield find_export (machine, kernel32, "GetCurrentProcessId", cancellable);
|
||||
@@ -333,7 +335,7 @@ namespace Frida.Barebone {
|
||||
// GetModuleFileNameA() reaches the name through the module table, indexing it by the word
|
||||
// at MODREF+0x10, which is the one instruction pair worth recognising.
|
||||
private static async uint64 find_module_table (Machine machine, uint64 kernel32, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
uint64 thunk = yield find_export (machine, kernel32, "GetModuleFileNameA", cancellable);
|
||||
@@ -367,7 +369,7 @@ namespace Frida.Barebone {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static async uint64 find_kernel32 (Machine machine, Cancellable? cancellable) throws Error, IOError {
|
||||
private static async uint64 find_kernel32 (Machine machine, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
for (uint64 candidate = KERNEL32_SEARCH_BASE; candidate < KERNEL32_SEARCH_LIMIT;
|
||||
candidate += IMAGE_ALIGNMENT) {
|
||||
if ((yield find_export (machine, candidate, "GetCurrentProcessId", cancellable)) != 0)
|
||||
@@ -378,7 +380,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async Gee.List<uint64?> read_service_table (Machine machine, DeviceDescriptorBlock ddb,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Bytes raw = yield machine.debugger.read_byte_array (ddb.service_table, ddb.service_count * 4, cancellable);
|
||||
Buffer buf = machine.debugger.make_buffer (raw);
|
||||
|
||||
@@ -437,11 +439,11 @@ namespace Frida.Barebone {
|
||||
};
|
||||
}
|
||||
|
||||
public async bool is_credible (Machine machine, Cancellable? cancellable) throws Error, IOError {
|
||||
public async bool is_credible (Machine machine, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Bytes first_entry;
|
||||
try {
|
||||
first_entry = yield machine.debugger.read_byte_array (service_table, 4, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
return false;
|
||||
}
|
||||
return is_arena_address (machine.debugger.make_buffer (first_entry).read_uint32 (0));
|
||||
|
||||
+27
-25
@@ -1,7 +1,9 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public static async WinNtLayout collect_winnt_layout (Machine machine, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
var symbols = new Gee.ArrayList<SymbolInfo> ();
|
||||
|
||||
Shape shape = Shape.of (machine.debugger);
|
||||
@@ -10,7 +12,7 @@ namespace Frida.Barebone {
|
||||
|
||||
LoadedModule? first = yield read_first_loaded_module (machine, anchors.module_list, shape, cancellable);
|
||||
if (first == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to find the kernel");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to find the kernel");
|
||||
|
||||
var kernel = new ModuleInfo () {
|
||||
name = first.name,
|
||||
@@ -55,7 +57,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async Anchors find_anchors (Machine machine, Shape shape, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (processor_control_region_is_reachable (machine.debugger, shape)) {
|
||||
uint64 version_block = yield find_version_block (machine, shape, cancellable);
|
||||
if (version_block != 0) {
|
||||
@@ -94,7 +96,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async Anchors read_anchors_from_kernel (Machine machine, Shape shape,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
KernelImage found = yield find_kernel_image (machine, shape, cancellable);
|
||||
uint64 kernel = found.address;
|
||||
|
||||
@@ -106,7 +108,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
if (!is_kernel_address (module_list, shape))
|
||||
throw new Error.NOT_SUPPORTED ("Unable to find the loaded module list");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to find the loaded module list");
|
||||
|
||||
return new Anchors () {
|
||||
module_list = module_list,
|
||||
@@ -116,7 +118,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async KernelImage find_kernel_image (Machine machine, Shape shape,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
for (uint attempt = 0; attempt != MAX_CATCH_ATTEMPTS; attempt++) {
|
||||
@@ -154,7 +156,7 @@ namespace Frida.Barebone {
|
||||
yield catch_processor_again (debugger, cancellable);
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to find the kernel image");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to find the kernel image");
|
||||
}
|
||||
|
||||
private static string? system_call_entry_register (Debugger debugger) {
|
||||
@@ -173,7 +175,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async KernelImage? find_image_below (Machine machine, uint64 address,
|
||||
Gee.Set<uint64?> visited, Cancellable? cancellable) throws Error, IOError {
|
||||
Gee.Set<uint64?> visited, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
KernelImage? image = yield sweep_for_image_below (machine, address, LARGE_PAGE_SIZE,
|
||||
make_visited (), cancellable);
|
||||
if (image != null)
|
||||
@@ -189,7 +191,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async KernelImage? sweep_for_image_below (Machine machine, uint64 address,
|
||||
uint64 stride, Gee.Set<uint64?> visited, Cancellable? cancellable) throws Error, IOError {
|
||||
uint64 stride, Gee.Set<uint64?> visited, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 image = address - (address % stride);
|
||||
for (uint step = 0; step != MAX_IMAGE_STEPS && visited.size != MAX_IMAGE_PROBES; step++,
|
||||
image -= stride) {
|
||||
@@ -199,7 +201,7 @@ namespace Frida.Barebone {
|
||||
Gee.List<Export> candidate;
|
||||
try {
|
||||
candidate = yield enumerate_exports (machine, image, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -223,7 +225,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async KernelImage? find_image_below_pointers (Machine machine, uint64 seed,
|
||||
Shape shape, Gee.Set<uint64?> visited, Cancellable? cancellable) throws Error, IOError {
|
||||
Shape shape, Gee.Set<uint64?> visited, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
|
||||
Debugger debugger = machine.debugger;
|
||||
uint64 seed_page = seed - (seed % PCR_SCAN_SIZE);
|
||||
@@ -245,7 +247,7 @@ namespace Frida.Barebone {
|
||||
// The processor control region points to the block that a kernel debugger uses. That block
|
||||
// gives the kernel and its module list, which is sufficient to find the other data.
|
||||
private static async uint64 find_version_block (Machine machine, Shape shape, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
uint64 pcr_base = yield find_processor_control_region (machine, shape, cancellable);
|
||||
@@ -269,7 +271,7 @@ namespace Frida.Barebone {
|
||||
// and points GS to it in kernel mode. In user mode GS points to the block of the current
|
||||
// thread, because the processor exchanges the two values.
|
||||
private static async uint64 find_processor_control_region (Machine machine, Shape shape,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
if (shape.pointer_size == 4) {
|
||||
@@ -282,7 +284,7 @@ namespace Frida.Barebone {
|
||||
uint64 candidate;
|
||||
try {
|
||||
candidate = yield thread.read_register (name, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
continue;
|
||||
}
|
||||
if (yield points_at_itself (machine, candidate, shape, cancellable))
|
||||
@@ -297,7 +299,7 @@ namespace Frida.Barebone {
|
||||
|
||||
// You can read only the value that GS holds now. Thus continue the guest and try again.
|
||||
private static async void catch_processor_again (Debugger debugger, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
yield debugger.resume (cancellable);
|
||||
|
||||
var source = new TimeoutSource (CATCH_INTERVAL_MS);
|
||||
@@ -313,7 +315,7 @@ namespace Frida.Barebone {
|
||||
|
||||
// A processor control region starts with its own address. Use this to identify a candidate.
|
||||
private static async bool points_at_itself (Machine machine, uint64 candidate, Shape shape,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (!is_kernel_address (candidate, shape))
|
||||
return false;
|
||||
|
||||
@@ -322,7 +324,7 @@ namespace Frida.Barebone {
|
||||
try {
|
||||
head = debugger.make_buffer (yield debugger.read_byte_array (candidate + shape.self,
|
||||
shape.pointer_size, cancellable));
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -332,20 +334,20 @@ namespace Frida.Barebone {
|
||||
// Both kernels use the 64-bit form of this structure, and a 32-bit kernel extends the sign of
|
||||
// the pointers in it.
|
||||
private static async uint64 read_loaded_module_list (Machine machine, uint64 version_block, Shape shape,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
uint64 list = read_pointer (debugger.make_buffer (yield debugger.read_byte_array (
|
||||
version_block + LOADED_MODULE_LIST_OFFSET, shape.pointer_size, cancellable)), 0, shape);
|
||||
if (!is_kernel_address (list, shape))
|
||||
throw new Error.NOT_SUPPORTED ("Unable to find the loaded module list");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to find the loaded module list");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// The kernel gives the addresses that a debugger needs here, and no module exports them.
|
||||
private static async uint64 read_process_list_head (Machine machine, uint64 version_block, Shape shape,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
uint64 data_list = read_pointer (debugger.make_buffer (yield debugger.read_byte_array (
|
||||
@@ -367,7 +369,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async LoadedModule? read_first_loaded_module (Machine machine, uint64 head, Shape shape,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Debugger debugger = machine.debugger;
|
||||
|
||||
uint64 entry = read_pointer (debugger.make_buffer (yield debugger.read_byte_array (head, shape.pointer_size,
|
||||
@@ -396,14 +398,14 @@ namespace Frida.Barebone {
|
||||
|
||||
private static async void add_export_symbols (Machine machine, LoadedModule module,
|
||||
Gee.List<Export>? known, Gee.List<SymbolInfo> symbols, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
Gee.List<Export> exports;
|
||||
if (known != null) {
|
||||
exports = known;
|
||||
} else {
|
||||
try {
|
||||
exports = yield enumerate_exports (machine, module.base_address, cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -419,7 +421,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async string read_unicode_string (Machine machine, Buffer owner, size_t offset, Shape shape,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint16 length = owner.read_uint16 (offset);
|
||||
uint64 buffer = read_pointer (owner, offset + shape.name_buffer, shape);
|
||||
if (length == 0 || length > MAX_NAME_SIZE || !is_kernel_address (buffer, shape))
|
||||
@@ -434,7 +436,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
internal static async bool stopped_in_kernel_mode (Debugger debugger, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
DebuggerThread thread = debugger.exception.thread;
|
||||
|
||||
if (debugger.arch == TargetArch.ARM64) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
internal class X86InlineHook : Object, InlineHook {
|
||||
@@ -20,7 +22,7 @@ namespace Frida.Barebone {
|
||||
private const size_t MAX_INSTRUCTION_SIZE = 15;
|
||||
|
||||
public static async InlineHook create (uint64 target, uint64 handler, Gum.CpuType cpu, Allocator allocator,
|
||||
Debugger debugger, Cancellable? cancellable) throws Error, IOError {
|
||||
Debugger debugger, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Allocation allocation = yield allocator.allocate (allocator.page_size, allocator.page_size, cancellable);
|
||||
uint64 code_va = allocation.virtual_address;
|
||||
|
||||
@@ -90,7 +92,7 @@ namespace Frida.Barebone {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public async void destroy (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void destroy (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (state == DESTROYED)
|
||||
return;
|
||||
|
||||
@@ -106,16 +108,16 @@ namespace Frida.Barebone {
|
||||
yield debugger.resume (cancellable);
|
||||
}
|
||||
|
||||
public async void enable (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void enable (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (state == ENABLED)
|
||||
return;
|
||||
if (state != DISABLED)
|
||||
throw new Error.INVALID_OPERATION ("Invalid operation");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Invalid operation");
|
||||
yield debugger.write_byte_array (target, new_target_code, cancellable);
|
||||
state = ENABLED;
|
||||
}
|
||||
|
||||
public async void disable (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void disable (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (state != ENABLED)
|
||||
return;
|
||||
yield debugger.write_byte_array (target, old_target_code, cancellable);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
internal class X86PageTables : Object {
|
||||
@@ -35,7 +37,7 @@ namespace Frida.Barebone {
|
||||
Object (debugger: debugger);
|
||||
}
|
||||
|
||||
public async Gee.List<RangeDetails> collect_ranges (Cancellable? cancellable) throws Error, IOError {
|
||||
public async Gee.List<RangeDetails> collect_ranges (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var result = new Gee.ArrayList<RangeDetails> ();
|
||||
|
||||
MMUParameters p = yield load_parameters (cancellable);
|
||||
@@ -59,7 +61,7 @@ namespace Frida.Barebone {
|
||||
|
||||
private async void collect_ranges_in_table (uint64 table_pa, uint level, uint64 upper_bits,
|
||||
Gum.PageProtection inherited, MMUParameters p, Gee.List<RangeDetails> ranges,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Level l = p.levels[level];
|
||||
Buffer entries = yield read_buffer (table_pa, l.num_entries * p.entry_size, cancellable);
|
||||
|
||||
@@ -82,7 +84,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
}
|
||||
|
||||
public async uint64 translate (uint64 va, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint64 translate (uint64 va, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
MMUParameters p = yield load_parameters (cancellable);
|
||||
if (!p.paging_enabled)
|
||||
return va;
|
||||
@@ -105,10 +107,10 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async void protect (uint64 va, size_t size, Gum.PageProtection prot, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
MMUParameters p = yield load_parameters (cancellable);
|
||||
if (!p.paging_enabled)
|
||||
throw new Error.INVALID_OPERATION ("Unable to change protection while paging is disabled");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Unable to change protection while paging is disabled");
|
||||
|
||||
uint64 start_va = page_start (va, PAGE_SIZE);
|
||||
uint64 end_va = round_address_up (va + size, PAGE_SIZE);
|
||||
@@ -164,9 +166,9 @@ namespace Frida.Barebone {
|
||||
// You cannot divide a large page here. Thus it is a problem only if it gives less than the
|
||||
// request. Windows maps its kernel pool this way, and other data shares the mapping.
|
||||
private static uint64 skip_large_page (uint64 page_va, uint64 end_va, LeafEntry mapping,
|
||||
Gum.PageProtection prot, MMUParameters p) throws Error {
|
||||
Gum.PageProtection prot, MMUParameters p) throws Efni.Error {
|
||||
if (!grants (mapping.value, prot, p)) {
|
||||
throw new Error.NOT_SUPPORTED (
|
||||
throw new Efni.Error.NOT_SUPPORTED (
|
||||
"Unable to change protection of the large page mapping at 0x%" +
|
||||
uint64.FORMAT_MODIFIER + "x", page_va);
|
||||
}
|
||||
@@ -184,14 +186,14 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async LeafEntry find_leaf_entry (uint64 va, MMUParameters p, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint64 table_pa = p.root_table;
|
||||
for (uint level = 0; ; level++) {
|
||||
uint64 slot_pa = slot_address (va, table_pa, level, p);
|
||||
uint64 entry = yield read_entry_at (slot_pa, p, cancellable);
|
||||
|
||||
if ((entry & PRESENT_BIT) == 0) {
|
||||
throw new Error.NOT_SUPPORTED ("Address 0x%" + uint64.FORMAT_MODIFIER + "x is not mapped",
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Address 0x%" + uint64.FORMAT_MODIFIER + "x is not mapped",
|
||||
va);
|
||||
}
|
||||
|
||||
@@ -209,7 +211,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void relax_parent_entries (uint64 va, Gum.PageProtection prot, MMUParameters p,
|
||||
Gee.HashSet<uint64?> already_relaxed, Cancellable? cancellable) throws Error, IOError {
|
||||
Gee.HashSet<uint64?> already_relaxed, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
uint64 table_pa = p.root_table;
|
||||
for (uint level = 0; level != p.leaf_level; level++) {
|
||||
uint64 slot_pa = slot_address (va, table_pa, level, p);
|
||||
@@ -234,10 +236,10 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async Allocation map (Gee.List<uint64?> physical_addresses, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
MMUParameters p = yield load_parameters (cancellable);
|
||||
if (!p.paging_enabled)
|
||||
throw new Error.NOT_SUPPORTED ("Paging is disabled");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Paging is disabled");
|
||||
|
||||
var run = new Run (physical_addresses.size);
|
||||
|
||||
@@ -254,13 +256,13 @@ namespace Frida.Barebone {
|
||||
throw_if_failed (failure);
|
||||
|
||||
if (allocation == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to find a free run of page table entries");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to find a free run of page table entries");
|
||||
|
||||
return allocation;
|
||||
}
|
||||
|
||||
private async bool scan_from_the_top (Run run, MMUParameters p, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
uint num_entries = p.levels[0].num_entries;
|
||||
|
||||
for (uint first = num_entries - 1; first >= num_entries / 2; first--) {
|
||||
@@ -277,7 +279,7 @@ namespace Frida.Barebone {
|
||||
// describing it, so anything longer than a table holds is gathered a segment
|
||||
// at a time and a gap anywhere above the leaves starts the search over.
|
||||
private async bool scan_for_run (Run run, uint64 table_pa, uint level, uint64 upper_bits,
|
||||
uint first, MMUParameters p, Cancellable? cancellable) throws Error, IOError {
|
||||
uint first, MMUParameters p, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
Level l = p.levels[level];
|
||||
bool at_leaf_level = level == p.leaf_level;
|
||||
|
||||
@@ -318,7 +320,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async Allocation occupy (Run run, Gee.List<uint64?> physical_addresses, MMUParameters p,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var displaced = new Gee.ArrayList<DisplacedEntries> ();
|
||||
|
||||
uint next_page = 0;
|
||||
@@ -346,7 +348,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async void restore_entries (Gee.List<DisplacedEntries> displaced, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
bool was_running = yield begin_access (cancellable);
|
||||
GLib.Error? failure = null;
|
||||
try {
|
||||
@@ -446,7 +448,7 @@ namespace Frida.Barebone {
|
||||
this.page_tables = page_tables;
|
||||
}
|
||||
|
||||
public async void deallocate (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void deallocate (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
yield page_tables.restore_entries (displaced, cancellable);
|
||||
}
|
||||
}
|
||||
@@ -519,13 +521,13 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private async uint64 read_entry_at (uint64 slot_pa, MMUParameters p, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
Buffer buf = yield read_buffer (slot_pa, p.entry_size, cancellable);
|
||||
return read_entry (buf, 0, p);
|
||||
}
|
||||
|
||||
private async void write_entry (uint64 slot_pa, uint64 entry, MMUParameters p, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
Buffer buf = debugger.make_buffer (new Bytes (new uint8[p.entry_size]));
|
||||
if (p.entry_size == 8)
|
||||
buf.write_uint64 (0, entry);
|
||||
@@ -539,27 +541,27 @@ namespace Frida.Barebone {
|
||||
return (p.entry_size == 8) ? entries.read_uint64 (offset) : entries.read_uint32 (offset);
|
||||
}
|
||||
|
||||
private async bool begin_access (Cancellable? cancellable) throws Error, IOError {
|
||||
private async bool begin_access (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (physical_memory != null)
|
||||
return false;
|
||||
|
||||
return yield enter_physical_addressing (debugger, cancellable);
|
||||
}
|
||||
|
||||
private async void end_access (bool was_running, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void end_access (bool was_running, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (physical_memory != null)
|
||||
return;
|
||||
|
||||
yield leave_physical_addressing (debugger, was_running, cancellable);
|
||||
}
|
||||
|
||||
private async Buffer read_buffer (uint64 pa, size_t size, Cancellable? cancellable) throws Error, IOError {
|
||||
private async Buffer read_buffer (uint64 pa, size_t size, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (physical_memory != null && physical_memory.contains (pa))
|
||||
return debugger.make_buffer (new Bytes (physical_memory.read (pa, size)));
|
||||
return yield debugger.read_buffer (pa, size, cancellable);
|
||||
}
|
||||
|
||||
private async void write_buffer (uint64 pa, Bytes data, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void write_buffer (uint64 pa, Bytes data, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (physical_memory != null && physical_memory.contains (pa)) {
|
||||
physical_memory.write (pa, data.get_data ());
|
||||
return;
|
||||
@@ -567,7 +569,7 @@ namespace Frida.Barebone {
|
||||
yield debugger.write_byte_array (pa, data, cancellable);
|
||||
}
|
||||
|
||||
private async MMUParameters load_parameters (Cancellable? cancellable) throws Error, IOError {
|
||||
private async MMUParameters load_parameters (Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
if (cached_parameters == null)
|
||||
cached_parameters = yield MMUParameters.load (debugger, cancellable);
|
||||
return cached_parameters;
|
||||
@@ -593,7 +595,7 @@ namespace Frida.Barebone {
|
||||
private const uint64 EFER_LMA = 1ULL << 10;
|
||||
private const uint64 EFER_NXE = 1ULL << 11;
|
||||
|
||||
public static async MMUParameters load (Debugger debugger, Cancellable? cancellable) throws Error, IOError {
|
||||
public static async MMUParameters load (Debugger debugger, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
ControlRegisters regs = yield ControlRegisters.read (debugger, cancellable);
|
||||
|
||||
var parameters = new MMUParameters ();
|
||||
@@ -669,7 +671,7 @@ namespace Frida.Barebone {
|
||||
public uint64 cr4;
|
||||
public uint64 efer;
|
||||
|
||||
public static async ControlRegisters read (Debugger debugger, Cancellable? cancellable) throws Error, IOError {
|
||||
public static async ControlRegisters read (Debugger debugger, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var regs = new ControlRegisters ();
|
||||
|
||||
if (yield regs.try_read_from_registers (debugger, cancellable))
|
||||
@@ -680,29 +682,29 @@ namespace Frida.Barebone {
|
||||
return regs;
|
||||
}
|
||||
|
||||
private async bool try_read_from_registers (Debugger debugger, Cancellable? cancellable) throws Error, IOError {
|
||||
private async bool try_read_from_registers (Debugger debugger, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
DebuggerException? exception = debugger.exception;
|
||||
if (exception == null)
|
||||
throw new Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
throw new Efni.Error.INVALID_OPERATION ("Unable to query in current state");
|
||||
DebuggerThread thread = exception.thread;
|
||||
|
||||
try {
|
||||
cr0 = yield thread.read_register ("cr0", cancellable);
|
||||
cr3 = yield thread.read_register ("cr3", cancellable);
|
||||
cr4 = yield thread.read_register ("cr4", cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
efer = yield thread.read_register ("efer", cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (Efni.Error e) {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void read_from_monitor (Debugger debugger, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void read_from_monitor (Debugger debugger, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
string dump = yield debugger.run_remote_command ("info registers", cancellable);
|
||||
|
||||
bool found_cr3 = false;
|
||||
@@ -732,7 +734,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
if (!found_cr3)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to determine the target's control registers");
|
||||
throw new Efni.Error.NOT_SUPPORTED ("Unable to determine the target's control registers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Efni;
|
||||
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
/// Where a process keeps its number and its name. XNU ships no type information and its
|
||||
@@ -10,7 +12,7 @@ namespace Frida.Barebone {
|
||||
|
||||
public static async XnuLayout? collect_xnu_layout (Machine machine, uint64 iterate_processes,
|
||||
uint64 find_process, uint64 release_process, Allocator allocator,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var processes = (iterate_processes != 0)
|
||||
? yield each_process (machine, iterate_processes, allocator, cancellable)
|
||||
: yield each_numbered_process (machine, find_process, release_process, cancellable);
|
||||
@@ -81,7 +83,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async Gee.List<uint64?> each_process (Machine machine, uint64 iterate_processes,
|
||||
Allocator allocator, Cancellable? cancellable) throws Error, IOError {
|
||||
Allocator allocator, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var found = new Gee.ArrayList<uint64?> ();
|
||||
|
||||
var landing = yield allocator.allocate (LANDING_SIZE, LANDING_SIZE, cancellable);
|
||||
@@ -105,7 +107,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
private static async Gee.List<uint64?> each_numbered_process (Machine machine, uint64 find_process,
|
||||
uint64 release_process, Cancellable? cancellable) throws Error, IOError {
|
||||
uint64 release_process, Cancellable? cancellable) throws Efni.Error, IOError {
|
||||
var found = new Gee.ArrayList<uint64?> ();
|
||||
if (find_process == 0 || release_process == 0)
|
||||
return found;
|
||||
@@ -136,7 +138,7 @@ namespace Frida.Barebone {
|
||||
}
|
||||
|
||||
public async uint64 handle_invocation (uint64[] args, CallFrame frame, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throws Efni.Error, IOError {
|
||||
if (found.size < ENOUGH_PROCESSES)
|
||||
found.add (args[0]);
|
||||
|
||||
|
||||
+94
-49
@@ -69,17 +69,30 @@ func build(options BuildOptions, onDiagnostic BuildDiagnosticCallback) (bundle s
|
||||
}
|
||||
|
||||
type WatchSession struct {
|
||||
mu sync.Mutex
|
||||
options BuildOptions
|
||||
callbacks BuildEventCallbacks
|
||||
ctx *buildContext
|
||||
onDispose SessionDisposeHandler
|
||||
mu sync.Mutex
|
||||
createContext ContextFactory
|
||||
callbacks BuildEventCallbacks
|
||||
ctx *buildContext
|
||||
onDispose SessionDisposeHandler
|
||||
closed bool
|
||||
}
|
||||
|
||||
type ContextFactory func(callbacks BuildEventCallbacks) (*buildContext, error)
|
||||
|
||||
type SessionDisposeHandler func()
|
||||
|
||||
func NewWatchSession(opts BuildOptions, onDispose SessionDisposeHandler, callbacks BuildEventCallbacks) (session *WatchSession, err error) {
|
||||
cbs := BuildEventCallbacks{
|
||||
func NewWatchSession(opts BuildOptions, onDispose SessionDisposeHandler, callbacks BuildEventCallbacks) (*WatchSession, error) {
|
||||
return newWatchSession(func(cbs BuildEventCallbacks) (*buildContext, error) {
|
||||
return makeContext(opts, cbs)
|
||||
}, onDispose, callbacks)
|
||||
}
|
||||
|
||||
func newWatchSession(createContext ContextFactory, onDispose SessionDisposeHandler, callbacks BuildEventCallbacks) (*WatchSession, error) {
|
||||
session := &WatchSession{
|
||||
createContext: createContext,
|
||||
onDispose: onDispose,
|
||||
}
|
||||
session.callbacks = BuildEventCallbacks{
|
||||
OnStart: callbacks.OnStart,
|
||||
OnEnd: callbacks.OnEnd,
|
||||
OnOutput: callbacks.OnOutput,
|
||||
@@ -92,26 +105,36 @@ func NewWatchSession(opts BuildOptions, onDispose SessionDisposeHandler, callbac
|
||||
},
|
||||
}
|
||||
|
||||
ctx, err := makeContext(opts, cbs)
|
||||
session.mu.Lock()
|
||||
defer session.mu.Unlock()
|
||||
|
||||
ctx, err := createContext(session.callbacks)
|
||||
if err != nil {
|
||||
return
|
||||
session.dispose()
|
||||
return nil, err
|
||||
}
|
||||
session.ctx = ctx
|
||||
|
||||
ctx.Watch(esbuild.WatchOptions{})
|
||||
|
||||
session = &WatchSession{
|
||||
options: opts,
|
||||
callbacks: cbs,
|
||||
ctx: ctx,
|
||||
onDispose: onDispose,
|
||||
if err := ctx.Watch(esbuild.WatchOptions{}); err != nil {
|
||||
session.dispose()
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (s *WatchSession) Dispose() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.dispose()
|
||||
}
|
||||
|
||||
func (s *WatchSession) dispose() {
|
||||
if s.closed {
|
||||
return
|
||||
}
|
||||
s.closed = true
|
||||
|
||||
if s.ctx != nil {
|
||||
s.ctx.Dispose()
|
||||
s.ctx = nil
|
||||
@@ -121,51 +144,43 @@ func (s *WatchSession) Dispose() {
|
||||
}
|
||||
|
||||
func (s *WatchSession) onConfigChange() {
|
||||
go func() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
go s.rebuild()
|
||||
}
|
||||
|
||||
if s.ctx != nil {
|
||||
s.ctx.Dispose()
|
||||
s.ctx = nil
|
||||
}
|
||||
func (s *WatchSession) rebuild() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
ctx, err := makeContext(s.options, s.callbacks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.ctx = ctx
|
||||
if s.closed {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Watch(esbuild.WatchOptions{})
|
||||
}()
|
||||
if s.ctx != nil {
|
||||
s.ctx.Dispose()
|
||||
s.ctx = nil
|
||||
}
|
||||
|
||||
ctx, err := s.createContext(s.callbacks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.ctx = ctx
|
||||
|
||||
ctx.Watch(esbuild.WatchOptions{})
|
||||
}
|
||||
|
||||
func makeContext(options BuildOptions, callbacks BuildEventCallbacks) (ctx *buildContext, err error) {
|
||||
var e error
|
||||
|
||||
var projectRoot string
|
||||
if projectRoot, e = filepath.EvalSymlinks(options.ProjectRoot); e != nil {
|
||||
err = fmt.Errorf("Failed to resolve project root: %w", e)
|
||||
if projectRoot, e = resolveProjectRoot(options.ProjectRoot); e != nil {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
|
||||
var entrypoint string
|
||||
if filepath.IsAbs(options.Entrypoint) {
|
||||
entrypoint = options.Entrypoint
|
||||
} else {
|
||||
entrypoint = filepath.Join(projectRoot, options.Entrypoint)
|
||||
}
|
||||
if entrypoint, e = filepath.EvalSymlinks(entrypoint); e != nil {
|
||||
err = fmt.Errorf("Failed to resolve entrypoint: %w", e)
|
||||
return
|
||||
}
|
||||
rel, e := filepath.Rel(projectRoot, entrypoint)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("Could not compute entrypoint path relative to project root: %w", e)
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(rel, "..") {
|
||||
err = fmt.Errorf("Entrypoint must be inside the project root")
|
||||
if entrypoint, e = resolveEntrypoint(projectRoot, options.Entrypoint); e != nil {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
|
||||
@@ -299,6 +314,36 @@ func changeToJS(p string) string {
|
||||
return base + ".js"
|
||||
}
|
||||
|
||||
func resolveProjectRoot(path string) (string, error) {
|
||||
root, err := filepath.Abs(path)
|
||||
if err == nil {
|
||||
root, err = filepath.EvalSymlinks(root)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to resolve project root: %w", err)
|
||||
}
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func resolveEntrypoint(projectRoot string, path string) (string, error) {
|
||||
entrypoint := path
|
||||
if !filepath.IsAbs(entrypoint) {
|
||||
entrypoint = filepath.Join(projectRoot, entrypoint)
|
||||
}
|
||||
entrypoint, err := filepath.EvalSymlinks(entrypoint)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to resolve entrypoint: %w", err)
|
||||
}
|
||||
rel, err := filepath.Rel(projectRoot, entrypoint)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Could not compute entrypoint path relative to project root: %w", err)
|
||||
}
|
||||
if strings.HasPrefix(rel, "..") {
|
||||
return "", fmt.Errorf("Entrypoint must be inside the project root")
|
||||
}
|
||||
return entrypoint, nil
|
||||
}
|
||||
|
||||
func makeBuildObserverPlugin(projectRoot, entrypoint string, options BuildOptions, callbacks BuildEventCallbacks) esbuild.Plugin {
|
||||
return esbuild.Plugin{
|
||||
Name: "frida-build-observer",
|
||||
|
||||
Loaded 100 of 268 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user