Краткая инструкция по использованию отладчика GDB

Инструкция по использованию отладчика GDB

GDB — это кон­соль­ное при­ложе­ние, выпол­ненное в клас­сичес­ком духе коман­дной стро­ки. Вот краткое руководство по использованию отладчика GDB.

Еще по теме: Обход защиты StarForce

И хотя за вре­мя сво­его сущес­тво­вания GDB успел обрасти ворохом кра­сивых гра­фичес­ких морд (сре­ди них DDD, Data Display Debugger, — ста­рей­ший и самый популяр­ный интерфейс), инте­рак­тивная отладка в сти­ле WinDBG в мире Linux край­не непопу­ляр­на.

Внеш­ний вид отладчи­ка GDB
Внеш­ний вид отладчи­ка GDB

Как пра­вило, это удел эмиг­рантов с Windows-плат­формы, соз­нание которых необ­ратимо иска­лече­но иде­оло­гией «око­шек». Гру­бо говоря, если WinDBG — сле­сар­ный инс­тру­мент, то GDB — токар­ный ста­нок с прог­рам­мным управле­нием. Ког­да‑нибудь вы полюбите его.

От­ладчик DDD — гра­фичес­кий интерфейс к GDB

Для отладки на уров­не исходных тек­стов прог­рамма дол­жна быть откомпи­лиро­вана с отла­доч­ной информа­цией. В GCC за это отве­чает ключ -g. Если отла­доч­ная информа­ция недос­тупна, GDB будет отла­живать прог­рамму на уров­не дизас­сем­блер­ных команд.

Обыч­но имя отла­жива­емо­го фай­ла переда­ется в коман­дной стро­ке:

Для отладки активно­го про­цес­са ука­жите в коман­дной стро­ке его ID, а для под­клю­чения коры (core dump) вос­поль­зуйтесь клю­чом:

Все три парамет­ра мож­но заг­ружать одновре­мен­но, попере­мен­но перек­люча­ясь меж­ду ними коман­дой target.

Пе­рек­люча­емся на отла­жива­емый файл:

На при­атта­чен­ный про­цесс:

Или на дамп коры:

Не­обя­затель­ный ключ -q подав­ляет вывод копирай­та. Заг­рузив прог­рамму в отладчик, мы дол­жны уста­новить точ­ку оста­нова. Для это­го слу­жит коман­да break (она же b).

Эта коман­да уста­нав­лива­ет точ­ку оста­нова на фун­кцию main язы­ка C, а вот эта:

на точ­ку вхо­да в ELF-файл (впро­чем, в некото­рых фай­лах она называ­ется по‑дру­гому). Мож­но уста­новить точ­ку оста­нова и на про­изволь­ный адрес:

или

Ре­гис­тры пишут­ся малень­кими бук­вами и пред­варя­ются зна­ком дол­лара. GDB понима­ет два «обще­сис­темных» регис­тра: $pc — ука­затель команд и  $sp — сте­ковый ука­затель. Толь­ко пом­ните, что непос­редс­твен­но пос­ле заг­рузки прог­раммы в отладчик никаких регис­тров у нее еще нет, они появ­ляют­ся толь­ко пос­ле запус­ка отла­жива­емо­го про­цес­са на выпол­нение (коман­да run, она же r).

От­ладчик самос­тоятель­но реша­ет, какую точ­ку оста­нова уста­новить, прог­рам­мную или аппа­рат­ную, и луч­ше ему не пре­пятс­тво­вать (коман­да при­нуди­тель­ной уста­нов­ки аппа­рат­ной точ­ки оста­нова hbreak работа­ет не на всех вер­сиях отладчи­ка). Точ­ки оста­нова на дан­ные в GDB называ­ются точ­ками наб­людения — watch point.

Перечис­лю основные при­емы работы с отладчи­ком.

  • watch addr вызыва­ет отладчик вся­кий раз, ког­да содер­жимое addr изме­няет­ся, а awatch addr — при чте­нии или записи в addr.
  • rwatch addr реаги­рует толь­ко на чте­ние, но работа­ет не во всех вер­сиях отладчи­ка.
  • info break позволяет прос­мотреть спи­сок уста­нов­ленных точек оста­нова.
  • clear уда­ляет все точ­ки оста­нова.
  • clear addr уда­ляет все точ­ки оста­нова, уста­нов­ленные на дан­ную фун­кцию, адрес или номер стро­ки.
  • enable и disable поз­воля­ют вре­мен­но вклю­чать и отклю­чать точ­ки оста­нова. Точ­ки оста­нова под­держи­вают раз­витый син­таксис условных команд, опи­сание которо­го мож­но най­ти в докумен­тации.
  • continue ( c) возоб­новля­ет выпол­нение прог­раммы, прер­ванное точ­кой оста­нова.
  • next N ( n N) выпол­няет N сле­дующих строк кода без вхо­да, а step N ( s N) сo вхо­дом во вло­жен­ные фун­кции. Если чис­ло N не задано, по умол­чанию выпол­няет­ся одна стро­ка.
  • nexti и stepi дела­ют то же самое, но работа­ют не со стро­ками исходно­го тек­ста, а с машин­ными коман­дами. Обыч­но они исполь­зуют­ся сов­мес­тно с коман­дой display/i $pc (x/i $pc), пред­писыва­ющей отладчи­ку отоб­ражать текущую машин­ную коман­ду. Ее дос­таточ­но вызывать один раз за сеанс.
  • jump addr переда­ет управле­ние в про­изволь­ную точ­ку прог­раммы, а call addr/fname вызыва­ет фун­кцию fname с аргу­мен­тами! Это­го нет даже во мно­гих Windows-отладчи­ках. А как час­то оно тре­бует­ся!
  • Дру­гие полез­ные коман­ды:
    • finish — про­дол­жать выпол­нение до выхода из текущей фун­кции;
    • until addr ( u addr) — про­дол­жать выпол­нение, пока ука­зан­ное мес­то не будет дос­тигну­то, при запус­ке без аргу­мен­тов — оста­новить выпол­нение при дос­тижении сле­дующей коман­ды (акту­аль­но для цик­лов!);
    • return — немед­ленно вер­нуть­ся в дочер­нюю фун­кцию.
  • Ко­ман­да print ( p) выводит зна­чение:
    • вы­раже­ния (нап­ример, p 1+2);
    • со­дер­жимого перемен­ной ( p my_var);
    • со­дер­жимого регис­тра ( p $eax);
    • ячей­ки памяти ( p *0x8048424, p *$eax).
  • Ес­ли необ­ходимо вывес­ти нес­коль­ко яче­ек, вос­поль­зуйтесь коман­дой x/Nh addr, где N — количес­тво выводи­мых яче­ек. Ста­вить сим­вол звез­дочки перед адре­сом в этом слу­чае не нуж­но.
  • info registers ( i r) выводит зна­чение всех дос­тупных регис­тров.
  • Мо­дифи­циру­ет содер­жимое яче­ек памяти/регис­тров коман­да set:
    • set $eax = 0 записы­вает в регистр eax ноль;
    • set var my_var = $ecx прис­ваивает перемен­ной my_var зна­чение регис­тра ecx;
    • set {unsigned char*}0x8048424=0xCC записы­вает по бай­товому адре­су 0x8048424 чис­ло 0xCC.
  • disassemble _addr_from _addr_to выда­ет содер­жимое памяти в виде дизас­сем­блер­ного лис­тинга, фор­мат пред­став­ления которо­го опре­деля­ется коман­дой set disassembly-flavor.
  • info frame, info args, info local отоб­ража­ют содер­жимое текуще­го фрей­ма сте­ка, аргу­мен­ты фун­кции и локаль­ные перемен­ные. Для перек­лючения на фрейм материн­ских фун­кций слу­жит коман­да frame N. Коман­да backtrace ( bt) дела­ет то же самое, что и call stack в Windows-отладчи­ках. При иссле­дова­нии дам­пов коры она незаме­нима.

Ко­роче говоря, приб­лизитель­ный сеанс работы с GDB выг­лядит так: гру­зим прог­рамму в отладчик, отда­ем ему коман­ду b main, а если не сра­бота­ет, то b _start, затем r, пос­ле чего отла­жива­ем прог­рамму по шагам ( n/s), при желании задав парамет­ры ( x/i $pc), что­бы GDB показы­вал, что у нас выпол­няет­ся в дан­ный момент.

Выходим из отладчи­ка по коман­де quit ( q). Опи­сание осталь­ных команд ищите в докумен­тации. Теперь по край­ней мере вы не заб­лудитесь в ней.

Еще есть гра­фичес­кий интерфейс gdbgui, который запус­кает­ся внут­ри бра­узе­ра.

Гра­фичес­кий интерфейс к GDB, выпол­няющий­ся в веб‑бра­узе­ре
Гра­фичес­кий интерфейс к GDB, выпол­няющий­ся в веб‑бра­узе­ре

Он пред­став­ляет собой сер­верное при­ложе­ние, написан­ное на Python, и уста­нав­лива­ется через pip:

На выпол­нение он запус­кает­ся подоб­но GDB:

Срав­нение Linux-отладчи­ков с Windows-отладчи­ками показы­вает зна­читель­ное отста­вание пос­ледних и их неп­рофес­сиональ­ную нап­равлен­ность. Трех­мерные кноп­ки, мас­шта­биру­емые икон­ки, всплы­вающие меню [kk9] — все это, конеч­но, очень кра­сиво, но в GDB про­ще написать мак­рос или исполь­зовать уже готовый (бла­го все, что толь­ко было мож­но зап­рограм­мировать, здесь зап­рограм­мирова­ли задол­го до нас).

Меж­ду тем отла­доч­ные средс­тва в Linux не замыка­ются на одном толь­ко GDB. Одна­ко GDB с течени­ем вре­мени доказы­вает свою исклю­читель­ность. Как мы уви­дели, GDB пок­рыва­ет все задачи отладки, и дру­гого в Linux не надо.

Единс­твен­ное, чего ему недос­тает, — нор­маль­ный ядер­ный отладчик сис­темно­го уров­ня, ори­енти­рован­ный на работу с дво­ичны­ми фай­лами без сим­воль­ной информа­ции и исходных тес­тов. Тяжелое детс­тво и ски­тание по мно­жес­тву плат­форм наложи­ло на UNIX мрач­ный отпе­чаток в виде стрем­ления к перено­симос­ти и кросс‑плат­формен­ности. Какое там хакерс­тво в таких усло­виях! Впро­чем, дос­тупность исходных тек­стов дела­ет эту проб­лему неак­туаль­ной.

ПОЛЕЗНЫЕ ССЫЛКИ:

Дима (Kozhuh)

Эксперт в кибербезопасности. Работал в ведущих компаниях занимающихся аналитикой компьютерных угроз. Анонсы новых статей в Телеграме.

Добавить комментарий