Jak śledzić nowo utworzone procesy w systemie Linux?

31

Wiem, że dzięki psmogę zobaczyć listę lub drzewo bieżących procesów uruchomionych w systemie. Ale chcę osiągnąć „śledzenie” nowych procesów tworzonych podczas korzystania z komputera.

Analogicznie, gdy używasz tail -fdo śledzenia nowej zawartości dołączonej do pliku lub dowolnego wejścia, chcę zachować listę śledzenia procesu, który jest obecnie tworzony.

Czy to w ogóle możliwe?

Pablo Matias Gomez
źródło

Odpowiedzi:

28

Jeśli kprobes są włączone w jądrze, możesz używać execsnoopz perf-tools :

W pierwszym terminalu:

% while true; do uptime; sleep 1; done

W innym terminalu:

% git clone https://github.com/brendangregg/perf-tools.git
% cd perf-tools
% sudo ./execsnoop
Tracing exec()s. Ctrl-C to end.
Instrumenting sys_execve
   PID   PPID ARGS
 83939  83937 cat -v trace_pipe
 83938  83934 gawk -v o=1 -v opt_name=0 -v name= -v opt_duration=0 [...]
 83940  76640 uptime
 83941  76640 sleep 1
 83942  76640 uptime
 83943  76640 sleep 1
 83944  76640 uptime
 83945  76640 sleep 1
^C
Ending tracing...
Kirill Spitsyn
źródło
2
Dla nowych wersji jądra (> = 4.17, jeśli dobrze rozumiem) na x84_64, perf-tools Gregga już nie działają - działają, ale nie ma żadnych raportów, ponieważ instrumentuje nieużywane wywołanie. Zgodnie z komentarzami Gregga w innym miejscu, poprawnym rozwiązaniem dla jąder> = 4.7 jest użycie implementacji BPF w kolekcji kompilatorów BPF dostępnej tutaj: github.com/iovisor/bcc#tools oraz na Ubuntu i nowoczesnych linuxach jako bpfcc-tools.
Guss
7

Najprostszym sposobem jest włączenie kontroli połączeń systemowych

Zobacz poniższy link, aby uzyskać szczegółowe informacje,

Czy ktoś zna prosty sposób monitorowania odradzania procesu rootowania Błąd serwera

Jeśli monitorujesz wszystkie procesy, po prostu usuń -F uid=0część

Dzienniki są zapisywane do /var/log/audit/audit.log

stokrotka
źródło
Żaden z tych 3 linków nie odpowiada na moje pytanie. Pierwsze dwa dotyczą kodowania czegoś, aby rozwiązać ten problem, a ostatnie nie odpowiada. Pytam o jakieś polecenie i nie pisanie kodu
Pablo Matias Gomez
@ PabloMatíasGomez zaktualizowany
stokrotka
3

CONFIG_PROC_EVENTS=y

Przykładowa sesja:

$ su
# ./proc_events.out &
set mcast listen ok
# sleep 2 & sleep 1 &
fork: parent tid=48 pid=48 -> child tid=56 pid=56
fork: parent tid=48 pid=48 -> child tid=57 pid=57
exec: tid=57 pid=57
exec: tid=56 pid=56
exit: tid=57 pid=57 exit_code=0
exit: tid=56 pid=56 exit_code=0

CONFIG_PROC_EVENTSudostępnia zdarzenia do użytkownika przez gniazdo netlink .

proc_events.c

#define _XOPEN_SOURCE 700
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>
#include <signal.h>
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

static volatile bool need_exit = false;

static int nl_connect()
{
    int rc;
    int nl_sock;
    struct sockaddr_nl sa_nl;

    nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
    if (nl_sock == -1) {
        perror("socket");
        return -1;
    }
    sa_nl.nl_family = AF_NETLINK;
    sa_nl.nl_groups = CN_IDX_PROC;
    sa_nl.nl_pid = getpid();
    rc = bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl));
    if (rc == -1) {
        perror("bind");
        close(nl_sock);
        return -1;
    }
    return nl_sock;
}

static int set_proc_ev_listen(int nl_sock, bool enable)
{
    int rc;
    struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
        struct nlmsghdr nl_hdr;
        struct __attribute__ ((__packed__)) {
            struct cn_msg cn_msg;
            enum proc_cn_mcast_op cn_mcast;
        };
    } nlcn_msg;

    memset(&nlcn_msg, 0, sizeof(nlcn_msg));
    nlcn_msg.nl_hdr.nlmsg_len = sizeof(nlcn_msg);
    nlcn_msg.nl_hdr.nlmsg_pid = getpid();
    nlcn_msg.nl_hdr.nlmsg_type = NLMSG_DONE;

    nlcn_msg.cn_msg.id.idx = CN_IDX_PROC;
    nlcn_msg.cn_msg.id.val = CN_VAL_PROC;
    nlcn_msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);

    nlcn_msg.cn_mcast = enable ? PROC_CN_MCAST_LISTEN : PROC_CN_MCAST_IGNORE;

    rc = send(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0);
    if (rc == -1) {
        perror("netlink send");
        return -1;
    }

    return 0;
}

static int handle_proc_ev(int nl_sock)
{
    int rc;
    struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
        struct nlmsghdr nl_hdr;
        struct __attribute__ ((__packed__)) {
            struct cn_msg cn_msg;
            struct proc_event proc_ev;
        };
    } nlcn_msg;
    while (!need_exit) {
        rc = recv(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0);
        if (rc == 0) {
            /* shutdown? */
            return 0;
        } else if (rc == -1) {
            if (errno == EINTR) continue;
            perror("netlink recv");
            return -1;
        }
        switch (nlcn_msg.proc_ev.what) {
            case PROC_EVENT_NONE:
                printf("set mcast listen ok\n");
                break;
            case PROC_EVENT_FORK:
                printf("fork: parent tid=%d pid=%d -> child tid=%d pid=%d\n",
                        nlcn_msg.proc_ev.event_data.fork.parent_pid,
                        nlcn_msg.proc_ev.event_data.fork.parent_tgid,
                        nlcn_msg.proc_ev.event_data.fork.child_pid,
                        nlcn_msg.proc_ev.event_data.fork.child_tgid);
                break;
            case PROC_EVENT_EXEC:
                printf("exec: tid=%d pid=%d\n",
                        nlcn_msg.proc_ev.event_data.exec.process_pid,
                        nlcn_msg.proc_ev.event_data.exec.process_tgid);
                break;
            case PROC_EVENT_UID:
                printf("uid change: tid=%d pid=%d from %d to %d\n",
                        nlcn_msg.proc_ev.event_data.id.process_pid,
                        nlcn_msg.proc_ev.event_data.id.process_tgid,
                        nlcn_msg.proc_ev.event_data.id.r.ruid,
                        nlcn_msg.proc_ev.event_data.id.e.euid);
                break;
            case PROC_EVENT_GID:
                printf("gid change: tid=%d pid=%d from %d to %d\n",
                        nlcn_msg.proc_ev.event_data.id.process_pid,
                        nlcn_msg.proc_ev.event_data.id.process_tgid,
                        nlcn_msg.proc_ev.event_data.id.r.rgid,
                        nlcn_msg.proc_ev.event_data.id.e.egid);
                break;
            case PROC_EVENT_EXIT:
                printf("exit: tid=%d pid=%d exit_code=%d\n",
                        nlcn_msg.proc_ev.event_data.exit.process_pid,
                        nlcn_msg.proc_ev.event_data.exit.process_tgid,
                        nlcn_msg.proc_ev.event_data.exit.exit_code);
                break;
            default:
                printf("unhandled proc event\n");
                break;
        }
    }

    return 0;
}

static void on_sigint(__attribute__ ((unused)) int unused)
{
    need_exit = true;
}

int main()
{
    int nl_sock;
    int rc = EXIT_SUCCESS;

    signal(SIGINT, &on_sigint);
    siginterrupt(SIGINT, true);
    nl_sock = nl_connect();
    if (nl_sock == -1)
        exit(EXIT_FAILURE);
    rc = set_proc_ev_listen(nl_sock, true);
    if (rc == -1) {
        rc = EXIT_FAILURE;
        goto out;
    }
    rc = handle_proc_ev(nl_sock);
    if (rc == -1) {
        rc = EXIT_FAILURE;
        goto out;
    }
    set_proc_ev_listen(nl_sock, false);
out:
    close(nl_sock);
    exit(rc);
}

Upitream GitHub , kod dostosowany z: https://bewareofgeek.livejournal.com/2945.html

Nie sądzę jednak, aby można było uzyskać dane procesowe, takie jak UID i argumenty procesowe, ponieważ exec_proc_eventzawiera tak mało danych: https://github.com/torvalds/linux/blob/v4.16/include/uapi/linux/cn_proc .h # L80 Moglibyśmy spróbować natychmiast odczytać go /proc, ale istnieje ryzyko, że proces się skończy, a inny przejmie PID, więc nie byłby niezawodny.

Testowany na Ubuntu 17.10, który jest CONFIG_PROC_EVENTS=ydomyślnie włączony.

Ciro Santilli
źródło
2

Najwyraźniej możesz śledzić proces za pomocą strace. Jeśli znasz PID procesu, możesz:

strace -o strace-<pid>.out -f -p <pid>

Zwróć uwagę na -fprzełącznik. Pomoże ci to śledzić nowo utworzone procesy, które są potomkami procesu, którego PID użyto w powyższym poleceniu. Aby uzyskać informacje na temat strace, zobacz to pytanie.

tejus
źródło
Najwyraźniej chciałeś dołączyć do procesu inicjowania z pid = 1, prawda? Niestety to nie działa, nie widzę w wynikach żadnego tworzenia nowych procesów, a liczba wierszy to kilkadziesiąt, podczas gdy obecny pid dla nowych procesów przeszedł kilkaset.
Cześć Anioł