Skip to content

Instantly share code, notes, and snippets.

@WangYihang
Created November 26, 2021 07:04
Show Gist options
  • Save WangYihang/18b3a837203dc02a7fbd01e7480fee0e to your computer and use it in GitHub Desktop.
Save WangYihang/18b3a837203dc02a7fbd01e7480fee0e to your computer and use it in GitHub Desktop.
Solution for scz's XOR challenge (http://scz.617.cn:8/misc/202007101723.txt)
'''
Solution for XOR challenge (http://scz.617.cn:8/misc/202007101723.txt)
'''
from termcolor import colored
from colorama import init
import string
import math
import shlex
init()
def padding(a, b):
'''
repeatly padding b to length(a)
eg:
a = "123456789"
b = "abcd"
padding(a, b) -> "abcdabcda"
'''
q = int(len(a) / len(b))
r = len(a) % len(b)
return b * q + b[:r]
def xor(a, b):
if len(a) > len(b):
b = padding(a, b)
if len(b) < len(a):
a = padding(b, a)
assert len(a) == len(b)
r = [0 for _ in range(len(a))]
for i in range(len(a)):
r[i] = a[i] ^ b[i]
return bytes(r)
def wrap(a, b):
'''
wrap a to a array of string which length is 4
eg:
a = "123456789"
b = 4
wrap(a, b) -> ["1234", "5678", "9"]
'''
r = []
for i in range(0, len(a), b):
r.append(a[i:i+b])
return r
def is_english_article(ch):
return ch in string.ascii_letters + string.digits + " ,.?"
def is_base64(ch):
return ch in string.ascii_letters + string.digits + "+/="
def is_printable(ch):
return ch in string.printable
def dump(data):
for value in data:
if chr(value) in string.printable:
print(chr(value), end="")
else:
print(".", end="")
def dump_with_index(data):
for index, value in enumerate(data):
if chr(value) in string.printable:
r = chr(value)
else:
r = "."
print("plain[{}] = {}".format(index, r))
def dump_wrapped(data, length, hl_x=-1, hl_y=-1):
def hl_print(ch):
if is_printable(ch):
ch = ch
color = "green"
else:
ch = "?"
color = "red"
print(colored(ch, color), end="")
def unhl_print(ch):
if is_printable(ch):
ch = ch
else:
ch = "?"
print(ch, end="")
blocks = wrap(data, length)
for y, block in enumerate(blocks):
for x, value in enumerate(block):
if x == hl_x or y == hl_y:
print_func = hl_print
else:
print_func = unhl_print
ch = chr(value)
print_func(ch)
print()
def plain_should_be(cipher, cipher_index, plain_value, key):
fixed_key = key.copy()
fixed_key[cipher_index %
len(fixed_key)] = cipher[cipher_index] ^ ord(plain_value)
return fixed_key
def guess_key(data, key_length=64):
key = [0 for i in range(key_length)]
blocks = wrap(data, len(key))
possible_values = {}
for i in range(len(key)):
possible_values[i] = []
for j in range(0x100):
key[i] = j
all_prinable = True
for block in blocks:
plain = xor(key, block)
if not is_english_article(chr(plain[i])):
all_prinable = False
if all_prinable:
possible_values[i].append(j)
guessed_key = [0 for _ in range(key_length)]
succeed = 0
for k, v in possible_values.items():
if len(v) == 1:
guessed_key[k] = v[0]
succeed += 1
if succeed == 0:
print("{} -> {:.2f}".format(key_length, succeed / len(key)), end="\r")
else:
print("{} -> {:.2f}".format(key_length, succeed / len(key)), end="\n")
return guessed_key
def interactive_guessing(data, key):
changes = []
current_x = 0
current_y = 0
max_x = len(key)
max_y = math.ceil(len(data) / len(key))
usage = '''Move:
[up|down|left|right] [value]
Set:
[value]'''
print(usage)
while True:
# dump memory
dump_wrapped(xor(data, key), len(key), hl_x=current_x, hl_y=current_y)
# parse arguments
args = shlex.split(
input(">> ").strip()
)
print(args)
if len(args) == 0:
continue
# set value
if len(args) == 1:
index = current_y * len(key) + current_x
value = args[0][0]
key = plain_should_be(data, index, value, key)
dump_wrapped(xor(data, key), len(key),
hl_x=current_x, hl_y=current_y)
changes.append((index, value))
print(changes)
continue
# move header
if len(args) == 2:
direction = args[0]
value = int(args[1])
print("moving {} steps towards {} from ({}, {})".format(
value, direction, current_x, current_y))
if direction == "up":
current_y = (current_y - value) % max_y
elif direction == "down":
current_y = (current_y + value) % max_y
elif direction == "left":
current_x = (current_x - value) % max_x
elif direction == "right":
current_x = (current_x + value) % max_x
else:
pass
continue
def challenge_01():
data = open("xor.bin", "rb").read()
key = guess_key(data)
# interactive_guessing(data, key)
changes = [(1, 'h'), (1, 'r'), (1, 'r'), (1, 'h'), (2, 'e'), (3, ' '), (14, 'o'), (20, 'e'), (23, 'r'), (26, 'e'), (55, 'c'), (183, 'l'), (291, 'v'), (293, 'c'), (741, 's'), (739, 'e'),
(478, 'a'), (351, 'O'), (362, 'n'), (382, ' '), (647, 'r'), (581, 'e'), (580, 'r'), (454, 'e'), (875, 'd'), (876, 'o'), (876, 'r'), (876, 'o'), (877, ' '), (877, '-'), (831, 'e')]
for index, value in changes:
key = plain_should_be(data, index, value, key)
print(key)
key = [190, 189, 128, 34, 3, 150, 173, 251, 17, 75, 13, 169, 164, 133, 124, 209, 237, 206, 2, 99, 62, 87, 84, 205, 200, 29, 166, 151, 158, 42, 250, 249,
176, 104, 1, 115, 179, 161, 105, 228, 124, 183, 238, 135, 101, 179, 218, 215, 152, 1, 41, 122, 156, 8, 116, 21, 231, 155, 161, 225, 115, 235, 196, 176]
print(xor(data, key).decode())
challenge_01()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment