Пример. Использование трассировки

Процесс, который мы будем отлаживать, осуществляет деление на 0. Это сделано для того, чтобы показать, как происходит прерывание выполнения отлаживаемого процесса, когда к нему приходит сигнал. Т.е. когда было произведено деление на 0, приходит сигнал, который мы и будем ловить в нашем отлаживаемом процессе, но в отличии от обычной ситуации, когда обработка по умолчанию на этот сигнал означала бы завершение процесса, в данном случае он будет только приостановлен. В процессе-отладчике, во-первых, описывается структура, при помощи которой будет осуществляться чтение из регистра; далее порождается процесс; в процессе-сыне запускается трассировка (вызов ptrace с командой PTRACE_TRACEME) и запускается замена тела процесса на процесс, который состоит из единственного деления на 0. Отец запускает wait(), первый выход из этого wait() будет сразу после execl() в сыне. После этого он добывает содержимое некоторых регистров, печатает их содержимое. Обратите внимание, что имена этих регистров – EIP(индексный регистр) и ESP(stack pointer) являются машинно-зависимыми, т.е. на другой машине структура REG, а именно имена полей в ней могут быть другими, но в целом структура REG описывает регистры общего назначения на данной архитектуре. Соответственно, они были получены с помощью команды PTRACE_GETREGS в вызове ptrace, затем они распечатываются, затем идет проверка на то, был ли процесс приостановлен с помощью сигнала SIGTRAP и, если этот так, то продолжается его выполнение.Если это не так (это будет в том случае, когда уже второй раз был получен сигнал SIGTRAP от отлаживаемого процесса, когда он остановился в результате деления на 0) он проверяет, что действительно в статусе передано, что этот процесс должен завершится, и он (процесс) закрывается.

Эта схема работает следующим образом: процесс – сын запускает трассировку и запускает execl(), после чего сразу приостанавливается, потому что к нему пришел сигнал SIGTRAP. Процесс-отец в цикле ожидает в wait(), выходит из ожидания, потому что процесс-сын приостанавливается, далее проверяет содержимое его регистров, печатает это содержимое и запускает опять его выполнение. Теперь процесс-сын выполняет программу, которая осуществляет деление на 0, так только он осуществил деление на 0 ему приходит сигнал SIGFPE, и так как он находится в ситуации трассировки, то он приостанавливается, и управление вновь передается процессу-отцу. Процесс-отец вновь вышел из wait(), проанализировал ситуацию, увидел из-за какого сигнала прервалось выполнение отлаживаемого процесса. Для этого он использует следующие макросы: WSTOPSIG() – это макрос, который позволяет из статуса процесса (из того что вернулось в функции wait()) получить информацию из-за чего тот был приостановлен: т.е. то, что он был приостановлен в результате сигнала или в результате какого сигнала. Далее из этого же статуса с помощью макроса WIFEXITED(), что процесс-потомок должен был завершиться, и в этом случае он просто убивается с помощью использования команды PTRACE_KILL в вызове ptrace.

/* Процесс-сын: */

int main(int argc, char **argv)

{

/* деление на ноль – здесь процессу будет послан сигнал SIGFPE – floating point exception */

return argc/0;

}

/* Процесс-родитель:*/

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

#include <signal.h>

#include <sys/ptrace.h>

#include <sys/user.h>

#include <sys/wait.h>

int main(int argc, char *argv[])

{

pid_t pid;

int status;

struct user_regs_struct REG;

if ((pid = fork()) == 0) {

/*находимся в процессе-потомке, разрешаем трассировку */

ptrace(PTRACE_TRACEME, 0, 0, 0);

execl(“son”, ”son”, 0); /* замещаем тело процесса */

/* здесь процесс-потомок будет остановлен с сигналом SIG_TRAP, ожидая команды продолжения выполнения от управляющего процесса*/

}

/* в процессе-родителе */

while (1) {

/* ждем, когда отлаживаемый процесс приостановится */

wait(&status);

/*читаем содержимое регистров отлаживаемого процесса */

ptrace(PTRACE_GETREGS, pid, &REG, &REG);

/* выводим статус отлаживаемого процесса, номер сигнала, который его остановил и значения прочитанных регистров */

printf("signal = %d, status = %#x, EIP=%#x ESP=%#x\n", WSTOPSIG(status), status, REG.eip, REG.esp);

if (WSTOPSIG(status)!= SIGTRAP) {

if (!WIFEXITED(status)) {

/* завершаем выполнение трассируемого процесса */

ptrace (PTRACE_KILL, pid, 0, 0);

}

break;

}

/* разрешаем выполнение трассируемому процессу */

ptrace (PTRACE_CONT, pid, 0, 0);

}

}



Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: