Skip to content

Commit

Permalink
events: hidden_kernel_module changes
Browse files Browse the repository at this point in the history
- Do not use GetSymbolByName (as it's not crucial and seem to have a
  strong impact on performance)
- Fix capability issue in getting the addresses from /proc/modules
- Cosmetics
  • Loading branch information
OriGlassman authored and randomname21 committed Jun 21, 2023
1 parent eb3c959 commit 204d045
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 134 deletions.
64 changes: 21 additions & 43 deletions pkg/ebpf/c/tracee.bpf.c
Expand Up @@ -646,9 +646,9 @@ statfunc bool init_shown_modules()
{
char modules_sym[8] = "modules";
struct list_head *head = (struct list_head *) get_symbol_addr(modules_sym);
kernel_module_t ker_mod = {};
bool iterated_all_modules = false;
struct module *pos, *n;
kernel_module_t *mod_from_map;

pos = list_first_entry_ebpf(head, typeof(*pos), list);
n = pos;
Expand All @@ -662,13 +662,8 @@ statfunc bool init_shown_modules()
iterated_all_modules = true;
break;
}
mod_from_map = bpf_map_lookup_elem(&modules_map, &pos);
if (mod_from_map == NULL) {
kernel_module_t m = {.seen_modules_list = true};
bpf_map_update_elem(&modules_map, &pos, &m, BPF_ANY);
} else {
mod_from_map->seen_modules_list = true; // updates the entry in map
}

bpf_map_update_elem(&modules_map, &pos, &ker_mod, BPF_ANY);
}

return !iterated_all_modules; // False is valid value
Expand All @@ -679,8 +674,7 @@ static u64 last_module_insert_time = 0;

statfunc bool is_hidden(u64 mod)
{
kernel_module_t *mod_from_map = bpf_map_lookup_elem(&modules_map, &mod);
if (mod_from_map != NULL && mod_from_map->seen_modules_list) {
if (bpf_map_lookup_elem(&modules_map, &mod) != NULL) {
return false;
}

Expand Down Expand Up @@ -838,12 +832,12 @@ static __always_inline u64 check_new_mods_only(program_data_t *p)
return 0;
}

statfunc bool check_is_proc_modules_hooked(program_data_t *p)
statfunc int check_is_proc_modules_hooked(program_data_t *p)
{
struct module *pos, *n;
bool finished_iterating = false;
int ret = 2;
u64 mod_base_addr;
char modules_sym[8] = "modules";
kernel_module_t *mod_from_map;
struct list_head *head = (struct list_head *) get_symbol_addr(modules_sym);
u32 flags = PROC_MODULES | HIDDEN_MODULE;

Expand All @@ -855,14 +849,17 @@ statfunc bool check_is_proc_modules_hooked(program_data_t *p)
pos = n;
n = list_next_entry_ebpf(n, list);
if (&pos->list == head) {
finished_iterating = true;
ret = 0;
break;
}

u64 mod = (u64) pos;
mod_from_map = bpf_map_lookup_elem(&modules_map, &mod);

if ((mod_from_map == NULL) || (mod_from_map != NULL && !mod_from_map->seen_proc_modules)) {
// Check with the address being the start of the memory area, since
// the address from /proc/modules is the base core layout.
mod_base_addr = (u64) BPF_CORE_READ(pos, core_layout.base);
if (unlikely(mod_base_addr == 0)) { // Module memory was possibly tampered.. submit an error
ret = 7;
break;
} else if (bpf_map_lookup_elem(&modules_map, &mod_base_addr) == NULL) {
// Was there any recent insertion of a module since we populated
// modules_list? if so, don't report as there's possible race
// condition. Note that this granularity (insertion of any module
Expand All @@ -874,36 +871,16 @@ statfunc bool check_is_proc_modules_hooked(program_data_t *p)
// the init_shown_mods time, but the time proc modules map was
// filled (userspace) - so assume it happened max 2 seconds prior to
// that.

if (start_scan_time_init_shown_mods - (2 * 1000000000) < last_module_insert_time) {
continue;
}

// Check again with the address being the start of the memory area, since
// there's a chance the module is in /proc/modules but not in /proc/kallsyms (since the
// file can be hooked).
mod = (u64) BPF_CORE_READ(pos, core_layout.base);
mod_from_map = bpf_map_lookup_elem(&modules_map, &mod);

// No need to check for seen_proc_modules flag here since if it IS
// in the map with the address being the start of the memory area,
// it necessarily got inserted to the map via the /proc/modules
// userspace logic.

if (mod_from_map == NULL) {
// Module was not seen in proc modules, report.
lkm_seeker_send_to_userspace(pos, &flags, p);
}

// We couldn't resolve the address from kallsyms but we did see the
// module in /proc/modules. This probably means that /proc/kallsyms
// is hooked, but we consider this module not hidden, as tools like
// lsmod will show it. Thus we gracefully continue and don't report
// this.
// Module was not seen in proc modules and there was no recent insertion, report.
lkm_seeker_send_to_userspace(pos, &flags, p);
}
}

return !finished_iterating;
return ret;
}

statfunc bool kern_ver_below_min_lkm(struct pt_regs *ctx)
Expand Down Expand Up @@ -1009,8 +986,9 @@ int lkm_seeker_proc_tail(struct pt_regs *ctx)
if (!init_tailcall_program_data(&p, ctx))
return -1;

if (check_is_proc_modules_hooked(&p) != 0) {
tracee_log(ctx, BPF_LOG_LVL_ERROR, BPF_LOG_ID_UNSPEC, 2);
int ret = check_is_proc_modules_hooked(&p);
if (ret != 0) {
tracee_log(ctx, BPF_LOG_LVL_ERROR, BPF_LOG_ID_UNSPEC, ret);
return 1;
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/ebpf/c/tracee.h
Expand Up @@ -28,15 +28,15 @@ statfunc int common_utimes(struct pt_regs *);
statfunc int common_file_modification_ent(struct pt_regs *);
statfunc int common_file_modification_ret(struct pt_regs *);

// TODO: related do kernel modules
// TODO: related to kernel modules

statfunc bool init_shown_modules();
statfunc bool is_hidden(u64);
statfunc bool find_modules_from_module_kset_list(program_data_t *);
statfunc struct latch_tree_node *__lt_from_rb(struct rb_node *, int);
statfunc bool walk_mod_tree(program_data_t *p, struct rb_node *, int);
statfunc bool find_modules_from_mod_tree(program_data_t *);
statfunc bool check_is_proc_modules_hooked(program_data_t *);
statfunc int check_is_proc_modules_hooked(program_data_t *);

// TODO: related to bpf tracing

Expand Down
3 changes: 1 addition & 2 deletions pkg/ebpf/c/types.h
Expand Up @@ -464,8 +464,7 @@ typedef struct net_ctx_ext {
} net_ctx_ext_t;

typedef struct kernel_mod {
bool seen_proc_modules;
bool seen_modules_list;
bool unused; // Empty struct yields an error from the verifier: "Invalid argument(-22)""
} kernel_module_t;

typedef struct kernel_new_mod {
Expand Down
29 changes: 16 additions & 13 deletions pkg/ebpf/hidden_kernel_module.go
Expand Up @@ -75,13 +75,12 @@ func (t *Tracee) lkmSeekerRoutine(ctx gocontext.Context) {
}

// Since on each module load the scan is triggered, the following variables
// are used to enforce that we scan at most once in minSleepBetweenRuns
// seconds, to avoid exhausting the system

// are used to enforce that we scan at most once in throttleSecs
// to avoid exhausting the system
lastTriggerTime := time.Now()
var throttleTimer <-chan time.Time

run := true // marks when the lkm hiding whole seeking logic should run.
run := true // Marks when the lkm hiding whole seeking logic should run.

for {
if run {
Expand All @@ -90,7 +89,7 @@ func (t *Tracee) lkmSeekerRoutine(ctx gocontext.Context) {
continue // A run is scheduled in the future, so don't run yet
}

// Throttling Timer: Do not execute before minSleepBetweenRuns!
// Throttling Timer: Do not execute before throttleSecs!
// (safe-guard against exhausting the system)

if lastTriggerTime.Add(throttleSecs * time.Second).After(time.Now()) {
Expand All @@ -103,21 +102,25 @@ func (t *Tracee) lkmSeekerRoutine(ctx gocontext.Context) {
continue
}

// update eBPF maps for kernel logic
err = derive.FillModulesFromProcFs(t.kernelSymbols)
// Update eBPF maps for kernel logic
err = derive.FillModulesFromProcFs()
if err != nil {
logger.Errorw("Hidden kernel module seeker stopped!: " + err.Error())
return
}

// prepare throttle timer
// Prepare throttle timer
lastTriggerTime = time.Now()

// run kernel logic
// Run kernel logic
t.triggerKernelModuleSeeker()

// clear eBPF maps
derive.ClearModulesState()
// Clear eBPF maps
err := derive.ClearModulesState()
if err != nil {
logger.Errorw("Hidden kernel module seeker stopped!: failed clearing maps. " + err.Error())
return
}

run = false
} else {
Expand All @@ -126,10 +129,10 @@ func (t *Tracee) lkmSeekerRoutine(ctx gocontext.Context) {
return

case <-time.After(generateRandomDuration(10, 300)):
run = true // run from time to time.
run = true // Run from time to time.

case <-throttleTimer:
throttleTimer = nil // cool-down period ended...
throttleTimer = nil // Cool-down period ended...
run = true // ...run now!

case scanReq := <-wakeupChan:
Expand Down

0 comments on commit 204d045

Please sign in to comment.