#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. Большое вам спасибо. Идеальный.