R0 CREW

Malware Analysis Tutorial 31: Exposing Hidden Control Flow (Перевод: coldfire)

Перейти к содержанию

Цели урока:

  1. Попрактиковаться в использовании WinDbg для анализа структуры данных ядра.
  2. Научиться использовать пакетный сниффер для мониторинга сетевой активности вредоносной программы.
  3. Изучить частоту использования сети вредоносной программы.
  4. Найти скрытый\недоступный поток управления вредоносной программы.

1. Введение

В этом урок мы изучим сетевую активность выполняемую max++.00.x86, когда не удается загрузить 147.47.xx.xx\max++.x86.dll. Мы покажем как использовать сетевой сниффер для помощи в анализе. Мы покажем как использовать отладчик для раскрытия и анализа скрытого\недоступного потока управления вредоносной программы.

2. Настройки

Мы предполагаем, что вы закончили урок 30 и max++.00.x86 уже находится в системе. Теперь установим точку останова на адрес 0x35671797 ( это место, где вредоносная программа пытается модифицировать структуру данных ядра о пути библиотеки max++. Позже мы вызовем Ole32.CoInitialize для удаленное загрузки). Теперь на сервере Ubuntu, запустим пакетный сниффер и будем прослушивать локальную сеть ( используй ifconfig для того, что бы найти адаптер для прослушки).

Теперь нажимайте F9 пока вы не окажетесь на адресе 0x35671797. В этот момент, в окне Wireshark, еще ни одного пакета не было перехвачено. Выполняйте программу пошагово пока вы не достигните адреса 0x35671D8B. Он находится прямо перед вызовом ole32.CoInitialize.

Рис. 1. Код, пытающийся загрузить удаленную DLL

3. Анализ данных в Wireshark

Приступим к интересной части урока, сделаем один шаг в WibDbg и увидим,что вызвалась функция ole32.CoInitialize. Далее мы видим, что происходит коммуникация между 169.253.236.201 ( экземпляр WinDbg) и 74.117.114.86. С рис. 2, мы можем сделать вывод, что используется специальный HTTP метод PROPFIND для получения max.x86.dll(отметим, что PROPFIND — метод, предоставляемый протокол WebDav, который является расширение HTTP).

Рис. 2. Трасировка Ole32.CoInitialize

Тем не менее, интересно, что если мы нажмем F9, находясь по адресу 0x35671797, мы получим показанное на рис. 3. Обратите внимание на разницу.

Рис. 3. Слегка другая сетевая активность

Очевидно, что вредоносная программа пытается выполнить /install/setup.ppc.php по адресу 108.61.4.52! Вопрос: кто отсылает запрос и почему мы не получаем его когда делаем пошаговое выполнение?

Задание 1. Найдите способ отследить отправителя пакета на адрес 108.61.4.52.

4. Запуск вредоносной программы без удаленной DLL

Мы заинтересованы в изучении оставшейся логики вредоносной программы и хотим узнать, как будет себя вести Max++.00.x86, если 74.117.114.86/max++.x86.dll будет загружена. Для этого нам надо немного настроить поток управления для наблюдения поведения. Мы должны сделать следующее:

(1) поставить точку останова на адрес 0x35671D8Dи запустить. Смотрим рис. 4. Он идет сразу после вызова ole32.CoInitialize(), который пытается сделать удаленную загрузку 74.117.114.86/max++.x86.dll. Тем не менее, файл больше не доступен и вызов потерпит неудачу и остановит весть процесс. Мы должны пропустить этот вызов для того, что бы продолжить изучение остатка логики работы вредоносной программы.

Рис. 4. Точка останова для отвода управления потоком когда загрузка удаленной DLL не будет выполнена

(2) Нажмите на 2ю кнопку на панели инструментов (окно Python) и наберите следующее imm.setReg(“EIP”, 0x35671D93). Это нужно для того, что бы пропустить вызов ole32.coInitialize и перехода на следующую инструкцию.

(3) Теперь в окне регистров, установите значение EAX равным 0. ( для того, что бы показать, что вызов произошел успешно).

После того как поток управления изменен успешно, max++.00.x86 прыгнет к функции 0x35674737, тело которой показано на рис. 5.

Рис. 5. Функция 0x35674737 — выделение памяти в куче

Далее вредоносная программа вызывает функцию 0x35671E37[отметим, что в этот момент 0x00182130 — начальный адрес в выделенной памяти в куче]. Как показано на рис. 6, она конструирует структуру данных по адресу 0x00182130(размер: 0x24 байта).

Задание 2. Используйте точки останова для нахождения типа конструируемой структуры данных функцией 0x35671E37.

Рис. 6. Функция 0x35671E37 заполняет структуру данных

Управление затем перейдет к 0x35671C4A. Внутри этой функции находятся несколько интересных вызовов, показанных на рис. 7. Она создает порт и начинает его прослушивать. Для изучение логики, мы аккуратно управляем выполнением функции 0x35671E61(потому что она выполняет функции из удаленной max++.x86.dll, которая не загрузится, если не будет сети).

Рис. 7. Тело функции 0x35671C4A

Теперь давайте рассмотрим функцию 0x35671E61, на рис. 8 видим ее первую часть. Она взывает ole32.CLSIDFromProgID(“JavaScript”). Функция находим содержимое реестра на основе информации о ID программы. Но этот вызов запускает удаленную DLL. Мы должны рассмотреть это подробней.

Рис. 8. Вызов, который запускает удаленную max++,x86.dll

Трассируя функцию old32.CLSIDFromProgID(“JavaScript”), мы можем отметить, что во время вызова ole32.CoGetComCatalog, она производит загрузку 74.117.114.86/max++.x86.dll. Как показано на рис. 9. Кажется, что CoGetComCatalog посещает загруженный модуль еще раз ( и читает обработанную информацию текущего модуля и таким образом пытается загрузить удаленный модуль. Это происходит аналогично вызову CoInitialize, как было рассмотрено в уроке 30).

Рис. 9. CLSIDFromProgID вызывает CoGetComCatalog

Так как имя удаленного модуля вызывает проблемы, мы могли бы попробовать восстановить его обратно. С урока 30, мы знаем, что имя\путь модуля располагается по адресу 0x002529c0. Мы можем изменить его на оригинальное имя “\.\C2CAD972#4079#4fd3#A68D#AD34CC121074\L\max++.00.x86”. ( Правый клик на адресе 002529c0 в окошке дампа памяти, выбираем Binary Edit, и затем вводим строку в строке UNICODE).

Рис. 10. Изменения имени модуля

Теперь давайте посмотрим на второй параметр функции CLSIDFromProgID на рис. 8. Посредством простого анализа мы можем сказать, что второй параметр располагается по адресу 0x009FFF48, как показано на рис. 11.

Рис. 11. Успешное завершение CLSIDFromProgID

Как показано на рис. 11, адрес 0x009FFF48 сохраняет ID класса. Обратите внимание на порядок байтов (вы должны получить первые 4 байта в обратном порядке). Например, для первых 4 байтов (60 C2 14 F4), мы получим 0xf414c260. Запустив поиск f414c260 в regedit, мы найдем CLSID {f414c260-6ac0-11cf…}, как показано на рис. 11. Вы можете проверить, что она совпадает с выделенной областью в панели дампа памяти в IMM. Прочитав больше деталей про CLSID {f414c260-6ac0-11cf…}, мы можем найти, что CLSID — отображается на jscript.dll в системном каталоге, как и ожидали (то есть, CLSIDFromProgID работает корректно, учитывая, что неверная ссылка на удаленную библиотеку не портит вызов CoGetComCatalog на рис. 10).

Тем не менее, отметим, что есть возможность, что когда удаленная библиотека загружена, она перепишет содержимое реестра, таким образом, что когда будет использоваться объект JSScript, она будет ссылать на функции с удаленной библиотеки. Так как у нас нету исполняемого файла 74.117.14.86/max++.x86.dll, мы больше ничего не можем сказать.

4.1 Анализ оставшейся логики функции 0x35671E61

Продолжим анализ с вызова функции CLSIDFromProgID. Снова, отметим, что CLSID хранится по адерсу 0x009FFF48.

Рис. 12 показывает последний кусок не исследуемой нами логики функции 0x35671E61. Главная часть этой функции это вызов функции CoCreateInstance, которая создает уникальный экземпляр СОМ объекта Jscript. Отметим, что второй параметр rrid — идентификатор интерфейса, который используется для взаимодействия с Jscript. Тем не менее, если совместная инициализация функции не закончится успешно, то CoCreateInstance() возвратит код ошибки 0x800410F0 ( это означает, что СОМ интерфейс не инициализирован корректно). В таком случаи, мы должны модифицировать значение регистра ЕАХ на 0x35671E90, что бы продолжить работу функции.

На рис. 12 можно видеть 3 вызова функции, которые относятся к СОМ объекту Jscript. Тем не менее, т. к. нам не удалась корректная совместная инициализация, у нас нету другого пути для того, что бы более детально узнать про эти 3 функции. Далее идет возврат с функции 0x35671E61.

Рис. 12. Взаимодействие с СОМ объектом

4.2 Функция 0x3567162D

Используя подобную технику, мы можем добиться соблюдения логики в функции 0x3567162D. На рис. 13 показано тело функции. Как показано на рис. 13, Мах++ читает начиная с ??\C2CAD…6cc2 и выделяет 15 байт по адресу 0x003E0000 и извлекает содержимое(контент) с файла в 0x003E0000.

Рис. 13. Загрузка новой вредоносной логики

Остаток функции 0x3567162D показан на рис. 14. Она применяет 2х уровнивое дешифрование для извлечения содержимого(контента) в 0x003E0000. Как показано на рис. 14, содержимое по адресу 0x003E0000 выглядит как XML спецификация. В этот момент, мы не знаем значение тэга “”. Но если вы посмотрите содержимое, то увидите, что оно выглядит как URL для загрузки с intensivedive.com и остаток похож на заголовок НТТР запроса.

Рис. 14. Извлечение зашифрованного контента

Задание 2. Проанализируйте логику функции 0x35671ECF. Отметим, что вы должны тщательно изучить логику, когда функция пытается выполнить функции с удаленной DLL.

4.3 Функция 0x356713AC

В конце функции 0x3567162D идет вызов функции 0x356713AC, который показан ниже. Эта функция сильно похожа на 0x3567162D. Она читает с другого скрытого файла, разрешает IP intensivedive.com и конструирует запрос полезной нагрузки(пейлоада).

Рис. 15. Функция 0x356713AC

В конце функции 0x356713AC, она вызывает 0x356712D8, чье тело функции показано ниже. Она подготавливает необходимые ресурсы(UUIDs), для общения с сокетом, в 0x35674237, которая вызывает функция 0x3567417c.

Рис. 16. Функция 0x356712D8 (первая часть)

Тело функции 0x3567417C показано на рис. 17. Отметим, что первый вызов функции ws32_socket завершится неудачей. Наиболее интересная часть (подсвеченная на рис.) - это вызов функции BindIoCompletionCallBack. Она устанавливает 0x356740D4 как обработчик на любой IoCompletion на обработку взаимодействия по сети. Давайте поставим точку останова и посмотрим произойдет ли ее вызов. Это точка останова, под текущими настройками никогда не будет достигнута, потому что вызов WSASocket заканчивается неудачей. Тем не менее, анализ этого бинарного кода возможен. Мы оставляем это как домашнее задание.

Рис. 17. Тело функции 0x3567417C

Задание 3. Проанализируйте функцию 0x356740D4

Остаток функции 0x356712D8 отвечает за отправку пакетов ( главным образом на intensivedive.com/install.ppc) и там возникает слишком много ошибок, таких как не удачная инициализация WSASocketW. Давайте вернемся к 0x35671C6F и посмотрим логику ее работы.

Рис. 18. Открытие порта

Как показано на рис. 18, очевидно, что Мах++ открывает порт ( используя zwCreatePort ), а далее идет большой цикл и на каждой итерации вызывается ZwReplyWaitReceivePortEx для прослушивания порта.

Задание 4. Найдите номер порта, который использует Мах++. Отметьте, что когда стек TCP/IP начинает контролироваться Мах++'ом, команда netstat не дает никакой интересной информации!

В следующем уроке, мы настроим поток управления Мах++ таким образом, что бы попасть при каждом вызове в функцию zwReplyWaitReceivePortEx и проверим используется ли Мах++ как бот-нет клиента.

© Translated by coldfire from r0 Crew

UP