Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #93, СЕНТЯБРЬ 2006 г.

Погружение в технику и философию gdb

Крис Касперски

Хакер, номер #093, стр. 093-122-4


(gdb) set $i = $pc

(gdb) set *(unsigned char*)$i++ = 0xB3

(gdb) set *(unsigned char*)$i++ = 0x2A

Здесь после ввода "set *(unsigned char*)$i++ = 0xB3" мы нажимаем "стрелку вверх" и всего лишь меняем 0xB3 на 0x2A (переменная $i увеличивается сама), что намного короче, но все равно длинно и нудно.

А теперь с помощью "define" объявим свою собственную команду dd, которая будет записывать байт по указанному адресу:

(gdb) define dd

type command for definition of "dd".

end with a line saying just "end".

>set *(unsigned char*) $arg0 = $arg1

>end

Обрати внимание, как gdb изменил тип приглашения (">"), когда началось определение команды! Закончив с вводом, мы говорим "end", и новая команда добавляется в память gdb наряду со всеми остальными. Она принимает два аргумента $arg0 – адрес, по которому писать, и $arg1 – записываемый байт.

Теперь для восстановления байт в точке входа достаточно дать следующую последовательность команд:

(gdb) set $i = $pc

(gdb) dd $i++ 0xB3

(gdb) dd $i++ 0x2A

Внимание! Если написать "dd $pc++ 0xB3", то после выполнения команды регистр $pc увеличится на единицу, что никак не входит в наши планы!

Пользовательские команды существуют только на протяжении текущего сеанса, погибая при выходе из gdb, что не есть хорошо, однако, мы можем загнать их в командный файл, к примеру, n2k_cmd:

# vi n2k_cmd

define dd

set *(unsigned char*)$arg0 = $arg1

end

Загрузка подобного файла в память осуществляется командой "source имя_файла" (в нашем случае: "source n2k_cmd"), причем, поскольку gdb поддерживает автодополнение ввода (свойственное практически всем командным интерпретаторам), совершенно необязательно выписывать "source" целиком. Достаточно набрать "so" и нажать <TAB>. Отладчик самостоятельно допишет остальное. Если существует несколько команд, начинающихся с "so", вместо автозавершения раздастся мерзкий писк, сигнализирующей о неоднозначности. Повторное нажатие <TAB>'а приводит к выводу всех возможных вариантов.

Создавая свои собственные команды (загружаемые из ~/.gdbinit или вручную), мы не только обеспечиваем комфортную работу, но и увеличиваем производительность труда! Те, кто обвиняют gdb в неудобстве, просто не умеют затачивать его под себя, но мы, мыщъх'и, — умеем! Кстати, сейчас как раз время, чтобы что-нибудь заточить.

[прежде, чем начинать трассировку]

В отличии от soft-ice (и даже debug.com!), gdb не показывает мнемоники машинных инструкций при трассировке, если его об этом не просят, что сильно смущает новичков, но идеологически так намного правильнее. Отобразить машинную команду по заданному адресу можно с помощью команды "x/i адрес", например:

(gdb) x/i 0x200008

0x200008: jmp 0x200008

Вместо адреса можно использовать любое другое выражение, переменную или регистр (если регистры доступны), однако, отлаживать программу в таком режиме крайне неудобно и лучше задействовать режим автоматического отображения, определяемый командой "display".

Режим автоматического отображения позволяет выводить значение любого выражения, регистра, ячейки памяти, машинной инструкции при каждой остановке gdb (например, при пошаговом выполнении). Команда "display/i $pc" (которую достаточно дать один раз за весь сеанс) будет отображать одну машинную инструкцию под $pc за раз, но это не очень удобно, и на практике постоянно возникает необходимость узнать — какая же будет следующая инструкция за выполняемой. Мыщъх обычно выводит по три инструкции за раз и довольствуется жизнью в полный рост:

Назад на стр. 093-122-3  Содержание  Вперед на стр. 093-122-5