custom code - Codegate 2024 Finals
データを2次元の画像データに変換するプログラムが与えられる
フラグを変換した画像も与えられるが、一部欠損していて正常にデコードできない
https://scrapbox.io/files/687532bdf253eb8cd5da802f.png
プログラムを読むと、パリティをつけてエンコードしていることがわかる
パリティとエラーから元の値を復元する方法がわからなかったので、bruteforceしてエラーが起きないデータを探すとフラグが得られた
code:python
import sys
import itertools
import string
from PIL import Image
off_59DCF0 = 0x0, 0x88, 0x8D, 0x71, 0x4C, 0xDA, 0x2B, 0x55, 0xB6, 0x1F, 0x34 off_59DCD0 = 0, 0, 1, 240, 2, 225, 241, 53, 3, 38, 226, 133, 242, 43, 54, 210, 4, 195, 39, 114, 227, 106, 134, 28, 243, 140, 44, 23, 55, 118, 211, 234, 5, 219, 196, 96, 40, 222, 115, 103, 228, 78, 107, 125, 135, 8, 29, 162, 244, 186, 141, 180, 45, 99, 24, 49, 56, 13, 119, 153, 212, 199, 235, 91, 6, 76, 220, 217, 197, 11, 97, 184, 41, 36, 223, 253, 116, 138, 104, 193, 229, 86, 79, 171, 108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84, 245, 173, 187, 204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207, 57, 147, 14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110, 92, 176, 7, 161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152, 98, 48, 185, 179, 42, 209, 37, 132, 224, 52, 254, 239, 117, 233, 139, 22, 105, 27, 194, 113, 230, 206, 87, 158, 80, 189, 172, 203, 109, 175, 166, 62, 127, 247, 146, 66, 137, 192, 35, 252, 10, 183, 75, 216, 31, 83, 33, 73, 164, 144, 85, 170, 246, 65, 174, 61, 188, 202, 205, 157, 143, 169, 82, 72, 182, 215, 191, 251, 47, 178, 89, 151, 101, 94, 160, 123, 26, 112, 232, 21, 51, 238, 208, 131, 58, 69, 148, 18, 15, 16, 68, 17, 121, 149, 129, 19, 155, 59, 249, 70, 214, 250, 168, 71, 201, 156, 64, 60, 237, 130, 111, 20, 93, 122, 177, 150 off_59DCB0 = 1, 2, 4, 8, 16, 32, 64, 128, 45, 90, 180, 69, 138, 57, 114, 228, 229, 231, 227, 235, 251, 219, 155, 27, 54, 108, 216, 157, 23, 46, 92, 184, 93, 186, 89, 178, 73, 146, 9, 18, 36, 72, 144, 13, 26, 52, 104, 208, 141, 55, 110, 220, 149, 7, 14, 28, 56, 112, 224, 237, 247, 195, 171, 123, 246, 193, 175, 115, 230, 225, 239, 243, 203, 187, 91, 182, 65, 130, 41, 82, 164, 101, 202, 185, 95, 190, 81, 162, 105, 210, 137, 63, 126, 252, 213, 135, 35, 70, 140, 53, 106, 212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250, 217, 159, 19, 38, 76, 152, 29, 58, 116, 232, 253, 215, 131, 43, 86, 172, 117, 234, 249, 223, 147, 11, 22, 44, 88, 176, 77, 154, 25, 50, 100, 200, 189, 87, 174, 113, 226, 233, 255, 211, 139, 59, 118, 236, 245, 199, 163, 107, 214, 129, 47, 94, 188, 85, 170, 121, 242, 201, 191, 83, 166, 97, 194, 169, 127, 254, 209, 143, 51, 102, 204, 181, 71, 142, 49, 98, 196, 165, 103, 206, 177, 79, 158, 17, 34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161, 111, 222, 145, 15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132, 37, 74, 148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248, 221, 151, 3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75, 150, 1 def size(img):
w, h = img.size
ww = (w - 400) // 100
hh = (h - 400) // 100
return ww, hh
def at(img, x, y):
xx = 200 + x * 100
yy = 200 + y * 100
result = ((img.getpixel((xx, yy))0+ img.getpixel((xx+99, yy))0+ img.getpixel((xx, yy+99))0+ img.getpixel((xx+99, yy+99))0)/4) if result > 0.5:
return 1
else:
return 0
def read(filename):
img = Image.open(filename)
w, h = size(img)
print(w, h)
data = []
for x in range(0, w, 2):
for y in range(h):
for dx in range(2):
data.append(at(img, x + dx, y))
return [sum([datai+j<<(7-j) for j in range(8)]) for i in range(0, len(data), 8)] table_v65 = {}
for v65 in range(32):
v113 = v65 << 10
v67 = v113
for k in range(14, 9, -1):
if v113 >> k == 1:
v118 = k - 10
v113 = v113 ^ (1335 << v118)
table_v65v65 = v67 | v113 table_v65_inv = {v: k for k, v in table_v65.items()}
def decode(v76):
v58 = []
for i in range(0, len(v76), 15):
for j in range(len(v158)):
min_diff = 0x10000
min_diff_candidate = 0
for candidate in range(32):
v76_ = []
v76_.append((v1580 >> 7) & 0xff) v76_.append((v1580 << 1) & 0xff | (v1581 >> 14) & 0xff) v76_.append((v1581 >> 6) & 0xff) v76_.append((v1581 << 2) & 0xff | (v1582 >> 13) & 0xff) v76_.append((v1582 >> 5) & 0xff) v76_.append((v1582 << 3) & 0xff | (v1583 >> 12) & 0xff) v76_.append((v1583 >> 4) & 0xff) v76_.append((v1583 << 4) & 0xff | (v1584 >> 11) & 0xff) v76_.append((v1584 >> 3) & 0xff) v76_.append((v1584 << 5) & 0xff | (v1585 >> 10) & 0xff) v76_.append((v1585 >> 2) & 0xff) v76_.append((v1585 << 6) & 0xff | (v1586 >> 9) & 0xff) v76_.append((v1586 >> 1) & 0xff) v76_.append((v1586 << 7) & 0xff | (v1587 >> 8) & 0xff) v76_.append((v1587 >> 0) & 0xff) diff = 0
for expect, actual in zip(v76i:, v76_): diff += int.bit_count(expect ^ actual)
if diff < min_diff:
min_diff = diff
min_diff_candidate = candidate
v65 = [table_v65_inv[v158j] for j in range(len(v158))] v60 = 0
for j in range(8):
for j in range(5):
v58.append((v60 >> (8*(5-j-1))) & 0xff)
return v58
def b(text):
buffer1 = list(text)
for i in range(0xf-len(buffer1)):
buffer1.append(0)
for i in range(5):
buffer2 = []
for j in range(0xb):
if v17 and v20:
v23 = (off_59DCD0v20 + off_59DCD0v17) % 0xff else:
v21 = 0
buffer2.append(v21)
for j in range(0xb):
# v76 = read('flag.png')
# v58 = decode(v76)
# v58 = [
# 'c', 'o', 'd', 'e', 'g', 'a', 't', 'e', '2', '0', '2', '4', '{', 'a', '9', 'a', '7', '4', '4', '8', '1', '2', '5',
# 'd', '9', '0', 'd', '0', '6', 'd', '6', 'f', '5', '8', '3', '9', '1', '8', 'd', 'a', '7', '0', '8', '9', '0', '5',
# '8', 'f', 'f', '2', '3', '0', '9', '0', '1', '0', '7', '7', '5', '7', '1', '5', '0', '8', '9', '8', 'f', '0', '5',
# 'd', '3', 'b', '3', '8', '6', 'a', '8', '}', '\x00', 'ÿ',
# '\x95', '\x87', '\xf2', '\x03', 'Ì', '\x13', '¸', 'Å', '\x33', '\x18', '9', 'J', '\xd5', 'ð', '\x5b', 'M', 'x', 'Ö', '\xad', '1', 'x', '\t', 'ö', '\x02', 'À', 'T', 'À', '\x99', '\x02', '\x01', '\x04', '«', '\x08', 'Ì', '\x00', 'h', '9', '\x8d', '-', 'Í', ',', 'Ô', 'n', 'u', 'æ', '\x04', 'ø', 'T', '«', '8', 'Þ', '0', '-', 'ç', '5', '\r', '\x0b', '\x06', 'ô', '³', '¤', '_', '?', '\x81', ':', 'Ù', '"', '\x0e', 'Î', '¹', '\x83', 'Æ', 'y', 'd', '\x0b', 'í', '\x84', 'Ò', 'à', '\x1d', '\x07', '6', '\x90', '@', 'º', '\x00', '0', 'õ', 'w', 'R', '³', '\x00', '}', 'Ò', 'e', ':', 'õ', '\x86', '6', 'b', 'f', 'p', 'b', '\x83', '½', 'O', '\x82', 'ä', 'ø', '\x02', '\x90', "'", ''\x0f', '`', '\x07', 'w', '\x9f', '¥', '¢', 'I', '\x84', 'Â', '(', '_', 'Ð', '¥', 'o', '(', '\x00', 'ë', '°', 'ð', 'g', 'É', '\x07', 'é', 'ñ', 'Ä', 'b', '', 'Á', '\x16', 'ù', '\x82', '´', 'f', 'ì', '-', '\x84', '\x00', 't', '$', '\x96', '\x88', 'ð', '¢', 'ô', 'X', 'S',
# ]
v58 = [
'c', 'o', 'd', 'e', 'g', 'a', 't', 'e', '2', '0', '2', '4', '{', 'a', '9', 'a', '7', '4', '4', '8', '8', 'd', '0',
'1', 'c', '0', 'd', '0', '6', 'd', '6', 'f', '7', '3', '9', '9', '1', '8', 'd', 'a', '7', '0', '8', '9', '0', '3', 'e', '0', 'b', 'd', '3', '0', '9', '0', '1', '0', '7', '7', '5', '7', 'e', '2', '8', '8', '9', '8', 'f', '0', '5', 'd', '3', 'b', '3', '8', '6', 'a', '8', '}', '\x00', 'ÿ',
'\x95', '\x87', 'ò', '\x03', 'Ì', '\x13', '¸', 'Å', '\x00', '8', '9', 'J', 'Õ', 'ð', ''M', 'x', 'Ö', '\xad', '1', 'x', '\t', 'ö', 'z', 'Ö', 'T', 'Ð', '\x99', '\x12', '\x01', '4', '«', '\x08', 'Ì', '\r', 'l', '9', '\x8f', '\xad', 'Í', ',', 'Ô', 'n', 'w', 'æ', '\x04', 'ø', 'T', '«', '8', 'Þ', '0', '-', 'ç', '5', 'Õ', '\x0b', '\x06', 'ô', '³', '¤', '_', '?', '\x81', ':', 'Ù', '"', '\x9e', 'Î', '¹', '\x83', 'Æ', 'y', 'd', '\x0b', 'í', '\x84', 'Ò', 'à', 'Ý', 'ï', '6', '\x90', '@', '¸', ' ', '0', 'õ', 'w', 'R', '³', '\x00', '}', 'Ò', 'e', '?', 'õ', '\x86', '6', 'b', 'g', 'ð', 'b', '\x83', '½', 'O', '\x9a', 'ï', 'ø', '\x02', '\x90', '§', '_', '\x8f', '~', '\x13', '÷', '/', '§', 'á', 'I', '\x84', 'Â', '(', '_', '7', 'å', 'o', '(', '\x17', 'ë', '¾', 'ý', 'g', 'é', '\x07', 'é', 'ñ', 'Ä', 'b', '', 'Ã', '\x16', 'Í', '\x82', '´', 'f', 'ì', '-', '\x84', '8', 't', '$', '\x96', '\x88', 'ð', '¢', 'ô', 'X', 'S'
]
flag_parts = []
parity_parts = []
for i in range(0, len(flag) // 5):
flag_parts.append(flag_part)
parity_parts.append(parity_part)
print('i', i)
print('parity', p1)
print('parity', p2)
if p1 == p2:
print('OK')
else:
print('NG')
print()
N = 10
L = 0
min_diff = 1000000
min_c = 0
for c in '0123456789abcdef':
diff = 0
p2 = b([i for i in flag_partsN]) diff = sum([int.bit_count(p1i ^ p2i) for i in range(10)]) if diff < min_diff:
min_diff = diff
min_c = c
print(min_c)
# for N in range(0, len(flag_parts)):
# min_diff = 1000000
# min_flag_part = ''
# candidate = None
# if N == 0 or N == 1 or N == 15:
# candidate = set('0123456789abcdef{}codegate')
# else:
# candidate = set('0123456789abcdef')
# for flag_part in itertools.permutations(candidate, 5):
# diff = sum([int.bit_count(p1i ^ p2i) for i in range(10)]) # if diff < min_diff:
# min_diff = diff
# min_flag_part = flag_part
# print(''.join(min_flag_part), end='')
# sys.stdout.flush()
# print()