Как сломать инструкцию с определенным кодом операции в GDB?
У меня есть код сборки некоторого кода, который будет выполняться в какой-то момент в программе. Я не знаю адрес кода в памяти.
Можно ли сделать разрыв GDB, когда текущая инструкция совпадает с введенной инструкции?
Например, я хочу, чтобы gdb ломался всякий раз, когда gdb достигает этой инструкции:
leaq 0x000008eb(%rip),%rax
3 ответа
Нет, это невозможно, и это также будет очень неэффективно для реализации.
Отладчик обычно поддерживает два вида точек останова:
- Аппаратные точки останова: отладчик просит ЦП вызвать специальное прерывание исключения, когда происходит какое-либо событие, например, изменение некоторого места в памяти.
- Программные точки останова: отладчик заменяет код операции по адресу точки останова специальной инструкцией "trap" (
int 3
/0xcc
на архитектуре x86).
Для соответствия операционному коду текущей инструкции потребуется либо поддержка ЦП для вставки аппаратной точки останова, либо отладчик должен знать адрес, чтобы использовать программную точку останова.
Теоретически, отладчик может просто искать во всей памяти последовательность байтов инструкции, но поскольку последовательность байтов также может происходить в середине инструкции или в данных, он может получить ложные срабатывания.
Так как инструкции по сборке имеют переменную длину, элемент управления может перейти на любой произвольный адрес или код может изменить себя, также нетрудно разобрать всю область памяти, чтобы найти какую-то конкретную инструкцию.
Таким образом, в принципе, единственный способ надежного поиска команды в произвольном коде сборки - это пошаговое выполнение на уровне команд. И это будет очень дорого, даже тривиальный вызов библиотеки, такой как printf()
может занять минуты на современном оборудовании, если вы пошагово выполняете каждую инструкцию.
Как говорили другие, это, вероятно, невозможно сделать эффективно, потому что нет аппаратной поддержки.
Но если вы действительно хотите это сделать, эта команда Python может служить отправной точкой:
class ContinueI(gdb.Command):
"""
Continue until instruction with given opcode.
ci OPCODE
Example:
ci callq
ci mov
"""
def __init__(self):
super().__init__(
'ci',
gdb.COMMAND_BREAKPOINTS,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
if arg == '':
gdb.write('Argument missing.\n')
else:
thread = gdb.inferiors()[0].threads()[0]
while thread.is_valid():
gdb.execute('si', to_string=True)
frame = gdb.selected_frame()
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instruction = arch.disassemble(pc)[0]['asm']
if instruction.startswith(arg + ' '):
gdb.write(instruction + '\n')
break
ContinueI()
Просто поставьте его:
source gdb.py
и используйте команду как:
breaki mov
breaki callq
и вы останетесь на первой инструкции, выполненной с заданным кодом операции.
TODO: это будет игнорировать ваши другие точки останова.
Для частного общего случая syscall
, ты можешь использовать catch syscall
: https://reverseengineering.stackexchange.com/questions/6835/setting-a-breakpoint-at-system-call
Я не знаю адрес кода в памяти.
Что мешает вам найти этот адрес? Бежать objdump -d
, найдите интересную инструкцию, запишите ее адрес. Задача решена? (Это тривиально распространяется и на разделяемые библиотеки.)