Конференция Персональные данные 2012: проблемы и решения

Вы здесь: Главная ЧИТАЛЬНЫЙ ЗАЛ Стеганография Текстовая стеганография

Вход Регистрация



Защита персональных данных
Статьи по защите персональных данных
Видео о защите персональных данных
Федеральный закон N 152-ФЗ "О персональных данных"
Федеральный закон Российской Федерации от 27 июля 2006 г. N 152-ФЗ "О персональных данных"
Об утверждении Положения об обеспечении безопасности персональных данных при их обработке в информационных системах персональных данных
Постановление Правительства РФ № 781 от 17 ноября 2007
Об утверждении Положения об особенностях обработки персональных данных, осуществляемой без использования средств автоматизации
Постановление Правительства Российской Федерации от 15 сентября 2008 г. N 687
See the entire folder …
СОФТ
Средства для оценки рисков
Средства разработки и внедрения политик безопасности
Средства мониторинга действий пользователей
Программы, предназначенные для контроля работы пользователей и администраторов за своими компьютерами и в сети Интернет. Они сообщают по сети информацию, которая интересует шефа, ему лично или уполномоченному им лицу.
Средства аудита и восстановления паролей
Шифровальщики файлов
Эти программы обеспечивают недорогое, надежное и быстрое шифрование файлов и дисков с использованием популярных алгоритмов (AES, 3DES, Blowfish, ...)
See the entire folder …

Текстовая стеганография

Send this page to somebody Print this page

Стеганография... Этот сравнительно недавно вошедший в компьютерный обиход термин обычно переводится как тайнопись. Совместно с криптографией стеганография (steganography) используется для защиты информации, фундаментального свойства окружающего мира, в таковой вовсе не нуждающейся, как, впрочем, и две другие его категории — материя и мера [1].

Назначение стеганографии

Требует защиты только изображение информации, представленное на материальном носителе, причем под ней понимается создание условий, исключающих либо затрудняющих доступ к носителю, внесение изменений или уничтожение носителя, а также восприятие представленных на нем данных, производимое с помощью методов криптографии и стеганографии. И если, образно говоря, криптография делает понятное непонятным, то стеганография делает видимое невидимым (иногда и в прямом смысле слова). Достигается это «растворением» скрываемой информации среди других данных значительно большего объема. Видимо, значение стеганографии в деле сокрытия информации сегодня помимо спецслужб в полной мере оценили лишь стоящие вне закона взломщики программ и авторы компьютерных вирусов.

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

Тем не менее некоторые авторы не склонны придавать значение собственной информации контейнера, считая ее безразличной как для отправителя, так и для получателя стеганосообщения [2]. Однако это не совсем так, что подтверждается все тем же примером компьютерного вируса.

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

Методы текстовой стеганографии

Стеганография, использующая текстовые контейнеры, называется текстовой (text steganography). Далее будет рассмотрено, каким образом можно применять текстовые контейнеры для хранения стего. Достаточно полная классификация подобных методов дана в работе [2]. Удивление вызывает лишь тот факт, что из автоматических методов текстовой стеганографии в этой статье упомянут только один — форматирование (т. е. выравнивание) текста с помощью пробелов.

Суть данного метода состоит в раздвижке строки путем увеличения пробелов между словами, когда один пробел соответствует, например, биту 0, два пробела — биту 1. Однако прямое его применение хотя и возможно, но на практике порождает массу неудобств, в частности, оформление текста становится неряшливым, что позволяет легко заподозрить в нем наличие стего.

В листинге 1 приведена программа, где подобные проблемы уже решены (этот листинг и другие, предложенные в статье, см. на «Мир ПК-диске», раздел «Студия программирования»). Программа попросту перераспределяет пробелы в пределах текущей длины строки, перенося по возможности длинные пробелы в ее конец. В результате строки выходного текста имеют аккуратный вид, затрудняющий выявление стего.

Листинг 1.

Кодирование стего выравниванием строк пробелами.
program StegoShift;
(***************************************************************)
(* Простая стеганографическая программа, использующая *)
(* раздвижку слов в строке для встраивания стего в *)
(* произвольные тексты. мспользует 2 или 3 параметра, которые *)
(* и определяют выполняемую программой функцию. При этом *)
(* первый из параметров всегда представляет файл-контейнер. *)
(* Если параметров два, стеганограмма извлекается из *)
(* контейнера, а результат записывается в заданный вторым *)
(* параметром файл и в виде эхопечати выводится на экран. *)
(* Если параметров три, то стеганографический текст из *)
(* файла заданного вторым параметром, упрятывается в *)
(* файл-контейнер, заданный первым параметром, а результат *)
(* стеганографического преобразования записывается в файл, *)
(* заданный третьим параметром. Совпадение двух имён файлов *)
(* допускается (трёх - абсурд). *)
(***************************************************************)

type
StringType = string [$FF];

const
TempName = '$$$$$$$$.$$$';
Key1 = $1234;
Key2 = $4567;

var
F, G, H : text;
Line, Head, Tail, Body : StringType;
B, I, K, L, N, LenBody, Z : byte;
C : char;
Tab : array [0..255] of byte;
Count : real;

begin
LowVideo;

if not (ParamCount in [1..3])
then
begin
WriteLn ('Ожидается ввод 2 или 3 параметров:');
WriteLn ('2 - вывод стеганограммы в файл;');
WriteLn ('3 - запись стеганограммы в файл.');
Exit
end;

Assign (F, ParamStr (1));
Reset (F);
Count := 0;

MemW [Dseg : $01FE] := Key1;
MemW [Dseg : $01FC] := Key2;

(*----------------------ВЫВОД СТЕГАНОГРАММЫ В ФАЙЛ---------------------*)
if ParamCount = 2
then
begin
if Pos (':', ParamStr (2)) <> 2
then Assign (G, TempName)
else Assign (G, Copy (ParamStr (2), 1, 2) + TempName);

Rewrite (G);
Z := 0;
L := 0;

while not Eof (F)
do
begin
ReadLn (F, Line);

(* 1. Выделяем тело строки *)
while (Line <> '')
and (Line [1] <= ' ')
do Delete (Line, 1, 1);

while (Line <> '')
and (Line [Length (Line)] <= ' ')
do Delete (Line, Length (Line), 1);

(* 2. Заполняем таблицу пробелов *)
FillChar (Tab, SizeOf (Tab), 0);
LenBody := Length (Line);

K := 0;
I := 0;

while I < LenBody
do
begin
while (I < LenBody) and (Line [I] <> ' ')
do I := Succ (I);

if (I < LenBody) and (Line [I] = ' ')
then
begin
K := Succ (K);
N := 0;

while (I < LenBody)
and (Line [I] = ' ')
do
begin
N := Succ (N);
I := Succ (I)
end;

Tab [K] := N
end
end;

if K > 0
then K := Pred (K);

(* 3. Декодируем биты *)
I := 0;

while I < K
do
begin
I := Succ (I);

if Tab [I] > 1
then
begin
Z := Z shr 1;

if Odd (Tab [I])
then Z := Z or $80;

L := Succ (L) mod 8;

if L = 0
then
begin
C := Chr (Z
xor Random (256));
Count := Count + 1;
Write (C);
Write (G, C);
Z := 0
end
end
end;
end;

(* 4. Завершаем работу *)
Close (F);
Close (G);

Assign (F, ParamStr (2));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (G, ParamStr (2));

if WhereX <> 1
then WriteLn;

WriteLn ('Прочитано ', Count : 0 : 0, ' байт стего...');
Exit
end;

(*----------------------ЗАПмСЬ СТЕГАНОГРАММЫ В ФАЙЛ--------------------*)
Assign (G, ParamStr (2));

if Pos (':', ParamStr (3)) <> 2
then Assign (H, TempName)
else Assign (H, Copy (ParamStr (3), 1, 2) + TempName);

Reset (G);
Rewrite (H);
L := 0;

while not Eof (F)
do
begin
(* 1. мнициализируем таблицу раздвижек *)
FillChar (Tab, SizeOf (Tab), 0);

(* 2. Читаем и разделяем строку на части *)
ReadLn (F, Line);
I := 0;

while (I < Length (Line)) and (Line [Succ (I)] <= ' ')
do I := Succ (I);

Tab [0] := I;
(* начало строки *)
Head := Copy (Line, 1, I);

I := Length (Line);

while (I > 0) and (Line [I] <= ' ')
do I := Pred (I);

(* конец строки *)
Tail := Copy (Line, Succ (I), Length (Line) - I);
(* тело строки *)
Body := Copy (Line, Succ (Tab [0]), I - Tab [0]);

(* 3. Редуцируем тело строки *)
LenBody := Length (Body);

while Pos (' ', Body) > 0
do Delete (Body, Pos (' ', Body), 1);

(* число вставляемых пробелов *)
N := LenBody - Length (Body);

(* 4. Заполняем таблицу раздвижек *)
K := 0;

for I := 1 to Length (Body)
do
if Body [I] = ' '
then
begin
K := Succ (K);
Tab [K] := 1
end;

(* 5. Распределяем значимые (информационные) пробелы *)
I := 1;

while I < K
do
begin
if L = 0
then (* извлекаем очередной байт *)
begin
if Eof (G)
then C := #00
else Read (G, C);

Count := Count + 1;
Z := Ord (C) xor Random (256);
B := Z and 1;
L := 1;
Write (C);
end;

if N > Succ (B)
then (* запас пробелов не исчерпан *)
begin
(* кодируем бит в таблице *)
Tab [I] := Tab [I] + Succ (B);
(* текущий запас пробелов *)
N := N - Succ (B);
(* указываем следующий бит *)
Z := Z shr 1;
B := Z and 1;
(* счётчик записанных битов *)
L := Succ (L) mod 9
end;

I := Succ (I)
end;

(* 6. Монотонно перераспределяем информационные пробелы *)
if K > 2
then (* число пробелов должно возрастать к концу строки *)
begin
I := 0;

while (Tab [Pred (K)] = 1) and (I < K)
do
begin
Move (Tab [1], Tab [2], K - 2);
Tab [1] := 1;
I := Succ (I)
end
end;

(* 7. Распределяем выравнивающие (незначимые) пробелы *)
while N > 1
do
begin
I := K;

while (I > 0) and (N > 1)
do
begin
if (Tab [I] > 1) or (I = K)
then
begin
Tab [I] := Tab [I] + 2;
N := N - 2
end;

I := Pred (I)
end
end;

Tab [K] := Tab [K] + N;

(* 8. Вставляем пробелы в тело строки *)
N := Length (Body);

while (N > 0) and (K > 0)
do
begin
while (N > 0) and (Body [N] <> ' ')
do N := Pred (N);

if (Tab [K] > 0) and (N > 0)
then
for I := 1 to Pred (Tab [K])
do Insert (' ', Body, N);

if K > 0
then K := Pred (K);

if N > 0
then N := Pred (N)
end;

(* 9. Формируем и записываем строку *)
Line := Head + Body + Tail;
WriteLn (H, Line)
end;

(* Заканчиваем обработку *)
Close (F);
Close (G);
Close (H);

Assign (F, ParamStr (3));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (H, ParamStr (3));

if WhereX <> 1
then WriteLn;

if (Count <> 0) and (L <> 0)
then Count := Count - 1;

WriteLn ('Записано ', Count : 0 : 0, ' байт стего...');
Exit
end.

Одиночные пробелы и пробелы перед последним словом строки не несут информационной нагрузки. В остальных случаях же четное число пробелов кодирует 0, нечетное — 1. Стего при записи предварительно шифруется, а при чтении расшифровывается с использованием операции исключающего «ИЛИ» и встроенного в систему программирования датчика случайных чисел, управляемого константами Key1 и Key2.

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

Необходимость учета множества нюансов делает данную программу довольно сложной. Поэтому представляют интерес способы встраивания стего, отнесенные в статье [2] к категории «многие другие». Из всех таких способов были выбраны простейшие. Если перечислять их с повышением уровня сложности, то это будет метод изменения порядка следования маркеров конца строки (листинг 2), метод хвостовых пробелов (листинг 3), метод знаков одинакового начертания (листинг 4) и метод двоичных нулей (листинг 5). Теперь кратко рассмотрим их.

Листинг 2.

Кодирование стего перестановкой маркеров концов строк.
program StegoCR_LF;

(***************************************************************)
(* Простая стеганографическая программа, использующая *)
(* замену порядка следования CR/LF в маркерах концов строк для *)
(* встраивания стего в произвольные тексты. мспользует от 1 до *)
(* 3 параметров, которые и определяют выполняемую программой *)
(* функцию. При этом первый из параметров всегда представляет *)
(* файл-контейнер. *)
(* Если этот параметр единственный, то проверяется *)
(* наличие стеганограммы в файле-контейнере с выводом *)
(* результата на экран. По сути это режим стеганодетектора. *)
(* Если таких параметров два, стеганограмма извлекается из *)
(* контейнера, а результат записывается в заданный вторым *)
(* параметром файл. *)
(* Наконец, если используются все три параметра, то *)
(* стеганографический текст из файла заданного вторым *)
(* параметром, упрятывается в файл-контейнер, заданный первым *)
(* параметром, а результат стеганографического преобразования *)
(* записывается в файл, заданный третьим параметром. *)
(* Совпадение двух имён файлов допускается (трёх - абсурд). *)
(***************************************************************)

type
StringType = string [$FF];

const
TempName = '$$$$$$$$.$$$';
Key1 = $1234;
Key2 = $4567;
CR = #$0D;
LF = #$0A;

var
F, G, H : text;
X : StringType;
I : integer;
K, L : byte;
C, CC : char;
Count : real;

begin
LowVideo;

if not (ParamCount in [1..3])
then
begin
WriteLn ('Ожидается от 1 до 3 параметров:');
WriteLn ('1 - вывод стеганограммы на экран;');
WriteLn ('2 - вывод стеганограммы в файл;');
WriteLn ('3 - запись стеганограммы в файл.');
Exit
end;

Assign (F, ParamStr (1));
Reset (F);
Count := 0;

MemW [Dseg : $01FE] := Key1;
MemW [Dseg : $01FC] := Key2;

(*---------------------ВЫВОД СТЕГАНОГРАММЫ НА ЭКРАН--------------------*)
if ParamCount = 1
then
begin
C := #00;
K := 0;
L := 0;

while not Eof (F)
do
begin
Read (F, CC);

if C = CR
then
if CC = LF
then
begin
L := Succ (L) mod 8;
K := K shr 1;
C := #00;

if L = 0
then
begin
Write (Chr (K));
K := 0;
L := 0
end
end
else C := CC
else
if C = LF
then
if CC = CR
then
begin
L := Succ (L) mod 8;
K := K shr 1 or $80;
C := #00;

if L = 0
then
begin
Write (Chr (K));
K := 0;
L := 0
end
end
else C := CC
else C := CC
end;

Close (F);

if WhereX <> 1
then WriteLn;

WriteLn ('Ok!');
Exit
end;

(*----------------------ВЫВОД СТЕГАНОГРАММЫ В ФАЙЛ---------------------*)
if ParamCount = 2
then
begin
if Pos (':', ParamStr (2)) <> 2
then Assign (G, TempName)
else Assign (G, Copy (ParamStr (2), 1, 2) + TempName);

C := #00;
K := 0;
L := 0;
Assign (G, TempName);
Rewrite (G);

while not Eof (F)
do
begin
Read (F, CC);

if C = CR
then
if CC = LF
then
begin
L := Succ (L) mod 8;
K := K shr 1;
C := #00;

if L = 0
then
begin
K := K xor Random (256);
Count := Count + 1;
Write (G, Chr (K));
Write (Chr (K));
K := 0;
L := 0
end
end
else C := CC
else
if C = LF
then
if CC = CR
then
begin
L := Succ (L) mod 8;
K := K shr 1 or $80;
C := #00;

if L = 0
then
begin
K := K
xor Random (256);
Count := Count + 1;
Write (G, Chr (K));
Write (Chr (K));
K := 0;
L := 0
end
end
else C := CC
else C := CC
end;

Close (F);
Close (G);
Assign (F, ParamStr (2));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (G, ParamStr (2));

if WhereX <> 1
then WriteLn;

WriteLn ('Прочитано ', Count : 0 : 0, ' байт стего...');
Exit
end;

(*----------------------ЗАПмСЬ СТЕГАНОГРАММЫ В ФАЙЛ--------------------*)
Assign (G, ParamStr (2));

if Pos (':', ParamStr (3)) <> 2
then Assign (H, TempName)
else Assign (H, Copy (ParamStr (3), 1, 2) + TempName);

Reset (G);
Rewrite (H);
L := 0;

while not Eof (F)
do
begin
ReadLn (F, X);
Write (H, X);

if L = 0
then
begin
Read (G, C);
Count := Count + 1;
Write (C);
K := Ord (C) xor Random (256)
end;

if K and 1 = 0
then Write (H, CR + LF)
else Write (H, LF + CR);

K := K shr 1;
L := Succ (L) mod 8
end;

Close (F);
Close (G);
Close (H);
Assign (F, ParamStr (3));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (H, ParamStr (3));

if WhereX <> 1
then WriteLn;

if (Count <> 0) and (L <> 0)
then Count := Count - 1;

WriteLn ('Записано ', Count : 0 : 0, ' байт стего...');
Exit
end.

Листинг 3.

Кодирование стего хвостовыми пробелами.
program StegoBlank;

(***************************************************************)
(* Простая стеганографическая программа, использующая *)
(* хвостовые пробелы для встраивания стего в произвольные *)
(* тексты. мспользует от 1 до 3 параметров, которые и *)
(* определяют выполняемую программой функцию. При этом первый *)
(* из параметров всегда представляет файл-контейнер. *)
(* Если этот параметр единственный, то проверяется *)
(* наличие стеганограммы в файле-контейнере с выводом *)
(* результата на экран. По сути это режим стеганодетектора. *)
(* Если таких параметров два, стеганограмма извлекается из *)
(* контейнера, а результат записывается в заданный вторым *)
(* параметром файл. *)
(* Наконец, если используются все три параметра, то *)
(* стеганографический текст из файла заданного вторым *)
(* параметром, упрятывается в файл-контейнер, заданный первым *)
(* параметром, а результат стеганографического преобразования *)
(* записывается в файл, заданный третьим параметром. *)
(* Совпадение двух имён файлов допускается (трёх - абсурд). *)
(***************************************************************)

type
StringType = string [$FF];

const
TempName = '$$$$$$$$.$$$';
Key1 = $1234;
Key2 = $4567;
Max = 240;

var
F, G, H : text;
Flag : boolean;
X : StringType;
I, K, L : byte;
C : char;
Count : real;

begin
LowVideo;

if not (ParamCount in [1..3])
then
begin
WriteLn ('Ожидается от 1 до 3 параметров:');
WriteLn ('1 - вывод стеганограммы на экран;');
WriteLn ('2 - вывод стеганограммы в файл;');
WriteLn ('3 - запись стеганограммы в файл.');
Exit
end;

Assign (F, ParamStr (1));
Reset (F);
Count := 0;

MemW [Dseg : $01FE] := Key1;
MemW [Dseg : $01FC] := Key2;

(*---------------------ВЫВОД СТЕГАНОГРАММЫ НА ЭКРАН--------------------*)
if ParamCount = 1
then
begin
Flag := False;

while not Eof (F)
do
begin
ReadLn (F, X);
L := Length (X);

if L < Max
then
begin
while X [L] = ' '
do L := Pred (L);

L := Length (X) - L;

if not Flag
then K := L
else Write (Chr (K or L shl 4));

Flag := not Flag
end
end;

Close (F);

if WhereX <> 1
then WriteLn;

WriteLn ('Ok!');
Exit
end;

(*----------------------ВЫВОД СТЕГАНОГРАММЫ В ФАЙЛ---------------------*)
if ParamCount = 2
then
begin
if Pos (':', ParamStr (2)) <> 2
then Assign (G, TempName)
else Assign (G, Copy (ParamStr (2), 1, 2) + TempName);

Assign (G, TempName);

Rewrite (G);
Flag := False;

while not Eof (F)
do
begin
ReadLn (F, X);
L := Length (X);

if L < Max
then
begin
while X [L] = ' '

do L := Pred (L);

L := Length (X) - L;

if not Flag
then K := L
else
begin
C := Chr ((K or L shl 4)
xor Random (256));
Write (G, C);
Write (C);
Count := Count + 1
end;

Flag := not Flag
end
end;

Close (F);
Close (G);
Assign (F, ParamStr (2));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (G, ParamStr (2));

if WhereX <> 1
then WriteLn;

if Flag and (Count <> 0)
then Count := Count - 1;

WriteLn ('Прочитано ', Count : 0 : 0, ' байт стего...');
Exit
end;

(*----------------------ЗАПмСЬ СТЕГАНОГРАММЫ В ФАЙЛ--------------------*)
Assign (G, ParamStr (2));

if Pos (':', ParamStr (3)) <> 2
then Assign (H, TempName)
else Assign (H, Copy (ParamStr (3), 1, 2) + TempName);

Reset (G);
Rewrite (H);
Flag := False;

while not Eof (F)
do
begin
ReadLn (F, X);

while X [Length (X)] = ' '
do X [0] := Pred (X [0]);

Write (H, X);

if Length (X) < Max - 15
then
begin
if Flag
then
for I := 1 to K shr $04
do Write (H, ' ')
else
begin
Read (G, C);
Write (C);
Count := Count + 1;
K := Ord (C) xor Random (256);

for I := 1 to K and $0F
do Write (H, ' ')
end;

Flag := not Flag
end;

WriteLn (H)
end;

Close (F);
Close (G);
Close (H);
Assign (F, ParamStr (3));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (H, ParamStr (3));

if WhereX <> 1
then WriteLn;

if Flag and (Count <> 0)
then Count := Count - 1;

WriteLn ('Записано ', Count : 0 : 0, ' байт стего...');
Exit
end.

Листинг 4.

Кодирование стего знаками совпадающего начертания.
program StegoChange;

(***************************************************************)
(* Простая стеганографическая программа для работы с *)
(* русскими текстами основанная на частичной замене русских *)
(* символов латинскими одинакового с ними начертания. *)
(* мспользует от 1 до 3 параметров, которые и определяют *)
(* выполняемую программой функцию. При этом первый из *)
(* параметров всегда представляет файл-контейнер. *)
(* Если этот параметр единственный, то проверяется *)
(* наличие стеганограммы в файле-контейнере с выводом *)
(* результата на экран. По сути это режим стеганодетектора. *)
(* Если таких параметров два, стеганограмма извлекается из *)
(* контейнера, а результат записывается в заданный вторым *)
(* параметром файл. *)
(* Наконец, если используются все три параметра, то *)
(* стеганографический текст из файла заданного вторым *)
(* параметром, упрятывается в файл-контейнер, заданный первым *)
(* параметром, а результат стеганографического преобразования *)
(* записывается в файл, заданный третьим параметром. *)
(* Совпадение двух имён файлов допускается (трёх - абсурд). *)
(* Литература: О.Шарапов. Программная русификация *)
(* матричных принтеров. //Монитор. - 1993, #3, стр. 48..57. *)
(***************************************************************)

type
StringType = string [$FF];
Index = (Rus, Lat);
SetOfChar = set of char;

const
TempName = '$$$$$$$$.$$$';
Max = 13;
Key1 = $1234;
Key2 = $4567;
Tab : array [Index, 1..22] of char = ('ВЕКМНРСТХаеосАОикпрту'#32,
'BEKMHPCTXaeocAOuknpmy'#00);
var
F, G, H : text;
X : StringType;
I, J : integer;
K, L : byte;
C, CC : char;
LatSet : SetOfChar;
CharSet : SetOfChar;
LargeLat : SetOfChar;
LargeChar : SetOfChar;
Count : real;

begin
LowVideo;

if not (ParamCount in [1..3])
then
begin
WriteLn ('Ожидается от 1 до 3 параметров:');
WriteLn ('1 - вывод стеганограммы на экран;');
WriteLn ('2 - вывод стеганограммы в файл;');
WriteLn ('3 - запись стеганограммы в файл.');
Exit
end;

Assign (F, ParamStr (1));
Reset (F);
Count := 0;

MemW [Dseg : $01FE] := Key1;
MemW [Dseg : $01FC] := Key2;

L := 0;
LatSet := [];
CharSet := [];

for I := 1 to Max
do
begin
LatSet := LatSet + [Tab [Lat, I]];
CharSet := CharSet + [Tab [Rus, I]] + [Tab [Lat, I]]
end;

LargeLat := LatSet;
LargeChar := CharSet;

for I := Succ (Max) to 21
do
begin
LargeLat := LargeLat + [Tab [Lat, I]];
LargeChar := LargeChar + [Tab [Lat, I]] + [Tab [Rus, I]]
end;

(*---------------------ВЫВОД СТЕГАНОГРАММЫ НА ЭКРАН--------------------*)
if ParamCount = 1
then
begin
while not Eof (F)
do
begin
ReadLn (F, X);

for I := 1 to Length (X)
do
begin
J := 1;
C := X [I];

if C in LargeChar
then
begin
K := K shl 1;

if C in LargeLat
then K := K or 1;

L := Succ (L) mod 8;

if L = 0
then Write (Chr (K))
end;
end
end;

Close (F);

if WhereX <> 1
then WriteLn;

WriteLn ('Ok!');
Exit
end;

(*----------------------ВЫВОД СТЕГАНОГРАММЫ В ФАЙЛ---------------------*)
if ParamCount = 2
then
begin
if Pos (':', ParamStr (2)) <> 2
then Assign (G, TempName)
else Assign (G, Copy (ParamStr (2), 1, 2) + TempName);

Assign (G, TempName);
Rewrite (G);

while not Eof (F)
do
begin
ReadLn (F, X);

for I := 1 to Length (X)
do
begin
J := 1;
C := X [I];

if C in CharSet
then
begin
K := K shl 1;

if C in LatSet
then K := K or 1;

L := Succ (L) mod 8;

if L = 0
then
begin
Count := Count + 1;
K := K xor Random (256);
Write (Chr (K));
Write (G, Chr (K))
end
end;
end
end;

Close (F);
Close (G);
Assign (F, ParamStr (2));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (G, ParamStr (2));

if WhereX <> 1
then WriteLn;

WriteLn ('Прочитано ', Count : 0 : 0, ' байт стего...');
Exit
end;

(*----------------------ЗАПмСЬ СТЕГАНОГРАММЫ В ФАЙЛ--------------------*)
Assign (G, ParamStr (2));

if Pos (':', ParamStr (3)) <> 2
then Assign (H, TempName)
else Assign (H, Copy (ParamStr (3), 1, 2) + TempName);

Reset (G);
Rewrite (H);

while not Eof (F)
do
begin
ReadLn (F, X);

for I := 1 to Length (X)
do
begin
J := 1;
C := X [I];

while (J <= Max) and (C <> Tab [Rus, J])
and (C <> Tab [Lat, J])
do J := Succ (J);

if J <= Max
then
begin
if L = 0
then
begin
if Eof (G)
then CC := #00
else Read (G, CC);

Count := Count + 1;
Write (CC);
K := Ord (CC) xor Random (256)
end;

if K and $80 <> 0
then X [I] := Tab [Lat, J]
else X [I] := Tab [Rus, J];

K := K shl 1;
L := Succ (L) mod 8
end
end;

WriteLn (H, X)
end;

Close (F);
Close (G);
Close (H);
Assign (F, ParamStr (3));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (H, ParamStr (3));

if WhereX <> 1
then WriteLn;

if (Count <> 0) and (L <> 0)
then Count := Count - 1;

WriteLn ('Записано ', Count : 0 : 0, ' байт стего...');
Exit
end.

Листинг 5.

Кодирование стего двоичными нулями.
program StegoZero;

(***************************************************************)
(* Простая стеганографическая программа для работы с *)
(* текстами основанная на подмене первого пробела в группе из *)
(* двух или более пробелов двоичным нулём. Режим работы *)
(* программы определяется параметрами вызова. Возможны от 1 до *)
(* 3 параметров, из которых первый описывает файл-контейнер. *)
(* Если этот параметр единственный, то проверяется *)
(* наличие стеганограммы в контейнере с выводом результата на *)
(* экран. По сути это режим стеганодетектора. *)
(* Если таких параметров два, стеганограмма извлекается из *)
(* контейнера, а результат записывается в заданный вторым *)
(* параметром файл. *)
(* Наконец, если используются все три параметра, то *)
(* стеганографический текст из файла заданного вторым *)
(* параметром, упрятывается в файл-контейнер, заданный первым *)
(* параметром, а результат стеганографического преобразования *)
(* записывается в файл, заданный третьим параметром. *)
(* Совпадение двух имён файлов допускается (трёх - абсурд). *)
(***************************************************************)

type
StringType = string [$FF];

const
TempName = '$$$$$$$$.$$$';
Key1 = $1234;
Key2 = $4567;

var
F, G, H : text;
Line : StringType;
I, K, L, Z : byte;
C : char;
Count : real;

begin
LowVideo;

if not (ParamCount in [1..3])
then
begin
WriteLn ('Ожидается от 1 до 3 параметров:');
WriteLn ('1 - вывод стеганограммы на экран;');
WriteLn ('2 - вывод стеганограммы в файл;');
WriteLn ('3 - запись стеганограммы в файл.');
Exit
end;

Assign (F, ParamStr (1));
Reset (F);
Count := 0;

MemW [Dseg : $01FE] := Key1;
MemW [Dseg : $01FC] := Key2;

(*---------------------ВЫВОД СТЕГАНОГРАММЫ НА ЭКРАН--------------------*)
if ParamCount = 1
then
begin
L := 0;
Z := 0;

while not Eof (F)
do
begin
ReadLn (F, Line);

while (Line <> '')
and (Line [1] <= ' ')
do Delete (Line, 1, 1);

while (Line <> '')
and (Line [Length (Line)] <= ' ')
do Delete (Line, Length (Line), 1);

while Line <> ''
do
begin
while (Line <> '') and (Line [1] > ' ')
do Delete (Line, 1, 1);

if (Length (Line) > 1)
and ((Copy (Line, 1, 2) = #00' ')
or (Copy (Line, 1, 2) = ' '))
then
begin
Z := Z shr 1;

if Line [1] = #00
then Z := Z or $80;

L := Succ (L) mod 8;

if L = 0
then Write (Chr (Z))
end;

while (Line <> '') and (Line [1] <= ' ')
do Delete (Line, 1, 1)
end
end;

Close (F);

if WhereX <> 1
then WriteLn;

WriteLn ('Ok!');
Exit
end;

(*----------------------ВЫВОД СТЕГАНОГРАММЫ В ФАЙЛ---------------------*)
if ParamCount = 2
then
begin
if Pos (':', ParamStr (2)) <> 2
then Assign (G, TempName)
else Assign (G, Copy (ParamStr (2), 1, 2) + TempName);

Rewrite (G);

L := 0;
Z := 0;

while not Eof (F)
do
begin
ReadLn (F, Line);

while (Line <> '')
and (Line [1] <= ' ')
do Delete (Line, 1, 1);

while (Line <> '')
and (Line [Length (Line)] <= ' ')
do Delete (Line, Length (Line), 1);

while Line <> ''
do
begin
while (Line <> '') and (Line [1] > ' ')
do Delete (Line, 1, 1);

if (Length (Line) > 1)
and ((Copy (Line, 1, 2) = #00' ')
or (Copy (Line, 1, 2) = ' '))
then
begin
Z := Z shr 1;

if Line [1] = #00
then Z := Z or $80;

L := Succ (L) mod 8;

if L = 0
then
begin
Count := Count + 1;
C := Chr (Z
xor Random (256));
Write (G, C);
Write (C)
end
end;

while (Line <> '') and (Line [1] <= ' ')
do Delete (Line, 1, 1)
end
end;

Close (F);
Close (G);
Assign (F, ParamStr (2));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (G, ParamStr (2));

if WhereX <> 1
then WriteLn;

WriteLn ('Прочитано ', Count : 0 : 0, ' байт стего...');
Exit
end;

(*----------------------ЗАПмСЬ СТЕГАНОГРАММЫ В ФАЙЛ--------------------*)
Assign (G, ParamStr (2));

if Pos (':', ParamStr (3)) <> 2
then Assign (H, TempName)
else Assign (H, Copy (ParamStr (3), 1, 2) + TempName);

Reset (G);
Rewrite (H);
L := 0;

while not Eof (F)
do
begin
ReadLn (F, Line);
I := 0; (* последний пробельный символ *)

while (I < Length (Line)) and (Line [Succ (I)] <= ' ')
do I := Succ (I);

K := Length (Line); (* последний непробельный символ *)

while (K > I) and (Line [K] <= ' ')
do K := Pred (K);

while I < K
do
begin
while (I < K) and (Line [Succ (I)] > ' ')
do I := Succ (I);

if (K - I > 2) and (Copy (Line, Succ (I), 2) = ' ')
then
begin
if L = 0
then
begin
if Eof (G)
then C := #00
else Read (G, C);

Z := Ord (C) xor Random (256)
end;

if Z and 1 = 1
then Line [Succ (I)] := #00;

Z := Z shr 1;
L := Succ (L) mod 8;
I := I + 2;

if L = 0
then
begin
Write (C);
Count := Count + 1
end
end;

while (I < K) and (Line [Succ (I)] = ' ')
do I := Succ (I)
end;

WriteLn (H, Line)
end;

Close (F);
Close (G);
Close (H);

Assign (F, ParamStr (3));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (H, ParamStr (3));

if WhereX <> 1
then WriteLn;

WriteLn ('Записано ', Count : 0 : 0, ' байт стего...');
Exit
end.

Метод изменения порядка следования маркеров конца строки CR/LF использует индифферентность подавляющего числа средств отображения текстовой информации к порядку следования символов перевода строки (CR) и возврата каретки (LF), ограничивающих строку текста. Традиционный порядок следования CR/LF соответствует 0, а инвертированный LF/CR означает 1.

Метод хвостовых пробелов предполагает дописывание в конце коротких строк (менее 225 символов; значение 225 выбрано достаточно произвольно) от 0 до 15 пробелов, кодирующих значение полубайта.

Метод знаков одинакового начертания предполагает подмену (бит 1) или отказ от такой подмены (бит 0) русского символа латинским того же начертания. Идея этого метода заимствована из статьи [3].

Метод двоичных нулей является разновидностью метода знаков одинакового начертания и предполагает либо замену первого в группе из двух или более внутренних пробелов двоичным нулем (бит 1), либо отказ от нее (бит 0).

Интерфейс программ листингов 2—5 одинаков. Всего возможны три режима их работы, определяемые числом параметров вызова, первый из которых всегда представляет файл стеганоконтейнера. Если этот параметр единственный, то каждая из программ реализует функции стеганодетектора: производится сканирование контейнера с выводом результата на экран. Если таких параметров два, то стеганограмма извлекается из контейнера и расшифровывается. Результат выводится в файл, указанный вторым параметром, а также на экран. При трех параметрах стего выбирается из файла, указанного вторым параметром, шифруется исключающим «ИЛИ» и помещается в файл контейнера, заданного первым. Модифицированной стеганограммой контейнер записывается в файл, заданный третьим параметром.

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

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

Листинг 6.

Склейка символов таблиц.
program FixTab;

(******************************************)
(* Программа фиксирует внутренние пробелы *)
(* в строке, заменяя их двоичными нулями *)
(******************************************)

type
StringType = string [$FF];

const
TempName = '$$$$$$$$.$$$';

var
F, G : text;
I : byte;
Line, Head, Tail, Body : StringType;

begin
LowVideo;

if ParamCount <> 2
then
begin
WriteLn ('Укажите в параметре ввода имена входного и');
WriteLn ('выходного файлов, которые могут совпадать.');
Exit
end;

Assign (F, ParamStr (1));

if Pos (':', ParamStr (2)) <> 2
then Assign (G, TempName)
else Assign (G, Copy (ParamStr (2), 1, 2) + TempName);

Reset (F);
Rewrite (G);

while not Eof (F)
do
begin
ReadLn (F, Line);
I := 0;

while (I < Length (Line)) and (Line [Succ (I)] <= ' ')
do I := Succ (I);

Head := Copy (Line, 1, I);
I := Length (Line);

while (I > 0) and (Line [I] <= ' ')
do I := Pred (I);

Tail := Copy (Line, Succ (I), Length (Line) - I);
Body := Copy (Line, Succ (Length (Head)), I - Length (Head));

for I := 1 to Length (Body)
do
if Body [I] = ' '
then Body [I] := #00;

Line := Head + Body + Tail;
WriteLn (G, Line);
WriteLn (Line)
end;

Close (F);
Close (G);

Assign (F, ParamStr (2));
(*$I-*)
Erase (F);
(*$I+*)
I := IoResult;
Rename (G, ParamStr (2));

if WhereX <> 1
then WriteLn;

WriteLn ('Ok!');
Exit
end.

Для наглядности все программы написаны на входном языке крайне компактного компилятора Turbo Pascal 3.x компании Borland, почти идеально (если бы не отсутствие встроенного ассемблера) подходящем для исследовательской работы.

Анализ реализации методов

Эффективность описанных методов упаковки стего в контейнере была исследована на переведенном в ASCII-вид тексте главы VI тома I книги «Мертвая вода» [1] объемом 126 729 байт и насчитывающим 2143 строки со строками, выровненными на 65-символьную границу при абзацном отступе в четыре символа. Полученная плотность упаковки (в порядке возрастания) представлена в следующей таблице:

Сравнение методов текстовой стеганографии

Метод Знаков стего Плотность, %
Чередование маркеров конца 267 0,21
Выравнивание пробелами 411 0,32
Двоичные нули 740 0,58
Хвостовые пробелы 1071 0,85
Знаки одинакового начертания 4065 3,21

Обращает на себя внимание необычно высокая эффективность упаковки стего с использованием подмены символов.

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

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

В завершение стоит упомянуть об одном неожиданном наблюдении, свидетельствующем о том, что текстовых файлов, пригодных для использования в качестве стеганоконтейнера, намного больше, чем это может показаться с первого взгляда. Действительно, таковыми являются и файлы баз данных, символьные поля записей которых фактически представляют собой строки фиксированной длины (естественно, без завершающих символов CR/LF). Надеюсь, читатель по достоинству оценит это наблюдение...

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

От редактора. В развитие темы стоит обратить внимание на работу Карташова Д. В., Чижухина Г. Н. Текстовая стеганография // Труды научно-технической конференции «Безопасность информационных технологий». Пенза, 2003. Примеры изощренных методов текстовой стеганографии приведены, в частности, в работе Xu J., Sung A., Shi P., Liu Q. Text Steganography Using Wavelet Transform // New Mexico Tech Socorro, USA, 2002, выполнявшейся для Министерства обороны США, в котором изложено использование волнового (wavelet) преобразования, а также в публикации Кричевского А. М. Нейронные сети в задачах компьютерной стеганографии // Школа-семинар БИКАМП’03. С.-Петербург, 2003 (http://bicamp.aanet.ru/2003/ papers/sectionIT/ KrichevskyAM.pdf)

Р. Б.

Литература
  1. Мертвая вода // http://www.dotu.ru, http://www.kpe.ru.
  2. Беляев А. Стеганограмма: скрытие информации // Программист, 2002, №1 (электронная версия).
  3. Шарапов О. Программная русификация матричных принтеров // Монитор, 1993, № 3, с. 48—57.

Автор: Василий Текин

Источник: osp.ru


30-05-2012
Positive Hack Days 2012
Москва, Дмитровское ш., д. 27 к. 1, Клуб "Молодая гвардия"
30-05-2012
VIII-й Специализированный форум «Современные системы безопасности — Антитеррор»
Россия, Красноярск, ул. Авиаторов, 19, МВДЦ «Сибирь».
31-05-2012
Информационная безопасность: новые потребности рынка
07-06-2012
IT & Security Forum 2012 Kazan
Казань, отель Корстон, ул. Николая Ершова 1А
07-06-2012
8-й Евразийский форум информационной безопасности и электронного взаимодействия «ИНФОФОРУМ-Евразия»
Москва, здание Правительства Москвы (ул. Новый Арбат, 36)

< Май 2012 >
Пн Вт Ср Чт Пт Сб Вс
12 3456
78910111213
1415 16 17 18 19 20
21222324252627
282930 31
Рассылка

Пресс-релизы компаний
Новости портала
Антивирусный вестник



©2003 - 2012 GlobalTrust
Разработка сайта: Maximaster
Рейтинг@Mail.ru Rambler's Top100 Yandex