Загрузить файлы в «/»
This commit is contained in:
BIN
BitVector.dll
Normal file
BIN
BitVector.dll
Normal file
Binary file not shown.
748
Program.cs
Normal file
748
Program.cs
Normal file
@@ -0,0 +1,748 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BitVectorLib;
|
||||
|
||||
namespace StegoHackathonConsole
|
||||
{
|
||||
class Program
|
||||
{
|
||||
// Настройки по умолчанию
|
||||
private static int _threads = Environment.ProcessorCount;
|
||||
private static bool _verbose = false;
|
||||
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
// Настройка кодировки для корректного вывода кириллицы
|
||||
Console.OutputEncoding = Encoding.UTF8;
|
||||
|
||||
PrintBanner();
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
PrintHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
var arguments = ParseArguments(args);
|
||||
string mode = arguments.GetValueOrDefault("mode") ?? string.Empty;
|
||||
mode = mode.ToLower();
|
||||
|
||||
// Глобальные настройки
|
||||
if (arguments.ContainsKey("threads"))
|
||||
int.TryParse(arguments["threads"], out _threads);
|
||||
if (arguments.ContainsKey("verbose"))
|
||||
_verbose = true;
|
||||
|
||||
try
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case "hack":
|
||||
await RunHackModeFast(arguments);
|
||||
break;
|
||||
case "encrypt":
|
||||
await RunEncryptMode(arguments);
|
||||
break;
|
||||
case "verify":
|
||||
await RunVerifyMode(arguments);
|
||||
break;
|
||||
default:
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine("ОШИБКА: Неизвестный режим работы.");
|
||||
Console.ResetColor();
|
||||
PrintHelp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"\nКРИТИЧЕСКАЯ ОШИБКА: {ex.Message}");
|
||||
if (_verbose) Console.WriteLine(ex.StackTrace);
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintBanner()
|
||||
{
|
||||
var process = Process.GetCurrentProcess();
|
||||
var gcInfo = GC.GetGCMemoryInfo();
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.WriteLine("================================================================================");
|
||||
Console.WriteLine(" STEGOHACKATHON CLI v1.0 ");
|
||||
Console.WriteLine("================================================================================");
|
||||
Console.ResetColor();
|
||||
|
||||
Console.WriteLine($" [SYSTEM] OS Description : {System.Runtime.InteropServices.RuntimeInformation.OSDescription}");
|
||||
Console.WriteLine($" [SYSTEM] Architecture : {System.Runtime.InteropServices.RuntimeInformation.OSArchitecture}");
|
||||
|
||||
string cpuName = GetCpuModelName();
|
||||
Console.WriteLine($" [CPU] Model : {cpuName}");
|
||||
Console.WriteLine($" [CPU] Logical Cores : {Environment.ProcessorCount}");
|
||||
|
||||
long totalRamMb = gcInfo.TotalAvailableMemoryBytes / 1024 / 1024;
|
||||
long appMemMb = process.WorkingSet64 / 1024 / 1024;
|
||||
Console.WriteLine($" [RAM] System Total : {totalRamMb:N0} MB");
|
||||
Console.WriteLine($" [RAM] App Current : {appMemMb:N0} MB");
|
||||
Console.WriteLine($" [PROC] Threads Limit : {_threads}");
|
||||
Console.WriteLine($" [PROC] GC Mode : {(System.Runtime.GCSettings.IsServerGC ? "Server" : "Workstation")}");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.WriteLine("================================================================================\n");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
static string GetCpuModelName()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux))
|
||||
{
|
||||
if (File.Exists("/proc/cpuinfo"))
|
||||
{
|
||||
var lines = File.ReadAllLines("/proc/cpuinfo");
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.StartsWith("model name", StringComparison.OrdinalIgnoreCase) ||
|
||||
line.StartsWith("Model", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var parts = line.Split(':');
|
||||
if (parts.Length > 1) return parts[1].Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
|
||||
{
|
||||
string? name = Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER");
|
||||
if (!string.IsNullOrEmpty(name)) return name;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return "Unknown / Generic";
|
||||
}
|
||||
|
||||
static void PrintHelp()
|
||||
{
|
||||
Console.WriteLine("Использование:");
|
||||
Console.WriteLine(" --mode hack --data <файл.bin> --book <книга.txt> [--threads <N>]");
|
||||
Console.WriteLine(" --mode encrypt --book <книга.txt> --out <вывод.bin>");
|
||||
Console.WriteLine(" --mode verify --data <файл.bin> --key <ключ.key.bin>");
|
||||
Console.WriteLine("\nПример:");
|
||||
Console.WriteLine(" dotnet run -- --mode hack --data task1.bin --book WarAndPeace.txt --threads 8");
|
||||
}
|
||||
|
||||
static Dictionary<string, string> ParseArguments(string[] args)
|
||||
{
|
||||
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i].StartsWith("--") || args[i].StartsWith("-"))
|
||||
{
|
||||
string key = args[i].TrimStart('-');
|
||||
string val = "true";
|
||||
if (i + 1 < args.Length && !args[i + 1].StartsWith("-"))
|
||||
{
|
||||
val = args[i + 1];
|
||||
i++;
|
||||
}
|
||||
dict[key] = val;
|
||||
}
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
// --- РЕЖИМ 1: ОПТИМИЗИРОВАННЫЙ ВЗЛОМ ---
|
||||
static async Task RunHackModeFast(Dictionary<string, string> args)
|
||||
{
|
||||
string stegoPath = args.GetValueOrDefault("data") ?? string.Empty;
|
||||
string bookPath = args.GetValueOrDefault("book") ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(stegoPath) || !File.Exists(stegoPath))
|
||||
{
|
||||
Console.WriteLine("[ERR] Файл данных не найден.");
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(bookPath) || !File.Exists(bookPath))
|
||||
{
|
||||
Console.WriteLine("[ERR] Файл книги не найден.");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"[INIT] Чтение файлов...");
|
||||
byte[] stegoBytes = await File.ReadAllBytesAsync(stegoPath);
|
||||
string bookText = await File.ReadAllTextAsync(bookPath, Encoding.UTF8);
|
||||
byte[] bookBytes = Encoding.UTF8.GetBytes(bookText);
|
||||
|
||||
Console.WriteLine($"[DATA] Стего-контейнер: {stegoBytes.Length} байт");
|
||||
Console.WriteLine($"[BOOK] Книга: {bookBytes.Length} байт");
|
||||
|
||||
string[] etalonStrings = AssocStego.GetDefaultEtalons10();
|
||||
int etalonLen = etalonStrings[0].Length;
|
||||
int blockByteSize = (etalonLen + 7) / 8;
|
||||
|
||||
byte[][] etalonsRaw = new byte[10][];
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
etalonsRaw[i] = new BitVector(etalonStrings[i]).ToByteArray();
|
||||
}
|
||||
|
||||
int bytesEncrypted = stegoBytes.Length / (3 * blockByteSize);
|
||||
Console.WriteLine($"[INFO] Длина блока: {blockByteSize} байт. Скрыто символов: {bytesEncrypted}");
|
||||
|
||||
List<byte[]> stegoChunks = new List<byte[]>(bytesEncrypted * 3);
|
||||
for (int i = 0; i < stegoBytes.Length; i += blockByteSize)
|
||||
{
|
||||
byte[] chunk = new byte[blockByteSize];
|
||||
Array.Copy(stegoBytes, i, chunk, 0, blockByteSize);
|
||||
stegoChunks.Add(chunk);
|
||||
}
|
||||
|
||||
int maxOffset = bookBytes.Length - bytesEncrypted;
|
||||
if (maxOffset <= 0)
|
||||
{
|
||||
Console.WriteLine("[FAIL] Размер книги меньше данных.");
|
||||
return;
|
||||
}
|
||||
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
long processed = 0;
|
||||
bool found = false;
|
||||
object lockObj = new object();
|
||||
|
||||
Console.WriteLine($"[RUN ] СТАРТ ПЕРЕБОРА (TURBO MODE) на {_threads} потоках...");
|
||||
|
||||
using var timer = new Timer(_ =>
|
||||
{
|
||||
long current = Interlocked.Read(ref processed);
|
||||
double progress = (double)current / maxOffset * 100.0;
|
||||
double speed = current / sw.Elapsed.TotalSeconds;
|
||||
long ram = Process.GetCurrentProcess().WorkingSet64 / 1024 / 1024;
|
||||
Console.Write($"\r[PROG] {progress:F2}% | Speed: {speed:N0} ops/s | RAM: {ram} MB | Time: {sw.Elapsed:mm\\:ss} ");
|
||||
}, null, 1000, 1000);
|
||||
|
||||
var pOptions = new ParallelOptions { MaxDegreeOfParallelism = _threads };
|
||||
|
||||
Parallel.For(0, maxOffset + 1, pOptions, (i, state) =>
|
||||
{
|
||||
if (found) { state.Stop(); return; }
|
||||
|
||||
if ((i & 4095) == 0) Interlocked.Add(ref processed, 4096);
|
||||
|
||||
byte[][] currentMasks = new byte[10][];
|
||||
for (int k = 0; k < 10; k++)
|
||||
{
|
||||
currentMasks[k] = new byte[blockByteSize];
|
||||
for (int b = 0; b < blockByteSize; b++) currentMasks[k][b] = 0xFF;
|
||||
}
|
||||
|
||||
bool[] digitUsed = new bool[10];
|
||||
bool possible = true;
|
||||
int quickCheckLen = (bytesEncrypted < 15) ? bytesEncrypted : 15;
|
||||
|
||||
for (int j = 0; j < quickCheckLen; j++)
|
||||
{
|
||||
byte b = bookBytes[i + j];
|
||||
int d2 = b / 100;
|
||||
int d1 = (b % 100) / 10;
|
||||
int d0 = b % 10;
|
||||
int idx = j * 3;
|
||||
|
||||
FastAndNotXor(currentMasks[d2], stegoChunks[idx], etalonsRaw[d2]);
|
||||
FastAndNotXor(currentMasks[d1], stegoChunks[idx + 1], etalonsRaw[d1]);
|
||||
FastAndNotXor(currentMasks[d0], stegoChunks[idx + 2], etalonsRaw[d0]);
|
||||
|
||||
digitUsed[d2] = true;
|
||||
digitUsed[d1] = true;
|
||||
digitUsed[d0] = true;
|
||||
}
|
||||
|
||||
for (int k = 0; k < 10; k++)
|
||||
{
|
||||
if (digitUsed[k])
|
||||
{
|
||||
if (FastPopCount(currentMasks[k]) < 2)
|
||||
{
|
||||
possible = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!possible) return;
|
||||
|
||||
for (int k = 0; k < 10; k++)
|
||||
{
|
||||
for (int b = 0; b < blockByteSize; b++) currentMasks[k][b] = 0xFF;
|
||||
digitUsed[k] = false;
|
||||
}
|
||||
|
||||
for (int j = 0; j < bytesEncrypted; j++)
|
||||
{
|
||||
byte b = bookBytes[i + j];
|
||||
int d2 = b / 100;
|
||||
int d1 = (b % 100) / 10;
|
||||
int d0 = b % 10;
|
||||
int idx = j * 3;
|
||||
|
||||
FastAndNotXor(currentMasks[d2], stegoChunks[idx], etalonsRaw[d2]);
|
||||
FastAndNotXor(currentMasks[d1], stegoChunks[idx + 1], etalonsRaw[d1]);
|
||||
FastAndNotXor(currentMasks[d0], stegoChunks[idx + 2], etalonsRaw[d0]);
|
||||
|
||||
digitUsed[d2] = true; digitUsed[d1] = true; digitUsed[d0] = true;
|
||||
}
|
||||
|
||||
int totalBits = 0;
|
||||
for (int k = 0; k < 10; k++)
|
||||
{
|
||||
if (digitUsed[k])
|
||||
totalBits += FastPopCount(currentMasks[k]);
|
||||
}
|
||||
|
||||
if (totalBits > 5)
|
||||
{
|
||||
lock (lockObj)
|
||||
{
|
||||
if (!found)
|
||||
{
|
||||
found = true;
|
||||
sw.Stop();
|
||||
Console.WriteLine($"\n\n[SUCCESS] Взлом успешен! Offset: {i}");
|
||||
Console.WriteLine($"[TIME] Время: {sw.ElapsedMilliseconds} мс");
|
||||
SaveFoundKey(stegoPath, bookBytes, i, bytesEncrypted, currentMasks, etalonStrings, etalonLen);
|
||||
state.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!found) Console.WriteLine("\n[FAIL] Совпадений не найдено.");
|
||||
}
|
||||
|
||||
static void FastAndNotXor(byte[] target, byte[] stego, byte[] etalon)
|
||||
{
|
||||
for (int i = 0; i < target.Length; i++)
|
||||
{
|
||||
target[i] = (byte)(target[i] & ~(stego[i] ^ etalon[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static int FastPopCount(byte[] data)
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
int n = data[i];
|
||||
while (n != 0)
|
||||
{
|
||||
count++;
|
||||
n &= (n - 1);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void SaveFoundKey(string stegoPath, byte[] bookBytes, int offset, int len, byte[][] masksRaw, string[] etalonsStr, int etalonLen)
|
||||
{
|
||||
byte[] decrypted = new byte[len];
|
||||
Array.Copy(bookBytes, offset, decrypted, 0, len);
|
||||
string text = Encoding.UTF8.GetString(decrypted);
|
||||
|
||||
Console.WriteLine("--- НАЧАЛО ТЕКСТА ---");
|
||||
Console.WriteLine(text.Substring(0, Math.Min(100, text.Length)) + "...");
|
||||
Console.WriteLine("---------------------");
|
||||
|
||||
BitVector[] keyVectors = new BitVector[10];
|
||||
for (int k = 0; k < 10; k++)
|
||||
{
|
||||
keyVectors[k] = new BitVector(masksRaw[k], etalonLen);
|
||||
}
|
||||
|
||||
var sys = new AssocStego(etalonsStr);
|
||||
string keyPath = Path.ChangeExtension(stegoPath, ".recovered.key.bin");
|
||||
string txtPath = Path.ChangeExtension(stegoPath, ".txt");
|
||||
|
||||
sys.SaveKeyToFile(keyPath, keyVectors);
|
||||
File.WriteAllText(txtPath, text);
|
||||
|
||||
Console.WriteLine($"[SAVE] Ключ: {keyPath}");
|
||||
Console.WriteLine($"[SAVE] Текст: {txtPath}");
|
||||
}
|
||||
|
||||
// --- РЕЖИМ 2: ШИФРОВАНИЕ ---
|
||||
static async Task RunEncryptMode(Dictionary<string, string> args)
|
||||
{
|
||||
string bookPath = args.GetValueOrDefault("book") ?? string.Empty;
|
||||
string outFile = args.GetValueOrDefault("out") ?? "output.bin";
|
||||
|
||||
if (string.IsNullOrEmpty(bookPath) || !File.Exists(bookPath)) throw new FileNotFoundException("Книга не найдена.");
|
||||
|
||||
string text = await File.ReadAllTextAsync(bookPath);
|
||||
Random rnd = new Random();
|
||||
int len = rnd.Next(200, 600);
|
||||
int start = rnd.Next(0, text.Length - len);
|
||||
string secretText = text.Substring(start, len);
|
||||
|
||||
Console.WriteLine($"[ENCRYPT] Фрагмент: \"{secretText.Substring(0, Math.Min(30, secretText.Length))}...\"");
|
||||
|
||||
var etalons = AssocStego.GetDefaultEtalons10();
|
||||
var system = new AssocStego(etalons);
|
||||
|
||||
Console.WriteLine("[KEY] Генерация масок...");
|
||||
var keys = system.CreateKey();
|
||||
|
||||
string keyFile = Path.ChangeExtension(outFile, ".key.bin");
|
||||
system.SaveKeyToFile(keyFile, keys);
|
||||
|
||||
Console.WriteLine($"[WRITE] Запись в {outFile}...");
|
||||
byte[] data = Encoding.UTF8.GetBytes(secretText);
|
||||
|
||||
using (var fs = new FileStream(outFile, FileMode.Create))
|
||||
using (var ss = new AssocStegoStream(fs, system))
|
||||
{
|
||||
ss.Write(data, 0, data.Length);
|
||||
}
|
||||
Console.WriteLine("[DONE] Успешно.");
|
||||
}
|
||||
|
||||
// --- РЕЖИМ 3: ПРОВЕРКА ---
|
||||
static async Task RunVerifyMode(Dictionary<string, string> args)
|
||||
{
|
||||
string dataPath = args.GetValueOrDefault("data") ?? string.Empty;
|
||||
string keyPath = args.GetValueOrDefault("key") ?? string.Empty;
|
||||
|
||||
if (!File.Exists(dataPath) || !File.Exists(keyPath)) throw new FileNotFoundException("Файлы не найдены.");
|
||||
|
||||
var etalons = AssocStego.GetDefaultEtalons10();
|
||||
var system = new AssocStego(etalons);
|
||||
|
||||
Console.WriteLine($"[LOAD] Ключ: {keyPath}");
|
||||
system.Key = system.LoadKeyFromFile(keyPath);
|
||||
|
||||
byte[] encrypted = await File.ReadAllBytesAsync(dataPath);
|
||||
int containerByteLen = (system.etalonLength + 7) / 8;
|
||||
int count = encrypted.Length / (containerByteLen * 3);
|
||||
byte[] buffer = new byte[count];
|
||||
|
||||
Console.WriteLine($"[DECRYPT] Расшифровка...");
|
||||
using (var ms = new MemoryStream(encrypted))
|
||||
using (var ss = new AssocStegoStream(ms, system))
|
||||
{
|
||||
ss.Read(buffer, 0, count);
|
||||
}
|
||||
Console.WriteLine($"[TEXT] {Encoding.UTF8.GetString(buffer)}");
|
||||
}
|
||||
}
|
||||
|
||||
// --- КЛАССЫ ЛОГИКИ (ИСПОЛЬЗУЮТ BitVectorLib) ---
|
||||
|
||||
public class AssocStegoStream : Stream
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly AssocStego _stegoAlg;
|
||||
private readonly int _containerByteLen;
|
||||
private readonly int _hiddenByteLen;
|
||||
|
||||
public AssocStegoStream(Stream stream, AssocStego stegoAlg)
|
||||
{
|
||||
_stream = stream;
|
||||
_stegoAlg = stegoAlg;
|
||||
_containerByteLen = (_stegoAlg.etalonLength + 7) / 8;
|
||||
_hiddenByteLen = 3 * _containerByteLen;
|
||||
}
|
||||
|
||||
public override bool CanRead => _stream.CanRead;
|
||||
public override bool CanSeek => false;
|
||||
public override bool CanWrite => _stream.CanWrite;
|
||||
public override long Length => _stream.Length;
|
||||
public override long Position { get => _stream.Position; set => throw new NotSupportedException(); }
|
||||
public override void Flush() => _stream.Flush();
|
||||
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
|
||||
public override void SetLength(long value) => throw new NotSupportedException();
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
for (int i = offset; i < offset + count; i++)
|
||||
{
|
||||
byte[] hidden = HideByte(buffer[i]);
|
||||
_stream.Write(hidden, 0, hidden.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int readCount = 0;
|
||||
for (int i = offset; i < offset + count; i++)
|
||||
{
|
||||
var hidden = new byte[_hiddenByteLen];
|
||||
int totalRead = 0;
|
||||
while (totalRead < _hiddenByteLen)
|
||||
{
|
||||
int r = _stream.Read(hidden, totalRead, _hiddenByteLen - totalRead);
|
||||
if (r == 0) return readCount;
|
||||
totalRead += r;
|
||||
}
|
||||
buffer[i] = DiscloseByte(hidden);
|
||||
readCount++;
|
||||
}
|
||||
return readCount;
|
||||
}
|
||||
|
||||
private byte[] HideByte(byte value)
|
||||
{
|
||||
int v = value;
|
||||
int d2 = v / 100;
|
||||
int rem = v % 100;
|
||||
int d1 = rem / 10;
|
||||
int d0 = rem % 10;
|
||||
|
||||
BitVector c2 = _stegoAlg.HideEtalon(d2);
|
||||
BitVector c1 = _stegoAlg.HideEtalon(d1);
|
||||
BitVector c0 = _stegoAlg.HideEtalon(d0);
|
||||
|
||||
byte[] b2 = c2.ToByteArray();
|
||||
byte[] b1 = c1.ToByteArray();
|
||||
byte[] b0 = c0.ToByteArray();
|
||||
|
||||
byte[] result = new byte[b2.Length + b1.Length + b0.Length];
|
||||
int pos = 0;
|
||||
Buffer.BlockCopy(b2, 0, result, pos, b2.Length); pos += b2.Length;
|
||||
Buffer.BlockCopy(b1, 0, result, pos, b1.Length); pos += b1.Length;
|
||||
Buffer.BlockCopy(b0, 0, result, pos, b0.Length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte DiscloseByte(byte[] hidden)
|
||||
{
|
||||
byte[] b2 = new byte[_containerByteLen];
|
||||
byte[] b1 = new byte[_containerByteLen];
|
||||
byte[] b0 = new byte[_containerByteLen];
|
||||
|
||||
int pos = 0;
|
||||
Buffer.BlockCopy(hidden, pos, b2, 0, _containerByteLen); pos += _containerByteLen;
|
||||
Buffer.BlockCopy(hidden, pos, b1, 0, _containerByteLen); pos += _containerByteLen;
|
||||
Buffer.BlockCopy(hidden, pos, b0, 0, _containerByteLen);
|
||||
|
||||
int d2 = _stegoAlg.DiscloseEtalon(new BitVector(b2, _stegoAlg.etalonLength));
|
||||
int d1 = _stegoAlg.DiscloseEtalon(new BitVector(b1, _stegoAlg.etalonLength));
|
||||
int d0 = _stegoAlg.DiscloseEtalon(new BitVector(b0, _stegoAlg.etalonLength));
|
||||
|
||||
if (d2 < 0 || d1 < 0 || d0 < 0) return 63;
|
||||
return (byte)(d2 * 100 + d1 * 10 + d0);
|
||||
}
|
||||
}
|
||||
|
||||
public class AssocStego
|
||||
{
|
||||
public readonly BitVector[] etalons;
|
||||
public readonly int etalonLength;
|
||||
private readonly List<int>[] maskUnitPositions;
|
||||
|
||||
private BitVector[]? key;
|
||||
private readonly BitVector zeroVector;
|
||||
|
||||
// Свойство выбрасывает исключение, если ключ не задан
|
||||
public BitVector[] Key
|
||||
{
|
||||
get => key ?? throw new InvalidOperationException("Ключ не сгенерирован!");
|
||||
set => key = value;
|
||||
}
|
||||
|
||||
public AssocStego(params string[] e)
|
||||
{
|
||||
etalonLength = e[0].Length;
|
||||
etalons = new BitVector[e.Length];
|
||||
maskUnitPositions = new List<int>[e.Length];
|
||||
zeroVector = new BitVector(new byte[(etalonLength + 7) / 8], etalonLength);
|
||||
|
||||
for (int i = 0; i < e.Length; i++)
|
||||
{
|
||||
etalons[i] = new BitVector(e[i]);
|
||||
maskUnitPositions[i] = new List<int>();
|
||||
}
|
||||
}
|
||||
|
||||
public BitVector[] CreateKey()
|
||||
{
|
||||
var eIndexes = Enumerable.Range(0, etalons.Length).ToList();
|
||||
ResetMaskUnitPositions();
|
||||
CreateKeyRec(eIndexes, true);
|
||||
var masks = new BitVector[etalons.Length];
|
||||
for (int i = 0; i < etalons.Length; i++)
|
||||
masks[i] = CreateVector(etalonLength, maskUnitPositions[i].ToArray());
|
||||
Key = masks;
|
||||
return masks;
|
||||
}
|
||||
|
||||
private void CreateKeyRec(List<int> eIndexes, bool firstPerm)
|
||||
{
|
||||
if (eIndexes.Count < 2) return;
|
||||
var c = firstPerm ? Shuffle(eIndexes) : eIndexes;
|
||||
var d1 = new List<int> { c[0] };
|
||||
BitVector a0 = etalons[c[0]] ^ etalons[c[1]];
|
||||
|
||||
for (int i = 0; i < c.Count - 2; i++)
|
||||
{
|
||||
BitVector a1 = etalons[c[0]] ^ etalons[c[i + 2]];
|
||||
BitVector a3 = a0 & a1;
|
||||
if (IsZero(a3)) d1.Add(c[i + 2]);
|
||||
else a0 = a3;
|
||||
}
|
||||
|
||||
int rndBit = GetRandomOneBitIndex(a0);
|
||||
foreach (int idx in c) maskUnitPositions[idx].Add(rndBit);
|
||||
var d2 = c.Except(d1).ToList();
|
||||
|
||||
Parallel.Invoke(() => CreateKeyRec(d1, false), () => CreateKeyRec(d2, false));
|
||||
}
|
||||
|
||||
private bool IsZero(BitVector v)
|
||||
{
|
||||
foreach (byte b in v.ToByteArray()) if (b != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<int> Shuffle(List<int> list)
|
||||
{
|
||||
var res = new List<int>(list);
|
||||
var rng = new Random();
|
||||
int n = res.Count;
|
||||
while (n > 1) { n--; int k = rng.Next(n + 1); (res[k], res[n]) = (res[n], res[k]); }
|
||||
return res;
|
||||
}
|
||||
|
||||
private void ResetMaskUnitPositions() { foreach (var l in maskUnitPositions) l.Clear(); }
|
||||
|
||||
private int GetRandomOneBitIndex(BitVector v)
|
||||
{
|
||||
var bytes = v.ToByteArray();
|
||||
var ones = new List<int>();
|
||||
for (int i = 0; i < bytes.Length * 8; i++)
|
||||
{
|
||||
int byteIdx = i / 8;
|
||||
if (byteIdx >= bytes.Length) break;
|
||||
int bitIdx = 7 - (i % 8);
|
||||
if ((bytes[byteIdx] & (1 << bitIdx)) != 0) ones.Add(i);
|
||||
}
|
||||
if (ones.Count == 0) return 0;
|
||||
return ones[new Random().Next(ones.Count)];
|
||||
}
|
||||
|
||||
private BitVector CreateVector(int len, int[] indexes)
|
||||
{
|
||||
int byteLen = (len + 7) / 8;
|
||||
byte[] arr = new byte[byteLen];
|
||||
foreach (int idx in indexes)
|
||||
{
|
||||
int byteIndex = idx / 8;
|
||||
int bitIndex = 7 - (idx % 8);
|
||||
if (byteIndex < arr.Length) arr[byteIndex] |= (byte)(1 << bitIndex);
|
||||
}
|
||||
return new BitVector(arr, len);
|
||||
}
|
||||
|
||||
public BitVector HideEtalon(int idx)
|
||||
{
|
||||
byte[] randomBytes = new byte[(etalonLength + 7) / 8];
|
||||
RandomNumberGenerator.Fill(randomBytes);
|
||||
BitVector container = new BitVector(randomBytes, etalonLength);
|
||||
return container ^ ((container ^ etalons[idx]) & Key[idx]);
|
||||
}
|
||||
|
||||
public int DiscloseEtalon(BitVector stego)
|
||||
{
|
||||
for (int i = 0; i < etalons.Length; i++)
|
||||
{
|
||||
BitVector a = stego & Key[i];
|
||||
BitVector b = etalons[i] & Key[i];
|
||||
if (a.ToByteArray().SequenceEqual(b.ToByteArray())) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void SaveKeyToFile(string path, BitVector[] k)
|
||||
{
|
||||
using var bw = new BinaryWriter(File.Create(path));
|
||||
bw.Write(0x41534B59);
|
||||
bw.Write(k.Length);
|
||||
bw.Write(etalonLength);
|
||||
foreach (var vec in k)
|
||||
{
|
||||
byte[] b = vec.ToByteArray();
|
||||
bw.Write(b.Length);
|
||||
bw.Write(b);
|
||||
}
|
||||
}
|
||||
|
||||
public BitVector[] LoadKeyFromFile(string path)
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(path));
|
||||
if (br.ReadInt32() != 0x41534B59) throw new Exception("Bad magic");
|
||||
int count = br.ReadInt32();
|
||||
int len = br.ReadInt32();
|
||||
var res = new BitVector[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int blen = br.ReadInt32();
|
||||
byte[] b = br.ReadBytes(blen);
|
||||
res[i] = new BitVector(b, len);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static string[] GetDefaultEtalons10() => new[] {
|
||||
"111111111111111111111111111111111111111111111111111111000000000000000000000000",
|
||||
"000000000100000000000000000111111111111111111100000000000000000000000011111111",
|
||||
"100000000000000000111111111111111111100000000111111111111111110000000000000000",
|
||||
"100000000100000000111111111100000000100000000000000000111111111111111111111111",
|
||||
"000000000111111111100000000111111111111111111100000000000000001111111100000000",
|
||||
"100000000111111111111111111100000000111111111111111111000000001111111100000000",
|
||||
"111111111100000000000000000100000000111111111111111111000000001111111111111111",
|
||||
"111111111100000000111111111100000000000000000000000000000000000000000011111111",
|
||||
"111111111111111111111111111111111111111111111111111111000000001111111100000000",
|
||||
"100000000111111111111111111111111111100000000000000000111111111111111100000000"
|
||||
}.Select(s => new string(s.Reverse().ToArray())).ToArray();
|
||||
}
|
||||
|
||||
public static class BitVectorHelpers
|
||||
{
|
||||
public static BitVector CreateFullMask(int bitLength)
|
||||
{
|
||||
int byteLen = (bitLength + 7) / 8;
|
||||
byte[] b = new byte[byteLen];
|
||||
for (int i = 0; i < byteLen; i++) b[i] = 0xFF;
|
||||
return new BitVector(b, bitLength);
|
||||
}
|
||||
public static BitVector CreateZeroMask(int bitLength) => new BitVector(new byte[(bitLength + 7) / 8], bitLength);
|
||||
public static BitVector AndNot(BitVector a, BitVector b)
|
||||
{
|
||||
byte[] ba = a.ToByteArray();
|
||||
byte[] bb = b.ToByteArray();
|
||||
int len = Math.Min(ba.Length, bb.Length);
|
||||
byte[] result = new byte[ba.Length];
|
||||
for (int i = 0; i < len; i++) result[i] = (byte)(ba[i] & ~bb[i]);
|
||||
return new BitVector(result, a.ToByteArray().Length * 8);
|
||||
}
|
||||
public static int PopCount(BitVector v)
|
||||
{
|
||||
byte[] bytes = v.ToByteArray();
|
||||
int count = 0;
|
||||
foreach (byte b in bytes)
|
||||
{
|
||||
int n = b;
|
||||
while (n > 0) { if ((n & 1) == 1) count++; n >>= 1; }
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
2188
Selyankin_Kogda-truba-zovet.txt
Normal file
2188
Selyankin_Kogda-truba-zovet.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
stego_data.bin
Normal file
BIN
stego_data.bin
Normal file
Binary file not shown.
4
stego_data.txt
Normal file
4
stego_data.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
в не слышно. И это закономерно: только специалист скажет, какая это мина — магнитная, взрывающаяся от присутствия железа, или акустическая. Вот и замер порт, притаился: если мина акустическая, не взорвется она в тишине, будет смирнехонько лежать на своем металлическом ложе, дождется минера.
|
||||
Однако сколько ни стой, а к мине идти нужно…
|
||||
Майор взял чемоданчик, стоявший у ног, и степенно пошел к шлюпке, поджидавшей его у берега. Теперь от его недавней торопливости не осталось и следа. Не потому, что онхотел порисоваться перед людьми, которые с тревогой и уважением смотрели на него. Просто Александр Николаевич знал, что к мине нельзя подходить взволнованным: минер во время работы должен быть всегда спокоен, наблюдателен и расчетлив. Даже то, что он бежал по городу в порт, а не приехал сюда на машине, подчинено этой же цели: бег поглотил излишнюю нервозность.
|
||||
Когда шлюпка отошла от землечерпалки, майор Варзин склонился над миной. Чуть короче человека, но зато толще его, она лежала в
|
||||
Reference in New Issue
Block a user