using System; using System.Drawing; namespace LSB_Winform { class LSB { public static Bitmap HideBitmap(String text, Bitmap bmp) //функция для скрывания алгоритмом LSB текста text в изображении bmp { bool end = false; //переменная логического типа (Может принимать одну из двух значений true (истина) и false (ложь)), отвечает за обозначения конца сообщения для скрытия int charValue = 0; //код текущего символа из строки для кодирования (от 0 до 255) int charIndex = 0; //позиция символа в тексте для кодирования int zeros = 0; //количество 0, которые указывают на конец скрытого сообщения long bitIndex = 0; // индекс текущего бита, каждый символ сообщения нуждается в 8 битах (1 байт) int R = 0, G = 0, B = 0; //RGB переменные, которые описывают цвет пикселя for (int i = 0; i < bmp.Height; i++) //проходим циклом по всей высоте картинки { for (int j = 0; j < bmp.Width; j++) //проходим по ширине картинки { Color pixel = bmp.GetPixel(j, i); // Получаем цвет пикселя в координате (j, i) //освобождаем последний бит значения каждого из каналов цвета, в него и будут записываться скрытые биты сообщения R = pixel.R - pixel.R % 2; G = pixel.G - pixel.G % 2; B = pixel.B - pixel.B % 2; for (int n = 0; n < 3; n++) //Цикл по 3 каналам каждого пикселя (R, G, B) { if (bitIndex % 8 == 0) //если достигнут байт (8 битов) - успешно скрыт 1 символ сообщения { if (end == true && zeros == 8) //Когда достигнут конец сообщения для скрытия И есть НУЛЕВОЙ байт (8 бит), который обозначает конец сообщения { if (((bitIndex - 1) % 3) < 2) //если нулевой символ закончили писать раньше синего канала (в котором записывают новый цвет) bmp.SetPixel(j, i, Color.FromArgb(R, G, B)); //обновляем значение цвета пикселя в координате (j, i) return bmp; //возвращаем картинку, так как уже все сообщение скрыто и есть нулевой байт, который указывает на конец сообщения } if (charIndex >= text.Length) //если достигнут конец сообщения для кодирования end = true; //обозначаем, что весь текст уже скрыт (достигнут конец сообщения) и продолжать не нужно else //иначе charValue = text[charIndex++]; //переходим к следующему символу сообщения text для скрытия } switch (bitIndex % 3) //получаем канал цвета, в который нужно закодировать текущий бит символа сообщения { case 0: //если канал красного цвета if (end == false) //если сообщение еще не скрыто { R += charValue % 2; //добавляем в красный канал текущий бит символа (0 или 1) charValue /= 2; //удаляем этот бит из символа } break; //переходим к следующему биту case 1: //если канал зеленого цвета if (end == false) //если сообщение еще полностью не скрыто { G += charValue % 2; //добавляем в зеленый канал текущий бит символа (0 или 1) charValue /= 2; //удаляем этот бит из символа } break; //переходим к следующему биту case 2: //если канал синего цвета if (end == false) //если сообщение еще полностью не скрыто { B += charValue % 2; //добавляем в синий канал текущий бит символа (0 или 1) charValue /= 2; //удаляем этот бит из символа } bmp.SetPixel(j, i, Color.FromArgb(R, G, B)); //цвет готов и изменяем на него пиксель в координате (j, i) break; //переходим к следующему биту } bitIndex++; //еще один бит был скрыт в изображении if (end == true) //Когда все сообщение было скрыто zeros++; //ув. количество 0 } } } return bmp; //возвращаем картинку с скрытым сообщением text } public static String ExtractBitmap(Bitmap bmp) //функция для получения скрытого сообщения из изображения bmp { String text = String.Empty; //само скрытое сообщения int charValue = 0; //ASCII-код каждого символа в сообщении, которое будет извлечено long colorChannelIndex = 0; //индекс канала цвета (0-красный, 1-зеленый, 2-синий) for (int i = 0; i < bmp.Height; i++) //проходим циклом по всей высоте картинки { for (int j = 0; j < bmp.Width; j++) //проходим по ширине картинки { Color pixel = bmp.GetPixel(j, i); // Получаем цвет пикселя в координате (j, i) for (int n = 0; n < 3; n++) // Цикл по 3 каналам каждого пикселя (R, G, B) { switch (colorChannelIndex % 3) // В каком канале цвета сейчас считываем символ скрытого сообщения { case 0: //если красный 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; //переходим к следующему каналу цвета } colorChannelIndex++; //ув. индекс канала (переходим к следующему) if (colorChannelIndex % 8 == 0) //если был считан байт (8 бит) { charValue = reverseBits(charValue); // получаем символ сообщения путем перестановки в обратном порядке битов текущего символа if (charValue == 0) // если считали нулевой байт (символ) - это признак конца скрытого сообщения return text; // возвращаем скрытый символ char c = (char)charValue; // получаем символ из ASCII-кода text += c; // добавляем этот символ в конец сообщения } } } } return text; //возвращаем скрытое сообщение } public static int reverseBits(int n) //функция разворота битов в числе { int result = 0; //число-результат for (int i = 0; i < 8; i++) //проходим по 8 битам числа { result = result * 2 + n % 2; //получаем последний бит входящего числа, и добавляем его в начало битов результирующего числа n /= 2; //уменьшаем входящее число, удалив последний бит } return result; //возвращаем число, полученное из битов в обратном порядке } } }