Введение в IDAPython. Часть 1
Специально для паблика SMARTRHINO.
0x00. Знакомство
В наших статьях по реверс-инжинирингу (1, 2) периодически упоминается такой инструмент как IDAPython. Пришло время поговорить про него подробнее, дать советы, поделиться опытом.
Для кого. Для тех, кто уже умеет работать в IDA Pro, но ни разу не писал скрипты на IDAPython. Если вы уже имеете опыт написания скриптов под IDAPython, то вряд ли найдёте здесь что-то новое.
Чего здесь не будет. Не будем учить программировать на Python, не будем учить базовой работе в IDA Pro.
Как очевидно из названия, IDAPython - это всего-навсего интерпретатор Python, встроенный в дизассемблер IDA как инструмент автоматизации . Функции IDAPython являются “обёртками” над IDC (внутренний С-подобный язык автоматизации IDA Pro).
К сожалению, до сих пор IDAPython имеет скудную документацию, и часто ответы на вопросы по API нужно искать в исходниках или получать опытным путём. Модули IDAPython находятся в поддиректории python дизассемблера IDA (обычно это C:\Program Files\IDA 7.0\python\
).
Подразумеваем работу в IDA Pro версии 7.0. IDAPython является плагином для IDA и идёт сразу в “коробочке” (нет необходимости его устанавливать). В версиях IDA до 7.4 используется Python 2.7 64-bit.
Примечание
Начиная с 7-й версии в IDAPython обновили API, а с версии 7.4 отключили поддержку старого API. Модуль idc_bc695.py
обеспечивает обратную совместимость, но в какой-то момент его перестанут поддерживать. В сети в статьях и советах до 2017 года используется старый API, поэтому будьте внимательны и сверяйтесь с текущей документацией.
0x01. Первое свидание
Поскольку IDAPython - это средство автоматизации, то исследователь должен понимать, как сделать то или иное действие вручную, чтобы это действие потом можно было автоматизировать.
Распространенными случаями для такой автоматизации могут быть:
- переименование функций;
- комментирование кода/данных;
- преобразование кода/данных (изменение типов данных, добавление в код перекрёстных ссылок);
- поиск каких-нибудь хитрых шаблонов кода/данных;
- патчинг кода.
Естественно, пространство для творчества велико.
Принятые обозначения и соглашения
При работе с IDAPython принимается ряд условных обозначений:
ea
– Effective Address – адрес в базе IDA, к которому применяется та или иная функция- функция
here()
возвращает текущий адрес, где установлен курсор в Disassembly-окне. Тип результата функции –long
. - большинство функций импортируются из модуля
idc
, этот модуль импортирован по умолчанию. Некоторые функции находятся в других модулях, в этом случае подключение модуля будет указано явно; - в качестве “подопытного” будет использоваться файл прошивки Носорога в формате ELF.
Как выполнить код
Выполнять IDAPython-код можно несколькими способами:
- короткие скрипты в командной строке IDA;
- запуск скрипта через меню File - Script file (Alt + F7);
- запуск скрипта через меню File - Script command (Shift + F2);
- запуск IDA Pro из командной строки с параметром
-S
.
Рассмотрим особенности каждого способа.
Командная строка IDA CLI
Командная строка расположена внизу главного окна IDA. Слева находится кнопка, позволяющая выбрать язык для вводимых команд – нужно кликнуть по ней и выбрать Python.
Особенности IDA CLI
- Ввод осуществляется построчно как в интерактивном режиме Python.
- Для блоков кода (функции, циклы, условия) отступы не добавляются автоматически, поэтому не забываем их добавить.
- Поддерживается автодополнение вводимых функций по клавише Tab.
- Автодополнение работает только для имён функций без указания имени модуля.
- Функции
help
иdir
работают как в обычном Python и позволяют узнать информацию об объекте. - Вывод результатов команд осуществляется в окне Output window (Alt + 0).
Пример
Функция Segments
из модуля idautils
возвращает генератор, выдающий начальные адреса сегментов внутри базы IDA.
Python>import idautils
Python>help(idautils.Segments)
Help on function Segments in module idautils:
Segments()
Get list of segments (sections) in the binary image
@return: List of segment start addresses.
Python>for ea in idautils.Segments():
Python> print("%08x %s" % (ea, idc.get_segm_name(ea)))
Python>
08000000 .isr_vector
080000c0 .text
08006f9c .rodata
08007a14 .init_array
08007a18 .fini_array
20000000 .data
200001f8 .bss
200006c8 ._user_heap_stack
200017f8 abs
Script File
Поскольку в IDA 7.0 используется Python 2.7, то необходимо указать кодировку файла (или использовать только латиницу в комментариях).
При работе с кодом и данными иногда возникает необходимость преобразовать операнды из одного представления в другое. Для этого есть функции с префиксом op_
, вот некоторые из них:
op_bin
– преобразование операнда в двоичный вид;op_dec
– преобразование операнда в десятичный вид;op_oct
– преобразование операнда в восьмеричное число;op_hex
– представление операнда в hex-виде;op_chr
– представление операнда в виде символа;op_seg
– представление операнда в виде ссылки на сегмент;op_stkvar
– преобразование операнда в стековую переменную;op_stroff
– преобразование операнда в поле структуры;op_enum
– представление операнда в виде ENUM-константы;op_plain_offset
– представление операнда в виде ссылки на объект.
Можно сформировать модуль для организации таких преобразований и выполнить его через меню File - Script file (Alt + F7):
# coding: utf-8
''' File: transforms.py
Функции преобразования отображения операндов
'''
START = 0x08000038
END = 0x08000084
def make_offsets32(start_ea, end_ea):
''' Преобразование данных в ссылки на объекты'''
for ea in xrange(start_ea, end_ea, 4):
idc.op_plain_offset(ea, 0, 0)
def make_dwords(start_ea, end_ea):
''' Преобразование данных в 32-битные числа в hex-представлении '''
for ea in xrange(start_ea, end_ea, 4):
idc.op_hex(ea, 0)
if __name__ == '__main__':
make_dwords(START, END)
По аналогии с выполнением скрипта в Python, весь код, находящийся в теле модуля выполняется. В данном случае будет выполнена функция make_dwords
с адреса START
по адрес END
. При этом, все функции, которые есть в загруженном файле, становятся доступны для выполнения в IDA CLI.
Для упрощения доступа к недавним скриптам в IDA есть окно Recent scripts (меню View – Recent scripts Alt + F9):
Важно: если вы разрабатываете и отлаживаете IDAPython-утилиту, которая состоит из нескольких модулей, то при обновлении вспомогательного модуля придётся перезапускать IDA, потому что Python 2 не умеет перезагружать модули, а IDA не умеет отдельно перезагружать свои плагины.
Script Command
Меню File - Script command (Shift + F2) позволяет написать и сохранить скрипт в текущей базе IDA. Это удобно, если предполагается, что некие действия могут выполняться много раз в текущей базе. В случае, если базу нужно передать коллегам, скрипты также автоматически будут переданы.
Код написанный в этом окне сохраняется автоматически. Для выполнения кода нужно нажать кнопку Run.
Запуск IDA c ключом -S
IDA Pro, как и многие приложения, поддерживает запуск с ключами через командную строку. Информацию по всем ключам запуска можно прочитать во встроенной справке (по клавише F1) в разделе Command line switches.
Среди прочих, есть ключи, позволяющие выполнить скрипт при запуске:
C:\Program Files\IDA 7.0\ida.exe" -A -Sscript_name.py rhino.idb
, где
-A
задаёт режим автономной работы без дополнительных диалоговых окон-S
служит для запуска скриптаscript_name.py
однократно при открытии IDB-файла.
Ввод/вывод
При взаимодействии с пользователем есть следующие особенности:
- оператор
print
выводит результат в окно Output window (Alt + 0); -
двойной клик по строке в окне Output window позволяет перейти в Disassembly-окно по адресу или имени объекта, если такой существует в базе IDA. Ниже приведён вывод из Output window с указанием случаев, когда будут и не будут выполняться переходы в Disassembly-окне.
Python>here() 134239060 # 1. при двойном клике здесь не будет перехода Python>hex(here()) 0x8005354L # 2. при двойном клике здесь не будет перехода Python>"%08x" % here() 08005354 # 3. при двойном клике будет выполнен переход Python>get_name(here()) aErrorWrongHead # 4. при двойном клике будет выполнен переход
- функции
input/raw_input
не работают; - для пользовательского ввода нужно использовать
ask_
-функции модулейida_kernwin
иidaapi
.
Отладка скриптов
В привычном понимании отладка скриптов под IDAPython невозможна в силу того, что скрипты должны иметь доступ к “внутренностям” IDA Pro. Имеющиеся рецепты неактуальны для IDA 7 версии.
Актуальными средствами отладки для IDAPython-скриптов можно назвать логирование и вывод промежуточных значений (в народе – “отладка принтами”).
IDAPyHelper
Автодополнение в командной строке помогает, если вы точно знаете или хотя бы предполагаете имя функции, к которой хотите обратиться. Для более наглядного выбора модуля и функции IDAPython можно воспользоваться скриптом IDAPyHelper, который выводит имена доступных модулей и их функций.
IDAPython Cheatsheet
Для упрощения работы с IDAPython мы сделали свою шпаргалку популярных функций. Особенность шпаргалки - цветовое представление типов аргументов и результатов функций.
0x02. Комментирование вызова функции
От общих слов – к конкретным примерам.
Удобным приёмом при исследовании бинарного кода является комментирование отдельных участков. Интересно, что если оставить комментарий в строке с вызовом функции, то этот комментарий будет отображаться в окне ссылок на функцию (Xrefs по клавише X), как это показано на рисунке ниже.
В случае, если обращений к функции достаточно много, расставлять комментарии вручную становится не очень приятно. Такую операцию можно и нужно автоматизировать c использованием IDAPython.
Расставим комментарии в местах обращений к функции memcpy
. Как видно из рисунка, в текущей базе известно 11 обращений к memcpy
:
Общий алгоритм для добавления комментариев будет таким:
- получить все кросс-ссылки на функцию
memcpy
; - в каждом месте обращения к
memcpy
составить список аргументов; - составить строку комментария;
- добавить комментарий.
Для выполнения этих действий потребуются следующие функции и классы:
idautils.CodeRefsTo(func_ea, flow)
– создаёт генератор, который возвращает объекты-ссылки из кода на указанный адрес. Параметрflow
(возможные значения – 0 и 1) задаёт необходимость учитывать ссылки, которые сформированы за счёт простого перехода от инструкции к инструкции. В нашем случаеflow = 0
;idc.prev_head(ea)
– возвращает адрес предыдущей инструкции относительно указанного адреса, это необходимо для прохода вверх по коду для поиска аргументов;idc.get_operand_value(ea, n)
– возвращает значение n-го операнда по указанному адресу. Если операнд является регистром, то возвращается номер регистра в соответствии с текущей процессорной архитектурой. Для архитектуры ARM номера регистров достаточно очевидны:- R0 – 0;
- R1 – 1;
- R2 – 2
- и так далее;
idc.get_operand_type(ea, n)
– возвращает тип n-го операнда по указанному адресу. Основные типы операндов:o_void (0)
– инструкция без операнда (например, NOP);o_reg (1)
– регистр;o_mem (2)
– адрес в памяти;o_phrase (3)
– составной адрес [Base Reg + Index Reg];o_displ (4)
– составной адрес [Base Reg + Index Reg + Displacement];o_imm (5)
– число-константа;o_far (6)
– FAR-адрес;o_near (7)
– NEAR-адрес;- другие типы операндов зависят от архитектуры;
idc.print_operand(ea, n)
– возвращает строковое представление операндаidc.set_cmt(ea, comment, repeat)
– добавление комментария по указанному адресу.
Аргументы функции
Поскольку в архитектуре ARM аргументы передаются в функцию через регистры (R0-R3), необходимо:
- пройти вверх по коду от точки обращения к функции для поиска аргументов;
- найти инструкции, где регистры в регистры R0, R1, R2 заносятся данные (функция
memcpy
принимает три аргумента); - если в регистры заносится число или адрес, то вернуть hex-представление этого числа, для всех остальных случаев вернуть просто текстовое представление операнда.
Комментарии в IDA
Комментарии в базе IDA бывают трёх типов:
-
Простые комментарии– отображаются только в той строке, где они установлены. Добавляются функцией
idc.set_cmt(ea, comment, rpt)
с аргументомrepeat
, равным 0 – – аналогия горячей клавише:
. -
Повторяемые комментарии (repeatable) – помимо основной строки отображаются еще и там, где есть ссылка на строку с комментарием. Добавляются функцией
idc.set_cmt(ea, comment, rpt)
с аргументомrepeat
, равным 1 – аналогия горячей клавише;
. -
Многострочные комментарии в коде – устанавливаются функцией
idc.update_extra_cmt(ea, n, comment)
.
Для нашего случая подходят простые неповторяемые комментарии.
Код
# coding: utf-8
''' File: funcargs.py
Добавление комментария в места вызова функции с аргументами
'''
import idautils
MEMCPY = 0x08006B26
def get_function_arg(ea, narg):
''' Поиск n-го аргумента функции '''
while True:
ea = idc.prev_head(ea)
if idc.get_operand_value(ea, 0) == narg:
break
if idc.get_operand_type(ea, 1) in (idc.o_imm, idc.o_mem):
res = "0x%x" % idc.get_operand_value(ea, 1)
else:
res = idc.print_operand(ea, 1)
return res
def set_comment_by_args(func_ea, nargs):
''' Добавление комментария к вызову функции '''
for ea in idautils.CodeRefsTo(func_ea, flow=0):
func_args = []
for i in range(0, nargs):
arg = get_function_arg(ea, i)
func_args.append(arg)
args = ', '.join(a for a in func_args)
comment = "(%s)" % args
idc.set_cmt(ea, comment, 0)
if __name__ == '__main__':
set_comment_by_args(MEMCPY, 3)
После выполнения кода комментарии будут отображаться в окне кросс-ссылок:
В этот раз мы провели базовое знакомство с инструментом IDAPython, написали несколько несложных функций. В следующий раз продолжим изучение IDAPython, примеров будет больше.
Есть вопросы? Спросить можно в чате SMARTRHINO.
Ссылки
- The Beginner’s Guide to IDAPython by Alexander Hanel (актуальная версия на момент написания статьи v.5.0, API для IDA версия 7.x)
- IDA + Python = IDAPython (используется API для IDA версий 6.x)
- Статьи Using IDAPython to Make Your Life Easier в блоге Palo Alto Networks (используется API для IDA версий 6.x)
- Вопросы-ответы по IDAPython на Reverse Engineering StackExchange
- Шпаргалка по IDAPython