Skip to content

Instantly share code, notes, and snippets.

@wting
Forked from briandeheus/punk.py
Last active April 12, 2017 21:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save wting/39b0f4c42fef828c5ef525a272ecf558 to your computer and use it in GitHub Desktop.
Save wting/39b0f4c42fef828c5ef525a272ecf558 to your computer and use it in GitHub Desktop.
Don't be a punk, punk
import binascii
import struct
class Punk(object):
"""Full blog post about hiding data within PNG files here:
http://blog.brian.jp/python/png/2016/07/07/file-fun-with-pyhon.html
Example use case:
from punk import Punk
Punk.encode(
injected_file='meme.png',
payload=file('doge.gif').read(),
)
Punk.decode(
injected_file='meme.png',
payload='doge.gif',
)
"""
_END_CHUNK_TYPE = 'IEND'
_PUNK_CHUNK_TYPE = 'puNk'
_MAX_BYTES = 2147483647
_chunks = dict()
def __init__(self):
self._mode = None
self._file = None
self._output = None
self._bytes_to_hide = None
self._bytes_read = 0
def encode(self, injected_file, payload):
self._mode = 'encode'
self._file = open(injected_file, 'rb+')
self._bytes_to_hide = payload
# First move cursor past the signature
self._read_bytes(8)
# Start reading chunks
self._read_next_chunk()
def decode(self, injected_file, hidden_bytes):
self._mode = 'decode'
self._file = open(injected_file, 'rb+')
self._output = open(hidden_bytes, 'wb+')
# First move cursor past the signature
self._read_bytes(8)
# Start reading chunks
self._read_next_chunk()
def _read_bytes_as_hex(self, position):
return self._read_bytes(position).encode('hex')
def _read_bytes_as_ascii(self, position):
return self._read_bytes(position).encode('ascii')
def _read_bytes_as_int(self, position):
return int(self._read_bytes_as_hex(position), 16)
def _read_bytes(self, byte_count):
self._bytes_read += byte_count
return self._file.read(byte_count)
def _rewind_bytes(self, byte_count):
self._bytes_read -= byte_count
self._file.seek(self._bytes_read)
def _inject_punk_chunk(self):
# Move back 8 bytes.
self._rewind_bytes(8)
chunk_size = len(self._bytes_to_hide)
print 'Hiding', (chunk_size / 1024), 'kB (', chunk_size, 'bytes)'
# Create a temporary byte array for the CRC check.
tmp_bytes = bytearray()
# First write the chunk type
tmp_bytes.extend(bytearray(self._PUNK_CHUNK_TYPE))
# Now write the bytes of whatever we're trying to hide
tmp_bytes.extend(self._bytes_to_hide)
print 'Injecting punk chunk'
# Write the chunk size
self._file.write(bytearray(struct.pack('!i', chunk_size)))
# And the type
self._file.write(bytearray(self._PUNK_CHUNK_TYPE))
self._file.write(self._bytes_to_hide)
crc = binascii.crc32(tmp_bytes)
self._file.write(bytearray(struct.pack('!i', crc)))
# Write the end chunk. Start with the size.
self._file.write(bytearray(struct.pack('!i', 0)))
# Then the chunk type.
self._file.write(bytearray(self._END_CHUNK_TYPE))
print 'Punk chunk injected'
def _read_next_chunk(self):
chunk_size = self._read_bytes_as_int(4)
print 'Chunk size:', chunk_size
chunk_type = self._read_bytes_as_ascii(4)
print 'Chunk type:', chunk_type
if self._mode == 'encode' and chunk_type == self._END_CHUNK_TYPE:
self._inject_punk_chunk()
print 'Reached EOF'
self._file.close()
return
content = self._read_bytes(chunk_size)
crc = self._read_bytes_as_hex(4)
print 'CRC:', crc
if self._mode == 'decode' and chunk_type == self._PUNK_CHUNK_TYPE:
print "Found a punk chunk", len(content), "bytes. Writing to file"
self._output.write(bytearray(content))
self._output.close()
self._file.close()
return
self._read_next_chunk()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment