Взлом VMProtect защиты. Полная распаковка программы скриптом + доработка (Глава 2)

Обсудить статью на форуме


Очень удобно, когда все крэкерские инструменты, книги и статьи в одном месте. Используйте сборник 2020 года от EXELAB - вот тут.

Из чего состоит этот туториал


Давайте немного поговорим о том, как построен этот туториал. Он состоит из 3 глав:
1. В первой главе даются общие подробности о самом протекторе, о его опциях и способах
защиты этим протектором. Также здесь рассматривается список литературы, который
желательно прочитать, если Вы хотите хорошо разобраться в распаковке этого протектора.
Во второй части первой главы мы познакомимся с собственноручно написанным
unpackme, узнаем о том, что он из себя представляет и как его "обезвредить".
Ссылка на главу: Взлом VMProtect защиты. Знакомство, подготовка крэкми (Глава 1)

2. Вторая глава туториала предназначена для самых маленьких :-) . Здесь мы попробуем
распаковать unpackme, но практически полностью автоматизируем эту работу с помощью
скрипта от LCF-AT . Поначалу я не хотел включать эту главу в туториал, но учитывая, что у
новичков возникает очень много вопросов по работе со скриптом, да и сам скрипт не
лишён недостатков, то всё-таки было решено показать, как работать с отладчиком и его
скриптовым языком. Так же попробуем разобраться, что делать, если файл не запускается
после распаковки. Опытные crackers могут пропустить эту главу.
Ссылка на главу: Взлом VMProtect защиты. Полная распаковка программы скриптом + доработка (Глава 2)

3. Третья глава, пожалуй, самая интересная. Она посвящена инлайн патчингу VMProtect. В
этой главе немного поговорим о теории инлайн патчинга VMProtect-а, попробуем
пропатчить наш unpackme, а также посмотрим, как немного можно ускорить этот процесс.
Вторая часть этой главы будет посвящена реальной коммерческой программе - Movienizer
6.3 . Здесь мы рассмотрим как можно пропатчить заблокированный ключ к программе, и
убрать онлайн проверку регистрации.
Ссылка на главу: Взлом VMProtect защиты. Инлайн патчинг на примере программы Movienizer (Глава 3)

В заключении хочу добавить, что все инструменты, а также программы, использующиеся в
качестве подопытных приложены к этому туториалу. Ссылка на архив (пароль: www.exelab.ru): Взлом VMProtect защиты.

Глава 2


Подготовка инструментов


Для изучения этого "чуда" нам понадобятся следующие инструменты:
1. OllyDbg 1.10 - прекрасный отладчик пользовательского режима;
2. Плагин Phantom 1.85 для скрытия отладчика OllyDbg;
3. Плагин OdbgScript 1.82.6 для использования скриптов;
4. Плагин CommandBar 3.20.110 для удобной установки брейкпоинтов (точек останова);
5. Scylla 0.9 или ImpREC 1.7 для восстановления импорта.
Конечно, каждый волен использовать тот инструмент, к которому он привык или считает лучше.
Поэтому этот список можно как расширить, так и сузить. Я привёл его лишь для того, чтобы
показать, чем привык пользоваться я.
Теперь нам нужно настроить инструменты и избежать обнаружения защитой нашего отладчика.
Начнём с отладчика. У меня он уже множество раз пропатчен и я, честно говоря, уже и не помню
где, когда и зачем патчил. Поэтому к туториалу прикладываю свой exe-шник. Убираем все
плагины, кроме трёх указанных выше и у нас должно получиться, что-то похожее на это:

Рисунок 4.Плагины для OllyDbg

Далее смотрим в папку с отладчиком и находим там файл dbghelp.dll - его нужно обновить до
последней версии, т.к. протектор использует ошибку в этой dll (переполнение буфера). В случае
если у Вас будет старая версия dbghelp.dll , то при открытии защищённой программы в отладчике,
отладчик просто сразу закроется.
Если Вы сейчас попробуете запустить программу в отладчике, то сначала получите такое
сообщение:

Рисунок 5. Предупреждение отладчика

А затем и это:



Рисунок 6. Обнаружение отладчика

Первое сообщение не относится к защите протектора, а является опцией OllyDbg, которая просто
указывает нам на то, что точка входа программы (в нашем случае это EP, а не OEP, т.к. программа
запакована) находится за пределами секции кода, указанного в PE заголовке. Дело здесь вот в
чем. Если мы посмотрим на карту памяти в отладчике, то увидим, что начало работы
запакованной программы начинается из секции .UPX1 :

Рисунок 7. Секции упакованного файла

В то время, как нормальная и не запакованная программа обычно начинает своё выполнение из
секции CODE:

Рисунок 8. Секции не упакованного файла

Многих новичков это предупреждение отладчика жутко раздражает, а некоторых вообще вводит
в ступор. Мне лично на это сообщение абсолютно всё равно, а иной раз оно даже помогает понять
запакована ли программа или нет. Если кого это предупреждение доводит до безумия, то его
можно отключить в плагине Phantom. Галка называется - fix OllyDbg bugs .
Второе сообщение в отличие от первого, относится как раз к защите протектора и указывает нам
на то, что всё, мы своё отладили... :-) Новые версии протектора более дружелюбны к отладчикам,
поэтому для того чтобы скрыть отладчик нужен всего лишь один плагин. Есть один такой
прекрасный плагин - Phantom от Hellspawn-а . Phantom делает свою работу прекрасно, прячет
отладчик практически от всего. Единственная просьба к Hellspawn-у - не ленись, допиливай
плагин ко второй ольке :-) , очень его там не хватает.
Итак, для того, чтобы анпакми нормально запускался, и его можно было отлаживать, необходимо
настроить плагин следующим образом:



Рисунок 9. Настройка плагина Phantom

И последнее, что нам нужно сделать, это немного настроить опции OllyDbg , а именно произвести
настройки вкладки "Exceptions":

Рисунок 10. Настройка исключений

Таким образом, мы должны проскакивать на всех сгенерированных исключениях. Вот и всё,
теперь можно спокойно "работать" с протектором.


Скрипт VMProtect Ultra Unpacker 1.0


Как уже говорилось выше, LCF-AT написал прекрасный скрипт для автоматической распаковки
VMProtect. Он поддерживает довольно много опций протектора, неплохо выполняет свою работу,
но всё же имеет определённые огрехи. Во-первых, он требует небольшой настройки при первом
использовании, во-вторых он для восстановления импорта использует библиотеку ARImpRec.dll от
Nacho_dj из команды ARTeam . Как мне кажется, это не совсем удачный вариант, т.к. она довольно
много косячит, что выливается в невозможности запуска распакованной программы на другой
операционной системе. Хотя это моё личное и субъективное мнение. И в-третьих даже после
удачной распаковки скриптом в некоторых случаях всё-таки приходится "дорабатывать" дамп
вручную. Несмотря на всё это, многие новички не читают, или не хотят читать инструкции по
работе со скриптом, которые автор скрипта приложил к самому скрипту. И самое главное новички
не хотят думать ГОЛОВОЙ, а это ещё хуже. Ребята ну запомните: скрипт, это НЕ автоматический
распаковщик, это всего лишь хороший помощник в наших начинаниях.
Первоначальная настройка скрипта занимает буквально одну-две минуты. Открываем скрипт в
блокноте, немного прокручиваем вниз и настраиваем следующее:


У меня настроено всё по умолчанию. Исправлено только расположение dll. Если Вы прописали
правильно расположение ARImpRec.dll, но скрипт всё равно упорно указывает, что не может его
найти, то попробуйте:
1. Перекачать скрипт с библиотекой (в самой первой версии такое было, потом поправили);
2. Поместить скрипт и dll в корень диска (например "C:\").
Также необходимо найти эту строчку:

Рисунок 11. Комментирование строки



И закомментировать строчку, которая выделена. Должно получиться:
// mov ARIMPREC_PATH, "C:\Nacho dll test\ARImpRec.dll"
Ну, всё подготовлено и настроено, поэтому "let’s go..."


Распаковка VMProtect с помощью скрипта


Загружаем файл Unpackme_vmp.exe под отладчиком, стоя на EP, запускаем скрипт командой Run
Script -> Open -> выбираем файл скрипта. Сначала скрипт покажет нам окошко с информацией о
нашей системе (если она была включена в настройках). Нажимаем ОК, т.к. она нам не интересна.
Дальше видя, что отладчик ничего не делает, новички впадают в панику, а нужно всего лишь
продолжить скрипт Правая кнопка мыши -> Script Function -> Resume , так как исполнение скрипта
просто приостановлено. Смотрим, как бегают разные буквы, затем перед нами появляется вопрос
скрипта:

Рисунок 12. Поиск OEP

Здесь нас спрашивают, каким образом необходимо найти OEP
? С помощью трюка ESP (hr esp-4)
? С помощью апи метода
Честно говоря, я всегда нажимал "ДА", т.е. пользовался методом ESP, т.к. он всегда срабатывал.
Немного ждём и скрипт извещает нам, что мы на OEP. Также он показывает нам - реальная ли это
оеп (1) или с украденными байтами (0). Тут бы мне хотелось обратить Ваше внимание на два
момента.
1) В моем случае скрипт остановился на абсолютно верном OEP (у меня не было украденных
байт), но в большинстве случаев он останавливается после вхождения в первый CALL после
OEP, имеющий примерно такой вид (рассматривается программа на Delphi):

Рисунок 13. Вид первой функции после OEP

Скрипт честно отрапортует, что он остановился РЯДОМ с OEP, а Вам нужно по F8 (пошаговая
трассировка) выйти из функции и чуть посмотреть реальный адрес OEP.
Теперь перезапускаем программу в отладчике ( F2 ) и в папке с программой ищем файл OEP RVA of
Unpackme_vmp.exe - .txt , открываем его и проверяем, какой OEP указан в первой строчке:



Рисунок 14. Проверяем OEP

Регистры не трогаем, а вот адрес OEP проверяем. Если он найден правильно, то оставляем, в
противном случае указываем правильный в RVA-формате (т.е. как Вы видите его в отладчике).
Когда всё проверено и исправлено, перезапускаем анпакми в отладчике и повторяем все
шаги, которые мы проделываем выше. Появится запрос скрипта о поиске "API LOGGER".

Рисунок 15. Поиск API Logger

API LOGER это, то место где протектор в открытом виде перебирает все API функции.
Существует два метода поиска этого места:
1) Автоматически (Нажать ДА)
2) Ручной (Нажать НЕТ). При выборе этого метода будет необходимо искать это место
вручную. К скрипту приложено видео, как найти это место. Выбирать второй вариант
необходимо, если Ваша программа запакована старыми версиями VMProtect.
Далее нам говорят о том, что скрипт установил новый адрес OEP и переопределил прыжок на
старый OEP:

Рисунок 16. Jump на OEP



Если Вам необходимо добавить украденные байты вручную, то это необходимо сделать сейчас.
После этого нажимаем ОК. Если после этого Вы получите сообщение о том, что не найдена
библиотека ARImpRec.dll , то проверьте настройки в скрипте, а лучше переместите скрипт и файл
DLL в корень диска (чтобы был наиболее короткий путь). В противном случае всё придётся
начинать сначала. Если Вы настроили всё правильно, то Вам поступит предложение
просканировать все ссылки на импорт в секции кода:

Рисунок 17. Сканирование импорта

Нажимаем ДА, и сразу же в следующем окне:

Рисунок 18. Выбор метода сканирования

Выбираем метод сканирования:
1) ДА - только в секции кода (быстрее)
2) НЕТ - во всех секциях (медленнее, зато надёжнее, так как в старых версиях протектора API
команды находятся в секции VMProtect)
Выбираем второй вариант, т.е. НЕТ. Далее появится запрос о проверке на Vmed APIs (честно
говоря, не понял, что это такое, т.к. этот вопрос появляется очень редко). Нас предупреждают,
что это может привести в дальнейшем к неработоспособности дампа. Давайте пропустим и
нажмём НЕТ. А вот при этом запросе:

Рисунок 19. Поиск "жёстких" ссылок на импорт

Советую нажимать НЕТ. Так как это практически всегда приводит к ошибкам.


Следующее сообщение рассказывает нам о CPUID и RDTSC :

Рисунок 20. Отчёт о CPUID и RDTSC

Читаем информацию и нажимаем ОК. Всё, скрипт закончил основную работу и даёт нам
общую информацию о нашем защищённом файле: показывает нам размер файла до и после
распаковки, уведомляет нас, что присутствовал локальный анти-дамп, анти-дамп ресурсов и
кучи, показывает размер таблицы импорта.


Доработка дампа (Проверяем импорт)


Если Вы сейчас запустите нашу распакованную программу, то увидите, что она запускается и
прекрасно работает. Но не спешите радоваться. Дело в том, что запускаться и работать она будет
только на системе где Вы распаковывали программу (в моем случае на Windows XP). Если Вы
попробуете запустить её на другой операционной системе (например, на Windows 7), то она
работать не будет. Это возможно по двум причинам:
1) Не совсем правильно восстановлена таблица импорта
2) Сработала защита ВМ
В первом случае, проблема возникает, скорее всего, из-за того, что в качестве утилиты для
восстановления импорта скрипт использовал ARImpRec.dll основанный на движке Import
Reconstruction. По сути это и есть тот же самый старый, добрый ImpREC, только с какими-то
изменениями. Какими именно я не знаю, но косячит он с импортом точно также. Import
REConstructor также иногда грешил этим делом. Как это дело проверить и исправить?
Запускаем наш дамп программы (на системе где распаковывали) в OllyDbg , В окне дампа
переключаемся в режим отображения адресов Правая кнопка мыши -> Long -> Address :

Рисунок 21. Режим отображения API адресов

Затем в главном окне нажимаем сочетание клавиш Ctrl+B и в появившемся окне производим
поиск любой API функции. Для Delphi Hex-значение соответствует FF25 , для C\C++ FF15 :

Рисунок 22. Поиск API функции

На первой же функции (например, по адресу 004011F4 ), нажимаем Правая кнопка мыши -> Follow
in Dump -> Memory Address , затем смотрим в дамп:



Рисунок 23. API функции операционной системы

На первый взгляд всё в порядке, все функции восстановлены, а нули между функциями - это всего
лишь разделители между библиотеками (на рисунке выше это- kernel32, user32, advapi32, oleaut32
и т.д.). Это нормально и так должно быть. Но первые впечатления обманчивы. Если в окне дампа
Вы немного прокрутите вверх, то увидите, например такую картину:

Рисунок 24. API функция GetVersion

Обратите внимание, как происходит переход к функции GetVersion . Он происходит через
дополнительный переход, который находится в секции протектора. На другой операционной
системе это может сослужить нам плохую службу. Скорее всего, это происходит, потому что ранее
при запросе скрипта о поиске жёстких ссылок на импорт мы ответили отказом. К сожалению эта
опции в скрипте у меня при согласии всегда приводит к ошибке (скрипт перестаёт работать).
Поэтому эту вещь нам придётся исправлять вручную. Можете покрутить окно дампа немного вниз,
и Вы также увидите, что такая проблема существует ещё с двумя API функциями:

Рисунок 25. Ещё две функции

Стоит добавить, что из всех протестированных мною программ, такая история происходит почти
всегда и только с функциями:
? GetVersion
? GetVersionExA
? GetVersionExW


Не знаю, виноват ли в этом скрипт или сам VMProtect, но в любом случае это желательно
исправлять (тем более, если Вам понадобится отрезать секции протектора). Но и это ещё не всё.
Попробуйте проделать все выше сказанные операции на другой операционной системе (у меня
Windows 7):

Рисунок 26. Импорт на Windows 7

Вообще всё не правильно ? . В окне дампа Вы можете видеть адреса, начинающиеся с 77XXXXXX
(адреса системных функций), но они указывают в "небо". Всё дело в том, что эти адреса
указывали на правильные системные функции на Windows XP, но в Windows 7 на этих местах
другой код. Этим частенько грешил Import Reconstruction, именно поэтому я и сделал вывод, что в
этом виновата именно DLL, входящая в комплект к скрипту. В любом случае это надо исправлять,
так что давайте начнём. Перезапускаем программу в отладчике ( Ctrl+F2 ), производим поиск
первой API функции и находим в дампе наш первый "неверный" GetVersion . Далее нажимаем
Ctrl+G и вводим название API функции GetVersion , нажимаем Enter, смотрим её адрес, а затем в
дампе на этой API функции правая кнопка мыши -> Modify , исправляем старый адрес:

Рисунок 27. Старый адрес

На новый:

Рисунок 28. Новый адрес

Нажимаем ОК и в окне дампа видим:



Рисунок 29. Нормальная IAT

Вот теперь всё соответствует стандарту :-) . Проделываем тоже самое и с остальными функциями
(GetVersionExA и ещё один GetVersion).
Ищем начало и конец IAT (при этом просматриваем другие функции):

Рисунок 30. Начало IAT


Рисунок 31. Конец IAT

Собираем общую информацию.
Начало : 0049B154
Конец : 0049B7E4
Размер : 0049B7E4 - 0049B154 = 690
Не закрывая отладчик с программой, запускаем Scylla , указываем нашу программу, OEP не
меняем, т.к. она указана правильно. Указываем начало таблицы импорта и её размер, затем жмём
кнопку Get Imports :



Рисунок 32. Восстановление импорта

Видим, что не определилось 9 функций - это мусор. Выделяем правой кнопкой всё дерево
функций и нажимаем delete tree node . Теперь всё прекрасно. Нажимаем кнопку Dump, указываем
имя для нового дампа программы и сохраняем его на диск. И наконец, жмём Fix Dump, указав
последний сохранённый файл. Смотрим как выглядит таблица импорта на Windows 7:

Рисунок 33. Нормальная таблица импорта

Видим, что всё идеально и красиво :-) . Даже GetVersion прописался как надо.


Доработка дампа (Фиксим ВМ)


Бывают случаи, когда вроде бы импорт в порядке, но при запуске приложения выскакивает такая
ошибка:

Рисунок 34. Ошибка на Windows 7

К сожалению, я так и не успел полностью разобраться из-за чего она происходит и как её
исправить. Но обходные пути всё-таки имеются :-) .
Одной из причин по которой происходит данная ошибка, может быть виртуальная машина
VMProtect-а. Как мы уже знаем ВМ протектора может быть:


Первый вариант, тяжело назвать сильной виртуальной машиной, т.к. в данном случае
оригинальный код просто мешается с мусором. Восстановить данный код не составляет
практически никакого труда.
Второй вариант является уже чистым исполнителем виртуальных команд, т.е. по сути это и есть
настоящая виртуальная машина. Восстановить этот код в разы сложнее.
Третий вариант - это смесь первого и второго варианта. Вот это настоящий экстрим. Восстановить
этот код.... очень проблематично :-) .
Примером мутации в нашем анпакми может служить проверка на включение\отключение пункта
меню InlineMe (Mutation) по адресу 0046C91C . Вообще, такую вм можно и не восстанавливать, т.к.
никаких ошибок и проверок в ней не будет, но бывают случаи, когда это необходимо, например
интересный код хочется посмотреть без мусора. Тем более процедура очистки мусора не
слишком сложная, и занимает немного времени. Давайте поставим точку останова ( F2 ) на адресе
0046C91C и запустим программу. Мы остановились вот здесь:


Вначале мы видим, что в стек кладётся тип виртуальной машины, а затем происходит прыжок на
её начало. С адреса 0046C94B по 0046C96A обращаем внимание на мусор, который протектор
оставил после обработки нашего кода. Сразу после него (0046C96E) идёт адрес возврата, т.е. адрес
на который мы попадём после выхода из виртуальной машины. Идём дальше ( F7 ), смотрим и
отсеиваем мусорный код:

Мусорный код зачеркнут, нужный выделен. Пока у нас есть две реальные команды:
0057A2F0 8D55 DC LEA EDX, DWORD PTR [EBP-24];
0057A2F7 A1 5C384700 MOV EAX, DWORD PTR [47385C];
Идём дальше:

Следующий кусок:

Далее:

Следующий большой кусочек. Как говорится "от души" :-) :

Уже почти конец:



Последний обфусцированный кусок:


Итак, смотрим что получилось:
LEA EDX, DWORD PTR [EBP-24]
MOV EAX, DWORD PTR [47385C]
MOV EAX, DWORD PTR [EAX]
CALL 00455370
MOV EAX, DWORD PTR [EBP-24]
LEA EDX, DWORD PTR [EBP-20]
CALL 00455370
LEA EAX, DWORD PTR [EBP-20]
MOV EDX, 0046CD10
CALL 00404720
MOV EAX, DWORD PTR [EBP-20]
CALL 004089D8
TEST AL, AL
JE 0046C96E
MOV EAX, DWORD PTR [EBP-4]
MOV EAX, DWORD PTR [EAX+324]
MOV DL, 1
CALL 00446EF4
JMP 0046C96E // выход из вм в программу
На месте ВМ писать нам нельзя, т.к. это место будет использоваться протектором в будущем, и мы
просто получим ошибку. Поэтому найдём немного свободного места (например, по адресу
00EDD0DA) и напишем:



Рисунок 35.Чистый код

Обращаем внимание на кусок кода выделенный жёлтым. Здесь идёт проверка на наличие файла
"InlineMe.txt", и если он есть, то ниже разблокируется меню InlineMe(Mutation) . Занопим
переход. В самом конце нашего кода поставим прыжок на возврат в наш оригинальный код.
Сохранимся ( Copy to executable -> Selection ), перезапустим анпакми ( Ctrl+F2 ). И последнее: там, где
был вход в вм протектора, ставим безусловный прыжок на наш код:

Рисунок 36. Прыжок на наш код

Сохраним файл ( Copy to executable -> Selection ) и проверим всё ли правильно. Видим, что всё в
порядке, а как бонус мы получили активированный пункт подменю:

Рисунок 37. Пункт подменю

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



Рисунок 38. Настройка исключений

Запускаем анпакми ( F9 ) и останавливаемся здесь:

Рисунок 39. Ошибка доступа

В отчёте отладчика видим, что произошла ошибка доступа при чтении по адресу 359668C5 . Что за
адрес такой интересный? Здесь дело вот в чём: по коду сразу видно, что это код самого
протектора (ВМ). А откуда вызывается сама виртуальная машина? Вот отсюда:

Рисунок 40. Вызов виртуальной машины

Если сейчас поставить сюда брекпоинт, перезапустить отладчик и запустить программу, то можно
будет потрейсить это место и попытаться разобраться, почему происходит эта ошибка. Я, к
сожалению так и не понял, что это такое ? . Самое интересное то, что всё прекрасно работает на
всех системах, кроме Windows 7 . Но мы можем воспользоваться тем, что это место не несёт
никакой смысловой нагрузки и просто перепрыгнуть его, перейдя в конец вм:

Рисунок 41. "Патч" ошибки

Это конечно нельзя назвать хорошим способом, но другого я пока не знаю. Узнаете, сообщите :-)

Обсуждение статьи: Взлом VMProtect защиты. Полная распаковка программы скриптом + доработка (Глава 2) >>>


При перепечатке ссылка на https://exelab.ru обязательна.



Видеокурс ВЗЛОМ