【问题标题】:Write hidden messages to image file c#将隐藏消息写入图像文件c#
【发布时间】:2015-04-02 10:58:52
【问题描述】:

我一直在寻找一个教程,其中描述了如何使用 C# 将隐藏文本写入/读取到图像文件中,但不幸的是我找不到任何关于此的好帖子,所以我决定在这里询问这个小技巧。

假设我有一个名为 test.jpg 的图像文件,有没有办法在这个文件中写入一个隐藏文本,如“Hello word”?我已阅读有关图像元数据的信息,我想我应该尝试以某种方式写入此元数据,但我不知道如何。 如果有人知道该怎么做,请帮助我了解如何去做。 谢谢。

【问题讨论】:

  • 是的,有这样的方法。您正在寻找的术语是隐写术,但是有许多算法可以实现您想要的。您是否特别询问如何通过将文本嵌入元数据或任何一般方法来实现这一点?您必须更具体地说明您要达到的目标,遇到困难的方式和位置。如果您只需要一种方法,那么在 SO 或 Google 中搜索最终会得到一些兼容的结果。
  • 我需要将隐藏文本写入/读取到图像文件中。好的,我会的,谢谢。

标签: c# image image-processing bitmap bitmapimage


【解决方案1】:

对此的正确术语是“隐写术”。

以下文章阐述了隐写术的定义:

隐写术是通过将信息嵌入其他信息来隐藏信息的艺术和科学。隐写术的工作原理是用一些不同的、不可见的信息替换常规计算机文件中无用或未使用的数据。这些隐藏信息可以是纯文本、密文,甚至是图片

逐步解释隐写术的工作原理:

  • 遍历图像的像素。在每次迭代中,将 RGB 值分别分隔为一个单独的整数。
  • 对于 R、G 和 B 中的每一个,使 LSB 等于 0。这些位将用于隐藏字符。
  • 获取当前字符并将其转换为整数。然后将其8位隐藏在R1,G1,B1,R2,G2,B2,R3,G3中,其中数字是指像素的数量。在这些元素的每个 LSB(从 R1 到 G3)中,连续隐藏字符的位。
  • 字符的8位处理完后,跳转到下一个字符,重复这个过程,直到处理完整个文本。
  • 文字可以根据文字的长度隐藏在图像的一小部分。因此,必须有一些东西表明我们到了文本的结尾。该指标只是 8 个连续的零。从图像中提取文本时需要这样做。

这比隐藏更简单。只需通过像素 直到找到 8 个连续的零。当你经过时,选择 来自每个像素元素(R、G、B)的 LSB 并将其附加到一个空的 价值。当这个值的 8 位完成后,将其转换回 字符,然后将该字符添加到您要查找的结果文本中。

示例源代码如下::

class SteganographyHelper
{
    public enum State
    {
        Hiding,
        Filling_With_Zeros
    };

    public static Bitmap embedText(string text, Bitmap bmp)
    {
        // initially, we'll be hiding characters in the image
        State state = State.Hiding;

        // holds the index of the character that is being hidden
        int charIndex = 0;

        // holds the value of the character converted to integer
        int charValue = 0;

        // holds the index of the color element (R or G or B) that is currently being processed
        long pixelElementIndex = 0;

        // holds the number of trailing zeros that have been added when finishing the process
        int zeros = 0;

        // hold pixel elements
        int R = 0, G = 0, B = 0;

        // pass through the rows
        for (int i = 0; i < bmp.Height; i++)
        {
            // pass through each row
            for (int j = 0; j < bmp.Width; j++)
            {
                // holds the pixel that is currently being processed
                Color pixel = bmp.GetPixel(j, i);

                // now, clear the least significant bit (LSB) from each pixel element
                R = pixel.R - pixel.R % 2;
                G = pixel.G - pixel.G % 2;
                B = pixel.B - pixel.B % 2;

                // for each pixel, pass through its elements (RGB)
                for (int n = 0; n < 3; n++)
                {
                    // check if new 8 bits has been processed
                    if (pixelElementIndex % 8 == 0)
                    {
                        // check if the whole process has finished
                        // we can say that it's finished when 8 zeros are added
                        if (state == State.Filling_With_Zeros && zeros == 8)
                        {
                            // apply the last pixel on the image
                            // even if only a part of its elements have been affected
                            if ((pixelElementIndex - 1) % 3 < 2)
                            {
                                bmp.SetPixel(j, i, Color.FromArgb(R, G, B));
                            }

                            // return the bitmap with the text hidden in
                            return bmp;
                        }

                        // check if all characters has been hidden
                        if (charIndex >= text.Length)
                        {
                            // start adding zeros to mark the end of the text
                            state = State.Filling_With_Zeros;
                        }
                        else
                        {
                            // move to the next character and process again
                            charValue = text[charIndex++];
                        }
                    }

                    // check which pixel element has the turn to hide a bit in its LSB
                    switch (pixelElementIndex % 3)
                    {
                        case 0:
                            {
                                if (state == State.Hiding)
                                {
                                    // the rightmost bit in the character will be (charValue % 2)
                                    // to put this value instead of the LSB of the pixel element
                                    // just add it to it
                                    // recall that the LSB of the pixel element had been cleared
                                    // before this operation
                                    R += charValue % 2;

                                    // removes the added rightmost bit of the character
                                    // such that next time we can reach the next one
                                    charValue /= 2;
                                }
                            } break;
                        case 1:
                            {
                                if (state == State.Hiding)
                                {
                                    G += charValue % 2;

                                    charValue /= 2;
                                }
                            } break;
                        case 2:
                            {
                                if (state == State.Hiding)
                                {
                                    B += charValue % 2;

                                    charValue /= 2;
                                }

                                bmp.SetPixel(j, i, Color.FromArgb(R, G, B));
                            } break;
                    }

                    pixelElementIndex++;

                    if (state == State.Filling_With_Zeros)
                    {
                        // increment the value of zeros until it is 8
                        zeros++;
                    }
                }
            }
        }

        return bmp;
    }

    public static string extractText(Bitmap bmp)
    {
        int colorUnitIndex = 0;
        int charValue = 0;

        // holds the text that will be extracted from the image
        string extractedText = String.Empty;

        // pass through the rows
        for (int i = 0; i < bmp.Height; i++)
        {
            // pass through each row
            for (int j = 0; j < bmp.Width; j++)
            {
                Color pixel = bmp.GetPixel(j, i);

                // for each pixel, pass through its elements (RGB)
                for (int n = 0; n < 3; n++)
                {
                    switch (colorUnitIndex % 3)
                    {
                        case 0:
                            {
                                // get the LSB from the pixel element (will be pixel.R % 2)
                                // then add one bit to the right of the current character
                                // this can be done by (charValue = charValue * 2)
                                // replace the added bit (which value is by default 0) with
                                // the LSB of the pixel element, simply by addition
                                charValue = charValue * 2 + pixel.R % 2;
                            } break;
                        case 1:
                            {
                                charValue = charValue * 2 + pixel.G % 2;
                            } break;
                        case 2:
                            {
                                charValue = charValue * 2 + pixel.B % 2;
                            } break;
                    }

                    colorUnitIndex++;

                    // if 8 bits has been added,
                    // then add the current character to the result text
                    if (colorUnitIndex % 8 == 0)
                    {
                        // reverse? of course, since each time the process occurs
                        // on the right (for simplicity)
                        charValue = reverseBits(charValue);

                        // can only be 0 if it is the stop character (the 8 zeros)
                        if (charValue == 0)
                        {
                            return extractedText;
                        }

                        // convert the character value from int to char
                        char c = (char)charValue;

                        // add the current character to the result text
                        extractedText += c.ToString();
                    }
                }
            }
        }

        return extractedText;
    }

    public static int reverseBits(int n)
    {
        int result = 0;

        for (int i = 0; i < 8; i++)
        {
            result = result * 2 + n % 2;

            n /= 2;
        }

        return result;
    }
} 

来源文章:http://www.codeproject.com/Tips/635715/Steganography-Simple-Implementation-in-Csharp

【讨论】:

  • 欢迎来到 StackOverflow。仅链接的答案不好,因为链接可能会损坏或服务器可能会关闭。请抓住链接所说的精髓并将其发布在您的答案中,以便为其他用户保留。
  • 此方法不适用于 jpeg 文件,在提问者放弃此要求之前,您的建议不会有用。
  • 它清楚地返回一个位图。您可以将这些位图保存为 JPG 或任何其他常见的文件格式。
  • jpeg 以有损方式对像素图进行编码以实现压缩。在这种情况下,压缩足以破坏嵌入的秘密。因此,即使您可以将位图保存为 jpeg,也不要期望找回秘密。此处需要无损图像文件,例如 bmp 或 png。
【解决方案2】:

你也可以参考这个.. Image Steganography Application C#

【讨论】:

  • 请避免仅链接的答案,因为它们可能会过时。此外,您建议的方法似乎并不相关,因为它是关于将图像隐藏在另一个图像中,而秘密不能存储在 jpeg 文件中。
  • 知道了。我只是想帮忙
  • @Reti43 位图速记在字节数组级别工作,在同一字节数组级别,“秘密消息”和“位图图像”被同等对待。至少可以说,您的无关紧要的话很荒谬。
  • @Visar 你没抓住重点。位图隐写术改变像素以隐藏信息。由于算法的有损压缩,将该图像保存为 jpeg 改变一些像素值,从而可能破坏您的秘密。 OP 明确表示他对 jpeg 封面图像感兴趣。
  • 显然这个明确的要求不存在,因为你要求 OP 删除它。好点
猜你喜欢
  • 2013-05-14
  • 2018-04-30
  • 2018-08-01
  • 2014-02-18
  • 2014-09-03
  • 2018-07-02
  • 1970-01-01
  • 2016-09-27
  • 2011-01-15
相关资源
最近更新 更多