Содержит приличный объем кода, который лучше видно в исходном блоге.

Введение и небольшой рассказ о пиратстве

Все свое детство я был исключительно пользователем ПК, никаких игровых консолей. Дело в том, что в моей стране видеоигры не были очень распространены и недешевы в те дни, бесплатные игры и пиратство были выходом. На самом деле, я считаю это культурной проблемой, которая существовала всегда и вроде бы остается по сей день, но, к счастью, современные цифровые магазины/платформы, такие как Steam, с их региональными ценами помогли значительно уменьшить эту проблему и мышление. Но вывод таков: пиратство нормализовалось, а (в мире ПК) взломщики и кейгены стали обычным явлением. Если и существовала какая-либо коммерческая игра, то она наверняка распространялась через Интернет вместе с одной из них.

Меня всегда поражали кейгены: открываешь игру, копируешь сюда этот код, генерируешь, возвращаешься в игру, вставляешь и получаешь прибыль, теперь у тебя полностью разблокированная игра. Не то чтобы трещины, так как они были, но вы никогда их не замечали. Я помню «процесс установки», которым руководил мой отец и который всегда включал кряк, серийный номер или кейген. Ностальгические времена, конечно. Тем не менее, это всегда было ключевым моментом: я думал, что это нормально, и не осознавал вреда, который влечет за собой пиратство. Вот что я имел в виду под культурной проблемой. Не пытаюсь это оправдать, но так оно и было.

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

В конце концов, этот день настал (ну, не буквально, это был процесс), и с тех пор я балуюсь с разного рода защитами. Как только вы войдете в нее, вы поймете, насколько сложной и увлекательной она может быть, и в этой лекции я собираюсь показать один интересный случай, игру, на которую мне плевать, и все же я здесь: Nicolausi от TOM Productions для DOS и Windows.

Игра

Nicolausi — это немецкая экшн-игра, в которой вы помогаете Санта-Клаусу (или в данном случае Николаусу, что, я полагаю, и дало название игре). от) собирать и доставлять подарки в правильный дом на каждом уровне. Конечно же, за вами гонится не кто иной, как Пасхальный кролик, экипированный своим Ghettoblaster и мощью техно-музыки, попытается уничтожить вас и испортить Рождество, поэтому вам лучше избегать его.

По сути, игра представляет собой изометрический лабиринт, довольно разочаровывающий и скучный, поскольку она больше связана с грубым прокладыванием пути, а не с тщательным обдумыванием своих ходов. Содержание также короткое: в условно-бесплатной версии доступно только 3 уровня. Но это больше не будет проблемой, так как мы разблокируем все 10 уровней зарегистрированной версии.

Но почему

Я не знаю, это просто случилось. Я думаю, это было связано с тем, что у этой игры не было кряка или зарегистрированной версии, доступной в Интернете, поэтому мне пришлось что-то делать (это не платить 14 долларов за дрянную игру 20-летней давности). Кроме того, как вы скоро прочтете, с этим заголовком происходит кое-что интересное.

Отказ от ответственности

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

Выбор между DOS и Windows

Итак, у нас есть игра для DOS, которую нужно взломать, что означает DOSBox Debugger. Я использовал его раньше, и хотя он не так удобен и прост в использовании, как программы Windows того же типа, он выполняет свою работу и определенно лучше любого родного отладчика DOS. Однако я использовал его только для отладки DOS API, таких как проверки компакт-дисков и т. д., но не очень успешно работал с игровой логикой, поэтому я пытаюсь избегать использования его всякий раз, когда я могу. К счастью, это один из тех счастливых случаев, поскольку есть версия игры для Windows.

Версия для Windows, разработанная 'в связи с высокими требованиями', представляет собой DirectDraw порт версии для DOS и упоминается как 'эмулятор'. (может быть, но я сомневаюсь, что они разработали чертов эмулятор DOS вместо того, чтобы просто портировать чертову игру или, что еще проще, выпускать ее с DOSBox). Существует также эмулятор Ad-lib (Sound-Engine), потому что почему бы и нет, и чтобы сохранить эту красивую техно-музыку, которая играет всякий раз, когда Пасхальный кролик рядом с вами. Наконец, эта версия также использует исходные файлы данных DOS.

Итак, первоначальный план довольно прост: взломать версию Windows, а затем посмотреть, сможем ли мы использовать некоторые из них для исправления исполняемого файла DOS. Даже если игра работает на эмуляторе или нет, логика игры должна быть похожей. Итак, прежде чем приступить к анализу, давайте посмотрим на различия между двумя версиями:

Проверка регистрации

Всякий раз, когда вы проходите третий уровень, игра будет открывать экран регистрации, где вы вводите имя и код:

Давайте загрузим этого плохого мальчика на Ghidra. На этом раннем этапе я просто искал строки и внезапно нашел кое-что, что привлекло мое внимание:

Этот конкретный блок выглядит довольно интересно, и эти условия могут привести к чему-то, связанному с проверками кода, которые мы ищем, поэтому мы будем следить за этим. Теперь давайте переключимся на x64dbg и начнем анализировать этот адрес памяти, чтобы увидеть код в действии:

Я ввел свое имя, когда игра спросила, и теперь мы видим, что оно появляется в этой области памяти. Если мы свяжем это значение по адресу 0x44A388 с декомпиляцией C в Ghidra, то теперь мы знаем, что первой проверкой является запись имени, чтобы убедиться, что это не NULL. Кроме того, имя должно состоять как минимум из двух слов, иначе игра не позволит вам продолжить.

Далее идет функция по адресу 0x4133C0, которая принимает строку имени в качестве аргумента. Мы рассмотрим это более подробно позже, так как мы можем предположить, что эта функция обрабатывает алгоритм генерации ключей на основе строки имени. Функция возвращает и сохраняет ключ в переменной, которая находится в следующем сравнении.

При проверке значения адреса памяти 0x44A294 в моем предыдущем тесте оно имело значение 0. Это номер ключа, который отображается по умолчанию, и я его не менял, поэтому давайте попробуем случайное число, а затем проверим еще раз:

Таким образом, мы можем подтвердить, что адрес памяти 0x44A294 хранит значение входного кода, а это означает, что во втором сравнении игра действительно сравнивает введенный нами ключ и правильный, ожидаемый ключ, сгенерированный алгоритмом. Теперь пришло время похлопать себя по спине, мы нашли то, что искали. Про остальную часть блока нам все равно, это просто дополнительные проверки на обработку ошибок, установка CHECKID, если ключ 0, или WRONGID, если введен неверный/недопустимый ключ.

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

Итак, теперь нужно просто изменить эти инструкции по переходу, и все готово:

Красиво, а как же DOS?

Однако есть проблема, которую, я думаю, ожидали с самого начала. Код обеих версий отличается, поэтому патч для версии Windows не применим к исполняемому файлу DOS, поэтому нам нужен другой подход.

Решение на самом деле очень простое, и оно даже лучше, чем конкретный патч. Помните, я говорил, что функция по адресу 0x4133C0 обрабатывает алгоритм генерации ключа? Ну вот, наслаждайтесь своим ключом. Теперь, когда у нас есть имя, и игра была достаточно любезна, чтобы рассчитать для нас ключ, мы можем пройти процесс регистрации обычным способом. Эта комбинация имя+ключ работает в обеих версиях игры, поэтому они взаимозаменяемы.

Это можно сделать двумя способами: обычным вводом регистрационной информации или копированием файлов G_NIC1.SCR и LICENSE.TOM.

ооооооооооооооооооооооо

Да, эта игра скучная, но мы все еще можем повеселиться.

На данный момент мы сделали кряк для одной версии и сгенерировали свои ключи для обеих, хотя и нестандартным способом, так что нам предстоит это исправить.

На самом деле это основной смысл всего этого поста — изменить алгоритм генерации ключей и реализовать его на языке высокого уровня, чтобы ключи можно было генерировать нажатием кнопки, а не поиском в памяти. во время выполнения. Что делает его более захватывающим, так это то, что я никогда не делал ничего подобного, не на этом низком уровне, и у меня есть нулевой алгоритм теория и опыт, так что терпите меня и пожелайте мне удачи.

Анализ алгоритма

Теперь пришло время разобрать функцию, расположенную по адресу 0x4133C0, которую я назову функция кейгена. Как видно на первой картинке с декомпилированным кодом, функция принимает два аргумента, первый — это указатель на строку имени, а второй, после некоторых исследований, — 32-bit соль, которую я назову основной солью. Мне все равно, откуда он берется, так как он всегда имеет одно и то же постоянное значение 0x3533335, и этого достаточно для работы над хешированием.

Функция генерации ключей начинается с установки 16-bit соли локальной области действия, которая генерируется из основной соли путем XORобъединения старших битов с младшими битами. Эта соль наследует консистенцию со значением 0x3066. Установка этого значения важна, так как это будет точка сброса соли, подробнее об этом чуть позже. После этого на основе локального устанавливается глобальная соль, та, которая фактически будет использоваться при хэшировании. Наконец, инициализируется возвращаемая переменная (для сгенерированного ключа).

Затем начинается разбор строки, берется первый символ из указателя. Я не буду вдаваться в подробности, так как это всего лишь парсер, но в основном: он проверяет каждый символ (в ASCII); если это пробел, число или символ, он просто увеличивает указатель на следующий символ, а если это буква, он устанавливает его значение в верхнем регистре в массив. В этом массиве будет храниться текущая рабочая строка, разделенная недопустимыми символами. Например, если входная строка Renzo123 Pigliacampo456, выходные рабочие массивы будут RENZO и PIGLIACAMPO, полностью игнорируя пробелы и числа. Эти строки позже будут обработаны в отдельных итерациях хеширования.

Раз уж мы заговорили об этом, вот забавный факт о записи имени в игре. Несмотря на то, что нет никаких проверок, чтобы контролировать переполнение предела размера массива в 80 байт, игра вас покрывает, так как если вы полностью заполните поле ввода (35 символов) в версии для Windows, она вылетит. И если вам интересно, нет, в DOS не происходит сбой, но текстовый блок в окне смещается на несколько пикселей вправо при каждой повторной попытке. Похоже, кто-то забыл проверить свои поля ввода.

После построения рабочего массива символов анализ строки имени останавливается и начинается хеширование. Локальная соль сбрасывается (на 0x3066) на тот случай, если это последовательная итерация, а затем начинается обработка каждого символа. Вызывается новая функция соли, которая умножает глобальное значение соли на новую соль, 0x15A4E35 плюс 0x01. Эта функция сохраняет результат 32-bit обратно в глобальную соль, но возвращает только его старшие биты. Затем это подписанное значение 16-bit будет XORed с фактически обрабатываемым символом, суммируя результат в окончательную ключевую переменную. Этот процесс повторяется для оставшихся символов, и после его завершения продолжается анализ оставшейся строки имени. Когда все сделано, младшие биты ключевой переменной ORзаменяются на 0x2000, а затем возвращается значение.

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

Делаем кейген

Теперь, когда мы знаем логику генерации ключа, мы можем воспроизвести его функциональность, используя инструменты программирования по своему усмотрению. Для этого я собирался выбрать JavaScript, так как последние пару месяцев занимался веб-разработкой, но понял, что, возможно, старый добрый и надежный C# больше подходит для этой задачи, так как на данный момент нам нужно простое консольное приложение. Если в будущем я в конечном итоге выпущу кейген, Windows Form с мигающими визуальными эффектами и музыкой в ​​стиле чиптюн будет обязательным, никаких оправданий.

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

Во-первых, мы инициализируем и настраиваем все соления:

// Despite being calculated, their values are consistent.
const uint mainSalt = 0x03533335;
const uint localSalt = mainSalt >> 0x10 ^ mainSalt & 0xFFFF; // 0x3066

// Set global salt.
SetGlobalSalt(localSalt);

static void SetGlobalSalt(uint localSalt) {
    // Set a 16-bit mask of the local salt.
    globalSalt = localSalt & 0xFFFF;
}

Затем анализ:

// Parse the input string.
while (nameInput.Length != 0) {
    // Uppercase chars and remove spaces and numbers.
    NameFormat();
}

static void NameFormat() {
    foreach (char letter in nameInput) {
        // Remove char from input string.
        nameInput = nameInput.Remove(0, 1);

        // Check if char is a space or number.
        if (letter != 0x20 && !Char.IsNumber(letter))
            // Add to the parsed string.
            nameParsed += (Char.ToUpper(letter));
        else break;
    }
}

Когда у нас есть рабочая строка, пришло время ее обработать:

// Set the first 4 bytes of the working string, needed for the hashing.
byte[] nameGlobal = Encoding.ASCII.GetBytes(nameParsed.PadRight(4, 0x00));
uint nameGlobal32 = BitConverter.ToUInt32(nameGlobal, 0);
nameGlobal32 = nameGlobal32 & 0xFFFF0000;

while (nameParsed.Length != 0) {
    // Set the lower 2 bytes of the working string, starting with the current character.
    byte[] nameLocal = Encoding.ASCII.GetBytes(nameParsed.PadRight(2, 0x00));
    uint nameLocal16 = BitConverter.ToUInt16(nameLocal, 0);

    // Actual hashing.
    charSalt = ReturnGlobalSalt();
    charSalt = charSalt ^ (nameGlobal32 ^ nameLocal16);

    // Sum the result to the key.
    keyCode += (int)charSalt;

    // Remove char from parsed string.
    nameParsed = nameParsed.Remove(0, 1);
}

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

Игра сначала сохраняет массив рабочих строк, созданный в непрерывном блоке 80 байтов, из которого только первые 4 байтов считываются в регистр, который будет использоваться для хранения обработки символов (переменная nameGlobal32 здесь). Например, при рабочей строке RENZO будет загружена только RENZ.

Но после обработки первого символа только младшие байты ANDзаменяются на 0x0000, а затем заменяются первыми 2 байтами строки, начинающейся со следующего указанного символа (nameLocal16). Это означает, что старшие байты всегда остаются постоянными во время хеширования этой рабочей строки. Таким образом, во второй итерации приведенного выше примера фактическое значение (сгенерированное nameGlobal32 ^ nameLocal16) будет ENNZ, которое затем будет хешировано с солью, затем следующие NZNZ, ZONZ, O0NZ и, наконец, 00NZ. Так как после последнего символа ничего нет, необходимо всегда дополнять nameLocal16, а в случае нашей реализации указать дополняющий символ 0x00, иначе функция PadRight добавит вместо него пробел (0x20), и мы конечно не хочу этого.

Наконец, когда все сделано, возвращаем ключ:

// Return lower bytes of the final key code result.
return (uint)keyCode & 0xFFFF | 0x2000;

Затем просто вызовите функцию из main:

static void Main() {
    // Name input.
    Console.Write("Enter name: ");
    string nameConsole = Console.ReadLine();
    
    // Key generation.
    uint keygenResult = KeyGenerate(nameConsole);
    Console.WriteLine("Your key is: " + keygenResult);
    Console.ReadKey();
}

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

Но подождите, есть еще

У разработчиков, TOM Productions, есть еще пара игр, и одна специально использует тот же движок, что и Nicolausi, PC-Bakterien, а также для DOS и Windows. Из любопытства решил взглянуть и на эту игру, и оказалось, что моя первоначальная теория была верна: кейген работает и с PC-Bakterien. Ну, не как есть, так как в этой игре другая соль (0x784326), но после исправления она работает как часы.

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

Закрытие

На все это у меня ушло примерно 3–4 дня, намного меньше, чем я ожидал. Это определенно было намного веселее, чем играть в саму игру. Но самое главное, я осуществил свою мечту сделать кейген (без причудливой графики и музыки, я знаю), и, надеюсь, это будет первый из многих других.

Теперь, серьезно, несмотря на то, что я ругал игру на протяжении всего написания, на самом деле она не так уж и плоха, и если вам нравятся такие игры, вы можете найти в них немного удовольствия. Однако эта радость из 1995 года определенно не стоит 14 долларов в 2021 году, и, кроме того, необходимость покупать ее прямо на веб-сайте разработчика сегодня является большой проблемой. Если вы являетесь частью TOM Productions и читаете это, размещайте свои материалы в Steam или GOG по доступной цене, по крайней мере, там вы будете намного более заметны. , и может на самом деле продавать что-то. Как я сказал в самом начале, единственный надежный способ для меня и кучи людей покупать игры — это через Steam и их региональные цены, и если бы игра была там, я бы купил ее без сомнений.

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

Тем не менее, когда разработчики перестанут поддерживать и/или продавать игру, и я это замечу, полный кейген как для Nicolausi, так и для PC-Bakterien будет доступен на GitHub для всех. А пока, надеюсь, вы хорошо провели время за чтением.

Обновление от 24/12/21: спойлер, они прекратили продажу игры. Какой рождественский подарок!

Гипноз 4:20 - Те, кто готов ждать, будут вознаграждены временем

Итак, как и ожидалось, TOM Productions, наконец, отключили свой магазин зла, сделав все свои игры официально недоступными для покупки. И, как и было обещано (как только это произошло), кейген теперь доступен для скачивания. Перейдите на страницу проекта на GitHub, чтобы получить генератор ключей в одном из двух вариантов: C# (WinForms и консоль) или JavaScript. Я решил добавить последний, так как я планирую в будущем создать страницу кейгенов на сайте, чтобы его можно было использовать, не скачивая ничего.

Так что осталось на будущее? Мне все еще нравится идея посетить другие их игры, а именно серию Robot, которая, как ни странно, имеет на удивление сильный культ поклонников, включая специальный веб-сайт и форум, на котором один человек даже изготовил коллекционную коробку на заказ. Ебена мать.

Пока этот день не настал, вы можете наслаждаться (или страдать) с Nicolausi и PC-Bakterien. Счастливого Рождества!