Živě.cz o počítačích a internetu

Tipy a triky v Delphi, díl 46. - Ukrytí textové zprávy do obrázku

Jan Šindelář - 26.6.2002

Ukrytí textové zprávy do obrázku

Možná znáte některé programy, které vám umožní schovat do běžného obrázku textová data tak, aby byl takto získaný obrázek k nerozeznání od originálu. K čemu je to dobré? Je to jeden ze zajímavých způsobů, jak bezpečným způsobem přenášet citlivá data například prostřednictvím e-mailu. Jistě namítnete, že mnohem lepší je data šifrovat. Ano, je to jeden z účinných způsobů, ale zašifrovaná data mohou být na první pohled podezřelá a mohou lákat ke zkoušení, jak šifru prolomit. A i když se prolomení nepodaří (tak by tomu tedy alespoň v drtivé většině případů mělo být), přesto samo zjištění jiné osoby, že posíláte nějakou šifrovanou poštu, může být pro vás dostatečně kompromitující. Když ovšem pošlete e-mailem naprosto neškodný obrázek, žádné podezření to nevzbudí a vaše paranoidní duše agenta může být klidná.

Ale teď vážně. Ukážeme si jeden jednoduchý způsob, jak tohoto efektu zakomponování textu do obrázku dosáhnout v Delphi. Na originální obrázek je namaskován zadaný text v jeho binární podobě. Při zpětném procesu (dešifrování) jsou porovnány jednotlivé pixely původního a zašifrovaného obrázku a rozdíly jsou opět zpětně "demaskovány", čímž se získají jednotlivé znaky uloženého textu.

V tom je právě menší nevýhoda tohoto jinak velmi jednoduchého postupu – totiž nutnost mít pro dešifrování také původní originální obrázek. V praxi to pak znamená, že pochopitelně nebudete pokaždé posílat oba obrázky (tedy původní originál a obrázek s ukrytým textem), ale originální obrázek si s člověkem, se kterým chcete touto formou komunikovat, vyměníte pouze jednou. Poté už pouze posíláte obrázky s ukrytými texty. Abychom byli opravdu precizní a učinili zadost všem agentským pravidlům, můžete ještě nenápadnost vašeho počínání zvýšit tím, že budete používat obrázků více (v extrémním případě až na každý den v roce jiný obrázek). Jinak by bylo totiž značně podezřelé, kdybyste posílali stejnému člověku stále dokola tentýž obrázek.

Pojďme však již k samotné implementaci v Delphi. Příklad jsou vlastně pouze dvě procedury – události stisku dvou tlačítek. První tlačítko zakóduje do obrázku zprávu, druhé tlačítko ji dekóduje. Kromě těchto dvou tlačítek ještě umístěte na formulář 3 komponenty TImage. První bude obsahovat originální obrázek, takže jej prostřednictvím Object Inspectoru můžete rovnou do komponenty načíst. Obrázek musí být ve formátu BMP. Zbylé dvě komponenty TImage slouží k zobrazení výsledného obrázku na tzv. delta snímku, ve kterém jsou žlutou barvou zvýrazněny klíčové změněné pixely. Tento obrázek slouží pouze pro přehled a nemá žádnou praktickou funkci. Poslední věcí, kterou je třeba na formulář přidat, je komponenta, jež bude obsahovat textovou zprávu, kterou chceme do obrázku ukrýt. V našem případě bude tuto funkci plnit komponenta TEdit.

Jak pojmenovat zmíněné komponenty, zjistíte snadno ze zdrojového kódu. První procedura slouží k "zakódování", druhá ke zpětnému získání textu z obrázku:

procedure TForm1.Button1Click(Sender: TObject);
var
  x, y, i, j: Integer;
  PixelData: TColor;
  CharMask, CharData: Byte;
begin
  imgTarget.Picture.Assign(imgOrig.Picture);
  imgDelta.Picture.Assign(imgOrig.Picture);
  imgTarget.Picture.Bitmap.PixelFormat := pf32bit;
  imgDelta.Picture.Bitmap.PixelFormat := pf32bit;
  x := 0;
  y := 0;
  with imgTarget.Picture.Bitmap do
    for i := 1 to Length(sourceMessage.Text) do
    begin
      CharMask := $80;
      for j := 1 to 8 do
      begin
        CharData := Byte(sourceMessage.Text[i]) and CharMask;
        if (CharData <> 0) then
        begin
          PixelData := Canvas.Pixels[x, y] xor $1;
          Canvas.Pixels[x, y] := PixelData;
        end;
        x := (x + 1) mod imgTarget.Picture.Bitmap.Width;
        if (x = 0) then
        begin
          Inc(y);
        end;
        CharMask := CharMask shr 1;
      end;
    end;
  for y := 0 to imgOrig.Picture.Bitmap.Height -1 do
    for x := 0 to imgOrig.Picture.Bitmap.Width -1 do
      if (imgOrig.Picture.Bitmap.Canvas.Pixels[x, y] <> imgTarget.Picture.Bitmap.Canvas.Pixels[x, y]) then
        imgDelta.Picture.Bitmap.Canvas.Pixels[x, y] := clYellow;
end;

procedure TForm1.Button2Click(Sender: TObject);
Var
  x, y: integer;
  mask, ch: byte;
begin
  sourceMessage.Clear;
  mask := $80;
  ch := 0;
  for y := 0 to imgOrig.Picture.Bitmap.Height -1 do
  begin
    for x := 0 to imgOrig.Picture.Bitmap.Width -1 do
    begin
      if (imgOrig.Picture.Bitmap.Canvas.Pixels[x, y] <>
      imgTarget.Picture.Bitmap.Canvas.Pixels[x, y]) then ch := ch or mask;
      mask := mask shr 1;
      if mask = 0 Then
      begin
        Edit1.Text := Edit1.Text + char(ch);
        mask := $80;
        ch := 0;
      end;
    end;
  end;
end;

Jak vidíte, výsledný obrázek není uložen na disk. Toho však snadno docílíte přidáním tohoto řádku na konec první procedury:

imgTarget.Picture.SaveToFile(`c:\vyslednyobrazek.bmp`);

Celý příklad berte spíše jako ukázkový. Je koncipován tak, abyste přímo viděli, jak daná věc funguje, a mohli experimentovat. V reálné aplikaci bude pochopitelně nutné přidat dialog pro výběr a následné uložení obrázku a také načítání vkládaného textu ze souboru.