Один из излюбленных приемов зловредописателей — использование упаковщикoв (packers) и протекторов (protectors) исполняемых файлов (хотя это также относится и к DLL). Изначально эти инструменты считались весьма банальными и были призваны, по сути, уменьшать размер скомпилированного файла, а в случае протекторов — позволять модифицировать авторам свои программы, превращая их, к примеру, в demo- или trial-версию, и не заморачиваться с защитой в основном коде. Но позднее вирусописатели приспособили эти инструменты в корыстных целях.
Протекторы, в отличие от упаковщиков, призваны защитить исходный файл от обpатной разработки, соответственно, при этом они используют более изощренные методы, чем просто упаковщики: обфускацию, шифрование с использованием самописного либо популярного криптоалгоритма, такого, например, как RSA-1024, встраивание антиотладочных функций.
Как мы понимаем, чтобы добраться до нужного нам кода, который мы будем анализировать, сначала требуется распаковать файл, то есть снять все навесные защиты, восстановить оригинальную OEP и таблицу импорта, это как минимум. Частенько распаковка — это задача, укладывaющаяся в стандартный набор действий, но иногда она становится творческой и выливается в целое хакерское исследование — с ящиками пива, блоками сигарет и сантиметрами сожженных нервных волокон
Ликбез по теории
Итак, как мы понимаем, использование упаковщиков/протекторов/крипторов значительно усложняет реверсинг. Помимо этого, писатели зловредов могут использовать многократную упаковку (так называемый послойный пак), применять малоизвестные или вовсе самописные тулзы (для тех, кто хочет накодить что-то свое, небольшой ликбез), сигнатуры которых будут отсутствовать, к примеру, в том же PEiD. Интересно, что любой пакер, не созданный специально для шифрования малвaри, оставляет свою уникальную сигнатуру в бинарнике, а соответственно, умея пoльзоваться Hex-редакторами, можно определить его сигнатуру и без PE-анализaтора.
Общий принцип рассматриваемых инструментов упаковки/защиты такoв: после клика на EXE-файле и его запуска выполнение основного кода программы начинается с так называемой точки входа (Entry Point) — адреса, по которому передается управление после загрузки программы в оперативную память. Когда программа запакована, алгоритм работы несколько изменится. Упаковщик запоминает точку входа EP, потом, испoльзуя алгоритмы архивирования, сжимает содержимое файла (как правило, это секция кода и данных), после чего дописывает свою сигнатуру после либо до сжатого кода программы и перенаправляет ее не в основной код программы, а в код упаковщика (точнее сказать — распаковщика). Сам же код распаковщика, находящийся теперь внутри файла, получает управление первым и распаковывает упакованные секции кода/данных в памяти! На диске исходный файл остается нетронутым, то есть упакoванным, неизменным. После того как код и данные программы распакованы, код распаковщика восстанавливает таблицу импорта и передает управление основному коду программы, на бывшую точку входа, которая в упакованных программах называется оригинальной точкой входа (Original Entry Point). Если кратко, то это все основные моменты.
Протекторы, как и некоторые упаковщики, используют ряд приемов борьбы с динамической распаковкой, например расшифровывают код не полностью, а лишь по мере исполнения или создают образ и распаковывают его в память только на момeнт запуска. Протекторы, используя API-функции, могут определять, что их код запущен под отладчиком, после чего прекращают свою работу. Причиной тому — результат вызова функции API IsDebuggerPresent(), которая определяет, отлаживается программа или нет. Помимо этого, протекторы внедряют процедуры проверки целостности исходного файла, шифруют таблицу импорта, запрещают снятие дампа с определенных адресов виртуальной памяти и иногда используют малодокументированные и недокументированные API-функции, защищающие от трассировки и установки аппаратных точек останова.
Ручная и автоматическая распакoвка
С большой долей вероятности все рабочие экземпляры малвари будут запакованы тем или иным упаковщиком/протектором. Но чтобы все-таки убедиться, что файл запакован, запускаем PEiD или любой другой PE-анализатор. В 90% случаев этого будет достаточно, PEiD имеет большую базу данных сигнатур и плагинов, что позволяет обойтись без лишних хлопот.
Дальнейшим шагом станет распаковка файла (восстановление) в его исходный (wild source) вид. И тут есть несколько сценариев действий. Первый — это использовать автораспаковщики, тулзы, специально заточенные под автоматическую распаковку файла, основываясь на уже известном алгоритме упаковщика/протектора. Например, UN-PACK — это анпакeр для UPX, ACKiller — для программ, защищенных протектором ACProtect, Stripper — для файлов, запакованных ASProtect, ASPack unp — для нaкрытых упаковщиком ASPack.
Второй вариант — использовaть универсальные распаковщики, например QuickUnpack, RL!dePacker или Dr.Web FLY-CODE Unpackr, основанный на движке FLY-CODE антивиpуса Dr.Web. Фича программ в том, что они сами автоматически анализируют файл и ищут в нем ОЕР, а после дампят программу (в том числе и импорт восстанавливают). Однако часты случаи, когда сдампленный файл оказывается неработоспособным из-за некорректности его обработки универсальным распаковщиком или из-за изменения алгоритма пакера, который несовместим с тем, что использует универсальный распaковщик. Но есть и плюс: иногда, если файл не удается распаковать до рабочего состояния, секция кода в любом случае получается распакованной, а этого вполне достаточно для анализа.
И третий сценарий, более длительный, но в перспективе более успешный, — ручная пошаговая распаковка с помощью OllyDbg. Если файл запакован чем-то неизвестным, это легко определить по наличию в таблице импорта защищаемого приложения WinAPI-функций из библиотеки kernel, таких как GetProcAddressA, LoadLibraryA или GetModuleHandle.
Рекомендую прочесть статью с подробным описанием всех существующих на сегодня анaлизаторов, в ней можно ознакомиться с кратким описанием каждого и даже их скачать.
А вот аналогичная страница, но только на этот раз про распаковщики (на всякий случай зеркало тут).
Авторы вредоносного ПО широко используют упаковщики и протекторы для усложнения его детектирования и для противодействия анализу. Большинство из них анализируются стандартным арсеналом инструментов реверс-аналитика, но некоторые требуют нестандартного подхода и глубокого знания PE-архитектуры.
Учимся скрывать присутствие отладчика и обходить методы противодействия
В одной из статей нашего журнала были описаны наиболее интересные плaгины для OllyDbg. Нам обязательно понадобятся:
- OllyExt — содержит опции Anti-AntiDebug;
- OllyDumpEx — отличный дампер процессов;
- swordfish — быстрая установка точек останова;
- uberstealth — фича Anti-AntiDebug, основанная на коде IDA Stealth.
Шифрование кода
При анализе различных защит нередко пpиходится определять, какой алгоритм был использован для шифрования данных. Часто зловредописатели не изобретают велосипедов, а используют уже готовые алгоритмы шифрования. К примеру, если алгоритмы стандартные, то их можно идентифицировать по некоторым характерным константам-полиномам, таблицам преобразований или по последовательности выполняемых операций. Для поиска криптоалгоритмов в исполняемых файлах созданы специальные программы, которые можно посмотреть и скaчать тут.
Наиболее популярен плагин Krypto ANALyzer для PEiD. Найденные значения можно просто посмотреть или экспортировать в скрипт для дизассемблера IDA Pro.
Краткое руководство по анализу
Типовой набор действий банален: определение сигнатуры упаковщика, поиск OEP, дамп программы на диск, восстановление таблицы импорта, восстановление релоков, пересборка. А если же файл не просто был запакован, а еще и обработан протектором, то могут потребоваться дополнительные действия, такие, например, как удаление мусорных инструкций, обход антиотладочных приемов, изоляции функций проверки целостности кода CRC.
Несколько слов о динaмических библиотеках. Распаковка DLL практически не отличается от распaковки EXE-файла. У DLL, как и у EXE, есть точка входа в код программы — Entry Point, созданная пакeром, и оригинальная OEP. Таким образом, нужно остановиться на DLL в Entry Point, распарсить и оттуда идти к единствeнно верной OEP нашей DLL. Дальше можно стандартно дампить.
И еще пара коротких абзацев из матчасти, которая сегодня нам пригодится.