Новости из Блогов Определение функций класса с использованием статического и динамического анализа

Discussion in 'Мировые новости. Обсуждения.' started by VY_CMa, 22 Dec 2012.

  1. VY_CMa

    VY_CMa Green member

    Joined:
    6 Jan 2012
    Messages:
    917
    Likes Received:
    492
    Reputations:
    724
    Определение функций класса с использованием статического и динамического анализа

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

    Статическим анализом легко можно определять конструкторы классов, или факт того, является ли функция членом класса или нет.
    Например, конструктор, как правило, инициализирует переменные класса, и в асм листинге все это выглядит достаточно узнаваемо:
    Code:
    3002E5A9 sub_3002E5A9    proc near                                  
    3002E5A9                 xor     eax, eax
    3002E5AB                 push    esi
    3002E5AC                 mov     esi, ecx
    3002E5AE                 mov     [esi], eax
    3002E5B0                 mov     [esi+4], eax
    3002E5B3                 mov     [esi+8], eax
    3002E5B6                 mov     [esi+0Ch], eax
    3002E5B9                 mov     [esi+10h], eax
    3002E5BC                 mov     [esi+14h], eax
    3002E5BF                 mov     [esi+18h], eax
    3002E5C2                 mov     [esi+1Ch], eax
    3002E5C5                 mov     [esi+20h], eax
    3002E5C8                 mov     [esi+24h], eax
    3002E5CB                 mov     [esi+28h], eax
    3002E5CE                 mov     [esi+2Ch], eax
    3002E5D1                 call    sub_300AC9DD
    3002E5D6                 mov     eax, esi
    3002E5D8                 pop     esi
    3002E5D9                 retn
    3002E5D9 sub_3002E5A9    endp
    
    Что касается принадлежности функции к классу, тут тоже все очевидно. Как гласит стандарт, для не статических функций-членов ключевое слово this является не явно передаваемым в ф-цию адресом того объекта, для которого вызывается функция.

    В асм листинке (для компиляторов от микрософт), указатель на объект класса всегда приходит в ф-цию не явно, через регистр ecx.

    Пример:
    Code:
    3002F131                 lea     ecx, [esi+34h]
    3002F134                 mov     [esi+2Ch], ebx
    3002F137                 mov     [esi+30h], ebx
    3002F13A                 call    sub_3002E5A9
    
    Таким образом, статическим анализом довольно просто ответить на два вышеупомянутых вопроса. А вот ответить на вопрос, является ли ф-ция func1 членом класса A, или членом класса B - уже сложнее. Но использовав динамический анализ, это становится довольно простым делом.

    Итак, используемые для статического анализа инструменты:
    1) IDA PRO
    2) Скрипты IdaPython ( генерация листинга ф-ций, принадлежащих тому или иному классу )
    Инструменты для динамического анализа:

    1) PIN ( http://software.intel.com/en-us/articles/pin-a-dynamic-binary-instrumentation-tool )
    2) Python скрипты для преобразования pin output logs в читабельный формат

    Основная мысль:

    * Имея на руках все ф-ции принадлежащие классам( все это определяется в статике ), прогоняем приложение в динамике(способов много, трассировка, эмуляция, динамическая рекомпиляция) и получаем значение ecx на входе в ф-цию.

    Алгоритм(кратко):

    1) В статике находим все функции, принадлежащие классам
    2) Их адреса записываем в input data file for PIN
    3) Пишем модуль для PIN, который на вход принимает файл с адресами ф-ций принадлежащих классам. А на выход дает файл содержащий пары: адрес ф-ции класса + значение ecx регистра.
    4) Прогоняем исследуемое приложение под PIN, получаем output file с результатами
    5) Парсим результаты, используя имена функций из базы IDA, в результате получаем: имя ф-ции + содержимое регистра ecx

    Алгоритм(чуть более подробно):

    Пожалуй единственное, что может вызвать затруднение, это нахождение всех ф-ций принадлежащих к классам.

    Алгоритм примерно такой:

    * перечисляем все ф-ции и исследуемом приложении
    * строим для каждой ф-ции базовые блоки
    * для каждого блока анализируем все инструкции
    * у инструкций анализируем операнды
    * вводим понятие состояний для регистров ( STATE_INIT, STATE_SAVE, STATE_RESTORE, STATE_MODIFICATE и так далее )
    * ведем списки состояний для базовых блоков

    Тогда для всех ф-ций, у второго операнда с типом reg и регистром ecx, нужно будет найти состояние STATE_SAVE. Причем в списке блока это состояние должно быть первым.

    Пример:

    Code:
    3002F110 sub_3002F110    proc near
    3002F110                 push    ebx
    3002F111                 push    esi
    3002F112                 push    [esp+8+arg_4]
    3002F116                 mov     esi, ecx        <== интересующая инструкция
    3002F118                 call    sub_301AC430
    Список состояний для регистра ecx в данном случае будет содержать первым состоянием STATE_SAVE. Это означает, что регистр сразу же начинает использоваться, без инициализации. То есть, вспоминая про this, ф-ция является членом класса, записываем её в список.

    * на выходе будем иметь список ф-ций принадлежащий классам исследуемого приложения.

    Что касается модуля PIN, то там все тривиально:

    1) устанавливается ф-ция IMG_AddInstrumentFunction, в колбеке которой указываем, за каким приложением/модулем нужно следить(нужно для оптимизации процесса).
    2) устанавливается ф-ция INS_AddInstrumentFunction, в колбеке которой указываем, какие аргументы нас интересуют(адрес инструкции и содержимое ecx):

    INS_InsertCall( ins, IPOINT_BEFORE, (AFUNPTR)InstructionHandler, IARG_INST_PTR, IARG_REG_VALUE, REG_ECX, IARG_END );

    Результаты всего этого бедлама и их анализ:

    Во-первых, несмотря на фильтрацию инструкций только от нужного exe/dll в PIN, результатов для большого приложения придется ждать довольно долго ( десятки минут ). Во-вторых, результаты придется анализировать.

    Выглядит все примерно следующим образом:
    Code:
    ...
    sub_30090C3F, ecx = 0x141af0a0
    sub_30069312, ecx = 0x12f460
    sub_30070AA7, ecx = 0x14192000
    sub_30070AA7, ecx = 0x14192000
    sub_3008FB93, ecx = 0x141af0a0
    sub_30084CA2, ecx = 0x141af0a0
    sub_3000D6B6, ecx = 0x189dc8
    sub_3000242F, ecx = 0x189de4
    ...
    
    Сразу заметно, что для ф-ций sub_30090C3F, sub_3008FB93, sub_30084CA2 значение ecx одинаково. Следовательно, они принадлежат к одному классу. Виртуальные ф-ции для такого способа также не являются проблемой. Конструкторы находятся также, они просто будут первыми в списке.
    Полученный список можно отсортировать по значению ecx, для более удобного анализа.

    Еще стоит упомянуть о том, что при выполнении исследуемой программы, вызываются не все ф-ции. Соответственно, не все ф-ции принадлежащие классам можно восстановить.

    Но полное покрытие это уже совсем другая задача, и вобще говоря, еще не решенная (см. информацию по symbolic execution).

    Суббота, 22 декабря 2012 г.
    автор: Tss
    http://kitrap08.blogspot.ru/2012/12/blog-post.html
     
    _________________________