Этот блог продолжает серию скриптов FLARE, в которой обсуждается патчинг файлов базы данных IDA Pro (IDB) для интерактивной эмуляции кода. Хотя самым быстрым способом анализа или распаковки вредоносного программного обеспечения часто является его запуск, оно не всегда успешно выполняется на виртуальной машине. Я использую (https://www.hex-rays.com/wp-content/uploads/2019/12/debugging_bochs.pdf) интеграцию Bochs IDA Pro в режиме IDB, чтобы обойти утомительные сценарии отладки и получить быстрые результаты. Bochs эмулирует коды операций непосредственно из вашей IDB в виртуальной машине Bochs без ОС.
Режим Bochs IDB устраняет отвлекающие факторы, такие как переключение виртуальных машин, настройка отладчика, нейтрализация анти-аналитических мер и перемещение счетчика программы к интересующей логике программы. Увы, там, где нет ОС, не может быть загрузчика или динамического импорта. Исполнение ограничено кодами операций, найденными в IDB. Это исключает эмуляцию подпрограмм, которые вызывают импортированные строковые функции или распределители памяти. Эмулятор Тома Беннетта поставляется с эмулированными версиями этих версий, но для анализа "off-the-cuff" (особенно, когда я не знаю, будет ли это оправдано), я предпочитаю интерактивно исследовать регистры и память, чтобы скорректировать свою тактику ad hoc.
Что если бы я мог принести свои импортированные функции в Bochs, как это делает эмулятор flare-emu? Я разработал такую технику, и я называю её трансплантацией кода. В этом посте я расскажу об особенностях статического связывания для общих функций в IDB, чтобы получить больше выгоды от эмулятора Bochs. Я продемонстрирую использование этого на примере EVILNEST для распаковки и дампа полезных нагрузок следующего этапа(next-stage) из эмулируемой памяти. Я также покажу, как я скопировал сложную последовательность вызовов из одной IDB в другую IDB, чтобы я мог сохранить процесс распаковки всего в одном сеансе отладки Bochs.
Сценарий EVILNEST
Мой сэмпл (MD5-хэш 37F7F1F691D42DCAD6AE740E6D9CAB63, который доступен через VirusTotal) был вариантом EVILNEST, который заполняет стек данными конфигурации перед вызовом промежуточной полезной нагрузки. На рисунке #1 показан этот необычный вызов.
Код на рисунке #1 выполняется в удаленном потоке внутри захваченного процесса iexplore.exe; вредоносная программа также использует анти-аналитическую тактику. У меня была промежуточная стадия полезной нагрузки, и я хотел распаковать полезную нагрузку следующего этара, не управляя сценарием отладки с несколькими процессами с анти-анализом. Я знал, что мог бы заглушить несколько вызовов функций в вредоносной программе, чтобы запустить всю соответствующую логику в Bochs.
Вот как я это сделал.
Вырезание кода
Мне нужны были опкоды для нескольких общих функций, чтобы инжектировать их в мои IDB и эмулировать в Bochs. Я построил простые реализации C выбранных функций и скомпилировал их в один двоичный файл. На рисунке # 2 показаны некоторые из этих подмен.
Я скомпилировал их и затем использовал код IDAPython, похожий на рисунок #3, чтобы извлечь байты кода операции функции.
Я курировал библиотеку опкодов функций в скрипте IDAPython, как показано на рисунке #4. Опкоды нестандартной функции в нижней части рисунка были ассемблированы вручную настолько кратко, насколько это было возможно, чтобы в общем случае возвращать конкретные значения и манипулировать стеком (или нет) в соответствии с соглашениями о вызовах.
Помимо простых функций, таких как memcpy, я реализовал аллокатор памяти. Аллокатор ссылался на глобальные данные о состоянии, то есть я не мог просто инжектировать их в IDB и ожидать, что они будут работать. Я прочитал дизассемблировванный спиок, чтобы найти ссылки на глобальные операнды и шаблонизировать их для использования с методом формата Python. На рисунке 5 показан пример для функции malloc.
Я организовал заглушки по имени, как показано на рисунке #6, чтобы вызывать функции, которые мне нужно будет пропатчить, и удобно добавлять больше заглушек функций по мере их использования. Искаженное имя, которое я указал как алиас free является оператором удаления.
Чтобы инжектировать эти функции в двоичный файл, я написал код, чтобы найти следующий доступный сегмент заданного размера. Я избегал занимать нижнюю память, потому что Bochs помещает свой сегмент загрузчика ниже 0x10000. Рядом с кодом в моем сегменте кода я включил пространство для данных, используемых моим распределителем памяти. На рисунке #7 показан результат патчинга этих функций и данных в IDB и присвоения имени каждому местоположению (функции-заглушки имеют префикс stub_).
Затем скрипт повторяет все соответствующие вызовы в двоичном файле и патчит их вызовами в своих реализациях заглушки во вновь добавленном сегменте. Как показано на рисунке #8, функция Assemble IDAPython сэкономила усилия по вычислению смещения для операнда вызова вручную. Обратите внимание, что функция Assemble здесь работает хорошо, но для более крупных задач Hex-Rays рекомендует выделенный ассемблер, такой как Keystone Engine и его плагин Keypatch для IDA Pro.
Скрипт Code Grafting обновил все соответствующие вызовs, чтобы они напоминали рисунок #9, с заменой целевых функций вызовами реализаций stub_, введенных ранее. Это предотвратило сбой Bochs в режиме IDB при попадании на эти вызовы, поскольку операнды вызовов теперь указывали на действительный код внутри IDB.
Работа с EVILNEST
Сценарий отладки для дроппера был немного неудобным, и одновременно он настраивал очень необычный вызов для точки входа полезной нагрузки. Я использовал Bochs для выполнения дроппера, пока он не поместил данные конфигурации в стек, а затем я использовал функцию idc.get_bytes IDAPython для извлечения результирующих данных стека. Я написал код сценария IDAPython, чтобы перебрать данные стека и собрать инструкции push в полезную нагрузку IDB, ведущую к инструкции вызова, указывающей на экспорт DLL. Это позволило мне отладить процесс распаковки из Bochs за один сеанс.
Я нажал на начало моего синтезированного вызова и нажал F4, чтобы запустить код в Bochs. Меня приветствовало предупреждение на рисунке #10, указывающее, что пропатченный IDB не будет соответствовать образам, сделанным отладчиком (что неверно в случае режима IDB Bochs). Bochs добросовестно выполнил мои введенные опкоды, и они дали именно тот результат, который мне нужен.
Я внимательно наблюдал за приближением указателя инструкций и прохождением проверки функции IsDebuggerPresent. Из-за вставленной мной заглушки (stub_IsDebuggerPresent) отладчик успешно прошел проверку, возвращая ноль, как показано на рисунке 11.
Я позволил счетчику программ перейти к адресу 0x1A1538, сразу после процедуры распаковки. На рисунке 12 показано состояние регистра в этой точке, которое отражает значение в EAX, которое было передано моим фальшивым распределителем кучи и которое я собирался посетить.
На рисунке 13 показано, что в этом месте действительно была сигнатура IMAGE_DOS_SIGNATURE ("MZ"). Я использовал функцию idc.get_bytes(), чтобы сдампить распакованный двоичный файл из местоположения поддельной кучи и сохранить его для анализа.
Через режим Bochs IDB, я также смог использовать интерактивный интерфейс отладчика IDA Pro, чтобы экспериментировать с манипулированием выполнением и обходом другой ветви, чтобы также распаковать другую полезную нагрузку для этой вредоносной программы.
Вывод
Хотя динамический анализ иногда является самым быстрым путем, его настройка и навигация отвлекают по мелочам меня от внимания, поэтому я разработал методику, которой я, скорее всего, буду эмулировать в Bochs, чтобы избежать этих отвлекающих факторов, все еще получая ответы. Инжекция кода в IDB расширяет набор функций, с которыми я могу это делать, позволяя мне получить больше от эмулятора Bochs. Это, в свою очередь, позволяет мне больше экспериментировать на лету, одноразово декодирую строки или проверять гипотезы, прежде чем атаковать что-либо в масштабе. Это также позволяет мне динамически экспериментировать с сэмплами, которые в любом случае не будут загружаться правильно, такими как распакованный код с поврежденными или неправильными заголовками PE.
Я поделился инструментами Code Grafting как частью репозитория flare-ida GitHub. Чтобы использовать это для своих собственных анализов нужно:
Из этого поста становится ясно, насколько далеко я пойду, чтобы не нарушать зрительный контакт с IDA.Если вы тоже любите использовать Bochs с IDA, то это мой подарок для вас. Наслаждайтесь вместе со мной!!!
Источник: https://www.fireeye.com/blog/threat...-grafting-to-unpack-malware-in-emulation.html
Автор перевода: yashechka
Переведено специально для портала xss.pro (c)
Режим Bochs IDB устраняет отвлекающие факторы, такие как переключение виртуальных машин, настройка отладчика, нейтрализация анти-аналитических мер и перемещение счетчика программы к интересующей логике программы. Увы, там, где нет ОС, не может быть загрузчика или динамического импорта. Исполнение ограничено кодами операций, найденными в IDB. Это исключает эмуляцию подпрограмм, которые вызывают импортированные строковые функции или распределители памяти. Эмулятор Тома Беннетта поставляется с эмулированными версиями этих версий, но для анализа "off-the-cuff" (особенно, когда я не знаю, будет ли это оправдано), я предпочитаю интерактивно исследовать регистры и память, чтобы скорректировать свою тактику ad hoc.
Что если бы я мог принести свои импортированные функции в Bochs, как это делает эмулятор flare-emu? Я разработал такую технику, и я называю её трансплантацией кода. В этом посте я расскажу об особенностях статического связывания для общих функций в IDB, чтобы получить больше выгоды от эмулятора Bochs. Я продемонстрирую использование этого на примере EVILNEST для распаковки и дампа полезных нагрузок следующего этапа(next-stage) из эмулируемой памяти. Я также покажу, как я скопировал сложную последовательность вызовов из одной IDB в другую IDB, чтобы я мог сохранить процесс распаковки всего в одном сеансе отладки Bochs.
Сценарий EVILNEST
Мой сэмпл (MD5-хэш 37F7F1F691D42DCAD6AE740E6D9CAB63, который доступен через VirusTotal) был вариантом EVILNEST, который заполняет стек данными конфигурации перед вызовом промежуточной полезной нагрузки. На рисунке #1 показан этот необычный вызов.
Код на рисунке #1 выполняется в удаленном потоке внутри захваченного процесса iexplore.exe; вредоносная программа также использует анти-аналитическую тактику. У меня была промежуточная стадия полезной нагрузки, и я хотел распаковать полезную нагрузку следующего этара, не управляя сценарием отладки с несколькими процессами с анти-анализом. Я знал, что мог бы заглушить несколько вызовов функций в вредоносной программе, чтобы запустить всю соответствующую логику в Bochs.
Вот как я это сделал.
Вырезание кода
Мне нужны были опкоды для нескольких общих функций, чтобы инжектировать их в мои IDB и эмулировать в Bochs. Я построил простые реализации C выбранных функций и скомпилировал их в один двоичный файл. На рисунке # 2 показаны некоторые из этих подмен.
Я скомпилировал их и затем использовал код IDAPython, похожий на рисунок #3, чтобы извлечь байты кода операции функции.
Я курировал библиотеку опкодов функций в скрипте IDAPython, как показано на рисунке #4. Опкоды нестандартной функции в нижней части рисунка были ассемблированы вручную настолько кратко, насколько это было возможно, чтобы в общем случае возвращать конкретные значения и манипулировать стеком (или нет) в соответствии с соглашениями о вызовах.
Помимо простых функций, таких как memcpy, я реализовал аллокатор памяти. Аллокатор ссылался на глобальные данные о состоянии, то есть я не мог просто инжектировать их в IDB и ожидать, что они будут работать. Я прочитал дизассемблировванный спиок, чтобы найти ссылки на глобальные операнды и шаблонизировать их для использования с методом формата Python. На рисунке 5 показан пример для функции malloc.
Я организовал заглушки по имени, как показано на рисунке #6, чтобы вызывать функции, которые мне нужно будет пропатчить, и удобно добавлять больше заглушек функций по мере их использования. Искаженное имя, которое я указал как алиас free является оператором удаления.
Чтобы инжектировать эти функции в двоичный файл, я написал код, чтобы найти следующий доступный сегмент заданного размера. Я избегал занимать нижнюю память, потому что Bochs помещает свой сегмент загрузчика ниже 0x10000. Рядом с кодом в моем сегменте кода я включил пространство для данных, используемых моим распределителем памяти. На рисунке #7 показан результат патчинга этих функций и данных в IDB и присвоения имени каждому местоположению (функции-заглушки имеют префикс stub_).
Затем скрипт повторяет все соответствующие вызовы в двоичном файле и патчит их вызовами в своих реализациях заглушки во вновь добавленном сегменте. Как показано на рисунке #8, функция Assemble IDAPython сэкономила усилия по вычислению смещения для операнда вызова вручную. Обратите внимание, что функция Assemble здесь работает хорошо, но для более крупных задач Hex-Rays рекомендует выделенный ассемблер, такой как Keystone Engine и его плагин Keypatch для IDA Pro.
Скрипт Code Grafting обновил все соответствующие вызовs, чтобы они напоминали рисунок #9, с заменой целевых функций вызовами реализаций stub_, введенных ранее. Это предотвратило сбой Bochs в режиме IDB при попадании на эти вызовы, поскольку операнды вызовов теперь указывали на действительный код внутри IDB.
Работа с EVILNEST
Сценарий отладки для дроппера был немного неудобным, и одновременно он настраивал очень необычный вызов для точки входа полезной нагрузки. Я использовал Bochs для выполнения дроппера, пока он не поместил данные конфигурации в стек, а затем я использовал функцию idc.get_bytes IDAPython для извлечения результирующих данных стека. Я написал код сценария IDAPython, чтобы перебрать данные стека и собрать инструкции push в полезную нагрузку IDB, ведущую к инструкции вызова, указывающей на экспорт DLL. Это позволило мне отладить процесс распаковки из Bochs за один сеанс.
Я нажал на начало моего синтезированного вызова и нажал F4, чтобы запустить код в Bochs. Меня приветствовало предупреждение на рисунке #10, указывающее, что пропатченный IDB не будет соответствовать образам, сделанным отладчиком (что неверно в случае режима IDB Bochs). Bochs добросовестно выполнил мои введенные опкоды, и они дали именно тот результат, который мне нужен.
Я внимательно наблюдал за приближением указателя инструкций и прохождением проверки функции IsDebuggerPresent. Из-за вставленной мной заглушки (stub_IsDebuggerPresent) отладчик успешно прошел проверку, возвращая ноль, как показано на рисунке 11.
Я позволил счетчику программ перейти к адресу 0x1A1538, сразу после процедуры распаковки. На рисунке 12 показано состояние регистра в этой точке, которое отражает значение в EAX, которое было передано моим фальшивым распределителем кучи и которое я собирался посетить.
На рисунке 13 показано, что в этом месте действительно была сигнатура IMAGE_DOS_SIGNATURE ("MZ"). Я использовал функцию idc.get_bytes(), чтобы сдампить распакованный двоичный файл из местоположения поддельной кучи и сохранить его для анализа.
Через режим Bochs IDB, я также смог использовать интерактивный интерфейс отладчика IDA Pro, чтобы экспериментировать с манипулированием выполнением и обходом другой ветви, чтобы также распаковать другую полезную нагрузку для этой вредоносной программы.
Вывод
Хотя динамический анализ иногда является самым быстрым путем, его настройка и навигация отвлекают по мелочам меня от внимания, поэтому я разработал методику, которой я, скорее всего, буду эмулировать в Bochs, чтобы избежать этих отвлекающих факторов, все еще получая ответы. Инжекция кода в IDB расширяет набор функций, с которыми я могу это делать, позволяя мне получить больше от эмулятора Bochs. Это, в свою очередь, позволяет мне больше экспериментировать на лету, одноразово декодирую строки или проверять гипотезы, прежде чем атаковать что-либо в масштабе. Это также позволяет мне динамически экспериментировать с сэмплами, которые в любом случае не будут загружаться правильно, такими как распакованный код с поврежденными или неправильными заголовками PE.
Я поделился инструментами Code Grafting как частью репозитория flare-ida GitHub. Чтобы использовать это для своих собственных анализов нужно:
- В командной строке IDAPython IDA Pro запустите code_grafter.py или импортируйте его как модуль.
- Создайте объект CodeGrafter и вызовите его метод graftCodeToIdb(): CodeGrafter ().GraftCodeToIdb()
- Используйте Bochs в режиме IDB, чтобы удобно выполнить ваш измененный сэмпл и экспериментировать!
Из этого поста становится ясно, насколько далеко я пойду, чтобы не нарушать зрительный контакт с IDA.Если вы тоже любите использовать Bochs с IDA, то это мой подарок для вас. Наслаждайтесь вместе со мной!!!
Источник: https://www.fireeye.com/blog/threat...-grafting-to-unpack-malware-in-emulation.html
Автор перевода: yashechka
Переведено специально для портала xss.pro (c)
Последнее редактирование модератором: