Реверс-инжиниринг бинарных программ, предназначенных для кастомных виртуальных машин (ВМ), представляет собой крайне сложную и ответственную задачу, особенно в условиях активного использования обфускации кода. Обфускация, основанная на внедрении виртуальной машины с тайным набором инструкций, является одной из самых эффективных и технологически продвинутых техник защиты программного обеспечения от анализа. Именно благодаря ей обеспечивается максимальная сложность для аналитика, стремящегося понять логику работы защищенного ПО или вредоносного кода. Впервые на профессиональной конференции Recon2012 специалисты Александр Чернов и Катерина Трошина поделились своими разработками и подходами к обратному проектированию бинарных программ для подобных виртуальных машин, озвучив при этом ряд эвристик, позволяющих упростить и ускорить процесс исследования VM-кода. В основе представленных методов лежат разумные предположения о типичной структуре бинарного кода, сгенерированного компиляторами, а также глубинный анализ кода виртуальных машин.
Знание архитектуры и набора инструкций ВМ - ключевой этап для дальнейшего успешного анализа и взлома - становится доступно не только за счет изучения VM-интерпретатора, но и благодаря исследованию самого бинарного кода программ, предназначенных для ВМ. Эксперты предложили систему анализа, включающую начальную верификацию программы. На этом этапе производится в том числе маркировка данных и кода, их структурное разделение с учетом, если таковые имеются, исходной информации по назначению программы или сопровождающей документации. Выделение точек входа в код - важная составляющая, позволяющая переключиться с общего осмотра на узконаправленное изучение интересующих участков. Следующий этап посвящен реконструкции структуры подпрограмм, что достигается через идентификацию границ подпрограмм и, прежде всего, определения инструкций возврата (RET).
В программировании подпрограммы отделяются при помощи таких инструкций, и логично предположить, что финальными командами кодовых сегментов являются именно они. Анализ вызовов подпрограмм (CALL) тесно связан с пониманием мест размещения RET, так как в большинстве случаев возврат управления после вызова должен происходить прямо за инструкцией возврата. Для выявления безусловных переходов (JMP) используется предположение, что выполнение кода стартует по конкретному фиксированному адресу. Анализируя стартовые участки, становится возможным выделять такие переходы, формирующие каркас алгоритма. В свою очередь, инструкции вызовов (CALL) зачастую внешне схожи с JMP, поэтому поиск и выделение их среди общего потока команд требует отдельного внимания.
Применяя детальный анализ кода и исследование инициализационных построек, можно сузить круг кандидатов для CALL, а с дальнейшей валидацией - выделить наиболее вероятные из них. Изучение переходов по абсолютным и относительным адресам происходит посредством оценки битового кодирования инструкции. Особое внимание уделяется проверке, соответствует ли параметр перехода смещению относительно адреса следующей команды. Таким образом выявляются относительные переходы, вызовы и кандидаты на условные переходы - все это критичные компоненты структуры управляющего потока. Важной составляющей исследования становится распознавание команд загрузки и сохранения данных в память.
Анализируя характерные паттерны, такие как операции копирования из памяти в память, удается выделить инструкции, отвечающие за загрузку (load) и сохранение (store) данных. Это существенно помогает в понимании оперирования данными внутри виртуальной машины. Особое внимание в докладе уделяется наблюдениям о структуре регистров ВМ и их ширине. Сколько регистров предполагается иметь у анализируемой машины, а также какого они размера - 8, 16, 32 или 64 бита? Такой анализ требует кропотливого изучения инструкций, применяемых в бинарном коде, а также их признаков, косвенно указывающих на архитектуру регистров. Кроме того, исследование арифметических и логических операций ведется в тесной связке с выявленными условными переходами.
Анализ пар операций и сопутствующих условных переходов дает возможность понять, как реализуются в ВМ базовые вычисления и сравнения, что является фундаментом для построения логики сложных алгоритмов. В рамках доклада эксперты продемонстрировали работу с реальными примерами бинарного кода, подвергнутого такой деконструкции и частичной деобфускации. Кроме того, было уделено внимание перспективам частичной или полной автоматизации этих процессов - тема, чрезвычайно актуальная для практикующих аналитиков и разработчиков инструментов безопасности. Инструментарий SmartDec, разрабатываемый авторами, иллюстрирует конкретные применения описанных эвристик. Модуль эвристического бинарного анализа включен непосредственно в этот декомпилятор и позволяет улучшить разбор и реконструкцию как самого бинарного кода ВМ, так и программ, работающих в рамках этой виртуальной машины.
Важным преимуществом современных подходов является возможность частичной декомпиляции кода после прохождения этапов рекструктуризации и маркировки. Это не только облегчает понимание устройства программы, но и значительно ускоряет процесс анализа и интеллектуального поиска уязвимостей. Таким образом, методы, озвученные на Recon2012, представляют собой значимый шаг вперед в области реверс-инжиниринга защищённых программ. Они предоставляют аналитикам мощный набор инструментов и подряд логических шагов для распутывания сложных защит, основанных на виртуальных машинах с нестандартным набором инструкций и архитектурой. Продолжение развития автоматизации и проведение все более глубоких исследований позволяют надеяться на дальнейшее улучшение эффективности анализа и создания надежных средств борьбы с обфускацией и малварью.
В итоге регулярное применение комплексных эвристических методов и современных декомпиляторов - ключ к успешному раскрытию внутренних механизмов кастомных ВМ, что актуально как для исследователей компьютерной безопасности, так и для разработчиков ПО, занимающихся анализом и обновлением старых или защищенных проектов. .