В Perl возможно ли убить открытие (SENDMAIL, "|$sendmail") перед закрытием (SENDMAIL)

Мне нужно взломать старую систему, которая использует open(SENDMAIL, "|$sendmail") а также close(SENDMAIL), Можно ли остановить отправку электронной почты после открытия потока? В этом случае, если обнаружен неожиданный спам-контент.

Я попробовал это без удачи:

$pid = open(SENDMAIL, "|$sendmail");
while (<>) {
    ....do lots of stuff in here....
    # Oops, we need to abort
    if ($needToAbort) {
        kill 9, $pid;
        exit(0);
    }
}
close(SENDMAIL);

Даже когда цикл достигает значения $needToAbort === true, электронное письмо по-прежнему отключается. Лучшее объяснение, которое я могу найти, это то, что kill 9, $pid, только принудительно закрывает поток, а не уничтожает его.

Чтобы убедиться, что $ pid существует, я попытался добавить в if:

if ($needToAbort) {
    $exists = kill 0, $pid;
    if ($exists) {
        kill 9, $pid;
        exit(0);
    }
}

При ведении журнала иногда кажется, что $ pid существует, а иногда нет. Система использует Perl 5, версия 16.

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

1 ответ

Решение

Кажется, что команда $sendmail не запускается sendmail запрограммировать напрямую, чтобы $pid вернулся open не sendmail(но оболочка?).

Найти PID sendmail сам процесс и kill должно сработать. (Или Кондер убивает всю группу процессов, см. Конец).

Вместо того чтобы делать это вручную ps Вы можете использовать Proc::ProcessTable

use Proc::ProcessTable;

my $pid = open my $sm_fh, '|-', $sendmail or die "Can't open sendmail: $!";

my $pid_sm;
my $pt = Proc::ProcessTable->new();
foreach my $proc (@{$pt->table}) {
    if ($proc->cmndline =~ /^sendmail/) {  # adjust regex for your system
        $pid_sm = $proc->pid;
        say "Sendmail command-line: ", $proc->cmndline;
        say "Sendmail process pid:  ", $proc->pid;
    }   
}

kill 9, $pid_sm;
my $gone_pid = waitpid $pid_sm, 0;
say "Process $gone_pid is gone";

# need a handler for SIGPIPE for prints to $sm_fh further in code

В моей системе CMD поле начинается с sendmailОтрегулируйте так, как оно есть у вас. Если может быть несколько sendmail процессы, что вполне возможно, вам потребуется более тщательный анализ.

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

Затем вы должны установить обработчик сигнала для SIGPIPE или программа умрет при следующей попытке печати в этот дескриптор файла, так как она получит SIGPIPE и его расположение должно прекратить.

Другое решение - обернуть sendmail обработка в Expect, которая устанавливает псевдо-терминал, чтобы вы могли отправить Ctrl-C при необходимости. (Своя hard_close Метод выполняет свою работу и в моих тестах.) Но для этого необходимо изменить операторы печати, чтобы они могли быть здесь запрещены.


Немного подробнее. Было выяснено, что команда: /usr/lib/sendmail -f$sender -t

Объект модуля ($pt выше) имеет много полей таблицы процессов, перечисленных $pt->fields, с описаниями на его "модуль заглушки". Я считаю более информативным распечатать и просмотреть их все на предмет интереса. Некоторые, которые могут быть полезны для этой цели: exec, cwdи различные ids.

Как именно определить процесс, зависит от деталей системы, но один из способов - посмотреть детали командной строки.

Приведенный выше пример немного расширен

$SIG{PIPE} = sub { print "Got $_[0]\n" };  # or just $SIG{PIPE} = 'IGNORE';

my $pid_sm;
foreach my $proc (@{$pt->table}) {
    my $cmd = $proc->cmndline;
    next if $cmd !~ m{^/usr/lib/sendmail};
    if ( (split ' ', $cmd)[1] eq "-f$sender" ) {
        $pid_sm = $proc->pid;
        say "Our process $pid_sm: $cmd";
    }
    else { say "Some other sendmail: $cmd" }
}
warn "Didn't find our sendmail process" if not $pid_sm;

if ($needToAbort and $pid_sm) {
    kill 9, $pid_sm;
    my $gone_pid = waitpid $pid_sm, 0;
    if    ($gone_pid == -1) { say "No process $pid_sm" }
    elsif ($gone_pid ==  0) { say "Process $pid_sm still around?" }
    else                    { say "Process $gone_pid is gone" }
};

Второе поле командной строки сверяется с точной фразой "-f$sender", что может быть relexad с помощью регулярных выражений вместо eq, Просмотрите командные строки, напечатанные для всех процессов выше, и отрегулируйте их при необходимости. Если есть проблемы, распечатайте все, что имеет 'sendmail' в этом.


Другой вариант - убить группу процессов: kill 9, -$pid (обратите внимание на минус). Это должно поймать sendmail сам процесс, но, конечно, убедитесь, что вы знаете, что сдулся.

Чтобы добавить, я сомневаюсь, что вам нужно использовать SIGKILL (9). Как только правильный пид найден SIGTERM (15 в моей системе, см. man 7 signal) вполне может быть достаточно хорошим, что гораздо приятнее.

Наконец, процесс может быть связан с ОС и находиться в непрерывном состоянии, в частности, в некоторых операциях ввода-вывода. Однако здесь это маловероятно, и я сначала попробую два подхода выше.

Другие вопросы по тегам