+ Reply to Thread
Results 1 to 1 of 1

Thread: Приемы анализа malware: Распаковка драйверов в Ring3

  1. #1
    root's Avatar

    Default Приемы анализа malware: Распаковка драйверов в Ring3

    Источник: habrahabr.ru


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


    Вступление

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


    Подготовка драйвера

    Сам драйвер, по своей структуре почти идентичен обычным динамическим библиотекам. И чтоб загрузить его в ring3, нужно изменить пару полей в PE-заголовке, а именно:
    • Тип Subsystem с Native на Windows GUI. (В PeTools кнопка Optional Header).
    • В поле IMAGE_FILE_HEADER.Characteristics выставить атрибут Dll. (В PeTools кнопка File Header, а затем Characteristics).
    Теперь нужно отвязать драйвер от системных низкоуровневых библиотек. Для этого напишем свою dll (или несколько, кому как угодно) с заглушками необходимых функций. Открываем список импортируемых API и ищем их прототипы в MSDN или в других информационных ресурсах.

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


    Рис. 1. Список API до патча

    Чтобы драйвер мог подгрузить именно нашу dll, а не системную, пропатчим имена импортируемых модулей, на подготовленные нами.


    Рис. 2. Список API после патча

    Теперь наш драйвер можно загрузить в отладчик пользовательского режима, например OllyDbg.


    Распаковка и решение проблем по мере выполнения

    В данном случае пакер примитивный — xor и разновидность алгоритма LZ. Рассматривать xor-декриптор я не буду, не смотря на то, что он немного замусорен. После дешифровки попадаем на следующий код:


    Рис. 3. Код после xor-декриптора

    Вот и первая неприятность. Если просто отпустить код выполняться, то сразу словим exception ACCESS_VIOLATION. Код берет из служебных структур адрес, находящийся внутри ntoskrnl.exe и находит ImageBase модуля. Но так как мы находимся в ring3, то структура, находящаяся в сегменте FS отличается от ядерной. И если потрассировать код, то из fs:[38] считается 0, а на следующей команде будет чтение по адресу 0+4. Естественно, никакого ntoskrnl у нас в памяти тоже нет, поэтому предположим, что обойдемся адресом ntdll (большая часть её API совпадает с ядерными функциями).

    Открываем карту памяти и смотрим что находится в сегменте FS. Должны увидеть TIB — Thread Information Block. Немного посмотрев, можно увидеть в ней и указатель на PEB — Process Environment Block. Выбираем любой подходящий адрес в ntdll (я выбрал PEB.FastPebLock).


    Рис. 4. PEB и TIB

    Можно просто заNOPить код, и по ходу трейса подменить адрес на ntdll. Но мы поступим по другому — изменим смещения.


    Рис. 5

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


    Рис. 6

    При этом EDX указывает на список имен необходимых функций


    Рис. 7

    Внимательные заметят чуть ниже пожатый MZ-заголовок, но об этом позже.
    Вроде ничего необычного. Все было бы хорошо, да только в ntdll нет некоторых необходимых API, или хотя бы похожих по прототипу. Но если немного подумать, то найдутся таковые в kernel32.dll.

    Code:
    PVOID ExAllocatePool(POOL_TYPE PoolType, SIZE_T NumberOfBytes);
    VOID ExFreePool(PVOID P);
    Аккуратно можно заменить на

    Code:
    HGLOBAL WINAPI GlobalAlloc(UINT uFlags, SIZE_T dwBytes);
    HGLOBAL WINAPI GlobalFree(HGLOBAL hMem);
    Меняем имена несуществующих функций, на любые имеющиеся, чтобы просто корректно отработал xGetProcAddress. Я их заменил на NtClose.


    Рис. 8

    Аккуратно трассируя, подменяем в регистрах адрес NtClose, на необходимые нам адреса из kernel32.dll. После отработки цикла, все необходимые адреса получены, как видно на изображении ниже. С этой проблемой покончено, следуем дальше.


    Рис. 9

    Убеждаемся, что подмененная нами ExAllocatePool на GlobalAlloc стабильно отрабатывает.


    Рис. 10

    Незаметно подошли к распаковке.


    Рис. 11

    Вероятно код писался на assembler’е, так как нет ничего лишнего. Код на скрине делает выделение памяти, распаковку в нее, потом делает подготовку образа, зануляет и чистит память, и в случае успеха прыгает на OEP.

    Алгоритм распаковки я не стал изучать, потому что на вид пожатые данные мне показались похожи на вариант LZW.


    Рис. 12

    Распаковщик не использует никаких API, поэтому прогоняется быстро и без проблем. Собственно, сразу после этого можно делать дамп региона с чистым драйвером. Но мне было интересно, на сколько можно будет продвинуться в анализе, находясь в ring3.

    Функция PrepareImage подготавливает распакованный образ: делает ремап секций по необходимым смещениям, получает адреса API из импорта, производит пересчет адресов по таблице relocations.

    Очередные палки в колеса нам сует цикл поиска функций для IAT, который не только запрашивает модули, которых у нас нет (ntoskrnl, hal и др), но и соответственно функции.


    Рис. 13

    Как видно я уже попался и вошел в цикл, но поменяв EIP на 0x008982d7, уменьшив ESP и установив в EAX = 0, более-менее корректно вышел из него. Правка reloc’ов не приносит нам каких-либо неприятностей, и мы наконец выходим на OEP. Но на этом придется остановиться, так как адреса импорта не восстановлены, а писать очередную dll с заглушками я не вижу смысла. Чистый код можно уже проанализировать статически в дизассемблере.


    Вместо вывода

    До:


    Рис. 14

    …и после:


    Рис. 15

    Чтобы не мучать Вас вбиванием строк с нижнего скриншота в поиск, скажу сразу, что это одна из версий Rustock’а

    В очередной раз убеждаюсь, что моя лень заставляет извращаться еще дольше, чем это можно было бы сделать решением «в лоб»
    Успех – это путь от провала до провала без потери энтузиазма. (В. Черчиль)

    Не бойся идти медленно, бойся остановиться. (Китайская пословица)

    When you lose fun and start doing things only for the payback, you're dead. (c) TCLH (Phrack 65, Intro)

  2. 2 пользователя(ей) сказали cпасибо:
    Mosc (21-05-2014) ximera (24-05-2014)
+ Reply to Thread

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
All times are GMT. The time now is 01:30
vBulletin® Copyright ©2000 - 2018
www.reverse4you.org