Compare commits

...
99 Commits
Author SHA1 Message Date
Ole André Vadla Ravnås 259ce04fe6 subprojects: Prepare for release 2025-03-21 14:20:50 +01:00
Ole André Vadla Ravnås 4cdfa4b09c subprojects: Bump outdated 2025-03-21 14:19:27 +01:00
Ole André Vadla Ravnås 7b54e41f88 droidy: Fix the MAX_MESSAGE_LENGTH declaration
The new maximum exceeds the range of a uint16.
2025-03-21 14:18:59 +01:00
Ole André Vadla Ravnås c7b1e17c3e subprojects: Prepare for release 2025-03-21 13:57:14 +01:00
Ole André Vadla Ravnås e844297a61 subprojects: Bump outdated 2025-03-20 20:38:52 +01:00
Ole André Vadla Ravnås 30b00eaa64 subprojects: Bump outdated 2025-03-20 17:00:25 +01:00
Ole André Vadla Ravnås 2109c38e06 submodules: Bump releng 2025-03-20 17:00:25 +01:00
Ole André Vadla Ravnås 1a3da37cf8 compiler: Bump @types/frida-gum 2025-03-20 16:59:53 +01:00
Jan S. 3d9fab2b0d fruity: Fix Input/Output Error on Windows (#1144)
By skipping devices with an empty UDID.
2025-03-19 15:12:07 +01:00
philippmao c58212fe8c droidy: Support more than ~8 ADB-connected devices (#1145)
By bumping the message size limit.
2025-03-19 15:07:44 +01:00
Ole André Vadla Ravnås bce2650980 subprojects: Bump outdated 2025-03-19 10:51:37 +01:00
Ole André Vadla Ravnås 0f03a0ee19 submodules: Bump releng 2025-03-19 10:51:36 +01:00
Ole André Vadla Ravnås d9b4d20adf linux: Include linux/unistd.h for syscall constant
Which, depending on the libc, may not be implicitly included.
2025-03-18 19:11:03 +00:00
Ole André Vadla Ravnås a8db90d6a5 subprojects: Bump outdated 2025-03-18 19:51:10 +01:00
Ole André Vadla Ravnås edcb76f066 submodules: Bump releng 2025-03-18 19:51:09 +01:00
Ole André Vadla Ravnås 1f72d5f102 subprojects: Prepare for release 2025-03-13 19:09:35 +01:00
Ole André Vadla Ravnås 8336d42b94 server: Create DeviceManager without local backend
Since we're pulling it in manually in order to run the frida-helper
in-process, we want to avoid having both implementations.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Kudos to @mrmacete for helping track this one down.

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

Also ensure any pending write operation is cancelled at that point.
2024-11-22 14:50:31 +01:00
69 changed files with 1969 additions and 847 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
github: frida
+2 -2
View File
@@ -44,7 +44,7 @@ jobs:
if: matrix.id == 'linux-x86'
run: |
sudo apt-get update
sudo apt-get install gcc-multilib lib32stdc++-11-dev
sudo apt-get install gcc-multilib lib32stdc++-13-dev
- name: Build
if: ${{ startsWith(matrix.id, 'windows-') }}
run: |
@@ -249,7 +249,7 @@ jobs:
check-linux-helpers:
strategy:
matrix:
arch: [x86, x86_64, arm, arm64, mips, mipsel, mips64, mips64el]
arch: [x86, x86_64, arm, armbe8, arm64, arm64be, arm64beilp32, mips, mipsel, mips64, mips64el]
runs-on: ubuntu-latest
container: ghcr.io/frida/core-linux-helpers-${{ matrix.arch }}:latest
steps:
+2 -2
View File
@@ -604,10 +604,10 @@ def grab_subprojects_from_parent(subprojects: dict[str, Path], releng_location:
continue
try:
subp_here.symlink_to(location, target_is_directory=True)
subp_here.symlink_to(Path("..") / ".." / name, target_is_directory=True)
continue
except OSError as e:
if not getattr(e, "winerror") == 1314:
if not getattr(e, "winerror", None) == 1314:
raise e
subprocess.run(["git", "worktree", "add", subp_here],
+15 -16
View File
@@ -1509,13 +1509,14 @@ namespace Frida.Agent {
}
public static NativeBridgeApi open () throws Error {
string? nb_mod = null;
string? vm_mod = null;
Gum.Process.enumerate_modules ((details) => {
if (/\/lib(64)?\/libnativebridge.so$/.match (details.path))
nb_mod = details.path;
else if (/^lib(art|dvm).so$/.match (details.name) && !/\/system\/fake-libs/.match (details.path))
vm_mod = details.path;
Gum.Module? nb_mod = null;
Gum.Module? vm_mod = null;
Gum.Process.enumerate_modules (module => {
unowned string path = module.path;
if (/\/lib(64)?\/libnativebridge.so$/.match (path))
nb_mod = module;
else if (/^lib(art|dvm).so$/.match (module.name) && !/\/system\/fake-libs/.match (path))
vm_mod = module;
bool carry_on = nb_mod == null || vm_mod == null;
return carry_on;
});
@@ -1530,29 +1531,27 @@ namespace Frida.Agent {
NBUnloadLibraryFunc? unload;
NBGetTrampolineFunc get_trampoline;
load = (NBLoadLibraryFunc) Gum.Module.find_export_by_name (nb_mod, "NativeBridgeLoadLibrary");;
load = (NBLoadLibraryFunc) nb_mod.find_export_by_name ("NativeBridgeLoadLibrary");;
if (load != null) {
flavor = MODERN;
load_ext = (NBLoadLibraryExtFunc) Gum.Module.find_export_by_name (nb_mod, "NativeBridgeLoadLibraryExt");
load_ext = (NBLoadLibraryExtFunc) nb_mod.find_export_by_name ("NativeBridgeLoadLibraryExt");
// XXX: NativeBridgeUnloadLibrary() is only a stub as of Android 11 w/ libndk_translation.so
unload = null;
get_trampoline = (NBGetTrampolineFunc) Gum.Module.find_export_by_name (nb_mod,
"NativeBridgeGetTrampoline");
get_trampoline = (NBGetTrampolineFunc) nb_mod.find_export_by_name ("NativeBridgeGetTrampoline");
} else {
flavor = LEGACY;
load = (NBLoadLibraryFunc) Gum.Module.find_export_by_name (nb_mod,
"_ZN7android23NativeBridgeLoadLibraryEPKci");
load_ext = (NBLoadLibraryExtFunc) Gum.Module.find_export_by_name (nb_mod,
load = (NBLoadLibraryFunc) nb_mod.find_export_by_name ("_ZN7android23NativeBridgeLoadLibraryEPKci");
load_ext = (NBLoadLibraryExtFunc) nb_mod.find_export_by_name (
"_ZN7android26NativeBridgeLoadLibraryExtEPKciPNS_25native_bridge_namespace_tE");
// XXX: Unload implementation seems to be unreliable.
unload = null;
get_trampoline = (NBGetTrampolineFunc) Gum.Module.find_export_by_name (nb_mod,
get_trampoline = (NBGetTrampolineFunc) nb_mod.find_export_by_name (
"_ZN7android25NativeBridgeGetTrampolineEPvPKcS2_j");
}
if (load == null || get_trampoline == null)
throw new Error.NOT_SUPPORTED ("NativeBridge API is not available on this system");
var get_vms = (JNIGetCreatedJavaVMsFunc) Gum.Module.find_export_by_name (vm_mod, "JNI_GetCreatedJavaVMs");
var get_vms = (JNIGetCreatedJavaVMsFunc) vm_mod.find_export_by_name ("JNI_GetCreatedJavaVMs");
if (get_vms == null)
throw new Error.NOT_SUPPORTED ("Unable to locate Java VM");
+8
View File
@@ -0,0 +1,8 @@
{
global:
frida_agent_main;
JNI_OnLoad;
local:
*;
};
-1
View File
@@ -1,7 +1,6 @@
{
global:
frida_agent_main;
JNI_OnLoad;
local:
*;
+1 -1
View File
@@ -28,7 +28,7 @@ if host_os_family == 'windows'
elif host_os_family == 'darwin'
extra_link_args += '-Wl,-exported_symbol,_frida_agent_main'
else
symscript = 'frida-agent.version'
symscript = (host_os == 'android') ? 'frida-agent-android.version' : 'frida-agent.version'
extra_link_args += '-Wl,--version-script,' + meson.current_source_dir() / symscript
extra_link_depends += symscript
endif
+4 -1
View File
@@ -43,14 +43,17 @@ if host_os_family == 'darwin'
]
endif
base_gir_name = f'FridaBase-@[email protected]'
base = static_library('frida-base-' + api_version, base_sources,
c_args: frida_component_cflags,
vala_args: [base_vala_args, '--pkg=libsoup-3.0'] + extra_vala_args,
vala_header: 'frida-base.h',
vala_gir: base_gir_name,
vala_vapi: f'frida-base-@[email protected]',
dependencies: [glib_dep, gobject_dep, gio_dep, libsoup_dep, nice_dep, usrsctp_dep, gee_dep, json_glib_dep, gum_dep, openssl_dep] + extra_deps,
install: true,
install_dir: [true, header_install_dir, true],
install_dir: [true, header_install_dir, true, false],
)
base_dep = declare_dependency(
+7
View File
@@ -204,6 +204,13 @@ namespace Frida {
return peek_scope ().get_object<Darwin.Xpc.UInt64> (Darwin.Xpc.UInt64.TYPE).get_value ();
}
public unowned uint8[] get_data_value () throws Error {
var data = peek_scope ().get_object<Darwin.Xpc.Data> (Darwin.Xpc.Data.TYPE);
unowned uint8[] buf = (uint8[]) data.get_bytes_ptr ();
buf.length = (int) data.get_length ();
return buf;
}
public unowned string get_string_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.String> (Darwin.Xpc.String.TYPE).get_string_ptr ();
}
+19 -16
View File
@@ -53,15 +53,18 @@ namespace Frida {
private delegate ssize_t ReadFunc (int fd, void * buf, size_t count);
construct {
Gum.Module.enumerate_imports ("libart.so", imp => {
if (imp.name == "read") {
read_slot = (ReadFunc *) imp.slot;
return false;
}
return true;
});
if (read_slot != null)
old_read_impl = update_read_slot (on_read);
var art = Gum.Process.find_module_by_name ("libart.so");
if (art != null) {
art.enumerate_imports (imp => {
if (imp.name == "read") {
read_slot = (ReadFunc *) imp.slot;
return false;
}
return true;
});
if (read_slot != null)
old_read_impl = update_read_slot (on_read);
}
}
~ThreadCountCloaker () {
@@ -212,34 +215,34 @@ namespace Frida {
construct {
var interceptor = Gum.Interceptor.obtain ();
unowned string libc = Gum.Process.query_libc_name ();
var libc = Gum.Process.get_libc_module ();
var open_listener = new OpenDirListener (this);
listeners.add (open_listener);
interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, "opendir"), open_listener);
interceptor.attach ((void *) libc.find_export_by_name ("opendir"), open_listener);
var close_listener = new CloseDirListener (this);
listeners.add (close_listener);
interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, "closedir"), close_listener);
interceptor.attach ((void *) libc.find_export_by_name ("closedir"), close_listener);
var readdir_impl = Gum.Module.find_export_by_name (libc, "readdir");
var readdir_impl = libc.find_export_by_name ("readdir");
var readdir_listener = new ReadDirListener (this, LEGACY);
listeners.add (readdir_listener);
interceptor.attach ((void *) readdir_impl, readdir_listener);
var readdir64_impl = Gum.Module.find_export_by_name (libc, "readdir64");
var readdir64_impl = libc.find_export_by_name ("readdir64");
if (readdir64_impl != 0 && readdir64_impl != readdir_impl) {
var listener = new ReadDirListener (this, MODERN);
listeners.add (listener);
interceptor.attach ((void *) readdir64_impl, listener);
}
var readdir_r_impl = Gum.Module.find_export_by_name (libc, "readdir_r");
var readdir_r_impl = libc.find_export_by_name ("readdir_r");
var readdir_r_listener = new ReadDirRListener (this, LEGACY);
listeners.add (readdir_r_listener);
interceptor.attach ((void *) readdir_r_impl, readdir_r_listener);
var readdir64_r_impl = Gum.Module.find_export_by_name (libc, "readdir64_r");
var readdir64_r_impl = libc.find_export_by_name ("readdir64_r");
if (readdir64_r_impl != 0 && readdir64_r_impl != readdir_r_impl) {
var listener = new ReadDirRListener (this, MODERN);
listeners.add (listener);
+4 -3
View File
@@ -32,16 +32,17 @@ namespace Frida {
unowned Gum.InvocationListener listener = this;
#if WINDOWS
interceptor.attach ((void *) Gum.Module.find_export_by_name ("kernel32.dll", "ExitProcess"), listener);
interceptor.attach ((void *) Gum.Process.find_module_by_name ("kernel32.dll").find_export_by_name ("ExitProcess"),
listener);
#else
unowned string libc = Gum.Process.query_libc_name ();
var libc = Gum.Process.get_libc_module ();
const string[] apis = {
"exit",
"_exit",
"abort",
};
foreach (var symbol in apis) {
interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, symbol), listener);
interceptor.attach ((void *) libc.find_export_by_name (symbol), listener);
}
#endif
}
+1 -1
View File
@@ -26,7 +26,7 @@ namespace Frida {
construct {
var interceptor = Gum.Interceptor.obtain ();
var close = Gum.Module.find_export_by_name (Gum.Process.query_libc_name (), "close");
var close = Gum.Process.get_libc_module ().find_export_by_name ("close");
close_listener = new CloseListener (this);
interceptor.attach ((void *) close, close_listener);
}
+16 -10
View File
@@ -45,9 +45,9 @@ namespace Frida {
}
static construct {
unowned string libc = Gum.Process.query_libc_name ();
fork_impl = (void *) Gum.Module.find_export_by_name (libc, "fork");
vfork_impl = (void *) Gum.Module.find_export_by_name (libc, "vfork");
var libc = Gum.Process.get_libc_module ();
fork_impl = (void *) libc.find_export_by_name ("fork");
vfork_impl = (void *) libc.find_export_by_name ("vfork");
}
construct {
@@ -61,15 +61,21 @@ namespace Frida {
string cmdline;
FileUtils.get_contents ("/proc/self/cmdline", out cmdline);
if (cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" || cmdline == "usap64") {
var set_argv0 = (void *) Gum.Module.find_export_by_name ("libandroid_runtime.so", "_Z27android_os_Process_setArgV0P7_JNIEnvP8_jobjectP8_jstring");
if (set_argv0 != null) {
interceptor.attach (set_argv0, listener, (void *) HookId.SET_ARGV0);
child_recovery_behavior = DEFERRED_UNTIL_SET_ARGV0;
var runtime = Gum.Process.find_module_by_name ("libandroid_runtime.so");
if (runtime != null) {
var set_argv0 = (void *) runtime.find_export_by_name ("_Z27android_os_Process_setArgV0P7_JNIEnvP8_jobjectP8_jstring");
if (set_argv0 != null) {
interceptor.attach (set_argv0, listener, (void *) HookId.SET_ARGV0);
child_recovery_behavior = DEFERRED_UNTIL_SET_ARGV0;
}
}
var setcontext = (void *) Gum.Module.find_export_by_name ("libselinux.so", "selinux_android_setcontext");
if (setcontext != null)
interceptor.attach (setcontext, listener, (void *) HookId.SET_CTX);
var selinux = Gum.Process.find_module_by_name ("libselinux.so");
if (selinux != null) {
var setcontext = (void *) selinux.find_export_by_name ("selinux_android_setcontext");
if (setcontext != null)
interceptor.attach (setcontext, listener, (void *) HookId.SET_CTX);
}
}
} catch (FileError e) {
}
+15 -12
View File
@@ -38,21 +38,24 @@ namespace Frida {
var interceptor = Gum.Interceptor.obtain ();
#if WINDOWS
var create_process_internal = Gum.Module.find_export_by_name ("kernelbase.dll", "CreateProcessInternalW");
if (create_process_internal == 0)
create_process_internal = Gum.Module.find_export_by_name ("kernel32.dll", "CreateProcessInternalW");
var kernelbase = Gum.Process.find_module_by_name ("kernelbase.dll");
var create_process_internal = (kernelbase != null) ? kernelbase.find_export_by_name ("CreateProcessInternalW") : 0;
if (create_process_internal == 0) {
create_process_internal = Gum.Process.find_module_by_name ("kernel32.dll")
.find_export_by_name ("CreateProcessInternalW");
}
assert (create_process_internal != 0);
interceptor.attach ((void *) create_process_internal, this);
#else
unowned string libc = Gum.Process.query_libc_name ();
var libc = Gum.Process.get_libc_module ();
#if DARWIN
posix_spawn = (PosixSpawnFunc) Gum.Module.find_export_by_name (libc, "posix_spawn");
posix_spawnattr_init = (PosixSpawnAttrInitFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_init");
posix_spawnattr_destroy = (PosixSpawnAttrDestroyFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_destroy");
posix_spawnattr_getflags = (PosixSpawnAttrSetFlagsFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_getflags");
posix_spawnattr_setflags = (PosixSpawnAttrSetFlagsFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_setflags");
posix_spawn = (PosixSpawnFunc) libc.find_export_by_name ("posix_spawn");
posix_spawnattr_init = (PosixSpawnAttrInitFunc) libc.find_export_by_name ("posix_spawnattr_init");
posix_spawnattr_destroy = (PosixSpawnAttrDestroyFunc) libc.find_export_by_name ("posix_spawnattr_destroy");
posix_spawnattr_getflags = (PosixSpawnAttrSetFlagsFunc) libc.find_export_by_name ("posix_spawnattr_getflags");
posix_spawnattr_setflags = (PosixSpawnAttrSetFlagsFunc) libc.find_export_by_name ("posix_spawnattr_setflags");
execve = (void *) Gum.Module.find_export_by_name (libc, "execve");
execve = (void *) libc.find_export_by_name ("execve");
interceptor.attach ((void *) posix_spawn, this);
@@ -60,10 +63,10 @@ namespace Frida {
#else
Gum.Address execve = 0;
#if ANDROID
execve = Gum.Module.find_symbol_by_name (libc, "__execve");
execve = libc.find_symbol_by_name ("__execve");
#endif
if (execve == 0)
execve = Gum.Module.find_export_by_name (libc, "execve");
execve = libc.find_export_by_name ("execve");
interceptor.attach ((void *) execve, this);
#endif
#endif
+4 -3
View File
@@ -26,9 +26,10 @@ namespace Frida {
construct {
var interceptor = Gum.Interceptor.obtain ();
task_threads = (TaskThreadsFunc) Gum.Module.find_export_by_name (LIBSYSTEM_KERNEL, "task_threads");
thread_suspend = (ThreadSuspendFunc) Gum.Module.find_export_by_name (LIBSYSTEM_KERNEL, "thread_suspend");
thread_resume = (ThreadResumeFunc) Gum.Module.find_export_by_name (LIBSYSTEM_KERNEL, "thread_resume");
var kernel = Gum.Process.find_module_by_name (LIBSYSTEM_KERNEL);
task_threads = (TaskThreadsFunc) kernel.find_export_by_name ("task_threads");
thread_suspend = (ThreadSuspendFunc) kernel.find_export_by_name ("thread_suspend");
thread_resume = (ThreadResumeFunc) kernel.find_export_by_name ("thread_resume");
interceptor.replace ((void *) task_threads, (void *) replacement_task_threads, this);
interceptor.replace ((void *) thread_suspend, (void *) replacement_thread_suspend, this);
+12 -4
View File
@@ -300,6 +300,7 @@ static gpointer
frida_find_vtable (void)
{
GumAddress result = 0;
GumModule * libunwind;
GumAddress export;
uint64_t address;
G_GNUC_UNUSED cs_err err;
@@ -309,11 +310,15 @@ frida_find_vtable (void)
size_t size;
const size_t max_size = 2048;
export = gum_module_find_export_by_name (FRIDA_LIBUNWIND_PATH, "unw_init_local");
libunwind = gum_process_find_module_by_name (FRIDA_LIBUNWIND_PATH);
if (libunwind == NULL)
goto beach;
export = gum_module_find_export_by_name (libunwind, "unw_init_local");
if (export == 0)
export = gum_module_find_export_by_name (FRIDA_LIBUNWIND_PATH, "_Unwind_RaiseException");
export = gum_module_find_export_by_name (libunwind, "_Unwind_RaiseException");
if (export == 0)
return NULL;
goto beach;
export = gum_strip_code_address (export);
address = export;
@@ -340,7 +345,7 @@ frida_find_vtable (void)
GumMemoryRange bss_range;
bss_range.base_address = 0;
gum_module_enumerate_sections (FRIDA_LIBUNWIND_PATH, (GumFoundSectionFunc) frida_find_bss_range, &bss_range);
gum_module_enumerate_sections (libunwind, (GumFoundSectionFunc) frida_find_bss_range, &bss_range);
while (cs_disasm_iter (capstone, &code, &size, &address, insn))
{
@@ -404,6 +409,9 @@ frida_find_vtable (void)
cs_free (insn, 1);
cs_close (&capstone);
beach:
g_clear_object (&libunwind);
return GSIZE_TO_POINTER (result);
}
+1 -1
View File
@@ -21,7 +21,7 @@ namespace Frida {
var interceptor = Gum.Interceptor.obtain ();
dyld_find_unwind_sections = (DyldFindUnwindSectionsFunc)
Gum.Module.find_export_by_name (LIBDYLD, "_dyld_find_unwind_sections");
Gum.Process.find_module_by_name (LIBDYLD).find_export_by_name ("_dyld_find_unwind_sections");
interceptor.replace ((void *) dyld_find_unwind_sections, (void *) replacement_dyld_find_unwind_sections, this);
+1 -1
View File
@@ -481,7 +481,7 @@ if have_fruity_backend
'dns=disabled',
'arp=disabled',
'ethernet=enabled',
'tcp_mss=1360',
'tcp_mss=4036',
'tcp_snd_buf=65535',
'tcp_wnd=65535',
]
+1 -1
Submodule releng updated: 8870e5861b...2eecc41cbe.
+51 -8
View File
@@ -3,6 +3,7 @@ namespace Frida.Server {
private const string DEFAULT_DIRECTORY = "re.frida.server";
private static bool output_version = false;
private static string? device_id = null;
private static string? listen_address = null;
private static string? certpath = null;
private static string? origin = null;
@@ -30,6 +31,7 @@ namespace Frida.Server {
const OptionEntry[] option_entries = {
{ "version", 0, 0, OptionArg.NONE, ref output_version, "Output version information and exit", null },
{ "device", 0, 0, OptionArg.STRING, ref device_id, "Serve device with the given ID", "ID" },
{ "listen", 'l', 0, OptionArg.STRING, ref listen_address, "Listen on ADDRESS", "ADDRESS" },
{ "certificate", 0, 0, OptionArg.FILENAME, ref certpath, "Enable TLS using CERTIFICATE", "CERTIFICATE" },
{ "origin", 0, 0, OptionArg.STRING, ref origin, "Only accept requests with “Origin” header matching ORIGIN " +
@@ -178,7 +180,7 @@ namespace Frida.Server {
#if DARWIN
var worker = new Thread<int> ("frida-server-main-loop", () => {
var exit_code = run_application (endpoint_params, options, on_ready);
var exit_code = run_application (device_id, endpoint_params, options, on_ready);
_stop_run_loop ();
@@ -190,15 +192,16 @@ namespace Frida.Server {
return exit_code;
#else
return run_application (endpoint_params, options, on_ready);
return run_application (device_id, endpoint_params, options, on_ready);
#endif
}
private static int run_application (EndpointParameters endpoint_params, ControlServiceOptions options, ReadyHandler on_ready) {
private static int run_application (string? device_id, EndpointParameters endpoint_params, ControlServiceOptions options,
ReadyHandler on_ready) {
TemporaryDirectory.always_use ((directory != null) ? directory : DEFAULT_DIRECTORY);
TemporaryDirectory.use_sysroot (options.sysroot);
application = new Application (new ControlService (endpoint_params, options));
application = new Application (device_id, endpoint_params, options);
Posix.signal (Posix.Signal.INT, (sig) => {
application.stop ();
@@ -235,19 +238,36 @@ namespace Frida.Server {
private class Application : Object {
public signal void ready (bool success);
public ControlService service {
public string? device_id {
get;
construct;
}
public EndpointParameters endpoint_params {
get;
construct;
}
public ControlServiceOptions options {
get;
construct;
}
private DeviceManager? manager;
private ControlService? service;
private Cancellable io_cancellable = new Cancellable ();
private MainLoop loop = new MainLoop ();
private int exit_code;
private bool stopping;
public Application (ControlService service) {
Object (service: service);
public Application (string? device_id, EndpointParameters endpoint_params, ControlServiceOptions options) {
Object (
device_id: device_id,
endpoint_params: endpoint_params,
options: options
);
}
public int run () {
@@ -265,6 +285,17 @@ namespace Frida.Server {
private async void start () {
try {
if (device_id != null && device_id != "local") {
manager = new DeviceManager.with_nonlocal_backends_only ();
var device = yield manager.get_device_by_id (device_id, 0, io_cancellable);
device.lost.connect (on_device_lost);
service = yield new ControlService.with_device (device, endpoint_params, options);
} else {
service = new ControlService (endpoint_params, options);
}
yield service.start (io_cancellable);
} catch (GLib.Error e) {
if (e is IOError.CANCELLED)
@@ -297,7 +328,15 @@ namespace Frida.Server {
io_cancellable.cancel ();
try {
yield service.stop ();
if (service != null) {
yield service.stop ();
service = null;
}
if (manager != null) {
yield manager.close ();
manager = null;
}
} catch (GLib.Error e) {
}
@@ -306,6 +345,10 @@ namespace Frida.Server {
return false;
});
}
private void on_device_lost () {
stop ();
}
}
private TlsCertificate? parse_certificate (string? path) throws GLib.Error {
+116 -30
View File
@@ -1,36 +1,49 @@
from __future__ import annotations
import argparse
import os
from dataclasses import dataclass
from io import StringIO
from pathlib import Path
import re
import sys
from typing import List, Set
import xml.etree.ElementTree as ET
CORE_NAMESPACE = "http://www.gtk.org/introspection/core/1.0"
C_NAMESPACE = "http://www.gtk.org/introspection/c/1.0"
GLIB_NAMESPACE = "http://www.gtk.org/introspection/glib/1.0"
GIR_NAMESPACES = {
"": CORE_NAMESPACE,
"c": C_NAMESPACE,
"glib": GLIB_NAMESPACE,
}
CORE_TAG_FIELD = f"{{{CORE_NAMESPACE}}}field"
CORE_TAG_CONSTRUCTOR = f"{{{CORE_NAMESPACE}}}constructor"
CORE_TAG_METHOD = f"{{{CORE_NAMESPACE}}}method"
def main():
parser = argparse.ArgumentParser(description="Generate refined Frida API definitions")
parser.add_argument('--output', dest='output_type', choices=['bundle', 'header', 'gir', 'vapi', 'vapi-stamp'], default='bundle')
parser.add_argument('api_version', metavar='api-version', type=str)
parser.add_argument('core_header', metavar='/path/to/frida-core.h', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('core_gir', metavar='/path/to/Frida-x.y.gir', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('core_vapi', metavar='/path/to/frida-core.vapi', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('core_header', metavar='/path/to/frida-core.h', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('base_vapi', metavar='/path/to/frida-base.vapi', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('base_header', metavar='/path/to/frida-base.h', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('base_gir', metavar='/path/to/FridaBase-x.y.gir', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('base_vapi', metavar='/path/to/frida-base.vapi', type=argparse.FileType('r', encoding='utf-8'))
parser.add_argument('output_dir', metavar='/output/dir')
args = parser.parse_args()
output_type = args.output_type
api_version = args.api_version
core_header = args.core_header.read()
core_gir = args.core_gir.read()
core_vapi = args.core_vapi.read()
core_header = args.core_header.read()
base_vapi = args.base_vapi.read()
base_header = args.base_header.read()
base_gir = args.base_gir.read()
base_vapi = args.base_vapi.read()
output_dir = Path(args.output_dir)
if output_type == 'gir':
(output_dir / f"Frida-{api_version}.gir").write_text(core_gir, encoding='utf-8')
return
if output_type == 'vapi-stamp':
(output_dir / f"frida-core-{api_version}.vapi.stamp").write_bytes(b"")
return
@@ -45,30 +58,36 @@ def main():
toplevel_sources = []
src_dir = Path(__file__).parent.parent.resolve()
for name in toplevel_names:
with open(src_dir / name, 'r', encoding='utf-8') as f:
toplevel_sources.append(f.read())
toplevel_sources.append((src_dir / name).read_text(encoding='utf-8'))
toplevel_code = "\n".join(toplevel_sources)
enable_header = False
enable_gir = False
enable_vapi = False
if output_type == 'bundle':
enable_header = True
enable_gir = True
enable_vapi = True
elif output_type == 'header':
enable_header = True
elif output_type == 'gir':
enable_gir = True
elif output_type == 'vapi':
enable_vapi = True
api = parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, base_header)
api = parse_api(api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi)
if enable_header:
emit_header(api, output_dir)
if enable_gir:
emit_gir(api, core_gir, base_gir, output_dir)
if enable_vapi:
emit_vapi(api, output_dir)
def emit_header(api, output_dir):
with open(output_dir / 'frida-core.h', 'w', encoding='utf-8') as output_header_file:
with OutputFile(output_dir / 'frida-core.h') as output_header_file:
output_header_file.write("#ifndef __FRIDA_CORE_H__\n#define __FRIDA_CORE_H__\n\n")
output_header_file.write("#include <glib.h>\n#include <glib-object.h>\n#include <gio/gio.h>\n#include <json-glib/json-glib.h>\n")
@@ -79,7 +98,6 @@ def emit_header(api, output_dir):
output_header_file.write("\ntypedef struct _%s %s;" % (object_type.c_name, object_type.c_name))
if object_type.c_iface_definition is not None:
output_header_file.write("\ntypedef struct _%sIface %sIface;" % (object_type.c_name, object_type.c_name))
output_header_file.write("\ntypedef struct _FridaHostSession FridaHostSession;")
for enum in api.enum_types:
output_header_file.write("\n\n" + enum.c_definition)
@@ -150,8 +168,61 @@ def emit_header(api, output_dir):
output_header_file.write("\n\n#endif\n")
def emit_gir(api: ApiSpec, core_gir: str, base_gir: str, output_dir: Path) -> str:
ET.register_namespace("", CORE_NAMESPACE)
ET.register_namespace("c", C_NAMESPACE)
ET.register_namespace("glib", GLIB_NAMESPACE)
core_tree = ET.ElementTree(ET.fromstring(core_gir))
base_tree = ET.ElementTree(ET.fromstring(base_gir))
core_root = core_tree.getroot()
base_root = base_tree.getroot()
merged_root = ET.Element(core_root.tag, core_root.attrib)
for elem in core_root.findall("include", GIR_NAMESPACES):
name = elem.get("name")
if name in {"GLib", "GObject", "Gio"}:
merged_root.append(elem)
for tag in ["package", "c:include"]:
for elem in core_root.findall(tag, GIR_NAMESPACES):
merged_root.append(elem)
core_namespace = core_root.find("namespace", GIR_NAMESPACES)
merged_namespace = ET.SubElement(merged_root, core_namespace.tag, core_namespace.attrib)
object_type_names = {obj.name for obj in api.object_types}
enum_type_names = {enum.name for enum in api.enum_types}
error_type_names = {error.name for error in api.error_types}
def merge_and_transform_elements(tag_name: str, spec_set: Set[str]):
core_elements = filter_elements(core_root.findall(f".//{tag_name}", GIR_NAMESPACES), spec_set)
base_elements = filter_elements(base_root.findall(f".//{tag_name}", GIR_NAMESPACES), spec_set)
for elem in core_elements + base_elements:
if tag_name == "class":
for child in list(elem):
if child.tag == CORE_TAG_FIELD or child.get("name").startswith("_"):
elem.remove(child)
merged_namespace.append(elem)
merge_and_transform_elements("class", object_type_names)
merge_and_transform_elements("interface", object_type_names)
merge_and_transform_elements("enumeration", enum_type_names | error_type_names)
ET.indent(merged_root, space=" ")
result = ET.tostring(merged_root,
encoding="unicode",
xml_declaration=True)
with OutputFile(output_dir / f"Frida-{api.version}.gir") as output_gir:
output_gir.write(result)
def filter_elements(elements: List[ET.Element], spec_set: Set[str]):
return [elem for elem in elements if elem.get("name") in spec_set]
def emit_vapi(api, output_dir):
with open(output_dir / "frida-core-{0}.vapi".format(api.version), "w", encoding='utf-8') as output_vapi_file:
with OutputFile(output_dir / f"frida-core-{api.version}.vapi") as output_vapi_file:
output_vapi_file.write("[CCode (cheader_filename = \"frida-core.h\", cprefix = \"Frida\", lower_case_cprefix = \"frida_\")]")
output_vapi_file.write("\nnamespace Frida {")
output_vapi_file.write("\n\tpublic static void init ();")
@@ -189,12 +260,12 @@ def emit_vapi(api, output_dir):
output_vapi_file.write("\n}\n")
with open(output_dir / "frida-core-{0}.deps".format(api.version), "w", encoding='utf-8') as output_deps_file:
with OutputFile(output_dir / f"frida-core-{api.version}.deps") as output_deps_file:
output_deps_file.write("glib-2.0\n")
output_deps_file.write("gobject-2.0\n")
output_deps_file.write("gio-2.0\n")
def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, base_header):
def parse_api(api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi):
all_headers = core_header + "\n" + base_header
all_enum_names = [m.group(1) for m in re.finditer(r"^\t+public\s+enum\s+(\w+)\s+", toplevel_code + "\n" + base_vapi, re.MULTILINE)]
@@ -287,8 +358,7 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
method_cname_lc = object_type.c_name_lc + '_' + method_name
if method_cname_lc not in seen_cfunctions:
seen_cfunctions.add(method_cname_lc)
if method_name not in ('construct', 'construct_with_host_session', 'get_main_context', 'get_provider', 'get_session') \
and not (object_type.name in ("Session", "Script") and method_name == 'get_id'):
if method_name != 'construct':
if (object_type.c_name + '*') in m.group(0):
if method_name == 'new' or method_name.startswith('new_'):
object_type.c_constructors.append(method_cprototype)
@@ -362,8 +432,7 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
current_object_type.vapi_signals.append(stripped_line)
elif "{ get" in stripped_line:
name = re.match(r".+?(\w+)\s+{", stripped_line).group(1)
if name not in ('main_context', 'provider', 'session'):
current_object_type.vapi_properties.append(stripped_line)
current_object_type.vapi_properties.append(stripped_line)
else:
m = re.match(r".+?(\w+)\s+\(", stripped_line)
if m is not None:
@@ -400,19 +469,19 @@ def function_is_public(name):
"negotiate_connection"
]
def parse_vala_object_types(source):
def parse_vala_object_types(source) -> List[ApiObjectType]:
return [ApiObjectType(m.group(2), m.group(1)) for m in re.finditer(r"^\t+public\s+(class|interface)\s+(\w+)\s+", source, re.MULTILINE)]
def parse_vapi_functions(vapi):
def parse_vapi_functions(vapi) -> List[ApiFunction]:
return [ApiFunction(m.group(1), m.group(0)) for m in re.finditer(r"^\tpublic static .+ (\w+) \(.+;", vapi, re.MULTILINE)]
@dataclass
class ApiSpec:
def __init__(self, version, object_types, functions, enum_types, error_types):
self.version = version
self.object_types = object_types
self.functions = functions
self.enum_types = enum_types
self.error_types = error_types
version: str
object_types: List[ApiObjectType]
functions: List[ApiFunction]
enum_types: List[ApiEnum]
error_types: List[ApiEnum]
class ApiEnum:
def __init__(self, name):
@@ -514,6 +583,23 @@ def fuzzysort(items, keys):
result.extend(remaining)
return result
class OutputFile:
def __init__(self, output_path):
self._output_path = output_path
self._io = StringIO()
def __enter__(self):
return self._io
def __exit__(self, *exc):
result = self._io.getvalue()
if self._output_path.exists():
existing_contents = self._output_path.read_text(encoding='utf-8')
if existing_contents == result:
return False
self._output_path.write_text(result, encoding='utf-8')
return False
if __name__ == '__main__':
main()
+13 -30
View File
@@ -2,54 +2,37 @@ generate_api = [python, files('generate.py')]
generate_api_args = [
api_version,
core_build_dir / 'frida-core.h',
core_build_dir / core_gir_name,
core_build_dir / 'frida-core.vapi',
core_build_dir / 'frida-core.h',
base_build_dir / f'frida-base-@[email protected]',
base_build_dir / 'frida-base.h',
base_build_dir / base_gir_name,
base_build_dir / f'frida-base-@[email protected]',
meson.current_build_dir(),
]
core_public_h = custom_target('frida-core-header',
input: [core, base],
output: ['frida-core.h'],
command: [
generate_api,
'--output=header',
generate_api_args,
],
install: true,
install_dir: header_install_dir,
)
core_public_gir = custom_target('frida-core-gir',
input: [core, base],
output: [core_gir_name],
command: [
generate_api,
'--output=gir',
generate_api_args,
],
install: true,
install_dir: girdir,
)
core_public_vapi = custom_target('frida-core-vapi',
core_api = custom_target('frida-core-api',
input: [core, base],
output: [
'frida-core.h',
core_gir_name,
f'frida-core-@[email protected]',
f'frida-core-@[email protected]',
],
command: [
generate_api,
'--output=vapi',
'--output=bundle',
generate_api_args,
],
install: true,
install_dir: vapidir,
install_dir: [header_install_dir, girdir, vapidir, vapidir],
)
core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
core_public_h = core_api[0]
core_public_gir = core_api[1]
core_public_vapi = [core_api[2], core_api[3]]
core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
input: core_public_vapi,
output: f'frida-core-@[email protected]',
command: [
+7 -7
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.1",
"@types/frida-gum": "18.8.1",
"@types/glob": "^8.0.0",
"@types/node": "18.7.23",
"glob": "^8.0.3",
@@ -468,9 +468,9 @@
"dev": true
},
"node_modules/@types/frida-gum": {
"version": "18.7.1",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.1.tgz",
"integrity": "sha512-++7xogrG/jWdgC2gEeK8Q2tVTcycWXsFHhGaEa/TfY8u11UTUsn9dF+nr3Lo1rUnFGZssKDhqV4vsHOHgsWiLQ==",
"version": "18.8.1",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.8.1.tgz",
"integrity": "sha512-FhlHORjaw43QbNYIyj5Ji7iYSvk7t5nPiC+GuYDVndqc1bFheeAs88b1odp8HhmpQUrrYVU90wapapKVLlmJHg==",
"dev": true,
"license": "MIT"
},
@@ -1466,9 +1466,9 @@
"dev": true
},
"@types/frida-gum": {
"version": "18.7.1",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.1.tgz",
"integrity": "sha512-++7xogrG/jWdgC2gEeK8Q2tVTcycWXsFHhGaEa/TfY8u11UTUsn9dF+nr3Lo1rUnFGZssKDhqV4vsHOHgsWiLQ==",
"version": "18.8.1",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.8.1.tgz",
"integrity": "sha512-FhlHORjaw43QbNYIyj5Ji7iYSvk7t5nPiC+GuYDVndqc1bFheeAs88b1odp8HhmpQUrrYVU90wapapKVLlmJHg==",
"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.1",
"@types/frida-gum": "18.8.1",
"@types/glob": "^8.0.0",
"@types/node": "18.7.23",
"glob": "^8.0.3",
+37 -21
View File
@@ -1,11 +1,6 @@
namespace Frida {
#if HAVE_LOCAL_BACKEND
public class ControlService : Object {
public HostSession host_session {
get;
construct;
}
public EndpointParameters endpoint_params {
get;
construct;
@@ -16,6 +11,8 @@ namespace Frida {
construct;
}
private HostSession host_session;
private State state = STOPPED;
private WebService service;
@@ -41,43 +38,69 @@ namespace Frida {
public ControlService (EndpointParameters endpoint_params, ControlServiceOptions? options = null) {
ControlServiceOptions opts = (options != null) ? options : new ControlServiceOptions ();
HostSession host_session;
HostSession session;
#if WINDOWS
var tempdir = new TemporaryDirectory ();
host_session = new WindowsHostSession (new WindowsHelperProcess (tempdir), tempdir);
session = new WindowsHostSession (new WindowsHelperProcess (tempdir), tempdir);
#endif
#if DARWIN
host_session = new DarwinHostSession (new DarwinHelperBackend (), new TemporaryDirectory (),
session = new DarwinHostSession (new DarwinHelperBackend (), new TemporaryDirectory (),
opts.sysroot, opts.report_crashes);
#endif
#if LINUX
var tempdir = new TemporaryDirectory ();
host_session = new LinuxHostSession (new LinuxHelperProcess (tempdir), tempdir, opts.report_crashes);
session = new LinuxHostSession (new LinuxHelperProcess (tempdir), tempdir, opts.report_crashes);
#endif
#if FREEBSD
host_session = new FreebsdHostSession ();
session = new FreebsdHostSession ();
#endif
#if QNX
host_session = new QnxHostSession ();
session = new QnxHostSession ();
#endif
Object (
host_session: host_session,
endpoint_params: endpoint_params,
options: opts
);
assign_session (session);
}
public ControlService.with_host_session (HostSession host_session, EndpointParameters endpoint_params,
public async ControlService.with_device (Device device, EndpointParameters endpoint_params,
ControlServiceOptions? options = null, Cancellable? cancellable = null) throws Error, IOError {
ControlServiceOptions opts = (options != null) ? options : new ControlServiceOptions ();
var session = yield device.get_host_session (cancellable);
Object (
endpoint_params: endpoint_params,
options: opts
);
assign_session (session);
}
internal ControlService.with_host_session (HostSession host_session, EndpointParameters endpoint_params,
ControlServiceOptions? options = null) {
Object (
host_session: host_session,
endpoint_params: endpoint_params,
options: (options != null) ? options : new ControlServiceOptions ()
);
assign_session (host_session);
}
construct {
var iface_observer = new TunnelInterfaceObserver ();
iface_observer.interface_detached.connect (on_interface_detached);
service = new WebService (endpoint_params, WebServiceFlavor.CONTROL, PortConflictBehavior.FAIL, iface_observer);
main_handler = new ConnectionHandler (this, null);
}
private void assign_session (HostSession session) {
host_session = session;
host_session.spawn_added.connect (notify_spawn_added);
host_session.child_added.connect (notify_child_added);
host_session.child_removed.connect (notify_child_removed);
@@ -85,13 +108,6 @@ namespace Frida {
host_session.output.connect (notify_output);
host_session.agent_session_detached.connect (on_agent_session_detached);
host_session.uninjected.connect (notify_uninjected);
var iface_observer = new TunnelInterfaceObserver ();
iface_observer.interface_detached.connect (on_interface_detached);
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 {
+65 -42
View File
@@ -86,7 +86,7 @@
# define CORE_FOUNDATION "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
#endif
typedef struct _FridaHelperContext FridaHelperContext;
typedef struct _FridaDispatchContext FridaDispatchContext;
typedef struct _FridaSpawnInstance FridaSpawnInstance;
typedef guint FridaDyldFlavor;
typedef struct _FridaSpawnInstanceDyldData FridaSpawnInstanceDyldData;
@@ -106,7 +106,7 @@ typedef union _FridaDebugState FridaDebugState;
typedef int FridaConvertThreadStateDirection;
typedef guint FridaAslr;
struct _FridaHelperContext
struct _FridaDispatchContext
{
dispatch_queue_t dispatch_queue;
};
@@ -376,7 +376,7 @@ enum _FridaAslr
};
static FridaSpawnInstance * frida_spawn_instance_new (FridaDarwinHelperBackend * backend);
static void frida_spawn_instance_free (FridaSpawnInstance * instance);
static void frida_spawn_instance_close (FridaSpawnInstance * instance);
static void frida_spawn_instance_resume (FridaSpawnInstance * self);
static void frida_spawn_instance_on_server_cancel (void * context);
@@ -391,12 +391,12 @@ static void frida_spawn_instance_set_libc_initialized (FridaSpawnInstance * self
static kern_return_t frida_spawn_instance_create_dyld_data (FridaSpawnInstance * self);
static void frida_spawn_instance_destroy_dyld_data (FridaSpawnInstance * self);
#if defined (HAVE_IOS) || defined (HAVE_TVOS)
static gboolean frida_pick_ios_tvos_bootstrapper (const GumModuleDetails * details, gpointer user_data);
static gboolean frida_pick_ios_tvos_bootstrapper (GumModule * module, gpointer user_data);
#endif
static void frida_spawn_instance_unset_helpers (FridaSpawnInstance * self);
static void frida_spawn_instance_call_set_helpers (FridaSpawnInstance * self, GumDarwinUnifiedThreadState * state, mach_vm_address_t helpers);
static void frida_spawn_instance_call_dlopen (FridaSpawnInstance * self, GumDarwinUnifiedThreadState * state, mach_vm_address_t lib_name, int mode);
static gboolean frida_find_cf_initialize (const GumModuleDetails * details, gpointer user_data);
static gboolean frida_find_cf_initialize (GumModule * module, gpointer user_data);
static void frida_spawn_instance_call_cf_initialize (FridaSpawnInstance * self, GumDarwinUnifiedThreadState * state);
static void frida_spawn_instance_set_nth_breakpoint (FridaSpawnInstance * self, guint n, GumAddress break_at, FridaBreakpointRepeat repeat);
static void frida_spawn_instance_enable_nth_breakpoint (FridaSpawnInstance * self, guint n);
@@ -410,7 +410,7 @@ static void frida_configure_terminal_attributes (gint fd);
static FridaInjectInstance * frida_inject_instance_new (FridaDarwinHelperBackend * backend, guint id, guint pid);
static FridaInjectInstance * frida_inject_instance_clone (const FridaInjectInstance * instance, guint id);
static void frida_inject_instance_free (FridaInjectInstance * instance);
static void frida_inject_instance_close (FridaInjectInstance * instance);
static gboolean frida_inject_instance_task_did_not_exec (FridaInjectInstance * instance);
static gboolean frida_inject_instance_start_thread (FridaInjectInstance * self, GError ** error);
@@ -762,30 +762,30 @@ permission_denied:
}
void
_frida_darwin_helper_backend_create_context (FridaDarwinHelperBackend * self)
_frida_darwin_helper_backend_create_dispatch_context (FridaDarwinHelperBackend * self)
{
FridaHelperContext * ctx;
FridaDispatchContext * ctx;
ctx = g_slice_new (FridaHelperContext);
ctx = g_slice_new (FridaDispatchContext);
ctx->dispatch_queue = dispatch_queue_create ("re.frida.helper.queue", DISPATCH_QUEUE_SERIAL);
self->context = ctx;
self->dispatch_context = ctx;
}
void
_frida_darwin_helper_backend_destroy_context (FridaDarwinHelperBackend * self)
_frida_darwin_helper_backend_destroy_dispatch_context (FridaDarwinHelperBackend * self)
{
FridaHelperContext * ctx = self->context;
FridaDispatchContext * ctx = self->dispatch_context;
dispatch_release (ctx->dispatch_queue);
g_slice_free (FridaHelperContext, ctx);
g_slice_free (FridaDispatchContext, ctx);
}
void
_frida_darwin_helper_backend_schedule_on_dispatch_queue (FridaDarwinHelperBackend * self, FridaDarwinHelperBackendDispatchWorker worker, gpointer user_data)
{
FridaHelperContext * ctx = self->context;
FridaDispatchContext * ctx = self->dispatch_context;
dispatch_async (ctx->dispatch_queue, ^
{
@@ -934,7 +934,7 @@ any_failure:
{
if (instance->pid != 0)
kill (instance->pid, SIGKILL);
frida_spawn_instance_free (instance);
frida_spawn_instance_close (instance);
pid = 0;
@@ -1869,7 +1869,7 @@ void
_frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHelperBackend * self, void * opaque_instance, guint task, GError ** error)
{
FridaSpawnInstance * instance = opaque_instance;
FridaHelperContext * ctx = self->context;
FridaDispatchContext * ctx = self->dispatch_context;
const gchar * failed_operation;
kern_return_t kr;
mach_port_t self_task, child_thread;
@@ -2200,9 +2200,9 @@ _frida_darwin_helper_backend_resume_spawn_instance (FridaDarwinHelperBackend * s
}
void
_frida_darwin_helper_backend_free_spawn_instance (FridaDarwinHelperBackend * self, void * instance)
_frida_darwin_helper_backend_close_spawn_instance (FridaDarwinHelperBackend * self, void * instance)
{
frida_spawn_instance_free (instance);
frida_spawn_instance_close (instance);
}
guint
@@ -2489,7 +2489,7 @@ mach_failure:
}
failure:
{
frida_inject_instance_free (instance);
frida_inject_instance_close (instance);
goto beach;
}
beach:
@@ -2599,7 +2599,7 @@ frida_inject_instance_start_thread (FridaInjectInstance * self, GError ** error)
mach_msg_type_number_t thread_state_count;
kern_return_t kr;
const gchar * failed_operation;
FridaHelperContext * ctx = self->backend->context;
FridaDispatchContext * ctx = self->backend->dispatch_context;
dispatch_source_t source;
thread_state = g_alloca (self->thread_state_count * sizeof (natural_t));
@@ -2646,7 +2646,7 @@ frida_inject_instance_on_thread_monitor_cancel (void * context)
FridaInjectInstance * self = context;
dispatch_release (g_steal_pointer (&self->thread_monitor_source));
frida_inject_instance_free (self);
frida_inject_instance_close (self);
}
static void
@@ -2705,7 +2705,7 @@ _frida_darwin_helper_backend_join_inject_instance_posix_thread (FridaDarwinHelpe
static void
frida_inject_instance_join_posix_thread (FridaInjectInstance * self, mach_port_t posix_thread)
{
FridaHelperContext * ctx = self->backend->context;
FridaDispatchContext * ctx = self->backend->dispatch_context;
mach_port_t self_task;
dispatch_source_t source;
@@ -2738,9 +2738,9 @@ _frida_darwin_helper_backend_get_pid_of_inject_instance (FridaDarwinHelperBacken
}
void
_frida_darwin_helper_backend_free_inject_instance (FridaDarwinHelperBackend * self, void * instance)
_frida_darwin_helper_backend_close_inject_instance (FridaDarwinHelperBackend * self, void * instance)
{
frida_inject_instance_free (instance);
frida_inject_instance_close (instance);
}
static FridaSpawnInstance *
@@ -2750,7 +2750,7 @@ frida_spawn_instance_new (FridaDarwinHelperBackend * backend)
guint i;
instance = g_slice_new0 (FridaSpawnInstance);
instance->backend = backend;
instance->backend = g_object_ref (backend);
instance->thread = MACH_PORT_NULL;
instance->server_port = MACH_PORT_NULL;
@@ -2773,11 +2773,13 @@ frida_spawn_instance_new (FridaDarwinHelperBackend * backend)
instance->page_pool[i].scratch_page = 0;
}
_frida_darwin_helper_backend_on_instance_created (backend, instance);
return instance;
}
static void
frida_spawn_instance_free (FridaSpawnInstance * instance)
frida_spawn_instance_close (FridaSpawnInstance * instance)
{
task_t self_task;
FridaExceptionPortSet * previous_ports;
@@ -2816,6 +2818,9 @@ frida_spawn_instance_free (FridaSpawnInstance * instance)
if (instance->dyld != NULL)
g_object_unref (instance->dyld);
_frida_darwin_helper_backend_on_instance_destroyed (instance->backend, instance);
g_object_unref (instance->backend);
g_slice_free (FridaSpawnInstance, instance);
}
@@ -2858,7 +2863,7 @@ frida_spawn_instance_on_server_cancel (void * context)
FridaSpawnInstance * self = context;
dispatch_release (g_steal_pointer (&self->server_recv_source));
frida_spawn_instance_free (self);
frida_spawn_instance_close (self);
}
static void
@@ -3646,22 +3651,25 @@ frida_spawn_instance_destroy_dyld_data (FridaSpawnInstance * self)
#if defined (HAVE_IOS) || defined (HAVE_TVOS)
static gboolean
frida_pick_ios_tvos_bootstrapper (const GumModuleDetails * details, gpointer user_data)
frida_pick_ios_tvos_bootstrapper (GumModule * module, gpointer user_data)
{
FridaSpawnInstanceDyldData * data = user_data;
const gchar * path;
const gchar * candidates[] = {
"/usr/lib/substitute-inserter.dylib",
"/usr/lib/pspawn_payload-stg2.dylib"
};
guint i;
path = gum_module_get_path (module);
for (i = 0; i != G_N_ELEMENTS (candidates); i++)
{
const gchar * bootstrapper = candidates[i];
if (strcmp (details->path, bootstrapper) == 0)
if (strcmp (path, bootstrapper) == 0)
{
strcpy (data->bootstrapper, details->path);
strcpy (data->bootstrapper, path);
return FALSE;
}
}
@@ -3824,15 +3832,15 @@ frida_spawn_instance_call_dlopen (FridaSpawnInstance * self, GumDarwinUnifiedThr
}
static gboolean
frida_find_cf_initialize (const GumModuleDetails * details, gpointer user_data)
frida_find_cf_initialize (GumModule * module, gpointer user_data)
{
FridaSpawnInstance * self = user_data;
GumDarwinModule * core_foundation;
if (strcmp (details->path, CORE_FOUNDATION) != 0)
if (strcmp (gum_module_get_path (module), CORE_FOUNDATION) != 0)
return TRUE;
core_foundation = gum_darwin_module_new_from_memory (CORE_FOUNDATION, self->task, details->range->base_address,
core_foundation = gum_darwin_module_new_from_memory (CORE_FOUNDATION, self->task, gum_module_get_range (module)->base_address,
GUM_DARWIN_MODULE_FLAGS_NONE, NULL);
self->cf_initialize_address = gum_darwin_module_resolve_symbol_address (core_foundation, "___CFInitialize");
@@ -4111,6 +4119,8 @@ frida_inject_instance_new (FridaDarwinHelperBackend * backend, guint id, guint p
instance->backend = g_object_ref (backend);
_frida_darwin_helper_backend_on_instance_created (backend, instance);
return instance;
}
@@ -4140,11 +4150,13 @@ frida_inject_instance_clone (const FridaInjectInstance * instance, guint id)
g_object_ref (clone->backend);
_frida_darwin_helper_backend_on_instance_created (clone->backend, clone);
return clone;
}
static void
frida_inject_instance_free (FridaInjectInstance * instance)
frida_inject_instance_close (FridaInjectInstance * instance)
{
FridaAgentContext * agent_context = instance->agent_context;
task_t self_task;
@@ -4184,6 +4196,7 @@ frida_inject_instance_free (FridaInjectInstance * instance)
if (instance->task != MACH_PORT_NULL)
mach_port_deallocate (self_task, instance->task);
_frida_darwin_helper_backend_on_instance_destroyed (instance->backend, instance);
g_object_unref (instance->backend);
g_slice_free (FridaInjectInstance, instance);
@@ -4289,9 +4302,10 @@ frida_agent_context_init (FridaAgentContext * self, const FridaAgentDetails * de
static gboolean
frida_agent_context_init_functions (FridaAgentContext * self, GumDarwinModuleResolver * resolver, GumDarwinMapper * mapper, GError ** error)
{
gboolean success = FALSE;
GumDarwinModule * module;
module = gum_darwin_module_resolver_find_module (resolver, "/usr/lib/system/libsystem_kernel.dylib");
module = gum_darwin_module_resolver_find_module_by_name (resolver, "/usr/lib/system/libsystem_kernel.dylib");
if (module == NULL)
goto no_libc;
FRIDA_AGENT_CONTEXT_RESOLVE (mach_task_self);
@@ -4300,8 +4314,10 @@ frida_agent_context_init_functions (FridaAgentContext * self, GumDarwinModuleRes
FRIDA_AGENT_CONTEXT_RESOLVE (mach_msg_receive);
FRIDA_AGENT_CONTEXT_RESOLVE (mach_port_destroy);
FRIDA_AGENT_CONTEXT_RESOLVE (thread_terminate);
g_object_unref (module);
module = NULL;
module = gum_darwin_module_resolver_find_module (resolver, "/usr/lib/system/libsystem_pthread.dylib");
module = gum_darwin_module_resolver_find_module_by_name (resolver, "/usr/lib/system/libsystem_pthread.dylib");
if (module == NULL)
goto no_libc;
FRIDA_AGENT_CONTEXT_TRY_RESOLVE (pthread_create_from_mach_thread);
@@ -4312,18 +4328,23 @@ frida_agent_context_init_functions (FridaAgentContext * self, GumDarwinModuleRes
FRIDA_AGENT_CONTEXT_TRY_RESOLVE (pthread_threadid_np);
FRIDA_AGENT_CONTEXT_RESOLVE (pthread_detach);
FRIDA_AGENT_CONTEXT_RESOLVE (pthread_self);
g_object_unref (module);
module = NULL;
if (mapper == NULL)
{
module = gum_darwin_module_resolver_find_module (resolver, "/usr/lib/system/libdyld.dylib");
module = gum_darwin_module_resolver_find_module_by_name (resolver, "/usr/lib/system/libdyld.dylib");
if (module == NULL)
goto no_libc;
FRIDA_AGENT_CONTEXT_RESOLVE (dlopen);
FRIDA_AGENT_CONTEXT_RESOLVE (dlsym);
FRIDA_AGENT_CONTEXT_RESOLVE (dlclose);
g_object_unref (module);
module = NULL;
}
return TRUE;
success = TRUE;
goto beach;
no_libc:
{
@@ -4331,7 +4352,7 @@ no_libc:
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unable to attach to processes without Apple's libc (for now)");
goto failure;
goto beach;
}
missing_symbol:
{
@@ -4339,11 +4360,13 @@ missing_symbol:
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"Unexpected error while resolving functions");
goto failure;
goto beach;
}
failure:
beach:
{
return FALSE;
g_clear_object (&module);
return success;
}
}
+61 -28
View File
@@ -20,7 +20,8 @@ namespace Frida {
protected delegate void DispatchWorker ();
protected delegate void LaunchCompletionHandler (owned StdioPipes? pipes, owned Error? error);
public void * context;
private MainContext main_context;
public void * dispatch_context;
public Gee.HashMap<uint, void *> spawn_instances = new Gee.HashMap<uint, void *> ();
private Gee.HashMap<uint, OutputStream> stdin_streams = new Gee.HashMap<uint, OutputStream> ();
@@ -32,6 +33,9 @@ namespace Frida {
private Gee.HashMap<void *, uint> inject_cleaner_by_instance = new Gee.HashMap<void *, uint> ();
private Gee.HashMap<uint, uint> inject_expiry_timers = new Gee.HashMap<uint, uint> ();
private Gee.Set<void *> instances = new Gee.HashSet<void *> ();
private SourceFunc? on_last_instance_destroyed = null;
public uint next_id = 1;
private PolicySoftener policy_softener;
@@ -40,7 +44,8 @@ namespace Frida {
private Cancellable io_cancellable = new Cancellable ();
construct {
_create_context ();
main_context = MainContext.ref_thread_default ();
_create_dispatch_context ();
dtrace_agent = DTraceAgent.try_open ();
if (dtrace_agent != null) {
@@ -63,11 +68,7 @@ namespace Frida {
}
~DarwinHelperBackend () {
foreach (var instance in spawn_instances.values)
_free_spawn_instance (instance);
foreach (var instance in inject_instances.values)
_free_inject_instance (instance);
_destroy_context ();
_destroy_dispatch_context ();
}
public async void close (Cancellable? cancellable) throws IOError {
@@ -77,11 +78,24 @@ namespace Frida {
pending.complete ();
foreach (var entry in inject_cleaner_by_instance.entries) {
_free_inject_instance (entry.key);
_close_inject_instance (entry.key);
Source.remove (entry.value);
}
inject_cleaner_by_instance.clear ();
foreach (var instance in spawn_instances.values)
_close_spawn_instance (instance);
spawn_instances.clear ();
bool any_pending = false;
lock (instances) {
any_pending = !instances.is_empty;
if (any_pending)
on_last_instance_destroyed = close.callback;
}
if (any_pending)
yield;
if (dtrace_agent != null) {
dtrace_agent.spawn_added.disconnect (on_dtrace_agent_spawn_added);
dtrace_agent.spawn_removed.disconnect (on_dtrace_agent_spawn_removed);
@@ -139,11 +153,11 @@ namespace Frida {
Error error = null;
_launch (identifier, options, (p, e) => {
Idle.add (() => {
schedule_on_frida_thread (() => {
pipes = p;
error = e;
launch.callback ();
return false;
return Source.REMOVE;
});
});
@@ -187,7 +201,7 @@ namespace Frida {
inject_cleaner_by_instance.unset (instance, out source_id);
Source.remove (source_id);
_free_inject_instance (instance);
_close_inject_instance (instance);
}
policy_softener.forget (pid);
@@ -205,7 +219,7 @@ namespace Frida {
void * instance;
if (spawn_instances.unset (pid, out instance))
_free_spawn_instance (instance);
_close_spawn_instance (instance);
child_dead (pid);
}
@@ -293,7 +307,7 @@ namespace Frida {
void * instance;
if (spawn_instances.unset (pid, out instance)) {
_resume_spawn_instance (instance);
_free_spawn_instance (instance);
_close_spawn_instance (instance);
} else {
resume_with_validation (pid);
}
@@ -412,7 +426,7 @@ namespace Frida {
} catch (GLib.Error e) {
if (instance_created_here) {
spawn_instances.unset (pid);
_free_spawn_instance (spawn_instance);
_close_spawn_instance (spawn_instance);
}
throw_api_error (e);
@@ -495,21 +509,21 @@ namespace Frida {
}
public void _on_spawn_instance_ready (uint pid) {
Idle.add (() => {
schedule_on_frida_thread (() => {
spawn_instance_ready (pid);
return false;
return Source.REMOVE;
});
}
public void _on_spawn_instance_error (uint pid, Error error) {
Idle.add (() => {
schedule_on_frida_thread (() => {
spawn_instance_error (pid, error);
return false;
return Source.REMOVE;
});
}
public void _on_mach_thread_dead (uint id, void * posix_thread) {
Idle.add (() => {
schedule_on_frida_thread (() => {
var instance = inject_instances[id];
assert (instance != null);
@@ -518,14 +532,14 @@ namespace Frida {
else
_destroy_inject_instance (id);
return false;
return Source.REMOVE;
});
}
public void _on_posix_thread_dead (uint id) {
Idle.add (() => {
schedule_on_frida_thread (() => {
_destroy_inject_instance (id);
return false;
return Source.REMOVE;
});
}
@@ -545,7 +559,7 @@ namespace Frida {
private void schedule_inject_instance_cleanup (void * instance) {
var cleanup_source = new TimeoutSource (50);
cleanup_source.set_callback (() => {
_free_inject_instance (instance);
_close_inject_instance (instance);
var removed = inject_cleaner_by_instance.unset (instance);
assert (removed);
@@ -601,6 +615,19 @@ namespace Frida {
policy_softener.forget (pid);
}
public void _on_instance_created (void * instance) {
lock (instances)
instances.add (instance);
}
public void _on_instance_destroyed (void * instance) {
lock (instances) {
instances.remove (instance);
if (on_last_instance_destroyed != null && instances.is_empty)
schedule_on_frida_thread ((owned) on_last_instance_destroyed);
}
}
private void on_dtrace_agent_spawn_added (HostSpawnInfo info) {
spawn_added (info);
}
@@ -611,11 +638,17 @@ namespace Frida {
private async void flush_dispatch_queue () {
_schedule_on_dispatch_queue (() => {
Idle.add (flush_dispatch_queue.callback);
schedule_on_frida_thread (flush_dispatch_queue.callback);
});
yield;
}
private void schedule_on_frida_thread (owned SourceFunc function) {
var source = new IdleSource ();
source.set_callback ((owned) function);
source.attach (main_context);
}
public extern static PipeEndpoints make_pipe_endpoints (uint local_task, uint remote_pid, uint remote_task) throws Error;
public extern static void make_pipe_endpoint_from_socket (uint pid, uint task, GLib.Socket sock, out string address)
throws Error;
@@ -626,8 +659,8 @@ namespace Frida {
public extern static bool is_mmap_available ();
public extern static MappedLibraryBlob mmap (uint task, Bytes blob) throws Error;
protected extern void _create_context ();
protected extern void _destroy_context ();
protected extern void _create_dispatch_context ();
protected extern void _destroy_dispatch_context ();
protected extern void _schedule_on_dispatch_queue (DispatchWorker worker);
protected extern uint _spawn (string path, HostSpawnOptions options, out StdioPipes? pipes) throws Error;
@@ -642,7 +675,7 @@ namespace Frida {
protected extern void * _create_spawn_instance (uint pid);
protected extern void _prepare_spawn_instance_for_injection (void * instance, uint task) throws Error;
protected extern void _resume_spawn_instance (void * instance);
protected extern void _free_spawn_instance (void * instance);
protected extern void _close_spawn_instance (void * instance);
protected extern uint _inject_into_task (uint pid, uint task, string path_or_name, MappedLibraryBlob? blob, string entrypoint, string data) throws Error;
protected extern void _demonitor (void * instance);
@@ -650,7 +683,7 @@ namespace Frida {
protected extern void _recreate_injectee_thread (void * instance, uint pid, uint task) throws Error;
protected extern void _join_inject_instance_posix_thread (void * instance, void * posix_thread);
protected extern uint _get_pid_of_inject_instance (void * instance);
protected extern void _free_inject_instance (void * instance);
protected extern void _close_inject_instance (void * instance);
}
public class DTraceAgent : Object {
+4 -2
View File
@@ -151,10 +151,12 @@ frida_replacement_xpc_look_up_endpoint (const char * service_name, uint32_t type
static FridaXpcLookUpEndpointFunc
frida_find_xpc_look_up_endpoint_implementation (void)
{
GumModule * libxpc;
guint32 * cursor;
cursor = GSIZE_TO_POINTER (
gum_strip_code_address (gum_module_find_export_by_name ("/usr/lib/system/libxpc.dylib", "xpc_endpoint_create_bs_named")));
libxpc = gum_process_find_module_by_name ("/usr/lib/system/libxpc.dylib");
cursor = GSIZE_TO_POINTER (gum_strip_code_address (gum_module_find_export_by_name (libxpc, "xpc_endpoint_create_bs_named")));
g_object_unref (libxpc);
if (cursor == NULL)
return NULL;
+1 -1
View File
@@ -791,7 +791,7 @@ namespace Frida.Droidy {
}
private const uint16 ADB_SERVER_DEFAULT_PORT = 5037;
private const uint16 MAX_MESSAGE_LENGTH = 1024;
private const size_t MAX_MESSAGE_LENGTH = 65536;
public static async Client open (Cancellable? cancellable = null) throws Error, IOError {
SocketConnectable? connectable = null;
+68 -91
View File
@@ -1,3 +1,4 @@
[CCode (gir_namespace = "Frida", gir_version = "1.0")]
namespace Frida {
public extern void init ();
public extern void init_with_runtime (Runtime runtime);
@@ -33,12 +34,24 @@ namespace Frida {
private Promise<bool> start_request;
private Promise<bool> stop_request;
private HostSessionService service = null;
private HostSessionService service;
private Gee.ArrayList<Device> devices = new Gee.ArrayList<Device> ();
private Gee.ArrayList<DeviceObserverEntry> on_device_added = new Gee.ArrayList<DeviceObserverEntry> ();
private Cancellable io_cancellable = new Cancellable ();
public DeviceManager () {
service = new HostSessionService.with_default_backends ();
}
public DeviceManager.with_nonlocal_backends_only () {
service = new HostSessionService.with_nonlocal_backends_only ();
}
public DeviceManager.with_socket_backend_only () {
service = new HostSessionService.with_socket_backend_only ();
}
public async void close (Cancellable? cancellable = null) throws IOError {
yield stop_service (cancellable);
}
@@ -303,7 +316,7 @@ namespace Frida {
}
}
public void _release_device (Device device) {
internal void _release_device (Device device) {
var device_did_exist = devices.remove (device);
assert (device_did_exist);
}
@@ -339,7 +352,6 @@ namespace Frida {
}
private async void start_service () {
service = new HostSessionService.with_default_backends ();
try {
service.provider_available.connect (on_provider_available);
service.provider_unavailable.connect (on_provider_unavailable);
@@ -524,18 +536,10 @@ namespace Frida {
}
}
public HostSessionProvider provider {
get;
construct;
}
public weak DeviceManager? manager {
get;
construct;
}
private string? _id;
private string? _name;
internal HostSessionProvider provider;
private unowned DeviceManager? manager;
private HostSessionOptions? host_session_options;
private Promise<HostSession>? host_session_request;
@@ -552,24 +556,26 @@ namespace Frida {
public delegate bool ProcessPredicate (Process process);
internal Device (DeviceManager? manager, HostSessionProvider provider, string? id = null, string? name = null,
internal Device (DeviceManager? mgr, HostSessionProvider prov, string? id = null, string? name = null,
HostSessionOptions? options = null) {
Object (
manager: manager,
provider: provider,
icon: provider.icon
);
Object (icon: prov.icon);
_id = id;
_name = name;
manager = mgr;
host_session_options = options;
assign_provider (prov);
}
construct {
_bus = new Bus (this);
}
private void assign_provider (HostSessionProvider prov) {
provider = prov;
provider.host_session_detached.connect (on_host_session_detached);
provider.agent_session_detached.connect (on_agent_session_detached);
_bus = new Bus (this);
}
public bool is_lost () {
@@ -1310,7 +1316,7 @@ namespace Frida {
throw new Error.INVALID_OPERATION ("Device is gone");
}
public async HostSession get_host_session (Cancellable? cancellable) throws Error, IOError {
internal async HostSession get_host_session (Cancellable? cancellable) throws Error, IOError {
while (host_session_request != null) {
try {
return yield host_session_request.future.wait_async (cancellable);
@@ -1338,16 +1344,6 @@ namespace Frida {
}
}
public HostSession get_host_session_sync (Cancellable? cancellable = null) throws Error, IOError {
return create<GetHostSessionTask> ().execute (cancellable);
}
private class GetHostSessionTask : DeviceTask<HostSession> {
protected override async HostSession perform_operation () throws Error, IOError {
return yield parent.get_host_session (cancellable);
}
}
private void on_host_session_detached (HostSession session) {
if (session != current_host_session)
return;
@@ -1462,7 +1458,7 @@ namespace Frida {
}
}
public async void _release_session (Session session, bool may_block, Cancellable? cancellable) throws IOError {
internal async void _release_session (Session session, bool may_block, Cancellable? cancellable) throws IOError {
AgentSessionId? session_id = null;
foreach (var entry in agent_sessions.entries) {
if (entry.value == session) {
@@ -2074,26 +2070,13 @@ namespace Frida {
construct;
}
public AgentSessionId id {
get;
construct;
}
public AgentSession session {
get {
return active_session;
}
}
public uint persist_timeout {
get;
construct;
}
public weak Device device {
get;
construct;
}
private AgentSessionId id;
private unowned Device device;
private State state = ATTACHED;
private Promise<bool> close_request;
@@ -2131,12 +2114,10 @@ namespace Frida {
}
internal Session (Device device, uint pid, AgentSessionId id, SessionOptions options) {
Object (
pid: pid,
id: id,
persist_timeout: options.persist_timeout,
device: device
);
Object (pid: pid, persist_timeout: options.persist_timeout);
this.id = id;
this.device = device;
}
public bool is_detached () {
@@ -2192,7 +2173,7 @@ namespace Frida {
uint last_tx_batch_id;
try {
yield session.resume (last_rx_batch_id, cancellable, out last_tx_batch_id);
yield active_session.resume (last_rx_batch_id, cancellable, out last_tx_batch_id);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2224,7 +2205,7 @@ namespace Frida {
check_open ();
try {
yield session.enable_child_gating (cancellable);
yield active_session.enable_child_gating (cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2244,7 +2225,7 @@ namespace Frida {
check_open ();
try {
yield session.disable_child_gating (cancellable);
yield active_session.disable_child_gating (cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2268,7 +2249,7 @@ namespace Frida {
AgentScriptId script_id;
try {
script_id = yield session.create_script (source, raw_options, cancellable);
script_id = yield active_session.create_script (source, raw_options, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2306,7 +2287,7 @@ namespace Frida {
AgentScriptId script_id;
try {
script_id = yield session.create_script_from_bytes (bytes.get_data (), raw_options,
script_id = yield active_session.create_script_from_bytes (bytes.get_data (), raw_options,
cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
@@ -2345,7 +2326,7 @@ namespace Frida {
uint8[] data;
try {
data = yield session.compile_script (source, raw_options, cancellable);
data = yield active_session.compile_script (source, raw_options, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2378,7 +2359,7 @@ namespace Frida {
uint8[] data;
try {
data = yield session.snapshot_script (embed_script, raw_options, cancellable);
data = yield active_session.snapshot_script (embed_script, raw_options, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2782,7 +2763,7 @@ namespace Frida {
PortalMembershipId membership_id;
try {
membership_id = yield session.join_portal (address, raw_options, cancellable);
membership_id = yield active_session.join_portal (address, raw_options, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -2944,7 +2925,7 @@ namespace Frida {
}
}
public void _release_script (AgentScriptId script_id) {
internal void _release_script (AgentScriptId script_id) {
var script_did_exist = scripts.unset (script_id);
assert (script_did_exist);
}
@@ -3010,7 +2991,7 @@ namespace Frida {
private async void close_session_and_peer_connection (Cancellable? cancellable) throws IOError {
try {
yield session.close (cancellable);
yield active_session.close (cancellable);
} catch (GLib.Error e) {
if (e is IOError.CANCELLED) {
discard_peer_connection ();
@@ -3033,6 +3014,10 @@ namespace Frida {
}
#endif
public DBusConnection _get_connection () {
return ((DBusProxy) active_session).g_connection;
}
private T create<T> () {
return Object.new (typeof (T), parent: this);
}
@@ -3049,22 +3034,18 @@ namespace Frida {
public signal void destroyed ();
public signal void message (string json, Bytes? data);
public AgentScriptId id {
get;
construct;
}
public weak Session session {
get;
construct;
}
private AgentScriptId id;
private unowned Session session;
private Promise<bool> close_request;
private Gum.InspectorServer? inspector_server;
internal Script (Session session, AgentScriptId script_id) {
Object (id: script_id, session: session);
Object ();
this.id = script_id;
this.session = session;
}
public bool is_destroyed () {
@@ -3075,7 +3056,7 @@ namespace Frida {
check_open ();
try {
yield session.session.load_script (id, cancellable);
yield session.active_session.load_script (id, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -3111,7 +3092,7 @@ namespace Frida {
check_open ();
try {
yield session.session.eternalize_script (id, cancellable);
yield session.active_session.eternalize_script (id, cancellable);
yield _do_close (false, cancellable);
} catch (GLib.Error e) {
@@ -3162,7 +3143,7 @@ namespace Frida {
inspector_server.message.connect (on_debugger_message_from_frontend);
try {
yield session.session.enable_debugger (id, cancellable);
yield session.active_session.enable_debugger (id, cancellable);
} catch (GLib.Error e) {
inspector_server = null;
@@ -3176,7 +3157,7 @@ namespace Frida {
inspector_server = null;
try {
yield session.session.disable_debugger (id, cancellable);
yield session.active_session.disable_debugger (id, cancellable);
} catch (GLib.Error e) {
}
@@ -3210,7 +3191,7 @@ namespace Frida {
inspector_server = null;
try {
yield session.session.disable_debugger (id, cancellable);
yield session.active_session.disable_debugger (id, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
@@ -3264,7 +3245,7 @@ namespace Frida {
if (may_block) {
try {
yield parent.session.destroy_script (id, cancellable);
yield parent.active_session.destroy_script (id, cancellable);
} catch (GLib.Error e) {
}
}
@@ -3287,23 +3268,19 @@ namespace Frida {
}
public class PortalMembership : Object {
public uint id {
get;
construct;
}
public Session session {
get;
construct;
}
private uint id;
private Session session;
internal PortalMembership (Session session, PortalMembershipId membership_id) {
Object (id: membership_id.handle, session: session);
Object ();
this.id = membership_id.handle;
this.session = session;
}
public async void terminate (Cancellable? cancellable = null) throws Error, IOError {
try {
yield session.session.leave_portal (PortalMembershipId (id), cancellable);
yield session.active_session.leave_portal (PortalMembershipId (id), cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
+166 -6
View File
@@ -19,7 +19,7 @@ namespace Frida.Fruity {
return null;
var iface_stream = new DataInputStream (iface.read ());
string iface_name = iface_stream.read_line ();
if (iface_name != "NCM Control" && iface_name != "AppleUSBEthernet")
if (iface_name != "NCM Control")
return null;
var serial = File.new_build_filename (dev_path, "..", "..", "..", "serial");
@@ -27,7 +27,7 @@ namespace Frida.Fruity {
return null;
var serial_stream = new DataInputStream (serial.read ());
return serial_stream.read_line ();
return UsbDevice.udid_from_serial_number (serial_stream.read_line ());
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("%s", e.message);
}
@@ -177,12 +177,14 @@ namespace Frida.Fruity {
Linux.Network.IfAddrs ifaddrs;
Linux.Network.getifaddrs (out ifaddrs);
for (unowned Linux.Network.IfAddrs candidate = ifaddrs; candidate != null; candidate = candidate.ifa_next) {
unowned Posix.SockAddr? address = candidate.ifa_addr;
if (address == null || address.sa_family != Posix.AF_INET6)
continue;
if (candidate.ifa_name != ifname)
continue;
if (candidate.ifa_addr.sa_family != Posix.AF_INET6)
continue;
return (InetSocketAddress)
SocketAddress.from_native ((void *) candidate.ifa_addr, sizeof (Posix.SockAddrIn6));
return (InetSocketAddress) SocketAddress.from_native ((void *) address, sizeof (Posix.SockAddrIn6));
}
throw new Error.NOT_SUPPORTED ("Unable to resolve interface address");
@@ -195,6 +197,164 @@ namespace Frida.Fruity {
}
}
namespace Network {
public async void wait_until_interfaces_ready (Gee.Collection<string> interface_names, Cancellable? cancellable)
throws Error, IOError {
try {
var connection = yield GLib.Bus.get (BusType.SYSTEM, cancellable);
NetworkManager.Service? nm = null;
Networkd.Service? netd = null;
if (yield system_has_service (NetworkManager.SERVICE_NAME, connection, cancellable)) {
nm = yield connection.get_proxy (NetworkManager.SERVICE_NAME, NetworkManager.SERVICE_PATH,
DO_NOT_LOAD_PROPERTIES, cancellable);
} else if (yield system_has_service (Networkd.SERVICE_NAME, connection, cancellable)) {
netd = yield connection.get_proxy (Networkd.SERVICE_NAME, Networkd.SERVICE_PATH,
DO_NOT_LOAD_PROPERTIES, cancellable);
} else {
return;
}
var remaining = interface_names.size + 1;
NotifyCompleteFunc on_complete = () => {
remaining--;
if (remaining == 0)
wait_until_interfaces_ready.callback ();
};
foreach (var name in interface_names) {
if (nm != null) {
NetworkManager.wait_until_interface_ready.begin (name, nm, connection, cancellable,
on_complete);
} else {
Networkd.wait_until_interface_ready.begin (name, netd, connection, cancellable,
on_complete);
}
}
var source = new IdleSource ();
source.set_callback (() => {
on_complete ();
return Source.REMOVE;
});
source.attach (MainContext.get_thread_default ());
yield;
} catch (GLib.Error e) {
}
}
private async bool system_has_service (string name, DBusConnection connection, Cancellable? cancellable) throws GLib.Error {
var v = yield connection.call (
"org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
"NameHasOwner",
new Variant.tuple ({ name }),
new VariantType.tuple ({ VariantType.BOOLEAN }),
DBusCallFlags.NONE, -1, cancellable);
bool has_owner;
v.get ("(b)", out has_owner);
return has_owner;
}
}
private delegate void NotifyCompleteFunc ();
namespace NetworkManager {
private async void wait_until_interface_ready (string name, Service service, DBusConnection connection,
Cancellable? cancellable, NotifyCompleteFunc on_complete) {
try {
string device_path = yield service.get_device_by_ip_iface (name);
Device device = yield connection.get_proxy (SERVICE_NAME, device_path, DBusProxyFlags.NONE, cancellable);
var device_proxy = (DBusProxy) device;
ulong handler = device_proxy.g_properties_changed.connect ((changed, invalidated) => {
if (changed.lookup_value ("StateReason", null) != null)
wait_until_interface_ready.callback ();
});
while (!cancellable.is_cancelled ()) {
uint32 state, reason;
device_proxy.get_cached_property ("StateReason").get ("(uu)", out state, out reason);
if (state == DEVICE_STATE_ACTIVATED)
break;
if (state == DEVICE_STATE_DISCONNECTED && reason != DEVICE_STATE_REASON_NONE)
break;
yield;
}
device_proxy.disconnect (handler);
} catch (GLib.Error e) {
}
on_complete ();
}
private const string SERVICE_NAME = "org.freedesktop.NetworkManager";
private const string SERVICE_PATH = "/org/freedesktop/NetworkManager";
[DBus (name = "org.freedesktop.NetworkManager")]
private interface Service : Object {
public abstract async string get_device_by_ip_iface (string iface) throws GLib.Error;
}
[DBus (name = "org.freedesktop.NetworkManager.Device")]
private interface Device : Object {
}
private const uint32 DEVICE_STATE_DISCONNECTED = 30;
private const uint32 DEVICE_STATE_ACTIVATED = 100;
private const uint32 DEVICE_STATE_REASON_NONE = 0;
}
namespace Networkd {
private async void wait_until_interface_ready (string name, Service service, DBusConnection connection,
Cancellable? cancellable, NotifyCompleteFunc on_complete) {
try {
int32 ifindex;
string link_path;
yield service.get_link_by_name (name, out ifindex, out link_path);
Link link = yield connection.get_proxy (SERVICE_NAME, link_path, DBusProxyFlags.NONE, cancellable);
var link_proxy = (DBusProxy) link;
ulong handler = link_proxy.g_properties_changed.connect ((changed, invalidated) => {
wait_until_interface_ready.callback ();
});
while (!cancellable.is_cancelled ()) {
string operational_state;
link_proxy.get_cached_property ("OperationalState").get ("s", out operational_state);
if (operational_state != "carrier")
break;
yield;
}
link_proxy.disconnect (handler);
} catch (GLib.Error e) {
}
on_complete ();
}
private const string SERVICE_NAME = "org.freedesktop.network1";
private const string SERVICE_PATH = "/org/freedesktop/network1";
[DBus (name = "org.freedesktop.network1.Manager")]
private interface Service : Object {
public abstract async void get_link_by_name (string name, out int32 ifindex, out string path) throws GLib.Error;
}
[DBus (name = "org.freedesktop.network1.Link")]
private interface Link : Object {
}
}
namespace Resolved {
public const string SERVICE_NAME = "org.freedesktop.resolve1";
public const string SERVICE_PATH = "/org/freedesktop/resolve1";
+27 -2
View File
@@ -129,6 +129,7 @@ namespace Frida.Fruity {
private ConnectionType _connection_type;
private string _udid;
private string _name;
private Bytes? _remote_unlock_host_key;
private Promise<Tunnel>? tunnel_request;
@@ -153,6 +154,13 @@ namespace Frida.Fruity {
var reader = new XpcObjectReader (device_info);
_name = reader.read_member ("name").get_string_value ();
reader.end_member ();
if (reader.has_member ("remoteUnlockHostKey")) {
_remote_unlock_host_key = new Bytes (reader.read_member ("remoteUnlockHostKey").get_data_value ());
reader.end_member ();
} else {
_remote_unlock_host_key = null;
}
}
private void on_state_changed (Object obj, ParamSpec pspec) {
@@ -224,7 +232,7 @@ namespace Frida.Fruity {
tunnel_request = new Promise<Tunnel> ();
try {
var tunnel = new MacOSTunnel (pairing_device);
var tunnel = new MacOSTunnel (pairing_device, _remote_unlock_host_key);
yield tunnel.attach (cancellable);
tunnel_request.resolve (tunnel);
@@ -246,6 +254,18 @@ namespace Frida.Fruity {
}
}
public int64 opened_at {
get {
return _opened_at;
}
}
public Bytes? remote_unlock_host_key {
get {
return _remote_unlock_host_key;
}
}
public Darwin.Xpc.Uuid? assertion_identifier {
get {
return _assertion_identifier;
@@ -253,12 +273,15 @@ namespace Frida.Fruity {
}
private XpcClient pairing_device;
private Bytes? _remote_unlock_host_key;
private Darwin.Xpc.Uuid? _assertion_identifier;
private InetAddress? tunnel_device_address;
private DiscoveryService? _discovery;
private int64 _opened_at = -1;
public MacOSTunnel (XpcClient pairing_device) {
public MacOSTunnel (XpcClient pairing_device, Bytes? remote_unlock_host_key) {
this.pairing_device = pairing_device;
_remote_unlock_host_key = remote_unlock_host_key;
}
public async void close (Cancellable? cancellable) throws IOError {
@@ -275,6 +298,8 @@ namespace Frida.Fruity {
r.body.set_int64 ("flags", 0);
var response = yield pairing_device.request (r.message, cancellable);
_opened_at = get_monotonic_time ();
var reader = new XpcObjectReader (response);
reader.read_member ("response");
+327 -180
View File
@@ -188,6 +188,18 @@ namespace Frida.Fruity {
new Gee.ArrayQueue<UsbmuxLockdownServiceRequest> ();
private LockdownClient? cached_usbmux_lockdown_client;
private const string[] LOCKDOWN_SERVICES_WITHOUT_ESCROW_BAG_SUPPORT = {
"com.apple.accessibility.axAuditDaemon.remoteserver",
"com.apple.afc",
"com.apple.companion_proxy",
"com.apple.crashreportcopymobile",
"com.apple.GPUTools.MobileService",
"com.apple.idamd",
"com.apple.PurpleReverseProxy.Conn",
"com.apple.streaming_zip_conduit",
"com.apple.webinspector",
};
internal void close () {
transports.clear ();
}
@@ -247,6 +259,9 @@ namespace Frida.Fruity {
checkin.set_string ("Request", "RSDCheckin");
checkin.set_string ("Label", "Xcode");
checkin.set_string ("ProtocolVersion", "2");
unowned Bytes? key = tunnel.remote_unlock_host_key;
if (key != null && lockdown_service_supports_escrow_bag (service_name))
checkin.set_bytes ("EscrowBag", key);
try {
yield service.query (checkin, cancellable);
@@ -283,6 +298,15 @@ namespace Frida.Fruity {
return yield request.promise.future.wait_async (cancellable);
}
// FIXME: Replace with `element in array`-check once Vala compiler bug has been fixed so generated C code is warning-free.
private static bool lockdown_service_supports_escrow_bag (string name) {
foreach (unowned string s in LOCKDOWN_SERVICES_WITHOUT_ESCROW_BAG_SUPPORT) {
if (s == name)
return false;
}
return true;
}
private async void process_usbmux_lockdown_service_requests () {
UsbmuxLockdownServiceRequest? req;
bool already_invalidated = false;
@@ -324,81 +348,8 @@ namespace Frida.Fruity {
unowned string location = tokens[1];
if (protocol == "tcp") {
var tunnel = yield find_tunnel (cancellable);
uint16 port;
ulong raw_port;
if (ulong.try_parse (location, out raw_port)) {
if (raw_port == 0 || raw_port > uint16.MAX)
throw new Error.INVALID_ARGUMENT ("Invalid TCP port");
port = (uint16) raw_port;
} else {
if (tunnel == null)
throw new Error.NOT_SUPPORTED ("Unable to resolve port name; tunnel not available");
var service_info = tunnel.discovery.get_service (location);
port = service_info.port;
}
Error? pending_error = null;
if (tunnel != null) {
try {
return yield tunnel.open_tcp_connection (port, cancellable);
} catch (Error e) {
if (e is Error.SERVER_NOT_RUNNING)
pending_error = e;
else
throw e;
}
}
var usbmux_device = find_usbmux_device ();
if (usbmux_device != null) {
if (usbmux_device.connection_type == USB) {
UsbmuxClient client = null;
try {
client = yield UsbmuxClient.open (cancellable);
yield client.connect_to_port (usbmux_device.id, port, cancellable);
return client.connection;
} catch (GLib.Error e) {
if (client != null)
client.close.begin ();
if (e is UsbmuxError.CONNECTION_REFUSED)
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
throw new Error.TRANSPORT ("%s", e.message);
}
}
InetSocketAddress device_address = usbmux_device.network_address;
var target_address = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
address: device_address.address,
port: port,
flowinfo: device_address.flowinfo,
scope_id: device_address.scope_id
);
var client = new SocketClient ();
try {
var connection = yield client.connect_async (target_address, cancellable);
Tcp.enable_nodelay (connection.socket);
return connection;
} 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);
}
}
if (pending_error != null)
throw pending_error;
throw new Error.TRANSPORT ("No viable transport found");
var channel = yield open_tcp_channel (location, ALLOW_ANY_TRANSPORT, cancellable);
return channel.stream;
}
if (protocol == "lockdown")
@@ -407,6 +358,88 @@ namespace Frida.Fruity {
throw new Error.NOT_SUPPORTED ("Unsupported channel address");
}
public async TcpChannel open_tcp_channel (string location, OpenTcpChannelFlags flags, Cancellable? cancellable)
throws Error, IOError {
var usbmux_device = find_usbmux_device ();
var tunnel = yield find_tunnel (cancellable);
uint16 port;
ulong raw_port;
if (ulong.try_parse (location, out raw_port)) {
if (raw_port == 0 || raw_port > uint16.MAX)
throw new Error.INVALID_ARGUMENT ("Invalid TCP port");
port = (uint16) raw_port;
} else {
if (tunnel == null)
throw new Error.NOT_SUPPORTED ("Unable to resolve port name; tunnel not available");
if ((flags & OpenTcpChannelFlags.ALLOW_TUNNEL) == 0)
throw new Error.NOT_SUPPORTED ("Connection to tunnel service not allowed by flags");
var service_info = tunnel.discovery.get_service (location);
port = service_info.port;
}
Error? pending_error = null;
if ((flags & OpenTcpChannelFlags.ALLOW_TUNNEL) != 0 && tunnel != null) {
try {
var stream = yield tunnel.open_tcp_connection (port, cancellable);
return new TcpChannel () { stream = stream, kind = TUNNEL };
} catch (Error e) {
if (e is Error.SERVER_NOT_RUNNING)
pending_error = e;
else
throw e;
}
}
if ((flags & OpenTcpChannelFlags.ALLOW_USBMUX) != 0 && usbmux_device != null) {
if (usbmux_device.connection_type == USB) {
UsbmuxClient client = null;
try {
client = yield UsbmuxClient.open (cancellable);
yield client.connect_to_port (usbmux_device.id, port, cancellable);
return new TcpChannel () { stream = client.connection, kind = USBMUX };
} catch (GLib.Error e) {
if (client != null)
client.close.begin ();
if (e is UsbmuxError.CONNECTION_REFUSED)
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
throw new Error.TRANSPORT ("%s", e.message);
}
}
InetSocketAddress device_address = usbmux_device.network_address;
var target_address = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
address: device_address.address,
port: port,
flowinfo: device_address.flowinfo,
scope_id: device_address.scope_id
);
var client = new SocketClient ();
try {
var connection = yield client.connect_async (target_address, cancellable);
Tcp.enable_nodelay (connection.socket);
return new TcpChannel () { stream = connection, kind = USBMUX };
} 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);
}
}
if (pending_error != null)
throw pending_error;
throw new Error.TRANSPORT ("No viable transport found");
}
private static int compare_transports (Transport a, Transport b) {
return score_transport (b) - score_transport (a);
}
@@ -432,6 +465,23 @@ namespace Frida.Fruity {
}
}
public class TcpChannel {
public IOStream stream;
public Kind kind;
public enum Kind {
USBMUX,
TUNNEL
}
}
[Flags]
public enum OpenTcpChannelFlags {
ALLOW_USBMUX,
ALLOW_TUNNEL,
ALLOW_ANY_TRANSPORT = ALLOW_USBMUX | ALLOW_TUNNEL,
}
public interface Transport : Object {
public abstract ConnectionType connection_type {
get;
@@ -466,6 +516,14 @@ namespace Frida.Fruity {
get;
}
public abstract int64 opened_at {
get;
}
public abstract Bytes? remote_unlock_host_key {
get;
}
public abstract async void close (Cancellable? cancellable) throws IOError;
public abstract async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError;
}
@@ -963,68 +1021,59 @@ namespace Frida.Fruity {
}
private async void handle_usb_device_arrival (LibUSB.Device raw_device) {
string? udid = null;
char serial[LibUSB.DEVICE_STRING_BYTES_MAX + 1];
var res = raw_device.get_device_string (SERIAL_NUMBER, serial);
if (res >= LibUSB.Error.SUCCESS) {
serial[res] = '\0';
udid = (string) serial;
if (udid.length == 24)
udid = udid[:8] + "-" + udid[8:];
try {
UsbDevice usb_device;
try {
usb_device = new UsbDevice (raw_device, this);
} catch (Error e) {
return;
}
unowned string udid = usb_device.udid;
// iPhones before iOS 17 on Windows are sometimes misdetected with an empty udid -> skip those devices
if (udid.length == 0)
return;
var transport = usb_transports.first_match (t => t.udid == udid);
bool may_need_time_to_settle = state != STARTING && (transport == null || transport.modeswitch_in_progress);
if (may_need_time_to_settle) {
try {
yield sleep (250, io_cancellable);
} catch (IOError e) {
}
}
if (transport != null) {
if (!transport.try_complete_modeswitch (raw_device))
transport = null;
}
if (io_cancellable.is_cancelled ())
return;
if (transport == null) {
transport = new PortableCoreDeviceUsbTransport (this, raw_device, udid, pairing_store);
transport = new PortableCoreDeviceUsbTransport (this, usb_device, pairing_store);
usb_transports.add (transport);
if (state != STARTING) {
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 (handle_usb_device_arrival.callback);
timeout_source.attach (main_context);
var cancel_source = new CancellableSource (io_cancellable);
cancel_source.set_callback (handle_usb_device_arrival.callback);
cancel_source.attach (main_context);
yield;
cancel_source.destroy ();
timeout_source.destroy ();
if (io_cancellable.is_cancelled ())
break;
}
try {
yield transport.open (io_cancellable);
break;
} catch (GLib.Error e) {
// We might still be waiting for a udev rule to run...
if (!(e is Error.PERMISSION_DENIED))
break;
}
try {
yield transport.open (io_cancellable);
} catch (GLib.Error e) {
}
}
transport_attached (transport);
}
} finally {
if (AtomicUint.dec_and_test (ref pending_usb_device_arrivals) && state == STARTING)
usb_started.resolve (true);
}
if (AtomicUint.dec_and_test (ref pending_usb_device_arrivals) && state == STARTING)
usb_started.resolve (true);
}
private async void handle_usb_device_departure (LibUSB.Device raw_device) {
var transport = usb_transports.first_match (t => t.raw_device == raw_device && !t.modeswitch_in_progress);
var transport = usb_transports.first_match (t => t.usb_device.raw_device == raw_device && !t.modeswitch_in_progress);
if (transport == null)
return;
@@ -1093,7 +1142,8 @@ namespace Frida.Fruity {
lock (state)
pending_usb_ops.remove (op);
usb_context.interrupt_event_handler ();
if (usb_context != null)
usb_context.interrupt_event_handler ();
}
private static uint32 make_usb_device_id (LibUSB.Device device, LibUSB.DeviceDescriptor desc) {
@@ -1155,9 +1205,9 @@ namespace Frida.Fruity {
}
private sealed class PortableCoreDeviceUsbTransport : Object, Transport {
public LibUSB.Device raw_device {
public UsbDevice usb_device {
get {
return _raw_device;
return _usb_device;
}
}
@@ -1169,7 +1219,7 @@ namespace Frida.Fruity {
public string udid {
get {
return _udid;
return _usb_device.udid;
}
}
@@ -1202,9 +1252,10 @@ namespace Frida.Fruity {
}
}
private weak PortableCoreDeviceBackend parent;
private LibUSB.Device _raw_device;
private string _udid;
private unowned PortableCoreDeviceBackend parent;
private UsbDevice _usb_device;
private Gee.List<InetSocketAddress> ncm_ifaddrs;
private UsbNcmConfig? ncm_config;
private string? _name;
private Promise<UsbDevice>? device_request;
@@ -1212,16 +1263,14 @@ namespace Frida.Fruity {
private Promise<Tunnel?>? tunnel_request;
private NcmPeer? ncm_peer;
public PortableCoreDeviceUsbTransport (PortableCoreDeviceBackend parent, LibUSB.Device raw_device, string udid,
PairingStore store) {
public PortableCoreDeviceUsbTransport (PortableCoreDeviceBackend parent, UsbDevice device, PairingStore store) {
Object (pairing_store: store);
this.parent = parent;
_raw_device = raw_device;
_udid = udid;
_usb_device = device;
char product[LibUSB.DEVICE_STRING_BYTES_MAX + 1];
var res = raw_device.get_device_string (PRODUCT, product);
var res = device.raw_device.get_device_string (PRODUCT, product);
if (res >= LibUSB.Error.SUCCESS) {
product[res] = '\0';
_name = (string) product;
@@ -1241,36 +1290,51 @@ namespace Frida.Fruity {
device_request = new Promise<UsbDevice> ();
try {
var device = yield UsbDevice.open (_raw_device, parent, cancellable);
ncm_ifaddrs = yield NcmPeer.detect_ncm_ifaddrs_on_system (_usb_device, cancellable);
if (ncm_ifaddrs.is_empty) {
_usb_device.ensure_open ();
if (parent.modeswitch_allowed) {
modeswitch_request = new Promise<LibUSB.Device> ();
if (yield device.maybe_modeswitch (cancellable)) {
var source = new TimeoutSource.seconds (2);
source.set_callback (() => {
if (modeswitch_request != null) {
modeswitch_request.reject (new Error.TRANSPORT ("Modeswitch timed out"));
modeswitch_request = null;
if (parent.modeswitch_allowed) {
modeswitch_request = new Promise<LibUSB.Device> ();
if (yield _usb_device.maybe_modeswitch (cancellable)) {
var source = new TimeoutSource.seconds (2);
source.set_callback (() => {
if (modeswitch_request != null) {
modeswitch_request.reject (new Error.TRANSPORT ("Modeswitch timed out"));
modeswitch_request = null;
}
return Source.REMOVE;
});
source.attach (MainContext.get_thread_default ());
LibUSB.Device raw_device = null;
try {
raw_device = yield modeswitch_request.future.wait_async (cancellable);
} finally {
source.destroy ();
}
return Source.REMOVE;
});
source.attach (MainContext.get_thread_default ());
try {
_raw_device = yield modeswitch_request.future.wait_async (cancellable);
} finally {
source.destroy ();
_usb_device = new UsbDevice (raw_device, parent);
_usb_device.ensure_open ();
} else {
modeswitch_request = null;
}
device = yield UsbDevice.open (_raw_device, parent, cancellable);
} else {
modeswitch_request = null;
}
bool device_configuration_changed;
try {
ncm_config = UsbNcmConfig.prepare (_usb_device, out device_configuration_changed);
if (device_configuration_changed)
yield sleep (250, cancellable);
} catch (Error e) {
}
ncm_ifaddrs = yield NcmPeer.detect_ncm_ifaddrs_on_system (_usb_device, cancellable);
}
device_request.resolve (device);
device_request.resolve (_usb_device);
return device;
return _usb_device;
} catch (GLib.Error e) {
device_request.reject (e);
device_request = null;
@@ -1345,18 +1409,26 @@ namespace Frida.Fruity {
PortableUsbTunnel? tunnel = null;
if (supported_by_os) {
if (ncm_peer == null)
ncm_peer = yield NcmPeer.locate (usb_device, cancellable);
if (ncm_peer == null) {
if (!ncm_ifaddrs.is_empty) {
ncm_peer = yield NcmPeer.locate_on_system_netifs (ncm_ifaddrs, cancellable);
} else if (ncm_config != null) {
ncm_peer = yield NcmPeer.establish_using_our_driver (usb_device, ncm_config,
cancellable);
}
}
tunnel = new PortableUsbTunnel (usb_device, ncm_peer, pairing_store);
tunnel.lost.connect (on_tunnel_lost);
try {
yield tunnel.open (cancellable);
} catch (Error e) {
if (e is Error.NOT_SUPPORTED)
tunnel = null;
else
throw e;
if (ncm_peer != null) {
tunnel = new PortableUsbTunnel (usb_device, ncm_peer, pairing_store);
tunnel.lost.connect (on_tunnel_lost);
try {
yield tunnel.open (cancellable);
} catch (Error e) {
if (e is Error.NOT_SUPPORTED)
tunnel = null;
else
throw e;
}
}
}
@@ -1386,38 +1458,61 @@ namespace Frida.Fruity {
ncm.close ();
}
public static async NcmPeer locate (UsbDevice usb_device, Cancellable? cancellable) throws Error, IOError {
var device_ifaddrs = detect_ncm_ifaddrs_on_system (usb_device);
if (!device_ifaddrs.is_empty)
return yield locate_on_system_netifs (device_ifaddrs, cancellable);
return yield establish_using_our_driver (usb_device, cancellable);
}
private static Gee.List<InetSocketAddress> detect_ncm_ifaddrs_on_system (UsbDevice usb_device) throws Error {
public static async Gee.List<InetSocketAddress> detect_ncm_ifaddrs_on_system (UsbDevice usb_device,
Cancellable? cancellable) throws Error, IOError {
var device_ifaddrs = new Gee.ArrayList<InetSocketAddress> ();
#if LINUX
var fruit_finder = FruitFinder.make_default ();
unowned string udid = usb_device.udid;
string raw_udid = udid.replace ("-", "");
var ncm_interfaces = new Gee.HashSet<string> ();
var names = if_nameindex ();
try {
for (Linux.Network.IfNameindex * cur = names; cur->if_index != 0; cur++) {
string? candidate_udid = fruit_finder.udid_from_iface (cur->if_name);
if (candidate_udid != udid)
continue;
ncm_interfaces.add (cur->if_name);
}
} finally {
if_freenameindex (names);
}
if (ncm_interfaces.is_empty)
return device_ifaddrs;
yield Network.wait_until_interfaces_ready (ncm_interfaces, cancellable);
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_addr.sa_family != Posix.AF_INET6)
unowned Posix.SockAddr? address = candidate.ifa_addr;
if (address == null || address.sa_family != Posix.AF_INET6)
continue;
string? candidate_udid = fruit_finder.udid_from_iface (candidate.ifa_name);
if (candidate_udid != raw_udid)
if (!ncm_interfaces.contains (candidate.ifa_name))
continue;
device_ifaddrs.add ((InetSocketAddress) SocketAddress.from_native ((void *) candidate.ifa_addr, sizeof (Posix.SockAddrIn6)));
device_ifaddrs.add ((InetSocketAddress) SocketAddress.from_native ((void *) address,
sizeof (Posix.SockAddrIn6)));
}
if (device_ifaddrs.is_empty && !ncm_interfaces.is_empty)
throw new Error.NOT_SUPPORTED ("no IPv6 address on NCM network interface");
#endif
return device_ifaddrs;
}
private static async NcmPeer locate_on_system_netifs (Gee.List<InetSocketAddress> ifaddrs, Cancellable? cancellable)
#if LINUX
[CCode (cheader_filename = "net/if.h", cname = "if_nameindex")]
private extern static Linux.Network.IfNameindex* if_nameindex ();
[CCode (cheader_filename = "net/if.h", cname = "if_freenameindex")]
private extern static void if_freenameindex (Linux.Network.IfNameindex* index);
#endif
public static async NcmPeer locate_on_system_netifs (Gee.List<InetSocketAddress> ifaddrs, Cancellable? cancellable)
throws Error, IOError {
var main_context = MainContext.ref_thread_default ();
@@ -1518,9 +1613,9 @@ namespace Frida.Fruity {
}
}
private static async NcmPeer establish_using_our_driver (UsbDevice usb_device, Cancellable? cancellable)
throws Error, IOError {
var ncm = yield UsbNcmDriver.open (usb_device, cancellable);
public static async NcmPeer establish_using_our_driver (UsbDevice usb_device, UsbNcmConfig ncm_config,
Cancellable? cancellable) throws Error, IOError {
var ncm = yield UsbNcmDriver.open (usb_device, ncm_config, cancellable);
if (ncm.remote_ipv6_address == null) {
ulong change_handler = ncm.notify["remote-ipv6-address"].connect ((obj, pspec) => {
@@ -1609,9 +1704,23 @@ namespace Frida.Fruity {
}
}
public int64 opened_at {
get {
return _opened_at;
}
}
public Bytes? remote_unlock_host_key {
get {
return _remote_unlock_host_key;
}
}
private UsbNcmDriver? ncm;
private TunnelConnection? tunnel_connection;
private DiscoveryService? _discovery_service;
private int64 _opened_at = -1;
private Bytes? _remote_unlock_host_key;
public PortableUsbTunnel (UsbDevice device, NcmPeer peer, PairingStore store) {
Object (
@@ -1640,10 +1749,12 @@ namespace Frida.Fruity {
);
var pairing_transport = new XpcPairingTransport (yield netstack.open_tcp_connection (tunnel_endpoint, cancellable));
var pairing_service = yield PairingService.open (pairing_transport, pairing_store, cancellable);
TunnelConnection tc = yield pairing_service.open_tunnel (ncm_peer.ip, netstack, cancellable);
tc.closed.connect (on_tunnel_connection_close);
_opened_at = get_monotonic_time ();
_remote_unlock_host_key = pairing_service.established_peer.remote_unlock_host_key;
var rsd_endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
address: tc.remote_address,
port: tc.remote_rsd_port,
@@ -1805,8 +1916,22 @@ namespace Frida.Fruity {
}
}
public int64 opened_at {
get {
return _opened_at;
}
}
public Bytes? remote_unlock_host_key {
get {
return _remote_unlock_host_key;
}
}
private TunnelConnection? tunnel_connection;
private DiscoveryService? _discovery_service;
private int64 _opened_at = -1;
private Bytes? _remote_unlock_host_key;
private const uint PAIRING_CONNECTION_TIMEOUT = 2000;
@@ -1828,6 +1953,9 @@ namespace Frida.Fruity {
TunnelConnection tc = yield pairing_service.open_tunnel (endpoint.get_address (), netstack, cancellable);
_opened_at = get_monotonic_time ();
_remote_unlock_host_key = pairing_service.established_peer.remote_unlock_host_key;
var rsd_endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
address: tc.remote_address,
port: tc.remote_rsd_port,
@@ -1908,4 +2036,23 @@ namespace Frida.Fruity {
public InetSocketAddress endpoint;
public InetSocketAddress interface_address;
}
private async void sleep (uint duration_msec, Cancellable? cancellable) throws IOError {
var main_context = MainContext.get_thread_default ();
var delay_source = new TimeoutSource (duration_msec);
delay_source.set_callback (sleep.callback);
delay_source.attach (main_context);
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (sleep.callback);
cancel_source.attach (main_context);
yield;
cancel_source.destroy ();
delay_source.destroy ();
cancellable.set_error_if_cancelled ();
}
}
+80 -16
View File
@@ -971,7 +971,7 @@ namespace Frida {
var transport_broker = server.transport_broker;
if (transport_broker != null) {
try {
entry.connection = yield establish_direct_connection (transport_broker, remote_session_id, device,
entry.connection = yield establish_direct_connection (transport_broker, remote_session_id, server,
cancellable);
} catch (Error e) {
if (e is Error.NOT_SUPPORTED)
@@ -1059,9 +1059,9 @@ namespace Frida {
try {
return yield get_remote_server (cancellable);
} catch (Error e) {
if (e is Error.PERMISSION_DENIED || e is Error.TIMED_OUT)
throw e;
return null;
if (e is Error.SERVER_NOT_RUNNING)
return null;
throw e;
}
}
@@ -1087,10 +1087,9 @@ namespace Frida {
DBusConnection? connection = null;
try {
var stream = yield device.open_channel (
("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (DEFAULT_CONTROL_PORT),
cancellable);
var channel = yield connect_to_remote_server (cancellable);
IOStream stream = channel.stream;
WebServiceTransport transport = PLAIN;
string? origin = null;
@@ -1118,7 +1117,7 @@ namespace Frida {
if (connection.closed)
throw new Error.SERVER_NOT_RUNNING ("Unable to connect to remote frida-server");
var server = new RemoteServer (session, connection, flavor, transport_broker);
var server = new RemoteServer (flavor, session, connection, channel, device, transport_broker);
attach_remote_server (server);
current_remote_server = server;
last_server_check_timer = null;
@@ -1136,10 +1135,8 @@ namespace Frida {
last_server_check_timer = null;
last_server_check_error = null;
} else {
if (e is Error.PERMISSION_DENIED || e is Error.TIMED_OUT) {
if (e is Error) {
api_error = e;
} else if (e is Error.SERVER_NOT_RUNNING) {
api_error = new Error.SERVER_NOT_RUNNING ("Unable to connect to remote frida-server");
} else if (connection != null) {
api_error = new Error.PROTOCOL ("Incompatible frida-server version");
} else {
@@ -1157,6 +1154,51 @@ namespace Frida {
}
}
private async Fruity.TcpChannel connect_to_remote_server (Cancellable? cancellable) throws Error, IOError {
var tunnel = yield device.find_tunnel (cancellable);
bool tunnel_recently_opened = tunnel != null && get_monotonic_time () - tunnel.opened_at < 1000000;
uint delays[] = { 0, 50, 250 };
uint max_attempts = tunnel_recently_opened ? delays.length : 1;
var main_context = MainContext.ref_thread_default ();
Error? pending_error = null;
for (uint attempts = 0; attempts != max_attempts; attempts++) {
uint delay = delays[attempts];
if (delay != 0) {
var timeout_source = new TimeoutSource (delay);
timeout_source.set_callback (connect_to_remote_server.callback);
timeout_source.attach (main_context);
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (connect_to_remote_server.callback);
cancel_source.attach (main_context);
yield;
cancel_source.destroy ();
timeout_source.destroy ();
if (cancellable.is_cancelled ())
break;
}
bool is_last_attempt = attempts == max_attempts - 1;
var open_flags = is_last_attempt
? Fruity.OpenTcpChannelFlags.ALLOW_ANY_TRANSPORT
: Fruity.OpenTcpChannelFlags.ALLOW_TUNNEL;
try {
return yield device.open_tcp_channel (DEFAULT_CONTROL_PORT.to_string (), open_flags, cancellable);
} catch (Error e) {
pending_error = e;
if (!(e is Error.SERVER_NOT_RUNNING))
break;
}
}
throw pending_error;
}
private void attach_remote_server (RemoteServer server) {
server.connection.on_closed.connect (on_remote_connection_closed);
@@ -1452,7 +1494,12 @@ namespace Frida {
}
}
private class RemoteServer : Object {
private class RemoteServer : Object, HostChannelProvider {
public Flavor flavor {
get;
construct;
}
public HostSession session {
get;
construct;
@@ -1463,7 +1510,12 @@ namespace Frida {
construct;
}
public Flavor flavor {
public Fruity.TcpChannel channel {
get;
construct;
}
public Fruity.Device device {
get;
construct;
}
@@ -1478,15 +1530,27 @@ namespace Frida {
set;
}
public RemoteServer (HostSession session, DBusConnection connection, Flavor flavor,
TransportBroker? transport_broker) {
public RemoteServer (Flavor flavor, HostSession session, DBusConnection connection, Fruity.TcpChannel channel,
Fruity.Device device, TransportBroker? transport_broker) {
Object (
flavor: flavor,
session: session,
connection: connection,
flavor: flavor,
channel: channel,
device: device,
transport_broker: transport_broker
);
}
public async IOStream open_channel (string address, Cancellable? cancellable) throws Error, IOError {
if (!address.has_prefix ("tcp:"))
throw new Error.NOT_SUPPORTED ("Unsupported channel address");
var flags = (channel.kind == TUNNEL)
? Fruity.OpenTcpChannelFlags.ALLOW_TUNNEL
: Fruity.OpenTcpChannelFlags.ALLOW_USBMUX;
var channel = yield device.open_tcp_channel (address[4:], flags, cancellable);
return channel.stream;
}
}
}
+378 -157
View File
@@ -6,6 +6,11 @@ namespace Frida.Fruity {
construct;
}
public UsbNcmConfig config {
get;
construct;
}
public VirtualNetworkStack netstack {
get {
return _netstack;
@@ -18,33 +23,32 @@ namespace Frida.Fruity {
}
}
private uint8 data_iface;
private int data_altsetting;
private uint8 rx_address;
private uint8 tx_address;
private uint32 ntb_in_max_size;
private uint32 ntb_out_max_size;
private uint16 ndp_out_divisor;
private uint16 ndp_out_payload_remainder;
private uint16 ndp_out_alignment;
private uint16 ntb_out_max_datagrams;
private size_t max_in_transfers;
private size_t max_out_transfers;
private VirtualNetworkStack? _netstack;
private Gee.Queue<Bytes> pending_output = new Gee.ArrayQueue<Bytes> ();
private bool writing = false;
private uint16 next_outgoing_sequence = 1;
private InetAddress? _remote_ipv6_address;
private Cancellable io_cancellable = new Cancellable ();
private enum UsbDescriptorType {
INTERFACE = 0x04,
private const uint16 TRANSFER_HEADER_SIZE = 4 + 2 + 2 + 2 + 2;
private const size_t MAX_TRANSFER_MEMORY = 60 * 1518;
private enum CdcRequest {
GET_NTB_PARAMETERS = 0x80,
SET_NTB_INPUT_SIZE = 0x86,
}
private enum UsbCommSubclass {
NCM = 0x0d,
}
private enum UsbDataSubclass {
UNDEFINED = 0x00,
}
private enum UsbCdcDescriptorSubtype {
ETHERNET = 0x0f,
}
private const uint16 NTB_PARAMETERS_MIN_SIZE = 28;
private enum EtherType {
IPV6 = 0x86dd,
@@ -54,8 +58,9 @@ namespace Frida.Fruity {
UDP = 0x11,
}
public static async UsbNcmDriver open (UsbDevice device, Cancellable? cancellable = null) throws Error, IOError {
var driver = new UsbNcmDriver (device);
public static async UsbNcmDriver open (UsbDevice device, UsbNcmConfig config, Cancellable? cancellable = null)
throws Error, IOError {
var driver = new UsbNcmDriver (device, config);
try {
yield driver.init_async (Priority.DEFAULT, cancellable);
@@ -66,69 +71,15 @@ namespace Frida.Fruity {
return driver;
}
private UsbNcmDriver (UsbDevice device) {
Object (device: device);
private UsbNcmDriver (UsbDevice device, UsbNcmConfig config) {
Object (device: device, config: config);
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
unowned LibUSB.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"));
var language_id = yield device.query_default_language_id (cancellable);
uint8 mac_address[6];
string mac_address_str = yield device.read_string_descriptor (mac_address_index, device.default_language_id,
cancellable);
string mac_address_str = yield device.read_string_descriptor (config.mac_address_index, language_id, cancellable);
if (mac_address_str.length != 12)
throw new Error.PROTOCOL ("Invalid MAC address");
for (uint i = 0; i != 6; i++) {
@@ -137,22 +88,62 @@ namespace Frida.Fruity {
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");
}
unowned LibUSB.DeviceHandle handle = device.handle;
try {
Usb.check (handle.claim_interface (data_iface), "Failed to claim USB interface");
Usb.check (handle.claim_interface (config.ctrl_iface), "Failed to claim control interface");
Usb.check (handle.claim_interface (config.data_iface), "Failed to claim data interface");
} catch (Error e) {
throw new Error.PERMISSION_DENIED ("%s",
make_user_error_message (@"Unable to claim USB CDC-NCM interface ($(e.message))"));
}
Usb.check (handle.set_interface_alt_setting (data_iface, data_altsetting),
uint8 raw_ntb_params[NTB_PARAMETERS_MIN_SIZE];
var raw_ntb_params_size = yield device.control_transfer (
LibUSB.RequestRecipient.INTERFACE | LibUSB.RequestType.CLASS | LibUSB.EndpointDirection.IN,
CdcRequest.GET_NTB_PARAMETERS,
0,
config.ctrl_iface,
raw_ntb_params,
1000,
cancellable);
if (raw_ntb_params_size < NTB_PARAMETERS_MIN_SIZE)
throw new Error.PROTOCOL ("Truncated NTB parameters response");
var ntb_params = new Buffer (new Bytes (raw_ntb_params[:raw_ntb_params_size]), LITTLE_ENDIAN);
uint32 device_ntb_in_max_size = ntb_params.read_uint32 (4);
ntb_in_max_size = uint32.min (device_ntb_in_max_size, 16384);
ntb_out_max_size = uint32.min (ntb_params.read_uint32 (16), 16384);
ndp_out_divisor = ntb_params.read_uint16 (20);
ndp_out_payload_remainder = ntb_params.read_uint16 (22);
ndp_out_alignment = ntb_params.read_uint16 (24);
ntb_out_max_datagrams = uint16.min (ntb_params.read_uint16 (26), 16);
if (ntb_in_max_size != device_ntb_in_max_size) {
var ntb_size_buf = new BufferBuilder (LITTLE_ENDIAN)
.append_uint32 (ntb_in_max_size)
.build ();
yield device.control_transfer (
LibUSB.RequestRecipient.INTERFACE | LibUSB.RequestType.CLASS | LibUSB.EndpointDirection.OUT,
CdcRequest.SET_NTB_INPUT_SIZE,
0,
config.ctrl_iface,
ntb_size_buf.get_data (),
1000,
cancellable);
}
var speed = device.raw_device.get_device_speed ();
if (speed >= LibUSB.Speed.SUPER) {
max_in_transfers = (5 * MAX_TRANSFER_MEMORY) / ntb_in_max_size;
max_out_transfers = (5 * MAX_TRANSFER_MEMORY) / ntb_out_max_size;
} else if (speed == LibUSB.Speed.HIGH) {
max_in_transfers = MAX_TRANSFER_MEMORY / ntb_in_max_size;
max_out_transfers = MAX_TRANSFER_MEMORY / ntb_out_max_size;
} else {
max_in_transfers = 4;
max_out_transfers = 4;
}
Usb.check (handle.set_interface_alt_setting (config.data_iface, config.data_altsetting),
"Failed to set USB interface alt setting");
_netstack = new VirtualNetworkStack (new Bytes (mac_address), null, 1500);
@@ -163,35 +154,46 @@ namespace Frida.Fruity {
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];
var pending = new Gee.ArrayQueue<Promise<Bytes>> ();
while (true) {
for (uint i = pending.size; i != max_in_transfers; i++) {
var request = transfer_next_input_batch ();
pending.offer (request);
}
try {
size_t n = yield device.bulk_transfer (rx_address, data, uint.MAX, io_cancellable);
handle_ncm_frame (data[:n]);
var frame = yield pending.poll ().future.wait_async (io_cancellable);
handle_ncm_frame (frame);
} catch (GLib.Error e) {
return;
}
}
}
private void handle_ncm_frame (uint8[] data) throws GLib.Error {
var input = new DataInputStream (new MemoryInputStream.from_data (data));
private Promise<Bytes> transfer_next_input_batch () {
var request = new Promise<Bytes> ();
do_transfer_input_batch.begin (request);
return request;
}
private async void do_transfer_input_batch (Promise<Bytes> request) {
var data = new uint8[ntb_in_max_size];
try {
size_t n = yield device.bulk_transfer (config.rx_address, data, uint.MAX, io_cancellable);
request.resolve (new Bytes (data[:n]));
} catch (GLib.Error e) {
request.reject (e);
}
}
private void handle_ncm_frame (Bytes frame) throws GLib.Error {
var input = new DataInputStream (new MemoryInputStream.from_bytes (frame));
input.byte_order = LITTLE_ENDIAN;
uint8 raw_signature[4 + 1];
@@ -239,47 +241,274 @@ namespace Frida.Fruity {
} while (ndp_index != 0);
}
private async void on_netif_outgoing_datagram (Bytes datagram) {
uint16 transfer_header_length = 12;
uint16 ndp_header_length = 16;
uint16 alignment_padding_length = 2;
private void on_netif_outgoing_datagram (Bytes datagram) {
pending_output.offer (datagram);
uint16 datagram_start_index = transfer_header_length + ndp_header_length + alignment_padding_length;
uint16 datagram_length = (uint16) datagram.length;
uint16 sentinel_start_index = 0;
uint16 sentinel_size = 0;
uint16 sequence = next_outgoing_sequence++;
uint16 block_length = datagram_start_index + datagram_length;
uint16 ndp_index = transfer_header_length;
uint16 next_ndp_index = 0;
uint16 alignment_padding_value = 0;
var frame = new BufferBuilder (LITTLE_ENDIAN)
.append_string ("NCMH", StringTerminator.NONE)
.append_uint16 (transfer_header_length)
.append_uint16 (sequence)
.append_uint16 (block_length)
.append_uint16 (ndp_index)
.append_string ("NCM0", StringTerminator.NONE)
.append_uint16 (ndp_header_length)
.append_uint16 (next_ndp_index)
.append_uint16 (datagram_start_index)
.append_uint16 (datagram_length)
.append_uint16 (sentinel_start_index)
.append_uint16 (sentinel_size)
.append_uint16 (alignment_padding_value)
.append_bytes (datagram)
.build ();
try {
yield device.bulk_transfer (tx_address, frame.get_data (), uint.MAX, io_cancellable);
} catch (GLib.Error e) {
if (!writing) {
writing = true;
var source = new IdleSource ();
source.set_callback (() => {
process_pending_output.begin ();
return false;
});
source.attach (MainContext.get_thread_default ());
}
}
private async void process_pending_output () {
try {
while (!pending_output.is_empty) {
var pending = new Gee.ArrayList<Promise<uint>> ();
do {
var request = transfer_next_output_batch ();
pending.add (request);
} while (pending.size < max_out_transfers && !pending_output.is_empty);
foreach (var request in pending)
yield request.future.wait_async (io_cancellable);
}
} catch (GLib.Error e) {
} finally {
writing = false;
}
}
private Promise<uint> transfer_next_output_batch () {
size_t num_datagrams = ntb_out_max_datagrams;
TransferLayout layout;
while ((layout = TransferLayout.compute (pending_output, num_datagrams, ndp_out_alignment,
ndp_out_divisor, ndp_out_payload_remainder)).size > ntb_out_max_size) {
num_datagrams--;
}
var batch = new Gee.ArrayList<Bytes> ();
for (var i = 0; i != layout.offsets.size; i++)
batch.add (pending_output.poll ());
var transfer = build_output_transfer (batch, layout, next_outgoing_sequence++);
var request = new Promise<uint> ();
do_transfer_output_batch.begin (transfer, request);
return request;
}
private async void do_transfer_output_batch (Bytes transfer, Promise<uint> request) {
try {
var size = yield device.bulk_transfer (config.tx_address, transfer.get_data (), uint.MAX, io_cancellable);
request.resolve ((uint) size);
} catch (GLib.Error e) {
request.reject (e);
}
}
private class TransferLayout {
public uint16 size;
public uint16 ndp_header_offset;
public uint16 ndp_header_size;
public Gee.List<uint16> offsets;
public static TransferLayout compute (Gee.Collection<Bytes> datagrams, size_t max_datagrams, size_t ndp_alignment,
size_t datagram_modulus, size_t datagram_remainder) {
size_t ndp_header_base_size = 4 + 2 + 2;
size_t ndp_entry_size = 2 + 2;
size_t ethernet_header_size = 14;
size_t ndp_header_offset = align (TRANSFER_HEADER_SIZE, ndp_alignment, 0);
size_t num_datagram_slots = size_t.min (datagrams.size, max_datagrams);
size_t ndp_header_size = ndp_header_base_size + ((num_datagram_slots + 1) * ndp_entry_size);
size_t current_transfer_size = ndp_header_offset + ndp_header_size;
var offsets = new Gee.ArrayList<uint16> ();
uint i = 0;
foreach (var datagram in datagrams) {
var size = (uint16) datagram.get_size ();
size_t start_offset =
align (current_transfer_size + ethernet_header_size, datagram_modulus, datagram_remainder);
size_t end_offset = start_offset + size;
if (end_offset > uint16.MAX)
break;
current_transfer_size = end_offset;
offsets.add ((uint16) start_offset);
i++;
if (i == max_datagrams)
break;
}
return new TransferLayout () {
size = (uint16) current_transfer_size,
ndp_header_offset = (uint16) ndp_header_offset,
ndp_header_size = (uint16) ndp_header_size,
offsets = offsets,
};
}
private static size_t align (size_t val, size_t modulus, size_t remainder) {
var delta = val % modulus;
if (delta != remainder)
return val + modulus - delta + remainder;
return val;
}
}
private static Bytes build_output_transfer (Gee.List<Bytes> datagrams, TransferLayout layout, uint16 sequence_number) {
var builder = new BufferBuilder (LITTLE_ENDIAN)
.append_string ("NCMH", StringTerminator.NONE)
.append_uint16 (TRANSFER_HEADER_SIZE)
.append_uint16 (sequence_number)
.append_uint16 (layout.size)
.append_uint16 (layout.ndp_header_offset);
uint16 next_ndp_index = 0;
builder
.seek (layout.ndp_header_offset)
.append_string ("NCM0", StringTerminator.NONE)
.append_uint16 (layout.ndp_header_size)
.append_uint16 (next_ndp_index);
int i;
i = 0;
foreach (var datagram in datagrams) {
builder
.append_uint16 (layout.offsets[i])
.append_uint16 ((uint16) datagram.get_size ());
i++;
}
i = 0;
foreach (var datagram in datagrams) {
builder
.seek (layout.offsets[i])
.append_bytes (datagram);
i++;
}
builder.seek (layout.size);
return builder.build ();
}
private static InetAddress? try_infer_remote_address_from_datagram (Bytes datagram) {
if (datagram.get_size () < 0x3e)
return null;
var buf = new Buffer (datagram, BIG_ENDIAN);
var ethertype = (EtherType) buf.read_uint16 (12);
if (ethertype != IPV6)
return null;
var next_header = (IPV6NextHeader) buf.read_uint8 (20);
if (next_header != UDP)
return null;
return new InetAddress.from_bytes (datagram[22:22 + 16].get_data (), IPV6);
}
}
internal class UsbNcmConfig {
public uint8 ctrl_iface;
public uint8 data_iface;
public int data_altsetting;
public uint8 rx_address;
public uint8 tx_address;
public uint8 mac_address_index;
private enum UsbDescriptorType {
INTERFACE = 0x04,
}
private enum UsbCommSubclass {
NCM = 0x0d,
}
private enum UsbDataSubclass {
UNDEFINED = 0x00,
}
private enum UsbCdcDescriptorSubtype {
ETHERNET = 0x0f,
}
public static UsbNcmConfig prepare (UsbDevice device, out bool device_configuration_changed) throws Error {
unowned LibUSB.Device raw_device = device.raw_device;
var dev_desc = LibUSB.DeviceDescriptor (raw_device);
LibUSB.ConfigDescriptor current_config;
Usb.check (raw_device.get_active_config_descriptor (out current_config), "Failed to get active config descriptor");
var config = new UsbNcmConfig ();
int desired_config_value = -1;
bool found_cdc_header = false;
bool found_data_interface = false;
for (uint8 config_value = dev_desc.bNumConfigurations; config_value != 0; config_value--) {
LibUSB.ConfigDescriptor config_desc;
Usb.check (raw_device.get_config_descriptor_by_value (config_value, out config_desc),
"Failed to get config descriptor");
foreach (var iface in config_desc.@interface) {
foreach (var setting in iface.altsetting) {
if (setting.bInterfaceClass == LibUSB.ClassCode.COMM &&
setting.bInterfaceSubClass == UsbCommSubclass.NCM) {
config.ctrl_iface = setting.bInterfaceNumber;
try {
parse_cdc_header (setting.extra, out config.mac_address_index);
found_cdc_header = true;
} catch (Error e) {
break;
}
} else if (setting.bInterfaceClass == LibUSB.ClassCode.DATA &&
setting.bInterfaceSubClass == UsbDataSubclass.UNDEFINED &&
setting.endpoint.length == 2) {
found_data_interface = true;
config.data_iface = setting.bInterfaceNumber;
config.data_altsetting = setting.bAlternateSetting;
foreach (var ep in setting.endpoint) {
if ((ep.bEndpointAddress & LibUSB.EndpointDirection.MASK) ==
LibUSB.EndpointDirection.IN) {
config.rx_address = ep.bEndpointAddress;
} else {
config.tx_address = ep.bEndpointAddress;
}
}
}
}
}
if (found_cdc_header || found_data_interface) {
desired_config_value = config_value;
break;
}
}
if (!found_cdc_header || !found_data_interface)
throw new Error.NOT_SUPPORTED ("%s", make_user_error_message ("No USB CDC-NCM interface found"));
if (current_config.bConfigurationValue != desired_config_value) {
unowned LibUSB.DeviceHandle handle = device.handle;
foreach (var iface in current_config.@interface) {
unowned LibUSB.InterfaceDescriptor setting = iface.altsetting[0];
var res = handle.kernel_driver_active (setting.bInterfaceNumber);
if (res == 1)
handle.detach_kernel_driver (setting.bInterfaceNumber);
}
Usb.check (handle.set_configuration (desired_config_value), "Failed to set configuration");
device_configuration_changed = true;
} else {
device_configuration_changed = false;
}
return config;
}
private static void parse_cdc_header (uint8[] header, out uint8 mac_address_index) throws Error {
var input = new DataInputStream (new MemoryInputStream.from_data (header));
input.set_byte_order (LITTLE_ENDIAN);
@@ -309,22 +538,14 @@ namespace Frida.Fruity {
throw new Error.PROTOCOL ("CDC Ethernet descriptor not found");
}
}
private static InetAddress? try_infer_remote_address_from_datagram (Bytes datagram) {
if (datagram.get_size () < 0x3e)
return null;
var buf = new Buffer (datagram, BIG_ENDIAN);
var ethertype = (EtherType) buf.read_uint16 (12);
if (ethertype != IPV6)
return null;
var next_header = (IPV6NextHeader) buf.read_uint8 (20);
if (next_header != UDP)
return null;
return new InetAddress.from_bytes (datagram[22:22 + 16].get_data (), IPV6);
}
private string make_user_error_message (string message) {
#if WINDOWS
return message + "; use https://zadig.akeo.ie to switch from Apple's official driver onto Microsoft's WinUSB " +
"driver, so libusb can access it";
#else
return message;
#endif
}
}
+1
View File
@@ -518,6 +518,7 @@ namespace Frida.Fruity {
if (self != null)
self->on_error (err);
});
pcb.set_flags (TIMESTAMP | SACK);
pcb.nagle_disable ();
pcb.bind_netif (&netstack.handle);
+57 -61
View File
@@ -1,6 +1,11 @@
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
namespace Frida.Fruity {
internal sealed class UsbDevice : Object, AsyncInitable {
internal sealed class UsbDevice : Object {
public string udid {
get;
construct;
}
public LibUSB.Device? raw_device {
get {
return _raw_device;
@@ -18,22 +23,8 @@ namespace Frida.Fruity {
}
}
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;
@@ -45,38 +36,24 @@ namespace Frida.Fruity {
private const string MODE_INITIAL_UNTETHERED = "3:3:3:0"; // => 5:3:3:0
private const string MODE_INITIAL_TETHERED = "4:4:3:4"; // => 5:4:3:4
public static async UsbDevice open (LibUSB.Device raw_device, UsbDeviceBackend backend, Cancellable? cancellable = null)
throws Error, IOError {
var device = new UsbDevice (raw_device, backend);
public UsbDevice (LibUSB.Device raw_device, UsbDeviceBackend backend) throws Error {
char serial[LibUSB.DEVICE_STRING_BYTES_MAX + 1];
var res = raw_device.get_device_string (SERIAL_NUMBER, serial);
Usb.check (res, "Failed to get serial number");
serial[res] = '\0';
try {
yield device.init_async (Priority.DEFAULT, cancellable);
} catch (GLib.Error e) {
throw_api_error (e);
}
Object (
udid: udid_from_serial_number ((string) serial),
backend: backend
);
return device;
}
private UsbDevice (LibUSB.Device raw_device, UsbDeviceBackend backend) {
Object (backend: backend);
_raw_device = raw_device;
}
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
public void ensure_open (Cancellable? cancellable = null) throws Error {
if (_handle != null)
return;
Usb.check (_raw_device.open (out _handle), "Failed to open USB device");
Bytes language_ids_response = yield read_string_descriptor_bytes (0, 0, cancellable);
if (language_ids_response.get_size () < sizeof (uint16))
throw new Error.PROTOCOL ("Invalid language IDs response");
Buffer language_ids = new Buffer (language_ids_response, LITTLE_ENDIAN);
_default_language_id = language_ids.read_uint16 (0);
var dev_desc = LibUSB.DeviceDescriptor (_raw_device);
string serial_number = yield read_string_descriptor (dev_desc.iSerialNumber, _default_language_id, cancellable);
_udid = udid_from_serial_number (serial_number);
return true;
}
public async void close (Cancellable? cancellable) throws IOError {
@@ -95,36 +72,38 @@ namespace Frida.Fruity {
}
public async bool maybe_modeswitch (Cancellable? cancellable) throws Error, IOError {
var response = yield control_transfer (
uint8 current_mode[4];
var n = yield control_transfer (
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
AppleSpecificRequest.GET_MODE,
0,
0,
4,
current_mode,
1000,
cancellable);
string mode = parse_mode (response);
string mode = parse_mode (current_mode[:n]);
bool is_initial_mode = mode == MODE_INITIAL_UNTETHERED || mode == MODE_INITIAL_TETHERED;
if (!is_initial_mode)
return false;
response = yield control_transfer (
uint8 set_mode_result[1];
var set_mode_result_size = yield control_transfer (
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
AppleSpecificRequest.SET_MODE,
0,
3,
1,
set_mode_result,
1000,
cancellable);
if (response.get_size () != 1 || response[0] != 0x00)
if (set_mode_result_size != 1 || set_mode_result[0] != 0x00)
return false;
return true;
}
private static string parse_mode (Bytes mode) throws Error {
private static string parse_mode (uint8[] mode) throws Error {
var result = new StringBuilder.sized (7);
foreach (uint8 byte in mode.get_data ()) {
foreach (uint8 byte in mode) {
if (result.len != 0)
result.append_c (':');
result.append_printf ("%u", byte);
@@ -132,12 +111,20 @@ namespace Frida.Fruity {
return result.str;
}
private static string udid_from_serial_number (string serial) {
public static string udid_from_serial_number (string serial) {
if (serial.length == 24)
return serial.substring (0, 8) + "-" + serial.substring (8);
return serial[:8] + "-" + serial[8:];
return serial;
}
public async uint16 query_default_language_id (Cancellable? cancellable) throws Error, IOError {
Bytes language_ids_response = yield read_string_descriptor_bytes (0, 0, cancellable);
if (language_ids_response.get_size () < sizeof (uint16))
throw new Error.PROTOCOL ("Invalid language IDs response");
Buffer language_ids = new Buffer (language_ids_response, LITTLE_ENDIAN);
return language_ids.read_uint16 (0);
}
public async string read_string_descriptor (uint8 index, uint16 language_id, Cancellable? cancellable)
throws Error, IOError {
var response = yield read_string_descriptor_bytes (index, language_id, cancellable);
@@ -162,16 +149,17 @@ namespace Frida.Fruity {
public async Bytes read_string_descriptor_bytes (uint8 index, uint16 language_id, Cancellable? cancellable)
throws Error, IOError {
var response = yield control_transfer (
uint8 response[1024];
var response_size = yield control_transfer (
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.STANDARD | LibUSB.EndpointDirection.IN,
LibUSB.StandardRequest.GET_DESCRIPTOR,
(LibUSB.DescriptorType.STRING << 8) | index,
language_id,
1024,
response,
1000,
cancellable);
try {
var input = new DataInputStream (new MemoryInputStream.from_bytes (response));
var input = new DataInputStream (new MemoryInputStream.from_data (response[:response_size]));
input.byte_order = LITTLE_ENDIAN;
uint8 length = input.read_byte ();
@@ -182,26 +170,29 @@ namespace Frida.Fruity {
if (type != LibUSB.DescriptorType.STRING)
throw new Error.PROTOCOL ("Invalid string descriptor type");
size_t remainder = response.get_size () - 2;
size_t remainder = response_size - 2;
length -= 2;
if (length > remainder)
throw new Error.PROTOCOL ("Invalid string descriptor length");
return response[2:2 + length];
return new Bytes (response[2:2 + length]);
} catch (GLib.Error e) {
throw new Error.PROTOCOL ("%s", e.message);
}
}
public async Bytes control_transfer (uint8 request_type, uint8 request, uint16 val, uint16 index, uint16 length,
public async size_t control_transfer (uint8 request_type, uint8 request, uint16 val, uint16 index, uint8[] buffer,
uint timeout, Cancellable? cancellable) throws Error, IOError {
var op = backend.allocate_usb_operation ();
unowned LibUSB.Transfer transfer = op.transfer;
var ready_closure = new TransferReadyClosure (control_transfer.callback);
var buffer = new uint8[sizeof (LibUSB.ControlSetup) + length];
LibUSB.Transfer.fill_control_setup (buffer, request_type, request, val, index, length);
transfer.fill_control_transfer (_handle, buffer, on_transfer_ready, ready_closure, timeout);
size_t control_setup_size = 8;
var transfer_buffer = new uint8[control_setup_size + buffer.length];
LibUSB.Transfer.fill_control_setup (transfer_buffer, request_type, request, val, index, (uint16) buffer.length);
if ((request_type & LibUSB.EndpointDirection.IN) == 0)
Memory.copy ((uint8 *) transfer_buffer + control_setup_size, buffer, buffer.length);
transfer.fill_control_transfer (_handle, transfer_buffer, on_transfer_ready, ready_closure, timeout);
var cancel_source = new CancellableSource (cancellable);
cancel_source.set_callback (() => {
@@ -221,7 +212,12 @@ namespace Frida.Fruity {
Usb.check_transfer (transfer.status, "Control transfer failed");
return new Bytes (((uint8[]) transfer.control_get_data ())[:transfer.actual_length]);
var n = transfer.actual_length;
if ((request_type & LibUSB.EndpointDirection.IN) != 0)
Memory.copy (buffer, transfer.control_get_data (), n);
return n;
}
public async size_t bulk_transfer (uint8 endpoint, uint8[] buffer, uint timeout, Cancellable? cancellable)
+153 -18
View File
@@ -146,6 +146,11 @@ namespace Frida.Fruity {
private set;
}
public PairingPeer? established_peer {
get;
private set;
}
private Gee.Map<uint64?, Promise<ObjectReader>> requests =
new Gee.HashMap<uint64?, Promise<ObjectReader>> (Numeric.uint64_hash, Numeric.uint64_equal);
private uint64 next_control_sequence_number = 0;
@@ -181,15 +186,35 @@ namespace Frida.Fruity {
yield attempt_pair_verify (cancellable);
Bytes? shared_key = yield verify_manual_pairing (cancellable);
if (shared_key == null) {
PairingPeer? peer = yield verify_manual_pairing (cancellable);
if (peer == null) {
if (!device_options.allows_pair_setup)
throw new Error.NOT_SUPPORTED ("Device not paired and pairing not allowed on current transport");
shared_key = yield setup_manual_pairing (cancellable);
peer = yield setup_manual_pairing (cancellable);
}
client_cipher = new ChaCha20Poly1305 (derive_chacha_key (shared_key, "ClientEncrypt-main"));
server_cipher = new ChaCha20Poly1305 (derive_chacha_key (shared_key, "ServerEncrypt-main"));
if (peer.remote_unlock_host_key == null) {
bool peer_modified = false;
try {
peer.remote_unlock_host_key = yield create_remote_unlock_key (cancellable);
peer_modified = true;
} catch (Error e) {
if (e is Error.INVALID_OPERATION) {
store.forget_peer (peer);
throw e;
}
if (!(e is Error.NOT_SUPPORTED))
throw e;
}
if (peer_modified) {
try {
store.save_peer (peer);
} catch (Error e) {
}
}
}
established_peer = peer;
return true;
}
@@ -377,7 +402,7 @@ namespace Frida.Fruity {
}
}
private async Bytes? verify_manual_pairing (Cancellable? cancellable) throws Error, IOError {
private async PairingPeer? verify_manual_pairing (Cancellable? cancellable) throws Error, IOError {
Key host_keypair = make_keypair (X25519);
uint8[] raw_host_pubkey = get_raw_public_key (host_keypair).get_data ();
@@ -414,10 +439,18 @@ namespace Frida.Fruity {
var cipher = new ChaCha20Poly1305 (operation_key);
// TODO: Verify signature using peer's public key.
/* var start_inner_response = */ new VariantReader (PairingParamsParser.parse (cipher.decrypt (
var start_inner_response = new VariantReader (PairingParamsParser.parse (cipher.decrypt (
new Bytes.static ("\x00\x00\x00\x00PV-Msg02".data[:12]),
start_response.read_member ("encrypted-data").get_data_value ())));
string peer_identifier = start_inner_response
.read_member ("identifier")
.get_uuid_value ();
PairingPeer? peer = store.find_peer_by_identifier (peer_identifier);
if (peer == null) {
yield notify_pair_verify_failed (cancellable);
return null;
}
// TODO: Verify signature using peer's public key.
unowned string host_identifier = store.self_identity.identifier;
@@ -457,7 +490,9 @@ namespace Frida.Fruity {
return null;
}
return shared_key;
setup_main_encryption_keys (shared_key);
return peer;
}
private async void notify_pair_verify_failed (Cancellable? cancellable) throws Error, IOError {
@@ -476,7 +511,7 @@ namespace Frida.Fruity {
.build (), cancellable);
}
private async Bytes setup_manual_pairing (Cancellable? cancellable) throws Error, IOError {
private async PairingPeer setup_manual_pairing (Cancellable? cancellable) throws Error, IOError {
Bytes start_params = new PairingParamsBuilder ()
.add_method (0)
.add_state (1)
@@ -612,9 +647,78 @@ namespace Frida.Fruity {
inner_finish_response.end_member ();
Bytes peer_info = inner_finish_response.read_member ("info").get_data_value ();
inner_finish_response.end_member ();
store.add_peer (peer_identifier, peer_pubkey, peer_info);
return shared_key;
PairingPeer peer = store.add_peer (peer_identifier, peer_pubkey, peer_info);
setup_main_encryption_keys (shared_key);
return peer;
}
private async Bytes? create_remote_unlock_key (Cancellable? cancellable) throws Error, IOError {
string request = Json.to_string (
new Json.Builder ()
.begin_object ()
.set_member_name ("request")
.begin_object ()
.set_member_name ("_0")
.begin_object ()
.set_member_name ("createRemoteUnlockKey")
.begin_object ()
.end_object ()
.end_object ()
.end_object ()
.end_object ()
.get_root (), false);
string response = yield request_encrypted (request, cancellable);
Json.Reader reader;
try {
reader = new Json.Reader (Json.from_string (response));
} catch (GLib.Error e) {
throw new Error.PROTOCOL ("Invalid createRemoteUnlockKey response JSON");
}
reader.read_member ("response");
reader.read_member ("_1");
if (reader.read_member ("errorExtended")) {
reader.read_member ("_0");
reader.read_member ("domain");
unowned string? domain = reader.get_string_value ();
reader.end_member ();
reader.read_member ("code");
int64 code = reader.get_int_value ();
reader.end_member ();
reader.read_member ("userInfo");
reader.read_member ("NSLocalizedDescription");
unowned string? description = reader.get_string_value ();
if (domain == null || description == null)
throw new Error.PROTOCOL ("Invalid createRemoteUnlockKey error response");
if (domain == "com.apple.CoreDevice.ControlChannelConnectionError" && code == 2)
throw new Error.INVALID_OPERATION ("%s", description);
throw new Error.NOT_SUPPORTED ("%s", description);
}
reader.end_member ();
reader.read_member ("createRemoteUnlockKey");
reader.read_member ("hostKey");
unowned string? key = reader.get_string_value ();
if (key == null)
throw new Error.PROTOCOL ("Malformed createRemoteUnlockKey response");
return new Bytes (Base64.decode (key));
}
private void setup_main_encryption_keys (Bytes shared_key) {
client_cipher = new ChaCha20Poly1305 (derive_chacha_key (shared_key, "ClientEncrypt-main"));
server_cipher = new ChaCha20Poly1305 (derive_chacha_key (shared_key, "ServerEncrypt-main"));
}
private async ObjectReader request_pairing_data (Bytes payload, Cancellable? cancellable) throws Error, IOError {
@@ -1383,7 +1487,7 @@ namespace Frida.Fruity {
_peers = load_peers ();
}
public void add_peer (string identifier, Bytes public_key, Bytes info) throws Error {
public PairingPeer add_peer (string identifier, Bytes public_key, Bytes info) throws Error {
var r = new VariantReader (OpackParser.parse (info));
Bytes irk = r.read_member ("altIRK").get_data_value ();
@@ -1406,6 +1510,7 @@ namespace Frida.Fruity {
model = model,
udid = udid,
info = info,
remote_unlock_host_key = null,
};
_peers.add (peer);
@@ -1413,6 +1518,12 @@ namespace Frida.Fruity {
save_peer (peer);
} catch (Error e) {
}
return peer;
}
public PairingPeer? find_peer_by_identifier (string identifier) {
return peers.first_match (p => p.identifier == identifier);
}
public PairingPeer? find_peer_matching_service (PairingServiceDetails service) {
@@ -1480,6 +1591,10 @@ namespace Frida.Fruity {
var r = new VariantReader (OpackParser.parse (info));
unowned string udid = r.read_member ("remotepairing_udid").get_string_value ();
Bytes? remote_unlock_host_key = null;
if (plist.has ("remoteUnlockHostKey"))
remote_unlock_host_key = plist.get_bytes ("remoteUnlockHostKey");
peers.add (new PairingPeer () {
identifier = plist.get_string ("identifier"),
public_key = plist.get_bytes ("publicKey"),
@@ -1488,6 +1603,7 @@ namespace Frida.Fruity {
model = plist.get_string ("model"),
udid = udid,
info = info,
remote_unlock_host_key = remote_unlock_host_key,
});
}
} catch (GLib.Error e) {
@@ -1496,7 +1612,7 @@ namespace Frida.Fruity {
return peers;
}
private static void save_peer (PairingPeer peer) throws Error {
public void save_peer (PairingPeer peer) throws Error {
var plist = new Plist ();
plist.set_string ("identifier", peer.identifier);
plist.set_bytes ("publicKey", peer.public_key);
@@ -1504,13 +1620,29 @@ namespace Frida.Fruity {
plist.set_string ("name", peer.name);
plist.set_string ("model", peer.model);
plist.set_bytes ("info", peer.info);
save_plist (plist, query_peers_location ().get_child (peer.identifier + ".plist"));
if (peer.remote_unlock_host_key != null)
plist.set_bytes ("remoteUnlockHostKey", peer.remote_unlock_host_key);
save_plist (plist, query_peer_location (peer));
}
public void forget_peer (PairingPeer peer) {
try {
query_peer_location (peer).delete ();
} catch (GLib.Error e) {
}
_peers.remove (peer);
}
private static File query_self_identity_location () {
return query_base_location ().get_child ("self-identity.plist");
}
private static File query_peer_location (PairingPeer peer) {
return query_peers_location ().get_child (peer.identifier + ".plist");
}
private static File query_peers_location () {
return query_base_location ().get_child ("peers");
}
@@ -1557,6 +1689,7 @@ namespace Frida.Fruity {
public string model;
public string udid;
public Bytes info;
public Bytes? remote_unlock_host_key;
}
public class PairingServiceMetadata {
@@ -1979,9 +2112,6 @@ namespace Frida.Fruity {
} catch (Error e) {
assert_not_reached ();
}
if (_tunnel_netstack != null)
_tunnel_netstack.stop ();
}
private async void process_incoming_messages () {
@@ -1994,6 +2124,8 @@ namespace Frida.Fruity {
} catch (GLib.Error e) {
}
io_cancellable.cancel ();
var source = new IdleSource ();
source.set_callback (process_incoming_messages.callback);
source.attach (MainContext.get_thread_default ());
@@ -2004,6 +2136,9 @@ namespace Frida.Fruity {
} catch (GLib.Error e) {
}
if (_tunnel_netstack != null)
_tunnel_netstack.stop ();
close_request.resolve (true);
closed ();
+22 -14
View File
@@ -9,26 +9,17 @@ namespace Frida {
public HostSessionService.with_default_backends () {
add_local_backends ();
#if !IOS && !ANDROID && !TVOS
#if HAVE_FRUITY_BACKEND
add_backend (new FruityHostSessionBackend ());
#endif
#if HAVE_DROIDY_BACKEND
add_backend (new DroidyHostSessionBackend ());
#endif
#endif
#if HAVE_SOCKET_BACKEND
add_backend (new SocketHostSessionBackend ());
#endif
#if HAVE_BAREBONE_BACKEND
add_backend (new BareboneHostSessionBackend ());
#endif
add_nonlocal_backends ();
}
public HostSessionService.with_local_backend_only () {
add_local_backends ();
}
public HostSessionService.with_nonlocal_backends_only () {
add_nonlocal_backends ();
}
public HostSessionService.with_socket_backend_only () {
#if HAVE_SOCKET_BACKEND
add_backend (new SocketHostSessionBackend ());
@@ -55,6 +46,23 @@ namespace Frida {
#endif
}
private void add_nonlocal_backends () {
#if !IOS && !ANDROID && !TVOS
#if HAVE_FRUITY_BACKEND
add_backend (new FruityHostSessionBackend ());
#endif
#if HAVE_DROIDY_BACKEND
add_backend (new DroidyHostSessionBackend ());
#endif
#endif
#if HAVE_SOCKET_BACKEND
add_backend (new SocketHostSessionBackend ());
#endif
#if HAVE_BAREBONE_BACKEND
add_backend (new BareboneHostSessionBackend ());
#endif
}
public async void start (Cancellable? cancellable = null) throws IOError {
var remaining = backends.size + 1;
+91 -28
View File
@@ -844,22 +844,22 @@ namespace Frida {
private const uint64 SOCK_CLOEXEC = 0x80000;
private class InjectSession : SeizeSession {
private static ProcMapsEntry local_libc;
private static ProcMapsSoEntry local_libc;
private static uint64 mmap_offset;
private static uint64 munmap_offset;
private static string fallback_ld;
private static string fallback_libc;
private static ProcMapsEntry? local_android_ld;
private static ProcMapsSoEntry? local_android_ld;
static construct {
string libc_name = Gum.Process.query_libc_name ();
var libc = Gum.Process.get_libc_module ();
uint local_pid = Posix.getpid ();
local_libc = ProcMapsEntry.find_by_path (local_pid, libc_name);
local_libc = ProcMapsSoEntry.find_by_path (local_pid, libc.path);
assert (local_libc != null);
mmap_offset = (uint64) (uintptr) Gum.Module.find_export_by_name (libc_name, "mmap") - local_libc.base_address;
munmap_offset = (uint64) (uintptr) Gum.Module.find_export_by_name (libc_name, "munmap") - local_libc.base_address;
mmap_offset = (uint64) (uintptr) libc.find_export_by_name ("mmap") - local_libc.base_address;
munmap_offset = (uint64) (uintptr) libc.find_export_by_name ("munmap") - local_libc.base_address;
try {
var program = new Gum.ElfModule.from_file ("/proc/self/exe");
@@ -877,7 +877,7 @@ namespace Frida {
}
#if ANDROID
local_android_ld = ProcMapsEntry.find_by_path (local_pid, fallback_ld);
local_android_ld = ProcMapsSoEntry.find_by_path (local_pid, fallback_ld);
#endif
}
@@ -1084,8 +1084,18 @@ namespace Frida {
uint64 remote_mmap = 0;
uint64 remote_munmap = 0;
ProcMapsEntry? remote_libc = ProcMapsEntry.find_by_path (pid, local_libc.path);
ProcMapsSoEntry? remote_libc = ProcMapsSoEntry.find_by_path (pid, local_libc.path);
#if ANDROID
bool same_libc = false;
if (remote_libc != null) {
bool same_device = remote_libc.identity.split (":")[0] == local_libc.identity.split (":")[0];
bool same_inode = remote_libc.identity.split (" ")[1] == local_libc.identity.split (" ")[1];
bool same_path = remote_libc.path == local_libc.path;
same_libc = same_device && same_inode && same_path;
}
#else
bool same_libc = remote_libc != null && remote_libc.identity == local_libc.identity;
#endif
if (same_libc) {
remote_mmap = remote_libc.base_address + mmap_offset;
remote_munmap = remote_libc.base_address + munmap_offset;
@@ -1186,7 +1196,7 @@ namespace Frida {
result.context.libc = &result.libc;
if (result.context.rtld_flavor == ANDROID && result.libc.dlopen == null) {
ProcMapsEntry? remote_ld = ProcMapsEntry.find_by_address (pid, (uintptr) result.context.rtld_base);
ProcMapsSoEntry? remote_ld = ProcMapsSoEntry.find_by_address (pid, (uintptr) result.context.rtld_base);
bool same_ld = remote_ld != null && local_android_ld != null && remote_ld.identity == local_android_ld.identity;
if (!same_ld)
throw new Error.NOT_SUPPORTED ("Unable to locate Android dynamic linker; please file a bug");
@@ -1236,7 +1246,7 @@ namespace Frida {
}
}
private static void * rebase_pointer (uintptr local_ptr, ProcMapsEntry local_module, ProcMapsEntry remote_module) {
private static void * rebase_pointer (uintptr local_ptr, ProcMapsSoEntry local_module, ProcMapsSoEntry remote_module) {
var offset = local_ptr - local_module.base_address;
return (void *) (remote_module.base_address + offset);
}
@@ -1988,15 +1998,15 @@ namespace Frida {
#elif ARM
target_address &= ~1;
uint32 arm_breakpoint_val = 0xe7f001f0U;
uint16 thumb_breakpoint_val = 0xde01;
uint32 arm_breakpoint_val = (0xe7f001f0U).to_little_endian ();
uint16 thumb_breakpoint_val = ((uint16) 0xde01).to_little_endian ();
bool is_thumb = (target & 1) != 0;
if (is_thumb)
breakpoint_data = (uint8[]) &thumb_breakpoint_val;
else
breakpoint_data = (uint8[]) &arm_breakpoint_val;
#elif ARM64
uint32 breakpoint_val = 0xd4200000U;
uint32 breakpoint_val = (0xd4200000U).to_little_endian ();
breakpoint_data = (uint8[]) &breakpoint_val;
#elif MIPS
uint32 breakpoint_val = 0x0000000dU;
@@ -2280,6 +2290,9 @@ namespace Frida {
this.session = session;
Gum.Linux.enumerate_ranges ((Posix.pid_t) session.pid, READ | EXECUTE, d => {
unowned Gum.FileMapping? file = d.file;
if (file != null && file.path.has_prefix ("memfd:"))
return true;
if (d.range.size >= code.length) {
code_start = d.range.base_address + d.range.size - round_size_to_page_size (code.length);
code_end = code_start + code.length;
@@ -3306,43 +3319,87 @@ namespace Frida {
}
#endif
private class ProcMapsEntry {
private class ProcMapsSoEntry {
public uint64 base_address;
public string path;
public string identity;
private ProcMapsEntry (uint64 base_address, string path, string identity) {
private ProcMapsSoEntry (uint64 base_address, string path, string identity) {
this.base_address = base_address;
this.path = path;
this.identity = identity;
}
public static ProcMapsEntry? find_by_address (uint pid, uint64 address) {
public static ProcMapsSoEntry? find_by_address (uint pid, uint64 address) {
var iter = MapsIter.for_pid (pid);
while (iter.next ()) {
uint64 start = iter.start_address;
uint64 end = iter.end_address;
if (address >= start && address < end)
return new ProcMapsEntry (start, iter.path, iter.identity);
return new ProcMapsSoEntry (start, iter.path, iter.identity);
}
return null;
}
public static ProcMapsEntry? find_by_path (uint pid, string path) {
public static ProcMapsSoEntry? find_by_path (uint pid, string path) {
var candidates = new Gee.ArrayList<Candidate> ();
Candidate? latest_candidate = null;
var iter = MapsIter.for_pid (pid);
while (iter.next ()) {
string candidate_path = iter.path;
if (candidate_path == path) {
#if ANDROID
if (candidate_path == Gum.Process.query_libc_name () && iter.flags[3] == 's')
continue;
unowned string libc_path = Gum.Process.get_libc_module ().path;
#endif
return new ProcMapsEntry (iter.start_address, candidate_path, iter.identity);
while (iter.next ()) {
string current_path = iter.path;
if (current_path == "[page size compat]")
continue;
if (current_path != path) {
latest_candidate = null;
continue;
}
string flags = iter.flags;
#if ANDROID
if (current_path == libc_path && flags[3] == 's')
continue;
#endif
if (iter.file_offset == 0) {
latest_candidate = new Candidate () {
entry = new ProcMapsSoEntry (iter.start_address, current_path, iter.identity),
total_ranges = 0,
executable_ranges = 0,
};
candidates.add (latest_candidate);
}
if (latest_candidate != null) {
latest_candidate.total_ranges++;
if (flags[2] == 'x')
latest_candidate.executable_ranges++;
}
}
return null;
candidates.sort ((a, b) => b.score () - a.score ());
if (candidates.is_empty)
return null;
return candidates.first ().entry;
}
private class Candidate {
public ProcMapsSoEntry entry;
public uint total_ranges;
public uint executable_ranges;
public int score () {
int result = (int) total_ranges;
if (executable_ranges == 0)
result = -result;
return result;
}
}
private class MapsIter {
@@ -3368,15 +3425,21 @@ namespace Frida {
}
}
public uint64 file_offset {
get {
return uint64.parse (info.fetch (4), 16);
}
}
public string identity {
owned get {
return info.fetch (4);
return info.fetch (5);
}
}
public string path {
owned get {
return info.fetch (5);
return info.fetch (6);
}
}
@@ -3391,7 +3454,7 @@ 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 (
if (!/^([0-9a-f]+)-([0-9a-f]+) (\S{4}) ([0-9a-f]+) ([0-9a-f]{2,}:[0-9a-f]{2,} \d+) +([^\n]+)$/m.match (
contents, 0, out info)) {
assert_not_reached ();
}
+6
View File
@@ -204,8 +204,14 @@ namespace Frida {
private async LinuxHelper obtain_for_64bit (Cancellable? cancellable) throws Error, IOError {
if (factory64 == null) {
var store = get_resource_store ();
#if !ARM64
/*
* If we are building for ARM64 and our pointer size is not 8-bytes, then we must be building for
* ILP32, so avoid the spurious error.
*/
if (sizeof (void *) != 8 && store.helper64 == null)
throw new Error.NOT_SUPPORTED ("Unable to handle 64-bit processes due to build configuration");
#endif
factory64 = new HelperFactory (store.helper64, store, main_context);
factory64.lost.connect (on_factory_lost);
factory64.output.connect (on_factory_output);
+1
View File
@@ -1,6 +1,7 @@
#ifndef __FRIDA_SYSCALL_H__
#define __FRIDA_SYSCALL_H__
#include <linux/unistd.h>
#include <sys/syscall.h>
#undef SYS_process_vm_readv
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -9,7 +9,7 @@
#endif
#ifndef __ELF_NATIVE_CLASS
# if defined (__x86_64__) || defined (__aarch64__) || defined (__mips64)
# if defined (__x86_64__) || (defined (__aarch64__) && !defined (__ILP32__)) || defined (__mips64)
# define __ELF_NATIVE_CLASS 64
# else
# define __ELF_NATIVE_CLASS 32
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+24 -16
View File
@@ -303,16 +303,20 @@ frida_receive_chunk (int sockfd, void * buffer, size_t length, const FridaLibcAp
.iov_base = cursor,
.iov_len = remaining
};
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &io,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0,
};
struct msghdr msg;
ssize_t n;
/*
* Avoid inline initialization to prevent the compiler attempting to insert
* a call to memset.
*/
msg.msg_name = NULL,
msg.msg_namelen = 0,
msg.msg_iov = &io,
msg.msg_iovlen = 1,
msg.msg_control = NULL,
msg.msg_controllen = 0,
n = libc->recvmsg (sockfd, &msg, 0);
if (n <= 0)
return false;
@@ -334,14 +338,18 @@ frida_receive_fd (int sockfd, const FridaLibcApi * libc)
.iov_len = sizeof (dummy)
};
FridaControlMessage control;
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &io,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof (control),
};
struct msghdr msg;
/*
* Avoid inline initialization to prevent the compiler attempting to insert
* a call to memset.
*/
msg.msg_name = NULL,
msg.msg_namelen = 0,
msg.msg_iov = &io,
msg.msg_iovlen = 1,
msg.msg_control = &control,
msg.msg_controllen = sizeof (control),
res = libc->recvmsg (sockfd, &msg, 0);
if (res == -1 || res == 0 || msg.msg_controllen == 0)
+23 -3
View File
@@ -197,20 +197,40 @@ diff --git a/tools/include/nolibc/std.h b/tools/include/nolibc/std.h
index 1747ae125..5f30e6e0c 100644
--- a/tools/include/nolibc/std.h
+++ b/tools/include/nolibc/std.h
@@ -25,25 +25,53 @@ typedef unsigned short uint16_t;
@@ -25,25 +25,71 @@ typedef unsigned short uint16_t;
typedef signed short int16_t;
typedef unsigned int uint32_t;
typedef signed int int32_t;
-typedef unsigned long long uint64_t;
-typedef signed long long int64_t;
+#if defined(__x86_64__) || defined(__aarch64__) || defined(__mips64) || defined(__riscv)
+#if defined(__ILP32__)
+typedef unsigned long long uint64_t;
+typedef signed long long int64_t;
+#else
+typedef unsigned long uint64_t;
+typedef signed long int64_t;
+#endif
typedef unsigned long size_t;
typedef signed long ssize_t;
typedef unsigned long uintptr_t;
typedef signed long intptr_t;
-typedef unsigned long uintptr_t;
-typedef signed long intptr_t;
+#if defined(__ILP32__)
+typedef unsigned int uintptr_t;
+typedef signed int intptr_t;
+#else
+typedef unsigned long uintptr_t;
+typedef signed long intptr_t;
+#endif
typedef signed long ptrdiff_t;
+#elif defined(__arm__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+typedef unsigned long long uint64_t;
+typedef signed long long int64_t;
+typedef unsigned int size_t;
+typedef signed int ssize_t;
+typedef unsigned long uintptr_t;
+typedef signed int intptr_t;
+typedef signed int ptrdiff_t;
+#else
+typedef unsigned long long uint64_t;
+typedef signed long long int64_t;
+15 -3
View File
@@ -48,7 +48,10 @@ ARCHS=(
x86
x86_64
arm
armbe8
arm64
arm64be
arm64beilp32
mips
mipsel
mips64
@@ -66,11 +69,14 @@ build_arch () {
exit 1
fi
if [[ "$ARCH" == arm* ]]; then
case "$ARCH" in
arm | arm64)
export FRIDA_HOST=android-$ARCH
else
;;
*)
export FRIDA_HOST=linux-$ARCH
fi
;;
esac
EXTRA_FLAGS=()
if [ "$FRIDA_HOST" == "linux-x86" ]; then
@@ -78,6 +84,12 @@ build_arch () {
export CC="gcc -m32" CXX="g++ -m32" STRIP="strip"
fi
case "$ARCH" in
armbe8 | arm64be | arm64beilp32)
EXTRA_FLAGS+=("--without-prebuilds=sdk:host")
;;
esac
cd "$FRIDA_CORE_DIR"
rm -rf "$BUILD_DIR"
+1 -1
View File
@@ -207,7 +207,7 @@ frida_is_directory_noexec (const gchar * directory)
static gchar *
frida_get_application_directory (void)
{
return g_path_get_dirname (gum_process_get_main_module ()->path);
return g_path_get_dirname (gum_module_get_path (gum_process_get_main_module ()));
}
static gboolean
+5
View File
@@ -319,6 +319,11 @@ if have_local_backend
else
host_lowlevel_abi = host_abi
endif
if host_lowlevel_abi == 'arm' and host_machine.endian() == 'big'
host_lowlevel_abi = 'armbe8'
elif host_lowlevel_abi == 'arm64' and host_machine.endian() == 'big'
host_lowlevel_abi = 'arm64be'
endif
fs = import('fs')
helper_backend_data = custom_target('frida-data-helper-backend',
input: [
+4 -1
View File
@@ -306,6 +306,7 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
{
gpointer code;
guint code_size;
GumModule * kernel32;
SIZE_T page_size, alloc_size;
DWORD old_protect;
@@ -316,7 +317,9 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
memset (rwc, 0, sizeof (FridaRemoteWorkerContext));
gum_module_enumerate_exports ("kernel32.dll", frida_remote_worker_context_collect_kernel32_export, rwc);
kernel32 = gum_process_find_module_by_name ("kernel32.dll");
gum_module_enumerate_exports (kernel32, frida_remote_worker_context_collect_kernel32_export, rwc);
g_object_unref (kernel32);
if (!frida_remote_worker_context_has_resolved_all_kernel32_functions (rwc))
goto failed_to_resolve_kernel32_functions;
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/frida-gum.git
revision = 16.5.7
revision = 16.7.2
depth = 1
[provide]
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/lwip.git
revision = 9f64c40cac2e77eddf6b3913961fdd056d307716
revision = 267d9f082e9e38083b643421a93c03d4a7046fd0
depth = 1
[provide]
+2 -2
View File
@@ -113,7 +113,7 @@ namespace Frida.HostSessionTest {
"execvp",
"execve",
};
if (Gum.Module.find_export_by_name (null, "execvpe") != 0) {
if (Gum.Module.find_global_export_by_name ("execvpe") != 0) {
exec_symbol_names += "execvpe";
}
foreach (var fork_symbol_name in fork_symbol_names) {
@@ -1140,7 +1140,7 @@ namespace Frida.HostSessionTest {
if (strategy == PEER) {
yield session.setup_peer_connection ();
peer_connection = ((DBusProxy) session.session).g_connection;
peer_connection = session._get_connection ();
bool disrupting = false;
var main_context = MainContext.ref_thread_default ();
filter_id = peer_connection.add_filter ((conn, message, incoming) => {
+18
View File
@@ -76,6 +76,20 @@ namespace Darwin.Xpc {
public uint64 get_value ();
}
[Compact]
[CCode (cname = "gpointer")]
public class Data : Object {
[CCode (cname = "XPC_TYPE_DATA")]
public static Type TYPE;
[CCode (cname = "xpc_data_create")]
public Data (uint8[] bytes);
public size_t get_length ();
public void * get_bytes_ptr ();
}
[Compact]
[CCode (cname = "gpointer")]
public class String : Object {
@@ -133,6 +147,10 @@ namespace Darwin.Xpc {
public uint64 get_uint64 (string key);
public void set_uint64 (string key, uint64 val);
[CCode (array_length_type = "size_t")]
public unowned uint8[]? get_data (string key);
public void set_data (string key, uint8[] bytes);
public unowned string? get_string (string key);
public void set_string (string key, string val);
+7
View File
@@ -204,6 +204,13 @@ namespace NGTcp2 {
public struct InternetPort : uint16 {
}
[CCode (cname = "ngtcp2_sockaddr_union", has_destroy_function = false)]
public struct SocketAddressUnion {
public SocketAddress sa;
public SocketAddressInternet in;
public SocketAddressInternet6 in6;
}
[CCode (cname = "ngtcp2_sockaddr", has_destroy_function = false)]
public struct SocketAddress {
public SocketAddressFamily sa_family;
+20
View File
@@ -189,6 +189,8 @@ namespace LWIP {
[CCode (cname = "tcp_err")]
public void set_error_callback (ErrorFunc f);
public void set_flags (Flags flags);
public void nagle_disable ();
public void nagle_enable ();
@@ -221,6 +223,24 @@ namespace LWIP {
[CCode (cname = "tcp_connected_fn", has_target = false)]
public delegate ErrorCode ConnectedFunc (void * user_data, TcpPcb pcb, ErrorCode err);
[Flags]
[CCode (cname = "tcpflags_t", cprefix = "TF_", has_type_id = false)]
public enum Flags {
ACK_DELAY,
ACK_NOW,
INFR,
CLOSEPEND,
RXCLOSED,
FIN,
NODELAY,
NAGLEMEMERR,
WND_SCALE,
BACKLOGPEND,
TIMESTAMP,
RTO,
SACK,
}
[Flags]
[CCode (cname = "u8_t", cprefix = "TCP_WRITE_FLAG_", has_type_id = false)]
public enum WriteFlags {