Skip to content

Instantly share code, notes, and snippets.

@qtc-de
Last active July 18, 2023 10:18
Show Gist options
  • Save qtc-de/b3ec429317247f644f101269e2d623bc to your computer and use it in GitHub Desktop.
Save qtc-de/b3ec429317247f644f101269e2d623bc to your computer and use it in GitHub Desktop.
XOR All The Things! Python script that searches for byte representations within the specified file or input and xors them with the specified key.
#!/usr/bin/env python3
import re
import sys
import argparse
class Xor:
'''
Helper class for performing xor operations.
'''
def __init__(self, key: str, verbose: bool) -> None:
'''
An Xor object is initialized with the key that is used to xor.
The key is expected to be a string that contains a hex sequence.
Parameters:
key xor key as hex sequence (e.g. aabbff)
verbose print verbose warning messages on xor errors
Returns:
None
'''
self.ctr = 0
self.key = bytes.fromhex(key)
self.key_len = len(self.key)
self.verbose = verbose
def apply_xor(self, byte: int) -> int:
'''
Obtains the current xor byte within the key and xors the given input
with it. Each xor iteration increments the internal counter and uses
the next byte within the key.
Parameters:
byte byte to xor with
Returns:
xor_byte current xor byte to use
'''
if byte < 0x00 or byte > 0xff:
raise ValueError()
xor_byte = self.key[self.ctr % self.key_len]
self.ctr += 1
return xor_byte ^ byte
def apply_match(self, re_match) -> str:
'''
Applies the xor operation to a regular expression match. The function
first determines what is contained within the match and applies the
corresponding apply function afterwards.
Parameters:
re_match regular expression match from the re library
Returns:
xor_replace xored replacement for the match
'''
stuff = re_match.group()
try:
if stuff.startswith("0x") or stuff.startswith(r"\x"):
return self.apply_hex(stuff)
elif stuff.startswith("0o"):
return self.apply_oct(stuff)
elif stuff.startswith("0b"):
return self.apply_bin(stuff)
else:
return self.apply_dec(stuff)
except ValueError:
if self.verbose:
sys.stderr.write(f"[!] Ignoring invalid byte: {stuff}\n")
return stuff
def apply_hex(self, string: str) -> str:
'''
Applies the xor operation to an incoming string that is expected to
be hex formatted.
Parameters:
string hex formatted input string
Returns:
xor_replace xored replacement for the string
'''
prefix = string[0:2]
value = int(string[2:], 16)
xor = self.apply_xor(value)
return '{}{:02x}'.format(prefix, xor)
def apply_oct(self, string: str) -> str:
'''
Applies the xor operation to an incoming string that is expected to
be octal formatted.
Parameters:
string oct formatted input string
Returns:
xor_replace xored replacement for the string
'''
prefix = string[0:2]
value = int(string[2:], 8)
xor = self.apply_xor(value)
return '{}{:03o}'.format(prefix, xor)
def apply_bin(self, string: str) -> str:
'''
Applies the xor operation to an incoming string that is expected to
be binary formatted.
Parameters:
string binary formatted input string
Returns:
xor_replace xored replacement for the string
'''
prefix = string[0:2]
value = int(string[2:], 2)
xor = self.apply_xor(value)
return '{}{:08b}'.format(prefix, xor)
def apply_dec(self, string: str) -> str:
'''
Applies the xor operation to an incoming string that is expected to
contain plain numbers.
Parameters:
string string containing numbers
Returns:
xor_replace xored replacement for the string
'''
value = int(string, 10)
xor = self.apply_xor(value)
return '{}'.format(xor)
def apply_char(self, char: str) -> int:
'''
Applies the xor operation to an incoming character. This is currently
only used then the --raw option is specified.
Parameters:
char incoming character
Returns:
xor_replace xored replacement for the character
'''
value = char
xor = self.apply_xor(value)
return bytes([xor])
parser = argparse.ArgumentParser(description='''xor v1.1.0 - xor encode the given file or input. When --raw is used, the whole
input is treated as raw bytes and gets encoded. If not specified, the script
searches for byte representations like 0xab, 0o111, 0b10101, 111, etc. and
applies the xor operation on them.''')
parser.add_argument('infile', nargs='?', default=sys.stdin, type=argparse.FileType('rb'), help='filepath (default: stdin)')
parser.add_argument('--key', default='ae', help='xor key to use (default: ae)')
parser.add_argument('--raw', action='store_true', help='encode raw input byte by byte')
parser.add_argument('-v', '--verbose', action='store_true', help='show xor warning on invalid bytes')
args = parser.parse_args()
try:
content = args.infile.read()
xor = Xor(args.key, args.verbose)
if args.raw:
output = b''
for char in content:
output += xor.apply_char(char)
sys.stdout.buffer.write(output)
else:
content = content.decode()
regex = re.compile(r"(?:(?<=[ ,.:|+-])|^)" + "(?:" + # Match bytes if prefixed by a separator (or at start)
r"0x[0-9a-f]{1,2}|\\x[0-9a-f]{1,2}" + "|" + # Hex pattern
r"0b[01]{1,8}" + "|" + # Bin pattern
r"0o[0-7]{1,3}" + "|" + # Oct pattern
r"\d{1,3}" + # Dec pattern
r")" + "(?=[ ,:.|+-]|$)") # Match bytes if suffixed by a separator (or at end)
output = regex.sub(xor.apply_match, content)
print(output, end='')
except KeyboardInterrupt:
print("[-] Aborted.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment