Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ''' ------------------------------------------------------------------------ '''
- ''' ------------------------------------------------------------------------ '''
- #"steganography_encoder_decoder_2025-05-22\embed_decode.py":
- #!/usr/bin/env python3
- #usage example: "py embed_encode.py img_file.png [-noexecute]"
- from sys import argv, stderr
- from os import system as cmd
- from math import floor
- from os.path import exists, splitext as split_file_extension
- from pprint import pprint
- from PIL import Image
- from time import time, sleep
- import subprocess
- def errFatal(errorString, returnCode = -1):
- print('ERROR: '+errorString, file=stderr)
- exit(returnCode)
- def rnd(x): return floor(x+0.5) # Python's built-in rounding is stupid
- def toInt(x):
- try: return rnd(eval(x))
- except Exception: return None
- fileName_img = argv[ 1] if len(argv) > 1 else None # Input image filename
- noExecute_str = argv[-1] if len(argv) > 2 else None
- noExecute = False
- if __name__ == '__main__':
- if not fileName_img: exit(0) # No file given; exit early
- if noExecute_str and noExecute_str == '-noexecute': noExecute = True
- img = None # Image object
- width = None
- height = None
- def get_channel_bit(n):
- global img
- global width
- which_channel = n%3
- n //= 3 # Integer divide
- x = n%width
- n //= width
- y = n
- r,g,b = img.getpixel((x,y))
- if which_channel == 0: return r&1
- elif which_channel == 1: return g&1
- else : return b&1
- if __name__ == '__main__':
- img = Image.open(fileName_img).convert('RGB')
- width, height = img.size
- numHeaderBits = (4+4)*8
- data = []
- # Get the data's file extension and size, in bytes
- for i in range(numHeaderBits):
- bit = i%8
- if bit == 0: data.append(0)
- data[-1] |= get_channel_bit(i)<<bit
- dataExt = str(bytes(data[0:4]), 'ascii').rstrip('\x00')
- dataLen = int.from_bytes(bytes(data[4:8]), 'little')
- data = []
- # Get rest of data
- for i in range(numHeaderBits, numHeaderBits+dataLen*8):
- bit = i%8
- if bit == 0: data.append(0)
- data[-1] |= get_channel_bit(i)<<bit
- data_bytes = bytes(data)
- # Write data to file
- fileName_out = 'data_out.' + dataExt
- file = open(fileName_out, 'wb')
- file.write(data_bytes)
- file.close()
- # This is potentially dangerous!
- # Only execute programs you know are safe!
- if not noExecute:
- if dataExt.lower() == 'py':
- exec(str(data_bytes, 'ascii'))
- elif dataExt.lower() == 'exe':
- pArgs = [ '.\\'+fileName_out, f'"{fileName_img}"' ]
- proc = subprocess.Popen(pArgs, shell=True)
- while proc.poll() == None: sleep(0.02)
- pExitCode = proc.poll()
- pOutput, pError = proc.communicate() # (pError is left unused)
- if len(pOutput) != 0: print(pOutput.decode("utf-8"))
- if pExitCode != 0: exit(pExitCode)
- ''' ------------------------------------------------------------------------ '''
- ''' ------------------------------------------------------------------------ '''
- #"steganography_encoder_decoder_2025-05-22\embed_encode.py":
- #!/usr/bin/env python3
- #usage example: "py embed_encode.py data_file.bin img_file.png"
- from sys import argv, stderr
- from os import system as cmd
- from math import floor
- from os.path import exists, splitext as split_file_extension
- from pprint import pprint
- from PIL import Image
- from random import random
- from time import time
- def errFatal(errorString, returnCode = -1):
- print('ERROR: '+errorString, file=stderr)
- exit(returnCode)
- def rnd(x): return floor(x+0.5) # Python's built-in rounding is stupid
- def toInt(x):
- try: return rnd(eval(x))
- except Exception: return None
- def append_filename(name, append):
- root, ext = split_file_extension(name)
- return root + append + ext
- fileName_dat = argv[1] if len(argv) > 1 else None # Data to be embedded filename
- fileName_img = argv[2] if len(argv) > 2 else None # Input image filename
- fileName_out = argv[3] if len(argv) > 3 else None # Output image filename
- fileExt_dat = None # File extension of data filename
- if __name__ == '__main__':
- if not fileName_dat: errFatal('First argument must be data file path!')
- if not fileName_img: errFatal('First argument must be image file path!')
- if not fileName_out: fileName_out = append_filename(fileName_img, '_output')
- dummyVariable, fileExt_dat = split_file_extension(fileName_dat)
- if not exists(fileName_dat): errFatal(f'"{fileName_dat}" doesn\'t exist!')
- if not exists(fileName_img): errFatal(f'"{fileName_img}" doesn\'t exist!')
- data = None # List of byte values, in the form of ints (0-255)
- img = None # Image object
- width = None
- height = None
- def get_channel_value(n):
- global img
- global width
- which_channel = n%3
- n //= 3 # Integer divide
- x = n%width
- n //= width
- y = n
- r,g,b = img.getpixel((x,y))
- if which_channel == 0: return r
- elif which_channel == 1: return g
- else : return b
- def set_channel_value(n, v):
- global img
- global width
- which_channel = n%3
- n //= 3
- x = n%width
- n //= width
- y = n
- r,g,b = img.getpixel((x,y))
- if which_channel == 0: r = v
- elif which_channel == 1: g = v
- else : b = v
- img.putpixel((x,y), (r,g,b))
- def get_data_bit(n, d):
- which_bit = n%8
- n //= 8
- which_byte = d[n]
- return (which_byte>>which_bit)&1
- if __name__ == '__main__':
- file_dat = open(fileName_dat, 'rb')
- data = list(file_dat.read())
- file_dat.close()
- if len(data) > 0xFFFFFFFF:
- errFatal(f'Data size ({len(data)}) > {0xFFFFFFFF}')
- img = Image.open(fileName_img).convert('RGB')
- width, height = img.size
- maxSize = (width*height*3)/8 - 8
- print(f'Percent of max data size: {floor((len(data)/maxSize)*10000)/100}%')
- if len(data) > maxSize:
- errFatal(f'Data size ({len(data)}) > (width*height*3)/8-8 ({maxSize})')
- if len(fileExt_dat) > 0 and fileExt_dat[0:1] == '.':
- fileExt_dat = fileExt_dat[1:]
- fileExt_padded = fileExt_dat.ljust(4, chr(0))[0:4]
- fileExt_bytes = list(bytes(fileExt_padded, 'ascii')) # List of 0-255 ints
- dataLen_bytes = list(len(data).to_bytes(4,'little')) # List of 0-255 ints
- # Write the file extension of the data file (the first 4 chars at, least)
- for i in range(len(fileExt_bytes)*8):
- # Get channel value, before unsetting the lowest bit
- v = get_channel_value(i) & (~1)
- # Replace lowest bit of v with the relevant data bit
- v |= get_data_bit(i, fileExt_bytes)
- # Set the relevant channel value to v
- set_channel_value(i, v)
- # Write the size of the data file, in bytes (as a 32-bit unsigned integer)
- add_to = len(fileExt_bytes)*8
- for i in range(len(dataLen_bytes)*8):
- v = get_channel_value(i+add_to) & (~1)
- v |= get_data_bit(i, dataLen_bytes)
- set_channel_value(i+add_to, v)
- # Write the actual data
- add_to += len(dataLen_bytes)*8
- for i in range(len(data)*8):
- v = get_channel_value(i+add_to) & (~1)
- v |= get_data_bit(i, data)
- set_channel_value(i+add_to, v)
- img.save(fileName_out)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement