mithril-security/NonSuspiciousImageDecoder
1
1#!/usr/bin/env python2# coding:UTF-83"""LSBSteg.py4 5Usage:6 LSBSteg.py encode -i <input> -o <output> -f <file>7 LSBSteg.py decode -i <input> -o <output>8 9Options:10 -h, --help Show this help11 --version Show the version12 -f,--file=<file> File to hide13 -i,--in=<input> Input image (carrier)14 -o,--out=<output> Output image (or extracted file)15"""16 17import cv218import docopt19import numpy as np20 21channel_order = (2, 1, 0, 3)22 23class SteganographyException(Exception):24 pass25 26 27class LSBSteg():28 def __init__(self, im):29 self.image = im30 self.height, self.width, self.nbchannels = im.shape31 self.size = self.width * self.height32 33 self.maskONEValues = [1,2,4,8,16,32,64,128]34 #Mask used to put one ex:1->00000001, 2->00000010 .. associated with OR bitwise35 self.maskONE = self.maskONEValues.pop(0) #Will be used to do bitwise operations36 37 self.maskZEROValues = [254,253,251,247,239,223,191,127]38 #Mak used to put zero ex:254->11111110, 253->11111101 .. associated with AND bitwise39 self.maskZERO = self.maskZEROValues.pop(0)40 41 self.curwidth = 0 # Current width position42 self.curheight = 0 # Current height position43 self.curchan = 0 # Current channel position44 45 def put_binary_value(self, bits): #Put the bits in the image46 for c in bits:47 val = list(self.image[self.curheight,self.curwidth]) #Get the pixel value as a list48 if int(c) == 1:49 val[self.curchan] = int(val[self.curchan]) | self.maskONE #OR with maskONE50 else:51 val[self.curchan] = int(val[self.curchan]) & self.maskZERO #AND with maskZERO52 53 self.image[self.curheight,self.curwidth] = tuple(val)54 self.next_slot() #Move "cursor" to the next space55 56 def next_slot(self):#Move to the next slot were information can be taken or put57 if self.curchan == self.nbchannels-1: #Next Space is the following channel58 self.curchan = 059 if self.curwidth == self.width-1: #Or the first channel of the next pixel of the same line60 self.curwidth = 061 if self.curheight == self.height-1:#Or the first channel of the first pixel of the next line62 self.curheight = 063 if self.maskONE == 128: #Mask 1000000, so the last mask64 raise SteganographyException("No available slot remaining (image filled)")65 else: #Or instead of using the first bit start using the second and so on..66 self.maskONE = self.maskONEValues.pop(0)67 self.maskZERO = self.maskZEROValues.pop(0)68 else:69 self.curheight +=170 else:71 self.curwidth +=172 else:73 self.curchan +=174 75 def read_bit(self): #Read a single bit int the image76 val = self.image[self.curheight,self.curwidth,channel_order[self.curchan]]77 val = int(val) & self.maskONE78 self.next_slot()79 if val > 0:80 return "1"81 else:82 return "0"83 84 def read_byte(self):85 return self.read_bits(8)86 87 def read_bits(self, nb): #Read the given number of bits88 bits = ""89 for i in range(nb):90 bits += self.read_bit()91 return bits92 93 def byteValue(self, val):94 return self.binary_value(val, 8)95 96 def binary_value(self, val, bitsize): #Return the binary value of an int as a byte97 binval = bin(val)[2:]98 if len(binval) > bitsize:99 raise SteganographyException("binary value larger than the expected size")100 while len(binval) < bitsize:101 binval = "0"+binval102 return binval103 104 def encode_text(self, txt):105 l = len(txt)106 binl = self.binary_value(l, 16) #Length coded on 2 bytes so the text size can be up to 65536 bytes long107 self.put_binary_value(binl) #Put text length coded on 4 bytes108 for char in txt: #And put all the chars109 c = ord(char)110 self.put_binary_value(self.byteValue(c))111 return self.image112 113 def decode_text(self):114 ls = self.read_bits(16) #Read the text size in bytes115 l = int(ls,2)116 i = 0117 unhideTxt = ""118 while i < l: #Read all bytes of the text119 tmp = self.read_byte() #So one byte120 i += 1121 unhideTxt += chr(int(tmp,2)) #Every chars concatenated to str122 return unhideTxt123 124 def encode_image(self, imtohide):125 w = imtohide.width126 h = imtohide.height127 if self.width*self.height*self.nbchannels < w*h*imtohide.channels:128 raise SteganographyException("Carrier image not big enough to hold all the datas to steganography")129 binw = self.binary_value(w, 16) #Width coded on to byte so width up to 65536130 binh = self.binary_value(h, 16)131 self.put_binary_value(binw) #Put width132 self.put_binary_value(binh) #Put height133 for h in range(imtohide.height): #Iterate the hole image to put every pixel values134 for w in range(imtohide.width):135 for chan in range(imtohide.channels):136 val = imtohide[h,w][chan]137 self.put_binary_value(self.byteValue(int(val)))138 return self.image139 140 141 def decode_image(self):142 width = int(self.read_bits(16),2) #Read 16bits and convert it in int143 height = int(self.read_bits(16),2)144 unhideimg = np.zeros((width,height, 3), np.uint8) #Create an image in which we will put all the pixels read145 for h in range(height):146 for w in range(width):147 for chan in range(unhideimg.channels):148 val = list(unhideimg[h,w])149 val[chan] = int(self.read_byte(),2) #Read the value150 unhideimg[h,w] = tuple(val)151 return unhideimg152 153 def encode_binary(self, data):154 l = len(data)155 if self.width*self.height*self.nbchannels < l+64:156 raise SteganographyException("Carrier image not big enough to hold all the datas to steganography")157 self.put_binary_value(self.binary_value(l, 64))158 for byte in data:159 byte = byte if isinstance(byte, int) else ord(byte) # Compat py2/py3160 self.put_binary_value(self.byteValue(byte))161 return self.image162 163 def decode_binary(self):164 l = int(self.read_bits(64), 2)165 output = b""166 for i in range(l):167 output += bytearray([int(self.read_byte(),2)])168 return output169 170 171def main():172 args = docopt.docopt(__doc__, version="0.2")173 in_f = args["--in"]174 out_f = args["--out"]175 in_img = cv2.imread(in_f, cv2.IMREAD_UNCHANGED)176 steg = LSBSteg(in_img)177 lossy_formats = ["jpeg", "jpg"]178 179 if args['encode']:180 #Handling lossy format181 out_f, out_ext = out_f.split(".")182 if out_ext in lossy_formats:183 out_f = out_f + ".png"184 print("Output file changed to ", out_f)185 186 data = open(args["--file"], "rb").read()187 res = steg.encode_binary(data)188 cv2.imwrite(out_f, res)189 190 elif args["decode"]:191 raw = steg.decode_binary()192 with open(out_f, "wb") as f:193 f.write(raw)194 195 196if __name__=="__main__":197 main()198 199 