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+$"; /// /// Расшифровка обфусцированного серийника (38 символов) /// 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); } } /// /// Расшифровка HEX серийника (старый формат) /// 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); } } /// /// Универсальный метод расшифровки - автоматически определяет формат /// 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; } } /// /// Декодирование из base64-подобного формата /// 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)); } /// /// Преобразование HEX строки в массив байт /// 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; } /// /// Генерация ожидаемого серийника из MAC адреса (для проверки) /// 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); } /// /// Проверка валидности серийника для MAC адреса /// public static bool VerifySerial(string macAddress, string serialOrEncrypted) { try { string decryptedSerial = DecryptSerial(serialOrEncrypted); string expectedSerial = GenerateSerialFromMac(macAddress); return decryptedSerial == expectedSerial; } catch { return false; } } /// /// Проверка валидности серийника для MAC адреса с возвратом подробной информации /// 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}"); } } }