Я не уверен, правильно ли я понял, о чем вы просите.Но если я правильно понял, вы хотите написать программу, которая:
- Запускает другую программу
- Передает какой-то заранее определенный ввод в другую программу
- Проверяет, есликакая-то точка останова в другой программе была нажата
Я не знаю, возможно ли это с помощью 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", ...);
...