Compare commits

...
258 Commits
Author SHA1 Message Date
Ole André Vadla Ravnås 3e75fc9b84 subprojects: Prepare for release 2025-03-31 21:22:52 +02:00
Ole André Vadla Ravnås 1b6ca8fe46 subprojects: Bump outdated 2025-03-31 20:17:34 +02:00
Ole André Vadla Ravnås 9e35e245b1 host-session: Add support for remote services
So they are no longer exclusively an out-of-band local API. This means
that ControlService/frida-server can serve service sessions.
2025-03-31 20:10:49 +02:00
Ole André Vadla Ravnås 4a8c0bb13b api: Expose sealed classes 2025-03-31 20:10:49 +02:00
Ole André Vadla Ravnås 7c39f1783a stream: Factor out IOStream commonalities 2025-03-31 20:10:49 +02:00
Ole André Vadla Ravnås 194f025a98 host-session: Add support for remote channels
So they are no longer exclusively an out-of-band local API. This means
that ControlService/frida-server can serve channels.
2025-03-31 20:10:46 +02:00
Ole André Vadla Ravnås e6b2a6d6dd session: Remove unused ObjectPath constant 2025-03-30 16:26:58 +02:00
Ole André Vadla Ravnås e66b93b647 host-session: Factor out duplicated local logic 2025-03-28 17:18:35 +01:00
Ole André Vadla Ravnås 4a887eaed5 host-session: Rename BaseDBusHostSession to LocalHostSession
For clarity.
2025-03-28 17:18:17 +01:00
Ole André Vadla Ravnås 180075a7a0 control-service: Fix session logic for remote device 2025-03-28 15:29:14 +01:00
Ole André Vadla Ravnås 20ebf7fa15 host-session-provider: Add unlink_agent_session()
To support use-cases such as ControlService, where one specific client
gets disconnected and we need to tear down its agent sessions.
2025-03-28 15:29:11 +01:00
Ole André Vadla Ravnås 1018aca28d compiler: Bump frida-compile 2025-03-25 20:14:26 +01:00
Ole André Vadla Ravnås 93870b9f66 compiler: Bump @types/frida-gum 2025-03-24 17:33:23 +01:00
Ole André Vadla Ravnås 259ce04fe6 subprojects: Prepare for release 2025-03-21 14:20:50 +01:00
Ole André Vadla Ravnås 4cdfa4b09c subprojects: Bump outdated 2025-03-21 14:19:27 +01:00
Ole André Vadla Ravnås 7b54e41f88 droidy: Fix the MAX_MESSAGE_LENGTH declaration
The new maximum exceeds the range of a uint16.
2025-03-21 14:18:59 +01:00
Ole André Vadla Ravnås c7b1e17c3e subprojects: Prepare for release 2025-03-21 13:57:14 +01:00
Ole André Vadla Ravnås e844297a61 subprojects: Bump outdated 2025-03-20 20:38:52 +01:00
Ole André Vadla Ravnås 30b00eaa64 subprojects: Bump outdated 2025-03-20 17:00:25 +01:00
Ole André Vadla Ravnås 2109c38e06 submodules: Bump releng 2025-03-20 17:00:25 +01:00
Ole André Vadla Ravnås 1a3da37cf8 compiler: Bump @types/frida-gum 2025-03-20 16:59:53 +01:00
Jan S. 3d9fab2b0d fruity: Fix Input/Output Error on Windows (#1144)
By skipping devices with an empty UDID.
2025-03-19 15:12:07 +01:00
philippmao c58212fe8c droidy: Support more than ~8 ADB-connected devices (#1145)
By bumping the message size limit.
2025-03-19 15:07:44 +01:00
Ole André Vadla Ravnås bce2650980 subprojects: Bump outdated 2025-03-19 10:51:37 +01:00
Ole André Vadla Ravnås 0f03a0ee19 submodules: Bump releng 2025-03-19 10:51:36 +01:00
Ole André Vadla Ravnås d9b4d20adf linux: Include linux/unistd.h for syscall constant
Which, depending on the libc, may not be implicitly included.
2025-03-18 19:11:03 +00:00
Ole André Vadla Ravnås a8db90d6a5 subprojects: Bump outdated 2025-03-18 19:51:10 +01:00
Ole André Vadla Ravnås edcb76f066 submodules: Bump releng 2025-03-18 19:51:09 +01:00
Ole André Vadla Ravnås 1f72d5f102 subprojects: Prepare for release 2025-03-13 19:09:35 +01:00
Ole André Vadla Ravnås 8336d42b94 server: Create DeviceManager without local backend
Since we're pulling it in manually in order to run the frida-helper
in-process, we want to avoid having both implementations.

This reduces our footprint from 58M down to 49M on macOS, up from 46M
before the --device feature.
2025-03-13 19:06:33 +01:00
Ole André Vadla Ravnås 9404147517 device-manager: Support running w/o local backend
So consumers that don't need it can get a smaller footprint.
2025-03-13 19:01:20 +01:00
Ole André Vadla Ravnås ed464e9ccf subprojects: Bump outdated 2025-03-13 15:07:33 +01:00
Ole André Vadla Ravnås ce6178e125 server: Add --device for serving a specific device 2025-03-13 14:43:36 +01:00
Ole André Vadla Ravnås 0e69ec3e77 control-service: Support serving a specific Device 2025-03-13 14:42:57 +01:00
Ole André Vadla Ravnås 4eba770491 subprojects: Bump outdated 2025-03-13 14:09:11 +01:00
WorksButNotTested dc022e4bbe linux: Build helpers for arm64be and arm64beilp32 (#1141) 2025-03-13 13:47:09 +01:00
WorksButNotTested df04bb40c3 linux: Add support for arm64beilp32 (#1140) 2025-03-12 14:26:54 +01:00
WorksButNotTested 0f685ea496 linux: Support building helpers for armbe8 (#1139) 2025-03-12 10:07:43 +01:00
WorksButNotTested 48bfb1e054 linux: Add support for arm64be 2025-03-12 09:33:04 +01:00
Ole André Vadla Ravnås ab8a0a3927 compiler: Bump @types/frida-gum 2025-03-12 08:55:15 +01:00
Ole André Vadla Ravnås 9594e1a8b8 linux: Handle compat ranges during injection
So our spurious range detection doesn't make an incomplete candidate,
and we might end up picking the wrong one.
2025-03-03 23:38:47 +01:00
Ole André Vadla Ravnås 60a6770dba darwin: Fix racy leakage of memory during teardown
Where DarwinHelperBackend.close() would cancel dispatch sources, but
return before the cancel handler got a chance to run. While this leak
typically happened on teardown and wasn't necessarily a big deal
locally, the worse part was that we also leaked large regions of memory
in remote processes that we uninjected from just before teardown.
2025-02-28 17:34:49 +01:00
Ole André Vadla Ravnås c5376242e9 darwin: Fix HelperBackend MainContext assumptions
We should not blindly dispatch to the default one, but take note at
construction time.

This is to avoid surprises down the road, and to improve consistency
across the codebase.
2025-02-28 17:34:49 +01:00
Ole André Vadla Ravnås 17d6161435 linux: Avoid spurious .so ranges during injection
By checking that the mapping starts at file offset zero, and considering
all candidates ranked by the number of mappings, and whether executable
mappings exist in a given cluster.

Now that Gum.Module objects live longer, and may have an Gum.ElfModule
that has the ELF mapped, there's a much higher likelihood of running
into such spurious ranges.
2025-02-20 10:18:44 +01:00
Ole André Vadla Ravnås 26d9b9b6b1 fruity: Fix crash in the Linux getifaddrs() logic
Which failed to handle interfaces without an address.
2025-02-20 08:48:30 +01:00
Ole André Vadla Ravnås 1ed5590c77 submodules: Bump releng 2025-02-18 11:15:09 +01:00
Ole André Vadla Ravnås 25a283e43b ci: Fix the linux-x86 job
Now that ubuntu-latest refers to a newer Ubuntu release, we need to
move to the newer lib32stdc++ package version that matches that of
the gcc-multilib package.
2025-02-18 11:12:51 +01:00
Ole André Vadla Ravnås 04e04166b7 subprojects: Bump outdated 2025-02-18 11:12:51 +01:00
Ole André Vadla Ravnås 2c6c52d22a darwin: Port to the new ModuleResolver API 2025-02-18 11:12:51 +01:00
Ole André Vadla Ravnås ba7d01f232 subprojects: Prepare for release 2025-01-27 20:54:28 +01:00
Ole André Vadla Ravnås 8a16d42652 subprojects: Bump outdated 2025-01-27 20:51:53 +01:00
Ole André Vadla Ravnås 9c13120d67 fruity: Fix lockdown over CoreDevice regression
Introduced by the recent change in d1df792, where our RSDCheckin
includes an EscrowBag in order to support networked lockdown with
services such as “com.apple.crashreportmover”.

Turns out this broke support for quite a few other services, as the
presence of an EscrowBag requires that the particular service is able
to talk to AppleKeyStoreUserClient.

Not yet sure how to handle this. For now we'll maintain a list of
services that lack such privileges, so we can omit the EscrowBag for
those.

Kudos to @as0ler for reporting and helping troubleshoot.
2025-01-27 19:35:52 +01:00
Ole André Vadla Ravnås e70812a1e0 subprojects: Prepare for release 2025-01-23 20:46:57 +01:00
Ole André Vadla Ravnås 283381ca9e subprojects: Bump outdated 2025-01-23 20:31:30 +01:00
Mohamed Amine Amor 72a96226ce linux: Avoid risky code swaps with memfd regions
As they may not be writable, and unlike regular regions, ptrace() won't
help us in case of a missing writable bit.
2025-01-23 19:13:59 +01:00
Mohamed Amine Amor b6577afe81 linux: Relax injector's libc matching for Android
Due to how APEXes are bind-mounted.
2025-01-23 19:13:59 +01:00
Ole André Vadla Ravnås d1df7922bc fruity: Support networked lockdown for CoreDevice
Where we need to provide the remote unlock host key as part of the
RSDCheckin.

Kudos to @as0ler and @mrmacete for reporting and helping get to the
bottom of this one.
2025-01-23 18:41:01 +01:00
Ole André Vadla Ravnås af9c36ce6e xpc: Add support for Data objects 2025-01-23 15:27:47 +01:00
Ole André Vadla Ravnås da58033e9b subprojects: Prepare for release 2025-01-17 16:49:07 +01:00
Ole André Vadla Ravnås 702f3e83eb subprojects: Bump outdated 2025-01-17 16:46:54 +01:00
WorksButNotTested 7644f3dc6d linux: Revive support for ARM BE8 (#1130) 2025-01-17 16:30:59 +01:00
Ole André Vadla Ravnås 24d2d6f055 subprojects: Prepare for release 2025-01-14 19:35:29 +01:00
Ole André Vadla Ravnås 47d44f07ae subprojects: Bump outdated 2025-01-14 19:33:17 +01:00
Ole André Vadla Ravnås 8119fc7569 submodules: Bump releng 2025-01-14 19:33:17 +01:00
Ole André Vadla Ravnås 57409c8056 windows: Port injector to new Gum.Module API 2025-01-14 18:09:55 +01:00
Ole André Vadla Ravnås f497a764b6 subprojects: Prepare for release 2025-01-13 22:20:39 +01:00
Ole André Vadla Ravnås 20b7856fae subprojects: Bump outdated 2025-01-13 22:18:02 +01:00
Ole André Vadla Ravnås f2a81c03e3 subprojects: Prepare for release 2025-01-10 02:04:08 +01:00
Ole André Vadla Ravnås 79b34b9772 subprojects: Bump outdated 2025-01-10 02:02:04 +01:00
Ole André Vadla Ravnås 2fb702ba80 subprojects: Bump outdated 2025-01-09 23:35:23 +01:00
Ole André Vadla Ravnås 262d8f91e5 submodules: Bump releng 2025-01-09 23:35:22 +01:00
Ole André Vadla Ravnås 7638290dcb agent: Exclude OS/arch symbols in version script
Newer toolchains, such as the default toolchain on FreeBSD 14.2,
don't like references to symbols that don't exist. Instead of
listing JNI_OnLoad, which is only defined for Android builds,
we use a separate version script for Android instead.
2025-01-09 23:34:01 +01:00
Ole André Vadla Ravnås 1e0f37faf6 subprojects: Prepare for release 2025-01-09 13:18:38 +01:00
Ole André Vadla Ravnås fb46efaa12 compiler: Bump @types/frida-gum 2025-01-09 13:16:05 +01:00
Ole André Vadla Ravnås bb4a906c16 subprojects: Bump outdated 2025-01-08 19:06:43 +01:00
Ole André Vadla Ravnås 34eff8e9d6 darwin: Fix iOS/tvOS compilation issues
By porting the remaining bits to the new Gum.Module API.
2025-01-06 16:37:55 +01:00
Ole André Vadla Ravnås f28e5d4493 subprojects: Bump outdated 2025-01-06 16:03:28 +01:00
Ole André Vadla Ravnås 6f3434b454 subprojects: Bump outdated 2025-01-06 01:11:39 +01:00
Ole André Vadla Ravnås 90c2420bae submodules: Bump releng 2025-01-06 01:11:38 +01:00
Ole André Vadla Ravnås 95394935cd Port to the new Gum.Module API 2025-01-06 01:09:07 +01:00
Ole André Vadla RavnåsandHåvard Sørbø a2ffb76ff3 ncm: Cap datagram count to 16 for performance
The layout computation could end up working its way down from a very
high number. Though we probably should optimize the layout computation,
there's no point for now.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-12-17 23:38:37 +01:00
Ole André Vadla Ravnås c1472f9cb8 Create FUNDING.yml 2024-12-12 13:37:04 +01:00
Ole André Vadla Ravnås aabe70d041 subprojects: Bump outdated 2024-12-09 00:58:03 +01:00
Ole André Vadla Ravnås abc464bf49 subprojects: Prepare for release 2024-12-09 00:52:26 +01:00
Ole André Vadla Ravnås b6d7c7259f subprojects: Prepare for release 2024-12-09 00:32:18 +01:00
Ole André Vadla Ravnås 78750a4644 subprojects: Bump outdated 2024-12-09 00:28:54 +01:00
Ole André Vadla RavnåsandHåvard Sørbø a68dc36d2d fruity: Boost NCM performance with multi-transfers
Co-authored-by: Håvard Sørbø <[email protected]>
2024-12-08 22:45:07 +01:00
Ole André Vadla Ravnås de54b7e4cf subprojects: Bump outdated 2024-12-08 16:31:53 +01:00
Ole André Vadla Ravnås 1f292bb856 submodules: Bump releng 2024-12-08 16:31:52 +01:00
Ole André Vadla Ravnås 2025b7aaad fruity: Enable lwIP TCP timestamps and SACK
To align with the Linux IP stack's defaults.
2024-12-08 03:23:34 +01:00
Ole André Vadla Ravnås 534d860a42 meson: Bump lwIP TCP MSS to 4036
For better TCP tunnel performance.
2024-12-08 03:22:06 +01:00
Ole André Vadla Ravnås db28eb0e70 fruity: Fix crash on USB op creation during teardown 2024-12-08 03:20:39 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 8d0b735a75 fruity: Improve userspace NCM driver to perform batching
As we observed packet loss in bursty situations, and implementing
batching avoids it.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-12-08 01:55:38 +01:00
Ole André Vadla Ravnås 930776dcca compat: Create relative subproject symlinks
So the source tree can be moved without breaking things.
2024-12-07 16:08:52 +01:00
Ole André Vadla Ravnås 7a38663672 compat: Fix subproject symlink_to() error-handling 2024-12-07 15:55:22 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 3779ce9f64 vapi: Add SocketAddressUnion to libngtcp2.vapi
Co-authored-by: Håvard Sørbø <[email protected]>
2024-12-05 15:24:13 +01:00
Ole André Vadla RavnåsandHåvard Sørbø b5065e275d fruity: Improve USB device handling on non-macOS
- Do not open a given USB device if kernel NCM is available, as opening
  may fail and won't actually do anything useful in the end.
- Throw when a kernel NCM interface lacks an IPv6 address, instead of
  silently degrading to legacy mode.
- Replace retry logic with a fixed 250 ms delay, applied when a device
  arrives after starting, or during a mode-switch.
- Change USB device configuration if needed.
- Wait for kernel NCM interface to be ready. For now this is limited to
  Linux systems with either NetworkManager or networkd.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-12-05 15:23:24 +01:00
Ole André Vadla RavnåsandHåvard Sørbø a682e51f37 fruity: Only assume jailed if connection refused
Bubble up all other errors.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-12-05 15:23:06 +01:00
Ole André Vadla RavnåsandHåvard Sørbø bdf120016a api: Avoid redundant writes to output files
Modify the output logic to skip writing to files if the contents remain
unchanged, preventing unnecessary file updates. This means quicker
incremental builds.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-12-05 15:23:00 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 61eb12f718 meson: Avoid parsing API multiple times
As Meson's custom_target() supports multiple outputs.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-12-05 15:22:54 +01:00
Ole André Vadla RavnåsandHåvard Sørbø e149f5e332 fruity: Avoid USB access in case of kernel NCM
So we won't run into permission issues, and avoid unneccessary work.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-11-26 23:04:10 +01:00
Ole André Vadla RavnåsandHåvard Sørbø ef4fa0de7d fruity: Account for frida-server bind() delay
Where we just opened the tunnel, but frida-server hasn't yet noticed the
new interface and had a chance to bind() on it. This means we should
wait a bit and try again. Otherwise we may end up falling back to
usbmuxd, and use a less desirable transport. And if we're really
unlucky, even that could fail, and we'd end up falling back to jailed
mode.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-11-26 22:02:47 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 1bd9d389b8 fruity: Fix direct channel reliability
Where we might establish the HostSession connection through usbmuxd, and
proceed to establish direct connections through the tunnel. This is
usually fine, except when an Apple service happens to bind to the same
port inside the tunnel. In that case we'd end up talking to that instead
of the broker port, which is listening on INADDR_ANY.

Kudos to @mrmacete for helping track this one down.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-11-26 19:43:42 +01:00
Ole André Vadla Ravnås b32408c607 device: Fix the constructor logic
Broken by the refactoring in 2790745.
2024-11-24 15:32:58 +01:00
Ole André Vadla Ravnås a8e01c2940 Fix some stylistic inconsistencies 2024-11-24 15:32:14 +01:00
Ole André Vadla Ravnås cd6366a4eb tests: Fix the HostSession suite 2024-11-24 00:58:28 +01:00
Ole André Vadla Ravnås 2c1cf1ec58 api: Add GIR attributes 2024-11-24 00:34:32 +01:00
Ole André Vadla Ravnås 279074501c api: Avoid exposing internal types 2024-11-24 00:33:17 +01:00
Ole André Vadla Ravnås 20cfd56a7a api: Generate a proper gir
- Needed types in frida-base included.
- Internal types omitted.
2024-11-23 23:54:20 +01:00
Ole André Vadla Ravnås d03bda5545 api: Omit APIs involving HostSession 2024-11-23 15:14:37 +01:00
Ole André Vadla Ravnås 951796196c api: Refactor generate.py to improve readability 2024-11-23 15:14:33 +01:00
Ole André Vadla Ravnås 312e38b2ec fruity: Improve TcpTunnelConnection teardown
So the VirtualNetworkStack is torn down right away upon the remote end
closing the connection, instead of once close() is called.

Also ensure any pending write operation is cancelled at that point.
2024-11-22 14:50:31 +01:00
Ole André Vadla Ravnås 36a8ffbb99 subprojects: Prepare for release 2024-11-14 00:30:38 +01:00
Ole André Vadla Ravnås aeb2c0f6e4 subprojects: Bump outdated 2024-11-14 00:28:54 +01:00
Ole André Vadla Ravnås d5591cc453 submodules: Bump releng 2024-11-14 00:28:54 +01:00
lx866 20f4df23a8 android: Revive injector's handling of shared libc ranges
So we still do the right thing if the target process' lowest libc.so
range is a shared mapping, for example:

    HNFRI:/ # grep libc.so /proc/22001/maps
    6e3e1cd000-6e3e2c7000 r--s 00000000 07:08 38 /apex/com.android.runtime/lib64/bionic/libc.so
    71eb6c2000-71eb6ff000 r--p 00000000 07:08 38 /apex/com.android.runtime/lib64/bionic/libc.so
    71eb6ff000-71eb781000 r-xp 0003d000 07:08 38 /apex/com.android.runtime/lib64/bionic/libc.so
    71eb781000-71eb786000 r--p 000bf000 07:08 38 /apex/com.android.runtime/lib64/bionic/libc.so
    71eb786000-71eb787000 rw-p 000c3000 07:08 38 /apex/com.android.runtime/lib64/bionic/libc.so

Originally implemented in 5e8314f, but lost in the injector rewrite.
2024-11-14 00:21:58 +01:00
JunGe-Y 89033cd1e8 compat: Fix compilation when using non-UTF-8 locales (#1122) 2024-11-13 23:55:57 +01:00
Ole André Vadla Ravnås a027f5b08e fruity: Skip jailed fallback in case of timeout 2024-11-13 20:37:07 +01:00
Ole André Vadla Ravnås 848f4b52e1 fruity: Expect networked device to respond quickly
When connecting to its pairing service, it should be reasonable to
assume that the TCP connection set up completes within two seconds.

This improves the UX when a device has gone to sleep, and its mDNS
records haven't yet timed out.
2024-11-13 20:35:42 +01:00
Ole André Vadla Ravnås e0524ad90b network-stack: Add open_tcp_connection_with_timeout() 2024-11-13 20:28:09 +01:00
Ole André Vadla Ravnås 5ce8ce0531 fruity: Always try USB transport first
As a network transport may be slower (best-case) or unavailable
(worst-case).
2024-11-13 20:15:52 +01:00
Ole André Vadla Ravnås 8f04f2f677 fruity: Fix TcpTunnelConnection teardown logic
Where it turns out IOStream.close_async() may throw other errors than
just IOError. This is a bug in gio-2.0.vapi, but still, once fixed there
we still want to catch (and ignore) all errors.

Kudos to @mrmacete for helping track this one down.
2024-11-13 18:03:38 +01:00
Ole André Vadla RavnåsandHåvard Sørbø f0593c2aec fruity: Fix QuicTunnelConnection teardown logic
And improve the ngtcp2 bindings.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-11-13 16:32:04 +01:00
Ole André Vadla RavnåsandHåvard Sørbø ec582967e8 fruity: Fix hang when a USB op starts at teardown
Which would result in such transfers never completing, as our USB worker
is no longer processing libusb events.

Kudos to @mrmacete for helping track this one down.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-11-13 13:23:39 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 4fce1c4321 fruity: Gracefully close TunnelConnection
Co-authored-by: Håvard Sørbø <[email protected]>
2024-11-06 13:39:42 +01:00
Ole André Vadla Ravnås 667c7491b5 fruity: Relax NCM interface presence check
One operational network interface is sufficient.
2024-11-06 10:47:39 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 85de7a01fb fruity: Add support for the TCP tunnel protocol
And use it by default, to match Apple's new behavior.

The FRIDA_FRUITY_TUNNEL_PROTOCOL environment variable can be used to
revert back to QUIC.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-11-06 10:47:03 +01:00
Ole André Vadla Ravnås 8fdff55b80 subprojects: Bump outdated 2024-11-06 10:21:11 +01:00
Ole André Vadla Ravnås a420358565 submodules: Bump releng 2024-11-06 10:21:10 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 2457ac45f6 fruity: Fix edge-case in tunnel logic for older OS versions
When usbmuxd is not available, we would try to set up a tunnel
connection. This would then fail when the OS is too old for CoreDevice
support, and we would erroneously cache the Tunnel implementation in its
unopened state. This meant it could be used as if it was opened, causing
undefined behavior.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-10-28 15:01:40 +01:00
Ole André Vadla Ravnås e797e829d8 fruity: Fix CoreDevice UDID logic for modern devices 2024-10-26 08:54:27 +02:00
WorksButNotTested aa95167156 linux: Pick other temp dir when $TMPDIR is noexec 2024-10-21 22:40:47 +02:00
Ole André Vadla Ravnås 03d609c1a5 linux: Improve helpers Makefile to use internal Meson 2024-10-21 14:55:20 +02:00
Harry Gunnarsson 36619e51e7 linux: Support overriding config when building helpers 2024-10-21 14:55:20 +02:00
Harry Gunnarsson e2b462823c linux: Improve injector's MUSL compatibility
The loader string could look different on different platforms.
2024-10-21 14:32:52 +02:00
Andrew Werner e8ee891924 linux: Allocate stack for injector's remote calls
For some target programs it's not reasonable to assume that any hijacked
thread has a large stack. For example, in Go, stacks are often small and
are allocated on the heap. The injector bootstrap program uses kilobytes
of stack. In order to side-step this problem, this patch changes the
boostrapper to allocate an auxiliary stack for remote calls to use and,
for the bootstrapper and loader, uses it.

The calls to mmap and munmap don't use much stack, so they are fine.

Fixes #544.
2024-10-21 14:07:00 +02:00
Andrew Werner d0fe3a1ae9 linux: Rebuild helper bin files
This commit utilizes the containers created in [frida/docker-images#2]
to rebuild all the Linux injector helper binaries for all the
architectures. It uses a new script to rebuild them and hooks up CI to
rebuild them and make sure that they are all the same as what is checked
in. This should allow us to make changes to these binaries without any
pain and with the confidence that what is checked in matches the source.

[frida/docker-images#2]: https://github.com/frida/docker-images/pull/2
2024-10-20 17:09:55 +02:00
Ole André Vadla Ravnås b223fc4235 darwin: Update to the new RpcClient.call() API 2024-10-20 16:42:12 +02:00
Ole André Vadla Ravnås 5ad248a894 android: Update to the new RpcClient.call() API 2024-10-20 16:40:56 +02:00
Yoav ShaiandOle André Vadla Ravnås d774d0e064 gadget: Support loading assets from APK on Android
On Android, when apps have `extractNativeLibs` set to `false`, the
configuration and potentially script files for frida-gadget are not
extracted and cannot be read from the filesystem.

Co-authored-by: Ole André Vadla Ravnås <[email protected]>
2024-10-20 16:40:15 +02:00
WorksButNotTested e11938c11f inject: Support binary data on stdin and stdout 2024-10-20 12:11:31 +02:00
WorksButNotTested fb0049af86 barebone: Support binary data in Script.post() 2024-10-20 12:11:31 +02:00
WorksButNotTested 2fe6922b25 rpc-client: Support binary data in call() 2024-10-20 12:11:31 +02:00
Ole André Vadla Ravnås c96d94f796 subprojects: Prepare for release 2024-10-14 15:32:15 +02:00
Ole André Vadla Ravnås a8b710bc54 subprojects: Bump outdated 2024-10-14 15:30:11 +02:00
Håvard SørbøandOle André Vadla Ravnås 2b0c4da879 fruity: Fix use-after-free in TcpConnection
The error callback might be called at a point where the PCB has already
been freed. This meant that us clearing its user data would result in a
use-after-free where a NULL pointer was written into the unknown.

Co-authored-by: Ole André Vadla Ravnås <[email protected]>
2024-10-14 13:24:37 +00:00
Håvard SørbøandOle André Vadla Ravnås d0129614cd fruity: Fix DTXArgumentList.parse() GValue init
Where we were using the wrong setter when encountering an object. This
was caught by GLib's runtime checks, but this went unnoticed because we
usually build without them.

Co-authored-by: Ole André Vadla Ravnås <[email protected]>
2024-10-14 13:22:41 +00:00
Håvard SørbøandOle André Vadla Ravnås 12348209da payload: Fix AddressSanitizer build regression
Co-authored-by: Ole André Vadla Ravnås <[email protected]>
2024-10-14 13:19:56 +00:00
Ole André Vadla Ravnås 0451ce09b2 subprojects: Prepare for release 2024-10-11 11:18:27 +02:00
Ole André Vadla Ravnås 0fa9765871 subprojects: Bump outdated 2024-10-11 11:16:06 +02:00
ajwerner 71e1068150 ci: Download proper ndk version (#551)
We were relying on the version built into the image being correct. It is
now too new.
2024-10-11 11:13:44 +02:00
Francesco Tamagni 690cc30260 control-service: Fix null dereference on incoming connection (#1060) 2024-10-11 11:12:15 +02:00
Ole André Vadla Ravnås 557aefc898 subprojects: Prepare for release 2024-10-10 22:07:51 +02:00
Ole André Vadla Ravnås 088edc09d7 subprojects: Bump outdated 2024-10-10 22:06:52 +02:00
Ole André Vadla Ravnås 19f6a2f378 compiler: Bump @types/frida-gum 2024-10-10 21:30:42 +02:00
Ole André Vadla Ravnås 16c9035bca subprojects: Prepare for release 2024-10-10 20:18:24 +02:00
Ole André Vadla Ravnås fbb46912db subprojects: Bump outdated 2024-10-10 20:08:54 +02:00
Ole André Vadla Ravnås 11aa03695f submodules: Bump releng 2024-10-10 20:08:53 +02:00
Håvard SørbøandOle André Vadla Ravnås a23fba6ee9 fruity: Bail on iOS tunnel service versions pre-17
Instead of crashing.

Co-authored-by: Ole André Vadla Ravnås <[email protected]>
2024-10-10 18:02:07 +00:00
Ole André Vadla RavnåsandHåvard Sørbø 364bf65c59 control-service: Fix reliability on CoreDevice systems
Where having a single transport broker listening on all interfaces may
result in an early TCP RST when trying to communicate with it from
inside a tunnel. The exact cause of this is not known, but we have
confirmed that having one broker per dynamic interface/tunnel does
resolve the issue. We also observed that listening on all interfaces,
but restricted to IPv6, also avoids the issue.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-10-10 19:03:41 +02:00
Ole André Vadla RavnåsandHåvard Sørbø dcdfcf53d1 fruity: Fix USB mode parsing on iOS < 16
Where the mode is a 3 byte blob.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-10-10 18:46:44 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 2698d7dd1d meson: Fix i/tvOS compilation with GLib as a subproject
Co-authored-by: Håvard Sørbø <[email protected]>
2024-10-10 18:45:28 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 208aec031c web-service: Close connections of removed dynamic interfaces
To avoid them sticking around until we run out of file-descriptors.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-10-08 13:15:10 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 03e70d26b7 socket: Transition GSource callbacks to Source.REMOVE
Co-authored-by: Håvard Sørbø <[email protected]>
2024-10-08 13:14:20 +02:00
Ole André Vadla Ravnås b22507ed32 meson: Disable Fruity and friends on FreeBSD
As we currently rely on a libusb API that only exists in our libusb.
2024-10-05 19:53:26 +00:00
Ole André Vadla Ravnås 1c4902e72d fruity: Use USB product string as transport name 2024-10-05 19:36:34 +00:00
Ole André Vadla RavnåsandHåvard Sørbø e5eaedaf85 fruity: Bubble up USB permission errors
Co-authored-by: Håvard Sørbø <[email protected]>
2024-10-05 19:36:31 +00:00
Ole André Vadla Ravnås 944380225a subprojects: Bump outdated 2024-10-05 17:24:56 +02:00
Ole André Vadla Ravnås f84f2887d2 submodules: Bump releng 2024-10-05 17:24:55 +02:00
Ole André Vadla Ravnås e67d513d06 subprojects: Bump outdated 2024-10-05 16:14:44 +02:00
Ole André Vadla Ravnås 558628ad5a submodules: Bump releng 2024-10-05 16:14:43 +02:00
Ole André Vadla Ravnås 9c27b7b453 subprojects: Bump outdated 2024-10-05 16:12:48 +02:00
Ole André Vadla Ravnås 441636acd1 submodules: Bump releng 2024-10-05 16:12:47 +02:00
Ole André Vadla Ravnås 3238e2268a network-stack: Fix racy TCP data loss upon peer closure
Where the closure resulted in us letting go of the PCB, and the recv()
logic would return early because the PCB was gone. At that point there
might still be data left in the RX buffer, but the application wouldn't
see it.
2024-10-03 10:45:36 +02:00
Ole André Vadla Ravnås 66099c705f network-stack: Handle abrupt disposal of TcpConnection
Where we would free the PCB TCB but fail to notify any live TcpIOSource
instances and blocked TcpInputStream.read() calls.
2024-09-20 19:06:33 +02:00
Ole André Vadla Ravnås ca8519cec7 network-stack: Fix race in TcpInputStream.read()
Where the notify::pending-io signal fires after the call to
is_readable() but before our signal handler is connected.
2024-09-20 19:01:04 +02:00
Ole André Vadla Ravnås 416d2ea388 subprojects: Prepare for release 2024-09-19 00:00:49 +02:00
Ole André Vadla Ravnås 02e83a6d4a subprojects: Bump outdated 2024-09-18 23:57:30 +02:00
Ole André Vadla Ravnås 539f359a1f fruity: Handle USB TunnelConnection dropping
Which happens on e.g. an iOS userspace reboot.
2024-09-18 23:49:56 +02:00
Ole André Vadla Ravnås 24dc7f4a3d fruity: Fix unreliable modeswitch activation
Where its Promise could be resolved more than once.
2024-09-18 23:14:35 +02:00
Ole André Vadla Ravnås f48ced3a4a subprojects: Prepare for release 2024-09-18 14:33:15 +02:00
Ole André Vadla Ravnås 54829052bd subprojects: Bump outdated 2024-09-18 14:31:38 +02:00
Ole André Vadla Ravnås 73abf705b1 submodules: Bump releng 2024-09-18 14:31:37 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 7e21f5cb1d fruity: Advertise our RemoteXPC protocol support
Like macOS does, but with most of the properties omitted.

We do this to more closely mimic macOS' behavior, and to avoid an error
ending up in the syslog:

    Invalid or missing remote device connection version flags

Co-authored-by: Håvard Sørbø <[email protected]>
2024-09-18 14:30:21 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 24a1f79b02 fruity: Handle iDevice not paired with anyone
Where the pairVerify step fails early.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-09-18 14:25:55 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 2d990888d9 fruity: Fix teardown when using our NCM driver
Co-authored-by: Håvard Sørbø <[email protected]>
2024-09-17 18:34:21 +02:00
Ole André Vadla Ravnås 0fd255d3a4 fruity: Fix error marshaling in find_tunnel() for non-macOS 2024-09-17 17:15:35 +02:00
Ole André Vadla Ravnås fc500a19d9 fruity: Fix a stylistic inconsistency 2024-09-17 17:12:12 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 7280bbdbf7 fruity: Reuse NCM peer when tunnel setup fails
Co-authored-by: Håvard Sørbø <[email protected]>
2024-09-17 13:25:25 +02:00
Ole André Vadla Ravnås 9539981511 subprojects: Prepare for release 2024-09-06 22:59:50 +02:00
Ole André Vadla Ravnås 3005ab2aa5 subprojects: Bump outdated 2024-09-06 22:58:46 +02:00
Ole André Vadla Ravnås 140c5cf2cd subprojects: Prepare for release 2024-09-06 21:57:05 +02:00
Ole André Vadla Ravnås 6bb27c17e9 subprojects: Bump outdated 2024-09-06 21:54:47 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 0f2d9c8b40 network-stack: Fix deadlock in perform_on_lwip_thread()
Where in the case of already being on the lwIP thread, we would enqueue
the request and then perform whichever is the oldest pending request. So
when there was already one or more requests pending, we would perform
that request instead. Then next we would join() our request, assuming it
had completed, and end up deadlocking the lwIP thread.

Kudos to @mrmacete for reporting and helping track this one down.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-09-06 15:16:48 +02:00
Ole André Vadla Ravnås 5606120f6a subprojects: Bump outdated 2024-08-30 23:00:20 +02:00
ajwerner 2376f425a3 linux: Handle process gone when detaching (#543)
This is an attempt to handle the situation where the process has gone
away and we're shutting down. I've observed the system looping
attempting to move the state out of ATTACHED. It's possible that the
state machine here needs to be made more complex (ALREADY_ATTACHED is
misleading).

Informs #478.
2024-08-30 22:37:41 +02:00
Ole André Vadla Ravnås 1f9e24e1ea windows: Revive the system session 2024-08-26 12:26:25 +02:00
Ole André Vadla Ravnås 694118cccc subprojects: Bump outdated 2024-08-26 01:51:50 +02:00
Ole André Vadla Ravnås 9d51496124 submodules: Bump releng 2024-08-26 01:51:49 +02:00
Ole André Vadla Ravnås 27849af528 subprojects: Bump outdated 2024-08-25 23:54:37 +02:00
Ole André Vadla Ravnås 9df97f9e6b windows: Add support for arm64 2024-08-25 23:52:42 +02:00
Ole André Vadla Ravnås 79126f0065 subprojects: Bump outdated 2024-08-25 13:41:41 +02:00
Ole André Vadla Ravnås 4b0d1eab04 meson: Add frida-netif to the public frida-core
It's needed by ControlService.
2024-08-25 13:40:51 +02:00
Ole André Vadla Ravnås 31188db39a subprojects: Prepare for release 2024-08-22 15:40:45 +02:00
Ole André Vadla Ravnås de09ec42d5 subprojects: Bump outdated 2024-08-22 15:39:00 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 34c1548ced tunnel-connection: Ensure JSON request ends up in its own UDP packet
When opening the tunnel, it seems problematic if a datagram and stream
data end up in the same UDP packet. We make our dummy-datagram larger to
avoid this.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-08-22 15:35:07 +02:00
Ole André Vadla RavnåsandHåvard Sørbø b753294acb tunnel-connection: Wire up missing stream close logic
Co-authored-by: Håvard Sørbø <[email protected]>
2024-08-22 15:35:00 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 498c9be83e tunnel-connection: Consistently avoid writes after connection is gone
Which would result in a crash.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-08-22 15:32:30 +02:00
Ole André Vadla RavnåsandHåvard Sørbø bbcbe91b3e network-stack: Handle perform_on_lwip_thread() from the lwIP thread
Instead of deadlocking.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-08-22 15:29:11 +02:00
Ole André Vadla Ravnås ce80773d21 subprojects: Prepare for release 2024-08-20 21:50:41 +02:00
Ole André Vadla Ravnås e06da4d410 subprojects: Bump outdated 2024-08-20 21:48:20 +02:00
Francesco Tamagni 120dc491c4 tunnel-interface-observer: Fix start() crash on i/tvOS
When SCDynamicStoreCopyKeyList() fails.
2024-08-20 21:12:49 +02:00
Ole André Vadla Ravnås 2ebec1addf subprojects: Prepare for release 2024-08-02 14:05:13 +02:00
Håvard Sørbø 6a1e373ba2 fruity: Make find_usbmux_device() non-throwing 2024-08-02 14:03:03 +02:00
Ole André Vadla Ravnås 100b7a63d7 fruity: Guard against lockdown open<->close loop 2024-08-02 14:03:03 +02:00
Håvard Sørbø b3f2d1e7cc fruity: Fix invalidation of closed lockdown client 2024-08-02 14:03:03 +02:00
Håvard Sørbø 313cdc89f8 lockdown-client: Bubble up CONNECTION_CLOSED error 2024-08-02 14:03:03 +02:00
Ole André Vadla Ravnås 3c8221dd55 subprojects: Bump outdated 2024-07-29 11:00:36 +02:00
Ole André Vadla Ravnås dca8509960 submodules: Bump releng 2024-07-29 11:00:36 +02:00
Ole André Vadla Ravnås 78ff6e5b08 subprojects: Prepare for release 2024-07-23 10:14:56 +02:00
Ole André Vadla Ravnås 6537809897 subprojects: Bump outdated 2024-07-23 10:11:56 +02:00
Ole André Vadla Ravnås ead2ff6701 submodules: Bump releng 2024-07-23 10:11:56 +02:00
xiofee d55232fe52 fruity: Fix crash when enumerating network interfaces on Windows (#538)
Some don't have a unicast address, and need to be ignored.
2024-07-23 10:11:26 +02:00
Ole André Vadla Ravnås dab3b91f9e subprojects: Prepare for release 2024-07-22 11:49:03 +02:00
Ole André Vadla Ravnås 71f7b2f16a subprojects: Bump outdated 2024-07-22 11:37:45 +02:00
Ole André Vadla Ravnås 4cf806641f submodules: Bump releng 2024-07-22 11:37:44 +02:00
Ole André Vadla Ravnås e34a533a35 fruity: Handle devices without tunnel support on non-macOS
In the portable CoreDevice backend.
2024-07-22 11:28:23 +02:00
Ole André Vadla Ravnås 6b56c88295 fruity: Use UsbmuxDevice from USB transport if present
So we can reach the loopback interface, and for better performance.
2024-07-19 23:10:15 +02:00
Ole André Vadla Ravnås d810111f1e fruity: Expose name from USB transport if present
It has a more descriptive name in case of UsbmuxTransport.
2024-07-19 23:09:42 +02:00
Ole André Vadla Ravnås 3d17ad39da subprojects: Prepare for release 2024-07-17 22:43:10 +02:00
Ole André Vadla Ravnås 429607e7e3 xpc-client: Wire up cancellable in request()
As we can't cancel the request itself, we have to cancel the connection.
2024-07-17 22:34:24 +02:00
Ole André Vadla Ravnås 6ef660718a subprojects: Bump outdated 2024-07-17 22:10:01 +02:00
Ole André Vadla Ravnås cf61fda141 subprojects: Prepare for release 2024-07-16 15:24:54 +02:00
Ole André Vadla Ravnås b272dda27d subprojects: Bump outdated 2024-07-16 15:20:35 +02:00
Ole André Vadla Ravnås 300d176e85 ci: Re-enable tests on macOS 2024-07-16 15:14:43 +02:00
Ole André Vadla Ravnås e7f045bbe9 darwin: Await ObjC init on macOS Sequoia and iOS 18
By making use of notifyObjCInit() if available.
2024-07-16 11:45:16 +02:00
Ole André Vadla Ravnås 0b6abfeb7c darwin: Handle dyld restart on macOS Sequoia and iOS 18 2024-07-16 11:12:02 +02:00
Ole André Vadla Ravnås c17c6e31bc tests: Avoid relying on executable name on Darwin 2024-07-16 11:06:55 +02:00
Ole André Vadla Ravnås 0825aab1d0 fruity: Improve CoreDevice pairing support
- Fix support for multiple pairing relationships.
- Keep the in-memory peer store up-to-date, so new pairing relationships
  don't require a process restart to be able to match them up with
  pairing services on the network.
2024-07-15 14:41:03 +02:00
Ole André Vadla Ravnås be65296610 ncm: Fix a stylistic inconsistency 2024-07-15 13:24:16 +02:00
Ole André Vadla Ravnås 0a6002a3ce ncm: Avoid using a broken kernel NCM driver
Where only one of the two network interfaces exist, i.e. the tethering
interface.
2024-07-15 13:03:49 +02:00
Ole André Vadla Ravnås a3deb22999 ncm: Detach all drivers before changing configuration
We would previously only try to detach for the data interface, which
makes no sense if that interface is on a different configuration.
2024-07-15 12:54:14 +02:00
Ole André Vadla Ravnås 5aeb800a7a subprojects: Prepare for release 2024-07-13 00:34:04 +02:00
Ole André Vadla Ravnås a01f5dcd3d subprojects: Bump outdated 2024-07-13 00:06:51 +02:00
Ole André Vadla Ravnås 23b15b5f4b ncm: Switch USB device configuration if needed 2024-07-12 15:31:43 +02:00
Ole André Vadla Ravnås f1fdd980aa fruity: Add support for networked devices on Windows
By making use of DnsServiceBrowse() if the OS supports it -- which means
Windows 10 and newer.
2024-07-12 15:29:15 +02:00
Ole André Vadla Ravnås 05a68332f6 network-stack: Fix TcpConnection use-after-free
When lwIP notifies us about the PCB being closed or an error, we must
clear its user data so any future callbacks don't end up using the
TcpConnection after it's gone.
2024-07-12 00:06:17 +02:00
Ole André Vadla Ravnås 13521724f7 fruity: Remove commented-out variable 2024-07-11 23:57:55 +02:00
Ole André Vadla Ravnås 6c700a511e fruity: Split out DnsPacketReader 2024-07-11 23:14:01 +02:00
Ole André Vadla Ravnås 02ccafe516 fruity: Move LinuxPairingBrowser to ResolveService
Which is simpler and avoids some roundtrips to systemd-resolved.
2024-07-11 23:09:08 +02:00
Ole André Vadla Ravnås fb672d7a0b ci: Move the macOS x86_64 job to macos-latest
As the macos-11 runner is gone.

Also skip running tests on macOS entirely for now, as a stopgap until
__CFInitialize() has been dealt with.
2024-07-11 15:13:14 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 81fbfe2ab5 fruity: Add support for networked devices on Linux
By making use of systemd-resolved for mDNS-SD lookups.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-11 15:09:12 +02:00
Ole André Vadla Ravnås 3aef106ec2 fruity: Fix the LWIP.NetworkInterface bindings
Where we would end up passing in a copy of the struct and have e.g.
neighbor discovery timers make use of dangling pointers.
2024-07-11 15:09:12 +02:00
Ole André Vadla Ravnås 370f1836a7 dtx: Fix teardown of DTXConnection vs. DTXChannel
So channels never make use of the connection once it's gone.
2024-07-11 15:09:12 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 70bb82bb5d fruity: Remove forgotten .pcap debug output
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-11 15:09:12 +02:00
Ole André Vadla Ravnås dc43be94c5 buffer: Add BufferReader 2024-07-11 15:09:12 +02:00
Ole André Vadla Ravnås 7c7367b010 buffer: Add content validation to read_fixed_string()
To support parsing untrusted data.
2024-07-11 15:09:12 +02:00
Ole André Vadla Ravnås a9073c07eb buffer: Add content validation to read_string()
To support parsing untrusted data.
2024-07-11 15:09:12 +02:00
127 changed files with 8392 additions and 4012 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
github: frida
+42 -3
View File
@@ -18,7 +18,7 @@ jobs:
include:
- { id: windows-x86_64, runner: '"windows-latest"' }
- { id: windows-x86, runner: '"windows-latest"' }
- { id: macos-x86_64, runner: '"macos-11"' }
- { id: macos-x86_64, runner: '"macos-latest"' }
- { id: macos-arm64, runner: '"macos-latest"' }
- { id: linux-x86_64, runner: '"ubuntu-latest"' }
- { id: linux-x86, runner: '"ubuntu-latest"' }
@@ -44,7 +44,7 @@ jobs:
if: matrix.id == 'linux-x86'
run: |
sudo apt-get update
sudo apt-get install gcc-multilib lib32stdc++-11-dev
sudo apt-get install gcc-multilib lib32stdc++-13-dev
- name: Build
if: ${{ startsWith(matrix.id, 'windows-') }}
run: |
@@ -68,7 +68,6 @@ jobs:
name: core-devkit-${{ matrix.id }}
path: build/src/devkit/
- name: Test
if: matrix.id != 'macos-arm64'
run: make test
cross:
@@ -151,6 +150,14 @@ jobs:
certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
- uses: nttld/setup-ndk@v1
if: startsWith(matrix.id, 'android-')
id: setup-ndk
with:
ndk-version: r25c
- name: Add NDK to env
if: startsWith(matrix.id, 'android-')
run: echo ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }} >> $GITHUB_ENV
- name: Build
run: |
./configure --host=${{ matrix.id }} ${{ env.FRIDA_CORE_OPTIONS }}
@@ -234,3 +241,35 @@ jobs:
cp -a tests/frida-tests tests/labrats lib/agent/frida-agent.so /tmp/pkg/
tar -C /tmp/pkg -cf /tmp/runner.tar .
/opt/sabrelite/run.sh /tmp/runner.tar /opt/frida/frida-tests
# This job is used to check that the Linux helpers are built correctly for all
# supported architectures. We do this because the binary artifacts are checked
# in, and we want to ensure that they are up-to-date with respect to the source
# for all architectures.
check-linux-helpers:
strategy:
matrix:
arch: [x86, x86_64, arm, armbe8, arm64, arm64be, arm64beilp32, mips, mipsel, mips64, mips64el]
runs-on: ubuntu-latest
container: ghcr.io/frida/core-linux-helpers-${{ matrix.arch }}:latest
steps:
- name: Check out repo
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build
run: |
./src/linux/helpers/rebuild.sh ${{ matrix.arch }}
- name: Check for unexpected changes
run: |
git config --global --add safe.directory "$(realpath .)"
status_output="$(git status --porcelain)"
if [ -n "$status_output" ]; then
echo "Unexpected changes detected:"
echo "$status_output"
echo "Diff:"
git diff
exit 1
else
echo "No unexpected changes detected."
fi
+52 -7
View File
@@ -4,6 +4,7 @@ import base64
from collections import OrderedDict
from dataclasses import dataclass, field
import itertools
import locale
import os
from pathlib import Path
import pickle
@@ -14,7 +15,7 @@ import subprocess
import sys
import tempfile
import traceback
from typing import Any, Literal, Mapping, Optional, Sequence
from typing import Any, Literal, Mapping, Optional, Sequence, Tuple
REPO_ROOT = Path(__file__).resolve().parent.parent
@@ -140,16 +141,19 @@ def setup(role: Role,
compat = set()
else:
summary = "enabled by user"
missing: list[MissingFeature] = []
if "native" in compat:
have_toolchain = True
other_label: Optional[str] = None
other_triplet: Optional[str] = None
extra_environ: dict[str, str] = {}
if host_os == "windows" and host_arch == "x86_64" and host_config == "mingw":
other_triplet = "i686-w64-mingw32"
have_toolchain = shutil.which(other_triplet + "-gcc") is not None
other_label = "x86"
have_toolchain, other_triplet = detect_mingw_toolchain_for("x86")
elif host_os == "linux" and host_arch == "x86_64" and host_config is None:
other_label = "x86"
other_triplet = "i686-linux-gnu"
have_toolchain = shutil.which(other_triplet + "-gcc") is not None
if not have_toolchain:
@@ -173,7 +177,7 @@ def setup(role: Role,
if not have_toolchain:
if not auto_detect:
raise ToolchainNotFoundError(f"unable to locate toolchain for {other_triplet}")
summary = "disabled due to missing toolchain for {other_triplet}"
missing.append(MissingFeature(other_label, other_triplet))
if host_os == "windows" and host_arch == "x86_64" and have_toolchain:
group = OutputGroup("x86", other_triplet, extra_environ)
@@ -269,6 +273,28 @@ def setup(role: Role,
]
if "emulated" in compat:
if host_os == "windows" and host_arch == "arm64":
for kind, emulated_arch in [("modern", "x86_64"), ("legacy", "x86")]:
if host_config == "mingw":
found, emulated_triplet = detect_mingw_toolchain_for(emulated_arch)
if not found:
if not auto_detect:
raise ToolchainNotFoundError(f"unable to locate toolchain for {emulated_triplet}")
missing.append(MissingFeature(emulated_arch, emulated_triplet))
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),
]
if host_os == "android" and host_arch in {"x86_64", "x86"}:
outputs[OutputGroup("arm")] = [
Output(identifier="agent_emulated_legacy",
@@ -290,6 +316,8 @@ def setup(role: Role,
state = State(role, builddir, top_builddir, frida_version, host_os, host_config, allowed_prebuilds, outputs)
serialized_state = base64.b64encode(pickle.dumps(state)).decode('ascii')
if missing:
summary = ", ".join([f"{m.label} disabled due to missing toolchain for {m.triplet}" for m in missing])
variable_names, output_names = zip(*[(output.identifier, output.name) \
for output in itertools.chain.from_iterable(outputs.values())])
print("ok")
@@ -349,6 +377,12 @@ class Output:
target: str
@dataclass
class MissingFeature:
label: str
triplet: str
def compile(privdir: Path, state: State):
releng_location = query_releng_location(state.role)
subprojects = detect_relevant_subprojects(releng_location)
@@ -367,7 +401,7 @@ def compile(privdir: Path, state: State):
**kwargs,
"stdout": subprocess.PIPE,
"stderr": subprocess.STDOUT,
"encoding": "utf-8",
"encoding": locale.getpreferredencoding(),
}
else:
silenced_kwargs = kwargs
@@ -570,10 +604,10 @@ def grab_subprojects_from_parent(subprojects: dict[str, Path], releng_location:
continue
try:
subp_here.symlink_to(location, target_is_directory=True)
subp_here.symlink_to(Path("..") / ".." / name, target_is_directory=True)
continue
except OSError as e:
if not getattr(e, "winerror") == 1314:
if not getattr(e, "winerror", None) == 1314:
raise e
subprocess.run(["git", "worktree", "add", subp_here],
@@ -589,6 +623,12 @@ def configure_import_path(releng_location: Path):
sys.path.insert(0, str(releng_location.parent))
def detect_mingw_toolchain_for(arch: str) -> Tuple[bool, Optional[str]]:
triplet = MINGW_ARCHS.get(arch, arch) + "-w64-mingw32"
found = shutil.which(triplet + "-gcc") is not None
return (found, triplet)
STATE_FILENAME = "state.dat"
DEPFILE_FILENAME = "compat.deps"
@@ -616,6 +656,11 @@ MSVS_ENVVARS = {
"LIB",
}
MINGW_ARCHS = {
"x86": "i686",
"arm64": "aarch64",
}
if __name__ == "__main__":
main(sys.argv)
+3
View File
@@ -1,6 +1,8 @@
helper_modern = get_option('helper_modern')
helper_legacy = get_option('helper_legacy')
helper_compat = ''
helper_emulated_modern = get_option('helper_emulated_modern')
helper_emulated_legacy = get_option('helper_emulated_legacy')
helper_depends = []
agent_modern = get_option('agent_modern')
@@ -18,6 +20,7 @@ server_depends = []
no_overridden_compat_bits = \
helper_modern == 'auto' and helper_legacy == 'auto' \
and helper_emulated_modern == 'auto' and helper_emulated_legacy == 'auto' \
and agent_modern == 'auto' and agent_legacy == 'auto' \
and agent_emulated_modern == 'auto' and agent_emulated_legacy == 'auto'
+61 -32
View File
@@ -388,7 +388,7 @@ namespace Frida.Inject {
return false;
}
script_runner.on_stdin (line);
script_runner.on_stdin (line, null);
return true;
}
@@ -414,7 +414,12 @@ namespace Frida.Inject {
buf[n] = 0;
script_runner.on_stdin ((string) buf);
if (script_runner.terminal_mode == TerminalMode.BINARY) {
var bytes = new Bytes (buf[:n]);
script_runner.on_stdin ("", bytes);
} else {
script_runner.on_stdin ((string) buf, null);
}
return true;
}
@@ -549,7 +554,7 @@ namespace Frida.Inject {
var stage = new Json.Node.alloc ().init_string ("early");
try {
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, io_cancellable);
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, null, io_cancellable);
} catch (GLib.Error e) {
}
}
@@ -591,7 +596,7 @@ namespace Frida.Inject {
private async TerminalMode query_terminal_mode () {
Json.Node mode_value;
try {
mode_value = yield rpc_client.call ("getFridaTerminalMode", new Json.Node[] {}, io_cancellable);
mode_value = yield rpc_client.call ("getFridaTerminalMode", new Json.Node[] {}, null, io_cancellable);
} catch (GLib.Error e) {
return COOKED;
}
@@ -599,7 +604,14 @@ namespace Frida.Inject {
if (mode_value.get_value_type () != typeof (string))
return COOKED;
return (mode_value.get_string () == "raw") ? TerminalMode.RAW : TerminalMode.COOKED;
switch (mode_value.get_string ()) {
case "raw":
return TerminalMode.RAW;
case "binary":
return TerminalMode.BINARY;
default:
return TerminalMode.COOKED;
}
}
private void apply_terminal_mode (TerminalMode mode) throws Error {
@@ -632,10 +644,10 @@ namespace Frida.Inject {
}
#endif
public void on_stdin (string data) {
var data_value = new Json.Node.alloc ().init_string (data);
public void on_stdin (string str, Bytes? data) {
var str_value = new Json.Node.alloc ().init_string (str);
rpc_client.call.begin ("onFridaStdin", new Json.Node[] { data_value }, io_cancellable);
rpc_client.call.begin ("onFridaStdin", new Json.Node[] { str_value }, data, io_cancellable);
}
private void on_script_file_changed (File file, File? other_file, FileMonitorEvent event_type) {
@@ -675,7 +687,7 @@ namespace Frida.Inject {
handled = try_handle_log_message (message);
break;
case "send":
handled = try_handle_stdout_message (message);
handled = try_handle_stdout_message (message, data);
break;
default:
handled = false;
@@ -722,20 +734,19 @@ namespace Frida.Inject {
*
* {"type":"send","payload":["frida:stdout","DATA"]}
*/
private bool try_handle_stdout_message (Json.Object message) {
private bool try_handle_stdout_message (Json.Object message, Bytes? data) {
var payload = message.get_member ("payload");
if (payload.get_node_type () != Json.NodeType.ARRAY)
return false;
var tuple = payload.get_array ();
if (tuple.get_length () != 2)
var tuple_len = tuple.get_length ();
if (tuple_len == 0)
return false;
var first_element = tuple.get_element (0);
if (first_element.get_value_type () != typeof (string))
var type = tuple.get_element (0).get_string ();
if (type == null)
return false;
var type = first_element.get_string ();
switch (type) {
case "frida:stdout":
case "frida:stderr":
@@ -744,31 +755,49 @@ namespace Frida.Inject {
return false;
}
var second_element = tuple.get_element (1);
if (second_element.get_value_type () != typeof (string))
return false;
var str = second_element.get_string ();
switch (type) {
case "frida:stdout":
stdout.write (str.data);
stdout.flush ();
return true;
case "frida:stderr":
stderr.write (str.data);
return true;
default:
if (tuple_len >= 2) {
var str = tuple.get_element (1).get_string ();
if (str == null)
return false;
switch (type) {
case "frida:stdout":
stdout.write (str.data);
stdout.flush ();
break;
case "frida:stderr":
stderr.write (str.data);
break;
default:
return false;
}
}
if (data != null) {
switch (type) {
case "frida:stdout":
stdout.write (data.get_data ());
stdout.flush ();
break;
case "frida:stderr":
stderr.write (data.get_data ());
break;
default:
return false;
}
}
return true;
}
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
script.post (json, null);
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
script.post (json, data);
}
}
private enum TerminalMode {
COOKED,
RAW
RAW,
BINARY
}
}
+15 -16
View File
@@ -1509,13 +1509,14 @@ namespace Frida.Agent {
}
public static NativeBridgeApi open () throws Error {
string? nb_mod = null;
string? vm_mod = null;
Gum.Process.enumerate_modules ((details) => {
if (/\/lib(64)?\/libnativebridge.so$/.match (details.path))
nb_mod = details.path;
else if (/^lib(art|dvm).so$/.match (details.name) && !/\/system\/fake-libs/.match (details.path))
vm_mod = details.path;
Gum.Module? nb_mod = null;
Gum.Module? vm_mod = null;
Gum.Process.enumerate_modules (module => {
unowned string path = module.path;
if (/\/lib(64)?\/libnativebridge.so$/.match (path))
nb_mod = module;
else if (/^lib(art|dvm).so$/.match (module.name) && !/\/system\/fake-libs/.match (path))
vm_mod = module;
bool carry_on = nb_mod == null || vm_mod == null;
return carry_on;
});
@@ -1530,29 +1531,27 @@ namespace Frida.Agent {
NBUnloadLibraryFunc? unload;
NBGetTrampolineFunc get_trampoline;
load = (NBLoadLibraryFunc) Gum.Module.find_export_by_name (nb_mod, "NativeBridgeLoadLibrary");;
load = (NBLoadLibraryFunc) nb_mod.find_export_by_name ("NativeBridgeLoadLibrary");;
if (load != null) {
flavor = MODERN;
load_ext = (NBLoadLibraryExtFunc) Gum.Module.find_export_by_name (nb_mod, "NativeBridgeLoadLibraryExt");
load_ext = (NBLoadLibraryExtFunc) nb_mod.find_export_by_name ("NativeBridgeLoadLibraryExt");
// XXX: NativeBridgeUnloadLibrary() is only a stub as of Android 11 w/ libndk_translation.so
unload = null;
get_trampoline = (NBGetTrampolineFunc) Gum.Module.find_export_by_name (nb_mod,
"NativeBridgeGetTrampoline");
get_trampoline = (NBGetTrampolineFunc) nb_mod.find_export_by_name ("NativeBridgeGetTrampoline");
} else {
flavor = LEGACY;
load = (NBLoadLibraryFunc) Gum.Module.find_export_by_name (nb_mod,
"_ZN7android23NativeBridgeLoadLibraryEPKci");
load_ext = (NBLoadLibraryExtFunc) Gum.Module.find_export_by_name (nb_mod,
load = (NBLoadLibraryFunc) nb_mod.find_export_by_name ("_ZN7android23NativeBridgeLoadLibraryEPKci");
load_ext = (NBLoadLibraryExtFunc) nb_mod.find_export_by_name (
"_ZN7android26NativeBridgeLoadLibraryExtEPKciPNS_25native_bridge_namespace_tE");
// XXX: Unload implementation seems to be unreliable.
unload = null;
get_trampoline = (NBGetTrampolineFunc) Gum.Module.find_export_by_name (nb_mod,
get_trampoline = (NBGetTrampolineFunc) nb_mod.find_export_by_name (
"_ZN7android25NativeBridgeGetTrampolineEPvPKcS2_j");
}
if (load == null || get_trampoline == null)
throw new Error.NOT_SUPPORTED ("NativeBridge API is not available on this system");
var get_vms = (JNIGetCreatedJavaVMsFunc) Gum.Module.find_export_by_name (vm_mod, "JNI_GetCreatedJavaVMs");
var get_vms = (JNIGetCreatedJavaVMsFunc) vm_mod.find_export_by_name ("JNI_GetCreatedJavaVMs");
if (get_vms == null)
throw new Error.NOT_SUPPORTED ("Unable to locate Java VM");
+8
View File
@@ -0,0 +1,8 @@
{
global:
frida_agent_main;
JNI_OnLoad;
local:
*;
};
-1
View File
@@ -1,7 +1,6 @@
{
global:
frida_agent_main;
JNI_OnLoad;
local:
*;
+1 -1
View File
@@ -28,7 +28,7 @@ if host_os_family == 'windows'
elif host_os_family == 'darwin'
extra_link_args += '-Wl,-exported_symbol,_frida_agent_main'
else
symscript = 'frida-agent.version'
symscript = (host_os == 'android') ? 'frida-agent-android.version' : 'frida-agent.version'
extra_link_args += '-Wl,--version-script,' + meson.current_source_dir() / symscript
extra_link_depends += symscript
endif
+135 -9
View File
@@ -428,21 +428,28 @@ namespace Frida {
return *((double *) &bits);
}
public string read_string (size_t offset) {
public string read_string (size_t offset) throws Error {
string * start = (string *) get_pointer (offset, sizeof (char));
size_t max_length = size - offset;
string * val = (string *) get_pointer (offset, sizeof (char));
string * end = memchr (val, 0, max_length);
assert (end != null);
size_t size = end - val;
return val->substring (0, (long) size);
string * end = memchr (start, 0, max_length);
if (end == null)
throw new Error.PROTOCOL ("Missing null character");
size_t size = end - start;
string val = start->substring (0, (long) size);
if (!val.validate ())
throw new Error.PROTOCOL ("Invalid UTF-8 string");
return val;
}
[CCode (cname = "memchr", cheader_filename = "string.h")]
private extern static string * memchr (string * s, int c, size_t n);
public string read_fixed_string (size_t offset, size_t size) {
string * val = (string *) get_pointer (offset, size);
return val->substring (0, (long) size);
public string read_fixed_string (size_t offset, size_t size) throws Error {
string * start = (string *) get_pointer (offset, size);
string val = start->substring (0, (long) size);
if (!val.validate ())
throw new Error.PROTOCOL ("Invalid UTF-8 string");
return val;
}
public unowned Buffer write_string (size_t offset, string val) {
@@ -458,4 +465,123 @@ namespace Frida {
return data + offset;
}
}
public class BufferReader {
public size_t available {
get {
return buffer.bytes.get_size () - offset;
}
}
private Buffer buffer;
private size_t offset = 0;
public BufferReader (Buffer buf) {
buffer = buf;
}
public uint64 read_pointer (size_t offset) throws Error {
var pointer_size = buffer.pointer_size;
check_available (pointer_size);
var ptr = buffer.read_pointer (offset);
offset += pointer_size;
return ptr;
}
public int8 read_int8 () throws Error {
check_available (sizeof (int8));
var val = buffer.read_int8 (offset);
offset += sizeof (int8);
return val;
}
public uint8 read_uint8 () throws Error {
check_available (sizeof (uint8));
var val = buffer.read_uint8 (offset);
offset += sizeof (uint8);
return val;
}
public int16 read_int16 () throws Error {
check_available (sizeof (int16));
var val = buffer.read_int16 (offset);
offset += sizeof (int16);
return val;
}
public uint16 read_uint16 () throws Error {
check_available (sizeof (uint16));
var val = buffer.read_uint16 (offset);
offset += sizeof (uint16);
return val;
}
public int32 read_int32 () throws Error {
check_available (sizeof (int32));
var val = buffer.read_int32 (offset);
offset += sizeof (int32);
return val;
}
public uint32 read_uint32 () throws Error {
check_available (sizeof (uint32));
var val = buffer.read_uint32 (offset);
offset += sizeof (uint32);
return val;
}
public int64 read_int64 () throws Error {
check_available (sizeof (int64));
var val = buffer.read_int64 (offset);
offset += sizeof (int64);
return val;
}
public uint64 read_uint64 () throws Error {
check_available (sizeof (uint64));
var val = buffer.read_uint64 (offset);
offset += sizeof (uint64);
return val;
}
public float read_float () throws Error {
check_available (sizeof (float));
var val = buffer.read_float (offset);
offset += sizeof (float);
return val;
}
public double read_double () throws Error {
check_available (sizeof (double));
var val = buffer.read_double (offset);
offset += sizeof (double);
return val;
}
public string read_string () throws Error {
check_available (1);
var val = buffer.read_string (offset);
offset += val.length + 1;
return val;
}
public string read_fixed_string (size_t size) throws Error {
check_available (size);
var val = buffer.read_fixed_string (offset, size);
offset += size;
return val;
}
public Bytes read_bytes (size_t size) throws Error {
check_available (size);
var val = buffer.bytes[offset:offset + size];
offset += size;
return val;
}
private void check_available (size_t n) throws Error {
if (available < n)
throw new Error.PROTOCOL ("Malformed buffer: truncated");
}
}
}
+240
View File
@@ -0,0 +1,240 @@
namespace Frida {
public class ChannelRegistry : Object {
public signal void channel_closed (ChannelId id);
private Gee.Map<ChannelId?, weak IOStream> channels =
new Gee.HashMap<ChannelId?, weak IOStream> (ChannelId.hash, ChannelId.equal);
~ChannelRegistry () {
clear ();
}
public void clear () {
foreach (IOStream stream in channels.values.to_array ()) {
var channel_stream = stream as ChannelStream;
if (channel_stream != null)
channel_stream.abandon ();
}
channels.clear ();
}
public void register (ChannelId id, IOStream stream) {
stream.set_data ("channel-registry-entry", new Entry (this, id));
lock (channels)
channels[id] = stream;
}
public IOStream link (ChannelId id) throws Error {
IOStream? stream;
lock (channels)
stream = channels[id];
if (stream == null)
throw new Error.INVALID_ARGUMENT ("Invalid channel ID");
return stream;
}
public void unlink (ChannelId id) {
lock (channels) {
if (!channels.unset (id))
return;
}
channel_closed (id);
}
private class Entry {
public ChannelRegistry parent;
public ChannelId id;
public Entry (ChannelRegistry parent, ChannelId id) {
this.parent = parent;
this.id = id;
}
~Entry () {
parent.unlink (id);
}
}
}
public class ChannelEndpoint : Object, Channel {
private IOStream? stream;
private ByteArray write_queue = new ByteArray ();
private bool writing = false;
private Cancellable io_cancellable = new Cancellable ();
public ChannelEndpoint (IOStream stream) {
this.stream = stream;
read_loop.begin ();
}
public async void close (Cancellable? cancellable) throws Error, IOError {
io_cancellable.cancel ();
}
public async void input (uint8[] data, Cancellable? cancellable) throws Error, IOError {
if (io_cancellable.is_cancelled ())
throw new Error.INVALID_OPERATION ("Channel is closed");
write_queue.append (data);
if (!writing) {
writing = true;
var source = new IdleSource ();
source.set_callback (() => {
process_write_queue.begin ();
return false;
});
source.attach (MainContext.get_thread_default ());
}
}
private async void read_loop () {
var input = stream.get_input_stream ();
var buffer = new uint8[64 * 1024];
while (true) {
ssize_t n;
try {
n = yield input.read_async (buffer, Priority.DEFAULT, io_cancellable);
} catch (GLib.Error e) {
break;
}
output (buffer[:n]);
}
stream.close_async.begin ();
stream = null;
}
private async void process_write_queue () {
if (stream != null) {
var output = stream.get_output_stream ();
while (write_queue.len > 0) {
uint8[] batch = write_queue.steal ();
size_t bytes_written;
try {
yield output.write_all_async (batch, Priority.DEFAULT, io_cancellable, out bytes_written);
} catch (GLib.Error e) {
break;
}
}
}
writing = false;
}
}
public class ChannelStream : VirtualStream {
public Channel channel {
get;
construct;
}
private ByteArray recv_queue = new ByteArray ();
private ByteArray send_queue = new ByteArray ();
public ChannelStream (Channel channel) {
Object (channel: channel);
}
construct {
channel.output.connect (on_output);
}
public void abandon () {
with_state_lock (() => {
state = CLOSED;
update_pending_io ();
});
}
protected override IOCondition query_events () {
IOCondition new_events = 0;
if (recv_queue.len != 0 || state == CLOSED)
new_events |= IN;
if (state == OPEN)
new_events |= OUT;
return new_events;
}
protected override void handle_close () {
channel.close.begin (null);
}
public override ssize_t read (uint8[] buffer) throws IOError {
ssize_t n = 0;
with_state_lock (() => {
n = ssize_t.min (recv_queue.len, buffer.length);
if (n > 0) {
Memory.copy (buffer, recv_queue.data, n);
recv_queue.remove_range (0, (uint) n);
update_pending_io ();
} else {
if (state == OPEN)
n = -1;
}
});
if (n == -1)
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
return n;
}
public override ssize_t write (uint8[] buffer) throws IOError {
with_state_lock (() => {
send_queue.append (buffer);
});
if (main_context.is_owner ()) {
process_send_queue ();
} else {
var source = new IdleSource ();
source.set_callback (() => {
process_send_queue ();
return Source.REMOVE;
});
source.attach (main_context);
}
return buffer.length;
}
private void process_send_queue () {
uint8[]? chunk = null;
with_state_lock (() => {
size_t n = send_queue.len;
if (n == 0)
return;
chunk = send_queue.data[0:n];
send_queue.remove_range (0, (uint) n);
});
if (chunk == null)
return;
channel.input.begin (chunk, io_cancellable);
}
private void on_output (uint8[] data) {
with_state_lock (() => {
if (data.length != 0)
recv_queue.append (data);
else
state = CLOSED;
update_pending_io ();
});
}
}
}
+7 -1
View File
@@ -1,6 +1,9 @@
base_sources = [
'session.vala',
'session-glue.c',
'channel.vala',
'service.vala',
'stream.vala',
'rpc.vala',
'p2p.vala',
'p2p-glue.c',
@@ -43,14 +46,17 @@ if host_os_family == 'darwin'
]
endif
base_gir_name = f'FridaBase-@[email protected]'
base = static_library('frida-base-' + api_version, base_sources,
c_args: frida_component_cflags,
vala_args: [base_vala_args, '--pkg=libsoup-3.0'] + extra_vala_args,
vala_header: 'frida-base.h',
vala_gir: base_gir_name,
vala_vapi: f'frida-base-@[email protected]',
dependencies: [glib_dep, gobject_dep, gio_dep, libsoup_dep, nice_dep, usrsctp_dep, gee_dep, json_glib_dep, gum_dep, openssl_dep] + extra_deps,
install: true,
install_dir: [true, header_install_dir, true],
install_dir: [true, header_install_dir, true, false],
)
base_dep = declare_dependency(
+34 -232
View File
@@ -452,6 +452,8 @@ namespace Frida {
if ((_pending_io & c) != 0)
source.set_ready_time (0);
}
notify_property ("pending-io");
}
private void prepare_for_io (int64 timeout, Cancellable? cancellable, out int64 deadline) throws IOError {
@@ -563,7 +565,7 @@ namespace Frida {
}
}
public class SctpConnection : IOStream {
public class SctpConnection : VirtualStream {
public DatagramBased transport_socket {
get;
construct;
@@ -584,55 +586,14 @@ namespace Frida {
construct;
}
public State state {
get {
return _state;
}
}
public override InputStream input_stream {
get {
return _input_stream;
}
}
public override OutputStream output_stream {
get {
return _output_stream;
}
}
public IOCondition pending_io {
get {
lock (state)
return sctp_events;
}
}
private State _state = CREATED;
private SctpInputStream _input_stream;
private SctpOutputStream _output_stream;
private DatagramBasedSource transport_source;
private uint8[] transport_buffer = new uint8[65536];
private void * sctp_socket;
private IOCondition sctp_events = 0;
private SctpTimerSource sctp_source;
private uint16 stream_id;
private ByteArray dcep_message = new ByteArray ();
private Gee.Map<unowned Source, IOCondition> sources = new Gee.HashMap<unowned Source, IOCondition> ();
private Cancellable io_cancellable = new Cancellable ();
public enum State {
CREATED,
OPENING,
OPENED,
CLOSED
}
public SctpConnection (DatagramBased transport_socket, PeerSetup setup, uint16 port, size_t max_message_size) {
Object (
transport_socket: transport_socket,
@@ -649,14 +610,9 @@ namespace Frida {
protected extern static void _initialize_sctp_backend ();
construct {
_input_stream = new SctpInputStream (this);
_output_stream = new SctpOutputStream (this);
sctp_socket = _create_sctp_socket ();
_connect_sctp_socket (sctp_socket, port);
var main_context = MainContext.get_thread_default ();
transport_source = transport_socket.create_source (IOCondition.IN, io_cancellable);
transport_source.set_callback (on_transport_socket_readable);
transport_source.attach (main_context);
@@ -669,17 +625,22 @@ namespace Frida {
protected extern void _connect_sctp_socket (void * sock, uint16 port);
public override bool close (GLib.Cancellable? cancellable) throws IOError {
do_close ();
return true;
protected override VirtualStream.State query_initial_state () {
return CREATED;
}
public override async bool close_async (int io_priority, GLib.Cancellable? cancellable) throws IOError {
do_close ();
return true;
protected override IOCondition query_events () {
return _query_sctp_socket_events (sctp_socket);
}
private void do_close () {
protected override void update_pending_io () {
base.update_pending_io ();
sctp_source.invalidate ();
}
protected extern static IOCondition _query_sctp_socket_events (void * sock);
protected override void handle_close () {
sctp_source.destroy ();
transport_source.destroy ();
@@ -689,13 +650,17 @@ namespace Frida {
public extern static void _close (void * sock);
public void shutdown (SctpShutdownType type) throws IOError {
_shutdown (sctp_socket, type);
public override void shutdown_read () throws IOError {
_shutdown (sctp_socket, READ);
}
public override void shutdown_write () throws IOError {
_shutdown (sctp_socket, WRITE);
}
public extern static void _shutdown (void * sock, SctpShutdownType type) throws IOError;
public ssize_t recv (uint8[] buffer) throws IOError {
public override ssize_t read (uint8[] buffer) throws IOError {
ssize_t n = -1;
try {
@@ -711,18 +676,18 @@ namespace Frida {
handle_dcep_message (stream_id, dcep_message.steal ());
}
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
} else if (protocol_id == NONE || _state != OPENED) {
} else if (protocol_id == NONE || state != OPEN) {
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
}
} finally {
update_sctp_events ();
update_pending_io ();
}
return n;
}
public ssize_t send (uint8[] buffer) throws IOError {
if (_state != OPENED)
public override ssize_t write (uint8[] buffer) throws IOError {
if (state != OPEN)
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
ssize_t n = ssize_t.min (buffer.length, (ssize_t) max_message_size);
@@ -730,20 +695,10 @@ namespace Frida {
try {
return _send (sctp_socket, stream_id, WEBRTC_BINARY, buffer[0:n]);
} finally {
update_sctp_events ();
update_pending_io ();
}
}
public void register_source (Source source, IOCondition condition) {
lock (state)
sources[source] = condition | IOCondition.ERR | IOCondition.HUP;
}
public void unregister_source (Source source) {
lock (state)
sources.unset (source);
}
private bool on_transport_socket_readable (DatagramBased datagram_based, IOCondition condition) {
var v = InputVector ();
v.buffer = transport_buffer;
@@ -783,9 +738,9 @@ namespace Frida {
}
protected void _on_sctp_socket_events_changed () {
update_sctp_events ();
update_pending_io ();
if (_state == CREATED && setup == ACTIVE && (sctp_events & IOCondition.OUT) != 0) {
if (state == CREATED && setup == ACTIVE && (pending_io & IOCondition.OUT) != 0) {
stream_id = 1;
uint8[] open_message = {
@@ -799,31 +754,12 @@ namespace Frida {
};
try {
_send (sctp_socket, stream_id, WEBRTC_DCEP, open_message);
_state = OPENING;
state = OPENING;
} catch (IOError e) {
}
}
}
private void update_sctp_events () {
IOCondition new_events = _query_sctp_socket_events (sctp_socket);
lock (state) {
sctp_events = new_events;
foreach (var entry in sources.entries) {
unowned Source source = entry.key;
IOCondition c = entry.value;
if ((new_events & c) != 0)
source.set_ready_time (0);
}
sctp_source.invalidate ();
}
}
protected extern static IOCondition _query_sctp_socket_events (void * sock);
protected extern static ssize_t _recv (void * sock, uint8[] buffer, out uint16 stream_id,
out PayloadProtocolId protocol_id, out SctpMessageFlags message_flags) throws IOError;
@@ -835,7 +771,7 @@ namespace Frida {
switch (type) {
case DATA_CHANNEL_OPEN: {
if (_state != CREATED || setup == ACTIVE)
if (state != CREATED || setup == ACTIVE)
return;
this.stream_id = stream_id;
@@ -843,15 +779,15 @@ namespace Frida {
uint8[] reply = { DcepMessageType.DATA_CHANNEL_ACK };
_send (sctp_socket, stream_id, WEBRTC_DCEP, reply);
_state = OPENED;
state = OPEN;
break;
}
case DATA_CHANNEL_ACK:
if (_state != OPENING)
if (state != OPENING)
return;
_state = OPENED;
state = OPEN;
break;
}
@@ -886,140 +822,6 @@ namespace Frida {
DATA_CHANNEL_ACK = 0x02
}
private class SctpInputStream : InputStream, PollableInputStream {
public weak SctpConnection connection {
get;
construct;
}
public SctpInputStream (SctpConnection connection) {
Object (connection: connection);
}
public override bool close (Cancellable? cancellable) throws IOError {
connection.shutdown (READ);
return true;
}
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
return close (cancellable);
}
public override ssize_t read (uint8[] buffer, Cancellable? cancellable) throws IOError {
assert_not_reached ();
}
public bool can_poll () {
return true;
}
public bool is_readable () {
return (connection.pending_io & IOCondition.IN) != 0;
}
public PollableSource create_source (Cancellable? cancellable) {
return new PollableSource.full (this, new SctpIOSource (connection, IOCondition.IN), cancellable);
}
public ssize_t read_nonblocking_fn (uint8[] buffer) throws GLib.Error {
return connection.recv (buffer);
}
}
private class SctpOutputStream : OutputStream, PollableOutputStream {
public weak SctpConnection connection {
get;
construct;
}
public SctpOutputStream (SctpConnection connection) {
Object (connection: connection);
}
public override bool close (Cancellable? cancellable) throws IOError {
connection.shutdown (WRITE);
return true;
}
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
return close (cancellable);
}
public override bool flush (GLib.Cancellable? cancellable) throws GLib.Error {
return true;
}
public override async bool flush_async (int io_priority, GLib.Cancellable? cancellable) throws GLib.Error {
return true;
}
public override ssize_t write (uint8[] buffer, Cancellable? cancellable) throws IOError {
assert_not_reached ();
}
public bool can_poll () {
return true;
}
public bool is_writable () {
return (connection.pending_io & IOCondition.OUT) != 0;
}
public PollableSource create_source (Cancellable? cancellable) {
return new PollableSource.full (this, new SctpIOSource (connection, IOCondition.OUT), cancellable);
}
public ssize_t write_nonblocking_fn (uint8[]? buffer) throws GLib.Error {
return connection.send (buffer);
}
public PollableReturn writev_nonblocking_fn (OutputVector[] vectors, out size_t bytes_written) throws GLib.Error {
assert_not_reached ();
}
}
private class SctpIOSource : Source {
public SctpConnection connection;
public IOCondition condition;
public SctpIOSource (SctpConnection connection, IOCondition condition) {
this.connection = connection;
this.condition = condition;
connection.register_source (this, condition);
}
~SctpIOSource () {
connection.unregister_source (this);
}
protected override bool prepare (out int timeout) {
timeout = -1;
return (connection.pending_io & condition) != 0;
}
protected override bool check () {
return (connection.pending_io & condition) != 0;
}
protected override bool dispatch (SourceFunc? callback) {
set_ready_time (-1);
if (callback == null)
return Source.REMOVE;
return callback ();
}
protected static bool closure_callback (Closure closure) {
var return_value = Value (typeof (bool));
closure.invoke (ref return_value, {});
return return_value.get_boolean ();
}
}
private class SctpTimerSource : Source {
private static int64 last_process_time = -1;
+3 -3
View File
@@ -11,7 +11,7 @@ namespace Frida {
Object (peer: peer);
}
public async Json.Node call (string method, Json.Node[] args, Cancellable? cancellable) throws Error, IOError {
public async Json.Node call (string method, Json.Node[] args, Bytes? data, Cancellable? cancellable) throws Error, IOError {
string request_id = Uuid.string_random ();
var request = new Json.Builder ();
@@ -39,7 +39,7 @@ namespace Frida {
pending_responses[request_id] = pending;
try {
yield peer.post_rpc_message (raw_request, cancellable);
yield peer.post_rpc_message (raw_request, data, cancellable);
} catch (Error e) {
if (pending_responses.unset (request_id))
pending.complete_with_error (e);
@@ -158,6 +158,6 @@ namespace Frida {
}
public interface RpcPeer : Object {
public abstract async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError;
public abstract async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError;
}
}
+48
View File
@@ -0,0 +1,48 @@
namespace Frida {
public class ServiceSessionRegistry : Object {
public signal void session_closed (ServiceSessionId id);
private Gee.Map<ServiceSessionId?, ServiceSession> sessions =
new Gee.HashMap<ServiceSessionId?, ServiceSession> (ServiceSessionId.hash, ServiceSessionId.equal);
~ServiceSessionRegistry () {
clear ();
}
public void clear () {
foreach (var session in sessions.values.to_array ())
session.close ();
sessions.clear ();
}
public void register (ServiceSessionId id, ServiceSession session) {
sessions[id] = session;
ServiceSessionId? boxed_id = id;
session.set_data ("service-session-id", boxed_id);
session.close.connect (on_session_close);
}
public ServiceSession link (ServiceSessionId id) throws Error {
var session = sessions[id];
if (session == null)
throw new Error.INVALID_ARGUMENT ("Invalid session ID");
return session;
}
public void unlink (ServiceSessionId id) {
ServiceSession? session;
if (sessions.unset (id, out session)) {
session.close.disconnect (on_session_close);
session_closed (id);
}
}
private void on_session_close (ServiceSession session) {
ServiceSessionId? id = session.get_data ("service-session-id");
unlink (id);
}
}
}
+81 -1
View File
@@ -27,6 +27,10 @@ namespace Frida {
public abstract async InjectorPayloadId inject_library_blob (uint pid, uint8[] blob, string entrypoint, string data,
Cancellable? cancellable) throws GLib.Error;
public abstract async ChannelId open_channel (string address, Cancellable? cancellable) throws GLib.Error;
public abstract async ServiceSessionId open_service (string address, Cancellable? cancellable) throws GLib.Error;
public signal void spawn_added (HostSpawnInfo info);
public signal void spawn_removed (HostSpawnInfo info);
public signal void child_added (HostChildInfo info);
@@ -34,6 +38,8 @@ namespace Frida {
public signal void process_crashed (CrashInfo crash);
public signal void output (uint pid, int fd, uint8[] data);
public signal void agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash);
public signal void channel_closed (ChannelId id);
public signal void service_session_closed (ServiceSessionId id);
public signal void uninjected (InjectorPayloadId id);
}
@@ -743,6 +749,23 @@ namespace Frida {
}
}
[DBus (name = "re.frida.Channel16")]
public interface Channel : Object {
public abstract async void close (Cancellable? cancellable) throws GLib.Error;
public abstract async void input (uint8[] data, Cancellable? cancellable) throws GLib.Error;
public signal void output (uint8[] data);
}
[DBus (name = "re.frida.ServiceSession16")]
public interface ServiceSession : Object {
public signal void close ();
public signal void message (Variant message);
public abstract async void activate (Cancellable? cancellable) throws GLib.Error;
public abstract async void cancel (Cancellable? cancellable) throws GLib.Error;
public abstract async Variant request (Variant parameters, Cancellable? cancellable) throws GLib.Error;
}
[DBus (name = "re.frida.TransportBroker16")]
public interface TransportBroker : Object {
public abstract async void open_tcp_transport (AgentSessionId id, Cancellable? cancellable, out uint16 port,
@@ -875,6 +898,14 @@ namespace Frida {
Cancellable? cancellable) throws Error, IOError {
throw_not_authorized ();
}
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
throw_not_authorized ();
}
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
throw_not_authorized ();
}
}
public class UnauthorizedPortalSession : Object, PortalSession {
@@ -1502,6 +1533,46 @@ namespace Frida {
}
}
public struct ChannelId {
public string handle;
public ChannelId (string handle) {
this.handle = handle;
}
public ChannelId.generate () {
this.handle = Uuid.string_random ().replace ("-", "");
}
public static uint hash (ChannelId? id) {
return id.handle.hash ();
}
public static bool equal (ChannelId? a, ChannelId? b) {
return a.handle == b.handle;
}
}
public struct ServiceSessionId {
public string handle;
public ServiceSessionId (string handle) {
this.handle = handle;
}
public ServiceSessionId.generate () {
this.handle = Uuid.string_random ().replace ("-", "");
}
public static uint hash (ServiceSessionId? id) {
return id.handle.hash ();
}
public static bool equal (ServiceSessionId? a, ServiceSessionId? b) {
return a.handle == b.handle;
}
}
public struct AgentScriptId {
public uint handle;
@@ -2048,7 +2119,8 @@ namespace Frida {
public const string AGENT_SESSION = "/re/frida/AgentSession";
public const string AGENT_CONTROLLER = "/re/frida/AgentController";
public const string AGENT_MESSAGE_SINK = "/re/frida/AgentMessageSink";
public const string CHILD_SESSION = "/re/frida/ChildSession";
public const string CHANNEL = "/re/frida/Channel";
public const string SERVICE = "/re/frida/Service";
public const string TRANSPORT_BROKER = "/re/frida/TransportBroker";
public const string PORTAL_SESSION = "/re/frida/PortalSession";
public const string BUS_SESSION = "/re/frida/BusSession";
@@ -2061,6 +2133,14 @@ namespace Frida {
public static string for_agent_message_sink (AgentSessionId id) {
return AGENT_MESSAGE_SINK + "/" + id.handle;
}
public static string for_channel (ChannelId id) {
return CHANNEL + "/" + id.handle;
}
public static string for_service_session (ServiceSessionId id) {
return SERVICE + "/" + id.handle;
}
}
namespace Marshal {
+499 -601
View File
File diff suppressed because it is too large. Load diff
+334
View File
@@ -0,0 +1,334 @@
namespace Frida {
public abstract class VirtualStream : IOStream {
public override InputStream input_stream {
get {
return _input_stream;
}
}
public override OutputStream output_stream {
get {
return _output_stream;
}
}
public IOCondition pending_io {
get {
lock (state)
return _pending_io;
}
}
protected State state = OPEN;
private VirtualInputStream _input_stream;
private VirtualOutputStream _output_stream;
private IOCondition _pending_io;
private Gee.Map<unowned Source, IOCondition> sources = new Gee.HashMap<unowned Source, IOCondition> ();
protected Cancellable io_cancellable = new Cancellable ();
protected MainContext main_context;
protected enum State {
CREATED,
OPENING,
OPEN,
CLOSED
}
protected delegate void WorkFunc ();
construct {
_input_stream = new VirtualInputStream (this);
_output_stream = new VirtualOutputStream (this);
main_context = MainContext.ref_thread_default ();
state = query_initial_state ();
_pending_io = query_events ();
}
public override void dispose () {
_output_stream.detach ();
_input_stream.detach ();
io_cancellable.cancel ();
base.dispose ();
}
protected virtual State query_initial_state () {
return OPEN;
}
protected abstract IOCondition query_events ();
public override bool close (GLib.Cancellable? cancellable) throws IOError {
_close ();
return true;
}
public override async bool close_async (int io_priority, GLib.Cancellable? cancellable) throws IOError {
_close ();
return true;
}
private void _close () {
if (main_context.is_owner ()) {
do_close ();
} else {
var source = new IdleSource ();
source.set_callback (() => {
do_close ();
return Source.REMOVE;
});
source.attach (main_context);
}
}
private void do_close () {
if (state == CLOSED)
return;
handle_close ();
}
protected abstract void handle_close ();
public virtual void shutdown_read () throws IOError {
}
public virtual void shutdown_write () throws IOError {
}
public abstract ssize_t read (uint8[] buffer) throws IOError;
public abstract ssize_t write (uint8[] buffer) throws IOError;
protected void with_state_lock (WorkFunc func) {
lock (state)
func ();
}
public void register_source (Source source, IOCondition condition) {
lock (state)
sources[source] = condition | IOCondition.ERR | IOCondition.HUP;
}
public void unregister_source (Source source) {
lock (state)
sources.unset (source);
}
protected virtual void update_pending_io () {
_pending_io = query_events ();
foreach (var entry in sources.entries) {
unowned Source source = entry.key;
IOCondition c = entry.value;
if ((_pending_io & c) != 0)
source.set_ready_time (0);
}
notify_property ("pending-io");
}
}
private class VirtualInputStream : InputStream, PollableInputStream {
private weak VirtualStream stream;
public VirtualInputStream (VirtualStream stream) {
Object ();
this.stream = stream;
}
internal void detach () {
stream = null;
}
public override bool close (Cancellable? cancellable) throws IOError {
if (stream != null)
stream.shutdown_read ();
return true;
}
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
return close (cancellable);
}
public override ssize_t read (uint8[] buffer, Cancellable? cancellable) throws IOError {
if (stream == null)
return 0;
if (!is_readable ()) {
bool done = false;
var mutex = Mutex ();
var cond = Cond ();
ulong io_handler = stream.notify["pending-io"].connect ((obj, pspec) => {
if (is_readable ()) {
mutex.lock ();
done = true;
cond.signal ();
mutex.unlock ();
}
});
ulong cancellation_handler = 0;
if (cancellable != null) {
cancellation_handler = cancellable.connect (() => {
mutex.lock ();
done = true;
cond.signal ();
mutex.unlock ();
});
}
if (!is_readable ()) {
mutex.lock ();
while (!done)
cond.wait (mutex);
mutex.unlock ();
}
if (cancellation_handler != 0)
cancellable.disconnect (cancellation_handler);
stream.disconnect (io_handler);
cancellable.set_error_if_cancelled ();
}
return stream.read (buffer);
}
public bool can_poll () {
return true;
}
public bool is_readable () {
if (stream == null)
return true;
return (stream.pending_io & IOCondition.IN) != 0;
}
public PollableSource create_source (Cancellable? cancellable) {
return new PollableSource.full (this, new VirtualIOSource (stream, IOCondition.IN), cancellable);
}
public ssize_t read_nonblocking_fn (uint8[] buffer) throws GLib.Error {
if (stream == null)
return 0;
return stream.read (buffer);
}
}
private class VirtualOutputStream : OutputStream, PollableOutputStream {
private weak VirtualStream? stream;
public VirtualOutputStream (VirtualStream stream) {
Object ();
this.stream = stream;
}
internal void detach () {
stream = null;
}
public override bool close (Cancellable? cancellable) throws IOError {
if (stream != null)
stream.shutdown_write ();
return true;
}
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
return close (cancellable);
}
public override bool flush (GLib.Cancellable? cancellable) throws GLib.Error {
return true;
}
public override async bool flush_async (int io_priority, GLib.Cancellable? cancellable) throws GLib.Error {
return true;
}
public override ssize_t write (uint8[] buffer, Cancellable? cancellable) throws IOError {
return stream.write (buffer);
}
public bool can_poll () {
return true;
}
public bool is_writable () {
if (stream == null)
return false;
return (stream.pending_io & IOCondition.OUT) != 0;
}
public PollableSource create_source (Cancellable? cancellable) {
return new PollableSource.full (this, new VirtualIOSource (stream, IOCondition.OUT), cancellable);
}
public ssize_t write_nonblocking_fn (uint8[]? buffer) throws GLib.Error {
if (stream == null)
throw new IOError.CLOSED ("Virtual is closed");
return stream.write (buffer);
}
public PollableReturn writev_nonblocking_fn (OutputVector[] vectors, out size_t bytes_written) throws GLib.Error {
assert_not_reached ();
}
}
private class VirtualIOSource : Source {
public VirtualStream? stream;
public IOCondition condition;
public VirtualIOSource (VirtualStream stream, IOCondition condition) {
this.stream = stream;
this.condition = condition;
if (stream != null)
stream.register_source (this, condition);
}
~VirtualIOSource () {
if (stream != null)
stream.unregister_source (this);
}
protected override bool prepare (out int timeout) {
timeout = -1;
return is_ready ();
}
protected override bool check () {
return is_ready ();
}
private bool is_ready () {
IOCondition pending_io = (stream != null) ? stream.pending_io : IOCondition.IN;
return (pending_io & condition) != 0;
}
protected override bool dispatch (SourceFunc? callback) {
set_ready_time (-1);
if (callback == null)
return Source.REMOVE;
return callback ();
}
protected static bool closure_callback (Closure closure) {
var return_value = Value (typeof (bool));
closure.invoke (ref return_value, {});
return return_value.get_boolean ();
}
}
}
+5
View File
@@ -13,6 +13,7 @@ namespace Frida {
public abstract unowned ObjectBuilder add_uint64_value (uint64 val);
public abstract unowned ObjectBuilder add_data_value (Bytes val);
public abstract unowned ObjectBuilder add_string_value (string val);
public abstract unowned ObjectBuilder add_uuid_value (uint8[] val);
public abstract unowned ObjectBuilder add_raw_value (Bytes val);
public abstract Bytes build ();
@@ -238,6 +239,10 @@ namespace Frida {
return this;
}
public unowned ObjectBuilder add_uuid_value (uint8[] val) {
assert_not_reached ();
}
public unowned ObjectBuilder add_raw_value (Bytes val) {
string uuid = Uuid.string_random ();
builder.add_string_value (uuid);
+16
View File
@@ -65,8 +65,17 @@ namespace Frida {
});
});
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (() => {
connection.cancel ();
return Source.REMOVE;
});
cancel_source.attach (main_context);
yield;
cancel_source.destroy ();
if (reply.type == Darwin.Xpc.Error.TYPE) {
var e = (Darwin.Xpc.Error) reply;
throw new Error.NOT_SUPPORTED ("%s", e.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION));
@@ -195,6 +204,13 @@ namespace Frida {
return peek_scope ().get_object<Darwin.Xpc.UInt64> (Darwin.Xpc.UInt64.TYPE).get_value ();
}
public unowned uint8[] get_data_value () throws Error {
var data = peek_scope ().get_object<Darwin.Xpc.Data> (Darwin.Xpc.Data.TYPE);
unowned uint8[] buf = (uint8[]) data.get_bytes_ptr ();
buf.length = (int) data.get_length ();
return buf;
}
public unowned string get_string_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.String> (Darwin.Xpc.String.TYPE).get_string_ptr ();
}
+73 -12
View File
@@ -751,7 +751,7 @@ namespace Frida.Gadget {
string config_data;
try {
FileUtils.get_contents (config_path, out config_data);
load_asset_text (config_path, out config_data);
} catch (FileError e) {
if (e is FileError.NOENT)
return new Config ();
@@ -1226,7 +1226,7 @@ namespace Frida.Gadget {
private ScriptConfig load_config (string path) throws Error {
string data;
try {
FileUtils.get_contents (path, out data);
load_asset_text (path, out data);
} catch (FileError e) {
if (e is FileError.NOENT)
return new ScriptConfig ();
@@ -1349,9 +1349,9 @@ namespace Frida.Gadget {
try {
var path = this.path;
uint8[] contents;
Bytes contents;
try {
FileUtils.get_data (path, out contents);
load_asset_bytes (path, out contents);
} catch (FileError e) {
throw new Error.INVALID_ARGUMENT ("%s", e.message);
}
@@ -1360,11 +1360,10 @@ namespace Frida.Gadget {
options.name = Path.get_basename (path).split (".", 2)[0];
ScriptEngine.ScriptInstance instance;
if (contents.length > 0 && contents[0] == QUICKJS_BYTECODE_MAGIC) {
instance = yield engine.create_script (null, new Bytes (contents), options);
} else {
instance = yield engine.create_script ((string) contents, null, options);
}
if (contents.length > 0 && contents[0] == QUICKJS_BYTECODE_MAGIC)
instance = yield engine.create_script (null, contents, options);
else
instance = yield engine.create_script ((string) contents.get_data (), null, options);
if (id.handle != 0)
yield engine.destroy_script (id);
@@ -1381,7 +1380,7 @@ namespace Frida.Gadget {
var stage = new Json.Node.alloc ().init_string ((peek_state () == State.CREATED) ? "early" : "late");
try {
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, null);
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, null, null);
} catch (GLib.Error e) {
}
}
@@ -1449,8 +1448,8 @@ namespace Frida.Gadget {
return true;
}
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
engine.post_to_script (id, json);
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
engine.post_to_script (id, json, data);
}
}
@@ -1928,6 +1927,14 @@ namespace Frida.Gadget {
throw new Error.NOT_SUPPORTED ("Unable to inject libraries when embedded");
}
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Unable to open channels when embedded");
}
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Unable to open services when embedded");
}
private void validate_pid (uint pid) throws Error {
if (pid != this_process.pid)
throw new Error.NOT_SUPPORTED ("Unable to act on other processes when embedded");
@@ -2076,6 +2083,60 @@ namespace Frida.Gadget {
}
#endif
private void load_asset_text (string filename, out string text) throws FileError {
Bytes raw_contents;
load_asset_bytes (filename, out raw_contents);
unowned string str = (string) raw_contents.get_data ();
if (!str.validate ())
throw new FileError.FAILED ("%s: invalid UTF-8", filename);
text = str;
}
private void load_asset_bytes (string filename, out Bytes bytes) throws FileError {
#if ANDROID
if (maybe_load_asset_bytes_from_apk (filename, out bytes))
return;
#endif
uint8[] data;
FileUtils.get_data (filename, out data);
bytes = new Bytes.take ((owned) data);
}
#if ANDROID
private bool maybe_load_asset_bytes_from_apk (string filename, out Bytes contents) throws FileError {
contents = null;
var tokens = filename.split ("!", 2);
if (tokens.length != 2 || !tokens[0].has_suffix (".apk"))
return false;
unowned string apk_path = tokens[0];
unowned string file_path = tokens[1];
var reader = Minizip.Reader.create ();
try {
if (reader.open_file (apk_path) != OK)
throw new FileError.FAILED ("Unable to open APK");
if (reader.locate_entry (file_path[1:], true) != OK)
throw new FileError.FAILED ("Unable to locate %s inside APK", file_path);
var size = reader.entry_save_buffer_length ();
var data = new uint8[size + 1];
if (reader.entry_save_buffer (data[:size]) != OK)
throw new FileError.FAILED ("Unable to extract %s from APK", file_path);
contents = new Bytes.take ((owned) data);
return true;
} finally {
reader.close ();
Minizip.Reader.destroy (ref reader);
}
}
#endif
private Json.Node make_empty_json_object () {
return new Json.Node.alloc ().init_object (new Json.Object ());
}
+5
View File
@@ -29,6 +29,11 @@ if host_os_family == 'darwin'
extra_link_args += '-Wl,-framework,Foundation'
endif
if host_os == 'android'
extra_vala_args += '--pkg=minizip'
platform_deps += minizip_dep
endif
if host_os_family == 'windows'
if host_toolchain != 'microsoft'
symfile = 'frida-gadget.symbols'
+14 -7
View File
@@ -1,6 +1,6 @@
public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
#if IOS || TVOS
private Gee.Map<string, InetAddress> interfaces = new Gee.HashMap<string, InetAddress> ();
private Gee.Map<string, DynamicInterface> interfaces = new Gee.HashMap<string, DynamicInterface> ();
private Darwin.SystemConfiguration.DynamicStore? store;
private Darwin.GCD.DispatchQueue event_queue =
@@ -30,11 +30,16 @@ public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
context.info = this;
store = new Darwin.SystemConfiguration.DynamicStore (null, CoreFoundation.String.make ("Frida"),
on_interfaces_changed_wrapper, context);
var pattern = CoreFoundation.String.make ("State:/Network/Interface/utun.*/IPv6");
var patterns = new CoreFoundation.Array (null, ((CoreFoundation.Type[]) &pattern)[:1]);
store.set_notification_keys (null, patterns);
store.set_dispatch_queue (event_queue);
handle_interface_changes (store.copy_key_list (pattern));
var initial_keys = store.copy_key_list (pattern);
if (initial_keys != null)
handle_interface_changes (initial_keys);
}
private static void on_interfaces_changed_wrapper (Darwin.SystemConfiguration.DynamicStore store,
@@ -57,9 +62,9 @@ public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
foreach (var key in CFArray.wrap<CoreFoundation.String> (changed_keys)) {
string name = key.to_string ().split ("/")[3];
InetAddress? address = null;
var val = (CoreFoundation.Dictionary) store.copy_value (key);
if (val != null) {
InetAddress? address = null;
foreach (var raw_address in CFArray.wrap<CoreFoundation.String> (val[addresses_str])) {
var str = raw_address.to_string ();
bool is_reserved_ipv6_range = str.has_prefix ("fc") || str.has_prefix ("fd");
@@ -70,12 +75,14 @@ public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
}
}
if (address != null && !interfaces.has_key (name)) {
interfaces[name] = address;
interface_attached (name, address);
var iface = new DynamicInterface (name, address);
interfaces[name] = iface;
interface_attached (iface);
}
} else {
if (interfaces.unset (name, out address))
interface_detached (name, address);
DynamicInterface iface;
if (interfaces.unset (name, out iface))
interface_detached (iface);
}
}
}
+19 -16
View File
@@ -53,15 +53,18 @@ namespace Frida {
private delegate ssize_t ReadFunc (int fd, void * buf, size_t count);
construct {
Gum.Module.enumerate_imports ("libart.so", imp => {
if (imp.name == "read") {
read_slot = (ReadFunc *) imp.slot;
return false;
}
return true;
});
if (read_slot != null)
old_read_impl = update_read_slot (on_read);
var art = Gum.Process.find_module_by_name ("libart.so");
if (art != null) {
art.enumerate_imports (imp => {
if (imp.name == "read") {
read_slot = (ReadFunc *) imp.slot;
return false;
}
return true;
});
if (read_slot != null)
old_read_impl = update_read_slot (on_read);
}
}
~ThreadCountCloaker () {
@@ -212,34 +215,34 @@ namespace Frida {
construct {
var interceptor = Gum.Interceptor.obtain ();
unowned string libc = Gum.Process.query_libc_name ();
var libc = Gum.Process.get_libc_module ();
var open_listener = new OpenDirListener (this);
listeners.add (open_listener);
interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, "opendir"), open_listener);
interceptor.attach ((void *) libc.find_export_by_name ("opendir"), open_listener);
var close_listener = new CloseDirListener (this);
listeners.add (close_listener);
interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, "closedir"), close_listener);
interceptor.attach ((void *) libc.find_export_by_name ("closedir"), close_listener);
var readdir_impl = Gum.Module.find_export_by_name (libc, "readdir");
var readdir_impl = libc.find_export_by_name ("readdir");
var readdir_listener = new ReadDirListener (this, LEGACY);
listeners.add (readdir_listener);
interceptor.attach ((void *) readdir_impl, readdir_listener);
var readdir64_impl = Gum.Module.find_export_by_name (libc, "readdir64");
var readdir64_impl = libc.find_export_by_name ("readdir64");
if (readdir64_impl != 0 && readdir64_impl != readdir_impl) {
var listener = new ReadDirListener (this, MODERN);
listeners.add (listener);
interceptor.attach ((void *) readdir64_impl, listener);
}
var readdir_r_impl = Gum.Module.find_export_by_name (libc, "readdir_r");
var readdir_r_impl = libc.find_export_by_name ("readdir_r");
var readdir_r_listener = new ReadDirRListener (this, LEGACY);
listeners.add (readdir_r_listener);
interceptor.attach ((void *) readdir_r_impl, readdir_r_listener);
var readdir64_r_impl = Gum.Module.find_export_by_name (libc, "readdir64_r");
var readdir64_r_impl = libc.find_export_by_name ("readdir64_r");
if (readdir64_r_impl != 0 && readdir64_r_impl != readdir_r_impl) {
var listener = new ReadDirRListener (this, MODERN);
listeners.add (listener);
+4 -3
View File
@@ -32,16 +32,17 @@ namespace Frida {
unowned Gum.InvocationListener listener = this;
#if WINDOWS
interceptor.attach ((void *) Gum.Module.find_export_by_name ("kernel32.dll", "ExitProcess"), listener);
interceptor.attach ((void *) Gum.Process.find_module_by_name ("kernel32.dll").find_export_by_name ("ExitProcess"),
listener);
#else
unowned string libc = Gum.Process.query_libc_name ();
var libc = Gum.Process.get_libc_module ();
const string[] apis = {
"exit",
"_exit",
"abort",
};
foreach (var symbol in apis) {
interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, symbol), listener);
interceptor.attach ((void *) libc.find_export_by_name (symbol), listener);
}
#endif
}
+1 -1
View File
@@ -26,7 +26,7 @@ namespace Frida {
construct {
var interceptor = Gum.Interceptor.obtain ();
var close = Gum.Module.find_export_by_name (Gum.Process.query_libc_name (), "close");
var close = Gum.Process.get_libc_module ().find_export_by_name ("close");
close_listener = new CloseListener (this);
interceptor.attach ((void *) close, close_listener);
}
+16 -10
View File
@@ -45,9 +45,9 @@ namespace Frida {
}
static construct {
unowned string libc = Gum.Process.query_libc_name ();
fork_impl = (void *) Gum.Module.find_export_by_name (libc, "fork");
vfork_impl = (void *) Gum.Module.find_export_by_name (libc, "vfork");
var libc = Gum.Process.get_libc_module ();
fork_impl = (void *) libc.find_export_by_name ("fork");
vfork_impl = (void *) libc.find_export_by_name ("vfork");
}
construct {
@@ -61,15 +61,21 @@ namespace Frida {
string cmdline;
FileUtils.get_contents ("/proc/self/cmdline", out cmdline);
if (cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" || cmdline == "usap64") {
var set_argv0 = (void *) Gum.Module.find_export_by_name ("libandroid_runtime.so", "_Z27android_os_Process_setArgV0P7_JNIEnvP8_jobjectP8_jstring");
if (set_argv0 != null) {
interceptor.attach (set_argv0, listener, (void *) HookId.SET_ARGV0);
child_recovery_behavior = DEFERRED_UNTIL_SET_ARGV0;
var runtime = Gum.Process.find_module_by_name ("libandroid_runtime.so");
if (runtime != null) {
var set_argv0 = (void *) runtime.find_export_by_name ("_Z27android_os_Process_setArgV0P7_JNIEnvP8_jobjectP8_jstring");
if (set_argv0 != null) {
interceptor.attach (set_argv0, listener, (void *) HookId.SET_ARGV0);
child_recovery_behavior = DEFERRED_UNTIL_SET_ARGV0;
}
}
var setcontext = (void *) Gum.Module.find_export_by_name ("libselinux.so", "selinux_android_setcontext");
if (setcontext != null)
interceptor.attach (setcontext, listener, (void *) HookId.SET_CTX);
var selinux = Gum.Process.find_module_by_name ("libselinux.so");
if (selinux != null) {
var setcontext = (void *) selinux.find_export_by_name ("selinux_android_setcontext");
if (setcontext != null)
interceptor.attach (setcontext, listener, (void *) HookId.SET_CTX);
}
}
} catch (FileError e) {
}
+18
View File
@@ -25,6 +25,24 @@ frida_run_atexit_handlers (void)
{
}
# ifdef HAVE_ASAN
__attribute__ ((constructor)) static void
frida_init_memory (void)
{
asm volatile ("");
}
# ifndef HAVE_DARWIN
__attribute__ ((destructor)) static void
frida_deinit_memory (void)
{
asm volatile ("");
}
# endif
# endif
#else
#define FRIDA_SHIM_LOCK() gum_spinlock_acquire (&frida_shim_lock)
+3 -3
View File
@@ -420,7 +420,7 @@ namespace Frida {
var reason_value = new Json.Node.alloc ().init_string (reason.to_nick ());
try {
yield rpc_client.call ("dispose", new Json.Node[] { reason_value }, null);
yield rpc_client.call ("dispose", new Json.Node[] { reason_value }, null, null);
} catch (GLib.Error e) {
}
@@ -467,10 +467,10 @@ namespace Frida {
this.debug_message (message);
}
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
if (script == null)
throw new Error.INVALID_OPERATION ("Script is destroyed");
script.post (json);
script.post (json, data);
}
}
}
+15 -12
View File
@@ -38,21 +38,24 @@ namespace Frida {
var interceptor = Gum.Interceptor.obtain ();
#if WINDOWS
var create_process_internal = Gum.Module.find_export_by_name ("kernelbase.dll", "CreateProcessInternalW");
if (create_process_internal == 0)
create_process_internal = Gum.Module.find_export_by_name ("kernel32.dll", "CreateProcessInternalW");
var kernelbase = Gum.Process.find_module_by_name ("kernelbase.dll");
var create_process_internal = (kernelbase != null) ? kernelbase.find_export_by_name ("CreateProcessInternalW") : 0;
if (create_process_internal == 0) {
create_process_internal = Gum.Process.find_module_by_name ("kernel32.dll")
.find_export_by_name ("CreateProcessInternalW");
}
assert (create_process_internal != 0);
interceptor.attach ((void *) create_process_internal, this);
#else
unowned string libc = Gum.Process.query_libc_name ();
var libc = Gum.Process.get_libc_module ();
#if DARWIN
posix_spawn = (PosixSpawnFunc) Gum.Module.find_export_by_name (libc, "posix_spawn");
posix_spawnattr_init = (PosixSpawnAttrInitFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_init");
posix_spawnattr_destroy = (PosixSpawnAttrDestroyFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_destroy");
posix_spawnattr_getflags = (PosixSpawnAttrSetFlagsFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_getflags");
posix_spawnattr_setflags = (PosixSpawnAttrSetFlagsFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_setflags");
posix_spawn = (PosixSpawnFunc) libc.find_export_by_name ("posix_spawn");
posix_spawnattr_init = (PosixSpawnAttrInitFunc) libc.find_export_by_name ("posix_spawnattr_init");
posix_spawnattr_destroy = (PosixSpawnAttrDestroyFunc) libc.find_export_by_name ("posix_spawnattr_destroy");
posix_spawnattr_getflags = (PosixSpawnAttrSetFlagsFunc) libc.find_export_by_name ("posix_spawnattr_getflags");
posix_spawnattr_setflags = (PosixSpawnAttrSetFlagsFunc) libc.find_export_by_name ("posix_spawnattr_setflags");
execve = (void *) Gum.Module.find_export_by_name (libc, "execve");
execve = (void *) libc.find_export_by_name ("execve");
interceptor.attach ((void *) posix_spawn, this);
@@ -60,10 +63,10 @@ namespace Frida {
#else
Gum.Address execve = 0;
#if ANDROID
execve = Gum.Module.find_symbol_by_name (libc, "__execve");
execve = libc.find_symbol_by_name ("__execve");
#endif
if (execve == 0)
execve = Gum.Module.find_export_by_name (libc, "execve");
execve = libc.find_export_by_name ("execve");
interceptor.attach ((void *) execve, this);
#endif
#endif
+4 -3
View File
@@ -26,9 +26,10 @@ namespace Frida {
construct {
var interceptor = Gum.Interceptor.obtain ();
task_threads = (TaskThreadsFunc) Gum.Module.find_export_by_name (LIBSYSTEM_KERNEL, "task_threads");
thread_suspend = (ThreadSuspendFunc) Gum.Module.find_export_by_name (LIBSYSTEM_KERNEL, "thread_suspend");
thread_resume = (ThreadResumeFunc) Gum.Module.find_export_by_name (LIBSYSTEM_KERNEL, "thread_resume");
var kernel = Gum.Process.find_module_by_name (LIBSYSTEM_KERNEL);
task_threads = (TaskThreadsFunc) kernel.find_export_by_name ("task_threads");
thread_suspend = (ThreadSuspendFunc) kernel.find_export_by_name ("thread_suspend");
thread_resume = (ThreadResumeFunc) kernel.find_export_by_name ("thread_resume");
interceptor.replace ((void *) task_threads, (void *) replacement_task_threads, this);
interceptor.replace ((void *) thread_suspend, (void *) replacement_thread_suspend, this);
+12 -4
View File
@@ -300,6 +300,7 @@ static gpointer
frida_find_vtable (void)
{
GumAddress result = 0;
GumModule * libunwind;
GumAddress export;
uint64_t address;
G_GNUC_UNUSED cs_err err;
@@ -309,11 +310,15 @@ frida_find_vtable (void)
size_t size;
const size_t max_size = 2048;
export = gum_module_find_export_by_name (FRIDA_LIBUNWIND_PATH, "unw_init_local");
libunwind = gum_process_find_module_by_name (FRIDA_LIBUNWIND_PATH);
if (libunwind == NULL)
goto beach;
export = gum_module_find_export_by_name (libunwind, "unw_init_local");
if (export == 0)
export = gum_module_find_export_by_name (FRIDA_LIBUNWIND_PATH, "_Unwind_RaiseException");
export = gum_module_find_export_by_name (libunwind, "_Unwind_RaiseException");
if (export == 0)
return NULL;
goto beach;
export = gum_strip_code_address (export);
address = export;
@@ -340,7 +345,7 @@ frida_find_vtable (void)
GumMemoryRange bss_range;
bss_range.base_address = 0;
gum_module_enumerate_sections (FRIDA_LIBUNWIND_PATH, (GumFoundSectionFunc) frida_find_bss_range, &bss_range);
gum_module_enumerate_sections (libunwind, (GumFoundSectionFunc) frida_find_bss_range, &bss_range);
while (cs_disasm_iter (capstone, &code, &size, &address, insn))
{
@@ -404,6 +409,9 @@ frida_find_vtable (void)
cs_free (insn, 1);
cs_close (&capstone);
beach:
g_clear_object (&libunwind);
return GSIZE_TO_POINTER (result);
}
+1 -1
View File
@@ -21,7 +21,7 @@ namespace Frida {
var interceptor = Gum.Interceptor.obtain ();
dyld_find_unwind_sections = (DyldFindUnwindSectionsFunc)
Gum.Module.find_export_by_name (LIBDYLD, "_dyld_find_unwind_sections");
Gum.Process.find_module_by_name (LIBDYLD).find_export_by_name ("_dyld_find_unwind_sections");
interceptor.replace ((void *) dyld_find_unwind_sections, (void *) replacement_dyld_find_unwind_sections, this);
+9 -2
View File
@@ -97,7 +97,7 @@ if cc.sizeof('void *') == 8
else
host_cpu_mode = '32'
endif
host_docks_mobile_devices = host_os in ['windows', 'macos', 'linux', 'freebsd'] and host_cpu_mode == '64'
host_docks_mobile_devices = host_os in ['windows', 'macos', 'linux'] and host_cpu_mode == '64'
gumjs_archs = {
'x86': 'ia32',
'x86_64': 'x64',
@@ -347,6 +347,13 @@ else
gio_unix_dep = dependency('gio-unix-2.0')
endif
if host_os == 'android'
minizip_dep = dependency('minizip', required: false, default_options: [
'zlib=enabled',
'lzma=disabled',
])
endif
have_local_backend = get_option('local_backend').allowed()
have_fruity_backend = get_option('fruity_backend') \
.disable_auto_if(not host_docks_mobile_devices) \
@@ -474,7 +481,7 @@ if have_fruity_backend
'dns=disabled',
'arp=disabled',
'ethernet=enabled',
'tcp_mss=1360',
'tcp_mss=4036',
'tcp_snd_buf=65535',
'tcp_wnd=65535',
]
+14 -2
View File
@@ -68,13 +68,25 @@ option('compat',
option('helper_modern',
type: 'string',
value: 'auto',
description: 'Prebuilt frida-helper to embed for use on modern systems'
description: 'Prebuilt frida-helper to embed for supporting modern targets'
)
option('helper_legacy',
type: 'string',
value: 'auto',
description: 'Prebuilt frida-helper to embed for use on legacy systems'
description: 'Prebuilt frida-helper to embed for supporting legacy targets'
)
option('helper_emulated_modern',
type: 'string',
value: 'auto',
description: 'Prebuilt frida-helper to embed for supporting modern emulated targets'
)
option('helper_emulated_legacy',
type: 'string',
value: 'auto',
description: 'Prebuilt frida-helper to embed for supporting legacy emulated targets'
)
option('agent_modern',
+1 -1
Submodule releng updated: 54cc29651c...2eecc41cbe.
+51 -8
View File
@@ -3,6 +3,7 @@ namespace Frida.Server {
private const string DEFAULT_DIRECTORY = "re.frida.server";
private static bool output_version = false;
private static string? device_id = null;
private static string? listen_address = null;
private static string? certpath = null;
private static string? origin = null;
@@ -30,6 +31,7 @@ namespace Frida.Server {
const OptionEntry[] option_entries = {
{ "version", 0, 0, OptionArg.NONE, ref output_version, "Output version information and exit", null },
{ "device", 0, 0, OptionArg.STRING, ref device_id, "Serve device with the given ID", "ID" },
{ "listen", 'l', 0, OptionArg.STRING, ref listen_address, "Listen on ADDRESS", "ADDRESS" },
{ "certificate", 0, 0, OptionArg.FILENAME, ref certpath, "Enable TLS using CERTIFICATE", "CERTIFICATE" },
{ "origin", 0, 0, OptionArg.STRING, ref origin, "Only accept requests with “Origin” header matching ORIGIN " +
@@ -178,7 +180,7 @@ namespace Frida.Server {
#if DARWIN
var worker = new Thread<int> ("frida-server-main-loop", () => {
var exit_code = run_application (endpoint_params, options, on_ready);
var exit_code = run_application (device_id, endpoint_params, options, on_ready);
_stop_run_loop ();
@@ -190,15 +192,16 @@ namespace Frida.Server {
return exit_code;
#else
return run_application (endpoint_params, options, on_ready);
return run_application (device_id, endpoint_params, options, on_ready);
#endif
}
private static int run_application (EndpointParameters endpoint_params, ControlServiceOptions options, ReadyHandler on_ready) {
private static int run_application (string? device_id, EndpointParameters endpoint_params, ControlServiceOptions options,
ReadyHandler on_ready) {
TemporaryDirectory.always_use ((directory != null) ? directory : DEFAULT_DIRECTORY);
TemporaryDirectory.use_sysroot (options.sysroot);
application = new Application (new ControlService (endpoint_params, options));
application = new Application (device_id, endpoint_params, options);
Posix.signal (Posix.Signal.INT, (sig) => {
application.stop ();
@@ -235,19 +238,36 @@ namespace Frida.Server {
private class Application : Object {
public signal void ready (bool success);
public ControlService service {
public string? device_id {
get;
construct;
}
public EndpointParameters endpoint_params {
get;
construct;
}
public ControlServiceOptions options {
get;
construct;
}
private DeviceManager? manager;
private ControlService? service;
private Cancellable io_cancellable = new Cancellable ();
private MainLoop loop = new MainLoop ();
private int exit_code;
private bool stopping;
public Application (ControlService service) {
Object (service: service);
public Application (string? device_id, EndpointParameters endpoint_params, ControlServiceOptions options) {
Object (
device_id: device_id,
endpoint_params: endpoint_params,
options: options
);
}
public int run () {
@@ -265,6 +285,17 @@ namespace Frida.Server {
private async void start () {
try {
if (device_id != null && device_id != "local") {
manager = new DeviceManager.with_nonlocal_backends_only ();
var device = yield manager.get_device_by_id (device_id, 0, io_cancellable);
device.lost.connect (on_device_lost);
service = yield new ControlService.with_device (device, endpoint_params, options);
} else {
service = new ControlService (endpoint_params, options);
}
yield service.start (io_cancellable);
} catch (GLib.Error e) {
if (e is IOError.CANCELLED)
@@ -297,7 +328,15 @@ namespace Frida.Server {
io_cancellable.cancel ();
try {
yield service.stop ();
if (service != null) {
yield service.stop ();
service = null;
}
if (manager != null) {
yield manager.close ();
manager = null;
}
} catch (GLib.Error e) {
}
@@ -306,6 +345,10 @@ namespace Frida.Server {
return false;
});
}
private void on_device_lost () {
stop ();
}
}
private TlsCertificate? parse_certificate (string? path) throws GLib.Error {
+117 -31
View File
@@ -1,36 +1,49 @@
from __future__ import annotations
import argparse
import os
from dataclasses import dataclass
from io import StringIO
from pathlib import Path
import re
import sys
from typing import List, Set
import xml.etree.ElementTree as ET
CORE_NAMESPACE = "http://www.gtk.org/introspection/core/1.0"
C_NAMESPACE = "http://www.gtk.org/introspection/c/1.0"
GLIB_NAMESPACE = "http://www.gtk.org/introspection/glib/1.0"
GIR_NAMESPACES = {
"": CORE_NAMESPACE,
"c": C_NAMESPACE,
"glib": GLIB_NAMESPACE,
}
CORE_TAG_FIELD = f"{{{CORE_NAMESPACE}}}field"
CORE_TAG_CONSTRUCTOR = f"{{{CORE_NAMESPACE}}}constructor"
CORE_TAG_METHOD = f"{{{CORE_NAMESPACE}}}method"
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('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('core_header', metavar='/path/to/frida-core.h', 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('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_dir', metavar='/output/dir')
args = parser.parse_args()
output_type = args.output_type
api_version = args.api_version
core_header = args.core_header.read()
core_gir = args.core_gir.read()
core_vapi = args.core_vapi.read()
core_header = args.core_header.read()
base_vapi = args.base_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 == 'gir':
(output_dir / f"Frida-{api_version}.gir").write_text(core_gir, encoding='utf-8')
return
if output_type == 'vapi-stamp':
(output_dir / f"frida-core-{api_version}.vapi.stamp").write_bytes(b"")
return
@@ -45,30 +58,36 @@ def main():
toplevel_sources = []
src_dir = Path(__file__).parent.parent.resolve()
for name in toplevel_names:
with open(src_dir / name, 'r', encoding='utf-8') as f:
toplevel_sources.append(f.read())
toplevel_sources.append((src_dir / name).read_text(encoding='utf-8'))
toplevel_code = "\n".join(toplevel_sources)
enable_header = False
enable_gir = False
enable_vapi = False
if output_type == 'bundle':
enable_header = True
enable_gir = True
enable_vapi = True
elif output_type == 'header':
enable_header = True
elif output_type == 'gir':
enable_gir = True
elif output_type == 'vapi':
enable_vapi = True
api = parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, base_header)
api = parse_api(api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi)
if enable_header:
emit_header(api, output_dir)
if enable_gir:
emit_gir(api, core_gir, base_gir, output_dir)
if enable_vapi:
emit_vapi(api, output_dir)
def emit_header(api, output_dir):
with open(output_dir / 'frida-core.h', 'w', encoding='utf-8') as output_header_file:
with OutputFile(output_dir / 'frida-core.h') as output_header_file:
output_header_file.write("#ifndef __FRIDA_CORE_H__\n#define __FRIDA_CORE_H__\n\n")
output_header_file.write("#include <glib.h>\n#include <glib-object.h>\n#include <gio/gio.h>\n#include <json-glib/json-glib.h>\n")
@@ -79,7 +98,6 @@ def emit_header(api, output_dir):
output_header_file.write("\ntypedef struct _%s %s;" % (object_type.c_name, object_type.c_name))
if object_type.c_iface_definition is not None:
output_header_file.write("\ntypedef struct _%sIface %sIface;" % (object_type.c_name, object_type.c_name))
output_header_file.write("\ntypedef struct _FridaHostSession FridaHostSession;")
for enum in api.enum_types:
output_header_file.write("\n\n" + enum.c_definition)
@@ -150,8 +168,61 @@ def emit_header(api, output_dir):
output_header_file.write("\n\n#endif\n")
def emit_gir(api: ApiSpec, core_gir: str, base_gir: str, output_dir: Path) -> str:
ET.register_namespace("", CORE_NAMESPACE)
ET.register_namespace("c", C_NAMESPACE)
ET.register_namespace("glib", GLIB_NAMESPACE)
core_tree = ET.ElementTree(ET.fromstring(core_gir))
base_tree = ET.ElementTree(ET.fromstring(base_gir))
core_root = core_tree.getroot()
base_root = base_tree.getroot()
merged_root = ET.Element(core_root.tag, core_root.attrib)
for elem in core_root.findall("include", GIR_NAMESPACES):
name = elem.get("name")
if name in {"GLib", "GObject", "Gio"}:
merged_root.append(elem)
for tag in ["package", "c:include"]:
for elem in core_root.findall(tag, GIR_NAMESPACES):
merged_root.append(elem)
core_namespace = core_root.find("namespace", GIR_NAMESPACES)
merged_namespace = ET.SubElement(merged_root, core_namespace.tag, core_namespace.attrib)
object_type_names = {obj.name for obj in api.object_types}
enum_type_names = {enum.name for enum in api.enum_types}
error_type_names = {error.name for error in api.error_types}
def merge_and_transform_elements(tag_name: str, spec_set: Set[str]):
core_elements = filter_elements(core_root.findall(f".//{tag_name}", GIR_NAMESPACES), spec_set)
base_elements = filter_elements(base_root.findall(f".//{tag_name}", GIR_NAMESPACES), spec_set)
for elem in core_elements + base_elements:
if tag_name == "class":
for child in list(elem):
if child.tag == CORE_TAG_FIELD or child.get("name").startswith("_"):
elem.remove(child)
merged_namespace.append(elem)
merge_and_transform_elements("class", object_type_names)
merge_and_transform_elements("interface", object_type_names)
merge_and_transform_elements("enumeration", enum_type_names | error_type_names)
ET.indent(merged_root, space=" ")
result = ET.tostring(merged_root,
encoding="unicode",
xml_declaration=True)
with OutputFile(output_dir / f"Frida-{api.version}.gir") as output_gir:
output_gir.write(result)
def filter_elements(elements: List[ET.Element], spec_set: Set[str]):
return [elem for elem in elements if elem.get("name") in spec_set]
def emit_vapi(api, output_dir):
with open(output_dir / "frida-core-{0}.vapi".format(api.version), "w", encoding='utf-8') as output_vapi_file:
with OutputFile(output_dir / f"frida-core-{api.version}.vapi") as output_vapi_file:
output_vapi_file.write("[CCode (cheader_filename = \"frida-core.h\", cprefix = \"Frida\", lower_case_cprefix = \"frida_\")]")
output_vapi_file.write("\nnamespace Frida {")
output_vapi_file.write("\n\tpublic static void init ();")
@@ -189,12 +260,12 @@ def emit_vapi(api, output_dir):
output_vapi_file.write("\n}\n")
with open(output_dir / "frida-core-{0}.deps".format(api.version), "w", encoding='utf-8') as output_deps_file:
with OutputFile(output_dir / f"frida-core-{api.version}.deps") as output_deps_file:
output_deps_file.write("glib-2.0\n")
output_deps_file.write("gobject-2.0\n")
output_deps_file.write("gio-2.0\n")
def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, base_header):
def parse_api(api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi):
all_headers = core_header + "\n" + base_header
all_enum_names = [m.group(1) for m in re.finditer(r"^\t+public\s+enum\s+(\w+)\s+", toplevel_code + "\n" + base_vapi, re.MULTILINE)]
@@ -287,8 +358,7 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
method_cname_lc = object_type.c_name_lc + '_' + method_name
if method_cname_lc not in seen_cfunctions:
seen_cfunctions.add(method_cname_lc)
if method_name not in ('construct', 'construct_with_host_session', 'get_main_context', 'get_provider', 'get_session') \
and not (object_type.name in ("Session", "Script") and method_name == 'get_id'):
if method_name != 'construct':
if (object_type.c_name + '*') in m.group(0):
if method_name == 'new' or method_name.startswith('new_'):
object_type.c_constructors.append(method_cprototype)
@@ -362,8 +432,7 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
current_object_type.vapi_signals.append(stripped_line)
elif "{ get" in stripped_line:
name = re.match(r".+?(\w+)\s+{", stripped_line).group(1)
if name not in ('main_context', 'provider', 'session'):
current_object_type.vapi_properties.append(stripped_line)
current_object_type.vapi_properties.append(stripped_line)
else:
m = re.match(r".+?(\w+)\s+\(", stripped_line)
if m is not None:
@@ -400,19 +469,19 @@ def function_is_public(name):
"negotiate_connection"
]
def parse_vala_object_types(source):
return [ApiObjectType(m.group(2), m.group(1)) for m in re.finditer(r"^\t+public\s+(class|interface)\s+(\w+)\s+", source, re.MULTILINE)]
def parse_vala_object_types(source) -> List[ApiObjectType]:
return [ApiObjectType(m.group(3), m.group(2)) for m in re.finditer(r"^\t+public\s+(sealed )?(class|interface)\s+(\w+)\s+", source, re.MULTILINE)]
def parse_vapi_functions(vapi):
def parse_vapi_functions(vapi) -> List[ApiFunction]:
return [ApiFunction(m.group(1), m.group(0)) for m in re.finditer(r"^\tpublic static .+ (\w+) \(.+;", vapi, re.MULTILINE)]
@dataclass
class ApiSpec:
def __init__(self, version, object_types, functions, enum_types, error_types):
self.version = version
self.object_types = object_types
self.functions = functions
self.enum_types = enum_types
self.error_types = error_types
version: str
object_types: List[ApiObjectType]
functions: List[ApiFunction]
enum_types: List[ApiEnum]
error_types: List[ApiEnum]
class ApiEnum:
def __init__(self, name):
@@ -514,6 +583,23 @@ def fuzzysort(items, keys):
result.extend(remaining)
return result
class OutputFile:
def __init__(self, output_path):
self._output_path = output_path
self._io = StringIO()
def __enter__(self):
return self._io
def __exit__(self, *exc):
result = self._io.getvalue()
if self._output_path.exists():
existing_contents = self._output_path.read_text(encoding='utf-8')
if existing_contents == result:
return False
self._output_path.write_text(result, encoding='utf-8')
return False
if __name__ == '__main__':
main()
+13 -30
View File
@@ -2,54 +2,37 @@ generate_api = [python, files('generate.py')]
generate_api_args = [
api_version,
core_build_dir / 'frida-core.h',
core_build_dir / core_gir_name,
core_build_dir / 'frida-core.vapi',
core_build_dir / 'frida-core.h',
base_build_dir / f'frida-base-@[email protected]',
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_public_h = custom_target('frida-core-header',
input: [core, base],
output: ['frida-core.h'],
command: [
generate_api,
'--output=header',
generate_api_args,
],
install: true,
install_dir: header_install_dir,
)
core_public_gir = custom_target('frida-core-gir',
input: [core, base],
output: [core_gir_name],
command: [
generate_api,
'--output=gir',
generate_api_args,
],
install: true,
install_dir: girdir,
)
core_public_vapi = custom_target('frida-core-vapi',
core_api = custom_target('frida-core-api',
input: [core, base],
output: [
'frida-core.h',
core_gir_name,
f'frida-core-@[email protected]',
f'frida-core-@[email protected]',
],
command: [
generate_api,
'--output=vapi',
'--output=bundle',
generate_api_args,
],
install: true,
install_dir: vapidir,
install_dir: [header_install_dir, girdir, vapidir, vapidir],
)
core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
core_public_h = core_api[0]
core_public_gir = core_api[1]
core_public_vapi = [core_api[2], core_api[3]]
core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
input: core_public_vapi,
output: f'frida-core-@[email protected]',
command: [
+39
View File
@@ -141,6 +141,29 @@ namespace Frida {
return yield this.host_session.link_agent_session (id, sink, cancellable);
}
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
if (host_session != this.host_session)
return;
this.host_session.unlink_agent_session (id);
}
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
}
public void unlink_channel (HostSession host_session, ChannelId id) {
}
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id, Cancellable? cancellable)
throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
}
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
}
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
agent_session_detached (id, reason, crash);
}
@@ -260,6 +283,14 @@ namespace Frida {
return session;
}
public void unlink_agent_session (AgentSessionId id) {
BareboneAgentSession? session = agent_sessions[id];
if (session == null)
return;
session.message_sink = null;
}
public async InjectorPayloadId inject_library_file (uint pid, string path, string entrypoint, string data,
Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
@@ -270,6 +301,14 @@ namespace Frida {
throw_not_supported ();
}
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
}
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
}
private void on_agent_session_closed (BareboneAgentSession session) {
AgentSessionId id = session.id;
+3 -1
View File
@@ -420,7 +420,9 @@ namespace Frida {
public void post (string json, Bytes? data) {
var json_val = ctx.make_string (json);
invoke_void (dispatch_message_func, { json_val }, runtime_obj);
var data_val = (data != null) ? ctx.make_array_buffer (data.get_data ()) : QuickJS.Null;
invoke_void (dispatch_message_func, { json_val, data_val }, runtime_obj);
ctx.free_value (data_val);
ctx.free_value (json_val);
perform_pending_io ();
+4 -4
View File
@@ -199,7 +199,7 @@ namespace Frida {
try {
var device = yield manager.get_device_by_type (LOCAL, 0, io_cancellable);
var session = yield device.get_host_session (io_cancellable);
var agent = new Agent (this, (BaseDBusHostSession) session);
var agent = new Agent (this, (LocalHostSession) session);
load_request.resolve (agent);
} catch (GLib.Error e) {
@@ -238,7 +238,7 @@ namespace Frida {
private Promise<string> source_request = new Promise<string> ();
private Promise<Bytes?> snapshot_request = new Promise<Bytes?> ();
public Agent (Compiler parent, BaseDBusHostSession host_session) {
public Agent (Compiler parent, LocalHostSession host_session) {
Object (
parent: parent,
host_session: host_session,
@@ -275,7 +275,7 @@ namespace Frida {
new Json.Node.alloc ().init_string (entrypoint),
new Json.Node.alloc ().init_string (opts.source_maps.to_nick ()),
new Json.Node.alloc ().init_string (opts.compression.to_nick ()),
}, cancellable);
}, null, cancellable);
return bundle.get_string ();
}
@@ -288,7 +288,7 @@ namespace Frida {
new Json.Node.alloc ().init_string (entrypoint),
new Json.Node.alloc ().init_string (opts.source_maps.to_nick ()),
new Json.Node.alloc ().init_string (opts.compression.to_nick ()),
}, cancellable);
}, null, cancellable);
}
private static string compute_project_root (string entrypoint, CompilerOptions options) {
+16 -15
View File
@@ -9,7 +9,7 @@
"version": "1.0.0",
"dependencies": {
"@frida/crosspath": "3.0.0",
"frida-compile": "16.4.1",
"frida-compile": "16.4.2",
"frida-fs": "5.2.5"
},
"devDependencies": {
@@ -17,7 +17,7 @@
"@rollup/plugin-node-resolve": "^14.1.0",
"@rollup/plugin-replace": "^4.0.0",
"@rollup/plugin-typescript": "^8.3.4",
"@types/frida-gum": "18.7.0",
"@types/frida-gum": "18.8.2",
"@types/glob": "^8.0.0",
"@types/node": "18.7.23",
"glob": "^8.0.3",
@@ -468,10 +468,11 @@
"dev": true
},
"node_modules/@types/frida-gum": {
"version": "18.7.0",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.0.tgz",
"integrity": "sha512-HhBomXE23fLDAWXEKi3BjJLrlH9wAv9IEQNfO/PaYHQNNbh0Bi06gx6JbXTspVpbqlbVqkWAuU7n6HaS9B6yXA==",
"dev": true
"version": "18.8.2",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.8.2.tgz",
"integrity": "sha512-5KH6ITrKJQe1UWrNnbXXx3KXRe/Lx9I1EwkBdC+AMmlo9F5UC0PxdC959aGxhfjXBRIHWxXwXPi3JmeWIbeqeg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/glob": {
"version": "8.0.0",
@@ -640,9 +641,9 @@
"dev": true
},
"node_modules/frida-compile": {
"version": "16.4.1",
"resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-16.4.1.tgz",
"integrity": "sha512-xI9HNtUFpHxuKBGXaL6XJAYH1zWTvNJDrHRnz1hp0oS24iPHt6c01Jmm3qljZek3oQWn8HhvfNvrzfgbsKzoBQ==",
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-16.4.2.tgz",
"integrity": "sha512-wNiFk37JVpCoEsuJF9AHZtccM9Z3KGj3yu0Amsb/DKzKnHCxvl8AlAU5qSfYXfAJtzbNuCKGqNBM8u96KvP28A==",
"dependencies": {
"@frida/assert": "^3.0.1",
"@frida/base64-js": "^2.0.3",
@@ -1465,9 +1466,9 @@
"dev": true
},
"@types/frida-gum": {
"version": "18.7.0",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.0.tgz",
"integrity": "sha512-HhBomXE23fLDAWXEKi3BjJLrlH9wAv9IEQNfO/PaYHQNNbh0Bi06gx6JbXTspVpbqlbVqkWAuU7n6HaS9B6yXA==",
"version": "18.8.2",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.8.2.tgz",
"integrity": "sha512-5KH6ITrKJQe1UWrNnbXXx3KXRe/Lx9I1EwkBdC+AMmlo9F5UC0PxdC959aGxhfjXBRIHWxXwXPi3JmeWIbeqeg==",
"dev": true
},
"@types/glob": {
@@ -1596,9 +1597,9 @@
"dev": true
},
"frida-compile": {
"version": "16.4.1",
"resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-16.4.1.tgz",
"integrity": "sha512-xI9HNtUFpHxuKBGXaL6XJAYH1zWTvNJDrHRnz1hp0oS24iPHt6c01Jmm3qljZek3oQWn8HhvfNvrzfgbsKzoBQ==",
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-16.4.2.tgz",
"integrity": "sha512-wNiFk37JVpCoEsuJF9AHZtccM9Z3KGj3yu0Amsb/DKzKnHCxvl8AlAU5qSfYXfAJtzbNuCKGqNBM8u96KvP28A==",
"requires": {
"@frida/assert": "^3.0.1",
"@frida/base64-js": "^2.0.3",
+2 -2
View File
@@ -9,7 +9,7 @@
},
"dependencies": {
"@frida/crosspath": "3.0.0",
"frida-compile": "16.4.1",
"frida-compile": "16.4.2",
"frida-fs": "5.2.5"
},
"devDependencies": {
@@ -17,7 +17,7 @@
"@rollup/plugin-node-resolve": "^14.1.0",
"@rollup/plugin-replace": "^4.0.0",
"@rollup/plugin-typescript": "^8.3.4",
"@types/frida-gum": "18.7.0",
"@types/frida-gum": "18.8.2",
"@types/glob": "^8.0.0",
"@types/node": "18.7.23",
"glob": "^8.0.3",
+518 -302
View File
File diff suppressed because it is too large. Load diff
+16 -89
View File
@@ -1,98 +1,24 @@
namespace Frida {
public class DarwinHostSessionBackend : Object, HostSessionBackend {
private DarwinHostSessionProvider local_provider;
public async void start (Cancellable? cancellable) throws IOError {
assert (local_provider == null);
local_provider = new DarwinHostSessionProvider ();
provider_available (local_provider);
}
public async void stop (Cancellable? cancellable) throws IOError {
assert (local_provider != null);
provider_unavailable (local_provider);
yield local_provider.close (cancellable);
local_provider = null;
public class DarwinHostSessionBackend : LocalHostSessionBackend {
protected override LocalHostSessionProvider make_provider () {
return new DarwinHostSessionProvider ();
}
}
public class DarwinHostSessionProvider : Object, HostSessionProvider {
public string id {
get { return "local"; }
}
public string name {
get { return "Local System"; }
}
public Variant? icon {
get { return _icon; }
}
private Variant? _icon;
public HostSessionProviderKind kind {
get { return HostSessionProviderKind.LOCAL; }
}
private DarwinHostSession host_session;
construct {
_icon = _try_extract_icon ();
}
public async void close (Cancellable? cancellable) throws IOError {
if (host_session == null)
return;
host_session.agent_session_detached.disconnect (on_agent_session_detached);
yield host_session.close (cancellable);
host_session = null;
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
public class DarwinHostSessionProvider : LocalHostSessionProvider {
protected override LocalHostSession make_host_session (HostSessionOptions? options) throws Error {
var tempdir = new TemporaryDirectory ();
host_session = new DarwinHostSession (new DarwinHelperProcess (tempdir), tempdir);
host_session.agent_session_detached.connect (on_agent_session_detached);
return host_session;
return new DarwinHostSession (new DarwinHelperProcess (tempdir), tempdir);
}
public async void destroy (HostSession session, Cancellable? cancellable) throws Error, IOError {
if (session != host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
host_session.agent_session_detached.disconnect (on_agent_session_detached);
yield host_session.close (cancellable);
host_session = null;
}
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
Cancellable? cancellable) throws Error, IOError {
if (host_session != this.host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
return yield this.host_session.link_agent_session (id, sink, cancellable);
}
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
if (host_session != this.host_session)
return;
this.host_session.unlink_agent_session (id);
}
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
agent_session_detached (id, reason, crash);
protected override Variant? load_icon () {
return _try_extract_icon ();
}
public extern static Variant? _try_extract_icon ();
}
public class DarwinHostSession : BaseDBusHostSession {
public class DarwinHostSession : LocalHostSession {
public DarwinHelper helper {
get;
construct;
@@ -933,27 +859,28 @@ namespace Frida {
}
public async void prepare_for_launch (string identifier, Cancellable? cancellable) throws Error, IOError {
yield call ("prepareForLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, cancellable);
yield call ("prepareForLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, null,
cancellable);
}
public async void cancel_launch (string identifier, Cancellable? cancellable) throws Error, IOError {
yield call ("cancelLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, cancellable);
yield call ("cancelLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, null, cancellable);
}
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
yield call ("enableSpawnGating", new Json.Node[] {}, cancellable);
yield call ("enableSpawnGating", new Json.Node[] {}, null, cancellable);
}
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
yield call ("disableSpawnGating", new Json.Node[] {}, cancellable);
yield call ("disableSpawnGating", new Json.Node[] {}, null, cancellable);
}
public async void claim_process (uint pid, Cancellable? cancellable) throws Error, IOError {
yield call ("claimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, cancellable);
yield call ("claimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, null, cancellable);
}
public async void unclaim_process (uint pid, Cancellable? cancellable) throws Error, IOError {
yield call ("unclaimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, cancellable);
yield call ("unclaimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, null, cancellable);
}
protected override void on_event (string type, Json.Array event) {
+88 -44
View File
@@ -86,7 +86,7 @@
# define CORE_FOUNDATION "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
#endif
typedef struct _FridaHelperContext FridaHelperContext;
typedef struct _FridaDispatchContext FridaDispatchContext;
typedef struct _FridaSpawnInstance FridaSpawnInstance;
typedef guint FridaDyldFlavor;
typedef struct _FridaSpawnInstanceDyldData FridaSpawnInstanceDyldData;
@@ -106,7 +106,7 @@ typedef union _FridaDebugState FridaDebugState;
typedef int FridaConvertThreadStateDirection;
typedef guint FridaAslr;
struct _FridaHelperContext
struct _FridaDispatchContext
{
dispatch_queue_t dispatch_queue;
};
@@ -183,6 +183,7 @@ struct _FridaSpawnInstance
GumAddress modern_entry_address;
/* V4+ */
GumAddress notify_objc_init;
GumAddress info_ptr_address;
/* V3- */
@@ -375,7 +376,7 @@ enum _FridaAslr
};
static FridaSpawnInstance * frida_spawn_instance_new (FridaDarwinHelperBackend * backend);
static void frida_spawn_instance_free (FridaSpawnInstance * instance);
static void frida_spawn_instance_close (FridaSpawnInstance * instance);
static void frida_spawn_instance_resume (FridaSpawnInstance * self);
static void frida_spawn_instance_on_server_cancel (void * context);
@@ -390,12 +391,12 @@ static void frida_spawn_instance_set_libc_initialized (FridaSpawnInstance * self
static kern_return_t frida_spawn_instance_create_dyld_data (FridaSpawnInstance * self);
static void frida_spawn_instance_destroy_dyld_data (FridaSpawnInstance * self);
#if defined (HAVE_IOS) || defined (HAVE_TVOS)
static gboolean frida_pick_ios_tvos_bootstrapper (const GumModuleDetails * details, gpointer user_data);
static gboolean frida_pick_ios_tvos_bootstrapper (GumModule * module, gpointer user_data);
#endif
static void frida_spawn_instance_unset_helpers (FridaSpawnInstance * self);
static void frida_spawn_instance_call_set_helpers (FridaSpawnInstance * self, GumDarwinUnifiedThreadState * state, mach_vm_address_t helpers);
static void frida_spawn_instance_call_dlopen (FridaSpawnInstance * self, GumDarwinUnifiedThreadState * state, mach_vm_address_t lib_name, int mode);
static gboolean frida_find_cf_initialize (const GumModuleDetails * details, gpointer user_data);
static gboolean frida_find_cf_initialize (GumModule * module, gpointer user_data);
static void frida_spawn_instance_call_cf_initialize (FridaSpawnInstance * self, GumDarwinUnifiedThreadState * state);
static void frida_spawn_instance_set_nth_breakpoint (FridaSpawnInstance * self, guint n, GumAddress break_at, FridaBreakpointRepeat repeat);
static void frida_spawn_instance_enable_nth_breakpoint (FridaSpawnInstance * self, guint n);
@@ -409,7 +410,7 @@ static void frida_configure_terminal_attributes (gint fd);
static FridaInjectInstance * frida_inject_instance_new (FridaDarwinHelperBackend * backend, guint id, guint pid);
static FridaInjectInstance * frida_inject_instance_clone (const FridaInjectInstance * instance, guint id);
static void frida_inject_instance_free (FridaInjectInstance * instance);
static void frida_inject_instance_close (FridaInjectInstance * instance);
static gboolean frida_inject_instance_task_did_not_exec (FridaInjectInstance * instance);
static gboolean frida_inject_instance_start_thread (FridaInjectInstance * self, GError ** error);
@@ -761,30 +762,30 @@ permission_denied:
}
void
_frida_darwin_helper_backend_create_context (FridaDarwinHelperBackend * self)
_frida_darwin_helper_backend_create_dispatch_context (FridaDarwinHelperBackend * self)
{
FridaHelperContext * ctx;
FridaDispatchContext * ctx;
ctx = g_slice_new (FridaHelperContext);
ctx = g_slice_new (FridaDispatchContext);
ctx->dispatch_queue = dispatch_queue_create ("re.frida.helper.queue", DISPATCH_QUEUE_SERIAL);
self->context = ctx;
self->dispatch_context = ctx;
}
void
_frida_darwin_helper_backend_destroy_context (FridaDarwinHelperBackend * self)
_frida_darwin_helper_backend_destroy_dispatch_context (FridaDarwinHelperBackend * self)
{
FridaHelperContext * ctx = self->context;
FridaDispatchContext * ctx = self->dispatch_context;
dispatch_release (ctx->dispatch_queue);
g_slice_free (FridaHelperContext, ctx);
g_slice_free (FridaDispatchContext, ctx);
}
void
_frida_darwin_helper_backend_schedule_on_dispatch_queue (FridaDarwinHelperBackend * self, FridaDarwinHelperBackendDispatchWorker worker, gpointer user_data)
{
FridaHelperContext * ctx = self->context;
FridaDispatchContext * ctx = self->dispatch_context;
dispatch_async (ctx->dispatch_queue, ^
{
@@ -933,7 +934,7 @@ any_failure:
{
if (instance->pid != 0)
kill (instance->pid, SIGKILL);
frida_spawn_instance_free (instance);
frida_spawn_instance_close (instance);
pid = 0;
@@ -1868,7 +1869,7 @@ void
_frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHelperBackend * self, void * opaque_instance, guint task, GError ** error)
{
FridaSpawnInstance * instance = opaque_instance;
FridaHelperContext * ctx = self->context;
FridaDispatchContext * ctx = self->dispatch_context;
const gchar * failed_operation;
kern_return_t kr;
mach_port_t self_task, child_thread;
@@ -2030,6 +2031,10 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
instance->dyld_flavor = (modern_entry_address != 0) ? FRIDA_DYLD_V4_PLUS : FRIDA_DYLD_V3_MINUS;
if (instance->dyld_flavor == FRIDA_DYLD_V4_PLUS)
{
instance->notify_objc_init = gum_darwin_module_resolve_symbol_address (dyld, "__ZN5dyld412RuntimeState14notifyObjCInitEPKNS_6LoaderE");
if (instance->notify_objc_init != 0)
modern_entry_address = instance->notify_objc_init;
instance->modern_entry_address = modern_entry_address;
legacy_entry_address = 0;
@@ -2108,7 +2113,12 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
if (instance->dyld_flavor == FRIDA_DYLD_V4_PLUS)
{
GumAddress restart_with_dyld_in_cache = gum_darwin_module_resolve_symbol_address (dyld,
"__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPK15DyldSharedCachePv");
if (restart_with_dyld_in_cache == 0)
{
restart_with_dyld_in_cache = gum_darwin_module_resolve_symbol_address (dyld,
"__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPv");
}
if (restart_with_dyld_in_cache != 0)
frida_spawn_instance_set_nth_breakpoint (instance, i++, restart_with_dyld_in_cache, FRIDA_BREAKPOINT_REPEAT_NEVER);
}
@@ -2190,9 +2200,9 @@ _frida_darwin_helper_backend_resume_spawn_instance (FridaDarwinHelperBackend * s
}
void
_frida_darwin_helper_backend_free_spawn_instance (FridaDarwinHelperBackend * self, void * instance)
_frida_darwin_helper_backend_close_spawn_instance (FridaDarwinHelperBackend * self, void * instance)
{
frida_spawn_instance_free (instance);
frida_spawn_instance_close (instance);
}
guint
@@ -2479,7 +2489,7 @@ mach_failure:
}
failure:
{
frida_inject_instance_free (instance);
frida_inject_instance_close (instance);
goto beach;
}
beach:
@@ -2589,7 +2599,7 @@ frida_inject_instance_start_thread (FridaInjectInstance * self, GError ** error)
mach_msg_type_number_t thread_state_count;
kern_return_t kr;
const gchar * failed_operation;
FridaHelperContext * ctx = self->backend->context;
FridaDispatchContext * ctx = self->backend->dispatch_context;
dispatch_source_t source;
thread_state = g_alloca (self->thread_state_count * sizeof (natural_t));
@@ -2636,7 +2646,7 @@ frida_inject_instance_on_thread_monitor_cancel (void * context)
FridaInjectInstance * self = context;
dispatch_release (g_steal_pointer (&self->thread_monitor_source));
frida_inject_instance_free (self);
frida_inject_instance_close (self);
}
static void
@@ -2695,7 +2705,7 @@ _frida_darwin_helper_backend_join_inject_instance_posix_thread (FridaDarwinHelpe
static void
frida_inject_instance_join_posix_thread (FridaInjectInstance * self, mach_port_t posix_thread)
{
FridaHelperContext * ctx = self->backend->context;
FridaDispatchContext * ctx = self->backend->dispatch_context;
mach_port_t self_task;
dispatch_source_t source;
@@ -2728,9 +2738,9 @@ _frida_darwin_helper_backend_get_pid_of_inject_instance (FridaDarwinHelperBacken
}
void
_frida_darwin_helper_backend_free_inject_instance (FridaDarwinHelperBackend * self, void * instance)
_frida_darwin_helper_backend_close_inject_instance (FridaDarwinHelperBackend * self, void * instance)
{
frida_inject_instance_free (instance);
frida_inject_instance_close (instance);
}
static FridaSpawnInstance *
@@ -2740,7 +2750,7 @@ frida_spawn_instance_new (FridaDarwinHelperBackend * backend)
guint i;
instance = g_slice_new0 (FridaSpawnInstance);
instance->backend = backend;
instance->backend = g_object_ref (backend);
instance->thread = MACH_PORT_NULL;
instance->server_port = MACH_PORT_NULL;
@@ -2763,11 +2773,13 @@ frida_spawn_instance_new (FridaDarwinHelperBackend * backend)
instance->page_pool[i].scratch_page = 0;
}
_frida_darwin_helper_backend_on_instance_created (backend, instance);
return instance;
}
static void
frida_spawn_instance_free (FridaSpawnInstance * instance)
frida_spawn_instance_close (FridaSpawnInstance * instance)
{
task_t self_task;
FridaExceptionPortSet * previous_ports;
@@ -2806,6 +2818,9 @@ frida_spawn_instance_free (FridaSpawnInstance * instance)
if (instance->dyld != NULL)
g_object_unref (instance->dyld);
_frida_darwin_helper_backend_on_instance_destroyed (instance->backend, instance);
g_object_unref (instance->backend);
g_slice_free (FridaSpawnInstance, instance);
}
@@ -2848,7 +2863,7 @@ frida_spawn_instance_on_server_cancel (void * context)
FridaSpawnInstance * self = context;
dispatch_release (g_steal_pointer (&self->server_recv_source));
frida_spawn_instance_free (self);
frida_spawn_instance_close (self);
}
static void
@@ -3085,9 +3100,15 @@ frida_spawn_instance_handle_breakpoint (FridaSpawnInstance * self, FridaBreakpoi
if (self->dyld_flavor == FRIDA_DYLD_V4_PLUS)
{
if (pc == self->modern_entry_address)
self->breakpoint_phase = FRIDA_BREAKPOINT_SET_LIBDYLD_INITIALIZE_CALLER_BREAKPOINT;
{
self->breakpoint_phase = (pc == self->notify_objc_init)
? FRIDA_BREAKPOINT_LIBSYSTEM_INITIALIZED
: FRIDA_BREAKPOINT_SET_LIBDYLD_INITIALIZE_CALLER_BREAKPOINT;
}
else
{
return frida_spawn_instance_handle_dyld_restart (self);
}
}
else
{
@@ -3353,6 +3374,7 @@ frida_spawn_instance_handle_dyld_restart (FridaSpawnInstance * self)
GumAddress * info_ptr;
struct dyld_all_image_infos * info = NULL;
GumDarwinModule * dyld = NULL;
gssize delta;
info_ptr = (GumAddress *) gum_darwin_read (self->task, self->info_ptr_address, sizeof (GumAddress), NULL);
if (info_ptr == NULL)
@@ -3367,7 +3389,11 @@ frida_spawn_instance_handle_dyld_restart (FridaSpawnInstance * self)
if (dyld == NULL)
goto beach;
self->modern_entry_address = GUM_ADDRESS (info->dyldImageLoadAddress) + (self->modern_entry_address - self->dyld->base_address);
delta = (gssize) info->dyldImageLoadAddress - (gssize) self->dyld->base_address;
self->modern_entry_address += delta;
if (self->notify_objc_init != 0)
self->notify_objc_init += delta;
g_object_unref (self->dyld);
self->dyld = g_steal_pointer (&dyld);
@@ -3625,22 +3651,25 @@ frida_spawn_instance_destroy_dyld_data (FridaSpawnInstance * self)
#if defined (HAVE_IOS) || defined (HAVE_TVOS)
static gboolean
frida_pick_ios_tvos_bootstrapper (const GumModuleDetails * details, gpointer user_data)
frida_pick_ios_tvos_bootstrapper (GumModule * module, gpointer user_data)
{
FridaSpawnInstanceDyldData * data = user_data;
const gchar * path;
const gchar * candidates[] = {
"/usr/lib/substitute-inserter.dylib",
"/usr/lib/pspawn_payload-stg2.dylib"
};
guint i;
path = gum_module_get_path (module);
for (i = 0; i != G_N_ELEMENTS (candidates); i++)
{
const gchar * bootstrapper = candidates[i];
if (strcmp (details->path, bootstrapper) == 0)
if (strcmp (path, bootstrapper) == 0)
{
strcpy (data->bootstrapper, details->path);
strcpy (data->bootstrapper, path);
return FALSE;
}
}
@@ -3803,15 +3832,15 @@ frida_spawn_instance_call_dlopen (FridaSpawnInstance * self, GumDarwinUnifiedThr
}
static gboolean
frida_find_cf_initialize (const GumModuleDetails * details, gpointer user_data)
frida_find_cf_initialize (GumModule * module, gpointer user_data)
{
FridaSpawnInstance * self = user_data;
GumDarwinModule * core_foundation;
if (strcmp (details->path, CORE_FOUNDATION) != 0)
if (strcmp (gum_module_get_path (module), CORE_FOUNDATION) != 0)
return TRUE;
core_foundation = gum_darwin_module_new_from_memory (CORE_FOUNDATION, self->task, details->range->base_address,
core_foundation = gum_darwin_module_new_from_memory (CORE_FOUNDATION, self->task, gum_module_get_range (module)->base_address,
GUM_DARWIN_MODULE_FLAGS_NONE, NULL);
self->cf_initialize_address = gum_darwin_module_resolve_symbol_address (core_foundation, "___CFInitialize");
@@ -4090,6 +4119,8 @@ frida_inject_instance_new (FridaDarwinHelperBackend * backend, guint id, guint p
instance->backend = g_object_ref (backend);
_frida_darwin_helper_backend_on_instance_created (backend, instance);
return instance;
}
@@ -4119,11 +4150,13 @@ frida_inject_instance_clone (const FridaInjectInstance * instance, guint id)
g_object_ref (clone->backend);
_frida_darwin_helper_backend_on_instance_created (clone->backend, clone);
return clone;
}
static void
frida_inject_instance_free (FridaInjectInstance * instance)
frida_inject_instance_close (FridaInjectInstance * instance)
{
FridaAgentContext * agent_context = instance->agent_context;
task_t self_task;
@@ -4163,6 +4196,7 @@ frida_inject_instance_free (FridaInjectInstance * instance)
if (instance->task != MACH_PORT_NULL)
mach_port_deallocate (self_task, instance->task);
_frida_darwin_helper_backend_on_instance_destroyed (instance->backend, instance);
g_object_unref (instance->backend);
g_slice_free (FridaInjectInstance, instance);
@@ -4268,9 +4302,10 @@ frida_agent_context_init (FridaAgentContext * self, const FridaAgentDetails * de
static gboolean
frida_agent_context_init_functions (FridaAgentContext * self, GumDarwinModuleResolver * resolver, GumDarwinMapper * mapper, GError ** error)
{
gboolean success = FALSE;
GumDarwinModule * module;
module = gum_darwin_module_resolver_find_module (resolver, "/usr/lib/system/libsystem_kernel.dylib");
module = gum_darwin_module_resolver_find_module_by_name (resolver, "/usr/lib/system/libsystem_kernel.dylib");
if (module == NULL)
goto no_libc;
FRIDA_AGENT_CONTEXT_RESOLVE (mach_task_self);
@@ -4279,8 +4314,10 @@ frida_agent_context_init_functions (FridaAgentContext * self, GumDarwinModuleRes
FRIDA_AGENT_CONTEXT_RESOLVE (mach_msg_receive);
FRIDA_AGENT_CONTEXT_RESOLVE (mach_port_destroy);
FRIDA_AGENT_CONTEXT_RESOLVE (thread_terminate);
g_object_unref (module);
module = NULL;
module = gum_darwin_module_resolver_find_module (resolver, "/usr/lib/system/libsystem_pthread.dylib");
module = gum_darwin_module_resolver_find_module_by_name (resolver, "/usr/lib/system/libsystem_pthread.dylib");
if (module == NULL)
goto no_libc;
FRIDA_AGENT_CONTEXT_TRY_RESOLVE (pthread_create_from_mach_thread);
@@ -4291,18 +4328,23 @@ frida_agent_context_init_functions (FridaAgentContext * self, GumDarwinModuleRes
FRIDA_AGENT_CONTEXT_TRY_RESOLVE (pthread_threadid_np);
FRIDA_AGENT_CONTEXT_RESOLVE (pthread_detach);
FRIDA_AGENT_CONTEXT_RESOLVE (pthread_self);
g_object_unref (module);
module = NULL;
if (mapper == NULL)
{
module = gum_darwin_module_resolver_find_module (resolver, "/usr/lib/system/libdyld.dylib");
module = gum_darwin_module_resolver_find_module_by_name (resolver, "/usr/lib/system/libdyld.dylib");
if (module == NULL)
goto no_libc;
FRIDA_AGENT_CONTEXT_RESOLVE (dlopen);
FRIDA_AGENT_CONTEXT_RESOLVE (dlsym);
FRIDA_AGENT_CONTEXT_RESOLVE (dlclose);
g_object_unref (module);
module = NULL;
}
return TRUE;
success = TRUE;
goto beach;
no_libc:
{
@@ -4310,7 +4352,7 @@ no_libc:
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unable to attach to processes without Apple's libc (for now)");
goto failure;
goto beach;
}
missing_symbol:
{
@@ -4318,11 +4360,13 @@ missing_symbol:
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error while resolving functions");
goto failure;
goto beach;
}
failure:
beach:
{
return FALSE;
g_clear_object (&module);
return success;
}
}
+61 -28
View File
@@ -20,7 +20,8 @@ namespace Frida {
protected delegate void DispatchWorker ();
protected delegate void LaunchCompletionHandler (owned StdioPipes? pipes, owned Error? error);
public void * context;
private MainContext main_context;
public void * dispatch_context;
public Gee.HashMap<uint, void *> spawn_instances = new Gee.HashMap<uint, void *> ();
private Gee.HashMap<uint, OutputStream> stdin_streams = new Gee.HashMap<uint, OutputStream> ();
@@ -32,6 +33,9 @@ namespace Frida {
private Gee.HashMap<void *, uint> inject_cleaner_by_instance = new Gee.HashMap<void *, uint> ();
private Gee.HashMap<uint, uint> inject_expiry_timers = new Gee.HashMap<uint, uint> ();
private Gee.Set<void *> instances = new Gee.HashSet<void *> ();
private SourceFunc? on_last_instance_destroyed = null;
public uint next_id = 1;
private PolicySoftener policy_softener;
@@ -40,7 +44,8 @@ namespace Frida {
private Cancellable io_cancellable = new Cancellable ();
construct {
_create_context ();
main_context = MainContext.ref_thread_default ();
_create_dispatch_context ();
dtrace_agent = DTraceAgent.try_open ();
if (dtrace_agent != null) {
@@ -63,11 +68,7 @@ namespace Frida {
}
~DarwinHelperBackend () {
foreach (var instance in spawn_instances.values)
_free_spawn_instance (instance);
foreach (var instance in inject_instances.values)
_free_inject_instance (instance);
_destroy_context ();
_destroy_dispatch_context ();
}
public async void close (Cancellable? cancellable) throws IOError {
@@ -77,11 +78,24 @@ namespace Frida {
pending.complete ();
foreach (var entry in inject_cleaner_by_instance.entries) {
_free_inject_instance (entry.key);
_close_inject_instance (entry.key);
Source.remove (entry.value);
}
inject_cleaner_by_instance.clear ();
foreach (var instance in spawn_instances.values)
_close_spawn_instance (instance);
spawn_instances.clear ();
bool any_pending = false;
lock (instances) {
any_pending = !instances.is_empty;
if (any_pending)
on_last_instance_destroyed = close.callback;
}
if (any_pending)
yield;
if (dtrace_agent != null) {
dtrace_agent.spawn_added.disconnect (on_dtrace_agent_spawn_added);
dtrace_agent.spawn_removed.disconnect (on_dtrace_agent_spawn_removed);
@@ -139,11 +153,11 @@ namespace Frida {
Error error = null;
_launch (identifier, options, (p, e) => {
Idle.add (() => {
schedule_on_frida_thread (() => {
pipes = p;
error = e;
launch.callback ();
return false;
return Source.REMOVE;
});
});
@@ -187,7 +201,7 @@ namespace Frida {
inject_cleaner_by_instance.unset (instance, out source_id);
Source.remove (source_id);
_free_inject_instance (instance);
_close_inject_instance (instance);
}
policy_softener.forget (pid);
@@ -205,7 +219,7 @@ namespace Frida {
void * instance;
if (spawn_instances.unset (pid, out instance))
_free_spawn_instance (instance);
_close_spawn_instance (instance);
child_dead (pid);
}
@@ -293,7 +307,7 @@ namespace Frida {
void * instance;
if (spawn_instances.unset (pid, out instance)) {
_resume_spawn_instance (instance);
_free_spawn_instance (instance);
_close_spawn_instance (instance);
} else {
resume_with_validation (pid);
}
@@ -412,7 +426,7 @@ namespace Frida {
} catch (GLib.Error e) {
if (instance_created_here) {
spawn_instances.unset (pid);
_free_spawn_instance (spawn_instance);
_close_spawn_instance (spawn_instance);
}
throw_api_error (e);
@@ -495,21 +509,21 @@ namespace Frida {
}
public void _on_spawn_instance_ready (uint pid) {
Idle.add (() => {
schedule_on_frida_thread (() => {
spawn_instance_ready (pid);
return false;
return Source.REMOVE;
});
}
public void _on_spawn_instance_error (uint pid, Error error) {
Idle.add (() => {
schedule_on_frida_thread (() => {
spawn_instance_error (pid, error);
return false;
return Source.REMOVE;
});
}
public void _on_mach_thread_dead (uint id, void * posix_thread) {
Idle.add (() => {
schedule_on_frida_thread (() => {
var instance = inject_instances[id];
assert (instance != null);
@@ -518,14 +532,14 @@ namespace Frida {
else
_destroy_inject_instance (id);
return false;
return Source.REMOVE;
});
}
public void _on_posix_thread_dead (uint id) {
Idle.add (() => {
schedule_on_frida_thread (() => {
_destroy_inject_instance (id);
return false;
return Source.REMOVE;
});
}
@@ -545,7 +559,7 @@ namespace Frida {
private void schedule_inject_instance_cleanup (void * instance) {
var cleanup_source = new TimeoutSource (50);
cleanup_source.set_callback (() => {
_free_inject_instance (instance);
_close_inject_instance (instance);
var removed = inject_cleaner_by_instance.unset (instance);
assert (removed);
@@ -601,6 +615,19 @@ namespace Frida {
policy_softener.forget (pid);
}
public void _on_instance_created (void * instance) {
lock (instances)
instances.add (instance);
}
public void _on_instance_destroyed (void * instance) {
lock (instances) {
instances.remove (instance);
if (on_last_instance_destroyed != null && instances.is_empty)
schedule_on_frida_thread ((owned) on_last_instance_destroyed);
}
}
private void on_dtrace_agent_spawn_added (HostSpawnInfo info) {
spawn_added (info);
}
@@ -611,11 +638,17 @@ namespace Frida {
private async void flush_dispatch_queue () {
_schedule_on_dispatch_queue (() => {
Idle.add (flush_dispatch_queue.callback);
schedule_on_frida_thread (flush_dispatch_queue.callback);
});
yield;
}
private void schedule_on_frida_thread (owned SourceFunc function) {
var source = new IdleSource ();
source.set_callback ((owned) function);
source.attach (main_context);
}
public extern static PipeEndpoints make_pipe_endpoints (uint local_task, uint remote_pid, uint remote_task) throws Error;
public extern static void make_pipe_endpoint_from_socket (uint pid, uint task, GLib.Socket sock, out string address)
throws Error;
@@ -626,8 +659,8 @@ namespace Frida {
public extern static bool is_mmap_available ();
public extern static MappedLibraryBlob mmap (uint task, Bytes blob) throws Error;
protected extern void _create_context ();
protected extern void _destroy_context ();
protected extern void _create_dispatch_context ();
protected extern void _destroy_dispatch_context ();
protected extern void _schedule_on_dispatch_queue (DispatchWorker worker);
protected extern uint _spawn (string path, HostSpawnOptions options, out StdioPipes? pipes) throws Error;
@@ -642,7 +675,7 @@ namespace Frida {
protected extern void * _create_spawn_instance (uint pid);
protected extern void _prepare_spawn_instance_for_injection (void * instance, uint task) throws Error;
protected extern void _resume_spawn_instance (void * instance);
protected extern void _free_spawn_instance (void * instance);
protected extern void _close_spawn_instance (void * instance);
protected extern uint _inject_into_task (uint pid, uint task, string path_or_name, MappedLibraryBlob? blob, string entrypoint, string data) throws Error;
protected extern void _demonitor (void * instance);
@@ -650,7 +683,7 @@ namespace Frida {
protected extern void _recreate_injectee_thread (void * instance, uint pid, uint task) throws Error;
protected extern void _join_inject_instance_posix_thread (void * instance, void * posix_thread);
protected extern uint _get_pid_of_inject_instance (void * instance);
protected extern void _free_inject_instance (void * instance);
protected extern void _close_inject_instance (void * instance);
}
public class DTraceAgent : Object {
+4 -2
View File
@@ -151,10 +151,12 @@ frida_replacement_xpc_look_up_endpoint (const char * service_name, uint32_t type
static FridaXpcLookUpEndpointFunc
frida_find_xpc_look_up_endpoint_implementation (void)
{
GumModule * libxpc;
guint32 * cursor;
cursor = GSIZE_TO_POINTER (
gum_strip_code_address (gum_module_find_export_by_name ("/usr/lib/system/libxpc.dylib", "xpc_endpoint_create_bs_named")));
libxpc = gum_process_find_module_by_name ("/usr/lib/system/libxpc.dylib");
cursor = GSIZE_TO_POINTER (gum_strip_code_address (gum_module_find_export_by_name (libxpc, "xpc_endpoint_create_bs_named")));
g_object_unref (libxpc);
if (cursor == NULL)
return NULL;
+1 -1
View File
@@ -791,7 +791,7 @@ namespace Frida.Droidy {
}
private const uint16 ADB_SERVER_DEFAULT_PORT = 5037;
private const uint16 MAX_MESSAGE_LENGTH = 1024;
private const size_t MAX_MESSAGE_LENGTH = 65536;
public static async Client open (Cancellable? cancellable = null) throws Error, IOError {
SocketConnectable? connectable = null;
+70
View File
@@ -158,6 +158,36 @@ namespace Frida {
return yield this.host_session.link_agent_session (id, sink, cancellable);
}
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
if (host_session != this.host_session)
return;
this.host_session.unlink_agent_session (id);
}
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
throws Error, IOError {
if (host_session != this.host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
return this.host_session.link_channel (id);
}
public void unlink_channel (HostSession host_session, ChannelId id) {
if (host_session != this.host_session)
return;
this.host_session.unlink_channel (id);
}
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id, Cancellable? cancellable)
throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
}
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
}
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
agent_session_detached (id, reason, crash);
}
@@ -211,6 +241,8 @@ namespace Frida {
private Gee.HashMap<AgentSessionId?, AgentSessionEntry> agent_sessions =
new Gee.HashMap<AgentSessionId?, AgentSessionEntry> (AgentSessionId.hash, AgentSessionId.equal);
private ChannelRegistry channel_registry = new ChannelRegistry ();
private Cancellable io_cancellable = new Cancellable ();
private const double MIN_SERVER_CHECK_INTERVAL = 5.0;
@@ -223,6 +255,10 @@ namespace Frida {
);
}
construct {
channel_registry.channel_closed.connect (on_channel_closed);
}
public async void close (Cancellable? cancellable) throws IOError {
if (remote_server_request != null) {
var server = yield try_get_remote_server (cancellable);
@@ -832,6 +868,15 @@ namespace Frida {
return session;
}
public void unlink_agent_session (AgentSessionId id) {
AgentSessionEntry? entry = agent_sessions[id];
if (entry == null || entry.sink_registration_id == 0)
return;
entry.connection.unregister_object (entry.sink_registration_id);
entry.sink_registration_id = 0;
}
public async InjectorPayloadId inject_library_file (uint pid, string path, string entrypoint, string data,
Cancellable? cancellable) throws Error, IOError {
var server = yield get_remote_server (cancellable);
@@ -852,6 +897,31 @@ namespace Frida {
}
}
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
var stream = yield channel_provider.open_channel (address, cancellable);
var id = ChannelId.generate ();
channel_registry.register (id, stream);
return id;
}
public IOStream link_channel (ChannelId id) throws Error {
return channel_registry.link (id);
}
public void unlink_channel (ChannelId id) {
channel_registry.unlink (id);
}
private void on_channel_closed (ChannelId id) {
channel_closed (id);
}
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
}
private void on_gadget_entry_detached (GadgetEntry entry, SessionDetachReason reason) {
AgentSessionId id = entry.local_session_id;
var no_crash = CrashInfo.empty ();
+42 -18
View File
@@ -2,6 +2,7 @@ from pathlib import Path
import shutil
import subprocess
import sys
import struct
def main(argv):
@@ -27,29 +28,36 @@ def main(argv):
embedded_assets = []
if host_os == "windows":
for agent, flavor in [(agent_modern, "64"),
(agent_legacy, "32")]:
embedded_agent = priv_dir / f"frida-agent-{flavor}.dll"
embedded_dbghelp = priv_dir / f"dbghelp-{flavor}.dll"
embedded_symsrv = priv_dir / f"symsrv-{flavor}.dll"
pending_archs = {"arm64", "x86_64", "x86"}
for agent in {agent_modern, agent_legacy, agent_emulated_modern, agent_emulated_legacy}:
if agent is None:
continue
arch = detect_pefile_arch(agent)
embedded_agent = priv_dir / f"frida-agent-{arch}.dll"
embedded_dbghelp = priv_dir / f"dbghelp-{arch}.dll"
embedded_symsrv = priv_dir / f"symsrv-{arch}.dll"
if agent is not None:
shutil.copy(agent, embedded_agent)
shutil.copy(agent, embedded_agent)
if agent_dbghelp_prefix is not None:
shutil.copy(agent_dbghelp_prefix / flavor / "dbghelp.dll", embedded_dbghelp)
else:
embedded_dbghelp.write_bytes(b"")
if agent_symsrv_prefix is not None:
shutil.copy(agent_symsrv_prefix / flavor / "symsrv.dll", embedded_symsrv)
else:
embedded_symsrv.write_bytes(b"")
if agent_dbghelp_prefix is not None:
shutil.copy(agent_dbghelp_prefix / arch / "dbghelp.dll", embedded_dbghelp)
else:
for f in [embedded_agent, embedded_dbghelp, embedded_symsrv]:
f.write_bytes(b"")
embedded_dbghelp.write_bytes(b"")
if agent_symsrv_prefix is not None:
shutil.copy(agent_symsrv_prefix / arch / "symsrv.dll", embedded_symsrv)
else:
embedded_symsrv.write_bytes(b"")
embedded_assets += [embedded_agent, embedded_dbghelp, embedded_symsrv]
pending_archs.remove(arch)
for missing_arch in pending_archs:
embedded_agent = priv_dir / f"frida-agent-{missing_arch}.dll"
embedded_dbghelp = priv_dir / f"dbghelp-{missing_arch}.dll"
embedded_symsrv = priv_dir / f"symsrv-{missing_arch}.dll"
for asset in {embedded_agent, embedded_dbghelp, embedded_symsrv}:
asset.write_bytes(b"")
embedded_assets += [asset]
elif host_os in {"macos", "ios", "watchos", "tvos"}:
embedded_agent = priv_dir / "frida-agent.dylib"
if agent_modern is not None and agent_legacy is not None:
@@ -103,5 +111,21 @@ def pop_cmd_array_arg(args):
return result
def detect_pefile_arch(location):
with location.open(mode="rb") as pe:
pe.seek(0x3c)
e_lfanew, = struct.unpack("<I", pe.read(4))
pe.seek(e_lfanew + 4)
machine, = struct.unpack("<H", pe.read(2))
return PE_MACHINES[machine]
PE_MACHINES = {
0x014c: "x86",
0x8664: "x86_64",
0xaa64: "arm64",
}
if __name__ == "__main__":
main(sys.argv)
+37 -6
View File
@@ -2,6 +2,7 @@ from pathlib import Path
import shutil
import subprocess
import sys
import struct
def main(argv):
@@ -14,12 +15,28 @@ def main(argv):
output_dir = Path(args.pop(0))
priv_dir = Path(args.pop(0))
resource_config = args.pop(0)
helper_modern, helper_legacy = [Path(p) if p else None for p in args[:2]]
helper_modern, helper_legacy, \
helper_emulated_modern, helper_emulated_legacy \
= [Path(p) if p else None for p in args[:4]]
priv_dir.mkdir(exist_ok=True)
embedded_assets = []
if host_os in {"macos", "ios", "watchos", "tvos"}:
if host_os == "windows":
pending_archs = {"arm64", "x86_64", "x86"}
for helper in {helper_modern, helper_legacy, helper_emulated_modern, helper_emulated_legacy}:
if helper is None:
continue
arch = detect_pefile_arch(helper)
embedded_helper = priv_dir / f"frida-helper-{arch}.exe"
shutil.copy(helper, embedded_helper)
embedded_assets += [embedded_helper]
pending_archs.remove(arch)
for missing_arch in pending_archs:
embedded_helper = priv_dir / f"frida-helper-{missing_arch}.exe"
embedded_helper.write_bytes(b"")
embedded_assets += [embedded_helper]
elif host_os in {"macos", "ios", "watchos", "tvos"}:
embedded_helper = priv_dir / "frida-helper"
if helper_modern is not None and helper_legacy is not None:
@@ -34,10 +51,8 @@ def main(argv):
embedded_assets += [embedded_helper]
else:
exe_suffix = ".exe" if host_os == "windows" else ""
embedded_helper_modern = priv_dir / f"frida-helper-64{exe_suffix}"
embedded_helper_legacy = priv_dir / f"frida-helper-32{exe_suffix}"
embedded_helper_modern = priv_dir / f"frida-helper-64"
embedded_helper_legacy = priv_dir / f"frida-helper-32"
if helper_modern is not None:
shutil.copy(helper_modern, embedded_helper_modern)
@@ -74,5 +89,21 @@ def pop_cmd_array_arg(args):
return result
def detect_pefile_arch(location):
with location.open(mode="rb") as pe:
pe.seek(0x3c)
e_lfanew, = struct.unpack("<I", pe.read(4))
pe.seek(e_lfanew + 4)
machine, = struct.unpack("<H", pe.read(2))
return PE_MACHINES[machine]
PE_MACHINES = {
0x014c: "x86",
0x8664: "x86_64",
0xaa64: "arm64",
}
if __name__ == "__main__":
main(sys.argv)
+7 -72
View File
@@ -1,82 +1,17 @@
namespace Frida {
public class FreebsdHostSessionBackend : Object, HostSessionBackend {
private FreebsdHostSessionProvider local_provider;
public async void start (Cancellable? cancellable) throws IOError {
assert (local_provider == null);
local_provider = new FreebsdHostSessionProvider ();
provider_available (local_provider);
}
public async void stop (Cancellable? cancellable) throws IOError {
assert (local_provider != null);
provider_unavailable (local_provider);
yield local_provider.close (cancellable);
local_provider = null;
public class FreebsdHostSessionBackend : LocalHostSessionBackend {
protected override LocalHostSessionProvider make_provider () {
return new FreebsdHostSessionProvider ();
}
}
public class FreebsdHostSessionProvider : Object, HostSessionProvider {
public string id {
get { return "local"; }
}
public string name {
get { return "Local System"; }
}
public Variant? icon {
get { return null; }
}
public HostSessionProviderKind kind {
get { return HostSessionProviderKind.LOCAL; }
}
private FreebsdHostSession host_session;
public async void close (Cancellable? cancellable) throws IOError {
if (host_session == null)
return;
host_session.agent_session_detached.disconnect (on_agent_session_detached);
yield host_session.close (cancellable);
host_session = null;
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
host_session = new FreebsdHostSession ();
host_session.agent_session_detached.connect (on_agent_session_detached);
return host_session;
}
public async void destroy (HostSession session, Cancellable? cancellable) throws Error, IOError {
if (session != host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
host_session.agent_session_detached.disconnect (on_agent_session_detached);
yield host_session.close (cancellable);
host_session = null;
}
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
Cancellable? cancellable) throws Error, IOError {
if (host_session != this.host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
return yield this.host_session.link_agent_session (id, sink, cancellable);
}
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
agent_session_detached (id, reason, crash);
public class FreebsdHostSessionProvider : LocalHostSessionProvider {
protected override LocalHostSession make_host_session (HostSessionOptions? options) throws Error {
return new FreebsdHostSession ();
}
}
public class FreebsdHostSession : BaseDBusHostSession {
public class FreebsdHostSession : LocalHostSession {
private AgentContainer system_session_container;
private AgentDescriptor agent_desc;
+182 -116
View File
@@ -1,3 +1,4 @@
[CCode (gir_namespace = "Frida", gir_version = "1.0")]
namespace Frida {
public extern void init ();
public extern void init_with_runtime (Runtime runtime);
@@ -33,12 +34,24 @@ namespace Frida {
private Promise<bool> start_request;
private Promise<bool> stop_request;
private HostSessionService service = null;
private HostSessionService service;
private Gee.ArrayList<Device> devices = new Gee.ArrayList<Device> ();
private Gee.ArrayList<DeviceObserverEntry> on_device_added = new Gee.ArrayList<DeviceObserverEntry> ();
private Cancellable io_cancellable = new Cancellable ();
public DeviceManager () {
service = new HostSessionService.with_default_backends ();
}
public DeviceManager.with_nonlocal_backends_only () {
service = new HostSessionService.with_nonlocal_backends_only ();
}
public DeviceManager.with_socket_backend_only () {
service = new HostSessionService.with_socket_backend_only ();
}
public async void close (Cancellable? cancellable = null) throws IOError {
yield stop_service (cancellable);
}
@@ -303,7 +316,7 @@ namespace Frida {
}
}
public void _release_device (Device device) {
internal void _release_device (Device device) {
var device_did_exist = devices.remove (device);
assert (device_did_exist);
}
@@ -339,7 +352,6 @@ namespace Frida {
}
private async void start_service () {
service = new HostSessionService.with_default_backends ();
try {
service.provider_available.connect (on_provider_available);
service.provider_unavailable.connect (on_provider_unavailable);
@@ -524,18 +536,10 @@ namespace Frida {
}
}
public HostSessionProvider provider {
get;
construct;
}
public weak DeviceManager? manager {
get;
construct;
}
private string? _id;
private string? _name;
internal HostSessionProvider provider;
private unowned DeviceManager? manager;
private HostSessionOptions? host_session_options;
private Promise<HostSession>? host_session_request;
@@ -547,29 +551,30 @@ namespace Frida {
private Gee.HashSet<Promise<Session>> pending_attach_requests = new Gee.HashSet<Promise<Session>> ();
private Gee.HashMap<AgentSessionId?, Promise<bool>> pending_detach_requests =
new Gee.HashMap<AgentSessionId?, Promise<bool>> (AgentSessionId.hash, AgentSessionId.equal);
private Gee.Set<Service> services = new Gee.HashSet<Service> ();
private Bus _bus;
public delegate bool ProcessPredicate (Process process);
internal Device (DeviceManager? manager, HostSessionProvider provider, string? id = null, string? name = null,
internal Device (DeviceManager? mgr, HostSessionProvider prov, string? id = null, string? name = null,
HostSessionOptions? options = null) {
Object (
manager: manager,
provider: provider,
icon: provider.icon
);
Object (icon: prov.icon);
_id = id;
_name = name;
manager = mgr;
host_session_options = options;
assign_provider (prov);
}
construct {
_bus = new Bus (this);
}
private void assign_provider (HostSessionProvider prov) {
provider = prov;
provider.host_session_detached.connect (on_host_session_detached);
provider.agent_session_detached.connect (on_agent_session_detached);
_bus = new Bus (this);
}
public bool is_lost () {
@@ -1232,11 +1237,16 @@ namespace Frida {
public async IOStream open_channel (string address, Cancellable? cancellable = null) throws Error, IOError {
check_open ();
var channel_provider = provider as HostChannelProvider;
if (channel_provider == null)
throw new Error.NOT_SUPPORTED ("Channels are not supported by this device");
var host_session = yield get_host_session (cancellable);
return yield channel_provider.open_channel (address, cancellable);
ChannelId id;
try {
id = yield host_session.open_channel (address, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
return yield provider.link_channel (host_session, id, cancellable);
}
public IOStream open_channel_sync (string address, Cancellable? cancellable = null) throws Error, IOError {
@@ -1256,15 +1266,18 @@ namespace Frida {
public async Service open_service (string address, Cancellable? cancellable = null) throws Error, IOError {
check_open ();
var service_provider = provider as HostServiceProvider;
if (service_provider == null)
throw new Error.NOT_SUPPORTED ("Services are not supported by this device");
var host_session = yield get_host_session (cancellable);
var service = yield service_provider.open_service (address, cancellable);
service.close.connect (on_service_closed);
services.add (service);
ServiceSessionId id;
try {
id = yield host_session.open_service (address, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
return service;
var service_session = yield provider.link_service_session (host_session, id, cancellable);
return new Service (service_session);
}
public Service open_service_sync (string address, Cancellable? cancellable = null) throws Error, IOError {
@@ -1281,10 +1294,6 @@ namespace Frida {
}
}
private void on_service_closed (Service service) {
services.remove (service);
}
public async void unpair (Cancellable? cancellable = null) throws Error, IOError {
check_open ();
@@ -1310,7 +1319,7 @@ namespace Frida {
throw new Error.INVALID_OPERATION ("Device is gone");
}
public async HostSession get_host_session (Cancellable? cancellable) throws Error, IOError {
internal async HostSession get_host_session (Cancellable? cancellable) throws Error, IOError {
while (host_session_request != null) {
try {
return yield host_session_request.future.wait_async (cancellable);
@@ -1338,16 +1347,6 @@ namespace Frida {
}
}
public HostSession get_host_session_sync (Cancellable? cancellable = null) throws Error, IOError {
return create<GetHostSessionTask> ().execute (cancellable);
}
private class GetHostSessionTask : DeviceTask<HostSession> {
protected override async HostSession perform_operation () throws Error, IOError {
return yield parent.get_host_session (cancellable);
}
}
private void on_host_session_detached (HostSession session) {
if (session != current_host_session)
return;
@@ -1393,10 +1392,6 @@ namespace Frida {
close_request = new Promise<bool> ();
try {
foreach (var service in services.to_array ())
yield service.cancel (cancellable);
services.clear ();
while (!pending_detach_requests.is_empty) {
var iterator = pending_detach_requests.entries.iterator ();
iterator.next ();
@@ -1462,7 +1457,7 @@ namespace Frida {
}
}
public async void _release_session (Session session, bool may_block, Cancellable? cancellable) throws IOError {
internal async void _release_session (Session session, bool may_block, Cancellable? cancellable) throws IOError {
AgentSessionId? session_id = null;
foreach (var entry in agent_sessions.entries) {
if (entry.value == session) {
@@ -2004,13 +1999,71 @@ namespace Frida {
}
}
public interface Service : Object {
public sealed class Service : Object {
public signal void close ();
public signal void message (Variant message);
public abstract bool is_closed ();
private ServiceSession? session;
private bool disposed = false;
public abstract async void activate (Cancellable? cancellable = null) throws Error, IOError;
internal Service (ServiceSession session) {
this.session = session;
session.close.connect (on_close);
session.message.connect (on_message);
}
public override void dispose () {
if (!disposed) {
disposed = true;
MainContext context = get_main_context ();
if (context.is_owner ()) {
abandon ();
} else {
var source = new IdleSource ();
source.set_callback (() => {
abandon ();
return false;
});
source.attach (context);
}
}
base.dispose ();
}
~Service () {
forget_session ();
}
private void abandon () {
var s = session;
if (s != null) {
forget_session ();
s.cancel.begin (null);
}
}
private void forget_session () {
session.close.disconnect (on_close);
session.message.disconnect (on_message);
session = null;
}
public bool is_closed () {
return session == null;
}
public async void activate (Cancellable? cancellable = null) throws Error, IOError {
check_open ();
try {
yield session.activate (cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
}
public void activate_sync (Cancellable? cancellable = null) throws Error, IOError {
create<ActivateTask> ().execute (cancellable);
@@ -2022,7 +2075,17 @@ namespace Frida {
}
}
public abstract async void cancel (Cancellable? cancellable = null) throws IOError;
public async void cancel (Cancellable? cancellable = null) throws IOError {
if (session == null)
return;
try {
yield session.cancel (cancellable);
} catch (GLib.Error e) {
if (e is IOError.CANCELLED)
cancellable.set_error_if_cancelled ();
}
}
public void cancel_sync (Cancellable? cancellable = null) throws IOError {
try {
@@ -2038,7 +2101,15 @@ namespace Frida {
}
}
public abstract async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError;
public async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError {
check_open ();
try {
return yield session.request (parameters, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
}
public Variant request_sync (Variant parameters, Cancellable? cancellable = null) throws Error, IOError {
var task = create<RequestTask> () as RequestTask;
@@ -2054,6 +2125,20 @@ namespace Frida {
}
}
private void check_open () throws Error {
if (session == null)
throw new Error.INVALID_OPERATION ("Session is gone");
}
private void on_close () {
forget_session ();
close ();
}
private void on_message (Variant v) {
message (v);
}
private T create<T> () {
return Object.new (typeof (T), parent: this);
}
@@ -2074,26 +2159,13 @@ namespace Frida {
construct;
}
public AgentSessionId id {
get;
construct;
}
public AgentSession session {
get {
return active_session;
}
}
public uint persist_timeout {
get;
construct;
}
public weak Device device {
get;
construct;
}
private AgentSessionId id;
private unowned Device device;
private State state = ATTACHED;
private Promise<bool> close_request;
@@ -2131,12 +2203,10 @@ namespace Frida {
}
internal Session (Device device, uint pid, AgentSessionId id, SessionOptions options) {
Object (
pid: pid,
id: id,
persist_timeout: options.persist_timeout,
device: device
);
Object (pid: pid, persist_timeout: options.persist_timeout);
this.id = id;
this.device = device;
}
public bool is_detached () {
@@ -2192,7 +2262,7 @@ namespace Frida {
uint last_tx_batch_id;
try {
yield session.resume (last_rx_batch_id, cancellable, out last_tx_batch_id);
yield active_session.resume (last_rx_batch_id, cancellable, out last_tx_batch_id);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2224,7 +2294,7 @@ namespace Frida {
check_open ();
try {
yield session.enable_child_gating (cancellable);
yield active_session.enable_child_gating (cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2244,7 +2314,7 @@ namespace Frida {
check_open ();
try {
yield session.disable_child_gating (cancellable);
yield active_session.disable_child_gating (cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2268,7 +2338,7 @@ namespace Frida {
AgentScriptId script_id;
try {
script_id = yield session.create_script (source, raw_options, cancellable);
script_id = yield active_session.create_script (source, raw_options, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2306,7 +2376,7 @@ namespace Frida {
AgentScriptId script_id;
try {
script_id = yield session.create_script_from_bytes (bytes.get_data (), raw_options,
script_id = yield active_session.create_script_from_bytes (bytes.get_data (), raw_options,
cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
@@ -2345,7 +2415,7 @@ namespace Frida {
uint8[] data;
try {
data = yield session.compile_script (source, raw_options, cancellable);
data = yield active_session.compile_script (source, raw_options, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2378,7 +2448,7 @@ namespace Frida {
uint8[] data;
try {
data = yield session.snapshot_script (embed_script, raw_options, cancellable);
data = yield active_session.snapshot_script (embed_script, raw_options, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2782,7 +2852,7 @@ namespace Frida {
PortalMembershipId membership_id;
try {
membership_id = yield session.join_portal (address, raw_options, cancellable);
membership_id = yield active_session.join_portal (address, raw_options, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2944,7 +3014,7 @@ namespace Frida {
}
}
public void _release_script (AgentScriptId script_id) {
internal void _release_script (AgentScriptId script_id) {
var script_did_exist = scripts.unset (script_id);
assert (script_did_exist);
}
@@ -3010,7 +3080,7 @@ namespace Frida {
private async void close_session_and_peer_connection (Cancellable? cancellable) throws IOError {
try {
yield session.close (cancellable);
yield active_session.close (cancellable);
} catch (GLib.Error e) {
if (e is IOError.CANCELLED) {
discard_peer_connection ();
@@ -3033,6 +3103,10 @@ namespace Frida {
}
#endif
public DBusConnection _get_connection () {
return ((DBusProxy) active_session).g_connection;
}
private T create<T> () {
return Object.new (typeof (T), parent: this);
}
@@ -3049,22 +3123,18 @@ namespace Frida {
public signal void destroyed ();
public signal void message (string json, Bytes? data);
public AgentScriptId id {
get;
construct;
}
public weak Session session {
get;
construct;
}
private AgentScriptId id;
private unowned Session session;
private Promise<bool> close_request;
private Gum.InspectorServer? inspector_server;
internal Script (Session session, AgentScriptId script_id) {
Object (id: script_id, session: session);
Object ();
this.id = script_id;
this.session = session;
}
public bool is_destroyed () {
@@ -3075,7 +3145,7 @@ namespace Frida {
check_open ();
try {
yield session.session.load_script (id, cancellable);
yield session.active_session.load_script (id, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -3111,7 +3181,7 @@ namespace Frida {
check_open ();
try {
yield session.session.eternalize_script (id, cancellable);
yield session.active_session.eternalize_script (id, cancellable);
yield _do_close (false, cancellable);
} catch (GLib.Error e) {
@@ -3162,7 +3232,7 @@ namespace Frida {
inspector_server.message.connect (on_debugger_message_from_frontend);
try {
yield session.session.enable_debugger (id, cancellable);
yield session.active_session.enable_debugger (id, cancellable);
} catch (GLib.Error e) {
inspector_server = null;
@@ -3176,7 +3246,7 @@ namespace Frida {
inspector_server = null;
try {
yield session.session.disable_debugger (id, cancellable);
yield session.active_session.disable_debugger (id, cancellable);
} catch (GLib.Error e) {
}
@@ -3210,7 +3280,7 @@ namespace Frida {
inspector_server = null;
try {
yield session.session.disable_debugger (id, cancellable);
yield session.active_session.disable_debugger (id, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -3264,7 +3334,7 @@ namespace Frida {
if (may_block) {
try {
yield parent.session.destroy_script (id, cancellable);
yield parent.active_session.destroy_script (id, cancellable);
} catch (GLib.Error e) {
}
}
@@ -3287,23 +3357,19 @@ namespace Frida {
}
public class PortalMembership : Object {
public uint id {
get;
construct;
}
public Session session {
get;
construct;
}
private uint id;
private Session session;
internal PortalMembership (Session session, PortalMembershipId membership_id) {
Object (id: membership_id.handle, session: session);
Object ();
this.id = membership_id.handle;
this.session = session;
}
public async void terminate (Cancellable? cancellable = null) throws Error, IOError {
try {
yield session.session.leave_portal (PortalMembershipId (id), cancellable);
yield session.active_session.leave_portal (PortalMembershipId (id), cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
+351 -168
View File
@@ -19,7 +19,7 @@ namespace Frida.Fruity {
return null;
var iface_stream = new DataInputStream (iface.read ());
string iface_name = iface_stream.read_line ();
if (iface_name != "NCM Control" && iface_name != "AppleUSBEthernet")
if (iface_name != "NCM Control")
return null;
var serial = File.new_build_filename (dev_path, "..", "..", "..", "serial");
@@ -27,7 +27,7 @@ namespace Frida.Fruity {
return null;
var serial_stream = new DataInputStream (serial.read ());
return serial_stream.read_line ();
return UsbDevice.udid_from_serial_number (serial_stream.read_line ());
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("%s", e.message);
}
@@ -35,209 +35,392 @@ namespace Frida.Fruity {
}
public class LinuxPairingBrowser : Object, PairingBrowser {
private DBusConnection connection;
private AvahiServer server;
private AvahiServiceBrowser browser;
private Gee.List<PairingServiceDetails> current_batch = new Gee.ArrayList<PairingServiceDetails> ();
private Resolved.Manager? resolved;
private Source? timer;
private Cancellable io_cancellable = new Cancellable ();
construct {
start.begin ();
}
private const size_t IF_NAMESIZE = 16;
private async void start () {
public async void start (Cancellable? cancellable) throws IOError {
try {
connection = yield GLib.Bus.get (BusType.SYSTEM, io_cancellable);
var connection = yield GLib.Bus.get (BusType.SYSTEM, cancellable);
server = yield connection.get_proxy (AVAHI_SERVICE_NAME, "/", DO_NOT_LOAD_PROPERTIES, io_cancellable);
resolved = yield connection.get_proxy (Resolved.SERVICE_NAME, Resolved.SERVICE_PATH, DO_NOT_LOAD_PROPERTIES,
cancellable);
GLib.ObjectPath browser_path = yield server.service_browser_prepare (-1, INET6, PAIRING_REGTYPE,
PAIRING_DOMAIN, 0, io_cancellable);
browser = yield connection.get_proxy (AVAHI_SERVICE_NAME, browser_path, DO_NOT_LOAD_PROPERTIES,
io_cancellable);
browser.item_new.connect (on_item_new);
browser.all_for_now.connect (on_all_for_now);
yield browser.start (io_cancellable);
try {
yield resolve_services (MDNS_IPV6 | NO_NETWORK, cancellable);
schedule_next_poll ();
} catch (Error e) {
handle_poll_timer_tick.begin ();
}
} catch (GLib.Error e) {
printerr ("Oopsie: %s\n", e.message);
}
}
private void on_item_new (int interface_index, AvahiProtocol protocol, string name, string type, string domain, uint flags) {
char raw_interface_name[Linux.Network.INTERFACE_NAME_SIZE];
Linux.Network.if_indextoname (interface_index, (string) raw_interface_name);
unowned string interface_name = (string) raw_interface_name;
current_batch.add (new LinuxPairingServiceDetails (name, interface_index, interface_name, protocol, server));
public async void stop (Cancellable? cancellable) throws IOError {
if (timer != null) {
timer.destroy ();
timer = null;
}
io_cancellable.cancel ();
}
private void on_all_for_now () {
services_discovered (current_batch.to_array ());
current_batch.clear ();
private void schedule_next_poll () {
timer = new TimeoutSource.seconds (5);
timer.set_callback (() => {
timer = null;
handle_poll_timer_tick.begin ();
return Source.REMOVE;
});
timer.attach (MainContext.get_thread_default ());
}
private async void handle_poll_timer_tick () {
try {
yield resolve_services (MDNS_IPV6, io_cancellable);
schedule_next_poll ();
} catch (GLib.Error e) {
if (!(e is IOError.CANCELLED))
schedule_next_poll ();
}
}
private async void resolve_services (Resolved.Flags flags, Cancellable? cancellable) throws Error, IOError {
Resolved.RRItem[] items;
uint64 ptr_flags;
try {
yield resolved.resolve_record (Resolved.ANY_INTERFACE, PairingService.DNS_SD_NAME, DnsRecordClass.IN,
DnsRecordType.PTR, flags, cancellable, out items, out ptr_flags);
} catch (GLib.Error e) {
throw (Error) parse_error (e);
}
var promises = new Gee.ArrayQueue<Promise<PairingServiceDetails?>> ();
foreach (var item in items) {
var pr = new DnsPacketReader (new Bytes (item.data));
DnsPtrRecord ptr = pr.read_ptr ();
var promise = new Promise<PairingServiceDetails> ();
resolve_service.begin (ptr, item.ifindex, flags, cancellable, promise);
promises.offer (promise);
}
Promise<PairingServiceDetails?>? p;
while ((p = promises.poll ()) != null) {
try {
yield p.future.wait_async (cancellable);
} catch (Error e) {
}
}
}
private async void resolve_service (DnsPtrRecord ptr, int32 ifindex, Resolved.Flags flags, Cancellable? cancellable,
Promise<PairingServiceDetails?> promise) {
try {
PairingServiceDetails? service = yield do_resolve_service (ptr, ifindex, flags, cancellable);
promise.resolve (service);
} catch (GLib.Error e) {
promise.reject (e);
}
}
private async PairingServiceDetails? do_resolve_service (DnsPtrRecord ptr, int32 ifindex, Resolved.Flags flags,
Cancellable? cancellable) throws Error, IOError {
Resolved.SrvItem[] srv_items;
Variant txt_items;
string canonical_name;
string canonical_type;
string canonical_domain;
uint64 srv_flags;
try {
yield resolved.resolve_service (ifindex, "", "", ptr.name, Posix.AF_INET6, flags, cancellable,
out srv_items, out txt_items, out canonical_name, out canonical_type, out canonical_domain,
out srv_flags);
} catch (GLib.Error e) {
throw (Error) parse_error (e);
}
var txt_record = new Gee.ArrayList<string> ();
foreach (var raw_item in txt_items) {
string item = ((string *) raw_item.get_data ())->substring (0, (long) raw_item.get_size ());
if (!item.validate ())
throw new Error.PROTOCOL ("Invalid TXT item");
txt_record.add (item);
}
var meta = PairingServiceMetadata.from_txt_record (txt_record);
var ip = new InetAddress.from_bytes (srv_items[0].addresses[0].ip, IPV6);
var service = new PairingServiceDetails () {
identifier = meta.identifier,
auth_tag = meta.auth_tag,
endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
address: ip,
port: srv_items[0].port,
scope_id: ip.get_is_link_local () ? ifindex : 0
),
interface_address = resolve_interface_address (ifindex),
};
service_discovered (service);
return service;
}
private static InetSocketAddress resolve_interface_address (int32 ifindex) throws Error {
char ifname_buf[IF_NAMESIZE];
unowned string? ifname = Linux.Network.if_indextoname (ifindex, (string) ifname_buf);
if (ifname == null)
throw new Error.INVALID_ARGUMENT ("Unable to resolve interface name");
Linux.Network.IfAddrs ifaddrs;
Linux.Network.getifaddrs (out ifaddrs);
for (unowned Linux.Network.IfAddrs candidate = ifaddrs; candidate != null; candidate = candidate.ifa_next) {
unowned Posix.SockAddr? address = candidate.ifa_addr;
if (address == null || address.sa_family != Posix.AF_INET6)
continue;
if (candidate.ifa_name != ifname)
continue;
return (InetSocketAddress) SocketAddress.from_native ((void *) address, sizeof (Posix.SockAddrIn6));
}
throw new Error.NOT_SUPPORTED ("Unable to resolve interface address");
}
private static GLib.Error parse_error (GLib.Error e) {
if (e is Error || e is IOError.CANCELLED)
return e;
return new Error.TRANSPORT ("%s", e.message);
}
}
private class LinuxPairingServiceDetails : Object, PairingServiceDetails {
public string name {
get { return _name; }
}
public uint interface_index {
get { return _interface_index; }
}
public string interface_name {
get { return _interface_name; }
}
private string _name;
private uint _interface_index;
private string _interface_name;
private AvahiProtocol protocol;
private AvahiServer server;
internal LinuxPairingServiceDetails (string name, uint interface_index, string interface_name, AvahiProtocol protocol,
AvahiServer server) {
_name = name;
_interface_index = interface_index;
_interface_name = interface_name;
this.protocol = protocol;
this.server = server;
}
public async Gee.List<PairingServiceHost> resolve (Cancellable? cancellable) throws Error, IOError {
AvahiServiceResolver resolver;
namespace Network {
public async void wait_until_interfaces_ready (Gee.Collection<string> interface_names, Cancellable? cancellable)
throws Error, IOError {
try {
GLib.ObjectPath path = yield server.service_resolver_prepare ((int) interface_index, protocol, name,
PAIRING_REGTYPE, PAIRING_DOMAIN, INET6, 0, cancellable);
DBusConnection connection = ((DBusProxy) server).get_connection ();
resolver = yield connection.get_proxy (AVAHI_SERVICE_NAME, path, DO_NOT_LOAD_PROPERTIES, cancellable);
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("%s", e.message);
}
var connection = yield GLib.Bus.get (BusType.SYSTEM, cancellable);
var promise = new Promise<Gee.List<PairingServiceHost>> ();
var hosts = new Gee.ArrayList<PairingServiceHost> ();
resolver.found.connect ((interface_index, protocol, name, type, domain, host, address_protocol, address, port, txt,
flags) => {
var txt_record = new Gee.ArrayList<string> ();
var iter = new VariantIter (txt);
Variant? cur;
while ((cur = iter.next_value ()) != null) {
unowned string raw_val = (string) cur.get_data ();
string val = raw_val.make_valid ((ssize_t) cur.get_size ());
txt_record.add (val);
NetworkManager.Service? nm = null;
Networkd.Service? netd = null;
if (yield system_has_service (NetworkManager.SERVICE_NAME, connection, cancellable)) {
nm = yield connection.get_proxy (NetworkManager.SERVICE_NAME, NetworkManager.SERVICE_PATH,
DO_NOT_LOAD_PROPERTIES, cancellable);
} else if (yield system_has_service (Networkd.SERVICE_NAME, connection, cancellable)) {
netd = yield connection.get_proxy (Networkd.SERVICE_NAME, Networkd.SERVICE_PATH,
DO_NOT_LOAD_PROPERTIES, cancellable);
} else {
return;
}
hosts.add (new LinuxPairingServiceHost (
host,
new InetSocketAddress.from_string (address, port),
port,
txt_record));
var remaining = interface_names.size + 1;
if (!promise.future.ready)
promise.resolve (hosts);
});
resolver.failure.connect (error => {
if (!promise.future.ready)
promise.reject (new Error.NOT_SUPPORTED ("%s", error));
});
NotifyCompleteFunc on_complete = () => {
remaining--;
if (remaining == 0)
wait_until_interfaces_ready.callback ();
};
foreach (var name in interface_names) {
if (nm != null) {
NetworkManager.wait_until_interface_ready.begin (name, nm, connection, cancellable,
on_complete);
} else {
Networkd.wait_until_interface_ready.begin (name, netd, connection, cancellable,
on_complete);
}
}
var source = new IdleSource ();
source.set_callback (() => {
on_complete ();
return Source.REMOVE;
});
source.attach (MainContext.get_thread_default ());
yield;
} catch (GLib.Error e) {
}
}
private async bool system_has_service (string name, DBusConnection connection, Cancellable? cancellable) throws GLib.Error {
var v = yield connection.call (
"org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
"NameHasOwner",
new Variant.tuple ({ name }),
new VariantType.tuple ({ VariantType.BOOLEAN }),
DBusCallFlags.NONE, -1, cancellable);
bool has_owner;
v.get ("(b)", out has_owner);
return has_owner;
}
}
private delegate void NotifyCompleteFunc ();
namespace NetworkManager {
private async void wait_until_interface_ready (string name, Service service, DBusConnection connection,
Cancellable? cancellable, NotifyCompleteFunc on_complete) {
try {
yield resolver.start (cancellable);
string device_path = yield service.get_device_by_ip_iface (name);
Device device = yield connection.get_proxy (SERVICE_NAME, device_path, DBusProxyFlags.NONE, cancellable);
var device_proxy = (DBusProxy) device;
ulong handler = device_proxy.g_properties_changed.connect ((changed, invalidated) => {
if (changed.lookup_value ("StateReason", null) != null)
wait_until_interface_ready.callback ();
});
while (!cancellable.is_cancelled ()) {
uint32 state, reason;
device_proxy.get_cached_property ("StateReason").get ("(uu)", out state, out reason);
if (state == DEVICE_STATE_ACTIVATED)
break;
if (state == DEVICE_STATE_DISCONNECTED && reason != DEVICE_STATE_REASON_NONE)
break;
yield;
}
device_proxy.disconnect (handler);
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("%s", e.message);
}
return yield promise.future.wait_async (cancellable);
on_complete ();
}
private const string SERVICE_NAME = "org.freedesktop.NetworkManager";
private const string SERVICE_PATH = "/org/freedesktop/NetworkManager";
[DBus (name = "org.freedesktop.NetworkManager")]
private interface Service : Object {
public abstract async string get_device_by_ip_iface (string iface) throws GLib.Error;
}
[DBus (name = "org.freedesktop.NetworkManager.Device")]
private interface Device : Object {
}
private const uint32 DEVICE_STATE_DISCONNECTED = 30;
private const uint32 DEVICE_STATE_ACTIVATED = 100;
private const uint32 DEVICE_STATE_REASON_NONE = 0;
}
namespace Networkd {
private async void wait_until_interface_ready (string name, Service service, DBusConnection connection,
Cancellable? cancellable, NotifyCompleteFunc on_complete) {
try {
int32 ifindex;
string link_path;
yield service.get_link_by_name (name, out ifindex, out link_path);
Link link = yield connection.get_proxy (SERVICE_NAME, link_path, DBusProxyFlags.NONE, cancellable);
var link_proxy = (DBusProxy) link;
ulong handler = link_proxy.g_properties_changed.connect ((changed, invalidated) => {
wait_until_interface_ready.callback ();
});
while (!cancellable.is_cancelled ()) {
string operational_state;
link_proxy.get_cached_property ("OperationalState").get ("s", out operational_state);
if (operational_state != "carrier")
break;
yield;
}
link_proxy.disconnect (handler);
} catch (GLib.Error e) {
}
on_complete ();
}
private const string SERVICE_NAME = "org.freedesktop.network1";
private const string SERVICE_PATH = "/org/freedesktop/network1";
[DBus (name = "org.freedesktop.network1.Manager")]
private interface Service : Object {
public abstract async void get_link_by_name (string name, out int32 ifindex, out string path) throws GLib.Error;
}
[DBus (name = "org.freedesktop.network1.Link")]
private interface Link : Object {
}
}
public class LinuxPairingServiceHost : Object, PairingServiceHost {
public string name {
get { return _name; }
namespace Resolved {
public const string SERVICE_NAME = "org.freedesktop.resolve1";
public const string SERVICE_PATH = "/org/freedesktop/resolve1";
public const int32 ANY_INTERFACE = 0;
[DBus (name = "org.freedesktop.resolve1.Manager")]
public interface Manager : Object {
public abstract async void resolve_record (int32 ifindex, string name, uint16 klass, uint16 type, uint64 flags,
Cancellable? cancellable, out RRItem[] items, out uint64 result_flags) throws GLib.Error;
public abstract async void resolve_service (int32 ifindex, string name, string type, string domain, int32 family,
uint64 flags, Cancellable? cancellable, out SrvItem[] srv_items,
[DBus (signature = "aay")] out Variant txt_items, out string canonical_name, out string canonical_type,
out string canonical_domain, out uint64 result_flags) throws GLib.Error;
}
public InetSocketAddress address {
get { return _address; }
[Flags]
public enum Flags {
DNS = 1 << 0,
LLMNR_IPV4 = 1 << 1,
LLMNR_IPV6 = 1 << 2,
MDNS_IPV4 = 1 << 3,
MDNS_IPV6 = 1 << 4,
NO_CNAME = 1 << 5,
NO_TXT = 1 << 6,
NO_ADDRESS = 1 << 7,
NO_SEARCH = 1 << 8,
AUTHENTICATED = 1 << 9,
NO_VALIDATE = 1 << 10,
NO_SYNTHESIZE = 1 << 11,
NO_CACHE = 1 << 12,
NO_ZONE = 1 << 13,
NO_TRUST_ANCHOR = 1 << 14,
NO_NETWORK = 1 << 15,
REQUIRE_PRIMARY = 1 << 16,
CLAMP_TTL = 1 << 17,
CONFIDENTIAL = 1 << 18,
SYNTHETIC = 1 << 19,
FROM_CACHE = 1 << 20,
FROM_ZONE = 1 << 21,
FROM_TRUST_ANCHOR = 1 << 22,
FROM_NETWORK = 1 << 23,
NO_STALE = 1 << 24,
RELAX_SINGLE_LABEL = 1 << 25,
}
public uint16 port {
get { return _port; }
public struct RRItem {
public int32 ifindex;
public uint16 klass;
public uint16 type;
public uint8[] data;
}
public Gee.List<string> txt_record {
get { return _txt_record; }
public struct SrvItem {
public uint16 priority;
public uint16 weight;
public uint16 port;
public string name;
public SrvAddress[] addresses;
public string canonical_name;
}
private string _name;
private InetSocketAddress _address;
private uint16 _port;
private Gee.List<string> _txt_record;
internal LinuxPairingServiceHost (string name, InetSocketAddress address, uint16 port, Gee.List<string> txt_record) {
_name = name;
_address = address;
_port = port;
_txt_record = txt_record;
public struct SrvAddress {
public int32 ifindex;
public int32 family;
public uint8[] ip;
}
public async Gee.List<InetSocketAddress> resolve (Cancellable? cancellable) throws Error, IOError {
var result = new Gee.ArrayList<InetSocketAddress> ();
result.add (address);
return result;
}
}
private const string AVAHI_SERVICE_NAME = "org.freedesktop.Avahi";
[DBus (name = "org.freedesktop.Avahi.Server2")]
private interface AvahiServer : Object {
public abstract async GLib.ObjectPath service_browser_prepare (int interface_index, AvahiProtocol protocol, string type,
string domain, uint flags, Cancellable? cancellable) throws GLib.Error;
public abstract async GLib.ObjectPath service_resolver_prepare (int interface_index, AvahiProtocol protocol, string name,
string type, string domain, AvahiProtocol aprotocol, AvahiLookupFlags flags, Cancellable? cancellable)
throws GLib.Error;
}
[DBus (name = "org.freedesktop.Avahi.ServiceBrowser")]
private interface AvahiServiceBrowser : Object {
public signal void item_new (int interface_index, AvahiProtocol protocol, string name, string type, string domain,
uint flags);
public signal void item_remove (int interface_index, AvahiProtocol protocol, string name, string type, string domain,
uint flags);
public signal void failure (string error);
public signal void all_for_now ();
public signal void cache_exhausted ();
public abstract async void start (Cancellable? cancellable) throws GLib.Error;
public abstract async void free (Cancellable? cancellable) throws GLib.Error;
}
[DBus (name = "org.freedesktop.Avahi.ServiceResolver")]
private interface AvahiServiceResolver : Object {
public signal void found (int interface_index, AvahiProtocol protocol, string name, string type, string domain, string host,
AvahiProtocol address_protocol, string address, uint16 port, [DBus (signature = "aay")] Variant txt, uint flags);
public signal void failure (string error);
public abstract async void start (Cancellable? cancellable) throws GLib.Error;
public abstract async void free (Cancellable? cancellable) throws GLib.Error;
}
private enum AvahiProtocol {
INET,
INET6,
UNSPEC = -1,
}
[Flags]
private enum AvahiLookupFlags {
USE_WIDE_AREA = 1,
USE_MULTICAST = 2,
NO_TXT = 4,
NO_ADDRESS = 8,
}
}
+28 -3
View File
@@ -129,6 +129,7 @@ namespace Frida.Fruity {
private ConnectionType _connection_type;
private string _udid;
private string _name;
private Bytes? _remote_unlock_host_key;
private Promise<Tunnel>? tunnel_request;
@@ -153,6 +154,13 @@ namespace Frida.Fruity {
var reader = new XpcObjectReader (device_info);
_name = reader.read_member ("name").get_string_value ();
reader.end_member ();
if (reader.has_member ("remoteUnlockHostKey")) {
_remote_unlock_host_key = new Bytes (reader.read_member ("remoteUnlockHostKey").get_data_value ());
reader.end_member ();
} else {
_remote_unlock_host_key = null;
}
}
private void on_state_changed (Object obj, ParamSpec pspec) {
@@ -211,7 +219,7 @@ namespace Frida.Fruity {
}
}
public async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
public async Tunnel? find_tunnel (UsbmuxDevice? device, Cancellable? cancellable) throws Error, IOError {
while (tunnel_request != null) {
try {
return yield tunnel_request.future.wait_async (cancellable);
@@ -224,7 +232,7 @@ namespace Frida.Fruity {
tunnel_request = new Promise<Tunnel> ();
try {
var tunnel = new MacOSTunnel (pairing_device);
var tunnel = new MacOSTunnel (pairing_device, _remote_unlock_host_key);
yield tunnel.attach (cancellable);
tunnel_request.resolve (tunnel);
@@ -246,6 +254,18 @@ namespace Frida.Fruity {
}
}
public int64 opened_at {
get {
return _opened_at;
}
}
public Bytes? remote_unlock_host_key {
get {
return _remote_unlock_host_key;
}
}
public Darwin.Xpc.Uuid? assertion_identifier {
get {
return _assertion_identifier;
@@ -253,12 +273,15 @@ namespace Frida.Fruity {
}
private XpcClient pairing_device;
private Bytes? _remote_unlock_host_key;
private Darwin.Xpc.Uuid? _assertion_identifier;
private InetAddress? tunnel_device_address;
private DiscoveryService? _discovery;
private int64 _opened_at = -1;
public MacOSTunnel (XpcClient pairing_device) {
public MacOSTunnel (XpcClient pairing_device, Bytes? remote_unlock_host_key) {
this.pairing_device = pairing_device;
_remote_unlock_host_key = remote_unlock_host_key;
}
public async void close (Cancellable? cancellable) throws IOError {
@@ -275,6 +298,8 @@ namespace Frida.Fruity {
r.body.set_int64 ("flags", 0);
var response = yield pairing_device.request (r.message, cancellable);
_opened_at = get_monotonic_time ();
var reader = new XpcObjectReader (response);
reader.read_member ("response");
+150 -1
View File
@@ -2,8 +2,12 @@
#include "../windows/icon-helpers.h"
#include <setupapi.h>
#include <devguid.h>
#include <iphlpapi.h>
#include <setupapi.h>
#include <windns.h>
typedef struct _FridaPairingBrowserBackend FridaPairingBrowserBackend;
typedef struct _FridaMobileDeviceInfo FridaMobileDeviceInfo;
typedef struct _FridaImageDeviceInfo FridaImageDeviceInfo;
@@ -13,8 +17,18 @@ typedef struct _FridaFindImageDeviceContext FridaFindImageDeviceContext;
typedef struct _FridaDeviceInfo FridaDeviceInfo;
typedef struct _FridaDnsApi FridaDnsApi;
typedef gboolean (* FridaEnumerateDeviceFunc) (const FridaDeviceInfo * device_info, gpointer user_data);
struct _FridaPairingBrowserBackend
{
FridaFruityWindowsPairingBrowserResultCallback callback;
gpointer callback_target;
DNS_SERVICE_CANCEL browse_handle;
};
struct _FridaMobileDeviceInfo
{
WCHAR * location;
@@ -49,6 +63,14 @@ struct _FridaDeviceInfo
PSP_DEVINFO_DATA device_info_data;
};
struct _FridaDnsApi
{
DNS_STATUS (WINAPI * browse) (DNS_SERVICE_BROWSE_REQUEST * request, DNS_SERVICE_CANCEL * cancel);
DNS_STATUS (WINAPI * browse_cancel) (DNS_SERVICE_CANCEL * cancel_handle);
};
static void WINAPI frida_fruity_windows_pairing_browser_on_browse_results (void * query_context, DNS_QUERY_RESULT * query_results);
static FridaMobileDeviceInfo * find_mobile_device_by_udid (const WCHAR * udid);
static FridaImageDeviceInfo * find_image_device_by_location (const WCHAR * location);
@@ -68,8 +90,108 @@ static WCHAR * frida_read_registry_string (HKEY key, WCHAR * value_name);
static WCHAR * frida_read_registry_multi_string (HKEY key, WCHAR * value_name);
static gpointer frida_read_registry_value (HKEY key, WCHAR * value_name, DWORD expected_type);
static FridaDnsApi * frida_try_get_dns_api (void);
static GUID GUID_APPLE_USB = { 0xF0B32BE3, 0x6678, 0x4879, { 0x92, 0x30, 0x0E4, 0x38, 0x45, 0xD8, 0x05, 0xEE } };
void
_frida_fruity_windows_pairing_browser_enumerate_network_interfaces (FridaFruityWindowsPairingBrowserNetifFoundFunc func,
gpointer func_target)
{
IP_ADAPTER_ADDRESSES * adapters;
ULONG buffer_size, result;
IP_ADAPTER_ADDRESSES_LH * adapter;
if (frida_try_get_dns_api () == NULL)
return;
buffer_size = 32768;
adapters = g_malloc (buffer_size);
do
{
result = GetAdaptersAddresses (AF_INET6, 0, NULL, adapters, &buffer_size);
if (result != ERROR_BUFFER_OVERFLOW)
break;
adapters = g_realloc (adapters, buffer_size);
}
while (result == ERROR_BUFFER_OVERFLOW);
if (result != ERROR_SUCCESS)
goto beach;
for (adapter = adapters; adapter != NULL; adapter = adapter->Next)
{
SOCKET_ADDRESS * raw_addr;
GInetSocketAddress * addr;
if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
continue;
if (adapter->FirstUnicastAddress == NULL)
continue;
raw_addr = &adapter->FirstUnicastAddress->Address;
addr = G_INET_SOCKET_ADDRESS (g_socket_address_new_from_native (raw_addr->lpSockaddr, raw_addr->iSockaddrLength));
func (adapter->IfIndex, adapter->AdapterName, addr, func_target);
}
beach:
g_free (adapters);
}
void *
_frida_fruity_windows_pairing_browser_monitor_create_backend (gulong interface_index,
FridaFruityWindowsPairingBrowserResultCallback callback, gpointer callback_target)
{
FridaPairingBrowserBackend * backend;
FridaDnsApi * api;
DNS_SERVICE_BROWSE_REQUEST r = { 0, };
api = frida_try_get_dns_api ();
if (api == NULL)
return NULL;
backend = g_slice_new0 (FridaPairingBrowserBackend);
backend->callback = callback;
backend->callback_target = callback_target;
r.Version = DNS_QUERY_REQUEST_VERSION2;
r.InterfaceIndex = interface_index;
r.QueryName = G_PASTE (L, FRIDA_FRUITY_PAIRING_SERVICE_DNS_SD_NAME);
r.pBrowseCallbackV2 = frida_fruity_windows_pairing_browser_on_browse_results;
r.pQueryContext = backend;
if (api->browse (&r, &backend->browse_handle) != DNS_REQUEST_PENDING)
{
g_slice_free (FridaPairingBrowserBackend, backend);
return NULL;
}
return backend;
}
void
_frida_fruity_windows_pairing_browser_monitor_destroy_backend (void * opaque_backend)
{
FridaPairingBrowserBackend * backend = opaque_backend;
if (backend == NULL)
return;
frida_try_get_dns_api ()->browse_cancel (&backend->browse_handle);
g_slice_free (FridaPairingBrowserBackend, backend);
}
static void WINAPI
frida_fruity_windows_pairing_browser_on_browse_results (void * query_context, DNS_QUERY_RESULT * query_results)
{
FridaPairingBrowserBackend * backend = query_context;
backend->callback (query_results, backend->callback_target);
}
void
_frida_fruity_usbmux_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
GError ** error)
@@ -426,3 +548,30 @@ beach:
return buffer;
}
static FridaDnsApi *
frida_try_get_dns_api (void)
{
static gsize api_value = 0;
if (g_once_init_enter (&api_value))
{
HMODULE mod;
FARPROC browse;
FridaDnsApi * api = NULL;
mod = GetModuleHandleW (L"dnsapi.dll");
browse = GetProcAddress (mod, "DnsServiceBrowse");
if (browse != NULL)
{
api = g_slice_new (FridaDnsApi);
api->browse = (gpointer) browse;
api->browse_cancel = (gpointer) GetProcAddress (mod, "DnsServiceBrowseCancel");
}
g_once_init_leave (&api_value, GPOINTER_TO_SIZE (api) + 1);
}
return GSIZE_TO_POINTER (api_value - 1);
}
+102
View File
@@ -0,0 +1,102 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public class WindowsPairingBrowser : Object, PairingBrowser {
private Gee.Map<string, Monitor> monitors = new Gee.HashMap<string, Monitor> ();
private MainContext main_context = MainContext.ref_thread_default ();
public async void start (Cancellable? cancellable) throws IOError {
_enumerate_network_interfaces ((index, name, address) => {
var monitor = new Monitor (this, index, address);
monitors[name] = monitor;
});
}
public async void stop (Cancellable? cancellable) throws IOError {
monitors.clear ();
}
private void on_result (WinDns.QueryResult * result, InetSocketAddress interface_address) {
try {
PairingServiceDetails service = parse_result (result, interface_address);
var source = new IdleSource ();
source.set_callback (() => {
service_discovered (service);
return Source.REMOVE;
});
source.attach (main_context);
} catch (GLib.Error e) {
}
}
private static PairingServiceDetails parse_result (WinDns.QueryResult * res,
InetSocketAddress interface_address) throws GLib.Error {
PairingServiceMetadata? meta = null;
InetAddress? ip = null;
uint16 port = 0;
for (WinDns.Record * r = res->query_records; r != null; r = r->next) {
switch (r->type) {
case TEXT:
WinDns.TxtData * txt = &r->txt;
var txt_record = new Gee.ArrayList<string> ();
foreach (unowned string16 str in txt->strings)
txt_record.add (str.to_utf8 ());
meta = PairingServiceMetadata.from_txt_record (txt_record);
break;
case AAAA:
ip = new InetAddress.from_bytes (r->aaaa.ip.data, IPV6);
break;
case SRV:
port = r->srv.port;
break;
default:
break;
}
}
if (meta == null || ip == null || port == 0)
throw new Error.PROTOCOL ("Incomplete result");
return new PairingServiceDetails () {
identifier = meta.identifier,
auth_tag = meta.auth_tag,
endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
address: ip,
port: port,
scope_id: ip.get_is_link_local () ? interface_address.get_scope_id () : 0
),
interface_address = interface_address,
};
}
private class Monitor {
private weak WindowsPairingBrowser parent;
private InetSocketAddress interface_address;
private void * backend;
public Monitor (WindowsPairingBrowser parent, ulong interface_index, InetSocketAddress interface_address) {
this.parent = parent;
this.interface_address = interface_address;
backend = _create_backend (interface_index, on_result);
}
~Monitor () {
_destroy_backend (backend);
}
private void on_result (void * result) {
parent.on_result (result, interface_address);
}
public extern static void * _create_backend (ulong interface_index, ResultCallback callback);
public extern static void _destroy_backend (void * backend);
}
public extern static void _enumerate_network_interfaces (NetifFoundFunc func);
public delegate void NetifFoundFunc (ulong index, string identifier, owned InetSocketAddress address);
public delegate void ResultCallback (void * result);
}
}
File diff suppressed because it is too large. Load diff
+174
View File
@@ -0,0 +1,174 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public class DnsPacketReader {
private BufferReader reader;
public DnsPacketReader (Bytes packet) {
reader = new BufferReader (new Buffer (packet, BIG_ENDIAN));
}
public DnsPtrRecord read_ptr () throws Error {
var rr = read_record ();
if (rr.key.type != PTR)
throw new Error.PROTOCOL ("Expected a PTR record");
if (rr.key.klass != IN)
throw new Error.PROTOCOL ("Expected a PTR record of class IN");
var subreader = new DnsPacketReader (rr.data);
var name = subreader.read_name ();
return new DnsPtrRecord () {
key = rr.key,
ttl = rr.ttl,
data = rr.data,
name = name,
};
}
public DnsTxtRecord read_txt () throws Error {
var rr = read_record ();
if (rr.key.type != TXT)
throw new Error.PROTOCOL ("Expected a TXT record");
if (rr.key.klass != IN)
throw new Error.PROTOCOL ("Expected a TXT record of class IN");
var entries = new Gee.ArrayList<string> ();
var subreader = new DnsPacketReader (rr.data);
while (subreader.reader.available != 0) {
string text = subreader.read_string ();
entries.add (text);
}
return new DnsTxtRecord () {
key = rr.key,
ttl = rr.ttl,
data = rr.data,
entries = entries.to_array (),
};
}
public DnsAaaaRecord read_aaaa () throws Error {
var rr = read_record ();
if (rr.key.type != AAAA)
throw new Error.PROTOCOL ("Expected an AAAA record");
if (rr.key.klass != IN)
throw new Error.PROTOCOL ("Expected an AAAA record of class IN");
var subreader = new DnsPacketReader (rr.data);
var raw_address = subreader.reader.read_bytes (16);
var address = new InetAddress.from_bytes (raw_address.get_data (), IPV6);
return new DnsAaaaRecord () {
key = rr.key,
ttl = rr.ttl,
data = rr.data,
address = address,
};
}
public DnsSrvRecord read_srv () throws Error {
var rr = read_record ();
if (rr.key.type != SRV)
throw new Error.PROTOCOL ("Expected a SRV record");
if (rr.key.klass != IN)
throw new Error.PROTOCOL ("Expected a SRV record of class IN");
var subreader = new DnsPacketReader (rr.data);
var priority = subreader.reader.read_uint16 ();
var weight = subreader.reader.read_uint16 ();
var port = subreader.reader.read_uint16 ();
var name = subreader.read_name ();
return new DnsSrvRecord () {
key = rr.key,
ttl = rr.ttl,
data = rr.data,
priority = priority,
weight = weight,
port = port,
name = name,
};
}
public DnsResourceRecord read_record () throws Error {
var key = read_key ();
var ttl = reader.read_uint32 ();
var size = reader.read_uint16 ();
var data = reader.read_bytes (size);
return new DnsResourceRecord () {
key = key,
ttl = ttl,
data = data,
};
}
public DnsResourceKey read_key () throws Error {
var name = read_name ();
var type = reader.read_uint16 ();
var klass = reader.read_uint16 ();
return new DnsResourceKey () {
name = name,
type = type,
klass = klass,
};
}
public string read_name () throws Error {
var name = new StringBuilder.sized (256);
while (true) {
size_t size = reader.read_uint8 ();
if (size == 0)
break;
if (size > 63)
throw new Error.PROTOCOL ("Invalid DNS name length");
var label = reader.read_fixed_string (size);
if (name.len != 0)
name.append_c ('.');
name.append (label);
}
return name.str;
}
public string read_string () throws Error {
size_t size = reader.read_uint8 ();
return reader.read_fixed_string (size);
}
}
public class DnsPtrRecord : DnsResourceRecord {
public string name;
}
public class DnsTxtRecord : DnsResourceRecord {
public string[] entries;
}
public class DnsAaaaRecord : DnsResourceRecord {
public InetAddress address;
}
public class DnsSrvRecord : DnsResourceRecord {
public uint16 priority;
public uint16 weight;
public uint16 port;
public string name;
}
public class DnsResourceRecord {
public DnsResourceKey key;
public uint32 ttl;
public Bytes data;
}
public class DnsResourceKey {
public string name;
public DnsRecordType type;
public DnsRecordClass klass;
}
public enum DnsRecordType {
PTR = 12,
TXT = 16,
AAAA = 28,
SRV = 33,
}
public enum DnsRecordClass {
IN = 1,
}
}
+17 -4
View File
@@ -504,6 +504,14 @@ namespace Frida.Fruity {
process_incoming_fragments.begin ();
}
public override void dispose () {
foreach (var channel in channels.values)
channel.transport = null;
channels.clear ();
base.dispose ();
}
private async void close (Cancellable? cancellable) throws IOError {
io_cancellable.cancel ();
@@ -878,9 +886,9 @@ namespace Frida.Fruity {
construct;
}
public weak DTXTransport transport {
public weak DTXTransport? transport {
get;
construct;
set;
}
public State state {
@@ -903,7 +911,7 @@ namespace Frida.Fruity {
}
public override void dispose () {
transport.remove_channel (this);
close ();
base.dispose ();
}
@@ -915,6 +923,11 @@ namespace Frida.Fruity {
var error = new Error.TRANSPORT ("Channel closed");
foreach (var request in pending_responses.values.to_array ())
request.reject (error);
if (transport != null) {
transport.remove_channel (this);
transport = null;
}
}
public async NSObject? invoke (string method_name, DTXArgumentListBuilder? args, Cancellable? cancellable)
@@ -1103,7 +1116,7 @@ namespace Frida.Fruity {
NSObject? obj = NSKeyedArchive.decode (buf);
if (obj != null) {
var gval = Value (Type.from_instance (obj));
gval.set_boxed (obj);
gval.set_instance (obj);
elements += (owned) gval;
} else {
var gval = Value (typeof (NSObject));
+256 -89
View File
@@ -42,7 +42,7 @@ namespace Frida {
}
}
public class FruityHostSessionProvider : Object, HostSessionProvider, HostChannelProvider, HostServiceProvider, Pairable {
public class FruityHostSessionProvider : Object, HostSessionProvider, HostChannelProvider, Pairable {
public Fruity.Device device {
get;
construct;
@@ -108,6 +108,43 @@ namespace Frida {
return yield this.host_session.link_agent_session (id, sink, cancellable);
}
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
if (host_session != this.host_session)
return;
this.host_session.unlink_agent_session (id);
}
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
throws Error, IOError {
if (host_session != this.host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
return this.host_session.link_channel (id);
}
public void unlink_channel (HostSession host_session, ChannelId id) {
if (host_session != this.host_session)
return;
this.host_session.unlink_channel (id);
}
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id, Cancellable? cancellable)
throws Error, IOError {
if (host_session != this.host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
return this.host_session.link_service_session (id);
}
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
if (host_session != this.host_session)
return;
this.host_session.unlink_service_session (id);
}
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
agent_session_detached (id, reason, crash);
}
@@ -116,37 +153,6 @@ namespace Frida {
return yield device.open_channel (address, cancellable);
}
public async Service open_service (string address, Cancellable? cancellable) throws Error, IOError {
string[] tokens = address.split (":", 2);
unowned string protocol = tokens[0];
unowned string service_name = tokens[1];
if (protocol == "plist") {
var stream = yield device.open_lockdown_service (service_name, cancellable);
return new PlistService (stream);
}
if (protocol == "dtx") {
var connection = yield Fruity.DTXConnection.obtain (device, cancellable);
return new DTXService (service_name, connection);
}
if (protocol == "xpc") {
var tunnel = yield device.find_tunnel (cancellable);
if (tunnel == null)
throw new Error.NOT_SUPPORTED ("RemoteXPC not supported by device");
var service_info = tunnel.discovery.get_service (service_name);
var stream = yield tunnel.open_tcp_connection (service_info.port, cancellable);
return new XpcService (new Fruity.XpcConnection (stream));
}
throw new Error.NOT_SUPPORTED ("Unsupported service address");
}
private async void unpair (Cancellable? cancellable) throws Error, IOError {
try {
var client = yield device.get_lockdown_client (cancellable);
@@ -179,6 +185,10 @@ namespace Frida {
private Gee.HashMap<AgentSessionId?, AgentSessionEntry> agent_sessions =
new Gee.HashMap<AgentSessionId?, AgentSessionEntry> (AgentSessionId.hash, AgentSessionId.equal);
private ChannelRegistry channel_registry = new ChannelRegistry ();
private ServiceSessionRegistry service_session_registry = new ServiceSessionRegistry ();
private Cancellable io_cancellable = new Cancellable ();
private const double MIN_SERVER_CHECK_INTERVAL = 5.0;
@@ -196,14 +206,22 @@ namespace Frida {
Object (device: device);
}
construct {
channel_registry.channel_closed.connect (on_channel_closed);
service_session_registry.session_closed.connect (on_service_session_closed);
}
public async void close (Cancellable? cancellable) throws IOError {
if (remote_server_request != null) {
var server = yield try_get_remote_server (cancellable);
if (server != null) {
try {
yield server.connection.close (cancellable);
} catch (GLib.Error e) {
try {
var server = yield try_get_remote_server (cancellable);
if (server != null) {
try {
yield server.connection.close (cancellable);
} catch (GLib.Error e) {
}
}
} catch (Error e) {
}
}
@@ -968,7 +986,7 @@ namespace Frida {
var transport_broker = server.transport_broker;
if (transport_broker != null) {
try {
entry.connection = yield establish_direct_connection (transport_broker, remote_session_id, device,
entry.connection = yield establish_direct_connection (transport_broker, remote_session_id, server,
cancellable);
} catch (Error e) {
if (e is Error.NOT_SUPPORTED)
@@ -996,6 +1014,15 @@ namespace Frida {
return session;
}
public void unlink_agent_session (AgentSessionId id) {
AgentSessionEntry? entry = agent_sessions[id];
if (entry == null || entry.sink_registration_id == 0)
return;
entry.connection.unregister_object (entry.sink_registration_id);
entry.sink_registration_id = 0;
}
public async InjectorPayloadId inject_library_file (uint pid, string path, string entrypoint, string data,
Cancellable? cancellable) throws Error, IOError {
var server = yield get_remote_server (cancellable);
@@ -1016,6 +1043,79 @@ namespace Frida {
}
}
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
var stream = yield device.open_channel (address, cancellable);
var id = ChannelId.generate ();
channel_registry.register (id, stream);
return id;
}
public IOStream link_channel (ChannelId id) throws Error {
return channel_registry.link (id);
}
public void unlink_channel (ChannelId id) {
channel_registry.unlink (id);
}
private void on_channel_closed (ChannelId id) {
channel_closed (id);
}
private void on_service_session_closed (ServiceSessionId id) {
service_session_closed (id);
}
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
var session = yield do_open_service (address, cancellable);
var id = ServiceSessionId.generate ();
service_session_registry.register (id, session);
return id;
}
private async ServiceSession do_open_service (string address, Cancellable? cancellable) throws Error, IOError {
string[] tokens = address.split (":", 2);
unowned string protocol = tokens[0];
unowned string service_name = tokens[1];
if (protocol == "plist") {
var stream = yield device.open_lockdown_service (service_name, cancellable);
return new PlistServiceSession (stream);
}
if (protocol == "dtx") {
var connection = yield Fruity.DTXConnection.obtain (device, cancellable);
return new DTXServiceSession (service_name, connection);
}
if (protocol == "xpc") {
var tunnel = yield device.find_tunnel (cancellable);
if (tunnel == null)
throw new Error.NOT_SUPPORTED ("RemoteXPC not supported by device");
var service_info = tunnel.discovery.get_service (service_name);
var stream = yield tunnel.open_tcp_connection (service_info.port, cancellable);
return new XpcServiceSession (new Fruity.XpcConnection (stream));
}
throw new Error.NOT_SUPPORTED ("Unsupported service address");
}
public ServiceSession link_service_session (ServiceSessionId id) throws Error {
return service_session_registry.link (id);
}
public void unlink_service_session (ServiceSessionId id) {
service_session_registry.unlink (id);
}
private void add_lldb_session (LLDBSession session) {
lldb_sessions[session.process.pid] = session;
@@ -1052,11 +1152,13 @@ namespace Frida {
entry.close.begin (io_cancellable);
}
private async RemoteServer? try_get_remote_server (Cancellable? cancellable) throws IOError {
private async RemoteServer? try_get_remote_server (Cancellable? cancellable) throws Error, IOError {
try {
return yield get_remote_server (cancellable);
} catch (Error e) {
return null;
if (e is Error.SERVER_NOT_RUNNING)
return null;
throw e;
}
}
@@ -1082,10 +1184,9 @@ namespace Frida {
DBusConnection? connection = null;
try {
var stream = yield device.open_channel (
("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (DEFAULT_CONTROL_PORT),
cancellable);
var channel = yield connect_to_remote_server (cancellable);
IOStream stream = channel.stream;
WebServiceTransport transport = PLAIN;
string? origin = null;
@@ -1113,7 +1214,7 @@ namespace Frida {
if (connection.closed)
throw new Error.SERVER_NOT_RUNNING ("Unable to connect to remote frida-server");
var server = new RemoteServer (session, connection, flavor, transport_broker);
var server = new RemoteServer (flavor, session, connection, channel, device, transport_broker);
attach_remote_server (server);
current_remote_server = server;
last_server_check_timer = null;
@@ -1131,8 +1232,8 @@ namespace Frida {
last_server_check_timer = null;
last_server_check_error = null;
} else {
if (e is Error.SERVER_NOT_RUNNING) {
api_error = new Error.SERVER_NOT_RUNNING ("Unable to connect to remote frida-server");
if (e is Error) {
api_error = e;
} else if (connection != null) {
api_error = new Error.PROTOCOL ("Incompatible frida-server version");
} else {
@@ -1150,6 +1251,51 @@ namespace Frida {
}
}
private async Fruity.TcpChannel connect_to_remote_server (Cancellable? cancellable) throws Error, IOError {
var tunnel = yield device.find_tunnel (cancellable);
bool tunnel_recently_opened = tunnel != null && get_monotonic_time () - tunnel.opened_at < 1000000;
uint delays[] = { 0, 50, 250 };
uint max_attempts = tunnel_recently_opened ? delays.length : 1;
var main_context = MainContext.ref_thread_default ();
Error? pending_error = null;
for (uint attempts = 0; attempts != max_attempts; attempts++) {
uint delay = delays[attempts];
if (delay != 0) {
var timeout_source = new TimeoutSource (delay);
timeout_source.set_callback (connect_to_remote_server.callback);
timeout_source.attach (main_context);
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (connect_to_remote_server.callback);
cancel_source.attach (main_context);
yield;
cancel_source.destroy ();
timeout_source.destroy ();
if (cancellable.is_cancelled ())
break;
}
bool is_last_attempt = attempts == max_attempts - 1;
var open_flags = is_last_attempt
? Fruity.OpenTcpChannelFlags.ALLOW_ANY_TRANSPORT
: Fruity.OpenTcpChannelFlags.ALLOW_TUNNEL;
try {
return yield device.open_tcp_channel (DEFAULT_CONTROL_PORT.to_string (), open_flags, cancellable);
} catch (Error e) {
pending_error = e;
if (!(e is Error.SERVER_NOT_RUNNING))
break;
}
}
throw pending_error;
}
private void attach_remote_server (RemoteServer server) {
server.connection.on_closed.connect (on_remote_connection_closed);
@@ -1445,7 +1591,12 @@ namespace Frida {
}
}
private class RemoteServer : Object {
private class RemoteServer : Object, HostChannelProvider {
public Flavor flavor {
get;
construct;
}
public HostSession session {
get;
construct;
@@ -1456,7 +1607,12 @@ namespace Frida {
construct;
}
public Flavor flavor {
public Fruity.TcpChannel channel {
get;
construct;
}
public Fruity.Device device {
get;
construct;
}
@@ -1471,34 +1627,40 @@ namespace Frida {
set;
}
public RemoteServer (HostSession session, DBusConnection connection, Flavor flavor,
TransportBroker? transport_broker) {
public RemoteServer (Flavor flavor, HostSession session, DBusConnection connection, Fruity.TcpChannel channel,
Fruity.Device device, TransportBroker? transport_broker) {
Object (
flavor: flavor,
session: session,
connection: connection,
flavor: flavor,
channel: channel,
device: device,
transport_broker: transport_broker
);
}
public async IOStream open_channel (string address, Cancellable? cancellable) throws Error, IOError {
if (!address.has_prefix ("tcp:"))
throw new Error.NOT_SUPPORTED ("Unsupported channel address");
var flags = (channel.kind == TUNNEL)
? Fruity.OpenTcpChannelFlags.ALLOW_TUNNEL
: Fruity.OpenTcpChannelFlags.ALLOW_USBMUX;
var channel = yield device.open_tcp_channel (address[4:], flags, cancellable);
return channel.stream;
}
}
}
private sealed class PlistService : Object, Service {
private sealed class PlistServiceSession : Object, ServiceSession {
public IOStream stream {
get;
construct;
}
private State state = INACTIVE;
private Fruity.PlistServiceClient client;
private bool client_closed = false;
private enum State {
INACTIVE,
ACTIVE,
}
public PlistService (IOStream stream) {
public PlistServiceSession (IOStream stream) {
Object (stream: stream);
}
@@ -1507,8 +1669,8 @@ namespace Frida {
client.closed.connect (on_client_closed);
}
public bool is_closed () {
return client_closed;
~PlistServiceSession () {
client.closed.disconnect (on_client_closed);
}
public async void activate (Cancellable? cancellable) throws Error, IOError {
@@ -1516,14 +1678,8 @@ namespace Frida {
}
private void ensure_active () throws Error {
if (state == INACTIVE) {
state = ACTIVE;
if (client_closed) {
close ();
throw new Error.INVALID_OPERATION ("Service is closed");
}
}
if (client_closed)
throw new Error.INVALID_OPERATION ("Service is closed");
}
public async void cancel (Cancellable? cancellable) throws IOError {
@@ -1532,6 +1688,8 @@ namespace Frida {
client_closed = true;
yield client.close (cancellable);
close ();
}
public async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError {
@@ -1686,7 +1844,7 @@ namespace Frida {
}
}
private sealed class DTXService : Object, Service {
private sealed class DTXServiceSession : Object, ServiceSession {
public string identifier {
get;
construct;
@@ -1706,7 +1864,7 @@ namespace Frida {
ACTIVE,
}
public DTXService (string identifier, Fruity.DTXConnection connection) {
public DTXServiceSession (string identifier, Fruity.DTXConnection connection) {
Object (identifier: identifier, connection: connection);
}
@@ -1714,8 +1872,8 @@ namespace Frida {
connection.notify["state"].connect (on_connection_state_changed);
}
public bool is_closed () {
return connection_closed;
~DTXServiceSession () {
connection.notify["state"].disconnect (on_connection_state_changed);
}
public async void activate (Cancellable? cancellable) throws Error, IOError {
@@ -1723,14 +1881,12 @@ namespace Frida {
}
private void ensure_active () throws Error {
if (connection_closed)
throw new Error.INVALID_OPERATION ("Service is closed");
if (state == INACTIVE) {
state = ACTIVE;
if (connection_closed) {
close ();
throw new Error.INVALID_OPERATION ("Service is closed");
}
channel = connection.make_channel (identifier);
channel.invocation.connect (on_channel_invocation);
channel.notification.connect (on_channel_notification);
@@ -1741,7 +1897,13 @@ namespace Frida {
if (connection_closed)
return;
connection_closed = true;
channel = null;
if (channel != null) {
channel.invocation.disconnect (on_channel_invocation);
channel.notification.disconnect (on_channel_notification);
channel = null;
}
close ();
}
@@ -1955,7 +2117,7 @@ namespace Frida {
}
}
private sealed class XpcService : Object, Service {
private sealed class XpcServiceSession : Object, ServiceSession {
public Fruity.XpcConnection connection {
get;
construct;
@@ -1969,7 +2131,7 @@ namespace Frida {
ACTIVE,
}
public XpcService (Fruity.XpcConnection connection) {
public XpcServiceSession (Fruity.XpcConnection connection) {
Object (connection: connection);
}
@@ -1978,8 +2140,11 @@ namespace Frida {
connection.message.connect (on_message);
}
public bool is_closed () {
return connection_closed;
~XpcServiceSession () {
connection.close.disconnect (on_close);
connection.message.disconnect (on_message);
connection.cancel ();
}
public async void activate (Cancellable? cancellable) throws Error, IOError {
@@ -1987,20 +2152,21 @@ namespace Frida {
}
private void ensure_active () throws Error {
if (connection_closed)
throw new Error.INVALID_OPERATION ("Service is closed");
if (state == INACTIVE) {
state = ACTIVE;
if (connection_closed) {
close ();
throw new Error.INVALID_OPERATION ("Service is closed");
}
connection.activate ();
}
}
public async void cancel (Cancellable? cancellable) throws IOError {
connection.cancel ();
if (state == ACTIVE)
connection.cancel ();
else if (!connection_closed)
on_close (null);
}
public async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError {
@@ -2024,6 +2190,7 @@ namespace Frida {
private void on_close (Error? error) {
connection_closed = true;
close ();
}
+3
View File
@@ -265,6 +265,8 @@ namespace Frida.Fruity {
}
private static LockdownError error_from_service (PlistServiceError e) {
if (e is PlistServiceError.CONNECTION_CLOSED)
return new LockdownError.CONNECTION_CLOSED ("%s", e.message);
return new LockdownError.PROTOCOL ("%s", e.message);
}
@@ -274,6 +276,7 @@ namespace Frida.Fruity {
}
public errordomain LockdownError {
CONNECTION_CLOSED,
INVALID_SERVICE,
NOT_PAIRED,
UNSUPPORTED,
+380 -135
View File
@@ -6,6 +6,11 @@ namespace Frida.Fruity {
construct;
}
public UsbNcmConfig config {
get;
construct;
}
public VirtualNetworkStack netstack {
get {
return _netstack;
@@ -18,33 +23,32 @@ namespace Frida.Fruity {
}
}
private uint8 data_iface;
private int data_altsetting;
private uint8 rx_address;
private uint8 tx_address;
private uint32 ntb_in_max_size;
private uint32 ntb_out_max_size;
private uint16 ndp_out_divisor;
private uint16 ndp_out_payload_remainder;
private uint16 ndp_out_alignment;
private uint16 ntb_out_max_datagrams;
private size_t max_in_transfers;
private size_t max_out_transfers;
private VirtualNetworkStack? _netstack;
private Gee.Queue<Bytes> pending_output = new Gee.ArrayQueue<Bytes> ();
private bool writing = false;
private uint16 next_outgoing_sequence = 1;
private InetAddress? _remote_ipv6_address;
private Cancellable io_cancellable = new Cancellable ();
private enum UsbDescriptorType {
INTERFACE = 0x04,
private const uint16 TRANSFER_HEADER_SIZE = 4 + 2 + 2 + 2 + 2;
private const size_t MAX_TRANSFER_MEMORY = 60 * 1518;
private enum CdcRequest {
GET_NTB_PARAMETERS = 0x80,
SET_NTB_INPUT_SIZE = 0x86,
}
private enum UsbCommSubclass {
NCM = 0x0d,
}
private enum UsbDataSubclass {
UNDEFINED = 0x00,
}
private enum UsbCdcDescriptorSubtype {
ETHERNET = 0x0f,
}
private const uint16 NTB_PARAMETERS_MIN_SIZE = 28;
private enum EtherType {
IPV6 = 0x86dd,
@@ -54,8 +58,9 @@ namespace Frida.Fruity {
UDP = 0x11,
}
public static async UsbNcmDriver open (UsbDevice device, Cancellable? cancellable = null) throws Error, IOError {
var driver = new UsbNcmDriver (device);
public static async UsbNcmDriver open (UsbDevice device, UsbNcmConfig config, Cancellable? cancellable = null)
throws Error, IOError {
var driver = new UsbNcmDriver (device, config);
try {
yield driver.init_async (Priority.DEFAULT, cancellable);
@@ -66,53 +71,15 @@ namespace Frida.Fruity {
return driver;
}
private UsbNcmDriver (UsbDevice device) {
Object (device: device);
private UsbNcmDriver (UsbDevice device, UsbNcmConfig config) {
Object (device: device, config: config);
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
unowned LibUSB.DeviceHandle handle = device.handle;
LibUSB.ConfigDescriptor config;
Usb.check (device.raw_device.get_active_config_descriptor (out config),
"Failed to get active USB config descriptor");
bool found_cdc_header = false;
bool found_data_interface = false;
uint8 mac_address_index = 0;
foreach (var iface in config.@interface) {
foreach (var setting in iface.altsetting) {
if (setting.bInterfaceClass == LibUSB.ClassCode.COMM &&
setting.bInterfaceSubClass == UsbCommSubclass.NCM) {
try {
parse_cdc_header (setting.extra, out mac_address_index);
found_cdc_header = true;
} catch (Error e) {
break;
}
} else if (setting.bInterfaceClass == LibUSB.ClassCode.DATA &&
setting.bInterfaceSubClass == UsbDataSubclass.UNDEFINED &&
setting.endpoint.length == 2) {
found_data_interface = true;
data_iface = setting.bInterfaceNumber;
data_altsetting = setting.bAlternateSetting;
foreach (var ep in setting.endpoint) {
if ((ep.bEndpointAddress & LibUSB.EndpointDirection.MASK) == LibUSB.EndpointDirection.IN)
rx_address = ep.bEndpointAddress;
else
tx_address = ep.bEndpointAddress;
}
}
}
}
if (!found_cdc_header || !found_data_interface)
throw_user_error ("No USB CDC-NCM interface found");
var language_id = yield device.query_default_language_id (cancellable);
uint8 mac_address[6];
string mac_address_str = yield device.read_string_descriptor (mac_address_index, device.default_language_id,
cancellable);
string mac_address_str = yield device.read_string_descriptor (config.mac_address_index, language_id, cancellable);
if (mac_address_str.length != 12)
throw new Error.PROTOCOL ("Invalid MAC address");
for (uint i = 0; i != 6; i++) {
@@ -121,13 +88,62 @@ namespace Frida.Fruity {
mac_address[i] = (uint8) v;
}
handle.detach_kernel_driver (data_iface);
unowned LibUSB.DeviceHandle handle = device.handle;
try {
Usb.check (handle.claim_interface (data_iface), "Failed to claim USB interface");
Usb.check (handle.claim_interface (config.ctrl_iface), "Failed to claim control interface");
Usb.check (handle.claim_interface (config.data_iface), "Failed to claim data interface");
} catch (Error e) {
throw_user_error (@"Unable to claim USB CDC-NCM interface ($(e.message))");
throw new Error.PERMISSION_DENIED ("%s",
make_user_error_message (@"Unable to claim USB CDC-NCM interface ($(e.message))"));
}
Usb.check (handle.set_interface_alt_setting (data_iface, data_altsetting),
uint8 raw_ntb_params[NTB_PARAMETERS_MIN_SIZE];
var raw_ntb_params_size = yield device.control_transfer (
LibUSB.RequestRecipient.INTERFACE | LibUSB.RequestType.CLASS | LibUSB.EndpointDirection.IN,
CdcRequest.GET_NTB_PARAMETERS,
0,
config.ctrl_iface,
raw_ntb_params,
1000,
cancellable);
if (raw_ntb_params_size < NTB_PARAMETERS_MIN_SIZE)
throw new Error.PROTOCOL ("Truncated NTB parameters response");
var ntb_params = new Buffer (new Bytes (raw_ntb_params[:raw_ntb_params_size]), LITTLE_ENDIAN);
uint32 device_ntb_in_max_size = ntb_params.read_uint32 (4);
ntb_in_max_size = uint32.min (device_ntb_in_max_size, 16384);
ntb_out_max_size = uint32.min (ntb_params.read_uint32 (16), 16384);
ndp_out_divisor = ntb_params.read_uint16 (20);
ndp_out_payload_remainder = ntb_params.read_uint16 (22);
ndp_out_alignment = ntb_params.read_uint16 (24);
ntb_out_max_datagrams = uint16.min (ntb_params.read_uint16 (26), 16);
if (ntb_in_max_size != device_ntb_in_max_size) {
var ntb_size_buf = new BufferBuilder (LITTLE_ENDIAN)
.append_uint32 (ntb_in_max_size)
.build ();
yield device.control_transfer (
LibUSB.RequestRecipient.INTERFACE | LibUSB.RequestType.CLASS | LibUSB.EndpointDirection.OUT,
CdcRequest.SET_NTB_INPUT_SIZE,
0,
config.ctrl_iface,
ntb_size_buf.get_data (),
1000,
cancellable);
}
var speed = device.raw_device.get_device_speed ();
if (speed >= LibUSB.Speed.SUPER) {
max_in_transfers = (5 * MAX_TRANSFER_MEMORY) / ntb_in_max_size;
max_out_transfers = (5 * MAX_TRANSFER_MEMORY) / ntb_out_max_size;
} else if (speed == LibUSB.Speed.HIGH) {
max_in_transfers = MAX_TRANSFER_MEMORY / ntb_in_max_size;
max_out_transfers = MAX_TRANSFER_MEMORY / ntb_out_max_size;
} else {
max_in_transfers = 4;
max_out_transfers = 4;
}
Usb.check (handle.set_interface_alt_setting (config.data_iface, config.data_altsetting),
"Failed to set USB interface alt setting");
_netstack = new VirtualNetworkStack (new Bytes (mac_address), null, 1500);
@@ -138,36 +154,46 @@ namespace Frida.Fruity {
return true;
}
[NoReturn]
private void throw_user_error (string message) throws Error {
#if WINDOWS
throw new Error.NOT_SUPPORTED ("%s; use https://zadig.akeo.ie to switch from Apple's official driver onto " +
"Microsoft's WinUSB driver, so libusb can access it", message);
#else
throw new Error.NOT_SUPPORTED ("%s", message);
#endif
}
public void close () {
io_cancellable.cancel ();
_netstack.stop ();
}
private async void process_incoming_datagrams () {
var data = new uint8[64 * 1024];
var pending = new Gee.ArrayQueue<Promise<Bytes>> ();
while (true) {
for (uint i = pending.size; i != max_in_transfers; i++) {
var request = transfer_next_input_batch ();
pending.offer (request);
}
try {
size_t n = yield device.bulk_transfer (rx_address, data, uint.MAX, io_cancellable);
handle_ncm_frame (data[:n]);
var frame = yield pending.poll ().future.wait_async (io_cancellable);
handle_ncm_frame (frame);
} catch (GLib.Error e) {
return;
}
}
}
private void handle_ncm_frame (uint8[] data) throws GLib.Error {
var input = new DataInputStream (new MemoryInputStream.from_data (data));
private Promise<Bytes> transfer_next_input_batch () {
var request = new Promise<Bytes> ();
do_transfer_input_batch.begin (request);
return request;
}
private async void do_transfer_input_batch (Promise<Bytes> request) {
var data = new uint8[ntb_in_max_size];
try {
size_t n = yield device.bulk_transfer (config.rx_address, data, uint.MAX, io_cancellable);
request.resolve (new Bytes (data[:n]));
} catch (GLib.Error e) {
request.reject (e);
}
}
private void handle_ncm_frame (Bytes frame) throws GLib.Error {
var input = new DataInputStream (new MemoryInputStream.from_bytes (frame));
input.byte_order = LITTLE_ENDIAN;
uint8 raw_signature[4 + 1];
@@ -215,47 +241,274 @@ namespace Frida.Fruity {
} while (ndp_index != 0);
}
private async void on_netif_outgoing_datagram (Bytes datagram) {
uint16 transfer_header_length = 12;
uint16 ndp_header_length = 16;
uint16 alignment_padding_length = 2;
private void on_netif_outgoing_datagram (Bytes datagram) {
pending_output.offer (datagram);
uint16 datagram_start_index = transfer_header_length + ndp_header_length + alignment_padding_length;
uint16 datagram_length = (uint16) datagram.length;
uint16 sentinel_start_index = 0;
uint16 sentinel_size = 0;
uint16 sequence = next_outgoing_sequence++;
uint16 block_length = datagram_start_index + datagram_length;
uint16 ndp_index = transfer_header_length;
uint16 next_ndp_index = 0;
uint16 alignment_padding_value = 0;
var frame = new BufferBuilder (LITTLE_ENDIAN)
.append_string ("NCMH", StringTerminator.NONE)
.append_uint16 (transfer_header_length)
.append_uint16 (sequence)
.append_uint16 (block_length)
.append_uint16 (ndp_index)
.append_string ("NCM0", StringTerminator.NONE)
.append_uint16 (ndp_header_length)
.append_uint16 (next_ndp_index)
.append_uint16 (datagram_start_index)
.append_uint16 (datagram_length)
.append_uint16 (sentinel_start_index)
.append_uint16 (sentinel_size)
.append_uint16 (alignment_padding_value)
.append_bytes (datagram)
.build ();
try {
yield device.bulk_transfer (tx_address, frame.get_data (), uint.MAX, io_cancellable);
} catch (GLib.Error e) {
if (!writing) {
writing = true;
var source = new IdleSource ();
source.set_callback (() => {
process_pending_output.begin ();
return false;
});
source.attach (MainContext.get_thread_default ());
}
}
private async void process_pending_output () {
try {
while (!pending_output.is_empty) {
var pending = new Gee.ArrayList<Promise<uint>> ();
do {
var request = transfer_next_output_batch ();
pending.add (request);
} while (pending.size < max_out_transfers && !pending_output.is_empty);
foreach (var request in pending)
yield request.future.wait_async (io_cancellable);
}
} catch (GLib.Error e) {
} finally {
writing = false;
}
}
private Promise<uint> transfer_next_output_batch () {
size_t num_datagrams = ntb_out_max_datagrams;
TransferLayout layout;
while ((layout = TransferLayout.compute (pending_output, num_datagrams, ndp_out_alignment,
ndp_out_divisor, ndp_out_payload_remainder)).size > ntb_out_max_size) {
num_datagrams--;
}
var batch = new Gee.ArrayList<Bytes> ();
for (var i = 0; i != layout.offsets.size; i++)
batch.add (pending_output.poll ());
var transfer = build_output_transfer (batch, layout, next_outgoing_sequence++);
var request = new Promise<uint> ();
do_transfer_output_batch.begin (transfer, request);
return request;
}
private async void do_transfer_output_batch (Bytes transfer, Promise<uint> request) {
try {
var size = yield device.bulk_transfer (config.tx_address, transfer.get_data (), uint.MAX, io_cancellable);
request.resolve ((uint) size);
} catch (GLib.Error e) {
request.reject (e);
}
}
private class TransferLayout {
public uint16 size;
public uint16 ndp_header_offset;
public uint16 ndp_header_size;
public Gee.List<uint16> offsets;
public static TransferLayout compute (Gee.Collection<Bytes> datagrams, size_t max_datagrams, size_t ndp_alignment,
size_t datagram_modulus, size_t datagram_remainder) {
size_t ndp_header_base_size = 4 + 2 + 2;
size_t ndp_entry_size = 2 + 2;
size_t ethernet_header_size = 14;
size_t ndp_header_offset = align (TRANSFER_HEADER_SIZE, ndp_alignment, 0);
size_t num_datagram_slots = size_t.min (datagrams.size, max_datagrams);
size_t ndp_header_size = ndp_header_base_size + ((num_datagram_slots + 1) * ndp_entry_size);
size_t current_transfer_size = ndp_header_offset + ndp_header_size;
var offsets = new Gee.ArrayList<uint16> ();
uint i = 0;
foreach (var datagram in datagrams) {
var size = (uint16) datagram.get_size ();
size_t start_offset =
align (current_transfer_size + ethernet_header_size, datagram_modulus, datagram_remainder);
size_t end_offset = start_offset + size;
if (end_offset > uint16.MAX)
break;
current_transfer_size = end_offset;
offsets.add ((uint16) start_offset);
i++;
if (i == max_datagrams)
break;
}
return new TransferLayout () {
size = (uint16) current_transfer_size,
ndp_header_offset = (uint16) ndp_header_offset,
ndp_header_size = (uint16) ndp_header_size,
offsets = offsets,
};
}
private static size_t align (size_t val, size_t modulus, size_t remainder) {
var delta = val % modulus;
if (delta != remainder)
return val + modulus - delta + remainder;
return val;
}
}
private static Bytes build_output_transfer (Gee.List<Bytes> datagrams, TransferLayout layout, uint16 sequence_number) {
var builder = new BufferBuilder (LITTLE_ENDIAN)
.append_string ("NCMH", StringTerminator.NONE)
.append_uint16 (TRANSFER_HEADER_SIZE)
.append_uint16 (sequence_number)
.append_uint16 (layout.size)
.append_uint16 (layout.ndp_header_offset);
uint16 next_ndp_index = 0;
builder
.seek (layout.ndp_header_offset)
.append_string ("NCM0", StringTerminator.NONE)
.append_uint16 (layout.ndp_header_size)
.append_uint16 (next_ndp_index);
int i;
i = 0;
foreach (var datagram in datagrams) {
builder
.append_uint16 (layout.offsets[i])
.append_uint16 ((uint16) datagram.get_size ());
i++;
}
i = 0;
foreach (var datagram in datagrams) {
builder
.seek (layout.offsets[i])
.append_bytes (datagram);
i++;
}
builder.seek (layout.size);
return builder.build ();
}
private static InetAddress? try_infer_remote_address_from_datagram (Bytes datagram) {
if (datagram.get_size () < 0x3e)
return null;
var buf = new Buffer (datagram, BIG_ENDIAN);
var ethertype = (EtherType) buf.read_uint16 (12);
if (ethertype != IPV6)
return null;
var next_header = (IPV6NextHeader) buf.read_uint8 (20);
if (next_header != UDP)
return null;
return new InetAddress.from_bytes (datagram[22:22 + 16].get_data (), IPV6);
}
}
internal class UsbNcmConfig {
public uint8 ctrl_iface;
public uint8 data_iface;
public int data_altsetting;
public uint8 rx_address;
public uint8 tx_address;
public uint8 mac_address_index;
private enum UsbDescriptorType {
INTERFACE = 0x04,
}
private enum UsbCommSubclass {
NCM = 0x0d,
}
private enum UsbDataSubclass {
UNDEFINED = 0x00,
}
private enum UsbCdcDescriptorSubtype {
ETHERNET = 0x0f,
}
public static UsbNcmConfig prepare (UsbDevice device, out bool device_configuration_changed) throws Error {
unowned LibUSB.Device raw_device = device.raw_device;
var dev_desc = LibUSB.DeviceDescriptor (raw_device);
LibUSB.ConfigDescriptor current_config;
Usb.check (raw_device.get_active_config_descriptor (out current_config), "Failed to get active config descriptor");
var config = new UsbNcmConfig ();
int desired_config_value = -1;
bool found_cdc_header = false;
bool found_data_interface = false;
for (uint8 config_value = dev_desc.bNumConfigurations; config_value != 0; config_value--) {
LibUSB.ConfigDescriptor config_desc;
Usb.check (raw_device.get_config_descriptor_by_value (config_value, out config_desc),
"Failed to get config descriptor");
foreach (var iface in config_desc.@interface) {
foreach (var setting in iface.altsetting) {
if (setting.bInterfaceClass == LibUSB.ClassCode.COMM &&
setting.bInterfaceSubClass == UsbCommSubclass.NCM) {
config.ctrl_iface = setting.bInterfaceNumber;
try {
parse_cdc_header (setting.extra, out config.mac_address_index);
found_cdc_header = true;
} catch (Error e) {
break;
}
} else if (setting.bInterfaceClass == LibUSB.ClassCode.DATA &&
setting.bInterfaceSubClass == UsbDataSubclass.UNDEFINED &&
setting.endpoint.length == 2) {
found_data_interface = true;
config.data_iface = setting.bInterfaceNumber;
config.data_altsetting = setting.bAlternateSetting;
foreach (var ep in setting.endpoint) {
if ((ep.bEndpointAddress & LibUSB.EndpointDirection.MASK) ==
LibUSB.EndpointDirection.IN) {
config.rx_address = ep.bEndpointAddress;
} else {
config.tx_address = ep.bEndpointAddress;
}
}
}
}
}
if (found_cdc_header || found_data_interface) {
desired_config_value = config_value;
break;
}
}
if (!found_cdc_header || !found_data_interface)
throw new Error.NOT_SUPPORTED ("%s", make_user_error_message ("No USB CDC-NCM interface found"));
if (current_config.bConfigurationValue != desired_config_value) {
unowned LibUSB.DeviceHandle handle = device.handle;
foreach (var iface in current_config.@interface) {
unowned LibUSB.InterfaceDescriptor setting = iface.altsetting[0];
var res = handle.kernel_driver_active (setting.bInterfaceNumber);
if (res == 1)
handle.detach_kernel_driver (setting.bInterfaceNumber);
}
Usb.check (handle.set_configuration (desired_config_value), "Failed to set configuration");
device_configuration_changed = true;
} else {
device_configuration_changed = false;
}
return config;
}
private static void parse_cdc_header (uint8[] header, out uint8 mac_address_index) throws Error {
var input = new DataInputStream (new MemoryInputStream.from_data (header));
input.set_byte_order (LITTLE_ENDIAN);
@@ -285,22 +538,14 @@ namespace Frida.Fruity {
throw new Error.PROTOCOL ("CDC Ethernet descriptor not found");
}
}
private static InetAddress? try_infer_remote_address_from_datagram (Bytes datagram) {
if (datagram.get_size () < 0x3e)
return null;
var buf = new Buffer (datagram, BIG_ENDIAN);
var ethertype = (EtherType) buf.read_uint16 (12);
if (ethertype != IPV6)
return null;
var next_header = (IPV6NextHeader) buf.read_uint8 (20);
if (next_header != UDP)
return null;
return new InetAddress.from_bytes (datagram[22:22 + 16].get_data (), IPV6);
}
private string make_user_error_message (string message) {
#if WINDOWS
return message + "; use https://zadig.akeo.ie to switch from Apple's official driver onto Microsoft's WinUSB " +
"driver, so libusb can access it";
#else
return message;
#endif
}
}
+141 -387
View File
@@ -11,6 +11,42 @@ namespace Frida.Fruity {
public abstract async IOStream open_tcp_connection (InetSocketAddress address, Cancellable? cancellable)
throws Error, IOError;
public async IOStream open_tcp_connection_with_timeout (InetSocketAddress address, uint timeout, Cancellable? cancellable)
throws Error, IOError {
bool timed_out = false;
var open_cancellable = new Cancellable ();
var main_context = MainContext.get_thread_default ();
var timeout_source = new TimeoutSource (timeout);
timeout_source.set_callback (() => {
timed_out = true;
open_cancellable.cancel ();
return Source.REMOVE;
});
timeout_source.attach (main_context);
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (() => {
open_cancellable.cancel ();
return Source.REMOVE;
});
cancel_source.attach (main_context);
try {
return yield open_tcp_connection (address, open_cancellable);
} catch (IOError e) {
assert (e is IOError.CANCELLED);
if (timed_out)
throw new Error.TIMED_OUT ("Networked Apple device is not responding");
throw e;
} finally {
timeout_source.destroy ();
cancel_source.destroy ();
}
}
public abstract UdpSocket create_udp_socket () throws Error;
}
@@ -147,6 +183,7 @@ namespace Frida.Fruity {
private State state = STARTED;
private Gee.Queue<Request> requests = new Gee.ArrayQueue<Request> ();
private unowned Thread<bool>? lwip_thread;
private LWIP.NetworkInterface handle;
private LWIP.IP6Address raw_ipv6_address;
@@ -154,8 +191,6 @@ namespace Frida.Fruity {
private MainContext main_context;
private DataOutputStream? pcap;
private enum State {
STARTED,
STOPPED
@@ -249,13 +284,11 @@ namespace Frida.Fruity {
public void handle_incoming_datagram (Bytes datagram) throws Error {
check_started ();
log_datagram (datagram);
check (perform_on_lwip_thread (() => {
var pbuf = LWIP.PacketBuffer.alloc (RAW, (uint16) datagram.get_size (), POOL);
pbuf.take (datagram.get_data ());
var err = handle.input (pbuf, handle);
var err = handle.input (pbuf, ref handle);
if (err == OK)
*((void **) &pbuf) = null;
@@ -282,10 +315,8 @@ namespace Frida.Fruity {
var datagram = new Bytes (packet[:pbuf.tot_len]);
schedule_on_frida_thread (() => {
if (state == STARTED) {
log_datagram (datagram);
if (state == STARTED)
outgoing_datagram (datagram);
}
return Source.REMOVE;
});
}
@@ -293,18 +324,29 @@ namespace Frida.Fruity {
internal LWIP.ErrorCode perform_on_lwip_thread (owned WorkFunc work) {
var req = new Request ((owned) work);
lock (requests)
requests.offer (req);
LWIP.Runtime.schedule (perform_next_request);
if (Thread.self<bool> () == lwip_thread) {
perform_request (req);
} else {
lock (requests)
requests.offer (req);
LWIP.Runtime.schedule (perform_next_request);
}
return req.join ();
}
private void perform_next_request () {
if (lwip_thread == null)
lwip_thread = Thread.self ();
Request req;
lock (requests)
req = requests.poll ();
perform_request (req);
}
private static void perform_request (Request req) {
LWIP.ErrorCode err = req.work ();
req.complete (err);
}
@@ -352,46 +394,7 @@ namespace Frida.Fruity {
source.attach (main_context);
}
private void log_datagram (Bytes datagram) {
lock (pcap) {
try {
if (pcap == null) {
char buf[40];
unowned string addr = raw_ipv6_address.to_string (buf);
var f = File.new_build_filename (Environment.get_user_special_dir (DESKTOP),
"vns-%s.pcap".printf (addr.replace (":", "")));
try {
f.delete ();
} catch (GLib.Error e) {
}
pcap = new DataOutputStream (f.create (REPLACE_DESTINATION));
pcap.set_byte_order (HOST_ENDIAN);
pcap.put_uint32 (0xa1b2c3d4U);
pcap.put_uint16 (2);
pcap.put_uint16 (4);
pcap.put_uint32 (0);
pcap.put_uint32 (0);
pcap.put_uint32 (16384);
pcap.put_uint32 ((ethernet_address != null) ? 1 : 229); // Ethernet or IPv6
pcap.flush ();
}
int64 timestamp = get_real_time ();
pcap.put_uint32 ((uint32) (timestamp / 1000000));
pcap.put_uint32 ((uint32) (timestamp % 1000000));
pcap.put_uint32 (datagram.length);
pcap.put_uint32 (datagram.length);
size_t written;
pcap.write_all (datagram.get_data (), out written);
pcap.flush ();
} catch (GLib.Error e) {
printerr ("%s\n", e.message);
assert_not_reached ();
}
}
}
private class TcpConnection : IOStream, AsyncInitable {
private class TcpConnection : VirtualStream, AsyncInitable {
public VirtualNetworkStack netstack {
get;
construct;
@@ -402,53 +405,13 @@ namespace Frida.Fruity {
construct;
}
public State state {
get {
return _state;
}
}
public override InputStream input_stream {
get {
return _input_stream;
}
}
public override OutputStream output_stream {
get {
return _output_stream;
}
}
public IOCondition pending_io {
get {
lock (state)
return events;
}
}
private Promise<bool> established = new Promise<bool> ();
private State _state = OPENING;
private TcpInputStream _input_stream;
private TcpOutputStream _output_stream;
private unowned LWIP.TcpPcb? pcb;
private IOCondition events = 0;
private ByteArray rx_buf = new ByteArray.sized (64 * 1024);
private ByteArray tx_buf = new ByteArray.sized (64 * 1024);
private size_t tx_space_available = 0;
private Gee.Map<unowned Source, IOCondition> sources = new Gee.HashMap<unowned Source, IOCondition> ();
private MainContext main_context;
public enum State {
OPENING,
OPENED,
CLOSED
}
public static async TcpConnection open (VirtualNetworkStack netstack, InetSocketAddress address,
Cancellable? cancellable) throws Error, IOError {
var connection = new TcpConnection (netstack, address);
@@ -466,22 +429,28 @@ namespace Frida.Fruity {
Object (netstack: netstack, address: address);
}
construct {
_input_stream = new TcpInputStream (this);
_output_stream = new TcpOutputStream (this);
main_context = MainContext.ref_thread_default ();
}
public override void dispose () {
_output_stream.detach ();
_input_stream.detach ();
stop ();
base.dispose ();
}
protected override VirtualStream.State query_initial_state () {
return OPENING;
}
protected override IOCondition query_events () {
IOCondition new_events = 0;
if (rx_buf.len != 0 || state == CLOSED)
new_events |= IN;
if (tx_space_available != 0)
new_events |= OUT;
return new_events;
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
LWIP.Runtime.schedule (do_start);
@@ -515,8 +484,9 @@ namespace Frida.Fruity {
if (self != null)
self->on_error (err);
});
pcb.set_flags (TIMESTAMP | SACK);
pcb.nagle_disable ();
pcb.bind_netif (netstack.handle);
pcb.bind_netif (&netstack.handle);
var err = pcb.connect (ip6_address_from_inet_socket_address (address), address.get_port (), (user_data, pcb, err) => {
TcpConnection * self = user_data;
@@ -542,15 +512,24 @@ namespace Frida.Fruity {
pcb = null;
return OK;
});
state = CLOSED;
update_pending_io ();
}
private void detach_from_pcb () {
pcb.set_user_data (null);
pcb = null;
}
private void on_connect () {
lock (state)
with_state_lock (() => {
tx_space_available = pcb.query_available_send_buffer_space ();
update_events ();
});
update_pending_io ();
schedule_on_frida_thread (() => {
_state = OPENED;
state = OPEN;
if (!established.future.ready)
established.resolve (true);
@@ -561,10 +540,10 @@ namespace Frida.Fruity {
private void on_recv (owned LWIP.PacketBuffer? pbuf, LWIP.ErrorCode err) {
if (pbuf == null) {
pcb = null;
detach_from_pcb ();
schedule_on_frida_thread (() => {
_state = CLOSED;
update_events ();
state = CLOSED;
update_pending_io ();
return Source.REMOVE;
});
return;
@@ -572,42 +551,41 @@ namespace Frida.Fruity {
var buffer = new uint8[pbuf.tot_len];
unowned uint8[] chunk = pbuf.get_contiguous (buffer, pbuf.tot_len);
lock (state)
with_state_lock (() => {
rx_buf.append (chunk[:pbuf.tot_len]);
update_events ();
});
update_pending_io ();
}
private void on_sent (uint16 len) {
lock (state)
with_state_lock (() => {
tx_space_available = pcb.query_available_send_buffer_space () - tx_buf.len;
update_events ();
});
update_pending_io ();
}
private void on_error (LWIP.ErrorCode err) {
pcb = null;
bool pcb_already_freed = err == ABRT;
if (pcb_already_freed)
pcb = null;
else
detach_from_pcb ();
schedule_on_frida_thread (() => {
_state = CLOSED;
update_events ();
state = CLOSED;
update_pending_io ();
if (!established.future.ready) {
if (!established.future.ready)
established.reject (parse_error (err));
}
return Source.REMOVE;
});
}
public override bool close (GLib.Cancellable? cancellable) throws IOError {
protected override void handle_close () {
stop ();
return true;
}
public override async bool close_async (int io_priority, GLib.Cancellable? cancellable) throws IOError {
stop ();
return true;
}
public void shutdown_rx () throws IOError {
public override void shutdown_read () throws IOError {
check_io (netstack.perform_on_lwip_thread (() => {
if (pcb == null)
return OK;
@@ -615,7 +593,7 @@ namespace Frida.Fruity {
}));
}
public void shutdown_tx () throws IOError {
public override void shutdown_write () throws IOError {
check_io (netstack.perform_on_lwip_thread (() => {
if (pcb == null)
return OK;
@@ -623,20 +601,22 @@ namespace Frida.Fruity {
}));
}
public ssize_t recv (uint8[] buffer) throws IOError {
public override ssize_t read (uint8[] buffer) throws IOError {
ssize_t n = 0;
netstack.perform_on_lwip_thread (() => {
if (pcb == null)
return OK;
lock (state) {
with_state_lock (() => {
n = ssize_t.min (buffer.length, rx_buf.len);
if (n == 0)
return OK;
return;
Memory.copy (buffer, rx_buf.data, n);
rx_buf.remove_range (0, (uint) n);
}
});
if (n == 0)
return OK;
if (pcb == null)
return OK;
size_t remainder = n;
while (remainder != 0) {
@@ -649,41 +629,43 @@ namespace Frida.Fruity {
});
if (n == 0) {
if (_state == CLOSED)
if (state == CLOSED)
return 0;
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
}
update_events ();
update_pending_io ();
return n;
}
public ssize_t send (uint8[] buffer) throws IOError {
public override ssize_t write (uint8[] buffer) throws IOError {
ssize_t n = 0;
netstack.perform_on_lwip_thread (() => {
if (pcb == null)
return OK;
lock (state) {
with_state_lock (() => {
n = ssize_t.min (buffer.length, (ssize_t) tx_space_available);
if (n == 0)
return OK;
return;
tx_buf.append (buffer[:n]);
tx_space_available -= n;
}
});
if (n == 0)
return OK;
size_t available_space = pcb.query_available_send_buffer_space ();
uint8[]? data = null;
lock (state) {
with_state_lock (() => {
size_t num_bytes_to_write = size_t.min (tx_buf.len, available_space);
if (num_bytes_to_write != 0) {
data = tx_buf.data[:num_bytes_to_write];
tx_buf.remove_range (0, (uint) num_bytes_to_write);
}
}
});
if (data == null)
return OK;
@@ -691,8 +673,9 @@ namespace Frida.Fruity {
pcb.output ();
available_space = pcb.query_available_send_buffer_space ();
lock (state)
with_state_lock (() => {
tx_space_available = available_space - tx_buf.len;
});
return OK;
});
@@ -700,44 +683,11 @@ namespace Frida.Fruity {
if (n == 0)
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
update_events ();
update_pending_io ();
return n;
}
public void register_source (Source source, IOCondition condition) {
lock (state)
sources[source] = condition | IOCondition.ERR | IOCondition.HUP;
}
public void unregister_source (Source source) {
lock (state)
sources.unset (source);
}
private void update_events () {
lock (state) {
IOCondition new_events = 0;
if (rx_buf.len != 0 || _state == CLOSED)
new_events |= IN;
if (tx_space_available != 0)
new_events |= OUT;
events = new_events;
foreach (var entry in sources.entries) {
unowned Source source = entry.key;
IOCondition c = entry.value;
if ((new_events & c) != 0)
source.set_ready_time (0);
}
}
notify_property ("pending-io");
}
private void schedule_on_frida_thread (owned SourceFunc function) {
var source = new IdleSource ();
source.set_callback ((owned) function);
@@ -745,199 +695,6 @@ namespace Frida.Fruity {
}
}
private class TcpInputStream : InputStream, PollableInputStream {
private weak TcpConnection connection;
public TcpInputStream (TcpConnection connection) {
Object ();
this.connection = connection;
}
internal void detach () {
connection = null;
}
public override bool close (Cancellable? cancellable) throws IOError {
if (connection != null)
connection.shutdown_rx ();
return true;
}
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
return close (cancellable);
}
public override ssize_t read (uint8[] buffer, Cancellable? cancellable) throws IOError {
if (connection == null)
return 0;
if (!is_readable ()) {
bool done = false;
var mutex = Mutex ();
var cond = Cond ();
ulong io_handler = connection.notify["pending-io"].connect ((obj, pspec) => {
if (is_readable ()) {
mutex.lock ();
done = true;
cond.signal ();
mutex.unlock ();
}
});
ulong cancellation_handler = 0;
if (cancellable != null) {
cancellation_handler = cancellable.connect (() => {
mutex.lock ();
done = true;
cond.signal ();
mutex.unlock ();
});
}
mutex.lock ();
while (!done)
cond.wait (mutex);
mutex.unlock ();
if (cancellation_handler != 0)
cancellable.disconnect (cancellation_handler);
connection.disconnect (io_handler);
cancellable.set_error_if_cancelled ();
}
return connection.recv (buffer);
}
public bool can_poll () {
return true;
}
public bool is_readable () {
if (connection == null)
return true;
return (connection.pending_io & IOCondition.IN) != 0;
}
public PollableSource create_source (Cancellable? cancellable) {
return new PollableSource.full (this, new TcpIOSource (connection, IOCondition.IN), cancellable);
}
public ssize_t read_nonblocking_fn (uint8[] buffer) throws GLib.Error {
if (connection == null)
return 0;
return connection.recv (buffer);
}
}
private class TcpOutputStream : OutputStream, PollableOutputStream {
private weak TcpConnection? connection;
public TcpOutputStream (TcpConnection connection) {
Object ();
this.connection = connection;
}
internal void detach () {
connection = null;
}
public override bool close (Cancellable? cancellable) throws IOError {
if (connection != null)
connection.shutdown_tx ();
return true;
}
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
return close (cancellable);
}
public override bool flush (GLib.Cancellable? cancellable) throws GLib.Error {
return true;
}
public override async bool flush_async (int io_priority, GLib.Cancellable? cancellable) throws GLib.Error {
return true;
}
public override ssize_t write (uint8[] buffer, Cancellable? cancellable) throws IOError {
assert_not_reached ();
}
public bool can_poll () {
return true;
}
public bool is_writable () {
if (connection == null)
return false;
return (connection.pending_io & IOCondition.OUT) != 0;
}
public PollableSource create_source (Cancellable? cancellable) {
return new PollableSource.full (this, new TcpIOSource (connection, IOCondition.OUT), cancellable);
}
public ssize_t write_nonblocking_fn (uint8[]? buffer) throws GLib.Error {
if (connection == null)
throw new IOError.CLOSED ("Connection is closed");
return connection.send (buffer);
}
public PollableReturn writev_nonblocking_fn (OutputVector[] vectors, out size_t bytes_written) throws GLib.Error {
assert_not_reached ();
}
}
private class TcpIOSource : Source {
public TcpConnection connection;
public IOCondition condition;
public TcpIOSource (TcpConnection? connection, IOCondition condition) {
this.connection = connection;
this.condition = condition;
if (connection != null)
connection.register_source (this, condition);
}
~TcpIOSource () {
if (connection != null)
connection.unregister_source (this);
}
protected override bool prepare (out int timeout) {
timeout = -1;
return is_ready ();
}
protected override bool check () {
return is_ready ();
}
private bool is_ready () {
IOCondition pending_io = (connection != null) ? connection.pending_io : IOCondition.IN;
return (pending_io & condition) != 0;
}
protected override bool dispatch (SourceFunc? callback) {
set_ready_time (-1);
if (callback == null)
return Source.REMOVE;
return callback ();
}
protected static bool closure_callback (Closure closure) {
var return_value = Value (typeof (bool));
closure.invoke (ref return_value, {});
return return_value.get_boolean ();
}
}
private class Ipv6UdpSocket : Object, UdpSocket, DatagramBased {
public VirtualNetworkStack netstack {
get;
@@ -952,14 +709,13 @@ namespace Frida.Fruity {
public IOCondition pending_io {
get {
lock (events)
return events;
lock (_pending_io)
return _pending_io;
}
}
//private VirtualNetworkStack? _netstack;
private unowned LWIP.UdpPcb? pcb;
private IOCondition events = OUT;
private IOCondition _pending_io = OUT;
private Gee.Queue<Packet> rx_queue = new Gee.ArrayQueue<Packet> ();
private Gee.Map<unowned Source, IOCondition> sources = new Gee.HashMap<unowned Source, IOCondition> ();
@@ -972,7 +728,7 @@ namespace Frida.Fruity {
netstack.perform_on_lwip_thread (() => {
pcb = LWIP.UdpPcb.make (V6);
pcb.set_recv_callback (on_recv);
pcb.bind_netif (netstack.handle);
pcb.bind_netif (&netstack.handle);
return OK;
});
}
@@ -995,9 +751,9 @@ namespace Frida.Fruity {
var sender = ip6_address_to_inet_socket_address (addr, port);
var packet = new Packet (bytes, sender);
lock (events)
lock (pending_io)
rx_queue.offer (packet);
update_events ();
update_pending_io ();
}
public void bind (InetSocketAddress address) throws Error {
@@ -1031,14 +787,14 @@ namespace Frida.Fruity {
int received;
for (received = 0; received != messages.length; received++) {
Packet? packet;
lock (events)
lock (pending_io)
packet = rx_queue.poll ();
if (packet == null) {
if (received == 0)
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
break;
}
update_events ();
update_pending_io ();
if (messages[received].address != null)
*messages[received].address = packet.address.ref ();
@@ -1117,28 +873,26 @@ namespace Frida.Fruity {
}
public void register_source (Source source, IOCondition condition) {
lock (events)
lock (pending_io)
sources[source] = condition | IOCondition.ERR | IOCondition.HUP;
}
public void unregister_source (Source source) {
lock (events)
lock (pending_io)
sources.unset (source);
}
private void update_events () {
lock (events) {
IOCondition new_events = OUT;
private void update_pending_io () {
lock (pending_io) {
_pending_io = OUT;
if (!rx_queue.is_empty)
new_events |= IN;
events = new_events;
_pending_io |= IN;
foreach (var entry in sources.entries) {
unowned Source source = entry.key;
IOCondition c = entry.value;
if ((new_events & c) != 0)
if ((_pending_io & c) != 0)
source.set_ready_time (0);
}
}
+245
View File
@@ -0,0 +1,245 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public class OpackBuilder {
protected BufferBuilder builder = new BufferBuilder (LITTLE_ENDIAN);
private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();
public OpackBuilder () {
push_scope (new Scope (ROOT));
}
public unowned OpackBuilder begin_dictionary () {
begin_value ();
size_t type_offset = builder.offset;
builder.append_uint8 (0x00);
push_scope (new CollectionScope (type_offset));
return this;
}
public unowned OpackBuilder set_member_name (string name) {
return add_string_value (name);
}
public unowned OpackBuilder end_dictionary () {
CollectionScope scope = pop_scope ();
size_t n = scope.num_values / 2;
if (n < 0xf) {
builder.write_uint8 (scope.type_offset, 0xe0 | n);
} else {
builder
.write_uint8 (scope.type_offset, 0xef)
.append_uint8 (0x03);
}
return this;
}
public unowned OpackBuilder add_string_value (string val) {
begin_value ();
size_t len = val.length;
if (len > uint32.MAX) {
builder
.append_uint8 (0x6f)
.append_string (val, StringTerminator.NUL);
return this;
}
if (len <= 0x20)
builder.append_uint8 ((uint8) (0x40 + len));
else if (len <= uint8.MAX)
builder.append_uint8 (0x61).append_uint8 ((uint8) len);
else if (len <= uint16.MAX)
builder.append_uint8 (0x62).append_uint16 ((uint16) len);
else if (len <= 0xffffff)
builder.append_uint8 (0x63).append_uint8 ((uint8) (len & 0xff)).append_uint16 ((uint16) (len >> 8));
else
builder.append_uint8 (0x64).append_uint32 ((uint32) len);
builder.append_string (val, StringTerminator.NONE);
return this;
}
public unowned OpackBuilder add_data_value (Bytes val) {
begin_value ();
size_t size = val.get_size ();
if (size <= 0x20)
builder.append_uint8 ((uint8) (0x70 + size));
else if (size <= uint8.MAX)
builder.append_uint8 (0x91).append_uint8 ((uint8) size);
else if (size <= uint16.MAX)
builder.append_uint8 (0x92).append_uint16 ((uint16) size);
else if (size <= 0xffffff)
builder.append_uint8 (0x93).append_uint8 ((uint8) (size & 0xff)).append_uint16 ((uint16) (size >> 8));
else
builder.append_uint8 (0x94).append_uint32 ((uint32) size);
builder.append_bytes (val);
return this;
}
private unowned OpackBuilder begin_value () {
peek_scope ().num_values++;
return this;
}
public Bytes build () {
return builder.build ();
}
private void push_scope (Scope scope) {
scopes.offer_tail (scope);
}
private Scope peek_scope () {
return scopes.peek_tail ();
}
private T pop_scope<T> () {
return (T) scopes.poll_tail ();
}
private class Scope {
public Kind kind;
public size_t num_values = 0;
public enum Kind {
ROOT,
COLLECTION,
}
public Scope (Kind kind) {
this.kind = kind;
}
}
private class CollectionScope : Scope {
public size_t type_offset;
public CollectionScope (size_t type_offset) {
base (COLLECTION);
this.type_offset = type_offset;
}
}
}
public class OpackParser {
private BufferReader reader;
[Flags]
private enum ValueFlags {
ALLOW_TERMINATOR = 1 << 0,
}
public static Variant parse (Bytes opack) throws Error {
var parser = new OpackParser (opack);
return parser.read_value ();
}
private OpackParser (Bytes opack) {
reader = new BufferReader (new Buffer (opack, LITTLE_ENDIAN));
}
private Variant? read_value (ValueFlags flags = 0) throws Error {
uint8 v = reader.read_uint8 ();
uint8 top = v >> 4;
uint8 bottom = v & 0b1111;
switch (top) {
case 0:
switch (bottom) {
case 3:
if ((flags & ValueFlags.ALLOW_TERMINATOR) == 0)
throw new Error.PROTOCOL ("Unexpected OPACK terminator");
return null;
}
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
case 3:
switch (bottom) {
case 0: return (int64) reader.read_int8 ();
case 1: return (int64) reader.read_int16 ();
case 2: return (int64) reader.read_int32 ();
case 3: return (int64) reader.read_int64 ();
}
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
case 4:
case 5:
case 6:
return read_string (v - 0x40);
case 7:
case 8:
case 9:
return read_data (v - 0x70);
case 0xe:
return read_dictionary (bottom);
default:
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
}
}
private Variant read_string (size_t len) throws Error {
if (len == 0x2f)
return reader.read_string ();
len = read_variable_length (len);
return reader.read_fixed_string (len);
}
private Variant read_data (size_t len) throws Error {
len = read_variable_length (len);
var bytes = reader.read_bytes (len);
return Variant.new_from_data (new VariantType.array (VariantType.BYTE), bytes.get_data (), true, bytes);
}
private size_t read_variable_length (size_t len) throws Error {
if (len <= 0x20)
return len;
switch (len) {
case 0x21:
return reader.read_uint8 ();
case 0x22:
return reader.read_uint16 ();
case 0x23:
uint32 bottom = reader.read_uint8 ();
uint32 top = reader.read_uint16 ();
return top << 8 | bottom;
case 0x24:
return reader.read_uint32 ();
default:
throw new Error.NOT_SUPPORTED ("Unsupported OPACK length: 0x%zx", len);
}
}
private Variant read_dictionary (size_t n) throws Error {
var builder = new VariantBuilder (VariantType.VARDICT);
bool has_terminator = n == 0xf;
size_t i = 0;
while (true) {
if (!has_terminator && i == n)
break;
var key = read_value (has_terminator ? ValueFlags.ALLOW_TERMINATOR : 0);
if (key == null)
break;
if (!key.is_of_type (VariantType.STRING))
throw new Error.PROTOCOL ("Unsupported OPACK dictionary key type");
var val = read_value ();
builder.add_value (new Variant.dict_entry (key, new Variant.variant (val)));
i++;
}
return builder.end ();
}
}
}
+109 -66
View File
@@ -1,34 +1,32 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
internal sealed class UsbDevice : Object, AsyncInitable {
internal sealed class UsbDevice : Object {
public string udid {
get;
construct;
}
public LibUSB.Device? raw_device {
get {
return _raw_device;
}
}
public UsbDeviceBackend backend {
get;
construct;
}
public LibUSB.DeviceHandle? handle {
get {
return _handle;
}
}
public string udid {
get {
return _udid;
}
}
public uint16 default_language_id {
get {
return _default_language_id;
}
}
private LibUSB.Device? _raw_device;
private LibUSB.DeviceHandle? _handle;
private string _udid;
private uint16 _default_language_id;
private uint num_pending_operations;
private Promise<bool>? pending_operations_completed;
private enum AppleSpecificRequest {
GET_MODE = 0x45,
@@ -38,85 +36,95 @@ namespace Frida.Fruity {
private const string MODE_INITIAL_UNTETHERED = "3:3:3:0"; // => 5:3:3:0
private const string MODE_INITIAL_TETHERED = "4:4:3:4"; // => 5:4:3:4
public static async UsbDevice open (LibUSB.Device raw_device, Cancellable? cancellable = null) throws Error, IOError {
var device = new UsbDevice (raw_device);
public UsbDevice (LibUSB.Device raw_device, UsbDeviceBackend backend) throws Error {
char serial[LibUSB.DEVICE_STRING_BYTES_MAX + 1];
var res = raw_device.get_device_string (SERIAL_NUMBER, serial);
Usb.check (res, "Failed to get serial number");
serial[res] = '\0';
try {
yield device.init_async (Priority.DEFAULT, cancellable);
} catch (GLib.Error e) {
throw_api_error (e);
}
Object (
udid: udid_from_serial_number ((string) serial),
backend: backend
);
return device;
}
private UsbDevice (LibUSB.Device raw_device) {
Object ();
_raw_device = raw_device;
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
public void ensure_open (Cancellable? cancellable = null) throws Error {
if (_handle != null)
return;
Usb.check (_raw_device.open (out _handle), "Failed to open USB device");
Bytes language_ids_response = yield read_string_descriptor_bytes (0, 0, cancellable);
if (language_ids_response.get_size () < sizeof (uint16))
throw new Error.PROTOCOL ("Invalid language IDs response");
Buffer language_ids = new Buffer (language_ids_response, LITTLE_ENDIAN);
_default_language_id = language_ids.read_uint16 (0);
var dev_desc = LibUSB.DeviceDescriptor (_raw_device);
string serial_number = yield read_string_descriptor (dev_desc.iSerialNumber, _default_language_id, cancellable);
_udid = udid_from_serial_number (serial_number);
return true;
}
public void close () {
public async void close (Cancellable? cancellable) throws IOError {
if (num_pending_operations != 0) {
pending_operations_completed = new Promise<bool> ();
try {
yield pending_operations_completed.future.wait_async (cancellable);
} catch (Error e) {
assert_not_reached ();
}
pending_operations_completed = null;
}
_handle = null;
_raw_device = null;
}
public async bool maybe_modeswitch (Cancellable? cancellable) throws Error, IOError {
var response = yield control_transfer (
uint8 current_mode[4];
var n = yield control_transfer (
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
AppleSpecificRequest.GET_MODE,
0,
0,
4,
current_mode,
1000,
cancellable);
string mode = parse_mode (response);
string mode = parse_mode (current_mode[:n]);
bool is_initial_mode = mode == MODE_INITIAL_UNTETHERED || mode == MODE_INITIAL_TETHERED;
if (!is_initial_mode)
return false;
response = yield control_transfer (
uint8 set_mode_result[1];
var set_mode_result_size = yield control_transfer (
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
AppleSpecificRequest.SET_MODE,
0,
3,
1,
set_mode_result,
1000,
cancellable);
if (response.get_size () != 1 || response[0] != 0x00)
if (set_mode_result_size != 1 || set_mode_result[0] != 0x00)
return false;
return true;
}
private static string parse_mode (Bytes mode) throws Error {
if (mode.get_size () != 4)
throw new Error.PROTOCOL ("Invalid mode response");
unowned uint8[] m = mode.get_data ();
return "%u:%u:%u:%u".printf (m[0], m[1], m[2], m[3]);
private static string parse_mode (uint8[] mode) throws Error {
var result = new StringBuilder.sized (7);
foreach (uint8 byte in mode) {
if (result.len != 0)
result.append_c (':');
result.append_printf ("%u", byte);
}
return result.str;
}
private static string udid_from_serial_number (string serial) {
public static string udid_from_serial_number (string serial) {
if (serial.length == 24)
return serial.substring (0, 8) + "-" + serial.substring (8);
return serial[:8] + "-" + serial[8:];
return serial;
}
public async uint16 query_default_language_id (Cancellable? cancellable) throws Error, IOError {
Bytes language_ids_response = yield read_string_descriptor_bytes (0, 0, cancellable);
if (language_ids_response.get_size () < sizeof (uint16))
throw new Error.PROTOCOL ("Invalid language IDs response");
Buffer language_ids = new Buffer (language_ids_response, LITTLE_ENDIAN);
return language_ids.read_uint16 (0);
}
public async string read_string_descriptor (uint8 index, uint16 language_id, Cancellable? cancellable)
throws Error, IOError {
var response = yield read_string_descriptor_bytes (index, language_id, cancellable);
@@ -141,16 +149,17 @@ namespace Frida.Fruity {
public async Bytes read_string_descriptor_bytes (uint8 index, uint16 language_id, Cancellable? cancellable)
throws Error, IOError {
var response = yield control_transfer (
uint8 response[1024];
var response_size = yield control_transfer (
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.STANDARD | LibUSB.EndpointDirection.IN,
LibUSB.StandardRequest.GET_DESCRIPTOR,
(LibUSB.DescriptorType.STRING << 8) | index,
language_id,
1024,
response,
1000,
cancellable);
try {
var input = new DataInputStream (new MemoryInputStream.from_bytes (response));
var input = new DataInputStream (new MemoryInputStream.from_data (response[:response_size]));
input.byte_order = LITTLE_ENDIAN;
uint8 length = input.read_byte ();
@@ -161,25 +170,29 @@ namespace Frida.Fruity {
if (type != LibUSB.DescriptorType.STRING)
throw new Error.PROTOCOL ("Invalid string descriptor type");
size_t remainder = response.get_size () - 2;
size_t remainder = response_size - 2;
length -= 2;
if (length > remainder)
throw new Error.PROTOCOL ("Invalid string descriptor length");
return response[2:2 + length];
return new Bytes (response[2:2 + length]);
} catch (GLib.Error e) {
throw new Error.PROTOCOL ("%s", e.message);
}
}
public async Bytes control_transfer (uint8 request_type, uint8 request, uint16 val, uint16 index, uint16 length,
public async size_t control_transfer (uint8 request_type, uint8 request, uint16 val, uint16 index, uint8[] buffer,
uint timeout, Cancellable? cancellable) throws Error, IOError {
var transfer = new LibUSB.Transfer ();
var op = backend.allocate_usb_operation ();
unowned LibUSB.Transfer transfer = op.transfer;
var ready_closure = new TransferReadyClosure (control_transfer.callback);
var buffer = new uint8[sizeof (LibUSB.ControlSetup) + length];
LibUSB.Transfer.fill_control_setup (buffer, request_type, request, val, index, length);
transfer.fill_control_transfer (_handle, buffer, on_transfer_ready, ready_closure, timeout);
size_t control_setup_size = 8;
var transfer_buffer = new uint8[control_setup_size + buffer.length];
LibUSB.Transfer.fill_control_setup (transfer_buffer, request_type, request, val, index, (uint16) buffer.length);
if ((request_type & LibUSB.EndpointDirection.IN) == 0)
Memory.copy ((uint8 *) transfer_buffer + control_setup_size, buffer, buffer.length);
transfer.fill_control_transfer (_handle, transfer_buffer, on_transfer_ready, ready_closure, timeout);
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (() => {
@@ -190,19 +203,27 @@ namespace Frida.Fruity {
try {
Usb.check (transfer.submit (), "Failed to submit control transfer");
on_operation_started ();
yield;
on_operation_ended ();
} finally {
cancel_source.destroy ();
}
Usb.check_transfer (transfer.status, "Control transfer failed");
return new Bytes (((uint8[]) transfer.control_get_data ())[:transfer.actual_length]);
var n = transfer.actual_length;
if ((request_type & LibUSB.EndpointDirection.IN) != 0)
Memory.copy (buffer, transfer.control_get_data (), n);
return n;
}
public async size_t bulk_transfer (uint8 endpoint, uint8[] buffer, uint timeout, Cancellable? cancellable)
throws Error, IOError {
var transfer = new LibUSB.Transfer ();
var op = backend.allocate_usb_operation ();
unowned LibUSB.Transfer transfer = op.transfer;
var ready_closure = new TransferReadyClosure (bulk_transfer.callback);
transfer.fill_bulk_transfer (_handle, endpoint, buffer, on_transfer_ready, ready_closure, timeout);
@@ -216,7 +237,9 @@ namespace Frida.Fruity {
try {
Usb.check (transfer.submit (), "Failed to submit bulk transfer");
on_operation_started ();
yield;
on_operation_ended ();
} finally {
cancel_source.destroy ();
}
@@ -226,6 +249,16 @@ namespace Frida.Fruity {
return transfer.actual_length;
}
private void on_operation_started () {
num_pending_operations++;
}
private void on_operation_ended () {
num_pending_operations--;
if (num_pending_operations == 0 && pending_operations_completed != null)
pending_operations_completed.resolve (true);
}
private static void on_transfer_ready (LibUSB.Transfer transfer) {
TransferReadyClosure * closure = transfer.user_data;
closure->schedule ();
@@ -252,6 +285,16 @@ namespace Frida.Fruity {
}
}
internal interface UsbDeviceBackend : Object {
public abstract UsbOperation allocate_usb_operation () throws Error;
}
internal interface UsbOperation : Object {
public abstract LibUSB.Transfer transfer {
get;
}
}
namespace Usb {
internal static void check (LibUSB.Error error, string prefix) throws Error {
if (error >= LibUSB.Error.SUCCESS)
+979 -381
View File
File diff suppressed because it is too large. Load diff
+170 -24
View File
@@ -9,26 +9,17 @@ namespace Frida {
public HostSessionService.with_default_backends () {
add_local_backends ();
#if !IOS && !ANDROID && !TVOS
#if HAVE_FRUITY_BACKEND
add_backend (new FruityHostSessionBackend ());
#endif
#if HAVE_DROIDY_BACKEND
add_backend (new DroidyHostSessionBackend ());
#endif
#endif
#if HAVE_SOCKET_BACKEND
add_backend (new SocketHostSessionBackend ());
#endif
#if HAVE_BAREBONE_BACKEND
add_backend (new BareboneHostSessionBackend ());
#endif
add_nonlocal_backends ();
}
public HostSessionService.with_local_backend_only () {
add_local_backends ();
}
public HostSessionService.with_nonlocal_backends_only () {
add_nonlocal_backends ();
}
public HostSessionService.with_socket_backend_only () {
#if HAVE_SOCKET_BACKEND
add_backend (new SocketHostSessionBackend ());
@@ -55,6 +46,23 @@ namespace Frida {
#endif
}
private void add_nonlocal_backends () {
#if !IOS && !ANDROID && !TVOS
#if HAVE_FRUITY_BACKEND
add_backend (new FruityHostSessionBackend ());
#endif
#if HAVE_DROIDY_BACKEND
add_backend (new DroidyHostSessionBackend ());
#endif
#endif
#if HAVE_SOCKET_BACKEND
add_backend (new SocketHostSessionBackend ());
#endif
#if HAVE_BAREBONE_BACKEND
add_backend (new BareboneHostSessionBackend ());
#endif
}
public async void start (Cancellable? cancellable = null) throws IOError {
var remaining = backends.size + 1;
@@ -160,7 +168,16 @@ namespace Frida {
public abstract async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
Cancellable? cancellable = null) throws Error, IOError;
public abstract void unlink_agent_session (HostSession host_session, AgentSessionId id);
public signal void agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash);
public abstract async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable = null)
throws Error, IOError;
public abstract void unlink_channel (HostSession host_session, ChannelId id);
public abstract async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id,
Cancellable? cancellable = null) throws Error, IOError;
public abstract void unlink_service_session (HostSession host_session, ServiceSessionId id);
}
public enum HostSessionProviderKind {
@@ -181,10 +198,6 @@ namespace Frida {
public abstract async IOStream open_channel (string address, Cancellable? cancellable = null) throws Error, IOError;
}
public interface HostServiceProvider : Object {
public abstract async Service open_service (string address, Cancellable? cancellable = null) throws Error, IOError;
}
public interface Pairable : Object {
public abstract async void unpair (Cancellable? cancellable = null) throws Error, IOError;
}
@@ -197,7 +210,129 @@ namespace Frida {
public abstract async void stop (Cancellable? cancellable = null) throws IOError;
}
public abstract class BaseDBusHostSession : Object, HostSession, AgentController {
public abstract class LocalHostSessionBackend : Object, HostSessionBackend {
private LocalHostSessionProvider local_provider;
public async void start (Cancellable? cancellable) throws IOError {
assert (local_provider == null);
local_provider = make_provider ();
provider_available (local_provider);
}
public async void stop (Cancellable? cancellable) throws IOError {
assert (local_provider != null);
provider_unavailable (local_provider);
yield local_provider.close (cancellable);
local_provider = null;
}
protected abstract LocalHostSessionProvider make_provider ();
}
public abstract class LocalHostSessionProvider : Object, HostSessionProvider {
public string id {
get { return "local"; }
}
public string name {
get { return "Local System"; }
}
public Variant? icon {
get { return _icon; }
}
private Variant? _icon;
public HostSessionProviderKind kind {
get { return HostSessionProviderKind.LOCAL; }
}
private LocalHostSession host_session;
construct {
_icon = load_icon ();
}
protected abstract LocalHostSession make_host_session (HostSessionOptions? options) throws Error;
protected void take_host_session (LocalHostSession session) {
host_session = session;
host_session.agent_session_detached.connect (on_agent_session_detached);
}
protected virtual Variant? load_icon () {
return null;
}
public async void close (Cancellable? cancellable) throws IOError {
if (host_session == null)
return;
host_session.agent_session_detached.disconnect (on_agent_session_detached);
yield host_session.close (cancellable);
host_session = null;
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
take_host_session (make_host_session (options));
return host_session;
}
public async void destroy (HostSession session, Cancellable? cancellable) throws Error, IOError {
if (session != host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
host_session.agent_session_detached.disconnect (on_agent_session_detached);
yield host_session.close (cancellable);
host_session = null;
}
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
Cancellable? cancellable) throws Error, IOError {
if (host_session != this.host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
return yield this.host_session.link_agent_session (id, sink, cancellable);
}
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
if (host_session != this.host_session)
return;
this.host_session.unlink_agent_session (id);
}
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
}
public void unlink_channel (HostSession host_session, ChannelId id) {
}
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id, Cancellable? cancellable)
throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
}
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
}
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
agent_session_detached (id, reason, crash);
}
}
public abstract class LocalHostSession : Object, HostSession, AgentController {
private Gee.HashMap<uint, Cancellable> pending_establish_ops = new Gee.HashMap<uint, Cancellable> ();
private Gee.HashMap<uint, Future<AgentEntry>> agent_entries = new Gee.HashMap<uint, Future<AgentEntry>> ();
@@ -762,6 +897,14 @@ namespace Frida {
return InjectorPayloadId (raw_id);
}
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
}
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
}
#if !WINDOWS
public async HostChildId prepare_to_fork (uint parent_pid, Cancellable? cancellable, out uint parent_injectee_id,
out uint child_injectee_id, out GLib.Socket child_socket) throws Error, IOError {
@@ -1280,7 +1423,7 @@ namespace Frida {
public abstract class InternalAgent : Object, AgentMessageSink, RpcPeer {
public signal void unloaded ();
public weak BaseDBusHostSession host_session {
public weak LocalHostSession host_session {
get;
construct;
}
@@ -1335,10 +1478,11 @@ namespace Frida {
protected virtual void on_event (string type, Json.Array event) {
}
protected async Json.Node call (string method, Json.Node[] args, Cancellable? cancellable) throws Error, IOError {
protected async Json.Node call (string method, Json.Node[] args, Bytes? data, Cancellable? cancellable)
throws Error, IOError {
yield ensure_loaded (cancellable);
return yield rpc_client.call (method, args, cancellable);
return yield rpc_client.call (method, args, data, cancellable);
}
protected async void post (Json.Node message, Cancellable? cancellable) throws Error, IOError {
@@ -1508,9 +1652,11 @@ namespace Frida {
printerr ("%s\n", json);
}
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
var has_data = data != null;
var data_param = has_data ? data.get_data () : new uint8[0];
try {
yield session.post_messages ({ AgentMessage (SCRIPT, script, json, false, {}) }, 0, cancellable);
yield session.post_messages ({ AgentMessage (SCRIPT, script, json, has_data, data_param) }, 0, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
+130 -31
View File
@@ -844,22 +844,22 @@ namespace Frida {
private const uint64 SOCK_CLOEXEC = 0x80000;
private class InjectSession : SeizeSession {
private static ProcMapsEntry local_libc;
private static ProcMapsSoEntry local_libc;
private static uint64 mmap_offset;
private static uint64 munmap_offset;
private static string fallback_ld;
private static string fallback_libc;
private static ProcMapsEntry? local_android_ld;
private static ProcMapsSoEntry? local_android_ld;
static construct {
string libc_name = Gum.Process.query_libc_name ();
var libc = Gum.Process.get_libc_module ();
uint local_pid = Posix.getpid ();
local_libc = ProcMapsEntry.find_by_path (local_pid, libc_name);
local_libc = ProcMapsSoEntry.find_by_path (local_pid, libc.path);
assert (local_libc != null);
mmap_offset = (uint64) (uintptr) Gum.Module.find_export_by_name (libc_name, "mmap") - local_libc.base_address;
munmap_offset = (uint64) (uintptr) Gum.Module.find_export_by_name (libc_name, "munmap") - local_libc.base_address;
mmap_offset = (uint64) (uintptr) libc.find_export_by_name ("mmap") - local_libc.base_address;
munmap_offset = (uint64) (uintptr) libc.find_export_by_name ("munmap") - local_libc.base_address;
try {
var program = new Gum.ElfModule.from_file ("/proc/self/exe");
@@ -877,7 +877,7 @@ namespace Frida {
}
#if ANDROID
local_android_ld = ProcMapsEntry.find_by_path (local_pid, fallback_ld);
local_android_ld = ProcMapsSoEntry.find_by_path (local_pid, fallback_ld);
#endif
}
@@ -1017,8 +1017,9 @@ namespace Frida {
establish_connection (launch, spec, bres, agent_ctrl, fallback_address, cancellable);
uint64 loader_base = (uintptr) bres.context.allocation_base;
var call_builder = new RemoteCallBuilder (loader_base, saved_regs);
GPRegs regs = saved_regs;
regs.stack_pointer = bres.allocated_stack.stack_root;
var call_builder = new RemoteCallBuilder (loader_base, regs);
call_builder.add_argument (loader_base + loader_layout.ctx_offset);
RemoteCall loader_call = call_builder.build (this);
RemoteCallResult loader_result = yield loader_call.execute (cancellable);
@@ -1076,13 +1077,25 @@ namespace Frida {
unowned uint8[] bootstrapper_code = Frida.Data.HelperBackend.get_bootstrapper_bin_blob ().data;
size_t bootstrapper_size = round_size_to_page_size (bootstrapper_code.length);
size_t stack_size = 64 * 1024;
uint64 allocation_base = 0;
size_t allocation_size = size_t.max (bootstrapper_size, loader_size);
size_t allocation_size = size_t.max (bootstrapper_size, loader_size) + stack_size;
uint64 remote_mmap = 0;
uint64 remote_munmap = 0;
ProcMapsEntry? remote_libc = ProcMapsEntry.find_by_path (pid, local_libc.path);
ProcMapsSoEntry? remote_libc = ProcMapsSoEntry.find_by_path (pid, local_libc.path);
#if ANDROID
bool same_libc = false;
if (remote_libc != null) {
bool same_device = remote_libc.identity.split (":")[0] == local_libc.identity.split (":")[0];
bool same_inode = remote_libc.identity.split (" ")[1] == local_libc.identity.split (" ")[1];
bool same_path = remote_libc.path == local_libc.path;
same_libc = same_device && same_inode && same_path;
}
#else
bool same_libc = remote_libc != null && remote_libc.identity == local_libc.identity;
#endif
if (same_libc) {
remote_mmap = remote_libc.base_address + mmap_offset;
remote_munmap = remote_libc.base_address + munmap_offset;
@@ -1117,10 +1130,12 @@ namespace Frida {
Memory.copy (&bootstrap_ctx, output_context, output_context.length);
allocation_base = (uintptr) bootstrap_ctx.allocation_base;
code_swap.revert ();
}
result.allocated_stack.stack_base = (void *) (allocation_base + allocation_size - stack_size);
result.allocated_stack.stack_size = stack_size;
try {
write_memory (allocation_base, bootstrapper_code);
maybe_fixup_helper_code (allocation_base, bootstrapper_code);
@@ -1129,7 +1144,9 @@ namespace Frida {
HelperBootstrapStatus status = SUCCESS;
do {
var call_builder = new RemoteCallBuilder (code_start, saved_regs);
GPRegs regs = saved_regs;
regs.stack_pointer = result.allocated_stack.stack_root;
var call_builder = new RemoteCallBuilder (code_start, regs);
unowned uint8[] fallback_ld_data = fallback_ld.data;
unowned uint8[] fallback_libc_data = fallback_libc.data;
@@ -1179,7 +1196,7 @@ namespace Frida {
result.context.libc = &result.libc;
if (result.context.rtld_flavor == ANDROID && result.libc.dlopen == null) {
ProcMapsEntry? remote_ld = ProcMapsEntry.find_by_address (pid, (uintptr) result.context.rtld_base);
ProcMapsSoEntry? remote_ld = ProcMapsSoEntry.find_by_address (pid, (uintptr) result.context.rtld_base);
bool same_ld = remote_ld != null && local_android_ld != null && remote_ld.identity == local_android_ld.identity;
if (!same_ld)
throw new Error.NOT_SUPPORTED ("Unable to locate Android dynamic linker; please file a bug");
@@ -1229,7 +1246,7 @@ namespace Frida {
}
}
private static void * rebase_pointer (uintptr local_ptr, ProcMapsEntry local_module, ProcMapsEntry remote_module) {
private static void * rebase_pointer (uintptr local_ptr, ProcMapsSoEntry local_module, ProcMapsSoEntry remote_module) {
var offset = local_ptr - local_module.base_address;
return (void *) (remote_module.base_address + offset);
}
@@ -1416,14 +1433,28 @@ namespace Frida {
}
}
private struct AllocatedStack {
public void * stack_base;
public size_t stack_size;
public uint64 stack_root {
get {
return (uint64) stack_base + (uint64) stack_size;
}
}
}
private class BootstrapResult {
public HelperBootstrapContext context;
public HelperLibcApi libc;
public AllocatedStack allocated_stack;
public BootstrapResult clone () {
var res = new BootstrapResult ();
res.context = context;
res.libc = libc;
res.allocated_stack = allocated_stack;
return res;
}
}
@@ -1931,6 +1962,10 @@ namespace Frida {
try {
yield suspend (null);
close ();
} catch (Error e) {
// If the process is gone, then there's no point in retrying.
if (e is Error.PROCESS_NOT_FOUND)
attach_state = ALREADY_ATTACHED;
} catch (GLib.Error e) {
}
}
@@ -1963,15 +1998,15 @@ namespace Frida {
#elif ARM
target_address &= ~1;
uint32 arm_breakpoint_val = 0xe7f001f0U;
uint16 thumb_breakpoint_val = 0xde01;
uint32 arm_breakpoint_val = (0xe7f001f0U).to_little_endian ();
uint16 thumb_breakpoint_val = ((uint16) 0xde01).to_little_endian ();
bool is_thumb = (target & 1) != 0;
if (is_thumb)
breakpoint_data = (uint8[]) &thumb_breakpoint_val;
else
breakpoint_data = (uint8[]) &arm_breakpoint_val;
#elif ARM64
uint32 breakpoint_val = 0xd4200000U;
uint32 breakpoint_val = (0xd4200000U).to_little_endian ();
breakpoint_data = (uint8[]) &breakpoint_val;
#elif MIPS
uint32 breakpoint_val = 0x0000000dU;
@@ -2255,6 +2290,9 @@ namespace Frida {
this.session = session;
Gum.Linux.enumerate_ranges ((Posix.pid_t) session.pid, READ | EXECUTE, d => {
unowned Gum.FileMapping? file = d.file;
if (file != null && file.path.has_prefix ("memfd:"))
return true;
if (d.range.size >= code.length) {
code_start = d.range.base_address + d.range.size - round_size_to_page_size (code.length);
code_end = code_start + code.length;
@@ -3281,38 +3319,87 @@ namespace Frida {
}
#endif
private class ProcMapsEntry {
private class ProcMapsSoEntry {
public uint64 base_address;
public string path;
public string identity;
private ProcMapsEntry (uint64 base_address, string path, string identity) {
private ProcMapsSoEntry (uint64 base_address, string path, string identity) {
this.base_address = base_address;
this.path = path;
this.identity = identity;
}
public static ProcMapsEntry? find_by_address (uint pid, uint64 address) {
public static ProcMapsSoEntry? find_by_address (uint pid, uint64 address) {
var iter = MapsIter.for_pid (pid);
while (iter.next ()) {
uint64 start = iter.start_address;
uint64 end = iter.end_address;
if (address >= start && address < end)
return new ProcMapsEntry (start, iter.path, iter.identity);
return new ProcMapsSoEntry (start, iter.path, iter.identity);
}
return null;
}
public static ProcMapsEntry? find_by_path (uint pid, string path) {
public static ProcMapsSoEntry? find_by_path (uint pid, string path) {
var candidates = new Gee.ArrayList<Candidate> ();
Candidate? latest_candidate = null;
var iter = MapsIter.for_pid (pid);
#if ANDROID
unowned string libc_path = Gum.Process.get_libc_module ().path;
#endif
while (iter.next ()) {
string candidate_path = iter.path;
if (candidate_path == path)
return new ProcMapsEntry (iter.start_address, candidate_path, iter.identity);
string current_path = iter.path;
if (current_path == "[page size compat]")
continue;
if (current_path != path) {
latest_candidate = null;
continue;
}
string flags = iter.flags;
#if ANDROID
if (current_path == libc_path && flags[3] == 's')
continue;
#endif
if (iter.file_offset == 0) {
latest_candidate = new Candidate () {
entry = new ProcMapsSoEntry (iter.start_address, current_path, iter.identity),
total_ranges = 0,
executable_ranges = 0,
};
candidates.add (latest_candidate);
}
if (latest_candidate != null) {
latest_candidate.total_ranges++;
if (flags[2] == 'x')
latest_candidate.executable_ranges++;
}
}
return null;
candidates.sort ((a, b) => b.score () - a.score ());
if (candidates.is_empty)
return null;
return candidates.first ().entry;
}
private class Candidate {
public ProcMapsSoEntry entry;
public uint total_ranges;
public uint executable_ranges;
public int score () {
int result = (int) total_ranges;
if (executable_ranges == 0)
result = -result;
return result;
}
}
private class MapsIter {
@@ -3332,15 +3419,27 @@ namespace Frida {
}
}
public string identity {
public string flags {
owned get {
return info.fetch (3);
}
}
public uint64 file_offset {
get {
return uint64.parse (info.fetch (4), 16);
}
}
public string identity {
owned get {
return info.fetch (5);
}
}
public string path {
owned get {
return info.fetch (4);
return info.fetch (6);
}
}
@@ -3355,8 +3454,8 @@ namespace Frida {
return;
}
if (!/^([0-9a-f]+)-([0-9a-f]+) \S{4} [0-9a-f]+ ([0-9a-f]{2,}:[0-9a-f]{2,} \d+) +([^\n]+)$/m.match (contents,
0, out info)) {
if (!/^([0-9a-f]+)-([0-9a-f]+) (\S{4}) ([0-9a-f]+) ([0-9a-f]{2,}:[0-9a-f]{2,} \d+) +([^\n]+)$/m.match (
contents, 0, out info)) {
assert_not_reached ();
}
}
+6
View File
@@ -204,8 +204,14 @@ namespace Frida {
private async LinuxHelper obtain_for_64bit (Cancellable? cancellable) throws Error, IOError {
if (factory64 == null) {
var store = get_resource_store ();
#if !ARM64
/*
* If we are building for ARM64 and our pointer size is not 8-bytes, then we must be building for
* ILP32, so avoid the spurious error.
*/
if (sizeof (void *) != 8 && store.helper64 == null)
throw new Error.NOT_SUPPORTED ("Unable to handle 64-bit processes due to build configuration");
#endif
factory64 = new HelperFactory (store.helper64, store, main_context);
factory64.lost.connect (on_factory_lost);
factory64.output.connect (on_factory_output);
+1
View File
@@ -1,6 +1,7 @@
#ifndef __FRIDA_SYSCALL_H__
#define __FRIDA_SYSCALL_H__
#include <linux/unistd.h>
#include <sys/syscall.h>
#undef SYS_process_vm_readv
+7 -10
View File
@@ -2,24 +2,21 @@ top_srcdir := ../../..
releng := $(top_srcdir)/releng
BUILDDIR ?= $(top_srcdir)/build
FRIDA_MONOREPO ?= $(top_srcdir)/..
MESON ?= $(releng)/meson/meson.py
ifdef FRIDA_HOST
host_os := $(shell echo $(FRIDA_HOST) | cut -f1 -d"-")
host_arch := $(shell echo $(FRIDA_HOST) | cut -f2 -d"-")
host_machine := $(FRIDA_HOST)
else
host_os := $(shell $(releng)/detect-os.sh)
host_arch := $(shell $(releng)/detect-arch.sh)
host_machine := $(shell basename $$(ls -1 $(BUILDDIR)/frida-linux-*.txt | tail -1) | cut -d"." -f1 | cut -d"-" -f2-)
endif
host_arch := $(shell echo $(host_machine) | cut -d"-" -f2)
crossfile := $(shell test -d "$(BUILDDIR)" \
&& echo "$(BUILDDIR)/frida-$(host_os)-$(host_arch).txt" \
|| echo $(FRIDA_MONOREPO)/build/frida*-$(host_os)-$(host_arch).txt)
crossfile := $(BUILDDIR)/frida-$(host_machine).txt
build: ext/linux/tools/include/nolibc/nolibc.h
rm -rf build
meson setup --cross-file $(crossfile) -Db_lto=true build
meson compile -C build
$(MESON) setup --cross-file $(crossfile) -Db_lto=true build
$(MESON) compile -C build
cp build/bootstrapper.bin bootstrapper-$(host_arch).bin
cp build/loader.bin loader-$(host_arch).bin
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+6 -2
View File
@@ -550,7 +550,9 @@ frida_infer_rtld_flavor_from_filename (const char * name)
if (frida_str_has_prefix (name, "ld-uClibc"))
return FRIDA_RTLD_UCLIBC;
if (strcmp (name, "libc.so") == 0)
if (strcmp (name, "libc.so") == 0 ||
frida_str_has_prefix (name, "libc.musl") ||
frida_str_has_prefix (name, "ld-musl"))
return FRIDA_RTLD_MUSL;
if (strcmp (name, "ld-android.so") == 0)
@@ -607,7 +609,9 @@ frida_path_is_libc (const char * path, FridaRtldFlavor rtld_flavor)
else
name = path;
return frida_str_has_prefix (name, "libc.so");
return frida_str_has_prefix (name, "libc.so") ||
frida_str_has_prefix (name, "libc.musl") ||
frida_str_has_prefix (name, "ld-musl");
}
static ssize_t
+1 -1
View File
@@ -9,7 +9,7 @@
#endif
#ifndef __ELF_NATIVE_CLASS
# if defined (__x86_64__) || defined (__aarch64__) || defined (__mips64)
# if defined (__x86_64__) || (defined (__aarch64__) && !defined (__ILP32__)) || defined (__mips64)
# define __ELF_NATIVE_CLASS 64
# else
# define __ELF_NATIVE_CLASS 32
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+24 -16
View File
@@ -303,16 +303,20 @@ frida_receive_chunk (int sockfd, void * buffer, size_t length, const FridaLibcAp
.iov_base = cursor,
.iov_len = remaining
};
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &io,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0,
};
struct msghdr msg;
ssize_t n;
/*
* Avoid inline initialization to prevent the compiler attempting to insert
* a call to memset.
*/
msg.msg_name = NULL,
msg.msg_namelen = 0,
msg.msg_iov = &io,
msg.msg_iovlen = 1,
msg.msg_control = NULL,
msg.msg_controllen = 0,
n = libc->recvmsg (sockfd, &msg, 0);
if (n <= 0)
return false;
@@ -334,14 +338,18 @@ frida_receive_fd (int sockfd, const FridaLibcApi * libc)
.iov_len = sizeof (dummy)
};
FridaControlMessage control;
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &io,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof (control),
};
struct msghdr msg;
/*
* Avoid inline initialization to prevent the compiler attempting to insert
* a call to memset.
*/
msg.msg_name = NULL,
msg.msg_namelen = 0,
msg.msg_iov = &io,
msg.msg_iovlen = 1,
msg.msg_control = &control,
msg.msg_controllen = sizeof (control),
res = libc->recvmsg (sockfd, &msg, 0);
if (res == -1 || res == 0 || msg.msg_controllen == 0)
+6 -1
View File
@@ -24,6 +24,10 @@ if host_machine.cpu_family() == 'arm'
extra_flags += '-marm'
endif
if host_machine.cpu_family() == 'x86'
extra_flags += '-fno-stack-protector'
endif
common_objcopy_flags = [
'-O', 'binary',
'-S',
@@ -53,9 +57,10 @@ extra_link_args = [
'-Wl,-T,' + helper_lds,
]
if host_machine.cpu_family() == 'arm'
extra_link_args += run_command('../../../../build/frida-android-arm-clang', '-print-libgcc-file-name', check: false).stdout().strip()
extra_link_args += run_command(cc, '-print-libgcc-file-name', check: false).stdout().strip()
endif
bootstrapper_sources = [
'bootstrapper.c',
'elf-parser.c',
Loaded 100 of 127 files, more files were not shown because too many files have changed in this diff. Show more