Извлечение массива. Требуется помощь и советы.

Тема в разделе "WASM.BEGINNERS", создана пользователем uinor, 14 сен 2009.

  1. uinor

    uinor New Member

    Публикаций:
    0
    Регистрация:
    14 сен 2009
    Сообщения:
    14
    Прошу проконсультировать меня, т.к. немного путаюсь.

    Вводное
    --
    Есть приложение (win32 / native / c++), знаю что где-то расположен массив байт (myArray), знаю, что он передается в качестве одного из параметров в функцию из динамической библиотеки (myFunc, присутствует в таблице импорта приложения).

    Задача
    --
    Получить массив целиком для своих нужд.

    Дополнительные условия
    --
    * Обойтись минимальными жертвами (без сторонних дизасмов и прочего)
    * Высокая скорость работы
    * Никаких сигнатур не известно, иначе использовал бы binary search

    Подробнее
    --
    Так выглядит массив в исходном коде (исключительно для примера):
    Код (Text):
    1. static const unsigned char myArray[] = {
    2.   0x0,0x0,0x0,0x6,0x76,0x73,0x6d,0x61,0x72,0x6b,  
    3. };
    Так выглядит вызов функции:
    Код (Text):
    1.   00404228                           SUB_L00404228:
    2.  00404228  55                               push    ebp
    3.  00404229  89E5                             mov ebp,esp
    4.  0040422B  83EC18                           sub esp,00000018h
    5.  0040422E  C744240C80464100                 mov dword ptr [esp+0Ch],L00414680
    6.  00404236  C7442408A0464100                 mov dword ptr [esp+08h],L004146A0
    7.  0040423E  C7442404E0464100                 mov dword ptr [esp+04h],L004146E0
    8.  00404246  C7042401000000                   mov dword ptr [esp],00000001h
    9.  0040424D  E8060A0000                       call    myFunc
    10.  00404252  B801000000                       mov eax,00000001h
    11.  00404257  C9                               leave
    12.  00404258  C3                               retn
    Где, адрес 00414680h указывает на расположение массива (идет последним параметром).

    Мои размышления
    1. Берем таблицу импорта, находим адрес myFunc
    2. Находим reference на него, попадаем в импорт(?) (jmp xxx)
    3. Находим reference на импорт, попадаем в подпрограмму, приведенную выше
    4. Получаем адрес массива, поднимаясь выше (не знаю как грамотно организовать)
    5. Переходим по адресу

    Я не так хорошо знаком с PE форматом, возможно это делается проще. У меня дикая путаница в голове относительно RVA & etc, буду курить материал, очень буду рад, если кто-то даст алгоритм максимально подробно. Как получить размер массива? Как грамотнее подняться до адреса массива в подпрограмме (можно дико захардкодить, но хотелось бы гибкости, ситуации могут быть разные)?
     
  2. uinor

    uinor New Member

    Публикаций:
    0
    Регистрация:
    14 сен 2009
    Сообщения:
    14
    Забыл добавить в условия:
    * Речь идет не об одном анализируемом приложении, ситуация может меняться (адрес myFunc, положение массива, его размер и прочее). Во всех случаях не меняется лишь общая схема работы.
     
  3. Velheart

    Velheart New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2008
    Сообщения:
    526
    Нужно динамически при запуске приложения? В какой момент, перед первым вызовом myFunc, или уже после? или может нужно в статике просто реверсить пачку похожих приложений? не совсем из поста как-то понятно)
     
  4. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    uinor
    как вы это без "дизасмов" собрались делать?
     
  5. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    ага, если можно динамически то используйте хуки через таблицу импорта
     
  6. vsnaum

    vsnaum New Member

    Публикаций:
    0
    Регистрация:
    12 мар 2009
    Сообщения:
    22
    @All: Прощу прощение, нужно все-таки статически, забыл указать.

    Asterix: Относительно дизассемблера - были мысли, что мой подход слишком наркоманский и существует более простое решение.
     
  7. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    vsnaum
    Код (Text):
    1. 0040424D  E8060A0000
    Что-то никак это не похоже на вызов импортируемой функции.
     
  8. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    vsnaum
    Извиняюсь... сонный... прогнал... там же может быть вызов на косвенный абсолютный прыжок. В общем, если совсем не морочить себе голову, то можно простым поиском по маске по кодовой секции. Из маски нужно выкинуть четыре dword'а: который после e8 в вызове функции и последние четыре байта в каждом из трёх запихиваний аргументов в стек. При каждом совпадении по маске проверять dword, который после e8. Если ведёт к импортируемой искомой функции, то это наш вызов. 99,99% - ложного совпадения не будет.
     
  9. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Опишите подробнее. Что постоянно ?
     
  10. vsnaum

    vsnaum New Member

    Публикаций:
    0
    Регистрация:
    12 мар 2009
    Сообщения:
    22
    Есть библиотека myLibrary.dll, которая экспортирует функцию myFunc с набором из трех параметров. Есть приложения (myApp1, myApp2, ..., myAppN), которые реализуют определенный функционал (может быть скомпилированно gcc/ms) и которые в большинстве случаев импортирует функцию myFunc из myLibrary (только статик импорт рассматриваем).

    В большинстве случаев автоматически кодогенерируется подпрограмма, которая оборачивает вызов myFunc (см. в коде из первого поста), иногда вызов осуществляется уже программистом.

    Размер массива лежит в дворде по указателю, это уже облегчение. Остается найти вызов myFunc в коде myAppM, получить адрес массива, который передается в myFunc в качестве третьего параметра, преобразовать его к file offset'у, нырнуть туда, считать размер, вытащить массив.

    Боюсь, что сигнатурный поиск провален. Небольшие эксперименты показали, что в приложениях myApp5 и myApp10 :) по сигнатуре уже найти проблематично. У кого-то stack frame иначе оформлен и т.д.
     
  11. vsnaum

    vsnaum New Member

    Публикаций:
    0
    Регистрация:
    12 мар 2009
    Сообщения:
    22
    Код (Text):
    1.  004EE800                           SUB_L004EE800:
    2.  004EE800  55                               push    ebp
    3.  004EE801  8BEC                             mov ebp,esp
    4.  004EE803  68D8956000                       push    L006095D8
    5.  004EE808  68683A7100                       push    L00713A68
    6.  004EE80D  68185E7100                       push    L00715E18
    7.  004EE812  6A01                             push    00000001h
    8.  004EE814  E8C1450000                       call    myFunc
    9.  004EE819  83C410                           add esp,00000010h
    10.  004EE81C  B801000000                       mov eax,00000001h
    11.  004EE821  5D                               pop ebp
    12.  004EE822  C3                               retn
    Пример иной генерации обертки над вызовом myFunc. Можно конечно для поиска по сигнатурам использовать различные схемы, учитывая все возможности, но это сомнительно. Все-таки рассчитываю на совет + подсказку как лучше и проще адрес массива перевести в файловый оффсет.
     
  12. vsnaum

    vsnaum New Member

    Публикаций:
    0
    Регистрация:
    12 мар 2009
    Сообщения:
    22
    Конечно, идеальным вариантом был бы именно анализ - получение адреса переходника, поиск всех ref'ов, анализ передаваемых параметров, вычленение нужного и последующий rip, но это требует слишком больших усилий и погружения в знания и техники, которые далеки от меня в силу специализации. Хотя возможно и в этом направлении существуют какие-то наработки у народа.

    Еще есть вариант гибридизации - использование динамических сигнатур для binary search. Т.е. определяем адрес переходника, за счет чего расширяем сигнатуру с E8* до E8{%address%}.

    До сих пор не до конца прочувствовал как получить адрес переходника в импорте. Из таблицы импорта я могу вытащить RVA, но ведь это не адрес переходника, который используется в конструкции call?
     
  13. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    vsnaum
    В таком случае статика бесполезна. Единственный возможный вариант - парсинг релоков(в таблице базовых поправок будет запись об подобной ссылке на массив), что используется для поиска подобных проблемных ссылок. Необходима информация об искомом массиве(назначение, размер, ссылки, содержание его и пр.).
     
  14. vsnaum

    vsnaum New Member

    Публикаций:
    0
    Регистрация:
    12 мар 2009
    Сообщения:
    22
    Искомый массив является результатом автоматической кодогенерации, контент любой (по сути, некий контейнер, контент которого контролируем со стороны стороннего разработчика). Назначение - используется в процессе работы ПО в качестве контейнера с уже готовой (кэшированной, по сути) информацией. Этот контейнер регистрируется в качестве источника данных как раз с помощью myFunc из myLibrary. Лежит в секции .data, понятное дело.

    Содержание не предсказуемо, но как показал эмпирический анализ - состоит из chunk'ов, предваренных размером (dword), так что по адресу массива лежит ключик к размеру (инкрементируем, доходим до конца, тут без проблем). Что делать с экстрактом данных я справлюсь, тут помощь не понадобится.

    Если я правильно соображаю, то таблица релоков отсутствует (проверено PE Explorer'ом).

    Ссылки непосредственного на массив отсутствуют. Массив используется в myLibrary, сама ПО только регистрирует его там.

    Я понимаю, насколько забавно это все звучит, это существующая ситуация.
     
  15. Velheart

    Velheart New Member

    Публикаций:
    0
    Регистрация:
    2 июн 2008
    Сообщения:
    526
    Если нужно только один раз вытащить инфу -- то проще написать к иде скрипт, он должен простым получиться, причем если никогда не писал для иды скриптов, будет проще посмотреть и научиться, чем эту же задачу решать просто кодом =)
     
  16. vsnaum

    vsnaum New Member

    Публикаций:
    0
    Регистрация:
    12 мар 2009
    Сообщения:
    22
    Clerk, благодарю за адекват.

    Но почему статика исключена однозначно? Самый дикий вариант, который можно попробовать - получение адреса переходника импорта и поиск по маске *E8{%address%}*. Велика вероятность фальш-срабатываний, но их можно постараться отсеять примитивным анализом окружения (допустим кол-во операндов до начала подпрограммы и до конца). На крайний случай расширение маски за счет формирования библиотеки масок поиска под разные ситуации (но это наркомания уже однозначно, даже руки марать не хочется).

    Жутко не хочется делать комбайн, но это тоже вариант - укрепить аналитику за счет встроенного небольшого дизасма (hde, допустим), но страшно не хочется в это влазить.
     
  17. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    для одного раза можно и вручную все сделать, без скриптов ;)
     
  18. vsnaum

    vsnaum New Member

    Публикаций:
    0
    Регистрация:
    12 мар 2009
    Сообщения:
    22
    Velheart, инфу нужно вытаскивать постоянно, более того результатом будет инструмент, позволяющий это делать просто и удобно. Если бы задача стояла единоразово вытащить массив, я бы не обращался к профессионалам, т.к. решается более чем просто (дизасмом, дебагом, чем угодно. в первый раз я вытащил его вообще с помощью PEExplorer'овского дизасма и WinHEX'a, собственно результаты изучения и присутствуют в первом посте).

    Решить задачу кодом не проблема, главное осознать схему и принцип. Даже язык реализации не важен.
     
  19. vsnaum

    vsnaum New Member

    Публикаций:
    0
    Регистрация:
    12 мар 2009
    Сообщения:
    22
    Asterix, полностью согласен. Господа, я бы реально не стал поднимать ветку из-за такой мелочи. Просто у меня специализация анализ "инородных" форматов (для которых нет спецификаций), но я немного далек от понимания устройства PE и различных схем / тактик / метод и прочего (по RE в том числе).
     
  20. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    E8 - это не абсолютный call, а относительный, т.е. операнд = знаковому смещению переходника jmp myFunc относительно адреса следующей команды. Поэтому алгоритм поиска call myFunc д.б.примерно таким
    1) Изучаем\повторяем формат PE и находим в IAT поле, соответствующее адресу myFunc
    2) В секции кода ищем переходник jmp myFunc по сигнатуре FF 25 {адрес_поля} и запоминаем его адрес. (Переходников в принципе м.б. несколько, если импорт myFunc объявлялся в разных модулях)
    3) В секции кода тупо перебираем все байты E8 и проверяем не равен ли следующий за ним dword значению (адрес_переходника - адрес_E8 - 5). Если равен, значит нашли вызов myFunc