Загрузить файлы в «/»

This commit is contained in:
2025-12-11 15:08:48 +03:00
parent 010a7ea23e
commit 6f696bcb60
5 changed files with 2940 additions and 0 deletions

BIN
BitVector.dll Normal file

Binary file not shown.

748
Program.cs Normal file
View 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;
}
}
}

File diff suppressed because it is too large Load Diff

BIN
stego_data.bin Normal file

Binary file not shown.

4
stego_data.txt Normal file
View File

@@ -0,0 +1,4 @@
в не слышно. И это закономерно: только специалист скажет, какая это мина — магнитная, взрывающаяся от присутствия железа, или акустическая. Вот и замер порт, притаился: если мина акустическая, не взорвется она в тишине, будет смирнехонько лежать на своем металлическом ложе, дождется минера.
Однако сколько ни стой, а к мине идти нужно…
Майор взял чемоданчик, стоявший у ног, и степенно пошел к шлюпке, поджидавшей его у берега. Теперь от его недавней торопливости не осталось и следа. Не потому, что онхотел порисоваться перед людьми, которые с тревогой и уважением смотрели на него. Просто Александр Николаевич знал, что к мине нельзя подходить взволнованным: минер во время работы должен быть всегда спокоен, наблюдателен и расчетлив. Даже то, что он бежал по городу в порт, а не приехал сюда на машине, подчинено этой же цели: бег поглотил излишнюю нервозность.
Когда шлюпка отошла от землечерпалки, майор Варзин склонился над миной. Чуть короче человека, но зато толще его, она лежала в