【发布时间】:2020-11-09 21:32:44
【问题描述】:
我正在用 C# 编写一个小型隐写术应用程序,并且能够隐藏图像中的文本。但是我使用的方法是GetPixel/SetPixel 方法,它对于较大的图像要慢得多,我在尝试隐藏图像中的 mp3 文件后注意到了这一点。经过一些谷歌搜索,我发现了LockBits。虽然速度确实大幅提高,但我发现我无法提取隐藏在图像中的加密数据(密文)。
我不确定问题出在我如何插入数据或提取数据时。尝试提取 Base64 密文时,它会被损坏(随机符号和字符)并引发异常,说明它不是 Base64String。我最终按照LockBits 文档中的内容更改了代码,我将其粘贴在下面。
合并密文
public static unsafe void MergeEncryptedData(string data, Bitmap bmp, string output) {
State s = State.HIDING;
int height = bmp.Height;
int width = bmp.Width;
var bitmapData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bmp.PixelFormat);
byte * scan0 = (byte * ) bitmapData.Scan0;
int bytesPerPixel = 4;
int dataIndex = 0;
byte dataValue = 0;
long colorUnitIndex = 0;
int zeros = 0;
byte R, G, B;
Parallel.For(0, height, (i, loopState) = > {
byte * currentLine = scan0 + (i * bitmapData.Stride);
for (int j = 0; j < (bitmapData.Width * bytesPerPixel); j += bytesPerPixel) {
R = currentLine[i + 2];
G = currentLine[i + 1];
B = currentLine[i];
for (int n = 0; n < 3; n++) {
if (colorUnitIndex % 8 == 0) {
if (zeros == 8) {
if ((colorUnitIndex - 1) % 3 < 2) {
currentLine[i + 2] = R;
currentLine[i + 1] = G;
currentLine[i] = B;
//bmp.SetPixel(j, i, Color.FromArgb(R, G, B));
}
loopState.Stop();
}
if (dataIndex >= data.Length) {
s = State.FILL_WITH_ZEROS;
} else {
dataValue = (byte) data[dataIndex++];
}
}
switch (colorUnitIndex % 3) {
case 0:
{
if (s == State.HIDING) {
B += (byte)(dataValue % 2);
dataValue /= 2;
}
}
break;
case 1:
{
if (s == State.HIDING) {
G += (byte)(dataValue % 2);
dataValue /= 2;
}
}
break;
case 2:
{
if (s == State.HIDING) {
R += (byte)(dataValue % 2);
dataValue /= 2;
}
currentLine[i + 2] = R;
currentLine[i + 1] = G;
currentLine[i] = B;
//bmp.SetPixel(j, i, Color.FromArgb(R, G, B));
}
break;
}
colorUnitIndex++;
if (s == State.FILL_WITH_ZEROS) {
zeros++;
}
}
}
});
bmp.UnlockBits(bitmapData);
bmp.Save(output, ImageFormat.Png);
}
提取密文
public static unsafe string ExtractData(Bitmap bmp) {
int height = bmp.Height;
int width = bmp.Width;
var bitmapData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bmp.PixelFormat);
byte * scan0 = (byte * ) bitmapData.Scan0.ToPointer();
int bytesPerPixel = 4;
int colorUnitIndex = 0;
int charValue = 0;
string extractedText = String.Empty;
Parallel.For(0, height, (i, loopState) = > {
byte * currentLine = scan0 + (i * bitmapData.Stride);
for (int j = 0; j < (bitmapData.Width * bytesPerPixel); j += bytesPerPixel) {
for (int n = 0; n < 3; n++) { //this particular loop feels incorrect
switch (colorUnitIndex % 3) {
case 0:
{
charValue = charValue * 2 + currentLine[i] % 2;
}
break;
case 1:
{
charValue = charValue * 2 + currentLine[i + 1] % 2;
}
break;
case 2:
{
charValue = charValue * 2 + currentLine[i + 2] % 2;
}
break;
}
colorUnitIndex++;
if (colorUnitIndex % 8 == 0) {
charValue = reverseBits(charValue);
if (charValue == 0) {
loopState.Stop();
}
char c = (char) charValue;
extractedText += c.ToString();
}
}
}
});
bmp.UnlockBits(bitmapData);
return extractedText;
}
抛出错误时提取的密文的示例:
I$I$I$I$I$I$I$I$I$I$I$I$I$I䥉II!J$$。
它应该是一个 Base-64 字符串
仅供参考,我使用 LUT PNG 图像来隐藏数据,因此与原始图像相比,我可以看到颜色略有不同。所以我知道 RGB 值确实在改变。
【问题讨论】:
-
是的,您可以在没有锁定位的情况下隐藏数据。
-
为了吸引答案,您可能需要将其简化为 minimal reproducible example。您是否尝试过确定
MergeEncryptedData()或ExtractData()中哪一个的行为与旧的GetPixel()和SetPixel()版本不同?让我们找出这两种方法中的哪一种有错误有点超出了堆栈溢出问题的范围。 -
首先,将
MergeEncryptedData()的输出与旧的SetPixel()版本进行比较;如果存在差异,则表明MergeEncryptedData()存在问题。接下来,尝试将旧版SetPixel()的有效输出发送到ExtractData()。如果有错误,则ExtractData()中存在错误。一旦找出问题所在,您就可以使用更准确的信息和可以正常工作的旧代码示例来更新您的问题。
标签: c# encryption bitmap steganography