using System.Text; using System.Text.RegularExpressions; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; namespace VberZero.Tools; public static class ImageLSB_Encoder { #region 隐藏、提取 消息 #region Base64 /// /// 将给定的消息(文件)隐藏在给定的载体(图像)中 /// /// /// /// public static string HideMessageBackBase64(string path, string message) { var payload = Encoding.UTF8.GetBytes(message); return HideBytesBackBase64(path, payload); } /// /// 将给定的消息(文件)隐藏在给定的载体(图像)中 /// /// /// /// public static string HideMessageBackBase64(this Image carrierImage, string message) { var payload = Encoding.UTF8.GetBytes(message); return HideBytesBackBase64(carrierImage, payload); } /// /// 将给定的消息(文件)隐藏在给定的载体(图像)中 /// /// /// /// public static string HideBytesBackBase64(string path, byte[] payload) { using var carrierImage = (Image)Image.Load(path); return HideBytesBackBase64(carrierImage, payload); } /// /// 将给定的消息(文件)隐藏在给定的载体(图像)中 /// /// /// /// public static string HideBytesBackBase64(this Image carrierImage, byte[] payload) { var image = HideBytes(carrierImage, payload); var base64Str = image.ToBase64String(PngFormat.Instance); return base64Str; } /// /// 从stego图像(base64String)中提取字符串消息 /// /// /// public static string ExtractMessageFromBase64(string base64String) { base64String = base64String.StartsWith("data") ? base64String.Split(',')[1] : base64String; byte[] data = Convert.FromBase64String(base64String); using var image = (Image)Image.Load(data); var payload = ExtractBytes(image); var message = Encoding.UTF8.GetString(payload); return message; } /// /// 从stego图像(base64String)中提取消息并将其作为字节数组返回 /// /// /// public static byte[] ExtractBytesFromBase64(string base64String) { base64String = base64String.StartsWith("data") ? base64String.Split(',')[1] : base64String; byte[] data = Convert.FromBase64String(base64String); using var image = (Image)Image.Load(data); var payload = ExtractBytes(image); return payload; } #endregion Base64 #region Hide /// /// 将给定的消息隐藏在给定的载体(图像)中 /// /// /// /// public static Image HideMessage(string path, string message) { using var carrierImage = (Image)Image.Load(path); return HideMessage(carrierImage, message); } /// /// 将给定的消息隐藏在给定的载体(图像)中 /// /// /// /// public static Image HideMessage(this Image carrierImage, string message) { var payload = Encoding.UTF8.GetBytes(message); return HideBytes(carrierImage, payload); } /// /// 将给定的消息(文件)隐藏在给定的载体(图像)中 /// /// /// /// public static Image HideBytes(string path, byte[] payload) { using var carrierImage = (Image)Image.Load(path); return HideBytes(carrierImage, payload); } /// /// 将给定的消息(文件)隐藏在给定的载体(图像)中 /// /// /// /// public static Image HideBytes(this Image carrierImage, byte[] payload) { //var stegoImage = carrierImage.Clone(); var stegoImage = carrierImage.Clone(); //获取消息对象的二进制字符串及其长度 var completeMessage = GenerateMessageBitPattern(payload); var completeMessageLength = (uint)completeMessage.Length; //如果消息太大,则引发异常 var carrierCapacity = CalculateCapacity(carrierImage, "bits"); if (completeMessageLength > carrierCapacity) throw new Exception("MessageTooBigException"); var pixelX = 0; var pixelY = 0; var messageBitCounter = 0; while (messageBitCounter < completeMessageLength) { if (pixelY >= stegoImage.Height) { break; } // 获取当前像素 var pixel = stegoImage[pixelX, pixelY]; //选择使用像素的三个LSB中的R、G、B byte color; switch (messageBitCounter % 3) { case 0: color = InsertMessageBit(pixel.R, completeMessage[messageBitCounter].ToString()); stegoImage[pixelX, pixelY] = new Rgba32(color, pixel.G, pixel.B, pixel.A); break; case 1: color = InsertMessageBit(pixel.G, completeMessage[messageBitCounter].ToString()); stegoImage[pixelX, pixelY] = new Rgba32(pixel.R, color, pixel.B, pixel.A); break; case 2: color = InsertMessageBit(pixel.B, completeMessage[messageBitCounter].ToString()); stegoImage[pixelX, pixelY] = new Rgba32(pixel.R, pixel.G, color, pixel.A); // 获取下一个像素 pixelX++; if (pixelX >= stegoImage.Width) { pixelX = 0; pixelY++; } break; } messageBitCounter++; } return stegoImage; } #endregion Hide #region Extract /// /// 从stego图像中提取字符串消息 /// /// /// public static string ExtractMessage(string path) { using var image = (Image)Image.Load(path); var payload = ExtractBytes(image); var message = Encoding.UTF8.GetString(payload); return message; } /// /// 从stego图像中提取字符串消息 /// /// /// public static string ExtractMessage(this Image carrierImage) { var payload = ExtractBytes(carrierImage); var message = Encoding.UTF8.GetString(payload); return message; } /// /// 从stego图像中提取消息并将其作为字节数组返回 /// /// /// public static byte[] ExtractBytes(string path) { using var image = (Image)Image.Load(path); var payload = ExtractBytes(image); return payload; } /// /// 从stego图像中提取消息并将其作为字节数组返回 /// /// /// public static byte[] ExtractBytes(this Image stegoImage) { //StringBuilder messageNameBuilder = new StringBuilder(); var payloadSizeBuilder = new StringBuilder(); var payloadBuilder = new StringBuilder(); Rgba32 pixel; var pixelX = 0; var pixelY = 0; var messageBitCounter = 0; // 提取有效载荷的大小 while (messageBitCounter < 24) { if (pixelY >= stegoImage.Height) { break; } pixel = stegoImage[pixelX, pixelY]; switch (messageBitCounter % 3) { case 0: payloadSizeBuilder.Append(ExtractLsbAsString(pixel.R)); break; case 1: payloadSizeBuilder.Append(ExtractLsbAsString(pixel.G)); break; case 2: payloadSizeBuilder.Append(ExtractLsbAsString(pixel.B)); pixelX++; if (pixelX >= stegoImage.Width) { pixelX = 0; pixelY++; } break; } messageBitCounter++; } // 组成有效载荷的大小 var payloadSizeString = payloadSizeBuilder.ToString(); var payloadSize = BinaryToUint(payloadSizeString); // 提取有效载荷 while (messageBitCounter < payloadSize + 24) { if (pixelY >= stegoImage.Height) { break; } pixel = stegoImage[pixelX, pixelY]; switch (messageBitCounter % 3) { case 0: payloadBuilder.Append(ExtractLsbAsString(pixel.R)); break; case 1: payloadBuilder.Append(ExtractLsbAsString(pixel.G)); break; case 2: payloadBuilder.Append(ExtractLsbAsString(pixel.B)); pixelX++; if (pixelX >= stegoImage.Width) { pixelX = 0; pixelY++; } break; } messageBitCounter++; } var payloadString = payloadBuilder.ToString(); var payload = ConvertBitStringToByteArray(payloadString); return payload; } #endregion Extract #endregion 隐藏、提取 消息 #region Private /// /// 计算完整消息(不仅是有效负载)和图像的LSB之间的汉明距离,并将它们作为整数返回(所有8字节最后一位置为偶数) /// /// /// /// /// /// private static int CalculateHammingDistance(Image carrierImage, string message) { // 获取有关载体的所有必要信息 uint carrierWidth = (uint)carrierImage.Width; uint carrierHeight = (uint)carrierImage.Height; uint maxCarrierPixels = (carrierWidth * carrierHeight); uint capacity = (3 * maxCarrierPixels); if (message.Length > capacity) { throw new Exception("MessageTooBigException"); } // 收集加扰载体的LSB string carrierLsb = CollectCarrierLsb(carrierImage); // 计算Hamming距离 int hammingDistance = 0; int lsbCounter = 0; foreach (char bit in message) { // 如果数组的索引达到2 ^ 31,则需要重新设置。这是因为数组只能通过int值访问 if (lsbCounter == int.MaxValue) { carrierLsb = carrierLsb.Substring(lsbCounter); lsbCounter = 0; } // 如果消息位和LSB不匹配,则增加汉明距离 if (!Equals(bit, carrierLsb[lsbCounter])) { hammingDistance++; } lsbCounter++; } return hammingDistance; } /// /// 收集从像素排序的给定载体图像的所有LSB /// (0,0)到(xMax,yMax)以及颜色R,G,B,并将它们作为字符串返回 /// /// private static string CollectCarrierLsb(Image carrierImage) { StringBuilder sb = new StringBuilder(); int width = carrierImage.Width; int height = carrierImage.Height; // 在整个图像上循环 for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { var pixel = carrierImage[x, y]; sb.Append(ExtractLsbAsString(pixel.R)); sb.Append(ExtractLsbAsString(pixel.G)); sb.Append(ExtractLsbAsString(pixel.B)); } } return sb.ToString(); } /// /// 从消息对象生成二进制位字符串 /// /// /// /// /// private static string GenerateMessageBitPattern(byte[] payload) { //将数据转换为二进制字符串 var payloadSize = (uint)payload.Length * 8; var payloadBinary = ByteArrayToBinary(payload); var payloadSizeBinary = DecimalToBinary(payloadSize, 24); var sb = new StringBuilder(); sb.Append(payloadSizeBinary); sb.Append(payloadBinary); return sb.ToString(); } /// /// 在任意字节值中插入新的LSB /// /// /// /// private static byte InsertMessageBit(byte carrierByte, string msgBit) { var sb = new StringBuilder(DecimalToBinary(carrierByte, 8)); sb.Remove(sb.Length - 1, 1); sb.Append(msgBit); var stegoByte = BinaryToByte(sb.ToString()); return stegoByte; } /// /// 从字节中提取LSB并将其作为字符串返回 /// /// private static string ExtractLsbAsString(byte inputByte) { return inputByte % 2 == 0 ? "0" : "1"; } private static uint CalculateCapacity(Image carrier, string unit) { var width = (uint)carrier.Width; var height = (uint)carrier.Height; switch (unit) { case "bit": return 3 * width * height; case "byte": return 3 * width * height / 8; case "kB": return 3 * width * height / 8192; case "mB": return 3 * width * height / 8192 / 1024; case null: return 3 * width * height; default: return 3 * width * height; } } #endregion Private #region Converter /// /// 将文本字符串转换为二进制字符串模式 /// /// /// /// private static string StringToBinary(string str, int zeroPadding) { var encoding = new UTF8Encoding(); var buffer = encoding.GetBytes(str); if (buffer.Length > 64) throw new Exception("MessageNameTooBigException"); if (buffer.Length < 64) Array.Resize(ref buffer, zeroPadding); var sb = new StringBuilder(); foreach (var element in buffer) sb.Append(Convert.ToString(element, 2).PadLeft(8, '0')); return sb.ToString(); } /// /// 将 int 对象转换为其二进制字符串表示形式,并用左侧指定数量的零 /// /// /// /// private static string DecimalToBinary(int decimalNumber, int zeroPadding = 0) { if (zeroPadding != 0) return Convert.ToString(decimalNumber, 2).PadLeft(zeroPadding, '0'); return Convert.ToString(decimalNumber, 2); } /// /// 将 uint 对象转换为其二进制字符串表示形式,并用左侧指定数量的零 /// /// /// /// private static string DecimalToBinary(uint decimalNumber, int zeroPadding = 0) { if (zeroPadding != 0) return Convert.ToString(decimalNumber, 2).PadLeft(zeroPadding, '0'); return Convert.ToString(decimalNumber, 2); } /// /// 将 byte 对象转换为其二进制字符串表示形式,并用左侧指定数量的零 /// /// /// /// private static string DecimalToBinary(byte decimalNumber, int zeroPadding = 0) { if (zeroPadding != 0) return Convert.ToString(decimalNumber, 2).PadLeft(zeroPadding, '0'); return Convert.ToString(decimalNumber, 2); } /// /// 将字节数组转换为其二进制字符串表示形式 /// /// /// private static string ByteArrayToBinary(byte[] array) { var sb = new StringBuilder(); foreach (var by in array) sb.Append(DecimalToBinary(by, 8)); return sb.ToString(); } /// /// 将二进制字符串模式转换为其各自的UTF8文本表示 /// /// /// private static string BinaryToString(string str) { var temp = BinaryToByteArray(str); var val = Encoding.UTF8.GetString(temp); return val; } /// /// 将二进制字符串模式转换为字节变量 /// /// /// private static byte BinaryToByte(string binary) { var by = Convert.ToByte(binary, 2); return by; } /// /// 将二进制字符串模式转换为字节数组 /// /// /// private static byte[] BinaryToByteArray(string binary) { return ConvertBitStringToByteArray(binary); } /// /// 将二进制字符串模式转换为 uint 变量 /// /// /// private static uint BinaryToUint(string binary) { var unsignedInt = Convert.ToUInt32(binary, 2); return unsignedInt; } /// /// 将存储位值的uint变量转换为存储kB值的双变量 /// /// /// private static double BitsToKiloBytes(uint bits) { // ReSharper disable once PossibleLossOfFraction float bytes = bits / 8; var kiloBytes = bytes / 1024; return Math.Round(kiloBytes, 5); } private static byte[] ConvertBitStringToByteArray(string source) { var data = Regex.Matches(source, ".{8}") .Select(m => Convert.ToByte(m.Groups[0].Value, 2)) .ToArray(); return data; } #endregion Converter }