From d63b8c7736c69480c5c5fbea447a35cab10379ba Mon Sep 17 00:00:00 2001 From: Roman Hergenreder Date: Wed, 5 Dec 2018 14:01:17 +0100 Subject: [PATCH] Teaser Punch Card + Day 5 --- Day 5/decode.py | 10 +- .../39A25/punch_card_decoded.txt | 0 .../39A25/punch_card_decoder.py | 341 ++++++++++++++++++ 3 files changed, 343 insertions(+), 8 deletions(-) create mode 100644 Teaser/_ZOoxjUSe1OVB7OPoVrsX.pdf.extracted/39A25/punch_card_decoded.txt create mode 100755 Teaser/_ZOoxjUSe1OVB7OPoVrsX.pdf.extracted/39A25/punch_card_decoder.py diff --git a/Day 5/decode.py b/Day 5/decode.py index 14dc65f..eaf0072 100644 --- a/Day 5/decode.py +++ b/Day 5/decode.py @@ -1,10 +1,4 @@ #!/usr/bin/python -in1 = "10:a2:a3:2d:60:2e:43:0f:9d:a1:12:5b:fa:1c:36:4e:6c:e2:de:e0:57:3d:8a:69:5a:24:fc:e8:f6:f3:f5:ce" -in1_bytes = [int(x, 16) for x in in1.split(":")] -print(in1_bytes) -print(len(in1_bytes)) - -in2 = in1.replace(":", "") -d = int(in2, 16) -print(d) +# https://securitytrails.com/list/apex_domain/hackvent.org +# Subdomain: osintiscoolisntit.hackvent.org diff --git a/Teaser/_ZOoxjUSe1OVB7OPoVrsX.pdf.extracted/39A25/punch_card_decoded.txt b/Teaser/_ZOoxjUSe1OVB7OPoVrsX.pdf.extracted/39A25/punch_card_decoded.txt new file mode 100644 index 0000000..e69de29 diff --git a/Teaser/_ZOoxjUSe1OVB7OPoVrsX.pdf.extracted/39A25/punch_card_decoder.py b/Teaser/_ZOoxjUSe1OVB7OPoVrsX.pdf.extracted/39A25/punch_card_decoder.py new file mode 100755 index 0000000..d1e1d7b --- /dev/null +++ b/Teaser/_ZOoxjUSe1OVB7OPoVrsX.pdf.extracted/39A25/punch_card_decoder.py @@ -0,0 +1,341 @@ +#!/usr/bin/env python +# +# punchcard.py +# +# Copyright (C) 2011: Michael Hamilton +# The code is GPL 3.0(GNU General Public License) ( http://www.gnu.org/copyleft/gpl.html ) +# +# import Image +from PIL import Image +import sys +from optparse import OptionParser + +CARD_COLUMNS = 80 +CARD_ROWS = 12 + +# found measurements at http://www.quadibloc.com/comp/cardint.htm +CARD_WIDTH = 7.0 + 3.0/8.0 # Inches +CARD_HEIGHT = 3.25 # Inches +CARD_COL_WIDTH = 0.087 # Inches +CARD_HOLE_WIDTH = 0.055 # Inches IBM, 0.056 Control Data +CARD_ROW_HEIGHT = 0.25 # Inches +CARD_HOLE_HEIGHT = 0.125 # Inches +CARD_TOPBOT_MARGIN = 3.0/16.0 # Inches at top and bottom +CARD_SIDE_MARGIN = 0.2235 # Inches on each side + + +CARD_SIDE_MARGIN_RATIO = CARD_SIDE_MARGIN/CARD_WIDTH # as proportion of card width (margin/width) +CARD_TOP_MARGIN_RATIO = CARD_TOPBOT_MARGIN/CARD_HEIGHT # as proportion of card height (margin/height) +CARD_ROW_HEIGHT_RATIO = CARD_ROW_HEIGHT/CARD_HEIGHT # as proportion of card height - works +CARD_COL_WIDTH_RATIO = CARD_COL_WIDTH/CARD_WIDTH # as proportion of card height - works +CARD_HOLE_HEIGHT_RATIO = CARD_HOLE_HEIGHT/CARD_HEIGHT # as proportion of card height - works +CARD_HOLE_WIDTH_RATIO = CARD_HOLE_WIDTH/CARD_WIDTH # as a proportion of card width + +BRIGHTNESS_THRESHOLD = 200 # pixel brightness value (i.e. (R+G+B)/3) + +IBM_MODEL_029_KEYPUNCH = """ + /&-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ:#@'="`.<(+|!$*);^~,%_>? | +12 / O OOOOOOOOO OOOOOO | +11| O OOOOOOOOO OOOOOO | + 0| O OOOOOOOOO OOOOOO | + 1| O O O O | + 2| O O O O O O O O | + 3| O O O O O O O O | + 4| O O O O O O O O | + 5| O O O O O O O O | + 6| O O O O O O O O | + 7| O O O O O O O O | + 8| O O O O OOOOOOOOOOOOOOOOOOOOOOOO | + 9| O O O O | + |__________________________________________________________________|""" + +translate = None +if translate == None: + translate = {} + # Turn the ASCII art sideways and build a hash look up for + # column values, for example: + # (O, , ,O, , , , , , , , ):A + # (O, , , ,O, , , , , , , ):B + # (O, , , , ,O, , , , , , ):C + rows = IBM_MODEL_029_KEYPUNCH[1:].split('\n'); + rotated = [[ r[i] for r in rows[0:13]] for i in range(5, len(rows[0]) - 1)] + for v in rotated: + translate[tuple(v[1:])] = v[0] + #print translate + +# generate a range of floats +def drange(start, stop, step=1.0): + r = start + while (step >= 0.0 and r < stop) or (step < 0.0 and r > stop): + yield r + r += step + +# Represents a punchcard image plus scanned data +class PunchCard(object): + + def __init__(self, image, bright=-1, debug=False, xstart=0, xstop=0, ystart=0, ystop=0, xadjust=0): + pass + self.text = '' + self.decoded = [] + self.surface = [] + self.debug = debug + self.threshold = 0 + self.ymin = ystart + self.ymax = ystop + self.xmin = xstart + self.xmax = xstop + self.xadjust = xadjust + self.image = image + self.pix = image.load() + self._crop() + self._scan(bright) + + # Brightness is the average of RGB values + def _brightness(self, pixel): + #print max(pixel) + return ( pixel[0] + pixel[1] + pixel[2] ) / 3 + + # For highlighting on the debug dump + def _flip(self, pixel): + return max(pixel) + + # The search is started from the "crop" edges. + # Either use crop boundary of the image size or the valyes supplied + # by the command line args + def _crop(self): + self.xsize, self.ysize = image.size + if self.xmax == 0: + self.xmax = self.xsize + if self.ymax == 0: + self.ymax = self.ysize + self.midx = self.xmin + (self.xmax - self.xmin) / 2 + self.xadjust + self.midy = self.ymin + (self.ymax - self.ymin) / 2 + + # heuristic for finding a reasonable cutoff brightness + def _find_threshold_brightness(self): + left = self._brightness(self.pix[self.xmin, self.midy]) + right = self._brightness(self.pix[self.xmax - 1, self.midy]) + return min(left, right, BRIGHTNESS_THRESHOLD) - 10 + vals = [] + last = 0 + for x in xrange(self.xmin,self.xmax): + val = self._brightness(self.pix[x, self.midy]) + if val > last: + left = val + else: + break + last = val + for x in xrange(self.xmax,self.xmin, -1): + val = self._brightness(self.pix[x, self.midy]) + if val > last: + right = val + else: + break + right = val + print left, right + return min(left, right,200) + + for x in xrange(self.xmin,self.xmax): + val = self._brightness(self.pix[x, self.midy]) + vals.append(val) + vals.sort() + last_val = vals[0] + biggest_diff = 0 + threshold = 0 + for val in vals: + diff = val - last_val + #print val, diff + if val > 127 and val < 200 and diff >= 5: + biggest_diff = diff + threshold = val + last_val = val + if self.debug: + print "Threshold diff=", biggest_diff, "brightness=", val + return threshold - 10 + + # Find the left and right edges of the data area at probe_y and from that + # figure out the column and hole vertical dimensions at probe_y. + def _find_data_horiz_dimensions(self, probe_y): + left_border, right_border = self.xmin, self.xmax - 1 + for x in xrange(self.xmin, self.midx): + if self._brightness(self.pix[x, probe_y]) < self.threshold: + left_border = x + break + for x in xrange(self.xmax-1, self.midx, -1): + if self._brightness(self.pix[x, probe_y]) < self.threshold: + right_border = x + break + width = right_border - left_border + card_side_margin_width = int(width * CARD_SIDE_MARGIN_RATIO) + data_left_x = left_border + card_side_margin_width + #data_right_x = right_border - card_side_margin_width + data_right_x = data_left_x + int((CARD_COLUMNS * width) * CARD_COL_WIDTH/CARD_WIDTH) + col_width = width * CARD_COL_WIDTH_RATIO + hole_width = width * CARD_HOLE_WIDTH_RATIO + #print col_width + if self.debug: + # mark left and right edges on the copy + for y in xrange(probe_y - self.ysize/100, probe_y + self.ysize/100): + self.debug_pix[left_border if left_border > 0 else 0,y] = 255 + self.debug_pix[right_border if right_border < self.xmax else self.xmax - 1,y] = 255 + for x in xrange(1, (self.xmax - self.xmin) / 200): + self.debug_pix[left_border + x, probe_y] = 255 + self.debug_pix[right_border - x, probe_y] = 255 + + return data_left_x, data_right_x, col_width, hole_width + + # find the top and bottom of the data area and from that the + # column and hole horizontal dimensions + def _find_data_vert_dimensions(self): + top_border, bottom_border = self.ymin, self.ymax + for y in xrange(self.ymin, self.midy): + #print pix[midx, y][0] + if self._brightness(self.pix[self.midx, y]) < self.threshold: + top_border = y + break + for y in xrange(self.ymax - 1, self.midy, -1): + if self._brightness(self.pix[self.midx, y]) < self.threshold: + bottom_border = y + break + card_height = bottom_border - top_border + card_top_margin = int(card_height * CARD_TOP_MARGIN_RATIO) + data_begins = top_border + card_top_margin + hole_height = int(card_height * CARD_HOLE_HEIGHT_RATIO) + data_top_y = data_begins + hole_height / 2 + col_height = int(card_height * CARD_ROW_HEIGHT_RATIO) + if self.debug: + # mark up the copy with the edges + for x in xrange(self.xmin, self.xmax-1): + self.debug_pix[x,top_border] = 255 + self.debug_pix[x,bottom_border] = 255 + if self.debug: + # mark search parameters + for x in xrange(self.midx - self.xsize/20, self.midx + self.xsize/20): + self.debug_pix[x,self.ymin] = 255 + self.debug_pix[x,self.ymax - 1] = 255 + for y in xrange(0, self.ymin): + self.debug_pix[self.midx,y] = 255 + for y in xrange(self.ymax - 1, self.ysize-1): + self.debug_pix[self.midx,y] = 255 + return data_top_y, data_top_y + col_height * 11, col_height, hole_height + + def _scan(self, bright=-1): + if self.debug: + # if debugging make a copy we can draw on + self.debug_image = self.image.copy() + self.debug_pix = self.debug_image.load() + + self.threshold = bright if bright > 0 else self._find_threshold_brightness() + #x_min, x_max, col_width = self._find_data_horiz_dimensions(image, pix, self.threshold, self.ystart, self.ystop) + y_data_pos, y_data_end, col_height, hole_height = self._find_data_vert_dimensions() + data = {} + + # Chads are narrow so find then heuristically by accumulating pixel brightness + # along the row. Should be forgiving if the image is slightly wonky. + y = y_data_pos #- col_height/8 + for row_num in xrange(CARD_ROWS): + probe_y = y + col_height if row_num == 0 else ( y - col_height if row_num == CARD_ROWS -1 else y ) # Line 0 has a corner missing + x_data_left, x_data_right, col_width, hole_width = self._find_data_horiz_dimensions(probe_y) + left_edge = -1 # of a punch-hole + for x in xrange(x_data_left, x_data_right): + # Chads are tall so we can be sure if we probe around the middle of their height + val = self._brightness(self.pix[x, y]) + if val >= self.threshold: + if left_edge == -1: + left_edge = x + if self.debug: + self.debug_pix[x,y] = self._flip(self.pix[x,y]) + else: + if left_edge > -1: + hole_length = x - left_edge + if hole_length >= hole_width * 0.75: + col_num = int((left_edge + hole_length / 2.0 - x_data_left) / col_width + 0.25) + data[(col_num, row_num)] = hole_length + left_edge = -1 + if (self.debug): + # Plot where holes might be on this row + expected_top_edge = y - hole_height / 2 + expected_bottom_edge = y + hole_height / 2 + blue = 255 * 256 * 256 + for expected_left_edge in drange(x_data_left, x_data_right - 1, col_width): + for y_plot in drange(expected_top_edge, expected_bottom_edge, 2): + self.debug_pix[expected_left_edge,y_plot] = blue + #self.debug_pix[x + hole_width/2,yline] = 255 * 256 * 256 + self.debug_pix[expected_left_edge + hole_width,y_plot] = blue + for x_plot in drange(expected_left_edge, expected_left_edge + hole_width): + self.debug_pix[x_plot, expected_top_edge] = blue + self.debug_pix[x_plot, expected_bottom_edge] = blue + y += col_height + + if self.debug: + self.debug_image.show() + # prevent run-a-way debug shows causing my desktop to run out of memory + raw_input("Press Enter to continue...") + self.decoded = [] + # Could fold this loop into the previous one - but would it be faster? + for col in xrange(0, CARD_COLUMNS): + col_pattern = [] + col_surface = [] + for row in xrange(CARD_ROWS): + key = (col, row) + # avergage for 1/3 of a column is greater than the threshold + col_pattern.append('O' if key in data else ' ') + col_surface.append(data[key] if key in data else 0) + tval = tuple(col_pattern) + global translate + self.text += translate[tval] if tval in translate else '@' + self.decoded.append(tval) + self.surface.append(col_surface) + + + return self + + # ASCII art image of card + def dump(self, id, raw_data=False): + print ' Card Dump of Image file:', id, 'Format', 'Raw' if raw_data else 'Dump', 'threshold=', self.threshold + print ' ' + '123456789-' * (CARD_COLUMNS/10) + print ' ' + '_' * CARD_COLUMNS + ' ' + print '/' + self.text + '_' * (CARD_COLUMNS - len(self.text)) + '|' + for rnum in xrange(len(self.decoded[0])): + sys.stdout.write('|') + if raw_data: + for val in self.surface: + sys.stdout.write(("(%d)" % val[rnum]) if val[rnum] != 0 else '.' ) + else: + for col in self.decoded: + sys.stdout.write(col[rnum] if col[rnum] == 'O' else '.') + print '|' + print '`' + '-' * CARD_COLUMNS + "'" + print ' ' + '123456789-' * (CARD_COLUMNS/10) + print '' + + +if __name__ == '__main__': + + usage = """usage: %prog [options] image [image...] + decode punch card image into ASCII.""" + parser = OptionParser(usage) + parser.add_option('-b', '--bright-threshold', type='int', dest='bright', default=-1, help='Brightness (R+G+B)/3, e.g. 127.') + parser.add_option('-s', '--side-margin-ratio', type='float', dest='side_margin_ratio', default=CARD_SIDE_MARGIN_RATIO, help='Manually set side margin ratio (sideMargin/cardWidth).') + parser.add_option('-d', '--dump', action='store_true', dest='dump', help='Output an ASCII-art version of the card.') + parser.add_option('-i', '--display-image', action='store_true', dest='display', help='Display an anotated version of the image.') + parser.add_option('-r', '--dump-raw', action='store_true', dest='dumpraw', help='Output ASCII-art with raw row/column accumulator values.') + parser.add_option('-x', '--x-start', type='int', dest='xstart', default=0, help='Start looking for a card edge at y position (pixels)') + parser.add_option('-X', '--x-stop', type='int', dest='xstop', default=0, help='Stop looking for a card edge at y position') + parser.add_option('-y', '--y-start', type='int', dest='ystart', default=0, help='Start looking for a card edge at y position') + parser.add_option('-Y', '--y-stop', type='int', dest='ystop', default=0, help='Stop looking for a card edge at y position') + parser.add_option('-a', '--adjust-x', type='int', dest='xadjust', default=0, help='Adjust middle edge detect location (pixels)') + (options, args) = parser.parse_args() + + for arg in args: + image = Image.open(arg) + card = PunchCard(image, bright=options.bright, debug=options.display, xstart=options.xstart, xstop=options.xstop, ystart=options.ystart, ystop=options.ystop, xadjust=options.xadjust) + print card.text + if (options.dump): + card.dump(arg) + if (options.dumpraw): + card.dump(arg, raw_data=True) + + +# main(["old_school.jpg", "-y", "5", "-b", "124"]) +# O2Z-H4PAL52Z4-3F19-HV18-0LD$-SCH0-0L1S-4W3S-0M3!-31-@&Q@33@ @@@2LC_B&@@T&@L&W@&@