mirror of
https://github.com/frida/frida-core.git
synced 2026-11-08 08:05:58 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0ada4a9ff |
No files matched your search
@@ -34,6 +34,7 @@ native_outdir := $(artifacts_dir)/native/$(host_arch)
|
||||
bpf_outdir := $(artifacts_dir)/bpf
|
||||
bpf_noarch_progs := \
|
||||
activity-sampler \
|
||||
softwall \
|
||||
$(NULL)
|
||||
bpf_arch_progs := \
|
||||
syscall-tracer \
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,275 @@
|
||||
#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";
|
||||
@@ -405,6 +405,9 @@ namespace Frida {
|
||||
if (protocol == "syscall-trace")
|
||||
return new SyscallTraceServiceSession ();
|
||||
|
||||
if (protocol == "softwall")
|
||||
return new SoftwallServiceSession ();
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported service address");
|
||||
}
|
||||
|
||||
@@ -2244,76 +2247,267 @@ namespace Frida {
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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 : "?";
|
||||
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 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;
|
||||
public async void activate (Cancellable? cancellable) throws Error, IOError {
|
||||
ensure_active ();
|
||||
}
|
||||
|
||||
private delegate void UInt32Handler (uint32 v) throws Error;
|
||||
private delegate void StringHandler (string s) throws Error;
|
||||
private void ensure_active () throws Error {
|
||||
if (wall == null)
|
||||
throw new Error.INVALID_OPERATION ("Service is closed");
|
||||
|
||||
private static uint32[] read_uint32_array (VariantReader reader) throws Error {
|
||||
uint n = reader.count_elements ();
|
||||
var arr = new uint32[n];
|
||||
if (wall.state == Softwall.State.STOPPED)
|
||||
wall.start ();
|
||||
}
|
||||
|
||||
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");
|
||||
public async void cancel (Cancellable? cancellable) throws IOError {
|
||||
if (wall == null)
|
||||
return;
|
||||
|
||||
arr[i] = (uint32) v;
|
||||
reader.end_element ();
|
||||
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 ();
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
if (type == "read-stats") {
|
||||
var stats = wall.read_stats ();
|
||||
|
||||
private static uint64[] read_uint64_array (VariantReader reader) throws Error {
|
||||
uint n = reader.count_elements ();
|
||||
var arr = new uint64[n];
|
||||
vardict_add (reply, "emitted-events", stats.emitted_events);
|
||||
vardict_add (reply, "emitted-bytes", stats.emitted_bytes);
|
||||
|
||||
for (uint i = 0; i != n; i++) {
|
||||
arr[i] = reader.read_element (i).get_uint64_value ();
|
||||
reader.end_element ();
|
||||
vardict_add (reply, "dropped-events", stats.dropped_events);
|
||||
vardict_add (reply, "dropped-bytes", stats.dropped_bytes);
|
||||
|
||||
return reply.end ();
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
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 ();
|
||||
}
|
||||
|
||||
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");
|
||||
if (reader.has_member ("uids")) {
|
||||
reader.read_member ("uids");
|
||||
foreach_uint32 (reader, uid => wall.add_target_uid (uid));
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
cb ((uint32) v);
|
||||
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 ();
|
||||
}
|
||||
|
||||
reader.end_element ();
|
||||
return reply.end ();
|
||||
}
|
||||
}
|
||||
|
||||
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 ();
|
||||
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);
|
||||
}
|
||||
|
||||
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)));
|
||||
private void on_events_available () {
|
||||
var b = new VariantBuilder (VariantType.VARDICT);
|
||||
vardict_add (b, "type", "events-available");
|
||||
message (b.end ());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 ();
|
||||
}
|
||||
|
||||
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 ();
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -333,6 +333,7 @@ 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',
|
||||
]
|
||||
@@ -362,8 +363,9 @@ 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',
|
||||
@@ -379,6 +381,7 @@ if have_local_backend
|
||||
'@INPUT2@',
|
||||
'@INPUT3@',
|
||||
'@INPUT4@',
|
||||
'@INPUT5@',
|
||||
],
|
||||
)
|
||||
helper_backend_sources += [helper_backend_data]
|
||||
|
||||
Reference in new issue
Block a user