Как я могу создать сценарий тестирования с помощью GDB

#linux #debugging #testing #assembly #gdb

#linux #отладка #тестирование #сборка #gdb

Вопрос:

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

Ответ №1:

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

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

Я не знаю, возможно ли это с помощью 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 которая является байтовой 0xCC )
    Это означает:
    вы должны знать адреса (не номера строк) точек останова и записывать 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,amp;code,0);

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

    /* Continue the program */
    ptrace(PTRACE_CONT,pid,0,0);
    waitpid(pid,amp;code,0);
    if((codeamp;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", ...);
    ...
      

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

1. Большое вам спасибо. Идеальный.