Небольшое предисловие
Стеганография, если кто не помнит, — это сокрытие информации в каких-либо контейнерах. Например, в картинках (обсуждалось тут и тут). Можно также скрыть данные в служебных таблицах файловой системы (об этом писалось тут), и даже в служебных пакетах протокола TCP. К сожалению, у всех этих методов есть один недостаток: чтобы незаметно «вкрапить» информацию в контейнер, нужны хитрые алгоритмы, учитывающие особенности внутреннего устройства контейнера. Да и с устойчивостью контейнера к манипуляциям возникают проблемы: например, если чуточку подредактировать картинку, скрытая информация теряется.
Можно ли как-то обойтись без хитрых алгоритмов и тонких манипуляций с данными, и при этом все-таки обеспечить работоспособность контейнера и приемлемый уровень сохранности скрытых данных? Забегая вперед скажу — да, можно! И даже утилитку предложу.
Кровавые подробности метода
Основная идея проста, как удар дубиной по лбу: на диске есть области, в которые операционная система никогда не пишет (или пишет в редких случаях). Чтобы не нужно было искать эти области хитрыми алгоритмами, воспользуемся избыточностью — то есть много-много раз продублируем нашу скрытую информацию по всем секторам диска. Затем прямо поверх всего этого благолепия можно создавать нужные разделы, форматировать файловые системы, писать файлы и ставить ОСи — все равно часть секретных данных сохранится и ее можно будет извлечь, а многократное дублирование поможет нам составить из кусочков исходное целое.
Достоинство такого метода очевидно: мы не зависим ни от формата файлов, ни даже от типа используемой файловой системы.
Недостатки тоже, думаю, очевидны:
- Секретные данные можно будет изменить только полной перезаписью всего диска, с последующим воссозданием видимого пользователю содержимого. При этом нельзя пользоваться софтом, воссоздающим диск из образа: он воссоздаст и предыдущие секретные данные.
- Чем больше объем секретных данных, тем больше вероятность потери части информации.
- Извлечение данных с диска может занять много времени. От нескольких минут до нескольких дней (современные диски больши-ие).
Теперь перейдем к частностям.
Понятно, что если просто размазать секретные данные по всему диску, то скрыты они будут только от невооруженного взгляда. Если вооружить взгляд, скажем, редактором диска, то данные предстанут во всей красе. Поэтому данные неплохо бы зашифровать, чтобы не отсвечивали. Шифровать будем простенько, но со вкусом: по алгоритму aes256-cbc. Ключ шифрования спросим у пользователя, пусть хороший пароль придумывает.
Следующий вопрос — в том, как нам отличить «правильные» данные от испорченных. Тут нам поможет контрольная сумма, да не простая, а SHA1. А что? Для git'а она достаточно хороша, значит, и нам подойдет. Решено: снабжаем каждый сохраненный фрагмент информации контрольной суммой, и если после расшифровки она совпала — значит, расшифровка удалась.
Еще обязательно понадобится номер фрагмента и общая длина секретных данных. Номер фрагмента — чтобы отслеживать, какие кусочки мы уже расшифровали, а какие остались. Общая длина нам пригодится при обработке последнего фрагмента, чтобы не писать лишние данные (сиречь паддинг). Ну и раз уж у нас все равно наклевывается заголовок, то добавим туда имя секретного файла. Оно пригодится уже после расшифровки, чтобы не гадать, чем его открывать.
Проверяем метод на практике
Для проверки возьмем самый распространенный носитель — флэшку. У меня нашлась старенькая на 1 Гб, что вполне подойдет для экспериментов. Если вам, как и мне, пришла в голову мысль не париться с физическими носителями, а потестировать на файлике — образе диска, то сразу скажу: не выйдет. При форматировании такого «диска» линукс создает файл заново, и все неиспользуемые сектора будут заполнены нулями.
В качестве машины с линуксом, к сожалению, пришлось воспользоваться валяющейся на балконе метеостанцией на Raspberry Pi 3. Памяти там негусто, поэтому большие файлы прятать не будем. Ограничимся максимальным размером в 10 мегабайт. Слишком маленькие файлы тоже прятать смысла нет: утилитка пишет данные на диск кластерами по 4 Кб. Поэтому снизу ограничимся файлом в 3 кб — он влезает в один такой кластер.
Издеваться над флэшкой будем поэтапно, проверяя после каждого этапа, читается ли скрытая информация:
- Быстрое форматирование в формате FAT16 с размером кластера 16 кб. Это то, что предлагает сделать Windows 7 с флэшкой, на которой отсутствует файловая система.
- Заполнение флэшки всяким мусором на 50%.
- Заполнение флэшки всяким мусором на 100%.
- «Долгое» форматирование в формате FAT16 (с перезаписью всего).
Первые два испытания вполне ожидаемо закончились полной победой: утилита смогла успешно извлечь 10 мегабайт секретных данных с флэшки. А вот после того, как флэшку забили файлами под завязку, произошел сбой:
Total clusters read: 250752, decrypted: 158
ERROR: cannot write incomplete secretFile
Как видим, удалось успешно расшифровать только 158 кластеров (632 килобайта сырых данных, что дает 636424 байта полезной нагрузки). Понятно, что 10 мегабайт тут никак не наберется, а ведь среди этих кластеров явно есть дубликаты. Даже 1 мегабайт таким образом уже не восстановишь. Но зато можно гарантировать, что 3 килобайта секретных данных мы восстановим с флэшки даже после того, как ее отформатируют и запишут под завязку. Впрочем, опыты показывают, что с такой флэшки вполне возможно извлечь файл длиной 120 килобайт.
Последнее испытание, к сожалению, показало, что флэшка перезаписалась вся:
$ sudo ./steganodisk -p password /dev/sda
Device size: 250752 clusters
250700 99%
Total clusters read: 250752, decrypted: 0
ERROR: cannot write incomplete secretFile
Ни одного кластера не сохранилось… Печально, но не трагично! Попробуем перед форматированием создать на флэшке раздел, а уже в нем — файловую систему. Кстати, с завода она пришла именно с таким форматированием, так что ничего подозрительного мы не делаем.
Вполне ожидаемо, что доступное пространство на флэшке немного уменьшилось.
Также вполне ожидаемо, что 10 мегабайт все-таки не удалось спрятать на полностью забитый диск. Но зато теперь количество успешно расшифрованных кластеров увеличилось более чем в два раза!
Total clusters read: 250752, decrypted: 405
Мегабайт, к сожалению, собрать из кусочков не получится, а вот килобайт двести — запросто.
Ну и весть о последней, 4-й проверке, на этот раз радостная: полное форматирование такой флэшки не привело к уничтожению всей информации! 120 килобайт секретных данных прекрасно влезло в неиспользуемое пространство.
Сводная таблица по тестированию:
Немного теоретизирования: о свободном месте и неиспользуемых секторах
Если вы когда-то разбивали жесткий диск на разделы, то могли обратить внимание, что далеко не всегда получается отвести все свободное пространство на диске. Первый раздел всегда начинается с некоторым отступом (обычно это 1 мегабайт, или 2048 секторов). За последним разделом тоже, бывает, остается небольшой «хвост» из неиспользуемых секторов. Да и между разделами иногда остаются промежутки, хотя и редко.
Другими словами, на диске есть сектора, к которым нет доступа при обычной работе с диском, но данные-то в эти сектора записать можно! А значит, и прочитать тоже. С поправкой на то, что есть еще таблица разделов и код загрузчика, которые как раз и расположены в пустой области в начале диска.
Отвлечемся на время от разделов и посмотрим на диск с высоты, так сказать, птичьего полета. Вот есть у нас на диске пустой раздел. Создадим в нем файловую систему. Можно ли сказать, что какие-то сектора на диске остались незатертыми?
И-и-и — барабанная дробь! Ответ практически всегда будет — да! Ведь в большинстве случаев создание файловой системы сводится к тому, что на диск записывается только несколько блоков служебной информации, а в остальном содержимое раздела не меняется.
А еще — чисто эмпирически — можно предположить, что файловая система не всегда может занять все отведенное ей пространство до последнего сектора. Например, файловая система FAT16 с размером кластера в 64 килобайта очевидно, не сможет полностью занять раздел с размером, не кратным 64 килобайтам. В конце такого раздела должен будет остаться «хвост» в несколько секторов, недоступный для хранения пользовательских данных. Впрочем, экспериментально это предположение подтвердить не удалось.
Итак, чтобы максимизировать место, доступное под стеганограмму, нужно использовать файловую систему с размером кластера побольше. Можно еще создать раздел, даже если это необязательно (на флэшке, например). Создавать пустые разделы или оставлять нераспределенные области не нужно — это привлечет внимание интересующихся граждан.
Утилита для экспериментов
Исходники утилиты можно пощупать здесь
Для сборки потребуется Qt версии 5.0 и выше и OpenSSL. Если что-то не собирается — возможно, придется подправить файл steganodisk.pro.
Можно поменять размер кластера с 4 Кб на, скажем, 512 байт (в secretfile.h). При этом вырастут расходы на служебную информацию: заголовок и контрольная сумма занимают фиксированные 68 байт.
Запускать утилиту нужно, естественно, с правами пользователя root, причем с осторожностью. Никаких вопросов перед перезаписью указанного файла или устройства не будет!
Наслаждайтесь.
Автор: berez