Compare commits

..
143 Commits
Author SHA1 Message Date
Ole André Vadla Ravnås 2a41a960fe subprojects: Prepare for release 2026-11-07 15:45:32 +01:00
Ole André Vadla Ravnås 25bd49299a subprojects: Bump outdated 2026-11-07 15:44:13 +01:00
tubibibo c84323b6ac linux: Don't pass our stdin to crash logcat
Under `adb shell`, frida-server's stdin and stdout are the same socket.
The crash monitor's logcat inherited it, so when frida-server died
abnormally, logcat kept it open and `adb shell` never saw EOF.

Give logcat /dev/null for stdin instead.
2026-11-07 15:38:00 +01:00
Ole André Vadla Ravnås 45aef70f35 subprojects: Prepare for release 2026-11-06 13:15:10 +01:00
Ole André Vadla Ravnås eb8efdf40a subprojects: Bump outdated 2026-11-06 12:19:36 +01:00
Ole André Vadla Ravnås 025e5c433d submodules: Bump releng 2026-11-06 12:19:35 +01:00
Ole André Vadla Ravnås f9314073dc subprojects: Prepare for release 2026-11-05 22:45:53 +01:00
Ole André Vadla Ravnås 4011e34449 ci: Run QNX tests out of /tmp
On the emulated QNX 6.5 sabrelite board, threads that concurrently
take first-touch page faults on code mapped from the boot image's
/opt sometimes get SIGSEGV with SEGV_MAPERR, even though the pages
are mapped. A standalone dlopen() test with two or more threads
calling into a fresh library crashes on the first iteration, and
passes when the library lives in /tmp. This made the agent tests
crash intermittently once the reloaded agent spun up its threads.
2026-11-05 22:43:11 +01:00
Ole André Vadla Ravnås 2fef8d7d20 subprojects: Bump outdated 2026-11-05 21:59:57 +01:00
Ole André Vadla Ravnås 9f681e26f1 subprojects: Bump outdated 2026-11-05 21:00:52 +01:00
Ole André Vadla Ravnås 18a156817f barebone: Bump @types/frida-gum to 19.13.0 2026-11-05 20:58:58 +01:00
Ole André Vadla Ravnås 2ea8b6278e compiler: Bump @types/frida-gum to 19.13.0 2026-11-05 20:58:58 +01:00
washedbeast 40ce660b54 linux: Fix memfd check in code swap fallback
Gum.Linux.enumerate_ranges() gives paths as they appear in the maps,
like "/memfd:jit-zygote-cache (deleted)", so the "memfd:" prefix check
from 72a96226 never matched. When libc could not be found in the
target, the code swap then picked ART's read-only JIT zygote cache and
the first write failed with "unable to perform ptrace pokedata: I/O
error".
2026-11-05 11:43:12 +01:00
washedbeast 7913d880c2 linux: Parse /proc/<pid>/maps as raw bytes
Mapping names are not always valid UTF-8: file paths may hold any
bytes, and on Android kernels before 5.17 an anonymous mapping's name
is read from a user pointer whenever the maps are read, so a name whose
string got unmapped shows up as random bytes. We hit this with Magisk's
Zygisk, where apps inherit [anon:System property context nodes] from
zygote with a garbage name.

GRegex rejects a subject that is not valid UTF-8 unless the pattern is
raw, so one such line made ProcMapsIter yield no mappings at all. The
injector then could not find libc in the target, and attach failed.
2026-11-05 11:43:12 +01:00
washedbeast 34e718e4a3 linux: Make syscall-trace strings valid UTF-8
g_variant_new_string() returns NULL when given invalid UTF-8, and the
g_variant_ref_sink() that Vala adds around it then dereferences the
NULL, crashing frida-server.

Module paths in resolve-symbols, and string arguments and AF_UNIX
socket paths in read-events, come from /proc/<pid>/maps and from the
kernel, so they can hold any byte. Replace invalid sequences before
they go into a GVariant.
2026-11-05 11:39:13 +01:00
tubibibo 10d6f866b1 agent: Retry emulated load through LoadLibraryExt
Houdini v3 rejects the deprecated NativeBridgeLoadLibrary() entrypoint
and returns NULL, which surfaces as "Process is not using emulation".
When the plain call fails and the extended entrypoint is available,
retry through it with a null namespace.
2026-11-05 11:31:24 +01:00
Ole André Vadla Ravnås d5bc484072 subprojects: Bump outdated 2026-11-04 12:53:53 +01:00
Ole André Vadla Ravnås 04bd09b7b7 submodules: Bump releng 2026-11-04 12:53:52 +01:00
Ole André Vadla Ravnås ef7dd9701c agent: Mirror native cloak in emulated realm
The emulated agent sees the native agent's threads when it walks the
host's thread list, as they are only cloaked in the native agent's Gum.

Have the native agent share its cloaked threads with the emulated agent,
as a snapshot followed by changes, applied to the emulated agent's own
cloak.

Depends on the CloakObserver interface in frida-gum.
2026-11-03 23:11:43 +01:00
Ole André Vadla Ravnås 8777bbe8af agent: Feed emulated thread registry from host
On Android's native bridge, threads are created and torn down by the
host's Bionic, which the emulated agent's Interceptor cannot hook. This
broke Process.attachThreadObserver() in the emulated realm.

Have the native agent expose its own thread registry to the emulated
agent, which plugs it into Gum as a ThreadProvider: a snapshot on
activation, followed by added, removed and renamed events.

Depends on the ThreadProvider interface in frida-gum.
2026-11-03 23:11:43 +01:00
Francesco Tamagni 8734d9875a fruity: Fix kill() fallback (#1282)
LLDB errors are Efni.Error since the move to idun, so catching
Frida.Error skipped the ProcessControlService fallback.
2026-11-03 17:35:54 +01:00
Francesco Tamagni d043c66c75 channel: Keep streams alive until linked
The registry only holds a weak reference, so a stream nobody else owns,
such as one relayed by idund, was gone before link().
2026-11-03 17:29:07 +01:00
Ole André Vadla Ravnås 317363ce80 subprojects: Prepare for release 2026-11-02 22:10:32 +01:00
Ole André Vadla Ravnås f1aca3182f subprojects: Bump outdated 2026-11-02 22:08:48 +01:00
Ole André Vadla Ravnås 92bc074641 linux: Skip libraries of a foreign ABI in mapper
On x86_64 Android with a native bridge, the arm64 guest libraries may
be mapped before the host ones, and since the mapper looks up
dependencies by basename, the x86_64 agent got bound to the guest libc.

Only accept libraries whose machine and pointer size match the agent.

Fixes #1281.
2026-11-02 21:01:10 +01:00
Ole André Vadla Ravnås 8c98e9b6c6 linux: Find module bases using the ELF layout
Since 00d8ba24, the mapper picks library bases through
find_module_by_path(), whose heuristic starts a new candidate at every
mapping with file offset 0 and prefers executable ones. This breaks
libraries whose code shares file page 0 with their headers, such as
Android's libdl.so on x86_64, where the code mapping was picked as the
base, one page too high.

Instead, accept a mapping at file offset 0 as the base only if the
library's code segments are mapped executable where the linker would
have put them. This also rejects stray mappings of the file, so the
Android-specific libc workaround is no longer needed.

Fixes #1280.
2026-11-02 21:00:51 +01:00
Ole André Vadla Ravnås b61866bf84 subprojects: Prepare for release 2026-11-01 15:11:39 +01:00
Ole André Vadla Ravnås 67d80c40a3 subprojects: Bump outdated 2026-11-01 15:10:19 +01:00
Ole André Vadla Ravnås 22b43c980f agent: Shut down Gum while still able to patch
When code memory is delegated to the host, Gum's early destructors need
to run while the host connection is still up, as some of them revert
hooks. Do so as the last step of removing instrumentation, which happens
inside unload() for agents that delegate.
2026-11-01 15:06:45 +01:00
Ole André Vadla Ravnås 00d8ba24d0 linux: Ignore stray library mappings in mapper
A process may map a library file outside of the dynamic linker. When
such a mapping came before the loaded image, the mapper used it as the
library's base and bound imports into non-executable memory.

Pick each library's base through find_module_by_path(), which prefers
the mapping that the linker actually loaded.

Fixes #1278.
2026-11-01 14:42:47 +01:00
Ole André Vadla Ravnås bf5d2e0516 windows: Include winternl.h for NTSTATUS
The helper backend glue relied on NTSTATUS being pulled in indirectly,
which is no longer the case with recent SDKs.
2026-11-01 14:14:55 +01:00
Ole André Vadla Ravnås cb997b4131 windows: Keep agent mapped while target exits
When the target exits, its other threads are terminated before DLL
detach notifications run, including the agent's. We then saw the
injector thread end without the agent having asked to stay resident,
and unmapped it while hooks such as the unwind broker's were still in
place. Anything running during the remainder of the shutdown could then
call into unmapped memory, e.g. the shim engine walking the stack.

Leave the agent mapped when the process is already exiting.

Fixes frida/frida-gum#1200.
2026-11-01 14:12:34 +01:00
Ole André Vadla Ravnås e106e11a07 subprojects: Bump outdated 2026-10-31 00:48:09 +01:00
Ole André Vadla Ravnås fa87aae7e4 subprojects: Bump outdated 2026-10-30 23:12:48 +01:00
Ole André Vadla Ravnås 340cca8edc subprojects: Bump outdated 2026-10-30 21:59:02 +01:00
Ole André Vadla Ravnås 275ce5a663 submodules: Bump releng 2026-10-30 21:59:01 +01:00
Ole André Vadla Ravnås aa97282d85 Update URLs of repos moved to frida-forks 2026-10-30 21:56:44 +01:00
Leo Conforti 6f68beba34 tests: Check that Linux spawn leaves SIGCHLD alone
A spawned process used to make GLib replace the host's SIGCHLD handler
with its own, and reset it to SIG_DFL once the process was gone, which
kept Node.js from reaping its child_process children. And if the host
reset SIGCHLD in the meantime, the spawned process was never reaped.

Both rely on GLib using pidfd for child watches, as it does since
frida/glib#17, so they are skipped on kernels without pidfd.

See frida/frida-node#121.
2026-10-30 20:40:24 +01:00
Ole André Vadla Ravnås f915561295 subprojects: Prepare for release 2026-10-30 14:33:53 +01:00
Ole André Vadla Ravnås 6429170a01 subprojects: Bump outdated 2026-10-30 14:01:20 +01:00
Ole André Vadla Ravnås 256ff4ee4f host-session: Unload delegating agents on close
Wait for in-flight agent unloads before cancelling pending I/O, so the
AgentController stays registered while agents that delegate code memory
to us revert their instrumentation. Also unload such agents instead of
just dropping their connection, so they don't stay resident.
2026-10-30 12:43:25 +01:00
Ole André Vadla Ravnås a5da784ccb windows: Guard tempdir state behind !HAVE_MAPPER 2026-10-30 11:17:04 +01:00
Ole André Vadla Ravnås 5666eb9043 subprojects: Bump outdated 2026-10-30 07:08:27 +01:00
Ole André Vadla Ravnås 8dd3c63a63 subprojects: Bump outdated 2026-10-30 06:59:55 +01:00
Ole André Vadla Ravnås 678f2710dc submodules: Bump releng 2026-10-30 06:59:54 +01:00
Ole André Vadla Ravnås 40820e1edf subprojects: Bump outdated 2026-10-29 23:02:08 +01:00
Ole André Vadla Ravnås 80b07d161e base: Use efni's GCD and XPC bindings
efni now ships identical bindings on all Darwin OSes, so drop our
duplicates to fix the redefinition errors.
2026-10-29 22:46:21 +01:00
Ole André Vadla Ravnås d4d2f603da subprojects: Bump outdated 2026-10-29 21:52:59 +01:00
Ole André Vadla Ravnås 09d80667e0 subprojects: Bump outdated 2026-10-29 21:28:10 +01:00
Ole André Vadla Ravnås 42d5e9ec9a submodules: Bump releng 2026-10-29 21:28:10 +01:00
Ole André Vadla Ravnås b816986f65 barebone: Sync the agent build docs with CI
The XNU agent is built for none-arm64-softfloat_pic with a target spec
from its directory, and the SDKs are published prebuilt. The kext is
built for macos-arm64e-kernel.
2026-10-29 21:24:32 +01:00
Ole André Vadla Ravnås 5f67e4665c barebone: Port the agent to GumMachoModule
The XNU agent still used the GumDarwinModule API, which was renamed.
2026-10-29 21:17:08 +01:00
Ole André Vadla Ravnås 24f9b838f3 agent: Delegate code memory to the host if needed
When the process can't create code itself, e.g. due to ACG on Windows,
plug in Gum memory hooks that have the host allocate and protect code
pages through the AgentController. Tear down instrumentation before
replying to unload(), while the host is still able to serve requests.
2026-10-29 19:33:57 +01:00
Ole André Vadla Ravnås 4833152542 windows: Drop unused derive_basename() binding
It's only called from the C glue.
2026-10-29 19:33:57 +01:00
Ole André Vadla Ravnås 3c0c3a61b9 windows: Remove unreachable catch clause 2026-10-29 19:33:57 +01:00
Ole André Vadla Ravnås 14b506d31c linux: Inject agents through Gum.Mapper
Map the agent out-of-process instead of having the target dlopen() it.
The loader loads any of the agent's dependencies that the target lacks,
reserves memory, and reports its address back. Imports are then
resolved against the target's libraries, and the loader is sent a plan
that it applies: the read-only segments are mapped straight from the
agent's file, so their pages are shared across targets, while the rest
is streamed over the control socket. The loader also applies
protections, runs the constructor, and on unload runs the destructor
and unmaps.

The target's libraries are opened through /proc/<pid>/map_files when
permitted, then through /proc/<pid>/root with an inode check, so
targets in containers resolve against their own files. When neither
works, e.g. because a library was replaced on disk, the image is copied
from the target's memory.

Gated by the mapper option, now enabled by default on Linux, and used
by the ptrace, procmem and kernel injectors alike. No memfd support is
required.
2026-10-29 19:33:57 +01:00
Ole André Vadla Ravnås 299b18ea4b linux: Tolerate the target exiting before detach
An agent may make its process exit right away, e.g. through exit() in
its entrypoint, and if that happens before we detach, the detach fails
with ESRCH even though the injection succeeded.
2026-10-29 19:33:57 +01:00
Ole André Vadla Ravnås ad89867ffd agent-container: Pass ctrlfd through parameters
The Linux container handed the agent its control socket through a
LinuxInjectorState, making it the only non-injected caller that passes
an injector state. Encode it in the transport URI instead and pass NULL
like on the other OSes.
2026-10-29 19:33:57 +01:00
Ole André Vadla Ravnås dbfb39c7d5 windows: Inject agents filelessly via Gum.Mapper
Map the agent straight into the target with the cross-platform
Gum.Mapper instead of writing a temporary DLL and loading it through
LoadLibrary. A small emitted stub runs the mapped images' constructors,
calls the agent's entrypoint, and runs the destructors when it is not
staying resident, in which case the mapper then unmaps it.

The agent is copied once into a pagefile-backed section per
architecture, whose handle helpers duplicate to map it, so its code
pages are shared across every target. The dbghelp and symsrv DLLs are
mapped alongside it and handed to the agent, so symbolication doesn't
load them either. The agent cloaks these mappings.

Gated by the existing mapper option, now enabled by default on
Windows, so nothing touches the filesystem; the LoadLibrary path stays
for builds with the mapper disabled. On either path the agent is handed
the range of the stub, so it stays cloaked for as long as it runs.
2026-10-29 19:33:57 +01:00
Ole André Vadla Ravnås 51a7762e97 darwin: Adopt the cross-platform Gum.Mapper API
The Mach-O parser and mapper were renamed from GumDarwin* to GumMacho*
in frida-gum, and the mapper now allocates the payload itself, with the
image first, followed by the runtime and the extra memory holding our
stub code, data and stack. It also no longer drives Mach APIs on the
resolver's task, so give it a GumNativeProcessMemory for the target
task.
2026-10-29 19:04:01 +01:00
Ole André Vadla Ravnås 2d4291add2 barebone: Adopt renamed Gum.Macho module API
The Mach-O parser types were renamed from Gum.Darwin* to Gum.Macho* in
frida-gum; follow suit here.
2026-10-29 19:04:01 +01:00
Ole André Vadla Ravnås 0ac43adad3 fruity: Adopt renamed Gum.Macho module API
The Mach-O parser types were renamed from Gum.Darwin* to Gum.Macho* in
frida-gum; follow suit here.
2026-10-29 19:04:01 +01:00
Ole André Vadla Ravnås 74c60230a4 build: Pass along Vala packages of efni and idun
When they're subprojects their vapis are passed by path, so valac never
sees the packages they refer to, such as json-glib-1.0 and libsoup-3.0.
Use the packages they export through "vala_packages".
2026-10-29 19:04:01 +01:00
Ole André Vadla Ravnås 1d3d7beaf8 tests: Use the right format modifier for uint64 2026-10-29 19:04:01 +01:00
Ole André Vadla Ravnås c44d56f175 tests: Close the device manager inside the try
Otherwise its IOError goes unhandled.
2026-10-29 19:04:01 +01:00
Ole André Vadla Ravnås 2986e7f8a8 build: Pass the Go config to Meson by reference
Have Meson write go-config.json through configure_file(), instead of
referring to the file detect.py wrote by name, which Meson warns about
and will reject in 2.0.
2026-10-29 19:04:01 +01:00
Ole André Vadla Ravnås 74e9d14a5d compat: Use parent releng when symlinked
Resolving REPO_ROOT followed a symlinked subprojects/frida-core to the
frida-core checkout, so the parent's releng was not found and
frida-core's own was used instead. When that pinned a different deps
version, it wiped the shared toolchain in the middle of the build.
2026-10-29 10:23:36 +01:00
Håvard Sørbø c817b9b2f4 server: Expand system session entitlements (#1274) 2026-10-26 15:24:48 +01:00
Ole André Vadla Ravnås 61708ddb0c subprojects: Bump outdated 2026-10-23 13:51:27 +02:00
Ole André Vadla Ravnås 4d55da3b5f submodules: Bump releng 2026-10-23 13:51:26 +02:00
Ole André Vadla Ravnås e0819e7917 subprojects: Bump outdated 2026-10-20 21:57:01 +02:00
Ole André Vadla Ravnås d9a1a4de1e subprojects: Bump outdated 2026-10-20 21:39:11 +02:00
Ole André Vadla Ravnås c0ac0832b8 submodules: Bump releng 2026-10-20 21:39:11 +02:00
Ole André Vadla Ravnås 802c13ea18 subprojects: Bump outdated 2026-10-20 21:33:12 +02:00
Ole André Vadla Ravnås 18f6f47e18 subprojects: Bump outdated 2026-10-20 20:23:35 +02:00
Ole André Vadla Ravnås ac41ddece4 build: Sync wrappers with releng 2026-10-20 20:21:18 +02:00
Ole André Vadla Ravnås 67621c8022 submodules: Bump releng 2026-10-20 20:21:18 +02:00
Ole André Vadla Ravnås 63033655d3 fruity: Embed idund for embedded assets
Idun now always goes through idund on Windows and Linux, so ship the
daemon inside the library when assets are embedded, extracting it to a
temporary file before starting the device monitor. With installed
assets Idun installs idund next to the library and resolves it from
there. A daemon that fails to start is reported on stderr and leaves
Apple devices unavailable instead of failing the whole backend.
2026-10-20 14:29:46 +02:00
Ole André Vadla Ravnås 9de990422c fruity: Migrate pairing records to Idun
Idun keeps its pairing identity and peers under its own config
directory now. Move existing records over on first use so pairings
survive the upgrade. Drop this on the next major bump.
2026-10-20 14:29:46 +02:00
Ole André Vadla Ravnås 819a797f31 Split out Efni and Idun as subprojects
Move the generic building blocks in lib/base into Efni, and the Apple
device protocol stack in src/fruity into Idun, both consumed as Meson
subprojects. The Gum-dependent injector and the host session stay as
the fruity backend on top of libidun. Tests for the moved code go
with it.

Efni has its own error domain; frida-core translates it to Frida.Error
at interface boundaries, and awaits go through a Promise facade so the
rest of the code keeps seeing Frida.Error. Barebone builds directly on
the Efni debugger abstraction and uses Efni.Error throughout.
2026-10-20 14:29:46 +02:00
Ole André Vadla Ravnås 6fb6bb6f05 subprojects: Bump outdated 2026-10-20 14:25:13 +02:00
Ole André Vadla Ravnås 43a961a6da submodules: Bump releng 2026-10-20 14:25:13 +02:00
Ole André Vadla Ravnås 943ebe2b92 build: Disable sysprof unconditionally in GLib
sysprof is unused and fails to build on several targets, so disable it
for every machine in the GLib fallback options instead of only QNX.
While here, order the options to follow GLib's meson.options, with the
conditional ones grouped last in the same order.
2026-10-20 13:52:21 +02:00
Ole André Vadla Ravnås 30c276063b socket: Resume WebSocket input on its own thread
Dispatch resume_input() to the main context that owns the WebSocket.
libsoup reads synchronously from it, which raced with that context's
thread over the incoming buffer and crashed in process_frame().
2026-10-19 21:31:35 +02:00
Ole André Vadla Ravnås 741dbe0642 fruity: Fix crash stopping Linux pairing browser
Skip the backend and don't stop it when stop() arrives before start()
has created it.
2026-10-19 20:52:10 +02:00
Ole André Vadla Ravnås 09968c4c5a fruity: Build SystemNetworkStack on macOS
Split VirtualNetworkStack, which needs lwIP, out of network-stack.vala
and build the rest on all hosts. The macOS tunnel uses UdpSocket and
SystemNetworkStack.
2026-10-19 20:52:10 +02:00
Håvard Sørbø 418f4727d1 socket: Cap total WebSocket message size
Newer libsoup also limits the size of reassembled fragmented messages
through max-total-message-size. Set it to the same limit as the
incoming payload size when the property is available.
2026-10-19 20:49:52 +02:00
Ole André Vadla Ravnås 3b1fea7623 fruity: Add UDP service session 2026-10-19 17:08:16 +02:00
Ole André Vadla Ravnås 0fac460ce9 fruity: Support uint64 and double in XPC requests 2026-10-19 17:08:16 +02:00
Ole André Vadla Ravnås 8e289a29d5 meson: Pass fallbacks the options deps.toml does
Drop the stale QuickJS bignum option, which broke configure.
2026-10-19 12:27:21 +02:00
Ole André Vadla Ravnås 57161b0ddd subprojects: Prepare for release 2026-10-19 10:33:25 +02:00
Ole André Vadla Ravnås 940e7d7277 subprojects: Bump outdated 2026-10-19 10:30:42 +02:00
Ole André Vadla Ravnås 0d793d22be submodules: Bump releng 2026-10-19 10:30:41 +02:00
Ole André Vadla Ravnås 81fac8f2ac fruity: Suggest firewall on mDNS reply timeout 2026-10-18 23:48:35 +02:00
Ole André Vadla Ravnås 8ff7b81b95 fruity: Fall back to Avahi for mDNS browsing 2026-10-18 23:48:35 +02:00
Ole André Vadla Ravnås 22fb4aecff fruity: Fix XPC flow control for large replies 2026-10-18 23:33:20 +02:00
Ole André Vadla Ravnås 38e33a9517 fruity: Decode double and date XPC objects 2026-10-18 23:33:20 +02:00
Ole André Vadla Ravnås 295c11cafc subprojects: Prepare for release 2026-10-18 13:53:35 +02:00
Ole André Vadla Ravnås 0ab2372653 subprojects: Bump outdated 2026-10-18 13:52:06 +02:00
Håvard Sørbø 4993c7e24c darwin: Don't leak control socket to helper 2026-10-18 13:11:19 +02:00
Ole André Vadla Ravnås 0d8d6def25 subprojects: Prepare for release 2026-10-17 12:54:25 +02:00
Ole André Vadla Ravnås a0c4988578 subprojects: Bump outdated 2026-10-17 12:53:30 +02:00
Ole André Vadla Ravnås 775237d99c submodules: Bump releng 2026-10-17 12:53:29 +02:00
Ole André Vadla Ravnås 522193f030 compiler: Inject global as globalThis
Node.js code and packages commonly refer to it.
2026-10-17 12:31:52 +02:00
Ole André Vadla Ravnås fbdfce3b12 compiler: Update package-lock.json 2026-10-17 12:26:34 +02:00
Ole André Vadla Ravnås 6dba2c4fa9 compiler: Bump @frida/process to 1.3.0 2026-10-17 12:19:19 +02:00
Ole André Vadla Ravnås 1a0cf99335 compiler: Inject Blob as a global 2026-10-17 12:19:19 +02:00
Ole André Vadla Ravnås 92bf833ebe compiler: Inject Response as a global 2026-10-17 12:19:19 +02:00
Ole André Vadla Ravnås 072613ac5f compiler: Add stream/web shim
Also inject ReadableStream as a global.
2026-10-17 12:19:19 +02:00
Ole André Vadla Ravnås a08a756dfb compiler: Add depd shim
The real one needs V8 stack traces.
2026-10-17 12:19:19 +02:00
Ole André Vadla Ravnås 3407bc73c2 compiler: Return default exports from require()
Node hands out module.exports, which for modules such as events is
the default export itself. Merge the named exports into it.
2026-10-17 12:19:19 +02:00
Ole André Vadla Ravnås 4115121519 compiler: Stub out unsupported Node builtins
Resolve them to an empty module, like frida-compile used to, so that
code which only touches them on optional paths can still load.
2026-10-17 12:19:19 +02:00
Ole André Vadla Ravnås eed070336e compiler: Bump @frida/http-parser-js to 1.1.0 2026-10-17 11:37:23 +02:00
Ole André Vadla Ravnås 070e46703e compiler: Bump @frida/crypto to 1.1.0 2026-10-17 11:21:11 +02:00
Ole André Vadla Ravnås 107335fe7b subprojects: Prepare for release 2026-10-16 15:37:39 +02:00
Ole André Vadla Ravnås df4fda9e9e subprojects: Bump outdated 2026-10-16 15:36:15 +02:00
Håvard Sørbø 7013560bb5 patterns: Fix std::mem::base_address()
Return the address the data is loaded at, as ImHex does, so a pattern
can turn an absolute pointer into an offset. A section is its own
address space and keeps 0.
2026-10-16 13:54:04 +02:00
Håvard Sørbø 394d285192 patterns: Skip pointer_base fields in views
A view cannot run the base function, so it read such a field as an
absolute pointer.

Leave these fields out of the view, toJSON(), pattern() and the
declarations, and warn that they are only resolved when parsing.
2026-10-16 13:54:04 +02:00
Håvard Sørbø c6b5dc3d26 patterns: Fix pointer_base offset resolution
A base function returns an offset in pattern space, like $ and
addressof(parent), but the decoder and parse() added it to the raw
value as an address. That only worked at address zero.

Resolve the target as the raw value plus the base, with $ at the
pointer field while the base function runs. Sign-extend sized
pointers such as T *p : s32, and treat zero as no target.

Add ImHex's std::ptr::relative_to_* functions and report the base
function in the type description.
2026-10-16 13:54:04 +02:00
Ole André Vadla Ravnås aebfac99ff subprojects: Bump outdated 2026-10-16 12:42:33 +02:00
Ole André Vadla Ravnås e36eb4e27a submodules: Bump releng 2026-10-16 12:42:33 +02:00
Ole André Vadla Ravnås 47d6c50906 meson: Disable zstd in libsoup fallback
Match the SDK's libsoup build.
2026-10-15 19:25:49 +02:00
Ole André Vadla Ravnås 0ee52b15b4 subprojects: Bump outdated 2026-10-15 18:18:02 +02:00
Ole André Vadla Ravnås c9867a73b0 subprojects: Bump outdated 2026-10-15 15:45:54 +02:00
Ole André Vadla Ravnås 793ef53bd5 submodules: Bump releng 2026-10-15 15:45:54 +02:00
Ole André Vadla Ravnås 345ce8c1a3 subprojects: Bump outdated 2026-10-15 15:13:03 +02:00
Ole André Vadla Ravnås de16aacccb barebone: Drop the g_io_watch_funcs stub
GLib now guards its IO watch references in gsourceclosure.c, so the
freestanding build no longer leaves the symbol undefined.
2026-10-15 14:44:08 +02:00
Ole André Vadla Ravnås 584b985c61 compiler: Serialize library output captures
The emitter writes files from several goroutines at once, so capturing
its output into a plain map crashed the process with concurrent map
writes.
2026-10-15 14:44:08 +02:00
Ole André Vadla Ravnås c731da6148 compiler: Tolerate library builds without sources
A watch rebuild that runs after the project's files are gone has no
sources and no patterns, and commonDirectory() then indexed an empty
slice, panicking the whole process.
2026-10-15 14:44:08 +02:00
Ole André Vadla Ravnås 1abfff3d16 libc-shim: Provide QNX's stdio objects
QNX's stdout is &_Stdout rather than a pointer variable, so code built
against its stdio.h handed libc's FILE to our fprintf(), which crashed
on its first log message. Define _Stdin, _Stdout and _Stderr ourselves,
and bind the slots through stdin, stdout and stderr.
2026-10-15 14:44:08 +02:00
Ole André Vadla Ravnås f3285577c1 gadget: Adapt to minizip-ng 4.x 2026-10-15 14:44:08 +02:00
Ole André Vadla Ravnås 0d348a800b Adapt to UnixSocketAddress.with_type changes
The new GIO bindings take the path as a byte array.
2026-10-15 14:44:08 +02:00
Ole André Vadla Ravnås 8c9f7928f4 agent: Drop the unused agent_dep
Newer Meson rejects link_with on a custom target whose output lacks a
library suffix, as the per-ABI agent does when building universal.
2026-10-15 14:44:08 +02:00
Ole André Vadla Ravnås 5480f6c5ed subprojects: Bump outdated 2026-10-15 14:44:08 +02:00
Ole André Vadla Ravnås d6b95cd124 compat: Adapt to Meson's option store
Meson now keeps options in an OptionStore rather than on CoreData, and
OptionKey no longer knows whether it is a project or builtin option.
2026-10-15 14:24:36 +02:00
Ole André Vadla Ravnås 63e5a4cf68 subprojects: Bump outdated 2026-10-15 14:24:36 +02:00
Ole André Vadla Ravnås a37ec4e5c1 submodules: Bump releng 2026-10-15 14:24:36 +02:00
Ole André Vadla Ravnås 2a1ed339d9 tests: Reuse remove_tree() in pattern tests 2026-10-14 10:50:36 +02:00
Ole André Vadla Ravnås 38dc1a7030 tests: Order compiler tests by dependency 2026-10-14 10:50:36 +02:00
Ole André Vadla Ravnås ee6b0a8eb3 tests: Drop static from namespace functions 2026-10-14 10:50:36 +02:00
Peter A. ce74a3c90d compiler: Stop watch session on dispose
Releasing a Compiler right after starting a watch could re-enter
finalization while a build callback took a reference, deleting the same
cgo.Handle twice. Stop the watch in dispose() and clear the handle
before calling into the backend.

Hold the session lock while starting the watch, so callbacks only see a
fully initialized session, and make disposal terminal so a queued
rebuild cannot recreate a context after the delegates are gone. Release
the delegates when construction fails.
2026-10-14 10:50:36 +02:00
Ole André Vadla Ravnås b03488ac72 subprojects: Prepare for release 2026-10-09 18:57:29 +02:00
Ole André Vadla Ravnås b62b0e5d7d subprojects: Bump outdated 2026-10-09 18:56:24 +02:00
238 changed files with 6724 additions and 30411 deletions

No files matched your search

+4 -1
View File
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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):
Vendored
+2 -2
View File
@@ -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
View File
@@ -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;
+3 -2
View File
@@ -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
-12
View File
@@ -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);
}
-797
View File
@@ -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
View File
@@ -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;
}
+2
View File
@@ -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 () {
-51
View File
@@ -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);
});
}
-26
View File
@@ -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
-126
View File
@@ -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);
}
}
}
+9
View File
@@ -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
View File
@@ -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()
+2
View File
@@ -1,3 +1,5 @@
using Efni;
#if HAVE_NICE
namespace Frida {
namespace PeerConnection {
+58 -124
View File
@@ -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
View File
@@ -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;
}
}
}
-25
View File
@@ -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
View File
@@ -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);
-336
View File
@@ -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 ();
}
}
}
-432
View File
@@ -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;
}
}
-291
View File
@@ -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);
}
}
}
}
+2
View File
@@ -1,3 +1,5 @@
using Efni;
namespace Frida.Gadget {
private sealed class Config : Object, Json.Serializable {
public Object interaction {
-1
View File
@@ -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'
+2
View File
@@ -1,3 +1,5 @@
using Efni;
namespace Frida {
public abstract class BaseAgentSession : Object, AgentSession {
public signal void closed ();
+10 -16
View File
@@ -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;
}
+2
View File
@@ -1,3 +1,5 @@
using Efni;
namespace Frida {
public sealed class PortalClient : Object, AgentSessionProvider {
public signal void resume ();
+2
View File
@@ -1,3 +1,5 @@
using Efni;
namespace Frida {
public extern void libc_shim_prepare_to_deinit ();
#if DARWIN
+2
View File
@@ -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);
+6 -6
View File
@@ -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 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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: 4e51c61880...164a6a4117.
+6
View File
@@ -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>
+2
View File
@@ -1,3 +1,5 @@
using Efni;
namespace Frida.Server {
private static Application application;
+3 -1
View File
@@ -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
+5 -14
View File
@@ -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;
}
+2
View File
@@ -1,3 +1,5 @@
using Efni;
namespace Frida {
public sealed class AndroidHelperClient : Object {
public AndroidHelperTransport transport {
+1 -1
View File
@@ -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
+83 -81
View File
@@ -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);
}
}
+18 -24
View File
@@ -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
+7 -6
View File
@@ -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
-18
View File
@@ -1,5 +1,4 @@
#![no_std]
#![cfg_attr(feature = "linux", feature(linkage))]
#[cfg(not(any(
feature = "xnu-core",
@@ -1135,23 +1134,6 @@ pub(crate) unsafe fn warm_async_io() {
}
}
// TEMPORARY: g_io_watch_funcs lives in GLib's giounix.c / giowin32.c, and a
// freestanding GLib builds neither, so gobject's gsourceclosure.c is left taking the
// address of a symbol nothing defines. The kernel module must resolve every symbol,
// and the agent never creates a GIOChannel watch, so stand in with a placeholder that
// GLib's own definition overrides once the freestanding build ships one.
#[cfg(feature = "linux")]
#[unsafe(no_mangle)]
#[linkage = "weak"]
pub static g_io_watch_funcs: bindings::GSourceFuncs = bindings::GSourceFuncs {
prepare: None,
check: None,
dispatch: None,
finalize: None,
closure_callback: None,
closure_marshal: None,
};
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());
+6 -6
View File
@@ -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
View File
@@ -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);
}
+5 -3
View File
@@ -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 {
}
}
}
+47 -45
View File
@@ -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;
+78 -76
View File
@@ -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) {
+20 -18
View File
@@ -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");
}
}
}
+19 -17
View File
@@ -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);
}
}
+21 -19
View File
@@ -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);
}
}
+316 -256
View File
@@ -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.
+2 -2
View File
@@ -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 ());
+6 -4
View File
@@ -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;
}
}
+3 -1
View File
@@ -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];
+5 -5
View File
@@ -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
View File
@@ -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;
+13 -11
View File
@@ -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);
+38 -36
View File
@@ -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);
+18 -16
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
+2 -2
View File
@@ -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
View File
@@ -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) {
+7 -5
View File
@@ -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 ();
}
+4 -4
View File
@@ -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"
},
+1 -1
View File
@@ -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"
}
+6 -4
View File
@@ -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
View File
@@ -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);
+53 -51
View File
@@ -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 ();
+9 -7
View File
@@ -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
View File
@@ -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 {
+27 -25
View File
@@ -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);
+33 -31
View File
@@ -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 ();
+7 -5
View File
@@ -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;
}
}
+16 -14
View File
@@ -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;
}
+10 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+7 -5
View File
@@ -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);
+35 -33
View File
@@ -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");
}
}
}
+6 -4
View File
@@ -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]);
+47 -26
View File
@@ -74,6 +74,7 @@ type WatchSession struct {
callbacks BuildEventCallbacks
ctx *buildContext
onDispose SessionDisposeHandler
closed bool
}
type ContextFactory func(callbacks BuildEventCallbacks) (*buildContext, error)
@@ -86,8 +87,12 @@ func NewWatchSession(opts BuildOptions, onDispose SessionDisposeHandler, callbac
}, onDispose, callbacks)
}
func newWatchSession(createContext ContextFactory, onDispose SessionDisposeHandler, callbacks BuildEventCallbacks) (session *WatchSession, err error) {
cbs := BuildEventCallbacks{
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,
@@ -100,26 +105,36 @@ func newWatchSession(createContext ContextFactory, onDispose SessionDisposeHandl
},
}
ctx, err := createContext(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{
createContext: createContext,
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
@@ -129,23 +144,29 @@ 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 := s.createContext(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) {
+7 -2
View File
@@ -1,3 +1,5 @@
using Efni;
namespace Frida {
/**
* Compiles a TypeScript or JavaScript project into a single script bundle
@@ -48,8 +50,10 @@ namespace Frida {
main_context = Frida.get_main_context ();
}
~Compiler () {
public override void dispose () {
cancel_watch ();
base.dispose ();
}
/**
@@ -149,8 +153,9 @@ namespace Frida {
private void cancel_watch () {
if (watch_session_handle != 0) {
CompilerBackend.WatchSession.dispose (watch_session_handle);
size_t handle = watch_session_handle;
watch_session_handle = 0;
CompilerBackend.WatchSession.dispose (handle);
}
}
+7
View File
@@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
esbuild "github.com/evanw/esbuild/pkg/api"
"github.com/frida/TypeScript/tsc/pkg/ast"
@@ -37,6 +38,7 @@ type libraryBuild struct {
*libraryBuilder
onDiagnostic BuildDiagnosticCallback
failed bool
outputsMutex sync.Mutex
outputs map[string]string
}
@@ -293,7 +295,9 @@ func (b *libraryBuild) display(path string) string {
}
func (b *libraryBuild) captureOutput(fileName string, text string, data *compiler.WriteFileData) error {
b.outputsMutex.Lock()
b.outputs[fileName] = text
b.outputsMutex.Unlock()
return nil
}
@@ -309,6 +313,9 @@ func (b *libraryBuild) commonRoot(sources []*ast.SourceFile) string {
}
func commonDirectory(paths []string) string {
if len(paths) == 0 {
return ""
}
common := strings.Split(tspath.GetDirectoryPath(paths[0]), "/")
for _, path := range paths[1:] {
parts := strings.Split(tspath.GetDirectoryPath(path), "/")
+42 -20
View File
@@ -10,18 +10,20 @@
"dependencies": {
"@frida/assert": "^3.0.2",
"@frida/base64-js": "^2.0.3",
"@frida/buffer": "^7.0.4",
"@frida/crypto": "^1.0.1",
"@frida/buffer": "^7.1.0",
"@frida/crypto": "^1.1.0",
"@frida/depd": "^1.0.0",
"@frida/diagnostics_channel": "^1.0.0",
"@frida/events": "^4.0.4",
"@frida/fetch": "^1.0.0",
"@frida/http": "^4.0.2",
"@frida/http-parser-js": "^1.0.0",
"@frida/http-parser-js": "^1.1.0",
"@frida/https": "^1.0.0",
"@frida/ieee754": "^2.0.2",
"@frida/net": "^5.0.0",
"@frida/os": "^1.0.2",
"@frida/path": "^2.0.3",
"@frida/process": "^1.2.1",
"@frida/process": "^1.3.0",
"@frida/punycode": "^3.0.0",
"@frida/querystring": "^1.0.0",
"@frida/readable-stream": "^4.1.3",
@@ -33,7 +35,8 @@
"@frida/url": "^1.0.2",
"@frida/util": "^1.0.3",
"@frida/vm": "^2.0.0",
"@types/frida-gum": "^19.12.0",
"@frida/web-streams": "^1.0.0",
"@types/frida-gum": "^19.13.0",
"@types/node": "18.19.103",
"frida-fs": "7.1.1"
}
@@ -65,9 +68,9 @@
"license": "MIT"
},
"node_modules/@frida/buffer": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/@frida/buffer/-/buffer-7.0.4.tgz",
"integrity": "sha512-RxQ1lZRRiCJj7nhcCiD8xeJx0NsLpGGnjqsmTg7jShGmbnVFMN5W7+J+3gqdPSQhc/IxNBIWc6zRXVp4+qnYHg==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@frida/buffer/-/buffer-7.1.0.tgz",
"integrity": "sha512-nT2UtZchD46tbPsSyLy9BhtbeN5VNq+NkYVifEztEdf+ys1ifUvswYVO+i0xH/nnAPpJX/ZJepRlY9AIun5hnQ==",
"funding": [
{
"type": "github",
@@ -84,14 +87,21 @@
],
"license": "MIT",
"dependencies": {
"@frida/web-streams": "^1.0.0",
"base64-js": "^1.5.1",
"ieee754": "^1.2.1"
}
},
"node_modules/@frida/crypto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@frida/crypto/-/crypto-1.0.1.tgz",
"integrity": "sha512-WXTkVjESvX8TIVXIynJJv9BKSIdQP0Iis04StIXknkDi1ULdMlrypA9p5C4KW1H6GCkNsVnKbybpzsulYCTvag==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@frida/crypto/-/crypto-1.1.0.tgz",
"integrity": "sha512-4WTTt82bp+9VTOn3jloIl40jqfdx5I9A87TtefeAbZA/v92yNDNs0SFmPvfdqaPyFjQY49F59f5yvyqOkO2kcA==",
"license": "MIT"
},
"node_modules/@frida/depd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@frida/depd/-/depd-1.0.0.tgz",
"integrity": "sha512-Va+dcBqmsWJcyUPfk3OwEVEb+e/bY0A30VJ39iXyOiM8GdR/pvRW4+52i/mpcOa636D8AwRSFALOHf7QKrDRTg==",
"license": "MIT"
},
"node_modules/@frida/diagnostics_channel": {
@@ -109,6 +119,12 @@
"node": ">=0.8.x"
}
},
"node_modules/@frida/fetch": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@frida/fetch/-/fetch-1.0.0.tgz",
"integrity": "sha512-ql84ADqTsYtPQ3alDuYabNj7vYsjhsqy+EmddpAa0dRSMf5E6nHKEiTnJvUwL/wkfM9zjB+8FM+E4xh76gV7ug==",
"license": "MIT"
},
"node_modules/@frida/http": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@frida/http/-/http-4.0.2.tgz",
@@ -119,9 +135,9 @@
}
},
"node_modules/@frida/http-parser-js": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@frida/http-parser-js/-/http-parser-js-1.0.0.tgz",
"integrity": "sha512-2nMrNXt/OeTlWbqnE8AH4Sfz4I2+BGoN206dzKEyC/g2svtn83Xu+zuv/V3TkwrA27s26Mcy84ZwsXeNlqNxUQ==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@frida/http-parser-js/-/http-parser-js-1.1.0.tgz",
"integrity": "sha512-AsjnC5mEPa4sNXFWoZnfq6omWq4sdWbl0reLZgC7BfnGCNjMhsMnDrc83LArgc12ecIJ541B79Qn6DnsYdci+w==",
"license": "MIT"
},
"node_modules/@frida/https": {
@@ -169,9 +185,9 @@
"license": "MIT"
},
"node_modules/@frida/process": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@frida/process/-/process-1.2.1.tgz",
"integrity": "sha512-nvCu22DstFW2ttGFtOKekHM7vnjbZm+XgtvavOt427GNT6uV7k0JYK9tnMbcLMRWv57DG6udAmuJlWs8Paq1ag==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@frida/process/-/process-1.3.0.tgz",
"integrity": "sha512-dfnuYakZCaN46WZdfd33qZuPiYsqjXcc6YylqhH9OpAkISKO8V+0P3MYHzuTRvFLlceSzYFdiJJCj2TAYRdXUA==",
"license": "MIT"
},
"node_modules/@frida/punycode": {
@@ -247,10 +263,16 @@
"integrity": "sha512-7fsjLWscZT5odNIBtg6qbLNI+vAk1xmii6H5W2kaYkMYt0vRohQEcDSUWacA+eaWlu5SvMjZI82Yibj/3G9pJw==",
"license": "MIT"
},
"node_modules/@frida/web-streams": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@frida/web-streams/-/web-streams-1.0.0.tgz",
"integrity": "sha512-isTVmXZQyitNWFrePlRyhowbRQxqmxHEqYaPQrv2J7qTKrd8w+OvnFXhd9c2xxFxDxma3dimKU6O5rcbX3o81g==",
"license": "MIT"
},
"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==",
"license": "MIT"
},
"node_modules/@types/node": {
+8 -5
View File
@@ -6,18 +6,20 @@
"dependencies": {
"@frida/assert": "^3.0.2",
"@frida/base64-js": "^2.0.3",
"@frida/buffer": "^7.0.4",
"@frida/crypto": "^1.0.1",
"@frida/buffer": "^7.1.0",
"@frida/crypto": "^1.1.0",
"@frida/depd": "^1.0.0",
"@frida/diagnostics_channel": "^1.0.0",
"@frida/events": "^4.0.4",
"@frida/fetch": "^1.0.0",
"@frida/http": "^4.0.2",
"@frida/http-parser-js": "^1.0.0",
"@frida/http-parser-js": "^1.1.0",
"@frida/https": "^1.0.0",
"@frida/ieee754": "^2.0.2",
"@frida/net": "^5.0.0",
"@frida/os": "^1.0.2",
"@frida/path": "^2.0.3",
"@frida/process": "^1.2.1",
"@frida/process": "^1.3.0",
"@frida/punycode": "^3.0.0",
"@frida/querystring": "^1.0.0",
"@frida/readable-stream": "^4.1.3",
@@ -29,7 +31,8 @@
"@frida/url": "^1.0.2",
"@frida/util": "^1.0.3",
"@frida/vm": "^2.0.0",
"@types/frida-gum": "^19.12.0",
"@frida/web-streams": "^1.0.0",
"@types/frida-gum": "^19.13.0",
"@types/node": "18.19.103",
"frida-fs": "7.1.1"
}
+4 -1
View File
@@ -144,7 +144,10 @@ func defineMemoryBuiltins() {
return int64(len(f.decoder.active.data)), nil
})
defineBuiltin("std::mem::base_address", 0, "$std_mem_base_address", func(f *frame, args []runtimeValue) (runtimeValue, error) {
return int64(0), nil
if f.decoder.active != f.decoder.sections[0] {
return int64(0), nil
}
return int64(f.decoder.base), nil
})
defineBuiltin("std::mem::reached", 1, "$std_mem_reached", func(f *frame, args []runtimeValue) (runtimeValue, error) {
address, err := toInt(args[0])
+16
View File
@@ -677,3 +677,19 @@ struct Scaled { u8 bytes[SCALE * 2]; };
t.Fatal("the cache should keep one compilation per set of defines")
}
}
func TestPointerAttributesReportMisuse(t *testing.T) {
cases := map[string]string{
"fn f(u128 v) { return v; }; struct A { u32 a [[pointer_base(\"f\")]]; };": "pointer_base can only be applied to pointer fields",
}
for source, expected := range cases {
_, diagnostics := Compile(source)
if len(diagnostics) == 0 {
t.Errorf("%q: expected a diagnostic", source)
continue
}
if !strings.Contains(diagnostics[0].Message, expected) {
t.Errorf("%q: got %q, expected %q", source, diagnostics[0].Message, expected)
}
}
}
+3 -3
View File
@@ -117,7 +117,7 @@ func (d *declarationEmitter) emitComposite(out *strings.Builder, qualifiedName s
fmt.Fprintf(out, " readonly $address: NativePointer;\n")
fmt.Fprintf(out, " readonly $size: number;\n")
for _, field := range fields {
if field.Name == "" {
if field.Name == "" || field.PointerBase != nil {
continue
}
writeDoc(out, " ", field.Doc)
@@ -131,7 +131,7 @@ func (d *declarationEmitter) emitComposite(out *strings.Builder, qualifiedName s
if !dynamic {
out.WriteString(" interface Fields {\n")
for _, field := range fields {
if field.Name != "" {
if field.Name != "" && field.PointerBase == nil {
fmt.Fprintf(out, " %s?: %s;\n", field.Name, d.fieldsType(field.Type))
}
}
@@ -140,7 +140,7 @@ func (d *declarationEmitter) emitComposite(out *strings.Builder, qualifiedName s
if view {
out.WriteString(" interface Values {\n")
for _, field := range fields {
if field.Name == "" || field.Hidden {
if field.Name == "" || field.Hidden || field.PointerBase != nil {
continue
}
optional := ""
+24 -10
View File
@@ -567,7 +567,7 @@ func (d *decoder) decodeBare(name string, t Type, offset int, scope *frame) *Dec
}
case *Pointer:
d.decodePointer(value, t, offset)
d.dereference(value, t, nil, scope)
d.dereference(value, t, nil, scope, offset)
case *Bitfield:
d.decodeBitfield(value, t, offset, scope)
case *Struct:
@@ -764,18 +764,27 @@ func (d *decoder) decodePointer(value *DecodedValue, t *Pointer, offset int) {
default:
target = order.Uint64(bytes)
}
if t.Width != nil && t.Width.Kind.IsSigned() {
shift := 64 - uint(size*8)
target = uint64(int64(target<<shift) >> shift)
}
value.Value = fmt.Sprintf("0x%x", target)
value.ValueKind = "pointer"
value.raw = target
}
func (d *decoder) dereference(node *DecodedValue, t *Pointer, base *Function, scope *frame) {
func (d *decoder) dereference(node *DecodedValue, t *Pointer, base *Function, scope *frame, at int) {
if node.Truncated || node.Section != 0 || scope == nil {
return
}
target := node.raw.(uint64)
raw := int64(node.raw.(uint64))
var offset int64
if base != nil {
result, err := scope.invoke(base, []runtimeValue{target})
owner := scope.cursorOwner()
saved := owner.cursor
owner.cursor = at
result, err := scope.invoke(base, []runtimeValue{raw})
owner.cursor = saved
if err != nil {
node.fail(err)
return
@@ -785,13 +794,18 @@ func (d *decoder) dereference(node *DecodedValue, t *Pointer, base *Function, sc
node.fail(err)
return
}
target += uint64(rebased)
node.raw = target
node.Value = fmt.Sprintf("0x%x", target)
offset = raw + rebased + int64(owner.base)
} else {
offset = raw - int64(d.base)
}
node.pointee, node.Fields, node.frame = nil, nil, nil
offset := int64(target) - int64(d.base)
if target == 0 || d.pointers >= maxPointerDepth || offset < 0 || offset >= int64(len(d.sections[0].data)) {
if raw == 0 || d.pointers >= maxPointerDepth {
return
}
target := uint64(int64(d.base) + offset)
node.raw = target
node.Value = fmt.Sprintf("0x%x", target)
if offset < 0 || offset >= int64(len(d.sections[0].data)) {
return
}
d.pointers++
@@ -1430,7 +1444,7 @@ func (f *frame) place(field *Field) error {
}
node.Name = field.Name
if pointer, isPointer := Unalias(field.Type).(*Pointer); isPointer && field.PointerBase != nil {
d.dereference(node, pointer, field.PointerBase, f)
d.dereference(node, pointer, field.PointerBase, f, offset)
}
if field.Hidden {
node.Hidden = true
+89
View File
@@ -511,3 +511,92 @@ struct Root {
t.Errorf("ref parameters should receive the pattern: %s", decoded)
}
}
func TestDecodeRelativePointers(t *testing.T) {
data := make([]byte, 0x100)
put := func(offset int, value int32) { binary.LittleEndian.PutUint32(data[offset:], uint32(value)) }
put(0, 1)
put(4, 0x3c)
put(8, 0x40)
put(12, 0x84)
put(0x40, 2)
put(0x44, -68)
copy(data[0x90:], "Swift\x00")
data[0xa0] = 3
decode := func(typeName string, data []byte, address uint64) map[string]*DecodedValue {
encoded, err := DecodeSource(relativeSource, typeName, data, address, Targets[0], nil)
if err != nil {
t.Fatal(err)
}
var value DecodedValue
if err := json.Unmarshal([]byte(encoded), &value); err != nil {
t.Fatal(err)
}
fields := map[string]*DecodedValue{}
for _, field := range value.Fields {
fields[field.Name] = field
}
return fields
}
a := decode("Node", data, 0x20000)
for name, expected := range map[string]string{"next": "0x20040", "prev": "0x20040", "label": "0x20090"} {
if a[name].Value != expected {
t.Errorf("%s = %v, expected %s", name, a[name].Value, expected)
}
if len(a[name].Fields) != 1 {
t.Errorf("%s should carry its pointee: %s", name, a[name].String())
}
}
if a["next"].Fields[0].Fields[0].Value != float64(2) || a["label"].Fields[0].Value != "S" {
t.Errorf("unexpected pointees:\nnext: %s\nlabel: %s", a["next"].String(), a["label"].String())
}
b := decode("Node", data[0x40:], 0x20040)
if b["next"].Value != "0x20000" || len(b["next"].Fields) != 0 {
t.Errorf("a negative offset should resolve without a pointee outside the data: %s", b["next"].String())
}
for _, name := range []string{"prev", "label"} {
if b[name].Value != "0x0" || len(b[name].Fields) != 0 {
t.Errorf("%s should stay null: %s", name, b[name].String())
}
}
if custom := decode("Custom", data[0xa0:], 0x200a0); custom["p"].Value != "0x200a9" {
t.Errorf("a custom base should rebase the offset: %s", custom["p"].String())
}
}
const baseAddressSource = `
import std.mem;
struct Inner {
u8 byte;
u64 section_base = std::mem::base_address() [[export]];
};
struct Probe {
u32 value;
u64 base = std::mem::base_address() [[export]];
std::mem::Section sec = std::mem::create_section("scratch");
std::mem::set_section_size(sec, 8);
Inner inner @ 0 in sec;
};
`
func TestDecodeBaseAddress(t *testing.T) {
module, diagnostics := Compile(baseAddressSource)
if len(diagnostics) > 0 {
t.Fatal(diagnostics)
}
probe := Decode(module, Targets[0], findType(module, "Probe"), make([]byte, 16), 0x1000)
fields := map[string]*DecodedValue{}
for _, field := range probe.Fields {
fields[field.Name] = field
}
if fields["base"].Value != integerJSON(0x1000, false) {
t.Errorf("base_address should be the decode address: %s", fields["base"])
}
if inner := fields["inner"]; inner.Fields[1].Value != integerJSON(0, false) {
t.Errorf("a section should keep its own address space: %s", inner)
}
}
+10
View File
@@ -71,6 +71,7 @@ type FieldDescription struct {
Hidden bool `json:"hidden,omitempty"`
Conditional bool `json:"conditional,omitempty"`
NoUniqueAddress bool `json:"no_unique_address,omitempty"`
PointerBase string `json:"pointer_base,omitempty"`
Type *TypeRefDescription `json:"type"`
Offset *int `json:"offset"`
Size *int `json:"size"`
@@ -220,6 +221,7 @@ func describeComposite(kind string, name string, doc string, t Type, layout *Mod
Hidden: field.Hidden,
Conditional: field.Guard != nil,
NoUniqueAddress: field.NoUniqueAddress,
PointerBase: pointerBaseName(field),
Type: describeTypeRef(field.Type),
})
}
@@ -232,6 +234,7 @@ func describeComposite(kind string, name string, doc string, t Type, layout *Mod
Hidden: f.Field.Hidden,
Conditional: f.Field.Guard != nil,
NoUniqueAddress: f.Field.NoUniqueAddress,
PointerBase: pointerBaseName(f.Field),
Type: describeTypeRef(f.Field.Type),
}
if !f.Dynamic {
@@ -295,6 +298,13 @@ func describeTypeRef(t Type) *TypeRefDescription {
return d
}
func pointerBaseName(field *Field) string {
if field.PointerBase == nil {
return ""
}
return field.PointerBase.Name
}
func orderName(order ByteOrder) string {
switch order {
case LittleEndian:
+14 -3
View File
@@ -637,7 +637,7 @@ func (c *compositeEmitter) emitAccessors(out *strings.Builder, entry *fieldEntry
entry.offset, entry.size(), c.endOf(c.previousEntry(entry)))
}
}
if _, isPadding := entry.field.Type.(*Padding); isPadding || entry.field.Name == "" {
if _, isPadding := entry.field.Type.(*Padding); isPadding || entry.field.Name == "" || entry.field.PointerBase != nil {
return
}
address := c.address(entry.offset)
@@ -789,6 +789,17 @@ func (e *jsEmitter) pointerReader(t *Pointer, address location) string {
return fmt.Sprintf("ptr(%s)", e.scalarReader(t.Width, address))
}
func (e *jsEmitter) pointerOffsetReader(t *Pointer, address location) string {
if t.Width == nil {
return fmt.Sprintf("%s(NULL, %s)", e.helper("$offset"), address.call("readPointer"))
}
raw := e.scalarReader(t.Width, address)
if t.Width.Kind.Size() == 8 {
return raw + ".toNumber()"
}
return raw
}
func (c *compositeEmitter) arrayReader(t *Array, address location, entry *fieldEntry) string {
kind := characterKind(t.Element)
if t.Length == nil {
@@ -866,7 +877,7 @@ func (c *compositeEmitter) emitToJSON(out *strings.Builder) {
out.WriteString(" toJSON() {\n return {")
first := true
for _, entry := range c.entries {
if entry.field.Name == "" || entry.field.Hidden {
if entry.field.Name == "" || entry.field.Hidden || entry.field.PointerBase != nil {
continue
}
if !first {
@@ -889,7 +900,7 @@ func (c *compositeEmitter) emitPattern(out *strings.Builder) {
fmt.Fprintf(out, " return %s(bytes);\n }\n", c.helper("$matchPattern"))
fmt.Fprintf(out, " static $encode(bytes, base, fields) {\n")
for _, entry := range c.entries {
if entry.field.Name == "" {
if entry.field.Name == "" || entry.field.PointerBase != nil {
continue
}
fmt.Fprintf(out, " if (fields.%s !== undefined) %s;\n", entry.accessor, c.encoder(entry.field.Type, "base + "+entry.offset, "fields."+entry.accessor, entry))
+3 -2
View File
@@ -597,7 +597,8 @@ func (p *parserEmitter) emitField(field *Field, indent string, sequential bool)
fmt.Fprintf(p.out, "%s %s(%s, $at, $n);\n", indent, p.helper("$check"), env)
}
if field.PointerBase != nil {
fmt.Fprintf(p.out, "%s $value = $value.add(%s($env, $this, $value));\n", indent, p.functionRef(field.PointerBase))
raw := p.pointerOffsetReader(Unalias(field.Type).(*Pointer), location{base: p.envBase(env), offset: "$at"})
fmt.Fprintf(p.out, "%s $value = %s(%s, %s, $at, %s, (raw) => %s($env, $this, raw));\n", indent, p.helper("$rebase"), env, p.envBase(env), raw, p.functionRef(field.PointerBase))
}
fmt.Fprintf(p.out, "%s $this.%s = $value;\n", indent, field.Name)
fmt.Fprintf(p.out, "%s %s.%s = new %s(%s, $at, $n);\n", indent, p.fields(), field.Name, p.helper("$Span"), p.envBase(env))
@@ -719,7 +720,7 @@ func (p *parserEmitter) emitStaticField(field *Field, indent string, size int, a
func (p *parserEmitter) emitFieldMetadata(field *Field, indent string, value string) {
if pointer, isPointer := Unalias(field.Type).(*Pointer); isPointer {
fmt.Fprintf(p.out, "%s%s.%s.target = () => %s;\n", indent, p.fields(), field.Name, p.reader(pointer.Target, fmt.Sprintf("%s(%s, %s)", p.helper("$offset"), p.base(), value)))
fmt.Fprintf(p.out, "%s%s.%s.target = () => %s.isNull() ? [null, 0] : %s;\n", indent, p.fields(), field.Name, value, p.reader(pointer.Target, fmt.Sprintf("%s(%s, %s)", p.helper("$offset"), p.base(), value)))
}
if field.Hidden {
fmt.Fprintf(p.out, "%s%s.%s.hidden = true;\n", indent, p.fields(), field.Name)
+5
View File
@@ -1138,10 +1138,15 @@ func (r *resolver) applyFieldAttributes(field *Field, attributes []Attribute, sc
}
}
case "pointer_base":
if _, isPointer := Unalias(field.Type).(*Pointer); !isPointer {
r.report(attribute.Position, "pointer_base can only be applied to pointer fields")
continue
}
if len(attribute.Arguments) == 1 {
if text, isString := attribute.Arguments[0].(*StringLiteral); isString {
if function, isFunction := r.lookupFunction(text.Value); isFunction {
field.PointerBase = function
r.warn(attribute.Position, fmt.Sprintf("%s is only resolved when parsing", field.Name))
continue
}
}
Loaded 100 of 238 files, more files were not shown because too many files have changed in this diff. Show more