Compare commits

...
127 Commits
Author SHA1 Message Date
Ole André Vadla Ravnås cee6330e8d subprojects: Prepare for release 2026-04-28 12:23:06 +02:00
Ole André Vadla Ravnås 74b86cb838 compat: Forward parent's apple-min-os
The compat sub-build creates fresh MachineSpecs and lets
env_apple fall back to APPLE_MINIMUM_OS_VERSIONS, which on
macOS is 11.0. With a higher parent setting (e.g. 15.0 to
unlock Apple's TLS backend) frida-core's TLS provider check
silently fails in the sub-build and we end up linking
gioopenssl into the compat slice — pulling libssl/libcrypto
into frida-agent-arm64e.dylib alongside the wrap-built libnice
that already uses CommonCrypto.

Forward host_os_family from compat/meson.build (mirroring how
tools/cgo/detect.py already takes it), parse the parent's
frida-{os}-{arch}.txt machine file for the existing
-target ...-apple-{os}<ver> stamp on Darwin, and feed that
back as apple_min_os to the recursive configure() call so the
sub-build inherits the same deployment floor.
2026-04-26 19:47:40 +02:00
Ole André Vadla Ravnås 07f8d8dd9a deps: Skip OpenSSL on macOS
Now that libnice uses the apple-crypto backend (CommonCrypto +
arc4random_buf) and the fruity sources that called into OpenSSL
(pairing.vala, tls-psk.vala, tunnel.vala) are gated on
host_os != 'macos', nothing on macOS depends on OpenSSL.

Drop openssl_dep / 'openssl' from backend_deps_private and the
fruity backend's --pkg list on macOS so it stops appearing in
frida-core-1.0.pc and getting merged into the devkit.
2026-04-26 17:35:09 +02:00
Ole André Vadla Ravnås 642129c7fc subprojects: Bump outdated 2026-04-26 17:21:09 +02:00
Ole André Vadla Ravnås 44021fc5f2 submodules: Bump releng 2026-04-26 17:19:16 +02:00
Ole André Vadla Ravnås 04952c9f74 fruity: Polish XPC UUID helper
Two small tidy-ups on the make_random_v4_uuid helper added in
the previous commit:

- Drop the redundant `static` qualifier on hex_nibble; at
  namespace scope (rather than inside a class) it's meaningless.
- Use `i != uuid.length` rather than `i < uuid.length` for the
  loop bound, matching the surrounding house style.
2026-04-26 15:34:02 +02:00
Ole André Vadla Ravnås c760728677 fruity: Use GLib.Uuid for XPC random UUID
Drop OpenSSL.Rng.generate from xpc.vala's make_random_v4_uuid in
favour of GLib.Uuid.string_random, parsing the 32 hex chars back
into 16 bytes. Removes the only remaining OpenSSL caller in
xpc.vala, and the v4-and-variant bit fixup goes with it -- the
GLib helper bakes those in already.
2026-04-26 15:28:13 +02:00
Ole André Vadla Ravnås 73031586ab fruity: Skip tls-psk.vala on macOS
TlsPskClientStream is only consumed by TcpTunnelConnection, and
tunnel.vala (along with the rest of the iOS USB tunneling glue)
is already excluded from the macOS build. Move tls-psk.vala into
the same conditional block so its OpenSSL TLS-PSK code stops
getting compiled in -- and pulled into Luma's macOS binary -- on
a host that never reaches that code path.
2026-04-26 15:25:27 +02:00
Ole André Vadla Ravnås fafb45499f package-server-fruity: Use lib/frida-1.0 paths
Catches up with the install-path rename from cd3b35bd. Both
ends move: the input is read from the new bundle layout, and
the output is placed where frida-server resolves the agent
at runtime (via FRIDA_LIBDIR_NAME).
2026-04-26 14:04:52 +02:00
Ole André Vadla Ravnås e600ff51ed compiler: Fix asset paths in c-archive mode
The compiler-backend asset path is only meaningful when the
backend lives as a separate file. In c-archive mode it is
linked into libfrida-core, so FRIDA_COMPILER_BACKEND_NAME is
never defined — narrow the gate to the installed-library and
installed-executable modes that actually consume it.
2026-04-26 13:33:57 +02:00
Ole André Vadla Ravnås cec816b7da subprojects: Bump outdated 2026-04-26 13:17:26 +02:00
Ole André Vadla Ravnås 9d67b97b6e nice: Pick Apple CommonCrypto backend on Darwin
Bump the libnice subproject to 9a3da6e3 (auto-picks
CommonCrypto + arc4random_buf on Darwin) and switch the wrap's
crypto-library default from openssl to auto, matching
releng/deps.toml. On Darwin that gets STUN's RAND/HMAC-SHA1/MD5
from the new Apple primitives instead of OpenSSL; elsewhere it
falls through to gnutls or openssl as before.
2026-04-26 13:12:33 +02:00
Ole André Vadla Ravnås 5cb91d9e25 compiler: Fix backend mode on Apple non-macOS
Use host_os_family to detect Apple platforms instead of
checking Go's GOOS, which is "ios" (not "darwin") for iOS.
The previous check always evaluated false on iOS, causing
configure to pick c-shared — unsupported by Go's toolchain
on any Apple OS other than macOS.
2026-04-26 11:28:23 +02:00
Ole André Vadla Ravnås 9a0880ff5d api: Merge referenced top-level delegates
The .gir merge pulls classes, interfaces, enums and gtype
structs from core and base, but not the top-level <callback>
elements Vala emits for namespace-level delegates. Downstream
consumers like frida-node's codegen then fail to resolve the
delegate type (e.g. WebRequestHeaderFunc).

Scan the merged namespace for Frida./FridaBase. type
references and pull in any source-gir <callback> whose name
is referenced but not yet present. This avoids relying on
beautified C typedef strings and naturally extends to any
future namespace-level delegate.
2026-04-26 11:09:15 +02:00
Ole André Vadla Ravnås 562a74ded7 Revert "api: Propagate top-level delegates"
This reverts commit 4393572b45.

The fix was a no-op: it scanned beautified typedef strings of
the form "(* FridaFoo)" with the regex \(\*(\w+)\), which never
matched because beautify_cprototype rewrites "(*" to "(* " and
the regex requires no space. delegate_c_types stayed empty, so
no callbacks were ever copied into the merged gir. Replaced
in a follow-up commit by a reference-driven merge.
2026-04-26 11:09:15 +02:00
Ole André Vadla Ravnås 3d0f4b0529 subprojects: Bump outdated 2026-04-26 10:28:18 +02:00
Ole André Vadla Ravnås 7c6ad4fdf8 meson: Flatten TLS provider default_options
Splice tls_provider_backend_options into dependency()'s
default_options with `+` instead of nesting it. meson rejects
array[list | str]; default_options must be array[str].
2026-04-26 10:21:04 +02:00
Ole André Vadla Ravnås 751f19e2db subprojects: Bump outdated 2026-04-26 10:16:28 +02:00
Ole André Vadla Ravnås 4059c74936 submodules: Bump releng 2026-04-26 10:16:27 +02:00
Ole André Vadla Ravnås af1b89d02c p2p: Replace OpenSSL cert gen with SecKey
On Darwin, _frida_generate_certificate now uses SecKey for RSA
keygen and signing and hand-rolls the X.509 TBSCertificate DER
instead of going through OpenSSL's X509 + EVP_PKEY + PEM APIs.
The OpenSSL path stays for non-Apple builds.
2026-04-26 09:22:56 +02:00
Ole André Vadla Ravnås f77ae76242 apple: Use gioapple TLS backend on Darwin
Select the Apple-native gioapple backend (Security.framework +
Network.framework) instead of gioopenssl on Darwin.

This removes frida-core's OpenSSL TLS dependency on Apple
platforms, eliminating the non-exempt-crypto footprint App
Store review calls out.
2026-04-26 09:22:56 +02:00
Ole André Vadla Ravnås 0fec62a283 host-session: Add simmy and local provider icons
Give the two remaining providers without artwork a bundled
glyph:

  - local: desktop monitor with a white shell prompt on a
    dark screen — now the default for LocalHostSessionProvider
    on Linux, FreeBSD, and QNX. Windows and Darwin keep
    overriding with their system-extracted icons.
  - simmy: light silver phone with macOS traffic-light dots
    on the screen — reads as a simulated iOS device on macOS.
2026-04-24 01:02:55 +02:00
Ole André Vadla Ravnås bdcae8b809 host-session: Modernize provider icons
Replace the hardcoded base64 RGBA blobs with clean 96x96 PNGs
bundled through the resource compiler (src/icons.resources).
Each provider now calls a shared make_provider_icon helper to
build the icon Variant from the embedded blob.

The new artwork (fruity-network phone, droidy head, socket
globe, barebone chip, portal hub) follows SF Symbols and
libadwaita conventions so it reads well in both SwiftUI and
GTK/Adwaita consumers.

Add tools/generate-provider-icons.py to regenerate the PNGs
from SVG sources.
2026-04-24 00:43:44 +02:00
cl45h ee784bdd18 compat: Fix build with empty prebuild set (#1230)
When FRIDA_ALLOWED_PREBUILDS is set to an empty string (e.g. when
building with --without-prebuilds on platforms without prebuilt
binaries), `"".split(",")` produces `['']` instead of `[]`. This
results in a set containing an empty string `{''}` rather than an
empty set, which is then passed as `allowed` to the build system
causing it to attempt downloading nonexistent prebuilds.

Use `filter(None, ...)` to strip empty strings from the split result,
so an empty env var correctly produces an empty set.

This fixes building from source on platforms like FreeBSD where no
prebuilt toolchain/SDK binaries are available.
2026-04-20 17:19:17 +02:00
Ole André Vadla Ravnås 5adb546db6 subprojects: Bump outdated 2026-04-20 17:16:30 +02:00
Ole André Vadla Ravnås 4393572b45 api: Propagate top-level delegates into merged gir
The .gir merge pulls classes, interfaces, enums and gtype
structs from core and base, but not the top-level <callback>
elements Vala emits for namespace-level delegates. Downstream
consumers like frida-node's codegen then fail to resolve the
delegate type (e.g. WebRequestHeaderFunc). Copy matching
callbacks over using the delegate c-types we already captured
per object type.
2026-04-20 17:00:32 +02:00
Ole André Vadla Ravnås 440a03f0d4 ci: Bypass success() chain for xcframework publish
The needs.xcframework.result alone isn't enough to escape GitHub
Actions' chained success() check; add !cancelled() so the condition
is evaluated even when needed jobs transitively report failure.
2026-04-20 16:34:21 +02:00
Ole André Vadla Ravnås cdcb36b0f3 subprojects: Bump outdated 2026-04-20 16:31:40 +02:00
Ole André Vadla Ravnås 862d4781e0 submodules: Bump releng 2026-04-20 16:31:39 +02:00
Ole André Vadla Ravnås a7be127b63 ci: Gate publish-xcframework on xcframework only
GitHub Actions' default success() check walks the whole needs
graph, so a Linux fluke failing native ended up skipping
publish-xcframework even though xcframework itself succeeded.
Reference needs.xcframework.result explicitly so only that
direct dependency decides whether to publish.
2026-04-20 15:16:04 +02:00
Ole André Vadla Ravnås be055ae47b ci: Add setuptools for Windows distutils shim
glib's gdbus-codegen still imports distutils, which Python 3.12
removed from the stdlib. setuptools ships a distutils-precedence
.pth that puts its own distutils back on sys.path, so installing
it is enough to keep the codegen running.
2026-04-20 15:15:51 +02:00
Ole André Vadla Ravnås 7bbf62c092 subprojects: Bump outdated 2026-04-20 14:52:15 +02:00
Ole André Vadla Ravnås 40bbe0b925 submodules: Bump releng 2026-04-20 14:52:15 +02:00
Ole André Vadla Ravnås 7c61cedeae ci: Drop now-unneeded wrap overrides
With releng isolating pkg-config per machine, the cross-mingw
and emulator jobs no longer leak host packages into their host
machine configuration, so the wraps for glib, pcre2, and openssl
kick in naturally. Keep --force-fallback-for=openssl on native
Windows since msys2's OpenSSL lacks Frida's QUIC patches.
2026-04-20 14:51:51 +02:00
Ole André Vadla Ravnås ec1981aeeb subprojects: Bump outdated 2026-04-20 14:40:58 +02:00
Ole André Vadla Ravnås 8488018e90 submodules: Bump releng 2026-04-20 14:40:58 +02:00
Ole André Vadla Ravnås b386666646 ci: Run xcframework despite unrelated failures
Use !cancelled() on the xcframework job so it still runs when
needed jobs (native, mobile) report failure for unrelated matrix
entries such as a flaky Linux build. The download-artifact steps
will fail if the macOS/iOS devkits are actually missing, so the
publish step still gates on xcframework's real success.
2026-04-20 14:32:26 +02:00
Ole André Vadla Ravnås c9028d73c2 ci: Force glib wrap on emulator and x86-mingw
Both the x-tools-linux-be-target container and the i686 mingw
cross-toolchain expose a target glib through pkg-config, which
the build now prefers over the wrap. That skips wrap-provided
deps such as zlib (needed by libunwind) and pcre2 (needed by
glib's gregex). Force the wrap so Frida's patched glib is used
along with its bundled subprojects.
2026-04-20 14:32:18 +02:00
Ole André Vadla Ravnås 71244206db ci: Force openssl wrap for fruity backend
System OpenSSL on the affected runners lacks the experimental
SSL_QUIC_METHOD API needed by ngtcp2's quictls helper, so the
ngtcp2 subproject builds neither libngtcp2_crypto_quictls nor
libngtcp2_crypto_ossl. Force the openssl wrap, which carries
Frida's QUIC patches, matching the windows-shared workflow.
2026-04-20 14:32:10 +02:00
Ole André Vadla Ravnås 96ebd59ffa core: Use Json.Reader helper to pin root node
Reroute remaining hand-constructed Json.Reader call sites through
make_json_reader_from_node, which attaches the original root node
to the reader as object data.

Json.Reader.set_root() copies the supplied node via json_node_copy().
For object/array nodes that copy is a shallow shell that shares the
JsonObject/JsonArray via refcount, but the members inside the shared
container keep their parent pointers set to the *original* root, not
the shell. Dropping the original leaves json_reader_end_member()'s
walk via json_node_get_parent() touching a freed node, which on
builds with glib checks enabled (e.g. Fedora) trips JSON_NODE_IS_VALID
and aborts.

See GNOME bugzilla #774684.
2026-04-19 16:40:25 +02:00
Ole André Vadla Ravnås 86be996ede compiler: Skip version script on Windows
Windows PE linkers don't honor --version-script. MSYS2 CLANGARM64's
lld rejects the unknown argument outright; the MinGW x86_64 gcc+bfd
toolchain used to silently ignore it. In c-shared mode on Windows,
Go's default export filtering already limits visibility to symbols
tagged with //export, so dropping the version script changes no
observable behavior.
2026-04-19 11:12:10 +02:00
Ole André Vadla Ravnås 2abe10aeec meson: Fall back to system lzfse without .pc file
Detect lzfse.h + -llzfse directly when pkg-config has no entry,
matching how frida-gum handles libdwarf. Lets Fedora's lzfse-devel
satisfy the dependency.
2026-04-18 23:58:30 +02:00
Ole André Vadla Ravnås 3d49a01980 compat: Skip helper/agent without local backend
Without the local backend, frida-helper and frida-agent are not
produced by the main or nested meson builds, so the compat
arch-support target failed trying to compile them for the other
architecture. Treat helper and agent as components gated on
have_local_backend, matching how gadget and server are already
wired, and drop empty output groups before state serialization.
2026-04-18 14:45:54 +02:00
Ole André Vadla Ravnås 7721867fd4 linux: Drop libbpf dep without local backend
The BPF wrappers in lib/base/linux.vala are only consumed by
helper-backend sources that ship as part of the local backend,
and nothing else references libbpf. Gate the class definitions
on HAVE_LOCAL_BACKEND and the libbpf lookup/link on
have_local_backend so socket-only or compiler-only builds on
Linux no longer require libbpf at all.
2026-04-18 14:45:08 +02:00
Ole André Vadla Ravnås b2023ade06 meson: Simplify libbpf dependency lookup
Drop the non-Frida-GLib branch that explicitly instantiated the
libbpf subproject before resolving the dependency. Meson already
falls back to the wrap automatically when the dependency is not
found, so the extra subproject() call was redundant.
2026-04-18 14:29:21 +02:00
Ole André Vadla Ravnås c5e3c37849 compiler: Fix shared-library build on Windows
backend-glue.c unconditionally referenced the cgo init symbol
whenever the compiler backend was enabled, but that symbol only
exists when the Go backend is statically linked (c-archive mode).
Gate it on a new HAVE_COMPILER_BACKEND_LINKED define to mirror
the Vala-side COMPILER_BACKEND_LINKED guard.
2026-04-18 14:18:35 +02:00
Ole André Vadla Ravnås b62bc0e17c compiler: Pass Go config via file, not cmdline
The base64-encoded config embeds the full process environment,
which can easily blow past command-line length limits on some
platforms. Write it to <build_root>/tools/cgo/go-config.json
instead and pass the path to build-backend.py.
2026-04-18 13:00:44 +02:00
Ole André Vadla Ravnås a787ecc4bf socket: Add WebRequestHandler support
Add a WebRequestHandler interface plus WebRequest / WebResponse
data classes. EndpointParameters gains a request_handler property;
ConnectionHandler consults it (via a new handle_http_request flow)
before falling through to asset_root, and returns 404 if neither
is set.

Also extend the API generator to list the new types and scan both
core_header and base_header for delegate typedefs, so types living
in lib/base (including WebRequestHeaderFunc) end up in the public
header.
2026-04-18 01:39:00 +02:00
Ole André Vadla Ravnås 86f76f8630 windows: Install dbghelp / symsrv alongside agent
frida-gum's ext/ subprojects install these helpers, but
releng/meson_make runs `meson install --skip-subprojects` when
default_library=shared to keep other subproject artifacts (e.g.
capstone) out of the prefix. That also drops the dbghelp /
symsrv rules on the floor. Re-install them from frida-core so
installed-mode distributions ship them next to frida-agent.dll,
where the host session's elevated-injection asset bundling
expects them.
2026-04-17 23:51:10 +02:00
Ole André Vadla Ravnås 5e1b97236d portal-service: Allow registering extra endpoints
Add add_cluster_endpoint() and add_control_endpoint() that queue
extra EndpointParameters. start() spins up a WebService per
registered endpoint; stop() tears them all down. Both throw
INVALID_OPERATION once the service has started — dynamic
add-after-start isn't supported.

Motivated by wanting a second control listener (e.g. a plain-HTTP
one behind a Cloudflare Tunnel) alongside the main TLS-pinned one.
2026-04-17 23:50:53 +02:00
Ole André Vadla Ravnås ec8df6a896 windows: Fix agent loading in installed-mode
The host session only populated the AgentDescriptor under
HAVE_EMBEDDED_ASSETS, so any attach with assets=installed
crashed inside Winjector.inject_library_resource() with a null
agent. Mirror the Linux pattern: take the #else branch through
inject_library_file_with_template() using Frida.agent_path (and
the per-arch dbghelp / symsrv neighbors as dependencies for
the elevated-injection asset bundle).

Two supporting fixes:

* AssetLocation.detect() now translates <prefix>\bin to
  <prefix>\lib on Windows, where the frida-core DLL ships in
  bin/ but agent assets live under lib/frida-1.0/.
* Winjector.inject_library_file_with_template() is now public,
  as on Linux, so host sessions can target an installed layout
  without building an AgentDescriptor.
2026-04-17 23:40:49 +02:00
Ole André Vadla Ravnås b92a4562b8 build: Force sqlite subproject to static
libsoup's internal `dependency('sqlite3')` would otherwise trigger the
wrap fallback with the parent's `default_library=shared`.
2026-04-17 23:40:49 +02:00
joe 25d7c2a648 fruity-syscall-trace: Tolerate sig fails (#1226)
In the add-targets handler, the loop that populates target_pids
previously let any failing query_symbolicator_signature call
propagate out and abort the entire request, meaning one unhappy
PID killed the whole trace.

Catch Frida.Error per PID and fall back to a bare Fruity.CsSignature
carrying just the pid. The trace keeps going for the remaining
targets, and the fallback signature is benign for downstream code
that only reads the pid field until a real signature becomes
available.
2026-04-17 20:21:15 +02:00
Megladon 6fef95a6dd syscall-trace: Support include-syscall (#1228) 2026-04-17 20:14:39 +02:00
Francesco Tamagni 1f0c537720 fruity: Detach lldb only if no attached gadgets
before: when multiple gadget entries relied on the same lldb session,
the first to detach also detached lldb
after: only the last detached gadget entry actually detaches lldb
2026-04-17 20:10:52 +02:00
Ole André Vadla Ravnås c41fadd01e core: Expose _-prefixed asset path init helpers
The init/deinit_asset_paths functions are implemented in Vala
and called from frida-glue.c. Declaring them internal made
Vala warn they were never used (it doesn't see the C callers).

Promote to public so Vala stops complaining, and add a leading
underscore to signal they aren't part of the stable public API.
2026-04-14 23:51:21 +02:00
Ole André Vadla Ravnås b0ded01fd3 fruity: Move sleep to device-monitor-portable
The sleep helper only has callers in device-monitor-portable.vala,
which isn't built on macOS — leaving the definition in
device-monitor.vala looked unused there and tripped Vala.
2026-04-14 23:51:21 +02:00
Ole André Vadla Ravnås 98546506e2 fruity: Move OpenSSL keypair helpers to pairing
The make_keypair / get_raw_public_key / get_raw_private_key
wrappers lived in xpc.vala but are only referenced from
pairing.vala and tunnel.vala, which aren't built on macOS.
That orphaned them there and tripped Vala "never used"
warnings on macOS builds.
2026-04-14 23:47:09 +02:00
Ole André Vadla Ravnås a3b336ec15 compiler: Keep backend Module resident
The Module holding the dlopen'd backend was a local in init(),
so it was released the moment init() returned — at which point
g_module_close() is free to unload the image out from under the
build/watch/dispose function pointers we just resolved.

Mark it resident so the module's lifetime matches the symbols.
2026-04-14 23:26:10 +02:00
Ole André Vadla Ravnås 36b092ea66 core: Drop soversion from shared library
The previous soversion tracked the outer Frida toolkit version,
bumping the SONAME on every Frida release regardless of whether
frida-core's ABI actually changed. Drop it for now; the "1.0" in
the library name should probably be replaced by the Frida major
version eventually, at which point any soversion scheme can be
revisited.
2026-04-14 18:43:41 +02:00
Ole André Vadla Ravnås a2a1cce8e9 compiler: Fix macOS c-shared symbol export
The exported_symbols_list patterns use Mach-O symbol names, which
add an underscore prefix to C names. Since CGO //export directives
already start with an underscore, the real Mach-O symbols are
__frida_compiler_backend_*, so the single-underscore pattern matched
nothing and the plugin exported zero symbols.
2026-04-14 18:29:27 +02:00
Ole André Vadla Ravnås b63ff37ad7 build: Always define FRIDA_LIBDIR_NAME
AssetLocation references it unconditionally, so it must
be present in config.h even for embedded-asset builds.
2026-04-13 16:02:12 +02:00
Ole André Vadla Ravnås 9fe9f529b5 subprojects: Bump outdated 2026-04-13 14:36:35 +02:00
Ole André Vadla Ravnås 2bd2816d9a ci: Add Windows shared library build
Test both x86_64 and arm64 using vcpkg for the system
dependencies (GLib, json-glib, libsoup).
2026-04-13 14:28:22 +02:00
Ole André Vadla Ravnås 5d221ea316 api: Bundle GModule-2.0.gir
Gio-2.0.gir depends on GModule, so g-ir-compiler
needs it when compiling the Frida typelib.
2026-04-13 14:28:16 +02:00
Ole André Vadla Ravnås 2c8bb33afb api: Pass girdir to g-ir-compiler
Supply --includedir so g-ir-compiler can find base GIR
files on platforms where they are not in a default search
path.
2026-04-13 14:28:11 +02:00
Ole André Vadla Ravnås 8b5b56990c build: Support shared library on Windows
Add Windows-specific asset directory layout with per-arch
subdirs. Set HAVE_EMBEDDED_ASSETS and HAVE_COMPILER_BACKEND
in config.h for the C code. Link shlwapi on Windows.
2026-04-13 13:52:35 +02:00
Ole André Vadla Ravnås 1875ededf2 windows: Gate embedded assets
Guard resource store and agent blob references with
HAVE_EMBEDDED_ASSETS so non-embedded builds compile.
2026-04-13 13:52:31 +02:00
Ole André Vadla Ravnås 35063b1dea build: Default to static for more fallbacks
Specify default_library=static for json-glib, libsoup,
and libnghttp2, consistent with how other fallback
subprojects are already configured.
2026-04-13 13:52:04 +02:00
Ole André Vadla Ravnås 66c4c0e288 build: Add nghttp2 wrap
Needed for builds without an SDK, where libnghttp2 would
otherwise not be available as a system dependency.
2026-04-13 13:52:04 +02:00
Ole André Vadla Ravnås 7a2127cfd3 build: Add relocatable asset resolution
For non-embedded asset builds, dynamically compute
asset paths at init time using the module's own
location instead of hardcoding absolute paths at
compile time.

Add AssetLocation in lib/base, which uses
Gum.Process.enumerate_modules to find the directory
containing our shared library, then derives asset
paths relative to it.
2026-04-13 13:51:59 +02:00
Ole André Vadla Ravnås d72314b3fa subprojects: Bump outdated 2026-04-10 23:55:08 +02:00
Ole André Vadla Ravnås 58d4b7fa17 submodules: Bump releng 2026-04-10 23:55:07 +02:00
Ole André Vadla Ravnås cfcc8ae9e4 meson: Fix has_function with internal libsoup
Work around a Meson bug where cc.has_function() rejects
internal (subproject) dependencies. Assume the feature is
present when libsoup comes from a subproject, matching
the existing GLib pattern.
2026-04-10 23:54:08 +02:00
Ole André Vadla Ravnås 21cdaee891 meson: Finish adding default_library=static
Cover all remaining dependency() calls that have a corresponding .wrap.
2026-04-10 23:41:56 +02:00
Ole André Vadla Ravnås 2251f7893e fruity: Fix non-macOS build after code split
Add OpenSSL using-directives to pairing.vala, and fully
qualify the few OpenSSL type references in tunnel.vala and
xpc.vala instead of pulling in using-directives for just a
handful of call sites. Also drop the now-unnecessary ones
from xpc.vala.
2026-04-10 20:07:47 +02:00
Ole André Vadla Ravnås 7dc83ad22a fruity: Split out portable tunnel and pairing code
Move UsbmuxTunnel and the PortableCoreDevice* classes from
device-monitor.vala into device-monitor-portable.vala.

Move TunnelConnection, TunnelParameters, TcpTunnelConnection,
QuicTunnelConnection, and TunnelKey from xpc.vala into
tunnel.vala.

Move PairingService, PairingTransport, XpcPairingTransport,
PlainPairingTransport, PairingStore, PairingIdentity,
PairingPeer, PairingServiceMetadata, DeviceOptions,
DeviceInfo, and supporting types from xpc.vala into
pairing.vala.

Also move network-stack.vala into the same conditional
group now that all its users are in excluded files.

These are all excluded at the meson.build level on macOS,
replacing the #if !MACOS blocks with file-level exclusion.
2026-04-10 14:28:38 +02:00
Ole André Vadla Ravnås 15a9fef3a5 subprojects: Bump outdated 2026-04-10 13:21:01 +02:00
Ole André Vadla Ravnås f6d274f49e macos: Fix shared library build
Drop libngtcp2, libusb-1.0, and lwip deps on macOS, where
Apple's CoreDevice tunnel is used instead. Guard the
portable USB/NCM/tunnel code with #if !MACOS in the Vala
sources, and exclude usb.vala/ncm.vala at the meson.build
level.

Fix gee-0.8 vapi discovery for builds using system libgee
by querying its pkg-config prefix to locate the vapidir.

Fix compat sub-builds to use installed assets and filter
conflicting options, avoiding the embed-agent chicken-and-
egg problem on macOS.

Also fix OpenSSL vapi headers, Capstone compat with system
packages, compiler backend linker flags on macOS, and add
missing CoreFoundation framework linkage.
2026-04-10 13:13:22 +02:00
Ole André Vadla Ravnås 7300274bcf subprojects: Bump outdated 2026-04-09 13:41:29 +02:00
Ole André Vadla Ravnås 3b04d85adf submodules: Bump releng 2026-04-09 13:41:29 +02:00
Ole André Vadla Ravnås ba97684104 subprojects: Bump outdated 2026-04-09 12:14:30 +02:00
Ole André Vadla Ravnås e90015f917 submodules: Bump releng 2026-04-09 12:14:29 +02:00
Ole André Vadla Ravnås 57b378387b subprojects: Bump outdated 2026-04-09 11:50:28 +02:00
Ole André Vadla Ravnås 627152da6a submodules: Bump releng 2026-04-09 11:50:27 +02:00
Ole André Vadla Ravnås f7511ea32d fruity: Drive PSK TLS via OpenSSL directly
The TCP tunnel handshake to a paired Apple device speaks TLS-PSK,
which we previously set up by getting at the underlying SSL handle
of a GTlsClientConnection through glib-networking's OpenSSL backend
(g_tls_connection_openssl_get_ssl). This only works on systems where
glib-networking ships an OpenSSL backend, which excludes most distros
that build it against GnuTLS — including Fedora — leaving the Fruity
backend with "GIO OpenSSL TLS backend not available" the moment a
shared build is run there.

Replace the glib-networking detour with a small TlsPskClientStream
that drives OpenSSL on the raw IOStream returned by the netstack:

  - The class is an IOStream wrapping the raw stream's pollable
    input/output. The handshake runs in init_async, alternating
    SSL_do_handshake with non-blocking BIO drains and reads from the
    underlying pollable stream.
  - Steady-state read/write go through SSL_read/SSL_write against
    a pair of memory BIOs. The non-blocking input path pulls fresh
    ciphertext via base.read_nonblocking when SSL signals WANT_READ;
    the non-blocking output path produces ciphertext into wbio and
    drains it via base.write_nonblocking, leaving any leftover in
    wbio so a subsequent call (woken by the chained pollable source)
    can finish flushing it. Back-pressure is structural — there are
    no buffers we own, no pump tasks, no watermarks.
  - The pollable sources chain directly to the base stream's source
    via PollableSource.full, so the consumer's poll loop wakes
    correctly and SSL_pending() is folded into is_readable() to
    avoid stalling on already-decrypted plaintext.

Drop the HAVE_GIOOPENSSL helpers and the runtime dlsym fallback in
xpc.vala — TcpTunnelConnection.open now wraps the raw stream with
TlsPskClientStream and TLS no longer touches GIO at all. Other parts
of frida-core that use the gioopenssl backend (portal, gadget, agent)
are unaffected since they still go through tls_provider_dep.

Extend vapi/openssl.vapi with the bits the new code needs:
SSL_set_bio, SSL_do_handshake, SSL_read/write, SSL_get_error,
SSL_pending, the SSL_ERROR_* enum, and BIO_new/free/read/write/
ctrl_pending/get_mem_data wrappers using void* so we can hand the
BIO ownership to the SSL via SSL_set_bio. Also add the missing
cheader_filename to the OpenSSL.Error namespace.
2026-04-09 11:04:43 +02:00
Ole André Vadla Ravnås 4a7ebebff5 base: Add make_json_reader() lifetime helper
Json.Reader.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 — as happens any
time we call `new Json.Reader (Json.from_string (...))` — the parent
walk inside json_reader_end_member() touches a freed node. On builds
with GLib checks enabled (e.g. Fedora) this trips JSON_NODE_IS_VALID
and aborts; on our toolchain builds it silently corrupts.

This is GNOME bugzilla #774684 from 2016. The fix lives in our
JSON-GLib fork but was never upstreamed.

Add a tiny helper that constructs the Json.Reader and attaches the
parsed root to it as object data, keeping the original alive for the
reader's lifetime so the parent pointers inside the shared container
remain valid. Migrate every existing call site that piped from_string
straight into a new Json.Reader.
2026-04-09 11:04:20 +02:00
Ole André Vadla Ravnås 30ef70353e fruity: Fix plist hash/eq for Bytes values
hash_value() and compare_values_eq() in the binary plist serializer
lumped Bytes in with PlistDict and PlistArray and routed all three
through g_value_get_object(). Bytes is a G_TYPE_BOXED, not a GObject,
so the call asserts under GLib builds with checks enabled (e.g. on
Fedora). Our toolchain builds disable checks, so the misuse went
unnoticed and returned the boxed pointer reinterpreted as a GObject*.

Use get_boxed() for Bytes and hash/compare by content, which is also
what the deduplication map presumably wanted in the first place: two
equal-content Bytes will now actually share an offset table entry
instead of staying distinct because their pointers differ.
2026-04-09 11:04:10 +02:00
Ole André Vadla Ravnås 89686d07bb build: Fix installed pkg-config for shared builds
Two issues with the installed frida-core-1.0.pc:

- vapidir was being overwritten by a build-tree path further down in
  meson.build, leaking a local source directory into the installed
  variables. Rename the local one to local_vapidir.

- Passing the core library object to pkg.generate caused Meson to
  auto-derive Requires.private from its declared dependencies, which
  pulled in a bunch of internal subprojects (frida-gum-1.0, libbpf,
  usrsctp, etc.) that aren't installed. In shared mode, pass the
  library as raw -L/-l strings instead so the shared library's
  DT_NEEDED stays the source of truth for runtime dependencies.
2026-04-08 16:43:57 +02:00
Ole André Vadla Ravnås 943cc1e261 compat: Rename upstream-glib payloads on install
When building with upstream GLib, the native-arch agent and gadget come
out of the compat subsystem alongside the cross-arch ones. They share
the same custom_target output list, so the native ones get an arch
suffix in the build directory to avoid colliding with the cross ones.

Install them under the canonical frida-agent.so / frida-gadget.so names
via a small install script that copies them into the correct per-bits
subdirectory of libdir/frida-1.0. Also strip frida-gum:gumjs and
frida-gum:inspector from the forwarded options so the nested compat
build picks them based on its own default_library=static rather than
inheriting the parent's shared-mode choices.
2026-04-08 16:43:57 +02:00
Ole André Vadla Ravnås cd3b35bdde build: Polish shared library output
A grab-bag of related improvements aimed at producing a clean shared
libfrida-core-1.0.so suitable for distro packaging:

- Use frida-1.0/ for the per-API-version asset directory (matching
  libfrida-core-1.0.so, frida-core-1.0.pc, etc.) so future API
  versions can install in parallel.
- Default the assets option to installed for shared builds, embedded
  for static.
- Install the typelib via gobject-introspection's typelibdir variable,
  so it lands next to the system's other typelibs.
- Force frida-gum, quickjs, and libtcc to static linking, and skip
  in-tree agent/gadget/payload + their pkg-config files when building
  shared (the agent and gadget come from compat in that case).
- Disable GumJS in shared builds and rely on the new inspector-only
  option in frida-gum to keep Script.enable_debugger() working.
- Build the compiler backend as an installable plugin under
  frida-1.0/plugins/ when assets=installed, with new
  COMPILER_BACKEND_INSTALLED_LIBRARY and
  COMPILER_BACKEND_INSTALLED_EXECUTABLE modes. Rename the existing
  modes to LINKED, EMBEDDED_LIBRARY, and EMBEDDED_EXECUTABLE for
  symmetry. The plugin is loaded by path at runtime, so distros can
  ship it as an optional separate package and frida-core gracefully
  reports a context-rich error when it's missing.
2026-04-08 16:43:57 +02:00
Ole André Vadla Ravnås 751f43145b subprojects: Bump outdated 2026-04-08 16:22:24 +02:00
Ole André Vadla Ravnås 7f3553d9fe submodules: Bump releng 2026-04-08 16:22:23 +02:00
Ole André Vadla Ravnås 420279280d subprojects: Bump outdated 2026-04-08 01:15:43 +02:00
Ole André Vadla Ravnås 96d4202ff6 submodules: Bump releng 2026-04-08 01:15:42 +02:00
Ole André Vadla Ravnås 8a72775477 compat: Strip subproject default_library opts too
When forcing default_library=static for upstream-glib builds, also
drop per-subproject overrides forwarded from the parent (e.g.
-Dfrida-gum:default_library=shared) so they don't escape the static
linking requirement.
2026-04-08 01:00:03 +02:00
Ole André Vadla Ravnås 287d211bb8 init: Only assign runtime inside once-init guard
Assigning runtime unconditionally meant a later frida_init() call
would clobber the runtime chosen by an earlier
frida_init_with_runtime(). E.g. an app can now select a runtime
before a language binding calls frida_init().
2026-04-07 12:26:03 +02:00
Ole André Vadla Ravnås a6bb4c536f subprojects: Bump outdated 2026-04-06 15:36:53 +02:00
Ole André Vadla Ravnås e7562fcf69 submodules: Bump releng 2026-04-06 15:36:52 +02:00
Ole André Vadla Ravnås 873f5cae3d docs: Add distro packaging instructions
Document the shared library build workflow and list
prerequisites for Fedora and Ubuntu.
2026-04-06 15:31:11 +02:00
Ole André Vadla Ravnås d841b5ea8d claude-md: Add code style section 2026-04-06 15:31:11 +02:00
Ole André Vadla Ravnås 3d37dfe966 Support upstream GLib and system dependencies
Guard Frida GLib fork APIs (glib_init, gio_init,
g_thread_set_garbage_handler, etc.) with HAVE_FRIDA_GLIB.
Guard libsoup back-pressure APIs with a feature check.

Support ngtcp2_crypto_ossl as alternative to the quictls
variant, with the correct API (configure_client_session
on SSL instead of configure_client_context on SSLContext).

Dynamically resolve gioopenssl symbols when not
statically linked, throwing NOT_SUPPORTED if the GIO
OpenSSL backend is unavailable.
2026-04-06 15:31:11 +02:00
Ole André Vadla Ravnås c178aee655 compat: Build agent via SDK with upstream GLib
When glib_flavor is "upstream", add a native-arch output
group for agent and gadget, force default_library=static,
and allow all prebuilds so the SDK is downloaded even if
the top-level build excluded it.
2026-04-06 15:31:11 +02:00
Ole André Vadla Ravnås 46d1b6be32 api: Generate symbol maps and refactor args
Add symbol-maps output mode that generates a version
script and symbols file exporting frida_* and _frida_*
while hiding all other symbols. Refactor argument
parsing so that output_dir is the first positional arg
and the remaining args are mode-specific.
2026-04-06 15:31:11 +02:00
Ole André Vadla Ravnås 6927214f6e build: Add distro packaging support
Detect shared vs static GLib, skip gioopenssl when
using a shared GLib (system GLib already has GIO TLS
support via glib-networking), produce a versioned
shared library with soversion when building shared,
generate typelib via g-ir-compiler when available, and
adjust pkg-config and dependency declarations for the
shared case.
2026-04-06 15:31:11 +02:00
Ole André Vadla Ravnås 94a9aec68d api: Include class records in GIR
The generated GIR was missing record elements for GType class structs
(e.g. DeviceManagerClass), which caused g-ir-compiler to fail with
"type reference not found" errors when compiling the typelib.
2026-04-06 15:31:11 +02:00
Ole André Vadla Ravnås 4d82b7e98b Add CLAUDE.md 2026-04-05 23:08:27 +02:00
Ole André Vadla Ravnås a62376a3d6 subprojects: Prepare for release 2026-03-27 11:36:34 +01:00
Ole André Vadla Ravnås f5803e76ca subprojects: Bump outdated 2026-03-27 11:34:53 +01:00
Ole André Vadla Ravnås 3b052905d7 package-manager: Fix semver pre-release overflow
Numeric pre-release identifiers like "202508252028" would
fail validation because parse_uint() rejects values beyond
UINT32_MAX. The same overflow affected version comparison,
which also used uint.

Fix validation to only check the semver-mandated rule that
numeric identifiers must not have leading zeros, and fix
comparison to use length-prefixed string comparison, which
handles arbitrarily large numeric identifiers correctly.
2026-03-27 11:32:50 +01:00
Ole André Vadla Ravnås c3864a15a1 subprojects: Prepare for release 2026-03-26 20:43:41 +01:00
Ole André Vadla Ravnås b49aac0765 subprojects: Bump outdated 2026-03-26 20:42:39 +01:00
Ole André Vadla Ravnås 1f77154b83 fruity: Add control-endpoint backend option
Default is `tcp:27042`. Only TCP endpoints are supported.
2026-03-26 20:26:02 +01:00
Ole André Vadla Ravnås d2f2c400dc droidy: Add control-endpoint backend option
Default is `tcp:27042`, but may be set to any endpoint supported by ADB,
such as `localabstract:/my-frida-server`.
2026-03-26 20:26:02 +01:00
Ole André Vadla Ravnås 3df058764b device: Add override_option()
Allow callers to override backend-specific options when creating
a host session.

Overrides take effect when establishing a host session. If a host
session already exists, updated values are applied on the next
connection.

Inspired-by: @giantpune
2026-03-26 20:25:33 +01:00
Ole André Vadla Ravnås f962931617 host-session-options: Add copy() 2026-03-26 20:06:47 +01:00
Ole André Vadla Ravnås be23998210 android: Skip 32-bit helper on 64-bit only systems
Do not waste time trying to spawn it.
2026-03-26 15:24:53 +01:00
Ole André Vadla Ravnås 0bee9cba2b linux: Support injection into group-stopped PIDs 2026-03-26 15:24:53 +01:00
NSEchoandOle André Vadla Ravnås 465698a907 linux: Add eBPF-based spawn gater implementation
Co-authored-by: Ole André Vadla Ravnås <[email protected]>
2026-03-26 15:24:53 +01:00
Ole André Vadla Ravnås 4e5a60a576 subprojects: Prepare for release 2026-03-24 15:40:37 +01:00
Ole André Vadla Ravnås aaa66bbafd subprojects: Bump outdated 2026-03-24 15:39:15 +01:00
Soumik Sarkhel 1dc4435f96 android: Modify RTLD path if dlopen(libart) fails 2026-03-24 15:38:14 +01:00
Ole André Vadla Ravnås ac9f8cac5f fruity: Fix TcpTunnelConnection stalls
The remotepairingdeviced service may defer CDTunnel TLS records
larger than 8192 bytes until a smaller record arrives, which can
stall sustained tunnel output.

When a large write drains the pending output queue, send a minimal
dummy IPv6 datagram to trigger delivery of deferred data and
improve reliability.

Kudos to @tux-mind for identifying this behavior and helping track
it down.

Fixes #1223.
2026-03-24 14:54:04 +01:00
Francesco Tamagni 0ed112fac1 fruity: Leak injector pages on deallocate fail
On iOS 26.4 `lldb.deallocate()` fails all the time for sandbox issues,
probably it's an iOS bug or a new "security" feature from Apple. This is
a sub-optimal workaround that will change in the future.
2026-03-23 14:08:08 +01:00
Francesco Tamagni 266032d035 fruity: Search restartWithDyldInCache by substring 2026-03-23 14:08:08 +01:00
Ole André Vadla Ravnås 8995f83898 fruity: Handle tunnel lost super-early
E.g. because remotepairingdeviced crashes.
2026-03-22 15:24:16 +01:00
Ole André Vadla Ravnås 680159ef78 fruity: Fix TreeSet comparator dropping transports
Gee.TreeSet treats elements as equal when the comparator returns 0.
Our comparator returned 0 for transports with the same score, causing
later transports to be dropped from the set.

This happens on macOS when a CoreDevice transport appears after usbmux
transports with the same score. Because the comparator reported
equality, the CoreDevice transport was not inserted.

As a result Frida would prefer usbmuxd with lwIP instead of the already
available CoreDevice tunnel.

Fix this by adding a deterministic tie-breaker based on pointer identity
so the comparator only returns 0 for the same instance.

Fixes #1220.
2026-03-16 19:34:40 +01:00
98 changed files with 9342 additions and 4799 deletions

No files matched your search

@@ -0,0 +1,4 @@
set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE dynamic)
set(VCPKG_BUILD_TYPE release)
@@ -0,0 +1,4 @@
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE dynamic)
set(VCPKG_BUILD_TYPE release)
+87 -2
View File
@@ -61,13 +61,17 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install gcc-multilib lib32stdc++-13-dev
- name: Install setuptools for distutils shim
if: ${{ startsWith(matrix.id, 'windows-') }}
run: pip install setuptools
- name: Build
if: ${{ startsWith(matrix.id, 'windows-') }}
run: |
.\configure `
--host=${{ matrix.id }} `
--enable-compiler-backend `
${{ env.FRIDA_CORE_OPTIONS }}
${{ env.FRIDA_CORE_OPTIONS }} `
-- --force-fallback-for=openssl
.\make
env:
MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }}
@@ -493,6 +497,7 @@ jobs:
xcframework:
name: Build FridaCore.xcframework
if: ${{ !cancelled() }}
runs-on: macos-latest
needs:
- native
@@ -585,7 +590,7 @@ jobs:
path: FridaCore.xcframework
publish-xcframework:
if: github.ref_type == 'tag'
if: ${{ !cancelled() && github.ref_type == 'tag' && needs.xcframework.result == 'success' }}
runs-on: macos-latest
needs:
- xcframework
@@ -621,3 +626,83 @@ jobs:
prerelease: ${{ contains(github.ref_name, 'snapshot') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
windows-shared:
strategy:
matrix:
include:
- { arch: x86_64, runner: windows-latest, msvc-arch: amd64, vcpkg-triplet: x64-windows-release }
- { arch: arm64, runner: windows-11-arm, msvc-arch: arm64, vcpkg-triplet: arm64-windows-release }
fail-fast: false
runs-on: ${{ matrix.runner }}
timeout-minutes: 120
steps:
- name: Check out repo
uses: actions/checkout@v6
with:
submodules: recursive
- uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.msvc-arch }}
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install build tools
run: |
pip install meson ninja setuptools
choco install pkgconfiglite -y
- name: Restore vcpkg cache
id: vcpkg-cache
uses: actions/cache/restore@v5
with:
path: C:\vcpkg\installed
key: vcpkg-${{ matrix.vcpkg-triplet }}-v5
- name: Install dependencies via vcpkg
if: steps.vcpkg-cache.outputs.cache-hit != 'true'
run: >-
vcpkg install
--overlay-triplets=${{ github.workspace }}/.github/vcpkg-triplets
--triplet ${{ matrix.vcpkg-triplet }}
glib json-glib libsoup
- name: Save vcpkg cache
if: steps.vcpkg-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: C:\vcpkg\installed
key: vcpkg-${{ matrix.vcpkg-triplet }}-v5
- name: Set up vcpkg environment
shell: bash
run: |
TRIPLET="${{ matrix.vcpkg-triplet }}"
VCPKG_PREFIX="C:/vcpkg/installed/$TRIPLET"
echo "$VCPKG_PREFIX/tools/pkgconf" >> $GITHUB_PATH
echo "$VCPKG_PREFIX/bin" >> $GITHUB_PATH
echo "$VCPKG_PREFIX/tools" >> $GITHUB_PATH
echo "PKG_CONFIG_PATH=$VCPKG_PREFIX/lib/pkgconfig" >> $GITHUB_ENV
- name: Configure
run: |
python -c "import sys; sys.path.insert(0, '.'); from releng.meson_configure import main; main()" `
. `
--host=windows-${{ matrix.arch }} `
--enable-shared `
--without-prebuilds=sdk `
"--prefix=$env:RUNNER_TEMP\frida-prefix" `
-- --force-fallback-for=openssl,libnghttp2 -Dcompiler_backend=disabled -Dtests=disabled
- name: Compile
run: python releng/meson/meson.py compile -C build
- name: Install
run: python releng/meson/meson.py install -C build --no-rebuild --skip-subprojects
- name: List installed files
shell: bash
run: find "$RUNNER_TEMP/frida-prefix" -type f | sort
+153
View File
@@ -0,0 +1,153 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code)
when working with code in this repository.
## What This Is
frida-core is the core library of the Frida dynamic
instrumentation toolkit. It packages GumJS into a shared
library, injects JavaScript instrumentation into target
processes, provides bidirectional RPC communication with
injected scripts, and enumerates apps/processes/devices.
Written primarily in Vala with OS-specific glue in
C/Objective-C/asm.
## Build Commands
```bash
# First-time setup (fetches git submodules, configures build):
./configure \
--enable-compiler-backend \
--with-devkits=core \
--enable-tests
# Build:
make
# Run tests:
make test
# Rebuild after code changes
make
```
The `configure` script and `Makefile` are thin wrappers
around `releng/meson_configure.py` and
`releng/meson_make.py`. The actual build system is Meson
(>=1.1.0).
Build artifacts go into `build/`. The triplet is
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
in version)
- **C/C++ standards**: gnu99/c99 for C, c++17 for C++
- **Go >= 1.24**: Required for compiler backend
- **Python 3**: Build scripts
## Architecture
### Backend System
The core abstraction is a pluggable backend system. Each
backend implements `HostSessionBackend` and provides a
`HostSessionProvider` to create sessions with target
processes:
- **local** — Injects into processes on the local system
(Linux, macOS, Windows, FreeBSD, QNX)
- **fruity** — iOS/tvOS/xrOS devices via USB/network
(QUIC, lockdown protocols)
- **droidy** — Android devices via ADB/JDWP
- **socket** — Remote devices via TCP (connects to
frida-server, frida-gadget, frida-portal)
- **barebone** — Minimal backend using QuickJS
- **simmy** — macOS Simulator
- **compiler** — TypeScript/ESBuild compilation backend
(Go + TypeScript in `src/compiler/`)
Backends are selected at build time via meson options
(e.g. `-Dlocal_backend=enabled`).
### Key Source Layout
- **`src/frida.vala`** — Public API: `DeviceManager`,
`Device`, `Session`, `Script`
- **`src/host-session-service.vala`** — Backend
orchestration, provider registration
- **`src/control-service.vala`** — the guts of frida-server
- **`src/portal-service.vala`** — the guts of frida-portal,
which looks like a frida-server that surfaces any joined
processes (think aggregator/reverse proxy)
- **`lib/base/`** — Core primitives: Session, RPC, streams,
sockets, P2P, buffers
- **`lib/agent/`** — In-process JavaScript agent runtime
- **`lib/payload/`** — Payload compilation and embedding
- **`lib/gadget/`** — Gadget (preloaded library)
implementation
- **`src/<platform>/`** — Platform-specific injectors and
helpers (`linux/`, `darwin/`, `windows/`, `freebsd/`,
`qnx/`)
- **`src/fruity/`** — iOS device protocol stack (USB, QUIC,
plist, XPC, LLDB)
- **`src/droidy/`** — Android device protocol stack
- **`src/compiler/`** — Go/TypeScript compiler backend
### Injection Flow
Each platform has its own injector (e.g. `Linjector` on
Linux, `Fruitjector` on Darwin, `Winjector` on Windows). A
helper process (`frida-helper`) performs privileged injection
operations out-of-process. Helpers and agents can be embedded
into the library or installed as separate files (controlled
by the `assets` meson option).
### Deliverables
- **frida-server** (`server/`) — Standalone daemon exposing
the full API over the network
- **frida-portal** (`portal/`) — Cluster node for
distributed instrumentation
- **frida-inject** (`inject/`) — CLI tool for one-shot
script injection
- **frida-gadget** (`lib/gadget/`) — Shared library for
preload-based instrumentation
### Resource Embedding
Build artifacts (frida-helper, frida-agent) are compiled
into resource blobs via `tools/resource-compiler.vala` and
embedding scripts (`src/embed-helper.py`,
`src/embed-agent.py`). On non-MSVC toolchains these become
`.S` assembly files; on MSVC they become `.obj` files.
## Tests
Tests are in `tests/` as Vala files using a custom async
test harness (`tests/async-harness.vala`). Test binary is
`tests/frida-tests`. Platform-specific runner scripts exist
(e.g. `tests/run-linux-x86_64.sh`).
Test modules: `test-system`, `test-injector`, `test-agent`,
`test-host-session`, `test-gadget`, `test-compiler`. Test
helper programs ("labrats") are in `tests/labrats/`.
## Code Style
### Function Order
Use newspaper/stepdown order: high-level entry points
first, low-level helpers and constants last. In C,
`main()` is always first. When ordering siblings at the
same level, use the chronological order of first use
within their caller.
## Commit Style
- Subject line: max 50 characters
- Body lines: wrap at 72 characters (use the full width, or
slightly less if it avoids making the next line awkward)
+49 -11
View File
@@ -1,15 +1,18 @@
# frida-core
Frida core library intended for static linking into bindings.
Frida core library.
- Lets you inject your own JavaScript instrumentation code into other processes,
optionally with your own [C code][] for performance-sensitive bits.
- Acts as a logistics layer that packages up [GumJS][] into a shared library.
- Provides a two-way communication channel for talking to your scripts,
if needed, and later unload them.
- Also lets you enumerate installed apps, running processes, and connected
devices.
- Written in [Vala][], with OS-specific glue code in C/Objective-C/asm.
- Lets you inject your own JavaScript instrumentation code
into other processes, optionally with your own [C code][]
for performance-sensitive bits.
- Acts as a logistics layer that packages up [GumJS][] into
a shared library.
- Provides a two-way communication channel for talking to
your scripts, if needed, and later unload them.
- Also lets you enumerate installed apps, running processes,
and connected devices.
- Written in [Vala][], with OS-specific glue code in
C/Objective-C/asm.
## Binaries
@@ -29,8 +32,43 @@ $ pip install frida # Python bindings
$ npm install frida # Node.js bindings
```
Or, for static linking into your own project written in a C-compatible language,
download a devkit from the Frida [releases][] page.
Or, for static linking into your own project written in a
C-compatible language, download a devkit from the Frida
[releases][] page.
## Distro Packaging
For building a shared library suitable for distro packaging:
```console
$ ./configure --enable-shared --without-prebuilds=sdk && make
```
This builds `libfrida-core-1.0.so` against system dependencies,
while `frida-agent.so` is built using Frida's SDK (with its GLib
fork, needed for the agent's inject/unload lifecycle).
Prerequisites on Fedora:
```console
$ sudo dnf install libgee-devel json-glib-devel \
libsoup3-devel libunwind-devel libdwarf-devel \
libnice-devel ngtcp2-crypto-ossl-devel
```
Prerequisites on Ubuntu:
```console
$ sudo apt install libgee-0.8-dev libjson-glib-dev \
libsoup-3.0-dev libunwind-dev libdwarf-dev \
libnice-dev libngtcp2-crypto-ossl-dev
```
Prerequisites on macOS:
```console
$ brew install libgee json-glib libsoup libnice capstone
```
## Internals
+195 -56
View File
@@ -9,6 +9,7 @@ import os
from pathlib import Path
import pickle
import platform
import re
import shlex
import shutil
import subprocess
@@ -35,6 +36,7 @@ def main(argv):
command.add_argument("top_builddir", help="top build directory", type=Path)
command.add_argument("frida_version", help="the Frida version")
command.add_argument("host_os", help="operating system binaries are being built for")
command.add_argument("host_os_family", help="operating system family, e.g. darwin")
command.add_argument("host_arch", help="architecture binaries are being built for")
command.add_argument("host_config", help="configuration binaries are being built for")
command.add_argument("compat", help="support for targets with a different architecture",
@@ -42,17 +44,20 @@ def main(argv):
command.add_argument("assets", help="whether assets are embedded vs installed and loaded at runtime")
command.add_argument("components", help="which components will be built",
type=parse_array_option_value)
command.add_argument("glib_flavor", help="upstream or frida", choices=["upstream", "frida"])
command.add_argument("compilers", help="compiler command arrays", nargs="+")
command.set_defaults(func=lambda args: setup(args.role,
args.builddir,
args.top_builddir,
args.frida_version,
args.host_os,
args.host_os_family,
args.host_arch,
args.host_config if args.host_config else None,
args.compat,
args.assets,
args.components,
args.glib_flavor,
parse_compilers(args.compilers)))
command = subparsers.add_parser("compile", help="compile compatibility assets")
@@ -113,11 +118,13 @@ def setup(role: Role,
top_builddir: Path,
frida_version: str,
host_os: str,
host_os_family: str,
host_arch: str,
host_config: Optional[str],
compat: set[str],
assets: str,
components: set[str],
glib_flavor: str,
compilers: Compilers):
try:
outputs: Mapping[str, Sequence[Output]] = OrderedDict()
@@ -192,16 +199,21 @@ def setup(role: Role,
if host_os == "windows" and host_arch in {"x86_64", "x86"} and have_toolchain:
group = OutputGroup(other_label, other_triplet, extra_environ)
outputs[group] = [
Output(identifier=f"helper_{other_kind}",
name=HELPER_FILE_WINDOWS.name,
file=HELPER_FILE_WINDOWS,
target=HELPER_TARGET),
Output(identifier=f"agent_{other_kind}",
name=AGENT_FILE_WINDOWS.name,
file=AGENT_FILE_WINDOWS,
target=AGENT_TARGET),
]
outputs[group] = []
if "helper" in components:
outputs[group] += [
Output(identifier=f"helper_{other_kind}",
name=HELPER_FILE_WINDOWS.name,
file=HELPER_FILE_WINDOWS,
target=HELPER_TARGET),
]
if "agent" in components:
outputs[group] += [
Output(identifier=f"agent_{other_kind}",
name=AGENT_FILE_WINDOWS.name,
file=AGENT_FILE_WINDOWS,
target=AGENT_TARGET),
]
if "gadget" in components:
outputs[group] += [
Output(identifier=f"gadget_{other_kind}",
@@ -218,16 +230,21 @@ def setup(role: Role,
other_arch = "arm64e"
kind = "modern"
group = OutputGroup(other_arch)
outputs[group] = [
Output(identifier=f"helper_{kind}",
name=f"frida-helper-{other_arch}",
file=HELPER_FILE_UNIX,
target=HELPER_TARGET),
Output(identifier=f"agent_{kind}",
name=f"frida-agent-{other_arch}.dylib",
file=AGENT_FILE_DARWIN,
target=AGENT_TARGET),
]
outputs[group] = []
if "helper" in components:
outputs[group] += [
Output(identifier=f"helper_{kind}",
name=f"frida-helper-{other_arch}",
file=HELPER_FILE_UNIX,
target=HELPER_TARGET),
]
if "agent" in components:
outputs[group] += [
Output(identifier=f"agent_{kind}",
name=f"frida-agent-{other_arch}.dylib",
file=AGENT_FILE_DARWIN,
target=AGENT_TARGET),
]
if "gadget" in components:
outputs[group] += [
Output(identifier=f"gadget_{kind}",
@@ -245,16 +262,21 @@ def setup(role: Role,
if host_os == "linux" and host_arch == "x86_64" and have_toolchain:
group = OutputGroup("x86", other_triplet, extra_environ)
outputs[group] = [
Output(identifier="helper_legacy",
name=HELPER_FILE_UNIX.name,
file=HELPER_FILE_UNIX,
target=HELPER_TARGET),
Output(identifier="agent_legacy",
name=AGENT_FILE_ELF.name,
file=AGENT_FILE_ELF,
target=AGENT_TARGET),
]
outputs[group] = []
if "helper" in components:
outputs[group] += [
Output(identifier="helper_legacy",
name=HELPER_FILE_UNIX.name,
file=HELPER_FILE_UNIX,
target=HELPER_TARGET),
]
if "agent" in components:
outputs[group] += [
Output(identifier="agent_legacy",
name=AGENT_FILE_ELF.name,
file=AGENT_FILE_ELF,
target=AGENT_TARGET),
]
if "gadget" in components:
outputs[group] += [
Output(identifier="gadget_legacy",
@@ -265,16 +287,21 @@ def setup(role: Role,
if host_os == "android" and host_arch in {"arm64", "x86_64"}:
group = OutputGroup("arm" if host_arch == "arm64" else "x86")
outputs[group] = [
Output(identifier="helper_legacy",
name=HELPER_FILE_UNIX.name,
file=HELPER_FILE_UNIX,
target=HELPER_TARGET),
Output(identifier="agent_legacy",
name=AGENT_FILE_ELF.name,
file=AGENT_FILE_ELF,
target=AGENT_TARGET),
]
outputs[group] = []
if "helper" in components:
outputs[group] += [
Output(identifier="helper_legacy",
name=HELPER_FILE_UNIX.name,
file=HELPER_FILE_UNIX,
target=HELPER_TARGET),
]
if "agent" in components:
outputs[group] += [
Output(identifier="agent_legacy",
name=AGENT_FILE_ELF.name,
file=AGENT_FILE_ELF,
target=AGENT_TARGET),
]
if "gadget" in components:
outputs[group] += [
Output(identifier="gadget_legacy",
@@ -295,18 +322,24 @@ def setup(role: Role,
continue
else:
emulated_triplet = None
outputs[OutputGroup(emulated_arch, emulated_triplet, extra_environ)] = [
Output(identifier=f"helper_emulated_{kind}",
name=HELPER_FILE_WINDOWS.name.replace(".exe", f"-{emulated_arch}.exe"),
file=HELPER_FILE_WINDOWS,
target=HELPER_TARGET),
Output(identifier=f"agent_emulated_{kind}",
name=AGENT_FILE_WINDOWS.name.replace(".dll", f"-{emulated_arch}.dll"),
file=AGENT_FILE_WINDOWS,
target=AGENT_TARGET),
]
group = OutputGroup(emulated_arch, emulated_triplet, extra_environ)
outputs[group] = []
if "helper" in components:
outputs[group] += [
Output(identifier=f"helper_emulated_{kind}",
name=HELPER_FILE_WINDOWS.name.replace(".exe", f"-{emulated_arch}.exe"),
file=HELPER_FILE_WINDOWS,
target=HELPER_TARGET),
]
if "agent" in components:
outputs[group] += [
Output(identifier=f"agent_emulated_{kind}",
name=AGENT_FILE_WINDOWS.name.replace(".dll", f"-{emulated_arch}.dll"),
file=AGENT_FILE_WINDOWS,
target=AGENT_TARGET),
]
if host_os == "android" and host_arch in {"x86_64", "x86"}:
if host_os == "android" and host_arch in {"x86_64", "x86"} and "agent" in components:
outputs[OutputGroup("arm")] = [
Output(identifier="agent_emulated_legacy",
name="frida-agent-arm.so",
@@ -321,10 +354,35 @@ def setup(role: Role,
target=AGENT_TARGET),
]
raw_allowed_prebuilds = os.environ.get("FRIDA_ALLOWED_PREBUILDS")
allowed_prebuilds = set(raw_allowed_prebuilds.split(",")) if raw_allowed_prebuilds is not None else None
if glib_flavor == "upstream":
kind = "modern" if arch_is_modern(host_os, host_arch) else "legacy"
_, agent_file, gadget_file = native_file_paths(host_os)
group = OutputGroup(host_arch)
outputs[group] = []
if "agent" in components:
outputs[group] += [
Output(identifier=f"agent_{kind}",
name=arch_suffixed_name(agent_file.name, host_arch),
file=agent_file,
target=AGENT_TARGET),
]
if "gadget" in components:
outputs[group] += [
Output(identifier=f"gadget_{kind}",
name=arch_suffixed_name(gadget_file.name, host_arch),
file=gadget_file,
target=GADGET_TARGET),
]
state = State(role, builddir, top_builddir, frida_version, host_os, host_config, allowed_prebuilds, outputs)
outputs = OrderedDict((g, items) for g, items in outputs.items() if items)
raw_allowed_prebuilds = os.environ.get("FRIDA_ALLOWED_PREBUILDS")
if raw_allowed_prebuilds is not None:
allowed_prebuilds = set(filter(None, raw_allowed_prebuilds.split(",")))
else:
allowed_prebuilds = None
state = State(role, builddir, top_builddir, frida_version, host_os, host_os_family, host_config, glib_flavor, allowed_prebuilds, outputs)
serialized_state = base64.b64encode(pickle.dumps(state)).decode('ascii')
if missing:
@@ -360,7 +418,9 @@ class State:
top_builddir: Path
frida_version: str
host_os: str
host_os_family: str
host_config: Optional[str]
glib_flavor: str
allowed_prebuilds: Optional[set[str]]
outputs: Mapping[OutputGroup, Sequence[Output]]
@@ -440,20 +500,33 @@ def compile(privdir: Path, state: State):
host_machine = MachineSpec(state.host_os, group.arch, state.host_config, group.triplet)
if state.glib_flavor == "upstream":
allowed = None
else:
allowed = state.allowed_prebuilds
if state.glib_flavor == "upstream":
forwarded_options = [o for o in options if not is_stripped_compat_option(o)]
forwarded_options += ["-Ddefault_library=static"]
else:
forwarded_options = [o for o in options if not is_overridden_compat_option(o)]
configure(sourcedir=REPO_ROOT,
builddir=workdir,
host_machine=host_machine,
environ={**build_env, **group.extra_environ},
allowed_prebuilds=state.allowed_prebuilds,
allowed_prebuilds=allowed,
apple_min_os=detect_parent_apple_min_os(top_builddir, state.host_os, state.host_os_family),
extra_meson_options=[
"-Dcompiler_backend=disabled",
"-Dassets=installed",
"-Dhelper_modern=",
"-Dhelper_legacy=",
"-Dagent_modern=",
"-Dagent_legacy=",
"-Dagent_emulated_modern=",
"-Dagent_emulated_legacy=",
*options,
*forwarded_options,
],
call_meson=call_internal_meson,
on_progress=lambda progress: None)
@@ -484,6 +557,18 @@ def compile(privdir: Path, state: State):
(state.builddir / DEPFILE_FILENAME).write_text("\n".join(depfile_lines), encoding="utf-8")
def is_stripped_compat_option(o: str) -> bool:
if is_overridden_compat_option(o):
return True
name = o.split("=", 1)[0].removeprefix("-D")
return name in STRIPPED_COMPAT_OPTIONS or name.endswith(":default_library")
def is_overridden_compat_option(o: str) -> bool:
name = o.split("=", 1)[0].removeprefix("-D")
return name in OVERRIDDEN_COMPAT_OPTIONS
def load_meson_options(top_builddir: Path,
role: Role,
subprojects: set[str]) -> Sequence[str]:
@@ -593,6 +678,19 @@ def ensure_submodules_checked_out(releng_location: Path):
check=True)
def detect_parent_apple_min_os(top_builddir: Path,
host_os: str,
host_os_family: str) -> Optional[Mapping[str, str]]:
if host_os_family != "darwin":
return None
pattern = re.compile(r"-target['\"],\s*['\"][^-]+-apple-" + re.escape(host_os) + r"([0-9.]+)")
for native_file in top_builddir.glob(f"frida-{host_os}-*.txt"):
match = pattern.search(native_file.read_text(encoding="utf-8"))
if match is not None:
return {host_os: match.group(1)}
return None
def detect_relevant_subprojects(releng_location: Path) -> dict[str, Path]:
subprojects = detect_relevant_subprojects_in(REPO_ROOT, releng_location)
gum_location = subprojects.get("frida-gum")
@@ -643,9 +741,48 @@ def detect_mingw_toolchain_for(arch: str) -> Tuple[bool, Optional[str]]:
return (found, triplet)
def arch_is_modern(host_os: str, host_arch: str) -> bool:
if host_os in {"macos", "ios", "tvos"} and host_arch == "arm64":
return host_arch == "arm64e"
return host_arch in MODERN_ARCHS
def native_file_paths(host_os: str) -> Tuple[Path, Path, Path]:
if host_os == "windows":
return (HELPER_FILE_WINDOWS, AGENT_FILE_WINDOWS, GADGET_FILE_WINDOWS)
elif host_os in {"macos", "ios", "tvos"}:
return (HELPER_FILE_UNIX, AGENT_FILE_DARWIN, GADGET_FILE_DARWIN)
else:
return (HELPER_FILE_UNIX, AGENT_FILE_ELF, GADGET_FILE_ELF)
def arch_suffixed_name(name: str, arch: str) -> str:
stem, _, ext = name.partition(".")
if ext:
return f"{stem}-{arch}.{ext}"
return f"{stem}-{arch}"
STATE_FILENAME = "state.dat"
DEPFILE_FILENAME = "compat.deps"
STRIPPED_COMPAT_OPTIONS = {
"default_library",
"frida-gum:gumjs",
"frida-gum:inspector",
}
OVERRIDDEN_COMPAT_OPTIONS = {
"compiler_backend",
"assets",
"helper_modern",
"helper_legacy",
"agent_modern",
"agent_legacy",
"agent_emulated_modern",
"agent_emulated_legacy",
}
HELPER_TARGET = "frida-helper"
HELPER_FILE_WINDOWS = Path("src") / "frida-helper.exe"
HELPER_FILE_UNIX = Path("src") / "frida-helper"
@@ -670,6 +807,8 @@ MSVS_ENVVARS = {
"LIB",
}
MODERN_ARCHS = {"x86_64", "arm64", "arm64e", "mips64", "riscv64"}
MINGW_ARCHS = {
"x86": "i686",
"arm64": "aarch64",
+15
View File
@@ -0,0 +1,15 @@
import os
import shutil
import sys
src = sys.argv[1]
install_subdir = sys.argv[2]
install_name = sys.argv[3]
destdir_prefix = os.environ["MESON_INSTALL_DESTDIR_PREFIX"]
dst_dir = os.path.join(destdir_prefix, install_subdir)
dst = os.path.join(dst_dir, install_name)
os.makedirs(dst_dir, exist_ok=True)
shutil.copy2(src, dst)
print(f"Installing {os.path.basename(src)} to {dst_dir}/{install_name}")
+40 -2
View File
@@ -28,6 +28,9 @@ if no_overridden_compat_bits
build_py = [python, files('build.py')]
components = []
if have_local_backend
components += ['helper', 'agent']
endif
if build_gadget
components += 'gadget'
endif
@@ -42,11 +45,13 @@ if no_overridden_compat_bits
meson.global_build_root(),
frida_version,
host_os,
host_os_family,
host_abi,
host_config,
','.join(get_option('compat')),
get_option('assets'),
assets_mode,
','.join(components),
have_shared_glib ? 'upstream' : 'frida',
'--',
'>>>', cc.cmd_array(), '<<<',
'>>>', cpp.cmd_array(), '<<<',
@@ -64,18 +69,27 @@ if no_overridden_compat_bits
depfile = lines[4]
state = lines[5]
native_arch_suffix = '-' + host_abi
if host_os_family == 'darwin'
install = false
install_dir = asset_dir
else
install = true
install_dir = []
i = 0
foreach varname : varnames
if varname != 'arch_support_bundle' and (get_option('assets') == 'installed' or varname.startswith('gadget_'))
output_name = outputs[i]
needs_rename = output_name.contains(native_arch_suffix) or varname.contains('_emulated_')
should_install = varname != 'arch_support_bundle' \
and not needs_rename \
and (assets_mode == 'installed' or varname.startswith('gadget_'))
if should_install
install_dir += varname.endswith('_modern') ? asset_dir_modern : asset_dir_legacy
else
install_dir += false
endif
i += 1
endforeach
endif
@@ -96,6 +110,7 @@ if no_overridden_compat_bits
foreach output : arch_support.to_list()
outpath = output.full_path()
varname = varnames[i]
output_name = outputs[i]
set_variable(varname, outpath)
@@ -115,6 +130,29 @@ if no_overridden_compat_bits
assert(false, f'unexpected variable name: @varname@')
endif
if host_os_family != 'darwin'
rename_target_dir = ''
if output_name.contains(native_arch_suffix)
rename_target_dir = asset_dir
elif varname.contains('_emulated_')
emulated_arch = output_name.split('-')[-1].split('.')[0]
rename_target_dir = root_asset_dir / emulated_arch
endif
if rename_target_dir != ''
install_name = output_name.replace(native_arch_suffix, '')
foreach arch_suffix : ['-x86_64', '-x86', '-arm64', '-arm']
install_name = install_name.replace(arch_suffix, '')
endforeach
if assets_mode == 'installed' or varname.startswith('gadget_')
meson.add_install_script(python, files('install-renamed.py'),
outpath,
rename_target_dir,
install_name,
)
endif
endif
endif
i += 1
endforeach
else
+6 -2
View File
@@ -9,7 +9,9 @@
# include <signal.h>
# endif
#endif
#ifdef HAVE_GIOOPENSSL
#if defined (HAVE_GIOAPPLE)
# include <gioapple.h>
#elif defined (HAVE_GIOOPENSSL)
# include <gioopenssl.h>
#endif
@@ -31,7 +33,9 @@ _frida_agent_environment_init (void)
g_thread_set_garbage_handler (_frida_agent_on_pending_thread_garbage, NULL);
#ifdef HAVE_GIOOPENSSL
#if defined (HAVE_GIOAPPLE)
g_io_module_apple_register ();
#elif defined (HAVE_GIOOPENSSL)
g_io_module_openssl_register ();
#endif
+2 -2
View File
@@ -67,7 +67,7 @@ modulated_agent = custom_target('frida-agent-modulated',
)
need_universal_agent = host_os_family == 'darwin' \
and get_option('assets') == 'installed' \
and assets_mode == 'installed' \
and agent_compat != ''
if host_os_family == 'darwin'
@@ -80,7 +80,7 @@ agent = custom_target('frida-agent',
output: need_universal_agent ? f'@agent_name@-@host_abi@' : agent_name,
command: post_process + ['shared-library', identity],
build_by_default: true,
install: get_option('assets') == 'installed' and not need_universal_agent,
install: assets_mode == 'installed' and not need_universal_agent,
install_dir: asset_dir,
)
+47
View File
@@ -0,0 +1,47 @@
namespace Frida {
public class AssetLocation : Object {
public string libdir {
get;
construct;
}
public static AssetLocation detect () {
string? module_path = null;
Gum.Address our_address = Gum.Address.from_pointer (Gum.strip_code_pointer ((void *) detect));
Gum.Process.enumerate_modules ((details) => {
var range = details.range;
if (our_address >= range.base_address && our_address < range.base_address + range.size) {
module_path = details.path;
return false;
}
return true;
});
assert (module_path != null);
string libdir = Path.get_dirname (module_path);
#if WINDOWS
if (Path.get_basename (libdir).down () == "bin") {
string parent = Path.get_dirname (libdir);
string candidate = Path.build_filename (parent, "lib");
if (FileUtils.test (candidate, IS_DIR))
libdir = candidate;
}
#endif
return new AssetLocation (libdir);
}
private AssetLocation (string libdir) {
Object (libdir: libdir);
}
public string derive_asset_path (string arch, string filename) {
return Path.build_filename (libdir, Config.FRIDA_LIBDIR_NAME, arch, filename);
}
public string derive_plugin_path (string filename) {
return Path.build_filename (libdir, Config.FRIDA_LIBDIR_NAME, "plugins", filename);
}
}
}
+4
View File
@@ -128,6 +128,7 @@ namespace Frida {
}
}
#if HAVE_LOCAL_BACKEND
public sealed class BpfObject {
public Programs programs {
get;
@@ -522,6 +523,7 @@ namespace Frida {
STOP,
}
}
#endif
namespace PerfEvent {
public FileDescriptor open (PerfEventAttr * attr, int pid, int cpu, int group_fd, uint flags) throws Error {
@@ -536,7 +538,9 @@ namespace Frida {
throw new Error.NOT_SUPPORTED ("%s (errno=%d: %s)".printf (message, Posix.errno, Posix.strerror (Posix.errno)));
}
#if HAVE_LOCAL_BACKEND
private size_t round_up_8 (size_t x) {
return (x + 7) & ~((size_t) 7);
}
#endif
}
+15 -10
View File
@@ -1,4 +1,5 @@
base_sources = [
'asset-location.vala',
'session.vala',
'session-glue.c',
'channel.vala',
@@ -47,8 +48,10 @@ if host_os_family == 'linux'
extra_vala_args += [
'--pkg=linux',
'--pkg=frida-atomics',
'--pkg=libbpf',
]
if have_local_backend
extra_vala_args += '--pkg=libbpf'
endif
syscall_arch_from_abi = {
'armbe': 'arm',
@@ -75,7 +78,7 @@ extra_deps = []
if host_os_family != 'windows'
extra_deps += gio_unix_dep
endif
if host_os_family == 'linux'
if host_os_family == 'linux' and have_local_backend
extra_deps += libbpf_dep
endif
@@ -112,7 +115,7 @@ base = static_library('frida-base-' + api_version, base_sources,
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,
install: true,
install: have_in_tree_payloads,
install_dir: [true, header_install_dir, true, false],
)
@@ -124,10 +127,12 @@ base_dep = declare_dependency(
base_build_dir = meson.current_build_dir()
pkg = import('pkgconfig')
pkg.generate(base,
filebase: f'frida-base-@api_version@',
name: 'Frida Base Library',
version: frida_version,
description: 'Basic building blocks',
)
if have_in_tree_payloads
pkg = import('pkgconfig')
pkg.generate(base,
filebase: f'frida-base-@api_version@',
name: 'Frida Base Library',
version: frida_version,
description: 'Basic building blocks',
)
endif
+417 -9
View File
@@ -1,26 +1,432 @@
#ifdef HAVE_NICE
#define OPENSSL_SUPPRESS_DEPRECATED
#include "frida-base.h"
#include <errno.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <usrsctp.h>
#ifdef HAVE_GIOAPPLE
# include <CoreFoundation/CoreFoundation.h>
# include <Security/Security.h>
#else
# define OPENSSL_SUPPRESS_DEPRECATED
# include <openssl/asn1.h>
# include <openssl/bio.h>
# include <openssl/bn.h>
# include <openssl/evp.h>
# include <openssl/pem.h>
# include <openssl/rsa.h>
# include <openssl/x509.h>
#endif
#ifdef HAVE_GIOAPPLE
static SecKeyRef frida_rsa_keypair_generate (void);
static void frida_rsa_export_public_key (SecKeyRef private_key, GByteArray * out);
static void frida_rsa_export_private_key (SecKeyRef private_key, GByteArray * out);
static void frida_rsa_sign_sha256 (SecKeyRef private_key, const guint8 * message, gsize message_length, GByteArray * out);
static void frida_build_tbs_certificate (GByteArray * out, const GByteArray * subject_public_key);
static void frida_build_name (GByteArray * out);
static void frida_build_rdn (GByteArray * out, const guint8 * oid, gsize oid_length, guint8 value_tag, const gchar * value);
static void frida_build_validity (GByteArray * out);
static void frida_build_subject_public_key_info (GByteArray * out, const GByteArray * rsa_public_key);
static void frida_build_signed_certificate (GByteArray * out, const GByteArray * tbs, const GByteArray * signature);
static void frida_build_algorithm_identifier (GByteArray * out, const guint8 * oid, gsize oid_length);
static void frida_build_pkcs8_private_key_info (GByteArray * out, const GByteArray * rsa_private_key);
static gchar * frida_pem_encode (const gchar * type, const guint8 * der, gsize der_length);
static void frida_der_append_tlv (GByteArray * out, guint8 tag, const guint8 * value, gsize length);
static void frida_der_append_length (GByteArray * out, gsize length);
static void frida_der_append_integer_uint (GByteArray * out, guint64 value);
static void frida_der_append_null (GByteArray * out);
static void frida_der_append_oid (GByteArray * out, const guint8 * oid, gsize length);
static void frida_der_append_utctime (GByteArray * out, time_t t);
static void frida_der_append_bit_string (GByteArray * out, const guint8 * bytes, gsize length);
static const guint8 frida_oid_country[] = { 0x55, 0x04, 0x06 };
static const guint8 frida_oid_organization[] = { 0x55, 0x04, 0x0a };
static const guint8 frida_oid_common_name[] = { 0x55, 0x04, 0x03 };
static const guint8 frida_oid_rsa_encryption[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 };
static const guint8 frida_oid_sha256_with_rsa[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b };
#else
static gchar * frida_steal_bio_to_string (BIO ** bio);
#endif
static int frida_on_connection_output (void * addr, void * buffer, size_t length, uint8_t tos, uint8_t set_df);
static void frida_on_debug_printf (const char * format, ...);
static void frida_on_upcall (struct socket * sock, void * user_data, int flags);
#ifdef HAVE_GIOAPPLE
void
_frida_generate_certificate (guint8 ** cert_der, gint * cert_der_length, gchar ** cert_pem, gchar ** key_pem)
{
SecKeyRef private_key;
GByteArray * rsa_public_key, * rsa_private_key, * subject_public_key, * tbs, * signature, * certificate, * pkcs8;
*cert_der = NULL;
*cert_der_length = 0;
*cert_pem = NULL;
*key_pem = NULL;
private_key = frida_rsa_keypair_generate ();
rsa_public_key = g_byte_array_new ();
frida_rsa_export_public_key (private_key, rsa_public_key);
rsa_private_key = g_byte_array_new ();
frida_rsa_export_private_key (private_key, rsa_private_key);
subject_public_key = g_byte_array_new ();
frida_build_subject_public_key_info (subject_public_key, rsa_public_key);
tbs = g_byte_array_new ();
frida_build_tbs_certificate (tbs, subject_public_key);
signature = g_byte_array_new ();
frida_rsa_sign_sha256 (private_key, tbs->data, tbs->len, signature);
certificate = g_byte_array_new ();
frida_build_signed_certificate (certificate, tbs, signature);
pkcs8 = g_byte_array_new ();
frida_build_pkcs8_private_key_info (pkcs8, rsa_private_key);
*cert_der = g_memdup2 (certificate->data, certificate->len);
*cert_der_length = certificate->len;
*cert_pem = frida_pem_encode ("CERTIFICATE", certificate->data, certificate->len);
*key_pem = frida_pem_encode ("PRIVATE KEY", pkcs8->data, pkcs8->len);
g_byte_array_unref (pkcs8);
g_byte_array_unref (certificate);
g_byte_array_unref (signature);
g_byte_array_unref (tbs);
g_byte_array_unref (subject_public_key);
g_byte_array_unref (rsa_private_key);
g_byte_array_unref (rsa_public_key);
CFRelease (private_key);
}
static SecKeyRef
frida_rsa_keypair_generate (void)
{
SecKeyRef private_key;
CFMutableDictionaryRef attrs;
const int key_size_bits = 2048;
CFNumberRef key_size;
attrs = CFDictionaryCreateMutable (kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
key_size = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &key_size_bits);
CFDictionarySetValue (attrs, kSecAttrKeyType, kSecAttrKeyTypeRSA);
CFDictionarySetValue (attrs, kSecAttrKeySizeInBits, key_size);
CFRelease (key_size);
private_key = SecKeyCreateRandomKey (attrs, NULL);
g_assert (private_key != NULL);
CFRelease (attrs);
return private_key;
}
static void
frida_rsa_export_public_key (SecKeyRef private_key, GByteArray * out)
{
SecKeyRef public_key;
CFDataRef data;
public_key = SecKeyCopyPublicKey (private_key);
g_assert (public_key != NULL);
data = SecKeyCopyExternalRepresentation (public_key, NULL);
g_assert (data != NULL);
g_byte_array_append (out, CFDataGetBytePtr (data), CFDataGetLength (data));
CFRelease (data);
CFRelease (public_key);
}
static void
frida_rsa_export_private_key (SecKeyRef private_key, GByteArray * out)
{
CFDataRef data;
data = SecKeyCopyExternalRepresentation (private_key, NULL);
g_assert (data != NULL);
g_byte_array_append (out, CFDataGetBytePtr (data), (guint) CFDataGetLength (data));
CFRelease (data);
}
static void
frida_rsa_sign_sha256 (SecKeyRef private_key, const guint8 * message, gsize message_length, GByteArray * out)
{
CFDataRef to_sign, signature;
to_sign = CFDataCreate (kCFAllocatorDefault, message, (CFIndex) message_length);
signature = SecKeyCreateSignature (private_key, kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256, to_sign, NULL);
g_assert (signature != NULL);
g_byte_array_append (out, CFDataGetBytePtr (signature), (guint) CFDataGetLength (signature));
CFRelease (signature);
CFRelease (to_sign);
}
static void
frida_build_tbs_certificate (GByteArray * out, const GByteArray * subject_public_key)
{
GByteArray * inner = g_byte_array_new ();
frida_der_append_integer_uint (inner, 1);
frida_build_algorithm_identifier (inner, frida_oid_sha256_with_rsa, sizeof (frida_oid_sha256_with_rsa));
frida_build_name (inner);
frida_build_validity (inner);
frida_build_name (inner);
g_byte_array_append (inner, subject_public_key->data, subject_public_key->len);
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
g_byte_array_unref (inner);
}
static void
frida_build_name (GByteArray * out)
{
GByteArray * inner = g_byte_array_new ();
frida_build_rdn (inner, frida_oid_country, sizeof (frida_oid_country), 0x13, "CA");
frida_build_rdn (inner, frida_oid_organization, sizeof (frida_oid_organization), 0x0c, "Frida");
frida_build_rdn (inner, frida_oid_common_name, sizeof (frida_oid_common_name), 0x0c, "lolcathost");
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
g_byte_array_unref (inner);
}
static void
frida_build_rdn (GByteArray * out, const guint8 * oid, gsize oid_length, guint8 value_tag, const gchar * value)
{
GByteArray * atv, * rdn;
atv = g_byte_array_new ();
rdn = g_byte_array_new ();
frida_der_append_oid (atv, oid, oid_length);
frida_der_append_tlv (atv, value_tag, (const guint8 *) value, strlen (value));
frida_der_append_tlv (rdn, 0x30, atv->data, atv->len);
frida_der_append_tlv (out, 0x31, rdn->data, rdn->len);
g_byte_array_unref (rdn);
g_byte_array_unref (atv);
}
static void
frida_build_validity (GByteArray * out)
{
const time_t lifetime_seconds = 15780000;
GByteArray * inner;
time_t not_before, not_after;
inner = g_byte_array_new ();
not_before = time (NULL);
not_after = not_before + lifetime_seconds;
frida_der_append_utctime (inner, not_before);
frida_der_append_utctime (inner, not_after);
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
g_byte_array_unref (inner);
}
static void
frida_build_subject_public_key_info (GByteArray * out, const GByteArray * rsa_public_key)
{
GByteArray * inner = g_byte_array_new ();
frida_build_algorithm_identifier (inner, frida_oid_rsa_encryption, sizeof (frida_oid_rsa_encryption));
frida_der_append_bit_string (inner, rsa_public_key->data, rsa_public_key->len);
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
g_byte_array_unref (inner);
}
static void
frida_build_signed_certificate (GByteArray * out, const GByteArray * tbs, const GByteArray * signature)
{
GByteArray * inner = g_byte_array_new ();
g_byte_array_append (inner, tbs->data, tbs->len);
frida_build_algorithm_identifier (inner, frida_oid_sha256_with_rsa, sizeof (frida_oid_sha256_with_rsa));
frida_der_append_bit_string (inner, signature->data, signature->len);
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
g_byte_array_unref (inner);
}
static void
frida_build_algorithm_identifier (GByteArray * out, const guint8 * oid, gsize oid_length)
{
GByteArray * inner = g_byte_array_new ();
frida_der_append_oid (inner, oid, oid_length);
frida_der_append_null (inner);
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
g_byte_array_unref (inner);
}
static void
frida_build_pkcs8_private_key_info (GByteArray * out, const GByteArray * rsa_private_key)
{
GByteArray * inner = g_byte_array_new ();
frida_der_append_integer_uint (inner, 0);
frida_build_algorithm_identifier (inner, frida_oid_rsa_encryption, sizeof (frida_oid_rsa_encryption));
frida_der_append_tlv (inner, 0x04, rsa_private_key->data, rsa_private_key->len);
frida_der_append_tlv (out, 0x30, inner->data, inner->len);
g_byte_array_unref (inner);
}
static gchar *
frida_pem_encode (const gchar * type, const guint8 * der, gsize der_length)
{
GString * pem;
gchar * base64;
gsize length, i;
base64 = g_base64_encode (der, der_length);
length = strlen (base64);
pem = g_string_sized_new (length + 128);
g_string_append_printf (pem, "-----BEGIN %s-----\n", type);
for (i = 0; i < length; i += 64)
{
gsize chunk = MIN (64, length - i);
g_string_append_len (pem, base64 + i, chunk);
g_string_append_c (pem, '\n');
}
g_string_append_printf (pem, "-----END %s-----\n", type);
g_free (base64);
return g_string_free (pem, FALSE);
}
static void
frida_der_append_tlv (GByteArray * out, guint8 tag, const guint8 * value, gsize length)
{
g_byte_array_append (out, &tag, 1);
frida_der_append_length (out, length);
if (length > 0)
g_byte_array_append (out, value, length);
}
static void
frida_der_append_length (GByteArray * out, gsize length)
{
gint i;
guint8 buf[9], header;
if (length < 0x80)
{
guint8 b = (guint8) length;
g_byte_array_append (out, &b, 1);
return;
}
i = 0;
while (length > 0)
{
buf[++i] = (guint8) (length & 0xff);
length >>= 8;
}
header = (guint8) (0x80 | i);
g_byte_array_append (out, &header, 1);
while (i > 0)
{
g_byte_array_append (out, &buf[i], 1);
i--;
}
}
static void
frida_der_append_integer_uint (GByteArray * out, guint64 value)
{
gint i;
guint8 buf[9];
gint start;
for (i = 7; i >= 0; i--)
{
buf[i + 1] = (guint8) (value & 0xff);
value >>= 8;
}
buf[0] = 0;
start = 0;
while (start < 8 && buf[start] == 0 && (buf[start + 1] & 0x80) == 0)
start++;
frida_der_append_tlv (out, 0x02, buf + start, (gsize) (9 - start));
}
static void
frida_der_append_null (GByteArray * out)
{
frida_der_append_tlv (out, 0x05, NULL, 0);
}
static void
frida_der_append_oid (GByteArray * out, const guint8 * oid, gsize length)
{
frida_der_append_tlv (out, 0x06, oid, length);
}
static void
frida_der_append_utctime (GByteArray * out, time_t t)
{
struct tm tm;
gchar buf[16];
gmtime_r (&t, &tm);
g_snprintf (buf, sizeof (buf), "%02d%02d%02d%02d%02d%02dZ",
tm.tm_year % 100, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
frida_der_append_tlv (out, 0x17, (const guint8 *) buf, strlen (buf));
}
static void
frida_der_append_bit_string (GByteArray * out, const guint8 * bytes, gsize length)
{
guint8 tag = 0x03;
guint8 unused_bits = 0;
g_byte_array_append (out, &tag, 1);
frida_der_append_length (out, length + 1);
g_byte_array_append (out, &unused_bits, 1);
if (length > 0)
g_byte_array_append (out, bytes, length);
}
#else
void
_frida_generate_certificate (guint8 ** cert_der, gint * cert_der_length, gchar ** cert_pem, gchar ** key_pem)
{
@@ -90,6 +496,8 @@ frida_steal_bio_to_string (BIO ** bio)
return result;
}
#endif
void
_frida_sctp_connection_initialize_sctp_backend (void)
{
+147 -11
View File
@@ -128,6 +128,11 @@ namespace Frida {
set;
}
public WebRequestHandler? request_handler {
get;
set;
}
public EndpointParameters (string? address = null, uint16 port = 0, TlsCertificate? certificate = null,
string? origin = null, AuthenticationService? auth_service = null, File? asset_root = null) {
Object (
@@ -141,6 +146,75 @@ namespace Frida {
}
}
public interface WebRequestHandler : Object {
public abstract async WebResponse? handle_request (WebRequest request, Cancellable? cancellable) throws Error, IOError;
}
public sealed class WebRequest : Object {
public string method {
get;
construct;
}
public string path {
get;
construct;
}
public string? query_string {
get;
construct;
}
public Bytes? body {
get;
construct;
}
private Soup.MessageHeaders raw_headers;
internal WebRequest (string method, string path, string? query_string, Soup.MessageHeaders headers, Bytes? body) {
Object (method: method, path: path, query_string: query_string, body: body);
raw_headers = headers;
}
public void foreach_header (WebRequestHeaderFunc func) {
raw_headers.foreach ((name, val) => {
func (name, val);
});
}
}
public delegate void WebRequestHeaderFunc (string name, string val);
public sealed class WebResponse : Object {
public uint status {
get;
construct;
}
public Bytes body {
get;
construct;
}
private Soup.MessageHeaders raw_headers = new Soup.MessageHeaders (RESPONSE);
public WebResponse (uint status, Bytes body) {
Object (status: status, body: body);
}
public void add_header (string name, string val) {
raw_headers.append (name, val);
}
internal void write_headers_to (Soup.MessageHeaders target) {
raw_headers.foreach ((name, val) => {
target.replace (name, val);
});
}
}
public async IOStream negotiate_connection (IOStream stream, WebServiceTransport transport, string host, string? origin,
Cancellable? cancellable) throws Error, IOError {
var input = (DataInputStream) Object.new (typeof (DataInputStream),
@@ -468,14 +542,14 @@ namespace Frida {
server.add_websocket_handler ("/ws", endpoint_params.origin, null, on_websocket_opened);
if (endpoint_params.asset_root != null)
server.add_handler (null, on_asset_request);
if (endpoint_params.request_handler != null || endpoint_params.asset_root != null)
server.add_handler (null, on_http_request);
}
public void close () {
io_cancellable.cancel ();
if (endpoint_params.asset_root != null)
if (endpoint_params.request_handler != null || endpoint_params.asset_root != null)
server.remove_handler ("/");
server.remove_handler ("/ws");
@@ -575,20 +649,77 @@ namespace Frida {
connections.remove (connection);
}
private void on_asset_request (Soup.Server server, Soup.ServerMessage msg, string path,
private void on_http_request (Soup.Server server, Soup.ServerMessage msg, string path,
HashTable<string, string>? query) {
msg.get_response_headers ().replace ("Server", "Frida/" + _version_string ());
unowned string method = msg.get_method ();
if (method != "GET" && method != "HEAD") {
msg.set_status (Soup.Status.METHOD_NOT_ALLOWED, null);
msg.pause ();
handle_http_request.begin (msg, path);
}
private async void handle_http_request (Soup.ServerMessage msg, string path) {
WebRequestHandler? handler = endpoint_params.request_handler;
if (handler != null) {
if (yield try_request_handler (handler, msg, path, io_cancellable))
return;
}
File? asset_root = endpoint_params.asset_root;
if (asset_root != null) {
unowned string method = msg.get_method ();
if (method != "GET" && method != "HEAD") {
msg.set_status (Soup.Status.METHOD_NOT_ALLOWED, null);
msg.unpause ();
return;
}
File location = asset_root.resolve_relative_path (path.next_char ());
yield handle_asset_request (path, location, msg);
return;
}
File location = endpoint_params.asset_root.resolve_relative_path (path.next_char ());
msg.set_status (Soup.Status.NOT_FOUND, null);
msg.unpause ();
}
msg.pause ();
handle_asset_request.begin (path, location, msg);
private static async bool try_request_handler (WebRequestHandler handler, Soup.ServerMessage msg, string path,
Cancellable? cancellable) {
var body = msg.get_request_body ();
Bytes? request_body = (body.length != 0) ? body.flatten () : null;
unowned Uri? uri = msg.get_uri ();
string? query_string = (uri != null) ? uri.get_query () : null;
var request = new WebRequest (msg.get_method (), path, query_string, msg.get_request_headers (),
request_body);
WebResponse? response;
try {
response = yield handler.handle_request (request, cancellable);
} catch (Error e) {
msg.set_status (Soup.Status.INTERNAL_SERVER_ERROR, null);
msg.set_response ("text/plain; charset=utf-8", Soup.MemoryUse.COPY, e.message.data);
msg.unpause ();
return true;
} catch (IOError e) {
msg.unpause ();
return true;
}
if (response == null)
return false;
apply_response (msg, response);
msg.unpause ();
return true;
}
private static void apply_response (Soup.ServerMessage msg, WebResponse response) {
msg.set_status (response.status, null);
response.write_headers_to (msg.get_response_headers ());
Bytes body = response.body;
if (body.length > 0)
msg.get_response_body ().append_bytes (body);
}
private async void handle_asset_request (string path, File file, Soup.ServerMessage msg) {
@@ -940,8 +1071,11 @@ namespace Frida {
Memory.copy (buffer, recv_queue.data, n);
recv_queue.remove_range (0, (uint) n);
if (recv_queue.len == 0)
if (recv_queue.len == 0) {
#if HAVE_SOUP_WEBSOCKET_BACKPRESSURE
websocket.resume_input ();
#endif
}
update_pending_io ();
} else {
@@ -1009,7 +1143,9 @@ namespace Frida {
private void on_message (int type, Bytes message) {
with_state_lock (() => {
recv_queue.append (message.get_data ());
#if HAVE_SOUP_WEBSOCKET_BACKPRESSURE
websocket.pause_input ();
#endif
update_pending_io ();
});
}
+26 -1
View File
@@ -288,7 +288,7 @@ namespace Frida {
public JsonObjectReader (string json) throws Error {
try {
reader = new Json.Reader (Json.from_string (json));
reader = make_json_reader (json);
} catch (GLib.Error e) {
throw new Error.INVALID_ARGUMENT ("%s", e.message);
}
@@ -404,4 +404,29 @@ namespace Frida {
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_from_node (Json.from_string (json));
}
public Json.Reader make_json_reader_from_node (Json.Node root) {
var reader = new Json.Reader (root);
reader.set_data<Json.Node> ("frida-json-root", root);
return reader;
}
}
+6 -2
View File
@@ -9,7 +9,9 @@
# include <signal.h>
#endif
#include <gumjs/gumscriptbackend.h>
#ifdef HAVE_GIOOPENSSL
#if defined (HAVE_GIOAPPLE)
# include <gioapple.h>
#elif defined (HAVE_GIOOPENSSL)
# include <gioopenssl.h>
#endif
@@ -91,7 +93,9 @@ frida_gadget_environment_init (void)
g_thread_set_garbage_handler (_frida_gadget_on_pending_thread_garbage, NULL);
#ifdef HAVE_GIOOPENSSL
#if defined (HAVE_GIOAPPLE)
g_io_module_apple_register ();
#elif defined (HAVE_GIOOPENSSL)
g_io_module_openssl_register ();
#endif
+8 -6
View File
@@ -4,10 +4,12 @@ endif
subdir('base')
subdir('netif')
subdir('pipe')
subdir('payload')
if have_local_backend
subdir('agent')
endif
if build_gadget
subdir('gadget')
if have_in_tree_payloads
subdir('payload')
if have_local_backend
subdir('agent')
endif
if build_gadget
subdir('gadget')
endif
endif
+258 -73
View File
@@ -224,15 +224,21 @@ helper_name = 'frida-helper' + exe_suffix
agent_name = 'frida-agent' + shlib_suffix
gadget_name = 'frida-gadget' + shlib_suffix
root_asset_dir = get_option('libdir') / 'frida'
frida_libdir_name = f'frida-@api_version@'
root_asset_dir = get_option('libdir') / frida_libdir_name
if host_os_family == 'darwin'
asset_dir = root_asset_dir
default_asset_path_template = get_option('prefix') / get_option('libdir') / 'frida'
default_asset_path_template = get_option('prefix') / get_option('libdir') / frida_libdir_name
elif host_os_family == 'windows'
asset_dir = root_asset_dir / host_abi
asset_dir_modern = root_asset_dir / 'x86_64'
asset_dir_legacy = root_asset_dir / 'x86'
default_asset_path_template = get_option('prefix') / get_option('libdir') / frida_libdir_name / '<arch>'
else
asset_dir = root_asset_dir / host_cpu_mode
asset_dir_modern = root_asset_dir / '64'
asset_dir_legacy = root_asset_dir / '32'
default_asset_path_template = get_option('prefix') / get_option('libdir') / 'frida' / '<arch>'
default_asset_path_template = get_option('prefix') / get_option('libdir') / frida_libdir_name / '<arch>'
endif
asset_path_template = get_option('asset_path_template')
@@ -240,11 +246,21 @@ if asset_path_template == ''
asset_path_template = default_asset_path_template
endif
if get_option('assets') == 'embedded'
is_shared = get_option('default_library') == 'shared'
assets_mode = get_option('assets')
if assets_mode == 'auto'
assets_mode = is_shared ? 'installed' : 'embedded'
endif
cdata.set_quoted('FRIDA_LIBDIR_NAME', frida_libdir_name)
if assets_mode == 'embedded'
cdata.set('HAVE_EMBEDDED_ASSETS', 1)
vala_flags += '--define=HAVE_EMBEDDED_ASSETS'
else
cdata.set_quoted('FRIDA_HELPER_PATH', asset_path_template / helper_name)
cdata.set_quoted('FRIDA_AGENT_PATH', asset_path_template / agent_name)
cdata.set_quoted('FRIDA_HELPER_NAME', helper_name)
cdata.set_quoted('FRIDA_AGENT_NAME', agent_name)
endif
cdata.set('HAVE_' + host_os_family.to_upper(), 1)
@@ -312,15 +328,21 @@ if get_option('b_sanitize') == 'address'
cdata.set('HAVE_ASAN', 1)
endif
have_in_tree_payloads = get_option('default_library') != 'shared'
gum_dep = dependency('frida-gum-1.0', default_options: [
'default_library=static',
f'frida_version=@frida_version@',
'gumjs=enabled',
'gumjs=' + (have_in_tree_payloads ? 'enabled' : 'disabled'),
'inspector=enabled',
])
gumjs_dep = dependency('frida-gumjs-1.0')
gumjs_inspector_dep = dependency('frida-gumjs-inspector-1.0')
if have_in_tree_payloads
gumjs_dep = dependency('frida-gumjs-1.0', default_options: ['default_library=static'])
endif
gumjs_inspector_dep = dependency('frida-gumjs-inspector-1.0', default_options: ['default_library=static'])
glib_version_req = '>=2.74'
glib_options = [
'default_library=static',
'cocoa=disabled',
'selinux=disabled',
'xattr=false',
@@ -337,6 +359,7 @@ if optimize_for_prod
endif
gee_options = [
'default_library=static',
'disable-introspection=true',
]
if optimize_for_prod
@@ -344,16 +367,36 @@ if optimize_for_prod
endif
glib_dep = dependency('glib-2.0', version: glib_version_req, default_options: glib_options)
if glib_dep.type_name() == 'internal'
have_shared_glib = get_option('default_library') != 'static'
else
glib_flavor_src = '''
#include <glib.h>
#ifdef GLIB_STATIC_COMPILATION
# error GLib is static
#endif
'''
have_shared_glib = cc.compiles(glib_flavor_src, name: 'compiling with shared GLib', dependencies: [glib_dep])
endif
have_frida_glib = glib_dep.type_name() == 'internal' or cc.has_function('g_thread_set_callbacks', dependencies: [glib_dep])
if have_frida_glib
cdata.set('HAVE_FRIDA_GLIB', 1)
vala_flags += '--define=HAVE_FRIDA_GLIB'
endif
gobject_dep = dependency('gobject-2.0')
gmodule_dep = dependency('gmodule-2.0')
gio_dep = dependency('gio-2.0')
gee_dep = dependency('gee-0.8', default_options: gee_options)
json_glib_dep = dependency('json-glib-1.0', version: '>=1.8', default_options: [
'default_library=static',
'introspection=disabled',
'gtk_doc=disabled',
'tests=false',
])
libsoup_dep = dependency('libsoup-3.0', default_options: [
'default_library=static',
'sqlite:default_library=static',
'gssapi=disabled',
'ntlm=disabled',
'brotli=disabled',
@@ -365,19 +408,48 @@ libsoup_dep = dependency('libsoup-3.0', default_options: [
'tests=false',
'sysprof=disabled',
])
brotlidec_dep = dependency('libbrotlidec')
if libsoup_dep.type_name() == 'internal' \
or cc.has_function('soup_websocket_connection_pause_input', dependencies: [libsoup_dep])
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'])
if capstone_dep.type_name() != 'internal' \
and not cc.has_function('cs_arch_register_x86', dependencies: [capstone_dep])
add_project_arguments(
'-Dcs_arch_register_x86()=',
'-Dcs_arch_register_arm()=',
'-Dcs_arch_register_arm64()=',
'-Dcs_arch_register_mips()=',
language: ['c', 'objc'],
)
endif
quickjs_dep = dependency('quickjs', required: get_option('barebone_backend'), default_options: [
'default_library=static',
'libc=false',
'bignum=true',
'atomics=disabled',
'stack_check=disabled',
])
lzfse_dep = dependency('lzfse', required: get_option('barebone_backend'))
lzfse_dep = dependency('lzfse', required: false, default_options: ['default_library=static'])
lzfse_from_system = false
if not lzfse_dep.found() and not meson.is_cross_build()
if cc.has_header_symbol('lzfse.h', 'lzfse_decode_buffer')
lzfse_lib = cc.find_library('lzfse', required: false)
if lzfse_lib.found()
lzfse_dep = declare_dependency(dependencies: lzfse_lib)
lzfse_from_system = true
endif
endif
endif
if not lzfse_dep.found()
lzfse_dep = dependency('lzfse', required: get_option('barebone_backend'), default_options: ['default_library=static'])
endif
native_glib_dep = dependency('glib-2.0', version: glib_version_req, native: true, default_options: glib_options)
native_gio_dep = dependency('gio-2.0', native: true)
native_gee_dep = dependency('gee-0.8', native: true, default_options: gee_options)
native_brotlienc_dep = dependency('libbrotlienc', native: true)
native_brotlienc_dep = dependency('libbrotlienc', native: true, default_options: ['default_library=static'])
if host_os_family == 'windows'
gio_windows_dep = dependency('gio-windows-2.0')
@@ -385,8 +457,10 @@ else
gio_unix_dep = dependency('gio-unix-2.0')
endif
if host_os_family == 'linux'
libbpf_dep = dependency('libbpf')
have_local_backend = get_option('local_backend').allowed()
if host_os_family == 'linux' and have_local_backend
libbpf_dep = dependency('libbpf', default_options: ['default_library=static'])
endif
if host_os == 'android'
@@ -434,7 +508,6 @@ else
codesign = ''
endif
have_local_backend = get_option('local_backend').allowed()
have_simmy_backend = get_option('simmy_backend') \
.disable_auto_if(host_os != 'macos') \
.require(host_os == 'macos', error_message: 'Simmy backend is only available on macOS') \
@@ -464,11 +537,13 @@ if compiler_backend_opt.allowed()
files('tools' / 'cgo' / 'detect.py'),
go,
host_os,
host_os_family,
host_abi,
libc,
exe_suffix,
shlib_suffix,
cc.get_id(),
assets_mode,
'>>>', cc.cmd_array(), '<<<',
'>>>', ar, '<<<',
'>>>', nm, '<<<',
@@ -481,16 +556,27 @@ if compiler_backend_opt.allowed()
status = lines[0]
if status == 'ok'
have_compiler_backend = true
tokens = lines[1].split(' ')
compiler_backend_mode = tokens[0]
go_config = tokens[1]
cdata.set('HAVE_COMPILER_BACKEND', 1)
compiler_backend_mode = lines[1]
go_config = lines[2]
if compiler_backend_mode == 'c-archive'
vala_flags += '--define=COMPILER_BACKEND_EMBEDDED'
vala_flags += '--define=COMPILER_BACKEND_LINKED'
cdata.set('HAVE_COMPILER_BACKEND_LINKED', 1)
elif compiler_backend_mode == 'c-shared'
vala_flags += '--define=COMPILER_BACKEND_SHARED'
if assets_mode == 'installed'
vala_flags += '--define=COMPILER_BACKEND_INSTALLED_LIBRARY'
cdata.set_quoted('FRIDA_COMPILER_BACKEND_NAME', 'frida-compiler-backend' + shlib_suffix)
else
vala_flags += '--define=COMPILER_BACKEND_EMBEDDED_LIBRARY'
endif
elif compiler_backend_mode == 'pie'
vala_flags += '--define=COMPILER_BACKEND_EXECUTABLE'
if assets_mode == 'installed'
vala_flags += '--define=COMPILER_BACKEND_INSTALLED_EXECUTABLE'
cdata.set_quoted('FRIDA_COMPILER_BACKEND_NAME', 'frida-compiler-backend' + exe_suffix)
else
vala_flags += '--define=COMPILER_BACKEND_EMBEDDED_EXECUTABLE'
endif
else
error('Unsupported compiler backend mode: ' + compiler_backend_mode)
endif
@@ -561,8 +647,10 @@ endif
gi_dep = dependency('gobject-introspection-1.0', required: false)
if gi_dep.found()
girdir = gi_dep.get_variable('girdir')
typelibdir = gi_dep.get_variable('typelibdir')
else
girdir = get_option('prefix') / get_option('datadir') / 'gir-1.0'
typelibdir = get_option('prefix') / get_option('libdir') / 'girepository-1.0'
endif
vapidir = get_option('prefix') / get_option('datadir') / 'vala' / 'vapi'
@@ -571,12 +659,15 @@ gum_vala_args = [
'--pkg=frida-gum-1.0',
'--vapidir=' + gum_dep.get_variable('frida_vapidir'),
]
gumjs_vala_args = ['--pkg=frida-gumjs-1.0', '--pkg=gio-2.0', gum_vala_args]
gumjs_inspector_vala_args = ['--pkg=frida-gumjs-inspector-1.0', gum_vala_args]
have_v8 = gumjs_dep.get_variable('gumjs_v8') == 'enabled'
if have_v8
vala_flags += ['--define=HAVE_V8']
if have_in_tree_payloads
gumjs_vala_args = ['--pkg=frida-gumjs-1.0', '--pkg=gio-2.0', gum_vala_args]
have_v8 = gumjs_dep.get_variable('gumjs_v8') == 'enabled'
if have_v8
vala_flags += ['--define=HAVE_V8']
endif
endif
backend_deps_private = []
@@ -584,6 +675,7 @@ backend_reqs_private = []
backend_libs_private = []
openssl_options = [
'default_library=static',
'cli=disabled',
]
if host_os_family == 'windows' and cc.get_argument_syntax() != 'msvc'
@@ -596,25 +688,48 @@ openssl_dep = dependency('openssl',
)
if have_fruity_backend
foreach dep : ['libnghttp2', 'libngtcp2', 'libngtcp2_crypto_quictls', 'libusb-1.0']
backend_deps_private += dependency(dep)
backend_reqs_private += dep
endforeach
backend_deps_private += dependency('libnghttp2', default_options: ['default_library=static'])
backend_reqs_private += 'libnghttp2'
lwip_options = [
'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'
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'
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
backend_deps_private += dependency('lwip', default_options: lwip_options)
backend_reqs_private += 'lwip'
endif
if quickjs_dep.found()
@@ -624,10 +739,14 @@ endif
if lzfse_dep.found()
backend_deps_private += lzfse_dep
backend_reqs_private += 'lzfse'
if lzfse_from_system
backend_libs_private += '-llzfse'
else
backend_reqs_private += 'lzfse'
endif
endif
if host_os_family == 'linux'
if host_os_family == 'linux' and have_local_backend
backend_deps_private += libbpf_dep
backend_reqs_private += 'libbpf'
endif
@@ -638,43 +757,87 @@ endif
if host_os == 'android'
termux_elf_cleaner = find_program('termux-elf-cleaner', native: true)
libselinux_dep = dependency('libselinux', version: '>=3.0')
libsepol_dep = dependency('libsepol', version: '>=3.0')
libselinux_dep = dependency('libselinux', version: '>=3.0', default_options: ['default_library=static'])
libsepol_dep = dependency('libsepol', version: '>=3.0', default_options: ['default_library=static'])
backend_deps_private += [libselinux_dep, libsepol_dep]
backend_reqs_private += ['libselinux', 'libsepol']
else
termux_elf_cleaner = []
endif
tls_provider_dep = dependency('gioopenssl', required: get_option('connectivity'), default_options: [
'gnutls=disabled',
'openssl=enabled',
'libproxy=disabled',
'gnome_proxy=disabled',
'tests=false',
])
tls_provider_vala_args = []
if tls_provider_dep.found()
tls_provider_vala_args += [
'--pkg=gioopenssl',
'--vapidir=' + tls_provider_dep.get_variable('vapidir'),
]
cdata.set('HAVE_GIOOPENSSL', 1)
vala_flags += ['--define=HAVE_GIOOPENSSL']
backend_deps_private += tls_provider_dep
backend_reqs_private += 'gioopenssl'
if have_shared_glib
tls_provider_dep = gio_dep
else
use_apple_tls = false
if host_os_family == 'darwin'
apple_tls_deployment_check = '''
#include <Availability.h>
#if defined (__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 150000
# error macOS deployment target below 15.0
#endif
#if defined (__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 180000
# error iOS deployment target below 18.0
#endif
#if defined (__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 180000
# error tvOS deployment target below 18.0
#endif
#if defined (__WATCH_OS_VERSION_MIN_REQUIRED) && __WATCH_OS_VERSION_MIN_REQUIRED < 110000
# error watchOS deployment target below 11.0
#endif
'''
use_apple_tls = cc.compiles(apple_tls_deployment_check, name: 'Apple TLS backend deployment target')
endif
if use_apple_tls
tls_provider_name = 'gioapple'
tls_provider_define_name = 'HAVE_GIOAPPLE'
tls_provider_backend_options = [
'gnutls=disabled',
'openssl=disabled',
'apple=enabled',
]
else
tls_provider_name = 'gioopenssl'
tls_provider_define_name = 'HAVE_GIOOPENSSL'
tls_provider_backend_options = [
'gnutls=disabled',
'openssl=enabled',
'apple=disabled',
]
endif
tls_provider_dep = dependency(tls_provider_name,
required: get_option('connectivity'),
default_options: tls_provider_backend_options + [
'libproxy=disabled',
'gnome_proxy=disabled',
'tests=false',
],
)
if tls_provider_dep.found()
tls_provider_vala_args += [
'--pkg=' + tls_provider_name,
'--vapidir=' + tls_provider_dep.get_variable('vapidir'),
]
cdata.set(tls_provider_define_name, 1)
vala_flags += ['--define=' + tls_provider_define_name]
backend_deps_private += tls_provider_dep
backend_reqs_private += tls_provider_name
endif
endif
nice_dep = dependency('nice', required: get_option('connectivity'), default_options: [
'default_library=static',
'gupnp=disabled',
'gstreamer=disabled',
'crypto-library=openssl',
'crypto-library=auto',
'examples=disabled',
'tests=disabled',
'introspection=disabled',
])
if nice_dep.found()
usrsctp_dep = dependency('usrsctp', default_options: [
'default_library=static',
'sctp_inet=false',
'sctp_inet6=false',
'sctp_build_programs=false',
@@ -682,8 +845,12 @@ if nice_dep.found()
cdata.set('HAVE_NICE', 1)
vala_flags += ['--define=HAVE_NICE']
backend_deps_private += [nice_dep, openssl_dep, usrsctp_dep]
backend_reqs_private += ['nice', 'openssl', 'usrsctp']
backend_deps_private += [nice_dep, usrsctp_dep]
backend_reqs_private += ['nice', 'usrsctp']
if host_os != 'macos'
backend_deps_private += openssl_dep
backend_reqs_private += 'openssl'
endif
if host_os_family in ['darwin', 'freebsd', 'qnx']
cdata.set('HAVE_SCONN_LEN', 1)
@@ -693,10 +860,10 @@ else
endif
if host_os_family == 'windows'
backend_libs_private += ['-lsetupapi']
backend_libs_private += ['-lsetupapi', '-lshlwapi']
endif
if host_os_family == 'darwin'
backend_libs_private += ['-Wl,-framework,Foundation', '-lbsm']
backend_libs_private += ['-Wl,-framework,Foundation', '-Wl,-framework,CoreFoundation', '-lbsm']
endif
if host_os == 'macos'
backend_libs_private += ['-Wl,-framework,AppKit']
@@ -809,11 +976,20 @@ if cc.get_id() == 'gcc' and cc.compiles('#if __GNUC__ >= 5\n#error NOTNEEDED\n#e
add_project_arguments('-w', language: 'c')
endif
vapidir = meson.current_source_dir() / 'vapi'
local_vapidir = meson.current_source_dir() / 'vapi'
vala_flags += [
'--vapidir=' + vapidir,
'--vapidir=' + local_vapidir,
'--pkg', 'config',
]
if gee_dep.type_name() not in ['internal', 'cmake']
gee_prefix = gee_dep.get_variable('prefix', default_value: '')
if gee_prefix != ''
gee_vapidir = gee_prefix / 'share' / 'vala' / 'vapi'
if fs.is_dir(gee_vapidir)
vala_flags += '--vapidir=' + gee_vapidir
endif
endif
endif
vala_flags += '--define=' + host_os_family.to_upper()
if host_os != host_os_family
@@ -825,13 +1001,22 @@ if host_abi != host_arch
endif
vala_flags += '--define=' + host_machine.endian().to_upper() + '_ENDIAN'
add_project_arguments(vala_flags, language: 'vala')
add_project_arguments('-I' + vapidir, language: 'c')
add_project_arguments('-I' + local_vapidir, language: 'c')
native_vala_flags = [
'--vapidir=' + meson.current_source_dir() / 'vapi',
'--vapidir=' + local_vapidir,
]
if native_gee_dep.type_name() not in ['internal', 'cmake']
native_gee_prefix = native_gee_dep.get_variable('prefix', default_value: '')
if native_gee_prefix != ''
native_gee_vapidir = native_gee_prefix / 'share' / 'vala' / 'vapi'
if fs.is_dir(native_gee_vapidir)
native_vala_flags += '--vapidir=' + native_gee_vapidir
endif
endif
endif
add_project_arguments(native_vala_flags, language: 'vala', native: true)
add_project_arguments('-I' + vapidir, language: 'c', native: true)
add_project_arguments('-I' + local_vapidir, language: 'c', native: true)
subdir('compat')
subdir('tools')
@@ -866,7 +1051,7 @@ summary(
summary(
{
'compat': compat_summary,
'assets': get_option('assets'),
'assets': assets_mode,
'mapper': have_mapper,
'connectivity': tls_provider_dep.found(),
'gadget': build_gadget,
+2 -2
View File
@@ -48,8 +48,8 @@ option('compiler_backend',
option('assets',
type: 'combo',
choices: ['embedded', 'installed'],
value: 'embedded',
choices: ['auto', 'embedded', 'installed'],
value: 'auto',
description: 'Whether assets such as frida-agent should be embedded vs installed and loaded at runtime'
)
+3 -1
View File
@@ -39,7 +39,9 @@ namespace Frida.Portal {
};
private static int main (string[] args) {
#if HAVE_GIOOPENSSL
#if HAVE_GIOAPPLE
GIOApple.register ();
#elif HAVE_GIOOPENSSL
GIOOpenSSL.register ();
#endif
+1 -1
Submodule releng updated: 5595d31705...10b201e27d.
+7 -2
View File
@@ -29,15 +29,20 @@ if host_os == 'macos'
extra_link_depends += [server_plist]
endif
server_deps = [core_dep]
if host_os_family == 'darwin'
server_deps += dependency('appleframeworks', modules: ['CoreFoundation'])
endif
raw_server = executable('frida-server-raw', server_sources,
vala_args: [core_vala_args, '--pkg=posix'],
link_args: extra_link_args,
link_depends: extra_link_depends,
dependencies: [core_dep],
dependencies: server_deps,
)
server_name = 'frida-server' + exe_suffix
need_universal_server = get_option('assets') == 'installed' and server_compat != ''
need_universal_server = assets_mode == 'installed' and server_compat != ''
server = custom_target('frida-server',
input: [raw_server, 'frida-server.xcent'],
+7 -7
View File
@@ -23,7 +23,7 @@ namespace Frida {
if (response.is_null ())
return HostApplicationInfo.empty ();
Json.Reader reader = new Json.Reader (response);
Json.Reader reader = make_json_reader_from_node (response);
string? identifier = null;
string? name = null;
@@ -75,7 +75,7 @@ namespace Frida {
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
Json.Reader reader = new Json.Reader (response);
Json.Reader reader = make_json_reader_from_node (response);
int num_apps = reader.count_elements ();
if (num_apps == -1)
@@ -141,7 +141,7 @@ namespace Frida {
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
Json.Reader reader = new Json.Reader (response);
Json.Reader reader = make_json_reader_from_node (response);
int num_processes = reader.count_elements ();
if (num_processes == -1)
@@ -195,7 +195,7 @@ namespace Frida {
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
var r = new Json.Reader (response);
var r = make_json_reader_from_node (response);
parse_envelope_or_throw (r);
r.read_element (1);
@@ -265,7 +265,7 @@ namespace Frida {
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
var r = new Json.Reader (response);
var r = make_json_reader_from_node (response);
parse_envelope_or_throw (r);
}
@@ -279,7 +279,7 @@ namespace Frida {
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
var r = new Json.Reader (response);
var r = make_json_reader_from_node (response);
parse_envelope_or_throw (r);
r.read_element (1);
@@ -292,7 +292,7 @@ namespace Frida {
private async void request_ok (Json.Builder stanza, Cancellable? cancellable) throws Error, IOError {
Json.Node response = yield transport.request (stanza.get_root (), cancellable);
var r = new Json.Reader (response);
var r = make_json_reader_from_node (response);
parse_envelope_or_throw (r);
}
File diff suppressed because it is too large. Load diff
+68 -37
View File
@@ -23,57 +23,49 @@ CORE_TAG_METHOD = f"{{{CORE_NAMESPACE}}}method"
OBJECT_TYPE_PATTERN = re.compile(r"\bpublic\s+(sealed )?(class|interface)\s+(\w+)\b")
TOPLEVEL_NAMES = [
"frida.vala",
"package-manager.vala",
"control-service.vala",
"portal-service.vala",
"file-monitor.vala",
Path("compiler") / "compiler.vala",
]
def main():
parser = argparse.ArgumentParser(description="Generate refined Frida API definitions")
parser.add_argument('--output', dest='output_type', choices=['bundle', 'header', 'gir', 'vapi', 'vapi-stamp'], default='bundle')
parser.add_argument('frida_version', metavar='frida-version', type=str)
parser.add_argument('frida_major_version', metavar='frida-major-version', type=str)
parser.add_argument('frida_minor_version', metavar='frida-minor-version', type=str)
parser.add_argument('frida_micro_version', metavar='frida-micro-version', type=str)
parser.add_argument('frida_nano_version', metavar='frida-nano-version', type=str)
parser.add_argument('api_version', metavar='api-version', type=str)
parser.add_argument('core_header', metavar='/path/to/frida-core.h', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('core_gir', metavar='/path/to/Frida-x.y.gir', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('core_vapi', metavar='/path/to/frida-core.vapi', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('base_header', metavar='/path/to/frida-base.h', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('base_gir', metavar='/path/to/FridaBase-x.y.gir', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('base_vapi', metavar='/path/to/frida-base.vapi', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('--output', dest='output_type', choices=['bundle', 'header', 'gir', 'vapi', 'vapi-stamp', 'symbol-maps'], default='bundle')
parser.add_argument('output_dir', metavar='/output/dir')
parser.add_argument('extra_args', nargs='*')
args = parser.parse_args()
output_type = args.output_type
frida_version = args.frida_version
frida_version_components = (
args.frida_major_version,
args.frida_minor_version,
args.frida_micro_version,
args.frida_nano_version,
)
api_version = args.api_version
core_header = args.core_header.read()
core_gir = args.core_gir.read()
core_vapi = args.core_vapi.read()
base_header = args.base_header.read()
base_gir = args.base_gir.read()
base_vapi = args.base_vapi.read()
output_dir = Path(args.output_dir)
if output_type == 'symbol-maps':
emit_symbol_maps_from_source(output_dir)
return
extra = args.extra_args
frida_version = extra[0]
frida_version_components = tuple(extra[1:5])
api_version = extra[5]
if output_type == 'vapi-stamp':
(output_dir / f"frida-core-{api_version}.vapi.stamp").write_bytes(b"")
return
toplevel_names = [
"frida.vala",
"package-manager.vala",
"control-service.vala",
"portal-service.vala",
"file-monitor.vala",
Path("compiler") / "compiler.vala",
]
core_header = Path(extra[6]).read_text(encoding='utf-8')
core_gir = Path(extra[7]).read_text(encoding='utf-8')
core_vapi = Path(extra[8]).read_text(encoding='utf-8')
base_header = Path(extra[9]).read_text(encoding='utf-8')
base_gir = Path(extra[10]).read_text(encoding='utf-8')
base_vapi = Path(extra[11]).read_text(encoding='utf-8')
toplevel_sources = []
src_dir = Path(__file__).parent.parent.resolve()
for name in toplevel_names:
for name in TOPLEVEL_NAMES:
toplevel_sources.append((src_dir / name).read_text(encoding='utf-8'))
toplevel_code = "\n".join(toplevel_sources)
@@ -249,6 +241,29 @@ def emit_gir(api: ApiSpec, core_gir: str, base_gir: str, output_dir: Path) -> st
merge_and_transform_elements("interface", object_type_names)
merge_and_transform_elements("enumeration", enum_type_names | error_type_names)
for source_root in [core_root, base_root]:
for record in source_root.findall(".//record", GIR_NAMESPACES):
owner = record.get(f"{{{GLIB_NAMESPACE}}}is-gtype-struct-for")
if owner is not None and owner in object_type_names:
merged_namespace.append(record)
known_names = object_type_names | enum_type_names | error_type_names
referenced_names = set()
for elem in merged_namespace.iter():
type_name = elem.get("name")
if type_name is None:
continue
for prefix in ("Frida.", "FridaBase."):
if type_name.startswith(prefix):
referenced_names.add(type_name[len(prefix):])
break
needed_names = referenced_names - known_names
for source_root in [core_root, base_root]:
source_namespace = source_root.find("namespace", GIR_NAMESPACES)
for callback in source_namespace.findall("callback", GIR_NAMESPACES):
if callback.get("name") in needed_names:
merged_namespace.append(callback)
ET.indent(merged_root, space=" ")
result = ET.tostring(merged_root,
encoding="unicode",
@@ -304,6 +319,17 @@ def emit_vapi(api, output_dir):
output_deps_file.write("gobject-2.0\n")
output_deps_file.write("gio-2.0\n")
def emit_symbol_maps_from_source(output_dir: Path):
with OutputFile(output_dir / 'frida-core.version') as f:
f.write("{\n")
f.write(" global:\n")
f.write(" frida_*;\n")
f.write(" _frida_*;\n")
f.write("\n")
f.write(" local:\n")
f.write(" *;\n")
f.write("};\n")
def parse_api(frida_version, frida_version_components, api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi):
all_headers = core_header + "\n" + base_header
@@ -325,6 +351,9 @@ def parse_api(frida_version, frida_version_components, api_version, toplevel_cod
"EndpointParameters": "PortalService",
"AuthenticationService": "EndpointParameters",
"StaticAuthenticationService": "AuthenticationService",
"WebRequestHandler": "StaticAuthenticationService",
"WebRequest": "WebRequestHandler",
"WebResponse": "WebRequest",
}
internal_type_prefixes = [
"AgentMessageKind",
@@ -419,7 +448,7 @@ def parse_api(frida_version, frida_version_components, api_version, toplevel_cod
object_type.c_method_prototypes.append(method_cprototype)
elif method_name == 'get_type':
object_type.c_get_type = method_cprototype.replace("G_GNUC_CONST ;", "G_GNUC_CONST;")
for d in re.finditer(r"^typedef.+?\(\*(" + object_type.c_name + r".+?)\) \(.+\);$", core_header, re.MULTILINE):
for d in re.finditer(r"^typedef.+?\(\*(" + object_type.c_name + r".+?)\) \(.+\);$", all_headers, re.MULTILINE):
delegate_cname = d.group(1)
if delegate_cname not in seen_cdelegates:
seen_cdelegates.add(delegate_cname)
@@ -510,6 +539,8 @@ def function_is_public(name):
"generate_certificate",
"get_dbus_context",
"invalidate_dbus_context",
"make_json_reader",
"make_json_reader_from_node",
"make_parameters_dict",
"compute_system_parameters",
"parse_control_address",
+75 -39
View File
@@ -3,6 +3,7 @@ fs = import('fs')
generate_api = [python, files('generate.py')]
generate_api_args = [
meson.current_build_dir(),
frida_version,
frida_major_version.to_string(),
frida_minor_version.to_string(),
@@ -15,7 +16,6 @@ generate_api_args = [
base_build_dir / 'frida-base.h',
base_build_dir / base_gir_name,
base_build_dir / f'frida-base-@[email protected]',
meson.current_build_dir(),
]
core_api = custom_target('frida-core-api',
@@ -44,6 +44,24 @@ glib_gir = fs.copyfile('GLib-2.0.gir', install: install_glib_girs, install_dir:
gobject_gir = fs.copyfile('GObject-2.0.gir', install: install_glib_girs, install_dir: girdir)
gio_gir = fs.copyfile('Gio-2.0.gir', install: install_glib_girs, install_dir: girdir)
g_ir_compiler = find_program('g-ir-compiler', required: false)
if g_ir_compiler.found()
custom_target('frida-core-typelib',
command: [
g_ir_compiler,
'--output', '@OUTPUT@',
'--includedir=' + girdir,
'--includedir=' + meson.current_source_dir(),
'@INPUT@',
],
input: core_public_gir,
output: f'Frida-@[email protected]',
depends: core,
install: true,
install_dir: typelibdir,
)
endif
core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
input: core_public_vapi,
output: f'frida-core-@[email protected]',
@@ -54,22 +72,26 @@ core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
],
)
core_public_lib = custom_target('frida-core-library',
input: [core] + internal_libs,
output: f'libfrida-core-@[email protected]',
command: [
python,
files('merge.py'),
lib,
libtool,
ar,
meson.current_build_dir(),
'@OUTPUT@',
'@INPUT@'
],
install: true,
install_dir: get_option('libdir'),
)
if is_shared
core_public_lib = core
else
core_public_lib = custom_target('frida-core-library',
input: [core] + internal_libs,
output: f'libfrida-core-@[email protected]',
command: [
python,
files('merge.py'),
lib,
libtool,
ar,
meson.current_build_dir(),
'@OUTPUT@',
'@INPUT@'
],
install: true,
install_dir: get_option('libdir'),
)
endif
core_public_extra_headers = []
if host_os == 'android'
@@ -96,29 +118,43 @@ installed_variables = {
'frida_vapidir': vapidir,
}
core_public_dep = declare_dependency(
sources: [core_public_h, core_public_gir, glib_gir, gobject_gir, gio_gir, core_public_vapi_stamp, core_public_extra_headers],
include_directories: include_directories('.'),
link_args: backend_libs_private,
link_with: core_public_lib,
dependencies: core_deps,
variables: uninstalled_variables,
)
core_public_dep_kwargs = {
'sources': [core_public_h, core_public_gir, glib_gir, gobject_gir, gio_gir, core_public_vapi_stamp] + core_public_extra_headers,
'include_directories': include_directories('.'),
'link_with': core_public_lib,
'dependencies': core_deps,
'variables': uninstalled_variables,
}
if not is_shared
core_public_dep_kwargs += {
'link_args': backend_libs_private,
}
endif
core_public_dep = declare_dependency(kwargs: core_public_dep_kwargs)
pkg = import('pkgconfig')
pkg.generate(
filebase: f'frida-core-@api_version@',
name: 'Frida Core Library',
version: frida_version,
description: 'Instrumentation toolkit for developers, reverse-engineers, and security researchers',
requires: ['glib-2.0', 'gobject-2.0', 'gio-2.0', 'json-glib-1.0'],
requires_private: ['gmodule-2.0', 'gee-0.8', 'libsoup-3.0', 'frida-gum-1.0', 'frida-gumjs-inspector-1.0', 'libbrotlidec'] + backend_reqs_private,
subdirs: f'frida-@api_version@',
libraries: core_public_lib,
libraries_private: backend_libs_private,
extra_cflags: ['-I${frida_includedir}'],
variables: installed_variables,
uninstalled_variables: uninstalled_variables,
)
pkg_kwargs = {
'filebase': f'frida-core-@api_version@',
'name': 'Frida Core Library',
'version': frida_version,
'description': 'Instrumentation toolkit for developers, reverse-engineers, and security researchers',
'requires': ['glib-2.0', 'gobject-2.0', 'gio-2.0', 'json-glib-1.0'],
'subdirs': f'frida-@api_version@',
'extra_cflags': ['-I${frida_includedir}'],
'variables': installed_variables,
'uninstalled_variables': uninstalled_variables,
}
if is_shared
pkg_kwargs += {
'libraries': ['-L${libdir}', f'-lfrida-core-@api_version@'],
}
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,
'libraries_private': backend_libs_private,
}
endif
pkg.generate(kwargs: pkg_kwargs)
meson.override_dependency(f'frida-core-@api_version@', core_public_dep)
+14 -3
View File
@@ -14,15 +14,21 @@ namespace Frida {
public sealed class BareboneHostSessionProvider : Object, HostSessionProvider {
public string id {
get { return "barebone"; }
get {
return "barebone";
}
}
public string name {
get { return "GDB Remote Stub"; }
get {
return "GDB Remote Stub";
}
}
public Variant? icon {
get { return null; }
get {
return _icon;
}
}
public HostSessionProviderKind kind {
@@ -31,8 +37,13 @@ namespace Frida {
}
}
private static Variant _icon;
private BareboneHostSession? host_session;
static construct {
_icon = make_provider_icon (Frida.Data.Icons.get_barebone_png_blob ().data);
}
public async void close (Cancellable? cancellable) throws IOError {
if (host_session != null) {
yield host_session.close (cancellable);
+1 -1
View File
@@ -401,7 +401,7 @@ namespace Frida.Barebone {
}
private void handle_message (Json.Node message) throws Error {
var r = new Json.Reader (message);
var r = make_json_reader_from_node (message);
bool is_response = r.read_member ("id");
uint id = 0;
+1 -1
View File
@@ -1,4 +1,4 @@
#ifdef _MSC_VER
#if defined (_MSC_VER) && defined (HAVE_COMPILER_BACKEND_LINKED)
#include <glib.h>
+1
View File
@@ -0,0 +1 @@
__frida_compiler_backend_*
+7 -4
View File
@@ -1,4 +1,3 @@
import base64
import json
import os
import shutil
@@ -10,7 +9,7 @@ from typing import List
def main(argv):
go_config = json.loads(base64.b64decode(argv[1]).decode("utf-8"))
go_config = json.loads(Path(argv[1]).read_text(encoding="utf-8"))
output_dir, priv_dir, go, npm, *inputs = [Path(d).resolve() for d in argv[2:]]
try:
@@ -74,8 +73,12 @@ def build_backend(
extra_go_args = config["extra_go_args"].copy()
if mode == "c-shared":
version_script = priv_dir / "backend.version"
extra_go_args.append(f"-ldflags=-linkmode=external -extldflags=-Wl,--version-script={version_script}")
if config["os_family"] == "darwin":
symbols_list = priv_dir / "backend.symbols"
extra_go_args.append(f"-ldflags=-linkmode=external -extldflags=-Wl,-exported_symbols_list,{symbols_list}")
elif config["os_family"] != "windows":
version_script = priv_dir / "backend.version"
extra_go_args.append(f"-ldflags=-linkmode=external -extldflags=-Wl,--version-script={version_script}")
run(
go,
+40 -13
View File
@@ -235,12 +235,24 @@ namespace Frida {
namespace CompilerBackend {
private void init () {
#if HAVE_COMPILER_BACKEND
#if COMPILER_BACKEND_EMBEDDED
#if COMPILER_BACKEND_LINKED
_init_go_runtime ();
build = (BuildFunc) _build;
watch = (WatchFunc) _watch;
WatchSession.dispose = (WatchSession.DisposeFunc) WatchSession._dispose;
#elif COMPILER_BACKEND_SHARED
#elif COMPILER_BACKEND_INSTALLED_LIBRARY
Module? backend = null;
try {
backend = new Module (Frida.compiler_backend_path, LOCAL);
} catch (ModuleError e) {
return;
}
backend.make_resident ();
build = resolve_symbol (backend, "_frida_compiler_backend_build");
watch = resolve_symbol (backend, "_frida_compiler_backend_watch");
WatchSession.dispose = resolve_symbol (backend, "_frida_compiler_backend_watch_session_dispose");
#elif COMPILER_BACKEND_EMBEDDED_LIBRARY
unowned uint8[] backend_so = Frida.Data.Compiler.get_frida_compiler_backend_so_blob ().data;
Module? backend = null;
@@ -272,11 +284,12 @@ namespace Frida {
assert_not_reached ();
}
}
backend.make_resident ();
build = resolve_symbol (backend, "_frida_compiler_backend_build");
watch = resolve_symbol (backend, "_frida_compiler_backend_watch");
WatchSession.dispose = resolve_symbol (backend, "_frida_compiler_backend_watch_session_dispose");
#elif COMPILER_BACKEND_EXECUTABLE
#elif COMPILER_BACKEND_EMBEDDED_EXECUTABLE || COMPILER_BACKEND_INSTALLED_EXECUTABLE
backend_process = new BackendProcess ();
build = executable_build;
@@ -287,8 +300,15 @@ namespace Frida {
}
private void check_available () throws Error {
if (build == null)
if (build == null) {
#if COMPILER_BACKEND_INSTALLED_LIBRARY || COMPILER_BACKEND_INSTALLED_EXECUTABLE
throw new Error.NOT_SUPPORTED (
"Compiler backend plugin not installed; expected at: %s",
Frida.compiler_backend_path);
#else
throw new Error.NOT_SUPPORTED ("Compiler backend disabled at build-time");
#endif
}
}
private BuildFunc? build;
@@ -305,7 +325,7 @@ namespace Frida {
string platform, string[] externals, StartingFunc on_starting, FinishedFunc on_finished,
OutputFunc on_output, DiagnosticFunc on_diagnostic, owned WatchReadyFunc on_ready);
#if COMPILER_BACKEND_EMBEDDED
#if COMPILER_BACKEND_LINKED
private extern void _init_go_runtime ();
private extern void _build ();
private extern void _watch ();
@@ -317,7 +337,7 @@ namespace Frida {
private DisposeFunc? dispose;
#if COMPILER_BACKEND_EMBEDDED
#if COMPILER_BACKEND_LINKED
private extern void _dispose ();
#endif
}
@@ -330,7 +350,7 @@ namespace Frida {
private delegate void DiagnosticFunc (owned string category, int code, owned string? path, int line, int character,
owned string text);
#if HAVE_COMPILER_BACKEND && COMPILER_BACKEND_SHARED
#if HAVE_COMPILER_BACKEND && (COMPILER_BACKEND_EMBEDDED_LIBRARY || COMPILER_BACKEND_INSTALLED_LIBRARY)
private T resolve_symbol<T> (Module m, string name) {
void * address;
if (!m.symbol (name, out address))
@@ -339,7 +359,7 @@ namespace Frida {
}
#endif
#if COMPILER_BACKEND_EXECUTABLE
#if COMPILER_BACKEND_EMBEDDED_EXECUTABLE || COMPILER_BACKEND_INSTALLED_EXECUTABLE
private BackendProcess? backend_process;
private static void executable_build (string project_root, string entrypoint, OutputFormat output_format,
@@ -393,7 +413,13 @@ namespace Frida {
if (process != null)
return;
#if COMPILER_BACKEND_INSTALLED_EXECUTABLE
unowned string path = Frida.compiler_backend_path;
bool unlink_after = false;
#else
string path = extract_backend_executable ();
bool unlink_after = true;
#endif
try {
var p = new Subprocess (STDIN_PIPE | STDOUT_PIPE | STDERR_PIPE, path);
process = p;
@@ -408,10 +434,12 @@ namespace Frida {
process_incoming_messages.begin ();
process_stderr_stream.begin (errput);
} finally {
FileUtils.unlink (path);
if (unlink_after)
FileUtils.unlink (path);
}
}
#if COMPILER_BACKEND_EMBEDDED_EXECUTABLE
private static string extract_backend_executable () throws GLib.Error {
unowned uint8[] blob = Frida.Data.Compiler.get_frida_compiler_backend_blob ().data;
@@ -425,6 +453,7 @@ namespace Frida {
return path;
}
#endif
private void handle_process_failure (string message) {
io_cancellable.cancel ();
@@ -714,15 +743,13 @@ namespace Frida {
}
private void handle_message (string json) throws Error {
Json.Node root;
Json.Reader reader;
try {
root = Json.from_string (json);
reader = make_json_reader (json);
} catch (GLib.Error e) {
throw new Error.PROTOCOL ("%s", e.message);
}
var reader = new Json.Reader (root);
reader.read_member ("type");
unowned string? type = reader.get_string_value ();
if (type == null)
+29 -19
View File
@@ -29,6 +29,7 @@ if have_compiler_backend
'backend.go',
'protocol_types.go',
'backend.version',
'backend.symbols',
'tscompiler.go',
'tsconfig.go',
'shims.go',
@@ -36,6 +37,7 @@ if have_compiler_backend
'package-lock.json',
],
output: output_files,
depend_files: go_config,
command: [
python,
files('build-backend.py'),
@@ -58,6 +60,8 @@ if have_compiler_backend
input: compiler_backend[0],
output: processed_filename,
command: post_process + ['shared-library', identity, 'elf-cleaner:off'],
install: assets_mode == 'installed',
install_dir: get_option('libdir') / frida_libdir_name / 'plugins',
)
elif compiler_backend_mode == 'pie'
processed_filename = 'frida-compiler-backend' + exe_suffix
@@ -65,27 +69,33 @@ if have_compiler_backend
input: compiler_backend[0],
output: processed_filename,
command: post_process + ['executable', processed_filename],
install: assets_mode == 'installed',
install_dir: get_option('libdir') / frida_libdir_name / 'plugins',
)
endif
compiler_data = custom_target('frida-data-compiler',
input: [
'compiler.resources',
compiler_backend_binary,
],
output: [
'frida-data-compiler.vapi',
'frida-data-compiler.h',
'frida-data-compiler.c',
'frida-data-compiler-blob' + resource_blob_extension,
],
command: [
resource_compiler_cmd_array,
'-c', '@INPUT0@',
'-o', meson.current_build_dir() / 'frida-data-compiler',
'@INPUT1@',
],
)
backend_sources += compiler_data
if assets_mode == 'installed'
core_extra_link_depends += compiler_backend_binary
else
compiler_data = custom_target('frida-data-compiler',
input: [
'compiler.resources',
compiler_backend_binary,
],
output: [
'frida-data-compiler.vapi',
'frida-data-compiler.h',
'frida-data-compiler.c',
'frida-data-compiler-blob' + resource_blob_extension,
],
command: [
resource_compiler_cmd_array,
'-c', '@INPUT0@',
'-o', meson.current_build_dir() / 'frida-data-compiler',
'@INPUT1@',
],
)
backend_sources += compiler_data
endif
endif
endif
+2 -2
View File
@@ -44,7 +44,7 @@ namespace Frida {
#if HAVE_EMBEDDED_ASSETS
return null;
#else
unowned string path = Config.FRIDA_AGENT_PATH;
unowned string path = Frida.agent_path;
# if IOS || TVOS
unowned string? cryptex_path = Environment.get_variable ("CRYPTEX_MOUNT_PATH");
if (cryptex_path != null)
@@ -1053,7 +1053,7 @@ namespace Frida {
var parameters = make_parameters_dict ();
try {
var header = new Json.Reader (Json.from_string (raw_header));
var header = make_json_reader (raw_header);
foreach (string member in header.list_members ()) {
header.read_member (member);
+1 -1
View File
@@ -512,7 +512,7 @@ namespace Frida {
tempdir));
FileUtils.chmod (helper.path, 0700);
#else
helper = new InstalledHelperFile.for_path (Config.FRIDA_HELPER_PATH);
helper = new InstalledHelperFile.for_path (Frida.helper_path);
#endif
}
+1 -1
View File
@@ -23,5 +23,5 @@ custom_target('core-devkit',
meson.current_build_dir(),
] + devkit_options,
install: true,
install_dir: get_option('libdir') / 'frida' / 'devkits' / 'core'
install_dir: get_option('libdir') / frida_libdir_name / 'devkits' / 'core'
)
+23 -13
View File
@@ -118,13 +118,7 @@ namespace Frida {
}
construct {
var builder = new VariantBuilder (VariantType.VARDICT);
builder.add ("{sv}", "format", new Variant.string ("rgba"));
builder.add ("{sv}", "width", new Variant.uint16 (16));
builder.add ("{sv}", "height", new Variant.uint16 (16));
var image = new Bytes (Base64.decode ("AAAAAAAAAAAAAAAAAAAAAP///0DS4pz/////MP///0D///9A////MNflqP////9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8QzN6Q/7vTa/+vy1L/r8tS/7vTa//O4JXv////EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1eSkz6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/9XkpM8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8vfjcKrIRf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+qyEX/8PXeYAAAAAAAAAAAAAAAAAAAAAAAAAAA////QNLinL+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/97qt6////9AAAAAAAAAAAAAAAAA2eatv7vTa//G2oP/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf/M3pD/u9Nr/9nmrb8AAAAAAAAAANLinP+kxDn/u9Nr/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/u9Nr/6TEOf/S4pz/AAAAAAAAAADS4pz/pMQ5/7vTa/+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/7vTa/+kxDn/0uKc/wAAAAAAAAAA0uKc/6TEOf+702v/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+702v/pMQ5/9LinP8AAAAAAAAAANLinP+kxDn/u9Nr/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/u9Nr/6TEOf/S4pz/AAAAAAAAAADO4JXvpMQ5/8DWd/+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/8DWd/+kxDn/zuCV7wAAAAAAAAAA7fPXUNLinIDl7sbfpMQ5/6TEOf+kxDn/pMQ5/6TEOf+kxDn/pMQ5/6TEOf/l7sbf0uKcgO3z11AAAAAAAAAAAAAAAAAAAAAA8PXeYMDWd/+qyEX/pMQ5/6/LUv+vy1L/pMQ5/6rIRf/A1nf/7fPXUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu9Nr/6TEOf/C2Hu/wth7v6TEOf+702v/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALvTa/+kxDn/wth7v8LYe7+kxDn/u9Nr/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc6LPPu9Nr/+HrvY/h672Pu9Nr/9nmrb8AAAAAAAAAAAAAAAAAAAAAAAAAAA=="));
builder.add ("{sv}", "image", Variant.new_from_data (new VariantType ("ay"), image.get_data (), true, image));
_icon = builder.end ();
_icon = make_provider_icon (Frida.Data.Icons.get_droidy_png_blob ().data);
}
public async void close (Cancellable? cancellable) throws IOError {
@@ -135,7 +129,18 @@ namespace Frida {
if (host_session != null)
throw new Error.INVALID_ARGUMENT ("Already created");
host_session = new DroidyHostSession (device_details, this);
string control_endpoint = ("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (DEFAULT_CONTROL_PORT);
if (options != null) {
Value? control_endpoint_val = options.map["control-endpoint"];
if (control_endpoint_val != null) {
if (!control_endpoint_val.holds (typeof (string)))
throw new Error.INVALID_ARGUMENT ("The control-endpoint option must be a string");
control_endpoint = control_endpoint_val.get_string ();
}
}
host_session = new DroidyHostSession (device_details, this, control_endpoint);
host_session.agent_session_detached.connect (on_agent_session_detached);
return host_session;
@@ -231,6 +236,11 @@ namespace Frida {
construct;
}
public string control_endpoint {
get;
construct;
}
private Promise<AndroidHelperClient>? helper_client_request;
private Droidy.ShellSession? helper_shell;
@@ -258,10 +268,12 @@ namespace Frida {
private const double MIN_SERVER_CHECK_INTERVAL = 5.0;
private const string GADGET_APP_ID = "re.frida.Gadget";
public DroidyHostSession (Droidy.DeviceDetails device_details, HostChannelProvider channel_provider) {
public DroidyHostSession (Droidy.DeviceDetails device_details, HostChannelProvider channel_provider,
string control_endpoint) {
Object (
device_details: device_details,
channel_provider: channel_provider
channel_provider: channel_provider,
control_endpoint: control_endpoint
);
}
@@ -838,9 +850,7 @@ namespace Frida {
DBusConnection? connection = null;
try {
var stream = yield channel_provider.open_channel (
("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (DEFAULT_CONTROL_PORT),
cancellable);
var stream = yield channel_provider.open_channel (control_endpoint, cancellable);
WebServiceTransport transport = PLAIN;
string? origin = null;
+28 -4
View File
@@ -1,7 +1,9 @@
#include "frida-core.h"
#include <gum/gum.h>
#ifdef HAVE_GIOOPENSSL
#if defined (HAVE_GIOAPPLE)
# include <gioapple.h>
#elif defined (HAVE_GIOOPENSSL)
# include <gioopenssl.h>
#endif
@@ -10,6 +12,11 @@ static GThread * main_thread;
static GMainLoop * main_loop;
static GMainContext * main_context;
#ifndef HAVE_EMBEDDED_ASSETS
extern void _frida_init_asset_paths (void);
extern void _frida_deinit_asset_paths (void);
#endif
static gpointer run_main_loop (gpointer data);
static gboolean dummy_callback (gpointer data);
static gboolean stop_main_loop (gpointer data);
@@ -25,18 +32,27 @@ frida_init_with_runtime (FridaRuntime rt)
{
static gsize frida_initialized = FALSE;
runtime = rt;
#ifdef HAVE_FRIDA_GLIB
g_thread_set_garbage_handler (frida_on_pending_garbage, NULL);
glib_init ();
#endif
if (g_once_init_enter (&frida_initialized))
{
runtime = rt;
#ifdef HAVE_FRIDA_GLIB
gio_init ();
#endif
gum_init ();
#ifndef HAVE_EMBEDDED_ASSETS
_frida_init_asset_paths ();
#endif
frida_error_quark (); /* Initialize early so GDBus will pick it up */
#ifdef HAVE_GIOOPENSSL
#if defined (HAVE_GIOAPPLE)
g_io_module_apple_register ();
#elif defined (HAVE_GIOOPENSSL)
g_io_module_openssl_register ();
#endif
@@ -109,12 +125,20 @@ frida_deinit (void)
frida_invalidate_dbus_context ();
gum_shutdown ();
#ifdef HAVE_FRIDA_GLIB
gio_shutdown ();
glib_shutdown ();
#endif
#ifndef HAVE_EMBEDDED_ASSETS
_frida_deinit_asset_paths ();
#endif
gum_deinit ();
#ifdef HAVE_FRIDA_GLIB
gio_deinit ();
glib_deinit ();
#endif
}
GMainContext *
+68 -1
View File
@@ -581,6 +581,40 @@ namespace Frida {
return close_request != null;
}
public void override_option (string name, Variant val) throws Error {
Value v;
switch (val.classify ()) {
case BOOLEAN:
v = Value (typeof (bool));
v.set_boolean (val.get_boolean ());
break;
case INT64:
v = Value (typeof (int64));
v.set_int64 (val.get_int64 ());
break;
case UINT64:
v = Value (typeof (uint64));
v.set_uint64 (val.get_uint64 ());
break;
case DOUBLE:
v = Value (typeof (double));
v.set_double (val.get_double ());
break;
case STRING:
v = Value (typeof (string));
v.set_string (val.get_string ());
break;
default:
throw new Error.INVALID_ARGUMENT ("Unsupported option type");
}
lock (host_session_options) {
if (host_session_options == null)
host_session_options = new HostSessionOptions ();
host_session_options.map[name] = v;
}
}
public async HashTable<string, Variant> query_system_parameters (Cancellable? cancellable = null) throws Error, IOError {
check_open ();
@@ -1332,7 +1366,11 @@ namespace Frida {
host_session_request = new Promise<HostSession> ();
try {
var session = yield provider.create (manager, host_session_options, cancellable);
HostSessionOptions? opts;
lock (host_session_options)
opts = (host_session_options != null) ? host_session_options.copy () : null;
var session = yield provider.create (manager, opts, cancellable);
attach_host_session (session);
current_host_session = session;
@@ -3583,11 +3621,39 @@ namespace Frida {
}
}
#if !HAVE_EMBEDDED_ASSETS
internal string helper_path;
internal string agent_path;
#if COMPILER_BACKEND_INSTALLED_LIBRARY || COMPILER_BACKEND_INSTALLED_EXECUTABLE
internal string compiler_backend_path;
#endif
public void _init_asset_paths () {
var location = AssetLocation.detect ();
helper_path = location.derive_asset_path ("<arch>", Config.FRIDA_HELPER_NAME);
agent_path = location.derive_asset_path ("<arch>", Config.FRIDA_AGENT_NAME);
#if COMPILER_BACKEND_INSTALLED_LIBRARY || COMPILER_BACKEND_INSTALLED_EXECUTABLE
compiler_backend_path = location.derive_plugin_path (Config.FRIDA_COMPILER_BACKEND_NAME);
#endif
}
public void _deinit_asset_paths () {
helper_path = null;
agent_path = null;
#if COMPILER_BACKEND_INSTALLED_LIBRARY || COMPILER_BACKEND_INSTALLED_EXECUTABLE
compiler_backend_path = null;
#endif
}
#endif
#if HAVE_FRIDA_GLIB
private Mutex gc_mutex;
private uint gc_generation = 0;
private bool gc_scheduled = false;
#endif
public void on_pending_garbage (void * data) {
#if HAVE_FRIDA_GLIB
gc_mutex.lock ();
gc_generation++;
bool already_scheduled = gc_scheduled;
@@ -3613,5 +3679,6 @@ namespace Frida {
return repeat;
});
#endif
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+62 -11
View File
@@ -57,7 +57,7 @@ namespace Frida {
}
public Variant? icon {
get { return device.icon; }
get { return device.icon ?? _network_icon; }
}
public HostSessionProviderKind kind {
@@ -70,6 +70,11 @@ namespace Frida {
private FruityHostSession? host_session;
private string _name;
private static Variant _network_icon;
static construct {
_network_icon = make_provider_icon (Frida.Data.Icons.get_fruity_network_png_blob ().data);
}
public FruityHostSessionProvider (Fruity.Device device) {
Object (device: device);
@@ -85,7 +90,21 @@ namespace Frida {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
host_session = new FruityHostSession (device);
uint16 control_port = DEFAULT_CONTROL_PORT;
if (options != null) {
Value? control_endpoint_val = options.map["control-endpoint"];
if (control_endpoint_val != null) {
if (!control_endpoint_val.holds (typeof (string)))
throw new Error.INVALID_ARGUMENT ("The control-endpoint option must be a string");
unowned string control_endpoint = control_endpoint_val.get_string ();
if (!control_endpoint.has_prefix ("tcp:"))
throw new Error.INVALID_ARGUMENT ("The control-endpoint must be TCP-flavored");
control_port = (uint16) uint.parse (control_endpoint[4:]);
}
}
host_session = new FruityHostSession (device, control_port);
host_session.agent_session_detached.connect (on_agent_session_detached);
return host_session;
@@ -172,6 +191,11 @@ namespace Frida {
construct;
}
public uint16 control_port {
get;
construct;
}
private Gee.Map<uint, Fruity.ProcessControlService> pids_launched_without_lldb =
new Gee.HashMap<uint, Fruity.ProcessControlService> ();
private Gee.HashMap<uint, LLDBSession> lldb_sessions = new Gee.HashMap<uint, LLDBSession> ();
@@ -205,8 +229,8 @@ namespace Frida {
DEBUGSERVER_ENDPOINT_LEGACY,
};
public FruityHostSession (Fruity.Device device) {
Object (device: device);
public FruityHostSession (Fruity.Device device, uint16 control_port) {
Object (device: device, control_port: control_port);
}
construct {
@@ -959,6 +983,7 @@ namespace Frida {
if (lldb_session != null) {
var gadget_details = yield lldb_session.query_gadget_details (cancellable);
lldb_session.attach_count++;
return yield attach_via_gadget (pid, options, gadget_details, cancellable);
}
@@ -985,6 +1010,7 @@ namespace Frida {
var gadget_details = yield lldb_session.query_gadget_details (cancellable);
lldb_session.attach_count++;
return yield attach_via_gadget (pid, options, gadget_details, cancellable);
}
@@ -1232,11 +1258,14 @@ namespace Frida {
var lldb_session = lldb_sessions[entry.pid];
if (lldb_session != null) {
remove_lldb_session (lldb_session);
if (reason == SessionDetachReason.APPLICATION_REQUESTED)
yield entry.detach_lldb (lldb_session, io_cancellable);
else
yield lldb_session.close (io_cancellable);
lldb_session.attach_count--;
if (lldb_session.attach_count == 0) {
remove_lldb_session (lldb_session);
if (reason == SessionDetachReason.APPLICATION_REQUESTED)
yield entry.detach_lldb (lldb_session, io_cancellable);
else
yield lldb_session.close (io_cancellable);
}
}
gadget_entries.unset (id);
@@ -1383,7 +1412,7 @@ namespace Frida {
: Fruity.OpenTcpChannelFlags.ALLOW_TUNNEL;
try {
return yield device.open_tcp_channel (DEFAULT_CONTROL_PORT.to_string (), open_flags, cancellable);
return yield device.open_tcp_channel (control_port.to_string (), open_flags, cancellable);
} catch (Error e) {
pending_error = e;
if (!(e is Error.SERVER_NOT_RUNNING))
@@ -1500,6 +1529,8 @@ namespace Frida {
construct;
}
public uint attach_count = 0;
private Promise<Fruity.Injector.GadgetDetails>? gadget_request;
private ulong exception_handler = 0;
private string? main_thread_id;
@@ -2859,7 +2890,15 @@ namespace Frida {
foreach (var pid in pids_to_trace) {
if (target_pids.has_key (pid))
continue;
var sig = yield info_service.query_symbolicator_signature (pid, cancellable);
Fruity.CsSignature sig;
try {
sig = yield info_service.query_symbolicator_signature (pid, cancellable);
} catch (Error e) {
sig = new Fruity.CsSignature ();
sig.pid = pid;
}
target_pids[pid] = sig;
}
l.free ();
@@ -2912,6 +2951,18 @@ namespace Frida {
return reply.end ();
}
if (type == "include-syscalls") {
reader.read_member ("native");
var nrs = read_int32_array (reader);
reader.end_member ();
var l = new MutexLocker (mutex);
excluded_syscalls.remove_all_array (nrs);
l.free ();
return reply.end ();
}
throw new Error.INVALID_ARGUMENT ("Unsupported request type: %s", type);
}
+22 -9
View File
@@ -186,8 +186,14 @@ namespace Frida.Fruity.Injector {
}
private async void teardown (Cancellable? cancellable) throws GLib.Error {
yield lldb.deallocate (scratch_page, cancellable);
yield lldb.deallocate (jit_page, cancellable);
try {
yield lldb.deallocate (scratch_page, cancellable);
} catch (Error e) {
}
try {
yield lldb.deallocate (jit_page, cancellable);
} catch (Error e) {
}
yield restore_main_thread_state (cancellable);
}
@@ -893,11 +899,14 @@ namespace Frida.Fruity.Injector {
uint64 initializer = (notify_objc_init != null) ? notify_objc_init : libdyld_initialize;
GDB.Breakpoint init_breakpoint = yield lldb.add_breakpoint (SOFT, initializer, 4, cancellable);
GDB.Breakpoint? restart_breakpoint = null;
uint64? restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN6mach_o6HeaderEPK15DyldSharedCachePv"];
if (restart_with_dyld_in_cache == null)
restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPK15DyldSharedCachePv"];
if (restart_with_dyld_in_cache == null)
restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPv"];
uint64? restart_with_dyld_in_cache = null;
foreach (var e in dyld_symbols.entries) {
if (e.key.contains ("restartWithDyldInCache")) {
restart_with_dyld_in_cache = e.value;
break;
}
}
if (restart_with_dyld_in_cache != null)
restart_breakpoint = yield lldb.add_breakpoint (SOFT, restart_with_dyld_in_cache, 4, cancellable);
@@ -1374,8 +1383,12 @@ namespace Frida.Fruity.Injector {
}
}
if (state != scratch_page)
yield lldb.deallocate (state, cancellable);
if (state != scratch_page) {
try {
yield lldb.deallocate (state, cancellable);
} catch (Error e) {
}
}
return new SymbolSet (symbols);
}
+2 -2
View File
@@ -151,7 +151,7 @@ namespace Frida.LLDB {
Json.Reader reader;
try {
reader = new Json.Reader (Json.from_string (response.payload));
reader = make_json_reader (response.payload);
} catch (GLib.Error e) {
throw new Error.PROTOCOL ("Invalid response");
}
@@ -194,7 +194,7 @@ namespace Frida.LLDB {
Json.Reader reader;
try {
reader = new Json.Reader (Json.from_string (response.payload));
reader = make_json_reader (response.payload);
} catch (GLib.Error e) {
throw new Error.PROTOCOL ("Invalid response");
}
File diff suppressed because it is too large. Load diff
+9 -3
View File
@@ -1592,8 +1592,11 @@ namespace Frida.Fruity {
if (t == typeof (string))
return str_hash (v.get_string ());
if (t == typeof (Bytes) || t == typeof (PlistDict) || t == typeof (PlistArray))
return (uint) v.get_object ();
if (t == typeof (Bytes))
return ((Bytes) v.get_boxed ()).hash ();
if (t == typeof (PlistDict) || t == typeof (PlistArray))
return (uint) (size_t) v.get_object ();
if (t == typeof (PlistUid))
return (uint) ((PlistUid) v.get_object ()).uid;
@@ -1623,7 +1626,10 @@ namespace Frida.Fruity {
if (t == typeof (string))
return a.get_string () == b.get_string ();
if (t == typeof (Bytes) || t == typeof (PlistDict) || t == typeof (PlistArray))
if (t == typeof (Bytes))
return ((Bytes) a.get_boxed ()).compare ((Bytes) b.get_boxed ()) == 0;
if (t == typeof (PlistDict) || t == typeof (PlistArray))
return a.get_object () == b.get_object ();
if (t == typeof (PlistNull))
+356
View File
@@ -0,0 +1,356 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public sealed class TlsPskClientStream : IOStream, AsyncInitable {
public IOStream base_stream {
get;
construct;
}
public string psk_identity {
get;
construct;
}
public Bytes psk {
get;
construct;
}
public override InputStream input_stream {
get {
return _input_stream;
}
}
public override OutputStream output_stream {
get {
return _output_stream;
}
}
private OpenSSL.SSLContext ssl_ctx;
private OpenSSL.SSL ssl;
private void * rbio;
private void * wbio;
private PollableInputStream base_pollable_input;
private PollableOutputStream base_pollable_output;
private TlsPskInputStream _input_stream;
private TlsPskOutputStream _output_stream;
private const int IO_CHUNK_SIZE = 64 * 1024;
private const string CIPHER_LIST =
"PSK-AES128-GCM-SHA256:PSK-AES256-GCM-SHA384:PSK-AES256-CBC-SHA384:PSK-AES128-CBC-SHA256";
public static async TlsPskClientStream open (IOStream base_stream, string psk_identity, Bytes psk,
Cancellable? cancellable) throws Error, IOError {
var stream = new TlsPskClientStream (base_stream, psk_identity, psk);
try {
yield stream.init_async (Priority.DEFAULT, cancellable);
} catch (GLib.Error e) {
throw_api_error (e);
}
return stream;
}
private TlsPskClientStream (IOStream base_stream, string psk_identity, Bytes psk) {
Object (base_stream: base_stream, psk_identity: psk_identity, psk: psk);
}
construct {
var raw_input = base_stream.get_input_stream ();
var raw_output = base_stream.get_output_stream ();
assert (raw_input is PollableInputStream && raw_output is PollableOutputStream);
base_pollable_input = (PollableInputStream) raw_input;
base_pollable_output = (PollableOutputStream) raw_output;
_input_stream = new TlsPskInputStream (this);
_output_stream = new TlsPskOutputStream (this);
ssl_ctx = new OpenSSL.SSLContext (OpenSSL.SSLMethod.tls_client ());
ssl = new OpenSSL.SSL (ssl_ctx);
ssl.set_app_data (this);
ssl.set_connect_state ();
ssl.set_cipher_list (CIPHER_LIST);
ssl.set_psk_client_callback ((ssl, hint, identity, psk) => {
TlsPskClientStream * self = ssl.get_app_data ();
unowned uint8[] ident_bytes = self->psk_identity.data;
Memory.copy (identity, ident_bytes, ident_bytes.length);
unowned uint8[] key = self->psk.get_data ();
Memory.copy (psk, key, key.length);
return key.length;
});
rbio = OpenSSL.BasicIO.raw_new (OpenSSL.BasicIOMethod.memory ());
wbio = OpenSSL.BasicIO.raw_new (OpenSSL.BasicIOMethod.memory ());
ssl.set_bio (rbio, wbio);
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
while (true) {
int ret = ssl.do_handshake ();
yield handshake_drain_wbio (cancellable);
if (ret == 1)
return true;
int err = ssl.get_error (ret);
if (err == OpenSSL.SSLErrorCode.WANT_READ) {
if (!yield handshake_feed_rbio (cancellable))
throw new Error.TRANSPORT ("Connection closed during TLS handshake");
} else if (err != OpenSSL.SSLErrorCode.WANT_WRITE) {
throw new Error.PROTOCOL ("TLS handshake failed: %s", openssl_error_string ());
}
}
}
private async void handshake_drain_wbio (Cancellable? cancellable) throws Error, IOError {
while (OpenSSL.BasicIO.raw_ctrl_pending (wbio) > 0) {
var buf = new uint8[IO_CHUNK_SIZE];
int n = OpenSSL.BasicIO.raw_read (wbio, buf);
try {
yield ((OutputStream) base_pollable_output).write_all_async (buf[0:n], Priority.DEFAULT,
cancellable, null);
} catch (GLib.Error e) {
throw new Error.TRANSPORT ("%s", e.message);
}
}
}
private async bool handshake_feed_rbio (Cancellable? cancellable) throws Error, IOError {
var buf = new uint8[IO_CHUNK_SIZE];
ssize_t n;
try {
n = yield ((InputStream) base_pollable_input).read_async (buf, Priority.DEFAULT, cancellable);
} catch (GLib.Error e) {
throw new Error.TRANSPORT ("%s", e.message);
}
if (n == 0)
return false;
OpenSSL.BasicIO.raw_write (rbio, buf[:n]);
return true;
}
internal ssize_t do_read_nonblocking (uint8[] buffer) throws GLib.Error {
while (true) {
int n = ssl.read (buffer);
if (n > 0)
return n;
int err = ssl.get_error (n);
if (err == OpenSSL.SSLErrorCode.ZERO_RETURN)
return 0;
if (err != OpenSSL.SSLErrorCode.WANT_READ)
throw new IOError.FAILED ("TLS read error: %s", openssl_error_string ());
var tmp = new uint8[IO_CHUNK_SIZE];
ssize_t r = base_pollable_input.read_nonblocking (tmp);
if (r == 0)
return 0;
OpenSSL.BasicIO.raw_write (rbio, tmp[:r]);
}
}
internal ssize_t do_read_blocking (uint8[] buffer, Cancellable? cancellable) throws IOError {
while (true) {
int n = ssl.read (buffer);
if (n > 0)
return n;
int err = ssl.get_error (n);
if (err == OpenSSL.SSLErrorCode.ZERO_RETURN)
return 0;
if (err != OpenSSL.SSLErrorCode.WANT_READ)
throw new IOError.FAILED ("TLS read error: %s", openssl_error_string ());
var tmp = new uint8[IO_CHUNK_SIZE];
ssize_t r = ((InputStream) base_pollable_input).read (tmp, cancellable);
if (r == 0)
return 0;
OpenSSL.BasicIO.raw_write (rbio, tmp[:r]);
}
}
internal bool do_is_readable () {
return ssl.pending () > 0 || base_pollable_input.is_readable ();
}
internal PollableSource do_create_input_source (Cancellable? cancellable) {
return new PollableSource.full (_input_stream,
base_pollable_input.create_source (cancellable), cancellable);
}
internal ssize_t do_write_nonblocking (uint8[] buffer) throws GLib.Error {
if (!flush_wbio_nonblocking ())
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
int n = ssl.write (buffer);
if (n <= 0) {
int err = ssl.get_error (n);
if (err == OpenSSL.SSLErrorCode.WANT_WRITE || err == OpenSSL.SSLErrorCode.WANT_READ)
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
throw new IOError.FAILED ("TLS write error: %s", openssl_error_string ());
}
flush_wbio_nonblocking ();
return n;
}
internal ssize_t do_write_blocking (uint8[] buffer, Cancellable? cancellable) throws IOError {
try {
flush_wbio_blocking (cancellable);
} catch (GLib.Error e) {
throw new IOError.FAILED ("%s", e.message);
}
int n = ssl.write (buffer);
if (n <= 0)
throw new IOError.FAILED ("TLS write error: %s", openssl_error_string ());
try {
flush_wbio_blocking (cancellable);
} catch (GLib.Error e) {
throw new IOError.FAILED ("%s", e.message);
}
return n;
}
internal bool do_is_writable () {
return base_pollable_output.is_writable ();
}
internal PollableSource do_create_output_source (Cancellable? cancellable) {
return new PollableSource.full (_output_stream, base_pollable_output.create_source (cancellable), cancellable);
}
private bool flush_wbio_nonblocking () throws GLib.Error {
while (true) {
unowned uint8[] data;
long len = OpenSSL.BasicIO.raw_get_mem_data (wbio, out data);
if (len == 0)
return true;
data.length = (int) len;
ssize_t written;
try {
written = base_pollable_output.write_nonblocking (data);
} catch (IOError.WOULD_BLOCK e) {
return false;
}
var scratch = new uint8[written];
OpenSSL.BasicIO.raw_read (wbio, scratch);
}
}
private void flush_wbio_blocking (Cancellable? cancellable) throws GLib.Error {
while (true) {
unowned uint8[] data;
long len = OpenSSL.BasicIO.raw_get_mem_data (wbio, out data);
if (len == 0)
return;
data.length = (int) len;
size_t written;
((OutputStream) base_pollable_output).write_all (data, out written, cancellable);
var scratch = new uint8[written];
OpenSSL.BasicIO.raw_read (wbio, scratch);
}
}
private static string openssl_error_string () {
var buf = new char[256];
OpenSSL.Error.error_string_n (OpenSSL.Error.get_error (), buf);
return (string) buf;
}
private class TlsPskInputStream : InputStream, PollableInputStream {
public weak TlsPskClientStream parent {
get;
construct;
}
public TlsPskInputStream (TlsPskClientStream parent) {
Object (parent: parent);
}
public override ssize_t read (uint8[] buffer, Cancellable? cancellable = null) throws IOError {
return parent.do_read_blocking (buffer, cancellable);
}
public override bool close (Cancellable? cancellable = null) throws IOError {
return true;
}
public bool can_poll () {
return true;
}
public bool is_readable () {
return parent.do_is_readable ();
}
public PollableSource create_source (Cancellable? cancellable) {
return parent.do_create_input_source (cancellable);
}
public ssize_t read_nonblocking_fn (uint8[] buffer) throws GLib.Error {
return parent.do_read_nonblocking (buffer);
}
}
private class TlsPskOutputStream : OutputStream, PollableOutputStream {
public weak TlsPskClientStream parent {
get;
construct;
}
public TlsPskOutputStream (TlsPskClientStream parent) {
Object (parent: parent);
}
public override ssize_t write (uint8[] buffer, Cancellable? cancellable = null) throws IOError {
return parent.do_write_blocking (buffer, cancellable);
}
public override bool close (Cancellable? cancellable = null) throws IOError {
return true;
}
public override bool flush (Cancellable? cancellable = null) throws GLib.Error {
return true;
}
public bool can_poll () {
return true;
}
public bool is_writable () {
return parent.do_is_writable ();
}
public PollableSource create_source (Cancellable? cancellable) {
return parent.do_create_output_source (cancellable);
}
public ssize_t write_nonblocking_fn (uint8[]? buffer) throws GLib.Error {
return parent.do_write_nonblocking (buffer);
}
public PollableReturn writev_nonblocking_fn (OutputVector[] vectors, out size_t bytes_written) throws GLib.Error {
assert_not_reached ();
}
}
}
}
File diff suppressed because it is too large. Load diff
+10 -2883
View File
File diff suppressed because it is too large. Load diff
+20 -1
View File
@@ -183,6 +183,19 @@ namespace Frida {
public abstract void unlink_service_session (HostSession host_session, ServiceSessionId id);
}
private const uint16 PROVIDER_ICON_SIZE = 96;
internal Variant make_provider_icon (uint8[] png) {
var image = new Bytes.static (png);
var builder = new VariantBuilder (VariantType.VARDICT);
builder.add ("{sv}", "format", new Variant.string ("png"));
builder.add ("{sv}", "width", new Variant.uint16 (PROVIDER_ICON_SIZE));
builder.add ("{sv}", "height", new Variant.uint16 (PROVIDER_ICON_SIZE));
builder.add ("{sv}", "image", Variant.new_from_data (new VariantType ("ay"),
image.get_data (), true, image));
return builder.end ();
}
public interface HostSessionConnection : Object {
public abstract HostSession host_session {
get;
@@ -231,6 +244,12 @@ namespace Frida {
set;
default = new Gee.HashMap<string, Value?> ();
}
public HostSessionOptions copy () {
var opts = new HostSessionOptions ();
opts.map.set_all (map);
return opts;
}
}
public interface HostChannelProvider : Object {
@@ -303,7 +322,7 @@ namespace Frida {
}
protected virtual Variant? load_icon () {
return null;
return make_provider_icon (Frida.Data.Icons.get_local_png_blob ().data);
}
public async void close (Cancellable? cancellable) throws IOError {
+2
View File
@@ -0,0 +1,2 @@
[resource-compiler]
namespace = Frida.Data.Icons
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

+38 -3
View File
@@ -12,6 +12,7 @@ namespace Frida {
private Gee.Map<uint, ExecTransitionSession> exec_transitions = new Gee.HashMap<uint, ExecTransitionSession> ();
private Gee.Map<uint, AwaitExecTransitionTask> exec_waiters = new Gee.HashMap<uint, AwaitExecTransitionTask> ();
private Gee.Map<uint, PausedSyscallSession> paused_syscalls = new Gee.HashMap<uint, PausedSyscallSession> ();
private Gee.Map<uint, InjectSession> suspended_by_inject = new Gee.HashMap<uint, InjectSession> ();
private Gee.Map<uint, RemoteAgent> agents = new Gee.HashMap<uint, RemoteAgent> ();
private Gee.Map<uint, Source> agent_expiries = new Gee.HashMap<uint, Source> ();
private Gee.Map<uint, Gee.Queue<TaskEntry>> task_queues = new Gee.HashMap<uint, Gee.Queue<TaskEntry>> ();
@@ -276,6 +277,12 @@ namespace Frida {
return null;
}
InjectSession inject_session;
if (backend.suspended_by_inject.unset (pid, out inject_session)) {
inject_session.close ();
return null;
}
SpawnedProcess? p = backend.spawned_processes[pid];
if (p != null) {
p.resume ();
@@ -313,7 +320,10 @@ namespace Frida {
yield pss.interrupt (cancellable);
var session = yield InjectSession.open (pid, cancellable);
RemoteAgent agent = yield session.inject (spec, cancellable);
session.close ();
if (session.was_group_stopped)
backend.suspended_by_inject[pid] = session;
else
session.close ();
return agent;
}
}
@@ -1819,6 +1829,11 @@ namespace Frida {
}
}
public bool was_group_stopped {
get;
private set;
}
public enum InitBehavior {
INTERRUPT,
CONTINUE
@@ -1865,6 +1880,8 @@ namespace Frida {
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
PtraceOptions options = PtraceOptions.TRACESYSGOOD | PtraceOptions.TRACEEXEC;
bool was_stopped = seize_supported && query_process_state (tid) == 'T';
PtraceRequest req;
long res;
if (seize_supported) {
@@ -1890,8 +1907,14 @@ namespace Frida {
attach_state = ATTACHED;
if (seize_supported) {
ptrace (INTERRUPT, tid);
yield wait_for_signal (TRAP, cancellable);
if (was_stopped) {
was_group_stopped = true;
yield wait_for_next_stop (cancellable);
Posix.kill ((Posix.pid_t) pid, Posix.Signal.CONT);
} else {
ptrace (INTERRUPT, tid);
yield wait_for_signal (TRAP, cancellable);
}
} else {
yield wait_for_signal (STOP, cancellable);
ptrace (SETOPTIONS, tid, null, (void *) options);
@@ -1920,6 +1943,18 @@ namespace Frida {
return true;
}
private static char query_process_state (uint tid) {
try {
string stat;
FileUtils.get_contents ("/proc/%u/stat".printf (tid), out stat);
int paren_end = stat.last_index_of_char (')');
if (paren_end != -1 && paren_end + 2 < stat.length)
return stat[paren_end + 2];
} catch (FileError e) {
}
return '?';
}
public void close () throws Error {
if (attach_state == ATTACHED) {
ptrace (DETACH, tid);
+17 -1
View File
@@ -36,6 +36,18 @@ namespace Frida {
_resource_store = null;
}
public async void preload (Cancellable? cancellable) throws IOError {
try {
yield obtain_for_64bit (cancellable);
} catch (Error e) {
}
try {
yield obtain_for_32bit (cancellable);
} catch (Error e) {
}
}
private ResourceStore get_resource_store () throws Error {
if (_resource_store == null)
_resource_store = new ResourceStore (tempdir);
@@ -189,6 +201,10 @@ namespace Frida {
private async LinuxHelper obtain_for_32bit (Cancellable? cancellable) throws Error, IOError {
if (factory32 == null) {
#if ANDROID
if (sizeof (void *) == 8 && _query_android_system_property ("ro.product.cpu.abilist32") == "")
throw new Error.NOT_SUPPORTED ("Android system does not support 32-bit processes");
#endif
var store = get_resource_store ();
if (sizeof (void *) != 4 && store.helper32 == null)
throw new Error.NOT_SUPPORTED ("Unable to handle 32-bit processes due to build configuration");
@@ -634,7 +650,7 @@ namespace Frida {
if (blob64.data.length > 0)
helper64 = make_temporary_helper ("frida-helper-64", blob64.data);
#else
var tpl = PathTemplate (Config.FRIDA_HELPER_PATH);
var tpl = PathTemplate (Frida.helper_path);
string path = tpl.expand ((sizeof (void *) == 8) ? "32" : "64");
HelperFile file = new InstalledHelperFile.for_path (path);
if (sizeof (void *) == 8)
+1
View File
@@ -36,6 +36,7 @@ bpf_noarch_progs := \
activity-sampler \
$(NULL)
bpf_arch_progs := \
spawn-gater \
syscall-tracer \
$(NULL)
bpf_noarch_pairs := \
Binary file not shown.
Binary file not shown.
Binary file not shown.
+55
View File
@@ -0,0 +1,55 @@
#include "frida-linux-syscalls.h"
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#define SIGSTOP 19
#define MAX_FILENAME 256
typedef struct _ExecveEvent ExecveEvent;
struct _ExecveEvent
{
int pid;
char command[MAX_FILENAME];
};
struct
{
__uint (type, BPF_MAP_TYPE_RINGBUF);
__uint (max_entries, 1 << 22);
}
events SEC (".maps");
struct trace_event_raw_sys_enter
{
__u64 unused;
long id;
unsigned long args[6];
};
SEC ("tracepoint/raw_syscalls/sys_enter")
int
on_execve_enter (struct trace_event_raw_sys_enter * ctx)
{
__s32 nr = (__s32) ctx->id;
if (nr != FRIDA_LINUX_SYSCALL_EXECVE)
return 0;
ExecveEvent * e = bpf_ringbuf_reserve (&events, sizeof (ExecveEvent), 0);
if (e == NULL)
return 0;
e->pid = bpf_get_current_pid_tgid () >> 32;
const char * filename = (const char *) ctx->args[0];
bpf_probe_read_user_str (e->command, sizeof (e->command), filename);
bpf_ringbuf_submit (e, 0);
bpf_send_signal (SIGSTOP);
return 0;
}
char LICENSE[] SEC ("license") = "Dual BSD/GPL";
+122 -21
View File
@@ -38,6 +38,8 @@ namespace Frida {
private CrashMonitor? crash_monitor;
#endif
private SpawnGater? spawn_gater;
#if !ANDROID
private ApplicationEnumerator application_enumerator = new ApplicationEnumerator ();
#endif
@@ -75,8 +77,8 @@ namespace Frida {
#if ANDROID
robo_launcher = new RoboLauncher (this, io_cancellable);
robo_launcher.spawn_added.connect (on_robo_launcher_spawn_added);
robo_launcher.spawn_removed.connect (on_robo_launcher_spawn_removed);
robo_launcher.spawn_added.connect (on_spawn_added);
robo_launcher.spawn_removed.connect (on_spawn_removed);
if (report_crashes) {
crash_monitor = new CrashMonitor ();
@@ -95,8 +97,8 @@ namespace Frida {
public override async void close (Cancellable? cancellable) throws IOError {
#if ANDROID
yield robo_launcher.close (cancellable);
robo_launcher.spawn_added.disconnect (on_robo_launcher_spawn_added);
robo_launcher.spawn_removed.disconnect (on_robo_launcher_spawn_removed);
robo_launcher.spawn_added.disconnect (on_spawn_added);
robo_launcher.spawn_removed.disconnect (on_spawn_removed);
if (android_helper_request != null) {
try {
@@ -107,6 +109,13 @@ namespace Frida {
}
#endif
if (spawn_gater != null) {
spawn_gater.spawn_added.disconnect (on_spawn_added);
spawn_gater.spawn_removed.disconnect (on_spawn_removed);
spawn_gater.stop ();
spawn_gater = null;
}
yield base.close (cancellable);
#if ANDROID
@@ -153,7 +162,7 @@ namespace Frida {
tpl = agent.get_path_template ();
}
#else
tpl = PathTemplate (Config.FRIDA_AGENT_PATH);
tpl = PathTemplate (Frida.agent_path);
#endif
if (path == null)
path = tpl.expand (arch_name);
@@ -268,27 +277,58 @@ namespace Frida {
}
public override async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
var helper_process = helper as LinuxHelperProcess;
if (helper_process != null)
yield helper_process.preload (cancellable);
var gater = ensure_spawn_gater ();
if (gater.state == STOPPED) {
#if ANDROID
try {
gater.start ();
} catch (Error e) {
}
#else
gater.start ();
#endif
}
#if ANDROID
yield robo_launcher.enable_spawn_gating (cancellable);
#else
throw new Error.NOT_SUPPORTED ("Not yet supported on this OS");
#endif
}
public override async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
spawn_gater?.stop ();
#if ANDROID
yield robo_launcher.disable_spawn_gating (cancellable);
#else
throw new Error.NOT_SUPPORTED ("Not yet supported on this OS");
#endif
}
private SpawnGater ensure_spawn_gater () {
if (spawn_gater == null) {
spawn_gater = new SpawnGater (helper);
spawn_gater.spawn_added.connect (on_spawn_added);
spawn_gater.spawn_removed.connect (on_spawn_removed);
}
return spawn_gater;
}
public override async HostSpawnInfo[] enumerate_pending_spawn (Cancellable? cancellable) throws Error, IOError {
var result = new HostSpawnInfo[0];
if (spawn_gater != null) {
foreach (var spawn in spawn_gater.enumerate_pending_spawn ())
result += spawn;
}
#if ANDROID
return robo_launcher.enumerate_pending_spawn ();
#else
throw new Error.NOT_SUPPORTED ("Not yet supported on this OS");
foreach (var spawn in robo_launcher.enumerate_pending_spawn ())
result += spawn;
#endif
return result;
}
public override async uint spawn (string program, HostSpawnOptions options, Cancellable? cancellable)
@@ -322,6 +362,9 @@ namespace Frida {
}
protected override async void perform_resume (uint pid, Cancellable? cancellable) throws Error, IOError {
if (spawn_gater != null && spawn_gater.try_resume (pid))
return;
#if ANDROID
if (robo_launcher.try_resume (pid))
return;
@@ -351,7 +394,7 @@ namespace Frida {
#if HAVE_EMBEDDED_ASSETS
id = yield linjector.inject_library_resource (pid, agent, entrypoint, parameters, features, cancellable);
#else
id = yield linjector.inject_library_file_with_template (pid, PathTemplate (Config.FRIDA_AGENT_PATH), entrypoint,
id = yield linjector.inject_library_file_with_template (pid, PathTemplate (Frida.agent_path), entrypoint,
parameters, features, cancellable);
#endif
injectee_by_pid[pid] = id;
@@ -444,14 +487,6 @@ namespace Frida {
}
}
private void on_robo_launcher_spawn_added (HostSpawnInfo info) {
spawn_added (info);
}
private void on_robo_launcher_spawn_removed (HostSpawnInfo info) {
spawn_removed (info);
}
protected override async CrashInfo? try_collect_crash (uint pid, Cancellable? cancellable) throws IOError {
if (crash_monitor == null)
return null;
@@ -471,6 +506,14 @@ namespace Frida {
}
#endif
private void on_spawn_added (HostSpawnInfo info) {
spawn_added (info);
}
private void on_spawn_removed (HostSpawnInfo info) {
spawn_removed (info);
}
private void on_output (uint pid, int fd, uint8[] data) {
output (pid, fd, data);
}
@@ -613,6 +656,17 @@ namespace Frida {
vm_mod = dlopen_ext (vm_soname, LAZY | LOCAL, info);
} else {
vm_mod = Android.Module.open (vm_soname, LAZY | LOCAL);
if (vm_mod == null) {
var libdir = ((sizeof (void *) == 8) ? "lib64" : "lib");
ensure_directories_on_ld_library_path ({
"/apex/com.android.runtime/" + libdir,
"/apex/com.android.art/" + libdir,
"/apex/com.android.conscrypt/" + libdir
});
vm_mod = Android.Module.open (vm_soname, LAZY | LOCAL);
}
}
if (vm_mod == null)
throw new Error.NOT_SUPPORTED ("Unable to load %s: %s", vm_soname, Android.Module.get_last_error ());
@@ -771,6 +825,31 @@ namespace Frida {
throw e;
}
private static void ensure_directories_on_ld_library_path (string[] dirs) {
var linker = Gum.Android.get_linker_module ();
var get_path = (GetLdLibraryPathFunc) linker.find_export_by_name ("__loader_android_get_LD_LIBRARY_PATH");
var update_path = (UpdateLdLibraryPathFunc) linker.find_export_by_name ("__loader_android_update_LD_LIBRARY_PATH");
if (get_path == null || update_path == null)
return;
var path_buf = new char[4096];
get_path (path_buf);
var entries = new Gee.ArrayList<string> ();
foreach (unowned string dir in dirs)
entries.add (dir);
unowned string old_path = (string) path_buf;
foreach (var dir in old_path.split (":")) {
if (!(dir in dirs))
entries.add (dir);
}
string new_path = string.joinv (":", entries.to_array ());
update_path (new_path);
}
[CCode (has_target = false)]
private delegate Android.Module? DlopenExtFunc (string filename, Android.DlOpenFlags flags, Android.DlExtInfo info);
@@ -786,6 +865,12 @@ namespace Frida {
[CCode (has_target = false)]
private delegate JNI.Result RegisterFrameworkNativesLegacyFunc (void * env, JNI.ClassRef * clazz);
[CCode (has_target = false)]
private delegate void GetLdLibraryPathFunc (char[] buf);
[CCode (has_target = false)]
private delegate void UpdateLdLibraryPathFunc (string path);
}
private class SigchainCompat : Object {
@@ -2841,6 +2926,22 @@ namespace Frida {
return reply.end ();
}
if (type == "include-syscalls") {
if (reader.has_member ("native")) {
reader.read_member ("native");
foreach_uint32 (reader, nr => tracer.include_syscall (NATIVE, nr));
reader.end_member ();
}
if (reader.has_member ("compat32")) {
reader.read_member ("compat32");
foreach_uint32 (reader, nr => tracer.include_syscall (COMPAT32, nr));
reader.end_member ();
}
return reply.end ();
}
throw new Error.INVALID_ARGUMENT ("Unsupported request type: %s", type);
}
+154
View File
@@ -0,0 +1,154 @@
namespace Frida {
public sealed class SpawnGater : Object {
public signal void spawn_added (HostSpawnInfo info);
public signal void spawn_removed (HostSpawnInfo info);
public LinuxHelper helper {
get;
construct;
}
public State state {
get {
return _state;
}
}
public enum State {
STOPPED,
STARTED,
}
private State _state = STOPPED;
private Gee.Map<uint, HostSpawnInfo?> pending_spawn = new Gee.HashMap<uint, HostSpawnInfo?> ();
private BpfRingbufReader? events_reader;
private IOChannel? events_channel;
private Source? events_source;
private Gee.Collection<BpfLink> links = new Gee.ArrayList<BpfLink> ();
private const size_t RINGBUF_SIZE = 1U << 22;
private const size_t MAX_FILENAME = 256;
public SpawnGater (LinuxHelper helper) {
Object (helper: helper);
}
protected override void dispose () {
stop ();
base.dispose ();
}
public void start () throws Error {
assert (state == STOPPED);
var obj = BpfObject.open ("spawn-gater.elf", Frida.Data.HelperBackend.get_spawn_gater_elf_blob ().data);
var events = obj.maps.get_by_name ("events");
obj.prepare ();
events_reader = new BpfRingbufReader (events);
obj.load ();
foreach (var program in obj.programs) {
var link = program.attach ();
links.add (link);
}
events_channel = new IOChannel.unix_new (events.fd);
var src = new IOSource (events_channel, IOCondition.IN);
var state = new EventsWatchState (this);
src.set_callback (state.on_ready);
src.attach (MainContext.get_thread_default ());
events_source = src;
_state = STARTED;
}
public void stop () {
if (_state == STOPPED)
return;
links.clear ();
events_source?.destroy ();
events_source = null;
process_pending_events ();
foreach (var spawn in pending_spawn.values) {
spawn_removed (spawn);
perform_resume.begin (spawn.pid);
}
pending_spawn.clear ();
events_channel = null;
events_reader = null;
_state = STOPPED;
}
public HostSpawnInfo[] enumerate_pending_spawn () {
var result = new HostSpawnInfo[pending_spawn.size];
var index = 0;
foreach (var spawn in pending_spawn.values)
result[index++] = spawn;
return result;
}
public bool try_resume (uint pid) {
HostSpawnInfo? spawn;
if (!pending_spawn.unset (pid, out spawn))
return false;
spawn_removed (spawn);
perform_resume.begin (pid);
return true;
}
private async void perform_resume (uint pid) {
try {
yield helper.resume (pid, null);
} catch (GLib.Error e) {
if (e is Error.INVALID_ARGUMENT)
Posix.kill ((Posix.pid_t) pid, Posix.Signal.CONT);
}
}
private void process_pending_events () {
events_reader.drain (payload => {
assert (payload.length == sizeof (ExecveEvent));
handle_event (payload);
return CONTINUE;
});
}
private void handle_event (ExecveEvent * e) {
var info = HostSpawnInfo (e->pid, (string) e->command);
pending_spawn[e->pid] = info;
spawn_added (info);
}
private struct ExecveEvent {
public int pid;
public char command[MAX_FILENAME];
}
private class EventsWatchState : Object {
public unowned SpawnGater gater;
public EventsWatchState (SpawnGater gater) {
this.gater = gater;
}
public bool on_ready (IOChannel ch, IOCondition cond) {
gater.process_pending_events ();
return Source.CONTINUE;
}
}
}
}
+5
View File
@@ -124,6 +124,11 @@ public sealed class Frida.SyscallTracer : Object {
excluded_syscalls.update_u64_u8 (key, 1);
}
public void include_syscall (Abi abi, uint nr) throws Error {
uint64 key = ((uint64) abi << 32) | (uint64) nr;
excluded_syscalls.remove_u64 (key);
}
public DrainStatus drain_events (SyscallEventHandler on_event) {
var status = syscall_events_reader.drain (payload => {
assert (payload.length >= sizeof (SyscallEvent));
+132 -28
View File
@@ -131,11 +131,28 @@ if have_local_backend
output: helper_name,
command: post_process_helper,
build_by_default: true,
install: get_option('assets') == 'installed',
install: assets_mode == 'installed',
install_dir: asset_dir,
)
if get_option('assets') == 'embedded'
if assets_mode == 'installed'
# frida-gum already has install_data() rules for these, but
# releng/meson_make installs with --skip-subprojects when
# default_library=shared (to keep other subproject artifacts
# like capstone out of the prefix), so re-install them here
# alongside the agent / helper where host sessions look for
# them at runtime.
install_data(
gum_dep.get_variable('frida_dbghelp_prefix') / host_abi / 'dbghelp.dll',
install_dir: asset_dir,
)
install_data(
gum_dep.get_variable('frida_symsrv_prefix') / host_abi / 'symsrv.dll',
install_dir: asset_dir,
)
endif
if assets_mode == 'embedded'
if helper_modern == 'auto'
helper_modern = host_is_modern ? helper : ''
endif
@@ -256,7 +273,7 @@ if have_local_backend
override_options: ['b_asneeded=false'],
)
need_universal_helper = get_option('assets') == 'installed' and helper_compat != ''
need_universal_helper = assets_mode == 'installed' and helper_compat != ''
helper = custom_target('frida-helper',
input: [
@@ -266,7 +283,7 @@ if have_local_backend
output: need_universal_helper ? f'@helper_name@-@host_abi@' : helper_name,
command: post_process_helper + '@INPUT1@',
build_by_default: true,
install: get_option('assets') == 'installed' and not need_universal_helper,
install: assets_mode == 'installed' and not need_universal_helper,
install_dir: asset_dir,
)
@@ -287,7 +304,7 @@ if have_local_backend
)
endif
if get_option('assets') == 'embedded'
if assets_mode == 'embedded'
if helper_modern == 'auto'
helper_modern = host_is_modern ? helper : ''
endif
@@ -336,6 +353,7 @@ if have_local_backend
'linux' / 'frida-helper-backend-glue.c',
'linux' / 'frida-helper-backend-types.c',
'linux' / 'frida-helper-types.vala',
'linux' / 'spawn-gater.vala',
'linux' / 'activity-sampler.vala',
'linux' / 'syscall-tracer.vala',
'linux' / 'symbol-resolver.vala',
@@ -368,6 +386,7 @@ if have_local_backend
helpers_native_dir / 'bootstrapper.bin',
helpers_native_dir / 'loader.bin',
helpers_bpf_noarch_dir / 'activity-sampler.elf',
helpers_bpf_arch_dir / 'spawn-gater.elf',
helpers_bpf_arch_dir / 'syscall-tracer.elf',
],
output: [
@@ -384,6 +403,7 @@ if have_local_backend
'@INPUT2@',
'@INPUT3@',
'@INPUT4@',
'@INPUT5@',
],
)
helper_backend_sources += [helper_backend_data]
@@ -428,11 +448,11 @@ if have_local_backend
output: helper_name,
command: post_process_helper,
build_by_default: true,
install: get_option('assets') == 'installed',
install: assets_mode == 'installed',
install_dir: asset_dir,
)
if get_option('assets') == 'embedded'
if assets_mode == 'embedded'
if helper_modern == 'auto'
helper_modern = host_is_modern ? helper : ''
endif
@@ -481,7 +501,7 @@ if have_local_backend
]
endif
if get_option('assets') == 'embedded'
if assets_mode == 'embedded'
if agent_modern == 'auto'
agent_modern = host_is_modern ? agent : ''
endif
@@ -579,10 +599,18 @@ if have_fruity_backend
'fruity' / 'kdebug.vala',
'fruity' / 'kperfdata.vala',
'fruity' / 'cs-signature.vala',
'fruity' / 'network-stack.vala',
'fruity' / 'usb.vala',
'fruity' / 'ncm.vala',
]
if host_os != 'macos'
backend_sources += [
'fruity' / 'device-monitor-portable.vala',
'fruity' / 'pairing.vala',
'fruity' / 'tunnel.vala',
'fruity' / 'tls-psk.vala',
'fruity' / 'network-stack.vala',
'fruity' / 'usb.vala',
'fruity' / 'ncm.vala',
]
endif
if host_os_family == 'windows'
backend_sources += 'fruity' / 'device-monitor-windows.vala'
@@ -600,15 +628,17 @@ if have_fruity_backend
backend_sources += 'fruity' / 'device-monitor-unix.c'
endif
backend_deps += openssl_dep
backend_vala_args += [
'--pkg=libnghttp2',
'--pkg=libngtcp2',
'--pkg=libngtcp2_crypto_quictls',
'--pkg=libusb-1.0',
'--pkg=lwip',
'--pkg=openssl',
]
backend_vala_args += '--pkg=libnghttp2'
if host_os != 'macos'
backend_deps += openssl_dep
backend_vala_args += [
'--pkg=openssl',
'--pkg=libngtcp2',
ngtcp2_crypto_quictls_dep.found() ? '--pkg=libngtcp2_crypto_quictls' : '--pkg=libngtcp2_crypto_ossl',
'--pkg=libusb-1.0',
'--pkg=lwip',
]
endif
if host_os_family == 'windows'
backend_vala_args += '--pkg=windows-windns'
elif host_os_family == 'darwin'
@@ -644,10 +674,44 @@ if have_barebone_backend
subdir('barebone')
endif
icons_data = custom_target('frida-data-icons',
input: [
'icons.resources',
'icons' / 'local.png',
'icons' / 'simmy.png',
'icons' / 'fruity-network.png',
'icons' / 'droidy.png',
'icons' / 'socket.png',
'icons' / 'barebone.png',
'icons' / 'portal.png',
],
output: [
'frida-data-icons.vapi',
'frida-data-icons.h',
'frida-data-icons.c',
'frida-data-icons-blob' + resource_blob_extension,
],
command: [
resource_compiler_cmd_array,
'-c', '@INPUT0@',
'-o', meson.current_build_dir() / 'frida-data-icons',
'@INPUT1@',
'@INPUT2@',
'@INPUT3@',
'@INPUT4@',
'@INPUT5@',
'@INPUT6@',
'@INPUT7@',
],
)
backend_sources += [icons_data]
if host_os_family != 'windows'
backend_vala_args_private += '--pkg=posix'
endif
core_extra_link_depends = []
subdir('compiler')
core_sources = base_sources + backend_sources + compiler_sources
@@ -687,8 +751,38 @@ core_vala_args = [
core_gir_name = f'Frida-@[email protected]'
core = library('frida-core', core_sources,
if is_shared
core_lib_name = f'frida-core-@api_version@'
else
core_lib_name = 'frida-core'
endif
core_extra_link_args = backend_libs_private
if is_shared
symbol_map_outdir = meson.current_build_dir()
run_command(python, files('api' / 'generate.py'),
'--output=symbol-maps',
symbol_map_outdir,
check: true,
)
if host_os_family == 'windows'
if host_toolchain == 'microsoft'
foreach sym : ['frida_init', 'frida_init_with_runtime', 'frida_shutdown', 'frida_deinit',
'frida_get_main_context', 'frida_unref', 'frida_version', 'frida_version_string']
core_extra_link_args += '/EXPORT:' + sym
endforeach
else
core_extra_link_args += '-Wl,--retain-symbols-file,' + symbol_map_outdir / 'frida-core.symbols'
endif
elif host_os_family != 'darwin'
core_extra_link_args += '-Wl,--version-script,' + symbol_map_outdir / 'frida-core.version'
endif
endif
core = library(core_lib_name, core_sources,
c_args: frida_component_cflags,
vala_header: 'frida-core.h',
vala_vapi: 'frida-core.vapi',
vala_args: [
core_vala_args,
backend_vala_args_private,
@@ -698,17 +792,27 @@ core = library('frida-core', core_sources,
'--pkg=libsoup-3.0',
],
vala_gir: core_gir_name,
link_args: backend_libs_private,
link_args: core_extra_link_args,
link_depends: core_extra_link_depends,
link_with: backend_libs,
dependencies: core_deps + internal_deps,
install: is_shared,
)
core_dep = declare_dependency(
include_directories: include_directories('.'),
link_args: backend_libs_private,
link_with: [core] + backend_libs,
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep] + backend_deps + internal_deps,
)
if is_shared
core_dep = declare_dependency(
include_directories: include_directories('.'),
link_with: [core] + backend_libs,
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep] + backend_deps + internal_deps,
)
else
core_dep = declare_dependency(
include_directories: include_directories('.'),
link_args: backend_libs_private,
link_with: [core] + backend_libs,
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep] + backend_deps + internal_deps,
)
endif
core_build_dir = meson.current_build_dir()
subdir('api')
+13 -14
View File
@@ -1686,7 +1686,7 @@ namespace Frida {
packument_cache[name] = packument_promise;
}
var packument_node = yield packument_promise.future.wait_async (cancellable);
var packument_reader = new Json.Reader (packument_node);
var packument_reader = make_json_reader_from_node (packument_node);
string? version_from_dist_tag = null;
if (!packument_reader.read_member ("dist-tags"))
@@ -2279,7 +2279,7 @@ namespace Frida {
private async Json.Reader fetch (string resource, Cancellable? cancellable) throws Error, IOError {
var root = yield fetch_node (resource, cancellable);
return new Json.Reader (root);
return make_json_reader_from_node (root);
}
private async Json.Node fetch_node (string resource, Cancellable? cancellable) throws Error, IOError {
@@ -2560,7 +2560,7 @@ namespace Frida {
} catch (GLib.Error e) {
throw new Error.PROTOCOL ("%s", e.message);
}
return new Json.Reader (p.get_root ());
return make_json_reader_from_node (p.get_root ());
}
private class SemverVersion {
@@ -2672,13 +2672,10 @@ namespace Frida {
is_potentially_numeric = false;
}
if (is_potentially_numeric) {
try {
parse_uint (id);
} catch (Error e) {
throw new Error.PROTOCOL ("Invalid numeric pre-release identifier '%s' in '%s': %s",
id, version, e.message);
}
if (is_potentially_numeric && id.length > 1 && id[0] == '0') {
throw new Error.PROTOCOL (
"Numeric pre-release identifier '%s' in '%s' must not have leading zeros",
id, version);
}
}
}
@@ -2754,10 +2751,12 @@ namespace Frida {
bool b_is_num = is_numeric_identifier (id_b);
if (a_is_num && b_is_num) {
var val_a = uint.parse (id_a);
var val_b = uint.parse (id_b);
if (val_a != val_b)
return val_a > val_b ? 1 : -1;
int len_cmp = id_a.length - id_b.length;
if (len_cmp != 0)
return len_cmp;
int digit_cmp = strcmp (id_a, id_b);
if (digit_cmp != 0)
return digit_cmp;
} else if (a_is_num) {
return -1;
} else if (b_is_num) {
+38 -22
View File
@@ -31,8 +31,8 @@ namespace Frida {
private State state = STOPPED;
private WebService cluster_service;
private WebService? control_service;
private Gee.List<WebService> cluster_services = new Gee.ArrayList<WebService> ();
private Gee.List<WebService> control_services = new Gee.ArrayList<WebService> ();
private Gee.Map<uint, ConnectionEntry> connections = new Gee.HashMap<uint, ConnectionEntry> ();
private Gee.MultiMap<string, ConnectionEntry> tags = new Gee.HashMultiMap<string, ConnectionEntry> ();
@@ -63,13 +63,9 @@ namespace Frida {
construct {
_device = new Device (null, new PortalHostSessionProvider (this));
cluster_service = new WebService (cluster_params, CLUSTER);
cluster_service.incoming.connect (on_incoming_cluster_connection);
if (control_params != null) {
control_service = new WebService (control_params, CONTROL);
control_service.incoming.connect (on_incoming_control_connection);
}
cluster_services.add (make_cluster_service (cluster_params));
if (control_params != null)
control_services.add (make_control_service (control_params));
}
public override void dispose () {
@@ -90,6 +86,30 @@ namespace Frida {
}
}
public void add_cluster_endpoint (EndpointParameters endpoint_params) throws Error {
if (state != STOPPED)
throw new Error.INVALID_OPERATION ("Cannot add endpoint after service has started");
cluster_services.add (make_cluster_service (endpoint_params));
}
public void add_control_endpoint (EndpointParameters endpoint_params) throws Error {
if (state != STOPPED)
throw new Error.INVALID_OPERATION ("Cannot add endpoint after service has started");
control_services.add (make_control_service (endpoint_params));
}
private WebService make_cluster_service (EndpointParameters endpoint_params) {
var service = new WebService (endpoint_params, CLUSTER);
service.incoming.connect (on_incoming_cluster_connection);
return service;
}
private WebService make_control_service (EndpointParameters endpoint_params) {
var service = new WebService (endpoint_params, CONTROL);
service.incoming.connect (on_incoming_control_connection);
return service;
}
public async void start (Cancellable? cancellable = null) throws Error, IOError {
if (state != STOPPED)
throw new Error.INVALID_OPERATION ("Invalid operation");
@@ -98,10 +118,11 @@ namespace Frida {
io_cancellable = new Cancellable ();
try {
yield cluster_service.start (cancellable);
foreach (var service in cluster_services)
yield service.start (cancellable);
if (control_service != null)
yield control_service.start (cancellable);
foreach (var service in control_services)
yield service.start (cancellable);
state = STARTED;
} catch (GLib.Error e) {
@@ -126,10 +147,11 @@ namespace Frida {
if (state != STARTED)
throw new Error.INVALID_OPERATION ("Invalid operation");
if (control_service != null)
control_service.stop ();
foreach (var service in control_services)
service.stop ();
cluster_service.stop ();
foreach (var service in cluster_services)
service.stop ();
if (io_cancellable != null)
io_cancellable.cancel ();
@@ -948,13 +970,7 @@ namespace Frida {
}
construct {
var builder = new VariantBuilder (VariantType.VARDICT);
builder.add ("{sv}", "format", new Variant.string ("rgba"));
builder.add ("{sv}", "width", new Variant.uint16 (16));
builder.add ("{sv}", "height", new Variant.uint16 (16));
var image = new Bytes (Base64.decode ("AAAAAAAAAAAAAAAAOjo6Dzo6OhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6TZCHbvlycnL4Ojo6iTo6OhMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6aa6fdv7878f/+/Te/93d3f9xcXH3Ojo6gTo6Og8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6F4KAfv//5Hn//fHK//r6+v/39/f/9/f3/9LS0v9kZGTzOjo6eDo6OgsAAAAAAAAAAAAAAAAAAAAAAAAAADo6Og6Tk5P/zc3N//z8/P/6+vr/8PDw/+7u7v/p6en/9PT0/8jIyP9XV1f2Ojo6SgAAAAAAAAAAAAAAAAAAAAA6OjoIb29v/8HBwf+5ubn/9/f3/+/v7//p6en/+Pj4/+np6f/o6Oj/4ODg/z09PcsAAAAAAAAAAAAAAAAAAAAAAAAAAjMzM1p8fHz/wsLC/7CwsP/x8fH/8/P0/9zc3f/09PT/+vr6/8vLy/9AQEDFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALS0tV2pqav7BwcH/rq6u/+bm5v/09PT/s7Oz/93d3f/R0dL/VVVVygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjIyNRWlpa+7+/v/+wsLD/oaGh/4iIiP9NTU7/VVVW/0BAQf89PT61Pj4/BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbG09NTU32urq6/4yMjP9ycnL/Pj4//1BQUf9tbW7/XFxd/z4+P8M+Pj8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExMTTD09PfBzc3P/LCwsvDAwMbVEREX/f3+A/6ioqf9tbW7zPj4/lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDQ0vGRkZggAAAAAAAAAAJycnh0NDRP2GhojujIyP4EtLS4k/Pz8YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjIyRoRUVFq21tbp5TU1ZUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkpK10AAAAWAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="));
builder.add ("{sv}", "image", Variant.new_from_data (new VariantType ("ay"), image.get_data (), true, image));
_icon = builder.end ();
_icon = make_provider_icon (Frida.Data.Icons.get_portal_png_blob ().data);
}
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
+7 -2
View File
@@ -71,7 +71,7 @@ namespace Frida {
}
public Variant? icon {
get { return null; }
get { return _icon; }
}
public HostSessionProviderKind kind {
@@ -80,8 +80,13 @@ namespace Frida {
}
}
private static Variant _icon;
private SimmyHostSession? host_session;
static construct {
_icon = make_provider_icon (Frida.Data.Icons.get_simmy_png_blob ().data);
}
public SimmyHostSessionProvider (Simmy.Device device) {
Object (device: device);
}
@@ -604,7 +609,7 @@ namespace Frida {
Json.Node response = yield call ("fetchApplicationIcons", args, null, cancellable);
var icons = new Gee.HashMap<string, Bytes> ();
var reader = new Json.Reader (response);
var reader = make_json_reader_from_node (response);
int n = reader.count_elements ();
for (int i = 0; i != n; i++) {
reader.read_element (i);
+1 -7
View File
@@ -38,13 +38,7 @@ namespace Frida {
private Cancellable io_cancellable = new Cancellable ();
construct {
var builder = new VariantBuilder (VariantType.VARDICT);
builder.add ("{sv}", "format", new Variant.string ("rgba"));
builder.add ("{sv}", "width", new Variant.uint16 (16));
builder.add ("{sv}", "height", new Variant.uint16 (16));
var image = new Bytes (Base64.decode ("AAAAAAAAAAAAAAAAOjo6Dzo6OhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6TZCHbvlycnL4Ojo6iTo6OhMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6aa6fdv7878f/+/Te/93d3f9xcXH3Ojo6gTo6Og8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOjo6F4KAfv//5Hn//fHK//r6+v/39/f/9/f3/9LS0v9kZGTzOjo6eDo6OgsAAAAAAAAAAAAAAAAAAAAAAAAAADo6Og6Tk5P/zc3N//z8/P/6+vr/8PDw/+7u7v/p6en/9PT0/8jIyP9XV1f2Ojo6SgAAAAAAAAAAAAAAAAAAAAA6OjoIb29v/8HBwf+5ubn/9/f3/+/v7//p6en/+Pj4/+np6f/o6Oj/4ODg/z09PcsAAAAAAAAAAAAAAAAAAAAAAAAAAjMzM1p8fHz/wsLC/7CwsP/x8fH/8/P0/9zc3f/09PT/+vr6/8vLy/9AQEDFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALS0tV2pqav7BwcH/rq6u/+bm5v/09PT/s7Oz/93d3f/R0dL/VVVVygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjIyNRWlpa+7+/v/+wsLD/oaGh/4iIiP9NTU7/VVVW/0BAQf89PT61Pj4/BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbG09NTU32urq6/4yMjP9ycnL/Pj4//1BQUf9tbW7/XFxd/z4+P8M+Pj8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExMTTD09PfBzc3P/LCwsvDAwMbVEREX/f3+A/6ioqf9tbW7zPj4/lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDQ0vGRkZggAAAAAAAAAAJycnh0NDRP2GhojujIyP4EtLS4k/Pz8YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjIyRoRUVFq21tbp5TU1ZUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkpK10AAAAWAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="));
builder.add ("{sv}", "image", Variant.new_from_data (new VariantType ("ay"), image.get_data (), true, image));
_icon = builder.end ();
_icon = make_provider_icon (Frida.Data.Icons.get_socket_png_blob ().data);
}
public async void close (Cancellable? cancellable) throws IOError {
+11 -5
View File
@@ -171,7 +171,7 @@ namespace Frida {
try {
string level_str = (level == PrivilegeLevel.ELEVATED) ? "ELEVATED" : "NORMAL";
void * process = spawn (resource_store.native_helper.path,
void * process = spawn (resource_store.native_helper_path,
"MANAGER %s %s".printf (level_str, transport.remote_address),
level);
instance = new HelperInstance (resource_store.helpers, transport, stream_request, process);
@@ -367,7 +367,7 @@ namespace Frida {
}
private sealed class ResourceStore {
public TemporaryFile native_helper {
public string native_helper_path {
get;
private set;
}
@@ -379,25 +379,30 @@ namespace Frida {
}
public ResourceStore (TemporaryDirectory tempdir) throws Error {
#if HAVE_EMBEDDED_ASSETS
var helper_arm64 = add_helper ("arm64", Frida.Data.Helper.get_frida_helper_arm64_exe_blob (), tempdir);
var helper_x86_64 = add_helper ("x86_64", Frida.Data.Helper.get_frida_helper_x86_64_exe_blob (), tempdir);
var helper_x86 = add_helper ("x86", Frida.Data.Helper.get_frida_helper_x86_exe_blob (), tempdir);
switch (Gum.Windows.query_native_cpu_type ()) {
case ARM64:
native_helper = helper_arm64;
native_helper_path = helper_arm64.path;
break;
case AMD64:
native_helper = helper_x86_64;
native_helper_path = helper_x86_64.path;
break;
case IA32:
native_helper = helper_x86;
native_helper_path = helper_x86.path;
break;
default:
assert_not_reached ();
}
#else
native_helper_path = Frida.helper_path;
#endif
}
#if HAVE_EMBEDDED_ASSETS
private TemporaryFile add_helper (string name, Frida.Data.Helper.Blob blob, TemporaryDirectory tempdir) throws Error {
var file = new TemporaryFile.from_stream (@"frida-helper-$name.exe",
new MemoryInputStream.from_data (blob.data, null),
@@ -405,5 +410,6 @@ namespace Frida {
helpers.add (file);
return file;
}
#endif
}
}
+32
View File
@@ -46,6 +46,7 @@ namespace Frida {
injector = new Winjector (helper, false, tempdir);
injector.uninjected.connect (on_uninjected);
#if HAVE_EMBEDDED_ASSETS
agent = new AgentDescriptor (PathTemplate ("<arch>\\frida-agent.dll"),
new Bytes.static (Frida.Data.Agent.get_frida_agent_arm64_dll_blob ().data),
new Bytes.static (Frida.Data.Agent.get_frida_agent_x86_64_dll_blob ().data),
@@ -66,8 +67,28 @@ namespace Frida {
},
tempdir
);
#endif
}
#if !HAVE_EMBEDDED_ASSETS
private static PathTemplate installed_agent_path_template () {
return PathTemplate (Frida.agent_path);
}
private static string[] installed_agent_dependencies () {
string lib_root = Path.get_dirname (Path.get_dirname (Frida.agent_path));
string[] deps = {};
foreach (unowned string arch in new string[] { "arm64", "x86_64", "x86" }) {
foreach (unowned string name in new string[] { "dbghelp.dll", "symsrv.dll" }) {
string p = Path.build_filename (lib_root, arch, name);
if (FileUtils.test (p, EXISTS))
deps += p;
}
}
return deps;
}
#endif
public override async void close (Cancellable? cancellable) throws IOError {
yield base.close (cancellable);
@@ -98,7 +119,11 @@ namespace Frida {
protected override async AgentSessionProvider create_system_session_provider (Cancellable? cancellable,
out DBusConnection connection) throws Error, IOError {
#if HAVE_EMBEDDED_ASSETS
var path_template = agent.get_path_template ();
#else
var path_template = installed_agent_path_template ();
#endif
unowned string arch;
switch (Gum.NATIVE_CPU) {
@@ -225,8 +250,15 @@ namespace Frida {
var stream_request = Pipe.open (t.local_address, cancellable);
var winjector = injector as Winjector;
#if HAVE_EMBEDDED_ASSETS
var id = yield winjector.inject_library_resource (pid, agent, "frida_agent_main",
make_agent_parameters (pid, t.remote_address, options), cancellable);
#else
var id = yield winjector.inject_library_file_with_template (pid,
installed_agent_path_template (), "frida_agent_main",
make_agent_parameters (pid, t.remote_address, options),
installed_agent_dependencies (), cancellable);
#endif
injectee_by_pid[pid] = id;
transport = t;
+1 -1
View File
@@ -46,7 +46,7 @@ namespace Frida {
cancellable);
}
private async uint inject_library_file_with_template (uint pid, PathTemplate path_template, string entrypoint, string data,
public async uint inject_library_file_with_template (uint pid, PathTemplate path_template, string entrypoint, string data,
string[] dependencies, Cancellable? cancellable) throws Error, IOError {
uint id = next_injectee_id++;
yield helper.inject_library_file (pid, path_template, entrypoint, data, dependencies, id, cancellable);
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/frida-gum.git
revision = 17.8.2
revision = 17.9.2
depth = 1
[provide]
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/libnice.git
revision = e12567b0a16a0c2eb5dfe5e0782baba8496772ff
revision = 9a3da6e3e5bbcf935fd85b6b8557ff8c7dd9032c
depth = 1
[provide]
+7
View File
@@ -0,0 +1,7 @@
[wrap-git]
url = https://github.com/frida/nghttp2.git
revision = ae13d24ea59c30e36ca53d1b22c4e664588d0445
depth = 1
[provide]
dependency_names = libnghttp2
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/ngtcp2.git
revision = 828dcaed498b40954e1b496664a3309796968db6
revision = 5b21d4418ff668d9b5ddc4f744b7a28af4196abf
depth = 1
[provide]
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/usrsctp.git
revision = f459ae9d3700c06e59d709901e92c08e31c6e623
revision = 377fecfe06bb766c69cc0e1e1f6d5c72cf7704e6
depth = 1
[provide]
+9 -2
View File
@@ -23,7 +23,9 @@ endif
if build_gadget
test_vala_args += '--define=HAVE_GADGET'
test_sources += 'test-gadget.vala'
test_depends += gadget
if not have_shared_glib
test_depends += gadget
endif
endif
subdir('labrats')
@@ -68,12 +70,17 @@ if host_os == 'macos'
extra_link_depends += [runner_plist]
endif
runner_deps = [core_dep]
if host_os_family == 'darwin'
runner_deps += dependency('appleframeworks', modules: ['Foundation'])
endif
raw_runner = executable('frida-tests-raw', 'main.vala',
vala_args: [core_vala_args, system_vala_args],
link_args: executable_no_exports_link_args + extra_link_args,
link_depends: executable_no_exports_link_depends + extra_link_depends,
link_with: frida_tests,
dependencies: [core_dep],
dependencies: runner_deps,
)
runner = custom_target('frida-tests',
+2 -2
View File
@@ -234,7 +234,7 @@ Interceptor.attach(Process.getModuleByName('/usr/lib/system/libsystem_kernel.dyl
while (true) {
var message = yield h.wait_for_message ();
var reader = new Json.Reader (Json.from_string (message.text));
var reader = make_json_reader (message.text);
reader.read_member ("type");
if (reader.get_string_value () != "send") {
@@ -278,7 +278,7 @@ Interceptor.attach(Process.getModuleByName('/usr/lib/system/libsystem_kernel.dyl
var message = yield h.wait_for_message ();
printerr ("got message: %s\n", message.text);
var reader = new Json.Reader (Json.from_string (message.text));
var reader = make_json_reader (message.text);
reader.read_member ("type");
if (reader.get_string_value () != "send") {
+4 -8
View File
@@ -1218,15 +1218,13 @@ namespace Frida.HostSessionTest {
}
""");
var message_handler = script.message.connect ((json, data) => {
var parser = new Json.Parser ();
Json.Reader reader;
try {
parser.load_from_data (json);
reader = make_json_reader (json);
} catch (GLib.Error e) {
assert_not_reached ();
}
var reader = new Json.Reader (parser.get_root ());
assert (reader.read_member ("type") && reader.get_string_value () == "send");
reader.end_member ();
@@ -1477,15 +1475,13 @@ namespace Frida.HostSessionTest {
}
""");
var message_handler = script.message.connect ((json, data) => {
var parser = new Json.Parser ();
Json.Reader reader;
try {
parser.load_from_data (json);
reader = make_json_reader (json);
} catch (GLib.Error e) {
assert_not_reached ();
}
var reader = new Json.Reader (parser.get_root ());
assert (reader.read_member ("type") && reader.get_string_value () == "send");
reader.end_member ();
+21 -7
View File
@@ -1,6 +1,5 @@
from __future__ import annotations
import base64
import json
import os
import platform
@@ -51,32 +50,37 @@ def main(argv: List[str]):
args = argv[1:]
go = Path(args.pop(0))
host_os = args.pop(0)
host_os_family = args.pop(0)
host_abi = args.pop(0)
libc = args.pop(0)
exe_suffix = args.pop(0)
shlib_suffix = args.pop(0)
cc_id = args.pop(0)
assets_mode = args.pop(0)
cc_cmd_array = pop_cmd_array_arg(args)
ar_cmd_array = pop_cmd_array_arg(args)
nm_cmd_array = pop_cmd_array_arg(args)
ranlib_cmd_array = pop_cmd_array_arg(args)
try:
config = detect_config(
mode, config_path = detect_config(
go,
host_os,
host_os_family,
host_abi,
libc,
exe_suffix,
shlib_suffix,
cc_id,
assets_mode,
cc_cmd_array,
ar_cmd_array,
nm_cmd_array,
ranlib_cmd_array,
)
print("ok")
print(config["mode"], base64.b64encode(json.dumps(config).encode("utf-8")).decode("ascii"))
print(mode)
print(config_path)
except Exception as e:
print("error")
print(str(e).replace("\n", "\\n"))
@@ -99,16 +103,18 @@ def pop_cmd_array_arg(args: List[str]) -> List[str]:
def detect_config(
go: Path,
host_os: str,
host_os_family: str,
host_abi: str,
libc: str,
exe_suffix: str,
shlib_suffix: str,
cc_id: str,
assets_mode: str,
cc_cmd_array: List[str],
ar_cmd_array: Optional[List[str]],
nm_cmd_array: Optional[List[str]],
ranlib_cmd_array: Optional[List[str]],
) -> dict:
) -> Tuple[str, Path]:
source_root = Path(os.environ["MESON_SOURCE_ROOT"])
build_root = Path(os.environ["MESON_BUILD_ROOT"])
subdir = Path(os.environ["MESON_SUBDIR"])
@@ -169,9 +175,12 @@ def detect_config(
env["GOOS"] = goos
env["GOARCH"] = goarch
if libc == "musl":
apple_non_macos = host_os_family == "darwin" and host_os != "macos"
if apple_non_macos:
mode = "c-archive"
elif libc == "musl":
mode = "pie"
elif host_os == "android":
elif host_os == "android" or assets_mode == "installed":
mode = "c-shared"
else:
mode = "c-archive"
@@ -187,6 +196,7 @@ def detect_config(
config = {
"mode": mode,
"os": host_os,
"os_family": goos,
"abi": host_abi,
"exe_suffix": exe_suffix,
"shlib_suffix": shlib_suffix,
@@ -198,7 +208,11 @@ def detect_config(
}
if mingw is not None:
config["mingw"] = mingw
return config
config_path = work_dir / "go-config.json"
config_path.write_text(json.dumps(config), encoding="utf-8")
return config["mode"], config_path
class BuildError(Exception):
+265
View File
@@ -0,0 +1,265 @@
#!/usr/bin/env python3
"""Regenerate the backend provider icons bundled by the resource compiler.
Requires `rsvg-convert` (librsvg) on PATH. The icons use accent colors
drawn from SF Symbols and libadwaita so they feel native in both
SwiftUI and GTK/Adwaita consumers. Each icon is rendered to a 96x96
PNG under src/icons/, where `icons.resources` picks it up.
"""
from __future__ import annotations
import subprocess
from pathlib import Path
def main() -> None:
ICONS_DIR.mkdir(exist_ok=True)
for name, svg in icons():
png_path = ICONS_DIR / f"{name}.png"
render_png(svg, ICON_SIZE, png_path)
print(f"updated {png_path.relative_to(ROOT)}")
def icons() -> list[tuple[str, str]]:
return [
("local", svg_local()),
("simmy", svg_simmy()),
("fruity-network", svg_fruity_network()),
("droidy", svg_droidy()),
("socket", svg_socket()),
("barebone", svg_barebone()),
("portal", svg_portal()),
]
def svg_local() -> str:
return f"""\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="case" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="{SLATE_LIGHT}"/>
<stop offset="1" stop-color="{SLATE_DARK}"/>
</linearGradient>
</defs>
<rect x="6" y="10" width="52" height="36" rx="3.5" fill="url(#case)"/>
<rect x="9" y="13" width="46" height="30" rx="1.8" fill="{SCREEN}"/>
<path d="M 17 22 L 23 28 L 17 34" stroke="#FFFFFF" stroke-width="2.8"
stroke-linecap="round" stroke-linejoin="round" fill="none"/>
<rect x="27" y="31" width="10" height="2.8" rx="0.7" fill="#FFFFFF" opacity="0.85"/>
<rect x="28" y="46" width="8" height="5" fill="url(#case)"/>
<rect x="16" y="51" width="32" height="4" rx="2" fill="url(#case)"/>
</svg>
"""
def svg_simmy() -> str:
return f"""\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="phone" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="{CHROME_LIGHT}"/>
<stop offset="1" stop-color="{CHROME_DARK}"/>
</linearGradient>
<linearGradient id="screen" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="{SIM_SCREEN_TOP}"/>
<stop offset="1" stop-color="{SIM_SCREEN_BOTTOM}"/>
</linearGradient>
</defs>
<rect x="21" y="6" width="22" height="52" rx="5" fill="url(#phone)"/>
<rect x="22.5" y="10" width="19" height="42" rx="2.5" fill="url(#screen)"/>
<circle cx="27.5" cy="14.5" r="1.6" fill="{TL_RED}"/>
<circle cx="32" cy="14.5" r="1.6" fill="{TL_YELLOW}"/>
<circle cx="36.5" cy="14.5" r="1.6" fill="{TL_GREEN}"/>
<rect x="29" y="54" width="6" height="1.3" rx="0.65" fill="{CHROME_DARK}" opacity="0.7"/>
</svg>
"""
def svg_fruity_network() -> str:
return f"""\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="phone" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="{SLATE_LIGHT}"/>
<stop offset="1" stop-color="{SLATE_DARK}"/>
</linearGradient>
<linearGradient id="wave" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="{BLUE_LIGHT}"/>
<stop offset="1" stop-color="{BLUE}"/>
</linearGradient>
</defs>
<g stroke="url(#wave)" stroke-linecap="round" fill="none">
<path d="M 11 22 Q 32 2 53 22" stroke-width="3.2" opacity="0.45"/>
<path d="M 17 24 Q 32 11 47 24" stroke-width="3.2" opacity="0.75"/>
<path d="M 23 26 Q 32 18 41 26" stroke-width="3.2" opacity="1.00"/>
</g>
<rect x="23.5" y="28" width="17" height="34" rx="4.5" fill="url(#phone)"/>
<rect x="25" y="29.5" width="14" height="31" rx="3.2" fill="{SCREEN}"/>
<rect x="28.5" y="31" width="7" height="2.4" rx="1.2" fill="{ISLAND}"/>
<rect x="29" y="58" width="6" height="1.3" rx="0.65" fill="{DETAIL}" opacity="0.8"/>
</svg>
"""
def svg_droidy() -> str:
return f"""\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="{GREEN_LIGHT}"/>
<stop offset="1" stop-color="{GREEN_DARK}"/>
</linearGradient>
</defs>
<path d="M 21 16 L 17 9" stroke="{GREEN_DARK}" stroke-width="3" stroke-linecap="round" fill="none"/>
<path d="M 43 16 L 47 9" stroke="{GREEN_DARK}" stroke-width="3" stroke-linecap="round" fill="none"/>
<path d="M 11 26 Q 11 12 32 12 Q 53 12 53 26 L 53 47 Q 53 51 49 51 L 15 51 Q 11 51 11 47 Z"
fill="url(#g)"/>
<circle cx="23" cy="30" r="3.2" fill="{GREEN_EYE}"/>
<circle cx="41" cy="30" r="3.2" fill="{GREEN_EYE}"/>
<path d="M 14 22 Q 20 14 32 14" stroke="#FFFFFF" stroke-width="1.6"
stroke-linecap="round" fill="none" opacity="0.40"/>
</svg>
"""
def svg_socket() -> str:
return f"""\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<radialGradient id="g" cx="0.35" cy="0.30" r="0.95">
<stop offset="0" stop-color="{BLUE_LIGHT}"/>
<stop offset="0.55" stop-color="{BLUE}"/>
<stop offset="1" stop-color="{BLUE_DARK}"/>
</radialGradient>
</defs>
<circle cx="32" cy="32" r="25" fill="url(#g)"/>
<g fill="none" stroke="#FFFFFF" stroke-width="1.6" opacity="0.92" stroke-linecap="round">
<ellipse cx="32" cy="32" rx="25" ry="10"/>
<ellipse cx="32" cy="32" rx="10" ry="25"/>
<ellipse cx="32" cy="32" rx="20" ry="25" opacity="0.55"/>
<ellipse cx="32" cy="32" rx="25" ry="20" opacity="0.55"/>
<line x1="7" y1="32" x2="57" y2="32"/>
</g>
<circle cx="32" cy="32" r="25" fill="none" stroke="{BLUE_DARK}" stroke-width="1" opacity="0.35"/>
</svg>
"""
def svg_barebone() -> str:
return f"""\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="chip" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="{CHIP_LIGHT}"/>
<stop offset="1" stop-color="{CHIP_DARK}"/>
</linearGradient>
<linearGradient id="pin" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="{PIN_LIGHT}"/>
<stop offset="1" stop-color="{PIN_DARK}"/>
</linearGradient>
<radialGradient id="led" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="{LED_BRIGHT}"/>
<stop offset="0.55" stop-color="{LED}"/>
<stop offset="1" stop-color="{LED_DARK}"/>
</radialGradient>
</defs>
<g fill="url(#pin)">
<rect x="3" y="19" width="9" height="4" rx="1"/>
<rect x="3" y="30" width="9" height="4" rx="1"/>
<rect x="3" y="41" width="9" height="4" rx="1"/>
<rect x="52" y="19" width="9" height="4" rx="1"/>
<rect x="52" y="30" width="9" height="4" rx="1"/>
<rect x="52" y="41" width="9" height="4" rx="1"/>
</g>
<rect x="12" y="12" width="40" height="40" rx="3.5" fill="url(#chip)"/>
<circle cx="32" cy="12" r="3" fill="{NOTCH}"/>
<circle cx="32" cy="36" r="5" fill="url(#led)"/>
<circle cx="32" cy="36" r="1.6" fill="#FFF7DC"/>
</svg>
"""
def svg_portal() -> str:
return f"""\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<radialGradient id="hub" cx="0.40" cy="0.35" r="0.85">
<stop offset="0" stop-color="{PURPLE_LIGHT}"/>
<stop offset="0.65" stop-color="{PURPLE}"/>
<stop offset="1" stop-color="{PURPLE_DARK}"/>
</radialGradient>
<linearGradient id="node" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="{TEAL_LIGHT}"/>
<stop offset="1" stop-color="{TEAL_DARK}"/>
</linearGradient>
</defs>
<g stroke="{TEAL}" stroke-width="3" stroke-linecap="round" opacity="0.85">
<line x1="32" y1="32" x2="13" y2="13"/>
<line x1="32" y1="32" x2="51" y2="13"/>
<line x1="32" y1="32" x2="13" y2="51"/>
<line x1="32" y1="32" x2="51" y2="51"/>
</g>
<circle cx="13" cy="13" r="6.5" fill="url(#node)"/>
<circle cx="51" cy="13" r="6.5" fill="url(#node)"/>
<circle cx="13" cy="51" r="6.5" fill="url(#node)"/>
<circle cx="51" cy="51" r="6.5" fill="url(#node)"/>
<circle cx="32" cy="32" r="11.5" fill="url(#hub)"/>
<circle cx="32" cy="32" r="4.5" fill="#FFFFFF" opacity="0.95"/>
</svg>
"""
def render_png(svg: str, size: int, out: Path) -> None:
subprocess.run(
["rsvg-convert", "-w", str(size), "-h", str(size), "-o", str(out)],
input=svg.encode("utf-8"),
check=True,
)
ROOT = Path(__file__).resolve().parent.parent
ICON_SIZE = 96
ICONS_DIR = ROOT / "src" / "icons"
BLUE = "#2E77E4"
BLUE_LIGHT = "#5AAEF5"
BLUE_DARK = "#1A5FB4"
CHROME_LIGHT = "#EFEEF1"
CHROME_DARK = "#9A9996"
SIM_SCREEN_TOP = "#3D3846"
SIM_SCREEN_BOTTOM = "#1F1B2B"
TL_RED = "#FF5F56"
TL_YELLOW = "#FFBD2E"
TL_GREEN = "#27C93F"
SLATE_LIGHT = "#8B8A95"
SLATE_DARK = "#4E4C58"
SCREEN = "#17131F"
ISLAND = "#050208"
DETAIL = "#DCDADF"
GREEN_LIGHT = "#57E389"
GREEN_DARK = "#26A269"
GREEN_EYE = "#0F3B20"
CHIP_LIGHT = "#4E4C58"
CHIP_DARK = "#1F1B2B"
PIN_LIGHT = "#B5B3BB"
PIN_DARK = "#6E6D75"
NOTCH = "#0F0B17"
LED_BRIGHT = "#FFD89B"
LED = "#FFA348"
LED_DARK = "#C65B10"
PURPLE = "#9141AC"
PURPLE_LIGHT = "#DC8ADD"
PURPLE_DARK = "#613583"
TEAL = "#33C7DE"
TEAL_LIGHT = "#5EDDEE"
TEAL_DARK = "#1D8DA8"
if __name__ == "__main__":
main()
+2 -2
View File
@@ -19,7 +19,7 @@ if [ ! -f "$executable" ]; then
exit 4
fi
agent=$prefix/usr/lib/frida/frida-agent.dylib
agent=$prefix/usr/lib/frida-1.0/frida-agent.dylib
if [ ! -f "$agent" ]; then
echo "$agent: not found" > /dev/stderr
exit 5
@@ -41,7 +41,7 @@ tmpdir="$(mktemp -d /tmp/package-server.XXXXXX)"
pkroot=$tmpdir$sysroot
bindir=$pkroot/usr/sbin
libdir=$pkroot/usr/lib/frida
libdir=$pkroot/usr/lib/frida-1.0
daedir=$pkroot/Library/LaunchDaemons
mkdir -p "$bindir/"
+4 -2
View File
@@ -1,6 +1,8 @@
[CCode (lower_case_cprefix = "")]
namespace Config {
public const string FRIDA_PREFIX;
public const string FRIDA_HELPER_PATH;
public const string FRIDA_AGENT_PATH;
public const string FRIDA_LIBDIR_NAME;
public const string FRIDA_HELPER_NAME;
public const string FRIDA_AGENT_NAME;
public const string FRIDA_COMPILER_BACKEND_NAME;
}
+28
View File
@@ -0,0 +1,28 @@
[CCode (cheader_filename = "ngtcp2/ngtcp2_crypto.h", lower_case_cprefix = "ngtcp2_crypto_", gir_namespace = "NGTcp2Crypto", gir_version = "1.0")]
namespace NGTcp2.Crypto {
[CCode (cheader_filename = "ngtcp2/ngtcp2_crypto_ossl.h")]
namespace Ossl {
public int configure_client_session (OpenSSL.SSL ssl);
}
[CCode (cname = "ngtcp2_crypto_conn_ref", has_destroy_function = false)]
public struct ConnectionRef {
public GetConnection get_conn;
public void * user_data;
}
[CCode (cname = "ngtcp2_crypto_get_conn", has_target = false)]
public delegate unowned Connection GetConnection (ConnectionRef conn_ref);
public ClientInitial client_initial_cb;
public RecvCryptoData recv_crypto_data_cb;
public Encrypt encrypt_cb;
public Decrypt decrypt_cb;
public HpMask hp_mask_cb;
public RecvRetry recv_retry_cb;
public UpdateKey update_key_cb;
public DeleteCryptoAeadCtx delete_crypto_aead_ctx_cb;
public DeleteCryptoCipherCtx delete_crypto_cipher_ctx_cb;
public GetPathChallengeData get_path_challenge_data_cb;
public VersionNegotiation version_negotiation_cb;
}
+46 -6
View File
@@ -1,7 +1,7 @@
[CCode (lower_case_cprefix = "OPENSSL_", gir_namespace = "OpenSSL", gir_version = "1.0")]
namespace OpenSSL {
[Compact]
[CCode (cname = "SSL_CTX", cprefix = "SSL_CTX_")]
[CCode (cheader_filename = "openssl/ssl.h", cname = "SSL_CTX", cprefix = "SSL_CTX_")]
public class SSLContext {
public SSLContext (SSLMethod meth);
@@ -12,10 +12,11 @@ namespace OpenSSL {
}
[Compact]
[CCode (cname = "SSL", cprefix = "SSL_")]
[CCode (cheader_filename = "openssl/ssl.h", cname = "SSL", cprefix = "SSL_")]
public class SSL {
public SSL (SSLContext ctx);
public void * get_app_data ();
public void set_app_data (void * data);
public void set_connect_state ();
@@ -26,13 +27,35 @@ namespace OpenSSL {
public void set_psk_client_callback (PskClientCallback callback);
public void set_quic_transport_version (int version);
public void set_bio (void * rbio, void * wbio);
public int do_handshake ();
public int read (uint8[] buf);
public int write (uint8[] buf);
public int shutdown ();
public int get_error (int ret);
public int pending ();
}
[CCode (cheader_filename = "openssl/ssl.h", cname = "int", cprefix = "SSL_ERROR_", has_type_id = false)]
public enum SSLErrorCode {
NONE,
SSL,
WANT_READ,
WANT_WRITE,
WANT_X509_LOOKUP,
SYSCALL,
ZERO_RETURN,
WANT_CONNECT,
WANT_ACCEPT,
}
[CCode (has_target = false)]
public delegate uint PskClientCallback (SSL ssl, string? hint, char[] identity, uint8[] psk);
[Compact]
[CCode (cname = "SSL_METHOD", cprefix = "SSL_METHOD_", free_function = "")]
[CCode (cheader_filename = "openssl/ssl.h", cname = "SSL_METHOD", cprefix = "SSL_METHOD_", free_function = "")]
public class SSLMethod {
[CCode (cname = "TLS_method")]
public static unowned SSLMethod tls ();
@@ -108,6 +131,23 @@ namespace OpenSSL {
public BasicIO.from_static_memory_buffer (uint8[] buf);
public long get_mem_data ([CCode (array_length = false)] out unowned uint8[] data);
public int read (uint8[] buf);
public int write (uint8[] buf);
public size_t ctrl_pending ();
[CCode (cname = "BIO_new")]
public static void * raw_new (BasicIOMethod method);
[CCode (cname = "BIO_free")]
public static int raw_free (void * bio);
[CCode (cname = "BIO_read")]
public static int raw_read (void * bio, uint8[] buf);
[CCode (cname = "BIO_write")]
public static int raw_write (void * bio, uint8[] buf);
[CCode (cname = "BIO_ctrl_pending")]
public static size_t raw_ctrl_pending (void * bio);
[CCode (cname = "BIO_get_mem_data")]
public static long raw_get_mem_data (void * bio, [CCode (array_length = false)] out unowned uint8[] data);
}
[Compact]
@@ -162,7 +202,7 @@ namespace OpenSSL {
[Compact]
[CCode (cname = "EVP_PKEY", cprefix = "EVP_PKEY_", copy_function = "EVP_PKEY_dup")]
public class Key {
[CCode (cname = "d2i_PUBKEY_bio")]
[CCode (cheader_filename = "openssl/x509.h", cname = "d2i_PUBKEY_bio")]
public Key.from_der (BasicIO source, Key ** a = null);
[CCode (cname = "EVP_PKEY_new_raw_public_key")]
public Key.from_raw_public_key (KeyType type, Engine? engine, uint8[] pub);
@@ -172,7 +212,7 @@ namespace OpenSSL {
public int get_raw_public_key ([CCode (array_length = false)] uint8[]? pub, ref size_t len);
public int get_raw_private_key ([CCode (array_length = false)] uint8[]? priv, ref size_t len);
[CCode (cname = "i2d_PUBKEY_bio", instance_pos = 2)]
[CCode (cheader_filename = "openssl/x509.h", cname = "i2d_PUBKEY_bio", instance_pos = 2)]
public int to_der (BasicIO sink);
[CCode (cname = "PEM_write_bio_PUBKEY", instance_pos = 2)]
@@ -534,7 +574,7 @@ namespace OpenSSL {
public int memcmp (void * a, void * b, size_t len);
}
[CCode (lower_case_cprefix = "ERR_")]
[CCode (cheader_filename = "openssl/err.h", lower_case_cprefix = "ERR_")]
namespace Error {
public ulong get_error ();
public void error_string_n (ulong e, char[] buf);