PKš¦—G$0EÌÌ.Stegano-0.4.4.data/scripts/steganalysis-parity#!/usr/bin/python2.7 #-*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.1 $" __date__ = "$Date: 2011/12/29 $" __license__ = "GPLv3" try: from stegano import steganalysisParity except: print("Install Stegano: sudo pip install Stegano") from PIL import Image from optparse import OptionParser parser = OptionParser() parser.add_option("-i", "--input", dest="input_image_file", help="Image file") parser.add_option("-o", "--output", dest="output_image_file", help="Image file") parser.set_defaults(input_image_file = './pictures/Lenna.png', output_image_file = './pictures/Lenna_steganalysed.png') (options, args) = parser.parse_args() input_image_file = Image.open(options.input_image_file) output_image = steganalysisParity.steganalyse(input_image_file) output_image.save(options.output_image_file) PKš¦—G›ªÍù ù #Stegano-0.4.4.data/scripts/slsb-set#!/usr/bin/python2.7 #-*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.1 $" __date__ = "$Date: 2011/12/29 $" __license__ = "GPLv3" try: from stegano import slsbset except: print("Install stegano: sudo pip install Stegano") from stegano import tools from optparse import OptionParser parser = OptionParser(version=__version__) parser.add_option('--hide', action='store_true', default=False, help="Hides a message in an image.") parser.add_option('--reveal', action='store_true', default=False, help="Reveals the message hided in an image.") # Original image parser.add_option("-i", "--input", dest="input_image_file", help="Input image file.") # Generator parser.add_option("-g", "--generator", dest="generator_function", help="Generator") # Image containing the secret parser.add_option("-o", "--output", dest="output_image_file", help="Output image containing the secret.") # Non binary secret message to hide parser.add_option("-m", "--secret-message", dest="secret_message", help="Your secret message to hide (non binary).") # Binary secret to hide (OGG, executable, etc.) parser.add_option("-f", "--secret-file", dest="secret_file", help="Your secret to hide (Text or any binary file).") # Output for the binary binary secret. parser.add_option("-b", "--binary", dest="secret_binary", help="Output for the binary secret (Text or any binary file).") parser.set_defaults(input_image_file = './pictures/Lenna.png', generator_function = 'fermat', output_image_file = './pictures/Lenna_enc.png', secret_message = '', secret_file = '', secret_binary = "") (options, args) = parser.parse_args() if options.hide: if options.secret_message != "" and options.secret_file == "": secret = options.secret_message elif options.secret_message == "" and options.secret_file != "": secret = tools.binary2base64(options.secret_file) img_encoded = slsbset.hide(options.input_image_file, secret, options.generator_function) try: img_encoded.save(options.output_image_file) except Exception as e: # If hide() returns an error (Too long message). print(e) elif options.reveal: try: secret = slsbset.reveal(options.input_image_file, options.generator_function) except IndexError: print("Impossible to detect message.") exit(0) if options.secret_binary != "": data = tools.base642binary(secret) with open(options.secret_binary, "w") as f: f.write(data) else: print(secret) PKš¦—G%H° ° Stegano-0.4.4.data/scripts/slsb#!/usr/bin/python2.7 #-*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.1 $" __date__ = "$Date: 2011/04/06 $" __license__ = "GPLv3" try: from stegano import slsb except: print("Install Stegano: sudo pip install Stegano") from stegano import tools from optparse import OptionParser parser = OptionParser(version=__version__) parser.add_option('--hide', action='store_true', default=False, help="Hides a message in an image.") parser.add_option('--reveal', action='store_true', default=False, help="Reveals the message hided in an image.") # Original image parser.add_option("-i", "--input", dest="input_image_file", help="Input image file.") # Image containing the secret parser.add_option("-o", "--output", dest="output_image_file", help="Output image containing the secret.") # Non binary secret message to hide parser.add_option("-m", "--secret-message", dest="secret_message", help="Your secret message to hide (non binary).") # Binary secret to hide (OGG, executable, etc.) parser.add_option("-f", "--secret-file", dest="secret_file", help="Your secret to hide (Text or any binary file).") # Output for the binary binary secret. parser.add_option("-b", "--binary", dest="secret_binary", help="Output for the binary secret (Text or any binary file).") parser.set_defaults(input_image_file = './pictures/Lenna.png', output_image_file = './pictures/Lenna_enc.png', secret_message = '', secret_file = '', secret_binary = "") (options, args) = parser.parse_args() if options.hide: if options.secret_message != "" and options.secret_file == "": secret = options.secret_message elif options.secret_message == "" and options.secret_file != "": secret = tools.binary2base64(options.secret_file) img_encoded = slsb.hide(options.input_image_file, secret) try: img_encoded.save(options.output_image_file) except Exception as e: # If hide() returns an error (Too long message). print(e) elif options.reveal: secret = slsb.reveal(options.input_image_file) if options.secret_binary != "": data = tools.base642binary(secret) with open(options.secret_binary, "w") as f: f.write(data) else: print(secret) PKw —Gú»;ÉSSstegano/slsb.py#!/usr/bin/env python #-*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.2 $" __date__ = "$Date: 2010/03/24 $" __license__ = "GPLv3" import sys from PIL import Image from . import tools def hide(input_image_file, message): """ Hide a message (string) in an image with the LSB (Least Significant Bit) technique. """ img = Image.open(input_image_file) encoded = img.copy() width, height = img.size index = 0 message = str(len(message)) + ":" + message #message_bits = tools.a2bits(message) message_bits = "".join(tools.a2bits_list(message)) npixels = width * height if len(message_bits) > npixels * 3: raise Exception("""The message you want to hide is too long (%s > %s).""" % (len(message_bits), npixels * 3)) for row in range(height): for col in range(width): if index + 3 <= len(message_bits) : # Get the colour component. (r, g, b) = img.getpixel((col, row)) # Change the Least Significant Bit of each colour component. r = tools.setlsb(r, message_bits[index]) g = tools.setlsb(g, message_bits[index+1]) b = tools.setlsb(b, message_bits[index+2]) # Save the new pixel encoded.putpixel((col, row), (r, g , b)) index += 3 return encoded def reveal(input_image_file): """ Find a message in an image (with the LSB technique). """ img = Image.open(input_image_file) width, height = img.size buff, count = 0, 0 bitab = [] limit = None for row in range(height): for col in range(width): # color = [r, g, b] for color in img.getpixel((col, row)): buff += (color&1)<<(7-count) count += 1 if count == 8: bitab.append(chr(buff)) buff, count = 0, 0 if bitab[-1] == ":" and limit == None: try: limit = int("".join(bitab[:-1])) except: pass if len(bitab)-len(str(limit))-1 == limit : return "".join(bitab)[len(str(limit))+1:] return "" def write(image, output_image_file): """ """ try: image.save(output_image_file) except Exception as e: # If hide() returns an error (Too long message). print(e) if __name__ == '__main__': # Point of entry in execution mode. from optparse import OptionParser parser = OptionParser(version=__version__) parser.add_option('--hide', action='store_true', default=False, help="Hides a message in an image.") parser.add_option('--reveal', action='store_true', default=False, help="Reveals the message hided in an image.") # Original image parser.add_option("-i", "--input", dest="input_image_file", help="Input image file.") # Image containing the secret parser.add_option("-o", "--output", dest="output_image_file", help="Output image containing the secret.") # Non binary secret message to hide parser.add_option("-m", "--secret-message", dest="secret_message", help="Your secret message to hide (non binary).") # Binary secret to hide (OGG, executable, etc.) parser.add_option("-f", "--secret-file", dest="secret_file", help="Your secret to hide (Text or any binary file).") # Output for the binary binary secret. parser.add_option("-b", "--binary", dest="secret_binary", help="Output for the binary secret (Text or any binary file).") parser.set_defaults(input_image_file = './pictures/Lenna.png', output_image_file = './pictures/Lenna_enc.png', secret_message = '', secret_file = '', secret_binary = "") (options, args) = parser.parse_args() if options.hide: if options.secret_message != "" and options.secret_file == "": secret = options.secret_message elif options.secret_message == "" and options.secret_file != "": secret = tools.binary2base64(options.secret_file) img_encoded = hide(options.input_image_file, secret) try: img_encoded.save(options.output_image_file) except Exception as e: # If hide() returns an error (Too long message). print(e) elif options.reveal: secret = reveal(options.input_image_file) if options.secret_binary != "": data = tools.base642binary(secret) with open(options.secret_binary, "w") as f: f.write(data) else: print(secret) PK€ —G¼t€€stegano/basic.py#!/usr/bin/env python # -*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.1 $" __date__ = "$Date: 2010/10/01 $" __license__ = "GPLv3" import sys from PIL import Image def hide(img, message): """ Hide a message (string) in an image. Use the red portion of a pixel (r, g, b) tuple to hide the message string characters as ASCII values. The red value of the first pixel is used for length of string. """ length = len(message) # Limit length of message to 255 if length > 255: return False # Use a copy of image to hide the text in encoded = img.copy() width, height = img.size index = 0 for row in range(height): for col in range(width): (r, g, b) = img.getpixel((col, row)) # first value is length of message if row == 0 and col == 0 and index < length: asc = length elif index <= length: c = message[index -1] asc = ord(c) else: asc = r encoded.putpixel((col, row), (asc, g , b)) index += 1 return encoded def reveal(img): """ Find a message in an image. Check the red portion of an pixel (r, g, b) tuple for hidden message characters (ASCII values). The red value of the first pixel is used for length of string. """ width, height = img.size message = "" index = 0 for row in range(height): for col in range(width): r, g, b = img.getpixel((col, row)) # First pixel r value is length of message if row == 0 and col == 0: length = r elif index <= length: message += chr(r) index += 1 return message if __name__ == '__main__': # Point of entry in execution mode. from optparse import OptionParser usage = "usage: %prog hide|reveal [options]" parser = OptionParser(usage) parser.add_option("-i", "--input", dest="input_image_file", help="Image file.") parser.add_option("-o", "--output", dest="output_image_file", help="Image file.") parser.add_option("-s", "--secret", dest="secret", help="Your secret (Message, Image, Music or any binary file).") parser.set_defaults(input_image_file = './pictures/Lenna.png', output_image_file = './pictures/Lenna_enc.png', secret = 'Hello World!') (options, args) = parser.parse_args() if sys.argv[1] == "hide": img = Image.open(options.input_image_file) img_encoded = hide(img, options.secret) img_encoded.save(options.output_image_file) elif sys.argv[1] == "reveal": img = Image.open(options.input_image_file) print(reveal(img)) PK„ —G9¬Gý ý stegano/generators.py#!/usr/bin/env python # -*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.2 $" __date__ = "$Date: 2011/12/28 $" __revision__ = "$Date: 2012/12/14 $" __license__ = "GPLv3" import math import itertools def identity(): """ f(x) = x """ n = 0 while True: yield n n += 1 def Dead_Man_Walking(): n = 0 while True: yield n + 7 n += 2 def OEIS_A000217(): """ http://oeis.org/A000217 Triangular numbers: a(n) = C(n+1,2) = n(n+1)/2 = 0+1+2+...+n. """ n = 0 while True: yield (n*(n+1))//2 n += 1 def fermat(): """ Generate the n-th Fermat Number. """ y = 5 while True: yield y y = pow(y-1,2)+1 def mersenne(): """ Generate 2^n-1. """ y = 1 while True: yield y y = 2*y + 1 def eratosthenes(): """ Generate the prime numbers with the sieve of Eratosthenes. """ d = {} for i in itertools.count(2): if i in d: for j in d[i]: d[i + j] = d.get(i + j, []) + [j] del d[i] else: d[i * i] = [i] yield i def eratosthenes_composite(): """ Generate the composite numbers with the sieve of Eratosthenes. """ p1 = 3 for p2 in eratosthenes(): for n in range(p1 + 1, p2): yield n p1 = p2 def carmichael(): for m in eratosthenes_composite(): for a in range(2, m): if pow(a,m,m) != a: break else: yield m def ackermann(m, n): """ Ackermann number. """ if m == 0: return n + 1 elif n == 0: return ackermann(m - 1, 1) else: return ackermann(m - 1, ackermann(m, n - 1)) def fibonacci(): """ A generator for Fibonacci numbers, goes to next number in series on each call. This generator start at 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, ... See: http://oeis.org/A000045 """ a, b = 1, 2 while True: yield a a, b = b, a + b def syracuse(l=15): """ Generate the sequence of Syracuse. """ y = l while True: yield y q,r = divmod(y,2) if r == 0: y = q else: y = 3*y + 1 def log_gen(): """ Logarithmic generator. """ y = 1 while True: adder = max(1, math.pow(10, int(math.log10(y)))) yield int(y) y = y + adder if __name__ == "__main__": # Point of entry in execution mode. f = fibonacci() for x in range(13): print(next(f), end=' ') # 0 1 1 2 3 5 8 13 21 34 55 89 144 PK‚ —GXÆ}®ëëstegano/exifHeader.py#!/usr/bin/env python #-*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.1 $" __date__ = "$Date: 2010/03/24 $" __license__ = "GPLv3" # Thanks to: http://www.julesberman.info/spec2img.htm def hide(img, img_enc, copyright="http://bitbucket.org/cedricbonhomme/stegano", \ secret_message = None, secret_file = None): """ """ import shutil import datetime from zlib import compress from zlib import decompress from base64 import b64encode from .exif.minimal_exif_writer import MinimalExifWriter if secret_file != None: with open(secret_file, "r") as f: secret_file_content = f.read() text = "\nImage annotation date: " text = text + str(datetime.date.today()) text = text + "\nImage description:\n" if secret_file != None: text = compress(b64encode(text + secret_file_content)) else: text = compress(b64encode(text + secret_message)) try: shutil.copy(img, img_enc) except Exception as e: print(("Impossible to copy image:", e)) return f = MinimalExifWriter(img_enc) f.removeExif() f.newImageDescription(text) f.newCopyright(copyright, addYear = 1) f.process() def reveal(img): """ """ from base64 import b64decode from zlib import decompress from .exif.minimal_exif_reader import MinimalExifReader try: g = MinimalExifReader(img) except: print("Impossible to read description.") return print((b64decode(decompress(g.imageDescription())))) print(("\nCopyright " + g.copyright())) #print g.dateTimeOriginal()s if __name__ == "__main__": # Point of entry in execution mode. from optparse import OptionParser parser = OptionParser(version=__version__) parser.add_option('--hide', action='store_true', default=False, help="Hides a message in an image.") parser.add_option('--reveal', action='store_true', default=False, help="Reveals the message hided in an image.") # Original image parser.add_option("-i", "--input", dest="input_image_file", help="Input image file.") # Image containing the secret parser.add_option("-o", "--output", dest="output_image_file", help="Output image containing the secret.") # Secret raw message to hide parser.add_option("-m", "--secret-message", dest="secret_message", help="Your raw secret message to hide.") # Secret text file to hide. parser.add_option("-f", "--secret-file", dest="secret_file", help="Your secret textt file to hide.") parser.set_defaults(input_image_file = './pictures/Elisha-Cuthbert.jpg', output_image_file = './pictures/Elisha-Cuthbert_enc.jpg', secret_message = '', secret_file = '') (options, args) = parser.parse_args() if options.hide: if options.secret_message != "" and options.secret_file == "": hide(img=options.input_image_file, img_enc=options.output_image_file, \ secret_message=options.secret_message) elif options.secret_message == "" and options.secret_file != "": hide(img=options.input_image_file, img_enc=options.output_image_file, \ secret_file=options.secret_file) elif options.reveal: reveal(img=options.input_image_file) PK“ —GZûê—È È stegano/tools.py#!/usr/bin/env python # -*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.1 $" __date__ = "$Date: 2010/10/01 $" __license__ = "GPLv3" import base64 from functools import reduce def a2bits(chars): """ Converts a string to its bits representation as a string of 0's and 1's. >>> a2bits("Hello World!") '010010000110010101101100011011000110111100100000010101110110111101110010011011000110010000100001' """ return bin(reduce(lambda x, y : (x<<8)+y, (ord(c) for c in chars), 1))[3:] def a2bits_list(chars): """ Convert a string to its bits representation as a list of 0's and 1's. >>> a2bits_list("Hello World!") ['01001000', '01100101', '01101100', '01101100', '01101111', '00100000', '01010111', '01101111', '01110010', '01101100', '01100100', '00100001'] >>> "".join(a2bits_list("Hello World!")) '010010000110010101101100011011000110111100100000010101110110111101110010011011000110010000100001' """ return [bin(ord(x))[2:].rjust(8,"0") for x in chars] def bs(s): """ Converts an int to its bits representation as a string of 0's and 1's. """ return str(s) if s<=1 else bs(s>>1) + str(s&1) def setlsb(component, bit): """ Set Least Significant Bit of a colour component. """ return component & ~1 | int(bit) def n_at_a_time(items, n, fillvalue): """ Returns an iterator which groups n items at a time. Any final partial tuple will be padded with the fillvalue >>> list(n_at_a_time([1, 2, 3, 4, 5], 2, 'X')) [(1, 2), (3, 4), (5, 'X')] """ it = iter(items) return its.izip_longest(*[it] * n, fillvalue=fillvalue) def binary2base64(binary_file): """ Convert a binary file (OGG, executable, etc.) to a printable string. """ # Use mode = "rb" to read binary file fin = open(binary_file, "rb") binary_data = fin.read() fin.close() # Encode binary to base64 string (printable) return base64.b64encode(binary_data) """fout = open(output_file, "w") fout.write(b64_data) fout.close""" def base642binary(b64_fname): """ Convert a printable file to a binary file. """ # Read base64 string #fin = open(b64_fname, "r") #b64_str = fin.read() #fin.close() # Decode base64 string to original binary sound object return base64.b64decode(b64_fname) PKÈa—GC߈4stegano/__init__.py PKz —G?^°C  stegano/steganalysisParity.py#!/usr/bin/env python #-*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.1 $" __date__ = "$Date: 2010/10/01 $" __license__ = "GPLv3" from PIL import Image def steganalyse(img): """ Steganlysis of the LSB technique. """ encoded = img.copy() width, height = img.size bits = "" for row in range(height): for col in range(width): r, g, b = img.getpixel((col, row)) if r % 2 == 0: r = 0 else: r = 255 if g % 2 == 0: g = 0 else: g = 255 if b % 2 == 0: b = 0 else: b = 255 encoded.putpixel((col, row), (r, g , b)) return encoded if __name__ == '__main__': # Point of entry in execution mode. from optparse import OptionParser parser = OptionParser() parser.add_option("-i", "--input", dest="input_image_file", help="Image file") parser.add_option("-o", "--output", dest="output_image_file", help="Image file") parser.set_defaults(input_image_file = './pictures/Lenna.png', output_image_file = './pictures/Lenna_steganalysed.png') (options, args) = parser.parse_args() input_image_file = Image.open(options.input_image_file) output_image = steganalyse(input_image_file) output_image.save(options.output_image_file) PK| —G!Tô• • !stegano/steganalysisStatistics.py#!/usr/bin/env python #-*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.1 $" __date__ = "$Date: 2010/10/01 $" __license__ = "GPLv3" import operator from PIL import Image from collections import Counter from collections import OrderedDict def steganalyse(img): """ Steganlysis of the LSB technique. """ encoded = img.copy() width, height = img.size colours = Counter() for row in range(height): for col in range(width): r, g, b = img.getpixel((col, row)) colours[r] += 1 most_common = colours.most_common(10) dict_colours = OrderedDict(sorted(list(colours.items()), key=lambda t: t[1])) colours = 0 for colour in list(dict_colours.keys()): colours += colour colours = colours / len(dict_colours) #return colours.most_common(10) return list(dict_colours.keys())[:30], most_common if __name__ == '__main__': # Point of entry in execution mode. from optparse import OptionParser parser = OptionParser() parser.add_option("-i", "--input", dest="input_image_file", help="Image file.") parser.add_option("-o", "--output", dest="output_image_file", help="Image file.") parser.set_defaults(input_image_file = './pictures/Lenna.png', output_image_file = './pictures/Lenna_steganalysed.png') (options, args) = parser.parse_args() input_image_file = Image.open(options.input_image_file) output_image = steganalyse(input_image_file) soutput_image.save(options.output_image_file) PKx —G£žM‚‚stegano/slsbset.py#!/usr/bin/env python #-*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2015 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.4 $" __date__ = "$Date: 2011/12/28 $" __license__ = "GPLv3" import sys from PIL import Image from . import tools from . import generators def hide(input_image_file, message, generator_function): """ Hide a message (string) in an image with the LSB (Least Significant Bit) technique. """ img = Image.open(input_image_file) img_list = list(img.getdata()) width, height = img.size index = 0 message = str(len(message)) + ":" + message #message_bits = tools.a2bits(message) message_bits = "".join(tools.a2bits_list(message)) npixels = width * height if len(message_bits) > npixels * 3: raise Exception("""The message you want to hide is too long (%s > %s).""" % (len(message_bits), npixels * 3)) generator = getattr(generators, generator_function)() while index + 3 <= len(message_bits) : generated_number = next(generator) (r, g, b) = img_list[generated_number] # Change the Least Significant Bit of each colour component. r = tools.setlsb(r, message_bits[index]) g = tools.setlsb(g, message_bits[index+1]) b = tools.setlsb(b, message_bits[index+2]) # Save the new pixel img_list[generated_number] = (r, g , b) index += 3 # create empty new image of appropriate format encoded = Image.new('RGB', (img.size)) # insert saved data into the image encoded.putdata(img_list) return encoded def reveal(input_image_file, generator_function): """ Find a message in an image (with the LSB technique). """ img = Image.open(input_image_file) img_list = list(img.getdata()) width, height = img.size buff, count = 0, 0 bitab = [] limit = None generator = getattr(generators, generator_function)() while True: generated_number = next(generator) # color = [r, g, b] for color in img_list[generated_number]: buff += (color&1)<<(7-count) count += 1 if count == 8: bitab.append(chr(buff)) buff, count = 0, 0 if bitab[-1] == ":" and limit == None: try: limit = int("".join(bitab[:-1])) except: pass if len(bitab)-len(str(limit))-1 == limit : return "".join(bitab)[len(str(limit))+1:] return "" def write(image, output_image_file): """ """ try: image.save(output_image_file) except Exception as e: # If hide() returns an error (Too long message). print(e) if __name__ == '__main__': # Point of entry in execution mode. from optparse import OptionParser parser = OptionParser(version=__version__) parser.add_option('--hide', action='store_true', default=False, help="Hides a message in an image.") parser.add_option('--reveal', action='store_true', default=False, help="Reveals the message hided in an image.") # Original image parser.add_option("-i", "--input", dest="input_image_file", help="Input image file.") # Generator parser.add_option("-g", "--generator", dest="generator_function", help="Generator") # Image containing the secret parser.add_option("-o", "--output", dest="output_image_file", help="Output image containing the secret.") # Non binary secret message to hide parser.add_option("-m", "--secret-message", dest="secret_message", help="Your secret message to hide (non binary).") # Binary secret to hide (OGG, executable, etc.) parser.add_option("-f", "--secret-file", dest="secret_file", help="Your secret to hide (Text or any binary file).") # Output for the binary binary secret. parser.add_option("-b", "--binary", dest="secret_binary", help="Output for the binary secret (Text or any binary file).") parser.set_defaults(input_image_file = './pictures/Lenna.png', generator_function = 'fermat', output_image_file = './pictures/Lenna_enc.png', secret_message = '', secret_file = '', secret_binary = "") (options, args) = parser.parse_args() if options.hide: if options.secret_message != "" and options.secret_file == "": secret = options.secret_message elif options.secret_message == "" and options.secret_file != "": secret = tools.binary2base64(options.secret_file) img_encoded = hide(options.input_image_file, secret, options.generator_function) try: img_encoded.save(options.output_image_file) except Exception as e: # If hide() returns an error (Too long message). print(e) elif options.reveal: try: secret = reveal(options.input_image_file, options.generator_function) except IndexError: print("Impossible to detect message.") exit(0) if options.secret_binary != "": data = tools.base642binary(secret) with open(options.secret_binary, "w") as f: f.write(data) else: print(secret) PKÈa—G¹ÕÆ®A®A#stegano/exif/minimal_exif_writer.py""" Offers one class, MinimalExifWriter, which takes a jpg filename in the constructor. Allows you to: remove exif section, add image description, add copyright. Typical usage: f = MinimalExifWriter('xyz.jpg') f.newImageDescription('This is a photo of something very interesting!') f.newCopyright('Jose Blow, All Rights Reserved', addCopyrightYear = 1) f.process() Class methods: newImageDescription(description)--will add Exif ImageDescription to file. newCopyright(copyright, addSymbol = 0, addYear = 0)--will add Exif Copyright to file. Will optionally prepend copyright symbol, or copyright symbol and current year. removeExif()--will obliterate existing exif section. process()--call after calling one or more of the above. Will remove existing exif section, optionally saving some existing tags (see below), and insert a new exif section with only three tags at most: description, copyright and date time original. If removeExif() not called, existing description (or new description if newDescription() called), existing copyright (or new copyright if newCopyright() called) and existing "DateTimeOriginal" (date/time picture taken) tags will be rewritten to the new minimal exif section. Run at comand line with no args to see command line usage. Does not work on unix due to differences in mmap. Not sure what's up there-- don't need it on unix! Brought to you by Megabyte Rodeo Software. http://www.fetidcascade.com/pyexif.html """ # Written by Chris Stromberger, 10/2004. Public Domain. # Last updated: 12/3/2004. DUMP_TIFF = 0 VERBOSE = 0 if VERBOSE: import binascii import mmap import sys from . import minimal_exif_reader #--------------------------------------------------------------------- class ExifFormatException(Exception): pass #--------------------------------------------------------------------------- class MinimalExifWriter: SOI_MARKER = '\xff\xd8' APP0_MARKER = '\xff\xe0' APP1_MARKER = '\xff\xe1' # Standard app0 segment that will work for all files. We hope. # Based on http://www.funducode.com/freec/Fileformats/format3/format3b.htm. APP0 = '\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00' def __init__(self, filename): self.filename = filename self.removeExifSection = 0 self.description = None self.copyright = None self.dateTimeOriginal = None #--------------------------------------------- def newImageDescription(self, description): self.description = description #--------------------------------------------- def newCopyright(self, copyright, addSymbol = 0, addYear = 0): if addYear: import time year = time.localtime()[0] self.copyright = "\xa9 %s %s" % (year, copyright) elif addSymbol: self.copyright = "\xa9 %s" % copyright else: self.copyright = copyright #--------------------------------------------- def removeExif(self): self.removeExifSection = 1 #--------------------------------------------- def process(self): if not self.removeExifSection: self.getExistingExifInfo() if VERBOSE: print(self) import os try: fd = os.open(self.filename, os.O_RDWR) except: sys.stderr.write('Unable to open "%s"\n' % filename) return self.m = mmap.mmap(fd, 0) os.close(fd) # We only add app0 if all we're doing is removing the exif section. justRemovingExif = self.description is None and self.copyright is None and self.removeExifSection if VERBOSE: print('justRemovingExif=%s' % justRemovingExif) self.removeExifInfo(addApp0 = justRemovingExif) if justRemovingExif: self.m.close() return # Get here means we are adding new description and/or copyright. self.removeApp0() totalTagsToBeAdded = len([_f for _f in (self.description, self.copyright, self.dateTimeOriginal) if _f]) assert(totalTagsToBeAdded > 0) # Layout will be: firstifd|description|copyright|exififd|datetime. # First ifd will have tags: desc|copyright|subifd tag. ifd = [self.twoBytesHexIntel(totalTagsToBeAdded)] ifdEnd = ['\x00\x00\x00\x00'] NUM_TAGS_LEN = 2 TAG_LEN = 12 NEXT_IFD_OFFSET_LEN = 4 TIFF_HEADER_LENGTH = 8 ifdLength = NUM_TAGS_LEN + TAG_LEN * totalTagsToBeAdded + NEXT_IFD_OFFSET_LEN # Subifd only has one tag. SUBIFD_LENGTH = NUM_TAGS_LEN + TAG_LEN + NEXT_IFD_OFFSET_LEN offsetToEndOfData = ifdLength + TIFF_HEADER_LENGTH if self.description: ifd.append(self.descriptionTag(len(self.description), offsetToEndOfData)) ifdEnd.append(self.description) offsetToEndOfData += len(self.description) if self.copyright: ifd.append(self.copyrightTag(len(self.copyright), offsetToEndOfData)) ifdEnd.append(self.copyright) offsetToEndOfData += len(self.copyright) if self.dateTimeOriginal: ifd.append(self.subIfdTag(offsetToEndOfData)) offsetToEndOfData += SUBIFD_LENGTH ifdEnd.append(self.buildSubIfd(len(self.dateTimeOriginal), offsetToEndOfData)) ifdEnd.append(self.dateTimeOriginal) app1 = self.buildApp1Section(ifd, ifdEnd) self.addApp1(app1) self.m.close() #--------------------------------------------- # Build exif subifd with one tag for datetime (0x9003). # Type is ascii (0x0002). def buildSubIfd(self, lenDateTime, offsetToEndOfData): return '\x01\x00\x03\x90\x02\x00%s%s\x00\x00\x00\x00' % (self.fourBytesHexIntel(lenDateTime), self.fourBytesHexIntel(offsetToEndOfData)) #--------------------------------------------- def getExistingExifInfo(self): # Save off the old stuff. try: f = minimal_exif_reader.MinimalExifReader(self.filename) except: # Assume no existing exif info in the file. We # don't care. return if not self.description: self.description = f.imageDescription() if not self.copyright: self.copyright = f.copyright() self.dateTimeOriginal = f.dateTimeOriginal() if self.dateTimeOriginal: # Restore ending nul. if self.dateTimeOriginal[-1] != '\x00': self.dateTimeOriginal += '\x00' #--------------------------------------------------------------------------- def removeExifInfo(self, addApp0 = 1): """Remove the app1 section of the jpg. This removes all exif info and the exif thumbnail. addApp0 should be 1 to add a minimal app0 section right after soi to make it a legitimate jpg, I think (various image programs can read the file without app0, but I think the standard requires one). """ # Read first bit of file to see if exif file. self.m.seek(0) if self.m.read(2) != self.SOI_MARKER: self.m.close() raise ExifFormatException("Missing SOI marker") app0DataLength = 0 appMarker = self.m.read(2) # See if there's an APP0 section, which sometimes appears. if appMarker == self.APP0_MARKER: if VERBOSE: print('app0 found') app0DataLength = ord(self.m.read(1)) * 256 + ord(self.m.read(1)) if VERBOSE: print('app0DataLength: %s' % app0DataLength) # Back up 2 bytes to get the length bytes. self.m.seek(-2, 1) existingApp0 = self.m.read(app0DataLength) appMarker = self.m.read(2) if appMarker != self.APP1_MARKER: # We don't care, we'll add our minimal app1 later. return exifHeader = self.m.read(8) if VERBOSE: print('exif header: %s' % binascii.hexlify(exifHeader)) if (exifHeader[2:6] != 'Exif' or exifHeader[6:8] != '\x00\x00'): self.m.close() raise ExifFormatException("Malformed APP1") app1Length = ord(exifHeader[0]) * 256 + ord(exifHeader[1]) if VERBOSE: print('app1Length: %s' % app1Length) originalFileSize = self.m.size() # Shift stuff just past app1 to overwrite app1. # Start at app1 length bytes in + other bytes not incl in app1 length. src = app1Length + len(self.SOI_MARKER) + len(self.APP1_MARKER) if app0DataLength: src += app0DataLength + len(self.APP0_MARKER) dest = len(self.SOI_MARKER) if addApp0: if app0DataLength != 0: # We'll re-add the existing app0. dest += app0DataLength + len(self.APP0_MARKER) else: # Add our generic app0. dest += len(self.APP0) count = originalFileSize - app1Length - len(self.SOI_MARKER) - len(self.APP1_MARKER) if app0DataLength: count -= app0DataLength + len(self.APP0_MARKER) if VERBOSE: print('self.m.move(%s, %s, %s)' % (dest, src, count)) self.m.move(dest, src, count) if addApp0: if app0DataLength != 0: self.m.resize(originalFileSize - app1Length - len(self.APP1_MARKER)) else: self.m.seek(len(self.SOI_MARKER)) self.m.write(self.APP0) self.m.resize(originalFileSize - app1Length - len(self.APP1_MARKER) + len(self.APP0)) else: self.m.resize(originalFileSize - app1Length - len(self.APP1_MARKER)) #--------------------------------------------------------------------------- def removeApp0(self): self.m.seek(0) header = self.m.read(6) if (header[0:2] != self.SOI_MARKER or header[2:4] != self.APP0_MARKER): if VERBOSE: print('no app0 found: %s' % binascii.hexlify(header)) return originalFileSize = self.m.size() app0Length = ord(header[4]) * 256 + ord(header[5]) if VERBOSE: print('app0Length:', app0Length) # Shift stuff to overwrite app0. # Start at app0 length bytes in + other bytes not incl in app0 length. src = app0Length + len(self.SOI_MARKER) + len(self.APP0_MARKER) dest = len(self.SOI_MARKER) count = originalFileSize - app0Length - len(self.SOI_MARKER) - len(self.APP0_MARKER) self.m.move(dest, src, count) if VERBOSE: print('m.move(%s, %s, %s)' % (dest, src, count)) self.m.resize(originalFileSize - app0Length - len(self.APP0_MARKER)) #--------------------------------------------------------------------------- def addApp1(self, app1): originalFileSize = self.m.size() # Insert app1 section. self.m.resize(originalFileSize + len(app1)) src = len(self.SOI_MARKER) dest = len(app1) + len(self.SOI_MARKER) count = originalFileSize - len(self.SOI_MARKER) self.m.move(dest, src, count) self.m.seek(len(self.SOI_MARKER)) self.m.write(app1) #--------------------------------------------------------------------------- def fourBytesHexIntel(self, number): return '%s%s%s%s' % (chr(number & 0x000000ff), chr((number >> 8) & 0x000000ff), chr((number >> 16) & 0x000000ff), chr((number >> 24) & 0x000000ff)) #--------------------------------------------------------------------------- def twoBytesHexIntel(self, number): return '%s%s' % (chr(number & 0x00ff), chr((number >> 8) & 0x00ff)) #--------------------------------------------------------------------------- def descriptionTag(self, numChars, loc): return self.asciiTag('\x0e\x01', numChars, loc) #--------------------------------------------------------------------------- def copyrightTag(self, numChars, loc): return self.asciiTag('\x98\x82', numChars, loc) #--------------------------------------------------------------------------- def subIfdTag(self, loc): return '\x69\x87\x04\x00\x01\x00\x00\x00%s' % self.fourBytesHexIntel(loc) #--------------------------------------------------------------------------- def asciiTag(self, tag, numChars, loc): """Create ascii tag. Assumes description > 4 chars long.""" return '%s\x02\x00%s%s' % (tag, self.fourBytesHexIntel(numChars), self.fourBytesHexIntel(loc)) #--------------------------------------------------------------------------- def buildApp1Section(self, ifdPieces, ifdEndPieces): """Create the APP1 section of an exif jpg. Consists of exif header plus tiff header + ifd and associated data.""" # Intel byte order, offset to first ifd will be 8. tiff = 'II\x2a\x00\x08\x00\x00\x00%s%s' % (''.join(ifdPieces), ''.join(ifdEndPieces)) if DUMP_TIFF: f = open('tiff.dump', 'wb') f.write(tiff) f.close() app1Length = len(tiff) + 8 return '\xff\xe1%s%sExif\x00\x00%s' % (chr((app1Length >> 8) & 0x00ff), chr(app1Length & 0x00ff), tiff) #--------------------------------------------------------------------------- def __str__(self): return """filename: %(filename)s removeExifSection: %(removeExifSection)s description: %(description)s copyright: %(copyright)s dateTimeOriginal: %(dateTimeOriginal)s """ % self.__dict__ #--------------------------------------------------------------------------- def usage(error = None): """Print command line usage and exit""" if error: print(error) print() print("""This program will remove exif info from an exif jpg, and can optionally add the ImageDescription exif tag and/or the Copyright tag. But it will always remove some or all existing exif info (depending on options--see below)! So don't run this on your original images without a backup. Options: -h: shows this message. -f : jpg to process (required). -x: remove exif info (including thumbnail). -d : remove exif info (including thumbnail) and then add exif ImageDescription. Will save the existing copyright tag if present, as well as the date time original tag (date & time photo taken), unless -x also passed (-x always means remove all exif info). It will attempt to open whatever is passed on the command line as a file; if successful, the contents of the file are added as the description, else the literal text on the command line is used as the description. -c : remove exif info (including thumbnail) and then add exif Copyright tag. Will save the existing image description tag if present, as well as the date time original tag (date & time photo taken), unless -x also passed (-x always means remove all exif info). It will attempt to open whatever is passed on the command line as a file; if successful, the contents of the file are added as the copyright, else the literal text on the command line is used as the copyright. -s: prepend copyright symbol to copyright. -y: prepend copyright symbol and current year to copyright. The image description and copyright must be > 4 characters long. This software courtesy of Megabyte Rodeo Software.""") sys.exit(1) #--------------------------------------------------------------------------- def parseArgs(args_): import getopt try: opts, args = getopt.getopt(args_, "yshxd:f:c:") except getopt.GetoptError: usage() filename = None description = '' copyright = '' addCopyrightSymbol = 0 addCopyrightYear = 0 removeExif = 0 for o, a in opts: if o == "-h": usage() if o == "-f": filename = a if o == "-d": try: f = open(a) description = f.read() f.close() except: description = a if o == "-c": try: f = open(a) copyright = f.read() f.close() except: copyright = a if o == '-x': removeExif = 1 if o == '-s': addCopyrightSymbol = 1 if o == '-y': addCopyrightYear = 1 if filename is None: usage('Missing jpg filename') if description and (len(description) <= 4 or len(description) > 60000): usage('Description too short or too long') if copyright and (len(copyright) <= 4 or len(copyright) > 60000): usage('Copyright too short or too long') if not description and not copyright and not removeExif: usage('Nothing to do!') return filename, description, copyright, removeExif, addCopyrightSymbol, addCopyrightYear #--------------------------------------------------------------------------- if __name__ == '__main__': try: filename, description, copyright, removeExif, addCopyrightSymbol, addCopyrightYear = parseArgs(sys.argv[1:]) f = MinimalExifWriter(filename) if description: f.newImageDescription(description) if copyright: f.newCopyright(copyright, addCopyrightSymbol, addCopyrightYear) if removeExif: f.removeExif() f.process() except ExifFormatException as ex: sys.stderr.write("Exif format error: %s\n" % ex) except SystemExit: pass except: sys.stderr.write("Unable to process %s\n" % filename) raise PKÈa—GbFœññ#stegano/exif/minimal_exif_reader.py""" This module offers one class, MinimalExifReader. Pass jpg filename to the constructor. Will read minimal exif info from the file. Three "public" functions available: imageDescription()--returns Exif ImageDescription tag (0x010e) contents, or '' if not found. copyright()--returns Exif copyright tag (0x8298) contents, or '' if not found. dateTimeOriginal()--returns Exif DateTimeOriginal tag (0x9003) contents, or '' if not found. If found, the trailing nul char is stripped. This function also takes an optional format string to apply time.strftime-style formatting to the date time. Brought to you by Megabyte Rodeo Software. """ # Written by Chris Stromberger, 10/2004. Public Domain. # Much is owed to Thierry Bousch's exifdump.py: # http://topo.math.u-psud.fr/~bousch/exifdump.py #--------------------------------------------------------------------- class ExifFormatException(Exception): pass #--------------------------------------------------------------------- class MinimalExifReader: IMAGE_DESCRIPTION_TAG = 0x010e COPYRIGHT_TAG = 0x8298 EXIF_SUBIFD_TAG = 0x8769 DATE_TIME_ORIGINAL_TAG = 0x9003 #--------------------------------------- def __init__(self, filename): """Pass in jpg exif file name to process. Will attempt to find tags of interest.""" self.tagsToFind = {self.IMAGE_DESCRIPTION_TAG:'', self.COPYRIGHT_TAG:'', self.DATE_TIME_ORIGINAL_TAG:''} # Read first bit of file to see if exif file. f = open(filename, 'rb') firstTwoBytes = f.read(2) if firstTwoBytes != '\xff\xd8': f.close() raise ExifFormatException("Missing SOI marker") appMarker = f.read(2) # See if there's an APP0 section, which sometimes appears. if appMarker == '\xff\xe0': #print "Skipping app0" # Yes, we have app0. Skip over it. app0DataLength = ord(f.read(1)) * 256 + ord(f.read(1)) app0 = f.read(app0DataLength - 2) appMarker = f.read(2) if appMarker != '\xff\xe1': raise ExifFormatException("Can't find APP1 marker") exifHeader = f.read(8) #import binascii #print binascii.hexlify(exifHeader) if (exifHeader[2:6] != 'Exif' or exifHeader[6:8] != '\x00\x00'): f.close() raise ExifFormatException("Malformed APP1") app1DataLength = ord(exifHeader[0]) * 256 + ord(exifHeader[1]) #print app1DataLength # Read exif info starting at the beginning of the self.tiff section. # This is 8 bytes into the app1 section, so subtract 8 from # app1 length. self.tiff = f.read(app1DataLength - 8) f.close() self.endian = self.tiff[0] if self.endian not in ('I', 'M'): raise ExifFormatException("Invalid endianess found: %s" % self.endian) # Now navigate to the items of interest and get them. ifdStart = self.getValueAtLocation(4, 4) self.ifdSearch(ifdStart) #--------------------------------------- def imageDescription(self): """Return image description tag contents or '' if not found.""" return self.tagsToFind[self.IMAGE_DESCRIPTION_TAG].strip('\x20\x00') #--------------------------------------- def copyright(self): """Return copyright tag contents or '' if not found.""" return self.tagsToFind[self.COPYRIGHT_TAG].strip('\x20\x00') #--------------------------------------- def dateTimeOriginal(self, formatString = None): """Pass in optional format string to get time.strftime style formatting, else get default exif format for date time string (without trailing nul). Returns '' if tag not found.""" # The datetime should end in nul, get rid of it. if formatString is None or not self.tagsToFind[self.DATE_TIME_ORIGINAL_TAG]: return self.tagsToFind[self.DATE_TIME_ORIGINAL_TAG].strip('\x20\x00') else: # This will only work if the datetime string is in the standard exif format (i.e., hasn't been altered). try: import time return time.strftime(formatString, time.strptime(self.tagsToFind[self.DATE_TIME_ORIGINAL_TAG].strip('\x20\x00'), '%Y:%m:%d %H:%M:%S')) except: return self.tagsToFind[self.DATE_TIME_ORIGINAL_TAG].strip('\x20\x00') #--------------------------------------- def ifdSearch(self, ifdStart): numIfdEntries = self.getValueAtLocation(ifdStart, 2) tagsStart = ifdStart + 2 for entryNum in range(numIfdEntries): # For my purposes, all files will have either no tags, or # only our tags of interest, so no need to waste time trying to # break out of the loop early. thisTagStart = tagsStart + 12 * entryNum tagId = self.getValueAtLocation(thisTagStart, 2) if tagId == self.EXIF_SUBIFD_TAG: # This is a special tag that points to another ifd. Our # date time original tag is in the sub ifd. self.ifdSearch(self.getTagValue(thisTagStart)) elif tagId in self.tagsToFind: assert(not self.tagsToFind[tagId]) self.tagsToFind[tagId] = self.getTagValue(thisTagStart) #--------------------------------------- def getValueAtLocation(self, offset, length): slice = self.tiff[offset:offset + length] if self.endian == 'I': val = self.s2n_intel(slice) else: val = self.s2n_motorola(slice) return val #--------------------------------------- def s2n_motorola(self, str): x = 0 for c in str: x = (x << 8) | ord(c) return x #--------------------------------------- def s2n_intel(self, str): x = 0 y = 0 for c in str: x = x | (ord(c) << y) y = y + 8 return x #--------------------------------------- def getTagValue(self, thisTagStart): datatype = self.getValueAtLocation(thisTagStart + 2, 2) numBytes = [ 1, 1, 2, 4, 8, 1, 1, 2, 4, 8 ] [datatype-1] * self.getValueAtLocation(thisTagStart + 4, 4) if numBytes > 4: offsetToValue = self.getValueAtLocation(thisTagStart + 8, 4) return self.tiff[offsetToValue:offsetToValue + numBytes] else: if datatype == 2 or datatype == 1 or datatype == 7: return self.tiff[thisTagStart + 8:thisTagStart + 8 + numBytes] else: return self.getValueAtLocation(thisTagStart + 8, numBytes) #--------------------------------------- def __str__(self): return str(self.tagsToFind) #--------------------------------------------------------------------- if __name__ == '__main__': import sys if len(sys.argv) == 1: print("Pass jpgs to process.") sys.exit(1) for filename in sys.argv[1:]: try: f = MinimalExifReader(filename) print(filename) print("description: '%s'" % f.imageDescription()) print("copyright: '%s'" % f.copyright()) print("dateTimeOriginal: '%s'" % f.dateTimeOriginal()) print("dateTimeOriginal: '%s'" % f.dateTimeOriginal('%B %d, %Y %I:%M:%S %p')) print() except ExifFormatException as ex: sys.stderr.write("Exif format error: %s\n" % ex) except: sys.stderr.write("Unable to process %s\n" % filename) PKÈa—GC߈4stegano/exif/__init__.py PKe§—G¥Ê  'Stegano-0.4.4.dist-info/DESCRIPTION.rstStéganô ======= A Python Steganography module. Installation ------------ .. code:: bash $ sudo pip install Stegano Use Stéganô as a library in your Python program ----------------------------------------------- If you want to use Stéganô in your Python program you just have to import the appropriate steganography technique. For example: .. code:: python >>> from stegano import slsb >>> secret = slsb.hide("./pictures/Lenna.png", "Hello Workd") >>> secret.save("./Lenna-secret.png") Use Stéganô as a program ------------------------ In addition you can use Stéganô as a program. Example: .. code:: bash $ slsb --hide -i ../examples/pictures/Lenna.png -o Lena1.png -m "Secret Message" Another example (hide the message with Sieve of Eratosthenes): .. code:: bash $ slsb-set --hide -i ../examples/pictures/Lenna.png -o Lena2.png --generator eratosthenes -m 'Secret Message' Examples -------- There are some examples in the folder *examples*. .. code:: bash $ git clone https://github.com/cedricbonhomme/Stegano.git $ cd stegano/examples Turorial -------- A `tutorial `_ is available. Contact ------- `My home page `_. Release History =============== 0.4.4 (2015-12-23) ------------------ * new project home page; * minor updated to the documentation. 0.4.3 (2015-10-06) ------------------ * bug fixes for Python 3; * bug fixes in the scripts in *./bin*. 0.4.2 (2015-10-05) ------------------ * first stable release on PypI. 0.4 (2012-01-02) ---------------- This release introduces a more advanced LSB (Least Significant Bit) method based on integers sets. The sets generated with Python generators (Sieve of Eratosthenes, Fermat, Carmichael numbers, etc.) are used to select the pixels used to hide the information. You can use these new methods in your Python codes as a Python module or as a program in your scripts. 0.3 (2011-04-15) ---------------- * you can now use Stéganô as a library in your Python program (python setup.py install) or as a 'program' thanks to the scripts provided in the bin directory; * new documentation (reStructuredText) comes with Stéganô. 0.2 (2011-03-24) ---------------- * this release introduces some bugfixes and a major speed improvement of the *reveal* function for the LSB method. Moreover it is now possible to hide a binary file (ogg, executable, etc.); * a new technique for hiding/revealing a message in a JPEG picture by using the description field of the image is provided. PKe§—G<ã6A))%Stegano-0.4.4.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Environment :: Console", "Topic :: Utilities", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)"], "extensions": {"python.details": {"contacts": [{"email": "cedric@cedricbonhomme.org", "name": "C\u00e9dric Bonhomme", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/cedricbonhomme/Stegano"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "license": "GPLv3", "metadata_version": "2.0", "name": "Stegano", "platform": "Linux", "run_requires": [{"requires": ["pillow"]}], "summary": "A Python Steganography module.", "version": "0.4.4"}PKe§—G¬`’ï%Stegano-0.4.4.dist-info/top_level.txtstegano PKe§—GŒ''\\Stegano-0.4.4.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any PKe§—G”*’q q Stegano-0.4.4.dist-info/METADATAMetadata-Version: 2.0 Name: Stegano Version: 0.4.4 Summary: A Python Steganography module. Home-page: https://github.com/cedricbonhomme/Stegano Author: Cédric Bonhomme Author-email: cedric@cedricbonhomme.org License: GPLv3 Platform: Linux Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Topic :: Utilities Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.4 Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) Requires-Dist: pillow Stéganô ======= A Python Steganography module. Installation ------------ .. code:: bash $ sudo pip install Stegano Use Stéganô as a library in your Python program ----------------------------------------------- If you want to use Stéganô in your Python program you just have to import the appropriate steganography technique. For example: .. code:: python >>> from stegano import slsb >>> secret = slsb.hide("./pictures/Lenna.png", "Hello Workd") >>> secret.save("./Lenna-secret.png") Use Stéganô as a program ------------------------ In addition you can use Stéganô as a program. Example: .. code:: bash $ slsb --hide -i ../examples/pictures/Lenna.png -o Lena1.png -m "Secret Message" Another example (hide the message with Sieve of Eratosthenes): .. code:: bash $ slsb-set --hide -i ../examples/pictures/Lenna.png -o Lena2.png --generator eratosthenes -m 'Secret Message' Examples -------- There are some examples in the folder *examples*. .. code:: bash $ git clone https://github.com/cedricbonhomme/Stegano.git $ cd stegano/examples Turorial -------- A `tutorial `_ is available. Contact ------- `My home page `_. Release History =============== 0.4.4 (2015-12-23) ------------------ * new project home page; * minor updated to the documentation. 0.4.3 (2015-10-06) ------------------ * bug fixes for Python 3; * bug fixes in the scripts in *./bin*. 0.4.2 (2015-10-05) ------------------ * first stable release on PypI. 0.4 (2012-01-02) ---------------- This release introduces a more advanced LSB (Least Significant Bit) method based on integers sets. The sets generated with Python generators (Sieve of Eratosthenes, Fermat, Carmichael numbers, etc.) are used to select the pixels used to hide the information. You can use these new methods in your Python codes as a Python module or as a program in your scripts. 0.3 (2011-04-15) ---------------- * you can now use Stéganô as a library in your Python program (python setup.py install) or as a 'program' thanks to the scripts provided in the bin directory; * new documentation (reStructuredText) comes with Stéganô. 0.2 (2011-03-24) ---------------- * this release introduces some bugfixes and a major speed improvement of the *reveal* function for the LSB method. Moreover it is now possible to hide a binary file (ogg, executable, etc.); * a new technique for hiding/revealing a message in a JPEG picture by using the description field of the image is provided. PKe§—G:ƒ)™××Stegano-0.4.4.dist-info/RECORDStegano-0.4.4.data/scripts/slsb,sha256=PIZHoD4zt9YZoq_pBJW9qzFFgJpAT3MjsFcx8N0oYHE,3248 Stegano-0.4.4.data/scripts/slsb-set,sha256=y9yh2EuNyRDOY150YEvhzDR4c_ChLf0Z70_MOdLmKrQ,3577 Stegano-0.4.4.data/scripts/steganalysis-parity,sha256=sva5EgF3A8gPVKHnLUxlYR21-nDZkAHc3U-MJwhjG28,1740 Stegano-0.4.4.dist-info/DESCRIPTION.rst,sha256=Iep44lz43jomTRB8VQLi3sYjNewgModOlrhDyp-rfRA,2579 Stegano-0.4.4.dist-info/METADATA,sha256=qw2JyacLtDby9feUtsljx70jKx5MYJeOFlKilfqaiZQ,3185 Stegano-0.4.4.dist-info/RECORD,, Stegano-0.4.4.dist-info/WHEEL,sha256=JTb7YztR8fkPg6aSjc571Q4eiVHCwmUDlX8PhuuqIIE,92 Stegano-0.4.4.dist-info/metadata.json,sha256=oBPdoUHumcy6yNZxXTN2CueIGFnuEe7Aih4XLw36v-g,809 Stegano-0.4.4.dist-info/top_level.txt,sha256=U6NoZmssNWYWI98JsnQNcM0u8-SekhZ3W75f67eEyUY,8 stegano/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2 stegano/basic.py,sha256=VfzeiSaW8alJQzkY9l_SBtCOd92ku7iJwS53WQN6Sq8,3712 stegano/exifHeader.py,sha256=tBw0hAlXy-WTGn2oPH_xvvZISrbkPLZHp84gAYT5aRs,4331 stegano/generators.py,sha256=UHPcfM6dxZPGli9denYg2JUodR3k0J-nNbpmuHt81iY,3581 stegano/slsb.py,sha256=jhzjlTut0FkCjZ6iaV9iFEnJDxBNQoO277STdZQfqm4,5715 stegano/slsbset.py,sha256=pEPzqdehBObXsARy6YVWYzQ59OW_CZs1-8cGkosWkBk,6274 stegano/steganalysisParity.py,sha256=Jj4NpOy3Udv3wsgGM_2dY9HjlnWMGrS73fWFpx67V9g,2325 stegano/steganalysisStatistics.py,sha256=YW53SEt-R3KEQgsMYUw9R_HVepzVd_y84pj4Vnb9ELo,2453 stegano/tools.py,sha256=8J4PkOHJ2sHJESHIejliND0WWt7-ai79oStZ3OnetC8,3272 stegano/exif/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2 stegano/exif/minimal_exif_reader.py,sha256=bXGdQB2--Et7XD3tjrKkI3IGV6KQyHZWM8CsS-cMAyk,7153 stegano/exif/minimal_exif_writer.py,sha256=joaogUU5wOxCuADagcTnLmYYVjA_2X5HqP_z8tGS2u4,16814 PKš¦—G$0EÌÌ.Stegano-0.4.4.data/scripts/steganalysis-parityPKš¦—G›ªÍù ù #Stegano-0.4.4.data/scripts/slsb-setPKš¦—G%H° ° RStegano-0.4.4.data/scripts/slsbPKw —Gú»;ÉSS?"stegano/slsb.pyPK€ —G¼t€€¿8stegano/basic.pyPK„ —G9¬Gý ý mGstegano/generators.pyPK‚ —GXÆ}®ëëUstegano/exifHeader.pyPK“ —GZûê—È È »fstegano/tools.pyPKÈa—GC߈4±sstegano/__init__.pyPKz —G?^°C  ässtegano/steganalysisParity.pyPK| —G!Tô• • !4}stegano/steganalysisStatistics.pyPKx —G£žM‚‚‡stegano/slsbset.pyPKÈa—G¹ÕÆ®A®A#ºŸstegano/exif/minimal_exif_writer.pyPKÈa—GbFœññ#©ástegano/exif/minimal_exif_reader.pyPKÈa—GC߈4Ûýstegano/exif/__init__.pyPKe§—G¥Ê  'þStegano-0.4.4.dist-info/DESCRIPTION.rstPKe§—G<ã6A))%kStegano-0.4.4.dist-info/metadata.jsonPKe§—G¬`’ï%× Stegano-0.4.4.dist-info/top_level.txtPKe§—GŒ''\\" Stegano-0.4.4.dist-info/WHEELPKe§—G”*’q q ¹ Stegano-0.4.4.dist-info/METADATAPKe§—G:ƒ)™××hStegano-0.4.4.dist-info/RECORDPK{