Далекий 1998 год. В компьютерный мир ворвалась новая защита от пиратства, которая спустя некоторое время станет одной из самых узнаваемых и известных DRM на свете. GTA IV, серия Command & Conquer, BioShock, The Witcher, WarCraft III, FarCry 2, TestDrive Unlimited, Gothic 4, Spore, Sims, — вот лишь неполный список бестселлеров, чьи OEP и CRC от посягательства пиратов пытался защитить автор SecuROM Рейнгард Блаукович. Он в поте лица работал над своим творением на компанию Sony DADC. В 2014 году с SecuROM будет связано одно из самых позорных и страшных поражений в истории DRM.

Рейнгард Блаукович как бы играет в пинг-понг с хакерами. И проигрывает!)
Рейнгард Блаукович как бы играет в пинг-понг с хакерами. И проигрывает!)
 

Как все началось

За окном мирно падал снег, начинался 2015 год. Блуждая по просторам интернета, я неожиданно для себя нашел на одном из хакерских форумов тему про взлом SecuROM 7 — именно седьмая и восьмая версии стали наиболее «любимыми». Тема датировалась 2007 годом и, судя по всему, эта версия только что появилась. В конце поста его автор сетовал на сложность защиты и говорил, что её нереально взломать и задавался вопросом, когда это сделают. Дочитав весь топик, мне оставалось только вскричать: «Да! Разобрать по винтикам мощную и непробиваемую защиту более чем реально!» Успешный разбор виртуальной машины, запуск игры в обход всей логики проверки компакт-диска без любимых «алкоголей» и Daemon Tools, кейген для онлайновой активации (PA Unlock), — всё это оказалось реально.

 

Виртуальная математика

INFO


Подробнее об устройстве виртуальной машины SecuROM читай в статье «Тибериумный реверсинг» («Хакер» №156 за апрель 2012 года).

Мне хорошо запомнилось, как в «Искусстве дизассемблирования» Крис Касперски описывал преобразование в байт-код как наиболее стойкую и мощную защиту от реверсинга. На ее преодоление хакеру потребуется чуть ли не вся жизнь и даже немного больше. Что до SecuROM 7.33.017, то на понимание принципов работы и разбор структуры виртуальной машины у меня ушло две полных недели, а уже через месяц я готов был написать декомпилятор.

Что для этого нужно? Всего лишь научиться мыслить так, как «мыслит» виртуальная машина, то есть рассматривать совокупность ассемблерного кода на еще более высоком уровне, чем какой нибудь С++. С другой стороны, если бы не запрятанные в коде строки ASCII с шутками разработчика, я бы мог подумать, что каждый раз ломаю одну и ту же виртуальную машину.

В двух словах, VM представляет собой совокупность независимых друг от друга кусков кода. Привычное слово «функция» к хендлам неприменимо хотя бы потому, что машинный стек здесь не играет основополагающей роли, как и регистры процессора (EAX, ECX, ESP и так далее). Если трассировать хендлы, склыдвается впечатление, что мы прыгаем по островкам. Роли хендлов бывают разные:

  • Стартовые хендлы. Инициализируют виртуальные регистры VM, вытаскивают ссылку на ленту p-code. Выполняются в самом начале.
  • Обычные хендлы. Выполняют примитивные операции — перекладывают данные, выполняют простые операции вроде XOR. 95% работы VM делают эти ребята.
  • Служебные хендлы. Отвечают за вызовы внешних функций, которые напрямую не относятся к VM, а также за их обработку и упаковку обратно в обменник VM. Восстанавливают реальное состояние стека и регистров CPU.
  • Хендлы выхода из VM. Их нужно искать в первую очередь. Знаем начало, нашли конец, значит цепочка вскрыта.

Вместо физических регистров CPU, в VM используются свои виртуальные регистры — этакая спираль, вокруг которой танцуют хендлы. В обычном понимании это главная структура (typedef struct VM_registers), в которой прописаны ключевые переменные для функционирования VM. Примыкает к этой структуре специально выделенная зона в памяти — она отведена под обмен данными между «островками». И, наконец, лента p-code, которая в каждом индивидуальном случае служит пошаговой инструкцией: на какой примитив прыгнуть, из какой ячейки в зоне обменна данными (или памяти) вытаскивать информацию. В ленту p-code также пихают всякие зашифрованные данные: офсеты, адреса, константы и адреса OEP (Original Entry Point), которые восстанавливаются в защищаемой программе по ходу исполнения.

Шутки Блауковича

Разбирая виртуальную машину, нельзя не заметить строки ASCII, которые Блаукович преподносит как пасхальные яйца. Только ради этого я попереломал два десятка виртуальных машин 🙂 «Nobody move, nobody gets hurt» , «< space for rent >», «You Are Now In A Restricted Area», «yates stilL Here kinda Ooooh», «Masses against classes», «cut my life into pieces (:», «FiSHy WiSHy», «ere, wuts goin on»… это всё оттуда. Как и анекдот про улитку в баре: «A snail walks into a bar and the barman tells him theres…» («Улитка входит в бар, и бармен говорит ей, что есть…» — продолжение ищите в восьмой версии). Помимо веселья, от этих строк есть и реальная польза — можно точно определить версию VM, которая, к слову, редко менялась, даже если статическая версия защиты была другой.

Одна из особенностей SecuROM — это шаблонность и предсказуемость в работе с виртуальными регистрами. Во всех хендлах, кроме выхода из VM, идут стандартные шаблонные действия с виртуальными регистрами (назовем их «Главным хранилищем»), которые выполнялись в строгой последовательности. Чаще всего виртуализируется инструкция MOV [r32], r32. Зная это, можно, наплевав на обфускацию, составить карту работы всех хендлов, и вуаля: 60% работы по девиртуализации выполнено!

Есть и еще более простая лазейка: в некоторых случаях по сдвигу указателя ленты p-code ADD [+4], Y в конце можно без анализа всего хендла точно угадать его роль! Там, где, для рабочих хендлов Y = 4 (один управляющий DWORD), сдвиг составляет 8 байт (управляющий DWORD + DWORD данных); в остальных же случаях (есть нечетные байты) сдвиг «кривой», это свидетельствует об отсутствии значимых операций на данном примитиве.

Хранилище дельта-смещений всех 255 хендлов («Хранилище №1,5» для простоты) – наш надежный навигатор и отличное место для установки перехватчиков, «виртуальная» таблица экспорта. Данные в нем шифруются динамически, перед началом самого раннего входа в VM. Шифрование производится на основе CPUID. Это значит, что разработчики сделали аппаратную привязку: если мы снимем дамп и прикрутим к нему VM, то работать NoCD/NoDVD будет только на нашей машине. Но все так радужно лишь в уме Блауковича. На самом же деле с такой замечательной структурой, как открытая таблица, где любезно собраны все адреса примитивов, нам не важно, зашифровано что-то или нет. Глупо не воспользоваться таким огромным подарком — нужно лишь разобрать каждый из 255 хендлов, руководствуясь находкой из предыдущего абзаца.

К сожалению, хранилище дельта-смещений есть только в седьмой версии SecuROM. Позже до Sony DADC дошло, почему взломщики так свободно и издевательски гуляют по виртуальной машине — наше «Хранилище №1,5» уничтожили… и лучше бы они этого не делали! 🙂 Конечно, жизнь немного усложнилась – на руках теперь нет точного местоположения всех хендлов, надо трассировать всю машину, чтобы откопать их адреса. Но нет худа без добра: Рейнгард Блаукович снова невольно помог в трассировке, когда добавил в восьмую версию так называемый «коллектор», куда стекается все управление от всех хендлов, а заодно убрал обфускацию, понадеявшись на сплетение безусловных переходов. В итоге, контролировать виртуальную машину можно даже по отдельным инструкциям в хендле, а не по целым примитивам, как в седьмой серии.

PUSHFD
XCHG DWORD PTR DS:[EBX+0C],EAX
ADD DWORD PTR DS:[EBX+20],EAX
XCHG DWORD PTR DS:[EBX+0C],EAX
POPFD
PUSH DWORD PTR DS:[EBX+20]
JMP ret_label
ret_label : RETN

Область обмена данными между хендлами – «план Б». Это самая лакомая и моя любимая часть виртуальной машины SecuROM 7-8. Именно благодаря ней не обязательно копаться в коде VM и вникать в суть интерпретации байт-кода. Данные — ключ ко всему! Для SecuROM 7 абсолютно реально написать простой анализатор данных, которые машинка бережно хранит в области обмена данными и с большой долей вероятности вычислить всю цепочку виртуализированных функций. Это самый универсальный метод борьбы в случае I и II типа вызова машины. Просто фантастика!

В SecuROM 8 для области обмена данными добавили флаг EXECUTE, и VM начала туда добавлять важный исполняемый код. Это потребовало усовершенствовать анализатор. Помимо этого различий между виртуальной машиной SecuROM 7 и 8 не так много.

В SecuROM 7 широко использовалась обфускация кода. Только вот самые ходовые хендлы в виртуальной машине обфускации не подвергались, да и сама обфускация оставляла желать лучшего. Блаукович понял это и убрал её из восьмой версии, заменив на паутину из безусловных переходов (по аналогии с SafeDisk 4.5).

Веселые особенности SecuROM

  • Исходный код SecuROM PA онлайновой активации байт в байт идентичен абсолютно во всех играх (различаются разве что адреса и специальные булевы константы).
  • Большинство защищенных игр почему-то имеют одинаковый публичный и приватный ключи RSA для request code.
  • В SecuROM PA есть некий аналог sprintf — этакий штаб онлайновой активации, откуда можно скрытно качать полезную информацию целыми ASCII-строками. Дело в том, что если внедрить туда программную точку останова, то можно без лишних телодвижений установить, что в данный момент делает защита.
  • По аналогии с патчингом модуля проверки оригинального диска, всегда можно спокойно пропатчить условные переходы после всех трех этапов нативной проверки unlock code. И плевать, что UC будет «12345-5678-…» - всё равно «Activation Success!»
 

Виртуальная геометрия

Компакт-диски. Не думай, что речь сейчас пойдет об эмуляции, Alcohol 120%, Daemon Tools, геометрии диска, секторах и блоках. Ничего подобного, всё это — прошлый век!

Уже в 2007 году существовало как минимум две дыры, которые позволяли без всяких «алкоголей», настоящих лицензионных дисков и их мини-образов пробивать проверку диска в приводе. Первую быстро закрыли (магический бит), для обхода второй был специальный скрипт для OllyDbg (трюк с управляющими байтами и мгновенным выходом на OEP), который применим только для седьмой версии австрийской защиты.

Самый громкий успех имела секретная третья дырища, подобраться к которой можно только при полном реверсинге модуля проверки диска. Этот метод годится для абсолютно любого билда в диапазоне от седьмой до самой последней восьмой версии. Вот краткая методика поиска нужных инструкций и перезаписи важных байтов.

Шаг 1. Для начала нам понадобится обычный физический привод (можно и виртуальный, но тогда сами скрывайте свой Daemon Tools от негодующего Рейнгарда) и любой диск CD или DVD.

Шаг 2. Вставляем его в дисковод и запускаем игрушку (к примеру, GTA IV или Brave) под OllyDbg. Естественно, SecuROM завопит «Wrong disc inserted. Please insert the original CD/DVD. Please have a look at http://www.securom.com/message.asp?m=wrongdisc for further, more detailed information». Но, оказывается, это несложно лечится! В двух словах, проверка диска защитой разбита на две части: сигнатурная и геометрическая (углы между секторами, синусы, дорожки).

Шаг 3. Сигнатурная проверка – Уровень 1. Разводка 1. Ищем следующую последовательность байтов: 83E0 1F 3C 1F5 (то есть две инструкции) и ставим первый Hardware Breakpoint.

AND EAX,0000001F
CMP AL,1F

В AL/EAX заносим 0x1F (31). Правильно угаданное местоположение и патчинг только этой разводки заставляет SecuROM выплюнуть сообщение: «Cannot authenticate the original disc. Your disc may require a different software version. Please contact the manufacturer of your application or visit http://www.securom.com/message.asp?m=disc for further information».

Шаг 4. Сигнатурная проверка – Уровень 1. Разводка 2. Ищем последовательность 84 98 C0 07 00 00 и ставим второй Hardware Breakpoint.

TEST BYTE PTR DS:[EAX+7C0],BL //BL = 1 инструкции

В EAX+0x7C0 может находиться любое число от 2 до 10 (в идеале там должна быть 9). Это какие-то специальные байты структуры, которая задает число проходов. Чем меньше число – тем меньше проходов и быстрее проходит сигнатурная проверка.

Шаг 5. Сигнатурная проверка – Уровень 1. Разводка 3. Возможны два варианта.

* В седьмой версии ищем последовательность `80BF F2070000`, инструкция `CMP BYTE PTR DS:[EDI+7F2],0`

* В восьмой версии ищем последовательность `80 78 02 00`, инструкция `CMP BYTE PTR DS:[EAX+2],0`

Ставим третий Hardware Breakpoint.

В EDI+7F2/EAX+2 находится множитель для интервала проверки. В идеале здесь должна стоять двойка. По факту – может быть любое ненулевое число, но рекомендую указывать от 5 до 1, чтобы попасть в интервал с первого раза.

Шаг 6. Отдельно! Ручное исправление ошибки 2fix. Если не исправить, то процесс аварийно завершится (SecuROM неверно обсчитывает свои данные с диска)! Ищем конструкцию, в [CONST](АДРЕС) для левого диска всегда будет 0. Тут придется немного погадать.

CMP BYTE PTR DS:[CONST],0
PUSHFD
PUSH CONST
JBE SHORT ADDRESS

Ставим четвертый Hardware Breakpoint. В [CONST] в идеале должна быть двойка. Отсюда и название — 2fix. Если все сделано правильно, то запустится вторая часть — геометрическая проверка: курсор сменится на крутящийся диск (temp.ani в директории %TEMP). После смены обратно начинается контрольный подсчет попадания в заданный интервал. Внутри проверки геометрии (когда курсор становится диском) важных разводок нет.

Шаг 7. Уровень 2. Найти и запатчить три раза разводки вида:

FLD QWORD PTR DS:[CONST]
FSUB QWORD PTR DS:[CONST]
FMUL QWORD PTR DS:[CONST]
FCOMP QWORD PTR DS:[CONST]
FSTSW AX
TEST AH,05

Адрес в FLD QWORD PTR DS:[адрес] всегда один и тот же во всех трех. Находим и ставим точку останова на память. Патчится результирующий регистр AH:

 Для geometry-1   Для geometry-2   Для geometry-3 
Значение старшего регистра AH 0 1 1

Если у тебя есть настоящий лицензионный диск к любой другой игрушке, защищенной SecuROM, то тут всё еще проще: проходим только «Уровень 1. Разводка 1» (сигнатуры) и «Уровень 2». Причем в некоторых случаях может повезти, и читерить с коррекцией интервалов под геометрию вообще не потребуется.

В некоторых играх после проверки геометрии идет проверка DFA/DFE (код ошибки >= 9000). Если тут все в порядке, тогда запускается вторичная полная проверка наличия отладчика, OEP, а затем и основной код игры.

Вот так позорно терпит поражение самая навороченная в мире защита компакт-дисков на основе геометрии. Остается ликовать или задаваться вопросом о том, как игровые компании дали себя так обмануть. Вот только ответа никто не даст — как минимум потому, что SecuROM больше не существует. Справедливости ради, скажу, что у Tages и SolidShield все было еще печальнее.

 

Виртуальная криптография или ultra-kill

«Ну и фиг с ними, с лазерными кругляшками и геометрией. Проиграли и ладно!», — подумал Блаукович и родил на свет новую фичу — онлайновую активацию, известную также как SecuROM PA Unlock (а потом еще и DFA, но ту слишком быстро научились эмулировать, банально используя API самого же SecuROM).

Есть мнение, что для онлайновой активации нельзя сделать гейген. Ерунда! Всю цепочку работы этой технологии успешно отреверсили и даже восстановили алгоритмы работы серверов активации Sony.

Игрушка Ys SEVEN использует самое красивое оформление онлайновой активации, опираясь на стандартные возможности paul.dll
Игрушка Ys SEVEN использует самое красивое оформление онлайновой активации, опираясь на стандартные возможности paul.dll

Суммируя информацию, которой я обладаю после взлома PA Unlock, могу сказать, что в целом есть два подхода к взлому онлайн-активации SecuROM: со знанием криптографии и без знания криптографии. Строго говоря, есть еще третий – использование описанной выше уязвимости, которая заставляет SecuROM мгновенно распаковывать основной код и передавать управление на OEP. Это позволяет обойти сразу все модули защиты, но работает только с версиями 7.3x и 7.4x.

Начнем с последнего подхода. Допустим, мы никогда не слышали о DES и RSA и нам абсолютно все равно, какая структура у unlock code, и что там вычисляется внутри с помощью пакета OpenSSL (на самом деле, не все криптографические функции модуля проверки онлайн-активации оттуда). У нас есть легендарный paul.dll, обертка (wrapper) для основного исполняемого файла. В paul.dll находится экспортная процедура drm_pagui_doit, которая сначала проверяет состояние активации (вызывает функции, прописанные в главном exe) и в зависимости от ответа предлагает пройти её, либо возвращает единицу в случае PA_ACTIVATION_SUCCESS.

INFO


Зарубежный издатель BigFish использует SecuROM в режиме Trial. Для Trial mode существует универсальный эксплоит, который позволяет обходить прямую верификацию на сервере и задействовать Manual Activation с последующей генерацией unlock code в кейгене. Сделать это просто: нужно заменить новые версии paul.dll (v2.x) на древние 1.x. Или берем и правим EAX = 1 после вызова doit_check_pa — вот и весь пробный режим!

Собственно, последней фичей народ и воспользовался: достаточно пропатчить paul.dll, и всё готово. С внутренним устройством онлайновой активации практически никто разбираться не стал. Но время шло — вышла восьмая версия SecuROM. «И конечно Sony DADC приложила мощные усилия, чтобы прекратить эту вакханалию?» — спросишь ты. На первый взгляд, да: с восьмой версией нехитрое ковыряние paul.dll не приведет к желаемому результату. Но на самом деле в Sony всего лишь чуть-чуть улучшили старую технологию, дописав несколько строк кода. В новой версии до вызова paul.dll введены две контрольные переменные, которые инициализируются с дефолтными значениями. Нужно лишь откопать их в толще обфусцированного кода.

0120BF24   MOV DWORD PTR DS:[2CB87FC],7F83983E 
0120BF2E   MOV DWORD PTR DS:[2CB87F8],0FEAE7DB

Как и в седьмой версии, paul.dll вызывает функцию проверки состояния онлайновой активации doit_check_pa, которая теперь перед тем, как отдать значение, создает его зашифрованную копию в первой переменной. Данный код виртуализирован, однако его очень просто вывести на чистую воду с помощью нашего SPR_I.

#SecuROM_VM_start
MOV EBX, EAX
XOR EBX, 0xBDDE699D
MOV [0x2CB87F8], EBX
POP EBX
RET EAX
#SecuROM_VM_end
SPR_I – охотник за виртуальными машинами SecuROM
SPR_I – охотник за виртуальными машинами SecuROM

Далее оставляем paul.dll в покое и проверяем результат его работы. Если PA_ACTIVATION_SUCCESS (return 1), тогда SecuROM направляет процессор на вторую копию проверки активации (drm_pagui_doit), только уже без участия paul.dll — все необходимое продублировано в исполняемом файле. Разница лишь в том, что оставшаяся переменная используется для хранения ответа-дубля (MOV [0x2CB87FC], EBX). Дальше же всё очевидно – имеем две переменных с ответами от двух проверок, остаётся разве что дописать конечную проверку.

0122F711   CMP DWORD PTR DS:[2CB87FC],7F83983E      
0122F71B   PUSHFD
0122F71C   PUSH 2919
0122F721   JE SHORT 0122F734
…
0122F74D   CMP DWORD PTR DS:[2CB87F8],0FEAE7DB
0122F757   PUSHFD
0122F758   PUSHFD
…
0122F8B2   MOV EAX,-35
0122F8B7   MOV EAX,DWORD PTR DS:[EAX+2CB8831] // == 2CB87FC
0122F8BD   CMP EAX,DWORD PTR DS:[2CB87F8]
0122F8C3   PUSHFD
0122F8C4   PUSH 3415
0122F8C9   JE SHORT 0122F8DD

Этот код требует некоторых пояснений. Пройти SecuROM PA можно в двух случаях: когда обе контрольные переменные имеют присвоенное по умолчанию значение или равны между собой. Все остальные ветки исполнения кода вводят SecuROM в ступор, и процесс банально завершается с кодом -1 (FAIL!). Обрати внимание, что на таких скрытых переменных могут базироваться так называемые «триггеры» — привязки частей исходного кода игры. Очевидно, что в данном случае проблемы могут начаться уже после успешного восстановления дампа.

Итак, мы разобрались с самым простым вариантом вскрытия SecuROM PA Unlock. Но ведь мы не ищем легких путей? Если рубить, то рубить на корню! Вооружаемся томиком Шнаера, плагином IDA Signsrch и HexRays (IDA Pro, естественно, прилагается в комплекте) и начинаем делать первые шаги в написании генератора unlock code. Перед тем, как выбрать главное направление для удара и погрузится в нативный режим SecuROM PA, предлагаю провести анализ имеющихся разведданных.

Ты наверняка хоть раз видел, как выглядит окно онлайновой активации SecuROM. Здесь есть: unlock request code (код-запрос); serial number (серийный номер); unlock code (код-ответ или UC); Первый всегда генерируется при показе окошка активации, причем буквы и цифры всегда разные даже в пределах одной машины. На самом деле это дешевый обман от Рейнгарда — на одной и том же железе код всегда одинаков. Рандомизации как таковой нет – иллюзия создается с помощью kernel32.GetSystemTimeAsFileTime(pFiletime) из WinAPI и жестко прописанных констант.

unsigned __int64 v1; // ST00_8@1
unsigned __int64 v2; // qax@1
struct _FILETIME SystemTimeAsFileTime; // [sp+0h] [bp-8h]@1

GetSystemTimeAsFileTime(&SystemTimeAsFileTime);
HIDWORD(v1) = ((*(_QWORD *)&SystemTimeAsFileTime + 0x2AC18000ui64) >> 0x20u) - 0x19DB1DF;
LODWORD(v1) = SystemTimeAsFileTime.dwLowDateTime + 0x2AC18000;
v2 = v1 / 0x989680;
if ( a1 )
    *(_DWORD *)a1 = v2;
return v2;

Данные для раскрытия истинного request code заложены в его структуру – собственно, поэтому он длиннее на пять символов, чем код-ответ. Но это не главное. Прежде всего, нетрудно догадаться, что request code наверняка содержит HWID (привязка к железу).

Из неочевидных данных подскажу, что код-запрос содержит в себе идентификатор игрушки в виде хеша MD5 персонального DES ключа (для каждой игрушки). На первый взгляд, отреверсенные алгоритмы генерации кода-запроса нам ничем не помогут, ведь мы можем сами сгенерировать их миллионами. Однако это не так! Фокус в том, что оттуда можно выдрать половину кода для кейгена 🙂

По странному совпадению алгоритм генерации кода-запроса содержит обратные функции, которые используются при проверке полученного unlock code с сервера активации Sony. Впрочем, утюжить «Оллидебагом» надо, прежде всего, эту самую функцию проверки unlock code (кода-ответа). И прежде чем мы приступим к её реверсингу, буквально пару слов о serial number.

Незнание серийного номера – это главное препятствие в получении unlock code на сайте securom.com. Как выяснилось, этот номер служит только для сверки по внутренней базе SecuROM. В структуре unlock code для s/n отведен WORD под его хеш, который на защищенной машине никак не используется. Лично до меня быстро дошло, что вместо хеша serial number можно писать что угодно – в валидном виде s/n нам не понадобится. Ситуация аналогична той, что была с лицензионным диском.

INFO


Официальная страница онлайн-активации SecuROM

Согласно программе импортозамещения, int соорудил нашу страничку SecuROM PA Unlock page как у Sony.

Итак, начинаем реверсить процедуру проверки unlock code (UC). Длина UC равна 0x2f (47) символов. За этим количеством шифрованных байтов скрывается сердце алгоритма и наш ключ к победе. Структура UC состоит из двух частей: служебная часть и HWID-часть. Следуя структуре UC, защита ведет с ним работу в три нативных уровня: распаковка с разбором служебной части, проверка введенного unlock code в своем черном списке и расшифровка HWID с последующей сверкой. Что представляет собой служебная часть, показано на картинке, а исходные коды можно найти на сайте eXeL@B. Если ты знаком с С/С++, то разобраться не составит труда. Дальше функция за функцией рассмотрим работу SecuROM с unlock code.

 

Первый этап – DES_free_key и DES_private_key

Группа из трех функций, которые выполняются последовательно. Каждая функция имеет три аргумента – входной буфер, выходной буфер и длина входного массива. Они выполняют распаковку переходной структуры unlock code. Я называю этот код «шелухой».

Очищенный от «шелухи» UC (а это 25 байтов) передается в главную и последнюю на данном этапе функцию (я называю ее «Level 4»), где криптографическая составляющая представлена одним стандартным алгоритмом DES с двумя типами ключей — свободный (DES_free_key) и индивидуальный (DES_private). Отдельно в этой функции используется самопальная разновидность DES (DES_warp) со встроенным ключом – как оказалось, это и есть самая запутанная часть онлайновой активации, несмотря на то, что несложно заставить работать этот алгоритм в режиме «шифрования», без разбора его внутренностей.

  1. Исследуя длинный цикл do-while, где используются эти алгоритмы, я пришел к выводу, что 25 байтов UC могут быть зашифрованы любым из ста ключей DES_free_key, то есть мы имеем жестко вшитый набор ключей. Если ни один ключ DES из набора не подошел, то добро пожаловать к выходу. Условие продолжения работы с расшифрованным unlock code определяет самый первый байт после работы des_decrypt — это CRC от всей правой части (24 байта).
  2. Если контрольная сумма правой части сходится с этим байтом, тогда SecuROM допускает, что unlock code настоящий. Теперь алгоритм хочет убедиться, что UC сгенерирован именно для этой игрушки. Еще один слой шифрования убирается с помощью упомянутой выше DES_warp для 21 байта. И тут в дело вступает MD5. Расчехлять GPGPU для брутфорса не требуется — это обычный digest. Защита обращается к первому аргументу функции «Level 4», где лежит заготовка для индивидуального ключа DES (DES_private_key) – три последовательности по 16 байтов (то есть всего 48), причем для второй и третьей последовательности первые восемь байтов всегда одинаковые: 0xE8, 0x96, 0xD4, 0xE1, 0xBD, 0xFC, 0x0E, 0x37 (заготовка хорошо видна в окне Hex Dump в OllyDbg). Считаем хеш MD5 и сравниваем с прописанным в unlock code (длина — два байта).
  3. Если хеши совпадают, то SecuROM поймет, что индивидуальный ключ (DES_private_key) подходит. Первая последовательность при помощи XOR объединяется со второй и третьей. Получается шестнадцать байтов, после чего первая половина ксорится со второй. В итоге получается DES_private_key в дистиллированном виде – 56 бит (собственно, для получения ключа из свободного набора выполняется эта же банальная операция). Дальше остается только выполнить des_decrypt с этим ключом.
  4. Расшифрованные 25 байтов копируются в буфер (второй аргумент) с затиранием переданных зашифрованных 25 байтов (от «шелухи»). Другими словами, входной буфер используется и как выходной.

Итак, unlock code с его полностью расшифрованной служебной частью у нас на руках. Отдышимся и проанализируем некоторые забавные детали работы SecuROM на первом этапе.

Прежде всего, я обратил внимание на то, что заготовка DES_primary_key (эти 48 байтов) представлена в реестре Windows. Ее можно встретить в HKEY_CURRENT_USER\Software\DSS\Product Activation. Значение в виде кучи непонятных циферок и буковок — это индивидуальные ключи DES для каждой игры, защищенной SecuROM PA.

Казалось, вроде бы очевидно, что вместо названия игрушки можно прописать её DES ключ. Однако я не поленился узнать, откуда функция «Level 4» получает 48 байтов заготовки. К моему удивлению, этот путь оказался на порядок тернистей, чем исследование всего остального процесса проверки UC – судя по всему, Рейнгард боялся, что индивидуальные ключи DES будут подменять при наличии хотя бы одного валидного unlock code (такой подход был бы уместным при условии динамической подмены HWID). Несмотря на это, я не вижу никакого смысла навешивать такое количество брони на этот участок – всё равно в самом конце разработчики сами отдают на руки DES_private_key.

Следующим под раздачу попадает уже элемент из самой структуры UC: LOCK-байт и его два байта, которые означают количественные характеристики блокировки. LOCK-байт может принимать значения от 0 до 4. В случае нуля unlock code не несет никаких ограничений на «железо» и чаще всего оказывается действительно сброшен. В случае значений от единицы до тройки появляется одно из следующих ограничений: ENDDATE, NUMBERDAYS, NUMBERLAUNCHES и PLAYTIME. Сломать все три блокировки не составляет труда — берем и нагло сбрасываем все три байта, заменяя нулями, в только что расшифрованной структуре unlock code. Теперь при каждом запуске SecuROM будет считать, что всё в порядке.

 

Второй этап – черный список

Не забыл, что существует еще и процедура отзыва введенного unlock code и s/n? Она называется revoke и не дает использовать один и тот же unlock code более трех раз. Только что расшифрованный unlock code длиной 25 байтов проверяется на присутствие в черном списке, который хранится в реестре (HKCU\Software\DSS\Product Activation плюс заготовка DES_private_key).

Получается, что произвести локальный revoke (очистить black-list) можно своими руками без помощи Sony – просто берём regedit и сносим нужную ветку реестра. В особых случаях вооружаемся руссиновским RegDelNull.exe и крушим HKEY_CURRENT_USER\Software\SecuROM с его нуль-терминантом в конце веток !CAUTION! NEVER DELETE OR CHANGE ANY KEY и License information. Это простой и эффективный способ получения виртуального ключика для третьего нативного уровня исследования SecuROM PA.

 

Третий этап – RSA

Прежде всего, честно скажу, что специально не буду раскрывать некоторые детали работы последнего алгоритма. Иначе уж слишком просто, получается, отдать весь код кейгена в руки общественности. Намекну только, что кроме RSA здесь учувствуют и другие знакомые по прошлым пунктам алгоритмы.

80_PA – кейген, взламывающий всё, начиная от Bioshock и заканчивая самыми последними играми, которые защищены SecuROM PA
80_PA – кейген, взламывающий всё, начиная от Bioshock и заканчивая самыми последними играми, которые защищены SecuROM PA

RSA здесь представлен в стандартном исполнении mod. По сравнению с ним DES нервно курит в «Level 4»: SecuROM сам нам дарит ключи DES, и никакого фокуса здесь нет. Несмотря на то, что весь третий этап работы с HWID (в SecuROM, кстати, используется другая аббревиатура – IMEI) разработчики попытались максимально оградить от чужих глаз с помощью VM и сильной обфускации.

Я установил своеобразный мировой рекорд — весь рейнгардовский забор был снесен чуть более чем за четыре минуты. Аппаратные точки останова и SPR I сделали своё дело! Правда, я не сразу понял, что передо мной RSA, а когда понял, стало очевидно, что закрытый ключ хранится на сервере Sony, и мне его не достать. Свой HWID не получится зашифровать, и это казалось окончательным тупиком. Но не тут-то было!

Концовка внезапно обернулась одним из самых немыслимых подарков, который Sony DADC сделала тем, кто отважился дойти до конца в исследовании Product Activation. Прежде всего, я обратил внимание на «открытую экспоненту» (Public Exponent), которая равна 0x10001h.

Смотрим длину, которая остается под зашифрованный HWID от общей структуры unlock code. Выкинув служебную часть и последний байт (RSA_copy_lenght), получаем 15 байтов (умножаем на 8). Результат навел меня на определенные мысли. Я вставил модуль N в RSA Tool by tE (первое, что попалось под руку), факторизовал N и вычислил приватную экспоненту D.

Не веря в полученный результат загружаю RSATool2v17.exe в отладчик, нахожу реверсингом функцию rsa_encrypt и вручную подставляю значения – шифрованные байты HWID совпадают! Далее, используя bdModExp(c, m, e, n) в сорцах BigDigits еще раз проверяю полученный результат. Все указывает на то, что благодаря неудачно выбранной длине ключа RSA и простых множителей, несколько минут назад мы взломали онлайновую активацию SecuROM.

 

Happy End

Вздохнув, я размечтался, что неплохо было бы отправиться на машине времени в тот год, когда вышла игра BioShock — она впервые использовала SecuROM PA. Какая шикарная была бы возможность потопить технологию онлайн-активации еще в зародыше!

Эпоха SecuROM подошла к концу. Нет теперь с нами других известных защит — SafeDisk, Armadillo, ExeCryptor... Остальные (Star Force, как яркий пример) находятся на верном пути к вымиранию. Мне же остается только поблагодарить завсегдатаев «Краклаба», которые очень помогли в исследовании SecuROM: Archer, reversecode, random, v00doo, Nightshade, OnLyOnE, mak и всех остальных, кого не перечислил. Удачи в вашей борьбе и до новых встреч!

Denuvo

Denuvo — новая защита, которая за короткий промежуток времени успела стать известной. Однако стоят за ней все те же Рейнгард Блаукович и Роберт Эрнандес из города Зальцбург, Австрия. Похоже, их выгнали из Sony DADC, и они решили снова подзаработать, открыв новую контору на другом конце города.

Denuvo – сферический SecuROM в отладочном вакууме
Denuvo – сферический SecuROM в отладочном вакууме

Меня очень позабавили заявления разработчиков, что Denuvo — это принципиально новая защита. Вывести их на чистую воду не составило труда. Берем свеженький LordsOfTheFallen.exe и кидаем его в рабочую зону отладчика. Вскрытие 64-разрядного кода пришельца показывает, что внутри — виртуальная машина с несколькими входами, где первый хендл (он отвечает за инициализацию) выглядит примерно так.

mov esi, dword ptr ss:[rsp+A0] //аргумент №1 (raw на ленту of p-code)
xor esi, CFDCF9B4
ror esi, 9
inc esi
mov rax, 100000000 //hi-addr
add rsi, rax //RSI - pointer to p-code

sub rsp, 140 //стек, как зона обмена данными (VM data exchange area)

lea r12, qword ptr ds:[14FE789F2] // Вау! Хранилище №1,5

mov al, byte ptr ds:[rsi] //извлекает байт из ленты p-code
add rsi, 1
neg al
rol al, 7
inc al
mov rdx, qword ptr ds:[r12+rax*8]  //следующий хендл
dec rdx // статическая расшифровка. В SecuROM на этом месте идет CPUID
jmp rdx

Этого достаточно, чтобы констатировать следующие два факта:

  1. ребята скурили весь лес в Австрии;
  2. перед нами обычный форк седьмой версии SecuROM.

Из того, что я повидал, больше всего на Denuvo похож SecuROM версии 7.42 в игре Blood Bowl: Legendary Edition. Конкретные различия в следующем:

  • Зона обмена данными между хендлами в стек (sub rsp, 0x140h) — в этом случае не нужно ломать голову с многопоточностью при использовании кода. Минус такого подхода — дикое количество ошибок при чтении данных из стека. Denuvo постоянно ошибается на этом. Идея, скорее всего, была подсмотрена в SolidShield v2.x.
  • Для статической расшифровки используется dec (bswap, sub). Тут уже стало очевидно, что для CPUID приготовили место посолидней. Как выяснилось позже — для 64-разрядного ключа.
  • Один аргумент (raw указатель на p-code) вместо двух у SecuROM. Возвращаться по заданному адресу Denuvo не умеет.
  • Практически нулевая антиотладка. Самое печальное, что иногда из-за собственных ошибок Denuvo сам себе обламывает работу единственного трика с ntdll.NtSetInformationThread(kernel32.GetCurrentThread(void),…)
  • Инструкция mov rdx, qword ptr ds:[r12+rax*8] – прямо бальзам на душу. Да это же аналог Хранилища №1,5 в SecuROM 7! Неужели разработчики снова доверили нам бразды правления виртуальной машиной? 🙂
  • Больше никаких пасхальных яиц. Теперь мы остались наедине с багами и ошибками! Брутальность достойная «Старфорса».

Мифическая крутость защиты Denuvo сводится к отсутствию нормального раскрученного x64 отладчика для комфортной работы. К сожалению, нет OllyDbg x64, а Mr.eXoDia dbg очень плохо переваривает что-то серьезней Hello, world!. Есть IDA dbg, но он больше на любителя и для самых преданных фанатов Ильфака Гильфанова.

2 комментария

  1. Аватар

    A.T.

    07.08.2015 в 16:42

    Хороший материал в стиле Криса. Разминка для мозгов.
    Все понял и вспомнил молодость.
    Спасибо!

  2. Аватар

    ishayahu

    17.08.2015 в 00:36

    Номер журнала 159, а не 156

Оставить мнение