Browse Source

Teaser Punch Card + Day 5

Roman Hergenreder 5 years ago
parent
commit
d63b8c7736

+ 2 - 8
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

+ 0 - 0
Teaser/_ZOoxjUSe1OVB7OPoVrsX.pdf.extracted/39A25/punch_card_decoded.txt


+ 341 - 0
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@&@