Wie erkennt man das Starten von Programmen unter Linux?

Lesezeit: 10 Minuten

Benutzer-Avatar
Kern

Ich habe einen einfachen Daemon geschrieben. Dieser Daemon sollte reagieren, wenn ich ein beliebiges Programm ausführe. Wie macht man das? In einer großen Daemon-Schleife:

while(1)
{
   /* function which catches new programm running */
}

Welche Funktionen müssen in Linux aufgerufen werden, wenn ich ein neues Programm ausführe (neuen Prozess erstellen)?

  • Ich denke, wir werden ein wenig mehr Informationen brauchen

    – Adam Batkin

    20. Mai 2011 um 16:40 Uhr

  • @Adam Batkin – Ich bin anderer Meinung, die Frage ist ausreichend gestellt, der Poster möchte über die Prozesserstellung informiert werden

    – Chris Stratton

    20. Mai 2011 um 16:42 Uhr

  • Startet dieser Daemon Firefox oder wird Firefox extern vom Benutzer gestartet und Sie möchten benachrichtigt werden? Ihre Frage könnte eine kleine Klarstellung gebrauchen.

    – Justin Dearing

    20. Mai 2011 um 16:44 Uhr

  • @Chris: Das ist sicherlich eine gültige Interpretation. Sie können die Frage gerne bearbeiten, um dies klarer zu machen

    – Adam Batkin

    20. Mai 2011 um 16:44 Uhr

  • Das Audit-Daemon kann Protokollieren Sie die ausgeführten Programme durch Verfolgen der execve Systemaufruf. Vielleicht kannst du dich davon inspirieren lassen.

    – Cristian Ciupitu

    20. Mai 2011 um 21:30 Uhr


Benutzer-Avatar
David Crookes

Für Linux scheint es eine Schnittstelle im Kernel zu geben. Bei der Untersuchung dieses Problems bin ich auf Leute gestoßen, die die Kernelkonfiguration CONFIG_CONNECTOR und CONFIG_PROC_EVENTS verwenden, um Ereignisse beim Prozesstod zu erhalten.

Etwas mehr Google und ich habe das gefunden:

http://netsplit.com/2011/02/09/the-proc-connector-and-socket-filters/

Der Proc-Anschluss und die Sockelfilter Gesendet am 9. Februar 2011 von Scott

Der Proc-Konnektor ist eines dieser interessanten Kernel-Features, auf das die meisten Leute selten stoßen und noch seltener eine Dokumentation darüber finden. Ebenso der Steckdosenfilter. Das ist eine Schande, denn beide sind wirklich sehr nützliche Schnittstellen, die einer Vielzahl von Zwecken dienen könnten, wenn sie besser dokumentiert wären.

Der Proc-Connector ermöglicht es Ihnen, Benachrichtigungen über Prozessereignisse wie Fork- und Exec-Aufrufe sowie über Änderungen an der UID, GID oder SID (Sitzungs-ID) eines Prozesses zu erhalten. Diese werden über eine Socket-basierte Schnittstelle bereitgestellt, indem Instanzen von struct proc_event gelesen werden, die im Kernel-Header definiert sind….

Der interessierende Header lautet:

#include <linux/cn_proc.h>

Beispielcode habe ich hier gefunden:

http://bewareofgeek.livejournal.com/2945.html

/* This file is licensed under the GPL v2 (http://www.gnu.org/licenses/gpl2.txt) (some parts was originally borrowed from proc events example)

pmon.c

code highlighted with GNU source-highlight 3.1
*/

#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>

/*
* connect to netlink
* returns netlink socket, or -1 on error
*/
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;
}

/*
* subscribe on proc events (process notifications)
*/
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;
}

/*
* handle a single process event
*/
static volatile bool need_exit = false;
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(int unused)
{
need_exit = true;
}

int main(int argc, const char *argv[])
{
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);
}

Ich habe festgestellt, dass dieser Code als root ausgeführt werden muss, um die Benachrichtigungen zu erhalten.

  • Splendid one – hilft bei der Erstellung der Lösung für superuser.com/questions/354150/… 😉

    – FrankH.

    17. Oktober 2013 um 15:22 Uhr

  • Das ist wunderschön, ich dachte schon, ich müsste in den Kernel-Code eintauchen …

    – notbad.jpeg

    19. Januar 2016 um 17:20 Uhr

  • Beachten Sie, dass Sie bei Bedarf die PID von erzeugten Prozessen verwenden können, um zusätzliche Prozessinformationen aus dem /proc/-Verzeichnis zu lesen. Ich habe dies verwendet, um Befehlszeileninformationen zu extrahieren, um auch den Namen eines Prozesses bei der Erstellung anzuzeigen.

    – h0r53

    20. Juni 2018 um 16:04 Uhr

Benutzer-Avatar
Chris Stratton

Ich war daran interessiert, herauszufinden, wie man dies ohne Abfrage macht. inotify() scheint nicht auf /proc zu funktionieren, also ist diese Idee raus.

Jedes dynamisch verknüpfte Programm greift jedoch beim Start auf bestimmte Dateien zu, z. B. den dynamischen Linker. Dies wäre aus Sicherheitsgründen nutzlos, da es bei einem statisch verknüpften Programm nicht ausgelöst wird, aber dennoch von Interesse sein könnte:

#include <stdio.h>
#include <sys/inotify.h>
#include <assert.h>
int main(int argc, char **argv) {
    char buf[256];
    struct inotify_event *event;
    int fd, wd;
    fd=inotify_init();
    assert(fd > -1);
    assert((wd=inotify_add_watch(fd, "/lib/ld-linux.so.2", IN_OPEN)) > 0);
    printf("Watching for events, wd is %x\n", wd);
    while (read(fd, buf, sizeof(buf))) {
      event = (void *) buf;
      printf("watch %d mask %x name(len %d)=\"%s\"\n",
         event->wd, event->mask, event->len, event->name);
    }
    inotify_rm_watch(fd, wd);
    return 0;
}

Die von diesem Ausdruck ausgegebenen Ereignisse enthalten keine interessanten Informationen – die PID des auslösenden Prozesses scheint von inotify nicht bereitgestellt zu werden. Es könnte jedoch verwendet werden, um aufzuwachen und einen erneuten Scan von /proc auszulösen

Beachten Sie auch, dass kurzlebige Programme wieder verschwinden können, bevor dieses Ding aufwacht und das Scannen von /proc beendet – vermutlich würden Sie erfahren, dass sie existiert haben, aber nicht in der Lage sein, zu erfahren, was sie waren. Und natürlich könnte jeder einfach einen fd zum dynamischen Linker öffnen und schließen, um Sie im Rauschen zu ertränken.

Benutzer-Avatar
Tobu

Verwenden forkstates ist der vollständigste Client für proc-Ereignisse:

sudo forkstat -e exec,comm,core

Gepackt in Ubuntu, Debian und dem AUR.


Davor gab es cn_proc:

 bzr branch lp:~kees/+junk/cn_proc

Das Makefile benötigt eine kleine Änderung (LDLIBS Anstatt von LDFLAGS).

cn_proc und exec-notify.c (die Arnaud gepostet hat) haben einen gemeinsamen Vorfahren; cn_proc verarbeitet ein paar Ereignisse mehr und hat eine sauberere Ausgabe, ist aber nicht belastbar, wenn Prozesse schnell beendet werden.


Ooh, ich habe einen weiteren Fork von exec-notify gefunden, extrahieren. Dieser rückt untergeordnete Prozesse unterhalb ihres übergeordneten Prozesses ein (unter Verwendung einer pid_depth-Heuristik).

  • Debian- und Ubuntu-apt-Pakete: forkstat und extrace. Übrigens: Aus irgendeinem Grund funktionieren beide Tools nicht in meiner WSL2-basierten Ubuntu 20.04 LTSwerden sie nicht über die Prozesserstellung benachrichtigt.

    – Abdul

    9. Januar um 20:18 Uhr

Benutzer-Avatar
Arnaud Meuret

Schau mal rein dieses kleine Programm von Sebastian Krahmer macht es genau das, was Sie verlangen, auf ressourceneffiziente Weise und mit ziemlich einfachem Code.

Es erfordert, dass Ihr Kernel CONFIG_PROC_EVENTS aktiviert hat, was beispielsweise beim neuesten Amazon Linux Image (2012.09) nicht der Fall ist.

UPDATE: Nach a Anfrage an Amazon die Amazon Linux Image-Kernel unterstützen jetzt PROC_EVENTS

Das Stichwort für die Suchmaschine Ihrer Wahl lautet „Process Event Connector“.

Ich habe zwei Tools gefunden, die sie verwenden, exec-benachrichtigen und cn_proc.

Ich mag die letzteren mehr, aber beide machen ihre Arbeit sehr gut.

Ich weiß nicht, ob es einen besseren Weg gibt, aber Sie könnten die regelmäßig scannen /proc Dateisystem.

Zum Beispiel, /proc/<pid>/exe ist ein symbolischer Link zur ausführbaren Datei des Prozesses.

Auf meinen Systemen (Ubuntu/RedHat) /proc/loadavg enthält die Anzahl der laufenden Prozesse (die Zahl nach dem Schrägstrich) sowie die PID des zuletzt gestarteten Prozesses. Wenn Ihr Daemon die Datei abfragt, wird ihm jede Änderung an einer der beiden Zahlen mitteilen, wann er erneut scannen muss /proc auf der Suche nach neuen Prozessen.

Das ist keineswegs kugelsicher, aber der geeignetste Mechanismus, der mir einfällt.

Sie können entweder das Betriebssystem nach Programmen durchsuchen, die Ihrem Kriterium entsprechen, oder Sie können warten, bis sich ein Programm an Ihren Daemon meldet. Welche Technik Sie wählen, hängt stark davon ab, wie viel Kontrolle Sie über Ihre Nicht-Daemon-Programme haben.

Das Scannen kann durch einen Kernel-Systemaufruf oder durch Lesen der vom Benutzerbereich angekündigten Kernel-Details (wie beim /proc-Dateisystem) durchgeführt werden. Beachten Sie, dass das Scannen keine Garantie dafür ist, dass Sie ein bestimmtes Programm finden, denn wenn das Programm es schafft, zwischen den Scanzyklen zu starten und zu beenden, wird es überhaupt nicht erkannt.

Kompliziertere Techniken der Prozesserfassung sind möglich, aber sie erfordern auch kompliziertere Implementierungen. Es ist wichtig zu wissen, was wirklich benötigt wird, bevor Sie nach exotischen Lösungen suchen (Einfügen von Kernel-Treibern usw.), da alles, was Sie tun, nicht unabhängig von dem System ist, das Sie überwachen. Sie ändern die Umgebung tatsächlich, indem Sie sie beobachten, und einige Methoden, die Umgebung zu beobachten, können sie auf unangemessene Weise verändern.

1366520cookie-checkWie erkennt man das Starten von Programmen unter Linux?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy