Введение в idapython

The citem_t class

Hex-Rays decompiler SDK refers to the AST as ctree and each node within the tree is represented by either a cinsn_t or a cexpr_t class instance. Both those classes are descendants of the citem_t base class:

struct citem_t
{
  ea_t ea; // address that corresponds to the item
  ctype_t op;// element type
  ...
}

One of the most important fields in a citem_t is its op field value which is of type ctype_t. ctype_t is an enum with constants identifying the type of the node.
Hex-Rays defines two types of constants: cot_xxxx and cit_xxxx. The former denote expression items while the latter denote statements (or instructions in Hex-Rays jargon).

Let us take a look at some of the ctype_t constants:

enum ctype_t
{
  cot_empty    = 0,
  cot_asg      = 2,   //  x = y
  cot_bor      = 19,  //  x | y (binary OR)
  cot_band     = 21,  //  x & y (binary AND)
  cot_eq       = 22,  //  x == y
  cot_add      = 35,  //  x + y
  cot_call     = 57,  //  x(...)
  cot_num      = 61,  //  n
  cot_fnum     = 62,  //  fpc
  ...
  cot_last     = cot_helper,
  // The statements
  cit_block    = 70,  //  block-statement: { ... }
  cit_if       = 72,  //  if-statement
  cit_return   = 79,  //  return-statement
  ...
}

Now given a citem_t instance we can check its op value and see whether to treat that citem_t as a cexpr_t or a cinsn_t.

Выравнивание структур

В изучаемой программе могут использоваться структуры, которые используют «нестандартное» выравнивание, скажем, по одному байту. Такие структуры можно без проблем создать в виде структур (Shift + F9), а в случае использования вида локальных типов нужно воспользоваться директивой pragma, а точнее pragma pack. pragma pack (n) задает выравнивание членов структур (чаще всего по умолчанию используется выравнивание 4 или 8 байт).

Например, если мы имеем дело с такой структурой

и знаем, что ее члены идут один за другим (т.е. сам экземпляр структуры занимает 5 байт), то при ее наложении на данные получим такую картину:

Видно, что между ch1 и qword образовалось неиспользуемое пространство, на месте которого по задумке должен был быть сам qword. Но если перепишем структуру следующим образом:

то получим корректный результат:

Сборка

Epub

В папке с файлами *.md необходимо выполнить команду

pandoc -f markdown -t epub -o ida.epub README.md chast-01.md chast-02.md chast-03.md chast-04.md chast-05.md chast-06.md chast-07.md chast-08.md chast-09.md chast-10.md chast-11.md chast-12.md chast-13.md chast-14.md chast-15.md chast-16.md chast-17.md chast-18.md chast-19.md chast-20.md chast-21.md chast-22.md chast-23.md chast-24.md chast-25.md chast-26.md chast-27.md chast-28.md chast-29.md chast-30.md chast-31.md chast-32.md chast-33.md chast-34.md chast-35.md chast-36.md chast-37.md chast-38.md chast-39.md chast-40.md chast-41.md chast-42.md chast-43.md chast-44.md chast-45.md chast-46.md chast-47.md chast-48.md chast-49.md chast-50.md chast-51.md chast-52.md chast-53.md chast-54.md chast-55.md chast-56.md chast-57.md chast-58.md chast-59.md chast-60.md chast-61.md chast-62.md chast-63.md chast-64.md chast-65.md chast-66.md chast-67.md

FB2

pandoc -f markdown -t fb2 -o ida.fb2 README.md chast-01.md chast-02.md chast-03.md chast-04.md chast-05.md chast-06.md chast-07.md chast-08.md chast-09.md chast-10.md chast-11.md chast-12.md chast-13.md chast-14.md chast-15.md chast-16.md chast-17.md chast-18.md chast-19.md chast-20.md chast-21.md chast-22.md chast-23.md chast-24.md chast-25.md chast-26.md chast-27.md chast-28.md chast-29.md chast-30.md chast-31.md chast-32.md chast-33.md chast-34.md chast-35.md chast-36.md chast-37.md chast-38.md chast-39.md chast-40.md chast-41.md chast-42.md chast-43.md chast-44.md chast-45.md chast-46.md chast-47.md chast-48.md chast-49.md chast-50.md chast-51.md chast-52.md chast-53.md chast-54.md chast-55.md chast-56.md chast-57.md chast-58.md chast-59.md chast-60.md chast-61.md chast-62.md chast-63.md chast-64.md chast-65.md chast-66.md chast-67.md

Function Documentation

int unhook_from_notification_point (   hook_type,
cb,
void *  user_data =  
)

Unregister a callback (also see ).

A plugin should unhook before being unloaded (preferably in its termination function). If different callbacks have the same callback function pointer and user_data is not NULL, only the callback whose associated user defined data matches will be removed.

Returns
number of unhooked functions.
invoke_callbacks (   hook_type,
int  notification_code,
va_list  va 
)

Generate event notification.

Parameters
hook_type hook type
notification_code event code
va additional arguments
Returns
!=0: event processed
bool register_post_event_visitor (   hook_type,
post_event_visitor_t &  visitor,
const plugin_t *  owner 
)

Register the post-event visitor.

The kernel will not take ownership, nor delete the VISITOR instance. Therefore, it’s up to the plugin to handle it. In addition, the post_event_visitor_t will be automatically unregistered when the owner plugin is unloaded from memory.

Parameters
hook_type hook type
visitor post-event visitor
owner the owner plugin of the post_event_visitor_t type
Returns
success
bool unregister_post_event_visitor (   hook_type,
post_event_visitor_t &  visitor 
)

Unregister the post-event visitor.

Parameters
hook_type hook type
visitor post-event visitor
Returns
success

И еще про gdbserver

И еще поворчу про работу с gdbserver (в этот раз — в случае присоединения к удаленному процессу). Если на целевой машине включен ASLR, то у IDA может не получиться понять реальное расположение образа отлаживаемого файла в памяти. В результате IDA будет пытаться работать с адресами, которые указывают в какое-то невалидное пространство памяти. Для борьбы с этим можно еще до присоединения к удаленному процессу сделать rebase idb на актуальный адрес, и только потом присоединяться к процессу.

Сбор мусора в IDAPython

Интерпретатор Python, встроенный в IDA работает всегда и не закрывается до закрытия самой IDA. Поэтому сущности вашего Python-скрипта или плагина не будут автоматически удаляться. Кроме очевидных последствий вроде внезапных для автора утечек памяти, это может привести и к более забавным побочным эффектам, как, например, во время использования модуля logging.

Если написать такой скрипт

и запустить его два раза подряд, увидим такой вывод:

Происходит это потому, что функция getLogger либо создает, либо получает из глобального кэша уже существующий объект класса Logger. Далее к полученному инстансу логгера (одинаковому при каждом запуске скрипта) добавляется еще один хэндлер. Проверить это можно просто: выводим кэш (словарь) логгеров и список хэндлеров:

Есть как минимум два способа бороться с этим:

или

Создаём til-файлы

Эти файлы нужны для хранения информации о типах, об аргументах функций и т.п. По умолчанию. Создаются они с помощью утилиты , которую необходимо положить в каталог с Идой (ей, почему-то, нужен файл).

Данной утилите нужно скормить include-файлы вашего SDK/DDK. При том, парсинг этой утилитой отличается от такового средством «Parse C header file…» в самой IDA. Вот описание из readme:

У этой утилиты есть один нюанс: она по умолчанию использует режим, когда символы заманглены либо имеют стоящее в начале имени нижнее подчёркивание. В случае работы со статически влинкованным кодом этот режим нужно выключить флагом .

По умолчанию, данная утилита принимает на вход только один include-файл. Если же файлов много, нужно соорудить include-файл следующего содержания:

Этот файл передаётся с помощью флага . Далее, передаём путь поиска остальных header-файлов и получаем следующую командую строку:

На выходе получаем til-файл, пригодный для использования. Кладём его в соответствующий каталог IDA: .

Проверяем результат

Закидываем ROM-файл в IDA, дожидаемся окончания анализа. Далее, необходимо указать компилятор. Для этого заходим в ->:

Теперь просто меняем на (в случае PSX). Остальное оставляем как есть:

Теперь жмём (либо меню ->->), жмём и выбираем нужный файл сигнатур:

Жмём и ждём, пока применяются сигнатуры (у меня получилось 482 распознанных функции).

Далее, необходимо применить библиотеку типов (til-файл). Для этого жмём (либо ->->) и понимаем, что IDA не может определить компилятор (несмотря на то, что мы его уже указали):

Но это нам всё равно не помешает выбрать til-файл (всё так же, через ):

Получаем то, что так хотели:

Теперь декомпилятор успешно подхватывает информацию о типах, и выхлоп становится куда лучше:

0x05. Раскраска кода и данных

IDA предоставляет функции для работы с цветом фона в окне листинга. Изменение цвета делает код наглядней, и, следовательно, упрощает работу с ним. Например, если изменить цвет фона инструкций, выполненных в режиме отладки, то будет легче понять структуру кода со множеством ветвлений.

Рассмотрим простой пример – установим для кода и данных разные цвета фона.

Основные функции для работы с цветом фона:

  • – получить цвет фона элемента;
  • – установить цвет фона элемента;

    • цвет представляется моделью RGB и задаётся hex-числом в формате 0xBBGGRR (голубой-зелёный-красный)
    • аргумент задаёт что раскрашивать:
      • – отдельная строка листинга;
      • – полностью функция;
      • – полностью сегмент.

Таким образом, код

раскрасит строки листинга в диапазоне от до в разные цвета:

Как упоминалось выше, при работе с ARM-кодом можно увидеть, что ниже кода функции находятся глобальные адреса объектов, если таковые используются в данной функции:

Определим задачу: установить в кодовых сегментах разный цвет для кода и данных.

Для решения задачи нужно выполнить следующие шаги:

  1. Получить список сегментов. Для этого воспользуемся генератором , который возвращает стартовые адреса сегментов.
  2. Из всех сегментов получить только сегменты с кодом. Для этого воспользуемся функцией , которая возвращает атрибут сегмента. Нас интересует тип сегмента (атрибут — ) .
  3. Пройти по всем элементам каждого сегмента. Тут воспользуемся генератором , который возвращает начальные адреса элементов (инструкций, данных) в интервале адресов от до .
  4. С помощью функций и проверить, содержится ли код в выбранном адресе .
  5. Выполнить раскрашивание сегмента.

Этот алгоритм можно представить следующим кодом:

Примечание

Обратите внимание, что функция принимает битовое поле флагов, а не адрес. Для получения флагов адреса необходимо использовать функцию

После выполнения функции сегменты кода примут вид:

Итак, в этом материале мы сперва познакомились с инструментом IDAPython и написали несколько несложных функций, а затем научились перебирать сегменты и элементы кода (функции и ), раскрашивать код (), а также запрашивать содержимое строки () и числа ().

Ссылки

  • 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

Typedef Documentation

typedef hook_cb_t(void *user_data, int notification_code, va_list va)

Callback provided to .

A plugin can hook to a notification point and receive notifications of all major events in IDA. The callback function will be called for each event.

Parameters
user_data data supplied in call to
notification_code or or , depending on the hook type
va additional parameters supplied with the notification. see the event descriptions for information
Return values
ok, the event should be processed further
!=0 the event is blocked and should be discarded. in the case of processor modules, the returned value is used as the return value of processor_t::notify()

The cfunc_t class

Now that we covered the basic tree elements, let us talk about the cfunc_t class, which is used to hold a decompiled function:

// Decompiled function. Decompilation result is kept here.
struct cfunc_t
{
  //  function entry address
  ea_t entry_ea;
  //  function body, must be a block
  cinsn_t body;
  //  maturity level
  ctree_maturity_t maturity;
  // The following maps must be accessed
  // using helper functions.
  // Example: for user_labels_t,
  // see functions starting with "user_labels_".
  //  user-defined labels.
  user_labels_t *user_labels;
  //  user-defined comments.
  user_cmts_t *user_cmts;
  //  user-defined number formats.
  user_numforms_t *numforms;
  //  user-defined item flags
  user_iflags_t *user_iflags;
  ...
}

When Hex-Rays is asked to decompile a function it returns a cfunc_t instance. The following is an excerpt from the example #1 found at the examples page:

  func_t *pfn = get_func(get_screen_ea());
  if ( pfn == NULL )
  {
    warning("Please position the cursor within a function");
    return;
  }
  hexrays_failure_t hf;
  cfunc_t *cfunc = decompile(pfn, &hf);
  if ( cfunc == NULL )
  {
    warning("#error \"%a: %s", hf.errea, hf.desc().c_str());
    return;
  }
  msg("%a: successfully decompiled\n", pfn->startEA);
  qstring bodytext;
  qstring_printer_t sp(cfunc, bodytext, false);
  cfunc->print_func(sp);
  msg("%s\n", bodytext.c_str());
  delete cfunc;

Among the fields in cfunc_t, body is the most important to us because it points to the root of the ctree. It can be used to traverse the tree manually, but the visitor utility classes provided by the Hex-Rays SDK make that task simpler.

Команда “LED ”

Кратко: исследование LED-команды, продолжаем переименовывать функции и переменные.

  • sub_8003B6E – x_create_struct
  • sub_800532C – x_get_value_1
  • sub_8005338 – x_get_value_2

x_get_value_3

  • работа ведётся со строкой bt_args (или структурой, содержащей строку);
  • когда на вход подается число 2, на выходе – число размером 1 байт;
  • когда на вход подается число 4, на выходе – число размером 2 байта.
  • x_get_value_1 — x_get_byte;
  • x_get_value_2 — x_get_word;
  • x_get_value_3 — x_unhexlify.

sub_8005344x_get_dword

  • пробелы в качестве разделителей;
  • без разделителей.
  • Индекс светодиода (idx) = 0x00;
  • Оттенок (hue) = 0x00;
  • Насыщенность (saturation) = 0xFF;
  • Значение (value) = 0xFF.
  • Включить первый светодиод зелёным:
  • Включить второй светодиод синим:
  • Включить третий светодиод фиолетовым:

sub_8003B7Cdword_20000624

  • функция sub_8004D84 – явно стартовая функция прошивки, так как внутри используется строка «\r\nHardware init done… Starting FreeRTOS\r\n» — переименуем эту функцию в x_main;
  • функция sub_8005A08 – в самом начале использует строку «LED task\r\n» — переименуем эту функцию в x_leds_task.
  • ближе к концу функции main;
  • после получения данных по Bluetooth в x_bluetooth_task;
  • в начале цикла функции x_leds_task.
  • dword_20000624 — leds_queue;
  • sub_8003BD0 — x_queue_recv;
  • sub_8003B7C — x_queue_send.
  • sub_800501C — x_sendMsg;
  • sub_8005044 — x_recvMsg.

x_leds_task

What’s IDA?

IDA(Interactive Disassembler Professional) is a disassembler for computer software which generates assembly language source code from machine-executable code. It supports a variety of executable formats for different processors and operating systems. It also can be used as a debugger for Windows PE, Mac OS X Mach-O and Linux ELF executables. A decompiler plug-in for programs compiled with a C/C++ compiler is available at extra cost. The latest full version of IDA Pro is commercial; while an earlier and less capable version is available for download free of charge. (version 7.0 as of February 2018)

IDA performs automatic code analysis, using cross-references between code sections, knowledge of parameters of API calls, and other information. However, the nature of disassembly precludes total accuracy, and a great deal of human intervention is necessarily required; IDA has interactive functionality to aid in improving the disassembly. A typical IDA user will begin with an automatically generated disassembly listing and then convert sections from code to data and vice versa, rename, annotate, and otherwise add information to the listing, until it becomes clear what it does.

Создаём sig-файлы

Для создания файла сигнатур необходимо воспользоваться набором FLAIR-утилит, доступных лицензионным пользователям IDA. Список необходимых утилит следующий:

  • — LIB/OBJ-parser, создаёт PAT-файл из COFF-объектных файлов
  • — LIB/OBJ-парсер, создаёт PAT-файл из ELF-файлов (Unix)
  • — LIB/OBJ-parser, создаёт PAT-файл из OMF-объектных файлов
  • — MACH-O-парсер, создаёт PAT-файл из MACH-O-файлов (MacOS)
  • — OBJ-парсер, создаёт PAT-файл из библиотечных файлов PSYQ
  • — OBJ-парсер, создаёт PAT-файл из библиотечных файлов Trimedia
  • — конвертирует ранее созданный PAT-файл в SIG-файл, перевариваемый IDA

В моём случае это . Собираю bat-файл, в котором перечисляю все и файлы, и добавляю к каждой строке вызов , чтобы получилось формирование итогового PAT-файла. Получилось следующее содержимое:

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

Видим некоторое количество ошибок парсинга, но в тех файлах всего 1 сигнатура (total 1), поэтому думаю, что это не критично. Далее преобразовываем PAT-файл в SIG-файл:

В итоге получаем следующий список файлов:

  • — его не трогаем
  • — его нужно будет отредактировать
  • — его тоже не трогаем

Открываем на редактирование -файл. Видим:

Если удалить , всё, что содержится в файле ниже, будет учитываться. Давайте взглянем на то, что там есть. Вот пример:

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

В итоге, если всё сделано правильно, получаем SIG-файл. Его нужно положить в соответствующую папку в каталоге установки IDA.

Type of licences available for IDA

Named licences

Named licences are linked to a specific end user and may be used on the user’s laptop, and two desktop computers. They are a logical choice for private users but are also available to corporations and universities if and when only one end user uses the software.

Computer licenses

Computer licences are linked to a specific computer and may be used by different end-users on that computer provided only one user is active at any time. This license type is suitable for corporations because they are not tied to physical persons and allow for easy license reassignment.

Floating licenses

Floating (or Network) licenses can be installed on unlimited number of computers (in one organization) but allow only a limited number of simultaneously running copies.

Note:

IDA Home comes under a Named license scheme only and can be bought as an annual subscription. IDA Home replaces IDA Starter, which discontinues at version 7.4. IDA Starter users with an active support license can convert their license to one of the 5 IDA Home licenses (x86/x64, ARM/ARM64, MIPS/MIPS64, PowerPC/PPC64, Motorola 68K/Coldfire) or to IDA Pro. This conversion is free of charge; future support extensions will be at the cost of the chosen edition. Corporate customer’s licenses will be converted automatically to IDA Pro.

Educational licenses

We offer free educational licenses to universities and other academic institutions that publicly enroll students on a regular basis. Military institutions do not qualify for educational licenses.

The educational license supports x86, x64, ARM, and ARM64 processors. It comes with a native local debugger for Windows or Linux. Python and IDC scripts are supported. Input files up to 1MB can be analyzed.

The license agreement for IDA Educational can be found here.

The cexpr_t class

Let us illustrate how a cexpr_t class instance can represent items with cot_xxxx type values. Below we outline the class members that are relevant to our discussion:

// Ctree element: expression.
// Depending on the exact expression item type,
// various fields of this structure are used.
struct cexpr_t : public citem_t
{
  ...
  union
  {
    //  used for cot_num
    cnumber_t *n;
    //  used for cot_fnum
    fnumber_t *fpc;
    struct
    {
      union
      {
        var_ref_t v;  //  used for cot_var
        ea_t obj_ea;  //  used for cot_obj
      };
      //  how many bytes are accessed? (-1: none)
      int refwidth;
    };
    struct
    {
      //  the first operand of the expression
      cexpr_t *x;
      union
      {
        //  the second operand of the expression
        cexpr_t *y;
        //  argument list (used for cot_call)
        carglist_t *a;
        //  member offset (used for cot_memptr, cot_memref)
        uint32 m;
      };
      union
      {
        //  the third operand of the expression
        cexpr_t *z;
        //  memory access size (used for cot_ptr, cot_memptr)
        int ptrsize;
      };
    };
    //  an embedded statement, they are
    //  prohibited at the final maturity stage
    cinsn_t *insn;
    //  helper name (used for cot_helper)
    //  string constant (used for cot_str)
    char *helper;
    char *string;
  ...
  };
  ...
};

As you notice, cexpr_t employs unions, thus the contained information depends on the op field.
For example if cexpr.op == cot_num then we can safely access cexpr.n field to get a cnumber_t instance and extract the constant number.
If the expression has two operands (e.g. cot_add, cot_sub, cot_bor and so on….), then we have two sub-expressions: cexpr.x is the left-hand side operand and cexpr.y is the right-hand side operand.
In the case of a function call (denoted by op == cot_call) the address of the called function is accessible via cexpr.x.obj_ea field and the arguments are in the a field (which is a carglist_t instance).

Bottom line: first check the op value and then extract the fields from a cexpr_t instance accordingly.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector