Compare commits

...
61 Commits
Author SHA1 Message Date
Ole André Vadla Ravnås bcd4a3d586 subprojects: Prepare for release 2025-12-15 21:28:35 +01:00
Ole André Vadla Ravnås 9937b40032 subprojects: Bump outdated 2025-12-15 21:16:49 +01:00
Ole André Vadla Ravnås 81299af1ef darwin: Use DO_NOT_REAP_CHILD when spawning helper
When the system is booted without -arm64e_preview_abi, the helper
binary is killed very early during startup. Enabling
DO_NOT_REAP_CHILD allows GLib to use posix_spawn(), insulating the
host process from signal-related side effects during helper launch.

This means the child process is no longer reaped automatically.
Since this code may run inside a host process that installs its own
signal handlers, GLib's child-watch support cannot be used. Instead,
the helper is reaped explicitly using non-blocking polling.
2025-12-15 17:54:19 +01:00
Ole André Vadla Ravnås f76a3315e7 submodules: Bump releng 2025-12-15 15:06:33 +01:00
Ole André Vadla Ravnås f5c01842c9 meson: Avoid repeating the GLib requirement 2025-12-15 15:05:40 +01:00
Ole André Vadla Ravnås 40879413c0 meson: Remove the unused frida_profile variable 2025-12-15 12:43:25 +01:00
Ole André Vadla Ravnås ab4a7f43f6 meson: Update version logic to support snapshot tags 2025-12-15 12:31:16 +01:00
Ole André Vadla Ravnås 8ed4a69e03 meson: Restore support for GLib being a subproject
Which was broken when gio-unix-2.0 became part of the public API on
non-Windows systems.
2025-12-15 12:09:12 +01:00
Ole André Vadla Ravnås d93557625d ci: Move xcframework .modulemap inside a subdir
To avoid clashes when an app links with multiple static frameworks, each
with their own modulemap.
2025-12-11 15:12:50 +01:00
Ole André Vadla Ravnås 7ecebf23b1 ci: Remove snapshot-tag.yml
Actually triggering ci.yml seems like a rabbit-hole. Leaving for later.
2025-12-08 23:48:06 +01:00
Ole André Vadla Ravnås e2b4b120d7 ci: Add modulemap to xcframework for FridaCore
Add a Clang module.modulemap to each header slice in the generated
FridaCore.xcframework so SwiftPM can load the FridaCore C module.
2025-12-08 23:44:34 +01:00
Ole André Vadla Ravnås e1cb645805 ci: Fix some stylistic inconsistencies 2025-12-08 23:43:35 +01:00
Ole André Vadla Ravnås a0ea09d4d6 ci: Improve snapshot-tag.yml so it triggers ci.yml 2025-12-08 22:46:53 +01:00
Ole André Vadla Ravnås 0f32cc8f4e darwin: Emit 128px app icons on macOS
Instead of two small sizes, we'll emit just one larger one.
2025-12-08 22:29:15 +01:00
Ole André Vadla Ravnås 46a566655f submodules: Bump releng 2025-12-08 22:21:08 +01:00
Ole André Vadla Ravnås 690597f908 ci: Add workflow for automated snapshot tagging 2025-12-08 22:21:08 +01:00
Ole André Vadla Ravnås 0c49c7ee62 ci: Publish releases with .xcframework 2025-12-08 22:21:08 +01:00
Ole André Vadla Ravnås ce40c30a20 ci: Build and upload .xcframework artifact 2025-12-08 22:21:08 +01:00
Ole André Vadla Ravnås 3bb44c222a ci: Add ios-arm64-simulator to the mobile matrix 2025-12-08 22:21:07 +01:00
Ole André Vadla Ravnås 5ec73cab32 ci: Temporarily disable mobile tests
As the backend infrastructure needs some love.
2025-12-08 18:05:39 +01:00
Ole André Vadla Ravnås 257f198659 ci: Fix the emulator job 2025-12-08 18:05:39 +01:00
Ole André Vadla Ravnås 5fca68ebe0 ci: Fix the macOS/x86_64 job
As --host was omitted, we ended up building for macOS/arm64.
2025-12-08 18:05:39 +01:00
Ole André Vadla Ravnås 9eab4f8975 linux: Add missing Gum #include
Which only worked in the past due to luck, i.e. some unrelated code
including it.
2025-11-12 13:18:00 +00:00
Ole André Vadla Ravnås afd8c8789f subprojects: Prepare for release 2025-11-05 18:59:04 +01:00
Ole André Vadla Ravnås cfd5a730e4 subprojects: Bump outdated 2025-11-05 18:57:16 +01:00
Ole André Vadla Ravnås 3f411908e7 subprojects: Prepare for release 2025-11-04 10:30:52 +01:00
Ole André Vadla Ravnås 4e44f4d243 subprojects: Bump outdated 2025-11-04 10:29:26 +01:00
Leo Conforti 9573f1aa22 compiler: Add platform and externals options
Introduce a platform property and externals API to CompilerOptions, and
plumb them through to the Go backend. The backend maps the platform to an
esbuild platform and passes externals to esbuild for exclusion from the
bundle.
2025-11-04 09:52:09 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 79a08e4767 simmy: Wire up spawn() argv and env options
Though `argv` was already wired up for the program case, we didn't wire
it up for the app case.

Co-authored-by: Håvard Sørbø <[email protected]>
2025-11-03 19:30:56 +01:00
Ole André Vadla Ravnås 57dfe9d5ad subprojects: Prepare for release 2025-11-01 00:38:11 +01:00
Ole André Vadla Ravnås 347662b45b subprojects: Bump outdated 2025-11-01 00:36:24 +01:00
Ole André Vadla Ravnås cc95375136 darwin: Revive app listing/launching on iOS >= 16
When running on rootful systems. This brings dccb612 back to life.
It was broken in 8108d4d, which fixed two Interceptor singleton leaks.
Turns out the one in springboard.m was intentional, as it was done as
part of init-once logic, with no teardown intended. And since nothing
else uses Interceptor in this case, the instrumentation we just
applied gets reverted right away.

Kudo to @alexhude for reporting.
2025-11-01 00:23:17 +01:00
Ole André Vadla Ravnås 329c4075d0 subprojects: Prepare for release 2025-10-31 20:24:06 +01:00
Ole André Vadla Ravnås c4198498d5 subprojects: Bump outdated 2025-10-31 20:21:36 +01:00
Ole André Vadla Ravnås 20ef7bb318 simmy: Degrade functionality when SIP is enabled
As get_frontmost_application() and icons rely on being able to inject
our internal agent into SpringBoard.

Down the road we might be able to reimplement this functionality as a
helper program that we spawn inside the Simulator runtime. The uncertain
part is whether we'll be able to give that program the appropriate
entitlements.
2025-10-31 20:09:20 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 367f8cf22b simmy: Handle app enumeration with selected IDs
Co-authored-by: Håvard Sørbø <[email protected]>
2025-10-30 14:58:35 +01:00
Ole André Vadla Ravnås 9f04675b87 subprojects: Prepare for release 2025-10-29 16:47:53 +01:00
Ole André Vadla Ravnås cd8bda0513 subprojects: Bump outdated 2025-10-29 16:45:40 +01:00
Ole André Vadla RavnåsandHåvard Sørbø e09e09fb85 simmy: Wire up application icons
Co-authored-by: Håvard Sørbø <[email protected]>
2025-10-29 15:08:50 +01:00
Ole André Vadla RavnåsandHåvard Sørbø c24a5da82c simmy: Implement get_frontmost_application()
Co-authored-by: Håvard Sørbø <[email protected]>
2025-10-29 15:01:15 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 05ec6563d1 internal-agent: Support non-local HostSession
So we can use it in the Simmy backend, too.

Co-authored-by: Håvard Sørbø <[email protected]>
2025-10-28 14:12:01 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 9dc9d30fa4 simmy: Fix the reported hardware.product
In query_system_parameters().

So it's e.g. `iPhone18,2` instead of `iPhone 17 Pro Max`.

Co-authored-by: Håvard Sørbø <[email protected]>
2025-10-28 13:15:38 +01:00
Ole André Vadla Ravnås 0b57936285 subprojects: Prepare for release 2025-10-24 15:25:15 +02:00
Ole André Vadla Ravnås 41be857a91 subprojects: Bump outdated 2025-10-24 15:23:37 +02:00
Ole André Vadla Ravnås afa847b898 submodules: Bump releng 2025-10-24 15:23:37 +02:00
Ole André Vadla Ravnås 3e43478de0 android: Bump frida-java-bridge in system-server 2025-10-24 15:22:18 +02:00
Ole André Vadla Ravnås a847a012c9 freebsd: Wire up PTY support 2025-10-12 08:11:13 +02:00
Ole André Vadla Ravnås c34fad1ff0 freebsd: Port remaining bits to the new stdio API 2025-10-12 07:50:00 +02:00
Ole André Vadla Ravnås ceef1d696b darwin: Fix a stylistic inconsistency 2025-10-12 07:47:09 +02:00
Ole André Vadla Ravnås d0cfe69dba subprojects: Prepare for release 2025-10-11 19:22:49 +02:00
Ole André Vadla Ravnås a35aff1865 api: Exclude HostSessionHub from .gir
As it's an internal interface.
2025-10-11 11:46:28 +02:00
Ole André Vadla Ravnås be8a59607a android: Bump frida-java-bridge in system-server 2025-10-11 11:07:51 +02:00
Ole André Vadla Ravnås 726740f96a simmy: Move to CoreSimulator.framework 2025-10-10 12:34:41 +02:00
Ole André Vadla Ravnås 9e11d57a76 Add new Simmy backend
For interacting with Apple's simulators on macOS.
2025-10-10 12:34:41 +02:00
Ole André Vadla Ravnås d8c3c9bfb5 darwin: Support early instrumentation of dyld_sim 2025-10-10 12:34:41 +02:00
Ole André Vadla Ravnås 2720968791 base: Add StdioPipes and FileDescriptor helpers
Factored out of the Darwin and Linux backends.
2025-10-10 12:34:41 +02:00
Ole André Vadla Ravnås b8998e52cd host-session: Support cross-backend communication 2025-10-10 12:34:41 +02:00
Ole André Vadla Ravnås c23f046968 value: Add VariantReader.list_members() 2025-10-10 12:34:41 +02:00
Francesco Tamagni 2d521bbeb7 fruity: Unpair on InvalidHostID
So the error doesn't become persistent, and the
device can be paired again.
2025-10-07 11:37:57 +02:00
Ole André Vadla Ravnås cdae790ba9 darwin: Port to the latest Gum.Darwin API 2025-10-01 10:52:33 +02:00
Ole André Vadla Ravnås 90d2d93799 subprojects: Bump outdated 2025-10-01 10:49:57 +02:00
44 changed files with 2732 additions and 487 deletions

No files matched your search

+145 -6
View File
@@ -2,11 +2,15 @@ name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
inputs:
tag:
description: "Tag to build/test"
required: false
type: string
env:
FRIDA_CORE_OPTIONS: '--with-devkits=core --enable-tests'
@@ -71,6 +75,7 @@ jobs:
if: ${{ !startsWith(matrix.id, 'windows-') && matrix.id != 'linux-x86' }}
run: |
./configure \
--host=${{ matrix.id }} \
--enable-compiler-backend \
${{ env.FRIDA_CORE_OPTIONS }}
make
@@ -205,7 +210,7 @@ jobs:
/sbin/entropy
cat /etc/motd
export PS1='\u@frida:\w\$ '
/root/frida-tests
/root/tests/frida-tests
status=\$?
echo "exit status: \$status"
if [ \$status -eq 0 ]; then
@@ -281,7 +286,8 @@ jobs:
COPY initscript /tmp/initramfs/init
RUN chmod +x /tmp/initramfs/init
COPY build/tests /tmp/initramfs/root/
COPY build/tests /tmp/initramfs/root/tests/
COPY build/lib/agent/frida-agent.so /tmp/initramfs/root/lib/agent/
RUN find . | cpio -o --format=newc -R root:root > /root/initramfs.img
COPY launch.sh /root/launch.sh
@@ -297,6 +303,7 @@ jobs:
matrix:
id:
- ios-arm64
- ios-arm64-simulator
- android-x86
- android-x86_64
- android-arm
@@ -341,6 +348,7 @@ jobs:
name: core-devkit-${{ matrix.id }}
path: build/src/devkit/
- name: Package tests
if: false
run: |
mkdir -p /tmp/pkg
cd build
@@ -360,7 +368,7 @@ jobs:
esac
tar -C /tmp/pkg -czf /tmp/runner.tar.gz .
- name: Test on Corellium iOS device
if: matrix.id == 'ios-arm64'
if: false
continue-on-error: true
uses: frida/corellium-action@v4
with:
@@ -376,7 +384,7 @@ jobs:
tar xf $ASSET_PATH
./frida-tests
- name: Test on Corellium Android device
if: matrix.id == 'android-arm64'
if: false
continue-on-error: true
uses: frida/corellium-action@v4
with:
@@ -480,3 +488,134 @@ jobs:
else
echo "No unexpected changes detected."
fi
xcframework:
name: Build FridaCore.xcframework
runs-on: macos-latest
needs:
- native
- mobile
steps:
- name: Check out repo
uses: actions/checkout@v4
- name: Download macOS/x86_64 devkit
uses: actions/download-artifact@v4
with:
name: core-devkit-macos-x86_64
path: devkits/macos-x86_64
- name: Download macOS/arm64 devkit
uses: actions/download-artifact@v4
with:
name: core-devkit-macos-arm64
path: devkits/macos-arm64
- name: Download iOS/arm64 devkit
uses: actions/download-artifact@v4
with:
name: core-devkit-ios-arm64
path: devkits/ios-arm64
- name: Download iOS/arm64-simulator devkit
uses: actions/download-artifact@v4
with:
name: core-devkit-ios-arm64-simulator
path: devkits/ios-arm64-simulator
- name: Create FridaCore.xcframework
shell: bash
run: |
set -euo pipefail
mkdir -p devkits/macos-universal
lipo -create \
devkits/macos-x86_64/libfrida-core.a \
devkits/macos-arm64/libfrida-core.a \
-output devkits/macos-universal/libfrida-core.a
platforms=(
"macos-universal"
"ios-arm64"
"ios-arm64-simulator"
)
for platform in "${platforms[@]}"; do
mkdir -p "xcframework_headers/${platform}/FridaCore"
done
cp devkits/macos-x86_64/frida-core.h \
xcframework_headers/macos-universal/frida-core-x86_64.h
cp devkits/macos-arm64/frida-core.h \
xcframework_headers/macos-universal/frida-core-arm64.h
cat > xcframework_headers/macos-universal/frida-core.h <<'EOF'
#if defined (__x86_64__)
# include "frida-core-x86_64.h"
#elif defined (__arm64__)
# include "frida-core-arm64.h"
#else
# error "Unsupported macOS architecture for FridaCore"
#endif
EOF
cp devkits/ios-arm64/frida-core.h \
xcframework_headers/ios-arm64/frida-core.h
cp devkits/ios-arm64-simulator/frida-core.h \
xcframework_headers/ios-arm64-simulator/frida-core.h
for platform in "${platforms[@]}"; do
cat > "xcframework_headers/${platform}/FridaCore/module.modulemap" <<'EOF'
module FridaCore [extern_c] {
header "../frida-core.h"
export *
}
EOF
done
xcodebuild -create-xcframework \
-library devkits/macos-universal/libfrida-core.a \
-headers xcframework_headers/macos-universal \
-library devkits/ios-arm64/libfrida-core.a \
-headers xcframework_headers/ios-arm64 \
-library devkits/ios-arm64-simulator/libfrida-core.a \
-headers xcframework_headers/ios-arm64-simulator \
-output FridaCore.xcframework
- name: Upload FridaCore.xcframework
uses: actions/upload-artifact@v4
with:
name: FridaCore.xcframework
path: FridaCore.xcframework
publish-xcframework:
if: github.ref_type == 'tag'
runs-on: macos-latest
needs:
- xcframework
steps:
- name: Download FridaCore.xcframework artifact
uses: actions/download-artifact@v4
with:
name: FridaCore.xcframework
path: dist
- name: Rewrap into FridaCore.xcframework directory
shell: bash
run: |
set -euo pipefail
cd dist
mkdir -p FridaCore.xcframework
shopt -s dotglob
for f in *; do
if [[ "$f" != "FridaCore.xcframework" ]]; then
mv "$f" FridaCore.xcframework/
fi
done
- name: Zip FridaCore.xcframework
run: |
cd dist
zip -r FridaCore.xcframework.zip FridaCore.xcframework
- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
files: |
dist/FridaCore.xcframework.zip
prerelease: ${{ contains(github.ref_name, 'snapshot') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-23
View File
@@ -11,29 +11,6 @@ namespace Frida {
return (linux_major == major && linux_minor >= minor) || linux_major > major;
}
public class FileDescriptor : Object, FileDescriptorBased {
public int handle;
public FileDescriptor (int handle) {
this.handle = handle;
}
~FileDescriptor () {
if (handle != -1)
Posix.close (handle);
}
public int steal () {
int result = handle;
handle = -1;
return result;
}
public int get_fd () {
return handle;
}
}
public sealed class PidFileDescriptor : FileDescriptor {
private uint pid;
+17 -2
View File
@@ -19,6 +19,13 @@ base_sources = [
extra_vala_args = []
if host_os == 'windows'
base_sources += 'windows.vala'
else
base_sources += 'posix.vala'
extra_vala_args += '--pkg=posix'
endif
if host_os == 'macos'
base_sources += [
'xpc.vala',
@@ -28,6 +35,7 @@ endif
if host_os_family == 'darwin'
base_sources += 'corefoundation.vala'
extra_vala_args += '--pkg=darwin-xnu'
endif
if host_os_family == 'linux'
@@ -35,13 +43,15 @@ if host_os_family == 'linux'
extra_vala_args += [
'--pkg=linux',
'--pkg=linux-system',
'--pkg=posix',
]
endif
if host_os_family == 'freebsd'
extra_vala_args += '--pkg=freebsd'
endif
extra_deps = []
if host_os_family != 'windows'
extra_vala_args += '--pkg=gio-unix-2.0'
extra_deps += gio_unix_dep
endif
@@ -49,6 +59,11 @@ base_vala_args = [gum_vala_args, '--pkg=gio-2.0', '--pkg=json-glib-1.0']
if nice_dep.found()
base_vala_args += '--pkg=nice'
endif
if host_os_family != 'windows'
base_vala_args += [
'--pkg=gio-unix-2.0',
]
endif
if host_os_family == 'darwin'
base_vala_args += [
'--pkg=corefoundation',
+153
View File
@@ -0,0 +1,153 @@
namespace Frida {
public sealed class StdioPipes : Object {
public OutputStream? input {
get;
construct;
}
public InputStream output {
get;
construct;
}
public InputStream error {
get;
construct;
}
public StdioPipes (FileDescriptor? input, FileDescriptor output, FileDescriptor error) {
Object (
input: (input != null) ? new UnixOutputStream (ensure_nonblocking (input.steal ()), true) : null,
output: new UnixInputStream (ensure_nonblocking (output.steal ()), true),
error: new UnixInputStream (ensure_nonblocking (error.steal ()), true)
);
}
private static int ensure_nonblocking (int fd) {
try {
Unix.set_fd_nonblocking (fd, true);
return fd;
} catch (GLib.Error e) {
assert_not_reached ();
}
}
}
public class FileDescriptor : Object, FileDescriptorBased {
public int handle;
public FileDescriptor (int handle) {
this.handle = handle;
}
~FileDescriptor () {
if (handle != -1)
Posix.close (handle);
}
public int steal () {
int result = handle;
handle = -1;
return result;
}
public int get_fd () {
return handle;
}
}
public StdioPipes? make_stdio_pipes (Stdio stdio, bool in_supported, out FileDescriptor? in_fd, out string? in_name,
out FileDescriptor? out_fd, out string? out_name, out FileDescriptor? err_fd, out string? err_name) throws Error {
if (stdio == PIPE) {
FileDescriptor? in_write = null;
FileDescriptor? out_read, err_read;
if (in_supported) {
make_stdio_pipe (out in_fd, out in_write, out in_name);
} else {
in_fd = null;
in_name = null;
}
make_stdio_pipe (out out_read, out out_fd, out out_name);
make_stdio_pipe (out err_read, out err_fd, out err_name);
return new StdioPipes (in_write, out_read, err_read);
} else {
in_fd = null;
in_name = null;
out_fd = null;
out_name = null;
err_fd = null;
err_name = null;
return null;
}
}
public void make_stdio_pipe (out FileDescriptor read, out FileDescriptor write, out string? name = null) throws Error {
#if HAVE_OPENPTY
int rfd = -1, wfd = -1;
char buf[Posix.Limits.PATH_MAX];
var res =
#if DARWIN
Darwin.XNU.openpty (out rfd, out wfd, buf, null, null)
#elif LINUX
Linux.openpty (out rfd, out wfd, buf, null, null)
#elif FREEBSD
FreeBSD.openpty (out rfd, out wfd, buf, null, null)
#endif
;
if (res == -1)
throw new Error.NOT_SUPPORTED ("Unable to open PTY: %s", strerror (errno));
name = (string) buf;
enable_close_on_exec (rfd);
enable_close_on_exec (wfd);
disable_sigpipe (rfd);
disable_sigpipe (wfd);
configure_terminal_attributes (rfd);
read = new FileDescriptor (rfd);
write = new FileDescriptor (wfd);
#else
name = null;
try {
int fds[2];
Unix.open_pipe (fds, Posix.FD_CLOEXEC);
read = new FileDescriptor (fds[0]);
write = new FileDescriptor (fds[1]);
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("Unable to open pipe: %s", e.message);
}
#endif
}
#if HAVE_OPENPTY
private void enable_close_on_exec (int fd) {
Posix.fcntl (fd, Posix.F_SETFD, Posix.fcntl (fd, Posix.F_GETFD) | Posix.FD_CLOEXEC);
}
private void disable_sigpipe (int fd) {
#if DARWIN
Posix.fcntl (fd, Darwin.XNU.F_SETNOSIGPIPE, true);
#endif
}
private void configure_terminal_attributes (int fd) {
var tios = Posix.termios ();
Posix.tcgetattr (fd, out tios);
tios.c_oflag &= ~Posix.ONLCR;
tios.c_cflag = (tios.c_cflag & Posix.CLOCAL) | Posix.CS8 | Posix.CREAD | Posix.HUPCL;
tios.c_lflag &= ~Posix.ECHO;
Posix.tcsetattr (fd, 0, tios);
}
#endif
}
+16
View File
@@ -57,6 +57,22 @@ namespace Frida {
push_scope (v);
}
public string[] list_members () throws Error {
var scope = peek_scope ();
if (scope.dict == null)
throw new Error.PROTOCOL ("Dictionary expected, but at %s", scope.val.print (true));
var members = new Gee.ArrayList<string> ();
foreach (var item in scope.val) {
string k;
Variant v;
item.get ("{sv}", out k, out v);
members.add (k);
}
return members.to_array ();
}
public bool has_member (string name) throws Error {
var scope = peek_scope ();
if (scope.dict == null)
+22
View File
@@ -0,0 +1,22 @@
namespace Frida {
public sealed class StdioPipes : Object {
public OutputStream input {
get;
construct;
}
public InputStream output {
get;
construct;
}
public InputStream error {
get;
construct;
}
public StdioPipes (OutputStream input, InputStream output, InputStream error) {
Object (input: input, output: output, error: error);
}
}
}
+38 -12
View File
@@ -32,18 +32,34 @@ frida_version = get_option('frida_version')
if frida_version == ''
frida_version = run_command(releng / 'frida_version.py', meson.project_source_root(), check: true).stdout().strip()
endif
tokens = frida_version.split('.')
frida_major_version = tokens[0].to_int()
frida_minor_version = tokens[1].to_int()
if tokens.length() == 4
frida_micro_version = tokens[2].split('-')[0].to_int() - 1
frida_nano_version = tokens[3].to_int() + 1
is_dev = frida_version.contains('-dev.')
if is_dev
dev_parts = frida_version.split('-dev.')
assert(dev_parts.length() == 2)
prefix = dev_parts[0]
frida_nano_version = dev_parts[1].to_int() + 1
else
assert(tokens.length() == 3)
frida_micro_version = tokens[2].to_int()
prefix = frida_version
frida_nano_version = 0
endif
frida_profile = (frida_version != '0.0.0' and frida_nano_version == 0) ? 'prod' : 'dev'
parts = prefix.split('-')
core = parts[0]
has_suffix = parts.length() > 1
core_tokens = core.split('.')
assert(core_tokens.length() == 3)
frida_major_version = core_tokens[0].to_int()
frida_minor_version = core_tokens[1].to_int()
micro_encoded = core_tokens[2].to_int()
if is_dev and not has_suffix
frida_micro_version = micro_encoded - 1
else
frida_micro_version = micro_encoded
endif
api_version = '1.0'
@@ -292,6 +308,7 @@ gum_dep = dependency('frida-gum-1.0', default_options: [
gumjs_dep = dependency('frida-gumjs-1.0')
gumjs_inspector_dep = dependency('frida-gumjs-inspector-1.0')
glib_version_req = '>=2.74'
glib_options = [
'cocoa=disabled',
'selinux=disabled',
@@ -315,7 +332,7 @@ if optimize_for_prod
gee_options += 'disable-internal-asserts=true'
endif
glib_dep = dependency('glib-2.0', version: '>=2.72', default_options: glib_options)
glib_dep = dependency('glib-2.0', version: glib_version_req, default_options: glib_options)
gobject_dep = dependency('gobject-2.0')
gmodule_dep = dependency('gmodule-2.0')
gio_dep = dependency('gio-2.0')
@@ -346,7 +363,7 @@ quickjs_dep = dependency('quickjs', required: get_option('barebone_backend'), de
])
lzfse_dep = dependency('lzfse', required: get_option('barebone_backend'))
native_glib_dep = dependency('glib-2.0', version: '>=2.74', native: true, default_options: glib_options)
native_glib_dep = dependency('glib-2.0', version: glib_version_req, native: true, default_options: glib_options)
native_gio_dep = dependency('gio-2.0', native: true)
native_gee_dep = dependency('gee-0.8', native: true, default_options: gee_options)
native_brotlienc_dep = dependency('libbrotlienc', native: true)
@@ -403,6 +420,10 @@ else
endif
have_local_backend = get_option('local_backend').allowed()
have_simmy_backend = get_option('simmy_backend') \
.disable_auto_if(host_os != 'macos') \
.require(host_os == 'macos', error_message: 'Simmy backend is only available on macOS') \
.allowed()
have_fruity_backend = get_option('fruity_backend') \
.disable_auto_if(not host_docks_mobile_devices) \
.allowed()
@@ -491,6 +512,7 @@ build_tests = get_option('tests') \
.allowed()
foreach b : [['local', have_local_backend],
['simmy', have_simmy_backend],
['fruity', have_fruity_backend],
['droidy', have_droidy_backend],
['socket', have_socket_backend],
@@ -581,7 +603,7 @@ if lzfse_dep.found()
backend_reqs_private += 'lzfse'
endif
if host_os_family == 'linux' and cc.has_function('openpty', prefix: '#include <pty.h>')
if host_os_family in ['darwin', 'freebsd'] or (host_os_family == 'linux' and cc.has_function('openpty', prefix: '#include <pty.h>'))
vala_flags += '--define=HAVE_OPENPTY'
endif
@@ -653,6 +675,9 @@ endif
if host_os in ['ios', 'tvos']
backend_libs_private += ['-Wl,-framework,CoreGraphics', '-Wl,-framework,UIKit']
endif
if host_os_family == 'freebsd'
backend_libs_private += ['-lutil']
endif
if get_option('devkits').length() != 0
if get_option('default_library') != 'static'
@@ -786,6 +811,7 @@ endif
summary(
{
'local': have_local_backend,
'simmy': have_simmy_backend,
'fruity': have_fruity_backend,
'droidy': have_droidy_backend,
'socket': have_socket_backend,
+6
View File
@@ -10,6 +10,12 @@ option('local_backend',
description: 'Include backend for the local system',
)
option('simmy_backend',
type: 'feature',
value: 'auto',
description: 'Include the iDevice Simulator backend',
)
option('fruity_backend',
type: 'feature',
value: 'auto',
+1 -1
Submodule releng updated: c24becede0...c144673b9d.
+3 -1
View File
@@ -238,7 +238,7 @@ def emit_gir(api: ApiSpec, core_gir: str, base_gir: str, output_dir: Path) -> st
for elem in core_elements + base_elements:
if tag_name == "class":
for child in list(elem):
if (child.tag == CORE_TAG_IMPLEMENTS and child.get("name") == "FridaBase.AgentMessageSink") \
if (child.tag == CORE_TAG_IMPLEMENTS and child.get("name") in {"Frida.HostSessionHub", "FridaBase.AgentMessageSink"}) \
or child.tag == CORE_TAG_FIELD \
or child.get("name").startswith("_"):
elem.remove(child)
@@ -512,6 +512,8 @@ def function_is_public(name):
"parse_socket_address",
"negotiate_connection",
"check_kernel_version",
"make_stdio_pipes",
"make_stdio_pipe",
]
def parse_vala_object_types(source) -> List[ApiObjectType]:
+2 -1
View File
@@ -40,7 +40,8 @@ namespace Frida {
}
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
+39 -3
View File
@@ -106,6 +106,8 @@ type BuildOptions struct {
DisableTypeCheck bool
SourceMap bool
Compress bool
Platform esbuild.Platform
Externals []string
}
type OutputFormat C.FridaOutputFormat
@@ -146,7 +148,7 @@ type BuildDiagnosticCallback func(d Diagnostic)
//export _frida_compiler_backend_build
func _frida_compiler_backend_build(cProjectRoot, cEntrypoint *C.char, outputFormat C.FridaOutputFormat, bundleFormat C.FridaBundleFormat,
disableTypeCheck, sourceMap, compress uintptr,
disableTypeCheck, sourceMap, compress uintptr, cPlatform *C.char, cExternals **C.char, numExternals C.int,
onCompleteFn C.FridaBuildCompleteFunc, onCompleteData unsafe.Pointer, onCompleteDataDestroy C.FridaDestroyFunc,
onDiagnosticFn C.FridaDiagnosticFunc, onDiagnosticData unsafe.Pointer) {
options := BuildOptions{
@@ -157,6 +159,8 @@ func _frida_compiler_backend_build(cProjectRoot, cEntrypoint *C.char, outputForm
DisableTypeCheck: disableTypeCheck != 0,
SourceMap: sourceMap != 0,
Compress: compress != 0,
Platform: platformFromFrida(C.GoString(cPlatform)),
Externals: parseCExternals(cExternals, numExternals),
}
onComplete := NewCDelegate(onCompleteFn, onCompleteData, onCompleteDataDestroy)
onDiagnostic := NewCDelegate(onDiagnosticFn, onDiagnosticData, nil)
@@ -181,7 +185,7 @@ func _frida_compiler_backend_build(cProjectRoot, cEntrypoint *C.char, outputForm
//export _frida_compiler_backend_watch
func _frida_compiler_backend_watch(cProjectRoot, cEntrypoint *C.char, outputFormat C.FridaOutputFormat, bundleFormat C.FridaBundleFormat,
disableTypeCheck, sourceMap, compress uintptr,
disableTypeCheck, sourceMap, compress uintptr, cPlatform *C.char, cExternals **C.char, numExternals C.int,
onReadyFn C.FridaWatchReadyFunc, onReadyData unsafe.Pointer, onReadyDataDestroy C.FridaDestroyFunc,
onStartingFn C.FridaStartingFunc, onStartingData unsafe.Pointer,
onFinishedFn C.FridaFinishedFunc, onFinishedData unsafe.Pointer,
@@ -195,6 +199,8 @@ func _frida_compiler_backend_watch(cProjectRoot, cEntrypoint *C.char, outputForm
DisableTypeCheck: disableTypeCheck != 0,
SourceMap: sourceMap != 0,
Compress: compress != 0,
Platform: platformFromFrida(C.GoString(cPlatform)),
Externals: parseCExternals(cExternals, numExternals),
}
onReady := NewCDelegate(onReadyFn, onReadyData, onReadyDataDestroy)
onStarting := NewCDelegate(onStartingFn, onStartingData, nil)
@@ -246,6 +252,35 @@ func _frida_compiler_backend_watch_session_dispose(h uintptr) {
handle.Delete()
}
func platformFromFrida(s string) esbuild.Platform {
switch s {
case "gum":
return esbuild.PlatformNode
case "browser":
return esbuild.PlatformBrowser
case "neutral":
return esbuild.PlatformNeutral
default:
panic(fmt.Sprintf("Unknown platform: %q", s))
}
}
func parseCExternals(cExternals **C.char, numExternals C.int) []string {
if numExternals == 0 {
return nil
}
n := int(numExternals)
cArray := (*[1 << 20]*C.char)(unsafe.Pointer(cExternals))[:n:n]
externals := make([]string, n)
for i := 0; i < n; i++ {
externals[i] = C.GoString(cArray[i])
}
return externals
}
func makeCBuildDiagnosticCallback(onDiagnostic *CDelegate[C.FridaDiagnosticFunc]) BuildDiagnosticCallback {
return func(d Diagnostic) {
var cPath *C.char
@@ -445,9 +480,10 @@ func makeContext(options BuildOptions, callbacks BuildEventCallbacks) (ctx esbui
Bundle: true,
Outdir: projectRoot,
AbsWorkingDir: projectRoot,
Platform: esbuild.PlatformNode,
Platform: options.Platform,
Format: format,
Inject: []string{"frida-builtins:///node-globals.js"},
External: options.Externals,
EntryPoints: []string{entrypoint},
Write: false,
Plugins: plugins,
+43 -5
View File
@@ -46,7 +46,8 @@ namespace Frida {
CompilerBackend.build (project_root, entrypoint, opts.output_format, opts.bundle_format,
(size_t) (opts.type_check == NONE), (size_t) (opts.source_maps == INCLUDED),
(size_t) (opts.compression == TERSER), (owned) on_complete, on_diagnostic);
(size_t) (opts.compression == TERSER), opts.platform.to_nick (),
opts.externals.to_array (), (owned) on_complete, on_diagnostic);
yield;
if (error_message != null)
@@ -96,7 +97,8 @@ namespace Frida {
CompilerBackend.watch (project_root, entrypoint, opts.output_format, opts.bundle_format,
(size_t) (opts.type_check == NONE), (size_t) (opts.source_maps == INCLUDED),
(size_t) (opts.compression == TERSER), (owned) on_ready, on_starting, on_finished,
(size_t) (opts.compression == TERSER), opts.platform.to_nick (),
opts.externals.to_array (), (owned) on_ready, on_starting, on_finished,
on_output, on_diagnostic);
yield;
@@ -283,13 +285,14 @@ namespace Frida {
[CCode (has_target = false)]
private delegate void BuildFunc (string project_root, string entrypoint, OutputFormat output_format,
BundleFormat bundle_format, size_t disable_type_check, size_t source_map, size_t compress,
owned BuildCompleteFunc on_complete, DiagnosticFunc on_diagnostic);
string platform, string[] externals, owned BuildCompleteFunc on_complete,
DiagnosticFunc on_diagnostic);
[CCode (has_target = false)]
private delegate void WatchFunc (string project_root, string entrypoint, OutputFormat output_format,
BundleFormat bundle_format, size_t disable_type_check, size_t source_map, size_t compress,
owned WatchReadyFunc on_ready, StartingFunc on_starting, FinishedFunc on_finished, OutputFunc on_output,
DiagnosticFunc on_diagnostic);
string platform, string[] externals, owned WatchReadyFunc on_ready, StartingFunc on_starting,
FinishedFunc on_finished, OutputFunc on_output, DiagnosticFunc on_diagnostic);
#if COMPILER_BACKEND_STATIC_COMPILATION
private extern void _init_go_runtime ();
@@ -373,6 +376,27 @@ namespace Frida {
set;
default = NONE;
}
public JsPlatform platform {
get;
set;
default = GUM;
}
internal Gee.List<string> externals = new Gee.ArrayList<string> ();
public void clear_externals () {
externals.clear ();
}
public void add_external (string external) {
externals.add (external);
}
public void enumerate_externals (Func<string> func) {
foreach (var external in externals)
func (external);
}
}
public sealed class BuildOptions : CompilerOptions {
@@ -446,4 +470,18 @@ namespace Frida {
return Marshal.enum_to_nick<JsCompression> (this);
}
}
public enum JsPlatform {
GUM,
BROWSER,
NEUTRAL;
public static JsPlatform from_nick (string nick) throws Error {
return Marshal.enum_from_nick<JsPlatform> (nick);
}
public string to_nick () {
return Marshal.enum_to_nick<JsPlatform> (this);
}
}
}
+13 -12
View File
@@ -845,8 +845,8 @@ namespace Frida {
construct;
}
public LaunchdAgent (DarwinHostSession host_session, Cancellable io_cancellable) {
Object (host_session: host_session, io_cancellable: io_cancellable);
public LaunchdAgent (HostSessionConnection connection, Cancellable io_cancellable) {
Object (connection: connection, io_cancellable: io_cancellable);
}
construct {
@@ -905,7 +905,7 @@ namespace Frida {
try {
if (path == XPC_PROXY_PATH) {
var agent = new XpcProxyAgent ((DarwinHostSession) host_session, identifier, pid);
var agent = new XpcProxyAgent (connection, identifier, pid);
yield agent.run_until_exec (io_cancellable);
}
@@ -921,7 +921,7 @@ namespace Frida {
try {
if (path == XPC_PROXY_PATH) {
var agent = new XpcProxyAgent ((DarwinHostSession) host_session, identifier, pid);
var agent = new XpcProxyAgent (connection, identifier, pid);
yield agent.run_until_exec (io_cancellable);
}
@@ -937,7 +937,8 @@ namespace Frida {
protected override async string? load_source (Cancellable? cancellable) throws Error, IOError {
string * raw_source = Frida.Data.Darwin.get_launchd_js_blob ().data;
return raw_source->replace ("@REPORT_CRASHES@", ((DarwinHostSession) host_session).report_crashes.to_string ());
return raw_source->replace ("@REPORT_CRASHES@",
((DarwinHostSession) connection.host_session).report_crashes.to_string ());
}
}
@@ -952,8 +953,8 @@ namespace Frida {
construct;
}
public XpcProxyAgent (DarwinHostSession host_session, string identifier, uint pid) {
Object (host_session: host_session, identifier: identifier, pid: pid);
public XpcProxyAgent (HostSessionConnection connection, string identifier, uint pid) {
Object (connection: connection, identifier: identifier, pid: pid);
}
construct {
@@ -965,7 +966,7 @@ namespace Frida {
public async void run_until_exec (Cancellable? cancellable) throws Error, IOError {
yield ensure_loaded (cancellable);
var helper = ((DarwinHostSession) host_session).helper;
var helper = ((DarwinHostSession) connection.host_session).helper;
yield helper.resume (pid, cancellable);
yield wait_for_unload (cancellable);
@@ -1002,10 +1003,10 @@ namespace Frida {
construct;
}
public ReportCrashAgent (DarwinHostSession host_session, uint pid, MappedAgentContainer mapped_agent_container,
public ReportCrashAgent (HostSessionConnection connection, uint pid, MappedAgentContainer mapped_agent_container,
Cancellable io_cancellable) {
Object (
host_session: host_session,
connection: connection,
pid: pid,
mapped_agent_container: mapped_agent_container,
io_cancellable: io_cancellable
@@ -1226,9 +1227,9 @@ namespace Frida {
construct;
}
public OSAnalyticsAgent (DarwinHostSession host_session, uint pid, Cancellable io_cancellable) {
public OSAnalyticsAgent (HostSessionConnection connection, uint pid, Cancellable io_cancellable) {
Object (
host_session: host_session,
connection: connection,
pid: pid,
io_cancellable: io_cancellable
);
+52 -93
View File
@@ -30,7 +30,6 @@
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <unistd.h>
#include <util.h>
#ifndef _POSIX_SPAWN_DISABLE_ASLR
# define _POSIX_SPAWN_DISABLE_ASLR 0x0100
@@ -181,6 +180,7 @@ struct _FridaSpawnInstance
mach_vm_address_t dyld_data;
GumAddress modern_entry_address;
GumAddress sim_notify_address;
/* V4+ */
GumAddress notify_objc_init;
@@ -405,9 +405,6 @@ static void frida_spawn_instance_disable_nth_breakpoint (FridaSpawnInstance * se
static guint32 frida_spawn_instance_put_software_breakpoint (FridaSpawnInstance * self, GumAddress where, guint index);
static guint32 frida_spawn_instance_overwrite_arm64_instruction (FridaSpawnInstance * self, GumAddress address, guint32 new_instruction);
static void frida_make_pty (int fds[2]);
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_close (FridaInjectInstance * instance);
@@ -804,12 +801,17 @@ _frida_darwin_helper_backend_spawn (FridaDarwinHelperBackend * self, const gchar
posix_spawnattr_t attributes;
sigset_t signal_mask_set;
short flags;
int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
FridaFileDescriptor * in_fd = NULL;
FridaFileDescriptor * out_fd = NULL;
FridaFileDescriptor * err_fd = NULL;
GError * stdio_error = NULL;
FridaAslr aslr = FRIDA_ASLR_AUTO;
GVariant * aslr_value;
gchar * old_cwd = NULL;
int result, spawn_errno;
*pipes = NULL;
instance = frida_spawn_instance_new (self);
argv = frida_host_spawn_options_compute_argv (options, path, NULL);
@@ -823,33 +825,13 @@ _frida_darwin_helper_backend_spawn (FridaDarwinHelperBackend * self, const gchar
flags = POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_START_SUSPENDED;
switch (options->stdio)
{
case FRIDA_STDIO_INHERIT:
*pipes = NULL;
*pipes = frida_make_stdio_pipes (options->stdio, TRUE, &in_fd, NULL, &out_fd, NULL, &err_fd, NULL, &stdio_error);
if (stdio_error != NULL)
goto propagate_stdio_error;
posix_spawn_file_actions_adddup2 (&file_actions, 0, 0);
posix_spawn_file_actions_adddup2 (&file_actions, 1, 1);
posix_spawn_file_actions_adddup2 (&file_actions, 2, 2);
break;
case FRIDA_STDIO_PIPE:
frida_make_pty (stdin_pipe);
frida_make_pty (stdout_pipe);
frida_make_pty (stderr_pipe);
*pipes = frida_stdio_pipes_new (stdin_pipe[1], stdout_pipe[0], stderr_pipe[0]);
posix_spawn_file_actions_adddup2 (&file_actions, stdin_pipe[0], 0);
posix_spawn_file_actions_adddup2 (&file_actions, stdout_pipe[1], 1);
posix_spawn_file_actions_adddup2 (&file_actions, stderr_pipe[1], 2);
break;
default:
g_assert_not_reached ();
}
posix_spawn_file_actions_adddup2 (&file_actions, (in_fd != NULL) ? in_fd->handle : 0, 0);
posix_spawn_file_actions_adddup2 (&file_actions, (out_fd != NULL) ? out_fd->handle : 1, 1);
posix_spawn_file_actions_adddup2 (&file_actions, (err_fd != NULL) ? err_fd->handle : 2, 2);
aslr_value = g_hash_table_lookup (options->aux, "aslr");
if (aslr_value != NULL && !frida_parse_aslr_option (aslr_value, &aslr, error))
@@ -872,13 +854,6 @@ _frida_darwin_helper_backend_spawn (FridaDarwinHelperBackend * self, const gchar
if (old_cwd != NULL)
chdir (old_cwd);
if (options->stdio == FRIDA_STDIO_PIPE)
{
close (stdin_pipe[0]);
close (stdout_pipe[1]);
close (stderr_pipe[1]);
}
posix_spawnattr_destroy (&attributes);
posix_spawn_file_actions_destroy (&file_actions);
@@ -892,6 +867,11 @@ _frida_darwin_helper_backend_spawn (FridaDarwinHelperBackend * self, const gchar
goto beach;
propagate_stdio_error:
{
g_propagate_error (error, stdio_error);
goto early_failure;
}
chdir_failed:
{
g_set_error (error,
@@ -936,12 +916,18 @@ any_failure:
kill (instance->pid, SIGKILL);
frida_spawn_instance_close (instance);
g_clear_object (pipes);
pid = 0;
goto beach;
}
beach:
{
g_clear_object (&in_fd);
g_clear_object (&out_fd);
g_clear_object (&err_fd);
g_free (old_cwd);
g_strfreev (envp);
g_strfreev (argv);
@@ -1057,6 +1043,8 @@ frida_darwin_helper_backend_launch_using_fbs (NSString * identifier, NSURL * url
FridaSpringboardApi * api;
NSMutableDictionary * debug_options, * open_options;
FridaStdioPipes * pipes = NULL;
gchar * stdout_name = NULL;
gchar * stderr_name = NULL;
GError * error = NULL;
FridaAslr aslr = FRIDA_ASLR_AUTO;
GVariant * aslr_value;
@@ -1092,22 +1080,12 @@ frida_darwin_helper_backend_launch_using_fbs (NSString * identifier, NSURL * url
if (strlen (spawn_options->cwd) > 0)
goto cwd_not_supported;
pipes = frida_make_stdio_pipes (spawn_options->stdio, FALSE, NULL, NULL, NULL, &stdout_name, NULL, &stderr_name, &error);
if (error != NULL)
goto failure;
if (spawn_options->stdio == FRIDA_STDIO_PIPE)
{
gint stdout_master, stdout_slave, stderr_master, stderr_slave;
gchar stdout_name[PATH_MAX], stderr_name[PATH_MAX];
openpty (&stdout_master, &stdout_slave, stdout_name, NULL, NULL);
openpty (&stderr_master, &stderr_slave, stderr_name, NULL, NULL);
pipes = frida_stdio_pipes_new (-1, stdout_master, stderr_master);
frida_configure_terminal_attributes (stdout_master);
frida_configure_terminal_attributes (stderr_master);
frida_stdio_pipes_retain (pipes, stdout_slave);
frida_stdio_pipes_retain (pipes, stderr_slave);
chmod (stdout_name, 0666);
chmod (stderr_name, 0666);
@@ -1174,7 +1152,7 @@ frida_darwin_helper_backend_launch_using_fbs (NSString * identifier, NSURL * url
}
});
return;
goto beach;
envp_not_supported:
{
@@ -1195,6 +1173,16 @@ cwd_not_supported:
failure:
{
on_complete (NULL, error, on_complete_target);
g_clear_object (&pipes);
goto beach;
}
beach:
{
g_free (stdout_name);
g_free (stderr_name);
return;
}
}
@@ -1881,7 +1869,7 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
mach_msg_type_number_t state_count = GUM_DARWIN_THREAD_STATE_COUNT;
thread_state_flavor_t state_flavor = GUM_DARWIN_THREAD_STATE_FLAVOR;
GumAddress dyld_start, dyld_granularity, dyld_chunk, dyld_header;
GumAddress modern_entry_address, legacy_entry_address;
GumAddress modern_entry_address, legacy_entry_address, sim_notify_address;
const gchar * launch_with_closure_names[] = {
"__ZN4dyldL17launchWithClosureEPKN5dyld37closure13LaunchClosureEPK15DyldSharedCachePKNS0_11MachOLoadedEmiPPKcSD_SD_R11DiagnosticsPmSG_PbSH_",
"__ZN4dyldL17launchWithClosureEPKN5dyld312launch_cache13binary_format7ClosureEPK15DyldSharedCachePK11mach_headermiPPKcSE_SE_PmSF_",
@@ -2101,6 +2089,9 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
instance->ret_gadget |= 1;
}
sim_notify_address = gum_darwin_module_resolve_symbol_address (dyld, "__ZN5dyld4L29sim_notifyMonitorOfMainCalledEv");
instance->sim_notify_address = sim_notify_address;
instance->ret_state = FRIDA_RET_FROM_HELPER;
kr = frida_get_debug_state (child_thread, &instance->previous_debug_state, instance->cpu_type);
@@ -2112,6 +2103,8 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
frida_spawn_instance_set_nth_breakpoint (instance, i++, legacy_entry_address, FRIDA_BREAKPOINT_REPEAT_ALWAYS);
if (modern_entry_address != 0)
frida_spawn_instance_set_nth_breakpoint (instance, i++, modern_entry_address, FRIDA_BREAKPOINT_REPEAT_ALWAYS);
if (sim_notify_address != 0)
frida_spawn_instance_set_nth_breakpoint (instance, i++, sim_notify_address, FRIDA_BREAKPOINT_REPEAT_ALWAYS);
if (instance->dyld_flavor == FRIDA_DYLD_V4_PLUS)
{
GumAddress restart_with_dyld_in_cache = gum_darwin_module_resolve_symbol_address (dyld,
@@ -3104,7 +3097,11 @@ frida_spawn_instance_handle_breakpoint (FridaSpawnInstance * self, FridaBreakpoi
if (self->breakpoint_phase == FRIDA_BREAKPOINT_DETECT_FLAVOR)
{
if (self->dyld_flavor == FRIDA_DYLD_V4_PLUS)
if (pc == self->sim_notify_address)
{
self->breakpoint_phase = FRIDA_BREAKPOINT_LIBSYSTEM_INITIALIZED;
}
else if (self->dyld_flavor == FRIDA_DYLD_V4_PLUS)
{
if (pc == self->modern_entry_address)
{
@@ -4065,44 +4062,6 @@ frida_spawn_instance_overwrite_arm64_instruction (FridaSpawnInstance * self, Gum
return original_instruction;
}
static void
frida_make_pty (int fds[2])
{
gboolean pipe_opened;
int i;
pipe_opened = openpty (&fds[0], &fds[1], NULL, NULL, NULL) != -1;
g_assert (pipe_opened);
for (i = 0; i != 2; i++)
{
const int fd = fds[i];
int res;
res = fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC);
g_assert (res == 0);
res = fcntl (fd, F_SETNOSIGPIPE, TRUE);
g_assert (res == 0);
}
frida_configure_terminal_attributes (fds[0]);
}
static void
frida_configure_terminal_attributes (gint fd)
{
struct termios tios;
tcgetattr (fd, &tios);
tios.c_oflag &= ~ONLCR;
tios.c_cflag = (tios.c_cflag & CLOCAL) | CS8 | CREAD | HUPCL;
tios.c_lflag &= ~ECHO;
tcsetattr (fd, 0, &tios);
}
static FridaInjectInstance *
frida_inject_instance_new (FridaDarwinHelperBackend * backend, guint id, guint pid)
{
+10 -62
View File
@@ -136,9 +136,9 @@ namespace Frida {
ChildWatch.add ((Pid) child_pid, on_child_dead);
if (pipes != null) {
stdin_streams[child_pid] = new UnixOutputStream (pipes.input, false);
process_next_output_from.begin (new UnixInputStream (pipes.output, false), child_pid, 1, pipes);
process_next_output_from.begin (new UnixInputStream (pipes.error, false), child_pid, 2, pipes);
stdin_streams[child_pid] = pipes.input;
process_next_output_from.begin (pipes.output, child_pid, 1, pipes);
process_next_output_from.begin (pipes.error, child_pid, 2, pipes);
}
return child_pid;
@@ -180,10 +180,8 @@ namespace Frida {
var pipes = pending.pipes;
if (pipes != null) {
pipes.clear_retained ();
process_next_output_from.begin (new UnixInputStream (pipes.output, false), pid, 1, pipes);
process_next_output_from.begin (new UnixInputStream (pipes.error, false), pid, 2, pipes);
process_next_output_from.begin (pipes.output, pid, 1, pipes);
process_next_output_from.begin (pipes.error, pid, 2, pipes);
}
}
@@ -439,8 +437,11 @@ namespace Frida {
private bool is_booting (uint task) throws Error {
Gum.Darwin.AllImageInfos infos;
if (!Gum.Darwin.query_all_image_infos (task, out infos))
throw new Error.PROCESS_NOT_FOUND ("Target process died unexpectedly");
try {
Gum.Darwin.query_all_image_infos (task, out infos);
} catch (Gum.Error e) {
throw new Error.PROCESS_NOT_FOUND ("%s", e.message);
}
return !infos.libsystem_initialized;
}
@@ -888,57 +889,4 @@ namespace Frida {
return false;
}
}
public sealed class StdioPipes : Object {
public int input {
get;
construct;
}
public int output {
get;
construct;
}
public int error {
get;
construct;
}
private int[] retained = {};
public StdioPipes (int input, int output, int error) {
Object (input: input, output: output, error: error);
}
construct {
try {
if (input != -1)
Unix.set_fd_nonblocking (input, true);
Unix.set_fd_nonblocking (output, true);
Unix.set_fd_nonblocking (error, true);
} catch (GLib.Error e) {
assert_not_reached ();
}
}
~StdioPipes () {
clear_retained ();
if (input != -1)
Posix.close (input);
Posix.close (output);
Posix.close (error);
}
public void clear_retained () {
foreach (var fd in retained)
Posix.close (fd);
retained = {};
}
public void retain (int fd) {
retained += fd;
}
}
}
+123 -21
View File
@@ -314,6 +314,9 @@ namespace Frida {
SocketService? service = null;
TimeoutSource? timeout_source = null;
bool timed_out = false;
bool child_exited = false;
int child_status = 0;
try {
string tempdir;
@@ -341,7 +344,6 @@ namespace Frida {
return false;
});
idle_source.attach (main_context);
yield;
var incoming_handler = service.incoming.connect ((c) => {
@@ -350,42 +352,63 @@ namespace Frida {
return true;
});
var timer = new Timer ();
timeout_source = new TimeoutSource (10);
timeout_source = new TimeoutSource.seconds (10);
timeout_source.set_callback (() => {
timed_out = true;
launch_helper.callback ();
return Source.CONTINUE;
return Source.REMOVE;
});
timeout_source.attach (main_context);
string[] argv = { helper_path, socket_address };
GLib.SpawnFlags flags = GLib.SpawnFlags.LEAVE_DESCRIPTORS_OPEN | /* GLib.SpawnFlags.CLOEXEC_PIPES */ 256;
GLib.SpawnFlags flags =
GLib.SpawnFlags.LEAVE_DESCRIPTORS_OPEN |
GLib.SpawnFlags.DO_NOT_REAP_CHILD |
/* GLib.SpawnFlags.CLOEXEC_PIPES */ 256;
GLib.Process.spawn_async (null, argv, null, flags, null, out pending_pid);
while (pending_stream == null && timer.elapsed () < 10.0 && !process_is_dead (pending_pid))
var exit_monitor = new ChildExitMonitor (pending_pid, main_context);
var exit_handler = exit_monitor.exited.connect ((status) => {
child_exited = true;
child_status = status;
launch_helper.callback ();
});
while (pending_stream == null && !timed_out && !child_exited)
yield;
exit_monitor.disconnect (exit_handler);
exit_monitor.stop ();
timeout_source.destroy ();
timeout_source = null;
service.disconnect (incoming_handler);
service.stop ();
service = null;
timeout_source.destroy ();
timeout_source = null;
if (pending_stream == null)
throw new Error.TIMED_OUT ("Unexpectedly timed out while spawning helper process");
if (pending_stream == null) {
if (child_exited)
throw new Error.PROCESS_NOT_FOUND ("Helper exited during launch (status=%d)", child_status);
if (timed_out)
throw new Error.TIMED_OUT ("Unexpectedly timed out while spawning helper");
}
pending_connection = yield new DBusConnection (pending_stream, ServerGuid.HOST_SESSION_SERVICE,
AUTHENTICATION_SERVER | AUTHENTICATION_ALLOW_ANONYMOUS, null, cancellable);
pending_proxy = yield pending_connection.get_proxy (null, ObjectPath.HELPER, DO_NOT_LOAD_PROPERTIES,
cancellable);
if (pending_connection.is_closed ())
throw new Error.NOT_SUPPORTED ("Helper terminated prematurely");
} catch (GLib.Error e) {
bool died_unexpectedly = pending_pid != 0 && process_is_dead (pending_pid);
if (pending_pid != 0 && !died_unexpectedly)
if (pending_pid != 0 && !child_exited) {
Posix.kill ((Posix.pid_t) pending_pid, Posix.Signal.KILL);
reap_later.begin (pending_pid);
}
if (timeout_source != null)
timeout_source.destroy ();
@@ -393,10 +416,6 @@ namespace Frida {
if (service != null)
service.stop ();
if (died_unexpectedly) {
throw new Error.PROCESS_NOT_FOUND ("Peer process (%d) died unexpectedly: %s",
pending_pid, e.message);
}
if (e is Error)
throw (Error) e;
throw new Error.PERMISSION_DENIED ("%s", e.message);
@@ -422,10 +441,6 @@ namespace Frida {
return proxy;
}
private static bool process_is_dead (uint pid) {
return Posix.kill ((Posix.pid_t) pid, 0) == -1 && Posix.errno == Posix.ESRCH;
}
private void on_connection_closed (bool remote_peer_vanished, GLib.Error? error) {
obtain_request = null;
@@ -441,7 +456,10 @@ namespace Frida {
connection.on_closed.disconnect (on_connection_closed);
connection = null;
Pid pid = process_pid;
process_pid = 0;
if (pid != 0)
reap_later.begin (pid);
}
private void on_output (uint pid, int fd, uint8[] data) {
@@ -559,4 +577,88 @@ namespace Frida {
return false;
}
}
private class ChildExitMonitor : Object {
public signal void exited (int status);
private Pid pid;
private MainContext? context;
private TimeoutSource? source;
private uint interval_ms = 10;
private const uint MAX_INTERVAL_MS = 1000;
public ChildExitMonitor (Pid pid, MainContext? ctx = null) {
this.pid = pid;
context = ctx ?? MainContext.get_thread_default ();
arm ();
}
public void stop () {
if (source != null) {
source.destroy ();
source = null;
}
}
private void arm () {
source = new TimeoutSource (interval_ms);
source.set_callback (() => {
int status;
if (try_reap (pid, out status)) {
stop ();
exited (status);
return Source.REMOVE;
}
if (interval_ms < MAX_INTERVAL_MS) {
interval_ms = uint.min (MAX_INTERVAL_MS, 2 * interval_ms);
stop ();
arm ();
return Source.REMOVE;
}
return Source.CONTINUE;
});
source.attach (context);
}
private static bool try_reap (Pid pid, out int status) {
status = 0;
var r = Posix.waitpid ((Posix.pid_t) pid, out status, Posix.WNOHANG);
if (r > 0)
return true;
if (r == 0)
return false;
if (Posix.errno == Posix.EINTR)
return false;
if (Posix.errno == Posix.ECHILD)
return true;
return true;
}
}
private async void reap_later (Pid pid) {
var monitor = new ChildExitMonitor (pid);
bool done = false;
ulong h = monitor.exited.connect ((status) => {
done = true;
reap_later.callback ();
});
try {
while (!done)
yield;
} finally {
monitor.disconnect (h);
monitor.stop ();
}
}
}
-2
View File
@@ -109,8 +109,6 @@ _frida_get_springboard_api (void)
gum_interceptor_replace (interceptor, frida_xpc_look_up_endpoint, frida_replacement_xpc_look_up_endpoint, NULL, NULL);
else
g_error ("Unable to locate _xpc_look_up_endpoint(); please file a bug");
g_object_unref (interceptor);
}
#endif
+1 -1
View File
@@ -489,7 +489,7 @@ static void
frida_add_app_icons (GHashTable * parameters, NSImage * image)
{
GVariantBuilder builder;
const guint sizes[] = { 16, 32 };
const guint sizes[] = { 128 };
guint i;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+2 -1
View File
@@ -130,7 +130,8 @@ namespace Frida {
public async void close (Cancellable? cancellable) throws IOError {
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_ARGUMENT ("Already created");
+19 -37
View File
@@ -172,8 +172,6 @@ static gboolean frida_exec_instance_try_perform_transition (FridaExecInstance *
static void frida_exec_instance_suspend (FridaExecInstance * self);
static void frida_exec_instance_resume (FridaExecInstance * self);
static void frida_make_pipe (int fds[2]);
static FridaInjectInstance * frida_inject_instance_new (FridaBinjector * binjector, guint id, guint pid, const FridaRemoteApi * api,
const gchar * temp_path);
static void frida_inject_instance_recreate_fifo (FridaInjectInstance * self);
@@ -211,7 +209,10 @@ _frida_binjector_do_spawn (FridaBinjector * self, const gchar * path, FridaHostS
{
FridaSpawnInstance * instance;
gchar ** argv, ** envp;
int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
FridaFileDescriptor * in_fd = NULL;
FridaFileDescriptor * out_fd = NULL;
FridaFileDescriptor * err_fd = NULL;
GError * stdio_error = NULL;
gchar * old_cwd = NULL;
gboolean success;
const gchar * failed_operation;
@@ -221,24 +222,9 @@ _frida_binjector_do_spawn (FridaBinjector * self, const gchar * path, FridaHostS
argv = frida_host_spawn_options_compute_argv (options, path, NULL);
envp = frida_host_spawn_options_compute_envp (options, NULL);
switch (options->stdio)
{
case FRIDA_STDIO_INHERIT:
*pipes = NULL;
break;
case FRIDA_STDIO_PIPE:
frida_make_pipe (stdin_pipe);
frida_make_pipe (stdout_pipe);
frida_make_pipe (stderr_pipe);
*pipes = frida_stdio_pipes_new (stdin_pipe[1], stdout_pipe[0], stderr_pipe[0]);
break;
default:
g_assert_not_reached ();
}
*pipes = frida_make_stdio_pipes (options->stdio, TRUE, &in_fd, NULL, &out_fd, NULL, &err_fd, NULL, &stdio_error);
if (stdio_error != NULL)
goto propagate_stdio_error;
if (strlen (options->cwd) > 0)
{
@@ -254,9 +240,9 @@ _frida_binjector_do_spawn (FridaBinjector * self, const gchar * path, FridaHostS
if (options->stdio == FRIDA_STDIO_PIPE)
{
dup2 (stdin_pipe[0], 0);
dup2 (stdout_pipe[1], 1);
dup2 (stderr_pipe[1], 2);
dup2 (in_fd->handle, 0);
dup2 (out_fd->handle, 1);
dup2 (err_fd->handle, 2);
}
ptrace (PT_TRACE_ME, 0, NULL, 0);
@@ -273,13 +259,6 @@ _frida_binjector_do_spawn (FridaBinjector * self, const gchar * path, FridaHostS
g_warning ("Failed to restore working directory");
}
if (options->stdio == FRIDA_STDIO_PIPE)
{
close (stdin_pipe[0]);
close (stdout_pipe[1]);
close (stderr_pipe[1]);
}
success = frida_wait_for_child_signal (instance->pid, SIGTRAP, NULL);
CHECK_OS_RESULT (success, !=, FALSE, "wait(SIGTRAP)");
@@ -290,6 +269,11 @@ _frida_binjector_do_spawn (FridaBinjector * self, const gchar * path, FridaHostS
goto beach;
propagate_stdio_error:
{
g_propagate_error (error, stdio_error);
goto failure;
}
chdir_failed:
{
g_set_error (error,
@@ -316,6 +300,10 @@ failure:
}
beach:
{
g_clear_object (&in_fd);
g_clear_object (&out_fd);
g_clear_object (&err_fd);
g_free (old_cwd);
g_strfreev (envp);
g_strfreev (argv);
@@ -736,12 +724,6 @@ frida_exec_instance_resume (FridaExecInstance * self)
ptrace (PT_DETACH, self->pid, NULL, 0);
}
static void
frida_make_pipe (int fds[2])
{
g_unix_open_pipe (fds, FD_CLOEXEC, NULL);
}
static FridaInjectInstance *
frida_inject_instance_new (FridaBinjector * binjector, guint id, guint pid, const FridaRemoteApi * api, const gchar * temp_path)
{
+3 -40
View File
@@ -68,9 +68,9 @@ namespace Frida {
monitor_child (child_pid);
if (pipes != null) {
stdin_streams[child_pid] = new UnixOutputStream (pipes.input, false);
process_next_output_from.begin (new UnixInputStream (pipes.output, false), child_pid, 1, pipes);
process_next_output_from.begin (new UnixInputStream (pipes.error, false), child_pid, 2, pipes);
stdin_streams[child_pid] = pipes.input;
process_next_output_from.begin (pipes.output, child_pid, 1, pipes);
process_next_output_from.begin (pipes.error, child_pid, 2, pipes);
}
return child_pid;
@@ -696,41 +696,4 @@ namespace Frida {
protected enum ProgressMessageType {
HELLO = 0xff
}
protected class StdioPipes : Object {
public int input {
get;
construct;
}
public int output {
get;
construct;
}
public int error {
get;
construct;
}
public StdioPipes (int input, int output, int error) {
Object (input: input, output: output, error: error);
}
construct {
try {
Unix.set_fd_nonblocking (input, true);
Unix.set_fd_nonblocking (output, true);
Unix.set_fd_nonblocking (error, true);
} catch (GLib.Error e) {
assert_not_reached ();
}
}
~StdioPipes () {
Posix.close (input);
Posix.close (output);
Posix.close (error);
}
}
}
+8 -2
View File
@@ -24,7 +24,7 @@ namespace Frida {
}
}
public sealed class DeviceManager : Object {
public sealed class DeviceManager : Object, HostSessionHub {
public signal void added (Device device);
public signal void removed (Device device);
public signal void changed ();
@@ -394,6 +394,12 @@ namespace Frida {
changed ();
}
private async HostSessionEntry resolve_host_session (string id, Cancellable? cancellable) throws Error, IOError {
var device = yield get_device_by_id (id, 0, cancellable);
var session = yield device.get_host_session (cancellable);
return new HostSessionEntry (device.provider, session);
}
private void check_open () throws Error {
if (stop_request != null)
throw new Error.INVALID_OPERATION ("Device manager is closed");
@@ -1326,7 +1332,7 @@ namespace Frida {
host_session_request = new Promise<HostSession> ();
try {
var session = yield provider.create (host_session_options, cancellable);
var session = yield provider.create (manager, host_session_options, cancellable);
attach_host_session (session);
current_host_session = session;
+2 -1
View File
@@ -80,7 +80,8 @@ namespace Frida {
yield Fruity.DTXConnection.close_all (device, cancellable);
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
+6 -2
View File
@@ -88,8 +88,12 @@ namespace Frida.Fruity {
request.set_string ("SystemBUID", system_buid);
var response = yield service.query (request, cancellable);
if (response.has ("Error"))
throw new LockdownError.PROTOCOL ("Unexpected response: %s", response.get_string ("Error"));
if (response.has ("Error")) {
var error = response.get_string ("Error");
if (error == "InvalidHostID")
yield unpair (cancellable);
throw new LockdownError.PROTOCOL ("Unexpected response: %s", error);
}
if (response.get_boolean ("EnableSessionSSL"))
service.stream = yield start_tls (service.stream, cancellable);
+55 -11
View File
@@ -47,6 +47,9 @@ namespace Frida {
}
private void add_nonlocal_backends () {
#if HAVE_SIMMY_BACKEND
add_backend (new SimmyHostSessionBackend ());
#endif
#if !IOS && !ANDROID && !TVOS
#if HAVE_FRUITY_BACKEND
add_backend (new FruityHostSessionBackend ());
@@ -161,7 +164,7 @@ namespace Frida {
get;
}
public abstract async HostSession create (HostSessionOptions? options = null,
public abstract async HostSession create (HostSessionHub hub, HostSessionOptions? options = null,
Cancellable? cancellable = null) throws Error, IOError;
public abstract async void destroy (HostSession session, Cancellable? cancellable = null) throws Error, IOError;
public signal void host_session_detached (HostSession session);
@@ -180,6 +183,42 @@ namespace Frida {
public abstract void unlink_service_session (HostSession host_session, ServiceSessionId id);
}
public interface HostSessionConnection : Object {
public abstract HostSession host_session {
get;
}
public abstract async AgentSession link_agent_session (AgentSessionId id, AgentMessageSink sink,
Cancellable? cancellable = null) throws Error, IOError;
public abstract void unlink_agent_session (AgentSessionId id);
}
public interface HostSessionHub : Object {
public abstract async HostSessionEntry resolve_host_session (string id, Cancellable? cancellable) throws Error, IOError;
}
public class NullHostSessionHub : Object, HostSessionHub {
public async HostSessionEntry resolve_host_session (string id, Cancellable? cancellable) throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Host session lookup not supported");
}
}
public class HostSessionEntry : Object {
public HostSessionProvider provider {
get;
construct;
}
public HostSession session {
get;
construct;
}
public HostSessionEntry (HostSessionProvider provider, HostSession session) {
Object (provider: provider, session: session);
}
}
public enum HostSessionProviderKind {
LOCAL,
REMOTE,
@@ -277,7 +316,8 @@ namespace Frida {
host_session = null;
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
@@ -332,7 +372,13 @@ namespace Frida {
}
}
public abstract class LocalHostSession : Object, HostSession, AgentController {
public abstract class LocalHostSession : Object, HostSession, HostSessionConnection, AgentController {
public HostSession host_session {
get {
return this;
}
}
private Gee.HashMap<uint, Cancellable> pending_establish_ops = new Gee.HashMap<uint, Cancellable> ();
private Gee.HashMap<uint, Future<AgentEntry>> agent_entries = new Gee.HashMap<uint, Future<AgentEntry>> ();
@@ -1423,7 +1469,7 @@ namespace Frida {
public abstract class InternalAgent : Object, AgentMessageSink, RpcPeer {
public signal void unloaded ();
public weak LocalHostSession host_session {
public weak HostSessionConnection connection {
get;
construct;
}
@@ -1447,11 +1493,7 @@ namespace Frida {
construct {
rpc_client = new RpcClient (this);
host_session.agent_session_detached.connect (on_agent_session_detached);
}
~InternalAgent () {
host_session.agent_session_detached.disconnect (on_agent_session_detached);
connection.host_session.agent_session_detached.connect (on_agent_session_detached);
}
public async void close (Cancellable? cancellable) throws IOError {
@@ -1463,6 +1505,8 @@ namespace Frida {
}
yield ensure_unloaded (cancellable);
connection.host_session.agent_session_detached.disconnect (on_agent_session_detached);
}
protected abstract async uint get_target_pid (Cancellable? cancellable) throws Error, IOError;
@@ -1516,9 +1560,9 @@ namespace Frida {
target_pid = yield get_target_pid (cancellable);
try {
session_id = yield host_session.attach (target_pid, attach_options, cancellable);
session_id = yield connection.host_session.attach (target_pid, attach_options, cancellable);
session = yield host_session.link_agent_session (session_id, (AgentMessageSink) this, cancellable);
session = yield connection.link_agent_session (session_id, (AgentMessageSink) this, cancellable);
string? source = yield load_source (cancellable);
if (source != null) {
+4 -4
View File
@@ -9,7 +9,7 @@
"version": "1.0.0",
"dependencies": {
"@frida/buffer": "^7.0.4",
"frida-java-bridge": "^7.0.8"
"frida-java-bridge": "^7.0.10"
},
"devDependencies": {
"esbuild": "^0.25.5",
@@ -534,9 +534,9 @@
"license": "MIT"
},
"node_modules/frida-java-bridge": {
"version": "7.0.8",
"resolved": "https://registry.npmjs.org/frida-java-bridge/-/frida-java-bridge-7.0.8.tgz",
"integrity": "sha512-EmqoTGzcDuCd+YFcjBJJPf7iVQCSbbj9fD0/TN8YYYwl5ZJtTqaCLTTsaC07o3bJaDVs/f3ks+Vu1bOw0S1PnQ==",
"version": "7.0.10",
"resolved": "https://registry.npmjs.org/frida-java-bridge/-/frida-java-bridge-7.0.10.tgz",
"integrity": "sha512-AtjgAHE7a2wV6M/8tZvjVqda0T8YGmSoXqkbD0r1Eq6XTFDKYbClKbZqMYHpI9OjxtkIx+xPqkHWWE3+0fDH7w==",
"license": "LGPL-2.0 WITH WxWindows-exception-3.1"
},
"node_modules/ieee754": {
+1 -1
View File
@@ -12,6 +12,6 @@
},
"dependencies": {
"@frida/buffer": "^7.0.4",
"frida-java-bridge": "^7.0.8"
"frida-java-bridge": "^7.0.10"
}
}
+9 -98
View File
@@ -36,21 +36,8 @@ namespace Frida {
string[] argv = options.compute_argv (path);
string[] envp = options.compute_envp ();
StdioPipes? pipes = null;
FileDescriptor? stdin_read = null, stdin_write = null;
FileDescriptor? stdout_read = null, stdout_write = null;
FileDescriptor? stderr_read = null, stderr_write = null;
switch (options.stdio) {
case INHERIT:
break;
case PIPE: {
make_pty (out stdin_read, out stdin_write);
make_pty (out stdout_read, out stdout_write);
make_pty (out stderr_read, out stderr_write);
pipes = new StdioPipes (stdin_write, stdout_read, stderr_read);
break;
}
}
FileDescriptor? in_fd, out_fd, err_fd;
StdioPipes? pipes = make_stdio_pipes (options.stdio, true, out in_fd, null, out out_fd, null, out err_fd, null);
string? old_cwd = null;
if (options.cwd.length > 0) {
@@ -68,10 +55,10 @@ namespace Frida {
if (pid == 0) {
Posix.setsid ();
if (options.stdio == PIPE) {
Posix.dup2 (stdin_read.handle, 0);
Posix.dup2 (stdout_write.handle, 1);
Posix.dup2 (stderr_write.handle, 2);
if (pipes != null) {
Posix.dup2 (in_fd.handle, 0);
Posix.dup2 (out_fd.handle, 1);
Posix.dup2 (err_fd.handle, 2);
}
if (_ptrace (TRACEME) == -1) {
@@ -637,9 +624,9 @@ namespace Frida {
monitor ();
if (pipes != null) {
stdin_stream = new UnixOutputStream (pipes.input.handle, false);
process_next_output_from.begin (new UnixInputStream (pipes.output.handle, false), 1);
process_next_output_from.begin (new UnixInputStream (pipes.error.handle, false), 2);
stdin_stream = pipes.input;
process_next_output_from.begin (pipes.output, 1);
process_next_output_from.begin (pipes.error, 2);
}
}
@@ -703,37 +690,6 @@ namespace Frida {
}
}
private sealed class StdioPipes : Object {
public FileDescriptor input {
get;
construct;
}
public FileDescriptor output {
get;
construct;
}
public FileDescriptor error {
get;
construct;
}
public StdioPipes (FileDescriptor input, FileDescriptor output, FileDescriptor error) {
Object (input: input, output: output, error: error);
}
construct {
try {
Unix.set_fd_nonblocking (input.handle, true);
Unix.set_fd_nonblocking (output.handle, true);
Unix.set_fd_nonblocking (error.handle, true);
} catch (GLib.Error e) {
assert_not_reached ();
}
}
}
private sealed class ExecTransitionSession : SeizeSession {
private ExecTransitionSession (uint pid) {
Object (pid: pid, on_init: SeizeSession.InitBehavior.CONTINUE);
@@ -3167,53 +3123,8 @@ namespace Frida {
return Linux.syscall (SysCall.tgkill, tgid, tid, sig);
}
public extern bool _syscall_satisfies (int syscall_id, LinuxSyscall mask);
private void make_pty (out FileDescriptor read, out FileDescriptor write) throws Error {
#if HAVE_OPENPTY
int rfd = -1, wfd = -1;
char name[Posix.Limits.PATH_MAX];
if (Linux.openpty (out rfd, out wfd, name, null, null) == -1)
throw new Error.NOT_SUPPORTED ("Unable to open PTY: %s", strerror (errno));
enable_close_on_exec (rfd);
enable_close_on_exec (wfd);
configure_terminal_attributes (rfd);
read = new FileDescriptor (rfd);
write = new FileDescriptor (wfd);
#else
try {
int fds[2];
Unix.open_pipe (fds, Posix.FD_CLOEXEC);
read = new FileDescriptor (fds[0]);
write = new FileDescriptor (fds[1]);
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("Unable to open pipe: %s", e.message);
}
#endif
}
#if HAVE_OPENPTY
private void enable_close_on_exec (int fd) {
Posix.fcntl (fd, Posix.F_SETFD, Posix.fcntl (fd, Posix.F_GETFD) | Posix.FD_CLOEXEC);
}
private void configure_terminal_attributes (int fd) {
var tios = Posix.termios ();
Posix.tcgetattr (fd, out tios);
tios.c_oflag &= ~Posix.ONLCR;
tios.c_cflag = (tios.c_cflag & Posix.CLOCAL) | Posix.CS8 | Posix.CREAD | Posix.HUPCL;
tios.c_lflag &= ~Posix.ECHO;
Posix.tcsetattr (fd, 0, tios);
}
#endif
private sealed class ProcMapsSoEntry {
public uint64 base_address;
public string path;
+7 -7
View File
@@ -758,9 +758,9 @@ namespace Frida {
set;
}
public ZygoteAgent (LinuxHostSession host_session, uint pid, string name) {
public ZygoteAgent (HostSessionConnection connection, uint pid, string name) {
Object (
host_session: host_session,
connection: connection,
pid: pid,
name: name
);
@@ -768,7 +768,7 @@ namespace Frida {
public async void load (Cancellable? cancellable) throws Error, IOError {
#if ARM || ARM64
LinuxHelper helper = ((LinuxHostSession) host_session).helper;
LinuxHelper helper = ((LinuxHostSession) connection.host_session).helper;
yield helper.await_syscall (pid, POLL_LIKE, cancellable);
try {
#endif
@@ -798,9 +798,9 @@ namespace Frida {
private sealed class SystemServerAgent : InternalAgent {
private delegate void CompletionNotify ();
public SystemServerAgent (LinuxHostSession host_session) {
public SystemServerAgent (HostSessionConnection connection) {
Object (
host_session: host_session,
connection: connection,
#if HAVE_V8
script_runtime: ScriptRuntime.V8
#else
@@ -996,7 +996,7 @@ namespace Frida {
}
};
LinuxHelper helper = ((LinuxHostSession) host_session).helper;
LinuxHelper helper = ((LinuxHostSession) connection.host_session).helper;
foreach (var tid in thread_ids) {
bool safe_to_suspend = false;
string thread_name;
@@ -1050,7 +1050,7 @@ namespace Frida {
}
private void resume_threads (Gee.List<uint> thread_ids) {
LinuxHelper helper = ((LinuxHostSession) host_session).helper;
LinuxHelper helper = ((LinuxHostSession) connection.host_session).helper;
foreach (var tid in thread_ids)
helper.resume_syscall.begin (tid, null);
}
+1
View File
@@ -4,6 +4,7 @@
#include <string.h>
#include <unistd.h>
#include <gio/gunixmounts.h>
#include <gum/gum.h>
typedef struct _FridaEnumerateProcessesOperation FridaEnumerateProcessesOperation;
+28
View File
@@ -500,6 +500,34 @@ if have_local_backend
endif
endif
if have_simmy_backend
backend_sources += [
'simmy' / 'simmy-host-session.vala',
'simmy' / 'simmy-host-session-glue.m',
'simmy' / 'text-plist.vala',
]
simmy_data = custom_target('frida-data-simmy',
input: [
'simmy' / 'frida-simmy.resources',
'simmy' / 'agent' / 'springboard.js',
],
output: [
'frida-data-simmy.vapi',
'frida-data-simmy.h',
'frida-data-simmy.c',
'frida-data-simmy-blob' + resource_blob_extension,
],
command: [
resource_compiler_cmd_array,
'-c', '@INPUT0@',
'-o', meson.current_build_dir() / 'frida-data-simmy',
'@INPUT1@',
],
)
backend_sources += [simmy_data]
endif
if have_fruity_backend
backend_sources += [
'fruity' / 'fruity-host-session.vala',
+2 -1
View File
@@ -957,7 +957,8 @@ namespace Frida {
_icon = builder.end ();
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (channel != null)
throw new Error.INVALID_OPERATION ("Already created");
+131
View File
@@ -0,0 +1,131 @@
const SBS = bind({
module: '/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices',
cprefix: 'SBS',
functions: {
copyFrontmostApplicationDisplayIdentifier: ['pointer', [ ]],
copyLocalizedApplicationNameForDisplayIdentifier: ['pointer', ['pointer' ]],
copyIconImagePNGDataForDisplayIdentifier: ['pointer', ['pointer' ]],
processIDForDisplayIdentifier: ['bool', ['pointer', 'pointer']],
}
});
const CF = bind({
module: '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation',
cprefix: 'CF',
functions: {
retain: ['pointer', ['pointer' ]],
release: ['void', ['pointer' ]],
stringCreateWithCString: ['pointer', ['pointer', 'pointer', 'uint' ]],
stringGetCStringPtr: ['pointer', ['pointer', 'uint' ]],
stringGetLength: ['long', ['pointer' ]],
stringGetMaximumSizeForEncoding: ['long', ['long', 'uint' ]],
stringGetCString: ['bool', ['pointer', 'pointer', 'long', 'uint']],
dataGetLength: ['long', ['pointer' ]],
dataGetBytePtr: ['pointer', ['pointer' ]],
}
});
const kCFBooleanTrue = Module.getGlobalExportByName('kCFBooleanTrue').readPointer();
const kCFStringEncodingUTF8 = 0x08000100;
rpc.exports = {
getFrontmostApplication(scope) {
const idObj = SBS.copyFrontmostApplicationDisplayIdentifier();
if (idObj.isNull())
return null;
const id = cfStringToUtf8(idObj);
const nameObj = SBS.copyLocalizedApplicationNameForDisplayIdentifier(idObj);
const name = cfStringToUtf8(nameObj);
const pidBuf = Memory.alloc(4);
SBS.processIDForDisplayIdentifier(idObj, pidBuf);
const pid = pidBuf.readU32();
CF.release(nameObj);
CF.release(idObj);
if (pid === 0)
return null;
return [
id,
name,
pid,
(scope === 'full') ? fetchApplicationIcon(id) : null
];
},
fetchApplicationIcons(appIds) {
return appIds.map(fetchApplicationIcon);
},
};
function fetchApplicationIcon(appId) {
const idObj = CF.stringCreateWithCString(NULL, Memory.allocUtf8String(appId), kCFStringEncodingUTF8);
const pngObj = SBS.copyIconImagePNGDataForDisplayIdentifier(idObj);
CF.release(idObj);
const png = CF.dataGetBytePtr(pngObj).readByteArray(CF.dataGetLength(pngObj));
CF.release(pngObj);
return base64FromBytes(png);
}
Interceptor.attach(Module.getGlobalExportByName('SecTaskCopyValueForEntitlement'), {
onEnter(args) {
this.entitlement = cfStringToUtf8(args[1]);
},
onLeave(retval) {
if (this.entitlement === 'com.apple.springboard.iconState' && retval.isNull())
retval.replace(CF.retain(kCFBooleanTrue));
}
});
function bind({ module, cprefix, functions }) {
const mod = Process.getModuleByName(module);
const nfOpts = { exceptions: 'propagate' };
return Object.fromEntries(Object.entries(functions).map(([name, [retType, argTypes]]) => [
name,
new NativeFunction(mod.getExportByName(cprefix + name.charAt(0).toUpperCase() + name.slice(1)), retType, argTypes, nfOpts)
]));
}
function cfStringToUtf8(cfstr) {
const direct = CF.stringGetCStringPtr(cfstr, kCFStringEncodingUTF8);
if (!direct.isNull())
return direct.readUtf8String();
const len16 = CF.stringGetLength(cfstr);
const maxBytes = CF.stringGetMaximumSizeForEncoding(len16, kCFStringEncodingUTF8).valueOf() + 1;
const buf = Memory.alloc(maxBytes);
CF.stringGetCString(cfstr, buf, maxBytes, kCFStringEncodingUTF8);
return buf.readUtf8String();
}
function base64FromBytes(buf) {
const bytes = new Uint8Array(buf);
const enc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
let i = 0;
const len = bytes.length;
let out = '';
while (i < len) {
const c1 = bytes[i++];
const c2 = (i !== len) ? bytes[i++] : 0;
const c3 = (i !== len) ? bytes[i++] : 0;
const o1 = c1 >> 2;
const o2 = ((c1 & 0x03) << 4) | (c2 >> 4);
const o3 = ((c2 & 0x0f) << 2) | (c3 >> 6);
const o4 = c3 & 0x3f;
if (i - 1 > len)
out += enc[o1] + enc[o2] + '==';
else if (i > len)
out += enc[o1] + enc[o2] + enc[o3] + '=';
else
out += enc[o1] + enc[o2] + enc[o3] + enc[o4];
}
return out;
}
+2
View File
@@ -0,0 +1,2 @@
[resource-compiler]
namespace = Frida.Data.Simmy
+528
View File
@@ -0,0 +1,528 @@
#include "frida-core.h"
#include <dlfcn.h>
#import <Foundation/Foundation.h>
typedef struct _FridaSimmyContext FridaSimmyContext;
struct _FridaSimmyContext
{
dispatch_queue_t dispatch_queue;
void * core_simulator;
NSString * SimDeviceLaunchApplicationKeyArguments;
NSString * SimDeviceLaunchApplicationKeyEnvironment;
NSString * SimDeviceLaunchApplicationKeyStandardErrPath;
NSString * SimDeviceLaunchApplicationKeyStandardOutPath;
NSString * SimDeviceLaunchApplicationKeyTerminateRunningProcess;
NSString * SimDeviceLaunchApplicationKeyWaitForDebugger;
NSString * SimDeviceSpawnKeyArguments;
NSString * SimDeviceSpawnKeyEnvironment;
NSString * SimDeviceSpawnKeyStderr;
NSString * SimDeviceSpawnKeyStdin;
NSString * SimDeviceSpawnKeyStdout;
NSString * SimDeviceSpawnKeyWaitForDebugger;
FridaSimmyHostSessionBackendDeviceAddedFunc on_device_added;
gpointer on_device_added_target;
};
static void frida_simmy_context_destroy (FridaSimmyContext * self);
static void frida_simmy_context_emit_devices (FridaSimmyContext * self);
static NSArray * frida_argv_to_arguments_array (gchar * const * argv, gint argv_length, gint start_index);
static NSDictionary * frida_envp_to_environment_dictionary (gchar * const * envp, gint envp_length);
@interface SimRuntime : NSObject
@property (nonatomic, readonly, strong) NSString * identifier;
@property (nonatomic, readonly, strong) NSString * shortName;
@property (nonatomic, readonly, strong) NSString * versionString;
@property (nonatomic, readonly, strong) NSString * root;
@end
@interface SimDevice : NSObject
@property (nonatomic, readonly, strong) NSUUID * UDID;
@property (nonatomic, readonly, strong) NSString * name;
@property (nonatomic, readonly, strong) SimRuntime * runtime;
@property (nonatomic, readonly, strong) NSString * stateString;
- (NSString *)getenv:(NSString *)name
error:(NSError * _Nullable * _Nullable)error;
- (NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)installedAppsWithError:(NSError * _Nullable * _Nullable)error;
- (void)launchApplicationAsyncWithID:(NSString *)identifier
options:(NSDictionary<NSString *, id> * _Nullable)options
completionQueue:(dispatch_queue_t)queue
completionHandler:(void (^) (NSError * error, int pid))handler;
- spawnAsyncWithPath:(NSString *)path
options:(NSDictionary<NSString *, id> * _Nullable)options
terminationQueue:(dispatch_queue_t)tq
terminationHandler:(void (^) (int status))th
completionQueue:(dispatch_queue_t)cq
completionHandler:(void (^) (NSError * error, int pid))ch;
@end
@interface SimDeviceSet : NSObject
@property (nonatomic, readonly, strong) NSArray<SimDevice *> *devices;
@end
@protocol SimServiceContextClass
+ (instancetype)serviceContextForDeveloperDir:(NSString *)dir
error:(NSError * _Nullable * _Nullable)error;
@end
@interface SimServiceContext : NSObject
- (SimDeviceSet *)defaultDeviceSetWithError:(NSError * _Nullable * _Nullable)error;
@end
void *
_frida_simmy_host_session_backend_start (FridaSimmyHostSessionBackendDeviceAddedFunc on_device_added, gpointer on_device_added_target,
FridaSimmyCompleteFunc on_complete, gpointer on_complete_target, GDestroyNotify on_complete_target_destroy_notify)
{
FridaSimmyContext * ctx;
ctx = g_slice_new0 (FridaSimmyContext);
ctx->dispatch_queue = dispatch_queue_create ("re.frida.simmy.queue", DISPATCH_QUEUE_SERIAL);
ctx->on_device_added = on_device_added;
ctx->on_device_added_target = on_device_added_target;
dispatch_async (ctx->dispatch_queue, ^
{
frida_simmy_context_emit_devices (ctx);
on_complete (on_complete_target);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
});
return ctx;
}
void
_frida_simmy_host_session_backend_stop (void * simmy_context, FridaSimmyCompleteFunc on_complete,
gpointer on_complete_target, GDestroyNotify on_complete_target_destroy_notify)
{
FridaSimmyContext * ctx = simmy_context;
dispatch_async (ctx->dispatch_queue, ^
{
frida_simmy_context_destroy (ctx);
g_slice_free (FridaSimmyContext, ctx);
on_complete (on_complete_target);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
});
}
static void
frida_simmy_context_destroy (FridaSimmyContext * self)
{
g_clear_pointer (&self->core_simulator, dlclose);
dispatch_release (self->dispatch_queue);
}
static void
frida_simmy_context_emit_devices (FridaSimmyContext * self)
{
void * xcselect_module;
bool (* xcselect_get_developer_dir_path) (char *, size_t, bool *, bool *, bool *);
char developer_dir[1024] = { 0, };
bool from_override, is_command_line_tools, from_fallback;
void * cs;
NSString ** str;
Class<SimServiceContextClass> SimServiceContextClass;
SimServiceContext * ctx;
SimDeviceSet * set;
xcselect_module = dlopen ("/usr/lib/libxcselect.dylib", RTLD_GLOBAL | RTLD_LAZY);
if (xcselect_module == NULL)
goto beach;
xcselect_get_developer_dir_path = dlsym (xcselect_module, "xcselect_get_developer_dir_path");
if (!xcselect_get_developer_dir_path (developer_dir, sizeof (developer_dir), &from_override, &is_command_line_tools, &from_fallback))
goto beach;
cs = dlopen ("/Library/Developer/PrivateFrameworks/CoreSimulator.framework/CoreSimulator", RTLD_GLOBAL | RTLD_LAZY);
if (cs == NULL)
goto beach;
self->core_simulator = cs;
#define FRIDA_ASSIGN_CS_CONSTANT(N) \
str = dlsym (cs, G_STRINGIFY (N)); \
g_assert (str != NULL); \
self->N = *str
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceLaunchApplicationKeyArguments);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceLaunchApplicationKeyEnvironment);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceLaunchApplicationKeyStandardErrPath);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceLaunchApplicationKeyStandardOutPath);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceLaunchApplicationKeyTerminateRunningProcess);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceLaunchApplicationKeyWaitForDebugger);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyArguments);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyEnvironment);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyStderr);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyStdin);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyStdout);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyWaitForDebugger);
#undef FRIDA_ASSIGN_CS_CONSTANT
SimServiceContextClass = NSClassFromString (@"SimServiceContext");
ctx = [SimServiceContextClass serviceContextForDeveloperDir:[NSString stringWithUTF8String:developer_dir] error:nil];
set = [ctx defaultDeviceSetWithError:nil];
for (SimDevice * device in set.devices)
{
FridaSimmyRuntime * runtime;
FridaSimmyDevice * d;
if (![device.stateString isEqualToString:@"Booted"])
continue;
runtime = frida_simmy_runtime_new (device.runtime);
d = frida_simmy_device_new ([device retain], device.UDID.UUIDString.UTF8String, device.name.UTF8String,
[device getenv:@"SIMULATOR_MODEL_IDENTIFIER" error:nil].UTF8String, runtime, self);
self->on_device_added (d, self->on_device_added_target);
g_object_unref (d);
g_object_unref (runtime);
}
beach:
if (xcselect_module != NULL)
dlclose (xcselect_module);
}
void
_frida_simmy_device_list_applications (FridaSimmyDevice * self, FridaSimmyDeviceListApplicationsCompleteFunc on_complete,
gpointer on_complete_target, GDestroyNotify on_complete_target_destroy_notify)
{
FridaSimmyContext * ctx = frida_simmy_device_get_simmy_context (self);
dispatch_async (ctx->dispatch_queue, ^
{
GeeArrayList * applications;
SimDevice * device;
applications = gee_array_list_new (FRIDA_SIMMY_TYPE_APPLICATION, g_object_ref, g_object_unref, NULL, NULL, NULL);
device = frida_simmy_device_get_handle (self);
NSDictionary<NSString *, NSDictionary<NSString *, id> *> * bundles = [device installedAppsWithError:nil];
for (NSString * identifier in bundles)
{
NSDictionary<NSString *, id> * bundle;
NSString * display_name;
FridaSimmyApplication * app;
bundle = bundles[identifier];
display_name = bundle[@"CFBundleDisplayName"];
app = frida_simmy_application_new (identifier.UTF8String, display_name.UTF8String);
gee_collection_add (GEE_COLLECTION (applications), app);
g_object_unref (app);
}
on_complete (GEE_LIST (applications), on_complete_target);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
g_object_unref (applications);
});
}
void
_frida_simmy_device_launch_application (FridaSimmyDevice * self, const gchar * identifier, FridaHostSpawnOptions * opts,
FridaSimmyDeviceLaunchApplicationCompleteFunc on_complete, gpointer on_complete_target,
GDestroyNotify on_complete_target_destroy_notify)
{
SimDevice * device;
FridaSimmyContext * ctx;
__block FridaStdioPipes * pipes;
__block FridaFileDescriptor * out_fd, * err_fd;
gchar * out_name, * err_name;
GError * error = NULL;
__block GMainContext * main_context;
device = frida_simmy_device_get_handle (self);
ctx = frida_simmy_device_get_simmy_context (self);
if (opts->has_envp)
goto envp_not_supported;
pipes = frida_make_stdio_pipes (opts->stdio, FALSE, NULL, NULL, &out_fd, &out_name, &err_fd, &err_name, &error);
if (error != NULL)
goto propagate_error;
main_context = g_main_context_ref_thread_default ();
@autoreleasepool
{
NSMutableDictionary<NSString *, id> * launch_opts;
launch_opts = [@{
ctx->SimDeviceLaunchApplicationKeyWaitForDebugger: @YES,
ctx->SimDeviceLaunchApplicationKeyTerminateRunningProcess: @YES,
} mutableCopy];
if (opts->has_argv)
launch_opts[ctx->SimDeviceLaunchApplicationKeyArguments] = frida_argv_to_arguments_array (opts->argv, opts->argv_length1, 1);
if (opts->has_env)
launch_opts[ctx->SimDeviceLaunchApplicationKeyEnvironment] = frida_envp_to_environment_dictionary (opts->env, opts->env_length1);
if (opts->stdio == FRIDA_STDIO_PIPE)
{
launch_opts[ctx->SimDeviceLaunchApplicationKeyStandardOutPath] = @(out_name);
launch_opts[ctx->SimDeviceLaunchApplicationKeyStandardErrPath] = @(err_name);
};
[device launchApplicationAsyncWithID:[NSString stringWithUTF8String:identifier]
options:launch_opts
completionQueue:ctx->dispatch_queue
completionHandler:
^(NSError * error, int pid)
{
FridaSimmySpawnedProcess * process = NULL;
if (error == nil)
process = frida_simmy_spawned_process_new (pid, pipes, main_context);
g_main_context_unref (main_context);
main_context = NULL;
g_clear_object (&pipes);
g_clear_object (&out_fd);
g_clear_object (&err_fd);
on_complete (error.localizedDescription.UTF8String, process, on_complete_target);
g_clear_object (&process);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
}];
}
g_free (out_name);
g_free (err_name);
return;
envp_not_supported:
{
error = g_error_new_literal (
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"The 'envp' option is not supported when spawning Simmy apps, use the 'env' option instead");
goto propagate_error;
}
propagate_error:
{
dispatch_async (ctx->dispatch_queue, ^
{
on_complete (error->message, NULL, on_complete_target);
g_error_free (error);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
});
return;
}
}
void
_frida_simmy_device_spawn_program (FridaSimmyDevice * self, const gchar * program, FridaHostSpawnOptions * opts,
FridaSimmyDeviceSpawnProgramCompleteFunc on_complete, gpointer on_complete_target,
GDestroyNotify on_complete_target_destroy_notify)
{
const FridaStdio stdio = opts->stdio;
SimDevice * device;
FridaSimmyContext * ctx;
__block FridaStdioPipes * pipes;
__block FridaFileDescriptor * in_fd, * out_fd, * err_fd;
GError * error = NULL;
__block GMainContext * main_context;
device = frida_simmy_device_get_handle (self);
ctx = frida_simmy_device_get_simmy_context (self);
if (opts->has_envp)
goto envp_not_supported;
pipes = frida_make_stdio_pipes (opts->stdio, TRUE, &in_fd, NULL, &out_fd, NULL, &err_fd, NULL, &error);
if (error != NULL)
goto propagate_error;
main_context = g_main_context_ref_thread_default ();
@autoreleasepool
{
NSMutableDictionary<NSString *, id> * spawn_opts;
__block FridaSimmySpawnedProcess * process = NULL;
spawn_opts = [@{
ctx->SimDeviceSpawnKeyWaitForDebugger: @YES,
} mutableCopy];
if (opts->has_argv)
spawn_opts[ctx->SimDeviceSpawnKeyArguments] = frida_argv_to_arguments_array (opts->argv, opts->argv_length1, 0);
if (opts->has_env)
spawn_opts[ctx->SimDeviceSpawnKeyEnvironment] = frida_envp_to_environment_dictionary (opts->env, opts->env_length1);
if (stdio == FRIDA_STDIO_PIPE)
{
spawn_opts[ctx->SimDeviceSpawnKeyStdin] = @(in_fd->handle);
spawn_opts[ctx->SimDeviceSpawnKeyStdout] = @(out_fd->handle);
spawn_opts[ctx->SimDeviceSpawnKeyStderr] = @(err_fd->handle);
};
[device spawnAsyncWithPath:[NSString stringWithUTF8String:program]
options:spawn_opts
terminationQueue:ctx->dispatch_queue
terminationHandler:
^(int status)
{
_frida_simmy_spawned_process_on_termination (process, status);
g_object_unref (process);
process = NULL;
}
completionQueue:ctx->dispatch_queue
completionHandler:
^(NSError * error, int pid)
{
if (error == nil)
process = frida_simmy_spawned_process_new (pid, pipes, main_context);
g_main_context_unref (main_context);
main_context = NULL;
g_clear_object (&pipes);
g_clear_object (&in_fd);
g_clear_object (&out_fd);
g_clear_object (&err_fd);
on_complete (error.localizedDescription.UTF8String, process, on_complete_target);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
}];
}
return;
envp_not_supported:
{
error = g_error_new_literal (
FRIDA_ERROR,
FRIDA_ERROR_NOT_SUPPORTED,
"The 'envp' option is not supported when spawning Simmy programs, use the 'env' option instead");
goto propagate_error;
}
propagate_error:
{
dispatch_async (ctx->dispatch_queue, ^
{
on_complete (error->message, NULL, on_complete_target);
g_error_free (error);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
});
return;
}
}
const gchar *
_frida_simmy_runtime_get_identifier (FridaSimmyRuntime * self)
{
return ((SimRuntime *) frida_simmy_runtime_get_handle (self)).identifier.UTF8String;
}
const gchar *
_frida_simmy_runtime_get_short_name (FridaSimmyRuntime * self)
{
return ((SimRuntime *) frida_simmy_runtime_get_handle (self)).shortName.UTF8String;
}
const gchar *
_frida_simmy_runtime_get_version_string (FridaSimmyRuntime * self)
{
return ((SimRuntime *) frida_simmy_runtime_get_handle (self)).versionString.UTF8String;
}
const gchar *
_frida_simmy_runtime_get_root (FridaSimmyRuntime * self)
{
return ((SimRuntime *) frida_simmy_runtime_get_handle (self)).root.UTF8String;
}
static NSArray *
frida_argv_to_arguments_array (gchar * const * argv, gint argv_length, gint start_index)
{
NSMutableArray * result;
gint i;
result = [NSMutableArray arrayWithCapacity:argv_length];
for (i = start_index; i < argv_length; i++)
[result addObject:[NSString stringWithUTF8String:argv[i]]];
return result;
}
static NSDictionary *
frida_envp_to_environment_dictionary (gchar * const * envp, gint envp_length)
{
NSMutableDictionary * result;
gint i;
result = [NSMutableDictionary dictionaryWithCapacity:envp_length];
for (i = 0; i != envp_length; i++)
{
const gchar * pair, * equals_sign, * name_start, * value_start;
NSUInteger name_size, value_size;
NSString * name, * value;
pair = envp[i];
equals_sign = strchr (pair, '=');
if (equals_sign == NULL)
continue;
name_start = pair;
name_size = equals_sign - name_start;
value_start = equals_sign + 1;
value_size = pair + strlen (pair) - value_start;
name = [[NSString alloc] initWithBytes:name_start
length:name_size
encoding:NSUTF8StringEncoding];
value = [[NSString alloc] initWithBytes:value_start
length:value_size
encoding:NSUTF8StringEncoding];
[result setObject:value forKey:name];
[value release];
[name release];
}
return result;
}
+898
View File
@@ -0,0 +1,898 @@
namespace Frida {
public sealed class SimmyHostSessionBackend : Object, HostSessionBackend {
private Gee.Map<string, SimmyHostSessionProvider> providers = new Gee.HashMap<string, SimmyHostSessionProvider> ();
private void * simmy_context;
private MainContext main_context;
private Cancellable io_cancellable = new Cancellable ();
construct {
main_context = MainContext.ref_thread_default ();
}
public async void start (Cancellable? cancellable) throws IOError {
simmy_context = _start (on_device_added, () => {
schedule_on_frida_thread (start.callback);
});
yield;
}
public async void stop (Cancellable? cancellable) throws IOError {
_stop (simmy_context, () => {
schedule_on_frida_thread (stop.callback);
});
yield;
simmy_context = null;
io_cancellable.cancel ();
foreach (var provider in providers.values) {
provider_unavailable (provider);
yield provider.close (cancellable);
}
providers.clear ();
}
private void on_device_added (Simmy.Device device) {
schedule_on_frida_thread (() => {
var prov = new SimmyHostSessionProvider (device);
providers[device.udid] = prov;
provider_available (prov);
return Source.REMOVE;
});
}
private void schedule_on_frida_thread (owned SourceFunc function) {
var source = new IdleSource ();
source.set_callback ((owned) function);
source.attach (main_context);
}
public extern static void * _start (DeviceAddedFunc on_device_added, owned Simmy.CompleteFunc on_complete);
public extern static void _stop (void * simmy_context, owned Simmy.CompleteFunc on_complete);
public delegate void DeviceAddedFunc (Simmy.Device device);
}
public sealed class SimmyHostSessionProvider : Object, HostSessionProvider {
public Simmy.Device device {
get;
construct;
}
public string id {
get { return device.udid; }
}
public string name {
get { return device.name; }
}
public Variant? icon {
get { return null; }
}
public HostSessionProviderKind kind {
get {
return HostSessionProviderKind.REMOTE;
}
}
private SimmyHostSession? host_session;
public SimmyHostSessionProvider (Simmy.Device device) {
Object (device: device);
}
public async void close (Cancellable? cancellable) throws IOError {
if (host_session == null)
return;
host_session.agent_session_detached.disconnect (on_agent_session_detached);
yield host_session.close (cancellable);
host_session = null;
}
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
HostSessionEntry local_system = yield hub.resolve_host_session ("local", cancellable);
host_session = new SimmyHostSession (device, local_system);
host_session.agent_session_detached.connect (on_agent_session_detached);
return host_session;
}
public async void destroy (HostSession session, Cancellable? cancellable) throws Error, IOError {
if (session != host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
host_session.agent_session_detached.disconnect (on_agent_session_detached);
yield host_session.close (cancellable);
host_session = null;
}
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
Cancellable? cancellable) throws Error, IOError {
if (host_session != this.host_session)
throw new Error.INVALID_ARGUMENT ("Invalid host session");
return yield this.host_session.link_agent_session (id, sink, cancellable);
}
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
if (host_session != this.host_session)
return;
this.host_session.unlink_agent_session (id);
}
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
}
public void unlink_channel (HostSession host_session, ChannelId id) {
}
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id, Cancellable? cancellable)
throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
}
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
}
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
agent_session_detached (id, reason, crash);
}
}
public sealed class SimmyHostSession : Object, HostSession, HostSessionConnection {
public Simmy.Device device {
get;
construct;
}
public HostSessionEntry local_system {
get;
construct;
}
public HostSession host_session {
get {
return this;
}
}
private Gee.Map<uint, Simmy.SpawnedProcess> spawned_processes = new Gee.HashMap<uint, Simmy.SpawnedProcess> ();
private SpringboardAgent springboard_agent;
private Cancellable io_cancellable = new Cancellable ();
public SimmyHostSession (Simmy.Device device, HostSessionEntry local_system) {
Object (device: device, local_system: local_system);
}
construct {
var s = local_system.session;
s.process_crashed.connect (on_process_crashed);
s.agent_session_detached.connect (on_agent_session_detached);
s.uninjected.connect (on_uninjected);
springboard_agent = new SpringboardAgent (this);
springboard_agent.unloaded.connect (on_springboard_agent_unloaded);
}
public async void close (Cancellable? cancellable) throws IOError {
springboard_agent.unloaded.disconnect (on_springboard_agent_unloaded);
yield springboard_agent.close (cancellable);
io_cancellable.cancel ();
}
public async void ping (uint interval_seconds, Cancellable? cancellable) throws Error, IOError {
throw new Error.INVALID_OPERATION ("Only meant to be implemented by services");
}
public async HashTable<string, Variant> query_system_parameters (Cancellable? cancellable) throws Error, IOError {
var parameters = new HashTable<string, Variant> (str_hash, str_equal);
var runtime = device.runtime;
unowned string os_name = runtime.short_name;
var os = new HashTable<string, Variant> (str_hash, str_equal);
os["id"] = os_name.down ();
os["name"] = os_name.replace ("iOS", "iPhone OS");
os["version"] = runtime.version_string;
parameters["os"] = os;
parameters["platform"] = "darwin";
parameters["arch"] = "arm64";
var hardware = new HashTable<string, Variant> (str_hash, str_equal);
hardware["product"] = device.model_identifier;
parameters["hardware"] = hardware;
parameters["access"] = "full";
parameters["name"] = device.name;
parameters["udid"] = device.udid;
return parameters;
}
public async HostApplicationInfo get_frontmost_application (HashTable<string, Variant> options,
Cancellable? cancellable) throws Error, IOError {
var opts = FrontmostQueryOptions._deserialize (options);
try {
return yield springboard_agent.get_frontmost_application (opts, cancellable);
} catch (Error e) {
if (e is Error.PERMISSION_DENIED)
throw new Error.NOT_SUPPORTED ("Frontmost query is only supported with SIP disabled (for now)");
throw e;
}
}
public async HostApplicationInfo[] enumerate_applications (HashTable<string, Variant> options,
Cancellable? cancellable) throws Error, IOError {
var opts = ApplicationQueryOptions._deserialize (options);
var scope = opts.scope;
Future<Gee.List<Simmy.Application>> list_apps_request = device.list_applications ();
Future<Gee.List<LaunchdJob>> list_jobs_request = list_launchd_jobs (cancellable);
var selected_ids = new Gee.HashSet<string> ();
opts.enumerate_selected_identifiers (identifier => {
selected_ids.add (identifier);
});
bool include_all_apps = selected_ids.is_empty;
var apps = new Gee.ArrayList<Simmy.Application> ();
foreach (Simmy.Application app in yield list_apps_request.wait_async (cancellable)) {
unowned string id = app.identifier;
if (include_all_apps || id in selected_ids)
apps.add (app);
}
Gee.Map<string, Bytes>? icons = null;
if (scope == FULL) {
var app_ids = new Gee.ArrayList<string> ();
foreach (var app in apps)
app_ids.add (app.identifier);
try {
icons = yield springboard_agent.fetch_application_icons (app_ids, cancellable);
} catch (Error e) {
}
}
var pids = new Gee.HashMap<string, uint> ();
foreach (var job in yield list_jobs_request.wait_async (cancellable)) {
if (job.pid == 0)
continue;
unowned string label = job.label;
if (!label.has_prefix ("UIKitApplication:"))
continue;
string identifier = label[17:].split ("[", 2)[0];
pids[identifier] = job.pid;
}
var result = new HostApplicationInfo[0];
foreach (var app in apps) {
unowned string identifier = app.identifier;
var info = HostApplicationInfo (identifier, app.display_name, pids[identifier], make_parameters_dict ());
if (scope == FULL && icons != null)
add_app_icons (info.parameters, icons[identifier]);
result += info;
}
return result;
}
public async HostProcessInfo[] enumerate_processes (HashTable<string, Variant> options, Cancellable? cancellable)
throws Error, IOError {
var opts = ProcessQueryOptions._deserialize (options);
if (!opts.has_selected_pids ()) {
foreach (var job in yield list_launchd_jobs (cancellable).wait_async (cancellable)) {
if (job.pid != 0)
opts.select_pid (job.pid);
}
}
try {
return yield local_system.session.enumerate_processes (opts._serialize (), cancellable);
} catch (GLib.Error e) {
throw_api_error (e);
}
}
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public async HostSpawnInfo[] enumerate_pending_spawn (Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public async HostChildInfo[] enumerate_pending_children (Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public async uint spawn (string program, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
Future<Simmy.SpawnedProcess> f;
if (program[0] == '/')
f = device.spawn_program (program, options);
else
f = device.launch_application (program, options);
var process = yield f.wait_async (cancellable);
uint pid = process.pid;
if (options.stdio == PIPE) {
spawned_processes[pid] = process;
process.terminated.connect (on_spawned_process_terminated);
process.output.connect (on_spawned_process_output);
}
return pid;
}
private void on_spawned_process_terminated (Simmy.SpawnedProcess process, int status) {
spawned_processes.unset (process.pid);
}
private void on_spawned_process_output (Simmy.SpawnedProcess process, int fd, uint8[] data) {
output (process.pid, fd, data);
if (data.length == 0)
spawned_processes.unset (process.pid);
}
public async void input (uint pid, uint8[] data, Cancellable? cancellable) throws Error, IOError {
var process = spawned_processes[pid];
if (process == null)
throw new Error.INVALID_ARGUMENT ("Invalid PID");
yield process.input (data, cancellable);
}
public async void resume (uint pid, Cancellable? cancellable) throws GLib.Error {
yield local_system.session.resume (pid, cancellable);
}
public async void kill (uint pid, Cancellable? cancellable) throws GLib.Error {
yield local_system.session.kill (pid, cancellable);
}
public async AgentSessionId attach (uint pid, HashTable<string, Variant> options,
Cancellable? cancellable) throws GLib.Error {
return yield local_system.session.attach (pid, options, cancellable);
}
public async void reattach (AgentSessionId id, Cancellable? cancellable) throws Error, IOError {
throw new Error.INVALID_OPERATION ("Only meant to be implemented by services");
}
public async AgentSession link_agent_session (AgentSessionId id, AgentMessageSink sink,
Cancellable? cancellable) throws Error, IOError {
return yield local_system.provider.link_agent_session (local_system.session, id, sink, cancellable);
}
public void unlink_agent_session (AgentSessionId id) {
local_system.provider.unlink_agent_session (local_system.session, id);
}
public async InjectorPayloadId inject_library_file (uint pid, string path, string entrypoint, string data,
Cancellable? cancellable) throws GLib.Error {
return yield local_system.session.inject_library_file (pid, path, entrypoint, data, cancellable);
}
public async InjectorPayloadId inject_library_blob (uint pid, uint8[] blob, string entrypoint, string data,
Cancellable? cancellable) throws GLib.Error {
return yield local_system.session.inject_library_blob (pid, blob, entrypoint, data, cancellable);
}
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public IOStream link_channel (ChannelId id) throws Error {
throw_not_supported ();
}
public void unlink_channel (ChannelId id) {
assert_not_reached ();
}
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public ServiceSession link_service_session (ServiceSessionId id) throws Error {
throw_not_supported ();
}
public void unlink_service_session (ServiceSessionId id) {
assert_not_reached ();
}
private void on_process_crashed (CrashInfo crash) {
process_crashed (crash);
}
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
agent_session_detached (id, reason, crash);
}
private void on_uninjected (InjectorPayloadId id) {
uninjected (id);
}
internal Future<Gee.List<LaunchdJob>> list_launchd_jobs (Cancellable? cancellable) {
var promise = new Promise<Gee.List<LaunchdJob>> ();
perform_list_launchd_jobs.begin (cancellable, promise);
return promise.future;
}
private async void perform_list_launchd_jobs (Cancellable? cancellable, Promise<Gee.List<LaunchdJob>> request) {
try {
RunResult res = yield run_and_capture_output ({
Path.build_filename (device.runtime.root, "bin", "launchctl"),
"list"
}, cancellable).wait_async (cancellable);
res.check ();
var jobs = new Gee.ArrayList<LaunchdJob> ();
string[] lines = res.standard_output.chomp ().split ("\n");
foreach (string line in lines[1:]) {
string[] tokens = line.split ("\t", 3);
if (tokens.length != 3)
throw new Error.PROTOCOL ("Unexpected launchctl output");
unowned string raw_pid = tokens[0];
unowned string label = tokens[2];
jobs.add (new LaunchdJob () {
pid = uint.parse (raw_pid),
label = label,
});
}
request.resolve (jobs);
} catch (GLib.Error e) {
request.reject (e);
}
}
internal class LaunchdJob {
public uint pid;
public string label;
}
private Future<RunResult> run_and_capture_output (string[] argv, Cancellable? cancellable) {
var promise = new Promise<RunResult> ();
perform_run_and_capture_output.begin (argv, cancellable, promise);
return promise.future;
}
private async void perform_run_and_capture_output (string[] argv, Cancellable? cancellable, Promise<RunResult> request) {
string[] argv_copy = argv;
var opts = HostSpawnOptions ();
opts.has_argv = true;
opts.argv = argv_copy;
opts.stdio = PIPE;
Simmy.SpawnedProcess process;
try {
process = yield device.spawn_program (argv_copy[0], opts).wait_async (cancellable);
} catch (GLib.Error e) {
request.reject (e);
return;
}
int status = -1;
var standard_output = new StringBuilder.sized (1024);
var standard_error = new StringBuilder.sized (1024);
process.terminated.connect ((s) => {
status = s;
perform_run_and_capture_output.callback ();
});
process.output.connect ((fd, data) => {
var buf = new char[data.length + 1];
Memory.copy (buf, data, data.length);
buf[data.length] = '\0';
unowned StringBuilder target = (fd == 1) ? standard_output : standard_error;
target.append ((string) buf);
});
process.resume ();
yield;
request.resolve (new RunResult (argv_copy) {
status = status,
standard_output = standard_output.str,
standard_error = standard_error.str,
});
}
private class RunResult {
public int status;
public string standard_output;
public string standard_error;
private string[] argv;
public RunResult (string[] argv) {
this.argv = argv;
}
public void check () throws Error {
if (status != 0) {
throw new Error.NOT_SUPPORTED ("Failed to run `%s`, it exited with status %d: %s",
string.joinv (" ", argv),
status,
standard_error.chomp ());
}
}
}
private void on_springboard_agent_unloaded (InternalAgent dead_agent) {
dead_agent.unloaded.disconnect (on_springboard_agent_unloaded);
springboard_agent = new SpringboardAgent (this);
springboard_agent.unloaded.connect (on_springboard_agent_unloaded);
}
[NoReturn]
private static void throw_not_supported () throws Error {
throw new Error.NOT_SUPPORTED ("Not yet supported by the Simmy backend");
}
}
private sealed class SpringboardAgent : InternalAgent {
public SpringboardAgent (HostSessionConnection connection) {
Object (connection: connection);
}
public async HostApplicationInfo get_frontmost_application (FrontmostQueryOptions options,
Cancellable? cancellable) throws Error, IOError {
var scope = options.scope;
var scope_node = new Json.Node.alloc ().init_string (scope.to_nick ());
Json.Node response = yield call ("getFrontmostApplication", new Json.Node[] { scope_node }, null, cancellable);
if (response.get_node_type () == NULL)
return HostApplicationInfo.empty ();
var item = response.get_array ();
var identifier = item.get_string_element (0);
var name = item.get_string_element (1);
var pid = (uint) item.get_int_element (2);
var info = HostApplicationInfo (identifier, name, pid, make_parameters_dict ());
if (scope == FULL)
add_app_icons (info.parameters, new Bytes.take (Base64.decode (item.get_string_element (3))));
return info;
}
public async Gee.Map<string, Bytes> fetch_application_icons (Gee.List<string> app_ids, Cancellable? cancellable)
throws Error, IOError {
var app_id_values = new Json.Array ();
foreach (var id in app_ids)
app_id_values.add_string_element (id);
var args = new Json.Node[] { new Json.Node.alloc ().init_array (app_id_values) };
Json.Node response = yield call ("fetchApplicationIcons", args, null, cancellable);
var icons = new Gee.HashMap<string, Bytes> ();
var reader = new Json.Reader (response);
int n = reader.count_elements ();
for (int i = 0; i != n; i++) {
reader.read_element (i);
if (!reader.get_null_value ())
icons[app_ids[i]] = new Bytes (Base64.decode (reader.get_string_value ()));
reader.end_element ();
}
return icons;
}
protected override async uint get_target_pid (Cancellable? cancellable) throws Error, IOError {
var host_session = (SimmyHostSession) connection.host_session;
foreach (var job in yield host_session.list_launchd_jobs (cancellable).wait_async (cancellable)) {
if (job.label == "com.apple.SpringBoard")
return job.pid;
}
throw new Error.NOT_SUPPORTED ("Unable to locate SpringBoard process");
}
protected override async string? load_source (Cancellable? cancellable) throws Error, IOError {
return (string) Frida.Data.Simmy.get_springboard_js_blob ().data;
}
}
private void add_app_icons (HashTable<string, Variant> parameters, Bytes png) {
var icons = new VariantBuilder (new VariantType.array (VariantType.VARDICT));
icons.open (VariantType.VARDICT);
icons.add ("{sv}", "format", new Variant.string ("png"));
icons.add ("{sv}", "image", Variant.new_from_data (new VariantType ("ay"), png.get_data (), true, png));
icons.close ();
parameters["icons"] = icons.end ();
}
[CCode (gir_namespace = "FridaSimmy", gir_version = "1.0")]
namespace Simmy {
public sealed class Device : Object {
public void * handle {
get;
construct;
}
public string udid {
get;
construct;
}
public string name {
get;
construct;
}
public string model_identifier {
get;
construct;
}
public Runtime runtime {
get;
construct;
}
public void * simmy_context {
get;
construct;
}
public Device (void * handle, string udid, string name, string model_identifier, Runtime runtime,
void * simmy_context) {
Object (
handle: handle,
udid: udid,
name: name,
model_identifier: model_identifier,
runtime: runtime,
simmy_context: simmy_context
);
}
public Future<Gee.List<Application>> list_applications () {
var promise = new Promise<Gee.List<Application>> ();
_list_applications (applications => {
promise.resolve (applications);
});
return promise.future;
}
public Future<SpawnedProcess> launch_application (string identifier, HostSpawnOptions opts) {
var promise = new Promise<SpawnedProcess> ();
_launch_application (identifier, opts, (error_message, process) => {
if (error_message != null)
promise.reject (new Error.NOT_SUPPORTED ("%s", error_message));
else
promise.resolve (process);
});
return promise.future;
}
public Future<SpawnedProcess> spawn_program (string program, HostSpawnOptions opts) {
var promise = new Promise<SpawnedProcess> ();
_spawn_program (program, opts, (error_message, process) => {
if (error_message != null)
promise.reject (new Error.NOT_SUPPORTED ("%s", error_message));
else
promise.resolve (process);
});
return promise.future;
}
public extern void _list_applications (owned ListApplicationsCompleteFunc on_complete);
public extern void _launch_application (string identifier, HostSpawnOptions options,
owned LaunchApplicationCompleteFunc on_complete);
public extern void _spawn_program (string program, HostSpawnOptions options,
owned SpawnProgramCompleteFunc on_complete);
public delegate void ListApplicationsCompleteFunc (Gee.List<Application> device);
public delegate void LaunchApplicationCompleteFunc (string? error_message, SpawnedProcess? process);
public delegate void SpawnProgramCompleteFunc (string? error_message, SpawnedProcess? process);
}
public sealed class Runtime : Object {
public void * handle {
get;
construct;
}
public string identifier {
get {
return _get_identifier ();
}
}
public string short_name {
get {
return _get_short_name ();
}
}
public string version_string {
get {
return _get_version_string ();
}
}
public string root {
get {
return _get_root ();
}
}
public Runtime (void * handle) {
Object (handle: handle);
}
public extern unowned string _get_identifier ();
public extern unowned string _get_short_name ();
public extern unowned string _get_version_string ();
public extern unowned string _get_root ();
}
public sealed class Application : Object {
public string identifier {
get;
construct;
}
public string display_name {
get;
construct;
}
public Application (string identifier, string display_name) {
Object (identifier: identifier, display_name: display_name);
}
}
public delegate void CompleteFunc ();
public sealed class SpawnedProcess : Object {
public signal void terminated (int status);
public signal void output (int fd, uint8[] data);
public uint pid {
get;
construct;
}
public StdioPipes? pipes {
get;
construct;
}
public MainContext main_context {
get;
construct;
}
private OutputStream? stdin_stream;
private Cancellable io_cancellable = new Cancellable ();
public SpawnedProcess (uint pid, StdioPipes? pipes, MainContext main_context) {
Object (
pid: pid,
pipes: pipes,
main_context: main_context
);
}
construct {
if (pipes != null) {
var source = new IdleSource ();
source.set_callback (() => {
start_processing_pipes ();
return Source.REMOVE;
});
source.attach (main_context);
}
}
private void start_processing_pipes () {
stdin_stream = pipes.input;
process_next_output_from.begin (pipes.output, 1);
process_next_output_from.begin (pipes.error, 2);
}
public void close () {
io_cancellable.cancel ();
}
public void resume () {
Posix.kill ((Posix.pid_t) pid, Posix.Signal.CONT);
}
public void _on_termination (int status) {
var source = new IdleSource ();
source.set_callback (() => {
if (stdin_stream != null) {
stdin_stream.close_async.begin ();
stdin_stream = null;
}
close ();
terminated (status);
return Source.REMOVE;
});
source.attach (main_context);
}
public async void input (uint8[] data, Cancellable? cancellable) throws Error, IOError {
if (stdin_stream == null)
throw new Error.NOT_SUPPORTED ("Unable to pass input to process spawned without piped stdio");
try {
yield stdin_stream.write_all_async (data, Priority.DEFAULT, cancellable, null);
} catch (GLib.Error e) {
throw new Error.TRANSPORT ("%s", e.message);
}
}
private async void process_next_output_from (InputStream stream, int fd) {
try {
var buf = new uint8[4096];
var n = yield stream.read_async (buf, Priority.DEFAULT, io_cancellable);
var data = buf[0:n];
output (fd, data);
if (n > 0)
process_next_output_from.begin (stream, fd);
} catch (GLib.Error e) {
stream.close_async.begin ();
if (!(e is IOError.CANCELLED))
output (fd, {});
}
}
}
}
}
+296
View File
@@ -0,0 +1,296 @@
[CCode (gir_namespace = "FridaSimmy", gir_version = "1.0")]
namespace Frida.TextPList {
public class Parser {
private Lexer lex;
private Token look;
public Parser (string text) {
lex = new Lexer (text);
}
public Variant parse () throws Error {
look = lex.next_token ();
Variant v = parse_value ();
if (look.type != EOF)
throw new Error.PROTOCOL ("Trailing content after root value");
return v;
}
private Variant parse_value () throws Error {
switch (look.type) {
case LBRACE: return parse_dict ();
case LPAREN: return parse_array ();
case NUMBER: return parse_number ();
case STRING: return parse_string ();
default:
throw new Error.PROTOCOL (@"Unexpected token %s".printf (look.type.to_string ()));
}
}
private Variant parse_dict () throws Error {
eat (LBRACE);
var b = new VariantBuilder (new VariantType ("a{sv}"));
while (look.type != RBRACE) {
if (look.type != STRING)
throw new Error.PROTOCOL ("Dictionary key must be STRING");
string key = look.text;
eat (STRING);
eat (EQUAL);
b.add ("{sv}", key, parse_value ());
if (look.type == SEMI)
eat (SEMI);
if (look.type == RBRACE)
break;
}
eat (RBRACE);
return b.end ();
}
private Variant parse_array () throws Error {
eat (LPAREN);
var b = new VariantBuilder (new VariantType ("av"));
if (look.type != RPAREN) {
b.add_value (parse_value ());
while (look.type == COMMA) {
eat (COMMA);
b.add_value (new Variant.variant (parse_value ()));
}
}
eat (RPAREN);
return b.end ();
}
private Variant parse_number () throws Error {
string raw = look.text;
eat (NUMBER);
if (raw.index_of_char ('.') != -1) {
double d;
if (!double.try_parse (raw, out d))
throw new Error.PROTOCOL (@"Bad double: %s".printf (raw));
return d;
} else {
int64 x;
if (!int64.try_parse (raw, out x))
throw new Error.PROTOCOL (@"Bad int: %s".printf (raw));
return x;
}
}
private Variant parse_string () throws Error {
string s = look.text;
eat (STRING);
return s;
}
private void eat (TokenType t) throws Error {
if (look.type != t)
throw new Error.PROTOCOL (@"Expected %s but found %s".printf (t.to_string (), look.type.to_string ()));
look = lex.next_token ();
}
}
private class Lexer {
private string s;
private int i = 0;
private int n;
public Lexer (string input) {
s = input;
n = s.length;
}
public Token next_token () throws Error {
skip_whitespace_and_comments ();
if (at_end ())
return new Token (EOF);
switch (peek ()) {
case '{': next (); return new Token (LBRACE);
case '}': next (); return new Token (RBRACE);
case '(': next (); return new Token (LPAREN);
case ')': next (); return new Token (RPAREN);
case '=': next (); return new Token (EQUAL);
case ';': next (); return new Token (SEMI);
case ',': next (); return new Token (COMMA);
case '"': return new Token (STRING, read_quoted ());
default:
var word = read_bare ();
if (word.length == 0)
throw new Error.PROTOCOL (@"Unexpected character '%c'".printf (peek ()));
return read_number_or_bareword (word);
}
}
private bool at_end () {
return i >= n;
}
private char peek () {
return s[i];
}
private char next () {
return s[i++];
}
private void skip_whitespace_and_comments () {
while (!at_end ()) {
if (is_whitespace (peek ())) {
next ();
continue;
}
if (peek () == '/' && i + 1 < n) {
if (s[i + 1] == '/') {
i += 2;
while (!at_end () && peek () != '\n')
next ();
continue;
}
if (s[i + 1] == '*') {
i += 2;
while (!at_end ()) {
if (peek () == '*' && i + 1 < n && s[i + 1] == '/') {
i += 2;
break;
}
next ();
}
continue;
}
}
break;
}
}
private static bool is_whitespace (char c) {
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
private string read_quoted () throws Error {
next ();
var sb = new StringBuilder ();
while (!at_end ()) {
char c = next ();
if (c == '\\') {
if (at_end ())
break;
char e = next ();
switch (e) {
case 'n':
sb.append_c ('\n');
break;
case 'r':
sb.append_c ('\r');
break;
case 't':
sb.append_c ('\t');
break;
case '"':
sb.append_c ('"');
break;
case '\\':
sb.append_c ('\\');
break;
default:
sb.append_c (e);
break;
}
} else if (c == '"') {
return sb.str;
} else {
sb.append_c (c);
}
}
throw new Error.PROTOCOL ("Unterminated quoted string");
}
private string read_bare () {
int start = i;
while (!at_end () && is_bare_char (peek ()))
next ();
return s.substring (start, i - start);
}
private static bool is_bare_char (char c) {
if (c.isalnum ())
return true;
const string extra = "._-/:$+@";
return extra.index_of_char (c) != -1;
}
private Token read_number_or_bareword (string word) {
bool numeric = true;
bool has_dot = false;
int len = word.length;
for (int k = 0; k != len; k++) {
char c = word[k];
if (k == 0 && (c == '+' || c == '-'))
continue;
if (c == '.') {
if (has_dot) {
numeric = false;
break;
}
has_dot = true;
continue;
}
if (!c.isdigit ()) {
numeric = false;
break;
}
}
if (numeric && word.length > 0 && !(word == "+" || word == "-"))
return new Token (NUMBER, word);
return new Token (STRING, word);
}
}
private class Token {
public TokenType type;
public string text;
public Token (TokenType t, string s = "") {
type = t;
text = s;
}
}
private enum TokenType {
LBRACE,
RBRACE,
LPAREN,
RPAREN,
EQUAL,
SEMI,
COMMA,
STRING,
NUMBER,
EOF,
}
}
+2 -1
View File
@@ -61,7 +61,8 @@ namespace Frida {
io_cancellable.cancel ();
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
string? raw_address = null;
TlsCertificate? certificate = null;
string? origin = null;
-21
View File
@@ -298,25 +298,4 @@ namespace Frida {
public extern void _do_resume ();
}
public sealed class StdioPipes : Object {
public OutputStream input {
get;
construct;
}
public InputStream output {
get;
construct;
}
public InputStream error {
get;
construct;
}
public StdioPipes (OutputStream input, InputStream output, InputStream error) {
Object (input: input, output: output, error: error);
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/frida-gum.git
revision = 17.3.2
revision = 17.5.2
depth = 1
[provide]
+15 -14
View File
@@ -470,7 +470,8 @@ namespace Frida.HostSessionTest {
get { return HostSessionProviderKind.LOCAL; }
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Not implemented");
}
@@ -1536,7 +1537,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var applications = yield session.enumerate_applications (make_parameters_dict (), cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
@@ -1577,7 +1578,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
uint pid = 0;
bool waiting = false;
@@ -1776,7 +1777,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var applications = yield session.enumerate_applications (make_parameters_dict (), cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
@@ -1814,7 +1815,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
uint pid = 0;
bool waiting = false;
@@ -1919,7 +1920,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
uint pid = 0;
bool waiting = false;
@@ -2398,7 +2399,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var id = yield host_session.attach (pid, make_parameters_dict (), cancellable);
var session = yield prov.link_agent_session (host_session, id, h, cancellable);
@@ -2549,7 +2550,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
assert_true (processes.length > 0);
@@ -2576,7 +2577,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
uint pid = 0;
bool waiting = false;
@@ -3307,7 +3308,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
assert_true (processes.length > 0);
@@ -3333,7 +3334,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
uint pid = 0;
bool waiting = false;
@@ -3584,7 +3585,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
assert_true (processes.length > 0);
@@ -3617,7 +3618,7 @@ namespace Frida.HostSessionTest {
Cancellable? cancellable = null;
stdout.printf ("connecting to frida-server\n");
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
stdout.printf ("enumerating processes\n");
var processes = yield host_session.enumerate_processes (make_parameters_dict (), cancellable);
assert_true (processes.length > 0);
@@ -4242,7 +4243,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
assert_true (processes.length > 0);
+14
View File
@@ -77,6 +77,18 @@ namespace Darwin.XNU {
[CCode (cheader_filename = "sys/sysctl.h")]
public int sysctlbyname (string name, void * oldp, size_t * oldlenp, void * newp = null, size_t newlen = 0);
[CCode (cheader_filename = "util.h")]
public int openpty (out int amaster, out int aslave, [CCode (array_length=false, array_null_terminated=true)] char[] name,
Posix.termios? termp, winsize? winp);
[CCode (cname = "struct winsize", has_type_id = false, cheader_filename = "sys/ttycom.h", destroy_function = "")]
public struct winsize {
public ushort ws_row;
public ushort ws_col;
public ushort ws_xpixel;
public ushort ws_ypixel;
}
[CCode (cname = "struct xinpgen", cheader_filename = "netinet/in_pcb.h")]
public struct InetPcbGeneration {
[CCode (cname = "xig_len")]
@@ -104,4 +116,6 @@ namespace Darwin.XNU {
[CCode (cheader_filename = "fcntl.h")]
public const int F_FULLFSYNC;
[CCode (cheader_filename = "fcntl.h")]
public const int F_SETNOSIGPIPE;
}
+14
View File
@@ -0,0 +1,14 @@
[CCode (lower_case_cprefix = "", gir_namespace = "FreeBSD", gir_version = "1.0")]
namespace FreeBSD {
[CCode (cheader_filename = "libutil.h")]
public int openpty (out int amaster, out int aslave, [CCode (array_length=false, array_null_terminated=true)] char[] name,
Posix.termios? termp, winsize? winp);
[CCode (cname = "struct winsize", has_type_id = false, cheader_filename = "sys/ttycom.h", destroy_function = "")]
public struct winsize {
public ushort ws_row;
public ushort ws_col;
public ushort ws_xpixel;
public ushort ws_ypixel;
}
}