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)
PK w —Gú»;ÉS S stegano/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߈4 stegano/__init__.py
PK z —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)
PK x —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߈4 stegano/exif/__init__.py
PK e§—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.
PK e§—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"}PK e§—G¬`’ï % Stegano-0.4.4.dist-info/top_level.txtstegano
PK e§—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
PK e§—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.
PK e§—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° ° R Stegano-0.4.4.data/scripts/slsbPK w —Gú»;ÉS S ?" stegano/slsb.pyPK € —G¼t€ € ¿8 stegano/basic.pyPK „ —G9¬Gý
ý
mG stegano/generators.pyPK ‚ —GXÆ}®ë ë U stegano/exifHeader.pyPK “ —GZûê—È È »f stegano/tools.pyPK Èa—GC߈4 ±s stegano/__init__.pyPK z —G?^°C äs stegano/steganalysisParity.pyPK | —G!Tô• • ! 4} stegano/steganalysisStatistics.pyPK x —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__.pyPK e§—G¥Ê
' þ Stegano-0.4.4.dist-info/DESCRIPTION.rstPK e§—G<ã6A) ) % k Stegano-0.4.4.dist-info/metadata.jsonPK e§—G¬`’ï % × Stegano-0.4.4.dist-info/top_level.txtPK e§—GŒ''\ \ " Stegano-0.4.4.dist-info/WHEELPK e§—G”*’q q ¹ Stegano-0.4.4.dist-info/METADATAPK e§—G:ƒ)™× × h Stegano-0.4.4.dist-info/RECORDPK {