bpf как проверить аргументы системного вызова - PullRequest
1 голос
/ 18 июня 2020

trace_output_kern.c отслеживает sys_write syscall и печатает pid в пользовательском пространстве:

#include <linux/ptrace.h>
#include <linux/version.h>
#include <uapi/linux/bpf.h>
#include "bpf_helpers.h"

struct bpf_map_def SEC("maps") my_map = {
    .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
    .key_size = sizeof(int),
    .value_size = sizeof(u32),
    .max_entries = 2,
};

SEC("kprobe/sys_write")
int bpf_prog1(struct pt_regs *ctx)
{
    struct S {
        u64 pid;
        u64 cookie;
    } data;

    data.pid = bpf_get_current_pid_tgid();
    data.cookie = 0x12345678;

    bpf_perf_event_output(ctx, &my_map, 0, &data, sizeof(data));

    return 0;
}

char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;

sys_read имеет подпись sys_read(unsigned int fd, char __user *buf, size_t count);, и в настоящее время мы видим только PID. Предпосылка отслеживания состоит в том, что мы можем перехватить и проверить аргументы. Я также пытался увидеть аргументы, которые передаются.

Если я изменю этот struct S, чтобы он содержал массив символов для хранения char *buf as

    struct S {
            u64 pid;
            u64 cookie;
            char bleh[128]; //<-- added this 
    } data;

, он будет соответствовать:

/usr/src/linux-5.4/samples/bpf# ./trace_output
bpf_load_program() err=13
0: (bf) r6 = r1
1: (85) call bpf_get_current_pid_tgid#14
2: (b7) r1 = 305419896
3: (7b) *(u64 *)(r10 -136) = r1
4: (7b) *(u64 *)(r10 -144) = r0
5: (bf) r4 = r10
6: (07) r4 += -144
7: (bf) r1 = r6
8: (18) r2 = 0xffff8975bd44aa00
10: (b7) r3 = 0
11: (b7) r5 = 144
12: (85) call bpf_perf_event_output#25
invalid indirect read from stack off -144+16 size 144
processed 12 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
0: (bf) r6 = r1
1: (85) call bpf_get_current_pid_tgid#14
2: (b7) r1 = 305419896
3: (7b) *(u64 *)(r10 -136) = r1
4: (7b) *(u64 *)(r10 -144) = r0
5: (bf) r4 = r10
6: (07) r4 += -144
7: (bf) r1 = r6
8: (18) r2 = 0xffff8975bd44aa00
10: (b7) r3 = 0
11: (b7) r5 = 144
12: (85) call bpf_perf_event_output#25
invalid indirect read from stack off -144+16 size 144
processed 12 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

if sys_write - плохой пример (вопрос), я также пытался отследить sys_execve, в котором есть список аргументов

asmlinkage long sys_execve(const char __user *filename,
                const char __user *const __user *argv,
                const char __user *const __user *envp);

Пожалуйста, укажите мне правильное направление, спасибо!

Изменить 1

Как мне перехватить аргументы, которые использовались для __x64_sys_execve?

Когда я попробую это ниже,

#include <linux/ptrace.h>
#include <linux/version.h>
#include <uapi/linux/bpf.h>
#include "bpf_helpers.h"

struct bpf_map_def SEC("maps") my_map = {
        .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
        .key_size = sizeof(int),
        .value_size = sizeof(u32),
        .max_entries = 2,
};

//SEC("kprobe/sys_write")
SEC("kprobe/__x64_sys_execve")

/* Signature of sys_execve: 
asmlinkage long sys_execve(const char __user *filename,
                const char __user *const __user *argv,
                const char __user *const __user *envp);
*/

int bpf_prog1(struct pt_regs *ctx, const char *filename)
{
        struct S {
                u64 pid;
                u64 cookie;
                char bleh[128];
        } data;

        data.pid = bpf_get_current_pid_tgid();
        data.cookie = 0x12345678;
        //bpf_get_current_comm(&data.bleh, 128);
        bpf_probe_read(&data.bleh, 128, (void *)filename);

        bpf_perf_event_output(ctx, &my_map, 0, &data, sizeof(data));

        return 0;
}

char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;

Взрывается так:

/usr/src/linux-5.4/samples/bpf# ./borky
bpf_load_program() err=13
0: (bf) r6 = r2
R2 !read_ok
processed 1 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
0: (bf) r6 = r2
R2 !read_ok
processed 1 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

1 Ответ

0 голосов
/ 21 июня 2020

На первую часть вашего вопроса ответил pchaigno: если вы расширите свой struct S и попытаетесь прочитать его (bpf_perf_event_output(ctx, &my_map, 0, &data, sizeof(data));), не инициализировав его, верификатор жалуется, потому что чтение неинициализированной памяти из ядра вводит безопасность риск. Что вы могли бы сделать, это, например, обнулить всю структуру при ее объявлении:

        struct S {
                u64 pid;
                u64 cookie;
                char bleh[128];
        } data = {0};

Что касается второй части вашего вопроса с sys_execve, оказывается, вы не можете передать аргументы syscall вашей функции bpf_prog1() как вы пытаетесь это сделать. Ваша функция должна принимать только struct pt_regs *ctx.

Путаница, вероятно, возникает из-за синтаксиса, используемого в b cc, где аргументы передаются таким образом, но важно понимать, что b cc переписывает некоторые части под капотом, в частности эту вещь о доступе к аргументам.

Вместо этого вы могли бы использовать набор PT_REGS_PARM*(ctx) макросов, которые специально определены для доступа к аргументам проверяемого функция из соответствующих регистров компьютера ( пример , определение ). Я думаю, что b cc тоже использует их при переписывании, но вы этого не увидите.

...