Compare commits

...
252 Commits
Author SHA1 Message Date
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
Ole André Vadla Ravnås fd5a274680 subprojects: Prepare for release 2024-07-08 14:27:06 +02:00
Ole André Vadla Ravnås d3f949a51c subprojects: Bump outdated 2024-07-08 14:25:06 +02:00
Ole André Vadla Ravnås 6d3ea5f589 fruity: Clean up OS-specific bits 2024-07-08 14:24:29 +02:00
Ole André Vadla Ravnås 3f6b41dc8c fruity: Handle libusb failing to initialize
Which may happen due to insufficient privileges, stripped down
environments such as containers, etc.
2024-07-08 10:26:46 +02:00
Ole André Vadla Ravnås 2c23afacfd subprojects: Prepare for release 2024-07-06 00:07:28 +02:00
Ole André Vadla Ravnås 51bf048d5b subprojects: Bump outdated 2024-07-06 00:06:32 +02:00
Ole André Vadla RavnåsandHåvard Sørbø be6a2b305c socket: Fix Host header logic in negotiate_connection()
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-06 00:04:35 +02:00
Ole André Vadla Ravnås 5625a08fd9 device: Fix property getters for id and name
When Device is created with a custom value for either.

Due to typos the getters ended up recursing until stack overflow.
2024-07-05 23:35:47 +02:00
Ole André Vadla Ravnås 684739a1e6 subprojects: Prepare for release 2024-07-05 15:32:45 +02:00
Ole André Vadla RavnåsandHåvard Sørbø e0c36e6826 fruity: Retry RSD port lookup on failure
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 15:31:42 +02:00
Ole André Vadla RavnåsandHåvard Sørbø eeb0c7a50a fruity: Fix gadget upload when no usbmux connection is available
By listening on all interfaces, not just loopback, so we can connect to
it across the tunnel.

Also switch to IPv6 while at it.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 15:22:40 +02:00
Ole André Vadla RavnåsandHåvard Sørbø f2dc84324a fruity: Skip fetching dyld symbols if libSystem is initialized
We only need them for early instrumentation.

This means we can attach to a running app not spawned by Frida, even on
latest iOS versions where dyld restarts.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 14:22:26 +02:00
Ole André Vadla RavnåsandHåvard Sørbø b9d07f0bfb fruity: Wire up MacOSCoreDeviceTransport event handling
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 13:55:34 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 8b54716bfc fruity: Ensure provider name doesn't mutate
In case of a transport departing and taking the string storage with it.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 13:51:59 +02:00
Ole André Vadla Ravnås 68bfde7ae0 xpc-object-reader: Add get_uuid_value() 2024-07-05 13:50:57 +02:00
Ole André Vadla RavnåsandHåvard Sørbø bcdcc090b6 device: Refactor construction and property wiring
We can compute some of the properties from the provider.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 13:50:19 +02:00
Ole André Vadla Ravnås 2a1a80abd9 fruity: Tweak ordering of system parameters 2024-07-05 10:44:44 +02:00
ea6307d60b fruity: Add os.build and hardware to system parameters (#534)
Co-authored-by: Ole André Vadla Ravnås <[email protected]>
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 10:10:17 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 2bf45ececd fruity: Await ObjC runtime initialization on iOS >= 18
By making use of notifyObjCInit() if available.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-04 15:20:51 +02:00
Ole André Vadla RavnåsandHåvard Sørbø d6b502eb3e fruity: Add support for dyld restart on iOS >= 18
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-04 14:56:51 +02:00
Ole André Vadla Ravnås 0de45afe2f meson: Auto-disable external device backends for 32-bit host machines 2024-07-04 13:49:25 +02:00
Ole André Vadla Ravnås 7262e55468 macos: Replace logging with TODOs in the CoreDevice transport 2024-07-04 13:36:01 +02:00
Ole André Vadla RavnåsandHåvard Sørbø def974818f socket: Support IPv6 addresses in negotiate_connection()
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-04 12:51:58 +02:00
Ole André Vadla Ravnås d5312d288f pipe: Fix tvOS build regression
We need to make sure we include the Mach headers before CoreFoundation
does, so our version availability overrides are used.
2024-07-04 11:16:06 +02:00
Ole André Vadla Ravnås 7e9fb1e1c6 subprojects: Bump outdated 2024-07-03 23:20:05 +02:00
Ole André Vadla RavnåsandHåvard Sørbø e415a2ec83 fruity: Fix the macOS CoreDevice connection type logic
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 14:20:35 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 248127623f tests: Add manual XPC application launching test
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:45:09 +02:00
Ole André Vadla RavnåsandHåvard Sørbø f246b4e17d xpc-service: Support type-annotating request parameters
Represented using a `(type, value)` tuple.

This means uint64 and UUID values can be passed in.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:42:48 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 7c4633358b fruity: Improve open_channel() to support tcp:service-name
Useful for e.g. `com.apple.coredevice.openstdiosocket`.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:35:08 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 85d063ab9b fruity: Revive HostChannelProvider implementation
Removed by accident in 08e5d37348.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:33:25 +02:00
Ole André Vadla RavnåsandHåvard Sørbø a6650efecb xpc: Make add_uuid_value() expect a raw UUID
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:31:54 +02:00
Ole André Vadla RavnåsandHåvard Sørbø d382734bca value: Remove unused UUID functionality
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:30:29 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 08e5d37348 fruity: Rework to support userspace NCM
To support systems without a compatible NCM driver, such as contemporary
Linux kernels, Windows, etc.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 11:20:16 +02:00
Ole André Vadla Ravnås 6317446c37 xpc-client: Add post() 2024-07-03 11:14:41 +02:00
Ole André Vadla Ravnås 7048e851b0 xpc-client: Handle closure in open state 2024-07-03 11:14:41 +02:00
Ole André Vadla Ravnås 7d22da8d3d meson: Summarize backends and more of the features 2024-07-03 11:14:41 +02:00
Ole André Vadla Ravnås 1f164564ef meson: Auto-disable external device backends on non-desktop 2024-07-03 11:14:41 +02:00
Ole André Vadla Ravnås 47a2e6728b submodules: Bump releng 2024-07-03 11:14:36 +02:00
Ole André Vadla Ravnås b19333420c web-service: Listen on dynamic interfaces last
So that specifying e.g. 0.0.0.0 takes precedence and won't cause start()
to fail.
2024-07-03 10:47:09 +02:00
Ole André Vadla Ravnås 29ec9f0c98 tunnel-interface-observer: Fix confusing variable name
The tunnel isn't only for development tools.
2024-07-03 10:43:02 +02:00
Ole André Vadla Ravnås cbcb7f37bf gadget: Integrate TunnelInterfaceObserver
So we listen on Apple's CoreDevice tunnel network interfaces on iOS and
tvOS.
2024-07-02 22:50:09 +02:00
Ole André Vadla Ravnås f53230bd7a control-service: Integrate TunnelInterfaceObserver
So we listen on Apple's CoreDevice tunnel network interfaces on iOS and
tvOS.
2024-07-02 22:50:09 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 39486a5331 netif: Add TunnelInterfaceObserver for iOS and tvOS
For observing Apple's CoreDevice tunnel network interfaces. Intended for
ControlService (frida-server) and frida-gadget to be able to listen on
such interfaces as they arrive and depart.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-02 22:50:05 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 67f6895e13 base: Add an iterable CFArray wrapper
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-02 22:26:15 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 1b80ce0779 vapi: Improve the CoreFoundation bindings
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-02 22:26:15 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 9bd3c3974f web-service: Support dynamic network interfaces
By providing a DynamicInterfaceObserver with the desired behavior.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-02 22:26:12 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 055fe8744a socket: Remove the partial NECP integration
It's not a good fit for keeping track of network interfaces.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-02 14:00:35 +02:00
Ole André Vadla RavnåsandHåvard Sørbø a0f7df8d0e socket: Sketch a partial NECP integration
In pursuit of notifications when network interfaces arrive and depart.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-06-28 14:14:01 +02:00
Ole André Vadla Ravnås 4b5fe35b00 devkit: Also install frida-core.gir 2024-06-07 09:54:32 +02:00
Håvard Sørbø 9330f02ca5 xpc-service: Fix handling of request() with arrays (#532)
Where we forgot to call begin/end to actually generate an array, and
would end up generating an invalid XPC object.
2024-06-06 22:46:35 +02:00
Ole André Vadla Ravnås 5d23227efc subprojects: Prepare for release 2024-06-04 14:14:40 +02:00
Ole André Vadla Ravnås 300a55b75c subprojects: Bump outdated 2024-06-04 14:12:37 +02:00
Ole André Vadla Ravnås f2e071ed78 subprojects: Prepare for release 2024-06-04 13:41:11 +02:00
Ole André Vadla Ravnås bc7f73b492 subprojects: Bump outdated 2024-06-04 13:38:40 +02:00
f91a3c92fe darwin: Avoid thread_set_state() during injection (#530)
So we don't get killed by the system on e.g. macOS >= 14.5.

We can create the thread in running state. There is no need to keep it
suspended until we have created the dispatch source.

Co-authored-by: Håvard Sørbø <[email protected]>
Co-authored-by: Ole André Vadla Ravnås <[email protected]>
2024-06-04 13:36:31 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 6eacc889cd fruity: Perform RSDCheckin on services that need it
In this way we retain backwards-compatibility for open_channel() of
lockdown services when a CoreDevice tunnel is available.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-06-04 12:27:57 +02:00
Ole André Vadla RavnåsandHåvard Sørbø d21ba21aea fruity: Stop caching the LockdownClient
To avoid issues with multiple consumers.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-06-04 11:34:55 +02:00
Ole André Vadla Ravnås cfcdc26d29 subprojects: Prepare for release 2024-05-31 22:52:22 +02:00
Ole André Vadla Ravnås 69555bbbb6 subprojects: Bump outdated 2024-05-31 22:50:47 +02:00
Ole André Vadla Ravnås d2d014c081 subprojects: Prepare for release 2024-05-31 15:42:49 +02:00
Ole André Vadla Ravnås 8f9c30953b subprojects: Bump outdated 2024-05-31 15:41:33 +02:00
Ole André Vadla Ravnås 2396ad5717 fruity: Remove temporary debug helpers
Useful during bring-up, but not so much anymore.
2024-05-31 14:14:27 +02:00
Ole André Vadla Ravnås ded45c9c93 fruity: Remove the xpc-test program
It has served its purpose. We now have tests that can be run instead.
2024-05-31 14:10:28 +02:00
Ole André Vadla Ravnås 8d375b5e46 fruity: Avoid leaking Posix types into the public API 2024-05-31 14:08:52 +02:00
Ole André Vadla Ravnås 1722429383 subprojects: Bump outdated 2024-05-31 06:45:37 +02:00
Ole André Vadla Ravnås ffd23574a9 submodules: Bump releng 2024-05-31 06:45:36 +02:00
Ole André Vadla Ravnås 5a8bd3fbaf meson: Add new Fruity private requirements to .pc 2024-05-31 00:06:55 +02:00
Ole André Vadla Ravnås 56605717ad meson: Fix build when glib-networking is a subproject 2024-05-30 23:15:15 +02:00
Ole André Vadla Ravnås d8a7c7d3fb subprojects: Bump outdated 2024-05-30 21:58:55 +02:00
Ole André Vadla Ravnås 39725e427b ci: Fix apt-get steps in the native and cross jobs
Need to update the database before installing.
2024-05-30 21:58:13 +02:00
Ole André Vadla Ravnås 7fe3a00543 subprojects: Bump outdated 2024-05-30 21:50:35 +02:00
Ole André Vadla Ravnås 41c31ba0a0 subprojects: Bump outdated 2024-05-30 21:42:28 +02:00
Ole André Vadla Ravnås c8e1ebdb7c ci: Allow Corellium test steps to fail for now
Until we figure out the stability issue.
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 3a678a3553 meson: Fix build when connectivity deps are subprojects
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 2048115cb3 meson: Change connectivity option default to enabled
So fallbacks trigger, and we build with the same features irrespective
of whether we build with a Frida SDK present.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 82a68e55cc fruity: Add RemoteXPC support
With two platform integrations:

- macOS: where we use Apple's tunnel, by talking to remotepairingd and
  creating an assertion to ensure the tunnel is up and stays up for as
  long as we need it. The challenging part here was figuring out the
  device-side RemoteServiceDiscovery (RSD) port inside the tunnel, as
  it appears no API exposes it. Our solution is to ask the kernel for
  a list of TCP sockets, and picking out the ones where the foreign
  address is the device-side IPv6 address inside the tunnel, **and**
  where the process that created the socket is remoted. This seems to
  consistenly result in us finding a single socket, connected to the RSD
  port, but we try all of them in case this behavior ends up changing.
- Linux: where we open our own tunnel, assuming the iDevice has been
  mode-switched (e.g. by usbmuxd), and the Linux CDC NCM driver has been
  patched something along the lines of:
  https://lore.kernel.org/all/[email protected]/T/

We don't yet have a platform integration for Windows, as Apple's
software doesn't currently set up a tunnel. The official driver also
appears to keep the USB device busy, meaning we can't easily trigger
a mode-switch. This is an area that needs more research.

Another area that needs future improvement is that we only support
cabled connectivity on macOS. This should be easy to improve on once we
have updated frida-server and frida-gadget so they listen on tunnel
interfaces whenever they appear.

Kudos to @doronz88 for figuring out most of the protocol bits that we
needed.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø e0d6c29c41 meson: Add wrap for lwip
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 1f820ae2ac meson: Add wrap for ngtcp2
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø c0bc5f5bd9 base: Add helpers for handling JSON and GLib.Variant
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 7ab0cf75ea fruity: Fix spawn() on jailed iOS 17
The jailed equivalent of 827eee33cc.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 2175cc756a dtx: Use more appropriate GValue setter for NSObject
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 90d60683dd dtx: Make DTXArgumentList elements visible to consumers
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø cf09ba46e4 dtx: Allow notification payload to be of any type
The value is not always an NSDictionary, e.g. in case of a DTTapMessage.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø ed9fcb10c1 dtx: Fix channel code interpretation for inbound invokes
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 333ca2f852 dtx: Bump max message size to 128 MB
Screenshots can be quite large, and we might in theory be asked to fetch
large files.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø c9e541cc0e keyed-archive: Add support for decoding DTTapMessage
And its subclasses.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 3e86270d77 keyed-archive: Add support for encoding NSArray
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 8a21929415 keyed-archive: Add NSArray.add_object()
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 4865ea07eb keyed-archive: Add support for decoding NSData
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 28f7895704 keyed-archive: Improve NSNumber support
So boolean/float/double values are treated distinctly from int64.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 859d6cd7de plist: Fix support for float and double in to_binary()
Co-authored-by: Håvard Sørbø <[email protected]>
2024-05-30 21:28:33 +02:00
Ole André Vadla Ravnås b71c861b1d subprojects: Bump outdated 2024-05-30 20:51:53 +02:00
Ole André Vadla Ravnås b486936b33 submodules: Bump releng 2024-05-30 20:51:53 +02:00
Ole André Vadla Ravnås 5b587f587f base: Add Numeric.int64_{hash,equal}() 2024-05-29 23:18:13 +02:00
Ole André Vadla Ravnås 39e2c7bf81 subprojects: Prepare for release 2024-05-21 12:50:31 +02:00
Ole André Vadla Ravnås 2b82c21df1 subprojects: Bump outdated 2024-05-21 12:48:55 +02:00
Ole André Vadla Ravnås e45989dbdf subprojects: Prepare for release 2024-05-20 22:54:26 +02:00
Ole André Vadla Ravnås 204b16a4b7 compat: Escape spaces in depfile paths
Quoting isn't supported, so we should be escaping the spaces instead.
2024-05-20 22:50:29 +02:00
Ole André Vadla Ravnås d9f33a918e meson: Find DbgHelp and SymSrv using Gum's variables 2024-05-20 22:41:48 +02:00
Ole André Vadla Ravnås 082b100ab9 subprojects: Bump outdated 2024-05-20 22:40:31 +02:00
Ole André Vadla Ravnås 20cdf64312 meson: Fix vapidir exported in our .pc 2024-05-20 22:39:15 +02:00
Ole André Vadla Ravnås 0bb558e2a1 meson: Refactor the (un)installed variables logic 2024-05-20 22:37:59 +02:00
Ole André Vadla Ravnås 04f22126a7 subprojects: Prepare for release 2024-05-18 23:04:59 +02:00
Ole André Vadla Ravnås 59deab2ea9 subprojects: Bump outdated 2024-05-18 22:57:58 +02:00
Ole André Vadla Ravnås 1b9e347650 submodules: Bump releng 2024-05-18 22:57:58 +02:00
Ole André Vadla Ravnås 5e149a3f2d compat: Forward yielding built-in options when subproject
So that the parent project's machine-neutral built-in options such as
`prefix` are used.
2024-05-18 22:50:57 +02:00
121 changed files with 16871 additions and 2065 deletions

No files matched your search

+49 -4
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"' }
@@ -42,7 +42,9 @@ jobs:
run: sudo security authorizationdb write system.privilege.taskport allow
- name: Install gcc-multilib
if: matrix.id == 'linux-x86'
run: sudo apt-get install gcc-multilib lib32stdc++-11-dev
run: |
sudo apt-get update
sudo apt-get install gcc-multilib lib32stdc++-11-dev
- name: Build
if: ${{ startsWith(matrix.id, 'windows-') }}
run: |
@@ -66,7 +68,6 @@ jobs:
name: core-devkit-${{ matrix.id }}
path: build/src/devkit/
- name: Test
if: matrix.id != 'macos-arm64'
run: make test
cross:
@@ -87,7 +88,9 @@ jobs:
with:
submodules: recursive
- name: Install toolchain
run: sudo apt-get install ${{ matrix.pkg }}
run: |
sudo apt-get update
sudo apt-get install ${{ matrix.pkg }}
- name: Build
run: |
./configure ${{ matrix.opts }} ${{ env.FRIDA_CORE_OPTIONS }}
@@ -147,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 }}
@@ -177,6 +188,7 @@ jobs:
tar -C /tmp/pkg -czf /tmp/runner.tar.gz .
- name: Test on Corellium iOS device
if: matrix.id == 'ios-arm64'
continue-on-error: true
uses: frida/corellium-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -192,6 +204,7 @@ jobs:
./frida-tests
- name: Test on Corellium Android device
if: matrix.id == 'android-arm64'
continue-on-error: true
uses: frida/corellium-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -228,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, arm64, 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
+57 -10
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
@@ -432,7 +466,7 @@ def compile(privdir: Path, state: State):
path = Path(raw_path)
if not path.is_absolute():
path = Path(os.path.relpath(workdir / path, top_builddir))
input_entries.append(quote(path.as_posix()))
input_entries.append(escape_depfile_path(path.as_posix()))
depfile_lines.append(f"{output_relpath}: {' '.join(input_entries)}")
(state.builddir / DEPFILE_FILENAME).write_text("\n".join(depfile_lines), encoding="utf-8")
@@ -480,6 +514,10 @@ def option_should_be_forwarded(k: "OptionKey",
return False
if not str(v.value):
return False
if not is_for_child and role == "subproject" and k.subproject in {"", our_project_id}:
if not coredata.BUILTIN_OPTIONS[k.as_root()].yielding:
return k.subproject == our_project_id
return True
if k.module == "python":
if k.name == "install_env" and v.value == "prefix":
@@ -519,10 +557,8 @@ def scrub_windows_devenv_dirs_from_path(raw_path: str, env: Mapping[str, str]) -
return ";".join(clean_entries)
def quote(path: str) -> str:
if " " not in path:
return path
return "\"" + path.replace ("\"", "\\\"") + "\""
def escape_depfile_path(path: str) -> str:
return path.replace(" ", "\\ ")
def query_releng_location(role: Role) -> Path:
@@ -587,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"
@@ -614,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
}
}
+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");
}
}
}
+34
View File
@@ -0,0 +1,34 @@
namespace Frida {
public class CFArray<T> {
private CoreFoundation.Array handle;
public static CFArray<T> wrap<T> (void * handle) {
return new CFArray<T> ((CoreFoundation.Array) handle);
}
private CFArray (CoreFoundation.Array handle) {
this.handle = handle;
}
public Iterator<T> iterator () {
return new Iterator<T> (handle);
}
public class Iterator<T> {
public CoreFoundation.Array handle;
private CoreFoundation.Index i = 0;
private CoreFoundation.Index length;
internal Iterator (CoreFoundation.Array arr) {
handle = arr;
length = arr.length;
}
public unowned T? next_value () {
if (i == length)
return null;
return handle[i++];
}
}
}
}
+51
View File
@@ -0,0 +1,51 @@
#include "frida-darwin.h"
gpointer
_frida_dispatch_retain (gpointer object)
{
dispatch_retain (object);
return object;
}
void
_frida_xpc_connection_set_event_handler (xpc_connection_t connection, FridaXpcHandler handler, gpointer user_data)
{
xpc_connection_set_event_handler (connection, ^(xpc_object_t object)
{
handler (object, user_data);
});
}
void
_frida_xpc_connection_send_message_with_reply (xpc_connection_t connection, xpc_object_t message, dispatch_queue_t replyq,
FridaXpcHandler handler, gpointer user_data, GDestroyNotify notify)
{
xpc_connection_send_message_with_reply (connection, message, replyq, ^(xpc_object_t object)
{
handler (object, user_data);
if (notify != NULL)
notify (user_data);
});
}
gchar *
_frida_xpc_object_to_string (xpc_object_t object)
{
gchar * result;
char * str;
str = xpc_copy_description (object);
result = g_strdup (str);
free (str);
return result;
}
gboolean
_frida_xpc_dictionary_apply (xpc_object_t dict, FridaXpcDictionaryApplier applier, gpointer user_data)
{
return xpc_dictionary_apply (dict, ^bool (const char * key, xpc_object_t val)
{
return applier (key, val, user_data);
});
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef __FRIDA_DARWIN_H__
#define __FRIDA_DARWIN_H__
#ifdef HAVE_MACOS
#include <glib.h>
#include <xpc/xpc.h>
typedef void (* FridaXpcHandler) (xpc_object_t object, gpointer user_data);
typedef gboolean (* FridaXpcDictionaryApplier) (const gchar * key, xpc_object_t val, gpointer user_data);
gpointer _frida_dispatch_retain (gpointer object);
void _frida_xpc_connection_set_event_handler (xpc_connection_t connection, FridaXpcHandler handler, gpointer user_data);
void _frida_xpc_connection_send_message_with_reply (xpc_connection_t connection, xpc_object_t message, dispatch_queue_t replyq,
FridaXpcHandler handler, gpointer user_data, GDestroyNotify notify);
gchar * _frida_xpc_object_to_string (xpc_object_t object);
gboolean _frida_xpc_dictionary_apply (xpc_object_t dict, FridaXpcDictionaryApplier applier, gpointer user_data);
#endif
#endif
+20
View File
@@ -9,9 +9,22 @@ base_sources = [
'socket.vala',
'socket-glue.c',
'buffer.vala',
'value.vala',
]
extra_vala_args = []
if host_os == 'macos'
base_sources += [
'xpc.vala',
'frida-darwin.c',
]
endif
if host_os_family == 'darwin'
base_sources += 'corefoundation.vala'
endif
extra_deps = []
if host_os_family != 'windows'
extra_vala_args += '--pkg=gio-unix-2.0'
@@ -22,6 +35,13 @@ base_vala_args = [gum_vala_args, '--pkg=gio-2.0', '--pkg=json-glib-1.0']
if nice_dep.found()
base_vala_args += '--pkg=nice'
endif
if host_os_family == 'darwin'
base_vala_args += [
'--pkg=corefoundation',
'--pkg=darwin-gcd',
'--pkg=darwin-xpc',
]
endif
base = static_library('frida-base-' + api_version, base_sources,
c_args: frida_component_cflags,
+2 -15
View File
@@ -774,25 +774,12 @@ namespace Frida {
protected extern void _handle_transport_packet (uint8[] data);
protected int _emit_transport_packet (uint8[] data) {
var v = OutputVector ();
v.buffer = data;
v.size = data.length;
OutputVector[] vectors = { v };
var m = OutputMessage ();
m.vectors = vectors;
m.num_vectors = vectors.length;
OutputMessage[] messages = { m };
try {
transport_socket.send_messages (messages, 0, 0, io_cancellable);
Udp.send (data, transport_socket, io_cancellable);
return 0;
} catch (GLib.Error e) {
return -1;
}
return 0;
}
protected void _on_sctp_socket_events_changed () {
+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;
}
}
+11
View File
@@ -2079,6 +2079,17 @@ namespace Frida {
}
namespace Numeric {
public uint int64_hash (int64? val) {
uint64 v = (uint64) val.abs ();
return (uint) ((v >> 32) ^ (v & 0xffffffffU));
}
public bool int64_equal (int64? val_a, int64? val_b) {
int64 a = val_a;
int64 b = val_b;
return a == b;
}
public uint uint64_hash (uint64? val) {
uint64 v = val;
return (uint) ((v >> 32) ^ (v & 0xffffffffU));
+563 -353
View File
File diff suppressed because it is too large. Load diff
+391
View File
@@ -0,0 +1,391 @@
namespace Frida {
public interface ObjectBuilder : Object {
public abstract unowned ObjectBuilder begin_dictionary ();
public abstract unowned ObjectBuilder set_member_name (string name);
public abstract unowned ObjectBuilder end_dictionary ();
public abstract unowned ObjectBuilder begin_array ();
public abstract unowned ObjectBuilder end_array ();
public abstract unowned ObjectBuilder add_null_value ();
public abstract unowned ObjectBuilder add_bool_value (bool val);
public abstract unowned ObjectBuilder add_int64_value (int64 val);
public abstract unowned ObjectBuilder add_uint64_value (uint64 val);
public abstract unowned ObjectBuilder add_data_value (Bytes val);
public abstract unowned ObjectBuilder add_string_value (string val);
public abstract unowned ObjectBuilder add_uuid_value (uint8[] val);
public abstract unowned ObjectBuilder add_raw_value (Bytes val);
public abstract Bytes build ();
}
public interface ObjectReader : Object {
public abstract bool has_member (string name) throws Error;
public abstract unowned ObjectReader read_member (string name) throws Error;
public abstract unowned ObjectReader end_member ();
public abstract uint count_elements () throws Error;
public abstract unowned ObjectReader read_element (uint index) throws Error;
public abstract unowned ObjectReader end_element () throws Error;
public abstract bool get_bool_value () throws Error;
public abstract uint8 get_uint8_value () throws Error;
public abstract uint16 get_uint16_value () throws Error;
public abstract int64 get_int64_value () throws Error;
public abstract uint64 get_uint64_value () throws Error;
public abstract Bytes get_data_value () throws Error;
public abstract unowned string get_string_value () throws Error;
public abstract unowned string get_uuid_value () throws Error;
}
public class VariantReader : Object, ObjectReader {
public Variant root_object {
get {
return scopes.peek_head ().val;
}
}
public Variant current_object {
get {
return scopes.peek_tail ().val;
}
}
private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();
public VariantReader (Variant v) {
push_scope (v);
}
public bool has_member (string name) throws Error {
var scope = peek_scope ();
if (scope.dict == null)
throw new Error.PROTOCOL ("Dictionary expected, but at %s", scope.val.print (true));
return scope.dict.contains (name);
}
public unowned ObjectReader read_member (string name) throws Error {
var scope = peek_scope ();
if (scope.dict == null)
throw new Error.PROTOCOL ("Dictionary expected, but at %s", scope.val.print (true));
Variant? v = scope.dict.lookup_value (name, null);
if (v == null)
throw new Error.PROTOCOL ("Key '%s' not found in dictionary: %s", name, scope.val.print (true));
push_scope (v);
return this;
}
public unowned ObjectReader end_member () {
pop_scope ();
return this;
}
public uint count_elements () throws Error {
var scope = peek_scope ();
scope.check_array ();
return (uint) scope.val.n_children ();
}
public unowned ObjectReader read_element (uint index) throws Error {
var scope = peek_scope ();
scope.check_array ();
push_scope (scope.val.get_child_value (index).get_variant ());
return this;
}
public unowned ObjectReader end_element () throws Error {
pop_scope ();
return this;
}
public bool get_bool_value () throws Error {
return peek_scope ().get_value (VariantType.BOOLEAN).get_boolean ();
}
public uint8 get_uint8_value () throws Error {
return peek_scope ().get_value (VariantType.BYTE).get_byte ();
}
public uint16 get_uint16_value () throws Error {
return peek_scope ().get_value (VariantType.UINT16).get_uint16 ();
}
public int64 get_int64_value () throws Error {
return peek_scope ().get_value (VariantType.INT64).get_int64 ();
}
public uint64 get_uint64_value () throws Error {
return peek_scope ().get_value (VariantType.UINT64).get_uint64 ();
}
public Bytes get_data_value () throws Error {
return peek_scope ().get_value (new VariantType.array (VariantType.BYTE)).get_data_as_bytes ();
}
public unowned string get_string_value () throws Error {
return peek_scope ().get_value (VariantType.STRING).get_string ();
}
public unowned string get_uuid_value () throws Error {
return peek_scope ().get_value (VariantType.STRING).get_string (); // TODO: Use a tuple to avoid ambiguity.
}
private void push_scope (Variant v) {
scopes.offer_tail (new Scope (v));
}
private Scope peek_scope () {
return scopes.peek_tail ();
}
private Scope pop_scope () {
return scopes.poll_tail ();
}
private class Scope {
public Variant val;
public VariantDict? dict;
public bool is_array = false;
public Scope (Variant v) {
val = v;
VariantType t = v.get_type ();
if (t.equal (VariantType.VARDICT))
dict = new VariantDict (v);
else if (t.is_subtype_of (VariantType.ARRAY))
is_array = true;
}
public Variant get_value (VariantType expected_type) throws Error {
if (!val.get_type ().equal (expected_type)) {
throw new Error.PROTOCOL ("Expected type '%s', got '%s'",
(string) expected_type.peek_string (),
(string) val.get_type ().peek_string ());
}
return val;
}
public void check_array () throws Error {
if (!is_array)
throw new Error.PROTOCOL ("Array expected, but at %s", val.print (true));
}
}
}
public class JsonObjectBuilder : Object, ObjectBuilder {
private Json.Builder builder = new Json.Builder ();
private Gee.Map<string, Bytes> raw_values = new Gee.HashMap<string, Bytes> ();
public unowned ObjectBuilder begin_dictionary () {
builder.begin_object ();
return this;
}
public unowned ObjectBuilder set_member_name (string name) {
builder.set_member_name (name);
return this;
}
public unowned ObjectBuilder end_dictionary () {
builder.end_object ();
return this;
}
public unowned ObjectBuilder begin_array () {
builder.begin_array ();
return this;
}
public unowned ObjectBuilder end_array () {
builder.end_array ();
return this;
}
public unowned ObjectBuilder add_null_value () {
builder.add_null_value ();
return this;
}
public unowned ObjectBuilder add_bool_value (bool val) {
builder.add_boolean_value (val);
return this;
}
public unowned ObjectBuilder add_int64_value (int64 val) {
builder.add_int_value (val);
return this;
}
public unowned ObjectBuilder add_uint64_value (uint64 val) {
builder.add_int_value ((int64) val);
return this;
}
public unowned ObjectBuilder add_data_value (Bytes val) {
builder.add_string_value (Base64.encode (val.get_data ()));
return this;
}
public unowned ObjectBuilder add_string_value (string val) {
builder.add_string_value (val);
return this;
}
public unowned ObjectBuilder add_uuid_value (uint8[] val) {
assert_not_reached ();
}
public unowned ObjectBuilder add_raw_value (Bytes val) {
string uuid = Uuid.string_random ();
builder.add_string_value (uuid);
raw_values[uuid] = val;
return this;
}
public Bytes build () {
string json = Json.to_string (builder.get_root (), false);
foreach (var e in raw_values.entries) {
unowned string uuid = e.key;
Bytes val = e.value;
unowned string raw_str = (string) val.get_data ();
string str = raw_str[:(long) val.get_size ()];
json = json.replace ("\"" + uuid + "\"", str);
}
return new Bytes (json.data);
}
}
public class JsonObjectReader : Object, ObjectReader {
private Json.Reader reader;
public JsonObjectReader (string json) throws Error {
try {
reader = new Json.Reader (Json.from_string (json));
} catch (GLib.Error e) {
throw new Error.INVALID_ARGUMENT ("%s", e.message);
}
}
public bool has_member (string name) throws Error {
bool result = reader.read_member (name);
reader.end_member ();
return result;
}
public unowned ObjectReader read_member (string name) throws Error {
if (!reader.read_member (name))
throw_dict_access_error ();
return this;
}
public unowned ObjectReader end_member () {
reader.end_member ();
return this;
}
[NoReturn]
private void throw_dict_access_error () throws Error {
GLib.Error e = reader.get_error ();
reader.end_member ();
throw new Error.PROTOCOL ("%s", e.message);
}
public uint count_elements () throws Error {
int n = reader.count_elements ();
if (n == -1)
throw_array_access_error ();
return n;
}
public unowned ObjectReader read_element (uint index) throws Error {
if (!reader.read_element (index)) {
GLib.Error e = reader.get_error ();
reader.end_element ();
throw new Error.PROTOCOL ("%s", e.message);
}
return this;
}
public unowned ObjectReader end_element () throws Error {
reader.end_element ();
return this;
}
[NoReturn]
private void throw_array_access_error () throws Error {
GLib.Error e = reader.get_error ();
reader.end_element ();
throw new Error.PROTOCOL ("%s", e.message);
}
public bool get_bool_value () throws Error {
bool v = reader.get_boolean_value ();
if (!v)
maybe_throw_value_access_error ();
return v;
}
public uint8 get_uint8_value () throws Error {
int64 v = get_int64_value ();
if (v < 0 || v > uint8.MAX)
throw new Error.PROTOCOL ("Invalid uint8");
return (uint8) v;
}
public uint16 get_uint16_value () throws Error {
int64 v = get_int64_value ();
if (v < 0 || v > uint16.MAX)
throw new Error.PROTOCOL ("Invalid uint16");
return (uint16) v;
}
public int64 get_int64_value () throws Error {
int64 v = reader.get_int_value ();
if (v == 0)
maybe_throw_value_access_error ();
return v;
}
public uint64 get_uint64_value () throws Error {
int64 v = get_int64_value ();
if (v < 0)
throw new Error.PROTOCOL ("Invalid uint64");
return v;
}
public Bytes get_data_value () throws Error {
return new Bytes (Base64.decode (get_string_value ()));
}
public unowned string get_string_value () throws Error {
unowned string? v = reader.get_string_value ();
if (v == null)
maybe_throw_value_access_error ();
return v;
}
public unowned string get_uuid_value () throws Error {
return get_string_value ();
}
private void maybe_throw_value_access_error () throws Error {
GLib.Error? e = reader.get_error ();
if (e == null)
return;
reader.end_member ();
throw new Error.PROTOCOL ("%s", e.message);
}
}
}
+260
View File
@@ -0,0 +1,260 @@
namespace Frida {
public class XpcClient : Object {
public signal void message (Darwin.Xpc.Object obj);
public State state {
get {
return _state;
}
}
public Darwin.Xpc.Connection connection {
get;
construct;
}
public Darwin.GCD.DispatchQueue queue {
get;
construct;
}
private State _state;
private string? close_reason;
private MainContext main_context;
public enum State {
OPEN,
CLOSING,
CLOSED,
}
public static XpcClient make_for_mach_service (string name, Darwin.GCD.DispatchQueue queue) {
return new XpcClient (Darwin.Xpc.Connection.create_mach_service (name, queue, 0), queue);
}
public XpcClient (Darwin.Xpc.Connection connection, Darwin.GCD.DispatchQueue queue) {
Object (connection: connection, queue: queue);
}
construct {
main_context = MainContext.ref_thread_default ();
connection.set_event_handler (on_event);
connection.activate ();
}
public override void dispose () {
if (close_reason != null) {
change_state (CLOSED);
} else {
change_state (CLOSING);
this.ref ();
connection.cancel ();
}
base.dispose ();
}
public async Darwin.Xpc.Object request (Darwin.Xpc.Object message, Cancellable? cancellable) throws Error, IOError {
Darwin.Xpc.Object? reply = null;
connection.send_message_with_reply (message, queue, r => {
schedule_on_frida_thread (() => {
reply = r;
request.callback ();
return Source.REMOVE;
});
});
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (() => {
connection.cancel ();
return Source.REMOVE;
});
cancel_source.attach (main_context);
yield;
cancel_source.destroy ();
if (reply.type == Darwin.Xpc.Error.TYPE) {
var e = (Darwin.Xpc.Error) reply;
throw new Error.NOT_SUPPORTED ("%s", e.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION));
}
return reply;
}
public void post (Darwin.Xpc.Object message) throws Error, IOError {
connection.send_message (message);
}
private void change_state (State new_state) {
_state = new_state;
notify_property ("state");
}
private void on_event (Darwin.Xpc.Object obj) {
schedule_on_frida_thread (() => {
if (obj.type == Darwin.Xpc.Error.TYPE) {
var e = (Darwin.Xpc.Error) obj;
close_reason = e.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION);
switch (state) {
case OPEN:
change_state (CLOSED);
break;
case CLOSING:
change_state (CLOSED);
unref ();
break;
case CLOSED:
assert_not_reached ();
}
} else {
message (obj);
}
return Source.REMOVE;
});
}
private void schedule_on_frida_thread (owned SourceFunc function) {
var source = new IdleSource ();
source.set_callback ((owned) function);
source.attach (main_context);
}
}
public class XpcObjectReader {
public Darwin.Xpc.Object root_object {
get {
return scopes.peek_head ().object;
}
}
public Darwin.Xpc.Object current_object {
get {
return scopes.peek_tail ().object;
}
}
private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();
public XpcObjectReader (Darwin.Xpc.Object obj) {
push_scope (obj);
}
public bool has_member (string name) throws Error {
return peek_scope ().get_dictionary ().get_value (name) != null;
}
public bool try_read_member (string name) throws Error {
var scope = peek_scope ();
var dict = scope.get_dictionary ();
var val = dict.get_value (name);
if (val == null)
return false;
push_scope (val);
return true;
}
public unowned XpcObjectReader read_member (string name) throws Error {
var scope = peek_scope ();
var dict = scope.get_dictionary ();
var val = dict.get_value (name);
if (val == null)
throw new Error.PROTOCOL ("Key '%s' not found in dictionary: %s", name, scope.object.to_string ());
push_scope (val);
return this;
}
public unowned XpcObjectReader end_member () {
pop_scope ();
return this;
}
public size_t count_elements () throws Error {
return peek_scope ().get_array ().count;
}
public unowned XpcObjectReader read_element (size_t index) throws Error {
push_scope (peek_scope ().get_array ().get_value (index));
return this;
}
public unowned XpcObjectReader end_element () throws Error {
pop_scope ();
return this;
}
public bool get_bool_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.Bool> (Darwin.Xpc.Bool.TYPE).get_value ();
}
public int64 get_int64_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.Int64> (Darwin.Xpc.Int64.TYPE).get_value ();
}
public uint64 get_uint64_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.UInt64> (Darwin.Xpc.UInt64.TYPE).get_value ();
}
public unowned string get_string_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.String> (Darwin.Xpc.String.TYPE).get_string_ptr ();
}
public unowned uint8[] get_uuid_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.Uuid> (Darwin.Xpc.Uuid.TYPE).get_bytes ()[:16];
}
public unowned string get_error_description () throws Error {
var error = peek_scope ().get_object<Darwin.Xpc.Error> (Darwin.Xpc.Error.TYPE);
return error.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION);
}
public unowned Darwin.Xpc.Object get_object_value (Darwin.Xpc.Type expected_type) throws Error {
return peek_scope ().get_object<Darwin.Xpc.Object> (expected_type);
}
private void push_scope (Darwin.Xpc.Object obj) {
scopes.offer_tail (new Scope (obj));
}
private Scope peek_scope () {
return scopes.peek_tail ();
}
private Scope pop_scope () {
return scopes.poll_tail ();
}
private class Scope {
public Darwin.Xpc.Object object;
private unowned Darwin.Xpc.Type type;
public Scope (Darwin.Xpc.Object obj) {
object = obj;
type = obj.type;
}
public unowned T get_object<T> (Darwin.Xpc.Type expected_type) throws Error {
if (type != expected_type)
throw new Error.PROTOCOL ("Expected type '%s', got '%s'", expected_type.name, type.name);
return object;
}
public unowned Darwin.Xpc.Array get_array () throws Error {
return get_object<Darwin.Xpc.Array> (Darwin.Xpc.Array.TYPE);
}
public unowned Darwin.Xpc.Dictionary get_dictionary () throws Error {
return get_object<Darwin.Xpc.Dictionary> (Darwin.Xpc.Dictionary.TYPE);
}
}
}
}
+67 -13
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);
}
}
@@ -1497,7 +1496,8 @@ namespace Frida.Gadget {
var endpoint_params = new EndpointParameters (interaction.address, interaction.port,
parse_certificate (interaction.certificate, location), interaction.origin, auth_service, asset_root);
service = new WebService (endpoint_params, CONTROL, interaction.on_port_conflict);
service = new WebService (endpoint_params, WebServiceFlavor.CONTROL, interaction.on_port_conflict,
new TunnelInterfaceObserver ());
service.incoming.connect (on_incoming_connection);
yield service.start (io_cancellable);
}
@@ -2075,6 +2075,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 ());
}
+6 -1
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'
@@ -60,7 +65,7 @@ raw_gadget = shared_library('frida-gadget-raw', gadget_sources,
link_depends: extra_link_depends,
vs_module_defs: 'frida-gadget.def',
override_options: ['b_asneeded=true'],
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep, gumjs_dep, base_dep, tls_provider_dep, payload_dep] + platform_deps,
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep, gumjs_dep, base_dep, netif_dep, tls_provider_dep, payload_dep] + platform_deps,
)
operations = [
+1
View File
@@ -2,6 +2,7 @@ if host_os == 'android'
subdir('selinux')
endif
subdir('base')
subdir('netif')
subdir('pipe')
subdir('payload')
if have_local_backend
+28
View File
@@ -0,0 +1,28 @@
netif_sources = [
'tunnel-interface-observer.vala',
]
extra_vala_args = []
extra_link_args = []
if host_os in ['ios', 'tvos']
extra_vala_args += [
'--pkg=corefoundation',
'--pkg=darwin-gcd',
'--pkg=darwin-systemconfiguration',
]
extra_link_args += '-Wl,-framework,SystemConfiguration'
endif
netif = static_library('frida-netif', netif_sources,
c_args: frida_component_cflags,
vala_args: [base_vala_args, extra_vala_args],
dependencies: [base_dep, gee_dep],
)
netif_dep = declare_dependency(
include_directories: include_directories('.'),
link_args: extra_link_args,
link_with: netif,
dependencies: base_dep,
)
+99
View File
@@ -0,0 +1,99 @@
public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
#if IOS || TVOS
private Gee.Map<string, DynamicInterface> interfaces = new Gee.HashMap<string, DynamicInterface> ();
private Darwin.SystemConfiguration.DynamicStore? store;
private Darwin.GCD.DispatchQueue event_queue =
new Darwin.GCD.DispatchQueue ("re.frida.endpoint-enumerator", Darwin.GCD.DispatchQueueAttr.SERIAL);
private MainContext? main_context;
public override void dispose () {
if (store != null) {
store.set_dispatch_queue (null);
store = null;
ref ();
event_queue.dispatch_async (do_dispose);
}
base.dispose ();
}
private void do_dispose () {
unref ();
}
public void start () {
main_context = MainContext.ref_thread_default ();
Darwin.SystemConfiguration.DynamicStoreContext context = { 0, };
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);
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,
CoreFoundation.Array changed_keys, void * info) {
unowned TunnelInterfaceObserver enumerator = (TunnelInterfaceObserver) info;
enumerator.on_interfaces_changed (changed_keys);
}
private void on_interfaces_changed (CoreFoundation.Array changed_keys) {
schedule_on_frida_thread (() => {
if (store != null)
handle_interface_changes (changed_keys);
return Source.REMOVE;
});
}
private void handle_interface_changes (CoreFoundation.Array changed_keys) {
var addresses_str = CoreFoundation.String.make ("Addresses");
foreach (var key in CFArray.wrap<CoreFoundation.String> (changed_keys)) {
string name = key.to_string ().split ("/")[3];
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");
bool is_tunnel = is_reserved_ipv6_range && str.has_suffix ("::1");
if (is_tunnel) {
address = new InetAddress.from_string (str);
break;
}
}
if (address != null && !interfaces.has_key (name)) {
var iface = new DynamicInterface (name, address);
interfaces[name] = iface;
interface_attached (iface);
}
} else {
DynamicInterface iface;
if (interfaces.unset (name, out iface))
interface_detached (iface);
}
}
}
private void schedule_on_frida_thread (owned SourceFunc function) {
var source = new IdleSource ();
source.set_callback ((owned) function);
source.attach (main_context);
}
#else
public void start () {
}
#endif
}
+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);
}
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
#include "pipe-glue.h"
#include "frida-tvos.h"
#include "pipe-glue.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
+105 -14
View File
@@ -92,6 +92,12 @@ else
host_arch = host_machine.cpu_family()
host_abi = host_arch
endif
if cc.sizeof('void *') == 8
host_cpu_mode = '64'
else
host_cpu_mode = '32'
endif
host_docks_mobile_devices = host_os in ['windows', 'macos', 'linux'] and host_cpu_mode == '64'
gumjs_archs = {
'x86': 'ia32',
'x86_64': 'x64',
@@ -156,12 +162,6 @@ if host_os_family == 'darwin'
endif
endif
if cc.sizeof('void *') == 8
host_cpu_mode = '64'
else
host_cpu_mode = '32'
endif
if host_os_family == 'darwin' and host_arch == 'arm64'
host_is_modern = host_abi == 'arm64e'
else
@@ -347,15 +347,35 @@ 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').allowed()
have_droidy_backend = get_option('droidy_backend').allowed()
have_fruity_backend = get_option('fruity_backend') \
.disable_auto_if(not host_docks_mobile_devices) \
.allowed()
have_droidy_backend = get_option('droidy_backend') \
.disable_auto_if(not host_docks_mobile_devices) \
.allowed()
have_socket_backend = get_option('socket_backend').allowed()
have_barebone_backend = quickjs_dep.found()
have_barebone_backend = get_option('barebone_backend') \
.disable_if(not quickjs_dep.found()) \
.disable_auto_if(not host_docks_mobile_devices) \
.allowed()
have_compiler_backend = get_option('compiler_backend') \
.disable_if(not have_local_backend, error_message: 'compiler backend requires the local backend for script execution') \
.disable_auto_if(host_os == 'watchos') \
.allowed()
if get_option('compiler_snapshot').auto()
# Generating our snapshot with V8's CPU simulator takes a very long time.
generate_compiler_snapshot = meson.can_run_host_binaries()
else
generate_compiler_snapshot = get_option('compiler_snapshot').enabled()
endif
build_gadget = get_option('gadget') \
.disable_auto_if(meson.is_subproject()) \
@@ -413,7 +433,7 @@ else
endif
uninstalled_girdir = meson.current_build_dir() / 'src'
vapidir = get_option('datadir') / 'vala' / 'vapi'
vapidir = get_option('prefix') / get_option('datadir') / 'vala' / 'vapi'
gum_vala_args = [
'--pkg=frida-gum-1.0',
@@ -437,6 +457,43 @@ backend_deps_private = []
backend_reqs_private = []
backend_libs_private = []
openssl_options = [
'cli=disabled',
]
if host_os_family == 'windows' and cc.get_argument_syntax() != 'msvc'
openssl_options += 'asm=disabled'
endif
openssl_dep = dependency('openssl',
required: have_fruity_backend or get_option('connectivity').enabled(),
modules: cc.get_argument_syntax() == 'msvc' ? ['OpenSSL::SSL'] : [],
default_options: openssl_options,
)
if have_fruity_backend
foreach dep : ['libnghttp2', 'libngtcp2', 'libngtcp2_crypto_quictls', 'libusb-1.0']
backend_deps_private += dependency(dep)
backend_reqs_private += dep
endforeach
lwip_options = [
'ipv4=disabled',
'ipv6=enabled',
'dns=disabled',
'arp=disabled',
'ethernet=enabled',
'tcp_mss=1360',
'tcp_snd_buf=65535',
'tcp_wnd=65535',
]
if optimize_for_prod
lwip_options += 'lwip_debug=disabled'
else
lwip_options += 'lwip_debug=enabled'
endif
backend_deps_private += dependency('lwip', default_options: lwip_options)
backend_reqs_private += 'lwip'
endif
if quickjs_dep.found()
backend_deps_private += quickjs_dep
backend_reqs_private += 'quickjs'
@@ -456,8 +513,19 @@ else
termux_elf_cleaner = []
endif
tls_provider_dep = dependency('gioopenssl', required: get_option('connectivity'))
tls_provider_dep = dependency('gioopenssl', required: get_option('connectivity'), default_options: [
'gnutls=disabled',
'openssl=enabled',
'libproxy=disabled',
'gnome_proxy=disabled',
'tests=false',
])
tls_provider_vala_args = []
if tls_provider_dep.found()
tls_provider_vala_args += [
'--pkg=gioopenssl',
'--vapidir=' + tls_provider_dep.get_variable('vapidir'),
]
cdata.set('HAVE_GIOOPENSSL', 1)
vala_flags += ['--define=HAVE_GIOOPENSSL']
backend_deps_private += tls_provider_dep
@@ -473,7 +541,6 @@ nice_dep = dependency('nice', required: get_option('connectivity'), default_opti
'introspection=disabled',
])
if nice_dep.found()
openssl_dep = dependency('openssl')
usrsctp_dep = dependency('usrsctp', default_options: [
'sctp_inet=false',
'sctp_inet6=false',
@@ -489,7 +556,6 @@ if nice_dep.found()
cdata.set('HAVE_SCONN_LEN', 1)
endif
else
openssl_dep = []
usrsctp_dep = []
endif
@@ -627,8 +693,9 @@ if cc.get_id() == 'gcc' and cc.compiles('#if __GNUC__ >= 5\n#error NOTNEEDED\n#e
add_project_arguments('-w', language: 'c')
endif
vapidir = meson.current_source_dir() / 'vapi'
vala_flags += [
'--vapidir=' + meson.current_source_dir() / 'vapi',
'--vapidir=' + vapidir,
'--pkg', 'config',
]
@@ -641,11 +708,13 @@ if host_abi != host_arch
vala_flags += ['--define=' + host_abi.to_upper()]
endif
add_project_arguments(vala_flags, language: 'vala')
add_project_arguments('-I' + vapidir, language: 'c')
native_vala_flags = [
'--vapidir=' + meson.current_source_dir() / 'vapi',
]
add_project_arguments(native_vala_flags, language: 'vala', native: true)
add_project_arguments('-I' + vapidir, language: 'c', native: true)
subdir('compat')
subdir('tools')
@@ -664,9 +733,31 @@ if build_tests
subdir('tests')
endif
summary(
{
'local': have_local_backend,
'fruity': have_fruity_backend,
'droidy': have_droidy_backend,
'socket': have_socket_backend,
'barebone': have_barebone_backend,
'compiler': have_compiler_backend,
},
section: 'Backends',
bool_yn: true,
)
summary(
{
'compat': compat_summary,
'assets': get_option('assets'),
'mapper': have_mapper,
'connectivity': tls_provider_dep.found(),
'compiler_snapshot': generate_compiler_snapshot,
'gadget': build_gadget,
'server': build_server,
'portal': build_portal,
'inject': build_inject,
'tests': build_tests,
},
section: 'Features',
bool_yn: true,
)
+15 -13
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',
@@ -101,16 +113,6 @@ option('agent_emulated_legacy',
description: 'Prebuilt agent to embed for use on legacy emulated targets'
)
option('agent_dbghelp_prefix',
type: 'string',
description: 'Location of DbgHelp binaries to embed alongside agent (Windows only)'
)
option('agent_symsrv_prefix',
type: 'string',
description: 'Location of SymSrv binaries to embed alongside agent (Windows only)'
)
option('gadget',
type: 'feature',
value: 'auto',
@@ -137,7 +139,7 @@ option('inject',
option('connectivity',
type: 'feature',
value: 'auto',
value: 'enabled',
description: 'Internet connectivity features (TLS and ICE support)'
)
+1 -1
View File
@@ -20,7 +20,7 @@ if tls_provider_dep.found()
endif
raw_portal = executable('frida-portal-raw', portal_sources,
vala_args: [core_vala_args, '--pkg=posix'],
vala_args: [core_vala_args, tls_provider_vala_args, '--pkg=posix'],
link_args: extra_link_args,
link_depends: extra_link_depends,
dependencies: [core_dep] + extra_deps,
+1 -1
Submodule releng updated: 56b262207c...8870e5861b.
+4 -2
View File
@@ -327,8 +327,10 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
if stripped_line == "}":
ignoring = False
else:
if stripped_line.startswith("public abstract") or stripped_line.startswith("public class Promise") \
or stripped_line.startswith("public interface Future"):
if stripped_line.startswith("public abstract") \
or stripped_line.startswith("public class Promise") \
or stripped_line.startswith("public interface Future") \
or stripped_line.startswith("public class CF"):
ignoring = True
elif stripped_line.startswith("public enum") or stripped_line.startswith("public errordomain"):
name = re.match(r"^public (?:enum|errordomain) (\w+) ", stripped_line).group(1)
+15 -15
View File
@@ -90,17 +90,25 @@ else
endif
uninstalled_vapidir = uninstalled_incdir
uninstalled_variables = {
'frida_includedir': uninstalled_incdir,
'frida_girdir': uninstalled_girdir,
'frida_vapidir': uninstalled_vapidir,
}
installed_variables = {
'frida_includedir': '${includedir}',
'frida_girdir': girdir,
'frida_vapidir': vapidir,
}
core_public_dep = declare_dependency(
sources: [core_public_h, core_public_gir, core_public_vapi_stamp, core_public_extra_headers],
include_directories: include_directories('.'),
link_args: backend_libs_private,
link_with: core_public_lib,
dependencies: core_deps,
variables: {
'frida_includedir': uninstalled_incdir,
'frida_girdir': uninstalled_girdir,
'frida_vapidir': uninstalled_vapidir,
},
variables: uninstalled_variables,
)
pkg = import('pkgconfig')
@@ -115,16 +123,8 @@ pkg.generate(
libraries: core_public_lib,
libraries_private: backend_libs_private,
extra_cflags: ['-I${frida_includedir}'],
variables: {
'frida_includedir': '${includedir}',
'frida_girdir': girdir,
'frida_vapidir': vapidir,
},
uninstalled_variables: {
'frida_includedir': uninstalled_incdir,
'frida_girdir': uninstalled_girdir,
'frida_vapidir': uninstalled_vapidir,
},
variables: installed_variables,
uninstalled_variables: uninstalled_variables,
)
meson.override_dependency(f'frida-core-@api_version@', core_public_dep)
+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 ();
+2 -2
View File
@@ -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) {
+1 -7
View File
@@ -3,13 +3,7 @@ compiler_sources = files(
)
if have_compiler_backend
if get_option('compiler_snapshot').auto()
# Generating our snapshot with V8's CPU simulator takes a very long time.
use_snapshot = meson.can_run_host_binaries()
else
use_snapshot = get_option('compiler_snapshot').enabled()
endif
compiler_mksnapshot = use_snapshot ? v8_mksnapshot : ''
compiler_mksnapshot = generate_compiler_snapshot ? v8_mksnapshot : ''
compiler_agent = custom_target('frida-compiler-agent',
input: [
+9 -8
View File
@@ -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.7.1",
"@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.7.1",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.1.tgz",
"integrity": "sha512-++7xogrG/jWdgC2gEeK8Q2tVTcycWXsFHhGaEa/TfY8u11UTUsn9dF+nr3Lo1rUnFGZssKDhqV4vsHOHgsWiLQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/glob": {
"version": "8.0.0",
@@ -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.7.1",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.1.tgz",
"integrity": "sha512-++7xogrG/jWdgC2gEeK8Q2tVTcycWXsFHhGaEa/TfY8u11UTUsn9dF+nr3Lo1rUnFGZssKDhqV4vsHOHgsWiLQ==",
"dev": true
},
"@types/glob": {
+1 -1
View File
@@ -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.7.1",
"@types/glob": "^8.0.0",
"@types/node": "18.7.23",
"glob": "^8.0.3",
+272 -151
View File
@@ -19,21 +19,18 @@ namespace Frida {
private State state = STOPPED;
private WebService service;
private Gee.Map<DBusConnection, Peer> peers = new Gee.HashMap<DBusConnection, Peer> ();
private ConnectionHandler main_handler;
private Gee.Map<string, ConnectionHandler> dynamic_interface_handlers = new Gee.HashMap<string, ConnectionHandler> ();
private Gee.Set<ControlChannel> spawn_gaters = new Gee.HashSet<ControlChannel> ();
private Gee.Map<uint, PendingSpawn> pending_spawn = new Gee.HashMap<uint, PendingSpawn> ();
private Gee.Map<AgentSessionId?, AgentSessionEntry> sessions =
new Gee.HashMap<AgentSessionId?, AgentSessionEntry> (AgentSessionId.hash, AgentSessionId.equal);
private SocketService broker_service = new SocketService ();
#if !WINDOWS
private uint16 broker_port = 0;
#endif
private Gee.Map<string, Transport> transports = new Gee.HashMap<string, Transport> ();
private Cancellable io_cancellable = new Cancellable ();
private MainContext? main_context;
private enum State {
STOPPED,
STARTING,
@@ -89,10 +86,12 @@ namespace Frida {
host_session.agent_session_detached.connect (on_agent_session_detached);
host_session.uninjected.connect (notify_uninjected);
service = new WebService (endpoint_params, CONTROL);
service.incoming.connect (on_server_connection);
var iface_observer = new TunnelInterfaceObserver ();
iface_observer.interface_detached.connect (on_interface_detached);
broker_service.incoming.connect (on_broker_service_connection);
service = new WebService (endpoint_params, WebServiceFlavor.CONTROL, PortConflictBehavior.FAIL, iface_observer);
main_handler = new ConnectionHandler (this, null);
}
public async void start (Cancellable? cancellable = null) throws Error, IOError {
@@ -100,6 +99,10 @@ namespace Frida {
throw new Error.INVALID_OPERATION ("Invalid operation");
state = STARTING;
main_context = MainContext.ref_thread_default ();
service.incoming.connect (on_server_connection);
try {
yield service.start (cancellable);
@@ -131,21 +134,17 @@ namespace Frida {
throw new Error.INVALID_OPERATION ("Invalid operation");
state = STOPPING;
broker_service.stop ();
service.stop ();
service.incoming.disconnect (on_server_connection);
io_cancellable.cancel ();
transports.clear ();
service.stop ();
foreach (var peer in peers.values.to_array ()) {
try {
yield peer.close ();
} catch (IOError e) {
assert_not_reached ();
}
}
peers.clear ();
foreach (var handler in dynamic_interface_handlers.values.to_array ())
yield handler.close (cancellable);
dynamic_interface_handlers.clear ();
yield main_handler.close (cancellable);
var base_host_session = host_session as BaseDBusHostSession;
if (base_host_session != null)
@@ -175,7 +174,7 @@ namespace Frida {
}
}
private void on_server_connection (IOStream connection, SocketAddress remote_address) {
private void on_server_connection (IOStream connection, SocketAddress remote_address, DynamicInterface? dynamic_iface) {
#if IOS || TVOS
/*
* We defer the launchd injection until the first connection is established in order
@@ -186,50 +185,28 @@ namespace Frida {
darwin_host_session.activate_crash_reporter_integration ();
#endif
handle_server_connection.begin (connection);
ConnectionHandler handler;
unowned string iface_name = dynamic_iface?.name;
if (iface_name != null) {
handler = dynamic_interface_handlers[iface_name];
if (handler == null) {
handler = new ConnectionHandler (this, dynamic_iface);
dynamic_interface_handlers[iface_name] = handler;
}
} else {
handler = main_handler;
}
handler.handle_server_connection.begin (connection);
}
private async void handle_server_connection (IOStream raw_connection) throws GLib.Error {
var connection = yield new DBusConnection (raw_connection, null, DELAY_MESSAGE_PROCESSING, null, io_cancellable);
connection.on_closed.connect (on_connection_closed);
Peer peer;
AuthenticationService? auth_service = endpoint_params.auth_service;
if (auth_service != null)
peer = new AuthenticationChannel (this, connection, auth_service);
else
peer = setup_control_channel (connection);
peers[connection] = peer;
connection.start_message_processing ();
}
private void on_connection_closed (DBusConnection connection, bool remote_peer_vanished, GLib.Error? error) {
Peer peer;
if (peers.unset (connection, out peer))
peer.close.begin (io_cancellable);
}
private async void promote_authentication_channel (AuthenticationChannel channel) throws GLib.Error {
DBusConnection connection = channel.connection;
peers.unset (connection);
yield channel.close (io_cancellable);
peers[connection] = setup_control_channel (connection);
}
private void kick_authentication_channel (AuthenticationChannel channel) {
var source = new IdleSource ();
source.set_callback (() => {
channel.connection.close.begin (io_cancellable);
return false;
private void on_interface_detached (DynamicInterface iface) {
schedule_on_frida_thread (() => {
ConnectionHandler handler;
if (dynamic_interface_handlers.unset (iface.name, out handler))
handler.close.begin (io_cancellable);
return Source.REMOVE;
});
source.attach (MainContext.get_thread_default ());
}
private ControlChannel setup_control_channel (DBusConnection connection) {
return new ControlChannel (this, connection);
}
private async void teardown_control_channel (ControlChannel channel) {
@@ -258,8 +235,235 @@ namespace Frida {
}
}
private void schedule_on_frida_thread (owned SourceFunc function) {
assert (main_context != null);
var source = new IdleSource ();
source.set_callback ((owned) function);
source.attach (main_context);
}
private class ConnectionHandler : Object {
public weak ControlService parent {
get;
construct;
}
public DynamicInterface? dynamic_iface {
get;
construct;
}
public HostSession host_session {
get {
return parent.host_session;
}
}
private Gee.Map<DBusConnection, Peer> peers = new Gee.HashMap<DBusConnection, Peer> ();
private SocketService broker_service = new SocketService ();
#if !WINDOWS
private uint16 broker_port = 0;
#endif
private Gee.Map<string, Transport> transports = new Gee.HashMap<string, Transport> ();
private Cancellable io_cancellable = new Cancellable ();
public ConnectionHandler (ControlService parent, DynamicInterface? dynamic_iface) {
Object (parent: parent, dynamic_iface: dynamic_iface);
}
construct {
broker_service.incoming.connect (on_broker_service_connection);
}
public async void close (Cancellable? cancellable) throws IOError {
broker_service.incoming.disconnect (on_broker_service_connection);
io_cancellable.cancel ();
broker_service.stop ();
transports.clear ();
foreach (var peer in peers.values.to_array ())
yield peer.close (cancellable);
peers.clear ();
}
public Gee.Iterator<ControlChannel> all_control_channels () {
return (Gee.Iterator<ControlChannel>) peers.values.filter (peer => peer is ControlChannel);
}
public async void handle_server_connection (IOStream raw_connection) throws GLib.Error {
var connection = yield new DBusConnection (raw_connection, null, DELAY_MESSAGE_PROCESSING, null,
io_cancellable);
connection.on_closed.connect (on_connection_closed);
AuthenticationService? auth_service = parent.endpoint_params.auth_service;
peers[connection] = (auth_service != null)
? (Peer) new AuthenticationChannel (this, connection, auth_service)
: (Peer) new ControlChannel (this, connection);
connection.start_message_processing ();
}
private void on_connection_closed (DBusConnection connection, bool remote_peer_vanished, GLib.Error? error) {
Peer peer;
if (peers.unset (connection, out peer))
peer.close.begin (io_cancellable);
}
public async void promote_authentication_channel (AuthenticationChannel channel) throws GLib.Error {
DBusConnection connection = channel.connection;
peers.unset (connection);
yield channel.close (io_cancellable);
peers[connection] = new ControlChannel (this, connection);
}
public void kick_authentication_channel (AuthenticationChannel channel) {
var source = new IdleSource ();
source.set_callback (() => {
channel.connection.close.begin (io_cancellable);
return false;
});
source.attach (MainContext.get_thread_default ());
}
public async void teardown_control_channel (ControlChannel channel) {
yield parent.teardown_control_channel (channel);
}
public async void enable_spawn_gating (ControlChannel requester) throws GLib.Error {
yield parent.enable_spawn_gating (requester);
}
public async void disable_spawn_gating (ControlChannel requester) throws GLib.Error {
yield parent.disable_spawn_gating (requester);
}
public HostSpawnInfo[] enumerate_pending_spawn () {
return parent.enumerate_pending_spawn ();
}
public async void resume (uint pid, ControlChannel requester) throws GLib.Error {
yield parent.resume (pid, requester);
}
public async AgentSessionId attach (uint pid, HashTable<string, Variant> options, ControlChannel requester,
Cancellable? cancellable) throws Error, IOError {
return yield parent.attach (pid, options, requester, cancellable);
}
public async void reattach (AgentSessionId id, ControlChannel requester, Cancellable? cancellable)
throws Error, IOError {
yield parent.reattach (id, requester, cancellable);
}
public void open_tcp_transport (AgentSessionId id, Cancellable? cancellable, out uint16 port, out string token)
throws Error {
#if WINDOWS
throw new Error.NOT_SUPPORTED ("Not yet supported on Windows");
#else
var base_host_session = host_session as BaseDBusHostSession;
if (base_host_session == null)
throw new Error.NOT_SUPPORTED ("Not supported for remote host sessions");
if (!base_host_session.can_pass_file_descriptors_to_agent_session (id))
throw new Error.INVALID_ARGUMENT ("Not supported by this particular agent session");
if (broker_port == 0) {
try {
if (dynamic_iface != null) {
SocketAddress effective_address;
broker_service.add_address (
new InetSocketAddress (dynamic_iface.ip, 0),
STREAM,
TCP,
null,
out effective_address);
broker_port = ((InetSocketAddress) effective_address).get_port ();
} else {
broker_port = broker_service.add_any_inet_port (null);
}
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("Unable to listen: %s", e.message);
}
broker_service.start ();
}
string transport_id = Uuid.string_random ();
var expiry_source = new TimeoutSource.seconds (20);
expiry_source.set_callback (() => {
transports.unset (transport_id);
return false;
});
expiry_source.attach (MainContext.get_thread_default ());
transports[transport_id] = new Transport (id, expiry_source);
port = broker_port;
token = transport_id;
#endif
}
private bool on_broker_service_connection (SocketConnection connection, Object? source_object) {
handle_broker_connection.begin (connection);
return true;
}
private async void handle_broker_connection (SocketConnection connection) throws GLib.Error {
var socket = connection.socket;
if (socket.get_family () != UNIX)
Tcp.enable_nodelay (socket);
const size_t uuid_length = 36;
var raw_token = new uint8[uuid_length + 1];
size_t bytes_read;
yield connection.input_stream.read_all_async (raw_token[0:uuid_length], Priority.DEFAULT, io_cancellable,
out bytes_read);
unowned string token = (string) raw_token;
Transport transport;
if (!transports.unset (token, out transport))
return;
transport.expiry_source.destroy ();
#if !WINDOWS
AgentSessionId session_id = transport.session_id;
var base_host_session = host_session as BaseDBusHostSession;
if (base_host_session == null)
throw new Error.NOT_SUPPORTED ("Not supported for remote host sessions");
AgentSessionProvider provider = base_host_session.obtain_session_provider (session_id);
yield provider.migrate (session_id, socket, io_cancellable);
#endif
}
private class Transport {
public AgentSessionId session_id;
public Source expiry_source;
public Transport (AgentSessionId session_id, Source expiry_source) {
this.session_id = session_id;
this.expiry_source = expiry_source;
}
}
}
private Gee.Iterator<ControlChannel> all_control_channels () {
return (Gee.Iterator<ControlChannel>) peers.values.filter (peer => peer is ControlChannel);
var channels = new Gee.ArrayList<ControlChannel> ();
channels.add_all_iterator (main_handler.all_control_channels ());
foreach (var handler in dynamic_interface_handlers.values)
channels.add_all_iterator (handler.all_control_channels ());
return channels.iterator ();
}
private async void enable_spawn_gating (ControlChannel requester) throws GLib.Error {
@@ -446,85 +650,12 @@ namespace Frida {
sessions.unset (entry.id);
}
private void open_tcp_transport (AgentSessionId id, Cancellable? cancellable, out uint16 port,
out string token) throws Error {
#if WINDOWS
throw new Error.NOT_SUPPORTED ("Not yet supported on Windows");
#else
var base_host_session = host_session as BaseDBusHostSession;
if (base_host_session == null)
throw new Error.NOT_SUPPORTED ("Not supported for remote host sessions");
if (!base_host_session.can_pass_file_descriptors_to_agent_session (id))
throw new Error.INVALID_ARGUMENT ("Not supported by this particular agent session");
if (broker_port == 0) {
try {
broker_port = broker_service.add_any_inet_port (null);
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("Unable to listen: %s", e.message);
}
broker_service.start ();
}
string transport_id = Uuid.string_random ();
var expiry_source = new TimeoutSource.seconds (20);
expiry_source.set_callback (() => {
transports.unset (transport_id);
return false;
});
expiry_source.attach (MainContext.get_thread_default ());
transports[transport_id] = new Transport (id, expiry_source);
port = broker_port;
token = transport_id;
#endif
}
private bool on_broker_service_connection (SocketConnection connection, Object? source_object) {
handle_broker_connection.begin (connection);
return true;
}
private async void handle_broker_connection (SocketConnection connection) throws GLib.Error {
var socket = connection.socket;
if (socket.get_family () != UNIX)
Tcp.enable_nodelay (socket);
const size_t uuid_length = 36;
var raw_token = new uint8[uuid_length + 1];
size_t bytes_read;
yield connection.input_stream.read_all_async (raw_token[0:uuid_length], Priority.DEFAULT, io_cancellable,
out bytes_read);
unowned string token = (string) raw_token;
Transport transport;
if (!transports.unset (token, out transport))
return;
transport.expiry_source.destroy ();
#if !WINDOWS
AgentSessionId session_id = transport.session_id;
var base_host_session = host_session as BaseDBusHostSession;
if (base_host_session == null)
throw new Error.NOT_SUPPORTED ("Not supported for remote host sessions");
AgentSessionProvider provider = base_host_session.obtain_session_provider (session_id);
yield provider.migrate (session_id, socket, io_cancellable);
#endif
}
private interface Peer : Object {
public abstract async void close (Cancellable? cancellable = null) throws IOError;
}
private class AuthenticationChannel : Object, Peer, AuthenticationService {
public weak ControlService parent {
public weak ConnectionHandler parent {
get;
construct;
}
@@ -541,7 +672,7 @@ namespace Frida {
private Gee.Collection<uint> registrations = new Gee.ArrayList<uint> ();
public AuthenticationChannel (ControlService parent, DBusConnection connection, AuthenticationService service) {
public AuthenticationChannel (ConnectionHandler parent, DBusConnection connection, AuthenticationService service) {
Object (
parent: parent,
connection: connection,
@@ -581,7 +712,7 @@ namespace Frida {
}
private class ControlChannel : Object, Peer, HostSession, TransportBroker {
public weak ControlService parent {
public weak ConnectionHandler parent {
get;
construct;
}
@@ -599,7 +730,7 @@ namespace Frida {
private Gee.Set<uint> registrations = new Gee.HashSet<uint> ();
private TimeoutSource? ping_timer;
public ControlChannel (ControlService parent, DBusConnection connection) {
public ControlChannel (ConnectionHandler parent, DBusConnection connection) {
Object (parent: parent, connection: connection);
}
@@ -848,16 +979,6 @@ namespace Frida {
expiry_timer = null;
}
}
private class Transport {
public AgentSessionId session_id;
public Source expiry_source;
public Transport (AgentSessionId session_id, Source expiry_source) {
this.session_id = session_id;
this.expiry_source = expiry_source;
}
}
}
#else
public class ControlService : Object {
+7 -6
View File
@@ -933,27 +933,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) {
+83 -9
View File
@@ -183,6 +183,7 @@ struct _FridaSpawnInstance
GumAddress modern_entry_address;
/* V4+ */
GumAddress notify_objc_init;
GumAddress info_ptr_address;
/* V3- */
@@ -428,6 +429,8 @@ static void frida_agent_context_emit_mach_stub_code (FridaAgentContext * self, g
static void frida_agent_context_emit_pthread_stub_code (FridaAgentContext * self, guint8 * code, GumDarwinModuleResolver * resolver,
GumDarwinMapper * mapper);
static gboolean frida_convert_thread_state_for_task (mach_port_t task, thread_state_flavor_t flavor, gconstpointer in_state,
mach_msg_type_number_t in_state_count, gpointer out_state, mach_msg_type_number_t * out_state_count, GError ** error);
static mach_port_t frida_obtain_thread_port_for_thread_id (mach_port_t task, uint64_t thread_id);
static kern_return_t frida_get_thread_state (mach_port_t thread, thread_state_flavor_t flavor, gpointer state,
mach_msg_type_number_t * count);
@@ -2028,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;
@@ -2106,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);
}
@@ -2583,16 +2595,24 @@ static gboolean
frida_inject_instance_start_thread (FridaInjectInstance * self, GError ** error)
{
gboolean success = FALSE;
thread_state_t thread_state;
mach_msg_type_number_t thread_state_count;
kern_return_t kr;
const gchar * failed_operation;
FridaHelperContext * ctx = self->backend->context;
dispatch_source_t source;
kr = thread_create (self->task, &self->thread);
CHECK_MACH_RESULT (kr, ==, KERN_SUCCESS, "thread_create");
thread_state = g_alloca (self->thread_state_count * sizeof (natural_t));
thread_state_count = self->thread_state_count;
kr = frida_set_thread_state (self->thread, self->thread_state_flavor, self->thread_state_data, self->thread_state_count);
CHECK_MACH_RESULT (kr, ==, KERN_SUCCESS, "set_thread_state");
if (!frida_convert_thread_state_for_task (self->task, self->thread_state_flavor, self->thread_state_data, self->thread_state_count,
thread_state, &thread_state_count, error))
{
return FALSE;
}
kr = thread_create_running (self->task, self->thread_state_flavor, thread_state, thread_state_count, &self->thread);
CHECK_MACH_RESULT (kr, ==, KERN_SUCCESS, "thread_create");
source = dispatch_source_create (DISPATCH_SOURCE_TYPE_MACH_SEND, self->thread, DISPATCH_MACH_SEND_DEAD, ctx->dispatch_queue);
self->thread_monitor_source = source;
@@ -2601,9 +2621,6 @@ frida_inject_instance_start_thread (FridaInjectInstance * self, GError ** error)
dispatch_source_set_event_handler_f (source, frida_inject_instance_on_mach_thread_dead);
dispatch_resume (source);
kr = thread_resume (self->thread);
CHECK_MACH_RESULT (kr, ==, KERN_SUCCESS, "thread_resume");
success = TRUE;
goto beach;
@@ -3078,9 +3095,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
{
@@ -3346,6 +3369,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)
@@ -3360,7 +3384,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);
@@ -5072,6 +5100,52 @@ frida_agent_context_emit_arm64_pthread_stub_body (FridaAgentContext * self, Frid
#endif
static gboolean
frida_convert_thread_state_for_task (mach_port_t task, thread_state_flavor_t flavor, gconstpointer in_state,
mach_msg_type_number_t in_state_count, gpointer out_state, mach_msg_type_number_t * out_state_count,
GError ** error)
{
gboolean success = FALSE;
kern_return_t kr;
const gchar * failed_operation;
thread_act_array_t threads = NULL;
mach_msg_type_number_t thread_count = 0;
kr = task_threads (task, &threads, &thread_count);
CHECK_MACH_RESULT (kr, ==, KERN_SUCCESS, "task_threads");
kr = frida_convert_thread_state (threads[0], FRIDA_CONVERT_THREAD_STATE_OUT, flavor, in_state, in_state_count,
out_state, out_state_count);
CHECK_MACH_RESULT (kr, ==, KERN_SUCCESS, "thread_convert_thread_state");
success = TRUE;
goto beach;
mach_failure:
{
g_set_error (error,
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error while converting thread state for task (%s returned '%s')",
failed_operation, mach_error_string (kr));
goto beach;
}
beach:
{
mach_msg_type_number_t i;
if (threads != NULL)
{
for (i = 0; i != thread_count; i++)
mach_port_deallocate (mach_task_self (), threads[i]);
vm_deallocate (mach_task_self (), (vm_address_t) threads, thread_count * sizeof (thread_t));
}
return success;
}
}
static mach_port_t
frida_obtain_thread_port_for_thread_id (mach_port_t task, uint64_t thread_id)
{
+1
View File
@@ -1,4 +1,5 @@
devkit_outputs = [
'frida-core.gir',
'frida-core.h',
static_lib_prefix + 'frida-core' + static_lib_suffix,
'frida-core-example.c',
+4 -4
View File
@@ -86,7 +86,7 @@ namespace Frida {
}
}
public class DroidyHostSessionProvider : Object, HostSessionProvider, ChannelProvider {
public class DroidyHostSessionProvider : Object, HostSessionProvider, HostChannelProvider {
public string id {
get { return device_details.serial; }
}
@@ -162,7 +162,7 @@ namespace Frida {
agent_session_detached (id, reason, crash);
}
public async IOStream open_channel (string address, Cancellable? cancellable = null) throws Error, IOError {
public async IOStream open_channel (string address, Cancellable? cancellable) throws Error, IOError {
if (address.contains (":")) {
Droidy.Client client = null;
try {
@@ -188,7 +188,7 @@ namespace Frida {
construct;
}
public weak ChannelProvider channel_provider {
public weak HostChannelProvider channel_provider {
get;
construct;
}
@@ -216,7 +216,7 @@ namespace Frida {
private const double MIN_SERVER_CHECK_INTERVAL = 5.0;
private const string GADGET_APP_ID = "re.frida.Gadget";
public DroidyHostSession (Droidy.DeviceDetails device_details, ChannelProvider channel_provider) {
public DroidyHostSession (Droidy.DeviceDetails device_details, HostChannelProvider channel_provider) {
Object (
device_details: device_details,
channel_provider: channel_provider
+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 / f"dbghelp-{flavor}.dll", embedded_dbghelp)
else:
embedded_dbghelp.write_bytes(b"")
if agent_symsrv_prefix is not None:
shutil.copy(agent_symsrv_prefix / f"symsrv-{flavor}.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)
+133 -31
View File
@@ -242,7 +242,7 @@ namespace Frida {
opts["keepalive_interval"] = interval;
}
var device = new Device (this, id, name, HostSessionProviderKind.REMOTE, socket_device.provider, raw_options);
var device = new Device (this, socket_device.provider, id, name, raw_options);
devices.add (device);
added (device);
changed ();
@@ -358,7 +358,7 @@ namespace Frida {
}
private void on_provider_available (HostSessionProvider provider) {
var device = new Device (this, provider.id, provider.name, provider.kind, provider);
var device = new Device (this, provider);
devices.add (device);
foreach (var observer in on_device_added.to_array ())
@@ -483,13 +483,19 @@ namespace Frida {
public signal void lost ();
public string id {
get;
construct;
get {
if (_id != null)
return _id;
return provider.id;
}
}
public string name {
get;
construct;
get {
if (_name != null)
return _name;
return provider.name;
}
}
public Variant? icon {
@@ -498,8 +504,18 @@ namespace Frida {
}
public DeviceType dtype {
get;
construct;
get {
switch (provider.kind) {
case HostSessionProviderKind.LOCAL:
return DeviceType.LOCAL;
case HostSessionProviderKind.REMOTE:
return DeviceType.REMOTE;
case HostSessionProviderKind.USB:
return DeviceType.USB;
default:
assert_not_reached ();
}
}
}
public Bus bus {
@@ -518,7 +534,8 @@ namespace Frida {
construct;
}
public delegate bool ProcessPredicate (Process process);
private string? _id;
private string? _name;
private HostSessionOptions? host_session_options;
private Promise<HostSession>? host_session_request;
@@ -530,34 +547,21 @@ 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;
internal Device (DeviceManager? manager, string id, string name, HostSessionProviderKind kind, HostSessionProvider provider,
HostSessionOptions? options = null) {
DeviceType dtype;
switch (kind) {
case HostSessionProviderKind.LOCAL:
dtype = DeviceType.LOCAL;
break;
case HostSessionProviderKind.REMOTE:
dtype = DeviceType.REMOTE;
break;
case HostSessionProviderKind.USB:
dtype = DeviceType.USB;
break;
default:
assert_not_reached ();
}
public delegate bool ProcessPredicate (Process process);
internal Device (DeviceManager? manager, HostSessionProvider provider, string? id = null, string? name = null,
HostSessionOptions? options = null) {
Object (
id: id,
name: name,
icon: provider.icon,
dtype: dtype,
manager: manager,
provider: provider,
manager: manager
icon: provider.icon
);
_id = id;
_name = name;
host_session_options = options;
}
@@ -1228,7 +1232,7 @@ namespace Frida {
public async IOStream open_channel (string address, Cancellable? cancellable = null) throws Error, IOError {
check_open ();
var channel_provider = provider as ChannelProvider;
var channel_provider = provider as HostChannelProvider;
if (channel_provider == null)
throw new Error.NOT_SUPPORTED ("Channels are not supported by this device");
@@ -1249,6 +1253,38 @@ 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 service = yield service_provider.open_service (address, cancellable);
service.close.connect (on_service_closed);
services.add (service);
return service;
}
public Service open_service_sync (string address, Cancellable? cancellable = null) throws Error, IOError {
var task = create<OpenServiceTask> ();
task.address = address;
return task.execute (cancellable);
}
private class OpenServiceTask : DeviceTask<Service> {
public string address;
protected override async Service perform_operation () throws Error, IOError {
return yield parent.open_service (address, cancellable);
}
}
private void on_service_closed (Service service) {
services.remove (service);
}
public async void unpair (Cancellable? cancellable = null) throws Error, IOError {
check_open ();
@@ -1357,6 +1393,10 @@ 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 ();
@@ -1964,6 +2004,68 @@ namespace Frida {
}
}
public interface Service : Object {
public signal void close ();
public signal void message (Variant message);
public abstract bool is_closed ();
public abstract async void activate (Cancellable? cancellable = null) throws Error, IOError;
public void activate_sync (Cancellable? cancellable = null) throws Error, IOError {
create<ActivateTask> ().execute (cancellable);
}
private class ActivateTask : ServiceTask<void> {
protected override async void perform_operation () throws Error, IOError {
yield parent.activate (cancellable);
}
}
public abstract async void cancel (Cancellable? cancellable = null) throws IOError;
public void cancel_sync (Cancellable? cancellable = null) throws IOError {
try {
create<CancelTask> ().execute (cancellable);
} catch (Error e) {
assert_not_reached ();
}
}
private class CancelTask : ServiceTask<void> {
protected override async void perform_operation () throws IOError {
yield parent.cancel (cancellable);
}
}
public abstract async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError;
public Variant request_sync (Variant parameters, Cancellable? cancellable = null) throws Error, IOError {
var task = create<RequestTask> () as RequestTask;
task.parameters = parameters;
return task.execute (cancellable);
}
private class RequestTask : ServiceTask<Variant> {
public Variant parameters;
protected override async Variant perform_operation () throws Error, IOError {
return yield parent.request (parameters, cancellable);
}
}
private T create<T> () {
return Object.new (typeof (T), parent: this);
}
private abstract class ServiceTask<T> : AsyncTask<T> {
public weak Service parent {
get;
construct;
}
}
}
public class Session : Object, AgentMessageSink {
public signal void detached (SessionDetachReason reason, Crash? crash);
@@ -40,7 +40,8 @@ static const FridaFruityModel fruity_models[] =
};
void
_frida_fruity_host_session_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon, GError ** error)
_frida_fruity_usbmux_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
GError ** error)
{
const FridaFruityModel * model;
guint i;
+266
View File
@@ -0,0 +1,266 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public class LinuxFruitFinder : Object, FruitFinder {
public string? udid_from_iface (string ifname) throws Error {
var net = "/sys/class/net";
var directory = File.new_build_filename (net, ifname);
if (!directory.query_exists ())
return null;
try {
var info = directory.query_info ("standard::*", 0);
if (!info.get_is_symlink ())
return null;
var dev_path = Path.build_filename (net, info.get_symlink_target ());
var iface = File.new_build_filename (dev_path, "..", "..", "interface");
if (!iface.query_exists ())
return null;
var iface_stream = new DataInputStream (iface.read ());
string iface_name = iface_stream.read_line ();
if (iface_name != "NCM Control" && iface_name != "AppleUSBEthernet")
return null;
var serial = File.new_build_filename (dev_path, "..", "..", "..", "serial");
if (!serial.query_exists ())
return null;
var serial_stream = new DataInputStream (serial.read ());
return serial_stream.read_line ();
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("%s", e.message);
}
}
}
public class LinuxPairingBrowser : Object, PairingBrowser {
private Resolved.Manager? resolved;
private Source? timer;
private Cancellable io_cancellable = new Cancellable ();
private const size_t IF_NAMESIZE = 16;
public async void start (Cancellable? cancellable) throws IOError {
try {
var connection = yield GLib.Bus.get (BusType.SYSTEM, cancellable);
resolved = yield connection.get_proxy (Resolved.SERVICE_NAME, Resolved.SERVICE_PATH, DO_NOT_LOAD_PROPERTIES,
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) {
}
}
public async void stop (Cancellable? cancellable) throws IOError {
if (timer != null) {
timer.destroy ();
timer = null;
}
io_cancellable.cancel ();
}
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) {
if (candidate.ifa_name != ifname)
continue;
if (candidate.ifa_addr.sa_family != Posix.AF_INET6)
continue;
return (InetSocketAddress)
SocketAddress.from_native ((void *) candidate.ifa_addr, 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);
}
}
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;
}
[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 struct RRItem {
public int32 ifindex;
public uint16 klass;
public uint16 type;
public uint8[] data;
}
public struct SrvItem {
public uint16 priority;
public uint16 weight;
public uint16 port;
public string name;
public SrvAddress[] addresses;
public string canonical_name;
}
public struct SrvAddress {
public int32 ifindex;
public int32 family;
public uint8[] ip;
}
}
}
+585
View File
@@ -0,0 +1,585 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public sealed class MacOSCoreDeviceBackend : Object, Backend {
private Gee.Map<string, MacOSCoreDeviceTransport> transports = new Gee.HashMap<string, MacOSCoreDeviceTransport> ();
private Promise<bool> all_current_devices_listed = new Promise<bool> ();
private Promise<bool> browse_request = new Promise<bool> ();
private XpcClient? pairingd;
private Darwin.GCD.DispatchQueue queue =
new Darwin.GCD.DispatchQueue ("re.frida.fruity.remotepairing", Darwin.GCD.DispatchQueueAttr.SERIAL);
public async void start (Cancellable? cancellable) throws IOError {
pairingd = XpcClient.make_for_mach_service ("com.apple.CoreDevice.remotepairingd", queue);
pairingd.notify["state"].connect (on_state_changed);
pairingd.message.connect (on_message);
do_browse.begin ();
try {
yield all_current_devices_listed.future.wait_async (cancellable);
} catch (Error e) {
}
}
private async void do_browse () {
try {
var r = new PairingdRequest ("RemotePairing.BrowseRequest");
r.body.set_bool ("currentDevicesOnly", false);
yield pairingd.request (r.message, null);
browse_request.resolve (true);
} catch (GLib.Error e) {
browse_request.reject (e);
}
}
public async void stop (Cancellable? cancellable) throws IOError {
}
private void on_state_changed (Object obj, ParamSpec pspec) {
if (pairingd.state == CLOSED && !all_current_devices_listed.future.ready) {
all_current_devices_listed.reject (
new Error.TRANSPORT ("Connection closed while waiting for initial device list"));
}
}
private void on_message (Darwin.Xpc.Object obj) {
var reader = new XpcObjectReader (obj);
try {
reader.read_member ("mangledTypeName");
if (reader.get_string_value () == "RemotePairing.ServiceEvent") {
reader
.end_member ()
.read_member ("value");
if (reader.try_read_member ("deviceFound")) {
reader
.read_member ("_0")
.read_member ("deviceInfo");
var udid = reader.read_member ("udid").get_string_value ();
reader.end_member ();
if (transports.has_key (udid))
return;
var device_info = (Darwin.Xpc.Dictionary) reader.current_object;
var pairing_device = new XpcClient (device_info.create_connection ("endpoint"), queue);
on_device_found (udid, pairing_device, device_info);
} else if (reader.try_read_member ("allCurrentDevicesListed")) {
all_current_devices_listed.resolve (true);
}
}
} catch (Error e) {
}
}
private void on_device_found (string udid, XpcClient pairing_device, Darwin.Xpc.Dictionary device_info) throws Error {
var transport = new MacOSCoreDeviceTransport (udid, pairing_device, device_info);
transport.closed.connect (on_transport_closed);
transports[udid] = transport;
transport_attached (transport);
}
private void on_transport_closed (MacOSCoreDeviceTransport transport) {
transport_detached (transport);
transports.unset (transport.udid);
}
}
public sealed class MacOSCoreDeviceTransport : Object, Transport {
public signal void closed ();
public XpcClient pairing_device {
get;
construct;
}
public ConnectionType connection_type {
get {
return _connection_type;
}
}
public string udid {
get {
return _udid;
}
}
public string? name {
get {
return _name;
}
}
public Variant? icon {
get {
return null;
}
}
public UsbmuxDevice? usbmux_device {
get {
return null;
}
}
private ConnectionType _connection_type;
private string _udid;
private string _name;
private Promise<Tunnel>? tunnel_request;
private Cancellable io_cancellable = new Cancellable ();
public MacOSCoreDeviceTransport (string udid, XpcClient pairing_device, Darwin.Xpc.Dictionary device_info) throws Error {
Object (pairing_device: pairing_device);
_udid = udid;
update_device_info (device_info);
}
construct {
pairing_device.notify["state"].connect (on_state_changed);
pairing_device.message.connect (on_message);
}
private void update_device_info (Darwin.Xpc.Dictionary device_info) throws Error {
_connection_type = (device_info.get_value ("untrustedRSDDeviceInfo") != null)
? ConnectionType.USB
: ConnectionType.NETWORK;
var reader = new XpcObjectReader (device_info);
_name = reader.read_member ("name").get_string_value ();
reader.end_member ();
}
private void on_state_changed (Object obj, ParamSpec pspec) {
if (pairing_device.state == CLOSED) {
io_cancellable.cancel ();
closed ();
}
}
private void on_message (Darwin.Xpc.Object obj) {
var reader = new XpcObjectReader (obj);
try {
reader.read_member ("mangledTypeName");
if (reader.get_string_value () == "RemotePairing.ServiceEvent") {
reader
.end_member ()
.read_member ("value");
if (reader.try_read_member ("deviceFound")) {
reader
.read_member ("_0")
.read_member ("deviceInfo");
var device_info = (Darwin.Xpc.Dictionary)
reader.get_object_value (Darwin.Xpc.Dictionary.TYPE);
update_device_info (device_info);
} else if (reader.try_read_member ("tunnelUsageAssertionsInvalidated")) {
reader.read_member ("assertionIdentifiers");
var ids = new Gee.ArrayList<Bytes> ();
size_t n = reader.count_elements ();
for (size_t i = 0; i != n; i++) {
reader.read_element (i);
ids.add (new Bytes (reader.get_uuid_value ()));
reader.end_element ();
}
on_tunnel_usage_assertions_invalidated.begin (ids);
}
}
} catch (Error e) {
}
}
private async void on_tunnel_usage_assertions_invalidated (Gee.List<Bytes> ids) {
if (tunnel_request == null)
return;
try {
var tunnel = (MacOSTunnel) yield tunnel_request.future.wait_async (io_cancellable);
unowned uint8[] tunnel_assertion_id = tunnel.assertion_identifier.get_bytes ();
foreach (var id in ids) {
if (Memory.cmp (id.get_data (), tunnel_assertion_id, id.get_size ()) == 0) {
tunnel_request = null;
return;
}
}
} catch (GLib.Error e) {
}
}
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);
} catch (Error e) {
throw e;
} catch (IOError e) {
cancellable.set_error_if_cancelled ();
}
}
tunnel_request = new Promise<Tunnel> ();
try {
var tunnel = new MacOSTunnel (pairing_device);
yield tunnel.attach (cancellable);
tunnel_request.resolve (tunnel);
return tunnel;
} catch (GLib.Error e) {
tunnel_request.reject (e);
tunnel_request = null;
throw_api_error (e);
}
}
}
public sealed class MacOSTunnel : Object, Tunnel {
public DiscoveryService discovery {
get {
return _discovery;
}
}
public Darwin.Xpc.Uuid? assertion_identifier {
get {
return _assertion_identifier;
}
}
private XpcClient pairing_device;
private Darwin.Xpc.Uuid? _assertion_identifier;
private InetAddress? tunnel_device_address;
private DiscoveryService? _discovery;
public MacOSTunnel (XpcClient pairing_device) {
this.pairing_device = pairing_device;
}
public async void close (Cancellable? cancellable) throws IOError {
var r = new PairingdRequest ("RemotePairing.ReleaseAssertionRequest");
r.body.set_value ("assertionIdentifier", _assertion_identifier);
try {
yield pairing_device.request (r.message, cancellable);
} catch (Error e) {
}
}
public async void attach (Cancellable? cancellable) throws Error, IOError {
var r = new PairingdRequest ("RemotePairing.CreateAssertionCommand");
r.body.set_int64 ("flags", 0);
var response = yield pairing_device.request (r.message, cancellable);
var reader = new XpcObjectReader (response);
reader.read_member ("response");
_assertion_identifier = (Darwin.Xpc.Uuid) reader
.read_member ("assertionIdentifier")
.get_object_value (Darwin.Xpc.Uuid.TYPE);
reader.end_member ();
string tunnel_ip_address = reader
.read_member ("info")
.read_member ("tunnelIPAddress")
.get_string_value ();
tunnel_device_address = new InetAddress.from_string (tunnel_ip_address);
_discovery = yield locate_discovery_service (tunnel_device_address, cancellable);
}
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
SocketConnection connection;
try {
var service_address = new InetSocketAddress (tunnel_device_address, port);
var client = new SocketClient ();
connection = yield client.connect_async (service_address, cancellable);
} catch (GLib.Error e) {
if (e is IOError.CONNECTION_REFUSED)
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
throw new Error.TRANSPORT ("%s", e.message);
}
Tcp.enable_nodelay (connection.socket);
return connection;
}
private static async DiscoveryService locate_discovery_service (InetAddress tunnel_device_address, Cancellable? cancellable)
throws Error, IOError {
var main_context = MainContext.get_thread_default ();
var path_buf = new char[4096];
unowned string path = (string) path_buf;
uint delays[] = { 0, 50, 250 };
for (uint attempts = 0; attempts != delays.length; attempts++) {
uint delay = delays[attempts];
if (delay != 0) {
var timeout_source = new TimeoutSource (delay);
timeout_source.set_callback (locate_discovery_service.callback);
timeout_source.attach (main_context);
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (locate_discovery_service.callback);
cancel_source.attach (main_context);
yield;
cancel_source.destroy ();
timeout_source.destroy ();
if (cancellable.is_cancelled ())
break;
}
foreach (var item in XNU.query_active_tcp_connections ()) {
if (item.family != IPV6)
continue;
if (!item.foreign_address.equal (tunnel_device_address))
continue;
if (Darwin.XNU.proc_pidpath (item.effective_pid, path_buf) <= 0)
continue;
if (path != "/usr/libexec/remoted")
continue;
try {
var connectable = new InetSocketAddress (tunnel_device_address, item.foreign_port);
var sc = new SocketClient ();
SocketConnection connection = yield sc.connect_async (connectable, cancellable);
Tcp.enable_nodelay (connection.socket);
return yield DiscoveryService.open (connection, cancellable);
} catch (GLib.Error e) {
}
}
}
throw new Error.NOT_SUPPORTED ("Unable to detect RSD port");
}
}
private sealed class PairingdRequest {
public Darwin.Xpc.Dictionary message = new Darwin.Xpc.Dictionary ();
public Darwin.Xpc.Dictionary body = new Darwin.Xpc.Dictionary ();
public PairingdRequest (string name) {
message.set_string ("mangledTypeName", name);
message.set_value ("value", body);
}
}
namespace XNU {
public PcbList query_active_tcp_connections () {
size_t size = 0;
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", null, &size);
var pcbs = new uint8[size];
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", pcbs, &size);
return new PcbList (pcbs);
}
public class PcbList {
private uint8[] pcbs;
internal PcbList (owned uint8[] pcbs) {
this.pcbs = (owned) pcbs;
}
public Iterator iterator () {
return new Iterator (this);
}
public class Iterator {
private PcbList list;
private InetItem * cursor;
internal Iterator (PcbList list) {
this.list = list;
var gen = (Darwin.XNU.InetPcbGeneration *) list.pcbs;
cursor = (InetItem *) ((uint8 *) list.pcbs + gen->length);
}
public Item? next_value () {
InetPcb * pcb = null;
while (true) {
if (cursor->length == 24)
return null;
switch (cursor->kind) {
case InetItemKind.PCB:
pcb = (InetPcb *) cursor;
break;
case InetItemKind.SOCKET:
var item = new Item (*pcb, *((InetSocket *) cursor));
advance ();
return item;
}
advance ();
}
}
private void advance () {
uint32 l = cursor->length;
if (l % 8 != 0)
l += 8 - (l % 8);
cursor = (InetItem *) ((uint8 *) cursor + l);
}
}
public class Item {
public SocketFamily family {
get {
return ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
? SocketFamily.IPV6
: SocketFamily.IPV4;
}
}
public InetAddress local_address {
get {
if (cached_local_address == null)
cached_local_address = parse_address (pcb.local_address);
return cached_local_address;
}
}
public uint16 local_port {
get {
return uint16.from_big_endian (pcb.local_port);
}
}
public InetAddress foreign_address {
get {
if (cached_foreign_address == null)
cached_foreign_address = parse_address (pcb.foreign_address);
return cached_foreign_address;
}
}
public uint16 foreign_port {
get {
return uint16.from_big_endian (pcb.foreign_port);
}
}
public int32 effective_pid {
get {
return sock.effective_pid;
}
}
private InetPcb pcb;
private InetSocket sock;
private InetAddress? cached_local_address;
private InetAddress? cached_foreign_address;
public Item (InetPcb pcb, InetSocket sock) {
this.pcb = pcb;
this.sock = sock;
}
private InetAddress parse_address (uint8[] bytes) {
if ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
return new InetAddress.from_bytes (bytes, IPV6);
var addr = (Darwin.XNU.InetAddr4in6 *) bytes;
return new InetAddress.from_bytes ((uint8[]) &addr->addr4.s_addr, IPV4);
}
}
}
[SimpleType]
public struct InetItem {
public uint32 length;
public uint32 kind;
}
public enum InetItemKind {
SOCKET = 0x001,
PCB = 0x010,
}
[SimpleType]
public struct InetPcb {
public uint32 length;
public uint32 kind;
public uint64 inpp;
public uint16 foreign_port;
public uint16 local_port;
public uint32 per_protocol_pcb_low;
public uint32 per_protocol_pcb_high;
public uint32 generation_count_low;
public uint32 generation_count_high;
public int flags;
public uint32 flow;
public uint8 version_flag;
public uint8 ip_ttl;
public uint8 ip_protocol;
public uint8 padding;
public uint8 foreign_address[16];
public uint8 local_address[16];
public InetDepend4 depend4;
public InetDepend6 depend6;
public uint32 flowhash;
public uint32 flags2;
}
[SimpleType]
public struct InetSocket {
public uint32 length;
public uint32 kind;
public uint64 so;
public int16 type;
public uint16 options_low;
public uint16 options_high;
public int16 linger;
public int16 state;
public uint16 pcb[4];
public uint16 protocol_low;
public uint16 protocol_high;
public uint16 family_low;
public uint16 family_high;
public int16 qlen;
public int16 incqlen;
public int16 qlimit;
public int16 timeo;
public uint16 error;
public int32 pgid;
public uint32 oobmark;
public uint32 uid;
public int32 last_pid;
public int32 effective_pid;
public uint64 gencnt;
public uint32 flags;
public uint32 flags1;
public int32 usecount;
public int32 retaincnt;
public uint32 filter_flags;
}
[SimpleType]
public struct InetDepend4 {
public uint8 ip_tos;
}
[SimpleType]
public struct InetDepend6 {
public uint8 hlim;
public int checksum;
public uint16 interface_index;
public int16 hops;
}
}
}
@@ -3,7 +3,7 @@
#include "frida-base.h"
void
_frida_fruity_host_session_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
_frida_fruity_usbmux_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
GError ** error)
{
*name = g_strdup ("iOS Device");
@@ -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,10 +90,110 @@ 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_host_session_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
_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)
{
gboolean result = FALSE;
@@ -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,
}
}
+167 -152
View File
@@ -1,18 +1,18 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public class DeviceInfoService : Object, AsyncInitable {
public ChannelProvider channel_provider {
public HostChannelProvider channel_provider {
get;
construct;
}
private DTXChannel channel;
private DeviceInfoService (ChannelProvider channel_provider) {
private DeviceInfoService (HostChannelProvider channel_provider) {
Object (channel_provider: channel_provider);
}
public static async DeviceInfoService open (ChannelProvider channel_provider, Cancellable? cancellable = null)
public static async DeviceInfoService open (HostChannelProvider channel_provider, Cancellable? cancellable = null)
throws Error, IOError {
var service = new DeviceInfoService (channel_provider);
@@ -74,21 +74,53 @@ namespace Frida.Fruity {
return "/private" + name;
return name;
}
public class ProcessInfo : Object {
public uint pid {
get;
set;
}
public string name {
get;
set;
}
public string real_app_name {
get;
set;
}
public bool is_application {
get;
set;
}
public bool foreground_running {
get;
set;
}
public DateTime? start_date {
get;
set;
}
}
}
public class ApplicationListingService : Object, AsyncInitable {
public ChannelProvider channel_provider {
public HostChannelProvider channel_provider {
get;
construct;
}
private DTXChannel channel;
private ApplicationListingService (ChannelProvider channel_provider) {
private ApplicationListingService (HostChannelProvider channel_provider) {
Object (channel_provider: channel_provider);
}
public static async ApplicationListingService open (ChannelProvider channel_provider, Cancellable? cancellable = null)
public static async ApplicationListingService open (HostChannelProvider channel_provider, Cancellable? cancellable = null)
throws Error, IOError {
var service = new ApplicationListingService (channel_provider);
@@ -178,21 +210,115 @@ namespace Frida.Fruity {
return result;
}
public class ApplicationInfo : Object {
public ApplicationType app_type {
get;
set;
}
public string display_name {
get;
set;
}
public string bundle_identifier {
get;
set;
}
public string bundle_path {
get;
set;
}
public string? version {
get;
set;
}
public bool placeholder {
get;
set;
}
public bool restricted {
get;
set;
}
public string? executable_name {
get;
set;
}
public string[]? app_extension_uuids {
get;
set;
}
public string? plugin_uuid {
get;
set;
}
public string? plugin_identifier {
get;
set;
}
public string? container_bundle_identifier {
get;
set;
}
public string? container_bundle_path {
get;
set;
}
}
public enum ApplicationType {
SYSTEM = 1,
USER,
PLUGIN_KIT;
public static ApplicationType from_nick (string nick) throws Error {
return Marshal.enum_from_nick<ApplicationType> (nick);
}
public string to_nick () {
return Marshal.enum_to_nick<ApplicationType> (this);
}
internal static ApplicationType from_dtx (string type) {
if (type == "System")
return SYSTEM;
if (type == "User")
return USER;
if (type == "PluginKit")
return PLUGIN_KIT;
assert_not_reached ();
}
}
}
public class ProcessControlService : Object, AsyncInitable {
public ChannelProvider channel_provider {
public HostChannelProvider channel_provider {
get;
construct;
}
private DTXChannel channel;
private ProcessControlService (ChannelProvider channel_provider) {
private ProcessControlService (HostChannelProvider channel_provider) {
Object (channel_provider: channel_provider);
}
public static async ProcessControlService open (ChannelProvider channel_provider, Cancellable? cancellable = null)
public static async ProcessControlService open (HostChannelProvider channel_provider, Cancellable? cancellable = null)
throws Error, IOError {
var service = new ProcessControlService (channel_provider);
@@ -220,132 +346,6 @@ namespace Frida.Fruity {
}
}
public class ProcessInfo : Object {
public uint pid {
get;
set;
}
public string name {
get;
set;
}
public string real_app_name {
get;
set;
}
public bool is_application {
get;
set;
}
public bool foreground_running {
get;
set;
}
public DateTime? start_date {
get;
set;
}
}
public class ApplicationInfo : Object {
public ApplicationType app_type {
get;
set;
}
public string display_name {
get;
set;
}
public string bundle_identifier {
get;
set;
}
public string bundle_path {
get;
set;
}
public string? version {
get;
set;
}
public bool placeholder {
get;
set;
}
public bool restricted {
get;
set;
}
public string? executable_name {
get;
set;
}
public string[]? app_extension_uuids {
get;
set;
}
public string? plugin_uuid {
get;
set;
}
public string? plugin_identifier {
get;
set;
}
public string? container_bundle_identifier {
get;
set;
}
public string? container_bundle_path {
get;
set;
}
}
public enum ApplicationType {
SYSTEM = 1,
USER,
PLUGIN_KIT;
public static ApplicationType from_nick (string nick) throws Error {
return Marshal.enum_from_nick<ApplicationType> (nick);
}
public string to_nick () {
return Marshal.enum_to_nick<ApplicationType> (this);
}
internal static ApplicationType from_dtx (string type) {
if (type == "System")
return SYSTEM;
if (type == "User")
return USER;
if (type == "PluginKit")
return PLUGIN_KIT;
assert_not_reached ();
}
}
public class DTXConnection : Object, DTXTransport {
public IOStream stream {
get;
@@ -363,7 +363,7 @@ namespace Frida.Fruity {
CLOSED
}
private static Gee.HashMap<ChannelProvider, Future<DTXConnection>> connections;
private static Gee.HashMap<HostChannelProvider, Future<DTXConnection>> connections;
private State _state = OPEN;
@@ -383,19 +383,21 @@ namespace Frida.Fruity {
private const uint32 DTX_FRAGMENT_MAGIC = 0x1f3d5b79U;
private const uint MAX_BUFFERED_COUNT = 100;
private const size_t MAX_BUFFERED_SIZE = 30 * 1024 * 1024;
private const size_t MAX_MESSAGE_SIZE = 1024 * 1024;
private const size_t MAX_MESSAGE_SIZE = 128 * 1024 * 1024;
private const size_t MAX_FRAGMENT_SIZE = 128 * 1024;
private const string REMOTESERVER_ENDPOINT_MODERN = "lockdown:com.apple.instruments.remoteserver.DVTSecureSocketProxy";
private const string REMOTESERVER_ENDPOINT_17PLUS = "lockdown:com.apple.instruments.dtservicehub";
private const string REMOTESERVER_ENDPOINT_14PLUS = "lockdown:com.apple.instruments.remoteserver.DVTSecureSocketProxy";
private const string REMOTESERVER_ENDPOINT_LEGACY = "lockdown:com.apple.instruments.remoteserver?tls=handshake-only";
private const string[] REMOTESERVER_ENDPOINT_CANDIDATES = {
REMOTESERVER_ENDPOINT_MODERN,
REMOTESERVER_ENDPOINT_17PLUS,
REMOTESERVER_ENDPOINT_14PLUS,
REMOTESERVER_ENDPOINT_LEGACY,
};
public static async DTXConnection obtain (ChannelProvider channel_provider, Cancellable? cancellable)
public static async DTXConnection obtain (HostChannelProvider channel_provider, Cancellable? cancellable)
throws Error, IOError {
if (connections == null)
connections = new Gee.HashMap<ChannelProvider, Future<DTXConnection>> ();
connections = new Gee.HashMap<HostChannelProvider, Future<DTXConnection>> ();
while (connections.has_key (channel_provider)) {
var future = connections[channel_provider];
@@ -442,7 +444,7 @@ namespace Frida.Fruity {
throw api_error;
}
public static async void close_all (ChannelProvider channel_provider, Cancellable? cancellable) throws IOError {
public static async void close_all (HostChannelProvider channel_provider, Cancellable? cancellable) throws IOError {
if (connections == null)
return;
@@ -502,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 ();
@@ -705,7 +715,9 @@ namespace Frida.Fruity {
int32 channel_code = message.channel_code;
bool is_notification = false;
if (message.type == RESULT && channel_code < 0) {
if (message.type == INVOKE) {
channel_code = -channel_code;
} else if (message.type == RESULT && channel_code < 0) {
channel_code = -channel_code;
is_notification = true;
}
@@ -866,7 +878,7 @@ namespace Frida.Fruity {
public class DTXChannel : Object {
public signal void invocation (string method_name, DTXArgumentList args, DTXMessageTransportFlags transport_flags);
public signal void notification (NSDictionary dict);
public signal void notification (NSObject obj);
public signal void barrier ();
public int32 code {
@@ -874,9 +886,9 @@ namespace Frida.Fruity {
construct;
}
public weak DTXTransport transport {
public weak DTXTransport? transport {
get;
construct;
set;
}
public State state {
@@ -899,7 +911,7 @@ namespace Frida.Fruity {
}
public override void dispose () {
transport.remove_channel (this);
close ();
base.dispose ();
}
@@ -911,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)
@@ -1016,10 +1033,8 @@ namespace Frida.Fruity {
}
internal void handle_notification (DTXMessage message) throws Error {
NSDictionary? dict = NSKeyedArchive.decode (message.payload_data) as NSDictionary;
if (dict == null)
throw new Error.PROTOCOL ("Malformed notification payload");
notification (dict);
var payload = NSKeyedArchive.decode (message.payload_data);
notification (payload);
}
internal void handle_barrier (DTXMessage message) throws Error {
@@ -1062,7 +1077,7 @@ namespace Frida.Fruity {
}
public class DTXArgumentList {
private Value[] elements;
public Value[] elements;
private DTXArgumentList (owned Value[] elements) {
this.elements = (owned) elements;
File diff suppressed because it is too large. Load diff
+2
View File
@@ -50,9 +50,11 @@ frida_fetch_dyld_symbols (char * output_buffer, const void * dyld_load_address)
const char * name = strings + sym->n_un.n_strx;
if (frida_str_contains (name, "libdyld_initialize") ||
frida_str_contains (name, "notifyObjCInit") ||
frida_str_contains (name, "restartWithDyldInCache") ||
frida_str_equals (name, "_gProcessInfo") ||
frida_str_equals (name, "__ZN5dyld412gProcessInfoE") ||
frida_str_equals (name, "__ZL12sProcessInfo") ||
frida_str_contains (name, "launchWithClosure") ||
frida_str_contains (name, "initializeMainExecutable") ||
frida_str_contains (name, "registerThreadHelpers") ||
+7 -7
View File
@@ -5,11 +5,15 @@ frida_listen (int rx_buffer_size, const FridaUploadApi * api)
{
uint8_t error_code;
int fd;
struct sockaddr_in addr;
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_ANY_INIT,
.sin6_port = 0,
};
socklen_t addr_len;
int res;
fd = api->socket (AF_INET, SOCK_STREAM, 0);
fd = api->socket (AF_INET6, SOCK_STREAM, 0);
if (fd == -1)
goto socket_failed;
@@ -17,10 +21,6 @@ frida_listen (int rx_buffer_size, const FridaUploadApi * api)
if (res == -1)
goto setsockopt_failed;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
addr.sin_port = 0;
addr_len = sizeof (addr);
res = api->bind (fd, (const struct sockaddr *) &addr, addr_len);
@@ -35,7 +35,7 @@ frida_listen (int rx_buffer_size, const FridaUploadApi * api)
if (res == -1)
goto listen_failed;
return ((uint64_t) fd << 16) | ntohs (addr.sin_port);
return ((uint64_t) fd << 16) | ntohs (addr.sin6_port);
socket_failed:
{
+116 -93
View File
@@ -1,6 +1,6 @@
[CCode (gir_namespace = "FridaFruityInjector", gir_version = "1.0")]
namespace Frida.Fruity.Injector {
public static async GadgetDetails inject (owned Gum.DarwinModule module, LLDB.Client lldb, ChannelProvider channel_provider,
public static async GadgetDetails inject (owned Gum.DarwinModule module, LLDB.Client lldb, HostChannelProvider channel_provider,
Cancellable? cancellable) throws Error, IOError {
var session = new Session (module, lldb, channel_provider);
return yield session.run (cancellable);
@@ -32,7 +32,7 @@ namespace Frida.Fruity.Injector {
construct;
}
public ChannelProvider channel_provider {
public HostChannelProvider channel_provider {
get;
construct;
}
@@ -64,13 +64,12 @@ namespace Frida.Fruity.Injector {
private LLDB.AppleDyldFields? dyld_fields;
private uint64 dyld_base;
private Gee.Map<string, uint64?>? dyld_symbols;
private bool libsystem_initialized;
private size_t module_size;
public Session (Gum.DarwinModule module, LLDB.Client lldb, ChannelProvider channel_provider) {
public Session (Gum.DarwinModule module, LLDB.Client lldb, HostChannelProvider channel_provider) {
Object (
module: module,
lldb: lldb,
@@ -160,7 +159,6 @@ namespace Frida.Fruity.Injector {
return true;
}, cancellable);
}
dyld_symbols = yield fetch_dyld_symbols (cancellable);
return null;
}
@@ -629,22 +627,24 @@ namespace Frida.Fruity.Injector {
/* Compiled from helpers/upload-listener.c */
private const uint8[] UPLOAD_LISTENER_CODE = {
0xff, 0x43, 0x01, 0xd1, 0xf6, 0x57, 0x02, 0xa9, 0xf4, 0x4f, 0x03, 0xa9, 0xfd, 0x7b, 0x04, 0xa9, 0xfd, 0x03, 0x01,
0x91, 0xf3, 0x03, 0x01, 0xaa, 0xe0, 0x1f, 0x00, 0xb9, 0x28, 0x00, 0x40, 0xf9, 0x40, 0x00, 0x80, 0x52, 0x21, 0x00,
0x80, 0x52, 0x02, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0x80, 0x05, 0x00, 0x54, 0xf4,
0x03, 0x00, 0xaa, 0x68, 0x06, 0x40, 0xf9, 0xe3, 0x73, 0x00, 0x91, 0xe1, 0xff, 0x9f, 0x52, 0x42, 0x00, 0x82, 0x52,
0x84, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0xa0, 0x04, 0x00, 0x54, 0x48, 0x00, 0x80,
0x52, 0xe8, 0x27, 0x00, 0x39, 0xe8, 0x0f, 0x80, 0x52, 0x08, 0x20, 0xa0, 0x72, 0xe8, 0x0f, 0x00, 0xb9, 0xff, 0x17,
0x00, 0x79, 0x08, 0x02, 0x80, 0x52, 0xe8, 0x07, 0x00, 0xb9, 0x68, 0x0a, 0x40, 0xf9, 0xe1, 0x23, 0x00, 0x91, 0xe0,
0x03, 0x14, 0xaa, 0x02, 0x02, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0x00, 0x03, 0x00, 0x54,
0x68, 0x12, 0x40, 0xf9, 0xe1, 0x23, 0x00, 0x91, 0xe2, 0x13, 0x00, 0x91, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f,
0xff, 0x83, 0x01, 0xd1, 0xf6, 0x57, 0x03, 0xa9, 0xf4, 0x4f, 0x04, 0xa9, 0xfd, 0x7b, 0x05, 0xa9, 0xfd, 0x43, 0x01,
0x91, 0xf3, 0x03, 0x01, 0xaa, 0x08, 0x00, 0x00, 0x90, 0x08, 0xb1, 0x04, 0x91, 0xa0, 0xc3, 0x1d, 0xb8, 0x00, 0x01,
0xc0, 0x3d, 0xe0, 0x07, 0x80, 0x3d, 0x00, 0xc1, 0xc0, 0x3c, 0xe0, 0xc3, 0x81, 0x3c, 0x28, 0x00, 0x40, 0xf9, 0xc0,
0x03, 0x80, 0x52, 0x21, 0x00, 0x80, 0x52, 0x02, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31,
0xc0, 0x04, 0x00, 0x54, 0xf4, 0x03, 0x00, 0xaa, 0x68, 0x06, 0x40, 0xf9, 0xa3, 0x93, 0x00, 0xd1, 0xe1, 0xff, 0x9f,
0x52, 0x42, 0x00, 0x82, 0x52, 0x84, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0xe0, 0x03,
0x00, 0x54, 0x88, 0x03, 0x80, 0x52, 0xe8, 0x0f, 0x00, 0xb9, 0x68, 0x0a, 0x40, 0xf9, 0xe1, 0x43, 0x00, 0x91, 0xe0,
0x03, 0x14, 0xaa, 0x82, 0x03, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0x00, 0x03, 0x00, 0x54,
0x68, 0x12, 0x40, 0xf9, 0xe1, 0x43, 0x00, 0x91, 0xe2, 0x33, 0x00, 0x91, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f,
0xd6, 0x1f, 0x04, 0x00, 0x31, 0x60, 0x02, 0x00, 0x54, 0x68, 0x0e, 0x40, 0xf9, 0xe0, 0x03, 0x14, 0xaa, 0x21, 0x00,
0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0xe0, 0x01, 0x00, 0x54, 0x88, 0x7e, 0x40, 0x93, 0xe9,
0x17, 0x40, 0x79, 0x29, 0x09, 0xc0, 0x5a, 0x35, 0x7d, 0x10, 0x53, 0x15, 0xbd, 0x70, 0xb3, 0x0d, 0x00, 0x00, 0x14,
0x27, 0x40, 0x79, 0x29, 0x09, 0xc0, 0x5a, 0x35, 0x7d, 0x10, 0x53, 0x15, 0xbd, 0x70, 0xb3, 0x0d, 0x00, 0x00, 0x14,
0x15, 0x20, 0xe0, 0xd2, 0x0b, 0x00, 0x00, 0x14, 0x15, 0x40, 0xe0, 0xd2, 0x06, 0x00, 0x00, 0x14, 0x15, 0x60, 0xe0,
0xd2, 0x04, 0x00, 0x00, 0x14, 0x15, 0x80, 0xe0, 0xd2, 0x02, 0x00, 0x00, 0x14, 0x15, 0xa0, 0xe0, 0xd2, 0x68, 0x42,
0x40, 0xf9, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f, 0xd6, 0xe0, 0x03, 0x15, 0xaa, 0xfd, 0x7b, 0x44, 0xa9, 0xf4,
0x4f, 0x43, 0xa9, 0xf6, 0x57, 0x42, 0xa9, 0xff, 0x43, 0x01, 0x91, 0xc0, 0x03, 0x5f, 0xd6
0x40, 0xf9, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f, 0xd6, 0xe0, 0x03, 0x15, 0xaa, 0xfd, 0x7b, 0x45, 0xa9, 0xf4,
0x4f, 0x44, 0xa9, 0xf6, 0x57, 0x43, 0xa9, 0xff, 0x83, 0x01, 0x91, 0xc0, 0x03, 0x5f, 0xd6, 0x00, 0x1e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00
};
/* Compiled from helpers/upload-receiver.c */
@@ -810,7 +810,7 @@ namespace Frida.Fruity.Injector {
return total;
}
private uint64 resolve_dyld_symbol (string name, string nick) throws Error {
private static uint64 resolve_dyld_symbol (string name, string nick, Gee.Map<string, uint64?> dyld_symbols) throws Error {
uint64? val = dyld_symbols[name];
if (val == null)
throw new Error.UNSUPPORTED ("Unsupported iOS version (%s not found)", nick);
@@ -821,13 +821,15 @@ namespace Frida.Fruity.Injector {
if (libsystem_initialized)
return;
var dyld_symbols = yield fetch_dyld_symbols (cancellable);
yield restore_main_thread_state (cancellable);
uint64? libdyld_initialize = dyld_symbols["__ZN5dyld44APIs19_libdyld_initializeEPKNS_16LibSystemHelpersE"];
if (libdyld_initialize != null)
yield ensure_libsystem_initialized_for_dyld_v4_and_above (libdyld_initialize, cancellable);
yield ensure_libsystem_initialized_for_dyld_v4_and_above (libdyld_initialize, dyld_symbols, cancellable);
else
yield ensure_libsystem_initialized_for_dyld_v3_and_below (cancellable);
yield ensure_libsystem_initialized_for_dyld_v3_and_below (dyld_symbols, cancellable);
yield save_main_thread_state (cancellable);
@@ -836,15 +838,21 @@ namespace Frida.Fruity.Injector {
}
private async void ensure_libsystem_initialized_for_dyld_v4_and_above (uint64 libdyld_initialize,
Cancellable? cancellable) throws GLib.Error {
uint64? process_info_ptr = dyld_symbols["_gProcessInfo"];
Gee.Map<string, uint64?> dyld_symbols, Cancellable? cancellable) throws GLib.Error {
uint64? process_info_ptr = dyld_symbols["__ZL12sProcessInfo"];
if (process_info_ptr == null)
process_info_ptr = dyld_symbols["__ZN5dyld412gProcessInfoE"];
if (process_info_ptr == null)
process_info_ptr = dyld_symbols["_gProcessInfo"];
GDB.Breakpoint init_breakpoint = yield lldb.add_breakpoint (SOFT, libdyld_initialize, 4, cancellable);
uint64? notify_objc_init = dyld_symbols["__ZN5dyld412RuntimeState14notifyObjCInitEPKNS_6LoaderE"];
uint64 initializer = (notify_objc_init != null) ? notify_objc_init : libdyld_initialize;
GDB.Breakpoint init_breakpoint = yield lldb.add_breakpoint (SOFT, initializer, 4, cancellable);
GDB.Breakpoint? restart_breakpoint = null;
uint64? restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPv"];
uint64? restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPK15DyldSharedCachePv"];
if (restart_with_dyld_in_cache == null)
restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPv"];
if (restart_with_dyld_in_cache != null)
restart_breakpoint = yield lldb.add_breakpoint (SOFT, restart_with_dyld_in_cache, 4, cancellable);
@@ -854,12 +862,18 @@ namespace Frida.Fruity.Injector {
if (hit_breakpoint == null)
throw new Error.UNSUPPORTED ("Unexpected exception");
if (restart_breakpoint != null)
yield restart_breakpoint.remove (cancellable);
yield init_breakpoint.remove (cancellable);
if (hit_breakpoint == restart_breakpoint) {
if (process_info_ptr == null)
throw new Error.UNSUPPORTED ("Missing gProcessInfo");
uint64 process_info = yield lldb.read_pointer (process_info_ptr, cancellable);
uint64 dyld_image_load_address = yield lldb.read_pointer (process_info + 0x20, cancellable);
initializer = dyld_image_load_address + (initializer - dyld_base);
var rebased_symbols = new Gee.HashMap<string, uint64?> ();
foreach (var e in dyld_symbols.entries)
rebased_symbols[e.key] = dyld_image_load_address + (e.value - dyld_base);
@@ -867,13 +881,12 @@ namespace Frida.Fruity.Injector {
dyld_base = dyld_image_load_address;
dyld_symbols = rebased_symbols;
yield restart_breakpoint.remove (cancellable);
yield init_breakpoint.remove (cancellable);
uint64 real_libdyld_initialize = dyld_symbols["__ZN5dyld44APIs19_libdyld_initializeEPKNS_16LibSystemHelpersE"];
init_breakpoint = yield lldb.add_breakpoint (SOFT, real_libdyld_initialize, 4, cancellable);
init_breakpoint = yield lldb.add_breakpoint (SOFT, initializer, 4, cancellable);
exception = (LLDB.Exception) yield lldb.continue_until_exception (cancellable);
yield init_breakpoint.remove (cancellable);
hit_breakpoint = exception.breakpoint;
if (hit_breakpoint == null)
throw new Error.UNSUPPORTED ("Unexpected exception");
@@ -882,7 +895,10 @@ namespace Frida.Fruity.Injector {
}
assert (hit_breakpoint == init_breakpoint);
yield init_breakpoint.remove (cancellable);
bool done_because_we_hit_notify_objc_init = initializer != libdyld_initialize;
if (done_because_we_hit_notify_objc_init)
return;
uint64 dyld_size = dyld_symbols["dyld_size"];
uint64 frame_here = exception.context["fp"];
@@ -911,7 +927,8 @@ namespace Frida.Fruity.Injector {
yield caller_breakpoint.remove (cancellable);
}
private async void ensure_libsystem_initialized_for_dyld_v3_and_below (Cancellable? cancellable) throws GLib.Error {
private async void ensure_libsystem_initialized_for_dyld_v3_and_below (Gee.Map<string, uint64?> dyld_symbols,
Cancellable? cancellable) throws GLib.Error {
GDB.Breakpoint? modern_breakpoint = null;
uint64 launch_with_closure = 0;
@@ -931,7 +948,8 @@ namespace Frida.Fruity.Injector {
modern_breakpoint = yield lldb.add_breakpoint (SOFT, run_initializers_call, 4, cancellable);
}
uint64 initialize_main_executable = resolve_dyld_symbol ("__ZN4dyld24initializeMainExecutableEv", "initializeMainExecutable");
uint64 initialize_main_executable = resolve_dyld_symbol ("__ZN4dyld24initializeMainExecutableEv",
"initializeMainExecutable", dyld_symbols);
GDB.Breakpoint legacy_breakpoint = yield lldb.add_breakpoint (SOFT, initialize_main_executable, 4, cancellable);
var exception = yield lldb.continue_until_exception (cancellable);
@@ -946,7 +964,7 @@ namespace Frida.Fruity.Injector {
yield modern_breakpoint.remove (cancellable);
if (hit_breakpoint == legacy_breakpoint)
yield initialize_libsystem_from_legacy_codepath (cancellable);
yield initialize_libsystem_from_legacy_codepath (dyld_symbols, cancellable);
else
assert (hit_breakpoint == modern_breakpoint);
}
@@ -987,7 +1005,8 @@ namespace Frida.Fruity.Injector {
throw new Error.UNSUPPORTED ("Unable to probe dyld v3 internals; please file a bug");
}
private async void initialize_libsystem_from_legacy_codepath (Cancellable? cancellable) throws GLib.Error {
private async void initialize_libsystem_from_legacy_codepath (Gee.Map<string, uint64?> dyld_symbols,
Cancellable? cancellable) throws GLib.Error {
uint64 code = jit_page;
var code_builder_pac = lldb.make_buffer_builder ();
@@ -1053,8 +1072,8 @@ namespace Frida.Fruity.Injector {
code_builder.write_pointer (error_buf_literal_offset, helpers + error_buffer_offset);
yield lldb.write_byte_array (code, code_builder.build (), cancellable);
uint64 register_thread_helpers =
resolve_dyld_symbol ("__ZL21registerThreadHelpersPKN4dyld16LibSystemHelpersE", "registerThreadHelpers");
uint64 register_thread_helpers = resolve_dyld_symbol ("__ZL21registerThreadHelpersPKN4dyld16LibSystemHelpersE",
"registerThreadHelpers", dyld_symbols);
yield invoke_remote_function (register_thread_helpers, {
helpers
}, null, cancellable);
@@ -1073,8 +1092,8 @@ namespace Frida.Fruity.Injector {
ExceptionHandler? strcmp_handler = null;
var dlopen = dyld_symbols.has_key ("_dlopen_internal")
? resolve_dyld_symbol ("_dlopen_internal", "dlopen")
: resolve_dyld_symbol ("_dlopen", "dlopen");
? resolve_dyld_symbol ("_dlopen_internal", "dlopen", dyld_symbols)
: resolve_dyld_symbol ("_dlopen", "dlopen", dyld_symbols);
yield invoke_remote_function (dlopen, {
libsystem_string,
9,
@@ -1181,67 +1200,71 @@ namespace Frida.Fruity.Injector {
private const uint8[] SYMBOL_FETCHER_CODE = {
0xff, 0x03, 0x02, 0xd1, 0xfc, 0x6f, 0x02, 0xa9, 0xfa, 0x67, 0x03, 0xa9, 0xf8, 0x5f, 0x04, 0xa9, 0xf6, 0x57, 0x05,
0xa9, 0xf4, 0x4f, 0x06, 0xa9, 0xfd, 0x7b, 0x07, 0xa9, 0xfd, 0xc3, 0x01, 0x91, 0xf4, 0x03, 0x01, 0xaa, 0xe0, 0x07,
0x00, 0xf9, 0x13, 0x00, 0x80, 0xd2, 0x19, 0x00, 0x80, 0xd2, 0x35, 0x80, 0x00, 0x91, 0x3a, 0x10, 0x40, 0xb9, 0xd6,
0x22, 0x00, 0x30, 0x1f, 0x20, 0x03, 0xd5, 0xd7, 0x22, 0x00, 0x10, 0x1f, 0x20, 0x03, 0xd5, 0x1a, 0x04, 0x00, 0x34,
0x00, 0xf9, 0x13, 0x00, 0x80, 0xd2, 0x19, 0x00, 0x80, 0xd2, 0x35, 0x80, 0x00, 0x91, 0x3a, 0x10, 0x40, 0xb9, 0x16,
0x00, 0x00, 0x90, 0xd6, 0x3e, 0x13, 0x91, 0x17, 0x00, 0x00, 0x90, 0xf7, 0x5a, 0x13, 0x91, 0x1a, 0x04, 0x00, 0x34,
0xa8, 0x02, 0x40, 0xb9, 0x1f, 0x09, 0x00, 0x71, 0xa0, 0x01, 0x00, 0x54, 0x1f, 0x2d, 0x00, 0x71, 0xa0, 0x01, 0x00,
0x54, 0x1f, 0x65, 0x00, 0x71, 0x81, 0x01, 0x00, 0x54, 0xb8, 0x22, 0x00, 0x91, 0xe0, 0x03, 0x18, 0xaa, 0xe1, 0x03,
0x16, 0xaa, 0xa3, 0x00, 0x00, 0x94, 0x80, 0x01, 0x00, 0x34, 0xb3, 0xa2, 0x41, 0xa9, 0xe8, 0x0b, 0x00, 0xf9, 0x04,
0x16, 0xaa, 0xaa, 0x00, 0x00, 0x94, 0x80, 0x01, 0x00, 0x34, 0xb3, 0xa2, 0x41, 0xa9, 0xe8, 0x0b, 0x00, 0xf9, 0x04,
0x00, 0x00, 0x14, 0xfc, 0x03, 0x15, 0xaa, 0x02, 0x00, 0x00, 0x14, 0xfb, 0x03, 0x15, 0xaa, 0xa8, 0x06, 0x40, 0xb9,
0xb5, 0x02, 0x08, 0x8b, 0x5a, 0x07, 0x00, 0x51, 0x7a, 0xfd, 0xff, 0x35, 0x09, 0x00, 0x00, 0x14, 0xe0, 0x03, 0x18,
0xaa, 0xe1, 0x03, 0x17, 0xaa, 0x94, 0x00, 0x00, 0x94, 0x00, 0xff, 0xff, 0x34, 0xa8, 0x0e, 0x40, 0xf9, 0xa9, 0x16,
0xaa, 0xe1, 0x03, 0x17, 0xaa, 0x9b, 0x00, 0x00, 0x94, 0x00, 0xff, 0xff, 0x34, 0xa8, 0x0e, 0x40, 0xf9, 0xa9, 0x16,
0x40, 0xf9, 0x19, 0x01, 0x09, 0xcb, 0xf4, 0xff, 0xff, 0x17, 0x88, 0x02, 0x13, 0xcb, 0x28, 0x03, 0x08, 0x8b, 0x89,
0x0b, 0x40, 0xb9, 0x13, 0x01, 0x09, 0x8b, 0x89, 0x13, 0x40, 0xb9, 0x19, 0x01, 0x09, 0x8b, 0x16, 0x17, 0x00, 0x10,
0x1f, 0x20, 0x03, 0xd5, 0xe8, 0x07, 0x40, 0xf9, 0xe8, 0x0f, 0x00, 0xf9, 0x35, 0x01, 0x80, 0x52, 0x78, 0x0b, 0x40,
0xb9, 0x68, 0x0f, 0x40, 0xb9, 0x1f, 0x03, 0x08, 0x6b, 0xc0, 0x09, 0x00, 0x54, 0xf7, 0x03, 0x18, 0x2a, 0xe8, 0xee,
0x7c, 0xd3, 0x68, 0x6a, 0x68, 0xb8, 0x3c, 0x03, 0x08, 0x8b, 0xe0, 0x03, 0x1c, 0xaa, 0xe1, 0x03, 0x16, 0xaa, 0x61,
0x00, 0x00, 0x94, 0x60, 0x06, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x41, 0x15, 0x00, 0x70, 0x1f, 0x20, 0x03, 0xd5,
0x5c, 0x00, 0x00, 0x94, 0xc0, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x61, 0x15, 0x00, 0x50, 0x1f, 0x20, 0x03,
0xd5, 0x6f, 0x00, 0x00, 0x94, 0x20, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x41, 0x15, 0x00, 0x10, 0x1f, 0x20,
0x03, 0xd5, 0x6a, 0x00, 0x00, 0x94, 0x80, 0x04, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x61, 0x15, 0x00, 0x50, 0x1f,
0x20, 0x03, 0xd5, 0x4d, 0x00, 0x00, 0x94, 0xe0, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x61, 0x15, 0x00, 0x10,
0x1f, 0x20, 0x03, 0xd5, 0x48, 0x00, 0x00, 0x94, 0x40, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x81, 0x15, 0x00,
0x30, 0x1f, 0x20, 0x03, 0xd5, 0x43, 0x00, 0x00, 0x94, 0xa0, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x81, 0x15,
0x00, 0x70, 0x1f, 0x20, 0x03, 0xd5, 0x5e, 0x00, 0x00, 0x94, 0x00, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x21,
0x15, 0x00, 0x70, 0x1f, 0x20, 0x03, 0xd5, 0x59, 0x00, 0x00, 0x94, 0x60, 0x01, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa,
0xc1, 0x14, 0x00, 0x70, 0x1f, 0x20, 0x03, 0xd5, 0x34, 0x00, 0x00, 0x94, 0xc0, 0x00, 0x00, 0x37, 0xe0, 0x03, 0x1c,
0xaa, 0xc1, 0x14, 0x00, 0x50, 0x1f, 0x20, 0x03, 0xd5, 0x2f, 0x00, 0x00, 0x94, 0x40, 0x02, 0x00, 0x34, 0xba, 0x00,
0x00, 0x34, 0xe8, 0x0f, 0x40, 0xf9, 0x49, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0x68,
0x12, 0x17, 0x8b, 0x08, 0x05, 0x40, 0xf9, 0x81, 0x02, 0x08, 0x8b, 0xe0, 0x63, 0x00, 0x91, 0x4d, 0x00, 0x00, 0x94,
0xe8, 0x0f, 0x40, 0xf9, 0x15, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0xe1, 0x03, 0x1c,
0xaa, 0x5e, 0x00, 0x00, 0x94, 0x5a, 0x07, 0x00, 0x11, 0x18, 0x07, 0x00, 0x11, 0xb1, 0xff, 0xff, 0x17, 0xe1, 0x23,
0x41, 0xa9, 0x49, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0x3e,
0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0x29, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9,
0x21, 0x11, 0x00, 0x70, 0x1f, 0x20, 0x03, 0xd5, 0xe0, 0x63, 0x00, 0x91, 0x4d, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40,
0xf9, 0xe9, 0x07, 0x40, 0xf9, 0x00, 0x01, 0x09, 0xcb, 0x1f, 0x01, 0x00, 0x39, 0xfd, 0x7b, 0x47, 0xa9, 0xf4, 0x4f,
0x46, 0xa9, 0xf6, 0x57, 0x45, 0xa9, 0xf8, 0x5f, 0x44, 0xa9, 0xfa, 0x67, 0x43, 0xa9, 0xfc, 0x6f, 0x42, 0xa9, 0xff,
0x03, 0x02, 0x91, 0xc0, 0x03, 0x5f, 0xd6, 0xf6, 0x57, 0xbd, 0xa9, 0xf4, 0x4f, 0x01, 0xa9, 0xfd, 0x7b, 0x02, 0xa9,
0xfd, 0x83, 0x00, 0x91, 0xf4, 0x03, 0x01, 0xaa, 0xf3, 0x03, 0x00, 0xaa, 0x35, 0x00, 0x40, 0x39, 0x68, 0x02, 0x40,
0x39, 0x28, 0x01, 0x00, 0x34, 0x1f, 0x01, 0x15, 0x6b, 0xa1, 0x00, 0x00, 0x54, 0xe0, 0x03, 0x13, 0xaa, 0xe1, 0x03,
0x14, 0xaa, 0x13, 0x00, 0x00, 0x94, 0x80, 0x00, 0x00, 0x37, 0x73, 0x06, 0x00, 0x91, 0xf7, 0xff, 0xff, 0x17, 0x13,
0x00, 0x80, 0xd2, 0x7f, 0x02, 0x00, 0xf1, 0xe0, 0x07, 0x9f, 0x1a, 0xfd, 0x7b, 0x42, 0xa9, 0xf4, 0x4f, 0x41, 0xa9,
0xf6, 0x57, 0xc3, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x14, 0x40, 0x38, 0x29, 0x14, 0x40, 0x38, 0x1f, 0x01, 0x00,
0x71, 0x00, 0x11, 0x49, 0x7a, 0x80, 0xff, 0xff, 0x54, 0x1f, 0x01, 0x09, 0x6b, 0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03,
0x5f, 0xd6, 0x28, 0x00, 0x40, 0x39, 0xa8, 0x00, 0x00, 0x34, 0x21, 0x04, 0x00, 0x91, 0x09, 0x14, 0x40, 0x38, 0x3f,
0x01, 0x08, 0x6b, 0x60, 0xff, 0xff, 0x54, 0x1f, 0x01, 0x00, 0x71, 0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6,
0x09, 0x00, 0x80, 0x52, 0x08, 0x00, 0x40, 0xf9, 0x8a, 0x07, 0x80, 0x52, 0xab, 0x0a, 0x00, 0x10, 0x1f, 0x20, 0x03,
0xd5, 0x5f, 0x11, 0x00, 0x31, 0x80, 0x01, 0x00, 0x54, 0x2c, 0x24, 0xca, 0x9a, 0x8c, 0x0d, 0x00, 0x72, 0x2d, 0x01,
0x00, 0x12, 0xa0, 0x19, 0x40, 0x7a, 0x29, 0x15, 0x9f, 0x1a, 0x89, 0x00, 0x00, 0x36, 0x8c, 0x1d, 0x40, 0x92, 0x6c,
0x69, 0x6c, 0x38, 0x0c, 0x15, 0x00, 0x38, 0x4a, 0x11, 0x00, 0x51, 0xf4, 0xff, 0xff, 0x17, 0x69, 0x00, 0x00, 0x37,
0x09, 0x06, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0x08, 0x00, 0x00, 0xf9, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x00, 0x40,
0xf9, 0x29, 0x00, 0x40, 0x39, 0x89, 0x00, 0x00, 0x34, 0x21, 0x04, 0x00, 0x91, 0x09, 0x15, 0x00, 0x38, 0xfc, 0xff,
0xff, 0x17, 0x08, 0x00, 0x00, 0xf9, 0xc0, 0x03, 0x5f, 0xd6, 0x6c, 0x69, 0x62, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x69,
0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x00, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x57, 0x69,
0x74, 0x68, 0x44, 0x79, 0x6c, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x00, 0x5f, 0x67, 0x50, 0x72, 0x6f,
0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x00, 0x5f, 0x5f, 0x5a, 0x4e, 0x35, 0x64, 0x79, 0x6c, 0x64, 0x34,
0x31, 0x32, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x00, 0x6c, 0x61, 0x75,
0x6e, 0x63, 0x68, 0x57, 0x69, 0x74, 0x68, 0x43, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x00, 0x69, 0x6e, 0x69, 0x74,
0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4d, 0x61, 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c,
0x65, 0x00, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x48, 0x65, 0x6c,
0x70, 0x65, 0x72, 0x73, 0x00, 0x5f, 0x64, 0x6c, 0x6f, 0x70, 0x65, 0x6e, 0x00, 0x5f, 0x73, 0x74, 0x72, 0x63, 0x6d,
0x70, 0x00, 0x64, 0x6f, 0x4d, 0x6f, 0x64, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x00, 0x64, 0x6f, 0x47, 0x65, 0x74, 0x44, 0x4f, 0x46, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00,
0x64, 0x79, 0x6c, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x5f, 0x5f,
0x4c, 0x49, 0x4e, 0x4b, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x0b, 0x40, 0xb9, 0x13, 0x01, 0x09, 0x8b, 0x89, 0x13, 0x40, 0xb9, 0x19, 0x01, 0x09, 0x8b, 0xe8, 0x07, 0x40, 0xf9,
0xe8, 0x0f, 0x00, 0xf9, 0x16, 0x00, 0x00, 0x90, 0xd6, 0x72, 0x0f, 0x91, 0x35, 0x01, 0x80, 0x52, 0x78, 0x0b, 0x40,
0xb9, 0x68, 0x0f, 0x40, 0xb9, 0x1f, 0x03, 0x08, 0x6b, 0xc0, 0x0a, 0x00, 0x54, 0x77, 0x52, 0x38, 0x8b, 0xe8, 0x02,
0x40, 0xb9, 0x3c, 0x03, 0x08, 0x8b, 0xe0, 0x03, 0x1c, 0xaa, 0xe1, 0x03, 0x16, 0xaa, 0x6a, 0x00, 0x00, 0x94, 0xa0,
0x07, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xbc, 0x0f, 0x91, 0x65, 0x00, 0x00, 0x94,
0x00, 0x07, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xf8, 0x0f, 0x91, 0x60, 0x00, 0x00,
0x94, 0x60, 0x06, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x54, 0x10, 0x91, 0x72, 0x00,
0x00, 0x94, 0xc0, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x8c, 0x10, 0x91, 0x6d,
0x00, 0x00, 0x94, 0x20, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xf4, 0x10, 0x91,
0x68, 0x00, 0x00, 0x94, 0x80, 0x04, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x40, 0x11,
0x91, 0x4c, 0x00, 0x00, 0x94, 0xe0, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x88,
0x11, 0x91, 0x47, 0x00, 0x00, 0x94, 0x40, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21,
0xec, 0x11, 0x91, 0x42, 0x00, 0x00, 0x94, 0xa0, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90,
0x21, 0x44, 0x12, 0x91, 0x5c, 0x00, 0x00, 0x94, 0x00, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00,
0x90, 0x21, 0x64, 0x12, 0x91, 0x57, 0x00, 0x00, 0x94, 0x60, 0x01, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00,
0x00, 0x90, 0x21, 0x84, 0x12, 0x91, 0x33, 0x00, 0x00, 0x94, 0xc0, 0x00, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01,
0x00, 0x00, 0x90, 0x21, 0xd0, 0x12, 0x91, 0x2e, 0x00, 0x00, 0x94, 0x20, 0x02, 0x00, 0x34, 0xba, 0x00, 0x00, 0x34,
0xe8, 0x0f, 0x40, 0xf9, 0x49, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe8, 0x06, 0x40,
0xf9, 0x81, 0x02, 0x08, 0x8b, 0xe0, 0x63, 0x00, 0x91, 0x4c, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0x15, 0x15,
0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0xe1, 0x03, 0x1c, 0xaa, 0x5d, 0x00, 0x00, 0x94, 0x5a,
0x07, 0x00, 0x11, 0x18, 0x07, 0x00, 0x11, 0xa9, 0xff, 0xff, 0x17, 0xe1, 0x23, 0x41, 0xa9, 0x49, 0x01, 0x80, 0x52,
0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0x3d, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40,
0xf9, 0x29, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0x01, 0x00, 0x00, 0x90, 0x21, 0x14,
0x13, 0x91, 0xe0, 0x63, 0x00, 0x91, 0x4c, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0xe9, 0x07, 0x40, 0xf9, 0x00,
0x01, 0x09, 0xcb, 0x1f, 0x01, 0x00, 0x39, 0xfd, 0x7b, 0x47, 0xa9, 0xf4, 0x4f, 0x46, 0xa9, 0xf6, 0x57, 0x45, 0xa9,
0xf8, 0x5f, 0x44, 0xa9, 0xfa, 0x67, 0x43, 0xa9, 0xfc, 0x6f, 0x42, 0xa9, 0xff, 0x03, 0x02, 0x91, 0xc0, 0x03, 0x5f,
0xd6, 0xf6, 0x57, 0xbd, 0xa9, 0xf4, 0x4f, 0x01, 0xa9, 0xfd, 0x7b, 0x02, 0xa9, 0xfd, 0x83, 0x00, 0x91, 0xf3, 0x03,
0x01, 0xaa, 0xf4, 0x03, 0x00, 0xaa, 0x35, 0x00, 0x40, 0x39, 0x96, 0x02, 0x40, 0x39, 0x36, 0x01, 0x00, 0x34, 0xdf,
0x02, 0x15, 0x6b, 0xa1, 0x00, 0x00, 0x54, 0xe0, 0x03, 0x14, 0xaa, 0xe1, 0x03, 0x13, 0xaa, 0x12, 0x00, 0x00, 0x94,
0x60, 0x00, 0x00, 0x37, 0x94, 0x06, 0x00, 0x91, 0xf7, 0xff, 0xff, 0x17, 0xdf, 0x02, 0x00, 0x71, 0xe0, 0x07, 0x9f,
0x1a, 0xfd, 0x7b, 0x42, 0xa9, 0xf4, 0x4f, 0x41, 0xa9, 0xf6, 0x57, 0xc3, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x14,
0x40, 0x38, 0x29, 0x14, 0x40, 0x38, 0x1f, 0x01, 0x00, 0x71, 0x00, 0x11, 0x49, 0x7a, 0x80, 0xff, 0xff, 0x54, 0x1f,
0x01, 0x09, 0x6b, 0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6, 0x28, 0x00, 0x40, 0x39, 0xa8, 0x00, 0x00, 0x34,
0x21, 0x04, 0x00, 0x91, 0x09, 0x14, 0x40, 0x38, 0x3f, 0x01, 0x08, 0x6b, 0x60, 0xff, 0xff, 0x54, 0x1f, 0x01, 0x00,
0x71, 0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6, 0x09, 0x00, 0x80, 0x52, 0x08, 0x00, 0x40, 0xf9, 0x8a, 0x07,
0x80, 0x52, 0x0b, 0x00, 0x00, 0x90, 0x6b, 0xc1, 0x13, 0x91, 0x5f, 0x11, 0x00, 0x31, 0x80, 0x01, 0x00, 0x54, 0x2c,
0x24, 0xca, 0x9a, 0x8c, 0x0d, 0x40, 0x92, 0x2d, 0x01, 0x00, 0x12, 0x9f, 0x01, 0x00, 0x71, 0xa0, 0x19, 0x40, 0x7a,
0x29, 0x15, 0x9f, 0x1a, 0x69, 0x00, 0x00, 0x36, 0x6c, 0x69, 0x6c, 0x38, 0x0c, 0x15, 0x00, 0x38, 0x4a, 0x11, 0x00,
0x51, 0xf4, 0xff, 0xff, 0x17, 0x69, 0x00, 0x00, 0x37, 0x09, 0x06, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0x08, 0x00,
0x00, 0xf9, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x00, 0x40, 0xf9, 0x29, 0x00, 0x40, 0x39, 0x89, 0x00, 0x00, 0x34, 0x21,
0x04, 0x00, 0x91, 0x09, 0x15, 0x00, 0x38, 0xfc, 0xff, 0xff, 0x17, 0x08, 0x00, 0x00, 0xf9, 0xc0, 0x03, 0x5f, 0xd6,
0x6c, 0x69, 0x62, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x00,
0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x62, 0x6a, 0x43, 0x49, 0x6e, 0x69, 0x74, 0x00, 0x72, 0x65, 0x73, 0x74,
0x61, 0x72, 0x74, 0x57, 0x69, 0x74, 0x68, 0x44, 0x79, 0x6c, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x00,
0x5f, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x00, 0x5f, 0x5f, 0x5a, 0x4e, 0x35,
0x64, 0x79, 0x6c, 0x64, 0x34, 0x31, 0x32, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f,
0x45, 0x00, 0x5f, 0x5f, 0x5a, 0x4c, 0x31, 0x32, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66,
0x6f, 0x00, 0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x57, 0x69, 0x74, 0x68, 0x43, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65,
0x00, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4d, 0x61, 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63,
0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x68, 0x72, 0x65,
0x61, 0x64, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x73, 0x00, 0x5f, 0x64, 0x6c, 0x6f, 0x70, 0x65, 0x6e, 0x00, 0x5f,
0x73, 0x74, 0x72, 0x63, 0x6d, 0x70, 0x00, 0x64, 0x6f, 0x4d, 0x6f, 0x64, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x75, 0x6e,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x6f, 0x47, 0x65, 0x74, 0x44, 0x4f, 0x46, 0x53, 0x65, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x5f, 0x5f, 0x54, 0x45,
0x58, 0x54, 0x00, 0x5f, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
};
+15 -35
View File
@@ -1,37 +1,33 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public class InstallationProxyClient : Object, AsyncInitable {
public LockdownClient lockdown {
public Device device {
get;
construct;
}
private PlistServiceClient service;
private InstallationProxyClient (LockdownClient lockdown) {
Object (lockdown: lockdown);
private InstallationProxyClient (Device device) {
Object (device: device);
}
public static async InstallationProxyClient open (LockdownClient lockdown, Cancellable? cancellable = null) throws InstallationProxyError, IOError {
var client = new InstallationProxyClient (lockdown);
public static async InstallationProxyClient open (Device device, Cancellable? cancellable = null) throws Error, IOError {
var client = new InstallationProxyClient (device);
try {
yield client.init_async (Priority.DEFAULT, cancellable);
} catch (GLib.Error e) {
throw_local_error (e);
throw_api_error (e);
}
return client;
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws InstallationProxyError, IOError {
try {
var stream = yield lockdown.start_service ("com.apple.mobile.installation_proxy", cancellable);
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
var stream = yield device.open_lockdown_service ("com.apple.mobile.installation_proxy", cancellable);
service = new PlistServiceClient (stream);
} catch (LockdownError e) {
throw new InstallationProxyError.PROTOCOL ("%s", e.message);
}
service = new PlistServiceClient (stream);
return true;
}
@@ -40,8 +36,7 @@ namespace Frida.Fruity {
yield service.close (cancellable);
}
public async Gee.ArrayList<ApplicationDetails> browse (Cancellable? cancellable = null)
throws InstallationProxyError, IOError {
public async Gee.ArrayList<ApplicationDetails> browse (Cancellable? cancellable = null) throws Error, IOError {
try {
var result = new Gee.ArrayList<ApplicationDetails> ();
@@ -75,7 +70,7 @@ namespace Frida.Fruity {
}
public async Gee.HashMap<string, ApplicationDetails> lookup (PlistDict query, Cancellable? cancellable = null)
throws InstallationProxyError, IOError {
throws Error, IOError {
try {
var result = new Gee.HashMap<string, ApplicationDetails> ();
@@ -189,28 +184,13 @@ namespace Frida.Fruity {
return new ApplicationDetails (identifier, name, version, build, path, containers, debuggable);
}
private static void throw_local_error (GLib.Error e) throws InstallationProxyError, IOError {
if (e is InstallationProxyError)
throw (InstallationProxyError) e;
if (e is IOError)
throw (IOError) e;
assert_not_reached ();
private static Error error_from_service (PlistServiceError e) {
return new Error.PROTOCOL ("%s", e.message);
}
private static InstallationProxyError error_from_service (PlistServiceError e) {
return new InstallationProxyError.PROTOCOL ("%s", e.message);
private static Error error_from_plist (PlistError e) {
return new Error.PROTOCOL ("Unexpected response: %s", e.message);
}
private static InstallationProxyError error_from_plist (PlistError e) {
return new InstallationProxyError.PROTOCOL ("Unexpected response: %s", e.message);
}
}
public errordomain InstallationProxyError {
INVALID_ARGUMENT,
PROTOCOL
}
public class ApplicationDetails : Object {
+174 -2
View File
@@ -23,6 +23,11 @@ namespace Frida.Fruity {
}
public class NSNumber : NSObject {
public Kind kind {
get;
private set;
}
public bool boolean {
get;
private set;
@@ -33,14 +38,44 @@ namespace Frida.Fruity {
private set;
}
public double number {
get;
private set;
}
public enum Kind {
BOOLEAN,
INTEGER,
FLOAT,
DOUBLE,
}
public NSNumber.from_boolean (bool val) {
kind = BOOLEAN;
boolean = val;
integer = val ? 1 : 0;
number = val ? 1.0 : 0.0;
}
public NSNumber.from_integer (int64 val) {
kind = INTEGER;
boolean = (val != 0) ? true : false;
integer = val;
number = val;
}
public NSNumber.from_float (float val) {
kind = FLOAT;
boolean = (val != 0.0f) ? true : false;
integer = (int64) val;
number = val;
}
public NSNumber.from_double (double val) {
kind = DOUBLE;
boolean = (val != 0.0) ? true : false;
integer = (int64) val;
number = val;
}
public override uint hash () {
@@ -51,7 +86,21 @@ namespace Frida.Fruity {
var other_number = other as NSNumber;
if (other_number == null)
return false;
return other_number.integer == integer;
if (other_number.kind != kind)
return false;
switch (kind) {
case BOOLEAN:
return other_number.boolean == boolean;
case INTEGER:
return other_number.integer == integer;
case FLOAT:
case DOUBLE:
return other_number.number == number;
}
return false;
}
public override string to_string () {
@@ -85,6 +134,32 @@ namespace Frida.Fruity {
}
}
public class NSData : NSObject {
public Bytes bytes {
get;
private set;
}
public NSData (Bytes bytes) {
this.bytes = bytes;
}
public override uint hash () {
return bytes.hash ();
}
public override bool is_equal_to (NSObject other) {
var other_data = other as NSData;
if (other_data == null)
return false;
return other_data.bytes.compare (bytes) == 0;
}
public override string to_string () {
return "NSData";
}
}
public class NSDictionary : NSObject {
public int size {
get {
@@ -198,6 +273,10 @@ namespace Frida.Fruity {
public NSArray (Gee.ArrayList<NSObject>? storage = null) {
this.storage = (storage != null) ? storage : new Gee.ArrayList<NSObject> (NSObject.equal_func);
}
public void add_object (NSObject obj) {
storage.add (obj);
}
}
public class NSDate : NSObject {
@@ -242,11 +321,23 @@ namespace Frida.Fruity {
}
}
public class DTTapMessage : NSObject {
public NSDictionary plist {
get;
private set;
}
public DTTapMessage (NSDictionary plist) {
this.plist = plist;
}
}
namespace NSKeyedArchive {
private Gee.HashMap<Type, EncodeFunc> encoders;
private Gee.HashMap<string, DecodeFunc> decoders;
private const string[] DICTIONARY_CLASS = { "NSDictionary", "NSObject" };
private const string[] ARRAY_CLASS = { "NSArray", "NSObject" };
[CCode (has_target = false)]
private delegate PlistUid EncodeFunc (NSObject instance, EncodingContext ctx);
@@ -319,9 +410,18 @@ namespace Frida.Fruity {
if (t == typeof (int64))
return new NSNumber.from_integer (val.get_int64 ());
if (t == typeof (float))
return new NSNumber.from_float (val.get_float ());
if (t == typeof (double))
return new NSNumber.from_double (val.get_double ());
if (t == typeof (string))
return new NSString (val.get_string ());
if (t == typeof (Bytes))
return new NSData ((Bytes) val.get_boxed ());
if (t == typeof (PlistDict)) {
var instance = (PlistDict) val.get_object ();
var klass = objects.get_dict ((int) instance.get_uid ("$class").uid);
@@ -354,6 +454,7 @@ namespace Frida.Fruity {
encoders[typeof (NSNumber)] = encode_number;
encoders[typeof (NSString)] = encode_string;
encoders[typeof (NSDictionary)] = encode_dictionary;
encoders[typeof (NSArray)] = encode_array;
}
private static void ensure_decoders_registered () {
@@ -365,11 +466,41 @@ namespace Frida.Fruity {
decoders["NSArray"] = decode_array;
decoders["NSDate"] = decode_date;
decoders["NSError"] = decode_error;
decoders["DTTapMessage"] = decode_tap_message;
decoders["DTSysmonTapMessage"] = decode_tap_message;
decoders["DTActivityTraceTapMessage"] = decode_tap_message;
decoders["DTTapStatusMessage"] = decode_tap_message;
decoders["DTTapHeartbeatMessage"] = decode_tap_message;
decoders["DTKTraceTapMessage"] = decode_tap_message;
}
private static PlistUid encode_number (NSObject instance, EncodingContext ctx) {
int64 val = ((NSNumber) instance).integer;
var n = (NSNumber) instance;
switch (n.kind) {
case BOOLEAN:
return encode_boolean (n.boolean, ctx);
case INTEGER:
return encode_integer (n.integer, ctx);
case FLOAT:
return encode_float (n.integer, ctx);
case DOUBLE:
return encode_double (n.integer, ctx);
}
assert_not_reached ();
}
private static PlistUid encode_boolean (bool val, EncodingContext ctx) {
var uid = ctx.find_existing_object (e => e.holds (typeof (bool)) && e.get_boolean () == val);
if (uid != null)
return uid;
var objects = ctx.objects;
uid = new PlistUid (objects.length);
objects.add_boolean (val);
return uid;
}
private static PlistUid encode_integer (int64 val, EncodingContext ctx) {
var uid = ctx.find_existing_object (e => e.holds (typeof (int64)) && e.get_int64 () == val);
if (uid != null)
return uid;
@@ -380,6 +511,28 @@ namespace Frida.Fruity {
return uid;
}
private static PlistUid encode_float (float val, EncodingContext ctx) {
var uid = ctx.find_existing_object (e => e.holds (typeof (float)) && e.get_float () == val);
if (uid != null)
return uid;
var objects = ctx.objects;
uid = new PlistUid (objects.length);
objects.add_float (val);
return uid;
}
private static PlistUid encode_double (double val, EncodingContext ctx) {
var uid = ctx.find_existing_object (e => e.holds (typeof (double)) && e.get_double () == val);
if (uid != null)
return uid;
var objects = ctx.objects;
uid = new PlistUid (objects.length);
objects.add_double (val);
return uid;
}
private static PlistUid encode_string (NSObject instance, EncodingContext ctx) {
string str = ((NSString) instance).str;
@@ -451,6 +604,21 @@ namespace Frida.Fruity {
}
}
private static PlistUid encode_array (NSObject instance, EncodingContext ctx) {
NSArray array = (NSArray) instance;
var object = new PlistDict ();
var uid = ctx.add_object (object);
var objs = new PlistArray ();
foreach (var element in array.elements)
objs.add_uid (encode_value (element, ctx));
object.set_array ("NS.objects", objs);
object.set_uid ("$class", ctx.get_class (ARRAY_CLASS));
return uid;
}
private static NSObject decode_array (PlistDict instance, DecodingContext ctx) throws Error, PlistError {
var objs = instance.get_array ("NS.objects");
@@ -486,6 +654,10 @@ namespace Frida.Fruity {
return new NSError (domain, code, (NSDictionary) user_info);
}
private static NSObject decode_tap_message (PlistDict instance, DecodingContext ctx) throws Error, PlistError {
return new DTTapMessage ((NSDictionary) decode_value (instance.get_uid ("DTTapMessagePlist"), ctx));
}
private class EncodingContext {
public PlistArray objects;
+29 -41
View File
@@ -1,18 +1,14 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public class LockdownClient : Object, AsyncInitable {
public class LockdownClient : Object {
public signal void closed ();
public DeviceDetails device_details {
public PlistServiceClient service {
get;
construct;
}
public IOStream stream {
get { return service.stream; }
}
private PlistServiceClient service;
private UsbmuxDevice? usbmux_device;
private Plist? pair_record;
private string? host_id;
private string? system_buid;
@@ -22,29 +18,20 @@ namespace Frida.Fruity {
private const uint16 LOCKDOWN_PORT = 62078;
private LockdownClient (DeviceDetails device_details) {
Object (device_details: device_details);
public LockdownClient (IOStream stream) {
Object (service: new PlistServiceClient (stream));
}
public static async LockdownClient open (DeviceDetails device_details, Cancellable? cancellable = null)
construct {
service.closed.connect (on_service_closed);
}
public static async LockdownClient open (UsbmuxDevice device, Cancellable? cancellable = null)
throws LockdownError, IOError {
var client = new LockdownClient (device_details);
try {
yield client.init_async (Priority.DEFAULT, cancellable);
} catch (GLib.Error e) {
throw_local_error (e);
}
return client;
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws LockdownError, IOError {
var device = device_details;
try {
var usbmux = yield UsbmuxClient.open (cancellable);
Plist pair_record;
try {
pair_record = yield usbmux.read_pair_record (device.udid, cancellable);
} catch (UsbmuxError e) {
@@ -52,6 +39,10 @@ namespace Frida.Fruity {
throw new LockdownError.NOT_PAIRED ("Not paired");
throw e;
}
string? host_id = null;
string? system_buid = null;
TlsCertificate? tls_certificate = null;
try {
host_id = pair_record.get_string ("HostID");
system_buid = pair_record.get_string ("SystemBUID");
@@ -64,15 +55,19 @@ namespace Frida.Fruity {
yield usbmux.connect_to_port (device.id, LOCKDOWN_PORT, cancellable);
service = new PlistServiceClient (usbmux.connection);
service.closed.connect (on_service_closed);
var client = new LockdownClient (usbmux.connection);
client.usbmux_device = device;
client.pair_record = pair_record;
client.host_id = host_id;
client.system_buid = system_buid;
client.tls_certificate = tls_certificate;
yield query_type (cancellable);
yield client.query_type (cancellable);
return client;
} catch (UsbmuxError e) {
throw new LockdownError.UNSUPPORTED ("%s", e.message);
}
return true;
}
public async void close (Cancellable? cancellable = null) throws IOError {
@@ -182,7 +177,7 @@ namespace Frida.Fruity {
bool enable_encryption = response.has ("EnableServiceSSL") && response.get_boolean ("EnableServiceSSL");
var client = yield UsbmuxClient.open (cancellable);
yield client.connect_to_port (device_details.id, (uint16) response.get_integer ("Port"), cancellable);
yield client.connect_to_port (usbmux_device.id, (uint16) response.get_integer ("Port"), cancellable);
SocketConnection raw_connection = client.connection;
IOStream stream = raw_connection;
@@ -242,7 +237,7 @@ namespace Frida.Fruity {
try {
var usbmux = yield UsbmuxClient.open (cancellable);
yield usbmux.delete_pair_record (device_details.udid, cancellable);
yield usbmux.delete_pair_record (usbmux_device.udid, cancellable);
} catch (UsbmuxError e) {
if (!(e is UsbmuxError.INVALID_ARGUMENT))
throw new LockdownError.PROTOCOL ("%s", e.message);
@@ -269,17 +264,9 @@ namespace Frida.Fruity {
return request;
}
private static void throw_local_error (GLib.Error e) throws LockdownError, IOError {
if (e is LockdownError)
throw (LockdownError) e;
if (e is IOError)
throw (IOError) e;
assert_not_reached ();
}
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);
}
@@ -289,6 +276,7 @@ namespace Frida.Fruity {
}
public errordomain LockdownError {
CONNECTION_CLOSED,
INVALID_SERVICE,
NOT_PAIRED,
UNSUPPORTED,
+330
View File
@@ -0,0 +1,330 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
internal sealed class UsbNcmDriver : Object, AsyncInitable {
public UsbDevice device {
get;
construct;
}
public VirtualNetworkStack netstack {
get {
return _netstack;
}
}
public InetAddress? remote_ipv6_address {
get {
return _remote_ipv6_address;
}
}
private uint8 data_iface;
private int data_altsetting;
private uint8 rx_address;
private uint8 tx_address;
private VirtualNetworkStack? _netstack;
private uint16 next_outgoing_sequence = 1;
private InetAddress? _remote_ipv6_address;
private Cancellable io_cancellable = new Cancellable ();
private enum UsbDescriptorType {
INTERFACE = 0x04,
}
private enum UsbCommSubclass {
NCM = 0x0d,
}
private enum UsbDataSubclass {
UNDEFINED = 0x00,
}
private enum UsbCdcDescriptorSubtype {
ETHERNET = 0x0f,
}
private enum EtherType {
IPV6 = 0x86dd,
}
private enum IPV6NextHeader {
UDP = 0x11,
}
public static async UsbNcmDriver open (UsbDevice device, Cancellable? cancellable = null) throws Error, IOError {
var driver = new UsbNcmDriver (device);
try {
yield driver.init_async (Priority.DEFAULT, cancellable);
} catch (GLib.Error e) {
throw_api_error (e);
}
return driver;
}
private UsbNcmDriver (UsbDevice device) {
Object (device: device);
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
unowned LibUSB.Device raw_device = device.raw_device;
unowned LibUSB.DeviceHandle handle = device.handle;
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");
int desired_config_value = -1;
bool found_cdc_header = false;
bool found_data_interface = false;
uint8 mac_address_index = 0;
for (uint8 config_value = dev_desc.bNumConfigurations; config_value != 0; config_value--) {
LibUSB.ConfigDescriptor config;
Usb.check (raw_device.get_config_descriptor_by_value (config_value, out config),
"Failed to get config descriptor");
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) {
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"));
uint8 mac_address[6];
string mac_address_str = yield device.read_string_descriptor (mac_address_index, device.default_language_id,
cancellable);
if (mac_address_str.length != 12)
throw new Error.PROTOCOL ("Invalid MAC address");
for (uint i = 0; i != 6; i++) {
uint v;
mac_address_str.substring (i * 2, 2).scanf ("%02X", out v);
mac_address[i] = (uint8) v;
}
if (current_config.bConfigurationValue != desired_config_value) {
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");
}
try {
Usb.check (handle.claim_interface (data_iface), "Failed to claim USB interface");
} catch (Error e) {
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),
"Failed to set USB interface alt setting");
_netstack = new VirtualNetworkStack (new Bytes (mac_address), null, 1500);
_netstack.outgoing_datagram.connect (on_netif_outgoing_datagram);
process_incoming_datagrams.begin ();
return true;
}
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
}
public void close () {
io_cancellable.cancel ();
_netstack.stop ();
}
private async void process_incoming_datagrams () {
var data = new uint8[64 * 1024];
while (true) {
try {
size_t n = yield device.bulk_transfer (rx_address, data, uint.MAX, io_cancellable);
handle_ncm_frame (data[:n]);
} catch (GLib.Error e) {
return;
}
}
}
private void handle_ncm_frame (uint8[] data) throws GLib.Error {
var input = new DataInputStream (new MemoryInputStream.from_data (data));
input.byte_order = LITTLE_ENDIAN;
uint8 raw_signature[4 + 1];
unowned string signature = (string) raw_signature;
size_t bytes_read;
input.read_all (raw_signature[:4], out bytes_read);
if (signature != "NCMH")
throw new Error.PROTOCOL ("Invalid NTH16 signature");
input.skip (6);
var ndp_index = input.read_uint16 ();
do {
input.seek (ndp_index, SET);
input.read_all (raw_signature[:4], out bytes_read);
if (signature != "NCM0")
throw new Error.PROTOCOL ("Invalid NDP16 signature");
input.skip (2);
var next_ndp_index = input.read_uint16 ();
while (true) {
var datagram_index = input.read_uint16 ();
var datagram_length = input.read_uint16 ();
if (datagram_index == 0 || datagram_length == 0)
break;
int64 previous_offset = input.tell ();
input.seek (datagram_index, SET);
var datagram_buf = new uint8[datagram_length];
input.read_all (datagram_buf, out bytes_read);
input.seek (previous_offset, SET);
var datagram = new Bytes.take ((owned) datagram_buf);
if (_remote_ipv6_address == null) {
_remote_ipv6_address = try_infer_remote_address_from_datagram (datagram);
if (_remote_ipv6_address != null)
notify_property ("remote-ipv6-address");
}
_netstack.handle_incoming_datagram (datagram);
}
ndp_index = next_ndp_index;
} 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;
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) {
}
}
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);
try {
for (int offset = 0; offset != header.length;) {
uint8 length = input.read_byte ();
if (length < 3)
throw new Error.PROTOCOL ("Invalid descriptor length");
uint8 descriptor_type = input.read_byte ();
if (descriptor_type != (LibUSB.RequestType.CLASS | UsbDescriptorType.INTERFACE))
throw new Error.PROTOCOL ("Invalid descriptor type");
uint8 descriptor_subtype = input.read_byte ();
if (descriptor_subtype == UsbCdcDescriptorSubtype.ETHERNET) {
mac_address_index = input.read_byte ();
return;
}
input.skip (length - 3);
offset += length;
}
} catch (IOError e) {
throw new Error.PROTOCOL ("%s", e.message);
}
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);
}
}
}
File diff suppressed because it is too large. Load diff
+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 ();
}
}
}
+12
View File
@@ -1573,6 +1573,12 @@ namespace Frida.Fruity {
if (t == typeof (int64))
return (uint) v.get_int64 ();
if (t == typeof (float))
return (uint) v.get_float ();
if (t == typeof (double))
return (uint) v.get_double ();
if (t == typeof (string))
return str_hash (v.get_string ());
@@ -1598,6 +1604,12 @@ namespace Frida.Fruity {
if (t == typeof (int64))
return a.get_int64 () == b.get_int64 ();
if (t == typeof (float))
return a.get_float () == b.get_float ();
if (t == typeof (double))
return a.get_double () == b.get_double ();
if (t == typeof (string))
return a.get_string () == b.get_string ();
+18 -39
View File
@@ -1,38 +1,33 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public class SpringboardServicesClient : Object, AsyncInitable {
public LockdownClient lockdown {
public Device device {
get;
construct;
}
private PlistServiceClient service;
private SpringboardServicesClient (LockdownClient lockdown) {
Object (lockdown: lockdown);
private SpringboardServicesClient (Device device) {
Object (device: device);
}
public static async SpringboardServicesClient open (LockdownClient lockdown,
Cancellable? cancellable = null) throws SpringboardServicesError, IOError {
var client = new SpringboardServicesClient (lockdown);
public static async SpringboardServicesClient open (Device device, Cancellable? cancellable = null) throws Error, IOError {
var client = new SpringboardServicesClient (device);
try {
yield client.init_async (Priority.DEFAULT, cancellable);
} catch (GLib.Error e) {
throw_local_error (e);
throw_api_error (e);
}
return client;
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws SpringboardServicesError, IOError {
try {
var stream = yield lockdown.start_service ("com.apple.springboardservices", cancellable);
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
var stream = yield device.open_lockdown_service ("com.apple.springboardservices", cancellable);
service = new PlistServiceClient (stream);
} catch (LockdownError e) {
throw new SpringboardServicesError.PROTOCOL ("%s", e.message);
}
service = new PlistServiceClient (stream);
return true;
}
@@ -41,15 +36,14 @@ namespace Frida.Fruity {
yield service.close (cancellable);
}
public async Bytes get_icon_png_data (string bundle_id,
Cancellable? cancellable = null) throws SpringboardServicesError, IOError {
public async Bytes get_icon_png_data (string bundle_id, Cancellable? cancellable = null) throws Error, IOError {
try {
var request = make_request ("getIconPNGData");
request.set_string ("bundleId", bundle_id);
var response = yield service.query (request, cancellable);
if (response.has ("Error"))
throw new SpringboardServicesError.INVALID_ARGUMENT ("%s", response.get_string ("Error"));
throw new Error.INVALID_ARGUMENT ("%s", response.get_string ("Error"));
return response.get_bytes ("pngData");
} catch (PlistServiceError e) {
@@ -59,8 +53,8 @@ namespace Frida.Fruity {
}
}
public async Gee.HashMap<string, Bytes> get_icon_png_data_batch (string[] bundle_ids,
Cancellable? cancellable = null) throws SpringboardServicesError, IOError {
public async Gee.HashMap<string, Bytes> get_icon_png_data_batch (string[] bundle_ids, Cancellable? cancellable = null)
throws Error, IOError {
try {
foreach (unowned string bundle_id in bundle_ids) {
var request = make_request ("getIconPNGData");
@@ -73,7 +67,7 @@ namespace Frida.Fruity {
do {
foreach (Plist response in yield service.read_messages (0, cancellable)) {
if (response.has ("Error")) {
throw new SpringboardServicesError.INVALID_ARGUMENT ("%s",
throw new Error.INVALID_ARGUMENT ("%s",
response.get_string ("Error"));
}
@@ -98,27 +92,12 @@ namespace Frida.Fruity {
return request;
}
private static void throw_local_error (GLib.Error e) throws SpringboardServicesError, IOError {
if (e is SpringboardServicesError)
throw (SpringboardServicesError) e;
if (e is IOError)
throw (IOError) e;
assert_not_reached ();
private static Error error_from_service (PlistServiceError e) {
return new Error.PROTOCOL ("%s", e.message);
}
private static SpringboardServicesError error_from_service (PlistServiceError e) {
return new SpringboardServicesError.PROTOCOL ("%s", e.message);
private static Error error_from_plist (PlistError e) {
return new Error.PROTOCOL ("Unexpected response: %s", e.message);
}
private static SpringboardServicesError error_from_plist (PlistError e) {
return new SpringboardServicesError.PROTOCOL ("Unexpected response: %s", e.message);
}
}
public errordomain SpringboardServicesError {
INVALID_ARGUMENT,
PROTOCOL
}
}
+337
View File
@@ -0,0 +1,337 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
internal sealed class UsbDevice : Object, AsyncInitable {
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,
SET_MODE = 0x52,
}
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, UsbDeviceBackend backend, Cancellable? cancellable = null)
throws Error, IOError {
var device = new UsbDevice (raw_device, backend);
try {
yield device.init_async (Priority.DEFAULT, cancellable);
} catch (GLib.Error e) {
throw_api_error (e);
}
return device;
}
private UsbDevice (LibUSB.Device raw_device, UsbDeviceBackend backend) {
Object (backend: backend);
_raw_device = raw_device;
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
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 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 (
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
AppleSpecificRequest.GET_MODE,
0,
0,
4,
1000,
cancellable);
string mode = parse_mode (response);
bool is_initial_mode = mode == MODE_INITIAL_UNTETHERED || mode == MODE_INITIAL_TETHERED;
if (!is_initial_mode)
return false;
response = yield control_transfer (
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
AppleSpecificRequest.SET_MODE,
0,
3,
1,
1000,
cancellable);
if (response.get_size () != 1 || response[0] != 0x00)
return false;
return true;
}
private static string parse_mode (Bytes mode) throws Error {
var result = new StringBuilder.sized (7);
foreach (uint8 byte in mode.get_data ()) {
if (result.len != 0)
result.append_c (':');
result.append_printf ("%u", byte);
}
return result.str;
}
private static string udid_from_serial_number (string serial) {
if (serial.length == 24)
return serial.substring (0, 8) + "-" + serial.substring (8);
return serial;
}
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);
try {
var input = new DataInputStream (new MemoryInputStream.from_bytes (response));
input.byte_order = LITTLE_ENDIAN;
size_t size = response.get_size ();
if (size % sizeof (unichar2) != 0)
throw new Error.PROTOCOL ("Invalid string descriptor");
size_t n = size / sizeof (unichar2);
var chars = new unichar2[n];
for (size_t i = 0; i != n; i++)
chars[i] = input.read_uint16 ();
unowned string16 str = (string16) chars;
return str.to_utf8 ((long) n);
} catch (GLib.Error e) {
throw new Error.PROTOCOL ("%s", e.message);
}
}
public async Bytes read_string_descriptor_bytes (uint8 index, uint16 language_id, Cancellable? cancellable)
throws Error, IOError {
var response = yield control_transfer (
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.STANDARD | LibUSB.EndpointDirection.IN,
LibUSB.StandardRequest.GET_DESCRIPTOR,
(LibUSB.DescriptorType.STRING << 8) | index,
language_id,
1024,
1000,
cancellable);
try {
var input = new DataInputStream (new MemoryInputStream.from_bytes (response));
input.byte_order = LITTLE_ENDIAN;
uint8 length = input.read_byte ();
if (length < 2)
throw new Error.PROTOCOL ("Invalid string descriptor length");
uint8 type = input.read_byte ();
if (type != LibUSB.DescriptorType.STRING)
throw new Error.PROTOCOL ("Invalid string descriptor type");
size_t remainder = response.get_size () - 2;
length -= 2;
if (length > remainder)
throw new Error.PROTOCOL ("Invalid string descriptor length");
return 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,
uint timeout, Cancellable? cancellable) throws Error, IOError {
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);
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (() => {
transfer.cancel ();
return Source.REMOVE;
});
cancel_source.attach (MainContext.get_thread_default ());
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]);
}
public async size_t bulk_transfer (uint8 endpoint, uint8[] buffer, uint timeout, Cancellable? cancellable)
throws Error, IOError {
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);
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (() => {
transfer.cancel ();
return Source.REMOVE;
});
cancel_source.attach (MainContext.get_thread_default ());
try {
Usb.check (transfer.submit (), "Failed to submit bulk transfer");
on_operation_started ();
yield;
on_operation_ended ();
} finally {
cancel_source.destroy ();
}
Usb.check_transfer (transfer.status, "Bulk transfer failed");
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 ();
}
private class TransferReadyClosure {
private SourceFunc? handler;
private MainContext main_context;
public TransferReadyClosure (owned SourceFunc handler) {
this.handler = (owned) handler;
main_context = MainContext.ref_thread_default ();
}
public void schedule () {
var source = new IdleSource ();
source.set_callback (() => {
handler ();
handler = null;
return Source.REMOVE;
});
source.attach (main_context);
}
}
}
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)
return;
string message = @"$prefix: $(error.get_description ())";
switch (error) {
case ACCESS:
throw new Error.PERMISSION_DENIED ("%s", message);
case NOT_FOUND:
throw new Error.INVALID_OPERATION ("%s", message);
case TIMEOUT:
throw new Error.TIMED_OUT ("%s", message);
default:
throw new Error.TRANSPORT ("%s", message);
}
}
internal static void check_transfer (LibUSB.TransferStatus status, string prefix) throws Error, IOError {
if (status == COMPLETED)
return;
string message = @"$prefix: $(status.to_string ())";
switch (status) {
case TIMED_OUT:
throw new Error.TIMED_OUT ("%s", message);
case CANCELLED:
throw new IOError.CANCELLED ("%s", message);
default:
throw new Error.TRANSPORT ("%s", message);
}
}
}
}
+29 -65
View File
@@ -1,13 +1,16 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
public class UsbmuxClient : Object, AsyncInitable {
public signal void device_attached (DeviceDetails details);
public signal void device_detached (DeviceId id);
public signal void device_attached (UsbmuxDevice device);
public signal void device_detached (UsbmuxDevice device);
public SocketConnection? connection {
get;
private set;
}
private Gee.Map<uint, UsbmuxDevice> devices = new Gee.HashMap<uint, UsbmuxDevice> ();
private InputStream? input;
private OutputStream? output;
private Cancellable io_cancellable = new Cancellable ();
@@ -132,11 +135,12 @@ namespace Frida.Fruity {
}
}
public async void connect_to_port (DeviceId device_id, uint16 port, Cancellable? cancellable = null) throws UsbmuxError, IOError {
public async void connect_to_port (uint device_id, uint16 port, Cancellable? cancellable = null)
throws UsbmuxError, IOError {
assert (is_processing_messages);
var request = create_request ("Connect");
request.set_integer ("DeviceID", device_id.raw_value);
request.set_integer ("DeviceID", device_id);
request.set_integer ("PortNumber", port.to_big_endian ());
var response = yield query (request, MODE_SWITCH, cancellable);
@@ -160,9 +164,9 @@ namespace Frida.Fruity {
}
}
public async Plist read_pair_record (Udid udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
public async Plist read_pair_record (string udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
var request = create_request ("ReadPairRecord");
request.set_string ("PairRecordID", udid.raw_value);
request.set_string ("PairRecordID", udid);
var response = yield query (request, REGULAR, cancellable);
try {
@@ -175,9 +179,9 @@ namespace Frida.Fruity {
}
}
public async void delete_pair_record (Udid udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
public async void delete_pair_record (string udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
var request = create_request ("DeletePairRecord");
request.set_string ("PairRecordID", udid.raw_value);
request.set_string ("PairRecordID", udid);
var response = yield query (request, REGULAR, cancellable);
check_pair_response (response, "DeletePairRecord");
@@ -276,28 +280,26 @@ namespace Frida.Fruity {
if (props.has ("ConnectionType") && props.get_string ("ConnectionType") == "Network")
connection_type = NETWORK;
var device_id = DeviceId ((uint) body.get_integer ("DeviceID"));
var device_id = (uint) body.get_integer ("DeviceID");
ProductId product_id;
if (connection_type == USB)
product_id = ProductId ((int) props.get_integer ("ProductID"));
else
product_id = ProductId (-1);
int product_id = (connection_type == USB) ? (int) props.get_integer ("ProductID") : -1;
string raw_udid = props.get_string ("SerialNumber");
if (raw_udid.length == 24)
raw_udid = raw_udid[:8] + "-" + raw_udid[8:];
var udid = Udid (raw_udid);
string udid = props.get_string ("SerialNumber");
if (udid.length == 24)
udid = udid[:8] + "-" + udid[8:];
InetSocketAddress? network_address = null;
if (connection_type == NETWORK)
network_address = parse_network_address (props.get_bytes ("NetworkAddress"));
var details =
new DeviceDetails (connection_type, device_id, product_id, udid, network_address);
device_attached (details);
var device =
new UsbmuxDevice (connection_type, device_id, product_id, udid, network_address);
devices[device_id] = device;
device_attached (device);
} else if (message_type == "Detached") {
device_detached (DeviceId ((uint) body.get_integer ("DeviceID")));
UsbmuxDevice? device;
if (devices.unset ((uint) body.get_integer ("DeviceID"), out device))
device_detached (device);
} else {
throw new UsbmuxError.PROTOCOL ("Unexpected message type: %s", message_type);
}
@@ -546,23 +548,23 @@ namespace Frida.Fruity {
PROTOCOL
}
public class DeviceDetails : Object {
public class UsbmuxDevice : Object {
public ConnectionType connection_type {
get;
construct;
}
public DeviceId id {
public uint id {
get;
construct;
}
public ProductId product_id {
public int product_id {
get;
construct;
}
public Udid udid {
public string udid {
get;
construct;
}
@@ -572,7 +574,7 @@ namespace Frida.Fruity {
construct;
}
public DeviceDetails (ConnectionType connection_type, DeviceId id, ProductId product_id, Udid udid,
public UsbmuxDevice (ConnectionType connection_type, uint id, int product_id, string udid,
InetSocketAddress? network_address) {
Object (
connection_type: connection_type,
@@ -583,42 +585,4 @@ namespace Frida.Fruity {
);
}
}
public enum ConnectionType {
USB,
NETWORK
}
public struct DeviceId {
public uint raw_value {
get;
private set;
}
public DeviceId (uint raw_value) {
this.raw_value = raw_value;
}
}
public struct ProductId {
public int raw_value {
get;
private set;
}
public ProductId (int raw_value) {
this.raw_value = raw_value;
}
}
public struct Udid {
public string raw_value {
get;
private set;
}
public Udid (string raw_value) {
this.raw_value = raw_value;
}
}
}
+4187
View File
File diff suppressed because it is too large. Load diff
+14 -7
View File
@@ -177,12 +177,16 @@ namespace Frida {
}
}
public interface ChannelProvider : Object {
public interface HostChannelProvider : Object {
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) throws Error, IOError;
public abstract async void unpair (Cancellable? cancellable = null) throws Error, IOError;
}
public interface HostSessionBackend : Object {
@@ -1331,10 +1335,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 {
@@ -1504,9 +1509,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);
}
@@ -1537,7 +1544,7 @@ namespace Frida {
#if HAVE_FRUITY_BACKEND || HAVE_DROIDY_BACKEND
internal async DBusConnection establish_direct_connection (TransportBroker broker, AgentSessionId id,
ChannelProvider channel_provider, Cancellable? cancellable) throws Error, IOError {
HostChannelProvider channel_provider, Cancellable? cancellable) throws Error, IOError {
uint16 port;
string token;
try {
+46 -10
View File
@@ -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,8 +1077,10 @@ 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;
@@ -1117,10 +1120,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 +1134,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;
@@ -1416,14 +1423,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 +1952,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) {
}
}
@@ -3308,8 +3333,13 @@ namespace Frida {
var iter = MapsIter.for_pid (pid);
while (iter.next ()) {
string candidate_path = iter.path;
if (candidate_path == path)
if (candidate_path == path) {
#if ANDROID
if (candidate_path == Gum.Process.query_libc_name () && iter.flags[3] == 's')
continue;
#endif
return new ProcMapsEntry (iter.start_address, candidate_path, iter.identity);
}
}
return null;
@@ -3332,18 +3362,24 @@ namespace Frida {
}
}
public string identity {
public string flags {
owned get {
return info.fetch (3);
}
}
public string path {
public string identity {
owned get {
return info.fetch (4);
}
}
public string path {
owned get {
return info.fetch (5);
}
}
public static MapsIter for_pid (uint pid) {
return new MapsIter (pid);
}
@@ -3355,8 +3391,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 ();
}
}
+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.
+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
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 -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',
-23
View File
@@ -303,26 +303,3 @@ index 78473d34e..f17e6f598 100644
+ mode = va_arg(args, /* mode_t */ unsigned int);
va_end(args);
}
diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h
index fbbc0e68c..d003c368d 100644
--- a/tools/include/nolibc/types.h
+++ b/tools/include/nolibc/types.h
@@ -92,6 +92,8 @@
#define FD_SETIDXMASK (8 * sizeof(unsigned long))
#define FD_SETBITMASK (8 * sizeof(unsigned long)-1)
+#ifndef __mips__
+
/* for select() */
typedef struct {
unsigned long fds[(FD_SETSIZE + FD_SETBITMASK) / FD_SETIDXMASK];
@@ -131,6 +133,8 @@ typedef struct {
__set->fds[__idx] = 0; \
} while (0)
+#endif
+
/* for poll() */
#define POLLIN 0x0001
#define POLLPRI 0x0002
+100
View File
@@ -0,0 +1,100 @@
#!/bin/bash
# This script is responsible for building Frida helpers for various Linux
# architectures. It can build helpers for a single specified architecture on the
# local machine, or for supported architectures in a container. The script uses
# Docker containers to ensure consistent build environments for each
# architecture.
#
# Note that the expectation is that when running the build for a specific
# architecture that it be run from inside the relevant container. This script is
# used by CI.
set -euo pipefail
CURRENT_FILE="${BASH_SOURCE[0]}"
HELPERS_DIR="$(cd "$(dirname "$CURRENT_FILE")" && pwd)"
FRIDA_CORE_DIR="$(cd "$HELPERS_DIR/../../.." && pwd)"
RELENG_DIR="$FRIDA_CORE_DIR/releng"
BUILD_DIR="$FRIDA_CORE_DIR/build"
RELATIVE_TO_FRIDA_CORE_DIR=$(realpath --relative-to="$FRIDA_CORE_DIR" "$CURRENT_FILE")
TMP_MESON_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_MESON_DIR"' EXIT
CONTAINER_REGISTRY="${CONTAINER_REGISTRY:-ghcr.io/frida}"
main () {
if [ "$#" -eq 0 ]; then
build_arches_in_container
return
fi
if [ "$#" -gt 1 ]; then
echo >&2 "Error: Too many arguments"
usage
fi
build_arch "$1"
}
usage () {
echo >&2 "Usage: $0 [<arch>]"
echo >&2 "If no arch is specified, then all helpers will be built in the container."
exit 1
}
ARCHS=(
x86
x86_64
arm
arm64
mips
mipsel
mips64
mips64el
)
build_arch () {
ARCH=$1
if [ -z "$ARCH" ]; then
usage
fi
if ! printf '%s\n' "${ARCHS[@]}" | grep -qx "$ARCH"; then
echo >&2 "Error: Invalid architecture '$ARCH'"
echo >&2 "Supported architectures: ${ARCHS[*]}"
exit 1
fi
if [[ "$ARCH" == arm* ]]; then
export FRIDA_HOST=android-$ARCH
else
export FRIDA_HOST=linux-$ARCH
fi
EXTRA_FLAGS=()
if [ "$FRIDA_HOST" == "linux-x86" ]; then
EXTRA_FLAGS+=("--build=linux-x86")
export CC="gcc -m32" CXX="g++ -m32" STRIP="strip"
fi
cd "$FRIDA_CORE_DIR"
rm -rf "$BUILD_DIR"
# Note that $XTOOLS_HOST is set by the container.
./configure --host="$XTOOLS_HOST" "${EXTRA_FLAGS[@]}"
make -C src/linux/helpers
}
build_arches_in_container () {
for ARCH in "${ARCHS[@]}"; do
docker run -u "$(id -u):$(id -g)" \
-w /frida-core \
-i -t \
-v "$FRIDA_CORE_DIR:/frida-core" \
"$CONTAINER_REGISTRY/core-linux-helpers-$ARCH:latest" \
"/frida-core/$RELATIVE_TO_FRIDA_CORE_DIR" "$ARCH"
done
}
main "$@"
+10 -9
View File
@@ -894,7 +894,7 @@ namespace Frida {
var scope = options.scope;
var scope_node = new Json.Node.alloc ().init_string (scope.to_nick ());
Json.Node result = yield call ("getFrontmostApplication", new Json.Node[] { scope_node }, cancellable);
Json.Node result = yield call ("getFrontmostApplication", new Json.Node[] { scope_node }, null, cancellable);
if (result.get_node_type () == NULL)
return HostApplicationInfo.empty ();
@@ -920,7 +920,7 @@ namespace Frida {
var scope = options.scope;
var scope_node = new Json.Node.alloc ().init_string (scope.to_nick ());
Json.Node apps = yield call ("enumerateApplications", new Json.Node[] { identifiers_node, scope_node },
Json.Node apps = yield call ("enumerateApplications", new Json.Node[] { identifiers_node, scope_node }, null,
cancellable);
var items = apps.get_array ();
@@ -946,7 +946,7 @@ namespace Frida {
var package_name_node = new Json.Node.alloc ().init_string (package);
var uid_node = new Json.Node.alloc ().init_int (uid);
Json.Node name = yield call ("getProcessName", new Json.Node[] { package_name_node, uid_node }, cancellable);
Json.Node name = yield call ("getProcessName", new Json.Node[] { package_name_node, uid_node }, null, cancellable);
return name.get_string ();
}
@@ -960,7 +960,8 @@ namespace Frida {
var scope_node = new Json.Node.alloc ().init_string (scope.to_nick ());
Json.Node by_pid = yield call ("getProcessParameters", new Json.Node[] { pids_node, scope_node }, cancellable);
Json.Node by_pid = yield call ("getProcessParameters", new Json.Node[] { pids_node, scope_node }, null,
cancellable);
var result = new Gee.HashMap<uint, HashTable<string, Variant>> ();
by_pid.get_object ().foreach_member ((object, pid_str, parameters_node) => {
@@ -982,20 +983,20 @@ namespace Frida {
if (entrypoint is DefaultActivityEntrypoint) {
var activity_node = new Json.Node.alloc ().init_null ();
yield call ("startActivity", new Json.Node[] { package_node, activity_node, uid_node }, cancellable);
yield call ("startActivity", new Json.Node[] { package_node, activity_node, uid_node }, null, cancellable);
} else if (entrypoint is ActivityEntrypoint) {
var e = entrypoint as ActivityEntrypoint;
var activity_node = new Json.Node.alloc ().init_string (e.activity);
yield call ("startActivity", new Json.Node[] { package_node, activity_node, uid_node }, cancellable);
yield call ("startActivity", new Json.Node[] { package_node, activity_node, uid_node }, null, cancellable);
} else if (entrypoint is BroadcastReceiverEntrypoint) {
var e = entrypoint as BroadcastReceiverEntrypoint;
var receiver_node = new Json.Node.alloc ().init_string (e.receiver);
var action_node = new Json.Node.alloc ().init_string (e.action);
yield call ("sendBroadcast", new Json.Node[] { package_node, receiver_node, action_node, uid_node },
yield call ("sendBroadcast", new Json.Node[] { package_node, receiver_node, action_node, uid_node }, null,
cancellable);
} else {
assert_not_reached ();
@@ -1006,13 +1007,13 @@ namespace Frida {
var package_node = new Json.Node.alloc ().init_string (package);
var uid_node = new Json.Node.alloc ().init_int (uid);
yield call ("stopPackage", new Json.Node[] { package_node, uid_node }, cancellable);
yield call ("stopPackage", new Json.Node[] { package_node, uid_node }, null, cancellable);
}
public async bool try_stop_package_by_pid (uint pid, Cancellable? cancellable) throws Error, IOError {
var pid_node = new Json.Node.alloc ().init_int (pid);
Json.Node success = yield call ("tryStopPackageByPid", new Json.Node[] { pid_node }, cancellable);
Json.Node success = yield call ("tryStopPackageByPid", new Json.Node[] { pid_node }, null, cancellable);
return success.get_boolean ();
}
+52 -1
View File
@@ -3,6 +3,7 @@
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include <gio/gunixmounts.h>
typedef struct _FridaEnumerateProcessesOperation FridaEnumerateProcessesOperation;
@@ -13,6 +14,8 @@ struct _FridaEnumerateProcessesOperation
};
static void frida_collect_process_info (guint pid, FridaEnumerateProcessesOperation * op);
static gboolean frida_is_directory_noexec (const gchar * directory);
static gchar * frida_get_application_directory (void);
static gboolean frida_add_process_metadata (GHashTable * parameters, const gchar * proc_entry_name);
static GDateTime * frida_query_boot_time (void);
static GVariant * frida_uid_to_name (uid_t uid);
@@ -151,12 +154,60 @@ frida_system_kill (guint pid)
gchar *
frida_temporary_directory_get_system_tmp (void)
{
const gchar * tmp_dir;
#ifdef HAVE_ANDROID
if (getuid () == 0)
return g_strdup ("/data/local/tmp");
#endif
return g_strdup (g_get_tmp_dir ());
tmp_dir = g_get_tmp_dir ();
/*
* If the temporary directory resides on a file-system which is marked
* `noexec`, then we won't be able to write the frida-agent.so there and
* subsequently dlopen() it inside the target application as it will result in
* permission denied.
*
* The mounting of the temporary file-system as `noexec` is sometimes used as
* an added security measure on embedded systems where the functionality is
* fixed and we aren't expecting any interactive user sessions.
*
* Since our current process is executing, we know that it must reside on a
* file-system which is not mounted `noexec`. Whilst it is possible that it is
* mounted read-only, or there may be some other reason why it isn't suitable,
* we know that the temporary directory is definitely unusable. If both these
* locations are found to be unsuitable, then a future implementation may seek
* to validate an ordered list of potential locations.
*/
if (frida_is_directory_noexec (tmp_dir))
return frida_get_application_directory ();
else
return g_strdup (tmp_dir);
}
static gboolean
frida_is_directory_noexec (const gchar * directory)
{
gboolean is_noexec;
g_autoptr(GUnixMountEntry) entry;
gchar ** options;
entry = g_unix_mount_for (directory, NULL);
if (entry == NULL)
return FALSE;
options = g_strsplit (g_unix_mount_get_options (entry), ",", 0);
is_noexec = g_strv_contains ((const char * const *) options, "noexec");
g_strfreev (options);
return is_noexec;
}
static gchar *
frida_get_application_directory (void)
{
return g_path_get_dirname (gum_process_get_main_module ()->path);
}
static gboolean
+65 -12
View File
@@ -44,6 +44,14 @@ if have_local_backend
'@INPUT0@',
]
if helper_emulated_modern == 'auto'
helper_emulated_modern = ''
endif
if helper_emulated_legacy == 'auto'
helper_emulated_legacy = ''
endif
if host_os_family == 'windows'
backend_sources += [
'windows' / 'windows-host-session.vala',
@@ -54,6 +62,7 @@ if have_local_backend
'windows' / 'frida-helper-process-glue.c',
'windows' / 'access-helpers.c',
]
backend_vala_args += '--pkg=frida-gum-windows-1.0'
helper_backend_sources = [
'windows' / 'frida-helper-backend.vala',
@@ -61,7 +70,10 @@ if have_local_backend
'windows' / 'frida-helper-types.vala',
'windows' / 'wait-handle-source.c',
]
helper_backend_vala_args = base_vala_args
helper_backend_vala_args = [
base_vala_args,
'--pkg=frida-gum-windows-1.0',
]
helper_backend = static_library('frida-helper-backend', helper_backend_sources,
vala_args: helper_backend_vala_args,
c_args: frida_component_cflags,
@@ -105,7 +117,7 @@ if have_local_backend
'frida-data-helper-process.c',
'frida-data-helper-process-blob' + resource_blob_extension,
],
command: embed_helper + [helper_modern, helper_legacy],
command: embed_helper + [helper_modern, helper_legacy, helper_emulated_modern, helper_emulated_legacy],
depends: helper_depends,
)
backend_sources += [helper_process_data]
@@ -177,6 +189,7 @@ if have_local_backend
helper_backend_vala_args,
gum_vala_args,
'--pkg=frida-gum-darwin-1.0',
'--pkg=gmodule-2.0',
'--pkg=gio-unix-2.0',
'--pkg=posix',
],
@@ -253,7 +266,7 @@ if have_local_backend
'frida-data-helper-process.c',
'frida-data-helper-process-blob' + resource_blob_extension,
],
command: embed_helper + [helper_modern, helper_legacy],
command: embed_helper + [helper_modern, helper_legacy, helper_emulated_modern, helper_emulated_legacy],
depends: helper_depends,
)
backend_sources += [helper_process_data]
@@ -406,7 +419,7 @@ if have_local_backend
'frida-data-helper-process.c',
'frida-data-helper-process-blob' + resource_blob_extension,
],
command: embed_helper + [helper_modern, helper_legacy],
command: embed_helper + [helper_modern, helper_legacy, helper_emulated_modern, helper_emulated_legacy],
depends: helper_depends,
)
backend_sources += [helper_process_data]
@@ -470,8 +483,8 @@ if have_local_backend
agent_legacy,
agent_emulated_modern,
agent_emulated_legacy,
get_option('agent_dbghelp_prefix'),
get_option('agent_symsrv_prefix'),
gum_dep.get_variable('frida_dbghelp_prefix'),
gum_dep.get_variable('frida_symsrv_prefix'),
],
depends: agent_depends,
)
@@ -481,6 +494,8 @@ endif
if have_fruity_backend
backend_sources += [
'fruity' / 'fruity-host-session.vala',
'fruity' / 'device-monitor.vala',
'fruity' / 'xpc.vala',
'fruity' / 'dtx.vala',
'fruity' / 'lockdown.vala',
'fruity' / 'installation-proxy.vala',
@@ -488,16 +503,53 @@ if have_fruity_backend
'fruity' / 'lldb.vala',
'fruity' / 'injector.vala',
'fruity' / 'usbmux.vala',
'fruity' / 'dns.vala',
'fruity' / 'keyed-archive.vala',
'fruity' / 'plist.vala',
'fruity' / 'plist-service.vala',
'fruity' / 'opack.vala',
'fruity' / 'network-stack.vala',
'fruity' / 'usb.vala',
'fruity' / 'ncm.vala',
]
if host_os_family == 'windows'
backend_sources += 'fruity' / 'device-monitor-windows.vala'
elif host_os == 'macos'
backend_sources += 'fruity' / 'device-monitor-macos.vala'
elif host_os == 'linux'
backend_sources += 'fruity' / 'device-monitor-linux.vala'
endif
if host_os_family == 'windows'
backend_sources += 'fruity' / 'device-monitor-windows.c'
elif host_os_family == 'darwin'
backend_sources += 'fruity' / 'device-monitor-darwin.m'
else
backend_sources += 'fruity' / 'device-monitor-unix.c'
endif
backend_deps += openssl_dep
backend_vala_args += [
'--pkg=libnghttp2',
'--pkg=libngtcp2',
'--pkg=libngtcp2_crypto_quictls',
'--pkg=libusb-1.0',
'--pkg=lwip',
'--pkg=openssl',
]
if host_os_family == 'windows'
backend_sources += 'fruity' / 'fruity-host-session-windows.c'
backend_vala_args += '--pkg=windows-windns'
elif host_os_family == 'darwin'
backend_sources += 'fruity' / 'fruity-host-session-darwin.m'
else
backend_sources += 'fruity' / 'fruity-host-session-unix.c'
backend_vala_args += [
'--pkg=darwin-xnu',
'--pkg=darwin-xpc',
'--pkg=darwin-gcd',
'--pkg=corefoundation',
]
endif
if host_os_family == 'linux'
backend_vala_args += '--pkg=linux'
endif
endif
@@ -529,8 +581,8 @@ subdir('compiler')
core_sources = base_sources + backend_sources + compiler_sources
internal_libs = [base, pipe] + backend_libs
internal_deps = [base_dep, pipe_dep]
internal_libs = [base, netif, pipe] + backend_libs
internal_deps = [base_dep, netif_dep, pipe_dep]
if host_os_family == 'windows'
gio_platform_vala_args = ['--pkg=gio-windows-2.0']
@@ -556,6 +608,7 @@ core_deps += backend_deps_private
core_vala_args = [
backend_vala_args,
base_vala_args,
'--pkg=gio-2.0',
gio_platform_vala_args,
'--pkg=json-glib-1.0',
+2 -4
View File
@@ -61,8 +61,7 @@ namespace Frida {
}
construct {
_device = new Device (null, "portal", "Portal", HostSessionProviderKind.LOCAL,
new PortalHostSessionProvider (this));
_device = new Device (null, new PortalHostSessionProvider (this));
cluster_service = new WebService (cluster_params, CLUSTER);
cluster_service.incoming.connect (on_incoming_cluster_connection);
@@ -930,9 +929,8 @@ namespace Frida {
}
public string name {
get { return _name; }
get { return "Portal"; }
}
private string _name = "Portal";
public Variant? icon {
get { return _icon; }
+161 -131
View File
@@ -2,6 +2,7 @@
#include <gio/gio.h>
#include <gum/gum.h>
#include <gum/arch-arm64/gumarm64writer.h>
#include <gum/arch-x86/gumx86writer.h>
#include <windows.h>
@@ -75,6 +76,7 @@ static void frida_propagate_open_process_error (guint32 pid, DWORD os_error, GEr
static gboolean frida_enable_debug_privilege (void);
static gboolean frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjectionDetails * details, GError ** error);
static gsize frida_remote_worker_context_emit_payload (FridaRemoteWorkerContext * rwc, gpointer code);
static void frida_remote_worker_context_destroy (FridaRemoteWorkerContext * rwc, FridaInjectionDetails * details);
static gboolean frida_remote_worker_context_has_resolved_all_kernel32_functions (const FridaRemoteWorkerContext * rwc);
@@ -304,16 +306,169 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
{
gpointer code;
guint code_size;
GumX86Writer cw;
const gchar * loadlibrary_failed = "loadlibrary_failed";
const gchar * skip_unload = "skip_unload";
const gchar * return_result = "return_result";
SIZE_T page_size, alloc_size;
DWORD old_protect;
gum_init ();
code = gum_alloc_n_pages (1, GUM_PAGE_RWX); /* Executable so debugger can be used to inspect code */
code_size = frida_remote_worker_context_emit_payload (rwc, code);
memset (rwc, 0, sizeof (FridaRemoteWorkerContext));
gum_module_enumerate_exports ("kernel32.dll", frida_remote_worker_context_collect_kernel32_export, rwc);
if (!frida_remote_worker_context_has_resolved_all_kernel32_functions (rwc))
goto failed_to_resolve_kernel32_functions;
StringCbCopyW (rwc->dll_path, sizeof (rwc->dll_path), details->dll_path);
StringCbCopyA (rwc->entrypoint_name, sizeof (rwc->entrypoint_name), details->entrypoint_name);
StringCbCopyA (rwc->entrypoint_data, sizeof (rwc->entrypoint_data), details->entrypoint_data);
page_size = gum_query_page_size ();
g_assert (code_size <= page_size);
alloc_size = page_size + sizeof (FridaRemoteWorkerContext);
rwc->entrypoint = VirtualAllocEx (details->process_handle, NULL, alloc_size, MEM_COMMIT, PAGE_READWRITE);
if (rwc->entrypoint == NULL)
goto virtual_alloc_ex_failed;
if (!WriteProcessMemory (details->process_handle, rwc->entrypoint, code, code_size, NULL))
goto write_process_memory_failed;
rwc->argument = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (rwc->entrypoint) + page_size);
if (!WriteProcessMemory (details->process_handle, rwc->argument, rwc, sizeof (FridaRemoteWorkerContext), NULL))
goto write_process_memory_failed;
if (!VirtualProtectEx (details->process_handle, rwc->entrypoint, page_size, PAGE_EXECUTE_READ, &old_protect))
goto virtual_protect_ex_failed;
gum_free_pages (code);
return TRUE;
/* ERRORS */
failed_to_resolve_kernel32_functions:
{
g_set_error (error,
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error while resolving kernel32 functions");
goto error_common;
}
virtual_alloc_ex_failed:
{
g_set_error (error,
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error allocating memory in target process (VirtualAllocEx returned 0x%08lx)",
GetLastError ());
goto error_common;
}
write_process_memory_failed:
{
g_set_error (error,
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error writing to memory in target process (WriteProcessMemory returned 0x%08lx)",
GetLastError ());
goto error_common;
}
virtual_protect_ex_failed:
{
g_set_error (error,
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error changing memory permission in target process (VirtualProtectEx returned 0x%08lx)",
GetLastError ());
goto error_common;
}
error_common:
{
frida_remote_worker_context_destroy (rwc, details);
gum_free_pages (code);
return FALSE;
}
}
#define EMIT_ARM64_LOAD(reg, field) \
gum_arm64_writer_put_ldr_reg_reg_offset (&cw, ARM64_REG_##reg, ARM64_REG_X20, G_STRUCT_OFFSET (FridaRemoteWorkerContext, field))
#define EMIT_ARM64_LOAD_ADDRESS_OF(reg, field) \
gum_arm64_writer_put_add_reg_reg_imm (&cw, ARM64_REG_##reg, ARM64_REG_X20, G_STRUCT_OFFSET (FridaRemoteWorkerContext, field))
#define EMIT_ARM64_MOVE(dstreg, srcreg) \
gum_arm64_writer_put_mov_reg_reg (&cw, ARM64_REG_##dstreg, ARM64_REG_##srcreg)
#define EMIT_ARM64_CALL(reg) \
gum_arm64_writer_put_blr_reg_no_auth (&cw, ARM64_REG_##reg)
static gsize
frida_remote_worker_context_emit_payload (FridaRemoteWorkerContext * rwc, gpointer code)
{
gsize code_size;
const gchar * loadlibrary_failed = "loadlibrary_failed";
const gchar * skip_unload = "skip_unload";
const gchar * return_result = "return_result";
#ifdef HAVE_ARM64
GumArm64Writer cw;
gum_arm64_writer_init (&cw, code);
gum_arm64_writer_put_push_reg_reg (&cw, ARM64_REG_FP, ARM64_REG_LR);
gum_arm64_writer_put_mov_reg_reg (&cw, ARM64_REG_FP, ARM64_REG_SP);
gum_arm64_writer_put_push_reg_reg (&cw, ARM64_REG_X19, ARM64_REG_X20);
/* x20 = (FridaRemoteWorkerContext *) lpParameter */
EMIT_ARM64_MOVE (X20, X0);
/* x19 = LoadLibrary (x20->dll_path) */
EMIT_ARM64_LOAD_ADDRESS_OF (X0, dll_path);
EMIT_ARM64_LOAD (X8, load_library_impl);
EMIT_ARM64_CALL (X8);
gum_arm64_writer_put_cbz_reg_label (&cw, ARM64_REG_X0, loadlibrary_failed);
EMIT_ARM64_MOVE (X19, X0);
/* x8 = GetProcAddress (x19, x20->entrypoint_name) */
EMIT_ARM64_MOVE (X0, X19);
EMIT_ARM64_LOAD_ADDRESS_OF (X1, entrypoint_name);
EMIT_ARM64_LOAD (X8, get_proc_address_impl);
EMIT_ARM64_CALL (X8);
EMIT_ARM64_MOVE (X8, X0);
/* x8 (x20->entrypoint_data, &x20->stay_resident, NULL) */
EMIT_ARM64_LOAD_ADDRESS_OF (X0, entrypoint_data);
EMIT_ARM64_LOAD_ADDRESS_OF (X1, stay_resident);
EMIT_ARM64_MOVE (X2, XZR);
EMIT_ARM64_CALL (X8);
/* if (!x20->stay_resident) { */
EMIT_ARM64_LOAD (X0, stay_resident);
gum_arm64_writer_put_cbnz_reg_label (&cw, ARM64_REG_X0, skip_unload);
/* FreeLibrary (xsi) */
EMIT_ARM64_MOVE (X0, X19);
EMIT_ARM64_LOAD (X8, free_library_impl);
EMIT_ARM64_CALL (X8);
/* } */
gum_arm64_writer_put_label (&cw, skip_unload);
/* result = ERROR_SUCCESS */
EMIT_ARM64_MOVE (X0, XZR);
gum_arm64_writer_put_b_label (&cw, return_result);
gum_arm64_writer_put_label (&cw, loadlibrary_failed);
/* result = GetLastError() */
EMIT_ARM64_LOAD (X8, get_last_error_impl);
EMIT_ARM64_CALL (X8);
gum_arm64_writer_put_label (&cw, return_result);
gum_arm64_writer_put_pop_reg_reg (&cw, ARM64_REG_X19, ARM64_REG_X20);
gum_arm64_writer_put_pop_reg_reg (&cw, ARM64_REG_FP, ARM64_REG_LR);
gum_arm64_writer_put_ret (&cw);
gum_arm64_writer_flush (&cw);
code_size = gum_arm64_writer_offset (&cw);
gum_arm64_writer_clear (&cw);
#else
GumX86Writer cw;
gum_x86_writer_init (&cw, code);
/* Will clobber these */
@@ -393,80 +548,9 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
gum_x86_writer_flush (&cw);
code_size = gum_x86_writer_offset (&cw);
gum_x86_writer_clear (&cw);
#endif
memset (rwc, 0, sizeof (FridaRemoteWorkerContext));
gum_module_enumerate_exports ("kernel32.dll", frida_remote_worker_context_collect_kernel32_export, rwc);
if (!frida_remote_worker_context_has_resolved_all_kernel32_functions (rwc))
goto failed_to_resolve_kernel32_functions;
StringCbCopyW (rwc->dll_path, sizeof (rwc->dll_path), details->dll_path);
StringCbCopyA (rwc->entrypoint_name, sizeof (rwc->entrypoint_name), details->entrypoint_name);
StringCbCopyA (rwc->entrypoint_data, sizeof (rwc->entrypoint_data), details->entrypoint_data);
page_size = gum_query_page_size ();
g_assert (code_size <= page_size);
alloc_size = page_size + sizeof (FridaRemoteWorkerContext);
rwc->entrypoint = VirtualAllocEx (details->process_handle, NULL, alloc_size, MEM_COMMIT, PAGE_READWRITE);
if (rwc->entrypoint == NULL)
goto virtual_alloc_ex_failed;
if (!WriteProcessMemory (details->process_handle, rwc->entrypoint, code, code_size, NULL))
goto write_process_memory_failed;
rwc->argument = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (rwc->entrypoint) + page_size);
if (!WriteProcessMemory (details->process_handle, rwc->argument, rwc, sizeof (FridaRemoteWorkerContext), NULL))
goto write_process_memory_failed;
if (!VirtualProtectEx (details->process_handle, rwc->entrypoint, page_size, PAGE_EXECUTE_READ, &old_protect))
goto virtual_protect_ex_failed;
gum_free_pages (code);
return TRUE;
/* ERRORS */
failed_to_resolve_kernel32_functions:
{
g_set_error (error,
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error while resolving kernel32 functions");
goto error_common;
}
virtual_alloc_ex_failed:
{
g_set_error (error,
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error allocating memory in target process (VirtualAllocEx returned 0x%08lx)",
GetLastError ());
goto error_common;
}
write_process_memory_failed:
{
g_set_error (error,
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error writing to memory in target process (WriteProcessMemory returned 0x%08lx)",
GetLastError ());
goto error_common;
}
virtual_protect_ex_failed:
{
g_set_error (error,
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error changing memory permission in target process (VirtualProtectEx returned 0x%08lx)",
GetLastError ());
goto error_common;
}
error_common:
{
frida_remote_worker_context_destroy (rwc, details);
gum_free_pages (code);
return FALSE;
}
return code_size;
}
static void
@@ -508,60 +592,6 @@ frida_remote_worker_context_collect_kernel32_export (const GumExportDetails * de
return TRUE;
}
gboolean
frida_windows_system_is_x64 (void)
{
static gboolean initialized = FALSE;
static gboolean system_is_x64;
if (!initialized)
{
SYSTEM_INFO si;
GetNativeSystemInfo (&si);
system_is_x64 = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
initialized = TRUE;
}
return system_is_x64;
}
gboolean
frida_windows_process_is_x64 (guint32 pid, GError ** error)
{
HANDLE process_handle;
BOOL is_wow64, success;
if (!frida_windows_system_is_x64 ())
return FALSE;
process_handle = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, pid);
if (process_handle == NULL)
goto open_failed;
success = IsWow64Process (process_handle, &is_wow64);
CloseHandle (process_handle);
if (!success)
goto query_failed;
return !is_wow64;
open_failed:
{
frida_propagate_open_process_error (pid, GetLastError (), error);
return FALSE;
}
query_failed:
{
g_set_error (error,
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error while interrogating process with pid %u (IsWow64Process failed)",
pid);
return FALSE;
}
}
static gboolean
frida_file_exists_and_is_readable (const WCHAR * filename)
{
+27 -9
View File
@@ -49,7 +49,7 @@ namespace Frida {
public async void inject_library_file (uint pid, PathTemplate path_template, string entrypoint, string data,
string[] dependencies, uint id, Cancellable? cancellable) throws Error {
string path = path_template.expand (WindowsProcess.is_x64 (pid) ? "64" : "32");
string path = path_template.expand (arch_name_from_pid (pid));
string target_dependent_path;
if (level == ELEVATED) {
@@ -101,6 +101,32 @@ namespace Frida {
protected extern static void _free_inject_instance (void * inject_instance, out bool is_resident);
}
public unowned string arch_name_from_pid (uint pid) throws Error {
switch (cpu_type_from_pid (pid)) {
case Gum.CpuType.IA32:
return "x86";
case Gum.CpuType.AMD64:
return "x86_64";
case Gum.CpuType.ARM64:
return "arm64";
default:
assert_not_reached ();
}
}
public Gum.CpuType cpu_type_from_pid (uint pid) throws Error {
try {
return Gum.Windows.cpu_type_from_pid (pid);
} catch (Gum.Error e) {
if (e is Gum.Error.NOT_FOUND)
throw new Error.PROCESS_NOT_FOUND ("Unable to find process with pid %u", pid);
else if (e is Gum.Error.PERMISSION_DENIED)
throw new Error.PERMISSION_DENIED ("Unable to access process with pid %u", pid);
else
throw new Error.NOT_SUPPORTED ("%s", e.message);
}
}
private class AssetDirectory {
public File file {
get;
@@ -206,14 +232,6 @@ namespace Frida {
}
}
namespace WindowsSystem {
public extern static bool is_x64 ();
}
namespace WindowsProcess {
public extern static bool is_x64 (uint32 pid) throws Error;
}
namespace WaitHandleSource {
public extern Source create (void * handle, bool owns_handle);
}
+33 -24
View File
@@ -57,9 +57,7 @@ namespace Frida {
string[] dependencies, uint id, Cancellable? cancellable) throws Error, IOError {
bool permission_denied = false;
try {
unowned string local_arch = sizeof (void *) == 8 ? "64" : "32";
unowned string remote_arch = WindowsProcess.is_x64 (pid) ? "64" : "32";
if (remote_arch == local_arch) {
if (cpu_type_from_pid (pid) == Gum.NATIVE_CPU) {
yield inprocess_backend.inject_library_file (pid, path_template, entrypoint, data, dependencies,
id, cancellable);
return;
@@ -172,15 +170,11 @@ namespace Frida {
Error? error = null;
try {
string native_helper_path = WindowsSystem.is_x64 ()
? resource_store.helper64.path
: resource_store.helper32.path;
string level_str = (level == PrivilegeLevel.ELEVATED) ? "ELEVATED" : "NORMAL";
void * process = spawn (native_helper_path,
void * process = spawn (resource_store.native_helper.path,
"MANAGER %s %s".printf (level_str, transport.remote_address),
level);
instance = new HelperInstance (resource_store.helper32, resource_store.helper64, transport,
stream_request, process);
instance = new HelperInstance (resource_store.helpers, transport, stream_request, process);
} catch (Error e) {
error = e;
}
@@ -247,8 +241,7 @@ namespace Frida {
}
}
private TemporaryFile helper32;
private TemporaryFile helper64;
private Gee.Collection<TemporaryFile> helpers;
private PipeTransport transport;
private Future<IOStream> stream_request;
private DBusConnection connection;
@@ -257,10 +250,9 @@ namespace Frida {
private Gee.Set<uint> injectee_ids = new Gee.HashSet<uint> ();
public HelperInstance (TemporaryFile helper32, TemporaryFile helper64, PipeTransport transport,
Future<IOStream> stream_request, void * process) {
this.helper32 = helper32;
this.helper64 = helper64;
public HelperInstance (Gee.Collection<TemporaryFile> helpers, PipeTransport transport, Future<IOStream> stream_request,
void * process) {
this.helpers = helpers;
this.transport = transport;
this.stream_request = stream_request;
this.process = process;
@@ -375,26 +367,43 @@ namespace Frida {
}
private class ResourceStore {
public TemporaryFile helper32 {
public TemporaryFile native_helper {
get;
private set;
}
public TemporaryFile helper64 {
public Gee.Collection<TemporaryFile> helpers {
get;
private set;
default = new Gee.ArrayList<TemporaryFile> ();
}
public ResourceStore (TemporaryDirectory tempdir) throws Error {
var blob32 = Frida.Data.Helper.get_frida_helper_32_exe_blob ();
helper32 = new TemporaryFile.from_stream ("frida-helper-32.exe",
new MemoryInputStream.from_data (blob32.data, null),
tempdir);
var helper_arm64 = add_helper ("arm64", Frida.Data.Helper.get_frida_helper_arm64_exe_blob (), tempdir);
var helper_x86_64 = add_helper ("x86_64", Frida.Data.Helper.get_frida_helper_x86_64_exe_blob (), tempdir);
var helper_x86 = add_helper ("x86", Frida.Data.Helper.get_frida_helper_x86_exe_blob (), tempdir);
var blob64 = Frida.Data.Helper.get_frida_helper_64_exe_blob ();
helper64 = new TemporaryFile.from_stream ("frida-helper-64.exe",
new MemoryInputStream.from_data (blob64.data, null),
switch (Gum.Windows.query_native_cpu_type ()) {
case ARM64:
native_helper = helper_arm64;
break;
case AMD64:
native_helper = helper_x86_64;
break;
case IA32:
native_helper = helper_x86;
break;
default:
assert_not_reached ();
}
}
private TemporaryFile add_helper (string name, Frida.Data.Helper.Blob blob, TemporaryDirectory tempdir) throws Error {
var file = new TemporaryFile.from_stream (@"frida-helper-$name.exe",
new MemoryInputStream.from_data (blob.data, null),
tempdir);
helpers.add (file);
return file;
}
}
}
+75 -102
View File
@@ -2,10 +2,12 @@
#include <windows.h>
#if GLIB_SIZEOF_VOID_P == 8
# define FRIDA_HELPER_SERVICE_ARCH "64"
#if defined (HAVE_ARM64)
# define FRIDA_HELPER_SERVICE_ARCH "arm64"
#elif GLIB_SIZEOF_VOID_P == 8
# define FRIDA_HELPER_SERVICE_ARCH "x86_64"
#else
# define FRIDA_HELPER_SERVICE_ARCH "32"
# define FRIDA_HELPER_SERVICE_ARCH "x86"
#endif
#define STANDALONE_JOIN_TIMEOUT_MSEC (5 * 1000)
@@ -17,25 +19,23 @@ struct _FridaServiceContext
gchar * service_basename;
SC_HANDLE scm;
SC_HANDLE service32;
SC_HANDLE service64;
HANDLE standalone32;
HANDLE standalone64;
GQueue system_services;
GQueue standalone_services;
};
static void WINAPI frida_managed_helper_service_main (DWORD argc, WCHAR ** argv);
static DWORD WINAPI frida_managed_helper_service_handle_control_code (DWORD control, DWORD event_type, void * event_data, void * context);
static void frida_managed_helper_service_report_status (DWORD current_state, DWORD exit_code, DWORD wait_hint);
static gboolean frida_register_and_start_services (FridaServiceContext * self);
static gboolean frida_register_and_start_services (FridaServiceContext * self, gchar ** archs, gint archs_length);
static void frida_stop_and_unregister_services (FridaServiceContext * self);
static gboolean frida_spawn_standalone_services (FridaServiceContext * self);
static gboolean frida_spawn_standalone_services (FridaServiceContext * self, gchar ** archs, gint archs_length);
static gboolean frida_join_standalone_services (FridaServiceContext * self);
static void frida_kill_standalone_services (FridaServiceContext * self);
static void frida_release_standalone_services (FridaServiceContext * self);
static gboolean frida_register_services (FridaServiceContext * self);
static gboolean frida_register_services (FridaServiceContext * self, gchar ** archs, gint archs_length);
static gboolean frida_unregister_services (FridaServiceContext * self);
static gboolean frida_start_services (FridaServiceContext * self);
static gboolean frida_stop_services (FridaServiceContext * self);
@@ -59,7 +59,7 @@ static WCHAR * frida_managed_helper_service_name = NULL;
static SERVICE_STATUS_HANDLE frida_managed_helper_service_status_handle = NULL;
void *
frida_helper_manager_start_services (const char * service_basename, FridaPrivilegeLevel level)
frida_helper_manager_start_services (const char * service_basename, gchar ** archs, gint archs_length, FridaPrivilegeLevel level)
{
FridaServiceContext * self;
@@ -72,7 +72,7 @@ frida_helper_manager_start_services (const char * service_basename, FridaPrivile
{
frida_unregister_stale_services (self);
if (!frida_register_and_start_services (self))
if (!frida_register_and_start_services (self, archs, archs_length))
{
CloseServiceHandle (self->scm);
self->scm = NULL;
@@ -81,7 +81,7 @@ frida_helper_manager_start_services (const char * service_basename, FridaPrivile
if (self->scm == NULL)
{
if (!frida_spawn_standalone_services (self))
if (!frida_spawn_standalone_services (self, archs, archs_length))
{
frida_service_context_free (self);
self = NULL;
@@ -138,15 +138,16 @@ char *
frida_helper_service_derive_filename_for_suffix (const char * suffix)
{
WCHAR filename_utf16[MAX_PATH + 1] = { 0, };
gchar * name, * tmp;
gchar * name, * tail, * tmp;
glong len;
GetModuleFileNameW (NULL, filename_utf16, MAX_PATH);
name = g_utf16_to_utf8 (filename_utf16, -1, NULL, &len, NULL);
if (g_str_has_suffix (name, "-32.exe") || g_str_has_suffix (name, "-64.exe"))
tail = strrchr (name, '-');
if (tail != NULL)
{
name[len - 6] = '\0';
tail[1] = '\0';
tmp = g_strconcat (name, suffix, ".exe", NULL);
g_free (name);
name = tmp;
@@ -293,9 +294,9 @@ frida_managed_helper_service_report_status (DWORD current_state, DWORD exit_code
}
static gboolean
frida_register_and_start_services (FridaServiceContext * self)
frida_register_and_start_services (FridaServiceContext * self, gchar ** archs, gint archs_length)
{
if (!frida_register_services (self))
if (!frida_register_services (self, archs, archs_length))
return FALSE;
if (!frida_start_services (self))
@@ -315,44 +316,35 @@ frida_stop_and_unregister_services (FridaServiceContext * self)
}
static gboolean
frida_spawn_standalone_services (FridaServiceContext * self)
frida_spawn_standalone_services (FridaServiceContext * self, gchar ** archs, gint archs_length)
{
HANDLE standalone32, standalone64;
gint i;
standalone32 = frida_spawn_standalone_service (self, "32");
if (standalone32 == NULL)
return FALSE;
if (frida_windows_system_is_x64 ())
for (i = 0; i != archs_length; i++)
{
standalone64 = frida_spawn_standalone_service (self, "64");
if (standalone64 == NULL)
{
frida_kill_standalone_service (self, standalone32);
CloseHandle (standalone32);
return FALSE;
}
HANDLE service = frida_spawn_standalone_service (self, archs[i]);
if (service == NULL)
goto unable_to_spawn;
g_queue_push_tail (&self->standalone_services, service);
}
else
{
standalone64 = NULL;
}
self->standalone32 = standalone32;
self->standalone64 = standalone64;
return TRUE;
unable_to_spawn:
{
frida_kill_standalone_services (self);
return FALSE;
}
}
static gboolean
frida_join_standalone_services (FridaServiceContext * self)
{
gboolean success = TRUE;
GList * cur;
if (frida_windows_system_is_x64 ())
success &= frida_join_standalone_service (self, self->standalone64);
success &= frida_join_standalone_service (self, self->standalone32);
for (cur = self->standalone_services.head; cur != NULL; cur = cur->next)
success &= frida_join_standalone_service (self, cur->data);
if (success)
frida_release_standalone_services (self);
@@ -363,10 +355,10 @@ frida_join_standalone_services (FridaServiceContext * self)
static void
frida_kill_standalone_services (FridaServiceContext * self)
{
if (frida_windows_system_is_x64 ())
frida_kill_standalone_service (self, self->standalone64);
GList * cur;
frida_kill_standalone_service (self, self->standalone32);
for (cur = self->standalone_services.head; cur != NULL; cur = cur->next)
frida_kill_standalone_service (self, cur->data);
frida_release_standalone_services (self);
}
@@ -374,94 +366,77 @@ frida_kill_standalone_services (FridaServiceContext * self)
static void
frida_release_standalone_services (FridaServiceContext * self)
{
if (frida_windows_system_is_x64 ())
{
g_assert (self->standalone64 != NULL);
CloseHandle (self->standalone64);
self->standalone64 = NULL;
}
HANDLE service;
g_assert (self->standalone32 != NULL);
CloseHandle (self->standalone32);
self->standalone32 = NULL;
while ((service = g_queue_pop_tail (&self->standalone_services)) != NULL)
CloseHandle (service);
}
static gboolean
frida_register_services (FridaServiceContext * self)
frida_register_services (FridaServiceContext * self, gchar ** archs, gint archs_length)
{
SC_HANDLE service32, service64;
gint i;
service32 = frida_register_service (self, "32");
if (service32 == NULL)
return FALSE;
if (frida_windows_system_is_x64 ())
for (i = 0; i != archs_length; i++)
{
service64 = frida_register_service (self, "64");
if (service64 == NULL)
{
frida_unregister_service (self, service32);
CloseServiceHandle (service32);
return FALSE;
}
SC_HANDLE service = frida_register_service (self, archs[i]);
if (service == NULL)
goto unable_to_register;
g_queue_push_tail (&self->system_services, service);
}
else
{
service64 = NULL;
}
self->service32 = service32;
self->service64 = service64;
return TRUE;
unable_to_register:
{
frida_unregister_services (self);
return FALSE;
}
}
static gboolean
frida_unregister_services (FridaServiceContext * self)
{
gboolean success = TRUE;
SC_HANDLE service;
if (frida_windows_system_is_x64 ())
while ((service = g_queue_pop_tail (&self->system_services)) != NULL)
{
success &= frida_unregister_service (self, self->service64);
CloseServiceHandle (self->service64);
self->service64 = NULL;
success &= frida_unregister_service (self, service);
CloseServiceHandle (service);
}
success &= frida_unregister_service (self, self->service32);
CloseServiceHandle (self->service32);
self->service32 = NULL;
return success;
}
static gboolean
frida_start_services (FridaServiceContext * self)
{
if (!frida_start_service (self, self->service32))
return FALSE;
GList * cur;
if (frida_windows_system_is_x64 ())
for (cur = self->system_services.head; cur != NULL; cur = cur->next)
{
if (!frida_start_service (self, self->service64))
{
frida_stop_service (self, self->service32);
return FALSE;
}
if (!frida_start_service (self, cur->data))
goto unable_to_start;
}
return TRUE;
unable_to_start:
{
frida_stop_services (self);
return FALSE;
}
}
static gboolean
frida_stop_services (FridaServiceContext * self)
{
gboolean success = TRUE;
GList * cur;
if (frida_windows_system_is_x64 ())
success &= frida_stop_service (self, self->service64);
success &= frida_stop_service (self, self->service32);
for (cur = self->system_services.head; cur != NULL; cur = cur->next)
success &= frida_stop_service (self, cur->data);
return success;
}
@@ -480,7 +455,7 @@ frida_register_service (FridaServiceContext * self, const gchar * suffix)
servicename_utf8 = g_strconcat (self->service_basename, suffix, NULL);
servicename = g_utf8_to_utf16 (servicename_utf8, -1, NULL, NULL, NULL);
displayname_utf8 = g_strdup_printf ("Frida %s-bit helper (%s)", suffix, servicename_utf8);
displayname_utf8 = g_strdup_printf ("Frida %s helper (%s)", suffix, servicename_utf8);
displayname = g_utf8_to_utf16 (displayname_utf8, -1, NULL, NULL, NULL);
filename_utf8 = frida_helper_service_derive_filename_for_suffix (suffix);
@@ -714,6 +689,7 @@ frida_service_context_new (const gchar * service_basename)
self = g_slice_new0 (FridaServiceContext);
self->service_basename = g_strdup (service_basename);
g_queue_init (&self->standalone_services);
return self;
}
@@ -721,11 +697,8 @@ frida_service_context_new (const gchar * service_basename)
static void
frida_service_context_free (FridaServiceContext * self)
{
g_assert (self->standalone64 == NULL);
g_assert (self->standalone32 == NULL);
g_assert (self->service64 == NULL);
g_assert (self->service32 == NULL);
g_assert (g_queue_is_empty (&self->system_services));
g_assert (g_queue_is_empty (&self->standalone_services));
if (self->scm != NULL)
CloseServiceHandle (self->scm);
+2 -1
View File
@@ -5,7 +5,8 @@
G_BEGIN_DECLS
G_GNUC_INTERNAL void * frida_helper_manager_start_services (const char * service_basename, FridaPrivilegeLevel level);
G_GNUC_INTERNAL void * frida_helper_manager_start_services (const char * service_basename, gchar ** archs, gint archs_length,
FridaPrivilegeLevel level);
G_GNUC_INTERNAL void frida_helper_manager_stop_services (void * context);
G_GNUC_INTERNAL char * frida_helper_service_derive_basename (void);
+26 -29
View File
@@ -60,8 +60,7 @@ namespace Frida {
private DBusConnection connection;
private uint registration_id;
private ServiceConnection helper32;
private ServiceConnection? helper64;
private Gee.Collection<ServiceConnection> helpers = new Gee.ArrayList<ServiceConnection> ();
private void * context;
public HelperManager (string parent_address, PrivilegeLevel level) {
@@ -98,18 +97,23 @@ namespace Frida {
private async void start () {
try {
helper32 = new ServiceConnection (HelperService.derive_svcname_for_suffix ("32"));
if (WindowsSystem.is_x64 ())
helper64 = new ServiceConnection (HelperService.derive_svcname_for_suffix ("64"));
var archs = new Gee.ArrayList<string> ();
if (Gum.Windows.query_native_cpu_type () == ARM64)
archs.add ("arm64");
if (Gum.Windows.query_native_cpu_type () != IA32)
archs.add ("x86_64");
archs.add ("x86");
context = start_services (HelperService.derive_basename (), level);
foreach (string arch in archs) {
var helper = new ServiceConnection (HelperService.derive_svcname_for_suffix (arch));
helpers.add (helper);
}
yield helper32.open ();
helper32.proxy.uninjected.connect (on_uninjected);
context = start_services (HelperService.derive_basename (), archs.to_array (), level);
if (helper64 != null) {
yield helper64.open ();
helper64.proxy.uninjected.connect (on_uninjected);
foreach (var helper in helpers) {
yield helper.open ();
helper.proxy.uninjected.connect (on_uninjected);
}
var stream_request = Pipe.open (parent_address, null);
@@ -130,20 +134,14 @@ namespace Frida {
}
public async void stop (Cancellable? cancellable) throws Error, IOError {
if (helper64 != null) {
foreach (var helper in helpers) {
try {
yield helper64.proxy.stop (cancellable);
yield helper.proxy.stop (cancellable);
} catch (GLib.Error e) {
if (e is IOError.CANCELLED)
throw (IOError) e;
}
}
try {
yield helper32.proxy.stop (cancellable);
} catch (GLib.Error e) {
if (e is IOError.CANCELLED)
throw (IOError) e;
}
Timeout.add (20, () => {
shutdown.begin ();
@@ -158,13 +156,14 @@ namespace Frida {
public async void inject_library_file (uint pid, PathTemplate path_template, string entrypoint, string data,
string[] dependencies, uint id, Cancellable? cancellable) throws Error, IOError {
try {
if (helper64 != null && yield helper64.proxy.can_handle_target (pid, cancellable)) {
yield helper64.proxy.inject_library_file (pid, path_template, entrypoint, data, dependencies, id,
cancellable);
} else {
yield helper32.proxy.inject_library_file (pid, path_template, entrypoint, data, dependencies, id,
cancellable);
foreach (var helper in helpers) {
if (yield helper.proxy.can_handle_target (pid, cancellable)) {
yield helper.proxy.inject_library_file (pid, path_template, entrypoint, data, dependencies,
id, cancellable);
return;
}
}
throw new Error.NOT_SUPPORTED ("Missing helper able to handle the given target");
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -210,7 +209,7 @@ namespace Frida {
}
}
private extern static void * start_services (string service_basename, PrivilegeLevel level);
private extern static void * start_services (string service_basename, string[] archs, PrivilegeLevel level);
private extern static void stop_services (void * context);
}
@@ -286,9 +285,7 @@ namespace Frida {
}
public async bool can_handle_target (uint pid, Cancellable? cancellable) throws Error, IOError {
uint local_arch = sizeof (void *) == 8 ? 64 : 32;
uint remote_arch = WindowsProcess.is_x64 (pid) ? 64 : 32;
return local_arch == remote_arch;
return cpu_type_from_pid (pid) == Gum.NATIVE_CPU;
}
public async void inject_library_file (uint pid, PathTemplate path_template, string entrypoint, string data,
+32 -14
View File
@@ -113,21 +113,23 @@ namespace Frida {
injector = new Winjector (helper, false, tempdir);
injector.uninjected.connect (on_uninjected);
var blob32 = Frida.Data.Agent.get_frida_agent_32_dll_blob ();
var blob64 = Frida.Data.Agent.get_frida_agent_64_dll_blob ();
var dbghelp32 = Frida.Data.Agent.get_dbghelp_32_dll_blob ();
var dbghelp64 = Frida.Data.Agent.get_dbghelp_64_dll_blob ();
var symsrv32 = Frida.Data.Agent.get_symsrv_32_dll_blob ();
var symsrv64 = Frida.Data.Agent.get_symsrv_64_dll_blob ();
agent = new AgentDescriptor (PathTemplate ("<arch>\\frida-agent.dll"),
new Bytes.static (blob32.data),
new Bytes.static (blob64.data),
new Bytes.static (Frida.Data.Agent.get_frida_agent_arm64_dll_blob ().data),
new Bytes.static (Frida.Data.Agent.get_frida_agent_x86_64_dll_blob ().data),
new Bytes.static (Frida.Data.Agent.get_frida_agent_x86_dll_blob ().data),
new AgentResource[] {
new AgentResource ("32\\dbghelp.dll", new Bytes.static (dbghelp32.data), tempdir),
new AgentResource ("32\\symsrv.dll", new Bytes.static (symsrv32.data), tempdir),
new AgentResource ("64\\dbghelp.dll", new Bytes.static (dbghelp64.data), tempdir),
new AgentResource ("64\\symsrv.dll", new Bytes.static (symsrv64.data), tempdir)
new AgentResource ("arm64\\dbghelp.dll",
new Bytes.static (Frida.Data.Agent.get_dbghelp_arm64_dll_blob ().data), tempdir),
new AgentResource ("arm64\\symsrv.dll",
new Bytes.static (Frida.Data.Agent.get_symsrv_arm64_dll_blob ().data), tempdir),
new AgentResource ("x86_64\\dbghelp.dll",
new Bytes.static (Frida.Data.Agent.get_dbghelp_x86_64_dll_blob ().data), tempdir),
new AgentResource ("x86_64\\symsrv.dll",
new Bytes.static (Frida.Data.Agent.get_symsrv_x86_64_dll_blob ().data), tempdir),
new AgentResource ("x86\\dbghelp.dll",
new Bytes.static (Frida.Data.Agent.get_dbghelp_x86_dll_blob ().data), tempdir),
new AgentResource ("x86\\symsrv.dll",
new Bytes.static (Frida.Data.Agent.get_symsrv_x86_dll_blob ().data), tempdir),
},
tempdir
);
@@ -164,7 +166,23 @@ namespace Frida {
protected override async AgentSessionProvider create_system_session_provider (Cancellable? cancellable,
out DBusConnection connection) throws Error, IOError {
var path_template = agent.get_path_template ();
var agent_path = path_template.expand (sizeof (void *) == 8 ? "64" : "32");
unowned string arch;
switch (Gum.NATIVE_CPU) {
case ARM64:
arch = "arm64";
break;
case AMD64:
arch = "x86_64";
break;
case IA32:
arch = "x86";
break;
default:
assert_not_reached ();
}
var agent_path = path_template.expand (arch);
system_session_container = yield AgentContainer.create (agent_path, cancellable);
+5 -4
View File
@@ -142,11 +142,12 @@ namespace Frida {
private PathTemplate? cached_path_template;
public AgentDescriptor (PathTemplate name_template, Bytes dll32, Bytes dll64, AgentResource[] dependencies,
TemporaryDirectory? tempdir = null) {
public AgentDescriptor (PathTemplate name_template, Bytes dll_arm64, Bytes dll_x86_64, Bytes dll_x86,
AgentResource[] dependencies, TemporaryDirectory? tempdir = null) {
var agents = new Gee.ArrayList<AgentResource> ();
agents.add (new AgentResource (name_template.expand ("32"), dll32, tempdir));
agents.add (new AgentResource (name_template.expand ("64"), dll64, tempdir));
agents.add (new AgentResource (name_template.expand ("arm64"), dll_arm64, tempdir));
agents.add (new AgentResource (name_template.expand ("x86_64"), dll_x86_64, tempdir));
agents.add (new AgentResource (name_template.expand ("x86"), dll_x86, tempdir));
Object (
name_template: name_template,
Loaded 100 of 121 files, more files were not shown because too many files have changed in this diff. Show more