Как обрабатывать SIGSTOP / SIGCONT в ptracer?

#c #linux #ptrace

#c #linux #ptrace

Вопрос:

Итак, у меня есть хорошая совместная PTRACE_TRACEME дочерняя программа, обрабатываемая PTRACE программой-оболочкой.

Проблема в том, что если администратор (или другой) решает SIGSTOP дочернюю программу, как я могу с этим справиться? И SIGCONT , конечно, забрать позже.

Ответ может быть «вы не можете»? Это PTRACE в конце концов!

Я вижу, что PTRACE_LISTEN (начиная с Linux 3.4) играет определенную роль, переводя дочернюю программу в остановленное состояние, но это документировано, чтобы работать только тогда, когда дочерний элемент подключен PTRACE_SEIZE .

Я попробовал тривиальную вещь вызова PTRACE_LISTEN в PTRACE_TRACEME режиме групповой остановки, и, как задокументировано, я получаю старую ошибку McDonald — EIO .

У меня есть мысль, что я мог бы ввести код для выполнения a sigsuspend , но, конечно, это приостановит только 1 поток дочерней программы. Мне пришлось бы вводить сигнал в каждый поток, чтобы затем остановить их все? А затем перезапустить?

Или, возможно, реализация redelivered SIGSTOP уже остановила их все? В каком случае мой sigsuspend действительно сработает? Думаю, я мог бы запустить его напрямую, если бы другой поток сообщил SIGCONT .

Хотя это звучит очень забавно для кода, мне интересно, не пропустил ли я трюк в PTRACE документации? Возможно, есть способ перейти в PTRACE_SEIZE режим совместимости, который мне не хватает?

[дополнительные замечания]

почему бы просто не использовать PTRACE_SEIZE в первую очередь?

  • Для PTRACE_TRACEME требуется меньше привилегий безопасности;
  • мы используем seccomp для фильтрации перехватов системных вызовов, которые являются основным моментом;
  • мы хотим быть в начале выполнения дочернего EXE-файла.

Итак, в заключение: существует ли механизм для перехода в PTRACE_SEIZE режим или для имитации SIGSTOP поведения в API в его нынешнем виде?

Ответ №1:

Ниже приведен пример тестовой программы PTRACE_SEIZE, которая имитирует поведение SIGSTOP.

 #define PTRACE_SEIZE        0x4206
  #define PTRACE_SEIZE_DEVEL    0x80000000

  static const struct timespec ts100ms = { .tv_nsec = 100000000 };
  static const struct timespec ts1s = { .tv_sec = 1 };
  static const struct timespec ts3s = { .tv_sec = 3 };

  int main(int argc, char **argv)
  {
      pid_t tracee;

      tracee = fork();
      if (tracee == 0) {
          nanosleep(amp;ts100ms, NULL);
          while (1) {
              printf("tracee: aliven");
              nanosleep(amp;ts1s, NULL);
          }
      }

      if (argc > 1)
          kill(tracee, SIGSTOP);

      nanosleep(amp;ts100ms, NULL);

      ptrace(PTRACE_SEIZE, tracee, NULL,
         (void *)(unsigned long)PTRACE_SEIZE_DEVEL);
      waitid(P_PID, tracee, NULL, WSTOPPED);
      ptrace(PTRACE_CONT, tracee, NULL, NULL);
      nanosleep(amp;ts3s, NULL);
      printf("tracer: exitingn");
      return 0;
  }
  

Когда приведенный выше код вызывается без аргумента, трассировка выводится из
состояния выполнения и продолжается. Когда трассировщик завершает работу, трассировка возвращается в
рабочее состояние и продолжает распечатку.

  # ./test-seize
  tracee: alive
  tracee: alive
  tracee: alive
  tracer: exiting
  # tracee: alive
  tracee: alive
  tracee: alive
  

При вызове с аргументом трассировка перехватывается из состояния stopped и
продолжается, и возвращается в состояние stopped при завершении трассировки.

     # ./test-seize
  tracee: alive
  tracee: alive
  tracee: alive
  tracer: exiting
  # ps -el|grep test-seize
  1 T     0  4720     1  0  80   0 -   941 signal ttyS0    00:00:00 test-seize
  

Вы можете прочитать больше в этой теме

Комментарии:

1. Да, действительно, я вижу, что PTRACE_SEIZE предоставляет такую возможность. Я отметил это в вопросе, и это обсуждается в общедоступной документации PTRACE . Но если я правильно прочитал, для использования любого из механизмов взлома, отличных от PTRACE_TRACEME, помимо прочего, требуются дополнительные привилегии безопасности. Также вопрос о том, можно ли остановить трассировку с момента ее создания?