Pull to refresh

Стеганография на Perl

Perl *
Sandbox
Доброго времени суток, уважаемые читатели.

Я — представитель типичной (для Хабра) современной «школоты». Интересуюсь околокомпьютерной тематикой, знаю пару языков программирования, верю, что что-то в этом смыслю. Но, пост не о том.

Просматривая хабр, я нередко натыкался на посты по теме стеганографии. Как начинающего (очень начинающего, ну совсем начинающего) программиста эта тема заинтересовала меня своей, с одной стороны неочевидностью, с другой — относительной простотой реализации. К тому-же, приближалась практика по программированию…
Пораскинув мозгами, я решил построить свой лунапарк, с блэк... написать свою стеганографирующую программу. Об этой программе, трудностях при ее написании и об их преодолении и будет мой первый хабрапост.


О чем шум?

Думаю, хабрапользователям нет необходимости объяснять, что же такое «стеганография», но, на всякий случай, позволю себе привести ссылку на пост, описывающий основные методы стеганографии.
Стеганография by Megas
На данный момент я использую метод LSB, более подробно описанным в данном посте, точнее — его обобщением для форматов BMP, PNG и WAV.

Глава первая, постановка задачи

Когда осознаешь, что изобретаешь очередной велосипед, в первую очередь пытаешься придумать, чем же он будет отличаться от велосипедов других, подобных тебе «изобретателей». Для себя я выделил следующие пункты (однако, не все из них (пока) удалось воплотить в жизнь):
  • Поддержка разных форматов файлов
  • Поддержка хотя бы базового уровня шифрования
  • Возможность застеганографировать файл любого формата в стег-контейнер (пока не реализовано).
  • Модульность, т.е. возможность пользователю с навыками программирования на Perl написать модули для работы данной программы с другими форматами (реализовано лишь «внутренне», API есть но недокументированно).
  • Адекватный графический интерфейс (выполнена лишь вторая часть этого пункта, интерфейс вышел «графический», но, на мой взгляд, его не назовешь «адекватным»)

В качестве языка я выбрал Perl 5, причины выбора сугубо субъективные — язык мне просто нравится (хотя осознаю что избежал бы многих подводных камней, выбрав, к примеру, C++).

Глава вторая, подводные камни

1. Работа с двоичными данными

Первый «камень» был встречен практически сразу же. Используемый метод LSB, заключается в изменении N младших битов в каждом байте. Однако, Perl не предоставляет возможности работать с файлом как с последовательностью битов, данные считываются минимум побайтово.
Данная трудность оказалось самой простой, за несколько минут была написанна подпрограмма str2bin, переводящая строку байтов в массив битов:
sub str2bin ($) {
	my @bin;
	my $str = shift;
	for (my $i=0; $i < length($str); $i++) {
		@bin = (@bin, split ('', sprintf "%08b", ord(substr($str, $i, 1))));
	}
	return @bin;
}

2. Программа-слоупок

Следующие несколько дней дело шло отлично, был написан модуль для работы с файлами в формате BMP, начата работа над форматом WAV. Однако, в процессе тестирования было замечено, что программа неоправданно долго обрабатывает сообщения длинной в тысячу и более символов (для сообщения из ~4500 символов время записи составляло 25 сек., чтения — 60 сек).
Путем долгого курения мануалов, нескольких сотен применений МНТ, было выяснено следущее: операции с изменением массивов в Perl'e медленные. Нет, даже так: меееедленные.
То есть, простая замена
for (my $i=0; $i < length($str); $i++) {
	@bin = (@bin, split ('', sprintf "%08b", ord(substr($str, $i, 1))));
}
return @bin;
}

на
for (my $i=0; $i <= $#t; $i++) {
	$bin .= substr(dec2bin(ord($t[$i])), 24, 8);
}
return split('', $bin);

сократило время чтения и записи с 25 сек и 60 сек, до 0.135 сек и 0.230 сек соответственно, то есть было достигнуто ускорение в сотни раз!

3. GUI, Perl, Кирилл и Мефодий

После написания модуля для WAV было выясненно, что используемый тогда для написания GUI тулкит Tkx не мог адекватно работать с кириллическими символами в поле TextEdit.
Эту проблему не удалось обойти, вследствие чего было принято решение переписать графическую часть на Win32::GUI.

4. Зачем же мертвых то мучать?

По ходу дела в голову все чаще приходили слова Megas:
Но данный способ имеет и не мало минусов. Во-первых он применим лишь к малому количеству контейнеров.
Возникало желание придумать метод для работы с более живыми форматами. Тут в голову пришли некогда прочитанные строчки Википедии о том, что формат PNG работает со сжатием без потерь.
В голову пришла простая, неэлегантная, но зато рабочая идея:
  1. Взять PNG-изображение
  2. Преобразовать его во временное, в формате BMP
  3. Применить ко временному уже реализованные методы стеганографии в BMP
  4. Преобразовать временный BMP обратно в PNG

Расшифровка сообщения — аналогично, через временный BMP.

Глава третья, заключительная (с картинками!)

Итак, очередной велосипед начал свой жизненный путь.
На последок, дам ссылку на git-репозиторий данной программы (KaiNS), а также покажу результаты ее работы:

Исходное png-изображение
In

Изображение-контейнер, содержащее 10903 раза повторенную строку «Привет, Хабр!»
Out

Исходный wav-файл (1.7 Mb)
Wav-контейнер, пароль — Utter (1.7 Mb)

Git:
git://github.com/utter-step/KAiNS.git
(необходимы установленные модули Perl: Win32::GUI, Imager).

Исполняемый файл:
KaiNS.exe

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

UPD: В комментариях предложен код, еще эффективнее работающий с массивом для ф-ии str2bin, а так-же принципиально более верное решение для работы с двоичными данными в Perl'e. Первое применю, со вторым пока разбираюсь в контексте данной задачи.
Tags:
Hubs:
Total votes 47: ↑44 and ↓3 +41
Views 4.7K
Comments Comments 32