Nik
Nik

Reputation: 1221

Extract image hidden after EOF

For a little lesson in steganography, I am appending an image to another image file like so:

my_image = open(output_image_path, "wb")
my_image.write(open(visible_image, "rb").read())
my_image.write(open(hidden_image, "rb").read())
my_image.close()

Now I want to extract the hidden image again. How would I do this? I tried with PIL by reading the image or by reading in the file as a bytes stream and then converting it, but I only get the visible image.

In case it matters, I should specify that all images are saved in .jpg format

Upvotes: 0

Views: 1191

Answers (2)

HansHirse
HansHirse

Reputation: 18925

I was preparing an answer, and just while typing you added your solution. Nevertheless, here's my version, capable extracting all images stored in the output image:

from io import BytesIO
from PIL import Image

# Create "image to the world"
my_image = open('to_the_world.jpg', 'wb')
my_image.write(open('images/0.jpg', 'rb').read())   # size=640x427
my_image.write(open('images/1.jpg', 'rb').read())   # size=1920x1080
my_image.write(open('images/2.jpg', 'rb').read())   # size=1920x1200
my_image.close()

# Try to read "image to the world" via Pillow
image = Image.open('to_the_world.jpg')
print('Read image via Pillow:\n{}\n'.format(image))

# Read "image to the world" via binary data
image = open('to_the_world.jpg', 'rb').read()

# Look for JPG "Start Of Image" segments, and split byte blocks
images = image.split(b'\xff\xd8')[1:]

# Convert byte blocks to Pillow Image objects
images = [Image.open(BytesIO(b'\xff\xd8' + image)) for image in images]
for i, image in enumerate(images):
    print('Extracted image #{}:\n{}\n'.format(i+1, image))

Of course, I also used the binary data of the output image, and split the binary data using the JPEG file format structure, the "Start of Image" segment FF D8 to be precise.

For the set of images, I used, the output would be the following:

Read image via Pillow:
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=640x427 at 0x1ECC333FF40>

Extracted image #1:
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=640x427 at 0x1ECC333FF10>

Extracted image #2:
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1920x1080 at 0x1ECC37D4C70>

Extracted image #3:
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1920x1200 at 0x1ECC37D4D30>
----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.16299-SP0
Python:        3.9.1
PyCharm:       2021.1.1
Pillow:        8.2.0
----------------------------------------

Upvotes: 1

Nik
Nik

Reputation: 1221

Ok got it, this is how to show the hidden image:

from io import BytesIO
import cv2
from PIL import Image

with open(my_image, 'rb') as img_bin:
    buff = BytesIO()
    buff.write(img_bin.read())
    buff.seek(0)
    bytesarray = buff.read()
    img = bytesarray.split(b"\xff\xd9")[1] + b"\xff\xd9"
    img_out = BytesIO()
    img_out.write(img)
    img = Image.open(img_out)
    img.show()

Upvotes: 1

Related Questions