Переводчик
0,0
рейтинг
10 января в 17:48

Разработка → Steam CEG от Valve и с чем его едят. Все сложное — просто

image

Добрый час, %USERNAME%! В моей предыдущей статье "Steam CEG от Valve и с чем его едят. Введение" было дано лишь абстрактное понимание и принципы работы технологии CEG. В этой статье будет по абсолютному минимуму теории и преобладающее большинство практики. Сегодня и сейчас мы рассмотрим, можно ли «отучить» от этой защиты заветный исполняемый файл.

Шаг I. Выбор «жертвы» и ее анализ

Наличие CEG, причем любой версии, можно легко обнаружить по наличию в заголовке файле секции .version и первых четырех байтов в ней же (78 56 34 12):

image

Заведомо зная эту информацию и немного порыскав в библиотеке Steam, мой выбор пал на HD переиздание легендарной Age of Mythology c приставкой "Extended Edition". После успешного завершения загрузки, подождав пару секунд, пока CEG подпишет исполняемый файл, открываем его в нашем отладчике, в моем случае в x64dbg. Ах да, стоит отметить, что товарищ CEG использует некоторые средства анти-отладки, поэтому будем использовать плагин ScyllaHide со следующими настройками:

image

Итак, открыв исполняемый файл, видим что он не накрыт сторонними упаковщиками, поэтому можем смело переходить ко второму шагу.

Шаг II. Разбор полетов

Абсолютно в любой версии CEG существует одна функция, которая производит общий расчет нужных значений. Выглядит она следующим образом:

image

Зная это, ставим бряк либо на инструкции
mov eax, dword ptr ds:[ecx]
,
либо на следующей:
mov dword ptr ds:[ecx],eax
:

image

Нажимаем "Run" (F9) и видим, что бряк сработал! «И что же это нам дало ?» — спросите Вы, отвечаю: точное значение EAX в буфере. В этом можно убедиться, посмотрев в правую колонку напротив вкладки "CPU":

image

Как видно из изображения, мы получили значение EDB88320. Скопируем его в буфер обмена и пойдем дальше. Нажимаем "Step Over" (F8) два раза и оказываемся внутри одной из функций, используемой CEG. И вот на этом этапе стоит сказать пару слов о существующих видах таких функций. Собственно, всего их может быть три типа:

1. Функции с константным значением (Constant) — возвращают константное значение, которое всегда будет одинаковым. Патчатся как:
mov eax, <значение> ret
;
2. Функции со случайным значением (Random) — возвращают любое целое значение. Патчатся как:
mov eax, <значение> ret
(используются лишь на новейших версиях CEG);
3. Защитные функции (Protect) — являются указателем, которые ведут к истинной функции Патчатся как:
jmp <адрес>
.

Теперь давайте вернемся к нашей игре. Мы внутри желанной функции и у нас в буфере правильное значение. Теперь мы должны определить вид нашей функции. Прокрутив пару инструкций вниз, мы увидим, что используется инструкция
lea eax, dword ptr ds[esi+ebx]
:

image

И это может означать только одно — мы имеем дело с функцией, которая возвращает константное значение. Вообще, следует запомнить, что если в буфере EAX оказываются значения типа: EDB88320, 60, 11, 5, 3f и т.д., то сразу будет понятно, что функция, которая использует это значение — будет константной. Почему? Потому, что это «общеизвестные» константные значения CEG, они будут одинаковыми на любой версии CEG. Поэтому, двигаемся в самое начало функции и корректируем ее, меняя на
mov eax, EDB88320
ret
:

image

Итак, что же дальше? А дальше мы снова все повторяем (разумеется, имея все тот же бряк на функции, которая вычисляет значения) до тех пор, пока игра не запустится (Profit!), что означает, что мы прошли все проверки CEG. Все ?! Ну давайте посмотрим. Сохраняем файл с проделанными патчами и пробуем запустить на другом ПК (на который уже заведомо была скачана игра). И тут… облом. Игра не хочет запускаться! Ах да, мы забыли про еще одну немаловажную деталь! Дело в том, что CEG, как говорилось в первой статье, использует так называемые файловые проверки. Снова открыв отладчик, мы сможем легко обнаружить эти проверки по наличию характерных функций замечательного WinAPI: GetFileInformationByHandle, CreateFileW, OpenFileById, StringFromGUID2, RegOpenKeyExW, SystemFunction036 и lstrcmpiW. В нашей игре — присутствует только одна такая проверка, найти ее можно сразу вбив GetFileInformationByHandle в поиск "References":

image

Идем в начало и ищем функцию, которая вызывает данную проверку (ПКМ -> Find references -> To Selected Address (es), либо сочетанием клавиш CTRL+R). Будет она выглядеть следующим образом:

image

Ну что же, давайте сделаем это! Хотя это можно сделать и более изощренным способом, но мы поступим проще, используя
mov eax, 1 ret


image

Сохраняем результат проделанной работы и тестируем второй раз и о Боги, игра запустилась на другом аккаунте Steam и на другом ПК!

Шаг III. Полная отвязка игры от Steam (Yarr! Edition)

Разумеется, после полученного удовольствия, захотелось больше экстрима. А что, если я хочу просто поиграть с другом по локалке без всякого там Steam как в бородатые времена? Ну не вопрос. Для этого я скачал отличный эмулятор Steam с частичной поддержкой оверлея (!) под названием SmartSteamEmu. Настроив его, я запустил игру без включенного Steam. И что же я вижу — создался файл *.STEAMSTART и игра повисла в процессах. Подумав около тридцати секунд, игра таки запустилась и работала исправно. Но почему? Мы же все вроде сделали, ведь так? Да, это так, но мы забыли об одном маленьком штрихе. Дело в том, что этот файл события создается, когда процесс Steam не запущен и по сути является последней микро-проверкой. Ну что же, открываем отладчик в третий раз и ищем все функции с CreateFileA. Не стесняясь, ставим бряки на все функции (в данном случае их всего две). При запуске игры бряк сработал:

image

STEAMSTART, мы видим Вас! Итак, для того, чтобы покончить с этим недоразумением, просто меняем верхний JE на JNZ или JMP:

image

Сохраняем патч, снова пробуем запустить игру без Steam скрестив пальцы. И вуаля — игра запустилась в считанные секунды! Мы сделали с тобой это, CEG, ты уж прости нас.

Собственно, вывод

Попавшийся CEG в игре Age of Mythology: Extended Edition был не сложным. В этой игре не было ни Protect, ни Random функций, лишь одни Constant, что значительно упростило задачу. Так же в этой игре не было минорных проверок, в виде старого доброго CRC или новейших проверок ID процессора либо серийного номера жесткого диска.
В будущем, надеюсь, я смогу осилить эти две новые проверки и написать новую статью, а пока — до скорых встреч!
Алексей @RESTORATiON
карма
18,0
рейтинг 0,0
Переводчик
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (14)

  • –1
    Крутая статья, читается буквально на одном дыхании, чего вряд ли скажешь о реверсе :)

    И сразу вопрос «почему?»:
    если в буфере EAX оказываются значения типа: EDB88320, 11, 5, 3f и т.д., то сразу будет понятно, что функция, которая использует это значение — будет константной
    • 0
      Так как эти значения будут одинаковыми на любой версии CEG. Так сказать, «общеизвестные» значения. Забыл еще значение 60.
  • +2
    Ждал большего. Интересны именно принципы работы CEG(лично мне), а не непосредственно реверс.
    Но все равно интересное начало. Поддержал плюсиками, надеюсь на более глубокое продолжение темы.
  • +2
    Статья для меня осталась непонятной.

    1. Что конкретно это за функции «Constant», «Random», «Protect»? От чего зависит «константное» значение?

    2. Что проверяется в «файловых» проверках?

    3. Что за микро-проверка с файлом *.STEAMSTART? Игра запускалась в любом случае, зачем она тогда и зачем её патчить?
    • 0
      1. О функциях написано в статье. Значения, ни от чего не зависит, они являются константными, то бишь неизменными для всех версий CEG (так же упоминалось в статье). Возможно, при анализе более сложного CEG (в обозримом будущем), я сделаю упор на полное разжевывание этих функций;
      2. О файловых проверках (а так же проверке реестра), писалось в предыдущей статье, ссылка на нее присутствует в начале статьи;
      3. Так же писалось в статье, данный файл создается, если Steam не запущен, то есть фактически HANDLE процесса Steam равен нулю.
      • 0
        1. Тогда зачем патчить константную функцию, если значение ни от чего не зависит?
        Как я понял, на компьютере А она будет возвращать EDB88320h. Перенесли игру на компьютер В — там она тоже возвращает EDB88320h?
        3. Вы перефразировали предложение из своей статьи, и, к сожалению, это ничем не помогло.
        этот файл события создается, когда процесс Steam не запущен и по сути является последней микро-проверкой
        Я спрашивал не откуда берётся этот файл, а зачем эта проверка, если она фактически ни на что не влияет? Игра же запустилась до патчинга CreateFileA?

        Выходит, из трёх проверок фактически работала только одна (первая — константа, с третьей — игра запускалась). Причём, что именно проверяла вторая проверка, осталось непонятным.
        • 0
          1. Если не патчить, то игра не запустится, так как каждая из функций будет вести к исключению. Если пропатчить не все функции, исход будет тот же.
          3. К сожалению, обо всем очень подробно и разжевано написать не удалось в размере всего двух статей. Если я буду писать дальше статьи на эту тематику, я обязательно поведаю больше информации.
          И насчет той проверки .STEAMSTART — это лишь полировка напильником, она ни на что не влияет, только на скорость старта игры.
          • 0
            Тогда буду ждать новых статей. Спасибо :)
  • 0
    А будет ли работать вариант, если просто получить исходный бинарник до того, как его подпишут? Возможно это было бы проще?
    • 0
      Если только напрямую от разработчиков получить незащищенную версию бинарника. Вероятность равна нулю, в общем.
      • +2
        Я сейчас прочитал ответ на этот вопрос в комментариях к предыдущей статье. Но он появился параллельно с моим вопросом здесь. В общем, как выяснилось, бинарник приходит с сервера порезанным и не может в таком виде запуститься. И этот крайне важный момент был изначально не понятен.
  • 0
    Вот на хабре добрались и до Steam…
    Тема на самом деле довольно глубокая, если копнуть хорошенько:
    github.com/atom0s/Steamless

    А на счет эмуляторов steam_api, то их сейчас вагон и маленькая тележка, каждая группа имеет такой, как следствие релизы игрушек из steam довольно быстро появлялись, до момента появления Denuvo, как уже выяснили aka VMProtect, поэтому достать готовый эмулятор не сложно, но думаю интереснее было бы сделать свой, используя Open Steamworks:
    github.com/SteamRE/open-steamworks
    Пусть это и будет велосипед, но ребята, которые на этом съели собаку не особо будут готовы делиться своими исходниками, да и для простеньких игр достаточно описать основные функции, но зато будет свое, родное.

    Надеюсь на появление и таких статей.
    • 0
      Согласен, возможно напишу статью, посвященную созданию простейшего эмулятора Steam (вопрос только, нужно ли это).
      • +6
        Возможно, для начала стоит все же более основательно раскрыт затронутые темы. То, что вы написали, это, простите, не статьи. Вот все их содержание:
        1. Есть такая штука, как CEG (хм… а по русски получается СУП :)), она вроде как что-то там защищает каким-то хитрым образом
        2. (магические пассы руками) смотрите… смотрите… смотрите… Вот, мы сломали защиту, правда, защита вроде как говеная да и не защита вовсе (во всяком случае, у меня сложилось впечатление, что вы открыли бинарник, сделали пару поисков подстрок, рядышком там что-то пропатчили и все заработало. В чем тогда ценность такой «защиты» абсолютно неясно, и зачем о ней статьи нужны, если она так легко ломается)

        Хочется все же поподробнее узнать о принципах, на чем основывается эта защита (кто что проверяет и почему эта проверка считается доказательством того, что игра не взломана), почему ее трудно (или наоборот, легко) ломать, какие интересные решения были применены, что можно было бы улучшить (раз сломали, значит возможно, и слабые места видите).

        Если статья об эмуляторе поможет раскрыть эти вопросы, то тогда будем ждать.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.