#!/usr/bin/env python # # # softstrip.py # http://wiki.yobi.be/wiki/PoC_or_GTFO # import re, struct, sys from PIL import Image # Author: @doegox # Original version was from # https://www.reddit.com/r/programming/comments/1ye0th/i_found_this_in_a_computer_magazine_from_1986/ # This version expects a perfect BW image, 1 pixel per dot # Relevant patents: # https://www.google.com/patents/US4692603 # https://www.google.com/patents/US4728783 # https://www.google.com/patents/US4782221 # very first lines: count number of black lines N -> there are (N+4)/2 nibbles in each data line # here 10 -> (10+4)/2 = 7 nibbles per line # dibit/bit/dibit_sync/dibit_P1/dibit_nibbles/dibit_P2/bit_or_dibit/dibit/bit_sync # P1: parity of the even bits # P2: parity of the odd bits # First data line should be 00 00 00 LL HH -> total nr of bytes image = Image.open("softstrip_bw.png") w, h = image.size def getcolor(pixel): return pixel^1 def paritycheck(bits): a = bits[::2] b = bits[1::2] if '?' in a: a = False else: a = (sum(a) % 2 == 0) if '?' in b: b = False else: b = (sum(b) % 2 == 0) return a and b def getnibbles(bits): 'return [(nibble, validmask)]' ret = [] par = paritycheck(bits) bits = bits[1:-1] # remove parity bits assert len(bits) % 4 == 0 while len(bits): nib, bits = bits[:4], bits[4:] n = 0 invalid = 0 for b in reversed(nib): n <<= 1 invalid <<= 1 if b not in (0, 1): invalid |= 1 else: n |= b if not par: invalid = 0b1111 ret.append((n, invalid ^ 0b1111)) return ret def getbytes(nibbles): bytes = [] while len(nibbles) >= 2: a, av = nibbles.pop(0) b, bv = nibbles.pop(0) c = chr((b << 4) | a) v = chr((bv << 4) | av) bytes.append((c, v)) return bytes firstline=[] nibbles = [] bytes = [] for y in range(h): dibits = [] for x in range(w): dibits.append(getcolor(image.getpixel((x,y)))) bits = [] for i in range(5, len(dibits) - 5, 2): a, b = dibits[i:i+2] if a != b: bits.append(int(a)) else: bits.append('?') print ''.join(' #'[b] for b in dibits), ''.join(map(str, bits)), ''.join("%x" % c for c, v in getnibbles(bits)), ''.join("%x" % v for c, v in getnibbles(bits)) if nibbles == [] and '?' in bits: continue if firstline == []: firstline = bits else: if bits != firstline: # Start recording data nibbles += getnibbles(bits) bytes += getbytes(nibbles) raw = ''.join(c for c, v in bytes) valid = ''.join(v for c, v in bytes) header = raw[:5] assert header[:3]=='\0\0\0' size, = struct.unpack('= size, 'data length doesn\'t match size given in header' if len(data) > size: print "Truncating data from %i to %i bytes" % (len(data), size) data=data[:size] open('out.bin', 'w').write(data) open('out.valid', 'w').write(valid) fmt = '%-48s %-16s %-32s' allvalid = bool(re.match(r'\xff+$', valid)) print print fmt % ('hex', 'ascii', '' if allvalid else 'valid bits') for i in range(0, len(data), 16): d = data[i:i+16] v = valid[i:i+16] hex = ' '.join('%02x' % ord(c) for c in d) ascii = ''.join((c if 32 <= ord(c) <= 126 else '.') for c in d) val = '' if allvalid else ''.join('%02x' % ord(c) for c in v) print fmt % (hex, ascii, val)