mirror of
https://github.com/frida/frida-core.git
synced 2026-11-09 10:09:38 -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:
|
||||
|
||||
+48
-4
@@ -14,7 +14,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 +140,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 +176,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 +272,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 +315,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 +376,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)
|
||||
@@ -589,6 +622,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 +655,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'
|
||||
|
||||
|
||||
+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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +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 (uint8[] val);
|
||||
public abstract unowned ObjectBuilder add_raw_value (Bytes val);
|
||||
|
||||
public abstract Bytes build ();
|
||||
@@ -238,6 +239,10 @@ namespace Frida {
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_uuid_value (uint8[] val) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_raw_value (Bytes val) {
|
||||
string uuid = Uuid.string_random ();
|
||||
builder.add_string_value (uuid);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -30,11 +30,16 @@ public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
|
||||
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);
|
||||
handle_interface_changes (store.copy_key_list (pattern));
|
||||
|
||||
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,
|
||||
|
||||
+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: 54cc29651c...f1f6586b6a.
@@ -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);
|
||||
|
||||
+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)
|
||||
@@ -35,209 +35,232 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
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 Resolved.Manager? resolved;
|
||||
private Source? timer;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
construct {
|
||||
start.begin ();
|
||||
}
|
||||
private const size_t IF_NAMESIZE = 16;
|
||||
|
||||
private async void start () {
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
try {
|
||||
connection = yield GLib.Bus.get (BusType.SYSTEM, io_cancellable);
|
||||
var connection = yield GLib.Bus.get (BusType.SYSTEM, cancellable);
|
||||
|
||||
server = yield connection.get_proxy (AVAHI_SERVICE_NAME, "/", DO_NOT_LOAD_PROPERTIES, io_cancellable);
|
||||
resolved = yield connection.get_proxy (Resolved.SERVICE_NAME, Resolved.SERVICE_PATH, DO_NOT_LOAD_PROPERTIES,
|
||||
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);
|
||||
try {
|
||||
yield resolve_services (MDNS_IPV6 | NO_NETWORK, cancellable);
|
||||
schedule_next_poll ();
|
||||
} catch (Error e) {
|
||||
handle_poll_timer_tick.begin ();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
if (timer != null) {
|
||||
timer.destroy ();
|
||||
timer = null;
|
||||
}
|
||||
|
||||
return yield promise.future.wait_async (cancellable);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxPairingServiceHost : Object, PairingServiceHost {
|
||||
public string name {
|
||||
get { return _name; }
|
||||
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;
|
||||
}
|
||||
|
||||
public InetSocketAddress address {
|
||||
get { return _address; }
|
||||
[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 uint16 port {
|
||||
get { return _port; }
|
||||
public struct RRItem {
|
||||
public int32 ifindex;
|
||||
public uint16 klass;
|
||||
public uint16 type;
|
||||
public uint8[] data;
|
||||
}
|
||||
|
||||
public Gee.List<string> txt_record {
|
||||
get { return _txt_record; }
|
||||
public struct SrvItem {
|
||||
public uint16 priority;
|
||||
public uint16 weight;
|
||||
public uint16 port;
|
||||
public string name;
|
||||
public SrvAddress[] addresses;
|
||||
public string canonical_name;
|
||||
}
|
||||
|
||||
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 struct SrvAddress {
|
||||
public int32 ifindex;
|
||||
public int32 family;
|
||||
public uint8[] ip;
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
|
||||
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);
|
||||
|
||||
@@ -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,8 +90,108 @@ 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_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)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+546
-201
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,
|
||||
}
|
||||
}
|
||||
+16
-3
@@ -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)
|
||||
|
||||
@@ -265,6 +265,8 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -274,6 +276,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public errordomain LockdownError {
|
||||
CONNECTION_CLOSED,
|
||||
INVALID_SERVICE,
|
||||
NOT_PAIRED,
|
||||
UNSUPPORTED,
|
||||
|
||||
+56
-32
@@ -71,44 +71,60 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
LibUSB.ConfigDescriptor config;
|
||||
Usb.check (device.raw_device.get_active_config_descriptor (out config),
|
||||
"Failed to get active USB config descriptor");
|
||||
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;
|
||||
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;
|
||||
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");
|
||||
|
||||
data_iface = setting.bInterfaceNumber;
|
||||
data_altsetting = setting.bAlternateSetting;
|
||||
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;
|
||||
|
||||
foreach (var ep in setting.endpoint) {
|
||||
if ((ep.bEndpointAddress & LibUSB.EndpointDirection.MASK) == LibUSB.EndpointDirection.IN)
|
||||
rx_address = ep.bEndpointAddress;
|
||||
else
|
||||
tx_address = ep.bEndpointAddress;
|
||||
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_user_error ("No USB CDC-NCM interface found");
|
||||
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,
|
||||
@@ -121,11 +137,20 @@ namespace Frida.Fruity {
|
||||
mac_address[i] = (uint8) v;
|
||||
}
|
||||
|
||||
handle.detach_kernel_driver (data_iface);
|
||||
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_user_error (@"Unable to claim USB CDC-NCM interface ($(e.message))");
|
||||
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");
|
||||
@@ -138,13 +163,12 @@ namespace Frida.Fruity {
|
||||
return true;
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private void throw_user_error (string message) throws Error {
|
||||
private string make_user_error_message (string message) {
|
||||
#if WINDOWS
|
||||
throw new Error.NOT_SUPPORTED ("%s; use https://zadig.akeo.ie to switch from Apple's official driver onto " +
|
||||
"Microsoft's WinUSB driver, so libusb can access it", message);
|
||||
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
|
||||
throw new Error.NOT_SUPPORTED ("%s", message);
|
||||
return message;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ namespace Frida.Fruity {
|
||||
private State state = STARTED;
|
||||
|
||||
private Gee.Queue<Request> requests = new Gee.ArrayQueue<Request> ();
|
||||
private unowned Thread<bool>? lwip_thread;
|
||||
|
||||
private LWIP.NetworkInterface handle;
|
||||
private LWIP.IP6Address raw_ipv6_address;
|
||||
@@ -154,8 +155,6 @@ namespace Frida.Fruity {
|
||||
|
||||
private MainContext main_context;
|
||||
|
||||
private DataOutputStream? pcap;
|
||||
|
||||
private enum State {
|
||||
STARTED,
|
||||
STOPPED
|
||||
@@ -249,13 +248,11 @@ namespace Frida.Fruity {
|
||||
public void handle_incoming_datagram (Bytes datagram) throws Error {
|
||||
check_started ();
|
||||
|
||||
log_datagram (datagram);
|
||||
|
||||
check (perform_on_lwip_thread (() => {
|
||||
var pbuf = LWIP.PacketBuffer.alloc (RAW, (uint16) datagram.get_size (), POOL);
|
||||
pbuf.take (datagram.get_data ());
|
||||
|
||||
var err = handle.input (pbuf, handle);
|
||||
var err = handle.input (pbuf, ref handle);
|
||||
if (err == OK)
|
||||
*((void **) &pbuf) = null;
|
||||
|
||||
@@ -282,10 +279,8 @@ namespace Frida.Fruity {
|
||||
var datagram = new Bytes (packet[:pbuf.tot_len]);
|
||||
|
||||
schedule_on_frida_thread (() => {
|
||||
if (state == STARTED) {
|
||||
log_datagram (datagram);
|
||||
if (state == STARTED)
|
||||
outgoing_datagram (datagram);
|
||||
}
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
@@ -293,18 +288,29 @@ namespace Frida.Fruity {
|
||||
internal LWIP.ErrorCode perform_on_lwip_thread (owned WorkFunc work) {
|
||||
var req = new Request ((owned) work);
|
||||
|
||||
lock (requests)
|
||||
requests.offer (req);
|
||||
LWIP.Runtime.schedule (perform_next_request);
|
||||
if (Thread.self<bool> () == lwip_thread) {
|
||||
perform_request (req);
|
||||
} else {
|
||||
lock (requests)
|
||||
requests.offer (req);
|
||||
LWIP.Runtime.schedule (perform_next_request);
|
||||
}
|
||||
|
||||
return req.join ();
|
||||
}
|
||||
|
||||
private void perform_next_request () {
|
||||
if (lwip_thread == null)
|
||||
lwip_thread = Thread.self ();
|
||||
|
||||
Request req;
|
||||
lock (requests)
|
||||
req = requests.poll ();
|
||||
|
||||
perform_request (req);
|
||||
}
|
||||
|
||||
private static void perform_request (Request req) {
|
||||
LWIP.ErrorCode err = req.work ();
|
||||
req.complete (err);
|
||||
}
|
||||
@@ -352,45 +358,6 @@ namespace Frida.Fruity {
|
||||
source.attach (main_context);
|
||||
}
|
||||
|
||||
private void log_datagram (Bytes datagram) {
|
||||
lock (pcap) {
|
||||
try {
|
||||
if (pcap == null) {
|
||||
char buf[40];
|
||||
unowned string addr = raw_ipv6_address.to_string (buf);
|
||||
var f = File.new_build_filename (Environment.get_user_special_dir (DESKTOP),
|
||||
"vns-%s.pcap".printf (addr.replace (":", "")));
|
||||
try {
|
||||
f.delete ();
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
pcap = new DataOutputStream (f.create (REPLACE_DESTINATION));
|
||||
pcap.set_byte_order (HOST_ENDIAN);
|
||||
pcap.put_uint32 (0xa1b2c3d4U);
|
||||
pcap.put_uint16 (2);
|
||||
pcap.put_uint16 (4);
|
||||
pcap.put_uint32 (0);
|
||||
pcap.put_uint32 (0);
|
||||
pcap.put_uint32 (16384);
|
||||
pcap.put_uint32 ((ethernet_address != null) ? 1 : 229); // Ethernet or IPv6
|
||||
pcap.flush ();
|
||||
}
|
||||
|
||||
int64 timestamp = get_real_time ();
|
||||
pcap.put_uint32 ((uint32) (timestamp / 1000000));
|
||||
pcap.put_uint32 ((uint32) (timestamp % 1000000));
|
||||
pcap.put_uint32 (datagram.length);
|
||||
pcap.put_uint32 (datagram.length);
|
||||
size_t written;
|
||||
pcap.write_all (datagram.get_data (), out written);
|
||||
pcap.flush ();
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("%s\n", e.message);
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TcpConnection : IOStream, AsyncInitable {
|
||||
public VirtualNetworkStack netstack {
|
||||
get;
|
||||
@@ -516,7 +483,7 @@ namespace Frida.Fruity {
|
||||
self->on_error (err);
|
||||
});
|
||||
pcb.nagle_disable ();
|
||||
pcb.bind_netif (netstack.handle);
|
||||
pcb.bind_netif (&netstack.handle);
|
||||
|
||||
var err = pcb.connect (ip6_address_from_inet_socket_address (address), address.get_port (), (user_data, pcb, err) => {
|
||||
TcpConnection * self = user_data;
|
||||
@@ -544,6 +511,11 @@ namespace Frida.Fruity {
|
||||
});
|
||||
}
|
||||
|
||||
private void detach_from_pcb () {
|
||||
pcb.set_user_data (null);
|
||||
pcb = null;
|
||||
}
|
||||
|
||||
private void on_connect () {
|
||||
lock (state)
|
||||
tx_space_available = pcb.query_available_send_buffer_space ();
|
||||
@@ -561,7 +533,7 @@ namespace Frida.Fruity {
|
||||
|
||||
private void on_recv (owned LWIP.PacketBuffer? pbuf, LWIP.ErrorCode err) {
|
||||
if (pbuf == null) {
|
||||
pcb = null;
|
||||
detach_from_pcb ();
|
||||
schedule_on_frida_thread (() => {
|
||||
_state = CLOSED;
|
||||
update_events ();
|
||||
@@ -584,14 +556,13 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private void on_error (LWIP.ErrorCode err) {
|
||||
pcb = null;
|
||||
detach_from_pcb ();
|
||||
schedule_on_frida_thread (() => {
|
||||
_state = CLOSED;
|
||||
update_events ();
|
||||
|
||||
if (!established.future.ready) {
|
||||
if (!established.future.ready)
|
||||
established.reject (parse_error (err));
|
||||
}
|
||||
|
||||
return Source.REMOVE;
|
||||
});
|
||||
@@ -957,7 +928,6 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
//private VirtualNetworkStack? _netstack;
|
||||
private unowned LWIP.UdpPcb? pcb;
|
||||
private IOCondition events = OUT;
|
||||
private Gee.Queue<Packet> rx_queue = new Gee.ArrayQueue<Packet> ();
|
||||
@@ -972,7 +942,7 @@ namespace Frida.Fruity {
|
||||
netstack.perform_on_lwip_thread (() => {
|
||||
pcb = LWIP.UdpPcb.make (V6);
|
||||
pcb.set_recv_callback (on_recv);
|
||||
pcb.bind_netif (netstack.handle);
|
||||
pcb.bind_netif (&netstack.handle);
|
||||
return OK;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
-7
@@ -7,6 +7,11 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public UsbDeviceBackend backend {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public LibUSB.DeviceHandle? handle {
|
||||
get {
|
||||
return _handle;
|
||||
@@ -29,6 +34,8 @@ namespace Frida.Fruity {
|
||||
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,
|
||||
@@ -38,8 +45,9 @@ namespace Frida.Fruity {
|
||||
private const string MODE_INITIAL_UNTETHERED = "3:3:3:0"; // => 5:3:3:0
|
||||
private const string MODE_INITIAL_TETHERED = "4:4:3:4"; // => 5:4:3:4
|
||||
|
||||
public static async UsbDevice open (LibUSB.Device raw_device, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var device = new UsbDevice (raw_device);
|
||||
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);
|
||||
@@ -50,8 +58,8 @@ namespace Frida.Fruity {
|
||||
return device;
|
||||
}
|
||||
|
||||
private UsbDevice (LibUSB.Device raw_device) {
|
||||
Object ();
|
||||
private UsbDevice (LibUSB.Device raw_device, UsbDeviceBackend backend) {
|
||||
Object (backend: backend);
|
||||
_raw_device = raw_device;
|
||||
}
|
||||
|
||||
@@ -71,7 +79,17 @@ namespace Frida.Fruity {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void close () {
|
||||
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;
|
||||
}
|
||||
@@ -174,7 +192,8 @@ namespace Frida.Fruity {
|
||||
|
||||
public async Bytes control_transfer (uint8 request_type, uint8 request, uint16 val, uint16 index, uint16 length,
|
||||
uint timeout, Cancellable? cancellable) throws Error, IOError {
|
||||
var transfer = new LibUSB.Transfer ();
|
||||
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];
|
||||
@@ -190,7 +209,9 @@ namespace Frida.Fruity {
|
||||
|
||||
try {
|
||||
Usb.check (transfer.submit (), "Failed to submit control transfer");
|
||||
on_operation_started ();
|
||||
yield;
|
||||
on_operation_ended ();
|
||||
} finally {
|
||||
cancel_source.destroy ();
|
||||
}
|
||||
@@ -202,7 +223,8 @@ namespace Frida.Fruity {
|
||||
|
||||
public async size_t bulk_transfer (uint8 endpoint, uint8[] buffer, uint timeout, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var transfer = new LibUSB.Transfer ();
|
||||
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);
|
||||
@@ -216,7 +238,9 @@ namespace Frida.Fruity {
|
||||
|
||||
try {
|
||||
Usb.check (transfer.submit (), "Failed to submit bulk transfer");
|
||||
on_operation_started ();
|
||||
yield;
|
||||
on_operation_ended ();
|
||||
} finally {
|
||||
cancel_source.destroy ();
|
||||
}
|
||||
@@ -226,6 +250,16 @@ namespace Frida.Fruity {
|
||||
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 ();
|
||||
@@ -252,6 +286,16 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
internal interface UsbDeviceBackend : Object {
|
||||
public abstract UsbOperation allocate_usb_operation ();
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
+372
-275
@@ -14,6 +14,8 @@ namespace Frida.Fruity {
|
||||
private Promise<Variant> handshake_promise = new Promise<Variant> ();
|
||||
private Variant handshake_body;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
public static async DiscoveryService open (IOStream stream, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var service = new DiscoveryService (stream);
|
||||
|
||||
@@ -42,6 +44,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public void close () {
|
||||
io_cancellable.cancel ();
|
||||
connection.cancel ();
|
||||
}
|
||||
|
||||
@@ -87,8 +90,30 @@ namespace Frida.Fruity {
|
||||
reader.read_member ("MessageType");
|
||||
unowned string message_type = reader.get_string_value ();
|
||||
|
||||
if (message_type == "Handshake")
|
||||
if (message_type == "Handshake") {
|
||||
handshake_promise.resolve (msg.body);
|
||||
|
||||
connection.post.begin (
|
||||
new XpcBodyBuilder ()
|
||||
.begin_dictionary ()
|
||||
.set_member_name ("MessageType")
|
||||
.add_string_value ("Handshake")
|
||||
.set_member_name ("MessagingProtocolVersion")
|
||||
.add_uint64_value (5)
|
||||
.set_member_name ("Services")
|
||||
.begin_dictionary ()
|
||||
.end_dictionary ()
|
||||
.set_member_name ("Properties")
|
||||
.begin_dictionary ()
|
||||
.set_member_name ("RemoteXPCVersionFlags")
|
||||
.add_uint64_value (0x100000000000006)
|
||||
.end_dictionary ()
|
||||
.set_member_name ("UUID")
|
||||
.add_uuid_value (make_random_v4_uuid ())
|
||||
.end_dictionary ()
|
||||
.build (),
|
||||
io_cancellable);
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
@@ -99,11 +124,18 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public class PairingService : Object, AsyncInitable {
|
||||
public const string DNS_SD_NAME = "_remotepairing._tcp.local";
|
||||
|
||||
public PairingTransport transport {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public PairingStore store {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public DeviceOptions device_options {
|
||||
get;
|
||||
private set;
|
||||
@@ -119,15 +151,12 @@ namespace Frida.Fruity {
|
||||
private uint64 next_control_sequence_number = 0;
|
||||
private uint64 next_encrypted_sequence_number = 0;
|
||||
|
||||
private File config_file;
|
||||
private string host_identifier;
|
||||
private Key pair_record_key;
|
||||
private ChaCha20Poly1305? client_cipher;
|
||||
private ChaCha20Poly1305? server_cipher;
|
||||
|
||||
public static async PairingService open (PairingTransport transport, Cancellable? cancellable = null)
|
||||
public static async PairingService open (PairingTransport transport, PairingStore store, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
var service = new PairingService (transport);
|
||||
var service = new PairingService (transport, store);
|
||||
|
||||
try {
|
||||
yield service.init_async (Priority.DEFAULT, cancellable);
|
||||
@@ -138,40 +167,13 @@ namespace Frida.Fruity {
|
||||
return service;
|
||||
}
|
||||
|
||||
private PairingService (PairingTransport transport) {
|
||||
Object (transport: transport);
|
||||
private PairingService (PairingTransport transport, PairingStore store) {
|
||||
Object (transport: transport, store: store);
|
||||
}
|
||||
|
||||
construct {
|
||||
transport.close.connect (on_close);
|
||||
transport.message.connect (on_message);
|
||||
|
||||
#if DARWIN
|
||||
config_file = File.new_for_path ("/var/db/lockdown/RemotePairing/user_%u/selfIdentity.plist".printf (
|
||||
(uint) Posix.getuid ()));
|
||||
#else
|
||||
config_file = File.new_build_filename (Environment.get_user_config_dir (), "frida", "remote-xpc.plist");
|
||||
#endif
|
||||
|
||||
Bytes? key = null;
|
||||
try {
|
||||
uint8[] raw_identity;
|
||||
FileUtils.get_data (config_file.get_path (), out raw_identity);
|
||||
Plist identity = new Plist.from_data (raw_identity);
|
||||
|
||||
unowned string identifier = identity.get_string ("identifier");
|
||||
key = identity.get_bytes ("privateKey");
|
||||
|
||||
host_identifier = identifier;
|
||||
} catch (GLib.Error e) {
|
||||
host_identifier = make_host_identifier ();
|
||||
}
|
||||
if (key == null) {
|
||||
uint8 dummy_key[32] = { 0, };
|
||||
key = new Bytes (dummy_key);
|
||||
}
|
||||
|
||||
pair_record_key = new Key.from_raw_private_key (ED25519, null, key.get_data ());
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -374,6 +376,10 @@ namespace Frida.Fruity {
|
||||
.build ();
|
||||
|
||||
var start_response = yield request_pairing_data (start_payload, cancellable);
|
||||
if (start_response.has_member ("error")) {
|
||||
yield notify_pair_verify_failed (cancellable);
|
||||
return null;
|
||||
}
|
||||
uint8[] raw_device_pubkey = start_response.read_member ("public-key").get_data_value ().get_data ();
|
||||
start_response.end_member ();
|
||||
var device_pubkey = new Key.from_raw_public_key (X25519, null, raw_device_pubkey);
|
||||
@@ -389,13 +395,15 @@ namespace Frida.Fruity {
|
||||
// TODO: Verify signature using peer's public key.
|
||||
/* var start_inner_response = */ new VariantReader (PairingParamsParser.parse (cipher.decrypt (
|
||||
new Bytes.static ("\x00\x00\x00\x00PV-Msg02".data[:12]),
|
||||
start_response.read_member ("encrypted-data").get_data_value ()).get_data ()));
|
||||
start_response.read_member ("encrypted-data").get_data_value ())));
|
||||
|
||||
unowned string host_identifier = store.self_identity.identifier;
|
||||
|
||||
var message = new ByteArray.sized (100);
|
||||
message.append (raw_host_pubkey);
|
||||
message.append (host_identifier.data);
|
||||
message.append (raw_device_pubkey);
|
||||
Bytes signature = compute_message_signature (ByteArray.free_to_bytes ((owned) message), pair_record_key);
|
||||
Bytes signature = compute_message_signature (ByteArray.free_to_bytes ((owned) message), store.self_identity.key);
|
||||
|
||||
Bytes inner_params = new PairingParamsBuilder ()
|
||||
.add_identifier (host_identifier)
|
||||
@@ -423,25 +431,29 @@ namespace Frida.Fruity {
|
||||
|
||||
ObjectReader finish_response = yield request_pairing_data (finish_payload, cancellable);
|
||||
if (finish_response.has_member ("error")) {
|
||||
yield post_plain (transport.make_object_builder ()
|
||||
.begin_dictionary ()
|
||||
.set_member_name ("event")
|
||||
.begin_dictionary ()
|
||||
.set_member_name ("_0")
|
||||
.begin_dictionary ()
|
||||
.set_member_name ("pairVerifyFailed")
|
||||
.begin_dictionary ()
|
||||
.end_dictionary ()
|
||||
.end_dictionary ()
|
||||
.end_dictionary ()
|
||||
.end_dictionary ()
|
||||
.build (), cancellable);
|
||||
yield notify_pair_verify_failed (cancellable);
|
||||
return null;
|
||||
}
|
||||
|
||||
return shared_key;
|
||||
}
|
||||
|
||||
private async void notify_pair_verify_failed (Cancellable? cancellable) throws Error, IOError {
|
||||
yield post_plain (transport.make_object_builder ()
|
||||
.begin_dictionary ()
|
||||
.set_member_name ("event")
|
||||
.begin_dictionary ()
|
||||
.set_member_name ("_0")
|
||||
.begin_dictionary ()
|
||||
.set_member_name ("pairVerifyFailed")
|
||||
.begin_dictionary ()
|
||||
.end_dictionary ()
|
||||
.end_dictionary ()
|
||||
.end_dictionary ()
|
||||
.end_dictionary ()
|
||||
.build (), cancellable);
|
||||
}
|
||||
|
||||
private async Bytes setup_manual_pairing (Cancellable? cancellable) throws Error, IOError {
|
||||
Bytes start_params = new PairingParamsBuilder ()
|
||||
.add_method (0)
|
||||
@@ -507,33 +519,29 @@ namespace Frida.Fruity {
|
||||
|
||||
var cipher = new ChaCha20Poly1305 (operation_key);
|
||||
|
||||
Key new_pair_record_key = make_keypair (ED25519);
|
||||
Bytes new_pair_record_pubkey = get_raw_public_key (new_pair_record_key);
|
||||
|
||||
uint8 raw_irk[16];
|
||||
Rng.generate (raw_irk);
|
||||
Bytes irk = new Bytes (raw_irk);
|
||||
|
||||
Bytes signing_key = derive_chacha_key (shared_key,
|
||||
"Pair-Setup-Controller-Sign-Info",
|
||||
"Pair-Setup-Controller-Sign-Salt");
|
||||
|
||||
unowned PairingIdentity self_identity = store.self_identity;
|
||||
Bytes self_identity_pubkey = get_raw_public_key (self_identity.key);
|
||||
|
||||
var message = new ByteArray.sized (100);
|
||||
message.append (signing_key.get_data ());
|
||||
message.append (host_identifier.data);
|
||||
message.append (new_pair_record_pubkey.get_data ());
|
||||
Bytes signature = compute_message_signature (ByteArray.free_to_bytes ((owned) message), new_pair_record_key);
|
||||
message.append (self_identity.identifier.data);
|
||||
message.append (self_identity_pubkey.get_data ());
|
||||
Bytes signature = compute_message_signature (ByteArray.free_to_bytes ((owned) message), self_identity.key);
|
||||
|
||||
Bytes info = new OpackBuilder ()
|
||||
Bytes self_info = new OpackBuilder ()
|
||||
.begin_dictionary ()
|
||||
.set_member_name ("name")
|
||||
.add_string_value (Environment.get_host_name ())
|
||||
.set_member_name ("accountID")
|
||||
.add_string_value (host_identifier)
|
||||
.add_string_value (self_identity.identifier)
|
||||
.set_member_name ("remotepairing_serial_number")
|
||||
.add_string_value ("AAAAAAAAAAAA")
|
||||
.set_member_name ("altIRK")
|
||||
.add_data_value (irk)
|
||||
.add_data_value (self_identity.irk)
|
||||
.set_member_name ("model")
|
||||
.add_string_value ("computer-model")
|
||||
.set_member_name ("mac")
|
||||
@@ -544,10 +552,10 @@ namespace Frida.Fruity {
|
||||
.build ();
|
||||
|
||||
Bytes inner_params = new PairingParamsBuilder ()
|
||||
.add_identifier (host_identifier)
|
||||
.add_raw_public_key (new_pair_record_pubkey)
|
||||
.add_identifier (self_identity.identifier)
|
||||
.add_raw_public_key (self_identity_pubkey)
|
||||
.add_signature (signature)
|
||||
.add_info (info)
|
||||
.add_info (self_info)
|
||||
.build ();
|
||||
|
||||
Bytes outer_params = new PairingParamsBuilder ()
|
||||
@@ -571,29 +579,18 @@ namespace Frida.Fruity {
|
||||
.end_dictionary ()
|
||||
.build ();
|
||||
|
||||
var finish_response = yield request_pairing_data (finish_payload, cancellable);
|
||||
var outer_finish_response = yield request_pairing_data (finish_payload, cancellable);
|
||||
var inner_finish_response = new VariantReader (PairingParamsParser.parse (
|
||||
cipher.decrypt (new Bytes.static ("\x00\x00\x00\x00PS-Msg06".data[:12]),
|
||||
outer_finish_response.read_member ("encrypted-data").get_data_value ())));
|
||||
|
||||
Bytes encrypted_response = finish_response.read_member ("encrypted-data").get_data_value ();
|
||||
Bytes raw_response = cipher.decrypt (new Bytes.static ("\x00\x00\x00\x00PS-Msg06".data[:12]), encrypted_response);
|
||||
// TODO: Wire up if/when needed.
|
||||
/* Variant response = */ PairingParamsParser.parse (raw_response.get_data ());
|
||||
|
||||
var config = new Plist ();
|
||||
config.set_string ("identifier", host_identifier);
|
||||
config.set_bytes ("publicKey", new_pair_record_pubkey);
|
||||
config.set_bytes ("privateKey", get_raw_private_key (new_pair_record_key));
|
||||
config.set_bytes ("irk", irk);
|
||||
try {
|
||||
config_file.get_parent ().make_directory_with_parents (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
try {
|
||||
FileUtils.set_contents (config_file.get_path (), config.to_xml ());
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
|
||||
pair_record_key = (owned) new_pair_record_key;
|
||||
string peer_identifier = inner_finish_response.read_member ("identifier").get_string_value ();
|
||||
inner_finish_response.end_member ();
|
||||
Bytes peer_pubkey = inner_finish_response.read_member ("public-key").get_data_value ();
|
||||
inner_finish_response.end_member ();
|
||||
Bytes peer_info = inner_finish_response.read_member ("info").get_data_value ();
|
||||
inner_finish_response.end_member ();
|
||||
store.add_peer (peer_identifier, peer_pubkey, peer_info);
|
||||
|
||||
return shared_key;
|
||||
}
|
||||
@@ -636,7 +633,7 @@ namespace Frida.Fruity {
|
||||
.read_member ("_0")
|
||||
.read_member ("data")
|
||||
.get_data_value ();
|
||||
Variant data = PairingParamsParser.parse (raw_data.get_data ());
|
||||
Variant data = PairingParamsParser.parse (raw_data);
|
||||
return new VariantReader (data);
|
||||
}
|
||||
|
||||
@@ -759,16 +756,6 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private static Key make_keypair (KeyType type) {
|
||||
var ctx = new KeyContext.for_key_type (type);
|
||||
ctx.keygen_init ();
|
||||
|
||||
Key? keypair = null;
|
||||
ctx.keygen (ref keypair);
|
||||
|
||||
return keypair;
|
||||
}
|
||||
|
||||
private static uint8[] key_to_der (Key key) {
|
||||
var sink = new BasicIO (BasicIOMethod.memory ());
|
||||
key.to_der (sink);
|
||||
@@ -1345,6 +1332,240 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class PairingStore {
|
||||
public PairingIdentity self_identity {
|
||||
get {
|
||||
return _self_identity;
|
||||
}
|
||||
}
|
||||
|
||||
public Gee.Iterable<PairingPeer> peers {
|
||||
get {
|
||||
return _peers;
|
||||
}
|
||||
}
|
||||
|
||||
private PairingIdentity _self_identity;
|
||||
private Gee.List<PairingPeer> _peers;
|
||||
|
||||
public PairingStore () {
|
||||
_self_identity = try_load_identity ();
|
||||
if (_self_identity == null) {
|
||||
_self_identity = PairingIdentity.make ();
|
||||
try {
|
||||
save_identity (_self_identity);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
_peers = load_peers ();
|
||||
}
|
||||
|
||||
public void add_peer (string identifier, Bytes public_key, Bytes info) throws Error {
|
||||
var r = new VariantReader (OpackParser.parse (info));
|
||||
|
||||
Bytes irk = r.read_member ("altIRK").get_data_value ();
|
||||
r.end_member ();
|
||||
|
||||
unowned string name = r.read_member ("name").get_string_value ();
|
||||
r.end_member ();
|
||||
|
||||
unowned string model = r.read_member ("model").get_string_value ();
|
||||
r.end_member ();
|
||||
|
||||
unowned string udid = r.read_member ("remotepairing_udid").get_string_value ();
|
||||
r.end_member ();
|
||||
|
||||
var peer = new PairingPeer () {
|
||||
identifier = identifier,
|
||||
public_key = public_key,
|
||||
irk = irk,
|
||||
name = name,
|
||||
model = model,
|
||||
udid = udid,
|
||||
info = info,
|
||||
};
|
||||
_peers.add (peer);
|
||||
|
||||
try {
|
||||
save_peer (peer);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public PairingPeer? find_peer_matching_service (PairingServiceDetails service) {
|
||||
var mac = OpenSSL.Envelope.MessageAuthCode.fetch (null, OpenSSL.ShortName.siphash);
|
||||
|
||||
size_t hash_size = 8;
|
||||
size_t return_size = OpenSSL.ParamReturnSize.UNMODIFIED;
|
||||
OpenSSL.Param mac_params[] = {
|
||||
{ OpenSSL.Envelope.MessageAuthParameter.SIZE, UNSIGNED_INTEGER,
|
||||
(uint8[]) &hash_size, return_size },
|
||||
{ null, INTEGER, null, return_size },
|
||||
};
|
||||
|
||||
foreach (var peer in _peers) {
|
||||
var ctx = new OpenSSL.Envelope.MessageAuthCodeContext (mac);
|
||||
ctx.init (peer.irk.get_data (), mac_params);
|
||||
ctx.update (service.identifier.data);
|
||||
uint8 output[8];
|
||||
size_t outlen = 0;
|
||||
ctx.final (output, out outlen);
|
||||
|
||||
uint8 tag[6];
|
||||
for (uint i = 0; i != 6; i++)
|
||||
tag[i] = output[5 - i];
|
||||
|
||||
if (Memory.cmp (tag, service.auth_tag.get_data (), service.auth_tag.get_size ()) == 0)
|
||||
return peer;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PairingIdentity? try_load_identity () {
|
||||
try {
|
||||
var plist = new Plist.from_data (query_self_identity_location ().load_bytes ().get_data ());
|
||||
return new PairingIdentity () {
|
||||
identifier = plist.get_string ("identifier"),
|
||||
key = new Key.from_raw_private_key (ED25519, null, plist.get_bytes ("privateKey").get_data ()),
|
||||
irk = plist.get_bytes ("irk"),
|
||||
};
|
||||
} catch (GLib.Error e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void save_identity (PairingIdentity identity) throws Error {
|
||||
var plist = new Plist ();
|
||||
plist.set_string ("identifier", identity.identifier);
|
||||
plist.set_bytes ("publicKey", get_raw_public_key (identity.key));
|
||||
plist.set_bytes ("privateKey", get_raw_private_key (identity.key));
|
||||
plist.set_bytes ("irk", identity.irk);
|
||||
save_plist (plist, query_self_identity_location ());
|
||||
}
|
||||
|
||||
private static Gee.List<PairingPeer> load_peers () {
|
||||
var peers = new Gee.ArrayList<PairingPeer> ();
|
||||
|
||||
try {
|
||||
var enumerator = query_peers_location ().enumerate_children (FileAttribute.STANDARD_NAME, FileQueryInfoFlags.NONE);
|
||||
File? child;
|
||||
while (enumerator.iterate (null, out child) && child != null) {
|
||||
var plist = new Plist.from_data (child.load_bytes ().get_data ());
|
||||
|
||||
var info = plist.get_bytes ("info");
|
||||
var r = new VariantReader (OpackParser.parse (info));
|
||||
unowned string udid = r.read_member ("remotepairing_udid").get_string_value ();
|
||||
|
||||
peers.add (new PairingPeer () {
|
||||
identifier = plist.get_string ("identifier"),
|
||||
public_key = plist.get_bytes ("publicKey"),
|
||||
irk = plist.get_bytes ("irk"),
|
||||
name = plist.get_string ("name"),
|
||||
model = plist.get_string ("model"),
|
||||
udid = udid,
|
||||
info = info,
|
||||
});
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
return peers;
|
||||
}
|
||||
|
||||
private static void save_peer (PairingPeer peer) throws Error {
|
||||
var plist = new Plist ();
|
||||
plist.set_string ("identifier", peer.identifier);
|
||||
plist.set_bytes ("publicKey", peer.public_key);
|
||||
plist.set_bytes ("irk", peer.irk);
|
||||
plist.set_string ("name", peer.name);
|
||||
plist.set_string ("model", peer.model);
|
||||
plist.set_bytes ("info", peer.info);
|
||||
save_plist (plist, query_peers_location ().get_child (peer.identifier + ".plist"));
|
||||
}
|
||||
|
||||
private static File query_self_identity_location () {
|
||||
return query_base_location ().get_child ("self-identity.plist");
|
||||
}
|
||||
|
||||
private static File query_peers_location () {
|
||||
return query_base_location ().get_child ("peers");
|
||||
}
|
||||
|
||||
private static File query_base_location () {
|
||||
return File.new_build_filename (Environment.get_user_config_dir (), "frida");
|
||||
}
|
||||
|
||||
private static void save_plist (Plist plist, File location) throws Error {
|
||||
try {
|
||||
location.get_parent ().make_directory_with_parents ();
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
try {
|
||||
location.replace_contents (plist.to_binary (), null, false, PRIVATE | REPLACE_DESTINATION, null);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PairingIdentity {
|
||||
public string identifier;
|
||||
public Key key;
|
||||
public Bytes irk;
|
||||
|
||||
public static PairingIdentity make () {
|
||||
uint8 raw_irk[16];
|
||||
Rng.generate (raw_irk);
|
||||
|
||||
return new PairingIdentity () {
|
||||
identifier = make_host_identifier (),
|
||||
key = make_keypair (ED25519),
|
||||
irk = new Bytes (raw_irk),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class PairingPeer {
|
||||
public string identifier;
|
||||
public Bytes public_key;
|
||||
public Bytes irk;
|
||||
public string name;
|
||||
public string model;
|
||||
public string udid;
|
||||
public Bytes info;
|
||||
}
|
||||
|
||||
public class PairingServiceMetadata {
|
||||
public string identifier;
|
||||
public Bytes auth_tag;
|
||||
|
||||
public static PairingServiceMetadata from_txt_record (Gee.Iterable<string> record) throws Error {
|
||||
string? identifier = null;
|
||||
Bytes? auth_tag = null;
|
||||
foreach (string item in record) {
|
||||
string[] tokens = item.split ("=", 2);
|
||||
if (tokens.length != 2)
|
||||
continue;
|
||||
|
||||
unowned string key = tokens[0];
|
||||
unowned string val = tokens[1];
|
||||
if (key == "identifier")
|
||||
identifier = val;
|
||||
else if (key == "authTag")
|
||||
auth_tag = new Bytes (Base64.decode (val));
|
||||
}
|
||||
if (identifier == null || auth_tag == null)
|
||||
throw new Error.PROTOCOL ("Missing TXT metadata");
|
||||
|
||||
return new PairingServiceMetadata () {
|
||||
identifier = identifier,
|
||||
auth_tag = auth_tag,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class DeviceOptions {
|
||||
public bool allows_pair_setup;
|
||||
public bool allows_pinless_pairing;
|
||||
@@ -1435,54 +1656,51 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private class PairingParamsParser {
|
||||
private Buffer buf;
|
||||
private size_t cursor = 0;
|
||||
private BufferReader reader;
|
||||
private EnumClass param_type_class;
|
||||
|
||||
public static Variant parse (uint8[] data) throws Error {
|
||||
var parser = new PairingParamsParser (new Bytes.static (data));
|
||||
public static Variant parse (Bytes pairing_params) throws Error {
|
||||
var parser = new PairingParamsParser (pairing_params);
|
||||
return parser.read_params ();
|
||||
}
|
||||
|
||||
private PairingParamsParser (Bytes bytes) {
|
||||
this.buf = new Buffer (bytes, LITTLE_ENDIAN);
|
||||
this.param_type_class = (EnumClass) typeof (PairingParamType).class_ref ();
|
||||
reader = new BufferReader (new Buffer (bytes, LITTLE_ENDIAN));
|
||||
param_type_class = (EnumClass) typeof (PairingParamType).class_ref ();
|
||||
}
|
||||
|
||||
private Variant read_params () throws Error {
|
||||
var byte_array = new VariantType.array (VariantType.BYTE);
|
||||
|
||||
var parameters = new Gee.HashMap<string, Variant> ();
|
||||
size_t size = buf.bytes.get_size ();
|
||||
while (cursor != size) {
|
||||
var raw_type = read_raw_uint8 ();
|
||||
while (reader.available != 0) {
|
||||
var raw_type = reader.read_uint8 ();
|
||||
unowned EnumValue? type_enum_val = param_type_class.get_value (raw_type);
|
||||
if (type_enum_val == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported pairing parameter type (0x%x)", raw_type);
|
||||
var type = (PairingParamType) raw_type;
|
||||
unowned string key = type_enum_val.value_nick;
|
||||
|
||||
var val_size = read_raw_uint8 ();
|
||||
Bytes val_bytes = read_raw_bytes (val_size);
|
||||
|
||||
var val_size = reader.read_uint8 ();
|
||||
Variant val;
|
||||
switch (type) {
|
||||
case IDENTIFIER:
|
||||
val = new Variant.string (reader.read_fixed_string (val_size));
|
||||
break;
|
||||
case STATE:
|
||||
case ERROR:
|
||||
if (val_bytes.length != 1) {
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid value for '%s': length=%d",
|
||||
key, val_bytes.length);
|
||||
}
|
||||
val = new Variant.byte (val_bytes[0]);
|
||||
if (val_size != 1)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid value for '%s': size=%u", key, val_size);
|
||||
val = new Variant.byte (reader.read_uint8 ());
|
||||
break;
|
||||
case RETRY_DELAY: {
|
||||
uint16 delay;
|
||||
switch (val_bytes.length) {
|
||||
switch (val_size) {
|
||||
case 1:
|
||||
delay = val_bytes[0];
|
||||
delay = reader.read_uint8 ();
|
||||
break;
|
||||
case 2:
|
||||
delay = new Buffer (val_bytes, LITTLE_ENDIAN).read_uint16 (0);
|
||||
delay = reader.read_uint16 ();
|
||||
break;
|
||||
default:
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid value for 'retry-delay'");
|
||||
@@ -1491,6 +1709,7 @@ namespace Frida.Fruity {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Bytes val_bytes = reader.read_bytes (val_size);
|
||||
var val_bytes_copy = new Bytes (val_bytes.get_data ());
|
||||
val = Variant.new_from_data (byte_array, val_bytes_copy.get_data (), true, val_bytes_copy);
|
||||
break;
|
||||
@@ -1517,26 +1736,6 @@ namespace Frida.Fruity {
|
||||
builder.add ("{sv}", e.key, e.value);
|
||||
return builder.end ();
|
||||
}
|
||||
|
||||
private uint8 read_raw_uint8 () throws Error {
|
||||
check_available (sizeof (uint8));
|
||||
var result = buf.read_uint8 (cursor);
|
||||
cursor += sizeof (uint8);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Bytes read_raw_bytes (size_t n) throws Error {
|
||||
check_available (n);
|
||||
Bytes result = buf.bytes[cursor:cursor + n];
|
||||
cursor += n;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void check_available (size_t required) throws Error {
|
||||
size_t available = buf.bytes.get_size () - cursor;
|
||||
if (available < required)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid pairing parameters: truncated");
|
||||
}
|
||||
}
|
||||
|
||||
private enum PairingParamType {
|
||||
@@ -1553,139 +1752,9 @@ namespace Frida.Fruity {
|
||||
INFO = 17,
|
||||
}
|
||||
|
||||
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 sealed class TunnelConnection : Object, AsyncInitable {
|
||||
public signal void close ();
|
||||
|
||||
public InetSocketAddress address {
|
||||
get;
|
||||
construct;
|
||||
@@ -1896,7 +1965,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private void on_control_stream_opened () {
|
||||
var zeroed_padding_packet = new uint8[1024];
|
||||
var zeroed_padding_packet = new uint8[PREFERRED_MTU];
|
||||
send_datagram (new Bytes.take ((owned) zeroed_padding_packet));
|
||||
|
||||
send_request (Json.to_string (
|
||||
@@ -1954,6 +2023,8 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public void cancel () {
|
||||
if (connection != null)
|
||||
close ();
|
||||
connection = null;
|
||||
socket = null;
|
||||
|
||||
@@ -2028,8 +2099,6 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private void send_datagram (Bytes datagram) {
|
||||
if (connection == null)
|
||||
return;
|
||||
tx_datagrams.offer (datagram);
|
||||
process_pending_writes ();
|
||||
}
|
||||
@@ -2059,7 +2128,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private void process_pending_writes () {
|
||||
if (write_idle != null)
|
||||
if (connection == null || write_idle != null)
|
||||
return;
|
||||
|
||||
var source = new IdleSource ();
|
||||
@@ -2185,6 +2254,13 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private int on_stream_close (uint32 flags, int64 stream_id, uint64 app_error_code) {
|
||||
if (!established.future.ready) {
|
||||
established.reject (new Error.TRANSPORT ("Connection closed early with QUIC app error code %" +
|
||||
uint64.FORMAT_MODIFIER + "u", app_error_code));
|
||||
}
|
||||
|
||||
cancel ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3669,6 +3745,16 @@ namespace Frida.Fruity {
|
||||
public const uint32 VERSION = 5;
|
||||
}
|
||||
|
||||
private Key make_keypair (KeyType type) {
|
||||
var ctx = new KeyContext.for_key_type (type);
|
||||
ctx.keygen_init ();
|
||||
|
||||
Key? keypair = null;
|
||||
ctx.keygen (ref keypair);
|
||||
|
||||
return keypair;
|
||||
}
|
||||
|
||||
private Bytes get_raw_public_key (Key key) {
|
||||
size_t size = 0;
|
||||
key.get_raw_public_key (null, ref size);
|
||||
@@ -3721,4 +3807,15 @@ namespace Frida.Fruity {
|
||||
|
||||
return result.str;
|
||||
}
|
||||
|
||||
private uint8[] make_random_v4_uuid () {
|
||||
uint8 uuid[16];
|
||||
OpenSSL.Rng.generate (uuid);
|
||||
|
||||
const uint8 uuid_version = 4;
|
||||
uuid[6] = (uuid_version << 4) | (uuid[6] & 0xf);
|
||||
uuid[8] = 0x80 | (uuid[8] & 0x3f);
|
||||
|
||||
return uuid;
|
||||
}
|
||||
}
|
||||
@@ -1931,6 +1931,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) {
|
||||
}
|
||||
}
|
||||
|
||||
+25
-7
@@ -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]
|
||||
@@ -253,7 +265,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 +418,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]
|
||||
@@ -490,15 +502,19 @@ 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 == 'macos'
|
||||
if host_os_family == 'windows'
|
||||
backend_sources += 'fruity' / 'device-monitor-windows.vala'
|
||||
elif host_os == 'macos'
|
||||
backend_sources += 'fruity' / 'device-monitor-macos.vala'
|
||||
elif host_os == 'linux'
|
||||
backend_sources += 'fruity' / 'device-monitor-linux.vala'
|
||||
@@ -521,7 +537,9 @@ if have_fruity_backend
|
||||
'--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-xpc',
|
||||
@@ -562,7 +580,7 @@ subdir('compiler')
|
||||
|
||||
core_sources = base_sources + backend_sources + compiler_sources
|
||||
|
||||
internal_libs = [base, pipe] + backend_libs
|
||||
internal_libs = [base, netif, pipe] + backend_libs
|
||||
internal_deps = [base_dep, netif_dep, pipe_dep]
|
||||
|
||||
if host_os_family == 'windows'
|
||||
|
||||
@@ -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.4.2
|
||||
revision = 16.5.2
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
+8
-8
@@ -33,7 +33,7 @@ namespace LWIP {
|
||||
public void ip6_addr_set_state (int8 addr_index, IP6AddressState state);
|
||||
|
||||
[CCode (cname = "netif_input")]
|
||||
public static ErrorCode default_input_handler (PacketBuffer pbuf, NetworkInterface netif);
|
||||
public static ErrorCode default_input_handler (PacketBuffer pbuf, ref NetworkInterface netif);
|
||||
|
||||
public IP6Address ip6_addr[IPV6_NUM_ADDRESSES];
|
||||
|
||||
@@ -56,16 +56,16 @@ namespace LWIP {
|
||||
}
|
||||
|
||||
[CCode (cname = "netif_init_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceInitFunc (NetworkInterface netif);
|
||||
public delegate ErrorCode NetworkInterfaceInitFunc (ref NetworkInterface netif);
|
||||
|
||||
[CCode (cname = "netif_input_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceInputFunc (PacketBuffer pbuf, NetworkInterface netif);
|
||||
public delegate ErrorCode NetworkInterfaceInputFunc (PacketBuffer pbuf, ref NetworkInterface netif);
|
||||
|
||||
[CCode (cname = "netif_linkoutput_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceLinkOutputFunc (NetworkInterface netif, PacketBuffer pbuf);
|
||||
public delegate ErrorCode NetworkInterfaceLinkOutputFunc (ref NetworkInterface netif, PacketBuffer pbuf);
|
||||
|
||||
[CCode (cname = "netif_output_ip6_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceOutputIP6Func (NetworkInterface netif, PacketBuffer pbuf, IP6Address address);
|
||||
public delegate ErrorCode NetworkInterfaceOutputIP6Func (ref NetworkInterface netif, PacketBuffer pbuf, IP6Address address);
|
||||
|
||||
[Flags]
|
||||
[CCode (cheader_filename = "lwip/netif.h", cprefix = "NETIF_FLAG_")]
|
||||
@@ -85,7 +85,7 @@ namespace LWIP {
|
||||
|
||||
namespace IPv6 {
|
||||
[CCode (cheader_filename = "lwip/ethip6.h", cname = "ethip6_output")]
|
||||
public ErrorCode output (NetworkInterface netif, PacketBuffer pbuf, IP6Address address);
|
||||
public ErrorCode output (ref NetworkInterface netif, PacketBuffer pbuf, IP6Address address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace LWIP {
|
||||
public ErrorCode close ();
|
||||
public ErrorCode shutdown (bool shut_rx, bool shut_tx);
|
||||
|
||||
public void bind_netif (NetworkInterface? netif);
|
||||
public void bind_netif (NetworkInterface * netif);
|
||||
|
||||
public ErrorCode connect (IP6Address address, uint16 port, ConnectedFunc connected);
|
||||
|
||||
@@ -241,7 +241,7 @@ namespace LWIP {
|
||||
public void set_recv_callback (RecvFunc f);
|
||||
|
||||
public ErrorCode bind (IP6Address? ipaddr = null, uint16 port = 0);
|
||||
public void bind_netif (NetworkInterface? netif);
|
||||
public void bind_netif (NetworkInterface * netif);
|
||||
|
||||
public ErrorCode connect (IP6Address ipaddr, uint16 port);
|
||||
|
||||
|
||||
@@ -384,6 +384,44 @@ namespace OpenSSL {
|
||||
public class MessageDigest {
|
||||
public static MessageDigest fetch (LibraryContext? ctx, string algorithm, string? properties = null);
|
||||
}
|
||||
|
||||
[Compact]
|
||||
[CCode (cname = "EVP_MAC_CTX", cprefix = "EVP_MAC_CTX_")]
|
||||
public class MessageAuthCodeContext {
|
||||
public MessageAuthCodeContext (MessageAuthCode mac);
|
||||
|
||||
[CCode (cname = "EVP_MAC_init")]
|
||||
public int init (uint8[] key, [CCode (array_length = false)] Param[]? params = null);
|
||||
[CCode (cname = "EVP_MAC_update")]
|
||||
public int update (uint8[] data);
|
||||
[CCode (cname = "EVP_MAC_final")]
|
||||
public int final ([CCode (array_length_pos = 2)] uint8[]? outdata, out size_t outlen);
|
||||
}
|
||||
|
||||
[Compact]
|
||||
[CCode (cname = "EVP_MAC", cprefix = "EVP_MAC_")]
|
||||
public class MessageAuthCode {
|
||||
public static MessageAuthCode fetch (LibraryContext? ctx, string algorithm, string? properties = null);
|
||||
}
|
||||
|
||||
[CCode (cheader_filename = "openssl/core_names.h", lower_case_cprefix = "OSSL_MAC_PARAM_")]
|
||||
namespace MessageAuthParameter {
|
||||
public const string KEY;
|
||||
public const string IV;
|
||||
public const string CUSTOM;
|
||||
public const string SALT;
|
||||
public const string XOF;
|
||||
public const string DIGEST_NOINIT;
|
||||
public const string DIGEST_ONESHOT;
|
||||
public const string C_ROUNDS;
|
||||
public const string D_ROUNDS;
|
||||
public const string CIPHER;
|
||||
public const string DIGEST;
|
||||
public const string PROPERTIES;
|
||||
public const string SIZE;
|
||||
public const string BLOCK_SIZE;
|
||||
public const string TLS_DATA_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
[Compact]
|
||||
@@ -426,6 +464,7 @@ namespace OpenSSL {
|
||||
public const string sha256;
|
||||
public const string sha384;
|
||||
public const string sha512;
|
||||
public const string siphash;
|
||||
public const string chacha20_poly1305;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
[CCode (cheader_filename = "windns.h", gir_namespace = "Windows", gir_version = "1.0")]
|
||||
namespace WinDns {
|
||||
[CCode (cname = "DNS_QUERY_RESULT")]
|
||||
public struct QueryResult {
|
||||
[CCode (cname = "Version")]
|
||||
public ulong version;
|
||||
[CCode (cname = "QueryStatus")]
|
||||
public long query_status;
|
||||
[CCode (cname = "QueryOptions")]
|
||||
public uint64 query_options;
|
||||
[CCode (cname = "pQueryRecords")]
|
||||
public Record * query_records;
|
||||
[CCode (cname = "Reserved")]
|
||||
public void * reserved;
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_RECORDW")]
|
||||
public struct Record {
|
||||
[CCode (cname = "pNext")]
|
||||
public Record * next;
|
||||
[CCode (cname = "pName")]
|
||||
public unowned string16 name;
|
||||
[CCode (cname = "wType")]
|
||||
public RecordType type;
|
||||
[CCode (cname = "wDataLength")]
|
||||
public uint16 data_length;
|
||||
[CCode (cname = "Flags.DW")]
|
||||
public uint32 flags;
|
||||
[CCode (cname = "dwTtl")]
|
||||
public uint32 ttl;
|
||||
[CCode (cname = "dwReserved")]
|
||||
public uint32 reserved;
|
||||
|
||||
[CCode (cname = "Data.A")]
|
||||
public AData a;
|
||||
[CCode (cname = "Data.PTR")]
|
||||
public PtrData ptr;
|
||||
[CCode (cname = "Data.TXT")]
|
||||
public TxtData txt;
|
||||
[CCode (cname = "Data.AAAA")]
|
||||
public AaaaData aaaa;
|
||||
[CCode (cname = "Data.SRV")]
|
||||
public SrvData srv;
|
||||
}
|
||||
|
||||
[CCode (cname = "WORD", cprefix = "DNS_TYPE_", has_type_id = false)]
|
||||
public enum RecordType {
|
||||
A,
|
||||
PTR,
|
||||
TEXT,
|
||||
AAAA,
|
||||
SRV,
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_A_DATA")]
|
||||
public struct AData {
|
||||
[CCode (cname = "IpAddress")]
|
||||
public uint32 ip;
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_PTR_DATAW")]
|
||||
public struct PtrData {
|
||||
[CCode (cname = "pNameHost")]
|
||||
public unowned string16 name;
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_TXT_DATAW")]
|
||||
public struct TxtData {
|
||||
[CCode (cname = "pStringArray", array_length_cname = "dwStringCount")]
|
||||
public unowned string16[] strings;
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_AAAA_DATA")]
|
||||
public struct AaaaData {
|
||||
[CCode (cname = "Ip6Address")]
|
||||
public IP6Address ip;
|
||||
}
|
||||
|
||||
[CCode (cname = "DNS_SRV_DATAW")]
|
||||
public struct SrvData {
|
||||
[CCode (cname = "pNameTarget")]
|
||||
public unowned string16 name_target;
|
||||
[CCode (cname = "wPriority")]
|
||||
public uint16 priority;
|
||||
[CCode (cname = "wWeight")]
|
||||
public uint16 weight;
|
||||
[CCode (cname = "wPort")]
|
||||
public uint16 port;
|
||||
}
|
||||
|
||||
[CCode (cname = "IP6_ADDRESS")]
|
||||
public struct IP6Address {
|
||||
[CCode (cname = "IP6Byte")]
|
||||
public uint8 data[16];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user