Форум программистов, компьютерный форум, киберфорум
Наши страницы
C++ Builder
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.59/401: Рейтинг темы: голосов - 401, средняя оценка - 4.59
Erravielle
341 / 52 / 3
Регистрация: 27.04.2009
Сообщений: 36
1

Стеганография в BMP. Полное руководство

11.03.2010, 21:54. Просмотров 73138. Ответов 60
Метки нет (Все метки)

СТЕГАНОГРАФИЯ в BMP ИЗОБРАЖЕНИЯХ

Данная статья содержит готовый проект с подробным описанием алгоритма реализации шифрования текстового сообщения в графический файл формата *.BMP

I. Теоретические предпосылки, или что нужно знать для начала работы . . .

Пиксел изображения в 24-х битном .BMP формате занимает 3 Байта (24 бита) памяти (соответственно по 1 байту на каждый канал - Red, Green, Blue (RGB))

Один символ текста занимает 1 Байт (8 бит) дискового пространства.

Если заменять Байт изображения Байтом текста, то мы получим обсалютно другой цвет пиксела, и, как следствие, сильное искажение изображения.
Поэтому наиболее удобно заменять только 1 бит одного из каналов Пиксела на 1 бит Текста. Такая подмена будет незаметна для человеческого глаза. (При желании можно заменять последний бит КАЖДОГО из каналов... Алгоритм изменится незначительно).

Таким образом, один символ текста будет кодироваться в 8 пикселов изображения.

Разумно задать следующий вопрос: "а какой бит заменять то?"

Рассмотрим пример:

Возьмем пиксел со значениями кодируемого канала: Red = 255;

BYTE R = 255; // 11111111


Предположим нам нужно заменить один бит этого канала на бит символа, равный '0'.
Если мы заменим первый бит Байта R, то получим:

BYTE R = 11111110; // теперь R = 127
(отсчет идет справа налево, а не слева направо!!! Вожно это знать!)
- согласитесь, разница значений очень велика!

Теперь попробуем заменить последний бит Пиксела:

BYTE R = 01111111; // теперь R = 254
т.о. оттенок цвета изменится незначительно.

Итак, решено - МЕНЯЕМ ПОСЛЕДНИЙ БИТ ПИКСЕЛА.

II. АЛГОРИТМ РАБОТЫ ПРОГРАММЫ . . .

ШИФРОВАНИЕ
1. Загружаем изображение
2. Определяем колличество пикселов, из которого оно состоит
3. Делим полученное значение на 8 и получаем максимальный размер текстового сообщения
3. Вводим шифуемый текст
4. Определяем размер текста и сопоставляем его с максимально допустимым
6. Определяем шаг продвижения по Битмапу
5. Организуем два цикла:
- Внешний: обход по символам
- Внутренний: обход по битам символа
6. В циклах заменяем биты изображения битами текста, двигаясь по изображению с заданным шагом
7. Сохраняем полученный результат
8. Записываем в файл "ключ", который позволит расшифровать сообщение

! Ключ содержит две переменных: ШАГ и РАЗМЕР ТЕКСТА

ДЕШИФРОВКА
1. Загружаем изображение
2. Загружаем ключ
3. Организуем два цикла:
- Внешний: обход по символам
- Внутренний: обход по битам символа
4. В циклах считываем с шагом, указанным в ключе, значения последних битов пикселов; Группируем биты в символы и формируем из символов Текстовую строку

III. РЕАЛИЗАЦИЯ . . .

1. Объявим глобальные переменные, которые позволят нам продолжить работу:

C++
1
2
3
4
5
6
7
8
9
10
Graphics::TBitmap *Bmp; // переменная для хранения BMP изображения
 
bool ERR_OPEN_BMP = true;  // переменные для отслеживания ошибок
bool ERR_OPEN_KEY = true;
bool ERR_NO_TEXT = true;
bool ERR_TEXT_LENGTH = false;
 
unsigned int IMAGE_SIZE = 0; // дополнительные переменные
unsigned int TEXT_SIZE = 0;
unsigned int MAX_TEXT_SIZE = 0;
2. Напишем функции для работы с Текстом и изображением:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
TPoint GetPosition(TPoint Point, int Shag, int Width)
/* Функция оперделяет следующую координату пиксела, который будет кодироваться
   TPoint Point - предыдущая координата
   Shag - шаг кодирования
   Width - ширина Битмапа
*/
{  TPoint REZ;
   REZ.y += Point.y;
 
int BUF = Width - Point.x - Shag;
 
if (BUF <= 0) {
                REZ.y++;  BUF = abs(BUF);
                while (BUF >= Width)
                {BUF -= Width; REZ.y++;}
              }
              else BUF = REZ.x + Width - BUF;
REZ.x = BUF;
 
return REZ;
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BYTE BinaryToByte (int *mass)
/* Собирает из массива битов один целый байт и возвращает его.
   Входной параметр: массив из восьми элементов, соответсвтующих
   битам Байта
*/
{
            BYTE Mask   = 00000001;
            BYTE Result = 00000000;
            BYTE Mask2;
 
            int j = 0;
            for (int i = 7; i > -1; i--, j++)
            {
              if (mass[i] == 1)  {Mask2 = (Mask << (j));
                                  Result = Result|Mask2;
                                  }
            }
            return Result;
}
C++
1
2
3
4
5
6
7
8
9
10
11
int GetBitValue(BYTE B, int N)
/* Получает значение N-ого бита в байте B
   Возвращает 1 или 0 (в зависимости от значения Бита)
*/
{ int k = 256;
 
      for (int i = 0; i < N; i++) k /= 2;
 
      if ((B & k) != 0) return 1;
      else return 0;
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
BYTE ReadBitToByte (int Bit, BYTE B)
/* Записывает в байт B на последнюю позицию бит Bit
*/
{
     BYTE A = 00000001;
     BYTE Result = B;
 
     if (Bit == GetBitValue(B,8)) return B;
        else if (Bit == 1) return Result = Result|A;
             else if (Bit == 0) return B - A;
 
             return NULL;
}
Следующие функции не используются в проекте, но могут представлять для вас интерес:

C++
1
2
3
4
5
6
7
8
9
10
11
int BinaryToDec (BYTE val)
/* Переводит Байт из двоичной системы в десятичную
*/
{   int Result = 0;
 
    for (int t = 1, i = 0; t < 129; t *= 2, i++)
            {
                if ((val & t) != 0) Result += pow(2,i);
            }
 return Result;
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int *ByteToBinary (BYTE val)
/* Разбивает исходный Байт на биты. Возвращает массив из восьми элементов
   Каждый Nй элемент соответствует Nму биту.
   Принимает значения 1 или 0
*/
{           int *mass = new int[8];
            int t, i;
            for (t = 128, i = 0; t > 0; t /= 2, i++)
            {
                if ((val & t) != 0) mass[i] = 1;
                else if ((val & t) == 0) mass[i] = 0;
            }
  return mass;
}
К сожалению из за катастрофической нехватки времени не могу подробно описать как работает каждая из функций ((( возможно сделаю это чуть позже. . .

3. Подготавливаем изображение для работы и определяем дополнительные переменные:

C++
1
2
3
4
5
6
Bmp = new Graphics::TBitmap;
    Bmp->LoadFromFile(FileName); // копируем изображение в Bmp
    Bmp->PixelFormat = pf24bit; // устанавливаем глубину цвета в 24 bit (стандарт без Альфа канала)
 
IMAGE_SIZE = WIDTH*HEIGHT;
MAX_TEXT_SIZE = IMAGE_SIZE/8;
4. Введем шифруемый текс:

Для этого я использовал компонент TRichEdit. Создаем свойство OnChange и пишем туда приблизительно такой код:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TEXT_SIZE = RichEdit1->Text.Length();
 
if (TEXT_SIZE != 0) ERR_NO_TEXT = false;
  else ERR_NO_TEXT = true;
 
if (TEXT_SIZE > MAX_TEXT_SIZE)
    {
       ERR_TEXT_LENGTH = true;
       Label9->Font->Color = clRed;
       Label12->Font->Color = clRed;
    }
    else
       {
         ERR_TEXT_LENGTH = false;
         Label9->Font->Color = clWhite;
         Label12->Font->Color = clWhite;
       }
Теперь при вводе символов в RichEdit1 программа будет самостоятельно высчитывать интересующие ее значения.

5. Шифруем сообщение :

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int Shag = MAX_TEXT_SIZE/TEXT_SIZE;
char *String = new char[RichEdit1->Text.Length()+1];// создаем переменную для хранения кодируемого текста
strcpy(String,RichEdit1->Text.c_str()); // копируем в нее кодируемый текст
 
 TPoint Next;    // позиция следующего кодируемого пиксела
 TPoint First;   // позиция кодированного пиксела
 BYTE TextByte;  // переменная для хранения символа
 
//---------------- ШИФРУЕМ СООБЩЕНИЕ -----------------//
 for (int i = 0; i < TEXT_SIZE; i++)  // перебор символов
    {
         TextByte = String[i]; // загоняем очередной символ
 
         for (int j = 0; j < 8; j++) // перебор битов символа
            {
             Next = GetPosition(First, Shag, WIDTH);
 
             TColor COLORR = Bmp->Canvas->Pixels[First.x][First.y];
 
             BYTE R=GetRValue(COLORR);    //
             BYTE G=GetGValue(COLORR);    // получили каналы
             BYTE B=GetBValue(COLORR);    //
 
             int bit = GetBitValue(TextByte, j+1); // получили записываемый бит
             R = ReadBitToByte (bit, R);         // записали jй бит в канал
 
             Bmp->Canvas->Pixels[First.x][First.y] = RGB(R,G,B);  // переопределили цвет
 
             First = Next;
            }
    }
//------------------ ЗАШИФРОВАЛИ ---------------------//


6. Сохраняем полученное изображение и ключ в файлы:

C++
1
2
3
4
5
6
7
Bmp->SaveToFile(SavePictureDialog1->FileName); // изображение
 
  ofstream READ;
  READ.open(FileName);
// ключ
  READ<<StrToInt(Shag)<<endl;
  READ<<StrToInt(TEXT_SIZE);
ШИФРОВАНИЕ ГОТОВО!!!!

Теперь ДЕШИФРОВКА:

... как открыть файл мы теперь знаем ...

1. Считываем коюч:

C++
1
2
3
4
  ifstream OPEN;
  OPEN.open(Stego->OpenDialog1->FileName.c_str());
 
  OPEN>>SHAG>>KOL;
2. Собственно дешифруем и выводим результат:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
TPoint Next;    // позиция следующего кодируемого пиксела
 TPoint First;   // позиция кодированного пиксела
 BYTE TextByte;  // переменная для хранения символа
 
 char *result = new char[KOL+1];
 strset(result, '\0');
 int *BitMass = new int[8]; // массив для хранения битов
 
    for (int i = 0; i < KOL; i++)  // перебор символов дешифруемого сообщения
        {
         for (int j = 0; j < 8; j++) // перебор битов символа
            {
             Next = GetPosition(First, SHAG, WIDTH);
 
             long COLORR = GetPixel(Bmp->Canvas->Handle,First.x,First.y);  // получили цвет
             BYTE R=GetRValue(COLORR);    //
             //BYTE G=GetGValue(COLORR);    // получили каналы
             //BYTE B=GetBValue(COLORR);    //
 
             BitMass[j] = GetBitValue(R, 8);
 
             First = Next;
            }
          result[i] =  BinaryToByte (BitMass);
          for (int k = 0; k < 8; k++) BitMass[k] = 0;
        }
 
 RichEdit1->Clear();
 result[KOL] = '\0';
 RichEdit1->Text = result;
Вот и Все, друзья... Во вложении вы найдети исходиники (среда разработки - C++ Builder 2009), краткую инструкцию по применению и примерчик....

Удачи в ваших начинаньях!!!
32
Вложения
Тип файла: rar Program.rar (1.65 Мб, 4776 просмотров)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
11.03.2010, 21:54
Ответы с готовыми решениями:

Стеганография в BMP попробуем вместе разложить все по полочкам
Здравствуйте уважаемые форумчане! Столкнулся с задачей написать программный...

Стеганография.Работа с изображением
Здравствуйте! Есть следующее задание - нужно реализовать метод наименьшего...

Руководство по libxml2
Доброго дня! Подскажите пожалуйста, где можно почитать о библиотеке libxml2?...

Руководство по C++ Builder X2 Embarcadero
Может ли кто нибудь помочь с руководством пользователя для C++ Builder X2...

Руководство на русском по GCC и g++
Подскажите по сабжу - надо разобраться с ключами, режимами и понять общую схему...

60
Erravielle
341 / 52 / 3
Регистрация: 27.04.2009
Сообщений: 36
12.03.2010, 07:50  [ТС] 2
Вот выложил исходники. . .

! И еще одно замечание - BMP-файл можно представить в виде вектора, размерностью [width*height*3] что упростит продвижение по изображению и сделает ненужной функцию GetPixel. Но учтите, перевод BMP файла в вектор потребует определенные затраты времени. . .
8
Evg
Эксперт CАвтор FAQ
19305 / 7160 / 533
Регистрация: 30.03.2009
Сообщений: 20,038
Записей в блоге: 30
12.03.2010, 14:15 3
Вот тут у нас поднялся вопрос о том, как засунуть информацию в *.jpg. Ибо передача файла *.bmp уже наводит на мысль о том, что там что-то спрятано. Поэтому кодирование информации в изображения с форматами бес сжатия - не есть интересно. Ты владеешь вопросом в части *.jpg?
3
Xdron
13 / 13 / 1
Регистрация: 21.02.2010
Сообщений: 45
01.06.2010, 21:25 4
вот такой вопрос а можно в этой проге ключ убрать?????
0
Evg
Эксперт CАвтор FAQ
19305 / 7160 / 533
Регистрация: 30.03.2009
Сообщений: 20,038
Записей в блоге: 30
01.06.2010, 21:36 5
Про какой ключ идёт речь?
0
Xdron
13 / 13 / 1
Регистрация: 21.02.2010
Сообщений: 45
01.06.2010, 21:39 6
ну в нутри есть программа она использует ключ для шифрации а можно как нить без него просто изображение сохраять!!????????
0
Evg
Эксперт CАвтор FAQ
19305 / 7160 / 533
Регистрация: 30.03.2009
Сообщений: 20,038
Записей в блоге: 30
01.06.2010, 21:45 7
Цитата Сообщение от Xdron Посмотреть сообщение
ну в нутри есть программа она использует ключ для шифрации а можно как нить без него просто изображение сохраять!!????????
Ах вот но что Тебе что конкретно надо? Самодельную программу, которая записывает файл bmp или тебе нужно просто записать bmp (например, посредством библиотечных функций)?
0
Xdron
13 / 13 / 1
Регистрация: 21.02.2010
Сообщений: 45
01.06.2010, 21:51 8
мне нужно просто шифровать текст в изображение BMP но без ключа ......

Добавлено через 55 секунд
текст нужно зашифровать в BMP...
как я примерно понял нужно разбить картинку на куски (по 8 пикселей) и в одном таком куске будет храниться 1 буква через двоичный код (тиипа 00010101)...

Добавлено через 2 минуты
а при зашифровке и расшифровке чтобы не выскакивал сохранить и заагрузить ключ а что бы он просто сразу загружал изображение и расшифровывал то что есть в изображении......т.е.текст!!!!!!!
0
Evg
Эксперт CАвтор FAQ
19305 / 7160 / 533
Регистрация: 30.03.2009
Сообщений: 20,038
Записей в блоге: 30
01.06.2010, 22:36 9
Я так понимаю, что тебе нужно исходники вот этой программы? Ну дык попроси автора да и всё. В этой теме тоже исходник лежит - бери его, да убирай ключ. Автор тут, можешь его спросить если что. Или ты не хочешь сам ничего делать, а тебе нужно всё готовенькое?
0
Xdron
13 / 13 / 1
Регистрация: 21.02.2010
Сообщений: 45
01.06.2010, 23:23 10
мне очень нужна вот эта программа Program.rar !!!!только без ключа!!! я уже чача три колыпаюсь и не могу убрать ключ!!!!

Добавлено через 33 секунды
не могли бы вы помоч??зарание большое спасибо!!!
0
Erravielle
341 / 52 / 3
Регистрация: 27.04.2009
Сообщений: 36
02.06.2010, 09:33  [ТС] 11
Чувак, я б с радостью помог, но времени нет - сессия у меня... если не срочно то в июле помогу.... а щас никак....
0
EVIL SnaKe
0 / 0 / 0
Регистрация: 22.03.2016
Сообщений: 2
02.06.2010, 22:54 12
Вопрос к автору проги (ну или кто разбираеться другой) какие конкртено функции отвечают за замену битов картинки ? Где что нужно поменять чтоб например записывалось не в последний бит, а в первый (ну вот надо мне так ) Я сам покопался, но пока не нашёл...
0
Erravielle
341 / 52 / 3
Регистрация: 27.04.2009
Сообщений: 36
03.06.2010, 16:19  [ТС] 13
Цитата Сообщение от EVIL SnaKe Посмотреть сообщение
Вопрос к автору проги (ну или кто разбираеться другой) какие конкртено функции отвечают за замену битов картинки ? Где что нужно поменять чтоб например записывалось не в последний бит, а в первый (ну вот надо мне так ) Я сам покопался, но пока не нашёл...
BYTE ReadBitToByte (int Bit, BYTE B) отвечает за это... видишь переменную A ??? это маска... вот с ней надо поколдовать чтобы заменить другой бит, ну еще и бит фонуцикй GetBitValue надо получать тот который интересует...
опять же говорю - времени пока вообще нет, так что экспериментируйте )))
1
cooller01
0 / 0 / 4
Регистрация: 04.10.2010
Сообщений: 115
04.10.2010, 11:17 14
а как быть с GIFом???

Добавлено через 2 минуты
в инете об этом маловато....(
0
Erravielle
341 / 52 / 3
Регистрация: 27.04.2009
Сообщений: 36
04.10.2010, 16:50  [ТС] 15
что касается GIFа - не знаю...
а вот с PNG легко можно... принцип во всяком случае тот же...
Основное правило - использовать формат без потери качества. В форматах с потерей используются алгоритмы сжатия, и такой фокус с заменой последнего бита не пройдет. Во всяком случае эти изменения достаточно легко обнаружить всвязи со спецификой формата
0
Gremlin
514 / 302 / 58
Регистрация: 30.07.2008
Сообщений: 607
17.10.2010, 07:14 16
у меня вот несколько вопросов:

1) для чего ключ автор использовал из двух частей?
2) у png 4 канала (RGBA)? и если да то как обрабатывать Alpha?
3) вот никак немогу узнать GIF с потерей качества или нет?
4) и вот думаю, если подменять не последний бит, а последний и предпоследний сильно ли это будет заметно (когда меняем последний то разница в цвете 2^3 а когда 2 последних то 4^3 так?)

я написал "стенограф" на C++Builder 6 с чтением png, gif, jpeg, bmp и сохранение bmp, png
ключ я записываю в начало текста "123|" где 123 сколько символов читать
0
Вложения
Тип файла: zip Стенография.zip (1.21 Мб, 556 просмотров)
Erravielle
341 / 52 / 3
Регистрация: 27.04.2009
Сообщений: 36
17.10.2010, 08:34  [ТС] 17
ОТВЕЧАЮ НА ВОПРОСЫ:

Цитата Сообщение от Gremlin Посмотреть сообщение
1) для чего ключ автор использовал из двух частей?
>> Моя прихоть - если мне не изменяет память, ключ содержит кол-во единиц информации и шаг, с которым мы движемся по вектору

Цитата Сообщение от Gremlin Посмотреть сообщение
2) у png 4 канала (RGBA)? и если да то как обрабатывать Alpha?
>> его в общем то обрабатывать так же, как и Red, Green, Blue каналы... визуально малое изменение прозрачности не скажется на качестве изображения

Цитата Сообщение от Gremlin Посмотреть сообщение
3) вот никак немогу узнать GIF с потерей качества или нет?
GIF использует формат сжатия LZW. А LWZ - относится к форматам сжатия без потерь. Так что и его можно использовать. . .

Цитата Сообщение от Gremlin Посмотреть сообщение
4) и вот думаю, если подменять не последний бит, а последний и предпоследний сильно ли это будет заметно (когда меняем последний то разница в цвете 2^3 а когда 2 последних то 4^3 так?)
>> При использовании 1го бита, мы получаем максимальную разницу 2^0 (то есть на 1), если мы будем использовать 2 бита то получим Максимальную (не постоянную) разницу в 2^0+2^1 = 3. Это еще куда не шло.. а вот если скажем 3 бита - 2^0+2^1+2^2= 7. . . Это уже будет весьма заметно...



P.S. Глянул вашу программку. . . Очень интересно бы исходники посмотреть, если это не коммрческий проект конечно )))

я написал "стенограф" на C++Builder 6 с чтением png, gif, jpeg, bmp и сохранение bmp, png
ключ я записываю в начало текста "123|" где 123 сколько символов читать
1
P1on3R
0 / 0 / 0
Регистрация: 29.10.2010
Сообщений: 3
29.10.2010, 22:24 18
Хмм интересненько, попробывал скомпилить, получаю:
[BCC32 Error] Steganografia.cpp(267): E2034 Cannot convert 'wchar_t *' to 'const char *'
[BCC32 Error] Steganografia.cpp(267): E2342 Type mismatch in parameter '__src' (wanted 'const char *', got 'wchar_t *')
Ругается на строчку:

C++
1
strcpy(String, RichEdit1->Text.c_str());
Изменил её на:

C++
1
strcpy[String, RichEdit1->Text.c_str()];
Одна ошибочка исчезла, осталась только:

[BCC32 Error] Steganografia.cpp(267): E2034 Cannot convert 'wchar_t *' to 'int'
ЗЫ CodeGear C++ Builder 2009 12.0.3420.21218, и апдэйты стоят, хмммм....

Добавлено через 1 час 39 минут
Решил свою проблему, правильная строчка выглядит так:
C++
1
strcpy(String,RichEdit1->Text.t_str());
Скобочки все-таки круглые оставляем
0
P1on3R
0 / 0 / 0
Регистрация: 29.10.2010
Сообщений: 3
31.10.2010, 12:19 19
Еще один нюанс В файле OpenKey.cpp ищем строчку
C++
1
OPEN.open(Stego->OpenDialog1->FileName.c_str());
и меняем её на:
C++
1
OPEN.open(Stego->OpenDialog1->FileName.t_str());
Тогда все пашет корректно с открытием ключа, для версии C++, что я указал выше
0
Erravielle
341 / 52 / 3
Регистрация: 27.04.2009
Сообщений: 36
31.10.2010, 13:02  [ТС] 20
Да, проблемы с c_str() и t_str() имеет место быть в различных версиях Builder...
Поэтому будет ли ошибка или ее не будет вовсе зависит от того, в каком Билдере вы компилируете проект...
0
31.10.2010, 13:02
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
31.10.2010, 13:02

Borland C++ Builder 6 руководство разработчика
Подсказали книгу по С++ (сказали что хорошая) Посмотрел уж больно дорогая она(...

Где скачать Руководство разработчика C++B6
Подскажите пожалуйста ссылочку, где можно БЕСПЛАТНО скачать &quot;Borland C++...

Краткое руководство по работе с классом TCanvas для начинающих
Вступление Часто на форуме возникают вопросы по поводу рисования той или...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru