В сегодняшней статье продолжим анализ популярных инструментов для защиты программ. В этот раз будем обходить защиту протектора Obsidium. На примере приложения, использующего для защиты одну из последних версий Obsidium, попробуем проверить прочность протектора.
Еще по теме: Взлом программы защищенной протектором Enigma
Для тех кто не в теме, Obsidium, вместе с VMProtect и Themida считаются одними из самых надежных. Функционал тоже не хилый: виртуализация, антиотладка, обнаружение VM, защита памяти, проверка целостности, защита импорта, свой API для интеграции в пользовательскую программу и другие фишки, которые усложняют взлом программы.
Обход защиты Obsidium
Итак, у нас имеется триальная версия программы, которую DIE идентифицирует следующим образом:
1 |
Obsidium v1.5.4.x - [ v1.6.x.x - 1.x.x ] - Obsidium Software - www.obsidium.de *ACM , Overlay : FE4711... Nothing detected |
С первого взгляда очевидно, что загружать такое в дизассемблер совершенно бесполезно: код сильно сжат или зашифрован (на самом деле и то и другое). Секции пустые, из импорта имеются в наличии всего четыре функции. Попробуем сразу загрузить ее в отладчик.
Воспользуемся x64dbg, ибо в нем есть прекрасные плагины Scylla и Scylla Hide, которые нам очень пригодятся. К примеру, для того чтобы не палить дебаггер, в Scylla Hide существует специальный профиль, заточенный исключительно под Obsidium. Включаем его и начинаем трассировку.
Хождение по безумному коду протектора — довольно нудное занятие и вдобавок небезопасное, ибо Obsidium, как и другие «взрослые» защиты типа VMProtect, умеет контролировать время прохождения определенных участков кода внутри себя. Скрыть собственное присутствие нам не поможет никакая Scylla Hide. Вдобавок мы, по моему обыкновению, хотим все сделать максимально быстро и с минимумом затрат, поэтому просто позволим программе запуститься до появления на экране основного окна.
Программа превосходно запускается и даже прерывается — Scylla Hide прекрасно отрабатывает свое назначение. В этот момент мы видим расшифрованный и распакованный модуль, который попытаемся сдампить при помощи той же Scylla. Для этого останавливаемся где‑нибудь внутри кода основного модуля и относительно данного OEP делаем автопоиск IAT. После чего Get Imports выдает очень длинный список ошибок, но при этом все‑таки строит таблицу импорта.
Кропотливо вымарываем помеченные красными крестиками ошибочные ноды и дампим модуль. Вроде бы все хорошо, мы, кажется, получили чистый модуль со снятым протектором, но, к сожалению, с полпинка работает такое далеко не всегда. В частности, после данной процедуры наш модуль отказывается запускаться — при этом не выдает никакой ошибки, просто молча завершается.
При ближайшем рассмотрении становится понятно почему: точка входа найдена неверно, она указывает на совершенно левую пустую процедуру. Найти правильную точку входа, по идее, возможно (как будет показано ниже), но дело даже не в этом. В принципе, в некоторых случаях задачу можно уже считать решенной и начинать пить шампанское — к примеру, если модуль написан на дельфи, бейсике, дотнете и подобном. В этом случае он уже вполне годится для загрузки в инструмент реверс‑инжиниринга. Да что там говорить, даже обычный скомпилированный модуль пригоден для реверса — к примеру, можно найти место для инлайн‑патча и написать лоадер или разобрать алгоритм для создания кейгена.
Попробуем развить последнюю идею технически. Как я уже говорил, у модуля всего четыре импортируемые функции: GetModuleHandleA, MessageBoxA, RegOpenKeyExA и ImageList_Add. Для начала установим точку останова на GetModuleHandleA. Программа запускается, и точка останова даже отрабатывает пару раз, однако после этого отладчик зацикливается с бесконечными попытками исключения по ACCESS_VIOLATION. Защита явно спалила отладчик, и ScyllaHide уже не помогает.
Отрицательный результат не отбивает у нас охоту работать в этом направлении, и мы по старой памяти пробуем повторить этот трюк с ReadFile. На удивление, бряк несколько раз вполне эффективно срабатывает и не зацикливается. Смотрим внимательно на стек вызовов — первые несколько раз там содержится полная каша, однако в определенный момент появляется осмысленная последовательность вложенных вызовов, снизу начинающаяся c вызова потока нашей программы из BaseThreadInitThunk и заканчивающаяся сверху в недрах модулей user32 и gdi32full. Это наша программа впервые вызвала LpkDrawTextEx, а он полез за шрифтом на диск.
Внимательно изучаем расположенный в самом низу списка код процедуры, выполняющейся в этом потоке, и находим начало этой самой процедуры — 0xB634EC. Итак, мы нашли потенциальную точку входа и теперь у нас есть за что зацепиться в процессе отладки.
Перезапускаем программу, в окно дампа выводим состояние памяти по этому адресу. Как и следовало ожидать, память девственно чиста и заполнена нулями. Запускаем программу, снова останавливаясь на ReadFile. Буквально через пару бряков видим по адресу 0xB634EC готовый распакованный код. Радостно ставим на него бряк, убрав его с ReadFile для ясности. Жмем на продолжение, и вот мы останавливаемся на самом входе в распакованную и расшифрованную программу.
На тот случай, если мы решим написать загрузчик, мы нашли хорошее место для инъекции. Теперь было бы неплохо отыскать код для инлайн‑патча. По логике вещей, для контроля лицензии программа должна вызывать функции Obsidium прямо изнутри себя, причем недалеко от начала загрузки.
Начинаем пошагово трассировать программу и буквально через несколько команд натыкаемся на подозрительную процедуру 0xB62030, вызывающую внутри себя игнорируемую попытку исключения примерно в таком контексте:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
02DC091A | 0F0B | ud2 | 02DC091C | 0F0B | ud2 | 02DC091E | EB 01 | jmp 2DC0921 | 02DC0920 | 70 EB | jo 2DC090D | 02DC0922 | 0115 F7F0EB05 | add dword ptr ds:[5EBF0F7],edx | 02DC0928 | D2A4A7 872CEB1E | shl byte ptr ds:[edi+1EEB2C87],cl | 02DC092F | EB 04 | jmp 2DC0935 | 02DC0931 | BD A4FCBFEB | mov ebp,EBBFFCA4 | 02DC0936 | 05 D21DA654 | add eax,54A61DD2 | 02DC093B | 5D | pop ebp | 02DC093C | 8B5424 30 | mov edx,dword ptr ss:[esp+30] | 02DC0940 | EB 03 | jmp 2DC0945 | 02DC0942 | A9 CBF2EBBA | test eax,BAEBF2CB | 02DC0947 | EB 04 | jmp 2DC094D | 02DC0949 | A2 29F963EB | mov byte ptr ds:[EB63F929],al | 02DC094E | 03D8 | add ebx,eax | 02DC0950 | A2 55EBE1EB | mov byte ptr ds:[EBE1EB55],al | |
Это чертовски похоже на код, на котором отладчик периодически спотыкался, пока мы медленно, но верно приближались к распаковке программы. Сразу за вызовом этой процедуры мы видим код следующего содержания:
1 2 3 4 |
00B63567 | 8B15 ACAEC000 | mov edx,dword ptr ds:[C0AEAC] | 00B6356D | 8B12 | mov edx,dword ptr ds:[edx] | 00B6356F | 8B92 1C030000 | mov edx,dword ptr ds:[edx+31C] | 00B63575 | 8B92 94010000 | mov edx,dword ptr ds:[edx+194] | [edx+194]:L"Unregistered trial version\r\nYou have 30 day(s) left" |
Фактически у нас не осталось никаких сомнений — именно эта конкретная процедура 0xB62030 вызывает проверку лицензии Obsidium и заполняет информацией о ней структуру по адресу, на который указывает ds:[C0AEAC].
Итак, наша исходная задача — малой кровью полностью снять протектор с этого модуля — была с самого начала обречена: защита интегрирована в образ. В модуле присутствуют ссылки и вызовы за его пределы, более того, эти ссылки инициализируются до начала основного потока.
Если вдумчиво покопать в IDA сдампленную программу, таких ссылок на Obsidium можно найти в коде достаточно много в местах проверки лицензии. Каждый такой вызов уникален (лично я с ходу не нашел повторяющихся, даже если они выполняют одинаковые функции), его код виртуализован и снабжен собственным антиотладчиком.
Конечно, можно повозиться и освободить код от всех этих вызовов (хотя это тоже не всегда достижимо, ибо теоретически Obsidium умеет виртуализировать отмеченные пользователем блоки основного кода программы), но проще все‑таки сделать лоадер и инлайн‑патч, кому как больше нравится.
Однако я обещал раскрыть метод обхода защиты для самых ленивых, безо всякого отладчика, реверса и ковыряния кода. Тот, кто читал мои статьи о Enigma, наверное, уже догадался, о чем речь. А речь идет о сбросе триала Obsidium внешними средствами — при помощи только ProcMon, RegEdit и умелых рук.
Допустим, у нас есть защищенная Obsibium программа, работающая ограниченный период, который очень хочется продлить. Желательно навечно. Однако лезть для этого в отладчик и снимать защиту полностью нам лень. Привычно запускаем ProcMon и изучаем лог обращений программы к реестру при загрузке.
Естественно, мгновенно нашему взору предстанет огромная простыня, размер которой повергает в уныние. Однако, на счастье, авторы Obsidium (так же как и авторы Enigma) особой фантазией не отличаются, и по поиску Obsidium тут же находится обращение к ключу (данные GUID уникальны для каждой программы):
1 |
\HKEY_CURRENT_USER\SOFTWARE\Obsidium\{XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX} |
Без особой надежды удаляем ключ из реестра и перезапускаем программу — естественно, сброса триала не происходит. Но, как я уже говорил, создатели этих защит особой фантазией не отличаются, поэтому предполагаем, что они подкрепляют реестровую проверку каким‑то хитро спрятанным файликом на диске. Начинаем изучать лог обращения к файловой системе, и тут нас ждет сюрприз!
Авторы Enigma оказались даже более оригинальными: они, как ты помнишь, хранили информацию о регистрации в файле с непроизносимым названием во временной системной папке. Авторы Obsidium для этого завели специальную подпапочку имени себя в подкаталоге RoamingAppData текущего пользователя. Путь к файлу выглядит примерно так (GUID тот же, что и в реестре).:
1 |
C:/Users//AppData/Roaming/Obsidium/{XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX} |
Удаляем этот файлик вместе с веткой реестра — бинго, триал сброшен!
Заключение
Как видишь, далеко не все виды защиты исполняемых файлов обходятся лихим кавалерийским наскоком: иногда разработчики стараются максимально усложнить жизнь исследователям и придумывают хитроумные способы антиотладки. Однако на любой хитрый болт, как известно, отыщется гайка с левой резьбой — сложность протектора иногда с лихвой компенсируется шаблонным подходом к работе с реестром и файловой системой. Чем понимающие люди при необходимости вполне могут воспользоваться.
Еще по теме: Отладка программ с помощью WinDbg