Last active
April 1, 2022 02:53
-
-
Save briandeheus/9df32136c756227df4bfbff580a1aadd to your computer and use it in GitHub Desktop.
Don't be a punk, punk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import binascii | |
import struct | |
class Punk(object): | |
_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 decode(self, input_file, output_file): | |
self._mode = 'decode' | |
self._file = open(input_file, 'rb+') | |
self._output = open(output_file, 'wb+') | |
# First move cursor past the signature | |
self._read_bytes(8) | |
# Start reading chunks | |
self._read_next_chunk() | |
def encode(self, input_file, bytes_to_hide): | |
self._mode = 'encode' | |
self._file = open(input_file, 'rb+') | |
self._bytes_to_hide = bytes_to_hide | |
# 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() |
@DavidBuchanan314 happy to announce it still works on imgur and others.
I've also updated the script to use Python 3.9 =>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think you forgot to add CRC for the IEND chunk in the output file.