Skip to content

Instantly share code, notes, and snippets.

@belkarx
Last active April 6, 2024 14:57
Show Gist options
  • Save belkarx/3627ce2d4f5ac3b206fef7c8052594b5 to your computer and use it in GitHub Desktop.
Save belkarx/3627ce2d4f5ac3b206fef7c8052594b5 to your computer and use it in GitHub Desktop.
xorgrant writeup

fun chall! my first thought was to

  • find the font -> do deterministic search for the bitarrays

i couldn't find the font, missed that the beginning of the b64 had the font set, and then thought:

  • find a grid size that segmented all the characters into equal rectangles, then just manually label stuff and compare rectangles

As it turns out, the lines were not consistent sizes, so i considered:

  • doing a flow algorithm to find all connected pixels so i could identify characters

I couldn't get this working and realized it was unnecessary so I instead settled on:

  • finding the bounding boxes of each character (loosely) by finding the all-white lines that segmented the picture into a grid
import imageio
import numpy as np
import cv2

white_pixel = [255, 255, 255, 255]
image = imageio.imread('image.png')

ar = np.array(image)

cols_mask = np.all(ar == white_pixel, axis=(1, 2))
rows_mask = np.all(ar == white_pixel, axis=(0, 2))

cols = np.where(cols_mask)[0]
rows = np.where(rows_mask)[0]

#get all white lines around each line, will be run on horizontal and vertical masks
def process_lines(cols):
    bounding = []
    for i in range(len(cols)-1):
        if cols[i+1] - cols[i] > 1:
            bounding.append(cols[i])
            bounding.append(cols[i+1])
            i += 1
    return bounding
            
r = process_lines(rows)
c = process_lines(cols)

all_cropped = []

# crop base image into characters
for i in range(0, len(c)-1, 2):
    for j in range(0, len(r)-1, 2):
        print(i, i+1)
        print(j, j+1)
        print()
        cropped = image[c[i]:c[i+1], r[j]:r[j+1]]
        all_cropped.append(cropped)
        #cv2.imshow("label", cropped)
        #cv2.waitKey(0)

#accidentally took in some blank squares at the end and quickly cleaned it up
new_cropped = []
for x in all_cropped:
    if not np.all(x == white_pixel):
        new_cropped.append(x)

# labelled all unique characters manually (there were like 100)
hashes = {}
for item in new_cropped:
    h = hash(tuple(item.flatten()))
    if h not in hashes:
        cv2.imshow('h', item)
        d = cv2.waitKey(0)
        dd = input("char here: ")
        cv2.destroyAllWindows()
        hashes[h] = dd
        print("adding " + dd)
    else:
        print("skipping")

# decoding by hash based on table created by manually labelling above
for x in new_cropped:
    h = hash(tuple(x.flatten()))
    flag.append(hashes[h])

#the end!
''.join(flag)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment