Встраивание Perl в IDA Pro 4.70 (Second Edition) — Архив WASM.RU

Все статьи

Встраивание Perl в IDA Pro 4.70 (Second Edition) — Архив WASM.RU

Встраивание Perl в IDA Pro 4.70 (Second Edition)

  Дети не любят
Никто не жалеет
Больше плюют
Да, я из погреба
я как умею
Так и живу...

Аукцыон "Пионер", 1990

  1. Назначение
  2. Требования
  3. Теория
  4. Получаем idc.out
  5. Процесс сборки
  6. Инсталляция
  7. Инструкция по использованию
  8. Примеры scriptов
  9. Описания исходных файлов
  10. Жалобы и предложения

Назначение

В данном труде описывается технология встраивания Perl в IDA Pro строго версии 4.70 как в качестве скриптового движка для работы в дизассемблере, так и в IDA debugger.

С 2002 года, когда я в первый раз описал технологию расширения функциональности IDA Pro через отличные от IDC script engines, IDA сильно продвинулась в своем развитии. Начиная с версии 4.70 существует документированный способ расширения встроенного языка IDC через ф-цию set_idc_func, так что может показаться, что нужда в сторонних script engines практически отпадает. Но это не так - мой трехлетний опыт работы с встроенным в IDA perl позволяет утверждать, что он намного удобнее IDC для написания таких скриптов как импорт данных из текстовых файлов, при работе с массивами и более сложными структурами данных (вроде списка hashей) и просто perl намного удобнее для расширения функциональности - достаточно сказать что за последний год я не использовал IDC вообще. Кроме того не так давно в IDA появился встроенный отладчик. Я покажу как добавить поддержку скриптовых языков также и к нему - и в результате такой нехитрой операции можно получить совершенно новый инструмент с практически неограниченными возможностями - управляемый через scriptы отладчик.

Требования

Вам потребуется следующее:
  • IDA Pro 4.70 + соответствующий SDK. Хм, было бы странно обойтись без этого пункта
  • Инсталлированный дистрибутив Perl. В принципе годится любая 5.x (где x > 3) версия perl для win32, лучше всего от ActiveState
  • Если вы собираетесь дописывать свои собственные функции расширения функциональности модулей IDA или IDADbg, то для автоматизации bindingа ф-ций потребуется SWIG. Я использовал SWIG версии 1.3.17
  • Компилятор C++ - лучше Visual C++. По крайней мере тестировалось все нижеописанное под Visual C++ 6, Visual C++ 7 & Intel C++ 8.0.
  • Знание собственно Perlа и умение написать на нем программу длиннее #!/usr/bin/perl
    совершенно необходимы !
  • Не помешает также опыт в создании собственных IDA plugins

Теория

Существует множество способов экспортировать функции из IDA Pro в script engine - я перечислю лишь некоторые из них:
  1. самый простой - реализовать все функции самостоятельно используя IDA SDK. Но, учитывая, что, например только в IDC более 300 функций - это удовольствие будет длиться неопределенное (хотя и конечное) время. Далее эти реализованные функции можно экспортировать в Perl либо через механизм XS, либо через тот же SWIG, либо же опять же вручную...
  2. Поступить как автор Python for IDA - модифицировать заголовки для IDA SDK таким образом, чтобы они стали понятными тому же SWIGу. Для некоторых языков такой грязный хак окажется вполне работоспосбоным (скажем для того же Python или Ruby), но для Perl он опять же неприемлем. Дело в том, что в IDA SDK содержится очень большое количество разнообразных объектов в виде классов C++, а SWIG же для экспорта классов в Perl не слишком удобен - уж слишком самобытно в Perl реализованы объекты и слишком громоздкими получаются имена функций.
  3. Использовать уже имеющиеся функции от IDC. Этот подход имеет кроме значительного сокращения ресурсов на разработку также то преимущество, что конечному пользователю не нужно будет переучиваться к чужому набору функций - если человек умеет писать скрипты на IDC, то в Perlе 90% функций будут просто теми же самыми, что и в IDC
Так что для встраивания Perl я использовал именно третий подход, хотя для встраивания Ruby (может быть когда-нибудь я опишу и этот инструмент) - второй.
Кроме того, для встраивания Perl в IDA debugger мне все равно пришлось написать некоторое количество функций самостоятельно - я сначала использовал SWIG, а потом просто дописал примерно половину функций руками.

Получаем idc.out

Прежде всего нам необходимо извлечь из самой IDA Pro описание ее IDC функций. Если вы помните, раньше сей процесс включал в себя дизассемблирование ida.wll, нахождение в нем массива IDCFuncs и запуск IDC scriptа, извлекающего всю необходимую нам информацию. Но начиная с версии IDA 4.50 Великий Яхве наконец услышал наши проклятья, и IDCFuncs стал экспортируемым из ida.wll символом. Таким образом задача сильно упростилась - я просто написал IDA plugin idc_dump и теперь процесс получения файла idc.out выглядит так:
  1. собираем из исходников в поддиректории idc_dump одноименный plugin. Если вы слишком ленивы то он уже есть в собранном виде для IDA 4.70
  2. Копируем его в подкаталог plugins IDA Pro
  3. Запускаем IDA Pro на любом файле, идем в меню Edit->plugins и вызываем наш plugin - и благодаря чорной магии в текущем каталоге появляется искомый файл idc.out
Впрочем, поскольку 99% людей настолько же ленивы как автор сего - я просто предоставляю готовый файл idc.out для IDA версии 4.70

Процесс сборки

Если вы не собираетесь добавлять свои собственные функции в модули - можете просто воспользоваться предоставленными файлами проекта rp_vc.dsp, rp_vc.dsw и perl_dbg\perl_dbg.dsp и дальше просто не читать. Без пересборки pluginов не обойтись, ибо скорее всего у вас стоит версия Perl, отличная от моей. По этой же причине я не предоставляю исполнимых файлов (кроме вышеупомянутого idc_dump.plw).

Если же вы хотите скажем добавить пару (или пару сотен) своих функций для их последующего использования в своих Perl scriptах, то вам необходимо сделать нижеописанные телодвижения. Во первых, информации из файла idc.out недостаточно - дело в том, что SWIG умеет работать только с полностью типизированными функциями, а у нас пока отсутствуют типы возвращаемых значений. Поэтому был написан файл outz\horror.pl, который умеет делать следующее - он берет файл idc.out, а также idc.idc из подкаталога idc IDA Pro, парзит последний и формирует из этой информации заголовочный файл ida.h, файл интерфейса для SWIG ida.i, и файл для marshalling аргументов и результатов для вызова IDC функций из нашего pluginа ida.cpp. Обратите внимание, что начиная с версии 4.70 изменился прототип вызова функций из массива IDCFuncs - если раньше они были Borland fastcall, то теперь они стали idaapi (что тоже самое что и stdcall). Так что старая версия horror.pl не годится. Кроме того, поскольку я реализовал некоторое количество функций не через IDCFuncs, а напрямую, используя средства IDA SDK - в файле ida.cpp реализации таких функций будут отсутствовать. Вызов horror.pl производится с ключами -t h для генерации ida.h, -t i для генерации ida.i и -t c для генерации ida.cpp. Далее запускаете SWIG с примерно такими параметрами

swig.exe -perl -static ida.i
(см. также файл outz/s.bat) и получаете файл с кодом bindingа ваших функций для Perl ida_wrap.c. Ну или не получаете... Одно из двух.
После этого у вас наконец есть все файлы для сборки pluginа - копируете все 3 файла на директорию выше и собираете plugin как описано выше.

Инсталляция

очень проста - скопируйте perl_dbg.plw и rp_vc.plw в подкаталог plugins IDA Pro, а файлы экспорта для модулей Perl pm/ida.pm и pm/idadbg.pm в подкаталог lib вашего дистрибутива Perl (pm-файлы).

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

rp_vc.plw - реализует подмножество IDC функций для модуля IDA, а также содержит perl script engine. Обратите внимание, что он не реализует функции из модуля IDADbg, так что при попытке их вызова из обычного perl scriptа вы получите сообщение об ошибке. Модуль экспорта для него лежит в файле pm/IDA.pm
perl_dbg.plw содержит в себе реализацию обоих модулей IDA и IDADbg. Так было сделано потому, что perl_dbg.plw работает только с x86 PE файлами и только через callback функции, вызываемые при отладке (так что в perl_dbg функция main скрипта не вызывается). Соответственно он требует для работы два модуля экспорта - pm/IDA.pm и pm/IDADbg
rp_vc.plw же работает с любым процессорным модулем и любым форматом файлов, и запускает функцию main в обрабатываемом скрипте автоматически.

В perl_dbg.plw реализована поддержка следующих событий отладчика (обратите внимание что имена обработчиков в скрипте должны иметь предопределенные имена. Впрочем, если эти имена вам не нравятся - можете изменить их в массиве cb_sub_names в файле dbg_callz.cpp):

  • cbProcessStart - вызывается при старте отлаживаемого процесса. В качестве аргумента получает DWORD - идентификатор процесса.
  • cbProcessExit - вызывается после завершения отлаживаемого процесса. В качестве аргументов получает идентификатор почившего процесса а также его exit_code
  • cbThreadStart - вызывается при старте нового потока. Аргументом служит идентификатор потока.
  • cbThreadExit - вызывается после завершения потока. Аргументами являются идентификатор потока t_id и код его завершения exit_code. Обратите внимание что в этом обработчике поток уже мертв и нельзя обращаться к его контексту через t_id
  • cbLibraryLoad - вызывается после загрузки какого-либо модуля, причем модуль может быть загружен как при старте процесса через его таблицу импорта, так и через явный вызов LoadLibrary. Аргументом передается ссылка на hash с характеристиками модуля
  • cbLibraryUnload - вызывается после выгрузки модуля. Аргументом передается имя выгруженного модуля, причем перед вызовом этого callbackа модуль уже удален из внутреннего списка модулей, получаемого из modules_list
  • cbBpt - Самый Главный Обработчик - вызывается при достижении breakpoint. Аргументы: t_id - идентификатор потока, addr - адрес случившегося breakpoint
  • cbException - вызывается при исключении. Аргументы: t_id- идентификатор потока, code - код исключения, addr - адрес исключения, info - строка с описанием исключения.
Кроме поддержки этих обработчиков модуль IDADbg также реализует следующие относительно полезные при работе с отладчиком функции:
  • ProcessQty - количество запущенных в системе процессов
  • GetProcessInfo(n, out_hash) - выдает информацию по процессу номер n в ссылке на hash out_hash (ключами в hash будут name - имя главного модуля процесса и id - идентификатор процесса)
  • ThreadQty - количество запущенных потоков
  • GetThreadN(n) - выдает идентификатор потока для потока под номером n, где n должен быть в диапазоне от 0 до ThreadQty() - 1
  • ProcessState - возвращает статус отлаживаемого процесса (см. описание get_process_state из файла dbg.hpp IDA SDK
  • StartProcess(path, args, dir) - запускает процесс под отладчиком. path - имя модуля, args - аргументы командной строки для модуля, dir - стартовая директория для процесса
  • SuspendProcess - замораживает отлаживаемый процесс
  • ContinueProcess - продолжает исполнение ранее замороженного процесса
  • Exit_Process - завершает отладку процесса
  • AttachProcess(p_id) - подключает отладчик к процессу с идентификатором p_id
  • DetachProcess - отключает отладчик от процесса
  • SelectThread(t_id) - активизирует поток с идентификатором t_id
  • StepInto - см. описание функции step_into в файле dbg.hpp IDA SDK
  • StepOver - см. описание функции step_over в файле dbg.hpp IDA SDK
  • RunTo(addr) - см. описание функции run_to в файле dbg.hpp IDA SDK
  • StepUntilRet - см. описание функции step_until_ret в файле dbg.hpp IDA SDK
  • BptQty - возвращает количество установленных breakpoints
  • GetBptN(n, out_hash) - возвращает характеристики точки останова номер n, где n должен быть в диапазоне от 0 до BptQty() - 1. Информация помещается в ссылку на hash out_hash, ключами в этом hash могут быть:
    • ea - адрес точки останова
    • size - размер точки останова
    • type - тип точки останова
    • pass_count - количество срабатываний точки останова
    • flags - флаги точки останова
    Смотри пример использования этой функции в примере скрипта для IDA debugger ниже
  • GetBpt(ea, out_hash) - практически полностью идентична предыдущей функции, только ищет точку останова не по ее номеру, а по адресу
  • AddBpt(ea, size, type) - устанавливает новую точку останова по адресу ea размером size и с типом type. В случае удачи возвращает 1. Смотри пример использования этой функции в примере скрипта для IDA debugger ниже
  • DelBpt(ea) - удаляет точку останова по адресу ea Смотри пример использования этой функции в примере скрипта для IDA debugger ниже
  • EnableBpt(ea) - разрешает обработку точки останова по адресу ea
  • DisableBpt(ea) - запрещает обработку точки останова по адресу ea
  • dbg_module(out_hash) - возвращает в ссылке на hash out_hash описание отлаживаемого модуля. Ключи в hash могут быть:
    • name - строка, имя модуля
    • base - базовый адрес модуля
    • size - размер модуля
    • rebase - фактический адрес, по которому модуль был загружен
  • modules_list(out_array) - возвращает ссылку на массив ссылок на hash с описаниями всех загруженных в отлаживаемом процессе модулей. Ключи в hash идентичны описанным в предыдущей функции. Смотри пример использования этой функции в примере скрипта для IDA debugger ниже
  • ThreadContext(t_id, kind_of_context, out_hash) - функция получает контекст потока с идентификатором t_id, содержимое контекста задается в переменной kind_of_context, (ее значения см. в описании поля ContextFlags структуры _CONTEXT в справке по Win32 API). В out_hash будет помещена ссылка на hash, содержащий в качестве ключей имена регистров, а в качестве значений для этих ключей - текущее значение соответствующих регистров. Смотри пример использования этой функции в примере скрипта для IDA debugger ниже
  • set_DrX(t_id, reg_value) где X может быть 0, 1, 2, 3, 6, 7. Устанавливает содержимое соответствующего регистра DrX для потока с идентификатором t_id в reg_value
  • set_Gs(t_id, reg_value) - аналогично для регистра Gs
  • set_Fs(t_id, reg_value) - аналогично для регистра Fs
  • set_Es(t_id, reg_value) - аналогично для регистра Es
  • set_Ds(t_id, reg_value) - аналогично для регистра Ds
  • set_Cs(t_id, reg_value) - аналогично для регистра Cs
  • set_Ss(t_id, reg_value) - аналогично для регистра Ss. Но вы должны точно знать что делаете ...
  • set_Efl(t_id, reg_value) - аналогично для регистра флагов
  • set_REG(t_id, reg_value)Устанавливает содержимое регистра общего назначения REG (REG можеть быть Edi, Esi, Ebx, Edx, Ecx, Eax, Ebp, Esp или Eip) для потока с идентификатором t_id в reg_value Смотри пример использования этой функции в примере скрипта для IDA debugger ниже

Примеры scriptов

Примерчик scriptа, который расставляет комментарии к двойным switch-tables (т.е. когда первая таблица содержит индекс во второй, а уже вторая содержит адреса перехода). Параметры ф-ции process_tables:
  1. адрес таблицы индексов
  2. адрес таблицы адресов
  3. delta, которую нужно прибавить к каждому индексу. Аргумент необязателен
  4. размер таблицы индексов. Можно не указывать - тогда будет вычислен как разность между вторым и первым аргументами
#!/usr/bin/perl
# This terrible script processed 2table switches
# (C) RedPlait, 25 Nov 2004

use IDA;

sub form_string
{
  local $ref = shift;
  local $delta = shift;
  local $res = '';
  local $latch = 0;
  foreach $str (@$ref)
  {
    if ( ! $latch )
    {
      $res = sprintf("0x%X", $str + $delta);
      $latch++;
    } else {
      $res .= ',' . sprintf("0x%X", $str + $delta);
    }
  }
  return $res;
}

sub process_tables
{
  local $first = shift;
  local $second = shift;
  local $delta = shift;
  local $size = shift;

  $delta = 0 if ( ! defined $delta );
  $size = $second - $first if ( ! defined $size );
  local %hash;
  local($i, $c, $hsize, $str, $ref);
  $hsize = 0;
  for ( $i = 0; $i < $size; $i++ )
  {
    $c = Byte($first + $i);
    if ( defined $hash{$c} )
    {
      $ref = $hash{$c};
      push @$ref, $i;
    } else {
      $hash{$c} = [ $i ];
      $hsize = $c if ( $c > $hsize );
    }
  }
  for ( $i = 0; $i <= $hsize; $i++ )
  {
    $str = form_string( $hash{$i}, $delta );
    next if ( $str eq '' );
    MakeComm($second + 4 * $i, $str);
  }
}

# main
process_tables 0x413720, 0x413755, 0x61;
Пример использования perl scriptа в IDA debuggerе. В сущности данный script не делает ничего особенно полезного...
#!/usr/bin/perl
# use stmt - указывать строго в таком порядке !
use IDA;
use IDADbg;

# on_brealpoint handler
sub cbBpt
{
  my( $t_id, $addr ) = @_;
  printf("t_id %X addr %X\n", $t_id, $addr);
  my $modules;
  my $str;
  if ( $addr == 0x10016a0 and modules_list($modules) )
  {
    # dump all loaded modules
    foreach $str ( @$modules )
    {
      printf(FILE "%s, base %X size %X rebase %X\n",
       $str->{'name'},
       $str->{'base'},
       $str->{'size'},
       $str->{'rebase'}
      );
    }
    my $context;
    if ( ThreadContext($t_id, CONTEXT_ALL, $context) )
    {
      my $reg;
      # dump all registers values
      foreach $reg ( keys %$context )
      {
        printf(FILE "%s .eq. %X\n", uc($reg), $context->{$reg} );
      }
      # set eax in some value
      set_Eax($t_id, 0x666);
    }
  }
  if ( $addr == 0x10016a0 )
  {
    # lets add new breakpoint from our script at 10016a6
    printf("try to add new bpt %d\n", AddBpt($addr + 6, 0, HWBPT_ANY) );
    printf("result of continue %d\n", ContinueProcess() );
  }
  if ( $addr == 0x10016a6 )
  {
    printf("My added breakpoint called !\n");
    # delete previous breakpoint
    DelBpt($addr - 6);
    # lets log value is EAX
    my $context;
    ThreadContext($t_id, CONTEXT_ALL, $context);
    printf(FILE "eax is %X\n", $context->{'Eax'});
    # and go on...
    ContinueProcess();
  }
}

sub cbProcessExit
{
  # be good boy and free all used resources here
  close FILE;
}

sub cbProcessStart
{
  open(FILE, ">test.log") or die("Cannot open test.log");
  # print all existing breakpoints
  my $c = BptQty();
  printf(FILE "BptQty %d\n", $c);
  local($i, $me);
  for ( $i = 0; $i < $c; $i++ )
  {
   last if ( ! GetBptN($i, $me) );
   printf(FILE "ea %X size %d type %d flags %X\n", 
     $me->{'ea'},    # addr of breakpoint
     $me->{'size'},  # size of breakpoint
     $me->{'type'},  # type of breakpoint
     $me->{'flags'}  # flags of breakpoint
    );
  }
  # print attrs of currently debugged module
  my $my_module;
  if ( dbg_module($my_module) )
  {
    printf(FILE "Me is %s, base %X size %X rebase %X\n",
     $my_module->{'name'},  # name of module
     $my_module->{'base'},  # base addr of module
     $my_module->{'size'},  # size of module
     $my_module->{'rebase'}
    );
  }
}

Описания исходных файлов

  • dbg_callz.cpp - реализация вызова perl ф-ций в качестве callbackов для событий отладчика
  • dbg_plugin.cpp - реализация pluginа perl for IDA debugger. Содержит реализацию функции dbg_plugin.cpp, которая устанавливается через hook_to_notification_point как обработчик событий от отладчика и собственно выполняет всю главную часть работы - вызывает функции из dbg_callz.cpp для нужных событий.
  • dstore.cpp & dstor.h - содержат реализацию внутреннего хранилища списка всех загруженных модулей. Дело в том что я не нашел в SDK способа получить такой список кроме как сохранять аргументы события dbg_library_load.
  • Embed_Perl.cpp - реализация встроенного perlа, также делает перехват стандартного perlового вывода в STDERR & STDIN для корректной их работы в IDA Pro
  • ida.cpp - автоматически сгенерированный horror.pl файл с реализацией вызовов IDC функций через таблицу IDCFuncs.
  • ida_wrap.c - сгенерированный SWIGом код для подключения функций из модуля IDA к perl
  • IDADbg_wrap.c - сгенерированный SWIGом код для подключения функций из модуля IDADbg к perl
  • init.cpp - version specific код для получения версии IDA Pro, а также массива IDC функций IDCFuncs
  • mydbgxs.cpp - некоторые методы из модуля IDADbg, реализованные в силу своей специфичности не через SWIG (и даже не через XS механизм), а непосредственно на C - например возвращающие hash или ссылку на массив hashей как modules_list
  • myimp.cpp - реализация некоторых методов непосредственно с помощью IDA SDK, а не через массив IDC функций IDCFuncs. Это сделано в основном для ускорения работы наиболее часто вызываемых функций, так как таким образом мы избавляемся от одного слоя marshallingа при конвертации аргументов для обращения к функциям IDC. Реализованы таким образом следующие функции: MakeByte, MakeWord, MakeDword, MakeQword, MakeOword, MakeFloat, MakeDouble, MakePackReal, MakeTbyte, MakeUnkn, OpBinary, OpOctal, OpDecimal, OpHex, OpChr, OpNumber, PatchByte, PatchWord, PatchDword, GetIdbPath, GetFlags, Byte, Word, Dword, Jump, RunPlugin, MakeComm, MakeRptCmt, GetInputFile, GetInputFilePath, ScreenEA, SetStatus, GetEntryPointQty, GetEntryPoint, GetStrucQty, loader_name, load_ids, save_idb, is_debugged, GetOriginalByte, GetOriginalWord, GetOriginalDword, MakeUnknRange, IsPublicName, MakeNamePublic, MakeNameNonPublic, HideName, ShowName, IdpName
  • rp_vc.cpp - реализация pluginа perl for IDA Pro
  • rp_vc.dsp & rp_vc.dsw - файлы проекта для Visual C++ 6 для сборки обоих pluginов
  • perl_dbg/perl_dbg.dsp - файл проекта для Visual C++ 6 для сборки perl_dbg plugin

Жалобы и предложения

а также умные и полезные советы, подробные и аргументированные описания найденных ошибок (если я сочту ошибку недостаточно подробно описанной - ответа даже не ждите), малоношенные теплые вещи и добровольные пожертвования можно отправлять автору по адресу redp@mail.ru. Автор не несёт ответственности за последствия работы его программ, а также за кривые руки (в 99% случаев это следствие неустранимой с помощью имеющейся биохакерской технологии баги в ДНК) собирающего все самостоятельно из прилагаемого исходного кода или неправильное толкование вышеописанного...
#include <std_disclaim.h>

2002-2013 (c) wasm.ru