Хроники битвы при Denuvo. Как «непробиваемая» игровая защита EA Origin оказалась пробиваемой

В 2014 году появилась защита от пиратства, которая позже станет одной из самых узнаваемых, — Denuvo. Она пришла на смену SecuROM и связана с одним действующим лицом — Рейнгардом Блауковичем. Пару лет назад я уже писал о том, как я однажды разреверсил SecuROM, разгромив защиту Блауковича в пух и прах. Сегодня я покажу, как проделал то же с Denuvo. Встречай новую серию «Тибериумного реверсинга»!

Да, эту статью можно было написать и раньше, но в жизни много других важных вещей. Работа в лаборатории, перемещения, моя муза и ее красный Ford Focus II за окном. На его капоте мирно таял свежий снег, когда начинался 2019 год. Мне же в голову начали лезть мысли о корпорации Electronic Arts, магазине Origin, игрушках Unravel и Battlefield 4, реализации EA DRM, Denuvo, VMProtect, и… все завертелось.

Если по Steam в Google можно наяндексить все что угодно — от кастомок до эмуляторов, то в связи с клиентом Origin вспоминается разве что динамическая библиотека ItsAMe_Origin.dll, которая вместо оригинала сама сабмитит запросы от клиента. Собственно, 100% пабликовых взломов Origin (3DM, CPY и другие) именно так и работают.

Прямо здесь начинается первый секрет: для взлома Origin вовсе не обязательно копировать систему ответов-запросов «клиент-сервер» (чаще всего это около десяти уникальных ID сообщений)! Потребуется чуть-чуть пореверсить клиент и поближе познакомиться с таким термином, как EA handle (далее — EAH). Но обо всем по порядку.

За рекламирование всякой ерунды, непомерные требования к ресурсам и вытекающую отсюда слоупочность официальный пакет Origin не ругает только ленивый, но мы пойдем дальше и покажем его уязвимую суть. На момент написания статьи вышла версия 10.5.31, а разбирать мы будем клиент 9.10.1.7. Принципиальной разницы между ними нет — EAH и там и там одинаков.

На наше счастье, клиент Origin SDK (который запихивается в саму игрушку) имеет много ошибок — как мелких, так и весьма серьезных. Например, при его работе остаются открытые хендлы объектов, для которых SDK почему-то забывает вызывать функции WinAPI RegCloseKey и CloseHandle. На это можно было бы закрыть глаза, но вот на вызов адреса с нулевым указателем можно смело сабмитить багрепорты.

Думаю, виной тому откровенная запутанность EAH: перегруженность критическими секциями и семафорами, а также фирменная рекурсия указателей на данные. В такой суматохе запросто можно забыть освободить выделенный блок памяти. Благо процедура уничтожения EAH срабатывает при выходе из игрушки (читай — вызове функции TerminateProcess), что, по мнению буржуйских разработчиков, автоматом избавляет их от этой проблемы.

Вот теперь минутка лулзов, леди и джентльмены! Сказ о том, как Denuvo Software Solution навешивала anti-tamper уже на саму Origin SDK. Делали они это весьма неумело, так как не курили мануалов и не представляют, что именно требуется защитить от посягательства пиратов. Всю мякотку я вынес в отдельную часть статьи.

К сожалению, в отличие от SecuROM в код Denuvo Блаукович не вставлял анекдотов. Однако если следовать традиции, то здесь была бы уместна история про ковбоя, который на спор обмочил весь бар, но ни разу не попал в бутылку.

Вот что делает защита Origin SDK:

  • выполняет виртуализацию функции MD5_Update в процедуре отправки запроса;
  • использует константы Origin и игрушки для вставки инструкций cpuid/ret;
  • для отдельных указателей ret (return С++) в процедурах SDK выполняет переход в динамику с дальнейшей обфускацией Denuvo.

Зачем все это? Зачем ей все шелка, цветные облака… зачем?! У нас ведь есть EAH и целая функция инициализации Origin SDK, о которой Denuvo почему-то не знает.

Виталий Кличко устроился работать в Electronic Arts

Не отстают от Denuvo Software Solution и наши спортсмены-игроделы. Количество контекстов VMProtect достигло сорока для каноничной второй версии и десяти для третьей. Причем сначала они утрамбовали старую вторую, а сверху повесили третью для защиты целостности второй. Это примерно как если StarForce защищать при помощи SecuROM.

Признаться, в первый раз я несколько прифигел, когда автоматом накрыл все сорок контекстов VMP 2.x за один раз. Однако после проследования станции метро OEP (Original Entry Point), ближе к перегону загрузки kit-файлов игрушки Unravel (Unravel _dump_SCY_dump.exe) тулза Denuvo_Profiler собственной сборки начала сигнализировать о перезаписи патченных хендлов старого импорта каким-то сторонним кодом. For great justice — последние версии Denuvo так и защищали, что вывело новую породу гибридных протекторов.

Denuvo_Profiler: именно этими данными формируется Denuvo HWID для твоей машины, плюс расшифровка файла лицензии EA Origin и последующее извлечение секретной таблицы Denuvo
 

EA DRM, OEP, dump

Первое, с чем придется столкнуться при взломе и отвязке игрушки от Origin, — это EA DRM. Опознать его легко по точке входа, которая катапультирует нас (jmp) в библиотеку Activation.dll или Activation64.dll — смотря какой разрядности винда. Вот как это выглядит у меня:

.ooa:0000000142D46000 public start
.ooa:0000000142D46000 start:
.ooa:0000000142D46000 nop
.ooa:0000000142D46001 jmp cs:Core_Activation64_100

При наличии купленной игрушки в библиотеке EA DRM снимается не сложнее, чем распаковывается UPX. Вся процедура сводится к тому, что SDK получает AES-ключик для расшифровки контента. Если ключ верный, то дальше SDK сам восстанавливает секции и делает импорт. Дальнейший переход в OEP защищаемой EA игрушки обычно выполняется ближе к концу вызываемой процедуры (в моем примере это call r9 по адресу 000007FEF1687412), либо перед этим вызывается функция GetModuleHandleW.

000007FEF16873E4 call qword ptr ds:[<&GetModuleHandleW>]
000007FEF16873EA lea rcx,qword ptr ss:[rbp+0x10]    
000007FEF16873EE mov rsi,rax
000007FEF16873F1 call <activation64_original.Verify_and_GetModuleHalde>
000007FEF16873F6 mov rbx,rax
000007FEF16873F9 test rax,rax
000007FEF16873FC je activation64_original.7FEF168741B
000007FEF16873FE nop 
000007FEF1687400 mov r9,qword ptr ds:[rbx]
000007FEF1687403 test r9,r9
000007FEF1687406 je activation64_original.7FEF168741B
000007FEF1687408 xor r8d,r8d
000007FEF168740B mov rcx,rsi
000007FEF168740E lea edx,dword ptr ds:[r8+0x1]  
000007FEF1687412 call r9
000007FEF1687415 add rbx,0x8
000007FEF1687419 jne activation64_original.7FEF1687400
000007FEF168741B call rdi
000007FEF168741D lea rcx,qword ptr ss:[rbp+0x6A0]

Здесь сложности могут возникнуть разве что при перехвате управления. Дело в том, что игрушку с определенным каналом (/SMOID= %хендл от CreateFileMapping%) должен вызывать сам Origin.exe, да еще иногда по нескольку раз. Пока мы будем аттачиться к процессу — пропустим момент выхода на OEP из Activation(64).dll.

Лайфхак прост — сделать подмену библиотеки Activation(64).dll на свою с редиректом вызовов в оригинал. При подхвате управления выводим радостный MessageBox и ожидаем аттача. Прокачаться в этой теме можно в статье «Deleaker, не болей! Ломаем защиту в обход VMProtect и пишем proxy DLL».

 

EA Origin SDK: Give me the handle and I’ll crack this!

Общая стратегия взлома Origin SDK (который инклудится в игрушку) имеет два разных сценария в зависимости от того, что ты хочешь получить в итоге. Первый вариант сложный, очень палевный и приватный. Он требуется для сетевых игр и обеспечивает возможность играть на официальных серверах. Через цепочку OriginGetDefaultUser() — OriginRequestAuthCode ("имя сервера", ex: GOS-BlazeServer-BF4-PC), нужно получить код авторизации, а затем постучать этим AuthCode на сервер. Короче, это тема для отдельной статьи.

Второй вариант гораздо проще. Он пригоден для прохождения одиночных кампаний на локальном компе и никак тебя не запалит в сети. Его мы и рассмотрим подробнее.

Для начала отыщем в коде игрушки процедуру инициализации Origin SDK. Она получает от Origin.exe (в доках он часто называется OriginCore) данные о версии сервера, пользователе и другую подобную инфу, необходимую для дальнейшего запуска игрушки. Если же во время инициализации произойдет ошибка, то процесс попросту завершится, что не входит в наши планы.

Самое главное — эта подпрограмма создает EAH, а всю присланную инфу OriginSDK сливает в эту структуру. Обнаружить код инициализации Origin SDK можно по следующим признакам:

  • использование функции getenv из библиотеки msvcr_xxx.dll (рантайма);
  • появление строк ContentId, EAConnectionId, OriginStartup entered;
  • указание характерных констант ошибок Origin SDK, например 0xa0020008 (ORIGIN_ERROR_CORE_NOT_INSTALLED) или 0xa0010000 (ORIGIN_ERROR_SDK_NOT_INITIALIZED);
  • работа с «карточкой» игрушки. Это такая специальная структура, код которой приводится ниже:
typedef struct struct_EA_ACCESS_request
{
char* ContentId; // 1031469 (идентификатор контента)
char* Title; // Unravel (название игры)
char* MultiplayerId; // 1031469 (идентификатор пользователя для сетевой игры)
char* Language; // en_US
}
EA_ACCESS_request, *pEA_ACCESS_request; // Идентификационная карточка приложения

К примеру, в последней на момент написания статьи версии Battlefield 4 (1.8.2.48475) вход в процедуру инициализации Origin SDK выглядел так:

0000000140DDB59B call <bf4_dump_scy.sub_140DDD1D0>  
0000000140DDB5A0 lea rcx,qword ptr ss:[rsp+0x38]    
0000000140DDB5A5 call <bf4_dump_scy.origin_startup> // А вот и вход!
0000000140DDB5AA movzx eax,al
0000000140DDB5AD test eax,eax
0000000140DDB5AF jne bf4_dump_scy.140DDB5B8 
0000000140DDB5B1 xor al,al  
0000000140DDB5B3 jmp bf4_dump_scy.140DDB669 

В Unravel он выглядел так:

00000001416EBC2D lea rcx,qword ptr ss:[rsp+0x30]    
00000001416EBC32 call <unravel_dump_scy.sub_1417A5B80>  
00000001416EBC37 call <unravel_dump_scy.origin_startup>// Вход! 
00000001416EBC3C test al,al 
00000001416EBC3E jne <unravel_dump_scy.loc_1416EBC50>   
00000001416EBC40 mov eax,0xC346A20F 
00000001416EBC45 lea eax,dword ptr ds:[rax+0x3CB95E01]  
00000001416EBC4B jmp <unravel_dump_scy.loc_1416EBDA4>

Во всех случаях бенефициантом является EAH, который при вызове этих процедур воплотится в куче (heap) как структура размером 968 байт (mov ecx, 0x3C8).

Материализовавшись, EAH послужит тем Солсберийским шпилем, возле которого будет виться остальной OriginSDK. Нельзя пройти мимо сигнатурного обращения к ней — сначала OriginSDK интересуется: «А не ноль ли там?» Если нет, то только тогда тащит оттуда значение:

0000000140DE30D0 xor eax,eax    is_EAH_init
0000000140DE30D2 cmp qword ptr ds:[<EAH>],rax   
0000000140DE30D9 setne al    // setne = Set if Not Equal. Эта инструкция как раз устанавливает байт в указанном операнде в значение 1, если нулевой флаг был очищен
0000000140DE30DC ret 
0000000140DE3DB0 mov rax,qword ptr ds:[<EAH>]   get_EAH
0000000140DE3DB7 ret 

Внимание, фокус! Делаем заглушку на процедуре инициализации EAH (mov eax, 1 & ret), запускаем и видим, что логи отладчика заспамлены гневными сообщениями: «Origin Error: update fail The Origin SDK was not running». Это как раз происходит из-за того, что теперь EAH равен нулю и перестало выполняться ключевое условие — флаг готовности сетевого соединения в его дочерней структуре WSA_socket не взведен:

0000000141989FD0 cmp qword ptr ds:[rcx+50],FFFFFFFFFFFFFFFF is_connection_established
0000000141989FD5 setne al   
0000000141989FD8 ret    

Кажется, у нас проблемы? Вовсе нет! Юмор в том, что это отнюдь не препятствие: после тщетных попыток связаться со своими из OriginCore хакнутая игрушка все равно соглашается запуститься. Занавес и выход на бис!

Источник: xakep.ru

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *