Вывод объединения содержимого файла и пользовательского текста в новый файл

Вы уничтожаете процесс, вызываяkill()(илиtkill())системный вызов (ядро ​​также может само уничтожать процессы/задачи (например, SIGINT, отправленный при нажатии Ctrl -C, или SIGKILL, отправленный из -из -убийцы памяти ). Некоторые сигналы могут быть отправлены в результате других системных вызовов, таких как ptrace).

При вызове kill()все происходит в ядре.

Только код ядра стоит между процессом, отправляющим сигнал, и процессом, получающим его (и, возможно, завершается как следствие ).

Теперь есть еще несколько функций ядра, которые мешают и которые вы можете использовать здесь:

  1. простые разрешения Unix . Цитирование справочной страницы kill(2)по Linux:

    For a process to have permission to send a signal, it must either be privileged (under Linux: have the CAP_KILL capability in the user namespace of the target process), or the real or effective user ID of the sending process must equal the real or saved set-user-ID of the target process.

  2. Модули безопасности Linux . LSM могут (и делают, по крайней мере, для Smack, SELinux и apparmor )фильтровать, что может посылать сигналы чему.

  3. Некоторые процессы невосприимчивы к уничтожению . Это случай процесса с идентификатором 1(init)в Linux. Корневой процесс других дочерних пространств имен также невосприимчив к сигналам, посылаемым другими процессами в его пространстве имен. Задачи ядра также невосприимчивы к сигналам.

  4. Кроме того, существуют механизмы инструментирования ядра , подобные используемому SystemTap, которые позволяют вам влиять на поведение ядра и здесь могут использоваться для перехвата доставки сигнала.

Но прежде чем добраться до этого, возможно, первое, что нужно попробовать, это остановить то, что отправляет этот сигнал SIGKILL.

Если это необходимо для предотвращения остановки критического процесса (, например, используемого для поддержки корневой файловой системы )при завершении работы, в большинстве систем инициализации будет способ предотвращения того, чтобы заданные процессы подвергались killall5или что-то подобное, что происходит тогда. См., например, /run/sendsigs.omit.dв некоторых версиях Debian или killmodeв systemd.

Убийственный процесс,что бы это ни было, должен быть способ определить, какой процесс убить. Если он основан на идентификаторе жертвы, хранящемся в файле (, таком как /run/victim.pid), вы можете изменить этот файл, если он основан на имени процесса (/proc/pid/task/tid/comm), его также можно изменить (, например, подключив отладчик. и вызовите prctl(PR_SET_NAME)), то же самое для списка аргументов (/proc/pid/cmdline, показанногоps -f).

Если процесс-убийца динамически связан, вы можете внедрить в него код, чтобы заменить kill()системный вызов функцией-оболочкой, которая отказывается делать это для данного pid:

#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 

int kill(pid_t pid, int sig)
{
  static pid_t pid_to_safeguard = 0;
  static int (*orig_kill)(pid_t, int) = 0;

  if (!orig_kill)
    orig_kill = (int (*)(pid_t, int)) dlsym (RTLD_NEXT, "kill");

  if (!orig_kill) abort();

  if (!pid_to_safeguard) {
    char *env = getenv("NOTME");
    if (env) pid_to_safeguard = atol(env);
  }

  if (pid_to_safeguard && pid == pid_to_safeguard) {
    errno = EPERM;
    return -1;
  }

  return orig_kill(pid, sig);
}

(вам может понадобиться сделать то же самое для tkill()и для pgid жертвы в зависимости от того, как убийца на самом деле посылает сигнал ).

Скомпилировать как:

gcc -fPIC -shared -o notme.so notme.c -ldl

И запустите команду kill как:

LD_PRELOAD=/path/to/notme.so NOTME=12345 killer args...

Или вы можете полностью скрыть процесс, запустив его в пространстве имен pid, отличном от (, но не в дочернем )пространстве имен для остальной части системы .

Если ни один из этих вариантов не подходит, мы можем пройтись по нашему списку выше, чтобы предотвратить доставку сигнала:

  1. Запустите жертву как пользователя, отличного от убийцы (, при условии, что убийца не работает какroot)
  2. Используйте LSM. Например, при использовании загрузки Smack (с security=smackв качестве параметра ядра )установки другого labelдля процесса-жертвы будет достаточно, чтобы другие процессы не смогли его увидеть, не говоря уже о его уничтожении. Например:

    sudo zsh -c 'echo unkillable > /proc/self/attr/current && exec sleep 1000'
    

    будет sleepработать в этом unkillableдомене (имя может быть любым, дело в том, что в настоящее время нет определенного правила, которое позволяет в любом случае вмешиваться в этот домен )и даже процессы, работающие как одно и то же uid не сможет его убить.rootхотя бы .

  3. Если вы запустите процесс-жертву в качестве лидера нового пространства имен pid, то он будет невосприимчив к своим потомкам.

    ~$ sudo unshare -p --fork --mount-proc zsh
    ~# kill -s KILL "$$"
    ~#
    

    (еще там ).

  4. Здесь можно использовать SystemTap. Обратите внимание, однако, что вам нужны символы ядра(linux-image--dbgsymв Debian ), чтобы иметь возможность использовать его, а SystemTap или внутренние функции ядра, к которым будет подключаться ваш скрипт stap, потенциально могут быть изменены. Так что, возможно, не самый стабильный из вариантов. Режим Гуру также следует использовать с осторожностью (и не пытаться делать что-то слишком замысловатое ).

    С помощью stapвы можете внедрять код в разные точки работающего ядра. Например, вы можете подключиться к функции ядра, которая обрабатывает системные вызовы kill()или tkill(), и сказать ей изменить сигнал на 0 (безопасный ), когда pid принадлежит вашей жертве.

    stap -ge 'probe kernel.function("sys_kill") { if ($pid == 12345) $sig = 0; }'
    

    (здесь для любого сигнала, вы также можете проверить $sig == 9, если хотите охватить только SIGKILL ). Теперь это не работает, когда используется tkill()или когда kill()вызывается с идентификатором процесса группы жертвы, поэтому нам нужно расширить это. Это не распространяется на случаи, когда сигнал отправляется самим ядром.

    Но мы также можем посмотреть код ядра и посмотреть, сможем ли мы зацепиться в том месте, где ядро ​​проверяет наличие разрешения на отправку сигнала.

    stap -ge 'probe kernel.function("check_kill_permission").return {
               if (@entry($t->pid) == 12345) $return = -1; }'
    

    Мы возвращаем -1(-EPERM), что также имеет то преимущество, что позволяет убийце узнать, что его kill()не удалось, когда запрошенный pid является идентификатором нашей цели (здесь 12345в качестве примера ).

    ~$ sleep 1000 &
    [1] 8508
    ~$ sudo stap -ge 'probe kernel.function("check_kill_permission").return {
      if (@entry($t->pid) == '"$!"') $return = -1; }' &
    [2] 8510
    ~$ kill -s KILL 8508
    kill: kill 8508 failed: operation not permitted
    

    это также работает для некоторых случаев, когда ядро ​​отправляет сигнал само по себе, но не для всех. Для этого нам нужно спуститься в самый низ -самой функции, которая выполняет доставку сигнала в коде ядра :__send_signal()(, по крайней мере, в текущих версиях ядра Linux ).

    Одним из способов было бы подключиться к функции prepare_signal(), которую __send_signal()вызывает в начале (и отключается, если она возвращает 0 );

    stap -ge 'probe kernel.function("prepare_signal").return {
      if (@entry($p->pid) == 12345) $return = 0; }'
    

    Тогда этот pid 12345 будет неубиваемым, пока этот stapпроцесс жив.

    Обратите внимание, что ядро ​​обычно предполагает, что SIGKILL сработает, поэтому не исключено, что приведенное выше может иметь неожиданные побочные эффекты в некоторых крайних случаях (, например, убийца oom -становится неэффективным, если он продолжает выбирать вашу неубиваемую жертву ).

3
03.12.2020, 18:48
1 ответ

Другой способ сделать это — использовать подстановку процесса:

cat file1 <(echo "newtextinbetween") file2 > file3

Редактировать :Если вы не знаете, что echoдобавить для разрыва строки, используйте вместо этого echo -n.

5
18.03.2021, 22:45

Теги

Похожие вопросы