Steganografie mit ASM und AutoIt

  • Hallo liebe AutoIt-Community! :)

    Wie die Überschrift schon verrät, bin ich dabei, ein Steganografie-Script nach diesem Vorbild zu schreiben. Mit AutoIt allein hat das ganze für mich auch kein Problem dargestellt. Nun wollte ich jedoch den Hauptteil, nämlich das Verstecken in der Bilddatei, mit Assembler (mit AssembleIt) umsetzen.
    Darin bin ich jedoch noch nicht gerade so erfahren wie in AutoIt, weshalb ich jetzt auch auf einige Problemchen stoße... ^^

    Zu meinem Skript:

    • Es wird per GDIPlus eine Bitmap-Datei geladen und der Pointer auf die Pixeldaten an den ASM-Code übergeben.
    • Der Geheimtext wird in eine DLL-Struct gepackt (ich hoffe, da mache ich soweit alles richtig - ganz sicher bin ich mir nicht...) und der Pointer dazu auch übergeben.
    • Der ASM-Code liest nun für jedes Pixel die ursprüngliche Pixelfarbe aus sowie ein Byte des Geheimtextes.
    • Die Pixelfarbe wird in (A)RGB getrennt und die untersten Bits der Farben durch Bits des Geheimtextes ersetzt (bei Rot und Grün 3 Bit, bei Blau 2 Bit -> insgesamt 8 Bit (1 Byte) pro Pixel).
    • Der neue Farbwert wird zusammengesetzt und am Ende in einer neuen Bitmap-Datei gespeichert.
    Skript
    [autoit]

    #include <assembleit.au3>
    #include <GDIPlus.au3>

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Variablen
    Local $Locked, $Scan0, $hImage
    Local $iWidth, $iHeight

    [/autoit] [autoit][/autoit] [autoit]

    ; GDIPlus lädt das Bild
    _GDIPlus_Startup()
    $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\test.bmp")
    $iWidth = _GDIPlus_ImageGetWidth($hImage)
    $iHeight = _GDIPlus_ImageGetHeight($hImage)

    [/autoit] [autoit][/autoit] [autoit]

    ; An die Pixeldaten gelangen
    $Locked = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iWidth, $iHeight, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
    $Scan0 = DllStructGetData($Locked, "Scan0")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $sString = "aaaa" ; String, der versteckt werden soll
    $String = DllStructCreate ("byte[" & ($iWidth * $iHeight) & "]") ; DLL-Struct für den String erstellen (für jeden Pixel ein Byte)
    $bBinary = StringToBinary ($sString) ; String in binär umwandeln
    DllStructSetData ($String, 1, $bBinary) ; DLL-Struct füllen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; ASM-Code auführen
    $_ASSEMBLEIT_FLAG = 1
    $return = _assembleit("int", "Steganografie", "ptr", $Scan0, "int", $iWidth, "int", $iHeight, "ptr", DllStructGetPtr ($String))

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    MsgBox (0, "", "Fertig. Übersprungene Pixel: " & $return)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Neues Bild speichern
    _GDIPlus_BitmapUnlockBits($hImage, $Locked)
    _GDIPlus_ImageSaveToFile ($hImage, @ScriptDir & "\output.bmp")
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func Steganografie ()

    [/autoit] [autoit][/autoit] [autoit]

    #cs

    [/autoit] [autoit][/autoit] [autoit]

    Kommentar zur Funktionsweise:

    [/autoit] [autoit][/autoit] [autoit]

    - es werden die letzten drei Bits vom Rot- und Grün-Wert eines jeden Pixels, sowie zwei Bit vom Blau-Wert durch
    insgesamt 8 Bit (ein Byte) des Geheimtextes ersetzt (bei Blau nur 2 Bit, damit man auf 1 Byte kommt)

    [/autoit] [autoit][/autoit] [autoit]

    #ce

    [/autoit] [autoit][/autoit] [autoit]

    _("use32")
    _("mov edi, [esp+4]") ; Pointer-Position für die Pixeldaten in edi speichern

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax, [esp+8]") ; Breite der Bitmap in eax
    _("mul dword [esp+12]") ; eax *= $Height -> eax = $Width * $Height (Pixelanzahl)
    _("mov ecx, eax") ; eax (Anzahl der Pixel) als Counter nach ecx

    [/autoit] [autoit][/autoit] [autoit]

    _("mov esi, [esp+16]") ; Pointer-Position auf den zu versteckenden Binärstring

    [/autoit] [autoit][/autoit] [autoit]

    _("Pixel:") ; Beginn der Schleife für jeden Pixel
    ; ################################################## Steganografie:
    ;
    _("mov edx, [esi]") ; edx sind die nächsten 4 Byte (32 Bit) des Binärstrings (aber nur das erste wird verwendet)

    [/autoit] [autoit][/autoit] [autoit]

    _("cmp edx, 0") ; edx ist manchmal 0 und das Programm stürzt dann ab (Wieso?)
    _("je Ende") ; -> in diesem Fall gleich beenden (und übersprungene Pixel ausgeben)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("mov eax, [edi]") ; Farbe des Pixels (= Wert an edi-Position) in eax Form: #AARRGGBB
    ;
    ; Rot-Wert-Berechnung:
    ;
    _("shl eax, 8") ; eax mit 2^8 = 256 multiplizieren
    ; -> der 32bit-Farbcode #AARRGGBB wird zu #RRGGBB00
    _("shr eax, 27") ; eax um 6 Hex-Stellen (24bit) + 3bit nach rechts shiften (die drei Bit werden durch drei Bits vom Geheimtext ersetzt)

    [/autoit] [autoit][/autoit] [autoit]

    _("shl eax, 3") ; und die 3bit wieder zurückshiften (sind jetzt dadurch alle null)
    ; -> eax ist nur noch #RR und die letzten drei Bit sind 0

    [/autoit] [autoit][/autoit] [autoit]

    _("shr edx, 29") ; von edx werden nur die ersten 3 Bit verwendet (für den Rot-Wert)

    [/autoit] [autoit][/autoit] [autoit]

    _("add eax, edx") ; eax (Farbwert Rot) und edx (3 bit String) verknüpfen

    [/autoit] [autoit][/autoit] [autoit]

    _("push eax") ; neuen Rot-Wert auf den Stack pushen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("mov edx, [esi]") ; edx sind noch einmal die 4 Byte (32 Bit) des Binärstrings wie bei Rot (aber nur das erste wird verwendet)

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax, dword [edi]"); Farbe des Pixels (= Wert an edi-Position) in eax Form: #AARRGGBB
    ;
    ; Grün-Wert-Berechnung:
    ;
    _("shl eax, 16") ; eax mit 2^16 = 256^2 multiplizieren
    ; -> der 32bit-Farbcode #AARRGGBB wird zu #GGBB0000
    _("shr eax, 27") ; eax um 6 Hex-Stellen (24bit) + 3bit nach rechts shiften (die drei Bit werden durch drei Bits vom Geheimtext ersetzt)
    _("shl eax, 3") ; und die 3bit wieder zurückshiften (sind jetzt null)
    ; -> eax ist nur noch #GG und die letzten drei Bit sind 0

    [/autoit] [autoit][/autoit] [autoit]

    _("shl edx, 3") ; von edx werden nur die Bits 4 - 6 verwendet (für den Grün-Wert)
    _("shr edx, 29") ; -> erst die ersten 3 Bit eliminieren und dann zurückshiften (->7-32 weg)

    [/autoit] [autoit][/autoit] [autoit]

    _("add eax, edx") ; eax (Farbwert Grün) und edx (3 bit String) verknüpfen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("push eax") ; Grün-Wert auf den Stack pushen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("mov edx, [esi]") ; edx sind noch einmal die 4 Byte (32 Bit) des Binärstrings wie bei Rot und Grün (aber nur das erste wird verwendet)

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax, dword [edi]"); Farbe des Pixels (= Wert an edi-Position) in eax Form: #AARRGGBB
    ;
    ; Blau-Wert-Berechnung:
    ;
    _("shl eax, 24") ; eax mit 2^24 = 256^3 multiplizieren
    ; -> der 32bit-Farbcode #AARRGGBB wird zu #BB000000
    _("shr eax, 26") ; eax um 6 Hex-Stellen (24bit) + 2bit nach rechts shiften (die zwei Bit werden durch drei Bits vom Geheimtext ersetzt)
    _("shl eax, 2") ; und die 2bit wieder zurückshiften
    ; -> eax ist nur noch #BB und die letzten zwei Bit sind 0

    [/autoit] [autoit][/autoit] [autoit]

    _("shl edx, 6") ; von edx werden nur die Bits 7 - 8 verwendet (für den Blau-Wert)
    _("shr edx, 30") ; -> erst die ersten 6 Bit eliminieren und dann zurückshiften (->9-32 weg)

    [/autoit] [autoit][/autoit] [autoit]

    _("add eax, edx") ; eax (Farbwert Blau) und edx (2 bit String) verknüpfen

    [/autoit] [autoit][/autoit] [autoit]

    ; ############## Farben zusammensetzen

    [/autoit] [autoit][/autoit] [autoit]

    _("mov edx, 0xFF000000"); edx auf RGB Farbwert 0 setzen, Alphawert auf FF (standard)
    _("add edx, eax") ; eax enthält noch den Blau-Wert -> dazu addieren
    _("pop eax") ; eax enthält nun den Grün-Wert
    _("shl eax, 8") ; eax um zwei Hex-Stellen nach links shiften -> #GG00 (Grün-Wert an richtige Stelle)
    _("add edx, eax") ; und Grün-Wert zu edx addieren
    _("pop eax") ; eax enthält den Rot-Wert
    _("shl eax, 16") ; eax um 4 Hex-Stellen nach links shiften -> #RR0000 (Rot-Wert an richtige Stelle)
    _("add edx, eax") ; und zu edx addieren

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("mov [edi], edx") ; Farbwert in der Bitmap speichern

    [/autoit] [autoit][/autoit] [autoit]

    ; ##################### Ende der Steganografie
    _("add esi, 1") ; ebx um 1 Byte erhöhen (es wird pro Pixel 1 Byte Text versteckt)
    _("add edi, 4") ; edi um 4 Bytes (1 Pixel) erhöhen
    _("dec ecx") ; ecx -= 1 -> Schleifendurchläufe rückwärts zählen
    _("cmp ecx, 0") ; Sind alle Pixel durchlaufen?
    _("jne Pixel") ; wenn nicht, dann Schleife wiederholen
    _("Ende:")
    _("mov eax, ecx")
    _("ret")

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc

    [/autoit]

    Was nicht so funktioniert: :S

    • Ich habe als Bitmap eine 2x2 Pixel reinweiße Bitmap genommen (Farbe: #FFFFFF; Datei siehe Anhang) und als Geheimtext "aaaa". Da in jedem Pixel genau ein Buchstabe versteckt wird, sollten alle Pixel am Ende wieder gleich sein (4 mal ein weißer Pixel mit "a" drin). Dies ist aber nicht der Fall, das erste Pixel ist andersfarbig.
    • Wenn die Länge des Geheimtextes kleiner ist als die Pixelanzahl, stürzt das Programm ab. Ich frage mich, wieso das Programm nicht richtig weiter macht, wenn es als Byte des Geheimtextes 0 ausliest. Denn das ist ja schließlich auch nur eine Zahl, oder nicht?


    Ich bedanke mich schonmal sehr für eure Hilfe, ich habe nun schon viele Stunden an diesem Skript verbracht und komme der Lösung leider nicht näher...

    LG Xenon :)

    PS: Ich habe es mal in diesem Forum gepostet, weil es hauptsächlich um ASM und weniger um AutoIt geht. Ich hoffe das ist okay so, ansonsten bitte verschieben. ;)

  • Hi,
    schöne Idee, die Steganographie in Assembler umzusetzen!

    Nach etwas debugging einige Tips:

    - Wenn du mit Bytes bspw aus einem String arbeitest, kannst du diese per "movsx edx, byte[adresse]" "movzx edx, byte[adresse]" (löscht die oberen 3 bytes = zero extension ! ) direkt ansprechen. Dann musst du auch nicht darauf achten (wie in deinem String) über das Ende der Stringstruct hinaus den Speicher auszulesen, das kann in die Hose gehen, wenn dieser Speicherbereich reserviert ist!

    - Maskiere die Bits bspw. des Buchstabens direkt in ein DWORD. Das heisst, schreibe die Bits von bspw
    "A" = chr(65) = 10000001 so in ein
    DWORD BGRA 00000001|00000000|00000100|11111111,
    dass du per AND einfach das ürsprüngliche Pixel mit dem DWORD verknüpfen kannst!
    Damit sparst du dir die PUSH/POPs.

    - Du bearbeitest, wenn du den String in ein DWORD einliest, das LETZTE Byte! Das heisst, bei dem zweiten, dritten und vierten Byte liest du immer nur NULL ein!
    Somit wird in deinem Beispiel nur ein Byte (das erste) "abgespeichert"!
    Anpassen könnte man das, indem man einfach 3 NullBytes (oder irgendetwas anderes) vor den String schreibt^^

    [autoit]

    $sString = chr(0)&chr(0)&chr(0)&chr(0)&chr(0)&chr(0)&chr(255);"ABCD" ; String, der versteckt werden soll
    $String = DllStructCreate ("char[" & ($iWidth * $iHeight) +4 & "]") ; DLL-Struct für den String erstellen (für jeden Pixel ein Byte)
    ;~ $bBinary = StringToBinary ($sString) ; String in binär umwandeln
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $bBinary = ' & $bBinary & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    DllStructSetData ($String, 1, $sString) ; DLL-Struct füllen

    [/autoit]

    macht dein Script lauffähig!

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    3 Mal editiert, zuletzt von Andy (18. April 2014 um 13:08)

  • Hallo Andy,

    erst einmal vielen Dank für deine Hilfe! :thumbup:

    Ich habe mein Problem jetzt lösen können, mein Programm funktioniert! Getestet habe ich es, indem ich die Entschlüsselung eben auch noch "gebastelt" habe. (Ich baue noch eine schöne GUI und dann veröffentliche ich das ganze im Skripte-Forum...)

    Spoiler anzeigen
    [autoit]

    #include <assembleit.au3>
    #include <GDIPlus.au3>

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Variablen
    Local $Locked, $Scan0, $hImage
    Local $iWidth, $iHeight

    [/autoit] [autoit][/autoit] [autoit]

    ; GDIPlus lädt das Bild
    _GDIPlus_Startup()
    $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\test.bmp")
    $iWidth = _GDIPlus_ImageGetWidth($hImage)
    $iHeight = _GDIPlus_ImageGetHeight($hImage)

    [/autoit] [autoit][/autoit] [autoit]

    ; An die Pixeldaten gelangen
    $Locked = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iWidth, $iHeight, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
    $Scan0 = DllStructGetData($Locked, "Scan0")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $sString = "test" ; String, der versteckt werden soll
    $String = DllStructCreate ("byte[" & ($iWidth * $iHeight) & "]") ; DLL-Struct für den String erstellen (für jeden Pixel ein Byte)
    $bBinary = StringToBinary ($sString) ; String in binär umwandeln
    DllStructSetData ($String, 1, $bBinary) ; DLL-Struct füllen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; ASM-Code auführen
    $_ASSEMBLEIT_FLAG = 1
    $return = _assembleit("int", "HideTextInPic", "ptr", $Scan0, "int", $iWidth, "int", $iHeight, "ptr", DllStructGetPtr ($String))

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Neues Bild speichern
    _GDIPlus_BitmapUnlockBits($hImage, $Locked)
    _GDIPlus_ImageSaveToFile ($hImage, @ScriptDir & "\output.bmp")
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func HideTextInPic ()

    [/autoit] [autoit][/autoit] [autoit]

    #cs

    [/autoit] [autoit][/autoit] [autoit]

    Kommentar zur Funktionsweise:

    [/autoit] [autoit][/autoit] [autoit]

    - es werden die letzten drei Bits vom Rot- und Grün-Wert eines jeden Pixels, sowie zwei Bit vom Blau-Wert durch
    insgesamt 8 Bit (ein Byte) des Geheimtextes ersetzt (bei Blau nur 2 Bit, damit man auf 1 Byte kommt)

    [/autoit] [autoit][/autoit] [autoit]

    #ce

    [/autoit] [autoit][/autoit] [autoit]

    _("use32")
    _("mov edi, [esp+4]") ; Pointer-Position für die Pixeldaten in edi speichern

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax, [esp+8]") ; Breite der Bitmap in eax
    _("mul dword [esp+12]") ; eax *= $Height -> eax = $Width * $Height (Pixelanzahl)
    _("mov ecx, eax") ; eax (Anzahl der Pixel) als Counter nach ecx

    [/autoit] [autoit][/autoit] [autoit]

    _("mov esi, [esp+16]") ; Pointer-Position auf den zu versteckenden Binärstring

    [/autoit] [autoit][/autoit] [autoit]

    _("Pixel:") ; Beginn der Schleife für jeden Pixel
    ; ################################################## Steganografie:
    ;
    _("movzx edx, byte[esi]"); edx ist das nächste Byte des Binärstrings

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax, [edi]") ; Farbe des Pixels (= Wert an edi-Position) in eax Form: #AARRGGBB
    ;
    ; Rot-Wert-Berechnung:
    ;
    _("shl eax, 8") ; eax mit 2^8 = 256 multiplizieren
    ; -> der 32bit-Farbcode #AARRGGBB wird zu #RRGGBB00
    _("shr eax, 27") ; eax um 6 Hex-Stellen (24bit) + 3bit nach rechts shiften (die drei Bit werden durch drei Bits vom Geheimtext ersetzt)

    [/autoit] [autoit][/autoit] [autoit]

    _("shl eax, 3") ; und die 3bit wieder zurückshiften (sind jetzt dadurch alle null)
    ; -> eax ist nur noch #RR und die letzten drei Bit sind 0

    [/autoit] [autoit][/autoit] [autoit]

    _("shr edx, 5") ; von edx werden nur die ersten 3 Bit verwendet (für den Rot-Wert)

    [/autoit] [autoit][/autoit] [autoit]

    _("add eax, edx") ; eax (Farbwert Rot) und edx (3 bit String) verknüpfen

    [/autoit] [autoit][/autoit] [autoit]

    _("push eax") ; neuen Rot-Wert auf den Stack pushen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("movzx edx, byte[esi]"); edx ist das nächste Byte des Binärstrings

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax, dword [edi]"); Farbe des Pixels (= Wert an edi-Position) in eax Form: #AARRGGBB
    ;
    ; Grün-Wert-Berechnung:
    ;
    _("shl eax, 16") ; eax mit 2^16 = 256^2 multiplizieren
    ; -> der 32bit-Farbcode #AARRGGBB wird zu #GGBB0000
    _("shr eax, 27") ; eax um 6 Hex-Stellen (24bit) + 3bit nach rechts shiften (die drei Bit werden durch drei Bits vom Geheimtext ersetzt)
    _("shl eax, 3") ; und die 3bit wieder zurückshiften (sind jetzt null)
    ; -> eax ist nur noch #GG und die letzten drei Bit sind 0

    [/autoit] [autoit][/autoit] [autoit]

    _("shl edx, 27") ; von edx werden nur die Bits 4 - 6 verwendet (für den Grün-Wert)
    _("shr edx, 29") ; -> erst die ersten 3 Bit eliminieren und dann zurückshiften (->7-32 weg)

    [/autoit] [autoit][/autoit] [autoit]

    _("add eax, edx") ; eax (Farbwert Grün) und edx (3 bit String) verknüpfen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("push eax") ; Grün-Wert auf den Stack pushen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("movzx edx, byte[esi]"); edx ist das nächste Byte des Binärstrings

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax, dword [edi]"); Farbe des Pixels (= Wert an edi-Position) in eax Form: #AARRGGBB
    ;
    ; Blau-Wert-Berechnung:
    ;
    _("shl eax, 24") ; eax mit 2^24 = 256^3 multiplizieren
    ; -> der 32bit-Farbcode #AARRGGBB wird zu #BB000000
    _("shr eax, 26") ; eax um 6 Hex-Stellen (24bit) + 2bit nach rechts shiften (die zwei Bit werden durch drei Bits vom Geheimtext ersetzt)
    _("shl eax, 2") ; und die 2bit wieder zurückshiften
    ; -> eax ist nur noch #BB und die letzten zwei Bit sind 0

    [/autoit] [autoit][/autoit] [autoit]

    _("shl edx, 30") ; von edx werden nur die Bits 7 - 8 verwendet (für den Blau-Wert)
    _("shr edx, 30") ; -> erst die ersten 6 Bit eliminieren und dann zurückshiften (->9-32 weg)

    [/autoit] [autoit][/autoit] [autoit]

    _("add eax, edx") ; eax (Farbwert Blau) und edx (2 bit String) verknüpfen

    [/autoit] [autoit][/autoit] [autoit]

    ; ############## Farben zusammensetzen

    [/autoit] [autoit][/autoit] [autoit]

    _("mov edx, 0xFF000000"); edx auf RGB Farbwert 0 setzen, Alphawert auf FF (standard)
    _("add edx, eax") ; eax enthält noch den Blau-Wert -> dazu addieren
    _("pop eax") ; eax enthält nun den Grün-Wert
    _("shl eax, 8") ; eax um zwei Hex-Stellen nach links shiften -> #GG00 (Grün-Wert an richtige Stelle)
    _("add edx, eax") ; und Grün-Wert zu edx addieren
    _("pop eax") ; eax enthält den Rot-Wert
    _("shl eax, 16") ; eax um 4 Hex-Stellen nach links shiften -> #RR0000 (Rot-Wert an richtige Stelle)
    _("add edx, eax") ; und zu edx addieren

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("mov [edi], edx") ; Farbwert in der Bitmap speichern

    [/autoit] [autoit][/autoit] [autoit]

    ; ##################### Ende der Steganografie
    _("add esi, 1") ; ebx um 1 Byte erhöhen (es wird pro Pixel 1 Byte Text versteckt)
    _("add edi, 4") ; edi um 4 Bytes (1 Pixel) erhöhen
    _("dec ecx") ; ecx -= 1 -> Schleifendurchläufe rückwärts zählen
    _("cmp ecx, 0") ; Sind alle Pixel durchlaufen?
    _("jne Pixel") ; wenn nicht, dann Schleife wiederholen
    _("Ende:")
    _("ret")

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc

    [/autoit]

    Deinen ersten Tipp mit movzx habe ich umgesetzt, wodurch sich auch das Problem mit den Null-Bytes (dein dritter Tipp) gelöst hat.
    Dein zweiter Vorschlag mit der Maske und der Verknüpfung erscheint auch logisch, aber den habe ich bis jetzt noch nicht umgesetzt. Dazu habe ich nämlich noch eine Frage. ^^ Was ist der genaue Vorteil? Denn mit meiner bisherigen Methode mit Stack klappt es bisher einwandfrei. Gibt es irgendwelche Nachteile bei der Verwendung des Stacks oder weshalb soll ich mir die push und pops sparen? Ich möchte deinen Tipp nicht ablehnen, mich interessiert nur der Unterschied zwischen den beiden Methoden. ;)

    Liebe Grüße,
    Xenon
    :)

  • Hi,

    Zitat

    Ich habe mein Problem jetzt lösen können, mein Programm funktioniert!

    DAS ist genau der Grund, wieso du sehr entspannt über

    Zitat

    Dein zweiter Vorschlag mit der Maske und der Verknüpfung erscheint auch logisch, aber den habe ich bis jetzt noch nicht umgesetzt.

    nachdenken kannst ;)

    Zitat

    Gibt es irgendwelche Nachteile bei der Verwendung des Stacks oder weshalb soll ich mir die push und pops sparen? Ich möchte deinen Tipp nicht ablehnen, mich interessiert nur der Unterschied zwischen den beiden Methoden.

    Naja, der "wirkliche" Grund ist natürlich völlig jenseits von Gut und Böse. Geschwindigkeit und Übersicht. Über die paar Takte, die das Speicherinterface bei PUSH/POP länger braucht, und mit dem Hintergrund, dass Assembler sowieso für die meisten sog. "Programmierer" völlig daneben und nicht lesbar ist, KÖNNTEST du das natürlich so lassen.
    Die Intension, Assembler zu nutzen ist imho aber, möglichst schnellen und kurzen Code zu erstellen.
    Und da ist jedes "unnötige" PUSH/POP ein Dorn im Fleisch :D
    Du hast auch keinerlei sog. Register-Pressure, d.h. du hast noch einige der Prozessorregister übrig, um darin Zwischenergebnisse zu puffern.

    Btw. leider funktioniert bei mir unter Win7/64 und 3.3.10.2 der in Assembleit integrierte Debugger nicht mehr. Das ist natürlich besonders ärgerlich, ich vermute wieder mal einige "Script breaking changes" in den bisher funktionierenden Funktionen...
    //EDIT schon übel, wenn man seine eigenen Programme nicht mehr bedienen kann, hab das

    [autoit]

    _("org " & FasmGetBasePtr($Fasm))

    [/autoit]

    vergessen :rolleyes:

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    Einmal editiert, zuletzt von Andy (22. April 2014 um 08:43)

  • Okay, vielen Dank für deine tolle Erklärung! ;)
    Ich habe mein Skript jetzt nochmal umgeschrieben und dabei ist es gleich noch kürzer geworden:

    Spoiler anzeigen
    [autoit]

    #include <assembleit.au3>
    #include <GDIPlus.au3>

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Variablen
    Local $Locked, $Scan0, $hImage
    Local $iWidth, $iHeight

    [/autoit] [autoit][/autoit] [autoit]

    ; GDIPlus lädt das Bild
    _GDIPlus_Startup()
    $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\landschaft.bmp")
    $iWidth = _GDIPlus_ImageGetWidth($hImage)
    $iHeight = _GDIPlus_ImageGetHeight($hImage)

    [/autoit] [autoit][/autoit] [autoit]

    ; An die Pixeldaten gelangen
    $Locked = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iWidth, $iHeight, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
    $Scan0 = DllStructGetData($Locked, "Scan0")

    [/autoit] [autoit][/autoit] [autoit]

    $StructPic = DllStructCreate ("byte[" & ($iWidth * $iHeight) & "]") ; DLL-Struct für den String erstellen (für jeden Pixel ein Byte) und ein int für die Länge der Datei
    $sString = "Hallo! :)"
    $bBinary = StringToBinary ($sString) ; String, der versteckt werden soll
    DllStructSetData ($StructPic, 1, $bBinary) ; DLL-Struct füllen (Binäre Nachricht)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; ASM-Code auführen
    $_ASSEMBLEIT_FLAG = 1
    $return = _assembleit("int", "HideTextInPic", "ptr", $Scan0, "int", $iWidth, "int", $iHeight, "ptr", DllStructGetPtr ($StructPic))

    [/autoit] [autoit][/autoit] [autoit]

    ; Neues Bild speichern
    _GDIPlus_BitmapUnlockBits($hImage, $Locked)
    _GDIPlus_ImageSaveToFile ($hImage, @ScriptDir & "\output.bmp")
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func HideTextInPic ()

    [/autoit] [autoit][/autoit] [autoit]

    #cs

    [/autoit] [autoit][/autoit] [autoit]

    Kommentar zur Funktionsweise:

    [/autoit] [autoit][/autoit] [autoit]

    - es werden die letzten drei Bits vom Rot- und Grün-Wert eines jeden Pixels, sowie zwei Bit vom Blau-Wert durch
    insgesamt 8 Bit (ein Byte) des Geheimtextes ersetzt (bei Blau nur 2 Bit, damit man auf 1 Byte kommt)

    [/autoit] [autoit][/autoit] [autoit]

    #ce

    [/autoit] [autoit][/autoit] [autoit]

    _("use32")
    _("mov edi, [esp+4]") ; Pointer-Position für die Pixeldaten in edi speichern
    _("mov esi, [esp+16]") ; Pointer-Position auf den zu versteckenden Binärstring (die ersten 4 Byte sind die Länge)
    _("mov eax, [esp+8]") ; eax = $iWidth
    _("mul dword[esp+12]") ; eax *= $iHeight
    _("mov ecx, eax") ; Counter für alle Pixel

    [/autoit] [autoit][/autoit] [autoit]

    _("Pixel:") ; Beginn der Schleife für jeden Pixel

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("movzx edx, byte[esi]") ; edx ist das nächste Byte des Binärstrings
    _("shr edx, 5") ; von edx werden nun die ersten 3 Bit genommen (für den Rot-Wert) -> Bits 4 - 8 eliminieren
    _("shl edx, 16") ; -> und an richtige Position schieben (binär: 00000000 00000RRR 00000000 00000000)

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax, edx") ; Rot-Wert in eax speichern

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("movzx edx, byte[esi]") ; edx ist wieder das Byte des Binärstrings wie bei rot
    _("shr edx, 2") ; von edx werden nun die Bits 4 - 6 genommen (für den Grün-Wert) -> Bits 1-3 und 7-8 eliminieren
    _("shl edx, 29")
    _("shr edx, 21") ; -> und an richtige Position schieben (binär: 00000000 00000000 00000GGG 00000000)

    [/autoit] [autoit][/autoit] [autoit]

    _("or eax, edx") ; Rot- und Grün-Wert verknüpfen

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("movzx edx, byte[esi]") ; edx ist wieder das Byte des Binärstrings wie bei rot und grün
    _("shl edx, 30") ; von edx werden nun die Bits 7 und 8 genommen (für den Blau-Wert) -> Bits 1-6 eliminieren
    _("shr edx, 30") ; -> und wieder zurück an richtige Position schieben (binär: 00000000 00000000 00000000 000000BB)

    [/autoit] [autoit][/autoit] [autoit]

    _("or eax, edx") ; Rot-, Grün- und Blau-Wert verknüpfen -> 00000000 00000RRR 00000GGG 000000BB

    [/autoit] [autoit][/autoit] [autoit]

    _("mov edx, dword [edi]") ; Farbe des Pixels (= Wert an edi-Position) in edx Form: #AARRGGBB
    _("and edx, 0xFFF8F8FC") ; Pixelfarbe mit 11111111 11111000 11111000 11111100 verknüpfen -> die Bits, die überschrieben werden auf 0 setzen
    _("or eax, edx") ; und mit den neuen Daten per OR verknüpfen
    _("mov [edi], eax") ; Farbwert in der Bitmap speichern

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _("add esi, 1") ; ebx um 1 Byte erhöhen (es wird pro Pixel 1 Byte Text versteckt)
    _("add edi, 4") ; edi um 4 Bytes (1 Pixel) erhöhen
    _("dec ecx") ; ecx -= 1 -> Schleifendurchläufe rückwärts zählen
    _("cmp ecx, 0") ; Sind alle Pixel durchlaufen?
    _("jne Pixel") ; wenn nicht, dann Schleife wiederholen
    _("Ende:")
    _("ret")

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc

    [/autoit]


    Jetzt läuft es ohne PUSH/POP und wird per AND bzw. OR verknüpft.


    Btw. leider funktioniert bei mir unter Win7/64 und 3.3.10.2 der in Assembleit integrierte Debugger nicht mehr. Das ist natürlich besonders ärgerlich, ich vermute wieder mal einige "Script breaking changes" in den bisher funktionierenden Funktionen...


    Also ich habe auch Win7/64 Bit und die Version 3.3.10.2, aber zumindest folgendes Debug-Beispiel von dir funktioniert bei mir:

    Spoiler anzeigen
    [autoit]

    ;testfile AssembleIt with Debugger
    ;left click on "Next..."-Button continues the asm-code
    ;click "End Debugging" to exit Debugger and to continue the Script
    ;"Kill"-Button ends Script

    [/autoit] [autoit][/autoit] [autoit]

    #include <assembleit.au3>

    [/autoit] [autoit][/autoit] [autoit]

    ;_asmdbg_() now with parameters!
    ; _ASMDBG_("$ebx<10") shows debugwindow only if EBX<9
    ;$return = _assembleit("int", "F2", "int", 100, "int", 200, "ptr", 0xFF00FF00)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;example how to use the $_ASSEMBLEIT_FLAG, if code is generated, please comment the following line or set flag to 1
    $_ASSEMBLEIT_FLAG = 1 ;generates the code for callwindowprocw(),comment this line or set flag to 1
    $return = _assembleit("int", "F2", "int", 100, "int", 200, "int", 5);returns EAX
    ;MsgBox(0, "Add", $return)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;the following lines are generated by AssembleIt(), they could be insert with CTRL+V, now the 3 lines above should be deleted/commented
    Global $tCodeBuffer = DllStructCreate("byte[9]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, "0x8B44240403442408C3") ;write opcodes into memory

    [/autoit] [autoit][/autoit] [autoit]

    ;change the parameters in the following line (generated from AssembleIt) from
    ;$ret=DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer),"int",Param1,"int",Param2,"int",0,"int",0)
    ;to
    $ret = DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer), "int", 100, "int", 200, "int", 0, "int", 0)
    ;remember that DllCall() returns an array!
    Msgbox(0,"Add",$ret[0])

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    DllCallbackFree($_DBG_)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func Add2Integer()
    _("use32")
    _("mov eax,[esp+4]") ;
    _("add eax,[esp+8]") ;
    _("ret")
    EndFunc ;==>Add2Integer

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func F2()
    _("use32")
    _("org " & FasmGetBasePtr($Fasm))

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; register füllen mit stackinhalt
    _("mov eax,[esp+4]") ;
    _("mov ecx,[esp+8]") ;

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;FPU-Register füllen mit konstanten
    _("finit")
    _("fldpi")
    _("fldz")
    _("fldl2t")
    _("fldl2e")
    _("fldln2")
    _("fldlg2")
    _("fldpi")

    [/autoit] [autoit][/autoit] [autoit]

    _ASMDBG_("fldpi") ;debug-gui anzeigen

    [/autoit] [autoit][/autoit] [autoit]

    _("mov ebx,[esp+12]") ;

    [/autoit] [autoit][/autoit] [autoit]

    _("mov edx,4") ;edx=schleifenzähler, 3x alles durchlaufen^^
    _("_testlabel:") ;schleife
    _("dec edx") ;3x durchlaufen

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax,75")
    _("fstp qword[" & DllStructGetPtr($struct_double) & "]");Wert aus FPU nach....
    _("movhpd xmm0,[" & DllStructGetPtr($struct_double) & "]");...XMM-Register transferieren

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _ASMDBG_() ;anzeigen
    _("stc") ;set carryflag
    _("@@: sub eax,1")
    _("stc") ;set carryflag
    ;folgende zeile läuft so lange, bis eax=50 ist! ImDebug-Fenster wird also erst eax=50 angezeigt
    _ASMDBG_("$eax=50") ;nur anzeigen wenn eax=50 ist
    _("cmp eax,5")
    _("ja @b")
    _("mov eax,0xDEADBEEF")
    _("push eax") ;stack verändern
    _ASMDBG_() ;anzeigen
    _("sub esp,8")
    _ASMDBG_()
    _("mov ebx,22") ;ebx=22
    _("@@: sub ebx,1") ;ebx=ebx-1
    ;folgende zeile läuft so lange, bis ebx<10 ist! Im Debug-Fenster wird also erst ebx=9 angezeigt !!!
    _ASMDBG_("$ebx<10") ;nur anzeigen wenn ebx<10 ist
    _("cmp ebx,5")
    _("ja @b")
    _("add esp,8") ;stack verändern
    _ASMDBG_()
    _("pop edi") ;stack verändern
    _("stc") ;carry-flag setzen
    _ASMDBG_()
    _("_weiter:")
    _("cmp edx,1")
    _("jne _testlabel")
    _ASMDBG_()
    _("ret") ;Ende

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    EndFunc ;==>F2

    [/autoit] [autoit][/autoit] [autoit][/autoit]

    MfG Xenon :)

    PS: Ich hatte leider über Ostern nicht so viel Zeit und konnte erst jetzt antworten...

  • Seeehr schick^^
    Dein Code ist jetzt kürzer, es geht aber noch wesentlich einfacher. Du musst nicht immer das Byte aus dem Speicher laden, mit den Schiebebefehlen kannst du einfach die RRR GGG BB trennen...

    [autoit]

    _("Pixel:") ; Beginn der Schleife für jeden Pixel

    [/autoit][autoit][/autoit][autoit]

    _("movzx edx, byte[esi]") ; edx ist das erste Byte des Binärstrings, wird nur 1x aus dem Speicher gelesen
    ;(binär EDX: 00000000 00000000 000000000 RRRGGGBB)
    _("shl edx, 11") ;(binär EDX: 00000000 00000RRR GGGBBB000 00000000) komlett nach links schieben
    _("shr dx, 5") ;(binär EDX: 00000000 00000RRR 00000GGG BB000000) nur DX shiften
    _("shr dl, 6") ;(binär EDX: 00000000 00000RRR 00000GGG 00000BB) nur lowbyte shiften

    [/autoit][autoit][/autoit][autoit]

    _("mov eax, dword [edi]") ; Farbe des Pixels (= Wert an edi-Position) in eax Form: #AARRGGBB
    _("and eax, 0xFFF8F8FC") ; Pixelfarbe mit 11111111 11111000 11111000 11111100 verknüpfen -> die Bits, die überschrieben werden auf 0 setzen
    _("or eax, edx") ; und mit den neuen Daten per OR verknüpfen
    _("mov [edi], eax") ; Farbwert in der Bitmap speichern

    [/autoit]


    Genauso kannst du natürlich auch beim "Auslesen" die Bits zusammenfassen....
    -------- -----RRR -----GGG ------BB
    low-byte 6 nach links shiften
    -------- -----RRR -----GGG BB-----
    high- und lowbyte zusammen (AX,BX, usw) 5 nach links
    -------- -----RRR GGGBB--- --------
    alles 11 nach rechts
    -------- -------- -------- RRRGGGBB
    voila :D


    Insgesamt könnte man noch einige Zeilen sparen, aber so ist es lesbarer :thumbup:

    Weiterhin läuft dein Programm nun alle Pixel der gesamten Grafik durch, du könntest beim Ende des Strings abbrechen!
    Die Struct, in der dein zu verbergender String steckt, müsste nur ein Byte (= NULL) größer sein als der String.

    Auch beim Aufrufen per Call reicht es, wenn du die Adresse des Bildes und die Adresse des Strings übergibst! Abbruchbedingung ist das Nullbyte am Ende des Strings!
    Die Abfrage, ob der String größer als die Anzahl der Pixel des Bildes ist, würde ich in eine Zeile AutoIt packen^^


    Zitat

    Also ich habe auch Win7/64 Bit und die Version 3.3.10.2, aber zumindest folgendes Debug-Beispiel von dir funktioniert bei mir:

    Natürlich funktioniert bei mir der Debugger auch, man sollte seine eigenen Programme auch bedienen können... :rolleyes:

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    14 Mal editiert, zuletzt von Andy (24. April 2014 um 11:41)

  • Hi,
    es geht natürlich NOCH kürzer/schneller, aber wen juckt das... 8o

    Anbei Kodierer/Dekodierer (der Dekodierer besteht aus 25 BYTES Code :thumbup: )

    Spoiler anzeigen
    [autoit]

    #include <assembleit.au3>
    #include <GDIPlus.au3>

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Variablen
    Local $Locked, $Scan0, $hImage
    Local $iWidth, $iHeight

    [/autoit] [autoit][/autoit] [autoit]

    ; GDIPlus lädt das Bild
    _GDIPlus_Startup()
    $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\desert.jpg") ;"\test.bmp")
    $iWidth = _GDIPlus_ImageGetWidth($hImage)
    $iHeight = _GDIPlus_ImageGetHeight($hImage)

    [/autoit] [autoit][/autoit] [autoit]

    ; An die Pixeldaten gelangen
    $Locked = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iWidth, $iHeight, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
    $Scan0 = DllStructGetData($Locked, "Scan0")

    [/autoit] [autoit][/autoit] [autoit]

    $sString = "Hallo 123 AutoIt :o) !"
    $StructPic = DllStructCreate("byte[" & StringLen($sString) + 1 & "]") ; DLL-Struct für den String erstellen (für jeden Pixel ein Byte) und ein int für die Länge der Datei
    $bBinary = StringToBinary($sString) ; String, der versteckt werden soll
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $bBinary = ' & $bBinary & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    DllStructSetData($StructPic, 1, $bBinary) ; DLL-Struct füllen (Binäre Nachricht)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; ASM-Code auführen
    $_ASSEMBLEIT_FLAG = 1
    $return = _assembleit("int", "HideTextInPic", "ptr", $Scan0, "ptr", DllStructGetPtr($StructPic))

    [/autoit] [autoit][/autoit] [autoit]

    ; Neues Bild speichern
    _GDIPlus_BitmapUnlockBits($hImage, $Locked)
    _GDIPlus_ImageSaveToFile($hImage, @ScriptDir & "\output.bmp")
    _GDIPlus_BitmapDispose($hImage)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;auslesen und dekodieren
    $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\output.bmp") ;"\test.bmp")
    $iWidth = _GDIPlus_ImageGetWidth($hImage)
    $iHeight = _GDIPlus_ImageGetHeight($hImage)

    [/autoit] [autoit][/autoit] [autoit]

    ; An die Pixeldaten gelangen
    $Locked = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iWidth, $iHeight, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
    $Scan0 = DllStructGetData($Locked, "Scan0")

    [/autoit] [autoit][/autoit] [autoit]

    $struct_text = DllStructCreate("char[" & $iWidth * $iHeight & "]") ;struct, in die der Text geschrieben wird

    [/autoit] [autoit][/autoit] [autoit]

    $return = _assembleit("int", "ShowTextInPic", "ptr", $Scan0, "ptr", DllStructGetPtr($struct_text))

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $text = DllStructGetData($struct_text, 1)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $text = ' & $text & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func ShowTextInPic() ;25 Byte :o)

    [/autoit] [autoit][/autoit] [autoit]

    _("use32")
    _("mov esi, [esp+4]") ; Pointer-Position für die Pixeldaten in edi speichern
    _("mov edi, [esp+8]") ; EDI = pointer auf den String
    _("Pixel:") ; Beginn der Schleife für jeden Pixel
    _("lodsd") ; EAX = dword[ESI] = Pixel , ESI=ESI+4 (binär EAX: -------- -----RRR -----GGG ------BB)
    _("shl al,6") ;(binär EAX: -------- -----RRR -----GGG BB------) nur lowbyte shiften bits zu einem byte zusammenschieben
    _("shl ax, 5") ;(binär EAX: -------- -----RRR GGGBB--- --------)
    _("shr eax, 11") ;(binär EAX: -------- -------- -------- RRRGGGBB)
    _("stosb") ; text nach [EDI] , edi=edi+1
    _("cmp al,0") ;Ende erreicht?
    _("jne Pixel")
    _("ret")

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc ;==>ShowTextInPic

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func HideTextInPic()

    [/autoit] [autoit][/autoit] [autoit]

    #cs

    Kommentar zur Funktionsweise:

    - es werden die letzten drei Bits vom Rot- und Grün-Wert eines jeden Pixels, sowie zwei Bit vom Blau-Wert durch
    insgesamt 8 Bit (ein Byte) des Geheimtextes ersetzt (bei Blau nur 2 Bit, damit man auf 1 Byte kommt)

    #ce

    [/autoit] [autoit][/autoit] [autoit]

    _("use32")
    _("mov edi, [esp+4]") ; Pointer-Position für die Pixeldaten in edi speichern
    _("mov esi, [esp+8]") ; esi = ptr String

    [/autoit] [autoit][/autoit] [autoit]

    _("Pixel:") ; Beginn der Schleife für jeden Pixel
    _("movzx edx, byte[esi]") ; edx ist das erste Byte des Strings, wird nur 1x aus dem Speicher gelesen
    _("mov bl,dl") ;byte zwischenspeichern, später test, ob stringende
    ;(binär EDX: 00000000 00000000 000000000 RRRGGGBB)
    _("shl edx, 11") ;(binär EDX: 00000000 00000RRR GGGBBB000 00000000) komlett nach links schieben
    _("shr dx, 5") ;(binär EDX: 00000000 00000RRR 00000GGG BB000000) nur DX shiften
    _("shr dl, 6") ;(binär EDX: 00000000 00000RRR 00000GGG 00000BB) nur lowbyte shiften

    [/autoit] [autoit][/autoit] [autoit]

    _("mov eax, dword [edi]") ; Farbe des Pixels (= Wert an edi-Position) in edx Form: #AARRGGBB
    _("and eax, 0xFFF8F8FC") ; Pixelfarbe mit 11111111 11111000 11111000 11111100 verknüpfen -> die Bits, die überschrieben werden auf 0 setzen
    _("or eax, edx") ; und mit den neuen Daten per OR verknüpfen

    [/autoit] [autoit][/autoit] [autoit]

    _("stosd") ;ersetzt _("mov [edi], eax") und add edi,4 ; Farbwert in der Bitmap speichern

    [/autoit] [autoit][/autoit] [autoit]

    _("add esi, 1") ; ebx um 1 Byte erhöhen (es wird pro Pixel 1 Byte Text versteckt)
    _("cmp bl,0") ;Ende erreicht?
    _("jne Pixel")

    [/autoit] [autoit][/autoit] [autoit]

    _("ret")

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc ;==>HideTextInPic

    [/autoit] [autoit][/autoit] [autoit][/autoit]

    AssembleIt braucht man natürlich nicht mehr, wenn man per $_ASSEMBLEIT_FLAG = 0 assembliert und den Code direkt ausführt.
    Dann sieht das so aus...

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Variablen
    Local $Locked, $Scan0, $hImage
    Local $iWidth, $iHeight

    [/autoit] [autoit][/autoit] [autoit]

    ; GDIPlus lädt das Bild
    _GDIPlus_Startup()
    $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\desert.jpg") ;"\test.bmp")
    $iWidth = _GDIPlus_ImageGetWidth($hImage)
    $iHeight = _GDIPlus_ImageGetHeight($hImage)

    [/autoit] [autoit][/autoit] [autoit]

    ; An die Pixeldaten gelangen
    $Locked = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iWidth, $iHeight, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
    $Scan0 = DllStructGetData($Locked, "Scan0")

    [/autoit] [autoit][/autoit] [autoit]

    $sString = "Hallo 123 AutoIt :o) !" ;der zu versteckende String

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $StructPic = DllStructCreate("byte[" & StringLen($sString) + 1 & "]") ; DLL-Struct für den String erstellen (für jeden Pixel ein Byte) und ein int für die Länge der Datei
    $bBinary = StringToBinary($sString) ; String, der versteckt werden soll
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $bBinary = ' & $bBinary & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    DllStructSetData($StructPic, 1, $bBinary) ; DLL-Struct füllen (Binäre Nachricht)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; ASM-Code auführen, kodieren
    Global $tCodeBuffer = DllStructCreate("byte[42]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, "0x8B7C24048B7424080FB61688D3C1E20B66C1EA05C0EA068B0725FCF8F8FF09D0AB83C60180FB0075DFC3") ;write opcodes into memory
    $ret = DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer), "ptr", $Scan0, "ptr", DllStructGetPtr($StructPic), "int", 0, "int", 0)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Neues Bild speichern
    _GDIPlus_BitmapUnlockBits($hImage, $Locked)
    _GDIPlus_ImageSaveToFile($hImage, @ScriptDir & "\output.bmp")
    _GDIPlus_BitmapDispose($hImage)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;auslesen und dekodieren
    $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\output.bmp") ;"\test.bmp")
    $iWidth = _GDIPlus_ImageGetWidth($hImage)
    $iHeight = _GDIPlus_ImageGetHeight($hImage)

    [/autoit] [autoit][/autoit] [autoit]

    ; An die Pixeldaten gelangen
    $Locked = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iWidth, $iHeight, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
    $Scan0 = DllStructGetData($Locked, "Scan0")

    [/autoit] [autoit][/autoit] [autoit]

    $struct_text = DllStructCreate("char[" & $iWidth * $iHeight & "]") ;struct, in die der Text geschrieben wird

    [/autoit] [autoit][/autoit] [autoit]

    ;dekoder
    Global $tCodeBuffer = DllStructCreate("byte[25]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, "0x8B7424048B7C2408ADC0E00666C1E005C1E80BAA3C0075F0C3") ;write opcodes into memory
    $ret = DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer), "ptr", $Scan0, "ptr", DllStructGetPtr($struct_text), "int", 0, "int", 0)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $text = DllStructGetData($struct_text, 1) ;text auslesen
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $text = ' & $text & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()

    [/autoit]

    Errorhandling sollte man tunlichst hinzufügen, denn wenn der Text länger ist, als das Bild Pixel hat, gibts nen Crash^^
    Aber das kann jeder selbst zusammenpfriemeln ;)

    Es bleibt natürlich offen, die Textbits statt -------- -----RRR -----GGG ------BB bspw. als -------- ------RR -----RGG -----GBB abzuspeichern, oder nur in jedem 5. Pixel. Oder nur 1 Bit in jedem 7. Pixel...
    Sehr einfach in ASM zu realisieren ist das tauschen der Bits, das käme noch einer internen Verschlüsselung gleich, also aus -------- ------RR -----RGG -----GBB würde dann bspw. -------- ------RB -----GRG -----BRG
    Der Phantasie sind keine Grenzen gesetzt!

    Zur Not kann man die Bilder auch als PNG speichern, dann sind sie etwas kleiner, aber immer noch verlustfrei komprimiert ;) Oder man verwendet libjpeg um verlustfrei in jpg zu transformieren.
    Btw. wird mit ca. 500MB/Sekunde kodiert/dekodiert! Was bedeutet, dass 500KB Text in einer Millisekunde in einem Bild verschlüsselt/entschlüsselt werden! Das entspricht ca. 6 Prozessortakten pro verschlüsseltem Buchstaben!

    Wer mag, kann natürlich auch binäre Dateien "verstecken", ich würde dann einfach einen Integer mit der Länge der Daten in die ersten 4 Pixel packen, und so lange kodieren/dekodieren, bis dieser Wert erreicht ist! Oder direkt Base64 verwenden...

    In diesem kleinen Kerl steckt übrigens auch ein Text -> autoit.de/wcf/attachment/24339/

    Beschränkt man sich auf Bitmap-Dateien (*.BMP), kann man sogar komplett auf GDI(+) verzichten und direkt in die "Pixel"-Daten schreiben.
    Das wäre extrem leicht in jeder anderen Computersprache (und jedem anderen Gerät) umzusetzen!