Как скопировать псевдокод в ассемблерный листинг? Как правильно выровнять структуры? Как лучше всего сделать экспорт куска данных для использования в скрипте? В этой статье я покажу компиляцию полезных, как мне кажется, советов для IDA Pro, честно украденных позаимствованных из разных источников и своего опыта.
Выравнивание структур
В изучаемой программе могут использоваться структуры, которые используют "нестандартное" выравнивание, скажем, по одному байту. Такие структуры можно без проблем создать в виде структур (Shift + F9), а в случае использования вида локальных типов нужно воспользоваться директивой pragma, а точнее pragma pack. pragma pack (n) задает выравнивание членов структур (чаще всего по умолчанию используется выравнивание 4 или 8 байт).
Например, если мы имеем дело с такой структурой
struct test_s
{
char ch1;
QWORD qword;
};
и знаем, что ее члены идут один за другим (т.е. сам экземпляр структуры занимает 5 байт), то при ее наложении на данные получим такую картину:
Видно, что между ch1 и qword образовалось неиспользуемое пространство, на месте которого по задумке должен был быть сам qword. Но если перепишем структуру следующим образом:
#pragma pack(1)
struct test_s
{
char ch1;
QWORD qword;
};
то получим корректный результат:
Текстовый поиск
В декомпиляторе нет Ctrl+F, но оказывается, что Alt+T, работающий в окне дизассемблера, точно так же работает и в окне декомпилятора.
Segments > Rebase
Если вы с первого раза не угадали правильный адрес загрузки прошивки, то можете воспользоваться функцией переноса idb на другой адрес.
Например, мы загрузили прошивку в IDA по нулевому адресу. По адресу 0x04 находится указатель на функцию, который сейчас указывает в область памяти, которой не сопоставлено ни одного сегмента в idb.
Зайдем в Edit>Segments>Rebase и перенесем нашу прошивку на адрес 0x8000000.
В результате у нас появился нормальный xref:
Бряки
В IDA, как и в большинстве отладчиков, можно ставить условные бряки, т.е. они будут срабатывать только если выполняется определенное условие. Условие представляет собой конструкцию на языке IDC или Python, в которой во время отладки доступны глобальные переменные с соответствующими именами для доступа к каждому из регистров.
Также во время отладки таким образом можно менять значения регистров и содержимое памяти.
Выделение участков данных и экспорт (Alt+L — Shift+E)
Для выделения большого сегмента данных можно воспользоваться сочетанием клавиш Alt+L, чтобы начать выделение региона и управлять границами выделяемого региона с помощью мыши или клавиш управления курсором. Выделенные данные можно экспортировать в один их удобных форматов (строка, hex-последовательность, массив байт C) и помощью сочетания клавиш Shift+E.
Удаленная отладка
IDA поддерживает удаленные GDB и Windbg-сервера, но также в ней есть возможность удаленной отладки с помощью своих собственных серверов (находятся в каталоге IDADIR\dbgsrv).
Строковые литералы в декомпиляторе
Часто может встречаться ситуация, когда read-only данные перемешаны в одном сегменте с read-write данными (например, при использовании старых компиляторов, компиляторов для RTOS и др.). При этом при использовании декомпилятора можно наблюдать следующее:
Как мы видим, вместо строковых литералов подставляются названия переменных. Это отрицательно сказывается на читабельности псевдокода. Для исправления этой неурядицы можем изменить тип строковых переменных с char на const char.
В результате строковая переменная с исправленным типом будет действительно отображаться в псевдокоде как строковый литерал.
Второй вариант — это настроить декомпилятор на отображение всех строк как литералов (а не только неизменяемых). Для этого убираем галку "Print only constant strings literals" в настройках декомпилятора.
В результате получаем красивый вывод:
Код little endian, а данные big endian
Иногда встречается Little Endian код для ARM, в то время, как данные имеют формат Big Endian. В этом случае для корректного отображения данных надо зайти в General options > Analysis > Processor specific analysis options > Edit ARM architecture options и поставить галку "BE-8 code (ARMB)".
Бряки и gdbserver
То ли я работаю с такими версиями gdbserver, то ли лыжи не едут, но включенная по умолчанию в IDA "single stepping support" у меня не работает, и из-за этого при отладке не срабатывают вообще никакие бряки, кроме самой первой. В этом случае я захожу в настройки отладчика, выбираю "Set specific options" и убираю галку "Use stepping support".
Эта настройка не сохраняется в idb, и для того, чтобы не делать этого каждый раз, в конфигурационном файле dbg_gdb.cfg необходимо исправить параметр SINGLE_STEP.
И еще про gdbserver
И еще поворчу про работу с gdbserver (в этот раз — в случае присоединения к удаленному процессу). Если на целевой машине включен ASLR, то у IDA может не получиться понять реальное расположение образа отлаживаемого файла в памяти. В результате IDA будет пытаться работать с адресами, которые указывают в какое-то невалидное пространство памяти. Для борьбы с этим можно еще до присоединения к удаленному процессу сделать rebase idb на актуальный адрес, и только потом присоединяться к процессу.
Сбор мусора в IDAPython
Интерпретатор Python, встроенный в IDA работает всегда и не закрывается до закрытия самой IDA. Поэтому сущности вашего Python-скрипта или плагина не будут автоматически удаляться. Кроме очевидных последствий вроде внезапных для автора утечек памяти, это может привести и к более забавным побочным эффектам, как, например, во время использования модуля logging.
Если написать такой скрипт
import logging
def main():
logger = logging.getLogger('test1-script')
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('[TEST 1] %(name)s - %(message)s')
ch = logging.StreamHandler()
ch.setFormatter(formatter)
logger.addHandler(ch)
logger.debug("Log message!")
if __name__ == "__main__":
main()
и запустить его два раза подряд, увидим такой вывод:
Происходит это потому, что функция getLogger либо создает, либо получает из глобального кэша уже существующий объект класса Logger. Далее к полученному инстансу логгера (одинаковому при каждом запуске скрипта) добавляется еще один хэндлер. Проверить это можно просто: выводим кэш (словарь) логгеров и список хэндлеров:
Есть как минимум два способа бороться с этим:
logging.Logger.manager.loggerDict.clear()
или
reload(logging)
И последнее
Минутка внимания еще нескольким трюкам:
- Начиная с версии 7.4, объявления локальных переменных в декомпиляторе могут быть "слохпнуты" по умолчанию — это настраивается в файле hexrays.cfg.
- Также в этой версии можно "прыгать" между фигурными скобками в псевдокоде по клавише "%" (Shift+5).
- С помощью горячей клавиши "/" можно скопировать псевдокод в ассемблерный листинг в виде комментариев.
Источники
- Друзья-коллеги
- IDA Pro Book
- https://twitter.com/idatips
- Release Notes
- И другие.
Veliant
Как отучить hexrays портить прототипы функций, делая их __usercall и добавлением в аргументы регистры esi, edi, ebp? Далеко не на всех файлах такое поведение.
sl4v Автор
Не сталкивался с таким, поэтому сходу ответить не могу. Думаю, в крайнем случае можно пробежаться скриптом по всем функциям.