mirror of
https://github.com/frida/frida-core.git
synced 2026-11-10 10:01:35 -10:00
Compare commits
No files matched your search
@@ -18,7 +18,7 @@ jobs:
|
||||
include:
|
||||
- { id: windows-x86_64, runner: '"windows-latest"' }
|
||||
- { id: windows-x86, runner: '"windows-latest"' }
|
||||
- { id: macos-x86_64, runner: '"macos-11"' }
|
||||
- { id: macos-x86_64, runner: '"macos-latest"' }
|
||||
- { id: macos-arm64, runner: '"macos-latest"' }
|
||||
- { id: linux-x86_64, runner: '"ubuntu-latest"' }
|
||||
- { id: linux-x86, runner: '"ubuntu-latest"' }
|
||||
@@ -68,7 +68,6 @@ jobs:
|
||||
name: core-devkit-${{ matrix.id }}
|
||||
path: build/src/devkit/
|
||||
- name: Test
|
||||
if: matrix.id != 'macos-arm64'
|
||||
run: make test
|
||||
|
||||
cross:
|
||||
@@ -151,6 +150,14 @@ jobs:
|
||||
certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
|
||||
certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
|
||||
keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
|
||||
- uses: nttld/setup-ndk@v1
|
||||
if: startsWith(matrix.id, 'android-')
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r25c
|
||||
- name: Add NDK to env
|
||||
if: startsWith(matrix.id, 'android-')
|
||||
run: echo ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }} >> $GITHUB_ENV
|
||||
- name: Build
|
||||
run: |
|
||||
./configure --host=${{ matrix.id }} ${{ env.FRIDA_CORE_OPTIONS }}
|
||||
@@ -234,3 +241,35 @@ jobs:
|
||||
cp -a tests/frida-tests tests/labrats lib/agent/frida-agent.so /tmp/pkg/
|
||||
tar -C /tmp/pkg -cf /tmp/runner.tar .
|
||||
/opt/sabrelite/run.sh /tmp/runner.tar /opt/frida/frida-tests
|
||||
|
||||
# This job is used to check that the Linux helpers are built correctly for all
|
||||
# supported architectures. We do this because the binary artifacts are checked
|
||||
# in, and we want to ensure that they are up-to-date with respect to the source
|
||||
# for all architectures.
|
||||
check-linux-helpers:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86, x86_64, arm, arm64, mips, mipsel, mips64, mips64el]
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/frida/core-linux-helpers-${{ matrix.arch }}:latest
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build
|
||||
run: |
|
||||
./src/linux/helpers/rebuild.sh ${{ matrix.arch }}
|
||||
- name: Check for unexpected changes
|
||||
run: |
|
||||
git config --global --add safe.directory "$(realpath .)"
|
||||
status_output="$(git status --porcelain)"
|
||||
if [ -n "$status_output" ]; then
|
||||
echo "Unexpected changes detected:"
|
||||
echo "$status_output"
|
||||
echo "Diff:"
|
||||
git diff
|
||||
exit 1
|
||||
else
|
||||
echo "No unexpected changes detected."
|
||||
fi
|
||||
+50
-5
@@ -4,6 +4,7 @@ import base64
|
||||
from collections import OrderedDict
|
||||
from dataclasses import dataclass, field
|
||||
import itertools
|
||||
import locale
|
||||
import os
|
||||
from pathlib import Path
|
||||
import pickle
|
||||
@@ -14,7 +15,7 @@ import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import traceback
|
||||
from typing import Any, Literal, Mapping, Optional, Sequence
|
||||
from typing import Any, Literal, Mapping, Optional, Sequence, Tuple
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
@@ -140,16 +141,19 @@ def setup(role: Role,
|
||||
compat = set()
|
||||
else:
|
||||
summary = "enabled by user"
|
||||
missing: list[MissingFeature] = []
|
||||
|
||||
if "native" in compat:
|
||||
have_toolchain = True
|
||||
other_label: Optional[str] = None
|
||||
other_triplet: Optional[str] = None
|
||||
extra_environ: dict[str, str] = {}
|
||||
|
||||
if host_os == "windows" and host_arch == "x86_64" and host_config == "mingw":
|
||||
other_triplet = "i686-w64-mingw32"
|
||||
have_toolchain = shutil.which(other_triplet + "-gcc") is not None
|
||||
other_label = "x86"
|
||||
have_toolchain, other_triplet = detect_mingw_toolchain_for("x86")
|
||||
elif host_os == "linux" and host_arch == "x86_64" and host_config is None:
|
||||
other_label = "x86"
|
||||
other_triplet = "i686-linux-gnu"
|
||||
have_toolchain = shutil.which(other_triplet + "-gcc") is not None
|
||||
if not have_toolchain:
|
||||
@@ -173,7 +177,7 @@ def setup(role: Role,
|
||||
if not have_toolchain:
|
||||
if not auto_detect:
|
||||
raise ToolchainNotFoundError(f"unable to locate toolchain for {other_triplet}")
|
||||
summary = "disabled due to missing toolchain for {other_triplet}"
|
||||
missing.append(MissingFeature(other_label, other_triplet))
|
||||
|
||||
if host_os == "windows" and host_arch == "x86_64" and have_toolchain:
|
||||
group = OutputGroup("x86", other_triplet, extra_environ)
|
||||
@@ -269,6 +273,28 @@ def setup(role: Role,
|
||||
]
|
||||
|
||||
if "emulated" in compat:
|
||||
if host_os == "windows" and host_arch == "arm64":
|
||||
for kind, emulated_arch in [("modern", "x86_64"), ("legacy", "x86")]:
|
||||
if host_config == "mingw":
|
||||
found, emulated_triplet = detect_mingw_toolchain_for(emulated_arch)
|
||||
if not found:
|
||||
if not auto_detect:
|
||||
raise ToolchainNotFoundError(f"unable to locate toolchain for {emulated_triplet}")
|
||||
missing.append(MissingFeature(emulated_arch, emulated_triplet))
|
||||
continue
|
||||
else:
|
||||
emulated_triplet = None
|
||||
outputs[OutputGroup(emulated_arch, emulated_triplet, extra_environ)] = [
|
||||
Output(identifier=f"helper_emulated_{kind}",
|
||||
name=HELPER_FILE_WINDOWS.name.replace(".exe", f"-{emulated_arch}.exe"),
|
||||
file=HELPER_FILE_WINDOWS,
|
||||
target=HELPER_TARGET),
|
||||
Output(identifier=f"agent_emulated_{kind}",
|
||||
name=AGENT_FILE_WINDOWS.name.replace(".dll", f"-{emulated_arch}.dll"),
|
||||
file=AGENT_FILE_WINDOWS,
|
||||
target=AGENT_TARGET),
|
||||
]
|
||||
|
||||
if host_os == "android" and host_arch in {"x86_64", "x86"}:
|
||||
outputs[OutputGroup("arm")] = [
|
||||
Output(identifier="agent_emulated_legacy",
|
||||
@@ -290,6 +316,8 @@ def setup(role: Role,
|
||||
state = State(role, builddir, top_builddir, frida_version, host_os, host_config, allowed_prebuilds, outputs)
|
||||
serialized_state = base64.b64encode(pickle.dumps(state)).decode('ascii')
|
||||
|
||||
if missing:
|
||||
summary = ", ".join([f"{m.label} disabled due to missing toolchain for {m.triplet}" for m in missing])
|
||||
variable_names, output_names = zip(*[(output.identifier, output.name) \
|
||||
for output in itertools.chain.from_iterable(outputs.values())])
|
||||
print("ok")
|
||||
@@ -349,6 +377,12 @@ class Output:
|
||||
target: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class MissingFeature:
|
||||
label: str
|
||||
triplet: str
|
||||
|
||||
|
||||
def compile(privdir: Path, state: State):
|
||||
releng_location = query_releng_location(state.role)
|
||||
subprojects = detect_relevant_subprojects(releng_location)
|
||||
@@ -367,7 +401,7 @@ def compile(privdir: Path, state: State):
|
||||
**kwargs,
|
||||
"stdout": subprocess.PIPE,
|
||||
"stderr": subprocess.STDOUT,
|
||||
"encoding": "utf-8",
|
||||
"encoding": locale.getpreferredencoding(),
|
||||
}
|
||||
else:
|
||||
silenced_kwargs = kwargs
|
||||
@@ -589,6 +623,12 @@ def configure_import_path(releng_location: Path):
|
||||
sys.path.insert(0, str(releng_location.parent))
|
||||
|
||||
|
||||
def detect_mingw_toolchain_for(arch: str) -> Tuple[bool, Optional[str]]:
|
||||
triplet = MINGW_ARCHS.get(arch, arch) + "-w64-mingw32"
|
||||
found = shutil.which(triplet + "-gcc") is not None
|
||||
return (found, triplet)
|
||||
|
||||
|
||||
STATE_FILENAME = "state.dat"
|
||||
DEPFILE_FILENAME = "compat.deps"
|
||||
|
||||
@@ -616,6 +656,11 @@ MSVS_ENVVARS = {
|
||||
"LIB",
|
||||
}
|
||||
|
||||
MINGW_ARCHS = {
|
||||
"x86": "i686",
|
||||
"arm64": "aarch64",
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
@@ -1,6 +1,8 @@
|
||||
helper_modern = get_option('helper_modern')
|
||||
helper_legacy = get_option('helper_legacy')
|
||||
helper_compat = ''
|
||||
helper_emulated_modern = get_option('helper_emulated_modern')
|
||||
helper_emulated_legacy = get_option('helper_emulated_legacy')
|
||||
helper_depends = []
|
||||
|
||||
agent_modern = get_option('agent_modern')
|
||||
@@ -18,6 +20,7 @@ server_depends = []
|
||||
|
||||
no_overridden_compat_bits = \
|
||||
helper_modern == 'auto' and helper_legacy == 'auto' \
|
||||
and helper_emulated_modern == 'auto' and helper_emulated_legacy == 'auto' \
|
||||
and agent_modern == 'auto' and agent_legacy == 'auto' \
|
||||
and agent_emulated_modern == 'auto' and agent_emulated_legacy == 'auto'
|
||||
|
||||
|
||||
+61
-32
@@ -388,7 +388,7 @@ namespace Frida.Inject {
|
||||
return false;
|
||||
}
|
||||
|
||||
script_runner.on_stdin (line);
|
||||
script_runner.on_stdin (line, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -414,7 +414,12 @@ namespace Frida.Inject {
|
||||
|
||||
buf[n] = 0;
|
||||
|
||||
script_runner.on_stdin ((string) buf);
|
||||
if (script_runner.terminal_mode == TerminalMode.BINARY) {
|
||||
var bytes = new Bytes (buf[:n]);
|
||||
script_runner.on_stdin ("", bytes);
|
||||
} else {
|
||||
script_runner.on_stdin ((string) buf, null);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -549,7 +554,7 @@ namespace Frida.Inject {
|
||||
var stage = new Json.Node.alloc ().init_string ("early");
|
||||
|
||||
try {
|
||||
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, io_cancellable);
|
||||
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, null, io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
@@ -591,7 +596,7 @@ namespace Frida.Inject {
|
||||
private async TerminalMode query_terminal_mode () {
|
||||
Json.Node mode_value;
|
||||
try {
|
||||
mode_value = yield rpc_client.call ("getFridaTerminalMode", new Json.Node[] {}, io_cancellable);
|
||||
mode_value = yield rpc_client.call ("getFridaTerminalMode", new Json.Node[] {}, null, io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
return COOKED;
|
||||
}
|
||||
@@ -599,7 +604,14 @@ namespace Frida.Inject {
|
||||
if (mode_value.get_value_type () != typeof (string))
|
||||
return COOKED;
|
||||
|
||||
return (mode_value.get_string () == "raw") ? TerminalMode.RAW : TerminalMode.COOKED;
|
||||
switch (mode_value.get_string ()) {
|
||||
case "raw":
|
||||
return TerminalMode.RAW;
|
||||
case "binary":
|
||||
return TerminalMode.BINARY;
|
||||
default:
|
||||
return TerminalMode.COOKED;
|
||||
}
|
||||
}
|
||||
|
||||
private void apply_terminal_mode (TerminalMode mode) throws Error {
|
||||
@@ -632,10 +644,10 @@ namespace Frida.Inject {
|
||||
}
|
||||
#endif
|
||||
|
||||
public void on_stdin (string data) {
|
||||
var data_value = new Json.Node.alloc ().init_string (data);
|
||||
public void on_stdin (string str, Bytes? data) {
|
||||
var str_value = new Json.Node.alloc ().init_string (str);
|
||||
|
||||
rpc_client.call.begin ("onFridaStdin", new Json.Node[] { data_value }, io_cancellable);
|
||||
rpc_client.call.begin ("onFridaStdin", new Json.Node[] { str_value }, data, io_cancellable);
|
||||
}
|
||||
|
||||
private void on_script_file_changed (File file, File? other_file, FileMonitorEvent event_type) {
|
||||
@@ -675,7 +687,7 @@ namespace Frida.Inject {
|
||||
handled = try_handle_log_message (message);
|
||||
break;
|
||||
case "send":
|
||||
handled = try_handle_stdout_message (message);
|
||||
handled = try_handle_stdout_message (message, data);
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
@@ -722,20 +734,19 @@ namespace Frida.Inject {
|
||||
*
|
||||
* {"type":"send","payload":["frida:stdout","DATA"]}
|
||||
*/
|
||||
private bool try_handle_stdout_message (Json.Object message) {
|
||||
private bool try_handle_stdout_message (Json.Object message, Bytes? data) {
|
||||
var payload = message.get_member ("payload");
|
||||
if (payload.get_node_type () != Json.NodeType.ARRAY)
|
||||
return false;
|
||||
|
||||
var tuple = payload.get_array ();
|
||||
if (tuple.get_length () != 2)
|
||||
var tuple_len = tuple.get_length ();
|
||||
if (tuple_len == 0)
|
||||
return false;
|
||||
|
||||
var first_element = tuple.get_element (0);
|
||||
if (first_element.get_value_type () != typeof (string))
|
||||
var type = tuple.get_element (0).get_string ();
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
var type = first_element.get_string ();
|
||||
switch (type) {
|
||||
case "frida:stdout":
|
||||
case "frida:stderr":
|
||||
@@ -744,31 +755,49 @@ namespace Frida.Inject {
|
||||
return false;
|
||||
}
|
||||
|
||||
var second_element = tuple.get_element (1);
|
||||
if (second_element.get_value_type () != typeof (string))
|
||||
return false;
|
||||
var str = second_element.get_string ();
|
||||
|
||||
switch (type) {
|
||||
case "frida:stdout":
|
||||
stdout.write (str.data);
|
||||
stdout.flush ();
|
||||
return true;
|
||||
case "frida:stderr":
|
||||
stderr.write (str.data);
|
||||
return true;
|
||||
default:
|
||||
if (tuple_len >= 2) {
|
||||
var str = tuple.get_element (1).get_string ();
|
||||
if (str == null)
|
||||
return false;
|
||||
|
||||
switch (type) {
|
||||
case "frida:stdout":
|
||||
stdout.write (str.data);
|
||||
stdout.flush ();
|
||||
break;
|
||||
case "frida:stderr":
|
||||
stderr.write (str.data);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (data != null) {
|
||||
switch (type) {
|
||||
case "frida:stdout":
|
||||
stdout.write (data.get_data ());
|
||||
stdout.flush ();
|
||||
break;
|
||||
case "frida:stderr":
|
||||
stderr.write (data.get_data ());
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
|
||||
script.post (json, null);
|
||||
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
|
||||
script.post (json, data);
|
||||
}
|
||||
}
|
||||
|
||||
private enum TerminalMode {
|
||||
COOKED,
|
||||
RAW
|
||||
RAW,
|
||||
BINARY
|
||||
}
|
||||
}
|
||||
+135
-9
@@ -428,21 +428,28 @@ namespace Frida {
|
||||
return *((double *) &bits);
|
||||
}
|
||||
|
||||
public string read_string (size_t offset) {
|
||||
public string read_string (size_t offset) throws Error {
|
||||
string * start = (string *) get_pointer (offset, sizeof (char));
|
||||
size_t max_length = size - offset;
|
||||
string * val = (string *) get_pointer (offset, sizeof (char));
|
||||
string * end = memchr (val, 0, max_length);
|
||||
assert (end != null);
|
||||
size_t size = end - val;
|
||||
return val->substring (0, (long) size);
|
||||
string * end = memchr (start, 0, max_length);
|
||||
if (end == null)
|
||||
throw new Error.PROTOCOL ("Missing null character");
|
||||
size_t size = end - start;
|
||||
string val = start->substring (0, (long) size);
|
||||
if (!val.validate ())
|
||||
throw new Error.PROTOCOL ("Invalid UTF-8 string");
|
||||
return val;
|
||||
}
|
||||
|
||||
[CCode (cname = "memchr", cheader_filename = "string.h")]
|
||||
private extern static string * memchr (string * s, int c, size_t n);
|
||||
|
||||
public string read_fixed_string (size_t offset, size_t size) {
|
||||
string * val = (string *) get_pointer (offset, size);
|
||||
return val->substring (0, (long) size);
|
||||
public string read_fixed_string (size_t offset, size_t size) throws Error {
|
||||
string * start = (string *) get_pointer (offset, size);
|
||||
string val = start->substring (0, (long) size);
|
||||
if (!val.validate ())
|
||||
throw new Error.PROTOCOL ("Invalid UTF-8 string");
|
||||
return val;
|
||||
}
|
||||
|
||||
public unowned Buffer write_string (size_t offset, string val) {
|
||||
@@ -458,4 +465,123 @@ namespace Frida {
|
||||
return data + offset;
|
||||
}
|
||||
}
|
||||
|
||||
public class BufferReader {
|
||||
public size_t available {
|
||||
get {
|
||||
return buffer.bytes.get_size () - offset;
|
||||
}
|
||||
}
|
||||
|
||||
private Buffer buffer;
|
||||
private size_t offset = 0;
|
||||
|
||||
public BufferReader (Buffer buf) {
|
||||
buffer = buf;
|
||||
}
|
||||
|
||||
public uint64 read_pointer (size_t offset) throws Error {
|
||||
var pointer_size = buffer.pointer_size;
|
||||
check_available (pointer_size);
|
||||
var ptr = buffer.read_pointer (offset);
|
||||
offset += pointer_size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
public int8 read_int8 () throws Error {
|
||||
check_available (sizeof (int8));
|
||||
var val = buffer.read_int8 (offset);
|
||||
offset += sizeof (int8);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint8 read_uint8 () throws Error {
|
||||
check_available (sizeof (uint8));
|
||||
var val = buffer.read_uint8 (offset);
|
||||
offset += sizeof (uint8);
|
||||
return val;
|
||||
}
|
||||
|
||||
public int16 read_int16 () throws Error {
|
||||
check_available (sizeof (int16));
|
||||
var val = buffer.read_int16 (offset);
|
||||
offset += sizeof (int16);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint16 read_uint16 () throws Error {
|
||||
check_available (sizeof (uint16));
|
||||
var val = buffer.read_uint16 (offset);
|
||||
offset += sizeof (uint16);
|
||||
return val;
|
||||
}
|
||||
|
||||
public int32 read_int32 () throws Error {
|
||||
check_available (sizeof (int32));
|
||||
var val = buffer.read_int32 (offset);
|
||||
offset += sizeof (int32);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint32 read_uint32 () throws Error {
|
||||
check_available (sizeof (uint32));
|
||||
var val = buffer.read_uint32 (offset);
|
||||
offset += sizeof (uint32);
|
||||
return val;
|
||||
}
|
||||
|
||||
public int64 read_int64 () throws Error {
|
||||
check_available (sizeof (int64));
|
||||
var val = buffer.read_int64 (offset);
|
||||
offset += sizeof (int64);
|
||||
return val;
|
||||
}
|
||||
|
||||
public uint64 read_uint64 () throws Error {
|
||||
check_available (sizeof (uint64));
|
||||
var val = buffer.read_uint64 (offset);
|
||||
offset += sizeof (uint64);
|
||||
return val;
|
||||
}
|
||||
|
||||
public float read_float () throws Error {
|
||||
check_available (sizeof (float));
|
||||
var val = buffer.read_float (offset);
|
||||
offset += sizeof (float);
|
||||
return val;
|
||||
}
|
||||
|
||||
public double read_double () throws Error {
|
||||
check_available (sizeof (double));
|
||||
var val = buffer.read_double (offset);
|
||||
offset += sizeof (double);
|
||||
return val;
|
||||
}
|
||||
|
||||
public string read_string () throws Error {
|
||||
check_available (1);
|
||||
var val = buffer.read_string (offset);
|
||||
offset += val.length + 1;
|
||||
return val;
|
||||
}
|
||||
|
||||
public string read_fixed_string (size_t size) throws Error {
|
||||
check_available (size);
|
||||
var val = buffer.read_fixed_string (offset, size);
|
||||
offset += size;
|
||||
return val;
|
||||
}
|
||||
|
||||
public Bytes read_bytes (size_t size) throws Error {
|
||||
check_available (size);
|
||||
var val = buffer.bytes[offset:offset + size];
|
||||
offset += size;
|
||||
return val;
|
||||
}
|
||||
|
||||
private void check_available (size_t n) throws Error {
|
||||
if (available < n)
|
||||
throw new Error.PROTOCOL ("Malformed buffer: truncated");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace Frida {
|
||||
public class CFArray<T> {
|
||||
private CoreFoundation.Array handle;
|
||||
|
||||
public static CFArray<T> wrap<T> (void * handle) {
|
||||
return new CFArray<T> ((CoreFoundation.Array) handle);
|
||||
}
|
||||
|
||||
private CFArray (CoreFoundation.Array handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public Iterator<T> iterator () {
|
||||
return new Iterator<T> (handle);
|
||||
}
|
||||
|
||||
public class Iterator<T> {
|
||||
public CoreFoundation.Array handle;
|
||||
private CoreFoundation.Index i = 0;
|
||||
private CoreFoundation.Index length;
|
||||
|
||||
internal Iterator (CoreFoundation.Array arr) {
|
||||
handle = arr;
|
||||
length = arr.length;
|
||||
}
|
||||
|
||||
public unowned T? next_value () {
|
||||
if (i == length)
|
||||
return null;
|
||||
return handle[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,10 @@ if host_os == 'macos'
|
||||
]
|
||||
endif
|
||||
|
||||
if host_os_family == 'darwin'
|
||||
base_sources += 'corefoundation.vala'
|
||||
endif
|
||||
|
||||
extra_deps = []
|
||||
if host_os_family != 'windows'
|
||||
extra_vala_args += '--pkg=gio-unix-2.0'
|
||||
@@ -33,6 +37,7 @@ if nice_dep.found()
|
||||
endif
|
||||
if host_os_family == 'darwin'
|
||||
base_vala_args += [
|
||||
'--pkg=corefoundation',
|
||||
'--pkg=darwin-gcd',
|
||||
'--pkg=darwin-xpc',
|
||||
]
|
||||
|
||||
+2
-15
@@ -774,25 +774,12 @@ namespace Frida {
|
||||
protected extern void _handle_transport_packet (uint8[] data);
|
||||
|
||||
protected int _emit_transport_packet (uint8[] data) {
|
||||
var v = OutputVector ();
|
||||
v.buffer = data;
|
||||
v.size = data.length;
|
||||
|
||||
OutputVector[] vectors = { v };
|
||||
|
||||
var m = OutputMessage ();
|
||||
m.vectors = vectors;
|
||||
m.num_vectors = vectors.length;
|
||||
|
||||
OutputMessage[] messages = { m };
|
||||
|
||||
try {
|
||||
transport_socket.send_messages (messages, 0, 0, io_cancellable);
|
||||
Udp.send (data, transport_socket, io_cancellable);
|
||||
return 0;
|
||||
} catch (GLib.Error e) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected void _on_sctp_socket_events_changed () {
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@ namespace Frida {
|
||||
Object (peer: peer);
|
||||
}
|
||||
|
||||
public async Json.Node call (string method, Json.Node[] args, Cancellable? cancellable) throws Error, IOError {
|
||||
public async Json.Node call (string method, Json.Node[] args, Bytes? data, Cancellable? cancellable) throws Error, IOError {
|
||||
string request_id = Uuid.string_random ();
|
||||
|
||||
var request = new Json.Builder ();
|
||||
@@ -39,7 +39,7 @@ namespace Frida {
|
||||
pending_responses[request_id] = pending;
|
||||
|
||||
try {
|
||||
yield peer.post_rpc_message (raw_request, cancellable);
|
||||
yield peer.post_rpc_message (raw_request, data, cancellable);
|
||||
} catch (Error e) {
|
||||
if (pending_responses.unset (request_id))
|
||||
pending.complete_with_error (e);
|
||||
@@ -158,6 +158,6 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public interface RpcPeer : Object {
|
||||
public abstract async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
}
|
||||
+563
-353
File diff suppressed because it is too large.
Load diff
+3
-4
@@ -13,7 +13,7 @@ namespace Frida {
|
||||
public abstract unowned ObjectBuilder add_uint64_value (uint64 val);
|
||||
public abstract unowned ObjectBuilder add_data_value (Bytes val);
|
||||
public abstract unowned ObjectBuilder add_string_value (string val);
|
||||
public abstract unowned ObjectBuilder add_uuid_value (string val);
|
||||
public abstract unowned ObjectBuilder add_uuid_value (uint8[] val);
|
||||
public abstract unowned ObjectBuilder add_raw_value (Bytes val);
|
||||
|
||||
public abstract Bytes build ();
|
||||
@@ -239,9 +239,8 @@ namespace Frida {
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_uuid_value (string val) {
|
||||
builder.add_string_value (val);
|
||||
return this;
|
||||
public unowned ObjectBuilder add_uuid_value (uint8[] val) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_raw_value (Bytes val) {
|
||||
|
||||
+27
-3
@@ -65,8 +65,17 @@ namespace Frida {
|
||||
});
|
||||
});
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
connection.cancel ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
|
||||
if (reply.type == Darwin.Xpc.Error.TYPE) {
|
||||
var e = (Darwin.Xpc.Error) reply;
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION));
|
||||
@@ -75,6 +84,10 @@ namespace Frida {
|
||||
return reply;
|
||||
}
|
||||
|
||||
public void post (Darwin.Xpc.Object message) throws Error, IOError {
|
||||
connection.send_message (message);
|
||||
}
|
||||
|
||||
private void change_state (State new_state) {
|
||||
_state = new_state;
|
||||
notify_property ("state");
|
||||
@@ -85,9 +98,16 @@ namespace Frida {
|
||||
if (obj.type == Darwin.Xpc.Error.TYPE) {
|
||||
var e = (Darwin.Xpc.Error) obj;
|
||||
close_reason = e.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION);
|
||||
if (state == CLOSING) {
|
||||
change_state (CLOSED);
|
||||
unref ();
|
||||
switch (state) {
|
||||
case OPEN:
|
||||
change_state (CLOSED);
|
||||
break;
|
||||
case CLOSING:
|
||||
change_state (CLOSED);
|
||||
unref ();
|
||||
break;
|
||||
case CLOSED:
|
||||
assert_not_reached ();
|
||||
}
|
||||
} else {
|
||||
message (obj);
|
||||
@@ -188,6 +208,10 @@ namespace Frida {
|
||||
return peek_scope ().get_object<Darwin.Xpc.String> (Darwin.Xpc.String.TYPE).get_string_ptr ();
|
||||
}
|
||||
|
||||
public unowned uint8[] get_uuid_value () throws Error {
|
||||
return peek_scope ().get_object<Darwin.Xpc.Uuid> (Darwin.Xpc.Uuid.TYPE).get_bytes ()[:16];
|
||||
}
|
||||
|
||||
public unowned string get_error_description () throws Error {
|
||||
var error = peek_scope ().get_object<Darwin.Xpc.Error> (Darwin.Xpc.Error.TYPE);
|
||||
return error.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION);
|
||||
|
||||
+67
-13
@@ -751,7 +751,7 @@ namespace Frida.Gadget {
|
||||
|
||||
string config_data;
|
||||
try {
|
||||
FileUtils.get_contents (config_path, out config_data);
|
||||
load_asset_text (config_path, out config_data);
|
||||
} catch (FileError e) {
|
||||
if (e is FileError.NOENT)
|
||||
return new Config ();
|
||||
@@ -1226,7 +1226,7 @@ namespace Frida.Gadget {
|
||||
private ScriptConfig load_config (string path) throws Error {
|
||||
string data;
|
||||
try {
|
||||
FileUtils.get_contents (path, out data);
|
||||
load_asset_text (path, out data);
|
||||
} catch (FileError e) {
|
||||
if (e is FileError.NOENT)
|
||||
return new ScriptConfig ();
|
||||
@@ -1349,9 +1349,9 @@ namespace Frida.Gadget {
|
||||
try {
|
||||
var path = this.path;
|
||||
|
||||
uint8[] contents;
|
||||
Bytes contents;
|
||||
try {
|
||||
FileUtils.get_data (path, out contents);
|
||||
load_asset_bytes (path, out contents);
|
||||
} catch (FileError e) {
|
||||
throw new Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
}
|
||||
@@ -1360,11 +1360,10 @@ namespace Frida.Gadget {
|
||||
options.name = Path.get_basename (path).split (".", 2)[0];
|
||||
|
||||
ScriptEngine.ScriptInstance instance;
|
||||
if (contents.length > 0 && contents[0] == QUICKJS_BYTECODE_MAGIC) {
|
||||
instance = yield engine.create_script (null, new Bytes (contents), options);
|
||||
} else {
|
||||
instance = yield engine.create_script ((string) contents, null, options);
|
||||
}
|
||||
if (contents.length > 0 && contents[0] == QUICKJS_BYTECODE_MAGIC)
|
||||
instance = yield engine.create_script (null, contents, options);
|
||||
else
|
||||
instance = yield engine.create_script ((string) contents.get_data (), null, options);
|
||||
|
||||
if (id.handle != 0)
|
||||
yield engine.destroy_script (id);
|
||||
@@ -1381,7 +1380,7 @@ namespace Frida.Gadget {
|
||||
var stage = new Json.Node.alloc ().init_string ((peek_state () == State.CREATED) ? "early" : "late");
|
||||
|
||||
try {
|
||||
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, null);
|
||||
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, null, null);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
@@ -1449,8 +1448,8 @@ namespace Frida.Gadget {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
|
||||
engine.post_to_script (id, json);
|
||||
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
|
||||
engine.post_to_script (id, json, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1497,7 +1496,8 @@ namespace Frida.Gadget {
|
||||
var endpoint_params = new EndpointParameters (interaction.address, interaction.port,
|
||||
parse_certificate (interaction.certificate, location), interaction.origin, auth_service, asset_root);
|
||||
|
||||
service = new WebService (endpoint_params, CONTROL, interaction.on_port_conflict);
|
||||
service = new WebService (endpoint_params, WebServiceFlavor.CONTROL, interaction.on_port_conflict,
|
||||
new TunnelInterfaceObserver ());
|
||||
service.incoming.connect (on_incoming_connection);
|
||||
yield service.start (io_cancellable);
|
||||
}
|
||||
@@ -2075,6 +2075,60 @@ namespace Frida.Gadget {
|
||||
}
|
||||
#endif
|
||||
|
||||
private void load_asset_text (string filename, out string text) throws FileError {
|
||||
Bytes raw_contents;
|
||||
load_asset_bytes (filename, out raw_contents);
|
||||
|
||||
unowned string str = (string) raw_contents.get_data ();
|
||||
if (!str.validate ())
|
||||
throw new FileError.FAILED ("%s: invalid UTF-8", filename);
|
||||
|
||||
text = str;
|
||||
}
|
||||
|
||||
private void load_asset_bytes (string filename, out Bytes bytes) throws FileError {
|
||||
#if ANDROID
|
||||
if (maybe_load_asset_bytes_from_apk (filename, out bytes))
|
||||
return;
|
||||
#endif
|
||||
|
||||
uint8[] data;
|
||||
FileUtils.get_data (filename, out data);
|
||||
bytes = new Bytes.take ((owned) data);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
private bool maybe_load_asset_bytes_from_apk (string filename, out Bytes contents) throws FileError {
|
||||
contents = null;
|
||||
|
||||
var tokens = filename.split ("!", 2);
|
||||
if (tokens.length != 2 || !tokens[0].has_suffix (".apk"))
|
||||
return false;
|
||||
unowned string apk_path = tokens[0];
|
||||
unowned string file_path = tokens[1];
|
||||
|
||||
var reader = Minizip.Reader.create ();
|
||||
try {
|
||||
if (reader.open_file (apk_path) != OK)
|
||||
throw new FileError.FAILED ("Unable to open APK");
|
||||
|
||||
if (reader.locate_entry (file_path[1:], true) != OK)
|
||||
throw new FileError.FAILED ("Unable to locate %s inside APK", file_path);
|
||||
|
||||
var size = reader.entry_save_buffer_length ();
|
||||
var data = new uint8[size + 1];
|
||||
if (reader.entry_save_buffer (data[:size]) != OK)
|
||||
throw new FileError.FAILED ("Unable to extract %s from APK", file_path);
|
||||
|
||||
contents = new Bytes.take ((owned) data);
|
||||
return true;
|
||||
} finally {
|
||||
reader.close ();
|
||||
Minizip.Reader.destroy (ref reader);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private Json.Node make_empty_json_object () {
|
||||
return new Json.Node.alloc ().init_object (new Json.Object ());
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ if host_os_family == 'darwin'
|
||||
extra_link_args += '-Wl,-framework,Foundation'
|
||||
endif
|
||||
|
||||
if host_os == 'android'
|
||||
extra_vala_args += '--pkg=minizip'
|
||||
platform_deps += minizip_dep
|
||||
endif
|
||||
|
||||
if host_os_family == 'windows'
|
||||
if host_toolchain != 'microsoft'
|
||||
symfile = 'frida-gadget.symbols'
|
||||
@@ -60,7 +65,7 @@ raw_gadget = shared_library('frida-gadget-raw', gadget_sources,
|
||||
link_depends: extra_link_depends,
|
||||
vs_module_defs: 'frida-gadget.def',
|
||||
override_options: ['b_asneeded=true'],
|
||||
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep, gumjs_dep, base_dep, tls_provider_dep, payload_dep] + platform_deps,
|
||||
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep, gumjs_dep, base_dep, netif_dep, tls_provider_dep, payload_dep] + platform_deps,
|
||||
)
|
||||
|
||||
operations = [
|
||||
|
||||
@@ -2,6 +2,7 @@ if host_os == 'android'
|
||||
subdir('selinux')
|
||||
endif
|
||||
subdir('base')
|
||||
subdir('netif')
|
||||
subdir('pipe')
|
||||
subdir('payload')
|
||||
if have_local_backend
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
netif_sources = [
|
||||
'tunnel-interface-observer.vala',
|
||||
]
|
||||
|
||||
extra_vala_args = []
|
||||
extra_link_args = []
|
||||
|
||||
if host_os in ['ios', 'tvos']
|
||||
extra_vala_args += [
|
||||
'--pkg=corefoundation',
|
||||
'--pkg=darwin-gcd',
|
||||
'--pkg=darwin-systemconfiguration',
|
||||
]
|
||||
extra_link_args += '-Wl,-framework,SystemConfiguration'
|
||||
endif
|
||||
|
||||
netif = static_library('frida-netif', netif_sources,
|
||||
c_args: frida_component_cflags,
|
||||
vala_args: [base_vala_args, extra_vala_args],
|
||||
dependencies: [base_dep, gee_dep],
|
||||
)
|
||||
|
||||
netif_dep = declare_dependency(
|
||||
include_directories: include_directories('.'),
|
||||
link_args: extra_link_args,
|
||||
link_with: netif,
|
||||
dependencies: base_dep,
|
||||
)
|
||||
@@ -0,0 +1,99 @@
|
||||
public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
|
||||
#if IOS || TVOS
|
||||
private Gee.Map<string, DynamicInterface> interfaces = new Gee.HashMap<string, DynamicInterface> ();
|
||||
|
||||
private Darwin.SystemConfiguration.DynamicStore? store;
|
||||
private Darwin.GCD.DispatchQueue event_queue =
|
||||
new Darwin.GCD.DispatchQueue ("re.frida.endpoint-enumerator", Darwin.GCD.DispatchQueueAttr.SERIAL);
|
||||
|
||||
private MainContext? main_context;
|
||||
|
||||
public override void dispose () {
|
||||
if (store != null) {
|
||||
store.set_dispatch_queue (null);
|
||||
store = null;
|
||||
ref ();
|
||||
event_queue.dispatch_async (do_dispose);
|
||||
}
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
private void do_dispose () {
|
||||
unref ();
|
||||
}
|
||||
|
||||
public void start () {
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
Darwin.SystemConfiguration.DynamicStoreContext context = { 0, };
|
||||
context.info = this;
|
||||
store = new Darwin.SystemConfiguration.DynamicStore (null, CoreFoundation.String.make ("Frida"),
|
||||
on_interfaces_changed_wrapper, context);
|
||||
|
||||
var pattern = CoreFoundation.String.make ("State:/Network/Interface/utun.*/IPv6");
|
||||
var patterns = new CoreFoundation.Array (null, ((CoreFoundation.Type[]) &pattern)[:1]);
|
||||
store.set_notification_keys (null, patterns);
|
||||
|
||||
store.set_dispatch_queue (event_queue);
|
||||
|
||||
var initial_keys = store.copy_key_list (pattern);
|
||||
if (initial_keys != null)
|
||||
handle_interface_changes (initial_keys);
|
||||
}
|
||||
|
||||
private static void on_interfaces_changed_wrapper (Darwin.SystemConfiguration.DynamicStore store,
|
||||
CoreFoundation.Array changed_keys, void * info) {
|
||||
unowned TunnelInterfaceObserver enumerator = (TunnelInterfaceObserver) info;
|
||||
enumerator.on_interfaces_changed (changed_keys);
|
||||
}
|
||||
|
||||
private void on_interfaces_changed (CoreFoundation.Array changed_keys) {
|
||||
schedule_on_frida_thread (() => {
|
||||
if (store != null)
|
||||
handle_interface_changes (changed_keys);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
private void handle_interface_changes (CoreFoundation.Array changed_keys) {
|
||||
var addresses_str = CoreFoundation.String.make ("Addresses");
|
||||
|
||||
foreach (var key in CFArray.wrap<CoreFoundation.String> (changed_keys)) {
|
||||
string name = key.to_string ().split ("/")[3];
|
||||
|
||||
var val = (CoreFoundation.Dictionary) store.copy_value (key);
|
||||
if (val != null) {
|
||||
InetAddress? address = null;
|
||||
foreach (var raw_address in CFArray.wrap<CoreFoundation.String> (val[addresses_str])) {
|
||||
var str = raw_address.to_string ();
|
||||
bool is_reserved_ipv6_range = str.has_prefix ("fc") || str.has_prefix ("fd");
|
||||
bool is_tunnel = is_reserved_ipv6_range && str.has_suffix ("::1");
|
||||
if (is_tunnel) {
|
||||
address = new InetAddress.from_string (str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (address != null && !interfaces.has_key (name)) {
|
||||
var iface = new DynamicInterface (name, address);
|
||||
interfaces[name] = iface;
|
||||
interface_attached (iface);
|
||||
}
|
||||
} else {
|
||||
DynamicInterface iface;
|
||||
if (interfaces.unset (name, out iface))
|
||||
interface_detached (iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void schedule_on_frida_thread (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
source.attach (main_context);
|
||||
}
|
||||
#else
|
||||
public void start () {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -25,6 +25,24 @@ frida_run_atexit_handlers (void)
|
||||
{
|
||||
}
|
||||
|
||||
# ifdef HAVE_ASAN
|
||||
|
||||
__attribute__ ((constructor)) static void
|
||||
frida_init_memory (void)
|
||||
{
|
||||
asm volatile ("");
|
||||
}
|
||||
|
||||
# ifndef HAVE_DARWIN
|
||||
__attribute__ ((destructor)) static void
|
||||
frida_deinit_memory (void)
|
||||
{
|
||||
asm volatile ("");
|
||||
}
|
||||
# endif
|
||||
|
||||
# endif
|
||||
|
||||
#else
|
||||
|
||||
#define FRIDA_SHIM_LOCK() gum_spinlock_acquire (&frida_shim_lock)
|
||||
|
||||
@@ -420,7 +420,7 @@ namespace Frida {
|
||||
var reason_value = new Json.Node.alloc ().init_string (reason.to_nick ());
|
||||
|
||||
try {
|
||||
yield rpc_client.call ("dispose", new Json.Node[] { reason_value }, null);
|
||||
yield rpc_client.call ("dispose", new Json.Node[] { reason_value }, null, null);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
@@ -467,10 +467,10 @@ namespace Frida {
|
||||
this.debug_message (message);
|
||||
}
|
||||
|
||||
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
|
||||
if (script == null)
|
||||
throw new Error.INVALID_OPERATION ("Script is destroyed");
|
||||
script.post (json);
|
||||
script.post (json, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "pipe-glue.h"
|
||||
|
||||
#include "frida-tvos.h"
|
||||
|
||||
#include "pipe-glue.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
+75
-12
@@ -92,6 +92,12 @@ else
|
||||
host_arch = host_machine.cpu_family()
|
||||
host_abi = host_arch
|
||||
endif
|
||||
if cc.sizeof('void *') == 8
|
||||
host_cpu_mode = '64'
|
||||
else
|
||||
host_cpu_mode = '32'
|
||||
endif
|
||||
host_docks_mobile_devices = host_os in ['windows', 'macos', 'linux'] and host_cpu_mode == '64'
|
||||
gumjs_archs = {
|
||||
'x86': 'ia32',
|
||||
'x86_64': 'x64',
|
||||
@@ -156,12 +162,6 @@ if host_os_family == 'darwin'
|
||||
endif
|
||||
endif
|
||||
|
||||
if cc.sizeof('void *') == 8
|
||||
host_cpu_mode = '64'
|
||||
else
|
||||
host_cpu_mode = '32'
|
||||
endif
|
||||
|
||||
if host_os_family == 'darwin' and host_arch == 'arm64'
|
||||
host_is_modern = host_abi == 'arm64e'
|
||||
else
|
||||
@@ -347,15 +347,35 @@ else
|
||||
gio_unix_dep = dependency('gio-unix-2.0')
|
||||
endif
|
||||
|
||||
if host_os == 'android'
|
||||
minizip_dep = dependency('minizip', required: false, default_options: [
|
||||
'zlib=enabled',
|
||||
'lzma=disabled',
|
||||
])
|
||||
endif
|
||||
|
||||
have_local_backend = get_option('local_backend').allowed()
|
||||
have_fruity_backend = get_option('fruity_backend').allowed()
|
||||
have_droidy_backend = get_option('droidy_backend').allowed()
|
||||
have_fruity_backend = get_option('fruity_backend') \
|
||||
.disable_auto_if(not host_docks_mobile_devices) \
|
||||
.allowed()
|
||||
have_droidy_backend = get_option('droidy_backend') \
|
||||
.disable_auto_if(not host_docks_mobile_devices) \
|
||||
.allowed()
|
||||
have_socket_backend = get_option('socket_backend').allowed()
|
||||
have_barebone_backend = quickjs_dep.found()
|
||||
have_barebone_backend = get_option('barebone_backend') \
|
||||
.disable_if(not quickjs_dep.found()) \
|
||||
.disable_auto_if(not host_docks_mobile_devices) \
|
||||
.allowed()
|
||||
have_compiler_backend = get_option('compiler_backend') \
|
||||
.disable_if(not have_local_backend, error_message: 'compiler backend requires the local backend for script execution') \
|
||||
.disable_auto_if(host_os == 'watchos') \
|
||||
.allowed()
|
||||
if get_option('compiler_snapshot').auto()
|
||||
# Generating our snapshot with V8's CPU simulator takes a very long time.
|
||||
generate_compiler_snapshot = meson.can_run_host_binaries()
|
||||
else
|
||||
generate_compiler_snapshot = get_option('compiler_snapshot').enabled()
|
||||
endif
|
||||
|
||||
build_gadget = get_option('gadget') \
|
||||
.disable_auto_if(meson.is_subproject()) \
|
||||
@@ -450,10 +470,28 @@ openssl_dep = dependency('openssl',
|
||||
)
|
||||
|
||||
if have_fruity_backend
|
||||
foreach dep : ['libnghttp2', 'libngtcp2', 'libngtcp2_crypto_quictls', 'lwip']
|
||||
foreach dep : ['libnghttp2', 'libngtcp2', 'libngtcp2_crypto_quictls', 'libusb-1.0']
|
||||
backend_deps_private += dependency(dep)
|
||||
backend_reqs_private += dep
|
||||
endforeach
|
||||
|
||||
lwip_options = [
|
||||
'ipv4=disabled',
|
||||
'ipv6=enabled',
|
||||
'dns=disabled',
|
||||
'arp=disabled',
|
||||
'ethernet=enabled',
|
||||
'tcp_mss=1360',
|
||||
'tcp_snd_buf=65535',
|
||||
'tcp_wnd=65535',
|
||||
]
|
||||
if optimize_for_prod
|
||||
lwip_options += 'lwip_debug=disabled'
|
||||
else
|
||||
lwip_options += 'lwip_debug=enabled'
|
||||
endif
|
||||
backend_deps_private += dependency('lwip', default_options: lwip_options)
|
||||
backend_reqs_private += 'lwip'
|
||||
endif
|
||||
|
||||
if quickjs_dep.found()
|
||||
@@ -528,7 +566,7 @@ if host_os_family == 'darwin'
|
||||
backend_libs_private += ['-Wl,-framework,Foundation', '-lbsm']
|
||||
endif
|
||||
if host_os == 'macos'
|
||||
backend_libs_private += ['-Wl,-framework,AppKit', '-Wl,-framework,IOKit']
|
||||
backend_libs_private += ['-Wl,-framework,AppKit']
|
||||
endif
|
||||
if host_os in ['ios', 'tvos']
|
||||
backend_libs_private += ['-Wl,-framework,CoreGraphics', '-Wl,-framework,UIKit']
|
||||
@@ -655,8 +693,9 @@ if cc.get_id() == 'gcc' and cc.compiles('#if __GNUC__ >= 5\n#error NOTNEEDED\n#e
|
||||
add_project_arguments('-w', language: 'c')
|
||||
endif
|
||||
|
||||
vapidir = meson.current_source_dir() / 'vapi'
|
||||
vala_flags += [
|
||||
'--vapidir=' + meson.current_source_dir() / 'vapi',
|
||||
'--vapidir=' + vapidir,
|
||||
'--pkg', 'config',
|
||||
]
|
||||
|
||||
@@ -669,11 +708,13 @@ if host_abi != host_arch
|
||||
vala_flags += ['--define=' + host_abi.to_upper()]
|
||||
endif
|
||||
add_project_arguments(vala_flags, language: 'vala')
|
||||
add_project_arguments('-I' + vapidir, language: 'c')
|
||||
|
||||
native_vala_flags = [
|
||||
'--vapidir=' + meson.current_source_dir() / 'vapi',
|
||||
]
|
||||
add_project_arguments(native_vala_flags, language: 'vala', native: true)
|
||||
add_project_arguments('-I' + vapidir, language: 'c', native: true)
|
||||
|
||||
subdir('compat')
|
||||
subdir('tools')
|
||||
@@ -692,9 +733,31 @@ if build_tests
|
||||
subdir('tests')
|
||||
endif
|
||||
|
||||
summary(
|
||||
{
|
||||
'local': have_local_backend,
|
||||
'fruity': have_fruity_backend,
|
||||
'droidy': have_droidy_backend,
|
||||
'socket': have_socket_backend,
|
||||
'barebone': have_barebone_backend,
|
||||
'compiler': have_compiler_backend,
|
||||
},
|
||||
section: 'Backends',
|
||||
bool_yn: true,
|
||||
)
|
||||
summary(
|
||||
{
|
||||
'compat': compat_summary,
|
||||
'assets': get_option('assets'),
|
||||
'mapper': have_mapper,
|
||||
'connectivity': tls_provider_dep.found(),
|
||||
'compiler_snapshot': generate_compiler_snapshot,
|
||||
'gadget': build_gadget,
|
||||
'server': build_server,
|
||||
'portal': build_portal,
|
||||
'inject': build_inject,
|
||||
'tests': build_tests,
|
||||
},
|
||||
section: 'Features',
|
||||
bool_yn: true,
|
||||
)
|
||||
+14
-2
@@ -68,13 +68,25 @@ option('compat',
|
||||
option('helper_modern',
|
||||
type: 'string',
|
||||
value: 'auto',
|
||||
description: 'Prebuilt frida-helper to embed for use on modern systems'
|
||||
description: 'Prebuilt frida-helper to embed for supporting modern targets'
|
||||
)
|
||||
|
||||
option('helper_legacy',
|
||||
type: 'string',
|
||||
value: 'auto',
|
||||
description: 'Prebuilt frida-helper to embed for use on legacy systems'
|
||||
description: 'Prebuilt frida-helper to embed for supporting legacy targets'
|
||||
)
|
||||
|
||||
option('helper_emulated_modern',
|
||||
type: 'string',
|
||||
value: 'auto',
|
||||
description: 'Prebuilt frida-helper to embed for supporting modern emulated targets'
|
||||
)
|
||||
|
||||
option('helper_emulated_legacy',
|
||||
type: 'string',
|
||||
value: 'auto',
|
||||
description: 'Prebuilt frida-helper to embed for supporting legacy emulated targets'
|
||||
)
|
||||
|
||||
option('agent_modern',
|
||||
|
||||
+1
-1
Submodule releng updated: b0e46f7b69...8870e5861b.
+4
-2
@@ -327,8 +327,10 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
|
||||
if stripped_line == "}":
|
||||
ignoring = False
|
||||
else:
|
||||
if stripped_line.startswith("public abstract") or stripped_line.startswith("public class Promise") \
|
||||
or stripped_line.startswith("public interface Future"):
|
||||
if stripped_line.startswith("public abstract") \
|
||||
or stripped_line.startswith("public class Promise") \
|
||||
or stripped_line.startswith("public interface Future") \
|
||||
or stripped_line.startswith("public class CF"):
|
||||
ignoring = True
|
||||
elif stripped_line.startswith("public enum") or stripped_line.startswith("public errordomain"):
|
||||
name = re.match(r"^public (?:enum|errordomain) (\w+) ", stripped_line).group(1)
|
||||
|
||||
@@ -420,7 +420,9 @@ namespace Frida {
|
||||
|
||||
public void post (string json, Bytes? data) {
|
||||
var json_val = ctx.make_string (json);
|
||||
invoke_void (dispatch_message_func, { json_val }, runtime_obj);
|
||||
var data_val = (data != null) ? ctx.make_array_buffer (data.get_data ()) : QuickJS.Null;
|
||||
invoke_void (dispatch_message_func, { json_val, data_val }, runtime_obj);
|
||||
ctx.free_value (data_val);
|
||||
ctx.free_value (json_val);
|
||||
|
||||
perform_pending_io ();
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace Frida {
|
||||
new Json.Node.alloc ().init_string (entrypoint),
|
||||
new Json.Node.alloc ().init_string (opts.source_maps.to_nick ()),
|
||||
new Json.Node.alloc ().init_string (opts.compression.to_nick ()),
|
||||
}, cancellable);
|
||||
}, null, cancellable);
|
||||
return bundle.get_string ();
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ namespace Frida {
|
||||
new Json.Node.alloc ().init_string (entrypoint),
|
||||
new Json.Node.alloc ().init_string (opts.source_maps.to_nick ()),
|
||||
new Json.Node.alloc ().init_string (opts.compression.to_nick ()),
|
||||
}, cancellable);
|
||||
}, null, cancellable);
|
||||
}
|
||||
|
||||
private static string compute_project_root (string entrypoint, CompilerOptions options) {
|
||||
|
||||
@@ -3,13 +3,7 @@ compiler_sources = files(
|
||||
)
|
||||
|
||||
if have_compiler_backend
|
||||
if get_option('compiler_snapshot').auto()
|
||||
# Generating our snapshot with V8's CPU simulator takes a very long time.
|
||||
use_snapshot = meson.can_run_host_binaries()
|
||||
else
|
||||
use_snapshot = get_option('compiler_snapshot').enabled()
|
||||
endif
|
||||
compiler_mksnapshot = use_snapshot ? v8_mksnapshot : ''
|
||||
compiler_mksnapshot = generate_compiler_snapshot ? v8_mksnapshot : ''
|
||||
|
||||
compiler_agent = custom_target('frida-compiler-agent',
|
||||
input: [
|
||||
|
||||
Generated
+9
-8
@@ -17,7 +17,7 @@
|
||||
"@rollup/plugin-node-resolve": "^14.1.0",
|
||||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"@rollup/plugin-typescript": "^8.3.4",
|
||||
"@types/frida-gum": "18.7.0",
|
||||
"@types/frida-gum": "18.7.1",
|
||||
"@types/glob": "^8.0.0",
|
||||
"@types/node": "18.7.23",
|
||||
"glob": "^8.0.3",
|
||||
@@ -468,10 +468,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/frida-gum": {
|
||||
"version": "18.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.0.tgz",
|
||||
"integrity": "sha512-HhBomXE23fLDAWXEKi3BjJLrlH9wAv9IEQNfO/PaYHQNNbh0Bi06gx6JbXTspVpbqlbVqkWAuU7n6HaS9B6yXA==",
|
||||
"dev": true
|
||||
"version": "18.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.1.tgz",
|
||||
"integrity": "sha512-++7xogrG/jWdgC2gEeK8Q2tVTcycWXsFHhGaEa/TfY8u11UTUsn9dF+nr3Lo1rUnFGZssKDhqV4vsHOHgsWiLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/glob": {
|
||||
"version": "8.0.0",
|
||||
@@ -1465,9 +1466,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/frida-gum": {
|
||||
"version": "18.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.0.tgz",
|
||||
"integrity": "sha512-HhBomXE23fLDAWXEKi3BjJLrlH9wAv9IEQNfO/PaYHQNNbh0Bi06gx6JbXTspVpbqlbVqkWAuU7n6HaS9B6yXA==",
|
||||
"version": "18.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.1.tgz",
|
||||
"integrity": "sha512-++7xogrG/jWdgC2gEeK8Q2tVTcycWXsFHhGaEa/TfY8u11UTUsn9dF+nr3Lo1rUnFGZssKDhqV4vsHOHgsWiLQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/glob": {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"@rollup/plugin-node-resolve": "^14.1.0",
|
||||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"@rollup/plugin-typescript": "^8.3.4",
|
||||
"@types/frida-gum": "18.7.0",
|
||||
"@types/frida-gum": "18.7.1",
|
||||
"@types/glob": "^8.0.0",
|
||||
"@types/node": "18.7.23",
|
||||
"glob": "^8.0.3",
|
||||
|
||||
+272
-151
@@ -19,21 +19,18 @@ namespace Frida {
|
||||
private State state = STOPPED;
|
||||
|
||||
private WebService service;
|
||||
private Gee.Map<DBusConnection, Peer> peers = new Gee.HashMap<DBusConnection, Peer> ();
|
||||
private ConnectionHandler main_handler;
|
||||
private Gee.Map<string, ConnectionHandler> dynamic_interface_handlers = new Gee.HashMap<string, ConnectionHandler> ();
|
||||
|
||||
private Gee.Set<ControlChannel> spawn_gaters = new Gee.HashSet<ControlChannel> ();
|
||||
private Gee.Map<uint, PendingSpawn> pending_spawn = new Gee.HashMap<uint, PendingSpawn> ();
|
||||
private Gee.Map<AgentSessionId?, AgentSessionEntry> sessions =
|
||||
new Gee.HashMap<AgentSessionId?, AgentSessionEntry> (AgentSessionId.hash, AgentSessionId.equal);
|
||||
|
||||
private SocketService broker_service = new SocketService ();
|
||||
#if !WINDOWS
|
||||
private uint16 broker_port = 0;
|
||||
#endif
|
||||
private Gee.Map<string, Transport> transports = new Gee.HashMap<string, Transport> ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
private MainContext? main_context;
|
||||
|
||||
private enum State {
|
||||
STOPPED,
|
||||
STARTING,
|
||||
@@ -89,10 +86,12 @@ namespace Frida {
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
host_session.uninjected.connect (notify_uninjected);
|
||||
|
||||
service = new WebService (endpoint_params, CONTROL);
|
||||
service.incoming.connect (on_server_connection);
|
||||
var iface_observer = new TunnelInterfaceObserver ();
|
||||
iface_observer.interface_detached.connect (on_interface_detached);
|
||||
|
||||
broker_service.incoming.connect (on_broker_service_connection);
|
||||
service = new WebService (endpoint_params, WebServiceFlavor.CONTROL, PortConflictBehavior.FAIL, iface_observer);
|
||||
|
||||
main_handler = new ConnectionHandler (this, null);
|
||||
}
|
||||
|
||||
public async void start (Cancellable? cancellable = null) throws Error, IOError {
|
||||
@@ -100,6 +99,10 @@ namespace Frida {
|
||||
throw new Error.INVALID_OPERATION ("Invalid operation");
|
||||
state = STARTING;
|
||||
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
service.incoming.connect (on_server_connection);
|
||||
|
||||
try {
|
||||
yield service.start (cancellable);
|
||||
|
||||
@@ -131,21 +134,17 @@ namespace Frida {
|
||||
throw new Error.INVALID_OPERATION ("Invalid operation");
|
||||
state = STOPPING;
|
||||
|
||||
broker_service.stop ();
|
||||
service.stop ();
|
||||
service.incoming.disconnect (on_server_connection);
|
||||
|
||||
io_cancellable.cancel ();
|
||||
|
||||
transports.clear ();
|
||||
service.stop ();
|
||||
|
||||
foreach (var peer in peers.values.to_array ()) {
|
||||
try {
|
||||
yield peer.close ();
|
||||
} catch (IOError e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
peers.clear ();
|
||||
foreach (var handler in dynamic_interface_handlers.values.to_array ())
|
||||
yield handler.close (cancellable);
|
||||
dynamic_interface_handlers.clear ();
|
||||
|
||||
yield main_handler.close (cancellable);
|
||||
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
if (base_host_session != null)
|
||||
@@ -175,7 +174,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private void on_server_connection (IOStream connection, SocketAddress remote_address) {
|
||||
private void on_server_connection (IOStream connection, SocketAddress remote_address, DynamicInterface? dynamic_iface) {
|
||||
#if IOS || TVOS
|
||||
/*
|
||||
* We defer the launchd injection until the first connection is established in order
|
||||
@@ -186,50 +185,28 @@ namespace Frida {
|
||||
darwin_host_session.activate_crash_reporter_integration ();
|
||||
#endif
|
||||
|
||||
handle_server_connection.begin (connection);
|
||||
ConnectionHandler handler;
|
||||
unowned string iface_name = dynamic_iface?.name;
|
||||
if (iface_name != null) {
|
||||
handler = dynamic_interface_handlers[iface_name];
|
||||
if (handler == null) {
|
||||
handler = new ConnectionHandler (this, dynamic_iface);
|
||||
dynamic_interface_handlers[iface_name] = handler;
|
||||
}
|
||||
} else {
|
||||
handler = main_handler;
|
||||
}
|
||||
|
||||
handler.handle_server_connection.begin (connection);
|
||||
}
|
||||
|
||||
private async void handle_server_connection (IOStream raw_connection) throws GLib.Error {
|
||||
var connection = yield new DBusConnection (raw_connection, null, DELAY_MESSAGE_PROCESSING, null, io_cancellable);
|
||||
connection.on_closed.connect (on_connection_closed);
|
||||
|
||||
Peer peer;
|
||||
AuthenticationService? auth_service = endpoint_params.auth_service;
|
||||
if (auth_service != null)
|
||||
peer = new AuthenticationChannel (this, connection, auth_service);
|
||||
else
|
||||
peer = setup_control_channel (connection);
|
||||
peers[connection] = peer;
|
||||
|
||||
connection.start_message_processing ();
|
||||
}
|
||||
|
||||
private void on_connection_closed (DBusConnection connection, bool remote_peer_vanished, GLib.Error? error) {
|
||||
Peer peer;
|
||||
if (peers.unset (connection, out peer))
|
||||
peer.close.begin (io_cancellable);
|
||||
}
|
||||
|
||||
private async void promote_authentication_channel (AuthenticationChannel channel) throws GLib.Error {
|
||||
DBusConnection connection = channel.connection;
|
||||
|
||||
peers.unset (connection);
|
||||
yield channel.close (io_cancellable);
|
||||
|
||||
peers[connection] = setup_control_channel (connection);
|
||||
}
|
||||
|
||||
private void kick_authentication_channel (AuthenticationChannel channel) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
channel.connection.close.begin (io_cancellable);
|
||||
return false;
|
||||
private void on_interface_detached (DynamicInterface iface) {
|
||||
schedule_on_frida_thread (() => {
|
||||
ConnectionHandler handler;
|
||||
if (dynamic_interface_handlers.unset (iface.name, out handler))
|
||||
handler.close.begin (io_cancellable);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
|
||||
private ControlChannel setup_control_channel (DBusConnection connection) {
|
||||
return new ControlChannel (this, connection);
|
||||
}
|
||||
|
||||
private async void teardown_control_channel (ControlChannel channel) {
|
||||
@@ -258,8 +235,235 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private void schedule_on_frida_thread (owned SourceFunc function) {
|
||||
assert (main_context != null);
|
||||
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
source.attach (main_context);
|
||||
}
|
||||
|
||||
private class ConnectionHandler : Object {
|
||||
public weak ControlService parent {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public DynamicInterface? dynamic_iface {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public HostSession host_session {
|
||||
get {
|
||||
return parent.host_session;
|
||||
}
|
||||
}
|
||||
|
||||
private Gee.Map<DBusConnection, Peer> peers = new Gee.HashMap<DBusConnection, Peer> ();
|
||||
|
||||
private SocketService broker_service = new SocketService ();
|
||||
#if !WINDOWS
|
||||
private uint16 broker_port = 0;
|
||||
#endif
|
||||
private Gee.Map<string, Transport> transports = new Gee.HashMap<string, Transport> ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
public ConnectionHandler (ControlService parent, DynamicInterface? dynamic_iface) {
|
||||
Object (parent: parent, dynamic_iface: dynamic_iface);
|
||||
}
|
||||
|
||||
construct {
|
||||
broker_service.incoming.connect (on_broker_service_connection);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
broker_service.incoming.disconnect (on_broker_service_connection);
|
||||
|
||||
io_cancellable.cancel ();
|
||||
|
||||
broker_service.stop ();
|
||||
|
||||
transports.clear ();
|
||||
|
||||
foreach (var peer in peers.values.to_array ())
|
||||
yield peer.close (cancellable);
|
||||
peers.clear ();
|
||||
}
|
||||
|
||||
public Gee.Iterator<ControlChannel> all_control_channels () {
|
||||
return (Gee.Iterator<ControlChannel>) peers.values.filter (peer => peer is ControlChannel);
|
||||
}
|
||||
|
||||
public async void handle_server_connection (IOStream raw_connection) throws GLib.Error {
|
||||
var connection = yield new DBusConnection (raw_connection, null, DELAY_MESSAGE_PROCESSING, null,
|
||||
io_cancellable);
|
||||
connection.on_closed.connect (on_connection_closed);
|
||||
|
||||
AuthenticationService? auth_service = parent.endpoint_params.auth_service;
|
||||
peers[connection] = (auth_service != null)
|
||||
? (Peer) new AuthenticationChannel (this, connection, auth_service)
|
||||
: (Peer) new ControlChannel (this, connection);
|
||||
|
||||
connection.start_message_processing ();
|
||||
}
|
||||
|
||||
private void on_connection_closed (DBusConnection connection, bool remote_peer_vanished, GLib.Error? error) {
|
||||
Peer peer;
|
||||
if (peers.unset (connection, out peer))
|
||||
peer.close.begin (io_cancellable);
|
||||
}
|
||||
|
||||
public async void promote_authentication_channel (AuthenticationChannel channel) throws GLib.Error {
|
||||
DBusConnection connection = channel.connection;
|
||||
|
||||
peers.unset (connection);
|
||||
yield channel.close (io_cancellable);
|
||||
|
||||
peers[connection] = new ControlChannel (this, connection);
|
||||
}
|
||||
|
||||
public void kick_authentication_channel (AuthenticationChannel channel) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
channel.connection.close.begin (io_cancellable);
|
||||
return false;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
|
||||
public async void teardown_control_channel (ControlChannel channel) {
|
||||
yield parent.teardown_control_channel (channel);
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (ControlChannel requester) throws GLib.Error {
|
||||
yield parent.enable_spawn_gating (requester);
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (ControlChannel requester) throws GLib.Error {
|
||||
yield parent.disable_spawn_gating (requester);
|
||||
}
|
||||
|
||||
public HostSpawnInfo[] enumerate_pending_spawn () {
|
||||
return parent.enumerate_pending_spawn ();
|
||||
}
|
||||
|
||||
public async void resume (uint pid, ControlChannel requester) throws GLib.Error {
|
||||
yield parent.resume (pid, requester);
|
||||
}
|
||||
|
||||
public async AgentSessionId attach (uint pid, HashTable<string, Variant> options, ControlChannel requester,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
return yield parent.attach (pid, options, requester, cancellable);
|
||||
}
|
||||
|
||||
public async void reattach (AgentSessionId id, ControlChannel requester, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
yield parent.reattach (id, requester, cancellable);
|
||||
}
|
||||
|
||||
public void open_tcp_transport (AgentSessionId id, Cancellable? cancellable, out uint16 port, out string token)
|
||||
throws Error {
|
||||
#if WINDOWS
|
||||
throw new Error.NOT_SUPPORTED ("Not yet supported on Windows");
|
||||
#else
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
if (base_host_session == null)
|
||||
throw new Error.NOT_SUPPORTED ("Not supported for remote host sessions");
|
||||
if (!base_host_session.can_pass_file_descriptors_to_agent_session (id))
|
||||
throw new Error.INVALID_ARGUMENT ("Not supported by this particular agent session");
|
||||
|
||||
if (broker_port == 0) {
|
||||
try {
|
||||
if (dynamic_iface != null) {
|
||||
SocketAddress effective_address;
|
||||
broker_service.add_address (
|
||||
new InetSocketAddress (dynamic_iface.ip, 0),
|
||||
STREAM,
|
||||
TCP,
|
||||
null,
|
||||
out effective_address);
|
||||
broker_port = ((InetSocketAddress) effective_address).get_port ();
|
||||
} else {
|
||||
broker_port = broker_service.add_any_inet_port (null);
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to listen: %s", e.message);
|
||||
}
|
||||
|
||||
broker_service.start ();
|
||||
}
|
||||
|
||||
string transport_id = Uuid.string_random ();
|
||||
|
||||
var expiry_source = new TimeoutSource.seconds (20);
|
||||
expiry_source.set_callback (() => {
|
||||
transports.unset (transport_id);
|
||||
return false;
|
||||
});
|
||||
expiry_source.attach (MainContext.get_thread_default ());
|
||||
|
||||
transports[transport_id] = new Transport (id, expiry_source);
|
||||
|
||||
port = broker_port;
|
||||
token = transport_id;
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool on_broker_service_connection (SocketConnection connection, Object? source_object) {
|
||||
handle_broker_connection.begin (connection);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void handle_broker_connection (SocketConnection connection) throws GLib.Error {
|
||||
var socket = connection.socket;
|
||||
if (socket.get_family () != UNIX)
|
||||
Tcp.enable_nodelay (socket);
|
||||
|
||||
const size_t uuid_length = 36;
|
||||
|
||||
var raw_token = new uint8[uuid_length + 1];
|
||||
size_t bytes_read;
|
||||
yield connection.input_stream.read_all_async (raw_token[0:uuid_length], Priority.DEFAULT, io_cancellable,
|
||||
out bytes_read);
|
||||
unowned string token = (string) raw_token;
|
||||
|
||||
Transport transport;
|
||||
if (!transports.unset (token, out transport))
|
||||
return;
|
||||
|
||||
transport.expiry_source.destroy ();
|
||||
|
||||
#if !WINDOWS
|
||||
AgentSessionId session_id = transport.session_id;
|
||||
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
if (base_host_session == null)
|
||||
throw new Error.NOT_SUPPORTED ("Not supported for remote host sessions");
|
||||
|
||||
AgentSessionProvider provider = base_host_session.obtain_session_provider (session_id);
|
||||
yield provider.migrate (session_id, socket, io_cancellable);
|
||||
#endif
|
||||
}
|
||||
|
||||
private class Transport {
|
||||
public AgentSessionId session_id;
|
||||
public Source expiry_source;
|
||||
|
||||
public Transport (AgentSessionId session_id, Source expiry_source) {
|
||||
this.session_id = session_id;
|
||||
this.expiry_source = expiry_source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Gee.Iterator<ControlChannel> all_control_channels () {
|
||||
return (Gee.Iterator<ControlChannel>) peers.values.filter (peer => peer is ControlChannel);
|
||||
var channels = new Gee.ArrayList<ControlChannel> ();
|
||||
channels.add_all_iterator (main_handler.all_control_channels ());
|
||||
foreach (var handler in dynamic_interface_handlers.values)
|
||||
channels.add_all_iterator (handler.all_control_channels ());
|
||||
return channels.iterator ();
|
||||
}
|
||||
|
||||
private async void enable_spawn_gating (ControlChannel requester) throws GLib.Error {
|
||||
@@ -446,85 +650,12 @@ namespace Frida {
|
||||
sessions.unset (entry.id);
|
||||
}
|
||||
|
||||
private void open_tcp_transport (AgentSessionId id, Cancellable? cancellable, out uint16 port,
|
||||
out string token) throws Error {
|
||||
#if WINDOWS
|
||||
throw new Error.NOT_SUPPORTED ("Not yet supported on Windows");
|
||||
#else
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
if (base_host_session == null)
|
||||
throw new Error.NOT_SUPPORTED ("Not supported for remote host sessions");
|
||||
if (!base_host_session.can_pass_file_descriptors_to_agent_session (id))
|
||||
throw new Error.INVALID_ARGUMENT ("Not supported by this particular agent session");
|
||||
|
||||
if (broker_port == 0) {
|
||||
try {
|
||||
broker_port = broker_service.add_any_inet_port (null);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to listen: %s", e.message);
|
||||
}
|
||||
|
||||
broker_service.start ();
|
||||
}
|
||||
|
||||
string transport_id = Uuid.string_random ();
|
||||
|
||||
var expiry_source = new TimeoutSource.seconds (20);
|
||||
expiry_source.set_callback (() => {
|
||||
transports.unset (transport_id);
|
||||
return false;
|
||||
});
|
||||
expiry_source.attach (MainContext.get_thread_default ());
|
||||
|
||||
transports[transport_id] = new Transport (id, expiry_source);
|
||||
|
||||
port = broker_port;
|
||||
token = transport_id;
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool on_broker_service_connection (SocketConnection connection, Object? source_object) {
|
||||
handle_broker_connection.begin (connection);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void handle_broker_connection (SocketConnection connection) throws GLib.Error {
|
||||
var socket = connection.socket;
|
||||
if (socket.get_family () != UNIX)
|
||||
Tcp.enable_nodelay (socket);
|
||||
|
||||
const size_t uuid_length = 36;
|
||||
|
||||
var raw_token = new uint8[uuid_length + 1];
|
||||
size_t bytes_read;
|
||||
yield connection.input_stream.read_all_async (raw_token[0:uuid_length], Priority.DEFAULT, io_cancellable,
|
||||
out bytes_read);
|
||||
unowned string token = (string) raw_token;
|
||||
|
||||
Transport transport;
|
||||
if (!transports.unset (token, out transport))
|
||||
return;
|
||||
|
||||
transport.expiry_source.destroy ();
|
||||
|
||||
#if !WINDOWS
|
||||
AgentSessionId session_id = transport.session_id;
|
||||
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
if (base_host_session == null)
|
||||
throw new Error.NOT_SUPPORTED ("Not supported for remote host sessions");
|
||||
|
||||
AgentSessionProvider provider = base_host_session.obtain_session_provider (session_id);
|
||||
yield provider.migrate (session_id, socket, io_cancellable);
|
||||
#endif
|
||||
}
|
||||
|
||||
private interface Peer : Object {
|
||||
public abstract async void close (Cancellable? cancellable = null) throws IOError;
|
||||
}
|
||||
|
||||
private class AuthenticationChannel : Object, Peer, AuthenticationService {
|
||||
public weak ControlService parent {
|
||||
public weak ConnectionHandler parent {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
@@ -541,7 +672,7 @@ namespace Frida {
|
||||
|
||||
private Gee.Collection<uint> registrations = new Gee.ArrayList<uint> ();
|
||||
|
||||
public AuthenticationChannel (ControlService parent, DBusConnection connection, AuthenticationService service) {
|
||||
public AuthenticationChannel (ConnectionHandler parent, DBusConnection connection, AuthenticationService service) {
|
||||
Object (
|
||||
parent: parent,
|
||||
connection: connection,
|
||||
@@ -581,7 +712,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private class ControlChannel : Object, Peer, HostSession, TransportBroker {
|
||||
public weak ControlService parent {
|
||||
public weak ConnectionHandler parent {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
@@ -599,7 +730,7 @@ namespace Frida {
|
||||
private Gee.Set<uint> registrations = new Gee.HashSet<uint> ();
|
||||
private TimeoutSource? ping_timer;
|
||||
|
||||
public ControlChannel (ControlService parent, DBusConnection connection) {
|
||||
public ControlChannel (ConnectionHandler parent, DBusConnection connection) {
|
||||
Object (parent: parent, connection: connection);
|
||||
}
|
||||
|
||||
@@ -848,16 +979,6 @@ namespace Frida {
|
||||
expiry_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private class Transport {
|
||||
public AgentSessionId session_id;
|
||||
public Source expiry_source;
|
||||
|
||||
public Transport (AgentSessionId session_id, Source expiry_source) {
|
||||
this.session_id = session_id;
|
||||
this.expiry_source = expiry_source;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
public class ControlService : Object {
|
||||
|
||||
@@ -933,27 +933,28 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async void prepare_for_launch (string identifier, Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("prepareForLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, cancellable);
|
||||
yield call ("prepareForLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, null,
|
||||
cancellable);
|
||||
}
|
||||
|
||||
public async void cancel_launch (string identifier, Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("cancelLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, cancellable);
|
||||
yield call ("cancelLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, null, cancellable);
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("enableSpawnGating", new Json.Node[] {}, cancellable);
|
||||
yield call ("enableSpawnGating", new Json.Node[] {}, null, cancellable);
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("disableSpawnGating", new Json.Node[] {}, cancellable);
|
||||
yield call ("disableSpawnGating", new Json.Node[] {}, null, cancellable);
|
||||
}
|
||||
|
||||
public async void claim_process (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("claimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, cancellable);
|
||||
yield call ("claimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, null, cancellable);
|
||||
}
|
||||
|
||||
public async void unclaim_process (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("unclaimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, cancellable);
|
||||
yield call ("unclaimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, null, cancellable);
|
||||
}
|
||||
|
||||
protected override void on_event (string type, Json.Array event) {
|
||||
|
||||
@@ -183,6 +183,7 @@ struct _FridaSpawnInstance
|
||||
GumAddress modern_entry_address;
|
||||
|
||||
/* V4+ */
|
||||
GumAddress notify_objc_init;
|
||||
GumAddress info_ptr_address;
|
||||
|
||||
/* V3- */
|
||||
@@ -2030,6 +2031,10 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
|
||||
instance->dyld_flavor = (modern_entry_address != 0) ? FRIDA_DYLD_V4_PLUS : FRIDA_DYLD_V3_MINUS;
|
||||
if (instance->dyld_flavor == FRIDA_DYLD_V4_PLUS)
|
||||
{
|
||||
instance->notify_objc_init = gum_darwin_module_resolve_symbol_address (dyld, "__ZN5dyld412RuntimeState14notifyObjCInitEPKNS_6LoaderE");
|
||||
if (instance->notify_objc_init != 0)
|
||||
modern_entry_address = instance->notify_objc_init;
|
||||
|
||||
instance->modern_entry_address = modern_entry_address;
|
||||
legacy_entry_address = 0;
|
||||
|
||||
@@ -2108,7 +2113,12 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
|
||||
if (instance->dyld_flavor == FRIDA_DYLD_V4_PLUS)
|
||||
{
|
||||
GumAddress restart_with_dyld_in_cache = gum_darwin_module_resolve_symbol_address (dyld,
|
||||
"__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPK15DyldSharedCachePv");
|
||||
if (restart_with_dyld_in_cache == 0)
|
||||
{
|
||||
restart_with_dyld_in_cache = gum_darwin_module_resolve_symbol_address (dyld,
|
||||
"__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPv");
|
||||
}
|
||||
if (restart_with_dyld_in_cache != 0)
|
||||
frida_spawn_instance_set_nth_breakpoint (instance, i++, restart_with_dyld_in_cache, FRIDA_BREAKPOINT_REPEAT_NEVER);
|
||||
}
|
||||
@@ -3085,9 +3095,15 @@ frida_spawn_instance_handle_breakpoint (FridaSpawnInstance * self, FridaBreakpoi
|
||||
if (self->dyld_flavor == FRIDA_DYLD_V4_PLUS)
|
||||
{
|
||||
if (pc == self->modern_entry_address)
|
||||
self->breakpoint_phase = FRIDA_BREAKPOINT_SET_LIBDYLD_INITIALIZE_CALLER_BREAKPOINT;
|
||||
{
|
||||
self->breakpoint_phase = (pc == self->notify_objc_init)
|
||||
? FRIDA_BREAKPOINT_LIBSYSTEM_INITIALIZED
|
||||
: FRIDA_BREAKPOINT_SET_LIBDYLD_INITIALIZE_CALLER_BREAKPOINT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return frida_spawn_instance_handle_dyld_restart (self);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3353,6 +3369,7 @@ frida_spawn_instance_handle_dyld_restart (FridaSpawnInstance * self)
|
||||
GumAddress * info_ptr;
|
||||
struct dyld_all_image_infos * info = NULL;
|
||||
GumDarwinModule * dyld = NULL;
|
||||
gssize delta;
|
||||
|
||||
info_ptr = (GumAddress *) gum_darwin_read (self->task, self->info_ptr_address, sizeof (GumAddress), NULL);
|
||||
if (info_ptr == NULL)
|
||||
@@ -3367,7 +3384,11 @@ frida_spawn_instance_handle_dyld_restart (FridaSpawnInstance * self)
|
||||
if (dyld == NULL)
|
||||
goto beach;
|
||||
|
||||
self->modern_entry_address = GUM_ADDRESS (info->dyldImageLoadAddress) + (self->modern_entry_address - self->dyld->base_address);
|
||||
delta = (gssize) info->dyldImageLoadAddress - (gssize) self->dyld->base_address;
|
||||
|
||||
self->modern_entry_address += delta;
|
||||
if (self->notify_objc_init != 0)
|
||||
self->notify_objc_init += delta;
|
||||
|
||||
g_object_unref (self->dyld);
|
||||
self->dyld = g_steal_pointer (&dyld);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
devkit_outputs = [
|
||||
'frida-core.gir',
|
||||
'frida-core.h',
|
||||
static_lib_prefix + 'frida-core' + static_lib_suffix,
|
||||
'frida-core-example.c',
|
||||
|
||||
+42
-18
@@ -2,6 +2,7 @@ from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import struct
|
||||
|
||||
|
||||
def main(argv):
|
||||
@@ -27,29 +28,36 @@ def main(argv):
|
||||
|
||||
embedded_assets = []
|
||||
if host_os == "windows":
|
||||
for agent, flavor in [(agent_modern, "64"),
|
||||
(agent_legacy, "32")]:
|
||||
embedded_agent = priv_dir / f"frida-agent-{flavor}.dll"
|
||||
embedded_dbghelp = priv_dir / f"dbghelp-{flavor}.dll"
|
||||
embedded_symsrv = priv_dir / f"symsrv-{flavor}.dll"
|
||||
pending_archs = {"arm64", "x86_64", "x86"}
|
||||
for agent in {agent_modern, agent_legacy, agent_emulated_modern, agent_emulated_legacy}:
|
||||
if agent is None:
|
||||
continue
|
||||
arch = detect_pefile_arch(agent)
|
||||
embedded_agent = priv_dir / f"frida-agent-{arch}.dll"
|
||||
embedded_dbghelp = priv_dir / f"dbghelp-{arch}.dll"
|
||||
embedded_symsrv = priv_dir / f"symsrv-{arch}.dll"
|
||||
|
||||
if agent is not None:
|
||||
shutil.copy(agent, embedded_agent)
|
||||
shutil.copy(agent, embedded_agent)
|
||||
|
||||
if agent_dbghelp_prefix is not None:
|
||||
shutil.copy(agent_dbghelp_prefix / flavor / "dbghelp.dll", embedded_dbghelp)
|
||||
else:
|
||||
embedded_dbghelp.write_bytes(b"")
|
||||
|
||||
if agent_symsrv_prefix is not None:
|
||||
shutil.copy(agent_symsrv_prefix / flavor / "symsrv.dll", embedded_symsrv)
|
||||
else:
|
||||
embedded_symsrv.write_bytes(b"")
|
||||
if agent_dbghelp_prefix is not None:
|
||||
shutil.copy(agent_dbghelp_prefix / arch / "dbghelp.dll", embedded_dbghelp)
|
||||
else:
|
||||
for f in [embedded_agent, embedded_dbghelp, embedded_symsrv]:
|
||||
f.write_bytes(b"")
|
||||
embedded_dbghelp.write_bytes(b"")
|
||||
|
||||
if agent_symsrv_prefix is not None:
|
||||
shutil.copy(agent_symsrv_prefix / arch / "symsrv.dll", embedded_symsrv)
|
||||
else:
|
||||
embedded_symsrv.write_bytes(b"")
|
||||
|
||||
embedded_assets += [embedded_agent, embedded_dbghelp, embedded_symsrv]
|
||||
pending_archs.remove(arch)
|
||||
for missing_arch in pending_archs:
|
||||
embedded_agent = priv_dir / f"frida-agent-{missing_arch}.dll"
|
||||
embedded_dbghelp = priv_dir / f"dbghelp-{missing_arch}.dll"
|
||||
embedded_symsrv = priv_dir / f"symsrv-{missing_arch}.dll"
|
||||
for asset in {embedded_agent, embedded_dbghelp, embedded_symsrv}:
|
||||
asset.write_bytes(b"")
|
||||
embedded_assets += [asset]
|
||||
elif host_os in {"macos", "ios", "watchos", "tvos"}:
|
||||
embedded_agent = priv_dir / "frida-agent.dylib"
|
||||
if agent_modern is not None and agent_legacy is not None:
|
||||
@@ -103,5 +111,21 @@ def pop_cmd_array_arg(args):
|
||||
return result
|
||||
|
||||
|
||||
def detect_pefile_arch(location):
|
||||
with location.open(mode="rb") as pe:
|
||||
pe.seek(0x3c)
|
||||
e_lfanew, = struct.unpack("<I", pe.read(4))
|
||||
pe.seek(e_lfanew + 4)
|
||||
machine, = struct.unpack("<H", pe.read(2))
|
||||
return PE_MACHINES[machine]
|
||||
|
||||
|
||||
PE_MACHINES = {
|
||||
0x014c: "x86",
|
||||
0x8664: "x86_64",
|
||||
0xaa64: "arm64",
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
+37
-6
@@ -2,6 +2,7 @@ from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import struct
|
||||
|
||||
|
||||
def main(argv):
|
||||
@@ -14,12 +15,28 @@ def main(argv):
|
||||
output_dir = Path(args.pop(0))
|
||||
priv_dir = Path(args.pop(0))
|
||||
resource_config = args.pop(0)
|
||||
helper_modern, helper_legacy = [Path(p) if p else None for p in args[:2]]
|
||||
helper_modern, helper_legacy, \
|
||||
helper_emulated_modern, helper_emulated_legacy \
|
||||
= [Path(p) if p else None for p in args[:4]]
|
||||
|
||||
priv_dir.mkdir(exist_ok=True)
|
||||
|
||||
embedded_assets = []
|
||||
if host_os in {"macos", "ios", "watchos", "tvos"}:
|
||||
if host_os == "windows":
|
||||
pending_archs = {"arm64", "x86_64", "x86"}
|
||||
for helper in {helper_modern, helper_legacy, helper_emulated_modern, helper_emulated_legacy}:
|
||||
if helper is None:
|
||||
continue
|
||||
arch = detect_pefile_arch(helper)
|
||||
embedded_helper = priv_dir / f"frida-helper-{arch}.exe"
|
||||
shutil.copy(helper, embedded_helper)
|
||||
embedded_assets += [embedded_helper]
|
||||
pending_archs.remove(arch)
|
||||
for missing_arch in pending_archs:
|
||||
embedded_helper = priv_dir / f"frida-helper-{missing_arch}.exe"
|
||||
embedded_helper.write_bytes(b"")
|
||||
embedded_assets += [embedded_helper]
|
||||
elif host_os in {"macos", "ios", "watchos", "tvos"}:
|
||||
embedded_helper = priv_dir / "frida-helper"
|
||||
|
||||
if helper_modern is not None and helper_legacy is not None:
|
||||
@@ -34,10 +51,8 @@ def main(argv):
|
||||
|
||||
embedded_assets += [embedded_helper]
|
||||
else:
|
||||
exe_suffix = ".exe" if host_os == "windows" else ""
|
||||
|
||||
embedded_helper_modern = priv_dir / f"frida-helper-64{exe_suffix}"
|
||||
embedded_helper_legacy = priv_dir / f"frida-helper-32{exe_suffix}"
|
||||
embedded_helper_modern = priv_dir / f"frida-helper-64"
|
||||
embedded_helper_legacy = priv_dir / f"frida-helper-32"
|
||||
|
||||
if helper_modern is not None:
|
||||
shutil.copy(helper_modern, embedded_helper_modern)
|
||||
@@ -74,5 +89,21 @@ def pop_cmd_array_arg(args):
|
||||
return result
|
||||
|
||||
|
||||
def detect_pefile_arch(location):
|
||||
with location.open(mode="rb") as pe:
|
||||
pe.seek(0x3c)
|
||||
e_lfanew, = struct.unpack("<I", pe.read(4))
|
||||
pe.seek(e_lfanew + 4)
|
||||
machine, = struct.unpack("<H", pe.read(2))
|
||||
return PE_MACHINES[machine]
|
||||
|
||||
|
||||
PE_MACHINES = {
|
||||
0x014c: "x86",
|
||||
0x8664: "x86_64",
|
||||
0xaa64: "arm64",
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
+33
-30
@@ -242,7 +242,7 @@ namespace Frida {
|
||||
opts["keepalive_interval"] = interval;
|
||||
}
|
||||
|
||||
var device = new Device (this, id, name, HostSessionProviderKind.REMOTE, socket_device.provider, raw_options);
|
||||
var device = new Device (this, socket_device.provider, id, name, raw_options);
|
||||
devices.add (device);
|
||||
added (device);
|
||||
changed ();
|
||||
@@ -358,7 +358,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void on_provider_available (HostSessionProvider provider) {
|
||||
var device = new Device (this, provider.id, provider.name, provider.kind, provider);
|
||||
var device = new Device (this, provider);
|
||||
devices.add (device);
|
||||
|
||||
foreach (var observer in on_device_added.to_array ())
|
||||
@@ -483,13 +483,19 @@ namespace Frida {
|
||||
public signal void lost ();
|
||||
|
||||
public string id {
|
||||
get;
|
||||
construct;
|
||||
get {
|
||||
if (_id != null)
|
||||
return _id;
|
||||
return provider.id;
|
||||
}
|
||||
}
|
||||
|
||||
public string name {
|
||||
get;
|
||||
construct;
|
||||
get {
|
||||
if (_name != null)
|
||||
return _name;
|
||||
return provider.name;
|
||||
}
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
@@ -498,8 +504,18 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public DeviceType dtype {
|
||||
get;
|
||||
construct;
|
||||
get {
|
||||
switch (provider.kind) {
|
||||
case HostSessionProviderKind.LOCAL:
|
||||
return DeviceType.LOCAL;
|
||||
case HostSessionProviderKind.REMOTE:
|
||||
return DeviceType.REMOTE;
|
||||
case HostSessionProviderKind.USB:
|
||||
return DeviceType.USB;
|
||||
default:
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Bus bus {
|
||||
@@ -518,7 +534,8 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public delegate bool ProcessPredicate (Process process);
|
||||
private string? _id;
|
||||
private string? _name;
|
||||
|
||||
private HostSessionOptions? host_session_options;
|
||||
private Promise<HostSession>? host_session_request;
|
||||
@@ -533,32 +550,18 @@ namespace Frida {
|
||||
private Gee.Set<Service> services = new Gee.HashSet<Service> ();
|
||||
private Bus _bus;
|
||||
|
||||
internal Device (DeviceManager? manager, string id, string name, HostSessionProviderKind kind, HostSessionProvider provider,
|
||||
HostSessionOptions? options = null) {
|
||||
DeviceType dtype;
|
||||
switch (kind) {
|
||||
case HostSessionProviderKind.LOCAL:
|
||||
dtype = DeviceType.LOCAL;
|
||||
break;
|
||||
case HostSessionProviderKind.REMOTE:
|
||||
dtype = DeviceType.REMOTE;
|
||||
break;
|
||||
case HostSessionProviderKind.USB:
|
||||
dtype = DeviceType.USB;
|
||||
break;
|
||||
default:
|
||||
assert_not_reached ();
|
||||
}
|
||||
public delegate bool ProcessPredicate (Process process);
|
||||
|
||||
internal Device (DeviceManager? manager, HostSessionProvider provider, string? id = null, string? name = null,
|
||||
HostSessionOptions? options = null) {
|
||||
Object (
|
||||
id: id,
|
||||
name: name,
|
||||
icon: provider.icon,
|
||||
dtype: dtype,
|
||||
manager: manager,
|
||||
provider: provider,
|
||||
manager: manager
|
||||
icon: provider.icon
|
||||
);
|
||||
|
||||
_id = id;
|
||||
_name = name;
|
||||
host_session_options = options;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ static const FridaFruityModel fruity_models[] =
|
||||
};
|
||||
|
||||
void
|
||||
_frida_fruity_host_session_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon, GError ** error)
|
||||
_frida_fruity_usbmux_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
GError ** error)
|
||||
{
|
||||
const FridaFruityModel * model;
|
||||
guint i;
|
||||
@@ -0,0 +1,266 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class LinuxFruitFinder : Object, FruitFinder {
|
||||
public string? udid_from_iface (string ifname) throws Error {
|
||||
var net = "/sys/class/net";
|
||||
|
||||
var directory = File.new_build_filename (net, ifname);
|
||||
if (!directory.query_exists ())
|
||||
return null;
|
||||
|
||||
try {
|
||||
var info = directory.query_info ("standard::*", 0);
|
||||
if (!info.get_is_symlink ())
|
||||
return null;
|
||||
var dev_path = Path.build_filename (net, info.get_symlink_target ());
|
||||
|
||||
var iface = File.new_build_filename (dev_path, "..", "..", "interface");
|
||||
if (!iface.query_exists ())
|
||||
return null;
|
||||
var iface_stream = new DataInputStream (iface.read ());
|
||||
string iface_name = iface_stream.read_line ();
|
||||
if (iface_name != "NCM Control" && iface_name != "AppleUSBEthernet")
|
||||
return null;
|
||||
|
||||
var serial = File.new_build_filename (dev_path, "..", "..", "..", "serial");
|
||||
if (!serial.query_exists ())
|
||||
return null;
|
||||
|
||||
var serial_stream = new DataInputStream (serial.read ());
|
||||
return serial_stream.read_line ();
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxPairingBrowser : Object, PairingBrowser {
|
||||
private Resolved.Manager? resolved;
|
||||
private Source? timer;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
private const size_t IF_NAMESIZE = 16;
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
try {
|
||||
var connection = yield GLib.Bus.get (BusType.SYSTEM, cancellable);
|
||||
|
||||
resolved = yield connection.get_proxy (Resolved.SERVICE_NAME, Resolved.SERVICE_PATH, DO_NOT_LOAD_PROPERTIES,
|
||||
cancellable);
|
||||
|
||||
try {
|
||||
yield resolve_services (MDNS_IPV6 | NO_NETWORK, cancellable);
|
||||
schedule_next_poll ();
|
||||
} catch (Error e) {
|
||||
handle_poll_timer_tick.begin ();
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
if (timer != null) {
|
||||
timer.destroy ();
|
||||
timer = null;
|
||||
}
|
||||
|
||||
io_cancellable.cancel ();
|
||||
}
|
||||
|
||||
private void schedule_next_poll () {
|
||||
timer = new TimeoutSource.seconds (5);
|
||||
timer.set_callback (() => {
|
||||
timer = null;
|
||||
handle_poll_timer_tick.begin ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
timer.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
|
||||
private async void handle_poll_timer_tick () {
|
||||
try {
|
||||
yield resolve_services (MDNS_IPV6, io_cancellable);
|
||||
schedule_next_poll ();
|
||||
} catch (GLib.Error e) {
|
||||
if (!(e is IOError.CANCELLED))
|
||||
schedule_next_poll ();
|
||||
}
|
||||
}
|
||||
|
||||
private async void resolve_services (Resolved.Flags flags, Cancellable? cancellable) throws Error, IOError {
|
||||
Resolved.RRItem[] items;
|
||||
uint64 ptr_flags;
|
||||
try {
|
||||
yield resolved.resolve_record (Resolved.ANY_INTERFACE, PairingService.DNS_SD_NAME, DnsRecordClass.IN,
|
||||
DnsRecordType.PTR, flags, cancellable, out items, out ptr_flags);
|
||||
} catch (GLib.Error e) {
|
||||
throw (Error) parse_error (e);
|
||||
}
|
||||
|
||||
var promises = new Gee.ArrayQueue<Promise<PairingServiceDetails?>> ();
|
||||
foreach (var item in items) {
|
||||
var pr = new DnsPacketReader (new Bytes (item.data));
|
||||
DnsPtrRecord ptr = pr.read_ptr ();
|
||||
var promise = new Promise<PairingServiceDetails> ();
|
||||
resolve_service.begin (ptr, item.ifindex, flags, cancellable, promise);
|
||||
promises.offer (promise);
|
||||
}
|
||||
|
||||
Promise<PairingServiceDetails?>? p;
|
||||
while ((p = promises.poll ()) != null) {
|
||||
try {
|
||||
yield p.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void resolve_service (DnsPtrRecord ptr, int32 ifindex, Resolved.Flags flags, Cancellable? cancellable,
|
||||
Promise<PairingServiceDetails?> promise) {
|
||||
try {
|
||||
PairingServiceDetails? service = yield do_resolve_service (ptr, ifindex, flags, cancellable);
|
||||
promise.resolve (service);
|
||||
} catch (GLib.Error e) {
|
||||
promise.reject (e);
|
||||
}
|
||||
}
|
||||
|
||||
private async PairingServiceDetails? do_resolve_service (DnsPtrRecord ptr, int32 ifindex, Resolved.Flags flags,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
Resolved.SrvItem[] srv_items;
|
||||
Variant txt_items;
|
||||
string canonical_name;
|
||||
string canonical_type;
|
||||
string canonical_domain;
|
||||
uint64 srv_flags;
|
||||
try {
|
||||
yield resolved.resolve_service (ifindex, "", "", ptr.name, Posix.AF_INET6, flags, cancellable,
|
||||
out srv_items, out txt_items, out canonical_name, out canonical_type, out canonical_domain,
|
||||
out srv_flags);
|
||||
} catch (GLib.Error e) {
|
||||
throw (Error) parse_error (e);
|
||||
}
|
||||
|
||||
var txt_record = new Gee.ArrayList<string> ();
|
||||
foreach (var raw_item in txt_items) {
|
||||
string item = ((string *) raw_item.get_data ())->substring (0, (long) raw_item.get_size ());
|
||||
if (!item.validate ())
|
||||
throw new Error.PROTOCOL ("Invalid TXT item");
|
||||
txt_record.add (item);
|
||||
}
|
||||
|
||||
var meta = PairingServiceMetadata.from_txt_record (txt_record);
|
||||
var ip = new InetAddress.from_bytes (srv_items[0].addresses[0].ip, IPV6);
|
||||
var service = new PairingServiceDetails () {
|
||||
identifier = meta.identifier,
|
||||
auth_tag = meta.auth_tag,
|
||||
endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: ip,
|
||||
port: srv_items[0].port,
|
||||
scope_id: ip.get_is_link_local () ? ifindex : 0
|
||||
),
|
||||
interface_address = resolve_interface_address (ifindex),
|
||||
};
|
||||
|
||||
service_discovered (service);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
private static InetSocketAddress resolve_interface_address (int32 ifindex) throws Error {
|
||||
char ifname_buf[IF_NAMESIZE];
|
||||
unowned string? ifname = Linux.Network.if_indextoname (ifindex, (string) ifname_buf);
|
||||
if (ifname == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to resolve interface name");
|
||||
|
||||
Linux.Network.IfAddrs ifaddrs;
|
||||
Linux.Network.getifaddrs (out ifaddrs);
|
||||
for (unowned Linux.Network.IfAddrs candidate = ifaddrs; candidate != null; candidate = candidate.ifa_next) {
|
||||
if (candidate.ifa_name != ifname)
|
||||
continue;
|
||||
if (candidate.ifa_addr.sa_family != Posix.AF_INET6)
|
||||
continue;
|
||||
return (InetSocketAddress)
|
||||
SocketAddress.from_native ((void *) candidate.ifa_addr, sizeof (Posix.SockAddrIn6));
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to resolve interface address");
|
||||
}
|
||||
|
||||
private static GLib.Error parse_error (GLib.Error e) {
|
||||
if (e is Error || e is IOError.CANCELLED)
|
||||
return e;
|
||||
return new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Resolved {
|
||||
public const string SERVICE_NAME = "org.freedesktop.resolve1";
|
||||
public const string SERVICE_PATH = "/org/freedesktop/resolve1";
|
||||
|
||||
public const int32 ANY_INTERFACE = 0;
|
||||
|
||||
[DBus (name = "org.freedesktop.resolve1.Manager")]
|
||||
public interface Manager : Object {
|
||||
public abstract async void resolve_record (int32 ifindex, string name, uint16 klass, uint16 type, uint64 flags,
|
||||
Cancellable? cancellable, out RRItem[] items, out uint64 result_flags) throws GLib.Error;
|
||||
public abstract async void resolve_service (int32 ifindex, string name, string type, string domain, int32 family,
|
||||
uint64 flags, Cancellable? cancellable, out SrvItem[] srv_items,
|
||||
[DBus (signature = "aay")] out Variant txt_items, out string canonical_name, out string canonical_type,
|
||||
out string canonical_domain, out uint64 result_flags) throws GLib.Error;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum Flags {
|
||||
DNS = 1 << 0,
|
||||
LLMNR_IPV4 = 1 << 1,
|
||||
LLMNR_IPV6 = 1 << 2,
|
||||
MDNS_IPV4 = 1 << 3,
|
||||
MDNS_IPV6 = 1 << 4,
|
||||
NO_CNAME = 1 << 5,
|
||||
NO_TXT = 1 << 6,
|
||||
NO_ADDRESS = 1 << 7,
|
||||
NO_SEARCH = 1 << 8,
|
||||
AUTHENTICATED = 1 << 9,
|
||||
NO_VALIDATE = 1 << 10,
|
||||
NO_SYNTHESIZE = 1 << 11,
|
||||
NO_CACHE = 1 << 12,
|
||||
NO_ZONE = 1 << 13,
|
||||
NO_TRUST_ANCHOR = 1 << 14,
|
||||
NO_NETWORK = 1 << 15,
|
||||
REQUIRE_PRIMARY = 1 << 16,
|
||||
CLAMP_TTL = 1 << 17,
|
||||
CONFIDENTIAL = 1 << 18,
|
||||
SYNTHETIC = 1 << 19,
|
||||
FROM_CACHE = 1 << 20,
|
||||
FROM_ZONE = 1 << 21,
|
||||
FROM_TRUST_ANCHOR = 1 << 22,
|
||||
FROM_NETWORK = 1 << 23,
|
||||
NO_STALE = 1 << 24,
|
||||
RELAX_SINGLE_LABEL = 1 << 25,
|
||||
}
|
||||
|
||||
public struct RRItem {
|
||||
public int32 ifindex;
|
||||
public uint16 klass;
|
||||
public uint16 type;
|
||||
public uint8[] data;
|
||||
}
|
||||
|
||||
public struct SrvItem {
|
||||
public uint16 priority;
|
||||
public uint16 weight;
|
||||
public uint16 port;
|
||||
public string name;
|
||||
public SrvAddress[] addresses;
|
||||
public string canonical_name;
|
||||
}
|
||||
|
||||
public struct SrvAddress {
|
||||
public int32 ifindex;
|
||||
public int32 family;
|
||||
public uint8[] ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,585 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public sealed class MacOSCoreDeviceBackend : Object, Backend {
|
||||
private Gee.Map<string, MacOSCoreDeviceTransport> transports = new Gee.HashMap<string, MacOSCoreDeviceTransport> ();
|
||||
|
||||
private Promise<bool> all_current_devices_listed = new Promise<bool> ();
|
||||
private Promise<bool> browse_request = new Promise<bool> ();
|
||||
|
||||
private XpcClient? pairingd;
|
||||
private Darwin.GCD.DispatchQueue queue =
|
||||
new Darwin.GCD.DispatchQueue ("re.frida.fruity.remotepairing", Darwin.GCD.DispatchQueueAttr.SERIAL);
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
pairingd = XpcClient.make_for_mach_service ("com.apple.CoreDevice.remotepairingd", queue);
|
||||
pairingd.notify["state"].connect (on_state_changed);
|
||||
pairingd.message.connect (on_message);
|
||||
|
||||
do_browse.begin ();
|
||||
|
||||
try {
|
||||
yield all_current_devices_listed.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private async void do_browse () {
|
||||
try {
|
||||
var r = new PairingdRequest ("RemotePairing.BrowseRequest");
|
||||
r.body.set_bool ("currentDevicesOnly", false);
|
||||
yield pairingd.request (r.message, null);
|
||||
browse_request.resolve (true);
|
||||
} catch (GLib.Error e) {
|
||||
browse_request.reject (e);
|
||||
}
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
}
|
||||
|
||||
private void on_state_changed (Object obj, ParamSpec pspec) {
|
||||
if (pairingd.state == CLOSED && !all_current_devices_listed.future.ready) {
|
||||
all_current_devices_listed.reject (
|
||||
new Error.TRANSPORT ("Connection closed while waiting for initial device list"));
|
||||
}
|
||||
}
|
||||
|
||||
private void on_message (Darwin.Xpc.Object obj) {
|
||||
var reader = new XpcObjectReader (obj);
|
||||
try {
|
||||
reader.read_member ("mangledTypeName");
|
||||
if (reader.get_string_value () == "RemotePairing.ServiceEvent") {
|
||||
reader
|
||||
.end_member ()
|
||||
.read_member ("value");
|
||||
if (reader.try_read_member ("deviceFound")) {
|
||||
reader
|
||||
.read_member ("_0")
|
||||
.read_member ("deviceInfo");
|
||||
|
||||
var udid = reader.read_member ("udid").get_string_value ();
|
||||
reader.end_member ();
|
||||
if (transports.has_key (udid))
|
||||
return;
|
||||
|
||||
var device_info = (Darwin.Xpc.Dictionary) reader.current_object;
|
||||
|
||||
var pairing_device = new XpcClient (device_info.create_connection ("endpoint"), queue);
|
||||
|
||||
on_device_found (udid, pairing_device, device_info);
|
||||
} else if (reader.try_read_member ("allCurrentDevicesListed")) {
|
||||
all_current_devices_listed.resolve (true);
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private void on_device_found (string udid, XpcClient pairing_device, Darwin.Xpc.Dictionary device_info) throws Error {
|
||||
var transport = new MacOSCoreDeviceTransport (udid, pairing_device, device_info);
|
||||
transport.closed.connect (on_transport_closed);
|
||||
transports[udid] = transport;
|
||||
transport_attached (transport);
|
||||
}
|
||||
|
||||
private void on_transport_closed (MacOSCoreDeviceTransport transport) {
|
||||
transport_detached (transport);
|
||||
transports.unset (transport.udid);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MacOSCoreDeviceTransport : Object, Transport {
|
||||
public signal void closed ();
|
||||
|
||||
public XpcClient pairing_device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public ConnectionType connection_type {
|
||||
get {
|
||||
return _connection_type;
|
||||
}
|
||||
}
|
||||
|
||||
public string udid {
|
||||
get {
|
||||
return _udid;
|
||||
}
|
||||
}
|
||||
|
||||
public string? name {
|
||||
get {
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public UsbmuxDevice? usbmux_device {
|
||||
get {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ConnectionType _connection_type;
|
||||
private string _udid;
|
||||
private string _name;
|
||||
|
||||
private Promise<Tunnel>? tunnel_request;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
public MacOSCoreDeviceTransport (string udid, XpcClient pairing_device, Darwin.Xpc.Dictionary device_info) throws Error {
|
||||
Object (pairing_device: pairing_device);
|
||||
_udid = udid;
|
||||
update_device_info (device_info);
|
||||
}
|
||||
|
||||
construct {
|
||||
pairing_device.notify["state"].connect (on_state_changed);
|
||||
pairing_device.message.connect (on_message);
|
||||
}
|
||||
|
||||
private void update_device_info (Darwin.Xpc.Dictionary device_info) throws Error {
|
||||
_connection_type = (device_info.get_value ("untrustedRSDDeviceInfo") != null)
|
||||
? ConnectionType.USB
|
||||
: ConnectionType.NETWORK;
|
||||
|
||||
var reader = new XpcObjectReader (device_info);
|
||||
_name = reader.read_member ("name").get_string_value ();
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
private void on_state_changed (Object obj, ParamSpec pspec) {
|
||||
if (pairing_device.state == CLOSED) {
|
||||
io_cancellable.cancel ();
|
||||
|
||||
closed ();
|
||||
}
|
||||
}
|
||||
|
||||
private void on_message (Darwin.Xpc.Object obj) {
|
||||
var reader = new XpcObjectReader (obj);
|
||||
try {
|
||||
reader.read_member ("mangledTypeName");
|
||||
if (reader.get_string_value () == "RemotePairing.ServiceEvent") {
|
||||
reader
|
||||
.end_member ()
|
||||
.read_member ("value");
|
||||
if (reader.try_read_member ("deviceFound")) {
|
||||
reader
|
||||
.read_member ("_0")
|
||||
.read_member ("deviceInfo");
|
||||
var device_info = (Darwin.Xpc.Dictionary)
|
||||
reader.get_object_value (Darwin.Xpc.Dictionary.TYPE);
|
||||
update_device_info (device_info);
|
||||
} else if (reader.try_read_member ("tunnelUsageAssertionsInvalidated")) {
|
||||
reader.read_member ("assertionIdentifiers");
|
||||
var ids = new Gee.ArrayList<Bytes> ();
|
||||
size_t n = reader.count_elements ();
|
||||
for (size_t i = 0; i != n; i++) {
|
||||
reader.read_element (i);
|
||||
ids.add (new Bytes (reader.get_uuid_value ()));
|
||||
reader.end_element ();
|
||||
}
|
||||
on_tunnel_usage_assertions_invalidated.begin (ids);
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private async void on_tunnel_usage_assertions_invalidated (Gee.List<Bytes> ids) {
|
||||
if (tunnel_request == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
var tunnel = (MacOSTunnel) yield tunnel_request.future.wait_async (io_cancellable);
|
||||
unowned uint8[] tunnel_assertion_id = tunnel.assertion_identifier.get_bytes ();
|
||||
foreach (var id in ids) {
|
||||
if (Memory.cmp (id.get_data (), tunnel_assertion_id, id.get_size ()) == 0) {
|
||||
tunnel_request = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async Tunnel? find_tunnel (UsbmuxDevice? device, Cancellable? cancellable) throws Error, IOError {
|
||||
while (tunnel_request != null) {
|
||||
try {
|
||||
return yield tunnel_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (IOError e) {
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
}
|
||||
tunnel_request = new Promise<Tunnel> ();
|
||||
|
||||
try {
|
||||
var tunnel = new MacOSTunnel (pairing_device);
|
||||
yield tunnel.attach (cancellable);
|
||||
|
||||
tunnel_request.resolve (tunnel);
|
||||
|
||||
return tunnel;
|
||||
} catch (GLib.Error e) {
|
||||
tunnel_request.reject (e);
|
||||
tunnel_request = null;
|
||||
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MacOSTunnel : Object, Tunnel {
|
||||
public DiscoveryService discovery {
|
||||
get {
|
||||
return _discovery;
|
||||
}
|
||||
}
|
||||
|
||||
public Darwin.Xpc.Uuid? assertion_identifier {
|
||||
get {
|
||||
return _assertion_identifier;
|
||||
}
|
||||
}
|
||||
|
||||
private XpcClient pairing_device;
|
||||
private Darwin.Xpc.Uuid? _assertion_identifier;
|
||||
private InetAddress? tunnel_device_address;
|
||||
private DiscoveryService? _discovery;
|
||||
|
||||
public MacOSTunnel (XpcClient pairing_device) {
|
||||
this.pairing_device = pairing_device;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
var r = new PairingdRequest ("RemotePairing.ReleaseAssertionRequest");
|
||||
r.body.set_value ("assertionIdentifier", _assertion_identifier);
|
||||
try {
|
||||
yield pairing_device.request (r.message, cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async void attach (Cancellable? cancellable) throws Error, IOError {
|
||||
var r = new PairingdRequest ("RemotePairing.CreateAssertionCommand");
|
||||
r.body.set_int64 ("flags", 0);
|
||||
var response = yield pairing_device.request (r.message, cancellable);
|
||||
|
||||
var reader = new XpcObjectReader (response);
|
||||
reader.read_member ("response");
|
||||
|
||||
_assertion_identifier = (Darwin.Xpc.Uuid) reader
|
||||
.read_member ("assertionIdentifier")
|
||||
.get_object_value (Darwin.Xpc.Uuid.TYPE);
|
||||
reader.end_member ();
|
||||
|
||||
string tunnel_ip_address = reader
|
||||
.read_member ("info")
|
||||
.read_member ("tunnelIPAddress")
|
||||
.get_string_value ();
|
||||
tunnel_device_address = new InetAddress.from_string (tunnel_ip_address);
|
||||
|
||||
_discovery = yield locate_discovery_service (tunnel_device_address, cancellable);
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
SocketConnection connection;
|
||||
try {
|
||||
var service_address = new InetSocketAddress (tunnel_device_address, port);
|
||||
|
||||
var client = new SocketClient ();
|
||||
connection = yield client.connect_async (service_address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
private static async DiscoveryService locate_discovery_service (InetAddress tunnel_device_address, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
var path_buf = new char[4096];
|
||||
unowned string path = (string) path_buf;
|
||||
|
||||
uint delays[] = { 0, 50, 250 };
|
||||
for (uint attempts = 0; attempts != delays.length; attempts++) {
|
||||
uint delay = delays[attempts];
|
||||
if (delay != 0) {
|
||||
var timeout_source = new TimeoutSource (delay);
|
||||
timeout_source.set_callback (locate_discovery_service.callback);
|
||||
timeout_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (locate_discovery_service.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
timeout_source.destroy ();
|
||||
|
||||
if (cancellable.is_cancelled ())
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var item in XNU.query_active_tcp_connections ()) {
|
||||
if (item.family != IPV6)
|
||||
continue;
|
||||
if (!item.foreign_address.equal (tunnel_device_address))
|
||||
continue;
|
||||
if (Darwin.XNU.proc_pidpath (item.effective_pid, path_buf) <= 0)
|
||||
continue;
|
||||
if (path != "/usr/libexec/remoted")
|
||||
continue;
|
||||
|
||||
try {
|
||||
var connectable = new InetSocketAddress (tunnel_device_address, item.foreign_port);
|
||||
|
||||
var sc = new SocketClient ();
|
||||
SocketConnection connection = yield sc.connect_async (connectable, cancellable);
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return yield DiscoveryService.open (connection, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect RSD port");
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PairingdRequest {
|
||||
public Darwin.Xpc.Dictionary message = new Darwin.Xpc.Dictionary ();
|
||||
public Darwin.Xpc.Dictionary body = new Darwin.Xpc.Dictionary ();
|
||||
|
||||
public PairingdRequest (string name) {
|
||||
message.set_string ("mangledTypeName", name);
|
||||
message.set_value ("value", body);
|
||||
}
|
||||
}
|
||||
|
||||
namespace XNU {
|
||||
public PcbList query_active_tcp_connections () {
|
||||
size_t size = 0;
|
||||
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", null, &size);
|
||||
|
||||
var pcbs = new uint8[size];
|
||||
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", pcbs, &size);
|
||||
|
||||
return new PcbList (pcbs);
|
||||
}
|
||||
|
||||
public class PcbList {
|
||||
private uint8[] pcbs;
|
||||
|
||||
internal PcbList (owned uint8[] pcbs) {
|
||||
this.pcbs = (owned) pcbs;
|
||||
}
|
||||
|
||||
public Iterator iterator () {
|
||||
return new Iterator (this);
|
||||
}
|
||||
|
||||
public class Iterator {
|
||||
private PcbList list;
|
||||
private InetItem * cursor;
|
||||
|
||||
internal Iterator (PcbList list) {
|
||||
this.list = list;
|
||||
|
||||
var gen = (Darwin.XNU.InetPcbGeneration *) list.pcbs;
|
||||
cursor = (InetItem *) ((uint8 *) list.pcbs + gen->length);
|
||||
}
|
||||
|
||||
public Item? next_value () {
|
||||
InetPcb * pcb = null;
|
||||
while (true) {
|
||||
if (cursor->length == 24)
|
||||
return null;
|
||||
|
||||
switch (cursor->kind) {
|
||||
case InetItemKind.PCB:
|
||||
pcb = (InetPcb *) cursor;
|
||||
break;
|
||||
case InetItemKind.SOCKET:
|
||||
var item = new Item (*pcb, *((InetSocket *) cursor));
|
||||
advance ();
|
||||
return item;
|
||||
}
|
||||
|
||||
advance ();
|
||||
}
|
||||
}
|
||||
|
||||
private void advance () {
|
||||
uint32 l = cursor->length;
|
||||
if (l % 8 != 0)
|
||||
l += 8 - (l % 8);
|
||||
cursor = (InetItem *) ((uint8 *) cursor + l);
|
||||
}
|
||||
}
|
||||
|
||||
public class Item {
|
||||
public SocketFamily family {
|
||||
get {
|
||||
return ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
? SocketFamily.IPV6
|
||||
: SocketFamily.IPV4;
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress local_address {
|
||||
get {
|
||||
if (cached_local_address == null)
|
||||
cached_local_address = parse_address (pcb.local_address);
|
||||
return cached_local_address;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 local_port {
|
||||
get {
|
||||
return uint16.from_big_endian (pcb.local_port);
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress foreign_address {
|
||||
get {
|
||||
if (cached_foreign_address == null)
|
||||
cached_foreign_address = parse_address (pcb.foreign_address);
|
||||
return cached_foreign_address;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 foreign_port {
|
||||
get {
|
||||
return uint16.from_big_endian (pcb.foreign_port);
|
||||
}
|
||||
}
|
||||
|
||||
public int32 effective_pid {
|
||||
get {
|
||||
return sock.effective_pid;
|
||||
}
|
||||
}
|
||||
|
||||
private InetPcb pcb;
|
||||
private InetSocket sock;
|
||||
private InetAddress? cached_local_address;
|
||||
private InetAddress? cached_foreign_address;
|
||||
|
||||
public Item (InetPcb pcb, InetSocket sock) {
|
||||
this.pcb = pcb;
|
||||
this.sock = sock;
|
||||
}
|
||||
|
||||
private InetAddress parse_address (uint8[] bytes) {
|
||||
if ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
return new InetAddress.from_bytes (bytes, IPV6);
|
||||
var addr = (Darwin.XNU.InetAddr4in6 *) bytes;
|
||||
return new InetAddress.from_bytes ((uint8[]) &addr->addr4.s_addr, IPV4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetItem {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
}
|
||||
|
||||
public enum InetItemKind {
|
||||
SOCKET = 0x001,
|
||||
PCB = 0x010,
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetPcb {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
|
||||
public uint64 inpp;
|
||||
public uint16 foreign_port;
|
||||
public uint16 local_port;
|
||||
public uint32 per_protocol_pcb_low;
|
||||
public uint32 per_protocol_pcb_high;
|
||||
public uint32 generation_count_low;
|
||||
public uint32 generation_count_high;
|
||||
public int flags;
|
||||
public uint32 flow;
|
||||
public uint8 version_flag;
|
||||
public uint8 ip_ttl;
|
||||
public uint8 ip_protocol;
|
||||
public uint8 padding;
|
||||
public uint8 foreign_address[16];
|
||||
public uint8 local_address[16];
|
||||
public InetDepend4 depend4;
|
||||
public InetDepend6 depend6;
|
||||
public uint32 flowhash;
|
||||
public uint32 flags2;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetSocket {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
|
||||
public uint64 so;
|
||||
public int16 type;
|
||||
public uint16 options_low;
|
||||
public uint16 options_high;
|
||||
public int16 linger;
|
||||
public int16 state;
|
||||
public uint16 pcb[4];
|
||||
public uint16 protocol_low;
|
||||
public uint16 protocol_high;
|
||||
public uint16 family_low;
|
||||
public uint16 family_high;
|
||||
public int16 qlen;
|
||||
public int16 incqlen;
|
||||
public int16 qlimit;
|
||||
public int16 timeo;
|
||||
public uint16 error;
|
||||
public int32 pgid;
|
||||
public uint32 oobmark;
|
||||
public uint32 uid;
|
||||
public int32 last_pid;
|
||||
public int32 effective_pid;
|
||||
public uint64 gencnt;
|
||||
public uint32 flags;
|
||||
public uint32 flags1;
|
||||
public int32 usecount;
|
||||
public int32 retaincnt;
|
||||
public uint32 filter_flags;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetDepend4 {
|
||||
public uint8 ip_tos;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetDepend6 {
|
||||
public uint8 hlim;
|
||||
public int checksum;
|
||||
public uint16 interface_index;
|
||||
public int16 hops;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "frida-base.h"
|
||||
|
||||
void
|
||||
_frida_fruity_host_session_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
_frida_fruity_usbmux_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
GError ** error)
|
||||
{
|
||||
*name = g_strdup ("iOS Device");
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
#include "../windows/icon-helpers.h"
|
||||
|
||||
#include <setupapi.h>
|
||||
#include <devguid.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <setupapi.h>
|
||||
#include <windns.h>
|
||||
|
||||
typedef struct _FridaPairingBrowserBackend FridaPairingBrowserBackend;
|
||||
|
||||
typedef struct _FridaMobileDeviceInfo FridaMobileDeviceInfo;
|
||||
typedef struct _FridaImageDeviceInfo FridaImageDeviceInfo;
|
||||
@@ -13,8 +17,18 @@ typedef struct _FridaFindImageDeviceContext FridaFindImageDeviceContext;
|
||||
|
||||
typedef struct _FridaDeviceInfo FridaDeviceInfo;
|
||||
|
||||
typedef struct _FridaDnsApi FridaDnsApi;
|
||||
|
||||
typedef gboolean (* FridaEnumerateDeviceFunc) (const FridaDeviceInfo * device_info, gpointer user_data);
|
||||
|
||||
struct _FridaPairingBrowserBackend
|
||||
{
|
||||
FridaFruityWindowsPairingBrowserResultCallback callback;
|
||||
gpointer callback_target;
|
||||
|
||||
DNS_SERVICE_CANCEL browse_handle;
|
||||
};
|
||||
|
||||
struct _FridaMobileDeviceInfo
|
||||
{
|
||||
WCHAR * location;
|
||||
@@ -49,6 +63,14 @@ struct _FridaDeviceInfo
|
||||
PSP_DEVINFO_DATA device_info_data;
|
||||
};
|
||||
|
||||
struct _FridaDnsApi
|
||||
{
|
||||
DNS_STATUS (WINAPI * browse) (DNS_SERVICE_BROWSE_REQUEST * request, DNS_SERVICE_CANCEL * cancel);
|
||||
DNS_STATUS (WINAPI * browse_cancel) (DNS_SERVICE_CANCEL * cancel_handle);
|
||||
};
|
||||
|
||||
static void WINAPI frida_fruity_windows_pairing_browser_on_browse_results (void * query_context, DNS_QUERY_RESULT * query_results);
|
||||
|
||||
static FridaMobileDeviceInfo * find_mobile_device_by_udid (const WCHAR * udid);
|
||||
static FridaImageDeviceInfo * find_image_device_by_location (const WCHAR * location);
|
||||
|
||||
@@ -68,10 +90,110 @@ static WCHAR * frida_read_registry_string (HKEY key, WCHAR * value_name);
|
||||
static WCHAR * frida_read_registry_multi_string (HKEY key, WCHAR * value_name);
|
||||
static gpointer frida_read_registry_value (HKEY key, WCHAR * value_name, DWORD expected_type);
|
||||
|
||||
static FridaDnsApi * frida_try_get_dns_api (void);
|
||||
|
||||
static GUID GUID_APPLE_USB = { 0xF0B32BE3, 0x6678, 0x4879, { 0x92, 0x30, 0x0E4, 0x38, 0x45, 0xD8, 0x05, 0xEE } };
|
||||
|
||||
void
|
||||
_frida_fruity_host_session_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
_frida_fruity_windows_pairing_browser_enumerate_network_interfaces (FridaFruityWindowsPairingBrowserNetifFoundFunc func,
|
||||
gpointer func_target)
|
||||
{
|
||||
IP_ADAPTER_ADDRESSES * adapters;
|
||||
ULONG buffer_size, result;
|
||||
IP_ADAPTER_ADDRESSES_LH * adapter;
|
||||
|
||||
if (frida_try_get_dns_api () == NULL)
|
||||
return;
|
||||
|
||||
buffer_size = 32768;
|
||||
adapters = g_malloc (buffer_size);
|
||||
|
||||
do
|
||||
{
|
||||
result = GetAdaptersAddresses (AF_INET6, 0, NULL, adapters, &buffer_size);
|
||||
if (result != ERROR_BUFFER_OVERFLOW)
|
||||
break;
|
||||
adapters = g_realloc (adapters, buffer_size);
|
||||
}
|
||||
while (result == ERROR_BUFFER_OVERFLOW);
|
||||
if (result != ERROR_SUCCESS)
|
||||
goto beach;
|
||||
|
||||
for (adapter = adapters; adapter != NULL; adapter = adapter->Next)
|
||||
{
|
||||
SOCKET_ADDRESS * raw_addr;
|
||||
GInetSocketAddress * addr;
|
||||
|
||||
if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
|
||||
continue;
|
||||
|
||||
if (adapter->FirstUnicastAddress == NULL)
|
||||
continue;
|
||||
|
||||
raw_addr = &adapter->FirstUnicastAddress->Address;
|
||||
addr = G_INET_SOCKET_ADDRESS (g_socket_address_new_from_native (raw_addr->lpSockaddr, raw_addr->iSockaddrLength));
|
||||
|
||||
func (adapter->IfIndex, adapter->AdapterName, addr, func_target);
|
||||
}
|
||||
|
||||
beach:
|
||||
g_free (adapters);
|
||||
}
|
||||
|
||||
void *
|
||||
_frida_fruity_windows_pairing_browser_monitor_create_backend (gulong interface_index,
|
||||
FridaFruityWindowsPairingBrowserResultCallback callback, gpointer callback_target)
|
||||
{
|
||||
FridaPairingBrowserBackend * backend;
|
||||
FridaDnsApi * api;
|
||||
DNS_SERVICE_BROWSE_REQUEST r = { 0, };
|
||||
|
||||
api = frida_try_get_dns_api ();
|
||||
if (api == NULL)
|
||||
return NULL;
|
||||
|
||||
backend = g_slice_new0 (FridaPairingBrowserBackend);
|
||||
backend->callback = callback;
|
||||
backend->callback_target = callback_target;
|
||||
|
||||
r.Version = DNS_QUERY_REQUEST_VERSION2;
|
||||
r.InterfaceIndex = interface_index;
|
||||
r.QueryName = G_PASTE (L, FRIDA_FRUITY_PAIRING_SERVICE_DNS_SD_NAME);
|
||||
r.pBrowseCallbackV2 = frida_fruity_windows_pairing_browser_on_browse_results;
|
||||
r.pQueryContext = backend;
|
||||
|
||||
if (api->browse (&r, &backend->browse_handle) != DNS_REQUEST_PENDING)
|
||||
{
|
||||
g_slice_free (FridaPairingBrowserBackend, backend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return backend;
|
||||
}
|
||||
|
||||
void
|
||||
_frida_fruity_windows_pairing_browser_monitor_destroy_backend (void * opaque_backend)
|
||||
{
|
||||
FridaPairingBrowserBackend * backend = opaque_backend;
|
||||
|
||||
if (backend == NULL)
|
||||
return;
|
||||
|
||||
frida_try_get_dns_api ()->browse_cancel (&backend->browse_handle);
|
||||
|
||||
g_slice_free (FridaPairingBrowserBackend, backend);
|
||||
}
|
||||
|
||||
static void WINAPI
|
||||
frida_fruity_windows_pairing_browser_on_browse_results (void * query_context, DNS_QUERY_RESULT * query_results)
|
||||
{
|
||||
FridaPairingBrowserBackend * backend = query_context;
|
||||
|
||||
backend->callback (query_results, backend->callback_target);
|
||||
}
|
||||
|
||||
void
|
||||
_frida_fruity_usbmux_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
GError ** error)
|
||||
{
|
||||
gboolean result = FALSE;
|
||||
@@ -426,3 +548,30 @@ beach:
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static FridaDnsApi *
|
||||
frida_try_get_dns_api (void)
|
||||
{
|
||||
static gsize api_value = 0;
|
||||
|
||||
if (g_once_init_enter (&api_value))
|
||||
{
|
||||
HMODULE mod;
|
||||
FARPROC browse;
|
||||
FridaDnsApi * api = NULL;
|
||||
|
||||
mod = GetModuleHandleW (L"dnsapi.dll");
|
||||
|
||||
browse = GetProcAddress (mod, "DnsServiceBrowse");
|
||||
if (browse != NULL)
|
||||
{
|
||||
api = g_slice_new (FridaDnsApi);
|
||||
api->browse = (gpointer) browse;
|
||||
api->browse_cancel = (gpointer) GetProcAddress (mod, "DnsServiceBrowseCancel");
|
||||
}
|
||||
|
||||
g_once_init_leave (&api_value, GPOINTER_TO_SIZE (api) + 1);
|
||||
}
|
||||
|
||||
return GSIZE_TO_POINTER (api_value - 1);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class WindowsPairingBrowser : Object, PairingBrowser {
|
||||
private Gee.Map<string, Monitor> monitors = new Gee.HashMap<string, Monitor> ();
|
||||
|
||||
private MainContext main_context = MainContext.ref_thread_default ();
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
_enumerate_network_interfaces ((index, name, address) => {
|
||||
var monitor = new Monitor (this, index, address);
|
||||
monitors[name] = monitor;
|
||||
});
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
monitors.clear ();
|
||||
}
|
||||
|
||||
private void on_result (WinDns.QueryResult * result, InetSocketAddress interface_address) {
|
||||
try {
|
||||
PairingServiceDetails service = parse_result (result, interface_address);
|
||||
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
service_discovered (service);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static PairingServiceDetails parse_result (WinDns.QueryResult * res,
|
||||
InetSocketAddress interface_address) throws GLib.Error {
|
||||
PairingServiceMetadata? meta = null;
|
||||
InetAddress? ip = null;
|
||||
uint16 port = 0;
|
||||
for (WinDns.Record * r = res->query_records; r != null; r = r->next) {
|
||||
switch (r->type) {
|
||||
case TEXT:
|
||||
WinDns.TxtData * txt = &r->txt;
|
||||
var txt_record = new Gee.ArrayList<string> ();
|
||||
foreach (unowned string16 str in txt->strings)
|
||||
txt_record.add (str.to_utf8 ());
|
||||
meta = PairingServiceMetadata.from_txt_record (txt_record);
|
||||
break;
|
||||
case AAAA:
|
||||
ip = new InetAddress.from_bytes (r->aaaa.ip.data, IPV6);
|
||||
break;
|
||||
case SRV:
|
||||
port = r->srv.port;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (meta == null || ip == null || port == 0)
|
||||
throw new Error.PROTOCOL ("Incomplete result");
|
||||
|
||||
return new PairingServiceDetails () {
|
||||
identifier = meta.identifier,
|
||||
auth_tag = meta.auth_tag,
|
||||
endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: ip,
|
||||
port: port,
|
||||
scope_id: ip.get_is_link_local () ? interface_address.get_scope_id () : 0
|
||||
),
|
||||
interface_address = interface_address,
|
||||
};
|
||||
}
|
||||
|
||||
private class Monitor {
|
||||
private weak WindowsPairingBrowser parent;
|
||||
private InetSocketAddress interface_address;
|
||||
|
||||
private void * backend;
|
||||
|
||||
public Monitor (WindowsPairingBrowser parent, ulong interface_index, InetSocketAddress interface_address) {
|
||||
this.parent = parent;
|
||||
this.interface_address = interface_address;
|
||||
|
||||
backend = _create_backend (interface_index, on_result);
|
||||
}
|
||||
|
||||
~Monitor () {
|
||||
_destroy_backend (backend);
|
||||
}
|
||||
|
||||
private void on_result (void * result) {
|
||||
parent.on_result (result, interface_address);
|
||||
}
|
||||
|
||||
public extern static void * _create_backend (ulong interface_index, ResultCallback callback);
|
||||
public extern static void _destroy_backend (void * backend);
|
||||
}
|
||||
|
||||
public extern static void _enumerate_network_interfaces (NetifFoundFunc func);
|
||||
|
||||
public delegate void NetifFoundFunc (ulong index, string identifier, owned InetSocketAddress address);
|
||||
public delegate void ResultCallback (void * result);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,174 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class DnsPacketReader {
|
||||
private BufferReader reader;
|
||||
|
||||
public DnsPacketReader (Bytes packet) {
|
||||
reader = new BufferReader (new Buffer (packet, BIG_ENDIAN));
|
||||
}
|
||||
|
||||
public DnsPtrRecord read_ptr () throws Error {
|
||||
var rr = read_record ();
|
||||
if (rr.key.type != PTR)
|
||||
throw new Error.PROTOCOL ("Expected a PTR record");
|
||||
if (rr.key.klass != IN)
|
||||
throw new Error.PROTOCOL ("Expected a PTR record of class IN");
|
||||
var subreader = new DnsPacketReader (rr.data);
|
||||
var name = subreader.read_name ();
|
||||
return new DnsPtrRecord () {
|
||||
key = rr.key,
|
||||
ttl = rr.ttl,
|
||||
data = rr.data,
|
||||
name = name,
|
||||
};
|
||||
}
|
||||
|
||||
public DnsTxtRecord read_txt () throws Error {
|
||||
var rr = read_record ();
|
||||
if (rr.key.type != TXT)
|
||||
throw new Error.PROTOCOL ("Expected a TXT record");
|
||||
if (rr.key.klass != IN)
|
||||
throw new Error.PROTOCOL ("Expected a TXT record of class IN");
|
||||
var entries = new Gee.ArrayList<string> ();
|
||||
var subreader = new DnsPacketReader (rr.data);
|
||||
while (subreader.reader.available != 0) {
|
||||
string text = subreader.read_string ();
|
||||
entries.add (text);
|
||||
}
|
||||
return new DnsTxtRecord () {
|
||||
key = rr.key,
|
||||
ttl = rr.ttl,
|
||||
data = rr.data,
|
||||
entries = entries.to_array (),
|
||||
};
|
||||
}
|
||||
|
||||
public DnsAaaaRecord read_aaaa () throws Error {
|
||||
var rr = read_record ();
|
||||
if (rr.key.type != AAAA)
|
||||
throw new Error.PROTOCOL ("Expected an AAAA record");
|
||||
if (rr.key.klass != IN)
|
||||
throw new Error.PROTOCOL ("Expected an AAAA record of class IN");
|
||||
var subreader = new DnsPacketReader (rr.data);
|
||||
var raw_address = subreader.reader.read_bytes (16);
|
||||
var address = new InetAddress.from_bytes (raw_address.get_data (), IPV6);
|
||||
return new DnsAaaaRecord () {
|
||||
key = rr.key,
|
||||
ttl = rr.ttl,
|
||||
data = rr.data,
|
||||
address = address,
|
||||
};
|
||||
}
|
||||
|
||||
public DnsSrvRecord read_srv () throws Error {
|
||||
var rr = read_record ();
|
||||
if (rr.key.type != SRV)
|
||||
throw new Error.PROTOCOL ("Expected a SRV record");
|
||||
if (rr.key.klass != IN)
|
||||
throw new Error.PROTOCOL ("Expected a SRV record of class IN");
|
||||
var subreader = new DnsPacketReader (rr.data);
|
||||
var priority = subreader.reader.read_uint16 ();
|
||||
var weight = subreader.reader.read_uint16 ();
|
||||
var port = subreader.reader.read_uint16 ();
|
||||
var name = subreader.read_name ();
|
||||
return new DnsSrvRecord () {
|
||||
key = rr.key,
|
||||
ttl = rr.ttl,
|
||||
data = rr.data,
|
||||
priority = priority,
|
||||
weight = weight,
|
||||
port = port,
|
||||
name = name,
|
||||
};
|
||||
}
|
||||
|
||||
public DnsResourceRecord read_record () throws Error {
|
||||
var key = read_key ();
|
||||
var ttl = reader.read_uint32 ();
|
||||
var size = reader.read_uint16 ();
|
||||
var data = reader.read_bytes (size);
|
||||
return new DnsResourceRecord () {
|
||||
key = key,
|
||||
ttl = ttl,
|
||||
data = data,
|
||||
};
|
||||
}
|
||||
|
||||
public DnsResourceKey read_key () throws Error {
|
||||
var name = read_name ();
|
||||
var type = reader.read_uint16 ();
|
||||
var klass = reader.read_uint16 ();
|
||||
return new DnsResourceKey () {
|
||||
name = name,
|
||||
type = type,
|
||||
klass = klass,
|
||||
};
|
||||
}
|
||||
|
||||
public string read_name () throws Error {
|
||||
var name = new StringBuilder.sized (256);
|
||||
|
||||
while (true) {
|
||||
size_t size = reader.read_uint8 ();
|
||||
if (size == 0)
|
||||
break;
|
||||
if (size > 63)
|
||||
throw new Error.PROTOCOL ("Invalid DNS name length");
|
||||
|
||||
var label = reader.read_fixed_string (size);
|
||||
if (name.len != 0)
|
||||
name.append_c ('.');
|
||||
name.append (label);
|
||||
}
|
||||
|
||||
return name.str;
|
||||
}
|
||||
|
||||
public string read_string () throws Error {
|
||||
size_t size = reader.read_uint8 ();
|
||||
return reader.read_fixed_string (size);
|
||||
}
|
||||
}
|
||||
|
||||
public class DnsPtrRecord : DnsResourceRecord {
|
||||
public string name;
|
||||
}
|
||||
|
||||
public class DnsTxtRecord : DnsResourceRecord {
|
||||
public string[] entries;
|
||||
}
|
||||
|
||||
public class DnsAaaaRecord : DnsResourceRecord {
|
||||
public InetAddress address;
|
||||
}
|
||||
|
||||
public class DnsSrvRecord : DnsResourceRecord {
|
||||
public uint16 priority;
|
||||
public uint16 weight;
|
||||
public uint16 port;
|
||||
public string name;
|
||||
}
|
||||
|
||||
public class DnsResourceRecord {
|
||||
public DnsResourceKey key;
|
||||
public uint32 ttl;
|
||||
public Bytes data;
|
||||
}
|
||||
|
||||
public class DnsResourceKey {
|
||||
public string name;
|
||||
public DnsRecordType type;
|
||||
public DnsRecordClass klass;
|
||||
}
|
||||
|
||||
public enum DnsRecordType {
|
||||
PTR = 12,
|
||||
TXT = 16,
|
||||
AAAA = 28,
|
||||
SRV = 33,
|
||||
}
|
||||
|
||||
public enum DnsRecordClass {
|
||||
IN = 1,
|
||||
}
|
||||
}
|
||||
+17
-4
@@ -504,6 +504,14 @@ namespace Frida.Fruity {
|
||||
process_incoming_fragments.begin ();
|
||||
}
|
||||
|
||||
public override void dispose () {
|
||||
foreach (var channel in channels.values)
|
||||
channel.transport = null;
|
||||
channels.clear ();
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
private async void close (Cancellable? cancellable) throws IOError {
|
||||
io_cancellable.cancel ();
|
||||
|
||||
@@ -878,9 +886,9 @@ namespace Frida.Fruity {
|
||||
construct;
|
||||
}
|
||||
|
||||
public weak DTXTransport transport {
|
||||
public weak DTXTransport? transport {
|
||||
get;
|
||||
construct;
|
||||
set;
|
||||
}
|
||||
|
||||
public State state {
|
||||
@@ -903,7 +911,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public override void dispose () {
|
||||
transport.remove_channel (this);
|
||||
close ();
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
@@ -915,6 +923,11 @@ namespace Frida.Fruity {
|
||||
var error = new Error.TRANSPORT ("Channel closed");
|
||||
foreach (var request in pending_responses.values.to_array ())
|
||||
request.reject (error);
|
||||
|
||||
if (transport != null) {
|
||||
transport.remove_channel (this);
|
||||
transport = null;
|
||||
}
|
||||
}
|
||||
|
||||
public async NSObject? invoke (string method_name, DTXArgumentListBuilder? args, Cancellable? cancellable)
|
||||
@@ -1103,7 +1116,7 @@ namespace Frida.Fruity {
|
||||
NSObject? obj = NSKeyedArchive.decode (buf);
|
||||
if (obj != null) {
|
||||
var gval = Value (Type.from_instance (obj));
|
||||
gval.set_boxed (obj);
|
||||
gval.set_instance (obj);
|
||||
elements += (owned) gval;
|
||||
} else {
|
||||
var gval = Value (typeof (NSObject));
|
||||
|
||||
+179
-511
@@ -1,129 +1,24 @@
|
||||
namespace Frida {
|
||||
public class FruityHostSessionBackend : Object, HostSessionBackend {
|
||||
private Fruity.UsbmuxClient control_client;
|
||||
|
||||
private Gee.HashSet<uint> devices = new Gee.HashSet<uint> ();
|
||||
private Gee.HashMap<uint, FruityHostSessionProvider> providers = new Gee.HashMap<uint, FruityHostSessionProvider> ();
|
||||
|
||||
private Promise<bool> start_request;
|
||||
private Cancellable start_cancellable;
|
||||
private SourceFunc on_start_completed;
|
||||
private Fruity.DeviceMonitor device_monitor = new Fruity.DeviceMonitor ();
|
||||
private Gee.Map<Fruity.Device, FruityHostSessionProvider> providers =
|
||||
new Gee.HashMap<Fruity.Device, FruityHostSessionProvider> ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
construct {
|
||||
device_monitor.device_attached.connect (on_device_attached);
|
||||
device_monitor.device_detached.connect (on_device_detached);
|
||||
}
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
start_request = new Promise<bool> ();
|
||||
start_cancellable = new Cancellable ();
|
||||
on_start_completed = start.callback;
|
||||
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
var timeout_source = new TimeoutSource (500);
|
||||
timeout_source.set_callback (start.callback);
|
||||
timeout_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (start.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
do_start.begin ();
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
timeout_source.destroy ();
|
||||
on_start_completed = null;
|
||||
}
|
||||
|
||||
private async void do_start () {
|
||||
bool success = yield try_start_control_connection ();
|
||||
if (success) {
|
||||
/* Perform a dummy-request to flush out any pending device attach notifications. */
|
||||
try {
|
||||
yield control_client.connect_to_port (Fruity.DeviceId (uint.MAX), 0, start_cancellable);
|
||||
assert_not_reached ();
|
||||
} catch (GLib.Error expected_error) {
|
||||
if (expected_error.code == IOError.CONNECTION_CLOSED) {
|
||||
/* Deal with usbmuxd closing the connection when receiving commands in the wrong state. */
|
||||
control_client.close.begin (null);
|
||||
|
||||
success = yield try_start_control_connection ();
|
||||
if (success) {
|
||||
Fruity.UsbmuxClient flush_client = null;
|
||||
try {
|
||||
flush_client = yield Fruity.UsbmuxClient.open (start_cancellable);
|
||||
try {
|
||||
yield flush_client.connect_to_port (
|
||||
Fruity.DeviceId (uint.MAX), 0,
|
||||
start_cancellable);
|
||||
assert_not_reached ();
|
||||
} catch (GLib.Error expected_error) {
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (flush_client != null)
|
||||
flush_client.close.begin (null);
|
||||
|
||||
if (!success && control_client != null) {
|
||||
control_client.close.begin (null);
|
||||
control_client = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_request.resolve (success);
|
||||
|
||||
if (on_start_completed != null)
|
||||
on_start_completed ();
|
||||
}
|
||||
|
||||
private async bool try_start_control_connection () {
|
||||
bool success = true;
|
||||
|
||||
try {
|
||||
control_client = yield Fruity.UsbmuxClient.open (start_cancellable);
|
||||
|
||||
control_client.device_attached.connect ((details) => {
|
||||
add_device.begin (details);
|
||||
});
|
||||
control_client.device_detached.connect ((id) => {
|
||||
remove_device (id);
|
||||
});
|
||||
|
||||
yield control_client.enable_listen_mode (start_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!success && control_client != null) {
|
||||
control_client.close.begin (null);
|
||||
control_client = null;
|
||||
}
|
||||
|
||||
return success;
|
||||
yield device_monitor.start (cancellable);
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
start_cancellable.cancel ();
|
||||
|
||||
try {
|
||||
yield start_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
if (control_client != null) {
|
||||
yield control_client.close (cancellable);
|
||||
control_client = null;
|
||||
}
|
||||
|
||||
io_cancellable.cancel ();
|
||||
|
||||
devices.clear ();
|
||||
yield device_monitor.stop (cancellable);
|
||||
|
||||
foreach (var provider in providers.values) {
|
||||
provider_unavailable (provider);
|
||||
@@ -132,148 +27,64 @@ namespace Frida {
|
||||
providers.clear ();
|
||||
}
|
||||
|
||||
private async void add_device (Fruity.DeviceDetails details) {
|
||||
var id = details.id;
|
||||
var raw_id = id.raw_value;
|
||||
if (devices.contains (raw_id))
|
||||
return;
|
||||
devices.add (raw_id);
|
||||
|
||||
string? name = null;
|
||||
Variant? icon = null;
|
||||
|
||||
if (details.connection_type == USB) {
|
||||
bool got_details = false;
|
||||
for (int i = 1; !got_details && devices.contains (raw_id); i++) {
|
||||
try {
|
||||
_extract_details_for_device (details.product_id.raw_value, details.udid.raw_value,
|
||||
out name, out icon);
|
||||
got_details = true;
|
||||
} catch (Error e) {
|
||||
if (i != 20) {
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
var delay_source = new TimeoutSource.seconds (1);
|
||||
delay_source.set_callback (add_device.callback);
|
||||
delay_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (io_cancellable);
|
||||
cancel_source.set_callback (add_device.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
delay_source.destroy ();
|
||||
|
||||
if (io_cancellable.is_cancelled ())
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!devices.contains (raw_id))
|
||||
return;
|
||||
if (!got_details) {
|
||||
remove_device (id);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
name = "iOS Device [%s]".printf (details.network_address.address.to_string ());
|
||||
}
|
||||
|
||||
var provider = new FruityHostSessionProvider (name, icon, details);
|
||||
providers[raw_id] = provider;
|
||||
|
||||
private void on_device_attached (Fruity.Device device) {
|
||||
var provider = new FruityHostSessionProvider (device);
|
||||
providers[device] = provider;
|
||||
provider_available (provider);
|
||||
}
|
||||
|
||||
private void remove_device (Fruity.DeviceId id) {
|
||||
var raw_id = id.raw_value;
|
||||
if (!devices.contains (raw_id))
|
||||
return;
|
||||
devices.remove (raw_id);
|
||||
|
||||
private void on_device_detached (Fruity.Device device) {
|
||||
FruityHostSessionProvider provider;
|
||||
if (providers.unset (raw_id, out provider)) {
|
||||
if (providers.unset (device, out provider)) {
|
||||
provider_unavailable (provider);
|
||||
provider.close.begin (io_cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
public extern static void _extract_details_for_device (int product_id, string udid, out string name,
|
||||
out Variant? icon) throws Error;
|
||||
}
|
||||
|
||||
public class FruityHostSessionProvider : Object, HostSessionProvider, HostChannelProvider, HostServiceProvider,
|
||||
FruityLockdownProvider, Pairable {
|
||||
public class FruityHostSessionProvider : Object, HostSessionProvider, HostChannelProvider, HostServiceProvider, Pairable {
|
||||
public Fruity.Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public string id {
|
||||
get { return device_details.udid.raw_value; }
|
||||
get { return device.udid; }
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return device_name; }
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return device_icon; }
|
||||
get { return device.icon; }
|
||||
}
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
get {
|
||||
return (device_details.connection_type == USB)
|
||||
return (device.connection_type == USB)
|
||||
? HostSessionProviderKind.USB
|
||||
: HostSessionProviderKind.REMOTE;
|
||||
}
|
||||
}
|
||||
|
||||
public string device_name {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Variant? device_icon {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Fruity.DeviceDetails device_details {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private FruityHostSession? host_session;
|
||||
private Promise<Fruity.Tunnel?>? tunnel_request;
|
||||
private string _name;
|
||||
|
||||
public FruityHostSessionProvider (string name, Variant? icon, Fruity.DeviceDetails details) {
|
||||
Object (
|
||||
device_name: name,
|
||||
device_icon: icon,
|
||||
device_details: details
|
||||
);
|
||||
public FruityHostSessionProvider (Fruity.Device device) {
|
||||
Object (device: device);
|
||||
_name = device.name;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
yield Fruity.DTXConnection.close_all (this, cancellable);
|
||||
|
||||
if (tunnel_request != null) {
|
||||
Fruity.Tunnel? tunnel = null;
|
||||
try {
|
||||
tunnel = yield find_tunnel (cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
|
||||
if (tunnel != null)
|
||||
yield tunnel.close (cancellable);
|
||||
}
|
||||
yield Fruity.DTXConnection.close_all (device, cancellable);
|
||||
}
|
||||
|
||||
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != null)
|
||||
throw new Error.INVALID_OPERATION ("Already created");
|
||||
|
||||
host_session = new FruityHostSession (this, this);
|
||||
host_session = new FruityHostSession (device);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
@@ -302,128 +113,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async IOStream open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
if (address.has_prefix ("tcp:")) {
|
||||
ulong raw_port;
|
||||
if (!ulong.try_parse (address.substring (4), out raw_port) || raw_port == 0 || raw_port > uint16.MAX)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid TCP port");
|
||||
uint16 port = (uint16) raw_port;
|
||||
|
||||
if (device_details.connection_type == USB) {
|
||||
Fruity.UsbmuxClient client = null;
|
||||
try {
|
||||
client = yield Fruity.UsbmuxClient.open (cancellable);
|
||||
|
||||
yield client.connect_to_port (device_details.id, port, cancellable);
|
||||
|
||||
return client.connection;
|
||||
} catch (GLib.Error e) {
|
||||
if (client != null)
|
||||
client.close.begin ();
|
||||
|
||||
if (e is Fruity.UsbmuxError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
InetSocketAddress device_address = device_details.network_address;
|
||||
var target_address = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: device_address.address,
|
||||
port: port,
|
||||
flowinfo: device_address.flowinfo,
|
||||
scope_id: device_address.scope_id
|
||||
);
|
||||
|
||||
var client = new SocketClient ();
|
||||
var connection = yield client.connect_async (target_address, cancellable);
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return connection;
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (address.has_prefix ("lockdown:")) {
|
||||
string service_name = address.substring (9);
|
||||
|
||||
if (service_name.length != 0) {
|
||||
var client = yield get_lockdown_client (cancellable);
|
||||
return yield open_lockdown_service (service_name, client, cancellable);
|
||||
} else {
|
||||
try {
|
||||
var client = yield Fruity.LockdownClient.open (device_details, cancellable);
|
||||
yield client.start_session (cancellable);
|
||||
return client.stream;
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported channel address");
|
||||
}
|
||||
|
||||
public async IOStream open_lockdown_service (string service_name, Fruity.LockdownClient client, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var tunnel = yield find_tunnel (cancellable);
|
||||
if (tunnel != null) {
|
||||
Fruity.ServiceInfo? service_info = null;
|
||||
bool needs_checkin = false;
|
||||
try {
|
||||
service_info = tunnel.discovery.get_service (service_name);
|
||||
} catch (Error e) {
|
||||
if (!(e is Error.NOT_SUPPORTED))
|
||||
throw e;
|
||||
}
|
||||
if (service_info == null) {
|
||||
service_info = tunnel.discovery.get_service (service_name + ".shim.remote");
|
||||
needs_checkin = true;
|
||||
}
|
||||
|
||||
var stream = yield tunnel.open_tcp_connection (service_info.port, cancellable);
|
||||
|
||||
if (needs_checkin) {
|
||||
var service = new Fruity.PlistServiceClient (stream);
|
||||
|
||||
var checkin = new Fruity.Plist ();
|
||||
checkin.set_string ("Request", "RSDCheckin");
|
||||
checkin.set_string ("Label", "Xcode");
|
||||
checkin.set_string ("ProtocolVersion", "2");
|
||||
|
||||
try {
|
||||
yield service.query (checkin, cancellable);
|
||||
|
||||
var result = yield service.read_message (cancellable);
|
||||
if (result.has ("Error")) {
|
||||
var error_type = result.get_string ("Error");
|
||||
if (error_type == "ServiceProhibited")
|
||||
throw new Error.PERMISSION_DENIED ("Service prohibited");
|
||||
throw new Error.NOT_SUPPORTED ("%s", error_type);
|
||||
}
|
||||
} catch (Fruity.PlistServiceError e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
} catch (Frida.Fruity.PlistError e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
try {
|
||||
return yield client.start_service (service_name, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is Fruity.LockdownError.INVALID_SERVICE)
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
return yield device.open_channel (address, cancellable);
|
||||
}
|
||||
|
||||
public async Service open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -432,20 +122,19 @@ namespace Frida {
|
||||
unowned string service_name = tokens[1];
|
||||
|
||||
if (protocol == "plist") {
|
||||
var client = yield get_lockdown_client (cancellable);
|
||||
var stream = yield open_lockdown_service (service_name, client, cancellable);
|
||||
var stream = yield device.open_lockdown_service (service_name, cancellable);
|
||||
|
||||
return new PlistService (stream);
|
||||
}
|
||||
|
||||
if (protocol == "dtx") {
|
||||
var connection = yield Fruity.DTXConnection.obtain (this, cancellable);
|
||||
var connection = yield Fruity.DTXConnection.obtain (device, cancellable);
|
||||
|
||||
return new DTXService (service_name, connection);
|
||||
}
|
||||
|
||||
if (protocol == "xpc") {
|
||||
var tunnel = yield find_tunnel (cancellable);
|
||||
var tunnel = yield device.find_tunnel (cancellable);
|
||||
if (tunnel == null)
|
||||
throw new Error.NOT_SUPPORTED ("RemoteXPC not supported by device");
|
||||
|
||||
@@ -458,58 +147,9 @@ namespace Frida {
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported service address");
|
||||
}
|
||||
|
||||
private async Fruity.LockdownClient get_lockdown_client (Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
var client = yield Fruity.LockdownClient.open (device_details, cancellable);
|
||||
yield client.start_session (cancellable);
|
||||
return client;
|
||||
} catch (Frida.Fruity.LockdownError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Fruity.Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
|
||||
while (tunnel_request != null) {
|
||||
try {
|
||||
return yield tunnel_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (IOError e) {
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
}
|
||||
tunnel_request = new Promise<Fruity.Tunnel> ();
|
||||
|
||||
try {
|
||||
bool supported_by_os = true;
|
||||
var lockdown = yield get_lockdown_client (cancellable);
|
||||
var response = yield lockdown.get_value (null, null, cancellable);
|
||||
Fruity.PlistDict properties = response.get_dict ("Value");
|
||||
if (properties.get_string ("ProductName") == "iPhone OS") {
|
||||
uint ios_major_version = uint.parse (properties.get_string ("ProductVersion").split (".")[0]);
|
||||
supported_by_os = ios_major_version >= 17;
|
||||
}
|
||||
|
||||
Fruity.Tunnel? tunnel = null;
|
||||
if (supported_by_os)
|
||||
tunnel = yield Fruity.TunnelFinder.make_default ().find (device_details.udid.raw_value, cancellable);
|
||||
|
||||
tunnel_request.resolve (tunnel);
|
||||
|
||||
return tunnel;
|
||||
} catch (GLib.Error e) {
|
||||
var api_error = new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
|
||||
tunnel_request.reject (api_error);
|
||||
tunnel_request = null;
|
||||
|
||||
throw_api_error (api_error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void unpair (Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
var client = yield Fruity.LockdownClient.open (device_details, cancellable);
|
||||
var client = yield device.get_lockdown_client (cancellable);
|
||||
yield client.unpair (cancellable);
|
||||
} catch (Fruity.LockdownError e) {
|
||||
if (e is Fruity.LockdownError.NOT_PAIRED)
|
||||
@@ -519,19 +159,8 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public interface FruityLockdownProvider : Object {
|
||||
public abstract async Fruity.LockdownClient get_lockdown_client (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async IOStream open_lockdown_service (string service_name, Fruity.LockdownClient client,
|
||||
Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
|
||||
public class FruityHostSession : Object, HostSession {
|
||||
public weak HostChannelProvider channel_provider {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public weak FruityLockdownProvider lockdown_provider {
|
||||
public Fruity.Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
@@ -563,21 +192,21 @@ namespace Frida {
|
||||
DEBUGSERVER_ENDPOINT_LEGACY,
|
||||
};
|
||||
|
||||
public FruityHostSession (HostChannelProvider channel_provider, FruityLockdownProvider lockdown_provider) {
|
||||
Object (
|
||||
channel_provider: channel_provider,
|
||||
lockdown_provider: lockdown_provider
|
||||
);
|
||||
public FruityHostSession (Fruity.Device device) {
|
||||
Object (device: device);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (remote_server_request != null) {
|
||||
var server = yield try_get_remote_server (cancellable);
|
||||
if (server != null) {
|
||||
try {
|
||||
yield server.connection.close (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
try {
|
||||
var server = yield try_get_remote_server (cancellable);
|
||||
if (server != null) {
|
||||
try {
|
||||
yield server.connection.close (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,7 +244,7 @@ namespace Frida {
|
||||
var parameters = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var lockdown = yield device.get_lockdown_client (cancellable);
|
||||
var response = yield lockdown.get_value (null, null, cancellable);
|
||||
Fruity.PlistDict properties = response.get_dict ("Value");
|
||||
|
||||
@@ -623,12 +252,19 @@ namespace Frida {
|
||||
os["id"] = "ios";
|
||||
os["name"] = properties.get_string ("ProductName");
|
||||
os["version"] = properties.get_string ("ProductVersion");
|
||||
os["build"] = properties.get_string ("BuildVersion");
|
||||
parameters["os"] = os;
|
||||
|
||||
parameters["platform"] = "darwin";
|
||||
|
||||
parameters["arch"] = properties.get_string ("CPUArchitecture").has_prefix ("arm64") ? "arm64" : "arm";
|
||||
|
||||
var hardware = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
hardware["product"] = properties.get_string ("ProductType");
|
||||
hardware["platform"] = properties.get_string ("HardwarePlatform");
|
||||
hardware["model"] = properties.get_string ("HardwareModel");
|
||||
parameters["hardware"] = hardware;
|
||||
|
||||
parameters["access"] = "jailed";
|
||||
|
||||
parameters["name"] = properties.get_string ("DeviceName");
|
||||
@@ -726,15 +362,10 @@ namespace Frida {
|
||||
add_process_metadata (info.parameters, process);
|
||||
|
||||
if (scope == FULL) {
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (lockdown, cancellable);
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (device, cancellable);
|
||||
|
||||
Bytes png = yield springboard.get_icon_png_data (identifier);
|
||||
add_app_icons (info.parameters, png);
|
||||
} catch (Fruity.SpringboardServicesError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
Bytes png = yield springboard.get_icon_png_data (identifier);
|
||||
add_app_icons (info.parameters, png);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,18 +396,13 @@ namespace Frida {
|
||||
|
||||
Gee.Map<string, Bytes>? icons = null;
|
||||
if (scope == FULL) {
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (lockdown, cancellable);
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (device, cancellable);
|
||||
|
||||
var app_ids = new Gee.ArrayList<string> ();
|
||||
foreach (var app in apps)
|
||||
app_ids.add (app.identifier);
|
||||
var app_ids = new Gee.ArrayList<string> ();
|
||||
foreach (var app in apps)
|
||||
app_ids.add (app.identifier);
|
||||
|
||||
icons = yield springboard.get_icon_png_data_batch (app_ids.to_array (), cancellable);
|
||||
} catch (Fruity.SpringboardServicesError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
icons = yield springboard.get_icon_png_data_batch (app_ids.to_array (), cancellable);
|
||||
}
|
||||
|
||||
Gee.List<Fruity.DeviceInfoService.ProcessInfo> processes =
|
||||
@@ -881,19 +507,14 @@ namespace Frida {
|
||||
if (scope == FULL) {
|
||||
icon_by_pid = new Gee.HashMap<uint, Bytes> ();
|
||||
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
try {
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (lockdown, cancellable);
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (device, cancellable);
|
||||
|
||||
var pngs = yield springboard.get_icon_png_data_batch (app_ids.to_array (), cancellable);
|
||||
var pngs = yield springboard.get_icon_png_data_batch (app_ids.to_array (), cancellable);
|
||||
|
||||
int i = 0;
|
||||
foreach (string app_id in app_ids) {
|
||||
icon_by_pid[app_pids[i]] = pngs[app_id];
|
||||
i++;
|
||||
}
|
||||
} catch (Fruity.SpringboardServicesError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
int i = 0;
|
||||
foreach (string app_id in app_ids) {
|
||||
icon_by_pid[app_pids[i]] = pngs[app_id];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -948,32 +569,25 @@ namespace Frida {
|
||||
|
||||
private async void fetch_apps (Promise<Gee.List<Fruity.ApplicationDetails>> promise, Cancellable? cancellable) {
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var installation_proxy = yield Fruity.InstallationProxyClient.open (lockdown, cancellable);
|
||||
var installation_proxy = yield Fruity.InstallationProxyClient.open (device, cancellable);
|
||||
|
||||
var apps = yield installation_proxy.browse (cancellable);
|
||||
|
||||
promise.resolve (apps);
|
||||
} catch (Error e) {
|
||||
} catch (GLib.Error e) {
|
||||
promise.reject (e);
|
||||
} catch (IOError e) {
|
||||
promise.reject (e);
|
||||
} catch (Fruity.InstallationProxyError e) {
|
||||
promise.reject (new Error.NOT_SUPPORTED ("%s", e.message));
|
||||
}
|
||||
}
|
||||
|
||||
private async void fetch_processes (Promise<Gee.List<Fruity.DeviceInfoService.ProcessInfo>> promise,
|
||||
Cancellable? cancellable) {
|
||||
try {
|
||||
var device_info = yield Fruity.DeviceInfoService.open (channel_provider, cancellable);
|
||||
var device_info = yield Fruity.DeviceInfoService.open (device, cancellable);
|
||||
|
||||
var processes = yield device_info.enumerate_processes (cancellable);
|
||||
|
||||
promise.resolve (processes);
|
||||
} catch (Error e) {
|
||||
promise.reject (e);
|
||||
} catch (IOError e) {
|
||||
} catch (GLib.Error e) {
|
||||
promise.reject (e);
|
||||
}
|
||||
}
|
||||
@@ -1156,46 +770,40 @@ namespace Frida {
|
||||
gadget_path = gadget_value.get_string ();
|
||||
}
|
||||
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var installation_proxy = yield Fruity.InstallationProxyClient.open (device, cancellable);
|
||||
|
||||
var installation_proxy = yield Fruity.InstallationProxyClient.open (lockdown, cancellable);
|
||||
var query = new Fruity.PlistDict ();
|
||||
var ids = new Fruity.PlistArray ();
|
||||
ids.add_string (program);
|
||||
query.set_array ("BundleIDs", ids);
|
||||
|
||||
var query = new Fruity.PlistDict ();
|
||||
var ids = new Fruity.PlistArray ();
|
||||
ids.add_string (program);
|
||||
query.set_array ("BundleIDs", ids);
|
||||
var matches = yield installation_proxy.lookup (query, cancellable);
|
||||
var app = matches[program];
|
||||
if (app == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to find app with bundle identifier “%s”", program);
|
||||
|
||||
var matches = yield installation_proxy.lookup (query, cancellable);
|
||||
var app = matches[program];
|
||||
if (app == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to find app with bundle identifier “%s”", program);
|
||||
|
||||
string[] argv = { app.path };
|
||||
if (options.has_argv) {
|
||||
var provided_argv = options.argv;
|
||||
var length = provided_argv.length;
|
||||
for (int i = 1; i < length; i++)
|
||||
argv += provided_argv[i];
|
||||
}
|
||||
|
||||
var lldb = yield start_lldb_service (lockdown, cancellable);
|
||||
var process = yield lldb.launch (argv, launch_options, cancellable);
|
||||
if (process.observed_state == ALREADY_RUNNING) {
|
||||
yield lldb.kill (cancellable);
|
||||
yield lldb.close (cancellable);
|
||||
|
||||
lldb = yield start_lldb_service (lockdown, cancellable);
|
||||
process = yield lldb.launch (argv, launch_options, cancellable);
|
||||
}
|
||||
|
||||
var session = new LLDBSession (lldb, process, gadget_path, channel_provider);
|
||||
add_lldb_session (session);
|
||||
|
||||
return process.pid;
|
||||
} catch (Fruity.InstallationProxyError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
string[] argv = { app.path };
|
||||
if (options.has_argv) {
|
||||
var provided_argv = options.argv;
|
||||
var length = provided_argv.length;
|
||||
for (int i = 1; i < length; i++)
|
||||
argv += provided_argv[i];
|
||||
}
|
||||
|
||||
var lldb = yield start_lldb_service (cancellable);
|
||||
var process = yield lldb.launch (argv, launch_options, cancellable);
|
||||
if (process.observed_state == ALREADY_RUNNING) {
|
||||
yield lldb.kill (cancellable);
|
||||
yield lldb.close (cancellable);
|
||||
|
||||
lldb = yield start_lldb_service (cancellable);
|
||||
process = yield lldb.launch (argv, launch_options, cancellable);
|
||||
}
|
||||
|
||||
var session = new LLDBSession (lldb, process, gadget_path, device);
|
||||
add_lldb_session (session);
|
||||
|
||||
return process.pid;
|
||||
}
|
||||
|
||||
public async void input (uint pid, uint8[] data, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -1241,15 +849,14 @@ namespace Frida {
|
||||
}
|
||||
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var lldb = yield start_lldb_service (lockdown, cancellable);
|
||||
var lldb = yield start_lldb_service (cancellable);
|
||||
var process = yield lldb.attach_by_pid (pid, cancellable);
|
||||
|
||||
lldb_session = new LLDBSession (lldb, process, null, channel_provider);
|
||||
lldb_session = new LLDBSession (lldb, process, null, device);
|
||||
yield lldb_session.kill (cancellable);
|
||||
yield lldb_session.close (cancellable);
|
||||
} catch (Error e) {
|
||||
var process_control = yield Fruity.ProcessControlService.open (channel_provider, cancellable);
|
||||
var process_control = yield Fruity.ProcessControlService.open (device, cancellable);
|
||||
yield process_control.kill (pid, cancellable);
|
||||
}
|
||||
}
|
||||
@@ -1276,13 +883,12 @@ namespace Frida {
|
||||
if (pid == 0)
|
||||
throw new Error.NOT_SUPPORTED ("The Frida system session is not available on jailed iOS");
|
||||
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var lldb = yield start_lldb_service (lockdown, cancellable);
|
||||
var lldb = yield start_lldb_service (cancellable);
|
||||
var process = yield lldb.attach_by_pid (pid, cancellable);
|
||||
|
||||
string? gadget_path = null;
|
||||
|
||||
lldb_session = new LLDBSession (lldb, process, gadget_path, channel_provider);
|
||||
lldb_session = new LLDBSession (lldb, process, gadget_path, device);
|
||||
add_lldb_session (lldb_session);
|
||||
|
||||
var gadget_details = yield lldb_session.query_gadget_details (cancellable);
|
||||
@@ -1294,11 +900,10 @@ namespace Frida {
|
||||
throw new Error.INVALID_OPERATION ("Only meant to be implemented by services");
|
||||
}
|
||||
|
||||
private async LLDB.Client start_lldb_service (Fruity.LockdownClient lockdown, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
private async LLDB.Client start_lldb_service (Cancellable? cancellable) throws Error, IOError {
|
||||
foreach (unowned string endpoint in DEBUGSERVER_ENDPOINT_CANDIDATES) {
|
||||
try {
|
||||
var lldb_stream = yield lockdown_provider.open_lockdown_service (endpoint, lockdown, cancellable);
|
||||
var lldb_stream = yield device.open_lockdown_service (endpoint, cancellable);
|
||||
return yield LLDB.Client.open (lldb_stream, cancellable);
|
||||
} catch (Error e) {
|
||||
if (!(e is Error.NOT_SUPPORTED))
|
||||
@@ -1313,7 +918,7 @@ namespace Frida {
|
||||
private async AgentSessionId attach_via_gadget (uint pid, HashTable<string, Variant> options,
|
||||
Fruity.Injector.GadgetDetails gadget_details, Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
var stream = yield channel_provider.open_channel (
|
||||
var stream = yield device.open_channel (
|
||||
("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (gadget_details.port),
|
||||
cancellable);
|
||||
|
||||
@@ -1366,8 +971,8 @@ namespace Frida {
|
||||
var transport_broker = server.transport_broker;
|
||||
if (transport_broker != null) {
|
||||
try {
|
||||
entry.connection = yield establish_direct_connection (transport_broker, remote_session_id,
|
||||
channel_provider, cancellable);
|
||||
entry.connection = yield establish_direct_connection (transport_broker, remote_session_id, device,
|
||||
cancellable);
|
||||
} catch (Error e) {
|
||||
if (e is Error.NOT_SUPPORTED)
|
||||
server.transport_broker = null;
|
||||
@@ -1450,10 +1055,12 @@ namespace Frida {
|
||||
entry.close.begin (io_cancellable);
|
||||
}
|
||||
|
||||
private async RemoteServer? try_get_remote_server (Cancellable? cancellable) throws IOError {
|
||||
private async RemoteServer? try_get_remote_server (Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
return yield get_remote_server (cancellable);
|
||||
} catch (Error e) {
|
||||
if (e is Error.PERMISSION_DENIED || e is Error.TIMED_OUT)
|
||||
throw e;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1480,7 +1087,7 @@ namespace Frida {
|
||||
|
||||
DBusConnection? connection = null;
|
||||
try {
|
||||
var stream = yield channel_provider.open_channel (
|
||||
var stream = yield device.open_channel (
|
||||
("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (DEFAULT_CONTROL_PORT),
|
||||
cancellable);
|
||||
|
||||
@@ -1529,7 +1136,9 @@ namespace Frida {
|
||||
last_server_check_timer = null;
|
||||
last_server_check_error = null;
|
||||
} else {
|
||||
if (e is Error.SERVER_NOT_RUNNING) {
|
||||
if (e is Error.PERMISSION_DENIED || e is Error.TIMED_OUT) {
|
||||
api_error = e;
|
||||
} else if (e is Error.SERVER_NOT_RUNNING) {
|
||||
api_error = new Error.SERVER_NOT_RUNNING ("Unable to connect to remote frida-server");
|
||||
} else if (connection != null) {
|
||||
api_error = new Error.PROTOCOL ("Incompatible frida-server version");
|
||||
@@ -2474,16 +2083,75 @@ namespace Frida {
|
||||
}
|
||||
|
||||
if (val.is_of_type (new VariantType ("av"))) {
|
||||
builder.begin_array ();
|
||||
add_vararray_values (val, builder);
|
||||
builder.end_array ();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case TUPLE:
|
||||
if (val.n_children () != 2) {
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid type annotation: %s",
|
||||
(string) val.get_type ().peek_string ());
|
||||
}
|
||||
|
||||
var type = val.get_child_value (0);
|
||||
if (!type.is_of_type (VariantType.STRING)) {
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid type annotation: %s",
|
||||
(string) val.get_type ().peek_string ());
|
||||
}
|
||||
unowned string type_str = type.get_string ();
|
||||
|
||||
add_variant_value_of_type (val.get_child_value (1), type_str, builder);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported type: %s", (string) val.get_type ().peek_string ());
|
||||
}
|
||||
|
||||
private static void add_variant_value_of_type (Variant val, string type, Fruity.XpcBodyBuilder builder) throws Error {
|
||||
switch (type) {
|
||||
case "bool":
|
||||
check_type (val, VariantType.BOOLEAN);
|
||||
builder.add_bool_value (val.get_boolean ());
|
||||
break;
|
||||
case "int64":
|
||||
check_type (val, VariantType.INT64);
|
||||
builder.add_int64_value (val.get_int64 ());
|
||||
break;
|
||||
case "uint64":
|
||||
check_type (val, VariantType.UINT64);
|
||||
builder.add_uint64_value (val.get_uint64 ());
|
||||
break;
|
||||
case "data":
|
||||
check_type (val, new VariantType ("ay"));
|
||||
builder.add_data_value (val.get_data_as_bytes ());
|
||||
break;
|
||||
case "string":
|
||||
check_type (val, VariantType.STRING);
|
||||
builder.add_string_value (val.get_string ());
|
||||
break;
|
||||
case "uuid":
|
||||
check_type (val, new VariantType ("ay"));
|
||||
if (val.get_size () != 16)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid UUID");
|
||||
unowned uint8[] data = (uint8[]) val.get_data ();
|
||||
builder.add_uuid_value (data[:16]);
|
||||
break;
|
||||
default:
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported type: %s", type);
|
||||
}
|
||||
}
|
||||
|
||||
private static void check_type (Variant v, VariantType t) throws Error {
|
||||
if (!v.is_of_type (t)) {
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid %s: %s",
|
||||
(string) t.peek_string (),
|
||||
(string) v.get_type ().peek_string ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ frida_fetch_dyld_symbols (char * output_buffer, const void * dyld_load_address)
|
||||
const char * name = strings + sym->n_un.n_strx;
|
||||
|
||||
if (frida_str_contains (name, "libdyld_initialize") ||
|
||||
frida_str_contains (name, "notifyObjCInit") ||
|
||||
frida_str_contains (name, "restartWithDyldInCache") ||
|
||||
frida_str_equals (name, "_gProcessInfo") ||
|
||||
frida_str_equals (name, "__ZN5dyld412gProcessInfoE") ||
|
||||
|
||||
@@ -5,11 +5,15 @@ frida_listen (int rx_buffer_size, const FridaUploadApi * api)
|
||||
{
|
||||
uint8_t error_code;
|
||||
int fd;
|
||||
struct sockaddr_in addr;
|
||||
struct sockaddr_in6 addr = {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_addr = IN6ADDR_ANY_INIT,
|
||||
.sin6_port = 0,
|
||||
};
|
||||
socklen_t addr_len;
|
||||
int res;
|
||||
|
||||
fd = api->socket (AF_INET, SOCK_STREAM, 0);
|
||||
fd = api->socket (AF_INET6, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
goto socket_failed;
|
||||
|
||||
@@ -17,10 +21,6 @@ frida_listen (int rx_buffer_size, const FridaUploadApi * api)
|
||||
if (res == -1)
|
||||
goto setsockopt_failed;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
|
||||
addr.sin_port = 0;
|
||||
|
||||
addr_len = sizeof (addr);
|
||||
|
||||
res = api->bind (fd, (const struct sockaddr *) &addr, addr_len);
|
||||
@@ -35,7 +35,7 @@ frida_listen (int rx_buffer_size, const FridaUploadApi * api)
|
||||
if (res == -1)
|
||||
goto listen_failed;
|
||||
|
||||
return ((uint64_t) fd << 16) | ntohs (addr.sin_port);
|
||||
return ((uint64_t) fd << 16) | ntohs (addr.sin6_port);
|
||||
|
||||
socket_failed:
|
||||
{
|
||||
|
||||
+109
-89
@@ -64,7 +64,6 @@ namespace Frida.Fruity.Injector {
|
||||
|
||||
private LLDB.AppleDyldFields? dyld_fields;
|
||||
private uint64 dyld_base;
|
||||
private Gee.Map<string, uint64?>? dyld_symbols;
|
||||
|
||||
private bool libsystem_initialized;
|
||||
|
||||
@@ -160,7 +159,6 @@ namespace Frida.Fruity.Injector {
|
||||
return true;
|
||||
}, cancellable);
|
||||
}
|
||||
dyld_symbols = yield fetch_dyld_symbols (cancellable);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -629,22 +627,24 @@ namespace Frida.Fruity.Injector {
|
||||
|
||||
/* Compiled from helpers/upload-listener.c */
|
||||
private const uint8[] UPLOAD_LISTENER_CODE = {
|
||||
0xff, 0x43, 0x01, 0xd1, 0xf6, 0x57, 0x02, 0xa9, 0xf4, 0x4f, 0x03, 0xa9, 0xfd, 0x7b, 0x04, 0xa9, 0xfd, 0x03, 0x01,
|
||||
0x91, 0xf3, 0x03, 0x01, 0xaa, 0xe0, 0x1f, 0x00, 0xb9, 0x28, 0x00, 0x40, 0xf9, 0x40, 0x00, 0x80, 0x52, 0x21, 0x00,
|
||||
0x80, 0x52, 0x02, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0x80, 0x05, 0x00, 0x54, 0xf4,
|
||||
0x03, 0x00, 0xaa, 0x68, 0x06, 0x40, 0xf9, 0xe3, 0x73, 0x00, 0x91, 0xe1, 0xff, 0x9f, 0x52, 0x42, 0x00, 0x82, 0x52,
|
||||
0x84, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0xa0, 0x04, 0x00, 0x54, 0x48, 0x00, 0x80,
|
||||
0x52, 0xe8, 0x27, 0x00, 0x39, 0xe8, 0x0f, 0x80, 0x52, 0x08, 0x20, 0xa0, 0x72, 0xe8, 0x0f, 0x00, 0xb9, 0xff, 0x17,
|
||||
0x00, 0x79, 0x08, 0x02, 0x80, 0x52, 0xe8, 0x07, 0x00, 0xb9, 0x68, 0x0a, 0x40, 0xf9, 0xe1, 0x23, 0x00, 0x91, 0xe0,
|
||||
0x03, 0x14, 0xaa, 0x02, 0x02, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0x00, 0x03, 0x00, 0x54,
|
||||
0x68, 0x12, 0x40, 0xf9, 0xe1, 0x23, 0x00, 0x91, 0xe2, 0x13, 0x00, 0x91, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f,
|
||||
0xff, 0x83, 0x01, 0xd1, 0xf6, 0x57, 0x03, 0xa9, 0xf4, 0x4f, 0x04, 0xa9, 0xfd, 0x7b, 0x05, 0xa9, 0xfd, 0x43, 0x01,
|
||||
0x91, 0xf3, 0x03, 0x01, 0xaa, 0x08, 0x00, 0x00, 0x90, 0x08, 0xb1, 0x04, 0x91, 0xa0, 0xc3, 0x1d, 0xb8, 0x00, 0x01,
|
||||
0xc0, 0x3d, 0xe0, 0x07, 0x80, 0x3d, 0x00, 0xc1, 0xc0, 0x3c, 0xe0, 0xc3, 0x81, 0x3c, 0x28, 0x00, 0x40, 0xf9, 0xc0,
|
||||
0x03, 0x80, 0x52, 0x21, 0x00, 0x80, 0x52, 0x02, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31,
|
||||
0xc0, 0x04, 0x00, 0x54, 0xf4, 0x03, 0x00, 0xaa, 0x68, 0x06, 0x40, 0xf9, 0xa3, 0x93, 0x00, 0xd1, 0xe1, 0xff, 0x9f,
|
||||
0x52, 0x42, 0x00, 0x82, 0x52, 0x84, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0xe0, 0x03,
|
||||
0x00, 0x54, 0x88, 0x03, 0x80, 0x52, 0xe8, 0x0f, 0x00, 0xb9, 0x68, 0x0a, 0x40, 0xf9, 0xe1, 0x43, 0x00, 0x91, 0xe0,
|
||||
0x03, 0x14, 0xaa, 0x82, 0x03, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0x00, 0x03, 0x00, 0x54,
|
||||
0x68, 0x12, 0x40, 0xf9, 0xe1, 0x43, 0x00, 0x91, 0xe2, 0x33, 0x00, 0x91, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f,
|
||||
0xd6, 0x1f, 0x04, 0x00, 0x31, 0x60, 0x02, 0x00, 0x54, 0x68, 0x0e, 0x40, 0xf9, 0xe0, 0x03, 0x14, 0xaa, 0x21, 0x00,
|
||||
0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0xe0, 0x01, 0x00, 0x54, 0x88, 0x7e, 0x40, 0x93, 0xe9,
|
||||
0x17, 0x40, 0x79, 0x29, 0x09, 0xc0, 0x5a, 0x35, 0x7d, 0x10, 0x53, 0x15, 0xbd, 0x70, 0xb3, 0x0d, 0x00, 0x00, 0x14,
|
||||
0x27, 0x40, 0x79, 0x29, 0x09, 0xc0, 0x5a, 0x35, 0x7d, 0x10, 0x53, 0x15, 0xbd, 0x70, 0xb3, 0x0d, 0x00, 0x00, 0x14,
|
||||
0x15, 0x20, 0xe0, 0xd2, 0x0b, 0x00, 0x00, 0x14, 0x15, 0x40, 0xe0, 0xd2, 0x06, 0x00, 0x00, 0x14, 0x15, 0x60, 0xe0,
|
||||
0xd2, 0x04, 0x00, 0x00, 0x14, 0x15, 0x80, 0xe0, 0xd2, 0x02, 0x00, 0x00, 0x14, 0x15, 0xa0, 0xe0, 0xd2, 0x68, 0x42,
|
||||
0x40, 0xf9, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f, 0xd6, 0xe0, 0x03, 0x15, 0xaa, 0xfd, 0x7b, 0x44, 0xa9, 0xf4,
|
||||
0x4f, 0x43, 0xa9, 0xf6, 0x57, 0x42, 0xa9, 0xff, 0x43, 0x01, 0x91, 0xc0, 0x03, 0x5f, 0xd6
|
||||
0x40, 0xf9, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f, 0xd6, 0xe0, 0x03, 0x15, 0xaa, 0xfd, 0x7b, 0x45, 0xa9, 0xf4,
|
||||
0x4f, 0x44, 0xa9, 0xf6, 0x57, 0x43, 0xa9, 0xff, 0x83, 0x01, 0x91, 0xc0, 0x03, 0x5f, 0xd6, 0x00, 0x1e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* Compiled from helpers/upload-receiver.c */
|
||||
@@ -810,7 +810,7 @@ namespace Frida.Fruity.Injector {
|
||||
return total;
|
||||
}
|
||||
|
||||
private uint64 resolve_dyld_symbol (string name, string nick) throws Error {
|
||||
private static uint64 resolve_dyld_symbol (string name, string nick, Gee.Map<string, uint64?> dyld_symbols) throws Error {
|
||||
uint64? val = dyld_symbols[name];
|
||||
if (val == null)
|
||||
throw new Error.UNSUPPORTED ("Unsupported iOS version (%s not found)", nick);
|
||||
@@ -821,13 +821,15 @@ namespace Frida.Fruity.Injector {
|
||||
if (libsystem_initialized)
|
||||
return;
|
||||
|
||||
var dyld_symbols = yield fetch_dyld_symbols (cancellable);
|
||||
|
||||
yield restore_main_thread_state (cancellable);
|
||||
|
||||
uint64? libdyld_initialize = dyld_symbols["__ZN5dyld44APIs19_libdyld_initializeEPKNS_16LibSystemHelpersE"];
|
||||
if (libdyld_initialize != null)
|
||||
yield ensure_libsystem_initialized_for_dyld_v4_and_above (libdyld_initialize, cancellable);
|
||||
yield ensure_libsystem_initialized_for_dyld_v4_and_above (libdyld_initialize, dyld_symbols, cancellable);
|
||||
else
|
||||
yield ensure_libsystem_initialized_for_dyld_v3_and_below (cancellable);
|
||||
yield ensure_libsystem_initialized_for_dyld_v3_and_below (dyld_symbols, cancellable);
|
||||
|
||||
yield save_main_thread_state (cancellable);
|
||||
|
||||
@@ -836,17 +838,21 @@ namespace Frida.Fruity.Injector {
|
||||
}
|
||||
|
||||
private async void ensure_libsystem_initialized_for_dyld_v4_and_above (uint64 libdyld_initialize,
|
||||
Cancellable? cancellable) throws GLib.Error {
|
||||
Gee.Map<string, uint64?> dyld_symbols, Cancellable? cancellable) throws GLib.Error {
|
||||
uint64? process_info_ptr = dyld_symbols["__ZL12sProcessInfo"];
|
||||
if (process_info_ptr == null)
|
||||
process_info_ptr = dyld_symbols["__ZN5dyld412gProcessInfoE"];
|
||||
if (process_info_ptr == null)
|
||||
process_info_ptr = dyld_symbols["_gProcessInfo"];
|
||||
|
||||
GDB.Breakpoint init_breakpoint = yield lldb.add_breakpoint (SOFT, libdyld_initialize, 4, cancellable);
|
||||
uint64? notify_objc_init = dyld_symbols["__ZN5dyld412RuntimeState14notifyObjCInitEPKNS_6LoaderE"];
|
||||
uint64 initializer = (notify_objc_init != null) ? notify_objc_init : libdyld_initialize;
|
||||
GDB.Breakpoint init_breakpoint = yield lldb.add_breakpoint (SOFT, initializer, 4, cancellable);
|
||||
|
||||
GDB.Breakpoint? restart_breakpoint = null;
|
||||
uint64? restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPv"];
|
||||
uint64? restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPK15DyldSharedCachePv"];
|
||||
if (restart_with_dyld_in_cache == null)
|
||||
restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPv"];
|
||||
if (restart_with_dyld_in_cache != null)
|
||||
restart_breakpoint = yield lldb.add_breakpoint (SOFT, restart_with_dyld_in_cache, 4, cancellable);
|
||||
|
||||
@@ -856,12 +862,18 @@ namespace Frida.Fruity.Injector {
|
||||
if (hit_breakpoint == null)
|
||||
throw new Error.UNSUPPORTED ("Unexpected exception");
|
||||
|
||||
if (restart_breakpoint != null)
|
||||
yield restart_breakpoint.remove (cancellable);
|
||||
yield init_breakpoint.remove (cancellable);
|
||||
|
||||
if (hit_breakpoint == restart_breakpoint) {
|
||||
if (process_info_ptr == null)
|
||||
throw new Error.UNSUPPORTED ("Missing gProcessInfo");
|
||||
uint64 process_info = yield lldb.read_pointer (process_info_ptr, cancellable);
|
||||
uint64 dyld_image_load_address = yield lldb.read_pointer (process_info + 0x20, cancellable);
|
||||
|
||||
initializer = dyld_image_load_address + (initializer - dyld_base);
|
||||
|
||||
var rebased_symbols = new Gee.HashMap<string, uint64?> ();
|
||||
foreach (var e in dyld_symbols.entries)
|
||||
rebased_symbols[e.key] = dyld_image_load_address + (e.value - dyld_base);
|
||||
@@ -869,13 +881,12 @@ namespace Frida.Fruity.Injector {
|
||||
dyld_base = dyld_image_load_address;
|
||||
dyld_symbols = rebased_symbols;
|
||||
|
||||
yield restart_breakpoint.remove (cancellable);
|
||||
yield init_breakpoint.remove (cancellable);
|
||||
uint64 real_libdyld_initialize = dyld_symbols["__ZN5dyld44APIs19_libdyld_initializeEPKNS_16LibSystemHelpersE"];
|
||||
init_breakpoint = yield lldb.add_breakpoint (SOFT, real_libdyld_initialize, 4, cancellable);
|
||||
init_breakpoint = yield lldb.add_breakpoint (SOFT, initializer, 4, cancellable);
|
||||
|
||||
exception = (LLDB.Exception) yield lldb.continue_until_exception (cancellable);
|
||||
|
||||
yield init_breakpoint.remove (cancellable);
|
||||
|
||||
hit_breakpoint = exception.breakpoint;
|
||||
if (hit_breakpoint == null)
|
||||
throw new Error.UNSUPPORTED ("Unexpected exception");
|
||||
@@ -884,7 +895,10 @@ namespace Frida.Fruity.Injector {
|
||||
}
|
||||
|
||||
assert (hit_breakpoint == init_breakpoint);
|
||||
yield init_breakpoint.remove (cancellable);
|
||||
|
||||
bool done_because_we_hit_notify_objc_init = initializer != libdyld_initialize;
|
||||
if (done_because_we_hit_notify_objc_init)
|
||||
return;
|
||||
|
||||
uint64 dyld_size = dyld_symbols["dyld_size"];
|
||||
uint64 frame_here = exception.context["fp"];
|
||||
@@ -913,7 +927,8 @@ namespace Frida.Fruity.Injector {
|
||||
yield caller_breakpoint.remove (cancellable);
|
||||
}
|
||||
|
||||
private async void ensure_libsystem_initialized_for_dyld_v3_and_below (Cancellable? cancellable) throws GLib.Error {
|
||||
private async void ensure_libsystem_initialized_for_dyld_v3_and_below (Gee.Map<string, uint64?> dyld_symbols,
|
||||
Cancellable? cancellable) throws GLib.Error {
|
||||
GDB.Breakpoint? modern_breakpoint = null;
|
||||
uint64 launch_with_closure = 0;
|
||||
|
||||
@@ -933,7 +948,8 @@ namespace Frida.Fruity.Injector {
|
||||
modern_breakpoint = yield lldb.add_breakpoint (SOFT, run_initializers_call, 4, cancellable);
|
||||
}
|
||||
|
||||
uint64 initialize_main_executable = resolve_dyld_symbol ("__ZN4dyld24initializeMainExecutableEv", "initializeMainExecutable");
|
||||
uint64 initialize_main_executable = resolve_dyld_symbol ("__ZN4dyld24initializeMainExecutableEv",
|
||||
"initializeMainExecutable", dyld_symbols);
|
||||
GDB.Breakpoint legacy_breakpoint = yield lldb.add_breakpoint (SOFT, initialize_main_executable, 4, cancellable);
|
||||
|
||||
var exception = yield lldb.continue_until_exception (cancellable);
|
||||
@@ -948,7 +964,7 @@ namespace Frida.Fruity.Injector {
|
||||
yield modern_breakpoint.remove (cancellable);
|
||||
|
||||
if (hit_breakpoint == legacy_breakpoint)
|
||||
yield initialize_libsystem_from_legacy_codepath (cancellable);
|
||||
yield initialize_libsystem_from_legacy_codepath (dyld_symbols, cancellable);
|
||||
else
|
||||
assert (hit_breakpoint == modern_breakpoint);
|
||||
}
|
||||
@@ -989,7 +1005,8 @@ namespace Frida.Fruity.Injector {
|
||||
throw new Error.UNSUPPORTED ("Unable to probe dyld v3 internals; please file a bug");
|
||||
}
|
||||
|
||||
private async void initialize_libsystem_from_legacy_codepath (Cancellable? cancellable) throws GLib.Error {
|
||||
private async void initialize_libsystem_from_legacy_codepath (Gee.Map<string, uint64?> dyld_symbols,
|
||||
Cancellable? cancellable) throws GLib.Error {
|
||||
uint64 code = jit_page;
|
||||
|
||||
var code_builder_pac = lldb.make_buffer_builder ();
|
||||
@@ -1055,8 +1072,8 @@ namespace Frida.Fruity.Injector {
|
||||
code_builder.write_pointer (error_buf_literal_offset, helpers + error_buffer_offset);
|
||||
yield lldb.write_byte_array (code, code_builder.build (), cancellable);
|
||||
|
||||
uint64 register_thread_helpers =
|
||||
resolve_dyld_symbol ("__ZL21registerThreadHelpersPKN4dyld16LibSystemHelpersE", "registerThreadHelpers");
|
||||
uint64 register_thread_helpers = resolve_dyld_symbol ("__ZL21registerThreadHelpersPKN4dyld16LibSystemHelpersE",
|
||||
"registerThreadHelpers", dyld_symbols);
|
||||
yield invoke_remote_function (register_thread_helpers, {
|
||||
helpers
|
||||
}, null, cancellable);
|
||||
@@ -1075,8 +1092,8 @@ namespace Frida.Fruity.Injector {
|
||||
ExceptionHandler? strcmp_handler = null;
|
||||
|
||||
var dlopen = dyld_symbols.has_key ("_dlopen_internal")
|
||||
? resolve_dyld_symbol ("_dlopen_internal", "dlopen")
|
||||
: resolve_dyld_symbol ("_dlopen", "dlopen");
|
||||
? resolve_dyld_symbol ("_dlopen_internal", "dlopen", dyld_symbols)
|
||||
: resolve_dyld_symbol ("_dlopen", "dlopen", dyld_symbols);
|
||||
yield invoke_remote_function (dlopen, {
|
||||
libsystem_string,
|
||||
9,
|
||||
@@ -1184,68 +1201,71 @@ namespace Frida.Fruity.Injector {
|
||||
0xff, 0x03, 0x02, 0xd1, 0xfc, 0x6f, 0x02, 0xa9, 0xfa, 0x67, 0x03, 0xa9, 0xf8, 0x5f, 0x04, 0xa9, 0xf6, 0x57, 0x05,
|
||||
0xa9, 0xf4, 0x4f, 0x06, 0xa9, 0xfd, 0x7b, 0x07, 0xa9, 0xfd, 0xc3, 0x01, 0x91, 0xf4, 0x03, 0x01, 0xaa, 0xe0, 0x07,
|
||||
0x00, 0xf9, 0x13, 0x00, 0x80, 0xd2, 0x19, 0x00, 0x80, 0xd2, 0x35, 0x80, 0x00, 0x91, 0x3a, 0x10, 0x40, 0xb9, 0x16,
|
||||
0x00, 0x00, 0x90, 0xd6, 0xb2, 0x12, 0x91, 0x17, 0x00, 0x00, 0x90, 0xf7, 0xce, 0x12, 0x91, 0x1a, 0x04, 0x00, 0x34,
|
||||
0x00, 0x00, 0x90, 0xd6, 0x3e, 0x13, 0x91, 0x17, 0x00, 0x00, 0x90, 0xf7, 0x5a, 0x13, 0x91, 0x1a, 0x04, 0x00, 0x34,
|
||||
0xa8, 0x02, 0x40, 0xb9, 0x1f, 0x09, 0x00, 0x71, 0xa0, 0x01, 0x00, 0x54, 0x1f, 0x2d, 0x00, 0x71, 0xa0, 0x01, 0x00,
|
||||
0x54, 0x1f, 0x65, 0x00, 0x71, 0x81, 0x01, 0x00, 0x54, 0xb8, 0x22, 0x00, 0x91, 0xe0, 0x03, 0x18, 0xaa, 0xe1, 0x03,
|
||||
0x16, 0xaa, 0xa5, 0x00, 0x00, 0x94, 0x80, 0x01, 0x00, 0x34, 0xb3, 0xa2, 0x41, 0xa9, 0xe8, 0x0b, 0x00, 0xf9, 0x04,
|
||||
0x16, 0xaa, 0xaa, 0x00, 0x00, 0x94, 0x80, 0x01, 0x00, 0x34, 0xb3, 0xa2, 0x41, 0xa9, 0xe8, 0x0b, 0x00, 0xf9, 0x04,
|
||||
0x00, 0x00, 0x14, 0xfc, 0x03, 0x15, 0xaa, 0x02, 0x00, 0x00, 0x14, 0xfb, 0x03, 0x15, 0xaa, 0xa8, 0x06, 0x40, 0xb9,
|
||||
0xb5, 0x02, 0x08, 0x8b, 0x5a, 0x07, 0x00, 0x51, 0x7a, 0xfd, 0xff, 0x35, 0x09, 0x00, 0x00, 0x14, 0xe0, 0x03, 0x18,
|
||||
0xaa, 0xe1, 0x03, 0x17, 0xaa, 0x96, 0x00, 0x00, 0x94, 0x00, 0xff, 0xff, 0x34, 0xa8, 0x0e, 0x40, 0xf9, 0xa9, 0x16,
|
||||
0xaa, 0xe1, 0x03, 0x17, 0xaa, 0x9b, 0x00, 0x00, 0x94, 0x00, 0xff, 0xff, 0x34, 0xa8, 0x0e, 0x40, 0xf9, 0xa9, 0x16,
|
||||
0x40, 0xf9, 0x19, 0x01, 0x09, 0xcb, 0xf4, 0xff, 0xff, 0x17, 0x88, 0x02, 0x13, 0xcb, 0x28, 0x03, 0x08, 0x8b, 0x89,
|
||||
0x0b, 0x40, 0xb9, 0x13, 0x01, 0x09, 0x8b, 0x89, 0x13, 0x40, 0xb9, 0x19, 0x01, 0x09, 0x8b, 0xe8, 0x07, 0x40, 0xf9,
|
||||
0xe8, 0x0f, 0x00, 0xf9, 0x16, 0x00, 0x00, 0x90, 0xd6, 0x22, 0x0f, 0x91, 0x35, 0x01, 0x80, 0x52, 0x78, 0x0b, 0x40,
|
||||
0xb9, 0x68, 0x0f, 0x40, 0xb9, 0x1f, 0x03, 0x08, 0x6b, 0x20, 0x0a, 0x00, 0x54, 0x77, 0x52, 0x38, 0x8b, 0xe8, 0x02,
|
||||
0x40, 0xb9, 0x3c, 0x03, 0x08, 0x8b, 0xe0, 0x03, 0x1c, 0xaa, 0xe1, 0x03, 0x16, 0xaa, 0x65, 0x00, 0x00, 0x94, 0x00,
|
||||
0x07, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x6c, 0x0f, 0x91, 0x60, 0x00, 0x00, 0x94,
|
||||
0x60, 0x06, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xc8, 0x0f, 0x91, 0x72, 0x00, 0x00,
|
||||
0x94, 0xc0, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x00, 0x10, 0x91, 0x6d, 0x00,
|
||||
0x00, 0x94, 0x20, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x68, 0x10, 0x91, 0x68,
|
||||
0x00, 0x00, 0x94, 0x80, 0x04, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xb4, 0x10, 0x91,
|
||||
0x4c, 0x00, 0x00, 0x94, 0xe0, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xfc, 0x10,
|
||||
0x91, 0x47, 0x00, 0x00, 0x94, 0x40, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x60,
|
||||
0x11, 0x91, 0x42, 0x00, 0x00, 0x94, 0xa0, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21,
|
||||
0xb8, 0x11, 0x91, 0x5c, 0x00, 0x00, 0x94, 0x00, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90,
|
||||
0x21, 0xd8, 0x11, 0x91, 0x57, 0x00, 0x00, 0x94, 0x60, 0x01, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00,
|
||||
0x90, 0x21, 0xf8, 0x11, 0x91, 0x33, 0x00, 0x00, 0x94, 0xc0, 0x00, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00,
|
||||
0x00, 0x90, 0x21, 0x44, 0x12, 0x91, 0x2e, 0x00, 0x00, 0x94, 0x20, 0x02, 0x00, 0x34, 0xba, 0x00, 0x00, 0x34, 0xe8,
|
||||
0x0f, 0x40, 0xf9, 0x49, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe8, 0x06, 0x40, 0xf9,
|
||||
0x81, 0x02, 0x08, 0x8b, 0xe0, 0x63, 0x00, 0x91, 0x4c, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0x15, 0x15, 0x00,
|
||||
0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0xe1, 0x03, 0x1c, 0xaa, 0x5d, 0x00, 0x00, 0x94, 0x5a, 0x07,
|
||||
0x00, 0x11, 0x18, 0x07, 0x00, 0x11, 0xae, 0xff, 0xff, 0x17, 0xe1, 0x23, 0x41, 0xa9, 0x49, 0x01, 0x80, 0x52, 0x09,
|
||||
0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0x3d, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9,
|
||||
0x29, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0x01, 0x00, 0x00, 0x90, 0x21, 0x88, 0x12,
|
||||
0x91, 0xe0, 0x63, 0x00, 0x91, 0x4c, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0xe9, 0x07, 0x40, 0xf9, 0x00, 0x01,
|
||||
0x09, 0xcb, 0x1f, 0x01, 0x00, 0x39, 0xfd, 0x7b, 0x47, 0xa9, 0xf4, 0x4f, 0x46, 0xa9, 0xf6, 0x57, 0x45, 0xa9, 0xf8,
|
||||
0x5f, 0x44, 0xa9, 0xfa, 0x67, 0x43, 0xa9, 0xfc, 0x6f, 0x42, 0xa9, 0xff, 0x03, 0x02, 0x91, 0xc0, 0x03, 0x5f, 0xd6,
|
||||
0xf6, 0x57, 0xbd, 0xa9, 0xf4, 0x4f, 0x01, 0xa9, 0xfd, 0x7b, 0x02, 0xa9, 0xfd, 0x83, 0x00, 0x91, 0xf3, 0x03, 0x01,
|
||||
0xaa, 0xf4, 0x03, 0x00, 0xaa, 0x35, 0x00, 0x40, 0x39, 0x96, 0x02, 0x40, 0x39, 0x36, 0x01, 0x00, 0x34, 0xdf, 0x02,
|
||||
0x15, 0x6b, 0xa1, 0x00, 0x00, 0x54, 0xe0, 0x03, 0x14, 0xaa, 0xe1, 0x03, 0x13, 0xaa, 0x12, 0x00, 0x00, 0x94, 0x60,
|
||||
0x00, 0x00, 0x37, 0x94, 0x06, 0x00, 0x91, 0xf7, 0xff, 0xff, 0x17, 0xdf, 0x02, 0x00, 0x71, 0xe0, 0x07, 0x9f, 0x1a,
|
||||
0xfd, 0x7b, 0x42, 0xa9, 0xf4, 0x4f, 0x41, 0xa9, 0xf6, 0x57, 0xc3, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x14, 0x40,
|
||||
0x38, 0x29, 0x14, 0x40, 0x38, 0x1f, 0x01, 0x00, 0x71, 0x00, 0x11, 0x49, 0x7a, 0x80, 0xff, 0xff, 0x54, 0x1f, 0x01,
|
||||
0x09, 0x6b, 0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6, 0x28, 0x00, 0x40, 0x39, 0xa8, 0x00, 0x00, 0x34, 0x21,
|
||||
0x04, 0x00, 0x91, 0x09, 0x14, 0x40, 0x38, 0x3f, 0x01, 0x08, 0x6b, 0x60, 0xff, 0xff, 0x54, 0x1f, 0x01, 0x00, 0x71,
|
||||
0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6, 0x09, 0x00, 0x80, 0x52, 0x08, 0x00, 0x40, 0xf9, 0x8a, 0x07, 0x80,
|
||||
0x52, 0x0b, 0x00, 0x00, 0x90, 0x6b, 0x01, 0x13, 0x91, 0x5f, 0x11, 0x00, 0x31, 0x80, 0x01, 0x00, 0x54, 0x2c, 0x24,
|
||||
0xca, 0x9a, 0x8c, 0x0d, 0x40, 0x92, 0x2d, 0x01, 0x00, 0x12, 0x9f, 0x01, 0x00, 0x71, 0xa0, 0x19, 0x40, 0x7a, 0x29,
|
||||
0x15, 0x9f, 0x1a, 0x69, 0x00, 0x00, 0x36, 0x6c, 0x69, 0x6c, 0x38, 0x0c, 0x15, 0x00, 0x38, 0x4a, 0x11, 0x00, 0x51,
|
||||
0xf4, 0xff, 0xff, 0x17, 0x69, 0x00, 0x00, 0x37, 0x09, 0x06, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0x08, 0x00, 0x00,
|
||||
0xf9, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x00, 0x40, 0xf9, 0x29, 0x00, 0x40, 0x39, 0x89, 0x00, 0x00, 0x34, 0x21, 0x04,
|
||||
0x00, 0x91, 0x09, 0x15, 0x00, 0x38, 0xfc, 0xff, 0xff, 0x17, 0x08, 0x00, 0x00, 0xf9, 0xc0, 0x03, 0x5f, 0xd6, 0x6c,
|
||||
0x69, 0x62, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x00, 0x72,
|
||||
0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x57, 0x69, 0x74, 0x68, 0x44, 0x79, 0x6c, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x63,
|
||||
0x68, 0x65, 0x00, 0x5f, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x00, 0x5f, 0x5f,
|
||||
0x5a, 0x4e, 0x35, 0x64, 0x79, 0x6c, 0x64, 0x34, 0x31, 0x32, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x45, 0x00, 0x5f, 0x5f, 0x5a, 0x4c, 0x31, 0x32, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x00, 0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x57, 0x69, 0x74, 0x68, 0x43, 0x6c, 0x6f, 0x73,
|
||||
0x75, 0x72, 0x65, 0x00, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4d, 0x61, 0x69, 0x6e, 0x45,
|
||||
0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54,
|
||||
0x68, 0x72, 0x65, 0x61, 0x64, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x73, 0x00, 0x5f, 0x64, 0x6c, 0x6f, 0x70, 0x65,
|
||||
0x6e, 0x00, 0x5f, 0x73, 0x74, 0x72, 0x63, 0x6d, 0x70, 0x00, 0x64, 0x6f, 0x4d, 0x6f, 0x64, 0x49, 0x6e, 0x69, 0x74,
|
||||
0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x6f, 0x47, 0x65, 0x74, 0x44, 0x4f, 0x46, 0x53,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x5f,
|
||||
0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x5f, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x00,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
|
||||
0xe8, 0x0f, 0x00, 0xf9, 0x16, 0x00, 0x00, 0x90, 0xd6, 0x72, 0x0f, 0x91, 0x35, 0x01, 0x80, 0x52, 0x78, 0x0b, 0x40,
|
||||
0xb9, 0x68, 0x0f, 0x40, 0xb9, 0x1f, 0x03, 0x08, 0x6b, 0xc0, 0x0a, 0x00, 0x54, 0x77, 0x52, 0x38, 0x8b, 0xe8, 0x02,
|
||||
0x40, 0xb9, 0x3c, 0x03, 0x08, 0x8b, 0xe0, 0x03, 0x1c, 0xaa, 0xe1, 0x03, 0x16, 0xaa, 0x6a, 0x00, 0x00, 0x94, 0xa0,
|
||||
0x07, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xbc, 0x0f, 0x91, 0x65, 0x00, 0x00, 0x94,
|
||||
0x00, 0x07, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xf8, 0x0f, 0x91, 0x60, 0x00, 0x00,
|
||||
0x94, 0x60, 0x06, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x54, 0x10, 0x91, 0x72, 0x00,
|
||||
0x00, 0x94, 0xc0, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x8c, 0x10, 0x91, 0x6d,
|
||||
0x00, 0x00, 0x94, 0x20, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xf4, 0x10, 0x91,
|
||||
0x68, 0x00, 0x00, 0x94, 0x80, 0x04, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x40, 0x11,
|
||||
0x91, 0x4c, 0x00, 0x00, 0x94, 0xe0, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x88,
|
||||
0x11, 0x91, 0x47, 0x00, 0x00, 0x94, 0x40, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21,
|
||||
0xec, 0x11, 0x91, 0x42, 0x00, 0x00, 0x94, 0xa0, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90,
|
||||
0x21, 0x44, 0x12, 0x91, 0x5c, 0x00, 0x00, 0x94, 0x00, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00,
|
||||
0x90, 0x21, 0x64, 0x12, 0x91, 0x57, 0x00, 0x00, 0x94, 0x60, 0x01, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00,
|
||||
0x00, 0x90, 0x21, 0x84, 0x12, 0x91, 0x33, 0x00, 0x00, 0x94, 0xc0, 0x00, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01,
|
||||
0x00, 0x00, 0x90, 0x21, 0xd0, 0x12, 0x91, 0x2e, 0x00, 0x00, 0x94, 0x20, 0x02, 0x00, 0x34, 0xba, 0x00, 0x00, 0x34,
|
||||
0xe8, 0x0f, 0x40, 0xf9, 0x49, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe8, 0x06, 0x40,
|
||||
0xf9, 0x81, 0x02, 0x08, 0x8b, 0xe0, 0x63, 0x00, 0x91, 0x4c, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0x15, 0x15,
|
||||
0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0xe1, 0x03, 0x1c, 0xaa, 0x5d, 0x00, 0x00, 0x94, 0x5a,
|
||||
0x07, 0x00, 0x11, 0x18, 0x07, 0x00, 0x11, 0xa9, 0xff, 0xff, 0x17, 0xe1, 0x23, 0x41, 0xa9, 0x49, 0x01, 0x80, 0x52,
|
||||
0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0x3d, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40,
|
||||
0xf9, 0x29, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0x01, 0x00, 0x00, 0x90, 0x21, 0x14,
|
||||
0x13, 0x91, 0xe0, 0x63, 0x00, 0x91, 0x4c, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0xe9, 0x07, 0x40, 0xf9, 0x00,
|
||||
0x01, 0x09, 0xcb, 0x1f, 0x01, 0x00, 0x39, 0xfd, 0x7b, 0x47, 0xa9, 0xf4, 0x4f, 0x46, 0xa9, 0xf6, 0x57, 0x45, 0xa9,
|
||||
0xf8, 0x5f, 0x44, 0xa9, 0xfa, 0x67, 0x43, 0xa9, 0xfc, 0x6f, 0x42, 0xa9, 0xff, 0x03, 0x02, 0x91, 0xc0, 0x03, 0x5f,
|
||||
0xd6, 0xf6, 0x57, 0xbd, 0xa9, 0xf4, 0x4f, 0x01, 0xa9, 0xfd, 0x7b, 0x02, 0xa9, 0xfd, 0x83, 0x00, 0x91, 0xf3, 0x03,
|
||||
0x01, 0xaa, 0xf4, 0x03, 0x00, 0xaa, 0x35, 0x00, 0x40, 0x39, 0x96, 0x02, 0x40, 0x39, 0x36, 0x01, 0x00, 0x34, 0xdf,
|
||||
0x02, 0x15, 0x6b, 0xa1, 0x00, 0x00, 0x54, 0xe0, 0x03, 0x14, 0xaa, 0xe1, 0x03, 0x13, 0xaa, 0x12, 0x00, 0x00, 0x94,
|
||||
0x60, 0x00, 0x00, 0x37, 0x94, 0x06, 0x00, 0x91, 0xf7, 0xff, 0xff, 0x17, 0xdf, 0x02, 0x00, 0x71, 0xe0, 0x07, 0x9f,
|
||||
0x1a, 0xfd, 0x7b, 0x42, 0xa9, 0xf4, 0x4f, 0x41, 0xa9, 0xf6, 0x57, 0xc3, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x14,
|
||||
0x40, 0x38, 0x29, 0x14, 0x40, 0x38, 0x1f, 0x01, 0x00, 0x71, 0x00, 0x11, 0x49, 0x7a, 0x80, 0xff, 0xff, 0x54, 0x1f,
|
||||
0x01, 0x09, 0x6b, 0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6, 0x28, 0x00, 0x40, 0x39, 0xa8, 0x00, 0x00, 0x34,
|
||||
0x21, 0x04, 0x00, 0x91, 0x09, 0x14, 0x40, 0x38, 0x3f, 0x01, 0x08, 0x6b, 0x60, 0xff, 0xff, 0x54, 0x1f, 0x01, 0x00,
|
||||
0x71, 0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6, 0x09, 0x00, 0x80, 0x52, 0x08, 0x00, 0x40, 0xf9, 0x8a, 0x07,
|
||||
0x80, 0x52, 0x0b, 0x00, 0x00, 0x90, 0x6b, 0xc1, 0x13, 0x91, 0x5f, 0x11, 0x00, 0x31, 0x80, 0x01, 0x00, 0x54, 0x2c,
|
||||
0x24, 0xca, 0x9a, 0x8c, 0x0d, 0x40, 0x92, 0x2d, 0x01, 0x00, 0x12, 0x9f, 0x01, 0x00, 0x71, 0xa0, 0x19, 0x40, 0x7a,
|
||||
0x29, 0x15, 0x9f, 0x1a, 0x69, 0x00, 0x00, 0x36, 0x6c, 0x69, 0x6c, 0x38, 0x0c, 0x15, 0x00, 0x38, 0x4a, 0x11, 0x00,
|
||||
0x51, 0xf4, 0xff, 0xff, 0x17, 0x69, 0x00, 0x00, 0x37, 0x09, 0x06, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0x08, 0x00,
|
||||
0x00, 0xf9, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x00, 0x40, 0xf9, 0x29, 0x00, 0x40, 0x39, 0x89, 0x00, 0x00, 0x34, 0x21,
|
||||
0x04, 0x00, 0x91, 0x09, 0x15, 0x00, 0x38, 0xfc, 0xff, 0xff, 0x17, 0x08, 0x00, 0x00, 0xf9, 0xc0, 0x03, 0x5f, 0xd6,
|
||||
0x6c, 0x69, 0x62, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x00,
|
||||
0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x62, 0x6a, 0x43, 0x49, 0x6e, 0x69, 0x74, 0x00, 0x72, 0x65, 0x73, 0x74,
|
||||
0x61, 0x72, 0x74, 0x57, 0x69, 0x74, 0x68, 0x44, 0x79, 0x6c, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x00,
|
||||
0x5f, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x00, 0x5f, 0x5f, 0x5a, 0x4e, 0x35,
|
||||
0x64, 0x79, 0x6c, 0x64, 0x34, 0x31, 0x32, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x45, 0x00, 0x5f, 0x5f, 0x5a, 0x4c, 0x31, 0x32, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x00, 0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x57, 0x69, 0x74, 0x68, 0x43, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65,
|
||||
0x00, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4d, 0x61, 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63,
|
||||
0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x68, 0x72, 0x65,
|
||||
0x61, 0x64, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x73, 0x00, 0x5f, 0x64, 0x6c, 0x6f, 0x70, 0x65, 0x6e, 0x00, 0x5f,
|
||||
0x73, 0x74, 0x72, 0x63, 0x6d, 0x70, 0x00, 0x64, 0x6f, 0x4d, 0x6f, 0x64, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x75, 0x6e,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x6f, 0x47, 0x65, 0x74, 0x44, 0x4f, 0x46, 0x53, 0x65, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x5f, 0x5f, 0x54, 0x45,
|
||||
0x58, 0x54, 0x00, 0x5f, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
|
||||
};
|
||||
|
||||
private async SymbolSet resolve_symbols (SymbolQuery query, Cancellable? cancellable) throws GLib.Error {
|
||||
|
||||
@@ -1,37 +1,33 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class InstallationProxyClient : Object, AsyncInitable {
|
||||
public LockdownClient lockdown {
|
||||
public Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private PlistServiceClient service;
|
||||
|
||||
private InstallationProxyClient (LockdownClient lockdown) {
|
||||
Object (lockdown: lockdown);
|
||||
private InstallationProxyClient (Device device) {
|
||||
Object (device: device);
|
||||
}
|
||||
|
||||
public static async InstallationProxyClient open (LockdownClient lockdown, Cancellable? cancellable = null) throws InstallationProxyError, IOError {
|
||||
var client = new InstallationProxyClient (lockdown);
|
||||
public static async InstallationProxyClient open (Device device, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var client = new InstallationProxyClient (device);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_local_error (e);
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws InstallationProxyError, IOError {
|
||||
try {
|
||||
var stream = yield lockdown.start_service ("com.apple.mobile.installation_proxy", cancellable);
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
var stream = yield device.open_lockdown_service ("com.apple.mobile.installation_proxy", cancellable);
|
||||
|
||||
service = new PlistServiceClient (stream);
|
||||
} catch (LockdownError e) {
|
||||
throw new InstallationProxyError.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
service = new PlistServiceClient (stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -40,8 +36,7 @@ namespace Frida.Fruity {
|
||||
yield service.close (cancellable);
|
||||
}
|
||||
|
||||
public async Gee.ArrayList<ApplicationDetails> browse (Cancellable? cancellable = null)
|
||||
throws InstallationProxyError, IOError {
|
||||
public async Gee.ArrayList<ApplicationDetails> browse (Cancellable? cancellable = null) throws Error, IOError {
|
||||
try {
|
||||
var result = new Gee.ArrayList<ApplicationDetails> ();
|
||||
|
||||
@@ -75,7 +70,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public async Gee.HashMap<string, ApplicationDetails> lookup (PlistDict query, Cancellable? cancellable = null)
|
||||
throws InstallationProxyError, IOError {
|
||||
throws Error, IOError {
|
||||
try {
|
||||
var result = new Gee.HashMap<string, ApplicationDetails> ();
|
||||
|
||||
@@ -189,28 +184,13 @@ namespace Frida.Fruity {
|
||||
return new ApplicationDetails (identifier, name, version, build, path, containers, debuggable);
|
||||
}
|
||||
|
||||
private static void throw_local_error (GLib.Error e) throws InstallationProxyError, IOError {
|
||||
if (e is InstallationProxyError)
|
||||
throw (InstallationProxyError) e;
|
||||
|
||||
if (e is IOError)
|
||||
throw (IOError) e;
|
||||
|
||||
assert_not_reached ();
|
||||
private static Error error_from_service (PlistServiceError e) {
|
||||
return new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
private static InstallationProxyError error_from_service (PlistServiceError e) {
|
||||
return new InstallationProxyError.PROTOCOL ("%s", e.message);
|
||||
private static Error error_from_plist (PlistError e) {
|
||||
return new Error.PROTOCOL ("Unexpected response: %s", e.message);
|
||||
}
|
||||
|
||||
private static InstallationProxyError error_from_plist (PlistError e) {
|
||||
return new InstallationProxyError.PROTOCOL ("Unexpected response: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public errordomain InstallationProxyError {
|
||||
INVALID_ARGUMENT,
|
||||
PROTOCOL
|
||||
}
|
||||
|
||||
public class ApplicationDetails : Object {
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
namespace Frida {
|
||||
using CoreFoundation;
|
||||
using Darwin.XNU;
|
||||
|
||||
internal class IORegistry {
|
||||
private MachPort main_port;
|
||||
|
||||
public static IORegistry open () throws Error {
|
||||
MachPort port;
|
||||
kern_check (Darwin.IOKit.main_port (MachPort.NULL, out port));
|
||||
return new IORegistry (port);
|
||||
}
|
||||
|
||||
private IORegistry (MachPort main_port) {
|
||||
this.main_port = main_port;
|
||||
}
|
||||
|
||||
public IOIterator<IORegistryEntry> matching_services (owned MutableDictionary matching_dict) throws Error {
|
||||
Darwin.IOKit.IOIterator h;
|
||||
kern_check (Darwin.IOKit.get_matching_services (main_port, matching_dict, out h));
|
||||
return new IOIterator<IORegistryEntry> ((owned) h);
|
||||
}
|
||||
}
|
||||
|
||||
internal class IORegistryEntry : GLib.Object {
|
||||
public IOObject io_object {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
internal IORegistryEntry (IOObject obj) {
|
||||
GLib.Object (io_object: obj);
|
||||
}
|
||||
|
||||
public MutableDictionary get_properties () throws Error {
|
||||
MutableDictionary properties;
|
||||
kern_check (unwrap ().create_cf_properties (out properties, null, 0));
|
||||
return properties;
|
||||
}
|
||||
|
||||
public string? get_string_property (string key) {
|
||||
var v = (String) unwrap ().create_cf_property (String.make (key), null, 0);
|
||||
return (v != null) ? v.to_string () : null;
|
||||
}
|
||||
|
||||
public IORegistryEntry parent (string plane) throws Error {
|
||||
Darwin.IOKit.IOObject h;
|
||||
kern_check (unwrap ().get_parent_entry (plane, out h));
|
||||
return new IORegistryEntry (new IOObject ((owned) h));
|
||||
}
|
||||
|
||||
private unowned Darwin.IOKit.IORegistryEntry unwrap () {
|
||||
return (Darwin.IOKit.IORegistryEntry) io_object.handle;
|
||||
}
|
||||
}
|
||||
|
||||
internal class IOIterator<T> {
|
||||
private Darwin.IOKit.IOIterator handle;
|
||||
|
||||
internal IOIterator (owned Darwin.IOKit.IOIterator handle) {
|
||||
this.handle = (owned) handle;
|
||||
}
|
||||
|
||||
public IOIterator<T> iterator () {
|
||||
return this;
|
||||
}
|
||||
|
||||
public T? next_value () {
|
||||
Darwin.IOKit.IOObject h = handle.next ();
|
||||
if (h == Darwin.IOKit.IOObject.NULL)
|
||||
return null;
|
||||
return GLib.Object.new (typeof (T), io_object: new IOObject ((owned) h));
|
||||
}
|
||||
}
|
||||
|
||||
internal class IOObject {
|
||||
internal Darwin.IOKit.IOObject handle;
|
||||
|
||||
internal IOObject (owned Darwin.IOKit.IOObject handle) {
|
||||
this.handle = (owned) handle;
|
||||
}
|
||||
}
|
||||
|
||||
private void kern_check (KernReturn result) throws Error {
|
||||
if (result != KernReturn.SUCCESS)
|
||||
throw new Error.NOT_SUPPORTED ("%s", mach_error_string (result));
|
||||
}
|
||||
}
|
||||
+29
-41
@@ -1,18 +1,14 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class LockdownClient : Object, AsyncInitable {
|
||||
public class LockdownClient : Object {
|
||||
public signal void closed ();
|
||||
|
||||
public DeviceDetails device_details {
|
||||
public PlistServiceClient service {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public IOStream stream {
|
||||
get { return service.stream; }
|
||||
}
|
||||
|
||||
private PlistServiceClient service;
|
||||
private UsbmuxDevice? usbmux_device;
|
||||
private Plist? pair_record;
|
||||
private string? host_id;
|
||||
private string? system_buid;
|
||||
@@ -22,29 +18,20 @@ namespace Frida.Fruity {
|
||||
|
||||
private const uint16 LOCKDOWN_PORT = 62078;
|
||||
|
||||
private LockdownClient (DeviceDetails device_details) {
|
||||
Object (device_details: device_details);
|
||||
public LockdownClient (IOStream stream) {
|
||||
Object (service: new PlistServiceClient (stream));
|
||||
}
|
||||
|
||||
public static async LockdownClient open (DeviceDetails device_details, Cancellable? cancellable = null)
|
||||
construct {
|
||||
service.closed.connect (on_service_closed);
|
||||
}
|
||||
|
||||
public static async LockdownClient open (UsbmuxDevice device, Cancellable? cancellable = null)
|
||||
throws LockdownError, IOError {
|
||||
var client = new LockdownClient (device_details);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_local_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws LockdownError, IOError {
|
||||
var device = device_details;
|
||||
|
||||
try {
|
||||
var usbmux = yield UsbmuxClient.open (cancellable);
|
||||
|
||||
Plist pair_record;
|
||||
try {
|
||||
pair_record = yield usbmux.read_pair_record (device.udid, cancellable);
|
||||
} catch (UsbmuxError e) {
|
||||
@@ -52,6 +39,10 @@ namespace Frida.Fruity {
|
||||
throw new LockdownError.NOT_PAIRED ("Not paired");
|
||||
throw e;
|
||||
}
|
||||
|
||||
string? host_id = null;
|
||||
string? system_buid = null;
|
||||
TlsCertificate? tls_certificate = null;
|
||||
try {
|
||||
host_id = pair_record.get_string ("HostID");
|
||||
system_buid = pair_record.get_string ("SystemBUID");
|
||||
@@ -64,15 +55,19 @@ namespace Frida.Fruity {
|
||||
|
||||
yield usbmux.connect_to_port (device.id, LOCKDOWN_PORT, cancellable);
|
||||
|
||||
service = new PlistServiceClient (usbmux.connection);
|
||||
service.closed.connect (on_service_closed);
|
||||
var client = new LockdownClient (usbmux.connection);
|
||||
client.usbmux_device = device;
|
||||
client.pair_record = pair_record;
|
||||
client.host_id = host_id;
|
||||
client.system_buid = system_buid;
|
||||
client.tls_certificate = tls_certificate;
|
||||
|
||||
yield query_type (cancellable);
|
||||
yield client.query_type (cancellable);
|
||||
|
||||
return client;
|
||||
} catch (UsbmuxError e) {
|
||||
throw new LockdownError.UNSUPPORTED ("%s", e.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable = null) throws IOError {
|
||||
@@ -182,7 +177,7 @@ namespace Frida.Fruity {
|
||||
bool enable_encryption = response.has ("EnableServiceSSL") && response.get_boolean ("EnableServiceSSL");
|
||||
|
||||
var client = yield UsbmuxClient.open (cancellable);
|
||||
yield client.connect_to_port (device_details.id, (uint16) response.get_integer ("Port"), cancellable);
|
||||
yield client.connect_to_port (usbmux_device.id, (uint16) response.get_integer ("Port"), cancellable);
|
||||
|
||||
SocketConnection raw_connection = client.connection;
|
||||
IOStream stream = raw_connection;
|
||||
@@ -242,7 +237,7 @@ namespace Frida.Fruity {
|
||||
|
||||
try {
|
||||
var usbmux = yield UsbmuxClient.open (cancellable);
|
||||
yield usbmux.delete_pair_record (device_details.udid, cancellable);
|
||||
yield usbmux.delete_pair_record (usbmux_device.udid, cancellable);
|
||||
} catch (UsbmuxError e) {
|
||||
if (!(e is UsbmuxError.INVALID_ARGUMENT))
|
||||
throw new LockdownError.PROTOCOL ("%s", e.message);
|
||||
@@ -269,17 +264,9 @@ namespace Frida.Fruity {
|
||||
return request;
|
||||
}
|
||||
|
||||
private static void throw_local_error (GLib.Error e) throws LockdownError, IOError {
|
||||
if (e is LockdownError)
|
||||
throw (LockdownError) e;
|
||||
|
||||
if (e is IOError)
|
||||
throw (IOError) e;
|
||||
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
private static LockdownError error_from_service (PlistServiceError e) {
|
||||
if (e is PlistServiceError.CONNECTION_CLOSED)
|
||||
return new LockdownError.CONNECTION_CLOSED ("%s", e.message);
|
||||
return new LockdownError.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
@@ -289,6 +276,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public errordomain LockdownError {
|
||||
CONNECTION_CLOSED,
|
||||
INVALID_SERVICE,
|
||||
NOT_PAIRED,
|
||||
UNSUPPORTED,
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
internal sealed class UsbNcmDriver : Object, AsyncInitable {
|
||||
public UsbDevice device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public VirtualNetworkStack netstack {
|
||||
get {
|
||||
return _netstack;
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress? remote_ipv6_address {
|
||||
get {
|
||||
return _remote_ipv6_address;
|
||||
}
|
||||
}
|
||||
|
||||
private uint8 data_iface;
|
||||
private int data_altsetting;
|
||||
private uint8 rx_address;
|
||||
private uint8 tx_address;
|
||||
|
||||
private VirtualNetworkStack? _netstack;
|
||||
private uint16 next_outgoing_sequence = 1;
|
||||
|
||||
private InetAddress? _remote_ipv6_address;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
private enum UsbDescriptorType {
|
||||
INTERFACE = 0x04,
|
||||
}
|
||||
|
||||
private enum UsbCommSubclass {
|
||||
NCM = 0x0d,
|
||||
}
|
||||
|
||||
private enum UsbDataSubclass {
|
||||
UNDEFINED = 0x00,
|
||||
}
|
||||
|
||||
private enum UsbCdcDescriptorSubtype {
|
||||
ETHERNET = 0x0f,
|
||||
}
|
||||
|
||||
private enum EtherType {
|
||||
IPV6 = 0x86dd,
|
||||
}
|
||||
|
||||
private enum IPV6NextHeader {
|
||||
UDP = 0x11,
|
||||
}
|
||||
|
||||
public static async UsbNcmDriver open (UsbDevice device, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var driver = new UsbNcmDriver (device);
|
||||
|
||||
try {
|
||||
yield driver.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
return driver;
|
||||
}
|
||||
|
||||
private UsbNcmDriver (UsbDevice device) {
|
||||
Object (device: device);
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
unowned LibUSB.Device raw_device = device.raw_device;
|
||||
unowned LibUSB.DeviceHandle handle = device.handle;
|
||||
|
||||
var dev_desc = LibUSB.DeviceDescriptor (raw_device);
|
||||
|
||||
LibUSB.ConfigDescriptor current_config;
|
||||
Usb.check (raw_device.get_active_config_descriptor (out current_config), "Failed to get active config descriptor");
|
||||
|
||||
int desired_config_value = -1;
|
||||
bool found_cdc_header = false;
|
||||
bool found_data_interface = false;
|
||||
uint8 mac_address_index = 0;
|
||||
for (uint8 config_value = dev_desc.bNumConfigurations; config_value != 0; config_value--) {
|
||||
LibUSB.ConfigDescriptor config;
|
||||
Usb.check (raw_device.get_config_descriptor_by_value (config_value, out config),
|
||||
"Failed to get config descriptor");
|
||||
|
||||
foreach (var iface in config.@interface) {
|
||||
foreach (var setting in iface.altsetting) {
|
||||
if (setting.bInterfaceClass == LibUSB.ClassCode.COMM &&
|
||||
setting.bInterfaceSubClass == UsbCommSubclass.NCM) {
|
||||
try {
|
||||
parse_cdc_header (setting.extra, out mac_address_index);
|
||||
found_cdc_header = true;
|
||||
} catch (Error e) {
|
||||
break;
|
||||
}
|
||||
} else if (setting.bInterfaceClass == LibUSB.ClassCode.DATA &&
|
||||
setting.bInterfaceSubClass == UsbDataSubclass.UNDEFINED &&
|
||||
setting.endpoint.length == 2) {
|
||||
found_data_interface = true;
|
||||
|
||||
data_iface = setting.bInterfaceNumber;
|
||||
data_altsetting = setting.bAlternateSetting;
|
||||
|
||||
foreach (var ep in setting.endpoint) {
|
||||
if ((ep.bEndpointAddress & LibUSB.EndpointDirection.MASK) ==
|
||||
LibUSB.EndpointDirection.IN) {
|
||||
rx_address = ep.bEndpointAddress;
|
||||
} else {
|
||||
tx_address = ep.bEndpointAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_cdc_header || found_data_interface) {
|
||||
desired_config_value = config_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_cdc_header || !found_data_interface)
|
||||
throw new Error.NOT_SUPPORTED ("%s", make_user_error_message ("No USB CDC-NCM interface found"));
|
||||
|
||||
uint8 mac_address[6];
|
||||
string mac_address_str = yield device.read_string_descriptor (mac_address_index, device.default_language_id,
|
||||
cancellable);
|
||||
if (mac_address_str.length != 12)
|
||||
throw new Error.PROTOCOL ("Invalid MAC address");
|
||||
for (uint i = 0; i != 6; i++) {
|
||||
uint v;
|
||||
mac_address_str.substring (i * 2, 2).scanf ("%02X", out v);
|
||||
mac_address[i] = (uint8) v;
|
||||
}
|
||||
|
||||
if (current_config.bConfigurationValue != desired_config_value) {
|
||||
foreach (var iface in current_config.@interface) {
|
||||
unowned LibUSB.InterfaceDescriptor setting = iface.altsetting[0];
|
||||
var res = handle.kernel_driver_active (setting.bInterfaceNumber);
|
||||
if (res == 1)
|
||||
handle.detach_kernel_driver (setting.bInterfaceNumber);
|
||||
}
|
||||
Usb.check (handle.set_configuration (desired_config_value), "Failed to set configuration");
|
||||
}
|
||||
try {
|
||||
Usb.check (handle.claim_interface (data_iface), "Failed to claim USB interface");
|
||||
} catch (Error e) {
|
||||
throw new Error.PERMISSION_DENIED ("%s",
|
||||
make_user_error_message (@"Unable to claim USB CDC-NCM interface ($(e.message))"));
|
||||
}
|
||||
Usb.check (handle.set_interface_alt_setting (data_iface, data_altsetting),
|
||||
"Failed to set USB interface alt setting");
|
||||
|
||||
_netstack = new VirtualNetworkStack (new Bytes (mac_address), null, 1500);
|
||||
_netstack.outgoing_datagram.connect (on_netif_outgoing_datagram);
|
||||
|
||||
process_incoming_datagrams.begin ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string make_user_error_message (string message) {
|
||||
#if WINDOWS
|
||||
return message + "; use https://zadig.akeo.ie to switch from Apple's official driver onto Microsoft's WinUSB " +
|
||||
"driver, so libusb can access it";
|
||||
#else
|
||||
return message;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void close () {
|
||||
io_cancellable.cancel ();
|
||||
_netstack.stop ();
|
||||
}
|
||||
|
||||
private async void process_incoming_datagrams () {
|
||||
var data = new uint8[64 * 1024];
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
size_t n = yield device.bulk_transfer (rx_address, data, uint.MAX, io_cancellable);
|
||||
handle_ncm_frame (data[:n]);
|
||||
} catch (GLib.Error e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handle_ncm_frame (uint8[] data) throws GLib.Error {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_data (data));
|
||||
input.byte_order = LITTLE_ENDIAN;
|
||||
|
||||
uint8 raw_signature[4 + 1];
|
||||
unowned string signature = (string) raw_signature;
|
||||
size_t bytes_read;
|
||||
|
||||
input.read_all (raw_signature[:4], out bytes_read);
|
||||
if (signature != "NCMH")
|
||||
throw new Error.PROTOCOL ("Invalid NTH16 signature");
|
||||
input.skip (6);
|
||||
var ndp_index = input.read_uint16 ();
|
||||
|
||||
do {
|
||||
input.seek (ndp_index, SET);
|
||||
input.read_all (raw_signature[:4], out bytes_read);
|
||||
if (signature != "NCM0")
|
||||
throw new Error.PROTOCOL ("Invalid NDP16 signature");
|
||||
input.skip (2);
|
||||
var next_ndp_index = input.read_uint16 ();
|
||||
|
||||
while (true) {
|
||||
var datagram_index = input.read_uint16 ();
|
||||
var datagram_length = input.read_uint16 ();
|
||||
if (datagram_index == 0 || datagram_length == 0)
|
||||
break;
|
||||
|
||||
int64 previous_offset = input.tell ();
|
||||
input.seek (datagram_index, SET);
|
||||
var datagram_buf = new uint8[datagram_length];
|
||||
input.read_all (datagram_buf, out bytes_read);
|
||||
input.seek (previous_offset, SET);
|
||||
|
||||
var datagram = new Bytes.take ((owned) datagram_buf);
|
||||
|
||||
if (_remote_ipv6_address == null) {
|
||||
_remote_ipv6_address = try_infer_remote_address_from_datagram (datagram);
|
||||
if (_remote_ipv6_address != null)
|
||||
notify_property ("remote-ipv6-address");
|
||||
}
|
||||
|
||||
_netstack.handle_incoming_datagram (datagram);
|
||||
}
|
||||
|
||||
ndp_index = next_ndp_index;
|
||||
} while (ndp_index != 0);
|
||||
}
|
||||
|
||||
private async void on_netif_outgoing_datagram (Bytes datagram) {
|
||||
uint16 transfer_header_length = 12;
|
||||
uint16 ndp_header_length = 16;
|
||||
uint16 alignment_padding_length = 2;
|
||||
|
||||
uint16 datagram_start_index = transfer_header_length + ndp_header_length + alignment_padding_length;
|
||||
uint16 datagram_length = (uint16) datagram.length;
|
||||
|
||||
uint16 sentinel_start_index = 0;
|
||||
uint16 sentinel_size = 0;
|
||||
|
||||
uint16 sequence = next_outgoing_sequence++;
|
||||
uint16 block_length = datagram_start_index + datagram_length;
|
||||
uint16 ndp_index = transfer_header_length;
|
||||
uint16 next_ndp_index = 0;
|
||||
|
||||
uint16 alignment_padding_value = 0;
|
||||
|
||||
var frame = new BufferBuilder (LITTLE_ENDIAN)
|
||||
.append_string ("NCMH", StringTerminator.NONE)
|
||||
.append_uint16 (transfer_header_length)
|
||||
.append_uint16 (sequence)
|
||||
.append_uint16 (block_length)
|
||||
.append_uint16 (ndp_index)
|
||||
.append_string ("NCM0", StringTerminator.NONE)
|
||||
.append_uint16 (ndp_header_length)
|
||||
.append_uint16 (next_ndp_index)
|
||||
.append_uint16 (datagram_start_index)
|
||||
.append_uint16 (datagram_length)
|
||||
.append_uint16 (sentinel_start_index)
|
||||
.append_uint16 (sentinel_size)
|
||||
.append_uint16 (alignment_padding_value)
|
||||
.append_bytes (datagram)
|
||||
.build ();
|
||||
|
||||
try {
|
||||
yield device.bulk_transfer (tx_address, frame.get_data (), uint.MAX, io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static void parse_cdc_header (uint8[] header, out uint8 mac_address_index) throws Error {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_data (header));
|
||||
input.set_byte_order (LITTLE_ENDIAN);
|
||||
|
||||
try {
|
||||
for (int offset = 0; offset != header.length;) {
|
||||
uint8 length = input.read_byte ();
|
||||
if (length < 3)
|
||||
throw new Error.PROTOCOL ("Invalid descriptor length");
|
||||
|
||||
uint8 descriptor_type = input.read_byte ();
|
||||
if (descriptor_type != (LibUSB.RequestType.CLASS | UsbDescriptorType.INTERFACE))
|
||||
throw new Error.PROTOCOL ("Invalid descriptor type");
|
||||
|
||||
uint8 descriptor_subtype = input.read_byte ();
|
||||
if (descriptor_subtype == UsbCdcDescriptorSubtype.ETHERNET) {
|
||||
mac_address_index = input.read_byte ();
|
||||
return;
|
||||
}
|
||||
|
||||
input.skip (length - 3);
|
||||
offset += length;
|
||||
}
|
||||
} catch (IOError e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
throw new Error.PROTOCOL ("CDC Ethernet descriptor not found");
|
||||
}
|
||||
|
||||
private static InetAddress? try_infer_remote_address_from_datagram (Bytes datagram) {
|
||||
if (datagram.get_size () < 0x3e)
|
||||
return null;
|
||||
|
||||
var buf = new Buffer (datagram, BIG_ENDIAN);
|
||||
|
||||
var ethertype = (EtherType) buf.read_uint16 (12);
|
||||
if (ethertype != IPV6)
|
||||
return null;
|
||||
|
||||
var next_header = (IPV6NextHeader) buf.read_uint8 (20);
|
||||
if (next_header != UDP)
|
||||
return null;
|
||||
|
||||
return new InetAddress.from_bytes (datagram[22:22 + 16].get_data (), IPV6);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,245 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class OpackBuilder {
|
||||
protected BufferBuilder builder = new BufferBuilder (LITTLE_ENDIAN);
|
||||
private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();
|
||||
|
||||
public OpackBuilder () {
|
||||
push_scope (new Scope (ROOT));
|
||||
}
|
||||
|
||||
public unowned OpackBuilder begin_dictionary () {
|
||||
begin_value ();
|
||||
|
||||
size_t type_offset = builder.offset;
|
||||
builder.append_uint8 (0x00);
|
||||
|
||||
push_scope (new CollectionScope (type_offset));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned OpackBuilder set_member_name (string name) {
|
||||
return add_string_value (name);
|
||||
}
|
||||
|
||||
public unowned OpackBuilder end_dictionary () {
|
||||
CollectionScope scope = pop_scope ();
|
||||
|
||||
size_t n = scope.num_values / 2;
|
||||
if (n < 0xf) {
|
||||
builder.write_uint8 (scope.type_offset, 0xe0 | n);
|
||||
} else {
|
||||
builder
|
||||
.write_uint8 (scope.type_offset, 0xef)
|
||||
.append_uint8 (0x03);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned OpackBuilder add_string_value (string val) {
|
||||
begin_value ();
|
||||
|
||||
size_t len = val.length;
|
||||
|
||||
if (len > uint32.MAX) {
|
||||
builder
|
||||
.append_uint8 (0x6f)
|
||||
.append_string (val, StringTerminator.NUL);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
if (len <= 0x20)
|
||||
builder.append_uint8 ((uint8) (0x40 + len));
|
||||
else if (len <= uint8.MAX)
|
||||
builder.append_uint8 (0x61).append_uint8 ((uint8) len);
|
||||
else if (len <= uint16.MAX)
|
||||
builder.append_uint8 (0x62).append_uint16 ((uint16) len);
|
||||
else if (len <= 0xffffff)
|
||||
builder.append_uint8 (0x63).append_uint8 ((uint8) (len & 0xff)).append_uint16 ((uint16) (len >> 8));
|
||||
else
|
||||
builder.append_uint8 (0x64).append_uint32 ((uint32) len);
|
||||
|
||||
builder.append_string (val, StringTerminator.NONE);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned OpackBuilder add_data_value (Bytes val) {
|
||||
begin_value ();
|
||||
|
||||
size_t size = val.get_size ();
|
||||
if (size <= 0x20)
|
||||
builder.append_uint8 ((uint8) (0x70 + size));
|
||||
else if (size <= uint8.MAX)
|
||||
builder.append_uint8 (0x91).append_uint8 ((uint8) size);
|
||||
else if (size <= uint16.MAX)
|
||||
builder.append_uint8 (0x92).append_uint16 ((uint16) size);
|
||||
else if (size <= 0xffffff)
|
||||
builder.append_uint8 (0x93).append_uint8 ((uint8) (size & 0xff)).append_uint16 ((uint16) (size >> 8));
|
||||
else
|
||||
builder.append_uint8 (0x94).append_uint32 ((uint32) size);
|
||||
|
||||
builder.append_bytes (val);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private unowned OpackBuilder begin_value () {
|
||||
peek_scope ().num_values++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Bytes build () {
|
||||
return builder.build ();
|
||||
}
|
||||
|
||||
private void push_scope (Scope scope) {
|
||||
scopes.offer_tail (scope);
|
||||
}
|
||||
|
||||
private Scope peek_scope () {
|
||||
return scopes.peek_tail ();
|
||||
}
|
||||
|
||||
private T pop_scope<T> () {
|
||||
return (T) scopes.poll_tail ();
|
||||
}
|
||||
|
||||
private class Scope {
|
||||
public Kind kind;
|
||||
public size_t num_values = 0;
|
||||
|
||||
public enum Kind {
|
||||
ROOT,
|
||||
COLLECTION,
|
||||
}
|
||||
|
||||
public Scope (Kind kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
}
|
||||
|
||||
private class CollectionScope : Scope {
|
||||
public size_t type_offset;
|
||||
|
||||
public CollectionScope (size_t type_offset) {
|
||||
base (COLLECTION);
|
||||
this.type_offset = type_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OpackParser {
|
||||
private BufferReader reader;
|
||||
|
||||
[Flags]
|
||||
private enum ValueFlags {
|
||||
ALLOW_TERMINATOR = 1 << 0,
|
||||
}
|
||||
|
||||
public static Variant parse (Bytes opack) throws Error {
|
||||
var parser = new OpackParser (opack);
|
||||
return parser.read_value ();
|
||||
}
|
||||
|
||||
private OpackParser (Bytes opack) {
|
||||
reader = new BufferReader (new Buffer (opack, LITTLE_ENDIAN));
|
||||
}
|
||||
|
||||
private Variant? read_value (ValueFlags flags = 0) throws Error {
|
||||
uint8 v = reader.read_uint8 ();
|
||||
uint8 top = v >> 4;
|
||||
uint8 bottom = v & 0b1111;
|
||||
switch (top) {
|
||||
case 0:
|
||||
switch (bottom) {
|
||||
case 3:
|
||||
if ((flags & ValueFlags.ALLOW_TERMINATOR) == 0)
|
||||
throw new Error.PROTOCOL ("Unexpected OPACK terminator");
|
||||
return null;
|
||||
}
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
|
||||
case 3:
|
||||
switch (bottom) {
|
||||
case 0: return (int64) reader.read_int8 ();
|
||||
case 1: return (int64) reader.read_int16 ();
|
||||
case 2: return (int64) reader.read_int32 ();
|
||||
case 3: return (int64) reader.read_int64 ();
|
||||
}
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
return read_string (v - 0x40);
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
return read_data (v - 0x70);
|
||||
case 0xe:
|
||||
return read_dictionary (bottom);
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported OPACK type 0x%02x", v);
|
||||
}
|
||||
}
|
||||
|
||||
private Variant read_string (size_t len) throws Error {
|
||||
if (len == 0x2f)
|
||||
return reader.read_string ();
|
||||
len = read_variable_length (len);
|
||||
return reader.read_fixed_string (len);
|
||||
}
|
||||
|
||||
private Variant read_data (size_t len) throws Error {
|
||||
len = read_variable_length (len);
|
||||
var bytes = reader.read_bytes (len);
|
||||
return Variant.new_from_data (new VariantType.array (VariantType.BYTE), bytes.get_data (), true, bytes);
|
||||
}
|
||||
|
||||
private size_t read_variable_length (size_t len) throws Error {
|
||||
if (len <= 0x20)
|
||||
return len;
|
||||
|
||||
switch (len) {
|
||||
case 0x21:
|
||||
return reader.read_uint8 ();
|
||||
case 0x22:
|
||||
return reader.read_uint16 ();
|
||||
case 0x23:
|
||||
uint32 bottom = reader.read_uint8 ();
|
||||
uint32 top = reader.read_uint16 ();
|
||||
return top << 8 | bottom;
|
||||
case 0x24:
|
||||
return reader.read_uint32 ();
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported OPACK length: 0x%zx", len);
|
||||
}
|
||||
}
|
||||
|
||||
private Variant read_dictionary (size_t n) throws Error {
|
||||
var builder = new VariantBuilder (VariantType.VARDICT);
|
||||
|
||||
bool has_terminator = n == 0xf;
|
||||
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
if (!has_terminator && i == n)
|
||||
break;
|
||||
|
||||
var key = read_value (has_terminator ? ValueFlags.ALLOW_TERMINATOR : 0);
|
||||
if (key == null)
|
||||
break;
|
||||
if (!key.is_of_type (VariantType.STRING))
|
||||
throw new Error.PROTOCOL ("Unsupported OPACK dictionary key type");
|
||||
var val = read_value ();
|
||||
builder.add_value (new Variant.dict_entry (key, new Variant.variant (val)));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return builder.end ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,33 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class SpringboardServicesClient : Object, AsyncInitable {
|
||||
public LockdownClient lockdown {
|
||||
public Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private PlistServiceClient service;
|
||||
|
||||
private SpringboardServicesClient (LockdownClient lockdown) {
|
||||
Object (lockdown: lockdown);
|
||||
private SpringboardServicesClient (Device device) {
|
||||
Object (device: device);
|
||||
}
|
||||
|
||||
public static async SpringboardServicesClient open (LockdownClient lockdown,
|
||||
Cancellable? cancellable = null) throws SpringboardServicesError, IOError {
|
||||
var client = new SpringboardServicesClient (lockdown);
|
||||
public static async SpringboardServicesClient open (Device device, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var client = new SpringboardServicesClient (device);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_local_error (e);
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws SpringboardServicesError, IOError {
|
||||
try {
|
||||
var stream = yield lockdown.start_service ("com.apple.springboardservices", cancellable);
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
var stream = yield device.open_lockdown_service ("com.apple.springboardservices", cancellable);
|
||||
|
||||
service = new PlistServiceClient (stream);
|
||||
} catch (LockdownError e) {
|
||||
throw new SpringboardServicesError.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
service = new PlistServiceClient (stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -41,15 +36,14 @@ namespace Frida.Fruity {
|
||||
yield service.close (cancellable);
|
||||
}
|
||||
|
||||
public async Bytes get_icon_png_data (string bundle_id,
|
||||
Cancellable? cancellable = null) throws SpringboardServicesError, IOError {
|
||||
public async Bytes get_icon_png_data (string bundle_id, Cancellable? cancellable = null) throws Error, IOError {
|
||||
try {
|
||||
var request = make_request ("getIconPNGData");
|
||||
request.set_string ("bundleId", bundle_id);
|
||||
|
||||
var response = yield service.query (request, cancellable);
|
||||
if (response.has ("Error"))
|
||||
throw new SpringboardServicesError.INVALID_ARGUMENT ("%s", response.get_string ("Error"));
|
||||
throw new Error.INVALID_ARGUMENT ("%s", response.get_string ("Error"));
|
||||
|
||||
return response.get_bytes ("pngData");
|
||||
} catch (PlistServiceError e) {
|
||||
@@ -59,8 +53,8 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public async Gee.HashMap<string, Bytes> get_icon_png_data_batch (string[] bundle_ids,
|
||||
Cancellable? cancellable = null) throws SpringboardServicesError, IOError {
|
||||
public async Gee.HashMap<string, Bytes> get_icon_png_data_batch (string[] bundle_ids, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
try {
|
||||
foreach (unowned string bundle_id in bundle_ids) {
|
||||
var request = make_request ("getIconPNGData");
|
||||
@@ -73,7 +67,7 @@ namespace Frida.Fruity {
|
||||
do {
|
||||
foreach (Plist response in yield service.read_messages (0, cancellable)) {
|
||||
if (response.has ("Error")) {
|
||||
throw new SpringboardServicesError.INVALID_ARGUMENT ("%s",
|
||||
throw new Error.INVALID_ARGUMENT ("%s",
|
||||
response.get_string ("Error"));
|
||||
}
|
||||
|
||||
@@ -98,27 +92,12 @@ namespace Frida.Fruity {
|
||||
return request;
|
||||
}
|
||||
|
||||
private static void throw_local_error (GLib.Error e) throws SpringboardServicesError, IOError {
|
||||
if (e is SpringboardServicesError)
|
||||
throw (SpringboardServicesError) e;
|
||||
|
||||
if (e is IOError)
|
||||
throw (IOError) e;
|
||||
|
||||
assert_not_reached ();
|
||||
private static Error error_from_service (PlistServiceError e) {
|
||||
return new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
private static SpringboardServicesError error_from_service (PlistServiceError e) {
|
||||
return new SpringboardServicesError.PROTOCOL ("%s", e.message);
|
||||
private static Error error_from_plist (PlistError e) {
|
||||
return new Error.PROTOCOL ("Unexpected response: %s", e.message);
|
||||
}
|
||||
|
||||
private static SpringboardServicesError error_from_plist (PlistError e) {
|
||||
return new SpringboardServicesError.PROTOCOL ("Unexpected response: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public errordomain SpringboardServicesError {
|
||||
INVALID_ARGUMENT,
|
||||
PROTOCOL
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
internal sealed class UsbDevice : Object, AsyncInitable {
|
||||
public LibUSB.Device? raw_device {
|
||||
get {
|
||||
return _raw_device;
|
||||
}
|
||||
}
|
||||
|
||||
public UsbDeviceBackend backend {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public LibUSB.DeviceHandle? handle {
|
||||
get {
|
||||
return _handle;
|
||||
}
|
||||
}
|
||||
|
||||
public string udid {
|
||||
get {
|
||||
return _udid;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 default_language_id {
|
||||
get {
|
||||
return _default_language_id;
|
||||
}
|
||||
}
|
||||
|
||||
private LibUSB.Device? _raw_device;
|
||||
private LibUSB.DeviceHandle? _handle;
|
||||
private string _udid;
|
||||
private uint16 _default_language_id;
|
||||
private uint num_pending_operations;
|
||||
private Promise<bool>? pending_operations_completed;
|
||||
|
||||
private enum AppleSpecificRequest {
|
||||
GET_MODE = 0x45,
|
||||
SET_MODE = 0x52,
|
||||
}
|
||||
|
||||
private const string MODE_INITIAL_UNTETHERED = "3:3:3:0"; // => 5:3:3:0
|
||||
private const string MODE_INITIAL_TETHERED = "4:4:3:4"; // => 5:4:3:4
|
||||
|
||||
public static async UsbDevice open (LibUSB.Device raw_device, UsbDeviceBackend backend, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
var device = new UsbDevice (raw_device, backend);
|
||||
|
||||
try {
|
||||
yield device.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
private UsbDevice (LibUSB.Device raw_device, UsbDeviceBackend backend) {
|
||||
Object (backend: backend);
|
||||
_raw_device = raw_device;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
Usb.check (_raw_device.open (out _handle), "Failed to open USB device");
|
||||
|
||||
Bytes language_ids_response = yield read_string_descriptor_bytes (0, 0, cancellable);
|
||||
if (language_ids_response.get_size () < sizeof (uint16))
|
||||
throw new Error.PROTOCOL ("Invalid language IDs response");
|
||||
Buffer language_ids = new Buffer (language_ids_response, LITTLE_ENDIAN);
|
||||
_default_language_id = language_ids.read_uint16 (0);
|
||||
|
||||
var dev_desc = LibUSB.DeviceDescriptor (_raw_device);
|
||||
string serial_number = yield read_string_descriptor (dev_desc.iSerialNumber, _default_language_id, cancellable);
|
||||
_udid = udid_from_serial_number (serial_number);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (num_pending_operations != 0) {
|
||||
pending_operations_completed = new Promise<bool> ();
|
||||
try {
|
||||
yield pending_operations_completed.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
pending_operations_completed = null;
|
||||
}
|
||||
|
||||
_handle = null;
|
||||
_raw_device = null;
|
||||
}
|
||||
|
||||
public async bool maybe_modeswitch (Cancellable? cancellable) throws Error, IOError {
|
||||
var response = yield control_transfer (
|
||||
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
|
||||
AppleSpecificRequest.GET_MODE,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
1000,
|
||||
cancellable);
|
||||
string mode = parse_mode (response);
|
||||
bool is_initial_mode = mode == MODE_INITIAL_UNTETHERED || mode == MODE_INITIAL_TETHERED;
|
||||
if (!is_initial_mode)
|
||||
return false;
|
||||
|
||||
response = yield control_transfer (
|
||||
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
|
||||
AppleSpecificRequest.SET_MODE,
|
||||
0,
|
||||
3,
|
||||
1,
|
||||
1000,
|
||||
cancellable);
|
||||
if (response.get_size () != 1 || response[0] != 0x00)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string parse_mode (Bytes mode) throws Error {
|
||||
var result = new StringBuilder.sized (7);
|
||||
foreach (uint8 byte in mode.get_data ()) {
|
||||
if (result.len != 0)
|
||||
result.append_c (':');
|
||||
result.append_printf ("%u", byte);
|
||||
}
|
||||
return result.str;
|
||||
}
|
||||
|
||||
private static string udid_from_serial_number (string serial) {
|
||||
if (serial.length == 24)
|
||||
return serial.substring (0, 8) + "-" + serial.substring (8);
|
||||
return serial;
|
||||
}
|
||||
|
||||
public async string read_string_descriptor (uint8 index, uint16 language_id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var response = yield read_string_descriptor_bytes (index, language_id, cancellable);
|
||||
try {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_bytes (response));
|
||||
input.byte_order = LITTLE_ENDIAN;
|
||||
|
||||
size_t size = response.get_size ();
|
||||
if (size % sizeof (unichar2) != 0)
|
||||
throw new Error.PROTOCOL ("Invalid string descriptor");
|
||||
size_t n = size / sizeof (unichar2);
|
||||
var chars = new unichar2[n];
|
||||
for (size_t i = 0; i != n; i++)
|
||||
chars[i] = input.read_uint16 ();
|
||||
|
||||
unowned string16 str = (string16) chars;
|
||||
return str.to_utf8 ((long) n);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Bytes read_string_descriptor_bytes (uint8 index, uint16 language_id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var response = yield control_transfer (
|
||||
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.STANDARD | LibUSB.EndpointDirection.IN,
|
||||
LibUSB.StandardRequest.GET_DESCRIPTOR,
|
||||
(LibUSB.DescriptorType.STRING << 8) | index,
|
||||
language_id,
|
||||
1024,
|
||||
1000,
|
||||
cancellable);
|
||||
try {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_bytes (response));
|
||||
input.byte_order = LITTLE_ENDIAN;
|
||||
|
||||
uint8 length = input.read_byte ();
|
||||
if (length < 2)
|
||||
throw new Error.PROTOCOL ("Invalid string descriptor length");
|
||||
|
||||
uint8 type = input.read_byte ();
|
||||
if (type != LibUSB.DescriptorType.STRING)
|
||||
throw new Error.PROTOCOL ("Invalid string descriptor type");
|
||||
|
||||
size_t remainder = response.get_size () - 2;
|
||||
length -= 2;
|
||||
if (length > remainder)
|
||||
throw new Error.PROTOCOL ("Invalid string descriptor length");
|
||||
|
||||
return response[2:2 + length];
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Bytes control_transfer (uint8 request_type, uint8 request, uint16 val, uint16 index, uint16 length,
|
||||
uint timeout, Cancellable? cancellable) throws Error, IOError {
|
||||
var op = backend.allocate_usb_operation ();
|
||||
unowned LibUSB.Transfer transfer = op.transfer;
|
||||
var ready_closure = new TransferReadyClosure (control_transfer.callback);
|
||||
|
||||
var buffer = new uint8[sizeof (LibUSB.ControlSetup) + length];
|
||||
LibUSB.Transfer.fill_control_setup (buffer, request_type, request, val, index, length);
|
||||
transfer.fill_control_transfer (_handle, buffer, on_transfer_ready, ready_closure, timeout);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
transfer.cancel ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
cancel_source.attach (MainContext.get_thread_default ());
|
||||
|
||||
try {
|
||||
Usb.check (transfer.submit (), "Failed to submit control transfer");
|
||||
on_operation_started ();
|
||||
yield;
|
||||
on_operation_ended ();
|
||||
} finally {
|
||||
cancel_source.destroy ();
|
||||
}
|
||||
|
||||
Usb.check_transfer (transfer.status, "Control transfer failed");
|
||||
|
||||
return new Bytes (((uint8[]) transfer.control_get_data ())[:transfer.actual_length]);
|
||||
}
|
||||
|
||||
public async size_t bulk_transfer (uint8 endpoint, uint8[] buffer, uint timeout, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var op = backend.allocate_usb_operation ();
|
||||
unowned LibUSB.Transfer transfer = op.transfer;
|
||||
var ready_closure = new TransferReadyClosure (bulk_transfer.callback);
|
||||
|
||||
transfer.fill_bulk_transfer (_handle, endpoint, buffer, on_transfer_ready, ready_closure, timeout);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
transfer.cancel ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
cancel_source.attach (MainContext.get_thread_default ());
|
||||
|
||||
try {
|
||||
Usb.check (transfer.submit (), "Failed to submit bulk transfer");
|
||||
on_operation_started ();
|
||||
yield;
|
||||
on_operation_ended ();
|
||||
} finally {
|
||||
cancel_source.destroy ();
|
||||
}
|
||||
|
||||
Usb.check_transfer (transfer.status, "Bulk transfer failed");
|
||||
|
||||
return transfer.actual_length;
|
||||
}
|
||||
|
||||
private void on_operation_started () {
|
||||
num_pending_operations++;
|
||||
}
|
||||
|
||||
private void on_operation_ended () {
|
||||
num_pending_operations--;
|
||||
if (num_pending_operations == 0 && pending_operations_completed != null)
|
||||
pending_operations_completed.resolve (true);
|
||||
}
|
||||
|
||||
private static void on_transfer_ready (LibUSB.Transfer transfer) {
|
||||
TransferReadyClosure * closure = transfer.user_data;
|
||||
closure->schedule ();
|
||||
}
|
||||
|
||||
private class TransferReadyClosure {
|
||||
private SourceFunc? handler;
|
||||
private MainContext main_context;
|
||||
|
||||
public TransferReadyClosure (owned SourceFunc handler) {
|
||||
this.handler = (owned) handler;
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
}
|
||||
|
||||
public void schedule () {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
handler ();
|
||||
handler = null;
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal interface UsbDeviceBackend : Object {
|
||||
public abstract UsbOperation allocate_usb_operation () throws Error;
|
||||
}
|
||||
|
||||
internal interface UsbOperation : Object {
|
||||
public abstract LibUSB.Transfer transfer {
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Usb {
|
||||
internal static void check (LibUSB.Error error, string prefix) throws Error {
|
||||
if (error >= LibUSB.Error.SUCCESS)
|
||||
return;
|
||||
|
||||
string message = @"$prefix: $(error.get_description ())";
|
||||
|
||||
switch (error) {
|
||||
case ACCESS:
|
||||
throw new Error.PERMISSION_DENIED ("%s", message);
|
||||
case NOT_FOUND:
|
||||
throw new Error.INVALID_OPERATION ("%s", message);
|
||||
case TIMEOUT:
|
||||
throw new Error.TIMED_OUT ("%s", message);
|
||||
default:
|
||||
throw new Error.TRANSPORT ("%s", message);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void check_transfer (LibUSB.TransferStatus status, string prefix) throws Error, IOError {
|
||||
if (status == COMPLETED)
|
||||
return;
|
||||
|
||||
string message = @"$prefix: $(status.to_string ())";
|
||||
|
||||
switch (status) {
|
||||
case TIMED_OUT:
|
||||
throw new Error.TIMED_OUT ("%s", message);
|
||||
case CANCELLED:
|
||||
throw new IOError.CANCELLED ("%s", message);
|
||||
default:
|
||||
throw new Error.TRANSPORT ("%s", message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-65
@@ -1,13 +1,16 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class UsbmuxClient : Object, AsyncInitable {
|
||||
public signal void device_attached (DeviceDetails details);
|
||||
public signal void device_detached (DeviceId id);
|
||||
public signal void device_attached (UsbmuxDevice device);
|
||||
public signal void device_detached (UsbmuxDevice device);
|
||||
|
||||
public SocketConnection? connection {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private Gee.Map<uint, UsbmuxDevice> devices = new Gee.HashMap<uint, UsbmuxDevice> ();
|
||||
|
||||
private InputStream? input;
|
||||
private OutputStream? output;
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
@@ -132,11 +135,12 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public async void connect_to_port (DeviceId device_id, uint16 port, Cancellable? cancellable = null) throws UsbmuxError, IOError {
|
||||
public async void connect_to_port (uint device_id, uint16 port, Cancellable? cancellable = null)
|
||||
throws UsbmuxError, IOError {
|
||||
assert (is_processing_messages);
|
||||
|
||||
var request = create_request ("Connect");
|
||||
request.set_integer ("DeviceID", device_id.raw_value);
|
||||
request.set_integer ("DeviceID", device_id);
|
||||
request.set_integer ("PortNumber", port.to_big_endian ());
|
||||
|
||||
var response = yield query (request, MODE_SWITCH, cancellable);
|
||||
@@ -160,9 +164,9 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public async Plist read_pair_record (Udid udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
|
||||
public async Plist read_pair_record (string udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
|
||||
var request = create_request ("ReadPairRecord");
|
||||
request.set_string ("PairRecordID", udid.raw_value);
|
||||
request.set_string ("PairRecordID", udid);
|
||||
|
||||
var response = yield query (request, REGULAR, cancellable);
|
||||
try {
|
||||
@@ -175,9 +179,9 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public async void delete_pair_record (Udid udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
|
||||
public async void delete_pair_record (string udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
|
||||
var request = create_request ("DeletePairRecord");
|
||||
request.set_string ("PairRecordID", udid.raw_value);
|
||||
request.set_string ("PairRecordID", udid);
|
||||
|
||||
var response = yield query (request, REGULAR, cancellable);
|
||||
check_pair_response (response, "DeletePairRecord");
|
||||
@@ -276,28 +280,26 @@ namespace Frida.Fruity {
|
||||
if (props.has ("ConnectionType") && props.get_string ("ConnectionType") == "Network")
|
||||
connection_type = NETWORK;
|
||||
|
||||
var device_id = DeviceId ((uint) body.get_integer ("DeviceID"));
|
||||
var device_id = (uint) body.get_integer ("DeviceID");
|
||||
|
||||
ProductId product_id;
|
||||
if (connection_type == USB)
|
||||
product_id = ProductId ((int) props.get_integer ("ProductID"));
|
||||
else
|
||||
product_id = ProductId (-1);
|
||||
int product_id = (connection_type == USB) ? (int) props.get_integer ("ProductID") : -1;
|
||||
|
||||
string raw_udid = props.get_string ("SerialNumber");
|
||||
if (raw_udid.length == 24)
|
||||
raw_udid = raw_udid[:8] + "-" + raw_udid[8:];
|
||||
var udid = Udid (raw_udid);
|
||||
string udid = props.get_string ("SerialNumber");
|
||||
if (udid.length == 24)
|
||||
udid = udid[:8] + "-" + udid[8:];
|
||||
|
||||
InetSocketAddress? network_address = null;
|
||||
if (connection_type == NETWORK)
|
||||
network_address = parse_network_address (props.get_bytes ("NetworkAddress"));
|
||||
|
||||
var details =
|
||||
new DeviceDetails (connection_type, device_id, product_id, udid, network_address);
|
||||
device_attached (details);
|
||||
var device =
|
||||
new UsbmuxDevice (connection_type, device_id, product_id, udid, network_address);
|
||||
devices[device_id] = device;
|
||||
device_attached (device);
|
||||
} else if (message_type == "Detached") {
|
||||
device_detached (DeviceId ((uint) body.get_integer ("DeviceID")));
|
||||
UsbmuxDevice? device;
|
||||
if (devices.unset ((uint) body.get_integer ("DeviceID"), out device))
|
||||
device_detached (device);
|
||||
} else {
|
||||
throw new UsbmuxError.PROTOCOL ("Unexpected message type: %s", message_type);
|
||||
}
|
||||
@@ -546,23 +548,23 @@ namespace Frida.Fruity {
|
||||
PROTOCOL
|
||||
}
|
||||
|
||||
public class DeviceDetails : Object {
|
||||
public class UsbmuxDevice : Object {
|
||||
public ConnectionType connection_type {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public DeviceId id {
|
||||
public uint id {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public ProductId product_id {
|
||||
public int product_id {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Udid udid {
|
||||
public string udid {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
@@ -572,7 +574,7 @@ namespace Frida.Fruity {
|
||||
construct;
|
||||
}
|
||||
|
||||
public DeviceDetails (ConnectionType connection_type, DeviceId id, ProductId product_id, Udid udid,
|
||||
public UsbmuxDevice (ConnectionType connection_type, uint id, int product_id, string udid,
|
||||
InetSocketAddress? network_address) {
|
||||
Object (
|
||||
connection_type: connection_type,
|
||||
@@ -583,42 +585,4 @@ namespace Frida.Fruity {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConnectionType {
|
||||
USB,
|
||||
NETWORK
|
||||
}
|
||||
|
||||
public struct DeviceId {
|
||||
public uint raw_value {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public DeviceId (uint raw_value) {
|
||||
this.raw_value = raw_value;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ProductId {
|
||||
public int raw_value {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public ProductId (int raw_value) {
|
||||
this.raw_value = raw_value;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Udid {
|
||||
public string raw_value {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Udid (string raw_value) {
|
||||
this.raw_value = raw_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
using Darwin.GCD;
|
||||
using Darwin.DNSSD;
|
||||
using Darwin.Net;
|
||||
|
||||
public class DarwinPairingBrowser : Object, PairingBrowser {
|
||||
private MainContext main_context;
|
||||
private DispatchQueue dispatch_queue = new DispatchQueue ("re.frida.fruity.browser-queue", DispatchQueueAttr.SERIAL);
|
||||
|
||||
private DNSService dns_connection;
|
||||
private DNSService browse_session;
|
||||
private TaskQueue task_queue;
|
||||
|
||||
private Gee.List<PairingServiceDetails> current_batch = new Gee.ArrayList<PairingServiceDetails> ();
|
||||
|
||||
construct {
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
dispatch_queue.dispatch_async (() => {
|
||||
DNSService.create_connection (out dns_connection);
|
||||
dns_connection.set_dispatch_queue (dispatch_queue);
|
||||
|
||||
DNSService session = dns_connection;
|
||||
DNSService.browse (ref session, PrivateFive | ShareConnection, 0, PAIRING_REGTYPE, PAIRING_DOMAIN,
|
||||
on_browse_reply);
|
||||
browse_session = session;
|
||||
|
||||
task_queue = new TaskQueue (this);
|
||||
});
|
||||
}
|
||||
|
||||
~DarwinPairingBrowser () {
|
||||
dispatch_queue.dispatch_sync (() => {
|
||||
browse_session.deallocate ();
|
||||
dns_connection.deallocate ();
|
||||
});
|
||||
}
|
||||
|
||||
private void on_browse_reply (DNSService sd_ref, DNSService.Flags flags, uint32 interface_index,
|
||||
DNSService.ErrorType error_code, string service_name, string regtype, string reply_domain) {
|
||||
if (error_code != NoError)
|
||||
return;
|
||||
|
||||
var interface_name_buf = new char[IFNAMSIZ];
|
||||
unowned string interface_name = if_indextoname (interface_index, interface_name_buf);
|
||||
|
||||
var service = new DarwinPairingServiceDetails (service_name, interface_index, interface_name, task_queue);
|
||||
current_batch.add (service);
|
||||
|
||||
if ((flags & DNSService.Flags.MoreComing) != 0)
|
||||
return;
|
||||
|
||||
var services = current_batch;
|
||||
current_batch = new Gee.ArrayList<PairingServiceDetails> ();
|
||||
|
||||
schedule_on_frida_thread (() => {
|
||||
services_discovered (services.to_array ());
|
||||
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);
|
||||
}
|
||||
|
||||
private class TaskQueue : Object, DNSServiceProvider {
|
||||
private weak DarwinPairingBrowser parent;
|
||||
|
||||
private DNSServiceTask? current = null;
|
||||
private Gee.Deque<DNSServiceTask> pending = new Gee.ArrayQueue<DNSServiceTask> ();
|
||||
|
||||
public TaskQueue (DarwinPairingBrowser parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
private async T with_dns_service<T> (DNSServiceTask task, Cancellable? cancellable) throws Error, IOError {
|
||||
var promise = new Promise<Object> ();
|
||||
task.set_data ("promise", promise);
|
||||
pending.offer_tail (task);
|
||||
|
||||
maybe_start_next ();
|
||||
|
||||
return (T) yield promise.future.wait_async (cancellable);
|
||||
}
|
||||
|
||||
private void maybe_start_next () {
|
||||
if (current != null)
|
||||
return;
|
||||
|
||||
DNSServiceTask? task = pending.poll_head ();
|
||||
if (task == null)
|
||||
return;
|
||||
current = task;
|
||||
|
||||
parent.dispatch_queue.dispatch_async (() => {
|
||||
current.dns_connection = parent.dns_connection;
|
||||
current.on_complete = on_complete;
|
||||
current.start ();
|
||||
});
|
||||
}
|
||||
|
||||
private void on_complete (Object? result, Error? error) {
|
||||
parent.schedule_on_frida_thread (() => {
|
||||
Promise<Object> promise = current.steal_data ("promise");
|
||||
|
||||
if (error != null)
|
||||
promise.reject (error);
|
||||
else
|
||||
promise.resolve (result);
|
||||
|
||||
current = null;
|
||||
maybe_start_next ();
|
||||
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface DNSServiceProvider : Object {
|
||||
public abstract async T with_dns_service<T> (DNSServiceTask task, Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
|
||||
private abstract class DNSServiceTask : Object {
|
||||
internal DNSService? dns_connection;
|
||||
internal CompleteFunc? on_complete;
|
||||
|
||||
protected DNSService? session;
|
||||
|
||||
public delegate void CompleteFunc (Object? result, Error? error);
|
||||
|
||||
public abstract void start ();
|
||||
|
||||
protected void complete (Object? result, Error? error) {
|
||||
if (session != null && session != dns_connection) {
|
||||
session.deallocate ();
|
||||
session = null;
|
||||
}
|
||||
|
||||
on_complete (result, error);
|
||||
on_complete = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class DarwinPairingServiceDetails : Object, PairingServiceDetails {
|
||||
public string name {
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public uint interface_index {
|
||||
get { return _interface_index; }
|
||||
}
|
||||
|
||||
public string interface_name {
|
||||
get { return _interface_name; }
|
||||
}
|
||||
|
||||
private string _name;
|
||||
private uint _interface_index;
|
||||
private string _interface_name;
|
||||
|
||||
private DNSServiceProvider dns;
|
||||
|
||||
internal DarwinPairingServiceDetails (string name, uint interface_index, string interface_name, DNSServiceProvider dns) {
|
||||
_name = name;
|
||||
_interface_index = interface_index;
|
||||
_interface_name = interface_name;
|
||||
|
||||
this.dns = dns;
|
||||
}
|
||||
|
||||
public async Gee.List<PairingServiceHost> resolve (Cancellable? cancellable) throws Error, IOError {
|
||||
var task = new ResolveTask (this);
|
||||
return yield dns.with_dns_service (task, cancellable);
|
||||
}
|
||||
|
||||
private class ResolveTask : DNSServiceTask {
|
||||
private weak DarwinPairingServiceDetails parent;
|
||||
|
||||
private Gee.List<PairingServiceHost> hosts = new Gee.ArrayList<PairingServiceHost> ();
|
||||
|
||||
public ResolveTask (DarwinPairingServiceDetails parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public override void start () {
|
||||
session = dns_connection;
|
||||
DNSService.resolve (ref session, PrivateFive | ShareConnection, parent.interface_index, parent.name,
|
||||
PAIRING_REGTYPE, PAIRING_DOMAIN, on_resolve_reply);
|
||||
}
|
||||
|
||||
private void on_resolve_reply (DNSService sd_ref, DNSService.Flags flags, uint32 interface_index,
|
||||
DNSService.ErrorType error_code, string fullname, string hosttarget, uint16 port,
|
||||
uint8[] raw_txt_record) {
|
||||
if (error_code != NoError) {
|
||||
complete (null,
|
||||
new Error.TRANSPORT ("Unable to resolve service '%s' on interface %s",
|
||||
parent.name, parent.interface_name));
|
||||
return;
|
||||
}
|
||||
|
||||
var txt_record = new Gee.ArrayList<string> ();
|
||||
size_t cursor = 0;
|
||||
size_t remaining = raw_txt_record.length;
|
||||
while (remaining != 0) {
|
||||
size_t len = raw_txt_record[cursor];
|
||||
cursor++;
|
||||
remaining--;
|
||||
if (len > remaining)
|
||||
break;
|
||||
|
||||
unowned string raw_val = (string) raw_txt_record[cursor:cursor + len];
|
||||
string val = raw_val.make_valid ((ssize_t) len);
|
||||
txt_record.add (val);
|
||||
|
||||
cursor += len;
|
||||
remaining -= len;
|
||||
}
|
||||
|
||||
hosts.add (new DarwinPairingServiceHost (parent, hosttarget, uint16.from_big_endian (port), txt_record,
|
||||
parent.dns));
|
||||
|
||||
if ((flags & DNSService.Flags.MoreComing) == 0)
|
||||
complete (hosts, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DarwinPairingServiceHost : Object, PairingServiceHost {
|
||||
public string name {
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public uint16 port {
|
||||
get { return _port; }
|
||||
}
|
||||
|
||||
public Gee.List<string> txt_record {
|
||||
get { return _txt_record; }
|
||||
}
|
||||
|
||||
private string _name;
|
||||
private uint16 _port;
|
||||
private Gee.List<string> _txt_record;
|
||||
|
||||
private PairingServiceDetails service;
|
||||
private DNSServiceProvider dns;
|
||||
|
||||
internal DarwinPairingServiceHost (PairingServiceDetails service, string name, uint16 port, Gee.List<string> txt_record,
|
||||
DNSServiceProvider dns) {
|
||||
_name = name;
|
||||
_port = port;
|
||||
_txt_record = txt_record;
|
||||
|
||||
this.service = service;
|
||||
this.dns = dns;
|
||||
}
|
||||
|
||||
public async Gee.List<InetSocketAddress> resolve (Cancellable? cancellable) throws Error, IOError {
|
||||
var task = new ResolveTask (this);
|
||||
return yield dns.with_dns_service (task, cancellable);
|
||||
}
|
||||
|
||||
private class ResolveTask : DNSServiceTask {
|
||||
private weak DarwinPairingServiceHost parent;
|
||||
|
||||
private Gee.List<InetSocketAddress> addresses = new Gee.ArrayList<InetSocketAddress> ();
|
||||
|
||||
public ResolveTask (DarwinPairingServiceHost parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public override void start () {
|
||||
session = dns_connection;
|
||||
DNSService.get_addr_info (ref session, PrivateFive | ShareConnection, parent.service.interface_index, IPv6,
|
||||
parent.name, on_info_reply);
|
||||
}
|
||||
|
||||
private void on_info_reply (DNSService sd_ref, DNSService.Flags flags, uint32 interface_index,
|
||||
DNSService.ErrorType error_code, string hostname, void * address, uint32 ttl) {
|
||||
if (error_code != NoError) {
|
||||
complete (null,
|
||||
new Error.TRANSPORT ("Unable to resolve host '%s' on interface %s",
|
||||
parent.name, parent.service.interface_name));
|
||||
return;
|
||||
}
|
||||
|
||||
addresses.add ((InetSocketAddress) SocketAddress.from_native (address, sizeof (Posix.SockAddrIn6)));
|
||||
|
||||
if ((flags & DNSService.Flags.MoreComing) == 0)
|
||||
complete (addresses, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,426 +0,0 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public sealed class LinuxTunnelFinder : Object, TunnelFinder {
|
||||
public async Tunnel? find (string udid, Cancellable? cancellable) throws Error, IOError {
|
||||
NcmPeer? peer = yield locate_ncm_peer (udid, cancellable);
|
||||
if (peer == null)
|
||||
return null;
|
||||
|
||||
var bootstrap_disco = yield DiscoveryService.open (
|
||||
yield peer.open_tcp_connection (58783, cancellable), cancellable);
|
||||
|
||||
var tunnel_service = bootstrap_disco.get_service ("com.apple.internal.dt.coredevice.untrusted.tunnelservice");
|
||||
var pairing_transport = new XpcPairingTransport (yield peer.open_tcp_connection (tunnel_service.port, cancellable));
|
||||
var pairing_service = yield PairingService.open (pairing_transport, cancellable);
|
||||
|
||||
TunnelConnection tc = yield pairing_service.open_tunnel (peer.address, cancellable);
|
||||
|
||||
var disco = yield DiscoveryService.open (yield tc.open_connection (tc.remote_rsd_port, cancellable), cancellable);
|
||||
|
||||
return new LinuxTunnel (tc, disco);
|
||||
}
|
||||
|
||||
private static async NcmPeer? locate_ncm_peer (string udid, Cancellable? cancellable) throws Error, IOError {
|
||||
var device_ifaddrs = new Gee.ArrayList<unowned Linux.Network.IfAddrs> ();
|
||||
var fruit_finder = FruitFinder.make_default ();
|
||||
string raw_udid = udid.replace ("-", "");
|
||||
Linux.Network.IfAddrs ifaddrs;
|
||||
Linux.Network.getifaddrs (out ifaddrs);
|
||||
for (unowned Linux.Network.IfAddrs candidate = ifaddrs; candidate != null; candidate = candidate.ifa_next) {
|
||||
if (candidate.ifa_addr.sa_family != Posix.AF_INET6)
|
||||
continue;
|
||||
|
||||
string? candidate_udid = fruit_finder.udid_from_iface (candidate.ifa_name);
|
||||
if (candidate_udid != raw_udid)
|
||||
continue;
|
||||
|
||||
device_ifaddrs.add (candidate);
|
||||
}
|
||||
if (device_ifaddrs.is_empty)
|
||||
return null;
|
||||
|
||||
var sockets = new Gee.ArrayList<Socket> ();
|
||||
var sources = new Gee.ArrayList<Source> ();
|
||||
var readable_sockets = new Gee.ArrayQueue<Socket> ();
|
||||
bool timed_out = false;
|
||||
bool waiting = false;
|
||||
|
||||
var remote_address = new InetSocketAddress.from_string ("ff02::fb", 5353);
|
||||
var remoted_mdns_request = make_remoted_mdns_request ();
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
foreach (unowned Linux.Network.IfAddrs ifaddr in device_ifaddrs) {
|
||||
try {
|
||||
var sock = new Socket (IPV6, DATAGRAM, UDP);
|
||||
sock.bind (SocketAddress.from_native ((void *) ifaddr.ifa_addr, sizeof (Posix.SockAddrIn6)), false);
|
||||
sock.send_to (remote_address, remoted_mdns_request.get_data (), cancellable);
|
||||
|
||||
var source = sock.create_source (IN, cancellable);
|
||||
source.set_callback (() => {
|
||||
readable_sockets.offer (sock);
|
||||
if (waiting)
|
||||
locate_ncm_peer.callback ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
|
||||
sockets.add (sock);
|
||||
sources.add (source);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
var timeout_source = new TimeoutSource.seconds (2);
|
||||
timeout_source.set_callback (() => {
|
||||
timed_out = true;
|
||||
if (waiting)
|
||||
locate_ncm_peer.callback ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
timeout_source.attach (main_context);
|
||||
sources.add (timeout_source);
|
||||
|
||||
Socket? sock;
|
||||
while ((sock = readable_sockets.poll ()) == null && !timed_out) {
|
||||
waiting = true;
|
||||
yield;
|
||||
waiting = false;
|
||||
}
|
||||
|
||||
foreach (var source in sources)
|
||||
source.destroy ();
|
||||
|
||||
if (sock == null && timed_out)
|
||||
throw new Error.TIMED_OUT ("Unexpectedly timed out while waiting for mDNS reply");
|
||||
|
||||
InetSocketAddress sender;
|
||||
try {
|
||||
SocketAddress raw_sender;
|
||||
var response_buf = new uint8[2048];
|
||||
sock.receive_from (out raw_sender, response_buf, cancellable);
|
||||
sender = (InetSocketAddress) raw_sender;
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
return new NcmPeer ("%s%%%u".printf (sender.address.to_string (), sender.scope_id));
|
||||
}
|
||||
|
||||
private static Bytes make_remoted_mdns_request () {
|
||||
uint16 transaction_id = 0;
|
||||
uint16 flags = 0;
|
||||
uint16 num_questions = 1;
|
||||
uint16 answer_rrs = 0;
|
||||
uint16 authority_rrs = 0;
|
||||
uint16 additional_rrs = 0;
|
||||
string components[] = { "_remoted", "_tcp", "local" };
|
||||
uint16 record_type = 12;
|
||||
uint16 dns_class = 1;
|
||||
return new BufferBuilder (BIG_ENDIAN)
|
||||
.append_uint16 (transaction_id)
|
||||
.append_uint16 (flags)
|
||||
.append_uint16 (num_questions)
|
||||
.append_uint16 (answer_rrs)
|
||||
.append_uint16 (authority_rrs)
|
||||
.append_uint16 (additional_rrs)
|
||||
.append_uint8 ((uint8) components[0].length)
|
||||
.append_string (components[0], StringTerminator.NONE)
|
||||
.append_uint8 ((uint8) components[1].length)
|
||||
.append_string (components[1], StringTerminator.NONE)
|
||||
.append_uint8 ((uint8) components[2].length)
|
||||
.append_string (components[2], StringTerminator.NUL)
|
||||
.append_uint16 (record_type)
|
||||
.append_uint16 (dns_class)
|
||||
.build ();
|
||||
}
|
||||
|
||||
private class NcmPeer {
|
||||
public string address;
|
||||
|
||||
public class NcmPeer (string address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
SocketConnection connection;
|
||||
try {
|
||||
NetworkAddress service_address = NetworkAddress.parse (address, port);
|
||||
|
||||
var client = new SocketClient ();
|
||||
connection = yield client.connect_async (service_address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LinuxTunnel : Object, Tunnel {
|
||||
public DiscoveryService discovery {
|
||||
get {
|
||||
return _discovery;
|
||||
}
|
||||
}
|
||||
|
||||
private TunnelConnection tunnel_connection;
|
||||
private DiscoveryService _discovery;
|
||||
|
||||
public LinuxTunnel (TunnelConnection conn, DiscoveryService disco) {
|
||||
tunnel_connection = conn;
|
||||
_discovery = disco;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
tunnel_connection.cancel ();
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
return yield tunnel_connection.open_connection (port, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxFruitFinder : Object, FruitFinder {
|
||||
public string? udid_from_iface (string ifname) throws Error {
|
||||
var net = "/sys/class/net";
|
||||
|
||||
var directory = File.new_build_filename (net, ifname);
|
||||
if (!directory.query_exists ())
|
||||
return null;
|
||||
|
||||
try {
|
||||
var info = directory.query_info ("standard::*", 0);
|
||||
if (!info.get_is_symlink ())
|
||||
return null;
|
||||
var dev_path = Path.build_filename (net, info.get_symlink_target ());
|
||||
|
||||
var iface = File.new_build_filename (dev_path, "..", "..", "interface");
|
||||
if (!iface.query_exists ())
|
||||
return null;
|
||||
var iface_stream = new DataInputStream (iface.read ());
|
||||
string iface_name = iface_stream.read_line ();
|
||||
if (iface_name != "NCM Control" && iface_name != "AppleUSBEthernet")
|
||||
return null;
|
||||
|
||||
var serial = File.new_build_filename (dev_path, "..", "..", "..", "serial");
|
||||
if (!serial.query_exists ())
|
||||
return null;
|
||||
|
||||
var serial_stream = new DataInputStream (serial.read ());
|
||||
return serial_stream.read_line ();
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxPairingBrowser : Object, PairingBrowser {
|
||||
private DBusConnection connection;
|
||||
private AvahiServer server;
|
||||
private AvahiServiceBrowser browser;
|
||||
|
||||
private Gee.List<PairingServiceDetails> current_batch = new Gee.ArrayList<PairingServiceDetails> ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
construct {
|
||||
start.begin ();
|
||||
}
|
||||
|
||||
private async void start () {
|
||||
try {
|
||||
connection = yield GLib.Bus.get (BusType.SYSTEM, io_cancellable);
|
||||
|
||||
server = yield connection.get_proxy (AVAHI_SERVICE_NAME, "/", DO_NOT_LOAD_PROPERTIES, io_cancellable);
|
||||
|
||||
GLib.ObjectPath browser_path = yield server.service_browser_prepare (-1, INET6, PAIRING_REGTYPE,
|
||||
PAIRING_DOMAIN, 0, io_cancellable);
|
||||
browser = yield connection.get_proxy (AVAHI_SERVICE_NAME, browser_path, DO_NOT_LOAD_PROPERTIES,
|
||||
io_cancellable);
|
||||
browser.item_new.connect (on_item_new);
|
||||
browser.all_for_now.connect (on_all_for_now);
|
||||
yield browser.start (io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("Oopsie: %s\n", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
private void on_item_new (int interface_index, AvahiProtocol protocol, string name, string type, string domain, uint flags) {
|
||||
char raw_interface_name[Linux.Network.INTERFACE_NAME_SIZE];
|
||||
Linux.Network.if_indextoname (interface_index, (string) raw_interface_name);
|
||||
unowned string interface_name = (string) raw_interface_name;
|
||||
current_batch.add (new LinuxPairingServiceDetails (name, interface_index, interface_name, protocol, server));
|
||||
}
|
||||
|
||||
private void on_all_for_now () {
|
||||
services_discovered (current_batch.to_array ());
|
||||
current_batch.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
private class LinuxPairingServiceDetails : Object, PairingServiceDetails {
|
||||
public string name {
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public uint interface_index {
|
||||
get { return _interface_index; }
|
||||
}
|
||||
|
||||
public string interface_name {
|
||||
get { return _interface_name; }
|
||||
}
|
||||
|
||||
private string _name;
|
||||
private uint _interface_index;
|
||||
private string _interface_name;
|
||||
private AvahiProtocol protocol;
|
||||
|
||||
private AvahiServer server;
|
||||
|
||||
internal LinuxPairingServiceDetails (string name, uint interface_index, string interface_name, AvahiProtocol protocol,
|
||||
AvahiServer server) {
|
||||
_name = name;
|
||||
_interface_index = interface_index;
|
||||
_interface_name = interface_name;
|
||||
this.protocol = protocol;
|
||||
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public async Gee.List<PairingServiceHost> resolve (Cancellable? cancellable) throws Error, IOError {
|
||||
AvahiServiceResolver resolver;
|
||||
try {
|
||||
GLib.ObjectPath path = yield server.service_resolver_prepare ((int) interface_index, protocol, name,
|
||||
PAIRING_REGTYPE, PAIRING_DOMAIN, INET6, 0, cancellable);
|
||||
DBusConnection connection = ((DBusProxy) server).get_connection ();
|
||||
resolver = yield connection.get_proxy (AVAHI_SERVICE_NAME, path, DO_NOT_LOAD_PROPERTIES, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
|
||||
var promise = new Promise<Gee.List<PairingServiceHost>> ();
|
||||
var hosts = new Gee.ArrayList<PairingServiceHost> ();
|
||||
resolver.found.connect ((interface_index, protocol, name, type, domain, host, address_protocol, address, port, txt,
|
||||
flags) => {
|
||||
var txt_record = new Gee.ArrayList<string> ();
|
||||
var iter = new VariantIter (txt);
|
||||
Variant? cur;
|
||||
while ((cur = iter.next_value ()) != null) {
|
||||
unowned string raw_val = (string) cur.get_data ();
|
||||
string val = raw_val.make_valid ((ssize_t) cur.get_size ());
|
||||
txt_record.add (val);
|
||||
}
|
||||
|
||||
hosts.add (new LinuxPairingServiceHost (
|
||||
host,
|
||||
new InetSocketAddress.from_string (address, port),
|
||||
port,
|
||||
txt_record));
|
||||
|
||||
if (!promise.future.ready)
|
||||
promise.resolve (hosts);
|
||||
});
|
||||
resolver.failure.connect (error => {
|
||||
if (!promise.future.ready)
|
||||
promise.reject (new Error.NOT_SUPPORTED ("%s", error));
|
||||
});
|
||||
|
||||
try {
|
||||
yield resolver.start (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
|
||||
return yield promise.future.wait_async (cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxPairingServiceHost : Object, PairingServiceHost {
|
||||
public string name {
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public InetSocketAddress address {
|
||||
get { return _address; }
|
||||
}
|
||||
|
||||
public uint16 port {
|
||||
get { return _port; }
|
||||
}
|
||||
|
||||
public Gee.List<string> txt_record {
|
||||
get { return _txt_record; }
|
||||
}
|
||||
|
||||
private string _name;
|
||||
private InetSocketAddress _address;
|
||||
private uint16 _port;
|
||||
private Gee.List<string> _txt_record;
|
||||
|
||||
internal LinuxPairingServiceHost (string name, InetSocketAddress address, uint16 port, Gee.List<string> txt_record) {
|
||||
_name = name;
|
||||
_address = address;
|
||||
_port = port;
|
||||
_txt_record = txt_record;
|
||||
}
|
||||
|
||||
public async Gee.List<InetSocketAddress> resolve (Cancellable? cancellable) throws Error, IOError {
|
||||
var result = new Gee.ArrayList<InetSocketAddress> ();
|
||||
result.add (address);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private const string AVAHI_SERVICE_NAME = "org.freedesktop.Avahi";
|
||||
|
||||
[DBus (name = "org.freedesktop.Avahi.Server2")]
|
||||
private interface AvahiServer : Object {
|
||||
public abstract async GLib.ObjectPath service_browser_prepare (int interface_index, AvahiProtocol protocol, string type,
|
||||
string domain, uint flags, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async GLib.ObjectPath service_resolver_prepare (int interface_index, AvahiProtocol protocol, string name,
|
||||
string type, string domain, AvahiProtocol aprotocol, AvahiLookupFlags flags, Cancellable? cancellable)
|
||||
throws GLib.Error;
|
||||
}
|
||||
|
||||
[DBus (name = "org.freedesktop.Avahi.ServiceBrowser")]
|
||||
private interface AvahiServiceBrowser : Object {
|
||||
public signal void item_new (int interface_index, AvahiProtocol protocol, string name, string type, string domain,
|
||||
uint flags);
|
||||
public signal void item_remove (int interface_index, AvahiProtocol protocol, string name, string type, string domain,
|
||||
uint flags);
|
||||
public signal void failure (string error);
|
||||
public signal void all_for_now ();
|
||||
public signal void cache_exhausted ();
|
||||
|
||||
public abstract async void start (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void free (Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
[DBus (name = "org.freedesktop.Avahi.ServiceResolver")]
|
||||
private interface AvahiServiceResolver : Object {
|
||||
public signal void found (int interface_index, AvahiProtocol protocol, string name, string type, string domain, string host,
|
||||
AvahiProtocol address_protocol, string address, uint16 port, [DBus (signature = "aay")] Variant txt, uint flags);
|
||||
public signal void failure (string error);
|
||||
|
||||
public abstract async void start (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void free (Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
private enum AvahiProtocol {
|
||||
INET,
|
||||
INET6,
|
||||
UNSPEC = -1,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum AvahiLookupFlags {
|
||||
USE_WIDE_AREA = 1,
|
||||
USE_MULTICAST = 2,
|
||||
NO_TXT = 4,
|
||||
NO_ADDRESS = 8,
|
||||
}
|
||||
}
|
||||
@@ -1,420 +0,0 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
using Darwin.GCD;
|
||||
using Darwin.IOKit;
|
||||
|
||||
public sealed class MacOSTunnelFinder : Object, TunnelFinder {
|
||||
public async Tunnel? find (string udid, Cancellable? cancellable) throws Error, IOError {
|
||||
var main_context = MainContext.ref_thread_default ();
|
||||
var event_queue = new DispatchQueue ("re.frida.fruity.tunnel", DispatchQueueAttr.SERIAL);
|
||||
|
||||
Darwin.Xpc.Dictionary? device_info = null;
|
||||
bool all_listed = false;
|
||||
bool waiting = false;
|
||||
|
||||
var pairingd = XpcClient.make_for_mach_service ("com.apple.CoreDevice.remotepairingd", event_queue);
|
||||
pairingd.notify["state"].connect ((obj, pspec) => {
|
||||
if (waiting)
|
||||
find.callback ();
|
||||
});
|
||||
pairingd.message.connect (obj => {
|
||||
var reader = new XpcObjectReader (obj);
|
||||
|
||||
try {
|
||||
reader.read_member ("mangledTypeName");
|
||||
if (reader.get_string_value () == "RemotePairing.ServiceEvent") {
|
||||
reader
|
||||
.end_member ()
|
||||
.read_member ("value");
|
||||
if (reader.try_read_member ("deviceFound")) {
|
||||
reader
|
||||
.read_member ("_0")
|
||||
.read_member ("deviceInfo")
|
||||
.read_member ("udid");
|
||||
if (reader.get_string_value () == udid) {
|
||||
reader.end_member ();
|
||||
device_info = (Darwin.Xpc.Dictionary) reader.current_object;
|
||||
}
|
||||
} else if (reader.has_member ("allCurrentDevicesListed")) {
|
||||
all_listed = true;
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
if (waiting)
|
||||
find.callback ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
});
|
||||
|
||||
var r = new PairingdRequest ("RemotePairing.BrowseRequest");
|
||||
r.body.set_bool ("currentDevicesOnly", true);
|
||||
yield pairingd.request (r.message, cancellable);
|
||||
|
||||
while (!all_listed && pairingd.state == OPEN) {
|
||||
waiting = true;
|
||||
yield;
|
||||
waiting = false;
|
||||
}
|
||||
|
||||
if (device_info == null) {
|
||||
if (all_listed)
|
||||
throw new Error.INVALID_ARGUMENT ("Device not found");
|
||||
else
|
||||
throw new Error.NOT_SUPPORTED ("Unexpectedly lost connection to remotepairingd");
|
||||
}
|
||||
|
||||
var pairing_device = new XpcClient (device_info.create_connection ("endpoint"), event_queue);
|
||||
var tunnel = new MacOSTunnel (pairing_device);
|
||||
yield tunnel.attach (cancellable);
|
||||
return tunnel;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MacOSTunnel : Object, Tunnel {
|
||||
public DiscoveryService discovery {
|
||||
get {
|
||||
return _discovery;
|
||||
}
|
||||
}
|
||||
|
||||
private XpcClient pairing_device;
|
||||
private Darwin.Xpc.Object? assertion_identifier;
|
||||
private InetAddress? tunnel_device_address;
|
||||
private DiscoveryService? _discovery;
|
||||
|
||||
public MacOSTunnel (XpcClient pairing_device) {
|
||||
this.pairing_device = pairing_device;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
var r = new PairingdRequest ("RemotePairing.ReleaseAssertionRequest");
|
||||
r.body.set_value ("assertionIdentifier", assertion_identifier);
|
||||
try {
|
||||
yield pairing_device.request (r.message, cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async void attach (Cancellable? cancellable) throws Error, IOError {
|
||||
var r = new PairingdRequest ("RemotePairing.CreateAssertionCommand");
|
||||
r.body.set_int64 ("flags", 0);
|
||||
var response = yield pairing_device.request (r.message, cancellable);
|
||||
|
||||
var reader = new XpcObjectReader (response);
|
||||
reader.read_member ("response");
|
||||
|
||||
assertion_identifier = reader
|
||||
.read_member ("assertionIdentifier")
|
||||
.get_object_value (Darwin.Xpc.Uuid.TYPE);
|
||||
reader.end_member ();
|
||||
|
||||
string tunnel_ip_address = reader
|
||||
.read_member ("info")
|
||||
.read_member ("tunnelIPAddress")
|
||||
.get_string_value ();
|
||||
tunnel_device_address = new InetAddress.from_string (tunnel_ip_address);
|
||||
|
||||
_discovery = yield locate_discovery_service (tunnel_device_address, cancellable);
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
SocketConnection connection;
|
||||
try {
|
||||
var service_address = new InetSocketAddress (tunnel_device_address, port);
|
||||
|
||||
var client = new SocketClient ();
|
||||
connection = yield client.connect_async (service_address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
private static async DiscoveryService locate_discovery_service (InetAddress tunnel_device_address, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var path_buf = new char[4096];
|
||||
unowned string path = (string) path_buf;
|
||||
|
||||
foreach (var item in XNU.query_active_tcp_connections ()) {
|
||||
if (item.family != IPV6)
|
||||
continue;
|
||||
if (!item.foreign_address.equal (tunnel_device_address))
|
||||
continue;
|
||||
if (Darwin.XNU.proc_pidpath (item.effective_pid, path_buf) <= 0)
|
||||
continue;
|
||||
if (path != "/usr/libexec/remoted")
|
||||
continue;
|
||||
|
||||
try {
|
||||
var connectable = new InetSocketAddress (tunnel_device_address, item.foreign_port);
|
||||
|
||||
var sc = new SocketClient ();
|
||||
SocketConnection connection = yield sc.connect_async (connectable, cancellable);
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return yield DiscoveryService.open (connection, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect RSD port");
|
||||
}
|
||||
}
|
||||
|
||||
private class PairingdRequest {
|
||||
public Darwin.Xpc.Dictionary message = new Darwin.Xpc.Dictionary ();
|
||||
public Darwin.Xpc.Dictionary body = new Darwin.Xpc.Dictionary ();
|
||||
|
||||
public PairingdRequest (string name) {
|
||||
message.set_string ("mangledTypeName", name);
|
||||
message.set_value ("value", body);
|
||||
}
|
||||
}
|
||||
|
||||
public class MacOSFruitFinder : Object, FruitFinder {
|
||||
public string? udid_from_iface (string ifname) throws Error {
|
||||
var matching_dict = Darwin.IOKit.service_matching (Darwin.IOKit.ETHERNET_INTERFACE_CLASS);
|
||||
if (matching_dict == null)
|
||||
return null;
|
||||
matching_dict.add (CoreFoundation.String.make (Darwin.IOKit.BSD_NAME_KEY), CoreFoundation.String.make (ifname));
|
||||
|
||||
var registry = IORegistry.open ();
|
||||
foreach (var service in registry.matching_services (matching_dict)) {
|
||||
var usb_serial = find_idevice (service.parent (Darwin.IOKit.IOSERVICE_PLANE));
|
||||
if (usb_serial != null)
|
||||
return usb_serial;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? find_idevice (IORegistryEntry service) throws Error {
|
||||
if (service.get_string_property ("CFBundleIdentifier") == "com.apple.driver.usb.cdc.ncm")
|
||||
return find_idevice (service.parent (Darwin.IOKit.IOSERVICE_PLANE));
|
||||
|
||||
var props = service.get_properties ();
|
||||
|
||||
var prod = props.get_string_value ("USB Product Name");
|
||||
if (prod != "iPhone" && prod != "iPad")
|
||||
return null;
|
||||
|
||||
return props.get_string_value ("USB Serial Number");
|
||||
}
|
||||
}
|
||||
|
||||
namespace XNU {
|
||||
public PcbList query_active_tcp_connections () {
|
||||
size_t size = 0;
|
||||
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", null, &size);
|
||||
|
||||
var pcbs = new uint8[size];
|
||||
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", pcbs, &size);
|
||||
|
||||
return new PcbList (pcbs);
|
||||
}
|
||||
|
||||
public class PcbList {
|
||||
private uint8[] pcbs;
|
||||
|
||||
internal PcbList (owned uint8[] pcbs) {
|
||||
this.pcbs = (owned) pcbs;
|
||||
}
|
||||
|
||||
public Iterator iterator () {
|
||||
return new Iterator (this);
|
||||
}
|
||||
|
||||
public class Iterator {
|
||||
private PcbList list;
|
||||
private InetItem * cursor;
|
||||
|
||||
internal Iterator (PcbList list) {
|
||||
this.list = list;
|
||||
|
||||
var gen = (Darwin.XNU.InetPcbGeneration *) list.pcbs;
|
||||
cursor = (InetItem *) ((uint8 *) list.pcbs + gen->length);
|
||||
}
|
||||
|
||||
public Item? next_value () {
|
||||
InetPcb * pcb = null;
|
||||
while (true) {
|
||||
if (cursor->length == 24)
|
||||
return null;
|
||||
|
||||
switch (cursor->kind) {
|
||||
case InetItemKind.PCB:
|
||||
pcb = (InetPcb *) cursor;
|
||||
break;
|
||||
case InetItemKind.SOCKET:
|
||||
var item = new Item (*pcb, *((InetSocket *) cursor));
|
||||
advance ();
|
||||
return item;
|
||||
}
|
||||
|
||||
advance ();
|
||||
}
|
||||
}
|
||||
|
||||
private void advance () {
|
||||
uint32 l = cursor->length;
|
||||
if (l % 8 != 0)
|
||||
l += 8 - (l % 8);
|
||||
cursor = (InetItem *) ((uint8 *) cursor + l);
|
||||
}
|
||||
}
|
||||
|
||||
public class Item {
|
||||
public SocketFamily family {
|
||||
get {
|
||||
return ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
? SocketFamily.IPV6
|
||||
: SocketFamily.IPV4;
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress local_address {
|
||||
get {
|
||||
if (cached_local_address == null)
|
||||
cached_local_address = parse_address (pcb.local_address);
|
||||
return cached_local_address;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 local_port {
|
||||
get {
|
||||
return uint16.from_big_endian (pcb.local_port);
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress foreign_address {
|
||||
get {
|
||||
if (cached_foreign_address == null)
|
||||
cached_foreign_address = parse_address (pcb.foreign_address);
|
||||
return cached_foreign_address;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 foreign_port {
|
||||
get {
|
||||
return uint16.from_big_endian (pcb.foreign_port);
|
||||
}
|
||||
}
|
||||
|
||||
public int32 effective_pid {
|
||||
get {
|
||||
return sock.effective_pid;
|
||||
}
|
||||
}
|
||||
|
||||
private InetPcb pcb;
|
||||
private InetSocket sock;
|
||||
private InetAddress? cached_local_address;
|
||||
private InetAddress? cached_foreign_address;
|
||||
|
||||
public Item (InetPcb pcb, InetSocket sock) {
|
||||
this.pcb = pcb;
|
||||
this.sock = sock;
|
||||
}
|
||||
|
||||
private InetAddress parse_address (uint8[] bytes) {
|
||||
if ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
return new InetAddress.from_bytes (bytes, IPV6);
|
||||
var addr = (Darwin.XNU.InetAddr4in6 *) bytes;
|
||||
return new InetAddress.from_bytes ((uint8[]) &addr->addr4.s_addr, IPV4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetItem {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
}
|
||||
|
||||
public enum InetItemKind {
|
||||
SOCKET = 0x001,
|
||||
PCB = 0x010,
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetPcb {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
|
||||
public uint64 inpp;
|
||||
public uint16 foreign_port;
|
||||
public uint16 local_port;
|
||||
public uint32 per_protocol_pcb_low;
|
||||
public uint32 per_protocol_pcb_high;
|
||||
public uint32 generation_count_low;
|
||||
public uint32 generation_count_high;
|
||||
public int flags;
|
||||
public uint32 flow;
|
||||
public uint8 version_flag;
|
||||
public uint8 ip_ttl;
|
||||
public uint8 ip_protocol;
|
||||
public uint8 padding;
|
||||
public uint8 foreign_address[16];
|
||||
public uint8 local_address[16];
|
||||
public InetDepend4 depend4;
|
||||
public InetDepend6 depend6;
|
||||
public uint32 flowhash;
|
||||
public uint32 flags2;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetSocket {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
|
||||
public uint64 so;
|
||||
public int16 type;
|
||||
public uint16 options_low;
|
||||
public uint16 options_high;
|
||||
public int16 linger;
|
||||
public int16 state;
|
||||
public uint16 pcb[4];
|
||||
public uint16 protocol_low;
|
||||
public uint16 protocol_high;
|
||||
public uint16 family_low;
|
||||
public uint16 family_high;
|
||||
public int16 qlen;
|
||||
public int16 incqlen;
|
||||
public int16 qlimit;
|
||||
public int16 timeo;
|
||||
public uint16 error;
|
||||
public int32 pgid;
|
||||
public uint32 oobmark;
|
||||
public uint32 uid;
|
||||
public int32 last_pid;
|
||||
public int32 effective_pid;
|
||||
public uint64 gencnt;
|
||||
public uint32 flags;
|
||||
public uint32 flags1;
|
||||
public int32 usecount;
|
||||
public int32 retaincnt;
|
||||
public uint32 filter_flags;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetDepend4 {
|
||||
public uint8 ip_tos;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetDepend6 {
|
||||
public uint8 hlim;
|
||||
public int checksum;
|
||||
public uint16 interface_index;
|
||||
public int16 hops;
|
||||
}
|
||||
}
|
||||
}
|
||||
+918
-1150
File diff suppressed because it is too large.
Load diff
@@ -1335,10 +1335,11 @@ namespace Frida {
|
||||
protected virtual void on_event (string type, Json.Array event) {
|
||||
}
|
||||
|
||||
protected async Json.Node call (string method, Json.Node[] args, Cancellable? cancellable) throws Error, IOError {
|
||||
protected async Json.Node call (string method, Json.Node[] args, Bytes? data, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
yield ensure_loaded (cancellable);
|
||||
|
||||
return yield rpc_client.call (method, args, cancellable);
|
||||
return yield rpc_client.call (method, args, data, cancellable);
|
||||
}
|
||||
|
||||
protected async void post (Json.Node message, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -1508,9 +1509,11 @@ namespace Frida {
|
||||
printerr ("%s\n", json);
|
||||
}
|
||||
|
||||
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
|
||||
var has_data = data != null;
|
||||
var data_param = has_data ? data.get_data () : new uint8[0];
|
||||
try {
|
||||
yield session.post_messages ({ AgentMessage (SCRIPT, script, json, false, {}) }, 0, cancellable);
|
||||
yield session.post_messages ({ AgentMessage (SCRIPT, script, json, has_data, data_param) }, 0, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
|
||||
@@ -1017,8 +1017,9 @@ namespace Frida {
|
||||
establish_connection (launch, spec, bres, agent_ctrl, fallback_address, cancellable);
|
||||
|
||||
uint64 loader_base = (uintptr) bres.context.allocation_base;
|
||||
|
||||
var call_builder = new RemoteCallBuilder (loader_base, saved_regs);
|
||||
GPRegs regs = saved_regs;
|
||||
regs.stack_pointer = bres.allocated_stack.stack_root;
|
||||
var call_builder = new RemoteCallBuilder (loader_base, regs);
|
||||
call_builder.add_argument (loader_base + loader_layout.ctx_offset);
|
||||
RemoteCall loader_call = call_builder.build (this);
|
||||
RemoteCallResult loader_result = yield loader_call.execute (cancellable);
|
||||
@@ -1076,8 +1077,10 @@ namespace Frida {
|
||||
unowned uint8[] bootstrapper_code = Frida.Data.HelperBackend.get_bootstrapper_bin_blob ().data;
|
||||
size_t bootstrapper_size = round_size_to_page_size (bootstrapper_code.length);
|
||||
|
||||
size_t stack_size = 64 * 1024;
|
||||
|
||||
uint64 allocation_base = 0;
|
||||
size_t allocation_size = size_t.max (bootstrapper_size, loader_size);
|
||||
size_t allocation_size = size_t.max (bootstrapper_size, loader_size) + stack_size;
|
||||
|
||||
uint64 remote_mmap = 0;
|
||||
uint64 remote_munmap = 0;
|
||||
@@ -1117,10 +1120,12 @@ namespace Frida {
|
||||
Memory.copy (&bootstrap_ctx, output_context, output_context.length);
|
||||
|
||||
allocation_base = (uintptr) bootstrap_ctx.allocation_base;
|
||||
|
||||
code_swap.revert ();
|
||||
}
|
||||
|
||||
result.allocated_stack.stack_base = (void *) (allocation_base + allocation_size - stack_size);
|
||||
result.allocated_stack.stack_size = stack_size;
|
||||
|
||||
try {
|
||||
write_memory (allocation_base, bootstrapper_code);
|
||||
maybe_fixup_helper_code (allocation_base, bootstrapper_code);
|
||||
@@ -1129,7 +1134,9 @@ namespace Frida {
|
||||
|
||||
HelperBootstrapStatus status = SUCCESS;
|
||||
do {
|
||||
var call_builder = new RemoteCallBuilder (code_start, saved_regs);
|
||||
GPRegs regs = saved_regs;
|
||||
regs.stack_pointer = result.allocated_stack.stack_root;
|
||||
var call_builder = new RemoteCallBuilder (code_start, regs);
|
||||
|
||||
unowned uint8[] fallback_ld_data = fallback_ld.data;
|
||||
unowned uint8[] fallback_libc_data = fallback_libc.data;
|
||||
@@ -1416,14 +1423,28 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private struct AllocatedStack {
|
||||
public void * stack_base;
|
||||
public size_t stack_size;
|
||||
|
||||
public uint64 stack_root {
|
||||
get {
|
||||
return (uint64) stack_base + (uint64) stack_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class BootstrapResult {
|
||||
public HelperBootstrapContext context;
|
||||
public HelperLibcApi libc;
|
||||
public AllocatedStack allocated_stack;
|
||||
|
||||
public BootstrapResult clone () {
|
||||
var res = new BootstrapResult ();
|
||||
res.context = context;
|
||||
res.libc = libc;
|
||||
res.allocated_stack = allocated_stack;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -1931,6 +1952,10 @@ namespace Frida {
|
||||
try {
|
||||
yield suspend (null);
|
||||
close ();
|
||||
} catch (Error e) {
|
||||
// If the process is gone, then there's no point in retrying.
|
||||
if (e is Error.PROCESS_NOT_FOUND)
|
||||
attach_state = ALREADY_ATTACHED;
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
@@ -3308,8 +3333,13 @@ namespace Frida {
|
||||
var iter = MapsIter.for_pid (pid);
|
||||
while (iter.next ()) {
|
||||
string candidate_path = iter.path;
|
||||
if (candidate_path == path)
|
||||
if (candidate_path == path) {
|
||||
#if ANDROID
|
||||
if (candidate_path == Gum.Process.query_libc_name () && iter.flags[3] == 's')
|
||||
continue;
|
||||
#endif
|
||||
return new ProcMapsEntry (iter.start_address, candidate_path, iter.identity);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -3332,18 +3362,24 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public string identity {
|
||||
public string flags {
|
||||
owned get {
|
||||
return info.fetch (3);
|
||||
}
|
||||
}
|
||||
|
||||
public string path {
|
||||
public string identity {
|
||||
owned get {
|
||||
return info.fetch (4);
|
||||
}
|
||||
}
|
||||
|
||||
public string path {
|
||||
owned get {
|
||||
return info.fetch (5);
|
||||
}
|
||||
}
|
||||
|
||||
public static MapsIter for_pid (uint pid) {
|
||||
return new MapsIter (pid);
|
||||
}
|
||||
@@ -3355,8 +3391,8 @@ namespace Frida {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^([0-9a-f]+)-([0-9a-f]+) \S{4} [0-9a-f]+ ([0-9a-f]{2,}:[0-9a-f]{2,} \d+) +([^\n]+)$/m.match (contents,
|
||||
0, out info)) {
|
||||
if (!/^([0-9a-f]+)-([0-9a-f]+) (\S{4}) [0-9a-f]+ ([0-9a-f]{2,}:[0-9a-f]{2,} \d+) +([^\n]+)$/m.match (
|
||||
contents, 0, out info)) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,21 @@ top_srcdir := ../../..
|
||||
releng := $(top_srcdir)/releng
|
||||
|
||||
BUILDDIR ?= $(top_srcdir)/build
|
||||
FRIDA_MONOREPO ?= $(top_srcdir)/..
|
||||
MESON ?= $(releng)/meson/meson.py
|
||||
|
||||
ifdef FRIDA_HOST
|
||||
host_os := $(shell echo $(FRIDA_HOST) | cut -f1 -d"-")
|
||||
host_arch := $(shell echo $(FRIDA_HOST) | cut -f2 -d"-")
|
||||
host_machine := $(FRIDA_HOST)
|
||||
else
|
||||
host_os := $(shell $(releng)/detect-os.sh)
|
||||
host_arch := $(shell $(releng)/detect-arch.sh)
|
||||
host_machine := $(shell basename $$(ls -1 $(BUILDDIR)/frida-linux-*.txt | tail -1) | cut -d"." -f1 | cut -d"-" -f2-)
|
||||
endif
|
||||
host_arch := $(shell echo $(host_machine) | cut -d"-" -f2)
|
||||
|
||||
crossfile := $(shell test -d "$(BUILDDIR)" \
|
||||
&& echo "$(BUILDDIR)/frida-$(host_os)-$(host_arch).txt" \
|
||||
|| echo $(FRIDA_MONOREPO)/build/frida*-$(host_os)-$(host_arch).txt)
|
||||
crossfile := $(BUILDDIR)/frida-$(host_machine).txt
|
||||
|
||||
build: ext/linux/tools/include/nolibc/nolibc.h
|
||||
rm -rf build
|
||||
meson setup --cross-file $(crossfile) -Db_lto=true build
|
||||
meson compile -C build
|
||||
$(MESON) setup --cross-file $(crossfile) -Db_lto=true build
|
||||
$(MESON) compile -C build
|
||||
cp build/bootstrapper.bin bootstrapper-$(host_arch).bin
|
||||
cp build/loader.bin loader-$(host_arch).bin
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -550,7 +550,9 @@ frida_infer_rtld_flavor_from_filename (const char * name)
|
||||
if (frida_str_has_prefix (name, "ld-uClibc"))
|
||||
return FRIDA_RTLD_UCLIBC;
|
||||
|
||||
if (strcmp (name, "libc.so") == 0)
|
||||
if (strcmp (name, "libc.so") == 0 ||
|
||||
frida_str_has_prefix (name, "libc.musl") ||
|
||||
frida_str_has_prefix (name, "ld-musl"))
|
||||
return FRIDA_RTLD_MUSL;
|
||||
|
||||
if (strcmp (name, "ld-android.so") == 0)
|
||||
@@ -607,7 +609,9 @@ frida_path_is_libc (const char * path, FridaRtldFlavor rtld_flavor)
|
||||
else
|
||||
name = path;
|
||||
|
||||
return frida_str_has_prefix (name, "libc.so");
|
||||
return frida_str_has_prefix (name, "libc.so") ||
|
||||
frida_str_has_prefix (name, "libc.musl") ||
|
||||
frida_str_has_prefix (name, "ld-musl");
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -24,6 +24,10 @@ if host_machine.cpu_family() == 'arm'
|
||||
extra_flags += '-marm'
|
||||
endif
|
||||
|
||||
if host_machine.cpu_family() == 'x86'
|
||||
extra_flags += '-fno-stack-protector'
|
||||
endif
|
||||
|
||||
common_objcopy_flags = [
|
||||
'-O', 'binary',
|
||||
'-S',
|
||||
@@ -53,9 +57,10 @@ extra_link_args = [
|
||||
'-Wl,-T,' + helper_lds,
|
||||
]
|
||||
if host_machine.cpu_family() == 'arm'
|
||||
extra_link_args += run_command('../../../../build/frida-android-arm-clang', '-print-libgcc-file-name', check: false).stdout().strip()
|
||||
extra_link_args += run_command(cc, '-print-libgcc-file-name', check: false).stdout().strip()
|
||||
endif
|
||||
|
||||
|
||||
bootstrapper_sources = [
|
||||
'bootstrapper.c',
|
||||
'elf-parser.c',
|
||||
|
||||
@@ -303,26 +303,3 @@ index 78473d34e..f17e6f598 100644
|
||||
+ mode = va_arg(args, /* mode_t */ unsigned int);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h
|
||||
index fbbc0e68c..d003c368d 100644
|
||||
--- a/tools/include/nolibc/types.h
|
||||
+++ b/tools/include/nolibc/types.h
|
||||
@@ -92,6 +92,8 @@
|
||||
#define FD_SETIDXMASK (8 * sizeof(unsigned long))
|
||||
#define FD_SETBITMASK (8 * sizeof(unsigned long)-1)
|
||||
|
||||
+#ifndef __mips__
|
||||
+
|
||||
/* for select() */
|
||||
typedef struct {
|
||||
unsigned long fds[(FD_SETSIZE + FD_SETBITMASK) / FD_SETIDXMASK];
|
||||
@@ -131,6 +133,8 @@ typedef struct {
|
||||
__set->fds[__idx] = 0; \
|
||||
} while (0)
|
||||
|
||||
+#endif
|
||||
+
|
||||
/* for poll() */
|
||||
#define POLLIN 0x0001
|
||||
#define POLLPRI 0x0002
|
||||
Executable
+100
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is responsible for building Frida helpers for various Linux
|
||||
# architectures. It can build helpers for a single specified architecture on the
|
||||
# local machine, or for supported architectures in a container. The script uses
|
||||
# Docker containers to ensure consistent build environments for each
|
||||
# architecture.
|
||||
#
|
||||
# Note that the expectation is that when running the build for a specific
|
||||
# architecture that it be run from inside the relevant container. This script is
|
||||
# used by CI.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CURRENT_FILE="${BASH_SOURCE[0]}"
|
||||
HELPERS_DIR="$(cd "$(dirname "$CURRENT_FILE")" && pwd)"
|
||||
FRIDA_CORE_DIR="$(cd "$HELPERS_DIR/../../.." && pwd)"
|
||||
RELENG_DIR="$FRIDA_CORE_DIR/releng"
|
||||
BUILD_DIR="$FRIDA_CORE_DIR/build"
|
||||
RELATIVE_TO_FRIDA_CORE_DIR=$(realpath --relative-to="$FRIDA_CORE_DIR" "$CURRENT_FILE")
|
||||
|
||||
TMP_MESON_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TMP_MESON_DIR"' EXIT
|
||||
|
||||
CONTAINER_REGISTRY="${CONTAINER_REGISTRY:-ghcr.io/frida}"
|
||||
|
||||
main () {
|
||||
if [ "$#" -eq 0 ]; then
|
||||
build_arches_in_container
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$#" -gt 1 ]; then
|
||||
echo >&2 "Error: Too many arguments"
|
||||
usage
|
||||
fi
|
||||
|
||||
build_arch "$1"
|
||||
}
|
||||
|
||||
usage () {
|
||||
echo >&2 "Usage: $0 [<arch>]"
|
||||
echo >&2 "If no arch is specified, then all helpers will be built in the container."
|
||||
exit 1
|
||||
}
|
||||
|
||||
ARCHS=(
|
||||
x86
|
||||
x86_64
|
||||
arm
|
||||
arm64
|
||||
mips
|
||||
mipsel
|
||||
mips64
|
||||
mips64el
|
||||
)
|
||||
|
||||
build_arch () {
|
||||
ARCH=$1
|
||||
if [ -z "$ARCH" ]; then
|
||||
usage
|
||||
fi
|
||||
if ! printf '%s\n' "${ARCHS[@]}" | grep -qx "$ARCH"; then
|
||||
echo >&2 "Error: Invalid architecture '$ARCH'"
|
||||
echo >&2 "Supported architectures: ${ARCHS[*]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$ARCH" == arm* ]]; then
|
||||
export FRIDA_HOST=android-$ARCH
|
||||
else
|
||||
export FRIDA_HOST=linux-$ARCH
|
||||
fi
|
||||
|
||||
EXTRA_FLAGS=()
|
||||
if [ "$FRIDA_HOST" == "linux-x86" ]; then
|
||||
EXTRA_FLAGS+=("--build=linux-x86")
|
||||
export CC="gcc -m32" CXX="g++ -m32" STRIP="strip"
|
||||
fi
|
||||
|
||||
cd "$FRIDA_CORE_DIR"
|
||||
|
||||
rm -rf "$BUILD_DIR"
|
||||
# Note that $XTOOLS_HOST is set by the container.
|
||||
./configure --host="$XTOOLS_HOST" "${EXTRA_FLAGS[@]}"
|
||||
make -C src/linux/helpers
|
||||
}
|
||||
|
||||
build_arches_in_container () {
|
||||
for ARCH in "${ARCHS[@]}"; do
|
||||
docker run -u "$(id -u):$(id -g)" \
|
||||
-w /frida-core \
|
||||
-i -t \
|
||||
-v "$FRIDA_CORE_DIR:/frida-core" \
|
||||
"$CONTAINER_REGISTRY/core-linux-helpers-$ARCH:latest" \
|
||||
"/frida-core/$RELATIVE_TO_FRIDA_CORE_DIR" "$ARCH"
|
||||
done
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -894,7 +894,7 @@ namespace Frida {
|
||||
var scope = options.scope;
|
||||
var scope_node = new Json.Node.alloc ().init_string (scope.to_nick ());
|
||||
|
||||
Json.Node result = yield call ("getFrontmostApplication", new Json.Node[] { scope_node }, cancellable);
|
||||
Json.Node result = yield call ("getFrontmostApplication", new Json.Node[] { scope_node }, null, cancellable);
|
||||
|
||||
if (result.get_node_type () == NULL)
|
||||
return HostApplicationInfo.empty ();
|
||||
@@ -920,7 +920,7 @@ namespace Frida {
|
||||
var scope = options.scope;
|
||||
var scope_node = new Json.Node.alloc ().init_string (scope.to_nick ());
|
||||
|
||||
Json.Node apps = yield call ("enumerateApplications", new Json.Node[] { identifiers_node, scope_node },
|
||||
Json.Node apps = yield call ("enumerateApplications", new Json.Node[] { identifiers_node, scope_node }, null,
|
||||
cancellable);
|
||||
|
||||
var items = apps.get_array ();
|
||||
@@ -946,7 +946,7 @@ namespace Frida {
|
||||
var package_name_node = new Json.Node.alloc ().init_string (package);
|
||||
var uid_node = new Json.Node.alloc ().init_int (uid);
|
||||
|
||||
Json.Node name = yield call ("getProcessName", new Json.Node[] { package_name_node, uid_node }, cancellable);
|
||||
Json.Node name = yield call ("getProcessName", new Json.Node[] { package_name_node, uid_node }, null, cancellable);
|
||||
|
||||
return name.get_string ();
|
||||
}
|
||||
@@ -960,7 +960,8 @@ namespace Frida {
|
||||
|
||||
var scope_node = new Json.Node.alloc ().init_string (scope.to_nick ());
|
||||
|
||||
Json.Node by_pid = yield call ("getProcessParameters", new Json.Node[] { pids_node, scope_node }, cancellable);
|
||||
Json.Node by_pid = yield call ("getProcessParameters", new Json.Node[] { pids_node, scope_node }, null,
|
||||
cancellable);
|
||||
|
||||
var result = new Gee.HashMap<uint, HashTable<string, Variant>> ();
|
||||
by_pid.get_object ().foreach_member ((object, pid_str, parameters_node) => {
|
||||
@@ -982,20 +983,20 @@ namespace Frida {
|
||||
if (entrypoint is DefaultActivityEntrypoint) {
|
||||
var activity_node = new Json.Node.alloc ().init_null ();
|
||||
|
||||
yield call ("startActivity", new Json.Node[] { package_node, activity_node, uid_node }, cancellable);
|
||||
yield call ("startActivity", new Json.Node[] { package_node, activity_node, uid_node }, null, cancellable);
|
||||
} else if (entrypoint is ActivityEntrypoint) {
|
||||
var e = entrypoint as ActivityEntrypoint;
|
||||
|
||||
var activity_node = new Json.Node.alloc ().init_string (e.activity);
|
||||
|
||||
yield call ("startActivity", new Json.Node[] { package_node, activity_node, uid_node }, cancellable);
|
||||
yield call ("startActivity", new Json.Node[] { package_node, activity_node, uid_node }, null, cancellable);
|
||||
} else if (entrypoint is BroadcastReceiverEntrypoint) {
|
||||
var e = entrypoint as BroadcastReceiverEntrypoint;
|
||||
|
||||
var receiver_node = new Json.Node.alloc ().init_string (e.receiver);
|
||||
var action_node = new Json.Node.alloc ().init_string (e.action);
|
||||
|
||||
yield call ("sendBroadcast", new Json.Node[] { package_node, receiver_node, action_node, uid_node },
|
||||
yield call ("sendBroadcast", new Json.Node[] { package_node, receiver_node, action_node, uid_node }, null,
|
||||
cancellable);
|
||||
} else {
|
||||
assert_not_reached ();
|
||||
@@ -1006,13 +1007,13 @@ namespace Frida {
|
||||
var package_node = new Json.Node.alloc ().init_string (package);
|
||||
var uid_node = new Json.Node.alloc ().init_int (uid);
|
||||
|
||||
yield call ("stopPackage", new Json.Node[] { package_node, uid_node }, cancellable);
|
||||
yield call ("stopPackage", new Json.Node[] { package_node, uid_node }, null, cancellable);
|
||||
}
|
||||
|
||||
public async bool try_stop_package_by_pid (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
var pid_node = new Json.Node.alloc ().init_int (pid);
|
||||
|
||||
Json.Node success = yield call ("tryStopPackageByPid", new Json.Node[] { pid_node }, cancellable);
|
||||
Json.Node success = yield call ("tryStopPackageByPid", new Json.Node[] { pid_node }, null, cancellable);
|
||||
|
||||
return success.get_boolean ();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <pwd.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <gio/gunixmounts.h>
|
||||
|
||||
typedef struct _FridaEnumerateProcessesOperation FridaEnumerateProcessesOperation;
|
||||
|
||||
@@ -13,6 +14,8 @@ struct _FridaEnumerateProcessesOperation
|
||||
};
|
||||
|
||||
static void frida_collect_process_info (guint pid, FridaEnumerateProcessesOperation * op);
|
||||
static gboolean frida_is_directory_noexec (const gchar * directory);
|
||||
static gchar * frida_get_application_directory (void);
|
||||
static gboolean frida_add_process_metadata (GHashTable * parameters, const gchar * proc_entry_name);
|
||||
static GDateTime * frida_query_boot_time (void);
|
||||
static GVariant * frida_uid_to_name (uid_t uid);
|
||||
@@ -151,12 +154,60 @@ frida_system_kill (guint pid)
|
||||
gchar *
|
||||
frida_temporary_directory_get_system_tmp (void)
|
||||
{
|
||||
const gchar * tmp_dir;
|
||||
|
||||
#ifdef HAVE_ANDROID
|
||||
if (getuid () == 0)
|
||||
return g_strdup ("/data/local/tmp");
|
||||
#endif
|
||||
|
||||
return g_strdup (g_get_tmp_dir ());
|
||||
tmp_dir = g_get_tmp_dir ();
|
||||
|
||||
/*
|
||||
* If the temporary directory resides on a file-system which is marked
|
||||
* `noexec`, then we won't be able to write the frida-agent.so there and
|
||||
* subsequently dlopen() it inside the target application as it will result in
|
||||
* permission denied.
|
||||
*
|
||||
* The mounting of the temporary file-system as `noexec` is sometimes used as
|
||||
* an added security measure on embedded systems where the functionality is
|
||||
* fixed and we aren't expecting any interactive user sessions.
|
||||
*
|
||||
* Since our current process is executing, we know that it must reside on a
|
||||
* file-system which is not mounted `noexec`. Whilst it is possible that it is
|
||||
* mounted read-only, or there may be some other reason why it isn't suitable,
|
||||
* we know that the temporary directory is definitely unusable. If both these
|
||||
* locations are found to be unsuitable, then a future implementation may seek
|
||||
* to validate an ordered list of potential locations.
|
||||
*/
|
||||
if (frida_is_directory_noexec (tmp_dir))
|
||||
return frida_get_application_directory ();
|
||||
else
|
||||
return g_strdup (tmp_dir);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_is_directory_noexec (const gchar * directory)
|
||||
{
|
||||
gboolean is_noexec;
|
||||
g_autoptr(GUnixMountEntry) entry;
|
||||
gchar ** options;
|
||||
|
||||
entry = g_unix_mount_for (directory, NULL);
|
||||
if (entry == NULL)
|
||||
return FALSE;
|
||||
|
||||
options = g_strsplit (g_unix_mount_get_options (entry), ",", 0);
|
||||
is_noexec = g_strv_contains ((const char * const *) options, "noexec");
|
||||
g_strfreev (options);
|
||||
|
||||
return is_noexec;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
frida_get_application_directory (void)
|
||||
{
|
||||
return g_path_get_dirname (gum_process_get_main_module ()->path);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
||||
+40
-25
@@ -44,6 +44,14 @@ if have_local_backend
|
||||
'@INPUT0@',
|
||||
]
|
||||
|
||||
if helper_emulated_modern == 'auto'
|
||||
helper_emulated_modern = ''
|
||||
endif
|
||||
|
||||
if helper_emulated_legacy == 'auto'
|
||||
helper_emulated_legacy = ''
|
||||
endif
|
||||
|
||||
if host_os_family == 'windows'
|
||||
backend_sources += [
|
||||
'windows' / 'windows-host-session.vala',
|
||||
@@ -54,6 +62,7 @@ if have_local_backend
|
||||
'windows' / 'frida-helper-process-glue.c',
|
||||
'windows' / 'access-helpers.c',
|
||||
]
|
||||
backend_vala_args += '--pkg=frida-gum-windows-1.0'
|
||||
|
||||
helper_backend_sources = [
|
||||
'windows' / 'frida-helper-backend.vala',
|
||||
@@ -61,7 +70,10 @@ if have_local_backend
|
||||
'windows' / 'frida-helper-types.vala',
|
||||
'windows' / 'wait-handle-source.c',
|
||||
]
|
||||
helper_backend_vala_args = base_vala_args
|
||||
helper_backend_vala_args = [
|
||||
base_vala_args,
|
||||
'--pkg=frida-gum-windows-1.0',
|
||||
]
|
||||
helper_backend = static_library('frida-helper-backend', helper_backend_sources,
|
||||
vala_args: helper_backend_vala_args,
|
||||
c_args: frida_component_cflags,
|
||||
@@ -105,7 +117,7 @@ if have_local_backend
|
||||
'frida-data-helper-process.c',
|
||||
'frida-data-helper-process-blob' + resource_blob_extension,
|
||||
],
|
||||
command: embed_helper + [helper_modern, helper_legacy],
|
||||
command: embed_helper + [helper_modern, helper_legacy, helper_emulated_modern, helper_emulated_legacy],
|
||||
depends: helper_depends,
|
||||
)
|
||||
backend_sources += [helper_process_data]
|
||||
@@ -177,6 +189,7 @@ if have_local_backend
|
||||
helper_backend_vala_args,
|
||||
gum_vala_args,
|
||||
'--pkg=frida-gum-darwin-1.0',
|
||||
'--pkg=gmodule-2.0',
|
||||
'--pkg=gio-unix-2.0',
|
||||
'--pkg=posix',
|
||||
],
|
||||
@@ -253,7 +266,7 @@ if have_local_backend
|
||||
'frida-data-helper-process.c',
|
||||
'frida-data-helper-process-blob' + resource_blob_extension,
|
||||
],
|
||||
command: embed_helper + [helper_modern, helper_legacy],
|
||||
command: embed_helper + [helper_modern, helper_legacy, helper_emulated_modern, helper_emulated_legacy],
|
||||
depends: helper_depends,
|
||||
)
|
||||
backend_sources += [helper_process_data]
|
||||
@@ -406,7 +419,7 @@ if have_local_backend
|
||||
'frida-data-helper-process.c',
|
||||
'frida-data-helper-process-blob' + resource_blob_extension,
|
||||
],
|
||||
command: embed_helper + [helper_modern, helper_legacy],
|
||||
command: embed_helper + [helper_modern, helper_legacy, helper_emulated_modern, helper_emulated_legacy],
|
||||
depends: helper_depends,
|
||||
)
|
||||
backend_sources += [helper_process_data]
|
||||
@@ -481,6 +494,7 @@ endif
|
||||
if have_fruity_backend
|
||||
backend_sources += [
|
||||
'fruity' / 'fruity-host-session.vala',
|
||||
'fruity' / 'device-monitor.vala',
|
||||
'fruity' / 'xpc.vala',
|
||||
'fruity' / 'dtx.vala',
|
||||
'fruity' / 'lockdown.vala',
|
||||
@@ -489,29 +503,30 @@ if have_fruity_backend
|
||||
'fruity' / 'lldb.vala',
|
||||
'fruity' / 'injector.vala',
|
||||
'fruity' / 'usbmux.vala',
|
||||
'fruity' / 'dns.vala',
|
||||
'fruity' / 'keyed-archive.vala',
|
||||
'fruity' / 'plist.vala',
|
||||
'fruity' / 'plist-service.vala',
|
||||
'fruity' / 'opack.vala',
|
||||
'fruity' / 'network-stack.vala',
|
||||
'fruity' / 'usb.vala',
|
||||
'fruity' / 'ncm.vala',
|
||||
]
|
||||
|
||||
if host_os_family == 'windows'
|
||||
backend_sources += 'fruity' / 'fruity-host-session-windows.c'
|
||||
elif host_os_family == 'darwin'
|
||||
backend_sources += 'fruity' / 'fruity-host-session-darwin.m'
|
||||
else
|
||||
backend_sources += 'fruity' / 'fruity-host-session-unix.c'
|
||||
backend_sources += 'fruity' / 'device-monitor-windows.vala'
|
||||
elif host_os == 'macos'
|
||||
backend_sources += 'fruity' / 'device-monitor-macos.vala'
|
||||
elif host_os == 'linux'
|
||||
backend_sources += 'fruity' / 'device-monitor-linux.vala'
|
||||
endif
|
||||
|
||||
if host_os_family == 'darwin'
|
||||
backend_sources += 'fruity' / 'xpc-darwin.vala'
|
||||
endif
|
||||
if host_os == 'macos'
|
||||
backend_sources += [
|
||||
'fruity' / 'xpc-macos.vala',
|
||||
'fruity' / 'iokit.vala',
|
||||
]
|
||||
elif host_os == 'linux'
|
||||
backend_sources += 'fruity' / 'xpc-linux.vala'
|
||||
if host_os_family == 'windows'
|
||||
backend_sources += 'fruity' / 'device-monitor-windows.c'
|
||||
elif host_os_family == 'darwin'
|
||||
backend_sources += 'fruity' / 'device-monitor-darwin.m'
|
||||
else
|
||||
backend_sources += 'fruity' / 'device-monitor-unix.c'
|
||||
endif
|
||||
|
||||
backend_deps += openssl_dep
|
||||
@@ -519,17 +534,17 @@ if have_fruity_backend
|
||||
'--pkg=libnghttp2',
|
||||
'--pkg=libngtcp2',
|
||||
'--pkg=libngtcp2_crypto_quictls',
|
||||
'--pkg=libusb-1.0',
|
||||
'--pkg=lwip',
|
||||
'--pkg=openssl',
|
||||
]
|
||||
if host_os_family == 'darwin'
|
||||
if host_os_family == 'windows'
|
||||
backend_vala_args += '--pkg=windows-windns'
|
||||
elif host_os_family == 'darwin'
|
||||
backend_vala_args += [
|
||||
'--pkg=darwin-xnu',
|
||||
'--pkg=darwin-iokit',
|
||||
'--pkg=darwin-xpc',
|
||||
'--pkg=darwin-gcd',
|
||||
'--pkg=darwin-net',
|
||||
'--pkg=darwin-dns-sd',
|
||||
'--pkg=corefoundation',
|
||||
]
|
||||
endif
|
||||
@@ -566,8 +581,8 @@ subdir('compiler')
|
||||
|
||||
core_sources = base_sources + backend_sources + compiler_sources
|
||||
|
||||
internal_libs = [base, pipe] + backend_libs
|
||||
internal_deps = [base_dep, pipe_dep]
|
||||
internal_libs = [base, netif, pipe] + backend_libs
|
||||
internal_deps = [base_dep, netif_dep, pipe_dep]
|
||||
|
||||
if host_os_family == 'windows'
|
||||
gio_platform_vala_args = ['--pkg=gio-windows-2.0']
|
||||
|
||||
@@ -61,8 +61,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
construct {
|
||||
_device = new Device (null, "portal", "Portal", HostSessionProviderKind.LOCAL,
|
||||
new PortalHostSessionProvider (this));
|
||||
_device = new Device (null, new PortalHostSessionProvider (this));
|
||||
|
||||
cluster_service = new WebService (cluster_params, CLUSTER);
|
||||
cluster_service.incoming.connect (on_incoming_cluster_connection);
|
||||
@@ -930,9 +929,8 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return _name; }
|
||||
get { return "Portal"; }
|
||||
}
|
||||
private string _name = "Portal";
|
||||
|
||||
public Variant? icon {
|
||||
get { return _icon; }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gum/gum.h>
|
||||
#include <gum/arch-arm64/gumarm64writer.h>
|
||||
#include <gum/arch-x86/gumx86writer.h>
|
||||
|
||||
#include <windows.h>
|
||||
@@ -75,6 +76,7 @@ static void frida_propagate_open_process_error (guint32 pid, DWORD os_error, GEr
|
||||
static gboolean frida_enable_debug_privilege (void);
|
||||
|
||||
static gboolean frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjectionDetails * details, GError ** error);
|
||||
static gsize frida_remote_worker_context_emit_payload (FridaRemoteWorkerContext * rwc, gpointer code);
|
||||
static void frida_remote_worker_context_destroy (FridaRemoteWorkerContext * rwc, FridaInjectionDetails * details);
|
||||
|
||||
static gboolean frida_remote_worker_context_has_resolved_all_kernel32_functions (const FridaRemoteWorkerContext * rwc);
|
||||
@@ -304,16 +306,169 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
|
||||
{
|
||||
gpointer code;
|
||||
guint code_size;
|
||||
GumX86Writer cw;
|
||||
const gchar * loadlibrary_failed = "loadlibrary_failed";
|
||||
const gchar * skip_unload = "skip_unload";
|
||||
const gchar * return_result = "return_result";
|
||||
SIZE_T page_size, alloc_size;
|
||||
DWORD old_protect;
|
||||
|
||||
gum_init ();
|
||||
|
||||
code = gum_alloc_n_pages (1, GUM_PAGE_RWX); /* Executable so debugger can be used to inspect code */
|
||||
code_size = frida_remote_worker_context_emit_payload (rwc, code);
|
||||
|
||||
memset (rwc, 0, sizeof (FridaRemoteWorkerContext));
|
||||
|
||||
gum_module_enumerate_exports ("kernel32.dll", frida_remote_worker_context_collect_kernel32_export, rwc);
|
||||
if (!frida_remote_worker_context_has_resolved_all_kernel32_functions (rwc))
|
||||
goto failed_to_resolve_kernel32_functions;
|
||||
|
||||
StringCbCopyW (rwc->dll_path, sizeof (rwc->dll_path), details->dll_path);
|
||||
StringCbCopyA (rwc->entrypoint_name, sizeof (rwc->entrypoint_name), details->entrypoint_name);
|
||||
StringCbCopyA (rwc->entrypoint_data, sizeof (rwc->entrypoint_data), details->entrypoint_data);
|
||||
|
||||
page_size = gum_query_page_size ();
|
||||
g_assert (code_size <= page_size);
|
||||
|
||||
alloc_size = page_size + sizeof (FridaRemoteWorkerContext);
|
||||
rwc->entrypoint = VirtualAllocEx (details->process_handle, NULL, alloc_size, MEM_COMMIT, PAGE_READWRITE);
|
||||
if (rwc->entrypoint == NULL)
|
||||
goto virtual_alloc_ex_failed;
|
||||
|
||||
if (!WriteProcessMemory (details->process_handle, rwc->entrypoint, code, code_size, NULL))
|
||||
goto write_process_memory_failed;
|
||||
|
||||
rwc->argument = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (rwc->entrypoint) + page_size);
|
||||
if (!WriteProcessMemory (details->process_handle, rwc->argument, rwc, sizeof (FridaRemoteWorkerContext), NULL))
|
||||
goto write_process_memory_failed;
|
||||
|
||||
if (!VirtualProtectEx (details->process_handle, rwc->entrypoint, page_size, PAGE_EXECUTE_READ, &old_protect))
|
||||
goto virtual_protect_ex_failed;
|
||||
|
||||
gum_free_pages (code);
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
failed_to_resolve_kernel32_functions:
|
||||
{
|
||||
g_set_error (error,
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unexpected error while resolving kernel32 functions");
|
||||
goto error_common;
|
||||
}
|
||||
virtual_alloc_ex_failed:
|
||||
{
|
||||
g_set_error (error,
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unexpected error allocating memory in target process (VirtualAllocEx returned 0x%08lx)",
|
||||
GetLastError ());
|
||||
goto error_common;
|
||||
}
|
||||
write_process_memory_failed:
|
||||
{
|
||||
g_set_error (error,
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unexpected error writing to memory in target process (WriteProcessMemory returned 0x%08lx)",
|
||||
GetLastError ());
|
||||
goto error_common;
|
||||
}
|
||||
virtual_protect_ex_failed:
|
||||
{
|
||||
g_set_error (error,
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unexpected error changing memory permission in target process (VirtualProtectEx returned 0x%08lx)",
|
||||
GetLastError ());
|
||||
goto error_common;
|
||||
}
|
||||
error_common:
|
||||
{
|
||||
frida_remote_worker_context_destroy (rwc, details);
|
||||
gum_free_pages (code);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
#define EMIT_ARM64_LOAD(reg, field) \
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset (&cw, ARM64_REG_##reg, ARM64_REG_X20, G_STRUCT_OFFSET (FridaRemoteWorkerContext, field))
|
||||
#define EMIT_ARM64_LOAD_ADDRESS_OF(reg, field) \
|
||||
gum_arm64_writer_put_add_reg_reg_imm (&cw, ARM64_REG_##reg, ARM64_REG_X20, G_STRUCT_OFFSET (FridaRemoteWorkerContext, field))
|
||||
#define EMIT_ARM64_MOVE(dstreg, srcreg) \
|
||||
gum_arm64_writer_put_mov_reg_reg (&cw, ARM64_REG_##dstreg, ARM64_REG_##srcreg)
|
||||
#define EMIT_ARM64_CALL(reg) \
|
||||
gum_arm64_writer_put_blr_reg_no_auth (&cw, ARM64_REG_##reg)
|
||||
|
||||
static gsize
|
||||
frida_remote_worker_context_emit_payload (FridaRemoteWorkerContext * rwc, gpointer code)
|
||||
{
|
||||
gsize code_size;
|
||||
const gchar * loadlibrary_failed = "loadlibrary_failed";
|
||||
const gchar * skip_unload = "skip_unload";
|
||||
const gchar * return_result = "return_result";
|
||||
#ifdef HAVE_ARM64
|
||||
GumArm64Writer cw;
|
||||
|
||||
gum_arm64_writer_init (&cw, code);
|
||||
|
||||
gum_arm64_writer_put_push_reg_reg (&cw, ARM64_REG_FP, ARM64_REG_LR);
|
||||
gum_arm64_writer_put_mov_reg_reg (&cw, ARM64_REG_FP, ARM64_REG_SP);
|
||||
gum_arm64_writer_put_push_reg_reg (&cw, ARM64_REG_X19, ARM64_REG_X20);
|
||||
|
||||
/* x20 = (FridaRemoteWorkerContext *) lpParameter */
|
||||
EMIT_ARM64_MOVE (X20, X0);
|
||||
|
||||
/* x19 = LoadLibrary (x20->dll_path) */
|
||||
EMIT_ARM64_LOAD_ADDRESS_OF (X0, dll_path);
|
||||
EMIT_ARM64_LOAD (X8, load_library_impl);
|
||||
EMIT_ARM64_CALL (X8);
|
||||
gum_arm64_writer_put_cbz_reg_label (&cw, ARM64_REG_X0, loadlibrary_failed);
|
||||
EMIT_ARM64_MOVE (X19, X0);
|
||||
|
||||
/* x8 = GetProcAddress (x19, x20->entrypoint_name) */
|
||||
EMIT_ARM64_MOVE (X0, X19);
|
||||
EMIT_ARM64_LOAD_ADDRESS_OF (X1, entrypoint_name);
|
||||
EMIT_ARM64_LOAD (X8, get_proc_address_impl);
|
||||
EMIT_ARM64_CALL (X8);
|
||||
EMIT_ARM64_MOVE (X8, X0);
|
||||
|
||||
/* x8 (x20->entrypoint_data, &x20->stay_resident, NULL) */
|
||||
EMIT_ARM64_LOAD_ADDRESS_OF (X0, entrypoint_data);
|
||||
EMIT_ARM64_LOAD_ADDRESS_OF (X1, stay_resident);
|
||||
EMIT_ARM64_MOVE (X2, XZR);
|
||||
EMIT_ARM64_CALL (X8);
|
||||
|
||||
/* if (!x20->stay_resident) { */
|
||||
EMIT_ARM64_LOAD (X0, stay_resident);
|
||||
gum_arm64_writer_put_cbnz_reg_label (&cw, ARM64_REG_X0, skip_unload);
|
||||
|
||||
/* FreeLibrary (xsi) */
|
||||
EMIT_ARM64_MOVE (X0, X19);
|
||||
EMIT_ARM64_LOAD (X8, free_library_impl);
|
||||
EMIT_ARM64_CALL (X8);
|
||||
|
||||
/* } */
|
||||
gum_arm64_writer_put_label (&cw, skip_unload);
|
||||
|
||||
/* result = ERROR_SUCCESS */
|
||||
EMIT_ARM64_MOVE (X0, XZR);
|
||||
gum_arm64_writer_put_b_label (&cw, return_result);
|
||||
|
||||
gum_arm64_writer_put_label (&cw, loadlibrary_failed);
|
||||
/* result = GetLastError() */
|
||||
EMIT_ARM64_LOAD (X8, get_last_error_impl);
|
||||
EMIT_ARM64_CALL (X8);
|
||||
|
||||
gum_arm64_writer_put_label (&cw, return_result);
|
||||
gum_arm64_writer_put_pop_reg_reg (&cw, ARM64_REG_X19, ARM64_REG_X20);
|
||||
gum_arm64_writer_put_pop_reg_reg (&cw, ARM64_REG_FP, ARM64_REG_LR);
|
||||
gum_arm64_writer_put_ret (&cw);
|
||||
|
||||
gum_arm64_writer_flush (&cw);
|
||||
code_size = gum_arm64_writer_offset (&cw);
|
||||
gum_arm64_writer_clear (&cw);
|
||||
#else
|
||||
GumX86Writer cw;
|
||||
|
||||
gum_x86_writer_init (&cw, code);
|
||||
|
||||
/* Will clobber these */
|
||||
@@ -393,80 +548,9 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
|
||||
gum_x86_writer_flush (&cw);
|
||||
code_size = gum_x86_writer_offset (&cw);
|
||||
gum_x86_writer_clear (&cw);
|
||||
#endif
|
||||
|
||||
memset (rwc, 0, sizeof (FridaRemoteWorkerContext));
|
||||
|
||||
gum_module_enumerate_exports ("kernel32.dll", frida_remote_worker_context_collect_kernel32_export, rwc);
|
||||
if (!frida_remote_worker_context_has_resolved_all_kernel32_functions (rwc))
|
||||
goto failed_to_resolve_kernel32_functions;
|
||||
|
||||
StringCbCopyW (rwc->dll_path, sizeof (rwc->dll_path), details->dll_path);
|
||||
StringCbCopyA (rwc->entrypoint_name, sizeof (rwc->entrypoint_name), details->entrypoint_name);
|
||||
StringCbCopyA (rwc->entrypoint_data, sizeof (rwc->entrypoint_data), details->entrypoint_data);
|
||||
|
||||
page_size = gum_query_page_size ();
|
||||
g_assert (code_size <= page_size);
|
||||
|
||||
alloc_size = page_size + sizeof (FridaRemoteWorkerContext);
|
||||
rwc->entrypoint = VirtualAllocEx (details->process_handle, NULL, alloc_size, MEM_COMMIT, PAGE_READWRITE);
|
||||
if (rwc->entrypoint == NULL)
|
||||
goto virtual_alloc_ex_failed;
|
||||
|
||||
if (!WriteProcessMemory (details->process_handle, rwc->entrypoint, code, code_size, NULL))
|
||||
goto write_process_memory_failed;
|
||||
|
||||
rwc->argument = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (rwc->entrypoint) + page_size);
|
||||
if (!WriteProcessMemory (details->process_handle, rwc->argument, rwc, sizeof (FridaRemoteWorkerContext), NULL))
|
||||
goto write_process_memory_failed;
|
||||
|
||||
if (!VirtualProtectEx (details->process_handle, rwc->entrypoint, page_size, PAGE_EXECUTE_READ, &old_protect))
|
||||
goto virtual_protect_ex_failed;
|
||||
|
||||
gum_free_pages (code);
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
failed_to_resolve_kernel32_functions:
|
||||
{
|
||||
g_set_error (error,
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unexpected error while resolving kernel32 functions");
|
||||
goto error_common;
|
||||
}
|
||||
virtual_alloc_ex_failed:
|
||||
{
|
||||
g_set_error (error,
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unexpected error allocating memory in target process (VirtualAllocEx returned 0x%08lx)",
|
||||
GetLastError ());
|
||||
goto error_common;
|
||||
}
|
||||
write_process_memory_failed:
|
||||
{
|
||||
g_set_error (error,
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unexpected error writing to memory in target process (WriteProcessMemory returned 0x%08lx)",
|
||||
GetLastError ());
|
||||
goto error_common;
|
||||
}
|
||||
virtual_protect_ex_failed:
|
||||
{
|
||||
g_set_error (error,
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unexpected error changing memory permission in target process (VirtualProtectEx returned 0x%08lx)",
|
||||
GetLastError ());
|
||||
goto error_common;
|
||||
}
|
||||
error_common:
|
||||
{
|
||||
frida_remote_worker_context_destroy (rwc, details);
|
||||
gum_free_pages (code);
|
||||
return FALSE;
|
||||
}
|
||||
return code_size;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -508,60 +592,6 @@ frida_remote_worker_context_collect_kernel32_export (const GumExportDetails * de
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
frida_windows_system_is_x64 (void)
|
||||
{
|
||||
static gboolean initialized = FALSE;
|
||||
static gboolean system_is_x64;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
SYSTEM_INFO si;
|
||||
|
||||
GetNativeSystemInfo (&si);
|
||||
system_is_x64 = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
|
||||
|
||||
initialized = TRUE;
|
||||
}
|
||||
|
||||
return system_is_x64;
|
||||
}
|
||||
|
||||
gboolean
|
||||
frida_windows_process_is_x64 (guint32 pid, GError ** error)
|
||||
{
|
||||
HANDLE process_handle;
|
||||
BOOL is_wow64, success;
|
||||
|
||||
if (!frida_windows_system_is_x64 ())
|
||||
return FALSE;
|
||||
|
||||
process_handle = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, pid);
|
||||
if (process_handle == NULL)
|
||||
goto open_failed;
|
||||
success = IsWow64Process (process_handle, &is_wow64);
|
||||
CloseHandle (process_handle);
|
||||
if (!success)
|
||||
goto query_failed;
|
||||
|
||||
return !is_wow64;
|
||||
|
||||
open_failed:
|
||||
{
|
||||
frida_propagate_open_process_error (pid, GetLastError (), error);
|
||||
return FALSE;
|
||||
}
|
||||
query_failed:
|
||||
{
|
||||
g_set_error (error,
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unexpected error while interrogating process with pid %u (IsWow64Process failed)",
|
||||
pid);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_file_exists_and_is_readable (const WCHAR * filename)
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Frida {
|
||||
|
||||
public async void inject_library_file (uint pid, PathTemplate path_template, string entrypoint, string data,
|
||||
string[] dependencies, uint id, Cancellable? cancellable) throws Error {
|
||||
string path = path_template.expand (WindowsProcess.is_x64 (pid) ? "64" : "32");
|
||||
string path = path_template.expand (arch_name_from_pid (pid));
|
||||
|
||||
string target_dependent_path;
|
||||
if (level == ELEVATED) {
|
||||
@@ -101,6 +101,32 @@ namespace Frida {
|
||||
protected extern static void _free_inject_instance (void * inject_instance, out bool is_resident);
|
||||
}
|
||||
|
||||
public unowned string arch_name_from_pid (uint pid) throws Error {
|
||||
switch (cpu_type_from_pid (pid)) {
|
||||
case Gum.CpuType.IA32:
|
||||
return "x86";
|
||||
case Gum.CpuType.AMD64:
|
||||
return "x86_64";
|
||||
case Gum.CpuType.ARM64:
|
||||
return "arm64";
|
||||
default:
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
public Gum.CpuType cpu_type_from_pid (uint pid) throws Error {
|
||||
try {
|
||||
return Gum.Windows.cpu_type_from_pid (pid);
|
||||
} catch (Gum.Error e) {
|
||||
if (e is Gum.Error.NOT_FOUND)
|
||||
throw new Error.PROCESS_NOT_FOUND ("Unable to find process with pid %u", pid);
|
||||
else if (e is Gum.Error.PERMISSION_DENIED)
|
||||
throw new Error.PERMISSION_DENIED ("Unable to access process with pid %u", pid);
|
||||
else
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
private class AssetDirectory {
|
||||
public File file {
|
||||
get;
|
||||
@@ -206,14 +232,6 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
namespace WindowsSystem {
|
||||
public extern static bool is_x64 ();
|
||||
}
|
||||
|
||||
namespace WindowsProcess {
|
||||
public extern static bool is_x64 (uint32 pid) throws Error;
|
||||
}
|
||||
|
||||
namespace WaitHandleSource {
|
||||
public extern Source create (void * handle, bool owns_handle);
|
||||
}
|
||||
|
||||
@@ -57,9 +57,7 @@ namespace Frida {
|
||||
string[] dependencies, uint id, Cancellable? cancellable) throws Error, IOError {
|
||||
bool permission_denied = false;
|
||||
try {
|
||||
unowned string local_arch = sizeof (void *) == 8 ? "64" : "32";
|
||||
unowned string remote_arch = WindowsProcess.is_x64 (pid) ? "64" : "32";
|
||||
if (remote_arch == local_arch) {
|
||||
if (cpu_type_from_pid (pid) == Gum.NATIVE_CPU) {
|
||||
yield inprocess_backend.inject_library_file (pid, path_template, entrypoint, data, dependencies,
|
||||
id, cancellable);
|
||||
return;
|
||||
@@ -172,15 +170,11 @@ namespace Frida {
|
||||
Error? error = null;
|
||||
|
||||
try {
|
||||
string native_helper_path = WindowsSystem.is_x64 ()
|
||||
? resource_store.helper64.path
|
||||
: resource_store.helper32.path;
|
||||
string level_str = (level == PrivilegeLevel.ELEVATED) ? "ELEVATED" : "NORMAL";
|
||||
void * process = spawn (native_helper_path,
|
||||
void * process = spawn (resource_store.native_helper.path,
|
||||
"MANAGER %s %s".printf (level_str, transport.remote_address),
|
||||
level);
|
||||
instance = new HelperInstance (resource_store.helper32, resource_store.helper64, transport,
|
||||
stream_request, process);
|
||||
instance = new HelperInstance (resource_store.helpers, transport, stream_request, process);
|
||||
} catch (Error e) {
|
||||
error = e;
|
||||
}
|
||||
@@ -247,8 +241,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private TemporaryFile helper32;
|
||||
private TemporaryFile helper64;
|
||||
private Gee.Collection<TemporaryFile> helpers;
|
||||
private PipeTransport transport;
|
||||
private Future<IOStream> stream_request;
|
||||
private DBusConnection connection;
|
||||
@@ -257,10 +250,9 @@ namespace Frida {
|
||||
|
||||
private Gee.Set<uint> injectee_ids = new Gee.HashSet<uint> ();
|
||||
|
||||
public HelperInstance (TemporaryFile helper32, TemporaryFile helper64, PipeTransport transport,
|
||||
Future<IOStream> stream_request, void * process) {
|
||||
this.helper32 = helper32;
|
||||
this.helper64 = helper64;
|
||||
public HelperInstance (Gee.Collection<TemporaryFile> helpers, PipeTransport transport, Future<IOStream> stream_request,
|
||||
void * process) {
|
||||
this.helpers = helpers;
|
||||
this.transport = transport;
|
||||
this.stream_request = stream_request;
|
||||
this.process = process;
|
||||
@@ -375,26 +367,43 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private class ResourceStore {
|
||||
public TemporaryFile helper32 {
|
||||
public TemporaryFile native_helper {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public TemporaryFile helper64 {
|
||||
public Gee.Collection<TemporaryFile> helpers {
|
||||
get;
|
||||
private set;
|
||||
default = new Gee.ArrayList<TemporaryFile> ();
|
||||
}
|
||||
|
||||
public ResourceStore (TemporaryDirectory tempdir) throws Error {
|
||||
var blob32 = Frida.Data.Helper.get_frida_helper_32_exe_blob ();
|
||||
helper32 = new TemporaryFile.from_stream ("frida-helper-32.exe",
|
||||
new MemoryInputStream.from_data (blob32.data, null),
|
||||
tempdir);
|
||||
var helper_arm64 = add_helper ("arm64", Frida.Data.Helper.get_frida_helper_arm64_exe_blob (), tempdir);
|
||||
var helper_x86_64 = add_helper ("x86_64", Frida.Data.Helper.get_frida_helper_x86_64_exe_blob (), tempdir);
|
||||
var helper_x86 = add_helper ("x86", Frida.Data.Helper.get_frida_helper_x86_exe_blob (), tempdir);
|
||||
|
||||
var blob64 = Frida.Data.Helper.get_frida_helper_64_exe_blob ();
|
||||
helper64 = new TemporaryFile.from_stream ("frida-helper-64.exe",
|
||||
new MemoryInputStream.from_data (blob64.data, null),
|
||||
switch (Gum.Windows.query_native_cpu_type ()) {
|
||||
case ARM64:
|
||||
native_helper = helper_arm64;
|
||||
break;
|
||||
case AMD64:
|
||||
native_helper = helper_x86_64;
|
||||
break;
|
||||
case IA32:
|
||||
native_helper = helper_x86;
|
||||
break;
|
||||
default:
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
private TemporaryFile add_helper (string name, Frida.Data.Helper.Blob blob, TemporaryDirectory tempdir) throws Error {
|
||||
var file = new TemporaryFile.from_stream (@"frida-helper-$name.exe",
|
||||
new MemoryInputStream.from_data (blob.data, null),
|
||||
tempdir);
|
||||
helpers.add (file);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#if GLIB_SIZEOF_VOID_P == 8
|
||||
# define FRIDA_HELPER_SERVICE_ARCH "64"
|
||||
#if defined (HAVE_ARM64)
|
||||
# define FRIDA_HELPER_SERVICE_ARCH "arm64"
|
||||
#elif GLIB_SIZEOF_VOID_P == 8
|
||||
# define FRIDA_HELPER_SERVICE_ARCH "x86_64"
|
||||
#else
|
||||
# define FRIDA_HELPER_SERVICE_ARCH "32"
|
||||
# define FRIDA_HELPER_SERVICE_ARCH "x86"
|
||||
#endif
|
||||
|
||||
#define STANDALONE_JOIN_TIMEOUT_MSEC (5 * 1000)
|
||||
@@ -17,25 +19,23 @@ struct _FridaServiceContext
|
||||
gchar * service_basename;
|
||||
|
||||
SC_HANDLE scm;
|
||||
SC_HANDLE service32;
|
||||
SC_HANDLE service64;
|
||||
|
||||
HANDLE standalone32;
|
||||
HANDLE standalone64;
|
||||
GQueue system_services;
|
||||
GQueue standalone_services;
|
||||
};
|
||||
|
||||
static void WINAPI frida_managed_helper_service_main (DWORD argc, WCHAR ** argv);
|
||||
static DWORD WINAPI frida_managed_helper_service_handle_control_code (DWORD control, DWORD event_type, void * event_data, void * context);
|
||||
static void frida_managed_helper_service_report_status (DWORD current_state, DWORD exit_code, DWORD wait_hint);
|
||||
|
||||
static gboolean frida_register_and_start_services (FridaServiceContext * self);
|
||||
static gboolean frida_register_and_start_services (FridaServiceContext * self, gchar ** archs, gint archs_length);
|
||||
static void frida_stop_and_unregister_services (FridaServiceContext * self);
|
||||
static gboolean frida_spawn_standalone_services (FridaServiceContext * self);
|
||||
static gboolean frida_spawn_standalone_services (FridaServiceContext * self, gchar ** archs, gint archs_length);
|
||||
static gboolean frida_join_standalone_services (FridaServiceContext * self);
|
||||
static void frida_kill_standalone_services (FridaServiceContext * self);
|
||||
static void frida_release_standalone_services (FridaServiceContext * self);
|
||||
|
||||
static gboolean frida_register_services (FridaServiceContext * self);
|
||||
static gboolean frida_register_services (FridaServiceContext * self, gchar ** archs, gint archs_length);
|
||||
static gboolean frida_unregister_services (FridaServiceContext * self);
|
||||
static gboolean frida_start_services (FridaServiceContext * self);
|
||||
static gboolean frida_stop_services (FridaServiceContext * self);
|
||||
@@ -59,7 +59,7 @@ static WCHAR * frida_managed_helper_service_name = NULL;
|
||||
static SERVICE_STATUS_HANDLE frida_managed_helper_service_status_handle = NULL;
|
||||
|
||||
void *
|
||||
frida_helper_manager_start_services (const char * service_basename, FridaPrivilegeLevel level)
|
||||
frida_helper_manager_start_services (const char * service_basename, gchar ** archs, gint archs_length, FridaPrivilegeLevel level)
|
||||
{
|
||||
FridaServiceContext * self;
|
||||
|
||||
@@ -72,7 +72,7 @@ frida_helper_manager_start_services (const char * service_basename, FridaPrivile
|
||||
{
|
||||
frida_unregister_stale_services (self);
|
||||
|
||||
if (!frida_register_and_start_services (self))
|
||||
if (!frida_register_and_start_services (self, archs, archs_length))
|
||||
{
|
||||
CloseServiceHandle (self->scm);
|
||||
self->scm = NULL;
|
||||
@@ -81,7 +81,7 @@ frida_helper_manager_start_services (const char * service_basename, FridaPrivile
|
||||
|
||||
if (self->scm == NULL)
|
||||
{
|
||||
if (!frida_spawn_standalone_services (self))
|
||||
if (!frida_spawn_standalone_services (self, archs, archs_length))
|
||||
{
|
||||
frida_service_context_free (self);
|
||||
self = NULL;
|
||||
@@ -138,15 +138,16 @@ char *
|
||||
frida_helper_service_derive_filename_for_suffix (const char * suffix)
|
||||
{
|
||||
WCHAR filename_utf16[MAX_PATH + 1] = { 0, };
|
||||
gchar * name, * tmp;
|
||||
gchar * name, * tail, * tmp;
|
||||
glong len;
|
||||
|
||||
GetModuleFileNameW (NULL, filename_utf16, MAX_PATH);
|
||||
|
||||
name = g_utf16_to_utf8 (filename_utf16, -1, NULL, &len, NULL);
|
||||
if (g_str_has_suffix (name, "-32.exe") || g_str_has_suffix (name, "-64.exe"))
|
||||
tail = strrchr (name, '-');
|
||||
if (tail != NULL)
|
||||
{
|
||||
name[len - 6] = '\0';
|
||||
tail[1] = '\0';
|
||||
tmp = g_strconcat (name, suffix, ".exe", NULL);
|
||||
g_free (name);
|
||||
name = tmp;
|
||||
@@ -293,9 +294,9 @@ frida_managed_helper_service_report_status (DWORD current_state, DWORD exit_code
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_register_and_start_services (FridaServiceContext * self)
|
||||
frida_register_and_start_services (FridaServiceContext * self, gchar ** archs, gint archs_length)
|
||||
{
|
||||
if (!frida_register_services (self))
|
||||
if (!frida_register_services (self, archs, archs_length))
|
||||
return FALSE;
|
||||
|
||||
if (!frida_start_services (self))
|
||||
@@ -315,44 +316,35 @@ frida_stop_and_unregister_services (FridaServiceContext * self)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_spawn_standalone_services (FridaServiceContext * self)
|
||||
frida_spawn_standalone_services (FridaServiceContext * self, gchar ** archs, gint archs_length)
|
||||
{
|
||||
HANDLE standalone32, standalone64;
|
||||
gint i;
|
||||
|
||||
standalone32 = frida_spawn_standalone_service (self, "32");
|
||||
if (standalone32 == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (frida_windows_system_is_x64 ())
|
||||
for (i = 0; i != archs_length; i++)
|
||||
{
|
||||
standalone64 = frida_spawn_standalone_service (self, "64");
|
||||
if (standalone64 == NULL)
|
||||
{
|
||||
frida_kill_standalone_service (self, standalone32);
|
||||
CloseHandle (standalone32);
|
||||
return FALSE;
|
||||
}
|
||||
HANDLE service = frida_spawn_standalone_service (self, archs[i]);
|
||||
if (service == NULL)
|
||||
goto unable_to_spawn;
|
||||
g_queue_push_tail (&self->standalone_services, service);
|
||||
}
|
||||
else
|
||||
{
|
||||
standalone64 = NULL;
|
||||
}
|
||||
|
||||
self->standalone32 = standalone32;
|
||||
self->standalone64 = standalone64;
|
||||
|
||||
return TRUE;
|
||||
|
||||
unable_to_spawn:
|
||||
{
|
||||
frida_kill_standalone_services (self);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_join_standalone_services (FridaServiceContext * self)
|
||||
{
|
||||
gboolean success = TRUE;
|
||||
GList * cur;
|
||||
|
||||
if (frida_windows_system_is_x64 ())
|
||||
success &= frida_join_standalone_service (self, self->standalone64);
|
||||
|
||||
success &= frida_join_standalone_service (self, self->standalone32);
|
||||
for (cur = self->standalone_services.head; cur != NULL; cur = cur->next)
|
||||
success &= frida_join_standalone_service (self, cur->data);
|
||||
|
||||
if (success)
|
||||
frida_release_standalone_services (self);
|
||||
@@ -363,10 +355,10 @@ frida_join_standalone_services (FridaServiceContext * self)
|
||||
static void
|
||||
frida_kill_standalone_services (FridaServiceContext * self)
|
||||
{
|
||||
if (frida_windows_system_is_x64 ())
|
||||
frida_kill_standalone_service (self, self->standalone64);
|
||||
GList * cur;
|
||||
|
||||
frida_kill_standalone_service (self, self->standalone32);
|
||||
for (cur = self->standalone_services.head; cur != NULL; cur = cur->next)
|
||||
frida_kill_standalone_service (self, cur->data);
|
||||
|
||||
frida_release_standalone_services (self);
|
||||
}
|
||||
@@ -374,94 +366,77 @@ frida_kill_standalone_services (FridaServiceContext * self)
|
||||
static void
|
||||
frida_release_standalone_services (FridaServiceContext * self)
|
||||
{
|
||||
if (frida_windows_system_is_x64 ())
|
||||
{
|
||||
g_assert (self->standalone64 != NULL);
|
||||
CloseHandle (self->standalone64);
|
||||
self->standalone64 = NULL;
|
||||
}
|
||||
HANDLE service;
|
||||
|
||||
g_assert (self->standalone32 != NULL);
|
||||
CloseHandle (self->standalone32);
|
||||
self->standalone32 = NULL;
|
||||
while ((service = g_queue_pop_tail (&self->standalone_services)) != NULL)
|
||||
CloseHandle (service);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_register_services (FridaServiceContext * self)
|
||||
frida_register_services (FridaServiceContext * self, gchar ** archs, gint archs_length)
|
||||
{
|
||||
SC_HANDLE service32, service64;
|
||||
gint i;
|
||||
|
||||
service32 = frida_register_service (self, "32");
|
||||
if (service32 == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (frida_windows_system_is_x64 ())
|
||||
for (i = 0; i != archs_length; i++)
|
||||
{
|
||||
service64 = frida_register_service (self, "64");
|
||||
if (service64 == NULL)
|
||||
{
|
||||
frida_unregister_service (self, service32);
|
||||
CloseServiceHandle (service32);
|
||||
return FALSE;
|
||||
}
|
||||
SC_HANDLE service = frida_register_service (self, archs[i]);
|
||||
if (service == NULL)
|
||||
goto unable_to_register;
|
||||
g_queue_push_tail (&self->system_services, service);
|
||||
}
|
||||
else
|
||||
{
|
||||
service64 = NULL;
|
||||
}
|
||||
|
||||
self->service32 = service32;
|
||||
self->service64 = service64;
|
||||
|
||||
return TRUE;
|
||||
|
||||
unable_to_register:
|
||||
{
|
||||
frida_unregister_services (self);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_unregister_services (FridaServiceContext * self)
|
||||
{
|
||||
gboolean success = TRUE;
|
||||
SC_HANDLE service;
|
||||
|
||||
if (frida_windows_system_is_x64 ())
|
||||
while ((service = g_queue_pop_tail (&self->system_services)) != NULL)
|
||||
{
|
||||
success &= frida_unregister_service (self, self->service64);
|
||||
CloseServiceHandle (self->service64);
|
||||
self->service64 = NULL;
|
||||
success &= frida_unregister_service (self, service);
|
||||
CloseServiceHandle (service);
|
||||
}
|
||||
|
||||
success &= frida_unregister_service (self, self->service32);
|
||||
CloseServiceHandle (self->service32);
|
||||
self->service32 = NULL;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_start_services (FridaServiceContext * self)
|
||||
{
|
||||
if (!frida_start_service (self, self->service32))
|
||||
return FALSE;
|
||||
GList * cur;
|
||||
|
||||
if (frida_windows_system_is_x64 ())
|
||||
for (cur = self->system_services.head; cur != NULL; cur = cur->next)
|
||||
{
|
||||
if (!frida_start_service (self, self->service64))
|
||||
{
|
||||
frida_stop_service (self, self->service32);
|
||||
return FALSE;
|
||||
}
|
||||
if (!frida_start_service (self, cur->data))
|
||||
goto unable_to_start;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
unable_to_start:
|
||||
{
|
||||
frida_stop_services (self);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_stop_services (FridaServiceContext * self)
|
||||
{
|
||||
gboolean success = TRUE;
|
||||
GList * cur;
|
||||
|
||||
if (frida_windows_system_is_x64 ())
|
||||
success &= frida_stop_service (self, self->service64);
|
||||
|
||||
success &= frida_stop_service (self, self->service32);
|
||||
for (cur = self->system_services.head; cur != NULL; cur = cur->next)
|
||||
success &= frida_stop_service (self, cur->data);
|
||||
|
||||
return success;
|
||||
}
|
||||
@@ -480,7 +455,7 @@ frida_register_service (FridaServiceContext * self, const gchar * suffix)
|
||||
servicename_utf8 = g_strconcat (self->service_basename, suffix, NULL);
|
||||
servicename = g_utf8_to_utf16 (servicename_utf8, -1, NULL, NULL, NULL);
|
||||
|
||||
displayname_utf8 = g_strdup_printf ("Frida %s-bit helper (%s)", suffix, servicename_utf8);
|
||||
displayname_utf8 = g_strdup_printf ("Frida %s helper (%s)", suffix, servicename_utf8);
|
||||
displayname = g_utf8_to_utf16 (displayname_utf8, -1, NULL, NULL, NULL);
|
||||
|
||||
filename_utf8 = frida_helper_service_derive_filename_for_suffix (suffix);
|
||||
@@ -714,6 +689,7 @@ frida_service_context_new (const gchar * service_basename)
|
||||
|
||||
self = g_slice_new0 (FridaServiceContext);
|
||||
self->service_basename = g_strdup (service_basename);
|
||||
g_queue_init (&self->standalone_services);
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -721,11 +697,8 @@ frida_service_context_new (const gchar * service_basename)
|
||||
static void
|
||||
frida_service_context_free (FridaServiceContext * self)
|
||||
{
|
||||
g_assert (self->standalone64 == NULL);
|
||||
g_assert (self->standalone32 == NULL);
|
||||
|
||||
g_assert (self->service64 == NULL);
|
||||
g_assert (self->service32 == NULL);
|
||||
g_assert (g_queue_is_empty (&self->system_services));
|
||||
g_assert (g_queue_is_empty (&self->standalone_services));
|
||||
|
||||
if (self->scm != NULL)
|
||||
CloseServiceHandle (self->scm);
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
G_GNUC_INTERNAL void * frida_helper_manager_start_services (const char * service_basename, FridaPrivilegeLevel level);
|
||||
G_GNUC_INTERNAL void * frida_helper_manager_start_services (const char * service_basename, gchar ** archs, gint archs_length,
|
||||
FridaPrivilegeLevel level);
|
||||
G_GNUC_INTERNAL void frida_helper_manager_stop_services (void * context);
|
||||
|
||||
G_GNUC_INTERNAL char * frida_helper_service_derive_basename (void);
|
||||
|
||||
@@ -60,8 +60,7 @@ namespace Frida {
|
||||
|
||||
private DBusConnection connection;
|
||||
private uint registration_id;
|
||||
private ServiceConnection helper32;
|
||||
private ServiceConnection? helper64;
|
||||
private Gee.Collection<ServiceConnection> helpers = new Gee.ArrayList<ServiceConnection> ();
|
||||
private void * context;
|
||||
|
||||
public HelperManager (string parent_address, PrivilegeLevel level) {
|
||||
@@ -98,18 +97,23 @@ namespace Frida {
|
||||
|
||||
private async void start () {
|
||||
try {
|
||||
helper32 = new ServiceConnection (HelperService.derive_svcname_for_suffix ("32"));
|
||||
if (WindowsSystem.is_x64 ())
|
||||
helper64 = new ServiceConnection (HelperService.derive_svcname_for_suffix ("64"));
|
||||
var archs = new Gee.ArrayList<string> ();
|
||||
if (Gum.Windows.query_native_cpu_type () == ARM64)
|
||||
archs.add ("arm64");
|
||||
if (Gum.Windows.query_native_cpu_type () != IA32)
|
||||
archs.add ("x86_64");
|
||||
archs.add ("x86");
|
||||
|
||||
context = start_services (HelperService.derive_basename (), level);
|
||||
foreach (string arch in archs) {
|
||||
var helper = new ServiceConnection (HelperService.derive_svcname_for_suffix (arch));
|
||||
helpers.add (helper);
|
||||
}
|
||||
|
||||
yield helper32.open ();
|
||||
helper32.proxy.uninjected.connect (on_uninjected);
|
||||
context = start_services (HelperService.derive_basename (), archs.to_array (), level);
|
||||
|
||||
if (helper64 != null) {
|
||||
yield helper64.open ();
|
||||
helper64.proxy.uninjected.connect (on_uninjected);
|
||||
foreach (var helper in helpers) {
|
||||
yield helper.open ();
|
||||
helper.proxy.uninjected.connect (on_uninjected);
|
||||
}
|
||||
|
||||
var stream_request = Pipe.open (parent_address, null);
|
||||
@@ -130,20 +134,14 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws Error, IOError {
|
||||
if (helper64 != null) {
|
||||
foreach (var helper in helpers) {
|
||||
try {
|
||||
yield helper64.proxy.stop (cancellable);
|
||||
yield helper.proxy.stop (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CANCELLED)
|
||||
throw (IOError) e;
|
||||
}
|
||||
}
|
||||
try {
|
||||
yield helper32.proxy.stop (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CANCELLED)
|
||||
throw (IOError) e;
|
||||
}
|
||||
|
||||
Timeout.add (20, () => {
|
||||
shutdown.begin ();
|
||||
@@ -158,13 +156,14 @@ namespace Frida {
|
||||
public async void inject_library_file (uint pid, PathTemplate path_template, string entrypoint, string data,
|
||||
string[] dependencies, uint id, Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
if (helper64 != null && yield helper64.proxy.can_handle_target (pid, cancellable)) {
|
||||
yield helper64.proxy.inject_library_file (pid, path_template, entrypoint, data, dependencies, id,
|
||||
cancellable);
|
||||
} else {
|
||||
yield helper32.proxy.inject_library_file (pid, path_template, entrypoint, data, dependencies, id,
|
||||
cancellable);
|
||||
foreach (var helper in helpers) {
|
||||
if (yield helper.proxy.can_handle_target (pid, cancellable)) {
|
||||
yield helper.proxy.inject_library_file (pid, path_template, entrypoint, data, dependencies,
|
||||
id, cancellable);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new Error.NOT_SUPPORTED ("Missing helper able to handle the given target");
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
@@ -210,7 +209,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private extern static void * start_services (string service_basename, PrivilegeLevel level);
|
||||
private extern static void * start_services (string service_basename, string[] archs, PrivilegeLevel level);
|
||||
private extern static void stop_services (void * context);
|
||||
}
|
||||
|
||||
@@ -286,9 +285,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async bool can_handle_target (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
uint local_arch = sizeof (void *) == 8 ? 64 : 32;
|
||||
uint remote_arch = WindowsProcess.is_x64 (pid) ? 64 : 32;
|
||||
return local_arch == remote_arch;
|
||||
return cpu_type_from_pid (pid) == Gum.NATIVE_CPU;
|
||||
}
|
||||
|
||||
public async void inject_library_file (uint pid, PathTemplate path_template, string entrypoint, string data,
|
||||
|
||||
@@ -113,21 +113,23 @@ namespace Frida {
|
||||
injector = new Winjector (helper, false, tempdir);
|
||||
injector.uninjected.connect (on_uninjected);
|
||||
|
||||
var blob32 = Frida.Data.Agent.get_frida_agent_32_dll_blob ();
|
||||
var blob64 = Frida.Data.Agent.get_frida_agent_64_dll_blob ();
|
||||
var dbghelp32 = Frida.Data.Agent.get_dbghelp_32_dll_blob ();
|
||||
var dbghelp64 = Frida.Data.Agent.get_dbghelp_64_dll_blob ();
|
||||
var symsrv32 = Frida.Data.Agent.get_symsrv_32_dll_blob ();
|
||||
var symsrv64 = Frida.Data.Agent.get_symsrv_64_dll_blob ();
|
||||
|
||||
agent = new AgentDescriptor (PathTemplate ("<arch>\\frida-agent.dll"),
|
||||
new Bytes.static (blob32.data),
|
||||
new Bytes.static (blob64.data),
|
||||
new Bytes.static (Frida.Data.Agent.get_frida_agent_arm64_dll_blob ().data),
|
||||
new Bytes.static (Frida.Data.Agent.get_frida_agent_x86_64_dll_blob ().data),
|
||||
new Bytes.static (Frida.Data.Agent.get_frida_agent_x86_dll_blob ().data),
|
||||
new AgentResource[] {
|
||||
new AgentResource ("32\\dbghelp.dll", new Bytes.static (dbghelp32.data), tempdir),
|
||||
new AgentResource ("32\\symsrv.dll", new Bytes.static (symsrv32.data), tempdir),
|
||||
new AgentResource ("64\\dbghelp.dll", new Bytes.static (dbghelp64.data), tempdir),
|
||||
new AgentResource ("64\\symsrv.dll", new Bytes.static (symsrv64.data), tempdir)
|
||||
new AgentResource ("arm64\\dbghelp.dll",
|
||||
new Bytes.static (Frida.Data.Agent.get_dbghelp_arm64_dll_blob ().data), tempdir),
|
||||
new AgentResource ("arm64\\symsrv.dll",
|
||||
new Bytes.static (Frida.Data.Agent.get_symsrv_arm64_dll_blob ().data), tempdir),
|
||||
new AgentResource ("x86_64\\dbghelp.dll",
|
||||
new Bytes.static (Frida.Data.Agent.get_dbghelp_x86_64_dll_blob ().data), tempdir),
|
||||
new AgentResource ("x86_64\\symsrv.dll",
|
||||
new Bytes.static (Frida.Data.Agent.get_symsrv_x86_64_dll_blob ().data), tempdir),
|
||||
new AgentResource ("x86\\dbghelp.dll",
|
||||
new Bytes.static (Frida.Data.Agent.get_dbghelp_x86_dll_blob ().data), tempdir),
|
||||
new AgentResource ("x86\\symsrv.dll",
|
||||
new Bytes.static (Frida.Data.Agent.get_symsrv_x86_dll_blob ().data), tempdir),
|
||||
},
|
||||
tempdir
|
||||
);
|
||||
@@ -164,7 +166,23 @@ namespace Frida {
|
||||
protected override async AgentSessionProvider create_system_session_provider (Cancellable? cancellable,
|
||||
out DBusConnection connection) throws Error, IOError {
|
||||
var path_template = agent.get_path_template ();
|
||||
var agent_path = path_template.expand (sizeof (void *) == 8 ? "64" : "32");
|
||||
|
||||
unowned string arch;
|
||||
switch (Gum.NATIVE_CPU) {
|
||||
case ARM64:
|
||||
arch = "arm64";
|
||||
break;
|
||||
case AMD64:
|
||||
arch = "x86_64";
|
||||
break;
|
||||
case IA32:
|
||||
arch = "x86";
|
||||
break;
|
||||
default:
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
var agent_path = path_template.expand (arch);
|
||||
|
||||
system_session_container = yield AgentContainer.create (agent_path, cancellable);
|
||||
|
||||
|
||||
@@ -142,11 +142,12 @@ namespace Frida {
|
||||
|
||||
private PathTemplate? cached_path_template;
|
||||
|
||||
public AgentDescriptor (PathTemplate name_template, Bytes dll32, Bytes dll64, AgentResource[] dependencies,
|
||||
TemporaryDirectory? tempdir = null) {
|
||||
public AgentDescriptor (PathTemplate name_template, Bytes dll_arm64, Bytes dll_x86_64, Bytes dll_x86,
|
||||
AgentResource[] dependencies, TemporaryDirectory? tempdir = null) {
|
||||
var agents = new Gee.ArrayList<AgentResource> ();
|
||||
agents.add (new AgentResource (name_template.expand ("32"), dll32, tempdir));
|
||||
agents.add (new AgentResource (name_template.expand ("64"), dll64, tempdir));
|
||||
agents.add (new AgentResource (name_template.expand ("arm64"), dll_arm64, tempdir));
|
||||
agents.add (new AgentResource (name_template.expand ("x86_64"), dll_x86_64, tempdir));
|
||||
agents.add (new AgentResource (name_template.expand ("x86"), dll_x86, tempdir));
|
||||
|
||||
Object (
|
||||
name_template: name_template,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 16.3.2
|
||||
revision = 16.5.7
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/libusb.git
|
||||
revision = ffff4bdfe8faa38cecfad5aab106cae923502d55
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
dependency_names = libusb-1.0
|
||||
|
||||
@@ -72,10 +72,8 @@ frida_test_process_backend_filename_of (void * handle)
|
||||
image_count = _dyld_image_count ();
|
||||
for (image_idx = 0; image_idx != image_count; image_idx++)
|
||||
{
|
||||
const gchar * image_path = _dyld_get_image_name (image_idx);
|
||||
|
||||
if (g_str_has_suffix (image_path, "/frida-tests"))
|
||||
return g_strdup (image_path);
|
||||
if (_dyld_get_image_header (image_idx)->filetype == MH_EXECUTE)
|
||||
return g_strdup (_dyld_get_image_name (image_idx));
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
|
||||
+152
-31
@@ -61,8 +61,13 @@ namespace Frida.HostSessionTest {
|
||||
h.run ();
|
||||
});
|
||||
|
||||
GLib.Test.add_func ("/HostSession/Fruity/Manual/xpc", () => {
|
||||
var h = new Harness ((h) => Fruity.Manual.xpc.begin (h as Harness));
|
||||
GLib.Test.add_func ("/HostSession/Fruity/Manual/Xpc/list", () => {
|
||||
var h = new Harness ((h) => Fruity.Manual.Xpc.list.begin (h as Harness));
|
||||
h.run ();
|
||||
});
|
||||
|
||||
GLib.Test.add_func ("/HostSession/Fruity/Manual/Xpc/launch", () => {
|
||||
var h = new Harness ((h) => Fruity.Manual.Xpc.launch.begin (h as Harness));
|
||||
h.run ();
|
||||
});
|
||||
#endif
|
||||
@@ -3632,6 +3637,9 @@ namespace Frida.HostSessionTest {
|
||||
|
||||
namespace Manual {
|
||||
|
||||
private const string DEVICE_ID = "<device-id>";
|
||||
private const string APP_ID = "<app-id>";
|
||||
|
||||
private static async void lockdown (Harness h) {
|
||||
if (!GLib.Test.slow ()) {
|
||||
stdout.printf ("<skipping, run in slow mode with iOS device connected> ");
|
||||
@@ -3641,15 +3649,13 @@ namespace Frida.HostSessionTest {
|
||||
|
||||
h.disable_timeout ();
|
||||
|
||||
var device_id = "<device-id>";
|
||||
var app_id = "<app-id>";
|
||||
string? target_name = null;
|
||||
uint target_pid = 0;
|
||||
|
||||
var device_manager = new DeviceManager ();
|
||||
|
||||
try {
|
||||
var device = yield device_manager.get_device_by_id (device_id);
|
||||
var device = yield device_manager.get_device_by_id (DEVICE_ID);
|
||||
|
||||
device.output.connect ((pid, fd, data) => {
|
||||
var chars = data.get_data ();
|
||||
@@ -3669,9 +3675,21 @@ namespace Frida.HostSessionTest {
|
||||
|
||||
var timer = new Timer ();
|
||||
|
||||
printerr ("device.query_system_parameters()");
|
||||
timer.reset ();
|
||||
var p = yield device.query_system_parameters ();
|
||||
printerr (" => got parameters, took %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
var iter = HashTableIter<string, Variant> (p);
|
||||
string k;
|
||||
Variant v;
|
||||
while (iter.next (out k, out v))
|
||||
printerr ("%s: %s\n", k, v.print (false));
|
||||
|
||||
printerr ("device.enumerate_applications()");
|
||||
timer.reset ();
|
||||
var apps = yield device.enumerate_applications ();
|
||||
var opts = new ApplicationQueryOptions ();
|
||||
opts.scope = FULL;
|
||||
var apps = yield device.enumerate_applications (opts);
|
||||
printerr (" => got %d apps, took %u ms\n", apps.size (), (uint) (timer.elapsed () * 1000.0));
|
||||
if (GLib.Test.verbose ()) {
|
||||
var length = apps.size ();
|
||||
@@ -3694,7 +3712,7 @@ namespace Frida.HostSessionTest {
|
||||
} else {
|
||||
printerr ("device.spawn()");
|
||||
timer.reset ();
|
||||
pid = yield device.spawn (app_id);
|
||||
pid = yield device.spawn (APP_ID);
|
||||
printerr (" => pid=%u, took %u ms\n", pid, (uint) (timer.elapsed () * 1000.0));
|
||||
}
|
||||
|
||||
@@ -3744,42 +3762,145 @@ namespace Frida.HostSessionTest {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
yield device_manager.close ();
|
||||
} catch (IOError e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
h.done ();
|
||||
}
|
||||
|
||||
private static async void xpc (Harness h) {
|
||||
if (!GLib.Test.slow ()) {
|
||||
stdout.printf ("<skipping, run in slow mode with iOS device connected> ");
|
||||
namespace Xpc {
|
||||
|
||||
private static async void list (Harness h) {
|
||||
if (!GLib.Test.slow ()) {
|
||||
stdout.printf ("<skipping, run in slow mode with iOS device connected> ");
|
||||
h.done ();
|
||||
return;
|
||||
}
|
||||
|
||||
var device_manager = new DeviceManager ();
|
||||
|
||||
try {
|
||||
var timer = new Timer ();
|
||||
var device = yield device_manager.get_device_by_id (DEVICE_ID);
|
||||
printerr ("[*] Got device in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
|
||||
timer.reset ();
|
||||
var appservice = yield device.open_service ("xpc:com.apple.coredevice.appservice");
|
||||
printerr ("[*] Opened service in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
|
||||
var parameters = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
parameters["CoreDevice.featureIdentifier"] = "com.apple.coredevice.feature.listprocesses";
|
||||
parameters["CoreDevice.action"] = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
parameters["CoreDevice.input"] = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
timer.reset ();
|
||||
var response = yield appservice.request (parameters);
|
||||
printerr ("[*] Made request in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
printerr ("Got response: %s\n", response.print (true));
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
yield;
|
||||
}
|
||||
|
||||
h.done ();
|
||||
return;
|
||||
}
|
||||
|
||||
var device_id = "<device-id>";
|
||||
private static async void launch (Harness h) {
|
||||
if (!GLib.Test.slow ()) {
|
||||
stdout.printf ("<skipping, run in slow mode with iOS device connected> ");
|
||||
h.done ();
|
||||
return;
|
||||
}
|
||||
|
||||
var device_manager = new DeviceManager ();
|
||||
var device_manager = new DeviceManager ();
|
||||
|
||||
try {
|
||||
var timer = new Timer ();
|
||||
var device = yield device_manager.get_device_by_id (device_id);
|
||||
printerr ("[*] Got device in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
try {
|
||||
var timer = new Timer ();
|
||||
var device = yield device_manager.get_device_by_id (DEVICE_ID);
|
||||
printerr ("[*] Got device in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
|
||||
timer.reset ();
|
||||
var appservice = yield device.open_service ("xpc:com.apple.coredevice.appservice");
|
||||
printerr ("[*] Opened service in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
timer.reset ();
|
||||
var appservice = yield device.open_service ("xpc:com.apple.coredevice.appservice");
|
||||
printerr ("[*] Opened service in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
|
||||
var parameters = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
parameters["CoreDevice.featureIdentifier"] = "com.apple.coredevice.feature.listprocesses";
|
||||
parameters["CoreDevice.action"] = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
parameters["CoreDevice.input"] = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
timer.reset ();
|
||||
var response = yield appservice.request (parameters);
|
||||
printerr ("[*] Made request in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
printerr ("Got response: %s\n", response.print (true));
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
var stdio_stream = yield device.open_channel ("tcp:com.apple.coredevice.openstdiosocket");
|
||||
uint8 stdio_uuid[16];
|
||||
size_t n;
|
||||
yield stdio_stream.get_input_stream ().read_all_async (stdio_uuid, Priority.DEFAULT, null, out n);
|
||||
|
||||
var parameters = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
parameters["CoreDevice.featureIdentifier"] = "com.apple.coredevice.feature.launchapplication";
|
||||
parameters["CoreDevice.action"] = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
|
||||
var standard_input = Variant.new_from_data<void> (new VariantType ("ay"), stdio_uuid, true);
|
||||
var standard_output = standard_input;
|
||||
var standard_error = standard_output;
|
||||
var input = new Variant.parsed (@"""{
|
||||
'applicationSpecifier': <{
|
||||
'bundleIdentifier': <{
|
||||
'_0': <'$APP_ID'>
|
||||
}>
|
||||
}>,
|
||||
'options': <{
|
||||
'arguments': <@av []>,
|
||||
'environmentVariables': <@a{sv} {}>,
|
||||
'standardIOUsesPseudoterminals': <true>,
|
||||
'startStopped': <false>,
|
||||
'terminateExisting': <true>,
|
||||
'user': <{
|
||||
'active': <true>
|
||||
}>,
|
||||
'platformSpecificOptions': <
|
||||
b'<?xml version="1.0" encoding="UTF-8"?><plist version="1.0"><dict/></plist>'
|
||||
>
|
||||
}>,
|
||||
'standardIOIdentifiers': <{
|
||||
'standardInput': <('uuid', %@ay)>,
|
||||
'standardOutput': <('uuid', %@ay)>,
|
||||
'standardError': <('uuid', %@ay)>
|
||||
}>
|
||||
}""",
|
||||
standard_input,
|
||||
standard_output,
|
||||
standard_error);
|
||||
printerr ("input: %s\n", input.print (true));
|
||||
parameters["CoreDevice.input"] = input;
|
||||
timer.reset ();
|
||||
var response = yield appservice.request (parameters);
|
||||
printerr ("[*] Made request in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
printerr ("Got response: %s\n", response.print (true));
|
||||
|
||||
process_stdio.begin (stdio_stream);
|
||||
|
||||
// Wait forever...
|
||||
h.disable_timeout ();
|
||||
yield;
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
yield;
|
||||
}
|
||||
|
||||
h.done ();
|
||||
}
|
||||
|
||||
private async void process_stdio (IOStream stream) {
|
||||
try {
|
||||
var input = new DataInputStream (stream.get_input_stream ());
|
||||
while (true) {
|
||||
var line = yield input.read_line_async ();
|
||||
if (line == null) {
|
||||
printerr ("process_stdio: EOF\n");
|
||||
break;
|
||||
}
|
||||
printerr ("process_stdio got line: %s\n", line);
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("process_stdio: %s\n", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
h.done ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user