while investigating some ideas, converting data to images, I came across this method. This small app will copy data into PNG-images...
But only to a certain size, I think it is about 10mb. It does so by converting 4 bytes of data into one pixel.
This way, you can obscure your data, and upload the resulting images to Flickr (as they give you 1tb now...)
Happy coding
Try to decode one of your PNG images (a small one!). You will probably find something hidden in there like "Smoke Marijuana".
Edited to remove the word "hide", as this does not *hide* data inside the images. Thanks, wilbert!
Version 1.0.2
- added LZMA-Compression
- made a quick test using flickr as harddrive
Version 1.1.1
- increased speed a lot (using DrawingBuffer())
- some smaller tweaks
- much bigger files (than 10mb) work now (tested with 150mb)
Version 1.2.0
- added MD5
- added optional AES
- source code now available (should compile on Windows and Linux, too)
- find images here: http://pb.quadworks.de/Gluecksklee_raus.png and http://pb.quadworks.de/Gluecksklee_rein.png
- download the App for OS X from http://www.the-screaming-eye.com/Data2PNG
Code: Select all
#Title = "Data 2 PNG"
#version = "1.2.0"
; (c) 2013, The Screaming Eye (www.the-screaming-eye.com)
; Save one file inside a PNG-image, and reveal the saved data.
; Automatically pack using LZMA, if not compressible, just store.
; Add optional AES256, saves MD5 to compare stored and recovered file
EnableExplicit
Global Window_0, event, Encode, Decode, Result, Kleerein, Kleeraus, link, lzma
Global packen, cyphern, cyph, md5gad
Global md5.s = Space(32), md52.s = Space(32)
Procedure HideDataInPng(*data, size, packsize = 0)
Protected position=0,i,v
Protected maxx = 1, maxy = 1, imageid, imagewidth
Protected *dest
; try to get a quadratic image
imagewidth = Sqr(size) + 2
; we store 4 bytes in each pixel. Add 1 pixel to store data size
If size / imagewidth > 1
maxy = size / imagewidth + 1
maxx = imagewidth
Else
maxx = size / 4 + size % 4 + 1
EndIf
; create image of minimum needed size
imageid = CreateImage(#PB_Any,maxx,maxy,32)
; draw and hide
StartDrawing(ImageOutput(imageid))
*dest = DrawingBuffer()
DrawingMode(#PB_2DDrawing_AllChannels)
; save packedsize in another pixel
CopyMemory(*data,*dest,size)
Plot(maxx-2,maxy-1,packsize)
; save datasize in very last pixel
Plot(maxx-1,maxy-1,size)
;save the MD5
For i = 0 To 8
v = PeekL(@md5+i*4)
Plot(maxx-3-i,maxy-1,v)
Next i
StopDrawing()
ProcedureReturn imageid
EndProcedure
Procedure RevealDataFromPng(ImageId)
Protected size, maxx, maxy, i, v
Define *Data, imagewidth, originalsize, *packed, *Source, key.s
StartDrawing(ImageOutput(ImageId))
*Source = DrawingBuffer() ; get address of drawing buffer
DrawingMode(#PB_2DDrawing_AllChannels)
maxx = ImageWidth(ImageId)
maxy = ImageHeight(ImageId)
size = Point(maxx-1,maxy-1) ; Original Size
originalsize = Point(maxx-2,maxy-1) ; Packed Size
If size = 0
StopDrawing()
ProcedureReturn -1
EndIf
If originalsize = 0
originalsize = size
EndIf
*packed = AllocateMemory(originalsize)
*Data = AllocateMemory(originalsize)
CopyMemory(*source,*packed,size)
; get the MD5
For i = 0 To 8
v = Point(maxx-3-i,maxy-1)
PokeL(@md5+i*4,v)
Next i
StopDrawing()
Debug size ; 314
Debug originalsize ; 445
; De-Cypher data
If GetGadgetState(cyphern) = 1
key = GetGadgetText(cyph)
If AESDecoder(*packed, *data, size, @Key, 128, ?InitializationVector)
CopyMemory(*data,*packed,size)
EndIf
EndIf
If OriginalSize <> size
size = UncompressMemory(*packed,size,*data,originalsize,#PB_Packer_LZMA)
lzma = 1
Else
CopyMemory(*packed,*data,size)
lzma = 0
EndIf
md52 = MD5Fingerprint(*data,originalsize)
FreeMemory(*packed)
ProcedureReturn *data
EndProcedure
Procedure OpenWindow_0()
Window_0 = OpenWindow(#PB_Any, 100, 100, 420, 210, #Title + " " + #Version, #PB_Window_SystemMenu)
Encode = ButtonImageGadget(#PB_Any, 10, 10, 120, 120, ImageID(KleeRein),#PB_Image_Raised)
GadgetToolTip(Encode, "Drop a file to convert it to PNG.")
EnableGadgetDrop(Encode,#PB_Drop_Files,#PB_Drag_Copy|#PB_Drag_Copy|#PB_Drag_Move|#PB_Drag_Link)
Decode = ButtonImageGadget(#PB_Any, 290, 10, 120, 120, ImageID(KleeRaus),#PB_Image_Raised)
GadgetToolTip(Decode, "Drop a PNG to recover the saved file.")
EnableGadgetDrop(Decode,#PB_Drop_Files,#PB_Drag_Copy|#PB_Drag_Copy|#PB_Drag_Move|#PB_Drag_Link)
TextGadget(#PB_Any, 140, 10, 150, 120, "Copy to and get data from PNGs. Or look for secret messages hidden " +
"inside your images..." + #CRLF$ + "(c) 2013 by http://the-screaming-eye.com")
link = HyperLinkGadget(#PB_Any, 8, 190, 420, 24, "'Glücksklee' by Kolossos, 2005, deriative work by the-screaming-eye.com.",RGB($1f,$1f,$9f),#PB_HyperLink_Underline)
GadgetToolTip(link,"Click to visit http://www.the-screaming-eye.com")
packen = CheckBoxGadget(#PB_Any, 10, 140, 130, 20, "Pack (with LZMA)")
cyphern = CheckBoxGadget(#PB_Any, 140, 140, 130, 20, "UnScramble (AES)")
cyph = StringGadget(#PB_Any, 280, 140, 130, 20, "")
md5gad = StringGadget(#PB_Any, 10, 164, 400, 20, "MD5: ")
GadgetToolTip(packen, "Check, if you like to have your file packed before putting it into the PNG. Only applies, if compressible." + #CRLF$ + "Decompression is automatically used, if needed.")
GadgetToolTip(cyphern, "Check, if you like to have your file scrambled with a 16-digit passphrase.")
GadgetToolTip(cyph, "Enter your passphrase here. Remember it, you cannot unscramble the content without it!")
GadgetToolTip(md5gad, "MD5 of latest operation. Please copy, if you need it.")
SetGadgetState(packen,1)
DisableGadget(cyph,1)
Result = TextGadget(#PB_Any, 140, 114, 130, 25, "")
AddKeyboardShortcut(Window_0, #PB_Shortcut_Command | #PB_Shortcut_Q, #PB_Event_CloseWindow)
EndProcedure
Procedure Encode_Data(filename$,output$)
Protected *myData, fileid, imageid, size, packsize = 0, *packed, key.s
fileid = OpenFile(#PB_Any,filename$)
size = FileSize(filename$)
If size > 0
*myData = AllocateMemory(size)
*packed = AllocateMemory(size)
If *myData
ReadData(fileid,*myData,size)
CloseFile(fileID)
md5 = MD5Fingerprint(*myData,size)
; Pack data
If GetGadgetState(packen) = 1
packsize = CompressMemory(*myData,size,*packed,size, #PB_Packer_LZMA)
EndIf
; Cypher data
If GetGadgetState(cyphern) = 1
key = GetGadgetText(cyph)
If packsize <> 0
If AESEncoder(*packed, *myData, packsize, @Key, 128, ?InitializationVector)
CopyMemory(*myData,*packed,packsize)
Debug "ja-pack"
EndIf
Else
If AESEncoder(*myData, *packed, size, @Key, 128, ?InitializationVector)
CopyMemory(*packed,*myData,size)
Debug "ja-nicht-pack"
EndIf
EndIf
EndIf
If packsize <> 0
imageid = HideDataInPng(*packed,packsize,size)
lzma = 1
Else
imageid = HideDataInPng(*myData,size)
lzma = 0
EndIf
If IsImage(imageid)
SaveImage(imageid,output$,#PB_ImagePlugin_PNG,0,32)
FreeMemory(*myData)
FreeMemory(*packed)
ProcedureReturn imageid
Else
ProcedureReturn -1
EndIf
Else
ProcedureReturn -1
EndIf
Else
ProcedureReturn -1
EndIf
EndProcedure
Procedure Decode_Data(filename$,output$)
Protected *myData, fileid, imageid, size
imageid = LoadImage(#PB_Any,filename$)
If IsImage(imageid)
*myData = RevealDataFromPng(imageid)
If *myData = -1
ProcedureReturn -1
EndIf
size = MemorySize(*myData)
Debug md5
Debug md52
If size <> 0
fileid = CreateFile(#PB_Any,output$)
WriteData(fileid,*mydata,size)
CloseFile(fileid)
FreeMemory(*myData)
ProcedureReturn imageid
Else
ProcedureReturn -1
EndIf
Else
ProcedureReturn -1
EndIf
EndProcedure
Procedure Window_0_Events(event)
Protected File$, Filename$, imageid, temp, file2$
Select event
Case #PB_Event_CloseWindow,#PB_Event_Menu
End
Case #PB_Event_GadgetDrop, #PB_Event_Gadget ;{ Drop
If event = #PB_Event_Gadget
Select EventGadget()
Case packen, md5gad
ProcedureReturn
Case cyph ; Passphrase has to be 16 chars
If Len(GetGadgetText(cyph))>16
SetGadgetText(cyph,Left(GetGadgetText(cyph),16))
EndIf
ProcedureReturn
Case cyphern
If GetGadgetState(cyphern) = 1
DisableGadget(cyph,0)
Else
DisableGadget(cyph,1)
EndIf
ProcedureReturn
Case link
RunProgram("open","http://www.the-screaming-eye.com","")
ProcedureReturn
EndSelect
EndIf
;}
;{ Umwandeln
temp = Len(GetGadgetText(cyph))
If Temp <> 16 And Temp <> 0
MessageRequester("Information","Please enter a 16 digit passphrase.")
SetGadgetText(Result,"Passphrase too short")
ProcedureReturn
EndIf
SetGadgetState(decode,ImageID(KleeRaus))
SetGadgetState(encode,ImageID(KleeRein))
SetGadgetText(Result,"Starting")
If event = #PB_Event_GadgetDrop
File$ = Trim(EventDropFiles(),Chr(10))
Else
File$ = OpenFileRequester("Select source file","input","",0)
If file$ = ""
SetGadgetText(Result,"Stopped. No Source!")
ProcedureReturn
EndIf
EndIf
If FindString(file$,Chr(10)) <> 0
MessageRequester("Information","Please drop only single files here.")
Else
If FileSize(file$) > 1024*1024*10 ; mehr als 10 mb
SetGadgetText(Result,"Filesize: " + StrF(FileSize(file$)/1024/1024,2) + "mb")
EndIf
Select EventGadget()
Case encode
filename$ = SaveFileRequester("Select destination file",file$ + ".png","",0)
If filename$ = ""
SetGadgetText(Result,"No destination!")
ProcedureReturn
EndIf
SetGadgetText(md5gad,"MD5: ")
imageid = Encode_Data(file$,filename$)
If imageid = -1
If IsImage(imageid)
FreeImage(imageid)
EndIf
SetGadgetText(Result,"Conversion failed!")
ProcedureReturn
Else
If IsImage(imageid)
ResizeImage(imageid,120,120,#PB_Image_Raw)
SetGadgetState(encode,ImageID(imageid))
FreeImage(imageid)
EndIf
If lzma = 1
SetGadgetText(Result,"OK. Packed!")
Else
SetGadgetText(Result,"OK.")
EndIf
EndIf
SetGadgetText(md5gad,"MD5: " + md5)
SetClipboardText(md5)
Case decode
file2$ = ReplaceString(file$,".png","")
filename$ = SaveFileRequester("Select destination file",file2$,"",0)
If filename$ = ""
SetGadgetText(Result,"No destination!")
ProcedureReturn
EndIf
SetGadgetText(md5gad,"MD5: ")
imageid = Decode_Data(file$,filename$)
If imageid = -1
MessageRequester("Sorry","Conversion failed.")
SetGadgetText(Result,"Conversion failed!")
ProcedureReturn
Else
If lzma = 1
SetGadgetText(Result,"OK. Unpacked!")
Else
SetGadgetText(Result,"OK.")
EndIf
EndIf
SetGadgetText(md5gad,"MD5: " + md52)
SetClipboardText(md52)
If md5 <> md52
MessageRequester("Information","The MD5 of the saved and recovered files are different. Either an error happened, or the file was compromised.")
SetGadgetText(Result,"MD5 not identical")
EndIf
EndSelect
EndIf
;}
EndSelect
ProcedureReturn #True
EndProcedure
; Test
UsePNGImageDecoder()
UsePNGImageEncoder()
UseLZMAPacker()
Kleerein = CatchImage(#PB_Any,?kleerein,?kleeraus-?kleerein)
Kleeraus = CatchImage(#PB_Any,?kleeraus,?kleeende-?kleeraus)
OpenWindow_0()
Repeat
Event = WaitWindowEvent()
Window_0_Events(Event)
ForEver
DataSection
kleerein: IncludeBinary "Gluecksklee_rein.png"
kleeraus: IncludeBinary "Gluecksklee_raus.png"
kleeende:
InitializationVector: Data.b $3d, $af, $ba, $42, $9d, $9e, $b4, $30, $b4, $22, $da, $80, $2c, $9f, $ac, $41
EndDataSection
