Возможно перехватывание записи по адресу (x86 - linux)

StackOverflow https://stackoverflow.com/questions/608584

  •  03-07-2019
  •  | 
  •  

Вопрос

Я хочу иметь возможность определять, когда происходит запись по адресу памяти - например, устанавливая обратный вызов, привязанный к прерыванию.Кто-нибудь знает, как это сделать?

Я хотел бы иметь возможность делать это во время выполнения (возможно, у gdb есть эта функция, но мое конкретное приложение приводит к сбою gdb).

Это было полезно?

Решение

Если вы хотите перехватывать записи на диапазон адресов, вы можете использовать mprotect() чтобы пометить соответствующую память как недоступную для записи, и установить обработчик сигналов с помощью sigaction() чтобы перехватить полученный SIGSEGV, выполните протоколирование или что-то еще и снова пометьте страницу как доступную для записи.

Другие советы

Все, что вам нужно, это доступ к регистрам отладки X86: http://en.wikipedia.org/wiki/Debug_register

Вам нужно будет установить адрес точки останова в одном из DR0 на DR3, а затем условие (запись данных) в DR7.Произойдет прерывание, и вы сможете запустить свой отладочный код, чтобы прочитать DR6 и найти причину возникновения точки останова.

Если GDB не работает, вы можете попробовать более простой / компактный отладчик, такой как http://sourceforge.net/projects/minibug/ - если это не работает, вы можете, по крайней мере, ознакомиться с кодом и понять, как самостоятельно использовать отладочное оборудование на процессоре.

Кроме того, есть отличный ресурс IBM для разработчиков по освоению методов отладки Linux, который должен предоставить некоторые дополнительные опции:

http://www.ibm.com/developerworks/linux/library/l-debug/

Здесь есть достаточно хорошая статья о том, как это сделать в Windows (я знаю, что вы работаете в Linux, но другие могут ответить на этот вопрос, желая сделать это в Windows):

http://www.codeproject.com/KB/debug/hardwarebreakpoint.aspx

-Адам

У GDB действительно есть такая функция:это называется hardware watchpoints, и оно очень хорошо поддерживается в Linux / x86:

(gdb) watch *(int *)0x12345678

Если в вашем приложении происходит сбой GDB, создайте текущую GDB из Руководитель CVS.

Если этот GDB по-прежнему выходит из строя, создайте GDB ошибка.

Скорее всего, мы сможем исправить GDB быстрее, чем вы сможете взломать обработчик SIGSEGV (при условии хорошего тестового примера), и исправления в GDB также помогут вам справиться с будущими проблемами.

у mprotect действительно есть недостаток:ваша память должна быть выровнена по границам страниц.У меня была проблемная память в стеке, и я не смог использовать mprotect ().

Как сказал Адам, все, что вы хотите, - это манипулировать регистрами отладки.В Windows я использовал это: http://www.morearty.com/code/breakpoint/ и это сработало великолепно.Я также портировал его на Mach-O (Mac OS X), и он тоже отлично работал.Это также было легко, потому что Mach-O имеет thread_set_state(), что эквивалентно SetThreadContext() .

Проблема linux в том, что у нее нет таких эквивалентов.Я нашел ptrace, но подумал, что этого не может быть, должно быть что-то попроще.Но это не так.Пока.Я думаю, что они работают над API hw_breakpoint как для ядра, так и для пользовательского пространства.(см . http://lwn.net/Articles/317153/)

Но когда я нашел это: http://blogs.oracle.com/nike/entry/memory_debugger_for_linux Я попробовал, и все оказалось не так уж плохо.Метод ptrace работает с помощью некоторого "внешнего процесса", действующего как "отладчик", подключающегося к вашей программе, вводящего новые значения для регистров отладки и завершающего работу, когда ваша программа продолжает работу с новой установленной точкой останова hw.Дело в том, что вы можете создать этот "внешний процесс" самостоятельно, используя fork() , (у меня не получилось с pthread) и выполнив эти простые шаги встроенно в ваш код.

Код addwatchpoint должен быть адаптирован для работы с 64-разрядным linux, но это просто изменение USER_DR7 и т.д.для смещения(struct user, u_debugreg[7]).Другое дело, что после PTRACE_ATTACH вам нужно дождаться, пока отладчик действительно остановится.Но вместо повторной попытки POKEUSER в цикле busy правильным решением было бы использовать waitpid() для вашего pid.

Единственная загвоздка с методом ptrace заключается в том, что к вашей программе может быть подключен только один "отладчик" одновременно.Таким образом, прикрепление ptrace завершится неудачей, если ваша программа уже запущена под управлением gdb.Но точно так же, как это делает пример кода, вы можете зарегистрировать обработчик сигнала для SIGTRAP, запустить без gdb и, когда вы поймаете сигнал, ввести цикл занятости, ожидающий подключения gdb.Оттуда вы можете увидеть, кто пытался записать вашу память.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top