Читать стек другого процесса?

16

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

Я также попытался прочитать данные /proc/$pid/memиз границ стека, извлеченные из /proc/$pid/mapsфайла, после первого использования ptrace для подключения к нему (как предлагается здесь ), но чтение продолжает давать сбой (даже при запуске от имени пользователя root), хотя тот же код успешно выполняется при попытке чтение из разных частей процесса (например, кучи).

Что я делаю неправильно? Есть ли другой вариант?

user4537
источник
Вы звонили waitpidмежду ptrace(PTRACE_ATTACH,…)и read(иначе возможное состояние гонки)? Какую ошибку readвозвращает? Делает ли ребенок что-то своеобразное с отображением памяти - можете ли вы попробовать свой код с простым ребенком, как sleep?
Жиль "ТАК - перестань быть злым"
Я использовал wait после ptrace, и я поместил scanf в ребенка, чтобы заставить его ждать.
user4537 20.02.11
Это только в Linux? В Solaris также есть файловая система / proc, но она полностью отличается от Linux, хотя и философски. Много «бинарных файлов».
Брюс Эдигер
просто сделайте систему ("pstack pid ") и проанализируйте вывод ..
vrdhn
Смотрите ps: полная команда слишком длинна для некоторых примеров
Стефан Шазелас

Ответы:

5

ptraceинтерфейс позволяет читать только одно слово за раз, а я пытаюсь сканировать большие части стека

Ну, тогда просто используйте цикл. Честно говоря, я не понимаю, с чем это связано ptrace, я все время использую его для удаленного доступа к процессам.

Я использую что-то вроде этого:

static int memcpy_from_target(pid_t pid, char *dest, long src, size_t n)
{
    static int const align = sizeof(long) - 1;

    while (n)
    {
        size_t todo = MIN(n, sizeof(long) - (src & align));
        long data = ptrace(PTRACE_PEEKTEXT, pid, src - (src & align), 0);
        if (errno)
        {
            perror("ptrace_peektext (memcpy_from_target)");
            return -1;
        }
        memcpy(dest, (char *)&data + (src & align), todo);

        dest += todo; src += todo; n -= todo;
    }

    return 0;
}
Сэм Хоцевар
источник
Привет Сэм, хотя твой код будет делать (и на самом деле это то, чем я сейчас занимаюсь), он сильно снижает производительность.
user4537
@ user4536: Понятно. Я имею в виду другую стратегию, о которой я опубликую, когда у меня будет время записать ее. Каковы ваши типичные размеры стека?
Сэм Хочевар
Трудно сказать на самом деле, так как мое исследование не предполагает определенного размера стека, но ради этого аргумента скажем, по крайней мере, несколько страниц в длину. Не могли бы вы дать намек относительно вашей стратегии? Все равно, спасибо за помощь!
user4537
1

Вот еще одна стратегия, которая может нуждаться в доработке, но должна быть более эффективной с большими порциями данных. Идея состоит в том, чтобы выполнить syscalls в удаленном процессе, чтобы извлечь содержимое стека. Для этого потребуется конкретный код архитектуры, но если вы ориентируетесь только на x86 / x86_64, это не должно вызывать особых хлопот.

  1. Создайте именованный канал, такой как "/tmp/fifo"в вашем процессе вызова.
  2. Шагайте в отслеживаемый процесс до тех пор, пока он не вернется из системного вызова, используя PTRACE_SYSCALLшаг, waitpid()чтобы подождать и PTRACE_GETREGS/ PTRACE_PEEKTEXTпроверить текущий исполняемый код операции.
  3. Резервное копирование регистров удаленного процесса и небольшой области его стека.
  4. Выполнение системных вызовов на удаленном процессе, перекрывая его стек с вашими собственными данными: open("/tmp/fifo"), write()содержимое стека, close()дескриптор.
  5. Восстановить состояние удаленного процесса.
  6. Прочитайте данные fifo из вашего процесса вызова.

Могут быть более элегантные альтернативы именованным каналам, но я не могу сейчас придумать ни одного. Причина, по которой я использую только системные вызовы, заключается в том, что удаленное внедрение кода довольно ненадежно в современных системах из-за различных мер безопасности. Недостатком является то, что он будет зависать, пока удаленный процесс не выполнит системный вызов (что может быть проблемой для некоторых программ, которые в основном выполняют вычисления).

Вы можете увидеть бесплатный код, реализующий большую часть работы в этом исходном файле . Обратная связь по коду приветствуется!

Сэм Хоцевар
источник
1

Еще одно предложение.

Когда / если он будет принят в основном дереве ядра Linux, вы сможете использовать патч Cross Memory Attach от Christopher Yeoh . Смотрите документацию для process_vm_readv, например.

Сэм Хоцевар
источник
1

Вы можете легко прочитать стек другого процесса, используя файловую систему proc (для этого вам понадобится root-доступ). Перед произвольным чтением из / proc / pid / mem вам нужно обратиться к / proc / pid / maps. Простое чтение в этом файле показывает много записей. Нас интересует запись, помеченная как стек. Как только вы получите это, вам нужно прочитать нижнюю и верхнюю границы стека. Теперь просто откройте файл / proc / pid / mem, найдите нижнюю границу стека и прочитайте правильный размер данных.

Аджай Брахмакшатрия
источник
1
Вы уверены, что имеете в виду memsи нет maps? (Я не вижу никаких memsзаписей в моей /procфайловой системе.) ОП уже упоминал, что читает границы стека /proc/$pid/maps- что вы предлагаете делать по-другому?
JigglyNaga
Отредактировал опечатку. Я сделал именно то, что упомянул в своем ответе, и он сбросил 132 КБ стековых данных. Нам нужно больше информации о том, что ОП сделал не так. Возможно, OP сможет поделиться кодом, который он использовал для чтения границ стека. Если он не ответит, я поделюсь своим.
Аджай Брахмакшатрия
0

Вы можете попробовать lsstack . Он использует ptrace, как и любая другая успешная программа «чтение стека другого процесса». Я не смог заставить работать программу, использующую чтение / proc / $ pid / mem. Я считаю, что вы не можете сделать это таким образом, хотя, по логике, вы должны.

Брюс Эдигер
источник