230 lines
8.2 KiB
C#
230 lines
8.2 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
|
||
public static class BoardUtility
|
||
{
|
||
// Константы из Python скрипта
|
||
private const uint POLY_A = 0x54505353;
|
||
private const uint POLY_B = 0x504F5450;
|
||
private const uint POLY_C = 0x53534F54;
|
||
private const uint POLY_SEED = 0x5450534F;
|
||
private const byte XOR_KEY = 0xD7;
|
||
private const uint SERIAL_LENGTH = 9;
|
||
private const uint ENCRYPTED_LENGTH = 38;
|
||
private const uint GARBAGE_LENGTH = 12;
|
||
|
||
private const string CHARSET = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
||
private const string ENCODE_TABLE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+$";
|
||
|
||
/// <summary>
|
||
/// Расшифровка обфусцированного серийника (38 символов)
|
||
/// </summary>
|
||
public static string DecryptSerialObfuscated(string obfuscatedData)
|
||
{
|
||
if (string.IsNullOrEmpty(obfuscatedData))
|
||
throw new ArgumentException("Input data cannot be null or empty");
|
||
|
||
if (obfuscatedData.Length != ENCRYPTED_LENGTH)
|
||
throw new ArgumentException($"Invalid length: expected {ENCRYPTED_LENGTH}, got {obfuscatedData.Length}");
|
||
|
||
try
|
||
{
|
||
// Извлекаем закодированные данные, пропуская мусор
|
||
StringBuilder encoded = new StringBuilder();
|
||
|
||
// Пропускаем первые 4 символа мусора (0-3)
|
||
// Берем символы 4-12 (9 символов данных)
|
||
encoded.Append(obfuscatedData.Substring(4, 9));
|
||
|
||
// Пропускаем символы 13-16 (4 символа мусора)
|
||
// Берем символы 17-25 (9 символов данных)
|
||
encoded.Append(obfuscatedData.Substring(17, 9));
|
||
|
||
// Декодируем (18 символов -> 9 байт)
|
||
byte[] encrypted = new byte[9];
|
||
string encodedStr = encoded.ToString();
|
||
|
||
for (int i = 0; i < 18; i += 2)
|
||
{
|
||
byte decodedByte = DecodeFromBase64Like(encodedStr[i], encodedStr[i + 1]);
|
||
encrypted[i / 2] = decodedByte;
|
||
}
|
||
|
||
// Расшифровываем XOR
|
||
StringBuilder decrypted = new StringBuilder();
|
||
byte prev = 0x3C;
|
||
|
||
for (int i = 0; i < encrypted.Length; i++)
|
||
{
|
||
byte decryptedChar = (byte)(encrypted[i] ^ XOR_KEY ^ (i * 17) ^ prev);
|
||
decrypted.Append((char)decryptedChar);
|
||
prev = encrypted[i];
|
||
}
|
||
|
||
return decrypted.ToString();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new Exception($"Obfuscated decryption error: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Расшифровка HEX серийника (старый формат)
|
||
/// </summary>
|
||
public static string DecryptSerialHex(string encryptedHex)
|
||
{
|
||
if (string.IsNullOrEmpty(encryptedHex))
|
||
throw new ArgumentException("Input data cannot be null or empty");
|
||
|
||
try
|
||
{
|
||
byte[] encryptedBytes = HexStringToByteArray(encryptedHex);
|
||
StringBuilder decrypted = new StringBuilder();
|
||
byte prev = 0x3C;
|
||
|
||
for (int i = 0; i < encryptedBytes.Length; i++)
|
||
{
|
||
byte decryptedChar = (byte)(encryptedBytes[i] ^ XOR_KEY ^ (i * 17) ^ prev);
|
||
decrypted.Append((char)decryptedChar);
|
||
prev = encryptedBytes[i];
|
||
}
|
||
|
||
return decrypted.ToString();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new Exception($"HEX decryption error: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Универсальный метод расшифровки - автоматически определяет формат
|
||
/// </summary>
|
||
public static string DecryptSerial(string encryptedData)
|
||
{
|
||
if (string.IsNullOrEmpty(encryptedData))
|
||
throw new ArgumentException("Input data cannot be null or empty");
|
||
|
||
// Определяем формат по длине
|
||
if (encryptedData.Length == ENCRYPTED_LENGTH) // Обфусцированный формат
|
||
{
|
||
return DecryptSerialObfuscated(encryptedData);
|
||
}
|
||
else if (encryptedData.Length == SERIAL_LENGTH * 2) // HEX формат
|
||
{
|
||
return DecryptSerialHex(encryptedData);
|
||
}
|
||
else // Предполагаем, что это уже открытый серийник
|
||
{
|
||
return encryptedData;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Декодирование из base64-подобного формата
|
||
/// </summary>
|
||
private static byte DecodeFromBase64Like(char c1, char c2)
|
||
{
|
||
int val1 = ENCODE_TABLE.IndexOf(c1);
|
||
int val2 = ENCODE_TABLE.IndexOf(c2);
|
||
|
||
if (val1 == -1) val1 = 0;
|
||
if (val2 == -1) val2 = 0;
|
||
|
||
return (byte)(((val1 << 2) & 0xFC) | ((val2 >> 4) & 0x03));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Преобразование HEX строки в массив байт
|
||
/// </summary>
|
||
private static byte[] HexStringToByteArray(string hex)
|
||
{
|
||
if (hex.Length % 2 != 0)
|
||
throw new ArgumentException("Hex string must have even length");
|
||
|
||
byte[] bytes = new byte[hex.Length / 2];
|
||
for (int i = 0; i < bytes.Length; i++)
|
||
{
|
||
string byteValue = hex.Substring(i * 2, 2);
|
||
bytes[i] = Convert.ToByte(byteValue, 16);
|
||
}
|
||
return bytes;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Генерация ожидаемого серийника из MAC адреса (для проверки)
|
||
/// </summary>
|
||
public static string GenerateSerialFromMac(string macAddress)
|
||
{
|
||
if (string.IsNullOrEmpty(macAddress))
|
||
throw new ArgumentException("MAC address cannot be null or empty");
|
||
|
||
// Преобразуем MAC в число
|
||
string cleanMac = macAddress.Replace(":", "").Replace("-", "");
|
||
ulong macNumber = ulong.Parse(cleanMac, System.Globalization.NumberStyles.HexNumber);
|
||
|
||
ulong macLow = macNumber & 0xFFFFFFFF;
|
||
ulong macHigh = (macNumber >> 32) & 0xFFFF;
|
||
|
||
ulong x = macLow ^ macHigh;
|
||
|
||
// Полином с обрезкой до 64 бит
|
||
ulong result = ((ulong)POLY_A * x * x * x + (ulong)POLY_B * x * x + (ulong)POLY_C * x + (ulong)POLY_SEED) & 0xFFFFFFFFFFFFFFFF;
|
||
|
||
char[] serial = new char[SERIAL_LENGTH];
|
||
for (int i = 0; i < SERIAL_LENGTH; i++)
|
||
{
|
||
int index = (int)((result >> (i * 5)) % (ulong)CHARSET.Length);
|
||
serial[i] = CHARSET[index];
|
||
result = ((result * (ulong)POLY_A + (ulong)POLY_B) ^ (result >> 13)) & 0xFFFFFFFFFFFFFFFF;
|
||
}
|
||
|
||
return new string(serial);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверка валидности серийника для MAC адреса
|
||
/// </summary>
|
||
public static bool VerifySerial(string macAddress, string serialOrEncrypted)
|
||
{
|
||
try
|
||
{
|
||
string decryptedSerial = DecryptSerial(serialOrEncrypted);
|
||
string expectedSerial = GenerateSerialFromMac(macAddress);
|
||
return decryptedSerial == expectedSerial;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Проверка валидности серийника для MAC адреса с возвратом подробной информации
|
||
/// </summary>
|
||
public static (bool IsValid, string Message) VerifySerialDetailed(string macAddress, string serialOrEncrypted)
|
||
{
|
||
try
|
||
{
|
||
string decryptedSerial = DecryptSerial(serialOrEncrypted);
|
||
string expectedSerial = GenerateSerialFromMac(macAddress);
|
||
|
||
if (decryptedSerial == expectedSerial)
|
||
{
|
||
return (true, "Valid serial");
|
||
}
|
||
else
|
||
{
|
||
return (false, $"Invalid! Expected: {expectedSerial}, Got: {decryptedSerial}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return (false, $"Verification error: {ex.Message}");
|
||
}
|
||
}
|
||
}
|