private const int HeaderSize = 28;
private const int BlockSize = 128;
private static int ReadHeader(Stream stream)
{
var buffer = new byte[HeaderSize];
stream.Read(buffer, 0, buffer.Length);
return int.Parse(Encoding.Unicode.GetString(buffer).Substring(buffer.Length / 2 - 3));
}
private static byte[] DecryptBlock(Stream stream)
{
var block = new byte[BlockSize];
stream.Read(block, 0, BlockSize);
Array.Reverse(block);
var valueBytes = new byte[129];
block.CopyTo(valueBytes, 0);
var modPowBytes = BigInteger.ModPow(new BigInteger(valueBytes), Keys.PrivateExponent413, Keys.Modulus413).ToByteArray();
Array.Clear(block, 0, block.Length);
Array.Copy(modPowBytes, block, modPowBytes.Length);
Array.Reverse(block);
int size = block[3];
if (size > 124)
throw new InvalidOperationException("DecryptBlock");
int position = 128 - size - ((124 - size) % 4);
var result = new byte[size];
Array.Copy(block, position, result, 0, size);
return result;
}
public static MemoryStream DecryptData(string filename)
{
MemoryStream uncompressedData;
using (var compressedData = new MemoryStream())
{
using (var stream = new FileStream(filename, FileMode.Open))
{
int cryptoVersion = ReadHeader(stream);
switch (cryptoVersion)
{
case 413:
var blockCount = (stream.Length - stream.Position) / BlockSize;
var block = DecryptBlock(stream);
compressedData.Write(block, 4, block.Length - 4);
for (var i = 1; i < blockCount; i++)
{
block = DecryptBlock(stream);
compressedData.Write(block, 0, block.Length);
}
break;
// TODO
}
}
compressedData.Seek(0, SeekOrigin.Begin);
using (var uncompressingStream = new ZlibStream(compressedData, CompressionMode.Decompress))
{
uncompressedData = new MemoryStream();
var buffer = new byte[0x1000];
int r;
while ((r = uncompressingStream.Read(buffer, 0, buffer.Length)) != 0)
{
uncompressedData.Write(buffer, 0, r);
}
}
}
uncompressedData.Seek(0, SeekOrigin.Begin);
return uncompressedData;
}
}
public static class Keys
{
public static BigInteger Modulus413 = new BigInteger(new byte[] {
0x83, 0x2D, 0xD7, 0x8F, 0x66, 0xC1, 0x3D, 0x12,
0x38, 0xDC, 0x89, 0x6A, 0xA8, 0xBA, 0x51, 0x45,
0x74, 0xC5, 0x9B, 0xD0, 0x11, 0x05, 0xD4, 0x14,
0x93, 0x74, 0xB6, 0x67, 0x78, 0x8F, 0xE6, 0x50,
0xE4, 0x3C, 0x08, 0x29, 0xE3, 0x4B, 0x8D, 0xC0,
0xB7, 0x5E, 0xB4, 0xF2, 0x9E, 0x7D, 0xA6, 0xC9,
0x02, 0x3A, 0x79, 0x81, 0x38, 0x63, 0x55, 0x86,
0x60, 0x03, 0x1D, 0xB7, 0xA0, 0xC0, 0x2C, 0xE7,
0xBD, 0xD7, 0xB5, 0xE5, 0xDD, 0xD1, 0x3E, 0x4D,
0x27, 0xEA, 0x6E, 0xBE, 0x03, 0x7D, 0x41, 0xB6,
0x50, 0x88, 0xA1, 0x94, 0x73, 0x6E, 0xE4, 0x75,
0x01, 0xF9, 0x9E, 0xDA, 0x2D, 0x29, 0x19, 0xFC,
0x3C, 0x4C, 0x6E, 0x1C, 0xBC, 0x29, 0xE8, 0xD6,
0xE1, 0x8A, 0x8A, 0xA3, 0x61, 0x16, 0xEF, 0x0F,
0x2F, 0x17, 0x8D, 0x7E, 0xD1, 0x0C, 0x0A, 0xEF,
0x37, 0xF7, 0xDD, 0x72, 0x84, 0x39, 0xDF, 0x97,
0x00
});
public static BigInteger PrivateExponent413 = new BigInteger(0x35u);
}
Это да, но нужда в этих реализациях отпадает после .NET Core 2.1.Ты юзаешь встроенный BigInteger. Хотя есть свои реализации.
Первые 4 байта расшифрованного массива - это размер данных в текущем блоке. Для всех блоков кроме последнего там записано число 124.6. Судя поВы не можете просматривать ссылку пожалуйста воспользуйтесь следующими ссылками Вход или Регистрация, часть этого остатка отсекается, а остальное считается за расшифрованный блок. Так вот тут мне чуток не понятно, что да как, из-за этой иерархии наследования и FilterInputStream`а (не совсем пойму, как оно работает).
Заметил это при отладке на примере ItemName. Чуть позже запущу для всех остальных и если будет так же само, тогда можно смело оптимизировать кусок кода из DecryptBlock.Первые 4 байта расшифрованного массива - это размер данных в текущем блоке. Для всех блоков кроме последнего там записано число 124.
Нужна ОЧЕНЬ мощная тачка или вообще набор тачек для нахождения множителей. Куда проще заменить ключ на свой.Позновательно, практически аналогичный метод и я собрал годика два назад. Но дальше застрял в тупике - нужен енкодер под оф сервер.
Должно же быть решения за столько лет. Неужели никто не смог...
gmp и zlib. На самом деле ничего сложного, мб чуть позже примерчик кину.Если я понял правильно очень многое в этом алгоритме упрощено за счет использование C# / Java (acmi).
На вскидку насколько сложно будет в сравнении с C# сделать подобное на C++. Мне нужно использовать это в UE4 как Third Party Library, поэтому я рассчитываю написать DLLку которую я потом использую в UE4.
Основные проблемы я вижу в использовании ByteBuffer'a, (такого в С++ вроде и нет) BigInteger'a (нет нативной поддержки, придется либу искать) и Zlib'ы ( тоже самое что и с BigInteger).
Немного говнокодисто, но мне лень переделывать по красоте.Если я понял правильно очень многое в этом алгоритме упрощено за счет использование C# / Java (acmi).
На вскидку насколько сложно будет в сравнении с C# сделать подобное на C++. Мне нужно использовать это в UE4 как Third Party Library, поэтому я рассчитываю написать DLLку которую я потом использую в UE4.
Основные проблемы я вижу в использовании ByteBuffer'a, (такого в С++ вроде и нет) BigInteger'a (нет нативной поддержки, придется либу искать) и Zlib'ы ( тоже самое что и с BigInteger).
#include <fstream>
#include <sstream>
#include <zlib.h>
#include "mini-gmp.h"
const char modulusBytes[] = {
0x97, 0xdf, 0x39, 0x84, 0x72, 0xdd, 0xf7, 0x37,
0xef, 0x0a, 0x0c, 0xd1, 0x7e, 0x8d, 0x17, 0x2f,
0x0f, 0xef, 0x16, 0x61, 0xa3, 0x8a, 0x8a, 0xe1,
0xd6, 0xe8, 0x29, 0xbc, 0x1c, 0x6e, 0x4c, 0x3c,
0xfc, 0x19, 0x29, 0x2d, 0xda, 0x9e, 0xf9, 0x01,
0x75, 0xe4, 0x6e, 0x73, 0x94, 0xa1, 0x88, 0x50,
0xb6, 0x41, 0x7d, 0x03, 0xbe, 0x6e, 0xea, 0x27,
0x4d, 0x3e, 0xd1, 0xdd, 0xe5, 0xb5, 0xd7, 0xbd,
0xe7, 0x2c, 0xc0, 0xa0, 0xb7, 0x1d, 0x03, 0x60,
0x86, 0x55, 0x63, 0x38, 0x81, 0x79, 0x3a, 0x02,
0xc9, 0xa6, 0x7d, 0x9e, 0xf2, 0xb4, 0x5e, 0xb7,
0xc0, 0x8d, 0x4b, 0xe3, 0x29, 0x08, 0x3c, 0xe4,
0x50, 0xe6, 0x8f, 0x78, 0x67, 0xb6, 0x74, 0x93,
0x14, 0xd4, 0x05, 0x11, 0xd0, 0x9b, 0xc5, 0x74,
0x45, 0x51, 0xba, 0xa8, 0x6a, 0x89, 0xdc, 0x38,
0x12, 0x3d, 0xc1, 0x66, 0x8f, 0xd7, 0x2d, 0x83
};
constexpr unsigned long privateExponent = 0x35;
constexpr int ReverseBytes(int value)
{
char* bytes = (char*)&value;
return (int)bytes[3] | ((int)bytes[2] << 8) | ((int)bytes[1] << 16) | ((int)bytes[0] << 24);
}
int main()
{
mpz_t modulus, readBlock, modPowedBlock;
mpz_init(modulus);
mpz_import(modulus, 128, 1, 1, 0, 0, modulusBytes);
mpz_init(readBlock);
mpz_init(modPowedBlock);
std::ifstream input("Q:\\Lineage2_NA_175_152\\system\\ItemName-e.dat", std::ios::binary);
char header[28] { 0 };
input.read(header, 28); // читаем заголовок, тот самый Lineage2Ver413
std::stringstream decryptedData;
char encBuffer[128] { 0 };
input.read(encBuffer, 128);
mpz_import(readBlock, 128, 1, 1, 0, 0, encBuffer);
mpz_powm_ui(modPowedBlock, readBlock, privateExponent, modulus);
size_t count = 32;
int decBuffer[32] { 0 };
mpz_export(decBuffer, &count, 1, 4, 1, 0, modPowedBlock);
int decryptedSize = ReverseBytes(decBuffer[0]);
int startPosition = 128 - decryptedSize - ((124 - decryptedSize) % 4);
int unpackedSize = ((unsigned int *)&((char *) decBuffer)[startPosition])[0];
startPosition += 4;
decryptedSize -= 4;
decryptedData.write(&((const char *) decBuffer)[startPosition], decryptedSize);
while (!input.eof())
{
input.read(encBuffer, 128);
mpz_import(readBlock, 128, 1, 1, 0, 0, encBuffer);
mpz_powm_ui(modPowedBlock, readBlock, privateExponent, modulus);
count = 32;
memset(decBuffer, 0, 128); // TODO: нужно ли?
mpz_export(decBuffer, &count, 1, 4, 1, 0, modPowedBlock);
decryptedSize = ReverseBytes(decBuffer[0]);
startPosition = 128 - decryptedSize - ((124 - decryptedSize) % 4);
decryptedData.write(&((const char *) decBuffer)[startPosition], decryptedSize);
}
input.close();
char* unpackedData = new char[unpackedSize];
z_stream unpackStream;
unpackStream.zalloc = Z_NULL;
unpackStream.zfree = Z_NULL;
unpackStream.opaque = Z_NULL;
unpackStream.avail_in = (uInt) decryptedData.str().size();
unpackStream.next_in = (Bytef *) decryptedData.str().c_str();
unpackStream.avail_out = unpackedSize;
unpackStream.next_out = (Bytef *) unpackedData;
inflateInit(&unpackStream);
inflate(&unpackStream, Z_NO_FLUSH);
inflateEnd(&unpackStream);
std::ofstream output("Q:\\Lineage2_NA_175_152\\system\\ItemName-e-dec.dat", std::ios::binary);
output.write(unpackedData, unpackedSize);
mpz_clear(modulus);
mpz_clear(readBlock);
mpz_clear(modPowedBlock);
delete[] unpackedData;
return 0;
}
const char modulusBytes[] = { // any hex int representation }
conversion from 'int' to 'const char' requires a narrowing conversion
const unsigned char modulusBytes[] = { // any }
...
unpackStream.avail_in = (uInt) decryptedData.str().size();
unpackStream.next_in = (Bytef *) decryptedData.str().c_str();
...
// Костыляга
const std::string& tmp = decrypted->str();
const char* cstr = tmp.c_str();
std::vector<char> decompressedData(decompressedSize);
z_stream unpackStream;
unpackStream.zalloc = Z_NULL;
unpackStream.zfree = Z_NULL;
unpackStream.opaque = Z_NULL;
unpackStream.avail_in = (uInt)decrypted->str().size();
unpackStream.next_in = (Bytef*)cstr;
Привет, а можно тебя попросить выложить весь проект на C#? с Zlib и т.п буду очень благодарен!Вся соль в реализации BigInteger и Zlib в C#. В Java BigInteger - число без знака и имеет порядок байт Big Endian, в отличие от C#, где для беззнаковости нужно в конце добавить нулевой байт, а сам порядок байт для задания BigInteger - Little Endian. Данную проблему решили в .NET Core 2.1, скорее всего войдет в релиз .NET Standard 2.1.Вы не можете просматривать ссылку пожалуйста воспользуйтесь следующими ссылками Вход или Регистрация
Также в C# отсутствует поддержка Zlib, насколько я понял, поэтому нужно дополнительно выкачивать модуль Zlib (я брал из DotNetZip).
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?