¿Cómo detectar el lanzamiento de programas en Linux?

11 minutos de lectura

avatar de usuario
protuberancia

Escribí un daemon simple. Este demonio debería responder cuando ejecuto cualquier programa. ¿Como hacer esto? En un bucle de demonio grande:

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

¿Qué funciones llamar en Linux cuando estoy ejecutando un nuevo programa (crear un nuevo proceso)?

  • Creo que vamos a necesitar un poco más de información.

    –Adam Batkin

    20 de mayo de 2011 a las 16:40

  • @Adam Batkin: no estoy de acuerdo, la pregunta está suficientemente formulada, el cartel quiere estar informado sobre la creación del proceso

    – Chris Stratton

    20 de mayo de 2011 a las 16:42

  • ¿Este demonio está iniciando Firefox o el usuario lo está iniciando externamente y desea recibir una notificación? Su pregunta podría usar una pequeña aclaración.

    – Justin Dearing

    20 de mayo de 2011 a las 16:44

  • @Chris: Esa es ciertamente una interpretación válida. Siéntase libre de editar la pregunta para que quede más claro.

    –Adam Batkin

    20 de mayo de 2011 a las 16:44

  • los demonio de auditoría lata registrar los programas que se ejecutan al rastrear el execve llamada al sistema. Tal vez puedas inspirarte en ello.

    – Cristian Ciupitu

    20 mayo 2011 a las 21:30


avatar de usuario
david crookes

Para Linux, parece haber una interfaz en el núcleo. Mientras investigaba este problema, me encontré con personas que usaban la configuración del kernel CONFIG_CONNECTOR y CONFIG_PROC_EVENTS para obtener eventos sobre la muerte del proceso.

Un poco más de google y encontré esto:

http://netsplit.com/2011/02/09/el-proc-conector-y-filtros-de-socket/

El conector Proc y los filtros de socket Publicado el 9 de febrero de 2011 por scott

El conector proc es una de esas características interesantes del kernel con las que la mayoría de la gente rara vez se encuentra, y aún más raramente encuentra documentación. Del mismo modo el filtro del zócalo. Es una pena, porque ambas son interfaces bastante útiles que podrían servir para una variedad de propósitos si estuvieran mejor documentadas.

El conector de proceso le permite recibir notificaciones de eventos de procesos, como llamadas fork y exec, así como cambios en el uid, gid o sid (id de sesión) de un proceso. Estos se proporcionan a través de una interfaz basada en socket mediante la lectura de instancias de struct proc_event definidas en el encabezado del núcleo….

El encabezado de interés es:

#include <linux/cn_proc.h>

Encontré un código de ejemplo aquí:

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);
}

Descubrí que este código debe ejecutarse como root para recibir las notificaciones.

  • Splendid one – ayuda a crear la solución para superusuario.com/questions/354150/… 😉

    – Frank H.

    17/10/2013 a las 15:22

  • Esto es hermoso, estaba empezando a pensar que iba a tener que sumergirme en el código del kernel…

    – no está mal.jpeg

    19 de enero de 2016 a las 17:20

  • Tenga en cuenta que puede usar el pid de los procesos generados para leer información adicional del proceso desde el directorio /proc/, si lo desea. Usé esto para extraer información de la línea de comando para mostrar también el nombre de un proceso en la creación.

    – h0r53

    20 de junio de 2018 a las 16:04

avatar de usuario
chris stratton

Estaba interesado en tratar de averiguar cómo hacer esto sin encuestas. inotify() no parece funcionar en /proc, por lo que esa idea está descartada.

Sin embargo, cualquier programa que esté vinculado dinámicamente accederá a ciertos archivos al inicio, como el vinculador dinámico. Esto sería inútil por motivos de seguridad, ya que no se activará en un programa vinculado estáticamente, pero aún podría ser de interés:

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

Los eventos que esto imprime no contienen ninguna información interesante: inotify no parece proporcionar el pid del proceso de activación. Sin embargo, podría usarse para activar y activar una nueva exploración de /proc

También tenga en cuenta que los programas de corta duración pueden desaparecer nuevamente antes de que esta cosa se despierte y termine de escanear /proc; presumiblemente, aprenderá que existieron, pero no podrá saber qué eran. Y, por supuesto, cualquiera podría seguir abriendo y cerrando un fd al enlazador dinámico para ahogarlo en el ruido.

avatar de usuario
tobu

Utilizar forkstates el cliente más completo para eventos de proc:

sudo forkstat -e exec,comm,core

Empaquetado en Ubuntu, Debian y AUR.


Antes de eso, había cn_proc:

 bzr branch lp:~kees/+junk/cn_proc

El Makefile necesita un pequeño cambio (LDLIBS en lugar de LDFLAGS).

cn_proc y exec-notificar.c (que Arnaud publicó) comparten un ancestro común; cn_proc maneja algunos eventos más y tiene un resultado más limpio, pero no es resistente cuando los procesos finalizan rápidamente.


Ooh, encontré otra bifurcación de exec-notify, extrae. Éste sangra los procesos secundarios debajo de su padre (usando una heurística pid_depth).

  • Paquetes aptos de Debian y Ubuntu: forkstat y extrace. Por cierto: por algún motivo, ambas herramientas no funcionan en mi sistema basado en WSL2. Ubuntu 20.04 LTSno reciben notificaciones para la creación de ningún proceso.

    – Abdull

    9 ene a las 20:18

avatar de usuario
Arnaud Meuret

Mira esto este pequeño programa por Sebastian Krahmer hace exactamente lo que usted está pidiendo de una manera eficiente en recursos y un código bastante simple.

Requiere que su kernel tenga habilitado CONFIG_PROC_EVENTS, que no es el caso en la última imagen de Amazon Linux (2012.09), por ejemplo.

ACTUALIZACIÓN: Siguiendo un solicitar a Amazon los kernels de imagen de Amazon Linux ahora son compatibles con PROC_EVENTS

La palabra clave para la máquina de búsqueda de su elección es “conector de eventos de proceso”.

Encontré dos herramientas que las utilizan, notificación ejecutiva y cn_proc.

Me gusta más el último, pero ambos hacen muy bien su trabajo.

No sé si existe una mejor manera, pero podría escanear periódicamente el /proc sistema de archivos

Por ejemplo, /proc/<pid>/exe es un enlace simbólico al ejecutable del proceso.

En mis sistemas (Ubuntu/RedHat), /proc/loadavg contiene el número de procesos en ejecución (el número después de la barra inclinada), así como el pid del proceso iniciado más recientemente. Si su daemon sondea el archivo, cualquier cambio en cualquiera de los dos números le indicará cuándo debe volver a escanear /proc buscando nuevos procesos.

Esto no es a prueba de balas, pero es el mecanismo más adecuado que se me ocurre.

Puede escanear el sistema operativo en busca de programas que coincidan con su criterio o puede esperar a que un programa se informe a su daemon. La técnica que elija dependerá en gran medida de cuánto control tenga sobre sus programas que no son demonios.

El escaneo se puede realizar mediante una llamada al sistema del kernel o leyendo los detalles del kernel anunciados en el espacio del usuario (como con el sistema de archivos /proc). Tenga en cuenta que el escaneo no es una garantía de que encontrará un programa en particular, ya que si el programa logra iniciarse y finalizar entre ciclos de escaneo, no se detectará en absoluto.

Son posibles técnicas más complicadas de detección de procesos, pero también requieren implementaciones más complicadas. Es importante saber lo que realmente se necesita antes de comenzar a buscar soluciones exóticas (insertar controladores de kernel, etc.), ya que todo lo que hace no es independiente del sistema que está monitoreando; en realidad cambias el entorno observándolo, y algunos métodos para observar el entorno pueden cambiarlo de manera inapropiada.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad