Дешифратор файлов Lineage2 версии 111. С#

BIT_hack

Знающий
Участник
Сообщения
257
Розыгрыши
0
Решения
2
Репутация
79
Реакции
163
Баллы
485
Это мой первый опыт использования XOR и C#. Возможно, профессионалу мой код покажется не самым оптимальным, но он выполняет свою задачу.

У меня вопрос: как получить из текста "Lineage2Ver111" байты, которые будут такими же, как и из файла? Я не уверен, как это называется, но, по-моему, это связано с нулевыми байтами.
Например, в коде я получаю байты для текста "Lineage2Ver111": "4C 69 6E....". А в файле для этого же текста я получаю такие байты: "4C 00 69 00 6E....".
Я пробовал разные кодировки и много чего ещё, но не добавлять же после каждого байта нулевые байты. Я думаю, что в C# должна быть стандартная функция преобразования для этого.

Этот код представляет собой дешифратор файлов Lineage2 версии 111. на C#
Чтобы не тратить ваше время, сразу скажу, что этот код работает.
У меня только один вопрос, который я описал выше.
C#:
using System.IO;

public static class Lineage2Decrypter
{
    // Ключ XOR для Lineage2Ver111
    private const byte KeyXOR = 0xAC;

    // Шаблон байтов для поиска начала зашифрованных данных Lineage2Ver111
    private static readonly byte[] ByteVersion111 = { 76, 0, 105, 0, 110, 0, 101, 0, 97, 0, 103, 0, 101, 0, 50, 0, 86, 0, 101, 0, 114, 0, 49, 0, 49, 0, 49, 0 };

    // Дешифрование файла Lineage2
    public static string L2DecodeU(string fileName)
    {
        if (!File.Exists(fileName)) return fileName;
        byte[] fileData = File.ReadAllBytes(fileName);
        int startIndex = FindStringIndex(fileData, ByteVersion111);
        if (startIndex >= 0)
        {
            startIndex += ByteVersion111.Length;
            byte[] decryptedData = DecryptData(fileData, startIndex, KeyXOR);
            string outputFilePath = "temp/dec-" + Path.GetFileName(fileName);
            Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath));
            using (FileStream outputFile = File.OpenWrite(outputFilePath))
            {
                outputFile.Write(decryptedData, 0, decryptedData.Length);
            }
            return outputFilePath;
        }
        return fileName;
    }

    // Поиск начала зашифрованных данных в массиве байт
    private static int FindStringIndex(byte[] data, byte[] pattern)
    {
        for (int i = 0; i <= data.Length - pattern.Length; i++)
        {
            bool match = true;
            for (int j = 0; j < pattern.Length; j++)
            {
                if (data[i + j] != pattern[j])
                {
                    match = false;
                    break;
                }
            }
            if (match) return i;
        }
        return -1;
    }

    // Дешифрование данных с помощью XOR
    private static byte[] DecryptData(byte[] data, int startIndex, byte key)
    {
        byte[] decryptedData = new byte[data.Length - startIndex];
        for (int i = startIndex; i < data.Length; i++)
        {
            decryptedData[i - startIndex] = (byte)(data[i] ^ key);
        }
        return decryptedData;
    }
}
 

Если вам понадобится использовать этот код, вы можете сделать это следующим образом:
C#:
L2DecodeU("interface.u");
После выполнения этой команды в текущей папке с программой появится папка temp, а в ней — файл dec-interface.u.
 
У меня вопрос: как получить из текста "Lineage2Ver111" байты, которые будут такими же, как и из файла? Я не уверен, как это называется, но, по-моему, это связано с нулевыми байтами.
Например, в коде я получаю байты для текста "Lineage2Ver111": "4C 69 6E....". А в файле для этого же текста я получаю такие байты: "4C 00 69 00 6E....".
Я пробовал разные кодировки и много чего ещё, но не добавлять же после каждого байта нулевые байты. Я думаю, что в C# должна быть стандартная функция преобразования для этого.
Текст Lineage2Ver111 закодирован как UTF-16LE - где на 1 символ отводится 2 байта.
 
Текст Lineage2Ver111 закодирован как UTF-16LE - где на 1 символ отводится 2 байта.
Я пробовал и так и так и в разных языках программирования но у меня все равно остаются нулевые байты(
Если есть возможно приведите свой код.
 
Я пробовал и так и так и в разных языках программирования но у меня все равно остаются нулевые байты(
Java:
    public final static String LINEAGE_2_HEADER    = "Lineage2Ver";
    public final static int LINEAGE_2_HEADER_SIZE  = (LINEAGE_2_HEADER.length() * 2) + (3 * 2);
   
    private CommonPair<Integer, Integer> getCryptVersion(File inputFile)
    {
        byte[] headerAsBytes = readHeader(inputFile);
        if (headerAsBytes.length == 0)
        {
            return null;
        }
        final String headerAsString = new String(headerAsBytes, StandardCharsets.UTF_16LE);
        if (headerAsString.startsWith(LINEAGE_2_HEADER))
        {
            int cryptVersion = Integer.parseInt(headerAsString.substring(LINEAGE_2_HEADER.length()));
            if (!(cryptVersion == 111 || cryptVersion == 121))
            {
                return null;
            }
            switch (cryptVersion)
            {
                case 111:
                {
                    return new CommonPair<>(cryptVersion, -1397969748);
                }
                case 121:
                {
                    String filename = getFileName().toLowerCase(Locale.ROOT); //_fileName.split("\\.")[_fileName.split("\\.").length - 1];
                    int xor = 0;
                    for (int index = 0; index < filename.length(); ++index)
                    {
                        xor += filename.charAt(index);
                    }
                    xor = xor & 0xff;
                    xor = xor | xor << 8 | xor << 16 | xor << 24;
                    return new CommonPair<>(cryptVersion, xor);
                }
            }
        }
        return null;
    }
Даже такой "жутко-код" выполнял свои функции.
 
@BIT_hack,
C#:
    private const int HeaderSize = 28;
    private const byte XorKey = 0xAC;
    private const string ExpectedHeader = "Lineage2Ver111";

    private static void DecryptFile(string filePath)
    {
        using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite);

        Span<byte> headerBytes = stackalloc byte[HeaderSize];
        fileStream.Read(headerBytes);

        string header = System.Text.Encoding.Unicode.GetString(headerBytes);
        if (!header.StartsWith(ExpectedHeader))
        {
            //Console.WriteLine($"File '{filePath}' does not require decryption."); or display message or just skip
            return;
        }

        string tempFilePath = $"{filePath}.tmp";
        using var tempFileStream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write);

        fileStream.Seek(HeaderSize, SeekOrigin.Begin);
        var buffer = ArrayPool<byte>.Shared.Rent(8192);
        int bytesRead;
        while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
        {
            for (int i = 0; i < bytesRead; i++)
            {
                buffer[i] ^= XorKey;
            }
            tempFileStream.Write(buffer.AsSpan(0, bytesRead));
        }

        ArrayPool<byte>.Shared.Return(buffer);

        fileStream.Close();
        tempFileStream.Close();

        File.Delete(filePath);
        File.Move(tempFilePath, filePath);
    }
 
@BIT_hack,
C#:
    private const int HeaderSize = 28;
    private const byte XorKey = 0xAC;
    private const string ExpectedHeader = "Lineage2Ver111";

    private static void DecryptFile(string filePath)
    {
        using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite);

        Span<byte> headerBytes = stackalloc byte[HeaderSize];
        fileStream.Read(headerBytes);

        string header = System.Text.Encoding.Unicode.GetString(headerBytes);
        if (!header.StartsWith(ExpectedHeader))
        {
            //Console.WriteLine($"File '{filePath}' does not require decryption."); or display message or just skip
            return;
        }

        string tempFilePath = $"{filePath}.tmp";
        using var tempFileStream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write);

        fileStream.Seek(HeaderSize, SeekOrigin.Begin);
        var buffer = ArrayPool<byte>.Shared.Rent(8192);
        int bytesRead;
        while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
        {
            for (int i = 0; i < bytesRead; i++)
            {
                buffer[i] ^= XorKey;
            }
            tempFileStream.Write(buffer.AsSpan(0, bytesRead));
        }

        ArrayPool<byte>.Shared.Return(buffer);

        fileStream.Close();
        tempFileStream.Close();

        File.Delete(filePath);
        File.Move(tempFilePath, filePath);
    }
вообще лучше будет декодировать файл по нужде наверное , Ане сразу весь грузить...
111 и 121 поддерживаю чтение определенного фрагмента, а не блоков ранее заложенные размера.
 
вообще лучше будет декодировать файл по нужде наверное , Ане сразу весь грузить...
111 и 121 поддерживаю чтение определенного фрагмента, а не блоков ранее заложенные размера.
1732049947253.webp
 
  • Ха-ха-ха
Реакции: kick
хз, я баловался с .utx и .u файлами - причитав заголовок (узнав что за крипт) - после этого просто читал значения в разных местах без полного чтения файла
Ещё раз
1732050453818.png
Открыли файл
1732050480002.png
Создали буфер в стеке для считывания заголовка длиной первых 28 байт и потом прочитали эти 28 байт...
1732050539099.png
Проверили соответствует ли заголовок 111 кодировки или нет, если нет, просто вышли и всё. Закомментирован вывод в командной строке и указано отобразить сообщением или ещё как или просто ничего не выводить, потому что не содержится указание на крипт.
Что ещё надо? Какие оптимизации, для другой и других кодировок можно сделать по другому, это лишь пример быстрой работы с созданием минимальных аллокаций. А делать тоже самое с выделением байтов и определять кодировку файла, какой смысл?
 
Спасибо ребята я разобрался там оказалось не все так страшно эти файл можно декодировать в несколько строк кода!
Алгоритм такой как вы описали выше!
 
Назад
Сверху Снизу