Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #104, АВГУСТ 2007 г.

Секреты Джеймса Бонда

Сергей «John Frost» Колесник

Хакер, номер #104, стр. 106

(John-Frost@yandex.ru)

Передаем скрытые сообщения в файлах формата *.txt

В последнее время интерес к стеганографии начинает превышать интерес к криптографии, и это понятно, ведь скорость взлома шифрованной информации зависит только от мощности вычислительных ресурсов взломщика. Стеганография же предоставляет возможность скрыть сам факт передачи скрытых данных.

Что это такое и с чем его едят

Слово «стеганография» происходит от греческих слов steganos (тайна) и graphy (запись) и означает «тайнопись». Получается, что главной задачей стеганографии является скрытие факта передачи секретной информации.

Поискав в интернете стеганографический софт, можно обнаружить большое количество программ от самых разных разработчиков: от продукции крупных компаний, занимающихся безопасностью, до всяческих «Суперскрывателей Текста В КартинГах», основанных на могучем алгоритме Василия Пупловенко, предположительно похищенном им из секретной лаборатории ГРУ :).

К большинству программ даже прилагаются исходники. Но, просмотрев пару-другую таких «шедевров» программерской мысли, мы погружаемся в дежавю: большинство авторов использует примерно один и тот же алгоритм (LSB,DCT и др.). Кто-то даже переделывает алгоритм под себя, что делает скрытие данных более устойчивым к вскрытию, а кто-то просто берет известную идею, прикрепляет к ней графические навороты и начинает продавать эту шпионскую софтину по заоблачным ценам. Ладно, оставим рассуждения преподавателям информатики и политикам (они за это деньги получают), а сами перейдем к делу.

О вредности известных алгоритмов

В настоящее время самыми популярными контейнерами, применяемыми для скрытия информации, являются графические, звуковые и видеофайлы. Причиной тому является несовершенство человеческий органов чувств: не каждый заметит небольшое изменение палитры изображения, очень трудно отличить звук CD-качества от его mp3-варианта с очень высоким битрейтом. Из этого следует, что в таких данных содержится избыточность, которую можно использовать для своих целей. Эти способы всем хороши, только все основные алгоритмы достаточно известны, и выудить скрытое послание не так уж сложно. Единственным выходом является написание своего метода скрытия информации, чем мы сейчас и займемся.

Вперед к новым тайнам

Итак, мы пришли к выводу, самой лучшей идеей будет создание своего собственного алгоритма для скрытия информации. Но перед тем как приступить к ее реализации, должны ответить на несколько вопросов:

1) Какой формат файлов мы используем для скрытия?

2) Какие требования мы предъявим к файлу?

3) Изменится ли размер файла?

4) Будет ли алгоритм изменяемым, чтобы его можно было переделать под себя?

Поразмыслив немного, я ответил следующим образом:

1) Мы будем использовать обычные текстовые файлы формата txt. Почему именно txt? Да потому что этот формат у большинства не вызывает подозрений, ведь он не содержит управляющих и служебных символов. Что написано текстом, то и есть на самом деле. Имеется и неплохой способ проверки - в ANSI-кодировке каждый символ занимает один байт, т.е. сколько символов текста, столько байт и должен занимать файл (юникод символ занимает два байта).

2)В нашем случае требования будут предъявляться только к объему файла (скоро узнаешь, почему).

3) Алгоритм не будет изменять оригинальный файл ни на байт и будет обладать неплохой изменяемостью.

Так, вроде бы с предварительной подготовкой мы закончили, перейдем к описанию самого алгоритма, а затем и непосредственно к кодингу.

Рождение идеи, или как это работает

Посмотри на рисунок с изображенными на нем хитрыми буковками. На первый взгляд, в нем нет ничего необычного и напоминает он открытый в стандартном виндовом редакторе файл шрифтов с сакраментальным «Съешь еще этих мягких французских…». Казалось бы, на нем изображен один и тот же текст, просто в разных строчках. Но зачем тогда слова english и russian? Конечно же, на этой картинке изображены буквы латинского и русского алфавита, имеющие одинаковое начертание. Заметить разницу можно только при переходе со стандартного шрифта на какой-нибудь покрасивее, но поскольку в файлах формата txt не хранится информация о шрифте и прочих красивостях, файл будет открываться со стандартным начертанием и, следовательно, никто не ничего не заподозрит (вряд ли кто-то, открывая readme.txt, сразу будет изменять шрифты). Все привыкли к тому, что в обычном txt-файле не может ничего содержаться - ни скрытых данных, ни вредоносных программ - только видимый текст, а привычка, как известно, великая сила. Этим мы и воспользуемся :).

Итак, мы получили в свое распоряжение 16 букв (10 больших и 6 маленьких), теперь наметим примерный алгоритм: берем какой-нибудь файл с русским текстом (можно книгу), просматриваем все символы и меняем русские символы на латинские только нам известным способом. А вот каков он будет, зависит лишь от твоей, дорогой читатель, фантазии. Я, к примеру, сделал так, что следующая буква после замененного символа становится буквой скрытого текста. Ты же можешь придумать что-нибудь свое.

Начинаем кодить

Сейчас мы напишем программу, реализующую мой алгоритм замены, а ты потом легко переделаешь ее под себя, так что даже если кто-то и обнаружит факт скрытия информации, прочесть ее он все равно не сможет, поскольку не знает твой алгоритм кодирования.

Запускай Visual Studio .Net, выбирай новый проект, в качестве языка выбирай С# (алгоритм не зависит от языка и может быть легко перенесен на любой другой). На появившиеся окно кидай TextBox в количестве двух штук, три Button и два label.

Один TextBox мы будем использовать для отражения содержимого текстового файла, второй - для скрытого послания. Одна кнопка будет служить для открытия нужного файла, вторая, соответственно, будет сохранять, ну а третья - выводить инфу о разработчике (то есть о тебе). В результате у тебя должно получиться нечто похожее на то, что изображено на рисунке (конечно, необязательно делать такой убогий дизайн)

Как известно, прежде чем что-то считывать из файла, надо туда что-то записать, следовательно, мы должны научить нашу прогу анализировать текст, подставлять туда нужные символы и сохранять это чудо в файл.

Алгоритм сохранения будет таков:

1) считываем с TextBox'а (в котором хранится обычный текст) подряд все буквы, сравниваем каждую из них с буквой из другого TextBox'а (в котором хранится секретный текст);

2) если они совпадают, то смотрим предыдущую букву (из обычного текста), и если она совпадает с одной из букв, которые имеют одинаковое написание в разных алфавитах, то заменяем ее русский вариант англоязычный аналогом;

3) записываем полученный текст в файл;

Этот алгоритм в реализации на си шарп ты можешь увидеть в блок-врезке (полный вариант - на нашем диске).

Теперь поговорим об алгоритме считывания скрытого текста из нашего файла. Тут все намного проще: открываем файл, если встречается англоязычный вариант нужной буквы, то записываем в секретный текст идущий слева от нее символ. Но как же быть, если там есть настоящий английский текст? Просто очень сложно найти текст полностью на русском. Думаем, ведь текст - это набор символов, значит, если это текст, то справа или/и слева должны стоять тоже английские буквы. Проблема решена: если рядом с найденным символом справа или слева будут стоять тоже английские буквы, то это текст и дешифровать его не надо. Не очень эффективно, но зато поможет, если в тексте есть небольшие английские слова. Посмотрим на функцию, решающую все эти проблемы и выдирающую секретный текст:

private string GetSecretSymbols(string text)

{

// для проверки, английский этот текст или всего лишь символ

bool beng=false;

//английский алфавит

string seng = "qwertyuiopasdfghjklzxcvbnm";

//строка, куда будут помещены секретные символы

string secrets="";

// перебираем все символы переданного нам текста

for (int i = 0; i < text.Length-1; i++)

{

switch (text[i])

{

//и если это один из наших замененных вариантов

case 'A':

case 'B':

case 'C':

case 'E':

case 'H':

case 'M':

case 'O':

case 'P':

case 'T':

case 'X':

case 'a':

case 'c':

case 'e':

case 'o':

case 'p':

case 'x':

// проверяем текст на англоязычность

for (int j = 0; j < seng.Length; j++)

{

if ((text[i - 1].ToString().ToUpper() == seng[j].ToString().ToUpper())

|| (text[i + 1].ToString().ToUpper() == seng[j].ToString().ToUpper()))

beng = true;

}

if (!beng) //если текст не английский

{

secrets += text[i + 1];

}

beng = false;

break;

}

}

retu secrets; //возвращаем секретный текст

}

Конец игры, Бонд

Подытожим, что же у нас получилось. А получилось у нас то, что нам и хотелось, - прога, которая неплохо скрывает текст в файлах формата *.txt. Да, при слишком большом объеме скрываемого сообщения она не подойдет. Зато, если переделать алгоритм под конкретные нужды, можно добиться неплохой производительности и результативности, а то, что софтина не изменяет размер модифицированного файла, - это большой плюс. Не нужно быть гением, чтобы догадаться, что можно скрыть больше текста, если исходный текст был набран в верхнем регистре.

CD

На диске ты найдешь исходник нашей программы, а также несколько текстовых файлов, в которых содержится скрытая информация и один оригинальный файл, который ты можешь использовать для кодирования своего текста.

INFO

Первое упоминание о стеганографических методах в литературе принадлежит Геродоту, описавшему случай передачи сообщения Демартом, который соскабливал воск с дощечек, писал письмо прямо на дереве, а потом заново покрывал дощечки воском.

Другой эпизод, который относят к тем же временам, - передача послания с использованием головы раба. Для передачи тайного сообщения голову раба обривали, наносили на кожу татуировку и, когда волосы отрастали, отправляли с посланием.

Функция, реализующая сохранение скрытого текста в обычном

private void bSaveFile_Click(object sender, EventArgs e)

{

// для хранения номера считанного символа обычного текста

int number2 = 0;

// текущий номер символа секретного текста

int number1 = 0;

// для хранения промежуточных расчетов

string temp = "";

for (int j = 0; j < tbVisibleText.Text.Length-1; j++)

//перебираем все символы обычного текста

{

// если текущий элемент секретного текста равен

// элементу обычного текста, то...

if ((tbHiddenText.Text[number1].ToString().ToUpper() == tbVisibleText.Text[j].ToString().ToUpper()))

{

switch (tbVisibleText.Text[j - 1])

//проверяем предыдущий символ

{

case 'А'://если это русская буква А

temp = temp.Substring(0, temp.Length - 1);

temp += "A"; //заменяем англоязычным вариантом

// теперь записываем следующие три символа

// во избежание совпадения с заменяемыми

temp += tbVisibleText.Text[j];

temp += tbVisibleText.Text[j+1];

temp += tbVisibleText.Text[j+2];

number1++; //записали на один скрытый символ больше

j+=2; //перепрыгиваем сразу на два символа

break;

... //здесь такой же кусок кода, только другие буквы

default:

temp += tbVisibleText.Text[j];

// если ни одна буква не совпадает,

// просто записываем один символ

break;

}

}

else temp += tbVisibleText.Text[j]; //иначе просто прибавляем его

}

...

//добавляем оставшийся кусок текста после манипуляций

temp += tbVisibleText.Text.Substring(number2);

//теперь записываем весь этот текст в текстбокс

tbVisibleText.Text = temp;

if (saveFileDialog1.ShowDialog() == DialogResult.OK)

//и сохраняем результат в файл

{

FileStream fs = new FileStream(saveFileDialog1.FileName, FileMode.Create);

byte[] barray = Encoding.Unicode.GetBytes(tbVisibleText.Text);

fs.Write(barray, 0, barray.Length);

fs.Close();

}

}

От редактора

Лозовский Александр

В принципе технология интересная. Но! Одно дело использовать ее, посылая письма молодой и симпатичной даме через TheBat (в формате plain text) так, чтобы ее бдительный бойфренд, перлюстрирующий почту, не смог ничего запалить :), и совсем другое – использовать для передачи действительно секретной информации. Алгоритм, в его нынешнем понимании, даже для студента-технаря представляет проблем не больше, чем знаменитый шифр Юлия Цезаря (да-да, тот самый «симетричний алгоритм шифрування підстановками» :)).

Содержание
ttfb: 22.207975387573 ms