mirror of
https://github.com/frida/frida-core.git
synced 2026-11-09 10:09:38 -10:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b54eeb7d8 | ||
|
|
08de3d3d07 | ||
|
|
174b93cbd4 | ||
|
|
d0f8162a65 | ||
|
|
a3427baeb3 | ||
|
|
c792a09002 | ||
|
|
27605f7fde | ||
|
|
ba8784fe8c | ||
|
|
0a6c2103fa | ||
|
|
14de0ef567 | ||
|
|
4165572919 |
No files matched your search
@@ -2462,26 +2462,10 @@ namespace Frida {
|
||||
|
||||
private static void add_variant_value_of_type (Variant val, string type, Fruity.XpcBodyBuilder builder) throws Error {
|
||||
switch (type) {
|
||||
case "bool":
|
||||
check_type (val, VariantType.BOOLEAN);
|
||||
builder.add_bool_value (val.get_boolean ());
|
||||
break;
|
||||
case "int64":
|
||||
check_type (val, VariantType.INT64);
|
||||
builder.add_int64_value (val.get_int64 ());
|
||||
break;
|
||||
case "uint64":
|
||||
check_type (val, VariantType.UINT64);
|
||||
builder.add_uint64_value (val.get_uint64 ());
|
||||
break;
|
||||
case "data":
|
||||
check_type (val, new VariantType ("ay"));
|
||||
builder.add_data_value (val.get_data_as_bytes ());
|
||||
break;
|
||||
case "string":
|
||||
check_type (val, VariantType.STRING);
|
||||
builder.add_string_value (val.get_string ());
|
||||
break;
|
||||
case "uuid":
|
||||
check_type (val, new VariantType ("ay"));
|
||||
if (val.get_size () != 16)
|
||||
|
||||
@@ -813,22 +813,23 @@ namespace Frida {
|
||||
private const uint64 SOCK_CLOEXEC = 0x80000;
|
||||
|
||||
private sealed class InjectSession : SeizeSession {
|
||||
private static ProcMapsSoEntry local_libc;
|
||||
private static ProcMapsSnapshot.Mapping local_libc;
|
||||
private static uint64 mmap_offset;
|
||||
private static uint64 munmap_offset;
|
||||
|
||||
private static string fallback_ld;
|
||||
private static string fallback_libc;
|
||||
|
||||
private static ProcMapsSoEntry? local_android_ld;
|
||||
private static ProcMapsSnapshot.Mapping? local_android_ld;
|
||||
|
||||
static construct {
|
||||
var libc = Gum.Process.get_libc_module ();
|
||||
uint local_pid = Posix.getpid ();
|
||||
local_libc = ProcMapsSoEntry.find_by_path (local_pid, libc.path);
|
||||
var local_maps = ProcMapsSnapshot.from_pid (local_pid);
|
||||
local_libc = local_maps.find_module_by_path (libc.path);
|
||||
assert (local_libc != null);
|
||||
mmap_offset = (uint64) (uintptr) libc.find_export_by_name ("mmap") - local_libc.base_address;
|
||||
munmap_offset = (uint64) (uintptr) libc.find_export_by_name ("munmap") - local_libc.base_address;
|
||||
mmap_offset = (uint64) (uintptr) libc.find_export_by_name ("mmap") - local_libc.start;
|
||||
munmap_offset = (uint64) (uintptr) libc.find_export_by_name ("munmap") - local_libc.start;
|
||||
|
||||
try {
|
||||
var program = new Gum.ElfModule.from_file ("/proc/self/exe");
|
||||
@@ -846,7 +847,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
local_android_ld = ProcMapsSoEntry.find_by_path (local_pid, fallback_ld);
|
||||
local_android_ld = local_maps.find_module_by_path (fallback_ld);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1051,9 +1052,10 @@ namespace Frida {
|
||||
uint64 allocation_base = 0;
|
||||
size_t allocation_size = size_t.max (bootstrapper_size, loader_size) + stack_size;
|
||||
|
||||
var remote_maps = ProcMapsSnapshot.from_pid (pid);
|
||||
var remote_libc = remote_maps.find_module_by_path (local_libc.path);
|
||||
uint64 remote_mmap = 0;
|
||||
uint64 remote_munmap = 0;
|
||||
ProcMapsSoEntry? remote_libc = ProcMapsSoEntry.find_by_path (pid, local_libc.path);
|
||||
#if ANDROID
|
||||
bool same_libc = remote_libc != null
|
||||
&& remote_libc.device.major == local_libc.device.major
|
||||
@@ -1066,8 +1068,8 @@ namespace Frida {
|
||||
&& remote_libc.path == local_libc.path;
|
||||
#endif
|
||||
if (same_libc) {
|
||||
remote_mmap = remote_libc.base_address + mmap_offset;
|
||||
remote_munmap = remote_libc.base_address + munmap_offset;
|
||||
remote_mmap = remote_libc.start + mmap_offset;
|
||||
remote_munmap = remote_libc.start + munmap_offset;
|
||||
}
|
||||
|
||||
if (remote_mmap != 0) {
|
||||
@@ -1165,7 +1167,9 @@ namespace Frida {
|
||||
result.context.libc = &result.libc;
|
||||
|
||||
if (result.context.rtld_flavor == ANDROID && result.libc.dlopen == null) {
|
||||
ProcMapsSoEntry? remote_ld = ProcMapsSoEntry.find_by_address (pid, (uintptr) result.context.rtld_base);
|
||||
if (restart_after_libc_load)
|
||||
remote_maps = ProcMapsSnapshot.from_pid (pid);
|
||||
var remote_ld = remote_maps.find_mapping ((uintptr) result.context.rtld_base);
|
||||
bool same_ld = remote_ld != null && local_android_ld != null
|
||||
&& remote_ld.device.equals (local_android_ld.device)
|
||||
&& remote_ld.inode == local_android_ld.inode;
|
||||
@@ -1217,9 +1221,9 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private static void * rebase_pointer (uintptr local_ptr, ProcMapsSoEntry local_module, ProcMapsSoEntry remote_module) {
|
||||
var offset = local_ptr - local_module.base_address;
|
||||
return (void *) (remote_module.base_address + offset);
|
||||
private static void * rebase_pointer (uintptr local_ptr, ProcMapsSnapshot.Mapping local, ProcMapsSnapshot.Mapping remote) {
|
||||
var offset = local_ptr - local.start;
|
||||
return (void *) (remote.start + offset);
|
||||
}
|
||||
|
||||
private static string make_fallback_address () {
|
||||
|
||||
@@ -34,7 +34,6 @@ native_outdir := $(artifacts_dir)/native/$(host_arch)
|
||||
bpf_outdir := $(artifacts_dir)/bpf
|
||||
bpf_noarch_progs := \
|
||||
activity-sampler \
|
||||
softwall \
|
||||
$(NULL)
|
||||
bpf_arch_progs := \
|
||||
syscall-tracer \
|
||||
@@ -50,11 +49,11 @@ bpf_arch_pairs := \
|
||||
|
||||
build-native: ext/linux/tools/include/nolibc/nolibc.h
|
||||
rm -rf build
|
||||
$(MESON) setup --cross-file $(crossfile) -Db_lto=true build
|
||||
$(MESON) compile -C build
|
||||
install -m644 build/bootstrapper.bin $(native_outdir)/bootstrapper.bin
|
||||
install -m644 build/loader.bin $(native_outdir)/loader.bin
|
||||
[ -f build/zymbiote.bin ] && install -m644 build/zymbiote.bin $(native_outdir)/zymbiote.bin || true
|
||||
$(MESON) setup --prefix /usr --cross-file $(crossfile) -Db_lto=true build
|
||||
DESTDIR=$(abspath build/dist) $(MESON) install -C build
|
||||
for f in build/dist/usr/share/frida-helpers/*; do \
|
||||
install -m644 $$f $(native_outdir)/; \
|
||||
done
|
||||
|
||||
bpf_targets :=
|
||||
bpf_obj := $(build_dir)/module.o
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,6 +6,7 @@ SECTIONS {
|
||||
KEEP(*(.text.entrypoint))
|
||||
*(.text*)
|
||||
*(.rodata*)
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
.dynstr : { *(.dynstr) }
|
||||
|
||||
@@ -94,14 +94,10 @@ if host_machine.system() == 'android'
|
||||
endif
|
||||
zymbiote = shared_module('zymbiote', ['zymbiote.c'],
|
||||
name_prefix: '',
|
||||
name_suffix: 'elf',
|
||||
c_args: zymbiote_flags + extra_flags,
|
||||
link_args: extra_link_args,
|
||||
link_depends: [helper_lds],
|
||||
)
|
||||
custom_target('zymbiote-bin',
|
||||
input: [zymbiote],
|
||||
output: ['zymbiote.bin'],
|
||||
command: [objcopy, objcopy_flags, '@INPUT0@', '@OUTPUT0@'],
|
||||
install_dir: 'share' / 'frida-helpers',
|
||||
install: true,
|
||||
)
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
#define MAX_TARGET_TGIDS 4096
|
||||
#define MAX_TARGET_UIDS 256
|
||||
|
||||
#define MAX_RULES 4096
|
||||
#define MAX_INODE_INDEX 8192
|
||||
|
||||
#define MAX_AUDIT_EVENTS (1 << 20)
|
||||
|
||||
typedef struct _Event Event;
|
||||
typedef __u16 EventType;
|
||||
|
||||
typedef struct _AuditEvent AuditEvent;
|
||||
|
||||
typedef struct _InodeKey InodeKey;
|
||||
typedef struct _FileOpenRule FileOpenRule;
|
||||
|
||||
typedef struct _Stats Stats;
|
||||
|
||||
struct _Event
|
||||
{
|
||||
__u64 time_ns;
|
||||
__u32 tgid;
|
||||
__u32 tid;
|
||||
|
||||
EventType type;
|
||||
|
||||
__u16 _pad0;
|
||||
};
|
||||
|
||||
enum _EventType
|
||||
{
|
||||
EVENT_TYPE_AUDIT
|
||||
};
|
||||
|
||||
struct _AuditEvent
|
||||
{
|
||||
Event parent;
|
||||
|
||||
__u32 rule_id;
|
||||
|
||||
__u32 _pad0;
|
||||
};
|
||||
|
||||
struct _InodeKey
|
||||
{
|
||||
__u64 dev;
|
||||
__u64 ino;
|
||||
};
|
||||
|
||||
struct _FileOpenRule
|
||||
{
|
||||
__u64 dev;
|
||||
__u64 ino;
|
||||
|
||||
__s32 errno_neg;
|
||||
__u32 _pad0;
|
||||
};
|
||||
|
||||
struct _Stats
|
||||
{
|
||||
__u64 emitted_events;
|
||||
__u64 emitted_bytes;
|
||||
|
||||
__u64 dropped_events;
|
||||
__u64 dropped_bytes;
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
__uint (type, BPF_MAP_TYPE_HASH);
|
||||
__uint (max_entries, MAX_TARGET_TGIDS);
|
||||
__type (key, __u32);
|
||||
__type (value, __u8);
|
||||
}
|
||||
target_tgids SEC (".maps");
|
||||
|
||||
struct
|
||||
{
|
||||
__uint (type, BPF_MAP_TYPE_HASH);
|
||||
__uint (max_entries, MAX_TARGET_UIDS);
|
||||
__type (key, __u32);
|
||||
__type (value, __u8);
|
||||
}
|
||||
target_uids SEC (".maps");
|
||||
|
||||
struct
|
||||
{
|
||||
__uint (type, BPF_MAP_TYPE_HASH);
|
||||
__uint (max_entries, MAX_RULES);
|
||||
__type (key, __u32);
|
||||
__type (value, FileOpenRule);
|
||||
}
|
||||
rules_by_id SEC (".maps");
|
||||
|
||||
struct
|
||||
{
|
||||
__uint (type, BPF_MAP_TYPE_HASH);
|
||||
__uint (max_entries, MAX_INODE_INDEX);
|
||||
__type (key, InodeKey);
|
||||
__type (value, __u32);
|
||||
}
|
||||
inode_index SEC (".maps");
|
||||
|
||||
struct
|
||||
{
|
||||
__uint (type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint (max_entries, MAX_AUDIT_EVENTS);
|
||||
}
|
||||
audit_events SEC (".maps");
|
||||
|
||||
struct
|
||||
{
|
||||
__uint (type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||
__uint (max_entries, 1);
|
||||
__type (key, __u32);
|
||||
__type (value, Stats);
|
||||
}
|
||||
stats SEC (".maps");
|
||||
|
||||
typedef __u32 dev_t;
|
||||
|
||||
struct super_block
|
||||
{
|
||||
dev_t s_dev;
|
||||
} __attribute__ ((preserve_access_index));
|
||||
|
||||
struct inode
|
||||
{
|
||||
struct super_block * i_sb;
|
||||
unsigned long i_ino;
|
||||
} __attribute__ ((preserve_access_index));
|
||||
|
||||
struct file
|
||||
{
|
||||
struct inode * f_inode;
|
||||
} __attribute__ ((preserve_access_index));
|
||||
|
||||
static bool should_enforce_current (__u32 * out_tgid, __u32 * out_tid);
|
||||
|
||||
static Stats * get_ringbuf_stats (void);
|
||||
static void note_emit (Stats * stats, __u64 bytes);
|
||||
static void note_drop (Stats * stats, __u64 wanted_bytes);
|
||||
|
||||
static AuditEvent * reserve_audit_event (void);
|
||||
|
||||
static bool get_file_inode_key (struct file * file, InodeKey * out_key);
|
||||
|
||||
SEC ("lsm/file_open")
|
||||
int
|
||||
BPF_PROG (on_file_open, struct file * file)
|
||||
{
|
||||
__u32 tgid, tid;
|
||||
if (!should_enforce_current (&tgid, &tid))
|
||||
return 0;
|
||||
|
||||
InodeKey key;
|
||||
if (!get_file_inode_key (file, &key))
|
||||
return 0;
|
||||
|
||||
__u32 * rule_id = bpf_map_lookup_elem (&inode_index, &key);
|
||||
if (rule_id == NULL)
|
||||
return 0;
|
||||
|
||||
FileOpenRule * rule = bpf_map_lookup_elem (&rules_by_id, rule_id);
|
||||
if (rule == NULL)
|
||||
return 0;
|
||||
|
||||
AuditEvent * ev = reserve_audit_event ();
|
||||
if (ev != NULL)
|
||||
{
|
||||
ev->parent.time_ns = bpf_ktime_get_ns ();
|
||||
ev->parent.tgid = tgid;
|
||||
ev->parent.tid = tid;
|
||||
|
||||
ev->parent.type = EVENT_TYPE_AUDIT;
|
||||
|
||||
ev->rule_id = *rule_id;
|
||||
ev->_pad0 = 0;
|
||||
|
||||
bpf_ringbuf_submit (ev, 0);
|
||||
}
|
||||
|
||||
return rule->errno_neg;
|
||||
}
|
||||
|
||||
static bool
|
||||
should_enforce_current (__u32 * out_tgid, __u32 * out_tid)
|
||||
{
|
||||
__u64 pid_tgid = bpf_get_current_pid_tgid ();
|
||||
__u32 tgid = pid_tgid >> 32;
|
||||
__u32 tid = (__u32) pid_tgid;
|
||||
|
||||
*out_tgid = tgid;
|
||||
*out_tid = tid;
|
||||
|
||||
__u8 * tgid_enabled = bpf_map_lookup_elem (&target_tgids, &tgid);
|
||||
if (tgid_enabled != NULL)
|
||||
return true;
|
||||
|
||||
__u64 uid_gid = bpf_get_current_uid_gid ();
|
||||
__u32 uid = (__u32) uid_gid;
|
||||
|
||||
__u8 * uid_enabled = bpf_map_lookup_elem (&target_uids, &uid);
|
||||
if (uid_enabled != NULL)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
get_file_inode_key (struct file * file, InodeKey * out_key)
|
||||
{
|
||||
if (file == NULL)
|
||||
return false;
|
||||
|
||||
struct inode * inode = BPF_CORE_READ (file, f_inode);
|
||||
if (inode == NULL)
|
||||
return false;
|
||||
|
||||
out_key->dev = BPF_CORE_READ (inode, i_sb, s_dev);
|
||||
out_key->ino = BPF_CORE_READ (inode, i_ino);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static AuditEvent *
|
||||
reserve_audit_event (void)
|
||||
{
|
||||
void * event = bpf_ringbuf_reserve (&audit_events, sizeof (AuditEvent), 0);
|
||||
|
||||
Stats * stats = get_ringbuf_stats ();
|
||||
if (event != NULL)
|
||||
note_emit (stats, sizeof (AuditEvent));
|
||||
else
|
||||
note_drop (stats, sizeof (AuditEvent));
|
||||
|
||||
return (AuditEvent *) event;
|
||||
}
|
||||
|
||||
static Stats *
|
||||
get_ringbuf_stats (void)
|
||||
{
|
||||
__u32 key = 0;
|
||||
return bpf_map_lookup_elem (&stats, &key);
|
||||
}
|
||||
|
||||
static void
|
||||
note_emit (Stats * stats, __u64 bytes)
|
||||
{
|
||||
if (stats == NULL)
|
||||
return;
|
||||
|
||||
stats->emitted_events++;
|
||||
stats->emitted_bytes += bytes;
|
||||
}
|
||||
|
||||
static void
|
||||
note_drop (Stats * stats, __u64 wanted_bytes)
|
||||
{
|
||||
if (stats == NULL)
|
||||
return;
|
||||
|
||||
stats->dropped_events++;
|
||||
stats->dropped_bytes += wanted_bytes;
|
||||
}
|
||||
|
||||
char LICENSE[] SEC ("license") = "Dual BSD/GPL";
|
||||
+155
-83
@@ -2,18 +2,28 @@
|
||||
#include <jni.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
typedef struct _FridaApi FridaApi;
|
||||
typedef struct _ZymbioteContext ZymbioteContext;
|
||||
|
||||
struct _FridaApi
|
||||
struct _ZymbioteContext
|
||||
{
|
||||
char name[64];
|
||||
char socket_path[64];
|
||||
|
||||
void ** art_method_slot;
|
||||
void * payload_base;
|
||||
size_t payload_size;
|
||||
size_t payload_original_protection;
|
||||
|
||||
char * package_name;
|
||||
|
||||
int (* original_setcontext) (uid_t uid, bool is_system_server, const char * seinfo, const char * name);
|
||||
void (* original_set_argv0) (JNIEnv * env, jobject clazz, jstring name);
|
||||
|
||||
int (* mprotect) (void * addr, size_t len, int prot);
|
||||
char * (* strdup) (const char * s);
|
||||
void (* free) (void * ptr);
|
||||
int (* socket) (int domain, int type, int protocol);
|
||||
int (* connect) (int sockfd, const struct sockaddr * addr, socklen_t addrlen);
|
||||
int * (* __errno) (void);
|
||||
@@ -25,12 +35,17 @@ struct _FridaApi
|
||||
int (* raise) (int sig);
|
||||
};
|
||||
|
||||
static volatile const FridaApi frida =
|
||||
ZymbioteContext zymbiote =
|
||||
{
|
||||
.name = "/frida-zymbiote-00000000000000000000000000000000",
|
||||
.socket_path = "/frida-zymbiote-00000000000000000000000000000000",
|
||||
};
|
||||
|
||||
static int frida_stop_and_return (JNIEnv * env, jobject clazz, jstring name);
|
||||
int frida_zymbiote_replacement_setargv0 (JNIEnv * env, jobject clazz, jstring name);
|
||||
int frida_zymbiote_replacement_setcontext (uid_t uid, bool is_system_server, const char * seinfo, const char * name);
|
||||
|
||||
static void frida_wait_for_permission_to_resume (const char * package_name, bool * revert_now);
|
||||
|
||||
static int frida_stop_and_return_from_setargv0 (JNIEnv * env, jobject clazz, jstring name);
|
||||
|
||||
static int frida_get_errno (void);
|
||||
|
||||
@@ -42,19 +57,71 @@ static ssize_t frida_recv (int sockfd, void * buf, size_t len, int flags);
|
||||
__attribute__ ((section (".text.entrypoint")))
|
||||
__attribute__ ((visibility ("default")))
|
||||
int
|
||||
frida_zymbiote_replacement_set_argv0 (JNIEnv * env, jobject clazz, jstring name)
|
||||
frida_zymbiote_replacement_setcontext (uid_t uid, bool is_system_server, const char * seinfo, const char * name)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = zymbiote.original_setcontext (uid, is_system_server, seinfo, name);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
|
||||
if (zymbiote.package_name == NULL)
|
||||
{
|
||||
zymbiote.mprotect (zymbiote.payload_base, zymbiote.payload_size, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
zymbiote.package_name = zymbiote.strdup (name);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
__attribute__ ((section (".text.entrypoint")))
|
||||
__attribute__ ((visibility ("default")))
|
||||
int
|
||||
frida_zymbiote_replacement_setargv0 (JNIEnv * env, jobject clazz, jstring name)
|
||||
{
|
||||
const char * name_utf8;
|
||||
bool revert_now;
|
||||
|
||||
zymbiote.original_set_argv0 (env, clazz, name);
|
||||
|
||||
if (zymbiote.package_name != NULL)
|
||||
name_utf8 = zymbiote.package_name;
|
||||
else
|
||||
name_utf8 = (*env)->GetStringUTFChars (env, name, NULL);
|
||||
|
||||
frida_wait_for_permission_to_resume (name_utf8, &revert_now);
|
||||
|
||||
if (zymbiote.package_name != NULL)
|
||||
{
|
||||
zymbiote.free (zymbiote.package_name);
|
||||
zymbiote.package_name = NULL;
|
||||
zymbiote.mprotect (zymbiote.payload_base, zymbiote.payload_size, zymbiote.payload_original_protection);
|
||||
}
|
||||
else
|
||||
{
|
||||
(*env)->ReleaseStringUTFChars (env, name, name_utf8);
|
||||
}
|
||||
|
||||
if (revert_now)
|
||||
{
|
||||
__attribute__ ((musttail))
|
||||
return frida_stop_and_return_from_setargv0 (env, clazz, name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
frida_wait_for_permission_to_resume (const char * package_name, bool * revert_now)
|
||||
{
|
||||
bool success = false;
|
||||
int fd;
|
||||
struct sockaddr_un addr;
|
||||
socklen_t addrlen;
|
||||
unsigned int name_len;
|
||||
|
||||
*frida.art_method_slot = frida.original_set_argv0;
|
||||
*revert_now = false;
|
||||
|
||||
frida.original_set_argv0 (env, clazz, name);
|
||||
|
||||
fd = frida.socket (AF_UNIX, SOCK_STREAM, 0);
|
||||
fd = zymbiote.socket (AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
goto beach;
|
||||
|
||||
@@ -62,15 +129,15 @@ frida_zymbiote_replacement_set_argv0 (JNIEnv * env, jobject clazz, jstring name)
|
||||
addr.sun_path[0] = '\0';
|
||||
|
||||
name_len = 0;
|
||||
for (unsigned int i = 0; i != sizeof (frida.name); i++)
|
||||
for (unsigned int i = 0; i != sizeof (zymbiote.socket_path); i++)
|
||||
{
|
||||
if (frida.name[i] == '\0')
|
||||
if (zymbiote.socket_path[i] == '\0')
|
||||
break;
|
||||
|
||||
if (1u + i >= sizeof (addr.sun_path))
|
||||
break;
|
||||
|
||||
addr.sun_path[1u + i] = frida.name[i];
|
||||
addr.sun_path[1u + i] = zymbiote.socket_path[i];
|
||||
name_len++;
|
||||
}
|
||||
|
||||
@@ -80,7 +147,6 @@ frida_zymbiote_replacement_set_argv0 (JNIEnv * env, jobject clazz, jstring name)
|
||||
goto beach;
|
||||
|
||||
{
|
||||
const char * name_utf8;
|
||||
struct
|
||||
{
|
||||
uint32_t pid;
|
||||
@@ -89,25 +155,21 @@ frida_zymbiote_replacement_set_argv0 (JNIEnv * env, jobject clazz, jstring name)
|
||||
} header;
|
||||
struct iovec iov[2];
|
||||
|
||||
name_utf8 = (*env)->GetStringUTFChars (env, name, NULL);
|
||||
|
||||
header.pid = frida.getpid ();
|
||||
header.ppid = frida.getppid ();
|
||||
header.pid = zymbiote.getpid ();
|
||||
header.ppid = zymbiote.getppid ();
|
||||
|
||||
header.package_name_len = 0;
|
||||
while (name_utf8[header.package_name_len] != '\0')
|
||||
while (package_name[header.package_name_len] != '\0')
|
||||
header.package_name_len++;
|
||||
|
||||
iov[0].iov_base = &header;
|
||||
iov[0].iov_len = sizeof (header);
|
||||
|
||||
iov[1].iov_base = (void *) name_utf8;
|
||||
iov[1].iov_base = (void *) package_name;
|
||||
iov[1].iov_len = header.package_name_len;
|
||||
|
||||
if (!frida_sendmsg_all (fd, iov, 2, MSG_NOSIGNAL))
|
||||
goto beach;
|
||||
|
||||
(*env)->ReleaseStringUTFChars (env, name, name_utf8);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -117,86 +179,96 @@ frida_zymbiote_replacement_set_argv0 (JNIEnv * env, jobject clazz, jstring name)
|
||||
goto beach;
|
||||
}
|
||||
|
||||
success = true;
|
||||
*revert_now = true;
|
||||
|
||||
beach:
|
||||
if (fd != -1)
|
||||
frida.close (fd);
|
||||
|
||||
if (success)
|
||||
{
|
||||
__attribute__ ((musttail))
|
||||
return frida_stop_and_return (env, clazz, name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
zymbiote.close (fd);
|
||||
}
|
||||
|
||||
__attribute__ ((naked, noinline))
|
||||
static int
|
||||
frida_stop_and_return (JNIEnv * env, jobject clazz, jstring name)
|
||||
{
|
||||
#if defined (__i386__)
|
||||
__asm__ __volatile__ (
|
||||
"movl $%c[sig], 4(%%esp)\n"
|
||||
|
||||
"call 1f\n"
|
||||
"1: pop %%eax\n"
|
||||
# define FRIDA_TAILCALL_TO_RAISE_SIGSTOP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"movl $%c[sig], 4(%%esp)\n" \
|
||||
\
|
||||
"call 1f\n" \
|
||||
"1: pop %%eax\n" \
|
||||
\
|
||||
"addl $(zymbiote-1b), %%eax\n" \
|
||||
"movl %c[raise_off](%%eax), %%eax\n" \
|
||||
\
|
||||
"jmp *%%eax\n" \
|
||||
: \
|
||||
: [sig] "i" (SIGSTOP), \
|
||||
[raise_off] "i" (offsetof (ZymbioteContext, raise)) \
|
||||
: "eax", "memory" \
|
||||
)
|
||||
|
||||
"addl $(frida-1b), %%eax\n"
|
||||
"movl %c[raise_off](%%eax), %%eax\n"
|
||||
|
||||
"jmp *%%eax\n"
|
||||
:
|
||||
: [sig] "i" (SIGSTOP),
|
||||
[raise_off] "i" (offsetof (FridaApi, raise))
|
||||
: "eax", "memory");
|
||||
#elif defined (__x86_64__)
|
||||
__asm__ __volatile__ (
|
||||
"mov $%c[sig], %%edi\n"
|
||||
|
||||
"leaq frida(%%rip), %%r11\n"
|
||||
"movq %c[raise_off](%%r11), %%r11\n"
|
||||
# define FRIDA_TAILCALL_TO_RAISE_SIGSTOP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"mov $%c[sig], %%edi\n" \
|
||||
\
|
||||
"leaq zymbiote(%%rip), %%r11\n" \
|
||||
"movq %c[raise_off](%%r11), %%r11\n" \
|
||||
\
|
||||
"jmp *%%r11\n" \
|
||||
: \
|
||||
: [sig] "i" (SIGSTOP), \
|
||||
[raise_off] "i" (offsetof (ZymbioteContext, raise)) \
|
||||
: "r11", "rdi", "memory" \
|
||||
)
|
||||
|
||||
"jmp *%%r11\n"
|
||||
:
|
||||
: [sig] "i" (SIGSTOP),
|
||||
[raise_off] "i" (offsetof (FridaApi, raise))
|
||||
: "r11", "rdi", "memory");
|
||||
#elif defined (__arm__)
|
||||
__asm__ __volatile__ (
|
||||
"mov r0, %[sig]\n"
|
||||
|
||||
"adr r12, frida\n"
|
||||
"ldr r12, [r12, %[raise_off]]\n"
|
||||
# define FRIDA_TAILCALL_TO_RAISE_SIGSTOP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"mov r0, %[sig]\n" \
|
||||
\
|
||||
"adr r12, zymbiote\n" \
|
||||
"ldr r12, [r12, %[raise_off]]\n" \
|
||||
\
|
||||
"bx r12\n" \
|
||||
: \
|
||||
: [sig] "i" (SIGSTOP), \
|
||||
[raise_off] "i" (offsetof (ZymbioteContext, raise)) \
|
||||
: "r12", "memory" \
|
||||
)
|
||||
|
||||
"bx r12\n"
|
||||
:
|
||||
: [sig] "i" (SIGSTOP),
|
||||
[raise_off] "i" (offsetof (FridaApi, raise))
|
||||
: "r12", "memory");
|
||||
#elif defined (__aarch64__)
|
||||
__asm__ __volatile__ (
|
||||
"mov w0, #%[sig]\n"
|
||||
|
||||
"adrp x16, frida\n"
|
||||
"add x16, x16, :lo12:frida\n"
|
||||
"ldr x16, [x16, %[raise_off]]\n"
|
||||
# define FRIDA_TAILCALL_TO_RAISE_SIGSTOP() \
|
||||
__asm__ __volatile__ ( \
|
||||
"mov w0, #%[sig]\n" \
|
||||
\
|
||||
"adrp x16, zymbiote\n" \
|
||||
"add x16, x16, :lo12:zymbiote\n" \
|
||||
"ldr x16, [x16, %[raise_off]]\n" \
|
||||
\
|
||||
"br x16\n" \
|
||||
: \
|
||||
: [sig] "i" (SIGSTOP), \
|
||||
[raise_off] "i" (offsetof (ZymbioteContext, raise)) \
|
||||
: "x16", "memory" \
|
||||
)
|
||||
|
||||
"br x16\n"
|
||||
:
|
||||
: [sig] "i" (SIGSTOP),
|
||||
[raise_off] "i" (offsetof (FridaApi, raise))
|
||||
: "x16", "memory");
|
||||
#else
|
||||
# error Unsupported architecture
|
||||
#endif
|
||||
|
||||
__attribute__ ((naked, noinline))
|
||||
static int
|
||||
frida_stop_and_return_from_setargv0 (JNIEnv * env, jobject clazz, jstring name)
|
||||
{
|
||||
FRIDA_TAILCALL_TO_RAISE_SIGSTOP ();
|
||||
}
|
||||
|
||||
static int
|
||||
frida_get_errno (void)
|
||||
{
|
||||
return *frida.__errno ();
|
||||
return *zymbiote.__errno ();
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -204,7 +276,7 @@ frida_connect (int sockfd, const struct sockaddr * addr, socklen_t addrlen)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (frida.connect (sockfd, addr, addrlen) == 0)
|
||||
if (zymbiote.connect (sockfd, addr, addrlen) == 0)
|
||||
return 0;
|
||||
|
||||
if (frida_get_errno () == EINTR)
|
||||
@@ -219,7 +291,7 @@ frida_sendmsg (int sockfd, const struct msghdr * msg, int flags)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
ssize_t n = frida.sendmsg (sockfd, msg, flags);
|
||||
ssize_t n = zymbiote.sendmsg (sockfd, msg, flags);
|
||||
if (n != -1)
|
||||
return n;
|
||||
|
||||
@@ -283,7 +355,7 @@ frida_recv (int sockfd, void * buf, size_t len, int flags)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
ssize_t n = frida.recv (sockfd, buf, len, flags);
|
||||
ssize_t n = zymbiote.recv (sockfd, buf, len, flags);
|
||||
if (n != -1)
|
||||
return n;
|
||||
|
||||
|
||||
+242
-315
@@ -405,9 +405,6 @@ namespace Frida {
|
||||
if (protocol == "syscall-trace")
|
||||
return new SyscallTraceServiceSession ();
|
||||
|
||||
if (protocol == "softwall")
|
||||
return new SoftwallServiceSession ();
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported service address");
|
||||
}
|
||||
|
||||
@@ -903,10 +900,19 @@ namespace Frida {
|
||||
}
|
||||
|
||||
if (prep.already_patched) {
|
||||
patches.apply (prep.replaced_ptr, prep.process_memory, prep.art_method_slot,
|
||||
new Bytes (prep.original_ptr));
|
||||
patches.apply (prep.replaced_setargv0_ptr, prep.process_memory, prep.setargv0_slot,
|
||||
new Bytes (prep.original_setargv0_ptr));
|
||||
} else {
|
||||
patches.apply (prep.replaced_ptr, prep.process_memory, prep.art_method_slot);
|
||||
patches.apply (prep.replaced_setargv0_ptr, prep.process_memory, prep.setargv0_slot);
|
||||
}
|
||||
|
||||
if (prep.setcontext_slot != 0) {
|
||||
if (prep.already_patched) {
|
||||
patches.apply (prep.replaced_setcontext_ptr, prep.process_memory, prep.setcontext_slot,
|
||||
new Bytes (prep.original_setcontext_ptr));
|
||||
} else {
|
||||
patches.apply (prep.replaced_setcontext_ptr, prep.process_memory, prep.setcontext_slot);
|
||||
}
|
||||
}
|
||||
|
||||
zymbiote_patches[pid] = patches;
|
||||
@@ -919,9 +925,13 @@ namespace Frida {
|
||||
public FileDescriptor process_memory;
|
||||
public bool already_patched;
|
||||
|
||||
public uint64 art_method_slot;
|
||||
public uint8[] original_ptr;
|
||||
public uint8[] replaced_ptr;
|
||||
public uint64 setargv0_slot;
|
||||
public uint8[] original_setargv0_ptr;
|
||||
public uint8[] replaced_setargv0_ptr;
|
||||
|
||||
public uint64 setcontext_slot;
|
||||
public uint8[] original_setcontext_ptr;
|
||||
public uint8[] replaced_setcontext_ptr;
|
||||
|
||||
public Bytes payload;
|
||||
public uint64 payload_base;
|
||||
@@ -955,35 +965,38 @@ namespace Frida {
|
||||
|
||||
private static ZymbiotePrepResult do_prepare_zymbiote_injection (uint pid, string server_name) throws Error, IOError {
|
||||
uint64 payload_base = 0;
|
||||
string? payload_path = null;
|
||||
int payload_original_protection = 0;
|
||||
unowned string? payload_path = null;
|
||||
uint64 payload_file_offset = 0;
|
||||
string? libc_path = null;
|
||||
string? runtime_path = null;
|
||||
Gee.List<Gum.MemoryRange?> heap_candidates = new Gee.ArrayList<Gum.MemoryRange?> ();
|
||||
unowned string? libc_path = null;
|
||||
unowned string? libselinux_path = null;
|
||||
unowned string? runtime_path = null;
|
||||
Gee.List<ProcMapsSnapshot.Mapping> heap_candidates = new Gee.ArrayList<ProcMapsSnapshot.Mapping> ();
|
||||
|
||||
var iter = ProcMapsIter.for_pid (pid);
|
||||
while (iter.next ()) {
|
||||
string path = iter.path;
|
||||
string flags = iter.flags;
|
||||
if (path.has_suffix ("/libstagefright.so") && "x" in flags) {
|
||||
var maps = ProcMapsSnapshot.from_pid (pid);
|
||||
|
||||
foreach (var m in maps) {
|
||||
unowned string path = m.path;
|
||||
if (path.has_suffix ("/libstagefright.so") && m.readable && m.executable) {
|
||||
if (payload_base == 0) {
|
||||
payload_base = iter.end_address - Gum.query_page_size ();
|
||||
payload_base = m.end - Gum.query_page_size ();
|
||||
payload_original_protection = Posix.PROT_READ | Posix.PROT_EXEC;
|
||||
if (m.writable)
|
||||
payload_original_protection |= Posix.PROT_WRITE;
|
||||
payload_path = path;
|
||||
payload_file_offset = iter.file_offset;
|
||||
payload_file_offset = m.file_offset;
|
||||
}
|
||||
} else if (path.has_suffix ("/libc.so")) {
|
||||
if (libc_path == null)
|
||||
libc_path = path;
|
||||
} else if (path.has_suffix ("/libselinux.so")) {
|
||||
if (libselinux_path == null)
|
||||
libselinux_path = path;
|
||||
} else if (path.has_suffix ("/libandroid_runtime.so")) {
|
||||
if (runtime_path == null)
|
||||
runtime_path = path;
|
||||
} else if (flags == "rw-p" && is_boot_heap (path)) {
|
||||
uint64 start = iter.start_address;
|
||||
uint64 end = iter.end_address;
|
||||
heap_candidates.add (Gum.MemoryRange () {
|
||||
base_address = start,
|
||||
size = (size_t) (end - start),
|
||||
});
|
||||
} else if (is_boot_heap (m)) {
|
||||
heap_candidates.add (m);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -996,81 +1009,182 @@ namespace Frida {
|
||||
if (heap_candidates.is_empty)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect any VM heap candidates");
|
||||
|
||||
var libc_entry = ProcMapsSoEntry.find_by_path (pid, libc_path);
|
||||
if (libc_entry == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect libc.so entry");
|
||||
var libc_mapping = maps.find_module_by_path (libc_path);
|
||||
if (libc_mapping == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect mapping for %s", libc_path);
|
||||
|
||||
var runtime_entry = ProcMapsSoEntry.find_by_path (pid, runtime_path);
|
||||
if (runtime_entry == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect libandroid_runtime.so entry");
|
||||
var runtime_mapping = maps.find_module_by_path (runtime_path);
|
||||
if (runtime_mapping == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect mapping for %s", runtime_path);
|
||||
|
||||
Gum.ElfModule libc;
|
||||
try {
|
||||
libc = new Gum.ElfModule.from_file (libc_path);
|
||||
} catch (Gum.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to parse libc.so: %s", e.message);
|
||||
throw new Error.NOT_SUPPORTED ("Unable to parse %s: %s", libc_path, e.message);
|
||||
}
|
||||
|
||||
uint64 original_setcontext = 0;
|
||||
if (libselinux_path != null) {
|
||||
Gum.ElfModule? libselinux;
|
||||
try {
|
||||
libselinux = new Gum.ElfModule.from_file (libselinux_path);
|
||||
} catch (Gum.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to parse %s: %s", libselinux_path, e.message);
|
||||
}
|
||||
|
||||
var libselinux_mapping = maps.find_module_by_path (libselinux_path);
|
||||
if (libselinux_mapping == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect mapping for %s", libselinux_path);
|
||||
|
||||
libselinux.enumerate_exports (e => {
|
||||
if (e.name == "selinux_android_setcontext") {
|
||||
original_setcontext = libselinux_mapping.start + e.address;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
Gum.ElfModule runtime;
|
||||
try {
|
||||
runtime = new Gum.ElfModule.from_file (runtime_path);
|
||||
} catch (Gum.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to parse libandroid_runtime.so: %s", e.message);
|
||||
throw new Error.NOT_SUPPORTED ("Unable to parse %s: %s", runtime_path, e.message);
|
||||
}
|
||||
|
||||
uint64 set_argv0_address = 0;
|
||||
uint64 original_setargv0 = 0;
|
||||
uint64 setcontext_slot = 0;
|
||||
runtime.enumerate_exports (e => {
|
||||
if (e.name == "_Z27android_os_Process_setArgV0P7_JNIEnvP8_jobjectP8_jstring") {
|
||||
set_argv0_address = runtime_entry.base_address + e.address;
|
||||
original_setargv0 = runtime_mapping.start + e.address;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (set_argv0_address == 0)
|
||||
if (original_setcontext != 0) {
|
||||
runtime.enumerate_imports (i => {
|
||||
if (i.name == "selinux_android_setcontext") {
|
||||
setcontext_slot = runtime_mapping.start + i.slot;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (original_setargv0 == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to locate android.os.Process.setArgV0(); please file a bug");
|
||||
|
||||
uint pointer_size = ("/lib64/" in libc_path) ? 8 : 4;
|
||||
uint pointer_size = libc.pointer_size;
|
||||
|
||||
var original_ptr = new uint8[pointer_size];
|
||||
var replaced_ptr = new uint8[pointer_size];
|
||||
|
||||
(new Buffer (new Bytes.static (original_ptr), ByteOrder.HOST, pointer_size)).write_pointer (0, set_argv0_address);
|
||||
(new Buffer (new Bytes.static (replaced_ptr), ByteOrder.HOST, pointer_size)).write_pointer (0, payload_base);
|
||||
uint64 replacement_setargv0;
|
||||
uint64 replacement_setcontext;
|
||||
Bytes payload = make_zymbiote_payload (server_name, payload_base, payload_original_protection, libc, libc_mapping,
|
||||
original_setargv0, original_setcontext, out replacement_setargv0, out replacement_setcontext);
|
||||
|
||||
var fd = open_process_memory (pid);
|
||||
|
||||
uint64 art_method_slot = 0;
|
||||
var original_setargv0_ptr = new uint8[pointer_size];
|
||||
var original_setcontext_ptr = new uint8[pointer_size];
|
||||
var replaced_setargv0_ptr = new uint8[pointer_size];
|
||||
var replaced_setcontext_ptr = new uint8[pointer_size];
|
||||
|
||||
(new Buffer (new Bytes.static (original_setargv0_ptr), ByteOrder.HOST, pointer_size))
|
||||
.write_pointer (0, original_setargv0);
|
||||
(new Buffer (new Bytes.static (original_setcontext_ptr), ByteOrder.HOST, pointer_size))
|
||||
.write_pointer (0, original_setcontext);
|
||||
(new Buffer (new Bytes.static (replaced_setargv0_ptr), ByteOrder.HOST, pointer_size))
|
||||
.write_pointer (0, replacement_setargv0);
|
||||
(new Buffer (new Bytes.static (replaced_setcontext_ptr), ByteOrder.HOST, pointer_size))
|
||||
.write_pointer (0, replacement_setcontext);
|
||||
|
||||
uint64 setargv0_slot = 0;
|
||||
bool already_patched = false;
|
||||
foreach (var candidate in heap_candidates) {
|
||||
var heap = new uint8[candidate.size];
|
||||
var n = fd.pread (heap, candidate.base_address);
|
||||
var n = fd.pread (heap, candidate.start);
|
||||
if (n != heap.length)
|
||||
throw new Error.NOT_SUPPORTED ("Short read");
|
||||
|
||||
void * p = memmem (heap, original_ptr);
|
||||
void * p = memmem (heap, original_setargv0_ptr);
|
||||
if (p == null) {
|
||||
p = memmem (heap, replaced_ptr);
|
||||
p = memmem (heap, replaced_setargv0_ptr);
|
||||
already_patched = p != null;
|
||||
}
|
||||
|
||||
if (p != null) {
|
||||
art_method_slot = candidate.base_address + ((uint8 *) p - (uint8 *) heap);
|
||||
setargv0_slot = candidate.start + ((uint8 *) p - (uint8 *) heap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (art_method_slot == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to locate method slot; please file a bug");
|
||||
if (setargv0_slot == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to locate android.os.Process.setArgV0() slot; please file a bug");
|
||||
|
||||
return new ZymbiotePrepResult () {
|
||||
process_memory = fd,
|
||||
already_patched = already_patched,
|
||||
|
||||
setargv0_slot = setargv0_slot,
|
||||
original_setargv0_ptr = original_setargv0_ptr,
|
||||
replaced_setargv0_ptr = replaced_setargv0_ptr,
|
||||
|
||||
setcontext_slot = setcontext_slot,
|
||||
original_setcontext_ptr = original_setcontext_ptr,
|
||||
replaced_setcontext_ptr = replaced_setcontext_ptr,
|
||||
|
||||
payload = payload,
|
||||
payload_base = payload_base,
|
||||
payload_path = payload_path,
|
||||
payload_file_offset = payload_file_offset,
|
||||
};
|
||||
}
|
||||
|
||||
private static Bytes make_zymbiote_payload (string server_name, uint64 payload_base, int payload_original_protection,
|
||||
Gum.ElfModule libc, ProcMapsSnapshot.Mapping libc_mapping, uint64 original_setargv0,
|
||||
uint64 original_setcontext, out uint64 replacement_setargv0, out uint64 replacement_setcontext) {
|
||||
var pointer_size = libc.pointer_size;
|
||||
|
||||
var blob = (pointer_size == 8)
|
||||
#if ARM || ARM64
|
||||
? Frida.Data.Android.get_zymbiote_arm64_bin_blob ()
|
||||
: Frida.Data.Android.get_zymbiote_arm_bin_blob ();
|
||||
? Frida.Data.Android.get_zymbiote_arm64_elf_blob ()
|
||||
: Frida.Data.Android.get_zymbiote_arm_elf_blob ();
|
||||
#else
|
||||
? Frida.Data.Android.get_zymbiote_x86_64_bin_blob ()
|
||||
: Frida.Data.Android.get_zymbiote_x86_bin_blob ();
|
||||
? Frida.Data.Android.get_zymbiote_x86_64_elf_blob ()
|
||||
: Frida.Data.Android.get_zymbiote_x86_elf_blob ();
|
||||
#endif
|
||||
|
||||
unowned uint8[] payload_template = blob.data;
|
||||
Gum.ElfModule zymbiote;
|
||||
try {
|
||||
zymbiote = new Gum.ElfModule.from_blob (new Bytes.static (blob.data));
|
||||
} catch (Gum.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
Gum.ElfSegmentDetails? text = null;
|
||||
zymbiote.enumerate_segments (s => {
|
||||
if ((s.protection & Gum.PageProtection.EXECUTE) != 0) {
|
||||
text = s;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
assert (text != null);
|
||||
|
||||
uint64 setargv0 = 0;
|
||||
uint64 setcontext = 0;
|
||||
zymbiote.enumerate_exports (e => {
|
||||
if (e.name == "frida_zymbiote_replacement_setargv0")
|
||||
setargv0 = payload_base + (e.address - text.vm_address);
|
||||
else if (e.name == "frida_zymbiote_replacement_setcontext")
|
||||
setcontext = payload_base + (e.address - text.vm_address);
|
||||
return true;
|
||||
});
|
||||
assert (setargv0 != 0);
|
||||
assert (setcontext != 0);
|
||||
replacement_setargv0 = setargv0;
|
||||
replacement_setcontext = setcontext;
|
||||
|
||||
unowned uint8[] payload_template = blob.data[text.file_offset:text.file_offset + text.file_size];
|
||||
|
||||
void * p = memmem (payload_template, "/frida-zymbiote-00000000000000000000000000000000".data);
|
||||
assert (p != null);
|
||||
size_t data_offset = (uint8 *) p - (uint8 *) payload_template;
|
||||
@@ -1081,13 +1195,27 @@ namespace Frida {
|
||||
payload.write_string (cursor, server_name);
|
||||
cursor += 64;
|
||||
|
||||
payload.write_pointer (cursor, art_method_slot);
|
||||
payload.write_pointer (cursor, payload_base);
|
||||
cursor += pointer_size;
|
||||
|
||||
payload.write_pointer (cursor, set_argv0_address);
|
||||
payload.write_pointer (cursor, payload_template.length);
|
||||
cursor += pointer_size;
|
||||
|
||||
payload.write_pointer (cursor, payload_original_protection);
|
||||
cursor += pointer_size;
|
||||
|
||||
cursor += pointer_size;
|
||||
|
||||
payload.write_pointer (cursor, original_setcontext);
|
||||
cursor += pointer_size;
|
||||
|
||||
payload.write_pointer (cursor, original_setargv0);
|
||||
cursor += pointer_size;
|
||||
|
||||
string[] wanted = {
|
||||
"mprotect",
|
||||
"strdup",
|
||||
"free",
|
||||
"socket",
|
||||
"connect",
|
||||
"__errno",
|
||||
@@ -1108,7 +1236,7 @@ namespace Frida {
|
||||
libc.enumerate_exports (e => {
|
||||
if (index_of.has_key (e.name)) {
|
||||
int idx = index_of[e.name];
|
||||
addrs[idx] = libc_entry.base_address + e.address;
|
||||
addrs[idx] = libc_mapping.start + e.address;
|
||||
pending--;
|
||||
}
|
||||
return pending != 0;
|
||||
@@ -1120,26 +1248,16 @@ namespace Frida {
|
||||
cursor += pointer_size;
|
||||
}
|
||||
|
||||
return new ZymbiotePrepResult () {
|
||||
process_memory = fd,
|
||||
already_patched = already_patched,
|
||||
|
||||
art_method_slot = art_method_slot,
|
||||
original_ptr = original_ptr,
|
||||
replaced_ptr = replaced_ptr,
|
||||
|
||||
payload = payload.bytes,
|
||||
payload_base = payload_base,
|
||||
payload_path = payload_path,
|
||||
payload_file_offset = payload_file_offset,
|
||||
};
|
||||
return payload.bytes;
|
||||
}
|
||||
|
||||
private static bool is_boot_heap (string path) {
|
||||
private static bool is_boot_heap (ProcMapsSnapshot.Mapping m) {
|
||||
if (!m.readable || !m.writable || m.executable || m.shared)
|
||||
return false;
|
||||
return
|
||||
"boot.art" in path ||
|
||||
"boot-framework.art" in path ||
|
||||
"dalvik-LinearAlloc" in path;
|
||||
"boot.art" in m.path ||
|
||||
"boot-framework.art" in m.path ||
|
||||
"dalvik-LinearAlloc" in m.path;
|
||||
}
|
||||
|
||||
[CCode (cname = "memmem", cheader_filename = "string.h")]
|
||||
@@ -2247,267 +2365,76 @@ namespace Frida {
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class SoftwallServiceSession : Object, ServiceSession {
|
||||
private Softwall? wall = new Softwall ();
|
||||
|
||||
private const size_t MAX_BATCH_BYTES = 4U * 1024U * 1024U;
|
||||
|
||||
construct {
|
||||
wall.events_available.connect (on_events_available);
|
||||
private static string inet_ntop_to_string (int af, uint8 * src, size_t srclen) {
|
||||
uint8 buf[46];
|
||||
unowned string? res = Posix.inet_ntop (af, src, buf);
|
||||
return (res != null) ? res : "?";
|
||||
}
|
||||
|
||||
public async void activate (Cancellable? cancellable) throws Error, IOError {
|
||||
ensure_active ();
|
||||
private static uint32 resolve_uid_from_username (string name) throws Error {
|
||||
unowned Posix.Passwd? pw = Posix.getpwnam (name);
|
||||
if (pw == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unknown user: %s", name);
|
||||
|
||||
return (uint32) pw.pw_uid;
|
||||
}
|
||||
|
||||
private void ensure_active () throws Error {
|
||||
if (wall == null)
|
||||
throw new Error.INVALID_OPERATION ("Service is closed");
|
||||
private delegate void UInt32Handler (uint32 v) throws Error;
|
||||
private delegate void StringHandler (string s) throws Error;
|
||||
|
||||
if (wall.state == Softwall.State.STOPPED)
|
||||
wall.start ();
|
||||
}
|
||||
private static uint32[] read_uint32_array (VariantReader reader) throws Error {
|
||||
uint n = reader.count_elements ();
|
||||
var arr = new uint32[n];
|
||||
|
||||
public async void cancel (Cancellable? cancellable) throws IOError {
|
||||
if (wall == null)
|
||||
return;
|
||||
for (uint i = 0; i != n; i++) {
|
||||
int64 v = reader.read_element (i).get_int64_value ();
|
||||
if (v < 0 || v > uint32.MAX)
|
||||
throw new Error.INVALID_ARGUMENT ("Value is out of range");
|
||||
|
||||
wall.stop ();
|
||||
wall = null;
|
||||
|
||||
close ();
|
||||
}
|
||||
|
||||
public async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError {
|
||||
ensure_active ();
|
||||
|
||||
var reader = new VariantReader (parameters);
|
||||
|
||||
string type = reader.read_member ("type").get_string_value ();
|
||||
reader.end_member ();
|
||||
|
||||
var reply = new VariantBuilder (VariantType.VARDICT);
|
||||
|
||||
if (type == "read-events") {
|
||||
var events = new VariantBuilder (new VariantType ("av"));
|
||||
|
||||
size_t total = 0;
|
||||
|
||||
var drain_status = wall.drain_events (ev => {
|
||||
Variant item = new Variant.tuple ({
|
||||
"audit",
|
||||
ev->parent.time_ns,
|
||||
ev->parent.tgid,
|
||||
ev->parent.tid,
|
||||
ev->rule_id
|
||||
});
|
||||
size_t item_size = (size_t) item.get_size ();
|
||||
|
||||
if (total != 0 && total + item_size > MAX_BATCH_BYTES)
|
||||
return Softwall.EventFlow.STOP;
|
||||
|
||||
events.add_value (new Variant.variant (item));
|
||||
total += item_size;
|
||||
|
||||
return Softwall.EventFlow.CONTINUE;
|
||||
});
|
||||
|
||||
vardict_add (reply, "events", events.end ());
|
||||
vardict_add (reply, "status",
|
||||
(drain_status == Softwall.DrainStatus.DRAINED) ? "drained" : "more");
|
||||
|
||||
return reply.end ();
|
||||
arr[i] = (uint32) v;
|
||||
reader.end_element ();
|
||||
}
|
||||
|
||||
if (type == "read-stats") {
|
||||
var stats = wall.read_stats ();
|
||||
return arr;
|
||||
}
|
||||
|
||||
vardict_add (reply, "emitted-events", stats.emitted_events);
|
||||
vardict_add (reply, "emitted-bytes", stats.emitted_bytes);
|
||||
private static uint64[] read_uint64_array (VariantReader reader) throws Error {
|
||||
uint n = reader.count_elements ();
|
||||
var arr = new uint64[n];
|
||||
|
||||
vardict_add (reply, "dropped-events", stats.dropped_events);
|
||||
vardict_add (reply, "dropped-bytes", stats.dropped_bytes);
|
||||
|
||||
return reply.end ();
|
||||
for (uint i = 0; i != n; i++) {
|
||||
arr[i] = reader.read_element (i).get_uint64_value ();
|
||||
reader.end_element ();
|
||||
}
|
||||
|
||||
if (type == "add-targets") {
|
||||
if (reader.has_member ("pids")) {
|
||||
reader.read_member ("pids");
|
||||
foreach_uint32 (reader, pid => wall.add_target_tgid (pid));
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
if (reader.has_member ("uids")) {
|
||||
reader.read_member ("uids");
|
||||
foreach_uint32 (reader, uid => wall.add_target_uid (uid));
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
if (reader.has_member ("users")) {
|
||||
reader.read_member ("users");
|
||||
foreach_string (reader, username => {
|
||||
var uid = resolve_uid_from_username (username);
|
||||
wall.add_target_uid (uid);
|
||||
});
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
return reply.end ();
|
||||
}
|
||||
|
||||
if (type == "remove-targets") {
|
||||
if (reader.has_member ("pids")) {
|
||||
reader.read_member ("pids");
|
||||
foreach_uint32 (reader, pid => wall.remove_target_tgid (pid));
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
if (reader.has_member ("uids")) {
|
||||
reader.read_member ("uids");
|
||||
foreach_uint32 (reader, uid => wall.remove_target_uid (uid));
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
if (reader.has_member ("users")) {
|
||||
reader.read_member ("users");
|
||||
foreach_string (reader, username => {
|
||||
var uid = resolve_uid_from_username (username);
|
||||
wall.remove_target_uid (uid);
|
||||
});
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
return reply.end ();
|
||||
}
|
||||
|
||||
if (type == "add-rule") {
|
||||
reader.read_member ("rule");
|
||||
|
||||
reader.read_member ("when");
|
||||
if (!reader.has_member ("file_open"))
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported rule.when (expected file_open)");
|
||||
|
||||
reader.read_member ("file_open");
|
||||
string path = reader.read_member ("path").get_string_value ();
|
||||
reader.end_member (); /* path */
|
||||
reader.end_member (); /* file_open */
|
||||
reader.end_member (); /* when */
|
||||
|
||||
string action_str = reader.read_member ("action").get_string_value ();
|
||||
reader.end_member (); /* action */
|
||||
|
||||
reader.end_member (); /* rule */
|
||||
|
||||
var action = parse_action (action_str);
|
||||
|
||||
var id = wall.add_file_open_rule (path, action);
|
||||
|
||||
vardict_add (reply, "id", id);
|
||||
|
||||
return reply.end ();
|
||||
}
|
||||
|
||||
if (type == "remove-rule") {
|
||||
uint32 id = (uint32) reader.read_member ("id").get_int64_value ();
|
||||
reader.end_member ();
|
||||
|
||||
wall.remove_rule (id);
|
||||
|
||||
return reply.end ();
|
||||
}
|
||||
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported request type: %s", type);
|
||||
return arr;
|
||||
}
|
||||
|
||||
private void on_events_available () {
|
||||
var b = new VariantBuilder (VariantType.VARDICT);
|
||||
vardict_add (b, "type", "events-available");
|
||||
message (b.end ());
|
||||
}
|
||||
private static void foreach_uint32 (VariantReader reader, UInt32Handler cb) throws Error {
|
||||
uint n = reader.count_elements ();
|
||||
for (uint i = 0; i != n; i++) {
|
||||
int64 v = reader.read_element (i).get_int64_value ();
|
||||
if (v < 0 || v > uint32.MAX)
|
||||
throw new Error.INVALID_ARGUMENT ("Value is out of range");
|
||||
|
||||
private static Softwall.Action parse_action (string s) throws Error {
|
||||
switch (s) {
|
||||
case "deny":
|
||||
return Softwall.Action.DENY;
|
||||
case "not-found":
|
||||
return Softwall.Action.NOT_FOUND;
|
||||
default:
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported action: %s", s);
|
||||
cb ((uint32) v);
|
||||
|
||||
reader.end_element ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string inet_ntop_to_string (int af, uint8 * src, size_t srclen) {
|
||||
uint8 buf[46];
|
||||
unowned string? res = Posix.inet_ntop (af, src, buf);
|
||||
return (res != null) ? res : "?";
|
||||
}
|
||||
|
||||
private uint32 resolve_uid_from_username (string name) throws Error {
|
||||
unowned Posix.Passwd? pw = Posix.getpwnam (name);
|
||||
if (pw == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unknown user: %s", name);
|
||||
|
||||
return (uint32) pw.pw_uid;
|
||||
}
|
||||
|
||||
private delegate void UInt32Handler (uint32 v) throws Error;
|
||||
private delegate void StringHandler (string s) throws Error;
|
||||
|
||||
private uint32[] read_uint32_array (VariantReader reader) throws Error {
|
||||
uint n = reader.count_elements ();
|
||||
var arr = new uint32[n];
|
||||
|
||||
for (uint i = 0; i != n; i++) {
|
||||
int64 v = reader.read_element (i).get_int64_value ();
|
||||
if (v < 0 || v > uint32.MAX)
|
||||
throw new Error.INVALID_ARGUMENT ("Value is out of range");
|
||||
|
||||
arr[i] = (uint32) v;
|
||||
reader.end_element ();
|
||||
private static void foreach_string (VariantReader reader, StringHandler cb) throws Error {
|
||||
uint n = reader.count_elements ();
|
||||
for (uint i = 0; i != n; i++) {
|
||||
unowned string s = reader.read_element (i).get_string_value ();
|
||||
cb (s);
|
||||
reader.end_element ();
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
private uint64[] read_uint64_array (VariantReader reader) throws Error {
|
||||
uint n = reader.count_elements ();
|
||||
var arr = new uint64[n];
|
||||
|
||||
for (uint i = 0; i != n; i++) {
|
||||
arr[i] = reader.read_element (i).get_uint64_value ();
|
||||
reader.end_element ();
|
||||
private static void vardict_add (VariantBuilder b, string key, Variant val) {
|
||||
b.add_value (new Variant.dict_entry (new Variant.string (key), new Variant.variant (val)));
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
private void foreach_uint32 (VariantReader reader, UInt32Handler cb) throws Error {
|
||||
uint n = reader.count_elements ();
|
||||
for (uint i = 0; i != n; i++) {
|
||||
int64 v = reader.read_element (i).get_int64_value ();
|
||||
if (v < 0 || v > uint32.MAX)
|
||||
throw new Error.INVALID_ARGUMENT ("Value is out of range");
|
||||
|
||||
cb ((uint32) v);
|
||||
|
||||
reader.end_element ();
|
||||
}
|
||||
}
|
||||
|
||||
private void foreach_string (VariantReader reader, StringHandler cb) throws Error {
|
||||
uint n = reader.count_elements ();
|
||||
for (uint i = 0; i != n; i++) {
|
||||
unowned string s = reader.read_element (i).get_string_value ();
|
||||
cb (s);
|
||||
reader.end_element ();
|
||||
}
|
||||
}
|
||||
|
||||
private void vardict_add (VariantBuilder b, string key, Variant val) {
|
||||
b.add_value (new Variant.dict_entry (new Variant.string (key), new Variant.variant (val)));
|
||||
}
|
||||
}
|
||||
+100
-71
@@ -8,27 +8,42 @@ namespace Frida {
|
||||
|
||||
private Gee.List<Mapping> maps = new Gee.ArrayList<Mapping> ();
|
||||
|
||||
private const uint64 VM_READ = 1;
|
||||
private const uint64 VM_WRITE = 2;
|
||||
private const uint64 VM_EXEC = 4;
|
||||
private const uint64 VM_SHARED = 8;
|
||||
|
||||
public class Mapping {
|
||||
public uint64 start;
|
||||
public uint64 end;
|
||||
public size_t size {
|
||||
get {
|
||||
return (size_t) (end - start);
|
||||
}
|
||||
}
|
||||
public uint64 file_offset;
|
||||
|
||||
public bool readable;
|
||||
public bool writable;
|
||||
public bool executable;
|
||||
public bool shared;
|
||||
|
||||
public DevId device;
|
||||
public uint64 inode;
|
||||
|
||||
public string module_id;
|
||||
public string path;
|
||||
|
||||
public uint32 start_gen;
|
||||
public uint32 end_gen;
|
||||
}
|
||||
|
||||
public static ProcMapsSnapshot from_pid (uint32 pid) throws Error {
|
||||
public static ProcMapsSnapshot from_pid (uint32 pid) {
|
||||
var snap = new ProcMapsSnapshot ();
|
||||
snap.build_from_pid (pid);
|
||||
return snap;
|
||||
}
|
||||
|
||||
private void build_from_pid (uint32 pid) throws Error {
|
||||
private void build_from_pid (uint32 pid) {
|
||||
maps.clear ();
|
||||
|
||||
var it = ProcMapsIter.for_pid (pid);
|
||||
@@ -40,10 +55,16 @@ namespace Frida {
|
||||
m.end = it.end_address;
|
||||
m.file_offset = it.file_offset;
|
||||
|
||||
string flags = it.flags;
|
||||
m.readable = flags[0] == 'r';
|
||||
m.writable = flags[1] == 'w';
|
||||
m.executable = flags[2] == 'x';
|
||||
m.shared = flags[3] == 's';
|
||||
|
||||
m.device = it.device;
|
||||
m.inode = it.inode;
|
||||
|
||||
m.module_id = normalize_module_id (it.path);
|
||||
m.path = normalize_path (it.path);
|
||||
|
||||
m.start_gen = 1;
|
||||
m.end_gen = 0;
|
||||
@@ -52,17 +73,26 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public void apply_create (uint32 new_gen, uint64 start, uint64 end, uint64 file_offset, DevId device, uint64 inode,
|
||||
string module_id) {
|
||||
public void apply_create (uint32 new_gen, uint64 start, uint64 end, uint64 file_offset, uint64 vm_flags, DevId device,
|
||||
uint64 inode, string path) {
|
||||
end_range_at_gen (start, end, new_gen);
|
||||
|
||||
var m = new Mapping ();
|
||||
|
||||
m.start = start;
|
||||
m.end = end;
|
||||
m.file_offset = file_offset;
|
||||
|
||||
m.readable = (vm_flags & VM_READ) != 0;
|
||||
m.writable = (vm_flags & VM_WRITE) != 0;
|
||||
m.executable = (vm_flags & VM_EXEC) != 0;
|
||||
m.shared = (vm_flags & VM_SHARED) != 0;
|
||||
|
||||
m.device = device;
|
||||
m.inode = inode;
|
||||
m.module_id = module_id;
|
||||
|
||||
m.path = path;
|
||||
|
||||
m.start_gen = new_gen;
|
||||
m.end_gen = 0;
|
||||
|
||||
@@ -108,7 +138,7 @@ namespace Frida {
|
||||
dead.file_offset = m.file_offset;
|
||||
dead.device = m.device;
|
||||
dead.inode = m.inode;
|
||||
dead.module_id = m.module_id;
|
||||
dead.path = m.path;
|
||||
dead.start_gen = m.start_gen;
|
||||
dead.end_gen = gen;
|
||||
|
||||
@@ -128,7 +158,7 @@ namespace Frida {
|
||||
dead.file_offset = m.file_offset + (start - m.start);
|
||||
dead.device = m.device;
|
||||
dead.inode = m.inode;
|
||||
dead.module_id = m.module_id;
|
||||
dead.path = m.path;
|
||||
dead.start_gen = m.start_gen;
|
||||
dead.end_gen = gen;
|
||||
|
||||
@@ -146,7 +176,7 @@ namespace Frida {
|
||||
dead.file_offset = m.file_offset + (start - m.start);
|
||||
dead.device = m.device;
|
||||
dead.inode = m.inode;
|
||||
dead.module_id = m.module_id;
|
||||
dead.path = m.path;
|
||||
dead.start_gen = m.start_gen;
|
||||
dead.end_gen = gen;
|
||||
|
||||
@@ -156,7 +186,7 @@ namespace Frida {
|
||||
right.file_offset = m.file_offset + (end - m.start);
|
||||
right.device = m.device;
|
||||
right.inode = m.inode;
|
||||
right.module_id = m.module_id;
|
||||
right.path = m.path;
|
||||
right.start_gen = m.start_gen;
|
||||
right.end_gen = 0;
|
||||
|
||||
@@ -168,13 +198,21 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private static string normalize_module_id (string path) {
|
||||
private static string normalize_path (string path) {
|
||||
const string suffix = " (deleted)";
|
||||
if (path.has_suffix (suffix))
|
||||
return path.substring (0, path.length - suffix.length);
|
||||
return path;
|
||||
}
|
||||
|
||||
public Gee.Iterator<Mapping> iterator () {
|
||||
return maps.iterator ();
|
||||
}
|
||||
|
||||
public Mapping? find_mapping (uint64 addr) {
|
||||
return find_mapping_at_gen (addr, gen);
|
||||
}
|
||||
|
||||
public Mapping? find_mapping_at_gen (uint64 addr, uint32 gen) {
|
||||
int lo = 0;
|
||||
int hi = maps.size - 1;
|
||||
@@ -224,92 +262,83 @@ namespace Frida {
|
||||
bool not_ended = m.end_gen == 0 || gen < m.end_gen;
|
||||
return started && not_ended;
|
||||
}
|
||||
}
|
||||
|
||||
public class ProcMapsSoEntry {
|
||||
public uint64 base_address;
|
||||
public string path;
|
||||
public DevId device;
|
||||
public uint64 inode;
|
||||
|
||||
private ProcMapsSoEntry (uint64 base_address, string path, DevId device, uint64 inode) {
|
||||
this.base_address = base_address;
|
||||
this.path = path;
|
||||
this.device = device;
|
||||
this.inode = inode;
|
||||
public Mapping? find_module_by_path (string path) {
|
||||
return find_module_by_path_at_gen (path, gen);
|
||||
}
|
||||
|
||||
public static ProcMapsSoEntry? find_by_address (uint pid, uint64 address) {
|
||||
var iter = ProcMapsIter.for_pid (pid);
|
||||
while (iter.next ()) {
|
||||
uint64 start = iter.start_address;
|
||||
uint64 end = iter.end_address;
|
||||
if (address >= start && address < end)
|
||||
return new ProcMapsSoEntry (start, iter.path, iter.device, iter.inode);
|
||||
}
|
||||
public Mapping? find_module_by_path_at_gen (string path, uint32 at_gen) {
|
||||
Mapping? best_base = null;
|
||||
int best_score = int.MIN;
|
||||
|
||||
return null;
|
||||
}
|
||||
Mapping? cur_base = null;
|
||||
uint cur_total = 0;
|
||||
uint cur_exec = 0;
|
||||
|
||||
public static ProcMapsSoEntry? find_by_path (uint pid, string path) {
|
||||
var candidates = new Gee.ArrayList<Candidate> ();
|
||||
Candidate? latest_candidate = null;
|
||||
var iter = ProcMapsIter.for_pid (pid);
|
||||
#if ANDROID
|
||||
unowned string libc_path = Gum.Process.get_libc_module ().path;
|
||||
#endif
|
||||
while (iter.next ()) {
|
||||
string current_path = iter.path;
|
||||
if (current_path == "[page size compat]")
|
||||
|
||||
for (int i = 0; i < maps.size; i++) {
|
||||
var m = maps[i];
|
||||
if (!mapping_alive_at (m, at_gen))
|
||||
continue;
|
||||
if (current_path != path) {
|
||||
latest_candidate = null;
|
||||
|
||||
unowned string mp = m.path;
|
||||
|
||||
if (mp == "[page size compat]")
|
||||
continue;
|
||||
|
||||
if (mp != path) {
|
||||
consider_candidate (ref best_base, ref best_score, cur_base, cur_total, cur_exec);
|
||||
cur_base = null;
|
||||
cur_total = 0;
|
||||
cur_exec = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
string flags = iter.flags;
|
||||
|
||||
#if ANDROID
|
||||
if (current_path == libc_path && flags[3] == 's')
|
||||
if (mp == libc_path && m.shared)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
if (iter.file_offset == 0) {
|
||||
latest_candidate = new Candidate () {
|
||||
entry = new ProcMapsSoEntry (iter.start_address, current_path, iter.device, iter.inode),
|
||||
total_ranges = 0,
|
||||
executable_ranges = 0,
|
||||
};
|
||||
candidates.add (latest_candidate);
|
||||
if (m.file_offset == 0) {
|
||||
consider_candidate (ref best_base, ref best_score, cur_base, cur_total, cur_exec);
|
||||
cur_base = m;
|
||||
cur_total = 0;
|
||||
cur_exec = 0;
|
||||
}
|
||||
|
||||
if (latest_candidate != null) {
|
||||
latest_candidate.total_ranges++;
|
||||
if (flags[2] == 'x')
|
||||
latest_candidate.executable_ranges++;
|
||||
if (cur_base != null) {
|
||||
cur_total++;
|
||||
if (m.executable)
|
||||
cur_exec++;
|
||||
}
|
||||
}
|
||||
|
||||
candidates.sort ((a, b) => b.score () - a.score ());
|
||||
consider_candidate (ref best_base, ref best_score, cur_base, cur_total, cur_exec);
|
||||
|
||||
if (candidates.is_empty)
|
||||
return null;
|
||||
|
||||
return candidates.first ().entry;
|
||||
return best_base;
|
||||
}
|
||||
|
||||
private class Candidate {
|
||||
public ProcMapsSoEntry entry;
|
||||
public uint total_ranges;
|
||||
public uint executable_ranges;
|
||||
private static void consider_candidate (ref Mapping? best_base, ref int best_score, Mapping? candidate_base, uint total,
|
||||
uint exec) {
|
||||
if (candidate_base == null)
|
||||
return;
|
||||
|
||||
public int score () {
|
||||
int result = (int) total_ranges;
|
||||
if (executable_ranges == 0)
|
||||
result = -result;
|
||||
return result;
|
||||
int score = score_candidate (total, exec);
|
||||
if (score > best_score) {
|
||||
best_score = score;
|
||||
best_base = candidate_base;
|
||||
}
|
||||
}
|
||||
|
||||
private static int score_candidate (uint total, uint exec) {
|
||||
int score = (int) total;
|
||||
if (exec == 0)
|
||||
score = -score;
|
||||
return score;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ProcMapsIter {
|
||||
|
||||
@@ -1,298 +0,0 @@
|
||||
public sealed class Frida.Softwall : Object {
|
||||
public signal void events_available ();
|
||||
|
||||
public State state {
|
||||
get {
|
||||
return _state;
|
||||
}
|
||||
}
|
||||
|
||||
public enum State {
|
||||
STOPPED,
|
||||
STARTED,
|
||||
}
|
||||
|
||||
private State _state = STOPPED;
|
||||
|
||||
private BpfMap? target_tgids;
|
||||
private BpfMap? target_uids;
|
||||
|
||||
private BpfMap? rules_by_id;
|
||||
private BpfMap? inode_index;
|
||||
|
||||
private BpfMap? stats;
|
||||
|
||||
private BpfRingbufReader? events_reader;
|
||||
private IOChannel? events_channel;
|
||||
private Source? events_source;
|
||||
|
||||
private Gee.Collection<BpfLink> links = new Gee.ArrayList<BpfLink> ();
|
||||
|
||||
private uint32 next_rule_id = 1;
|
||||
|
||||
protected override void dispose () {
|
||||
stop ();
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
public void start () throws Error {
|
||||
assert (state == STOPPED);
|
||||
|
||||
var obj = BpfObject.open ("softwall.elf", Frida.Data.HelperBackend.get_softwall_elf_blob ().data);
|
||||
|
||||
target_tgids = obj.maps.get_by_name ("target_tgids");
|
||||
target_uids = obj.maps.get_by_name ("target_uids");
|
||||
|
||||
rules_by_id = obj.maps.get_by_name ("rules_by_id");
|
||||
inode_index = obj.maps.get_by_name ("inode_index");
|
||||
|
||||
var events = obj.maps.get_by_name ("audit_events");
|
||||
|
||||
stats = obj.maps.get_by_name ("stats");
|
||||
|
||||
obj.prepare ();
|
||||
|
||||
events_reader = new BpfRingbufReader (events);
|
||||
|
||||
obj.load ();
|
||||
|
||||
foreach (var program in obj.programs) {
|
||||
var link = program.attach ();
|
||||
links.add (link);
|
||||
}
|
||||
|
||||
events_channel = new IOChannel.unix_new (events.fd);
|
||||
arm_events_watch ();
|
||||
|
||||
_state = STARTED;
|
||||
}
|
||||
|
||||
public void stop () {
|
||||
events_source?.destroy ();
|
||||
events_source = null;
|
||||
events_channel = null;
|
||||
events_reader = null;
|
||||
|
||||
links.clear ();
|
||||
|
||||
stats = null;
|
||||
|
||||
inode_index = null;
|
||||
rules_by_id = null;
|
||||
|
||||
target_uids = null;
|
||||
target_tgids = null;
|
||||
|
||||
next_rule_id = 1;
|
||||
|
||||
_state = STOPPED;
|
||||
}
|
||||
|
||||
public void add_target_tgid (uint tgid) throws Error {
|
||||
target_tgids.update_u32_u8 (tgid, 1);
|
||||
}
|
||||
|
||||
public void remove_target_tgid (uint tgid) throws Error {
|
||||
target_tgids.remove_u32 (tgid);
|
||||
}
|
||||
|
||||
public void add_target_uid (uint uid) throws Error {
|
||||
target_uids.update_u32_u8 (uid, 1);
|
||||
}
|
||||
|
||||
public void remove_target_uid (uint uid) throws Error {
|
||||
target_uids.remove_u32 (uid);
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
DENY,
|
||||
NOT_FOUND,
|
||||
}
|
||||
|
||||
public uint32 add_file_open_rule (string path, Action action) throws Error {
|
||||
assert (state == STARTED);
|
||||
|
||||
uint64 dev, ino;
|
||||
resolve_path_to_inode (path, out dev, out ino);
|
||||
|
||||
var id = next_rule_id++;
|
||||
var errno_neg = map_action_to_errno (action);
|
||||
|
||||
var rule = FileOpenRule ();
|
||||
rule.dev = dev;
|
||||
rule.ino = ino;
|
||||
rule.errno_neg = errno_neg;
|
||||
rule._pad0 = 0;
|
||||
|
||||
rules_by_id.update_raw ((uint8[]) &id, (uint8[]) &rule);
|
||||
|
||||
var key = InodeKey ();
|
||||
key.dev = dev;
|
||||
key.ino = ino;
|
||||
|
||||
inode_index.update_raw ((uint8[]) &key, (uint8[]) &id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public void remove_rule (uint32 id) throws Error {
|
||||
assert (state == STARTED);
|
||||
|
||||
var rule = FileOpenRule ();
|
||||
try {
|
||||
rules_by_id.lookup_raw ((uint8[]) &id, (uint8[]) &rule);
|
||||
} catch (Error e) {
|
||||
throw new Error.INVALID_ARGUMENT ("Unknown rule id: %u".printf (id));
|
||||
}
|
||||
|
||||
var key = InodeKey ();
|
||||
key.dev = rule.dev;
|
||||
key.ino = rule.ino;
|
||||
|
||||
try {
|
||||
inode_index.remove_raw ((uint8[]) &key);
|
||||
} catch (Error e) {
|
||||
}
|
||||
|
||||
rules_by_id.remove_u32 (id);
|
||||
}
|
||||
|
||||
public DrainStatus drain_events (AuditEventHandler on_event) {
|
||||
var status = events_reader.drain (payload => {
|
||||
assert (payload.length >= sizeof (AuditEvent));
|
||||
var e = (AuditEvent *) payload;
|
||||
|
||||
return (on_event (e) == CONTINUE)
|
||||
? BpfRingbufReader.RecordAction.CONTINUE
|
||||
: BpfRingbufReader.RecordAction.STOP;
|
||||
});
|
||||
|
||||
if (status == DRAINED)
|
||||
arm_events_watch ();
|
||||
|
||||
return (status == DRAINED)
|
||||
? DrainStatus.DRAINED
|
||||
: DrainStatus.STOPPED;
|
||||
}
|
||||
|
||||
public enum DrainStatus {
|
||||
DRAINED,
|
||||
STOPPED,
|
||||
}
|
||||
|
||||
public delegate EventFlow AuditEventHandler (AuditEvent * ev);
|
||||
|
||||
public enum EventFlow {
|
||||
CONTINUE,
|
||||
STOP,
|
||||
}
|
||||
|
||||
private void arm_events_watch () {
|
||||
if (events_source != null)
|
||||
return;
|
||||
|
||||
var src = new IOSource (events_channel, IOCondition.IN);
|
||||
var state = new AuditEventsWatchState (this);
|
||||
src.set_callback (state.on_ready);
|
||||
src.attach (MainContext.get_thread_default ());
|
||||
events_source = src;
|
||||
}
|
||||
|
||||
private class AuditEventsWatchState : Object {
|
||||
public unowned Softwall wall;
|
||||
|
||||
public AuditEventsWatchState (Softwall wall) {
|
||||
this.wall = wall;
|
||||
}
|
||||
|
||||
public bool on_ready (IOChannel ch, IOCondition cond) {
|
||||
wall.on_events_readable ();
|
||||
return Source.REMOVE;
|
||||
}
|
||||
}
|
||||
|
||||
private void on_events_readable () {
|
||||
events_source?.destroy ();
|
||||
events_source = null;
|
||||
|
||||
events_available ();
|
||||
}
|
||||
|
||||
public Stats read_stats () throws Error {
|
||||
Stats total = Stats ();
|
||||
|
||||
uint32 key = 0;
|
||||
stats.foreach_percpu_value<Stats?> ((uint8[]) &key, (cpu, s) => {
|
||||
total.emitted_events += s.emitted_events;
|
||||
total.emitted_bytes += s.emitted_bytes;
|
||||
|
||||
total.dropped_events += s.dropped_events;
|
||||
total.dropped_bytes += s.dropped_bytes;
|
||||
});
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
private static int32 map_action_to_errno (Action action) {
|
||||
switch (action) {
|
||||
case Action.NOT_FOUND:
|
||||
return -Posix.ENOENT;
|
||||
case Action.DENY:
|
||||
default:
|
||||
return -Posix.EACCES;
|
||||
}
|
||||
}
|
||||
|
||||
private static void resolve_path_to_inode (string path, out uint64 dev, out uint64 ino) throws Error {
|
||||
Posix.Stat st;
|
||||
if (Posix.stat (path, out st) != 0)
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to stat path: %s", path);
|
||||
|
||||
dev = (uint64) st.st_dev;
|
||||
ino = (uint64) st.st_ino;
|
||||
}
|
||||
|
||||
public struct Event {
|
||||
public uint64 time_ns;
|
||||
public uint32 tgid;
|
||||
public uint32 tid;
|
||||
|
||||
public uint16 type;
|
||||
|
||||
public uint16 _pad0;
|
||||
}
|
||||
|
||||
public enum EventType {
|
||||
AUDIT
|
||||
}
|
||||
|
||||
public struct AuditEvent {
|
||||
public Event parent;
|
||||
|
||||
public uint32 rule_id;
|
||||
|
||||
public uint32 _pad0;
|
||||
}
|
||||
|
||||
public struct InodeKey {
|
||||
public uint64 dev;
|
||||
public uint64 ino;
|
||||
}
|
||||
|
||||
private struct FileOpenRule {
|
||||
public uint64 dev;
|
||||
public uint64 ino;
|
||||
|
||||
public int32 errno_neg;
|
||||
public uint32 _pad0;
|
||||
}
|
||||
|
||||
public struct Stats {
|
||||
public uint64 emitted_events;
|
||||
public uint64 emitted_bytes;
|
||||
|
||||
public uint64 dropped_events;
|
||||
public uint64 dropped_bytes;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ public sealed class Frida.SymbolResolver : Object {
|
||||
public uint64 end;
|
||||
|
||||
public uint64 file_offset;
|
||||
public uint64 vm_flags;
|
||||
public DevId device;
|
||||
public uint64 inode;
|
||||
public string? path;
|
||||
@@ -34,12 +35,12 @@ public sealed class Frida.SymbolResolver : Object {
|
||||
}
|
||||
|
||||
uint mod_idx;
|
||||
if (!used_index.has_key (m.module_id)) {
|
||||
if (!used_index.has_key (m.path)) {
|
||||
mod_idx = used_modules.size;
|
||||
used_index[m.module_id] = mod_idx;
|
||||
used_modules.add (m.module_id);
|
||||
used_index[m.path] = mod_idx;
|
||||
used_modules.add (m.path);
|
||||
} else {
|
||||
mod_idx = used_index[m.module_id];
|
||||
mod_idx = used_index[m.path];
|
||||
}
|
||||
|
||||
uint64 rel64 = (addr - m.start) + m.file_offset;
|
||||
@@ -92,6 +93,7 @@ public sealed class Frida.SymbolResolver : Object {
|
||||
ev.start = start;
|
||||
ev.end = end;
|
||||
ev.file_offset = file_offset;
|
||||
ev.vm_flags = vm_flags;
|
||||
ev.device = device;
|
||||
ev.inode = inode;
|
||||
ev.path = path;
|
||||
@@ -143,8 +145,8 @@ public sealed class Frida.SymbolResolver : Object {
|
||||
q.unset (next_gen);
|
||||
|
||||
if (ev.is_create) {
|
||||
string module_id = make_module_id (ev.device, ev.inode, ev.path);
|
||||
snap.apply_create (next_gen, ev.start, ev.end, ev.file_offset, ev.device, ev.inode, module_id);
|
||||
string path = make_path (ev.device, ev.inode, ev.path);
|
||||
snap.apply_create (next_gen, ev.start, ev.end, ev.file_offset, ev.vm_flags, ev.device, ev.inode, path);
|
||||
} else {
|
||||
snap.apply_destroy_range (next_gen, ev.start, ev.end);
|
||||
}
|
||||
@@ -159,7 +161,7 @@ public sealed class Frida.SymbolResolver : Object {
|
||||
cache.clear ();
|
||||
}
|
||||
|
||||
private static string make_module_id (DevId device, uint64 inode, string? path) {
|
||||
private static string make_path (DevId device, uint64 inode, string? path) {
|
||||
if (path != null)
|
||||
return path;
|
||||
|
||||
|
||||
+5
-8
@@ -38,10 +38,10 @@ if host_os == 'android' or have_droidy_backend
|
||||
input: [
|
||||
'android.resources',
|
||||
android_helper_dex,
|
||||
fs.copyfile(helpers_native_prefix / 'x86' / 'zymbiote.bin', 'zymbiote-x86.bin'),
|
||||
fs.copyfile(helpers_native_prefix / 'x86_64' / 'zymbiote.bin', 'zymbiote-x86_64.bin'),
|
||||
fs.copyfile(helpers_native_prefix / 'arm' / 'zymbiote.bin', 'zymbiote-arm.bin'),
|
||||
fs.copyfile(helpers_native_prefix / 'arm64' / 'zymbiote.bin', 'zymbiote-arm64.bin'),
|
||||
fs.copyfile(helpers_native_prefix / 'x86' / 'zymbiote.elf', 'zymbiote-x86.elf'),
|
||||
fs.copyfile(helpers_native_prefix / 'x86_64' / 'zymbiote.elf', 'zymbiote-x86_64.elf'),
|
||||
fs.copyfile(helpers_native_prefix / 'arm' / 'zymbiote.elf', 'zymbiote-arm.elf'),
|
||||
fs.copyfile(helpers_native_prefix / 'arm64' / 'zymbiote.elf', 'zymbiote-arm64.elf'),
|
||||
],
|
||||
output: [
|
||||
'frida-data-android.vapi',
|
||||
@@ -333,7 +333,6 @@ if have_local_backend
|
||||
'linux' / 'frida-helper-types.vala',
|
||||
'linux' / 'activity-sampler.vala',
|
||||
'linux' / 'syscall-tracer.vala',
|
||||
'linux' / 'softwall.vala',
|
||||
'linux' / 'symbol-resolver.vala',
|
||||
'linux' / 'proc-maps.vala',
|
||||
]
|
||||
@@ -363,9 +362,8 @@ if have_local_backend
|
||||
'linux' / 'frida-helper-backend.resources',
|
||||
helpers_native_dir / 'bootstrapper.bin',
|
||||
helpers_native_dir / 'loader.bin',
|
||||
helpers_bpf_noarch_dir / 'activity-sampler.elf',
|
||||
helpers_bpf_noarch_dir / 'activity-sampler.elf',
|
||||
helpers_bpf_arch_dir / 'syscall-tracer.elf',
|
||||
helpers_bpf_noarch_dir / 'softwall.elf',
|
||||
],
|
||||
output: [
|
||||
'frida-data-helper-backend.vapi',
|
||||
@@ -381,7 +379,6 @@ if have_local_backend
|
||||
'@INPUT2@',
|
||||
'@INPUT3@',
|
||||
'@INPUT4@',
|
||||
'@INPUT5@',
|
||||
],
|
||||
)
|
||||
helper_backend_sources += [helper_backend_data]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = bda339b66655f29b5e82277b68235addfbd8fac7
|
||||
revision = 17.7.2
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
Reference in new issue
Block a user