mirror of
https://github.com/frida/frida-core.git
synced 2026-11-08 17:12:49 -10:00
Compare commits
No files matched your search
+145
-6
@@ -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 }}
|
||||
@@ -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
@@ -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',
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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]:
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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}"));
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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": {
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@frida/buffer": "^7.0.4",
|
||||
"frida-java-bridge": "^7.0.8"
|
||||
"frida-java-bridge": "^7.0.10"
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <gio/gunixmounts.h>
|
||||
#include <gum/gum.h>
|
||||
|
||||
typedef struct _FridaEnumerateProcessesOperation FridaEnumerateProcessesOperation;
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
[resource-compiler]
|
||||
namespace = Frida.Data.Simmy
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 17.3.2
|
||||
revision = 17.5.2
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user