Как я могу создать тестовый скрипт с GDB - PullRequest
1 голос
/ 03 апреля 2019

У меня есть набор входов, которые я хочу использовать для тестирования моей программы, чтобы увидеть, какой вход достигнет точки останова.Я хочу создать скрипт для проверки этих входов один за другим и, если он достигнет точки останова, распечатать или сохранить результат в файл.Пожалуйста, дайте мне знать, если это возможно, и если да, как я могу это сделать.Спасибо.

1 Ответ

1 голос
/ 03 апреля 2019

Я не уверен, правильно ли я понял, о чем вы просите.Но если я правильно понял, вы хотите написать программу, которая:

  • Запускает другую программу
  • Передает какой-то заранее определенный ввод в другую программу
  • Проверяет, есликакая-то точка останова в другой программе была нажата

Я не знаю, возможно ли это с помощью gdb, но можно было бы написать собственный отладчик:

  • Запустите тестируемую программу, используя fork и одну из функций exec (например, execlp)
  • До вызова функции exec ptrace(PTRACE_TRACEME,0,0,0)
  • Вызов waitpid;если exec успешно, программа будет немедленно остановлена.«Код состояния» (второй аргумент), возвращаемый waitpid, будет 0x57F (при условии использования процессора x86).
  • Если waitpid возвращает другой код выхода, exec не выполнен, и вы не можете продолжить.
  • Используйте ptrace(PTRACE_PEEKTEXT,...) и ptrace(PTRACE_POKETEXT,...) для изменения программы: Вы устанавливаете точку останова на некоторый адрес, заменяя инструкцию по этому адресу инструкцией «точка останова» (на процессорах x86: int3, что является байтом *).1036 *)
    Это означает:
    Вы должны знать адреса (не номера строк) точек останова и писать 0xCC для каждого адреса, используя ptrace().Поскольку PTRACE_POKETEXT может изменять только 4 байта (x86_32) или 8 байтов (x86_64) одновременно, сначала вы должны прочитать старые значения этих 4 или 8 байтов, используя PTRACE_PEEKTEXT, изменить 1 из 4 или 8 байтов и записать все4 или 8 байтов назад.
  • Если ваша программа не всегда загружается на один и тот же адрес (из-за ASLR и т. Д.), Вы можете прочитать счетчик программы (используя PTRACE_PEEKUSER): он должен быть (фактическим)адрес точки входа в программу.
  • Позвоните ptrace(PTRACE_CONT,pid,0,0), чтобы запустить тестируемую программу
  • Позвоните waitpid, чтобы дождаться остановки программы или выйти
  • Если waitpid возвращает 0x57F в качестве «кода состояния», вы находитесь в точке останова.Теперь вы можете использовать kill(pid, SIGKILL) для остановки вашей программы.
  • Вы можете использовать PTRACE_PEEKUSER, чтобы проверить значение счетчика программы (rip на x86-64), чтобы вы знали, какая точка останова была достигнута.Обратите внимание, что программный счетчик может быть адресом точки останова плюс 1 , поэтому, если достигнута точка останова по адресу 0x12340000, rip может быть 0x12340001.
  • Если возвращается waitpidлюбое другое значение с младшим байтом 0x7F программа вызвала исключение.Вы должны использовать kill(pid,SIGKILL), чтобы окончательно остановить его.
  • В противном случае (если младший байт, возвращаемый waitpid, не равен 0x7F), программа завершила работу, не вызвав исключения и не достигнув точки останова.

Вот пример кода:

int pid, code;
long tmpLong;

pid=fork();

if(!pid)
{
    ptrace(PTRACE_TRACEME,0,0,0);
    execlp("program_to_be_tested","program_to_be_tested",NULL);
    exit(123);
}

waitpid(pid,&code,0);

if(code!=0x57F)
{
    /* Starting the program failed ... */
}
else
{
    /* Set breakpoints - here assuming x86-64 */
    tmpLong=ptrace(PTRACE_PEEKDATA,pid,(void *)(address&~7),0);
    ((char *)&tmpLong)[address&7]=0xCC;
    ptrace(PTRACE_POKEDATA,pid,(void *)(address&~7),(void *)tmpLong);

    /* Continue the program */
    ptrace(PTRACE_CONT,pid,0,0);
    waitpid(pid,&code,0);
    if((code&0xFF)!=0x7F)
    {
        /* Program did not hit a breakpoint
         * and did not cause an exception */
    }
    else if(code==0x57F)
    {
        /* Breakpoint hit */
        kill(pid,SIGKILL);
    }
    else
    {
        /* Program caused an exception */
        kill(pid,SIGKILL);
    }
}

Чтобы передать ввод в вашу программу, у вас есть два возможных варианта:

  • Несколько раз запустить отладчик:

    echo "Input to be tested" | ./myDebugger
    

    Поскольку ваш отладчик не читает из STDIN, ввод будет передан в тестируемую программу.

  • Использование pipe и dup2 при создании дочернего процесса:

    ...
    pipe(pipes);
    pid=fork();
    if(!pid)
    {
        dup2(pipes[0],0);
        close(pipes[0]);
        close(pipes[1]);
        ...
    }
    close(pipes[0]);
    write(pipes[1],"Input to be sent to program", ...);
    ...
    
...