Как обнаружить запуск программ в Linux?
Я написал простой демон. Этот демон должен отвечать, когда я запускаю любую программу. Как это сделать? В большом цикле демона:
while(1)
{
/* function which catches new programm running */
}
Какие функции вызывать в Linux, когда я запускаю новую программу (создать новый процесс)?
8 ответов
Я не знаю, существует ли лучший способ, но вы могли бы периодически сканировать /proc
файловая система.
Например, /proc/<pid>/exe
символическая ссылка на исполняемый файл процесса
В моих системах (Ubuntu/RedHat), /proc/loadavg
содержит количество запущенных процессов (число после косой черты), а также pid самого последнего запущенного процесса. Если ваш демон опрашивает файл, любое изменение любого из этих двух чисел сообщит ему, когда ему необходимо выполнить повторное сканирование. /proc
ищу новые процессы.
Это ни в коем случае не пуленепробиваемый, но самый подходящий механизм, который я могу придумать.
Для Linux в ядре есть интерфейс. Во время исследования этой проблемы я сталкивался с людьми, использующими конфигурацию ядра CONFIG_CONNECTOR и CONFIG_PROC_EVENTS для получения событий о смерти процесса.
Еще немного Google, и я нашел это:
http://netsplit.com/2011/02/09/the-proc-connector-and-socket-filters/
Разъем Proc и Socket Filters Опубликовано 9 февраля 2011 по Скотт
Разъем proc - одна из тех интересных функций ядра, с которыми редко сталкиваются большинство людей, и еще реже находят документацию. Аналогично сокетный фильтр. Это позор, потому что они оба действительно весьма полезные интерфейсы, которые могли бы служить различным целям, если бы они были лучше задокументированы.
Соединитель proc позволяет получать уведомления о событиях процесса, таких как вызовы fork и exec, а также об изменениях uid, gid или sid процесса (идентификатор сеанса). Они предоставляются через интерфейс на основе сокетов, читая экземпляры struct proc_event, определенные в заголовке ядра....
Интересующий заголовок:
#include <linux/cn_proc.h>
Я нашел пример кода здесь:
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);
}
Я обнаружил, что этот код должен запускаться от имени root для получения уведомлений.
Я был заинтересован в попытке выяснить, как сделать это без голосования. Похоже, что inotify() не работает с /proc, так что идея отсутствует.
Однако любая программа, которая динамически связана, при запуске будет получать доступ к определенным файлам, таким как динамический компоновщик. Это было бы бесполезно в целях безопасности, так как оно не будет запускаться в статически связанной программе, но все же может представлять интерес:
#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;
}
События, которые это выводит на печать, не содержат никакой интересной информации - pid процесса запуска, кажется, не предоставлен inotify. Однако его можно использовать для пробуждения и запуска повторного сканирования / proc
Также имейте в виду, что недолговечные программы могут снова исчезнуть до того, как эта штука проснется и закончит сканирование / процесс - возможно, вы узнаете, что они существуют, но не сможете узнать, чем они были. И, конечно, кто угодно может просто открывать и закрывать fd для dyanmic линкера, чтобы утопить вас в шуме.
Использование forkstat
это самый полный клиент для событий proc:
sudo forkstat -e exec,comm,core
- GitWeb: http://kernel.ubuntu.com/git?p=cking/forkstat.git
- Объявление: http://smackerelofopinion.blogspot.com/2014/03/forkstat-new-tool-to-trace-process.html
Упаковано в Ubuntu, Debian и AUR.
До этого был cn_proc:
bzr branch lp:~kees/+junk/cn_proc
Makefile нуждается в небольшом изменении (LDLIBS
вместо LDFLAGS
).
cn_proc и exec-notify.c (которые опубликовал Арно) имеют общего предка; cn_proc обрабатывает еще несколько событий и имеет более чистый вывод, но не устойчив, когда процессы быстро завершаются.
Ой, нашел еще одну развилку exec- notify, extrace. Этот отступ делает дочерний процесс ниже своего родителя (используя эвристику pid_depth).
Посмотрите на эту маленькую программу Себастьяна Крахмера, она делает именно то, что вы просите, эффективным способом и довольно простым кодом.
Требуется, чтобы в вашем ядре была включена функция CONFIG_PROC_EVENTS, чего нет, например, в последнем образе Amazon Linux (2012.09).
ОБНОВЛЕНИЕ: После запроса к Amazon ядра Amazon Linux Image теперь поддерживают PROC_EVENTS
CONFIG_FTRACE
а также CONFIG_KPROBES
через brendangregg/perf-tools
git clone https://github.com/brendangregg/perf-tools.git
cd perf-tools
git checkout 98d42a2a1493d2d1c651a5c396e015d4f082eb20
sudo ./execsnoop
На другой оболочке:
while true; do sleep 1; date; done
Первая оболочка показывает данные формата:
Tracing exec()s. Ctrl-C to end.
Instrumenting sys_execve
PID PPID ARGS
20109 4336 date
20110 4336 sleep 1
20111 4336 date
20112 4336 sleep 1
20113 4336 date
20114 4336 sleep 1
20115 4336 date
20116 4336 sleep 1
Вы можете либо сканировать операционную систему на наличие программ, соответствующих вашему критерию, либо подождать, пока программа сообщит о себе своему демону. Какой метод вы выберете, будет сильно зависеть от того, насколько вы контролируете свои программы, не являющиеся демонами.
Сканирование может быть выполнено с помощью системного вызова ядра или путем чтения сведений о ядре, объявленных в пространстве пользователя (как в файловой системе /proc). Обратите внимание, что сканирование не является гарантией того, что вы найдете какую-либо конкретную программу, так как, если программа удастся запустить и завершить между циклами сканирования, она вообще не будет обнаружена.
Возможны более сложные методы обнаружения процессов, но они также требуют более сложных реализаций. Важно знать, что действительно необходимо, прежде чем вы начнете искать экзотические решения (вставка драйверов ядра и т. Д.), Поскольку все, что вы делаете, не зависит от системы, которую вы отслеживаете; вы фактически изменяете окружающую среду, наблюдая за ней, и некоторые методы наблюдения за окружающей средой могут изменить ее ненадлежащим образом.