96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
import struct
|
|
from hackingscripts import util
|
|
from PIL import Image
|
|
from pyzbar.pyzbar import decode
|
|
|
|
def bit_stream_to_qr(bit_stream, qr_size=(29, 32), pix_size=10):
|
|
img = Image.new("RGB", (qr_size[1] * pix_size, qr_size[0] * pix_size))
|
|
pix = img.load()
|
|
|
|
columns = ["" for r in range(qr_size[1])]
|
|
for i, b in enumerate(bit_stream):
|
|
columns[i % qr_size[1]] += b
|
|
|
|
for ci, column in enumerate(columns):
|
|
for ri, b in enumerate(column):
|
|
color = (0,0,0) if b == "1" else (255,255,255)
|
|
for xi in range(pix_size):
|
|
for yi in range(pix_size):
|
|
pix[ci*pix_size+xi,ri*pix_size+yi] = color
|
|
|
|
return img
|
|
|
|
def apply_scramble(data, loop_count, shift_direction, shift_type_1, shift_type_2):
|
|
for i in range(loop_count):
|
|
if shift_direction == 0:
|
|
if shift_type_1 != -1:
|
|
offset = 0x70 + shift_type_1
|
|
value = data[offset]
|
|
for x in range(0x1c+1):
|
|
data[offset] = data[offset - 4]
|
|
offset -= 4
|
|
data[offset + 4] = value
|
|
if shift_type_2 != -1:
|
|
offset = shift_type_2 * 4
|
|
value = data[offset]
|
|
for x in range(3+1):
|
|
data[offset] = data[offset + 1]
|
|
offset += 1
|
|
data[offset - 1] = value
|
|
elif shift_direction == 1:
|
|
if shift_type_2 != -1:
|
|
offset = 3 + shift_type_2 * 4
|
|
value = data[offset]
|
|
for x in range(3+1):
|
|
data[offset] = data[offset - 1]
|
|
offset -= 1
|
|
data[offset + 1] = value
|
|
if shift_type_1 != -1:
|
|
offset = shift_type_1
|
|
value = data[offset]
|
|
for x in range(0x1c + 1):
|
|
data[offset] = data[offset + 4]
|
|
offset += 4
|
|
data[offset - 4] = value
|
|
else:
|
|
raise
|
|
return data
|
|
|
|
def bytes_to_bit_stream(data, qr_size=(29, 32)):
|
|
bit_stream = ""
|
|
for v in data:
|
|
bit_stream += util.lpad(bin(v)[2:], 8, "0")
|
|
bit_stream = bit_stream[0:qr_size[0] * qr_size[1]]
|
|
return bit_stream
|
|
|
|
if __name__ == "__main__":
|
|
|
|
with open("HV23", "rb") as f:
|
|
prog_data = f.read()
|
|
|
|
QR_DATA = bytearray(prog_data[0x8a3e:0x8a3e+0x70+7])
|
|
SCRAMBLE_DATA = prog_data[0x628:0x628+0x60]
|
|
NUM_SCRAMBLES = 0x18
|
|
assert len(SCRAMBLE_DATA) == NUM_SCRAMBLES * 4
|
|
|
|
scrambles = []
|
|
for scramble in range(0, NUM_SCRAMBLES*4, 4):
|
|
scramble_data = SCRAMBLE_DATA[scramble:scramble+4]
|
|
shift_type_2, shift_type_1, loop_count, shift_direction = struct.unpack("bbBB", scramble_data)
|
|
scrambles.append((loop_count, shift_direction, shift_type_1, shift_type_2))
|
|
|
|
data = QR_DATA
|
|
while True:
|
|
for scramble in scrambles:
|
|
data = apply_scramble(data, *scramble)
|
|
bit_stream = bytes_to_bit_stream(data)
|
|
|
|
# last 3 columns are all white? we might have a valid QR-code
|
|
if all(bit_stream[row*32+29:row*32+32] == "000" for row in range(29)):
|
|
img = bit_stream_to_qr(bit_stream)
|
|
decoded_objects = decode(img)
|
|
if decoded_objects:
|
|
img.save(f"qr_code.png")
|
|
print("[+] Flag:", decoded_objects[0].data.decode())
|
|
exit()
|