#c #pointers #gdb
#c #указатели #gdb
Вопрос:
Почему
Я хочу использовать gdb как способ перехвата open
системного вызова и заставить приложение получить дескриптор другого файла, чем он запрашивал. Что-то вроде:
replace-file filea=fileb cat filea
# prints out contents of fileb
Я могу перехватить open
системный вызов, но мне нужно указать новое имя файла, которое должно быть строкой, находящейся в памяти подчиненного устройства.
Я хотел избежать использования malloc
для этого, потому что тогда я зависел от того, что библиотека доступна в нижней части, и вместо этого сохранял ее в стеке («после» части, используемой процессом), поскольку она нужна мне только для следующей кодовой строки, и у меня нет проблем с ее последующей перезаписью.
Что
Однако мне трудно писать в стек.
Я знаю, что стек растет вниз, поэтому я пытаюсь уменьшить указатель стека на размер моей строки, скопировать строку в указатель, а затем увеличить указатель стека обратно. У меня возникли проблемы с копированием строки в местоположение, на которое указывает указатель стека.
Что у меня есть до сих пор:
# script.gdb
handle all pass handle al pass
set print thread-events off
set $file_a = "/etc/fstab"
set $file_b = "/etc/passwd"
set $len = $_strlen($file_b)
set $len = $len 1
catch syscall open
commands
silent
# $rax == return value
# IF x64, $rdi == filename
set $outside = ! $outside
if ( $_streq((char *)$rdi, $file_a) )
printf "rsp: %dn", $rsp
set $rsp = $rsp - $len
printf "rsp: %dn", $rsp
call strcpy($rsp, $file_b)
printf "rsp: %dn", $rsp
printf "%d: %sn", $rsp,$rsp
set $rsp = $rsp $len
end
continue
end
run
Тестовый пример:
$ gdb -batch -q -x script.gdb --args python -c "print open('/etc/fstab').read()"
Catchpoint 1 (syscall 'open' [2])
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
rsp: -10392
rsp: -10404
$1 = -10404
rsp: -10404
-10404: /etc/passwd
Traceback (most recent call last):
File "<string>", line 1, in <module>
IOError: [Errno 38] Function not implemented: '/etc/fstab'
[Inferior 1 (process 25336) exited with code 01]
Комментарии:
1. Возможно ли, что проблема в выравнивании стека? x86-64 в Linux ожидает выравнивания стека на 16 байт. Так что что-то вроде
set $old = $rsp
,set $rsp = $rsp - $len
,set $rsp = (unsigned long long)$rsp amp; (unsigned long long)~0xf
,.....
,set $rsp = $old
может сработать2. @Andrew нет. Хотя выравнивание интересно, потому что в нем упоминается «strcpy-sse2-unaligned». Я подозреваю, что strcpy здесь плохой и его нужно заменить чем-то другим
3. В настоящее время я думаю, что проблема заключается в выполнении кода в середине системного вызова. Ошибки, которые я получаю, выглядят примерно так github.com/mozilla/rr/issues/605
Ответ №1:
Ваши аргументы для strcpy неверны. Это:
char *strcpy(char *dest, const char *src);
тем не менее, у вас есть:
call strcpy($newfile, $rsp)
Я предполагаю, что $newfile
это действительно $file_b
новое имя, которое вы хотите использовать, поэтому это должно быть src, в то время $rsp
как вы хотите поместить новое имя файла, поэтому dest .
Вам также необходимо сохранить измененное значение $rsp
into $rdi
, чтобы open
увидеть измененный параметр.
Комментарии:
1. Это был хороший шаг, но он еще не работает — отредактировано с результатом. Я думаю, что я пишу поверх другой части стека, потому
Function not implemented: '/etc/fstab
что явно связано с моим именем файла2. Я заглянул в исходный код GDB, и я не думаю, что это будет работать в настоящее время. Использование
call
внутри системного вызова catch приводит к повреждению внутреннего состояния GDB, так что системный вызов завершается некорректно. Я думаю, что ответом может быть написание вспомогательной функции python, которая копирует строку в стек по одному байту за раз, чтобы вы могли избежать использованияcall
.3. можете ли вы связать меня с источником? Звучит очень интересно
4. @Nitz Я думаю, что это хорошая отправная точка sourceware.org/git/gitweb.cgi ? p= binutils-gdb.git;a= blob; f = gdb / … если вы включите ‘set debug infrun 1’ и ‘set debug lin-lwp 1’, вы увидите, что
lp->syscall_state
состояние, которое, как ожидается, будет кэшироваться между входом и выходом системного вызова, повреждается при использовании вызова (пока остановлено при вводе системного вызова). Если вы используете gdb в gdb и размещаете наlp->syscall_state
нем контрольную точку, должно быть достаточно легко увидеть, где именно это происходит.