Skip to content

Instantly share code, notes, and snippets.

@monoxgas
Last active July 18, 2023 22:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save monoxgas/b0e3ec74bc3d190f76951ac1ac9b6f2a to your computer and use it in GitHub Desktop.
Save monoxgas/b0e3ec74bc3d190f76951ac1ac9b6f2a to your computer and use it in GitHub Desktop.
VoiceCrypt Crypto
import sys
import struct
import binascii
from itertools import cycle, zip_longest
from operator import itemgetter, xor
from collections import Counter
import re
# Some root key constants from the binary
g_key = b'\x34\x8C\x55\x3E\xC7\xFA\x1E\x21\x28\x41\x4F\x6C\xBE\x81\xD0\xC5\xE3\x82\x2C\x7F\x70\xC0\x92\xB7\xAB\xBD\x3B\x25\xD1\xA7\x15\x78\x29\x06\x27\xA2\x71\x26\xAD\x5C\x4A\xDB\xC9\xFF\x2F\xE6\x4E\x2D\xE0\xA1\x86\x75\x68\xD7\x38\x08\x5A\xD4\xCA\x0D\xE2\xEE\x54\x13\xCF\x59\x1B\xE7\x14\x9A\xD5\xAF\xE4\xA4\xD8\x97\x60\x3D\xF2\x56\xA0\x51\x5E\x31\x30\x11\xD2\x93\x17\x7A\xC2\xE9\x12\x4B\x07\x39\x6B\xCD\x8B\xE1\xF6\x0F\x33\x24\x52\xB4\x89\x5D\x87\xFB\x09\x10\x61\x9B\x32\xC6\x72\x57\xC8\xAE\x74\x2A\x23\x16\xCB\xDF\x94\x42\x0C\x44\x3C\xED\x88\xAC\xF1\x45\x7B\xDE\x9C\xC4\xFC\xF9\x53\x99\x76\xB8\xDA\x20\x40\x8D\x0B\x8F\xB6\x2B\x01\x84\xC1\xD6\x69\xBA\xB1\xFD\x0A\x58\x65\x6F\x64\x62\xEF\x47\xF0\x77\x7E\xCC\x5F\x19\xC3\xA5\x91\xAA\xFE\x37\xA9\x63\x80\xB2\xEA\x8E\xA3\x48\x0E\xA8\x5B\x7D\x04\xB3\xEC\x6A\x67\xE5\xA6\xD9\x00\x7C\x18\x9F\xB0\x35\x79\x2E\x98\xE8\x95\xDC\xBB\x22\x46\x36\x83\x1F\xBC\x1A\x1D\x4D\xEB\x9E\x90\xBF\xF5\x05\x1C\xF7\x43\xD3\xCE\x49\xB5\x50\x4C\x85\x9D\x6D\x3F\xDD\x66\x96\xB9\xF4\x03\x3A\xF3\x6E\x02\x8A\xF8\x73'
g_key2 = b'\xCA\x9A\xFC\xF8\xC2\xE5\x21\x5E\x37\x6E\xA2\x96\x80\x3B\xBE\x65\x6F\x55\x5C\x3F\x44\x1E\x7B\x58\xCC\xAF\xDD\x42\xE6\xDE\x06\xDB\x93\x07\xD7\x7A\x67\x1B\x25\x22\x08\x20\x79\x99\x12\x2F\xD1\x2C\x54\x53\x72\x66\x00\xCF\xD9\xB5\x36\x5F\xF9\x1A\x82\x4D\x03\xF2\x94\x09\x7F\xE8\x81\x87\xD8\xA9\xBD\xEB\x28\x5D\xEE\xDF\x2E\x0A\xED\x51\x68\x8E\x3E\x02\x4F\x75\xA3\x41\x38\xC0\x27\x6B\x52\xAE\x4C\x70\xA7\xB7\xA6\xA4\xF4\xC6\x34\x9E\xC5\x60\x0B\xF1\xFB\xA5\x14\x24\x74\xFF\x78\x33\x90\xAB\x1F\xD0\x59\x88\xCB\xC1\xAC\x13\xB8\x0D\x11\xDA\x9B\xEF\x32\x6C\x84\x6A\xFD\x62\x01\x95\xBB\x97\xE2\xB2\x16\x57\x7E\xD4\xF5\x4B\xD2\x8F\x45\x71\x8A\xF0\xE1\xCD\x50\x31\x23\xBC\x49\xB1\xC8\x1D\xBF\xB6\xB3\x18\x85\x26\x77\x47\xCE\xA0\xB9\xC3\x69\xEC\x98\x17\x91\xF6\x9F\xD6\xDC\x19\x0C\xE3\x15\x9C\x5A\xB0\x8B\x0F\x73\x04\x76\x2A\x3A\x7C\xAD\x61\xEA\x40\x0E\x1C\x56\xE9\x39\x46\x9D\x35\x4A\xC9\x92\x29\xD5\xF3\x89\x7D\x30\x63\x3C\x10\x48\xC7\x2D\x43\xD3\x5B\xBA\xE0\xC4\x83\x3D\xA8\xAA\x86\x4E\xFA\xF7\xE4\x64\xE7\xFE\x8D\x05\x6D\x8C\xA1\xB4\x2B'
KeySize = 32
# Helper functions
def keystream_from_rand(seed, length):
out = []
for i in range(length):
seed = (214013 * seed + 2531011) & 0x7fffffff
rand = ((seed >> 16) & 0xff)
out.append(((rand >> 31) ^ abs(rand) - (rand >> 31)))
return bytes(out)
def xor(d, k):
return bytearray([a ^ b for a, b in zip(d, cycle(k))])
def xor_with_rand(data, seed):
out = []
for b in data:
seed = (214013 * seed + 2531011) & 0x7fffffff
out.append(g_key[b ^ ((seed >> 16) & 0xff)])
return bytes(out)
def reverse_xor_k1(data, key):
data = bytearray(data)
i = len(data)
while i > 0:
i -= 1
data[i] = g_key[data[i] ^ key[i % len(key)]]
return bytes(data)
def reverse_xor_k2(data, key):
data = bytearray(data)
i = len(data)
while i > 0:
i -= 1
data[i] = g_key2[data[i] ^ key[i % len(key)]]
return bytes(data)
def xor_k1(data, key):
data = bytearray(data)
i = len(data)
while i > 0:
i -= 1
data[i] = g_key[data[i]] ^ key[i % len(key)]
return bytes(data)
def xor_k2(data, key):
data = bytearray(data)
i = len(data)
while i > 0:
i -= 1
data[i] = g_key2[data[i]] ^ key[i % len(key)]
return bytes(data)
def chunkinto(d, s):
return [d[i:i + s] for i in range(0, len(d), s)]
def transpose(l):
return list(zip_longest(*l, fillvalue=0))
top_english = {c: i for i, c in enumerate(list('etaoinsrhdlucmfywgpbvkxqjz'))}
scoring_factor = 26
def score_english(data):
score = 0
try: text = data.decode('ascii').lower()
except: return score
if re.search(r'[^\x20-\x7e]', text):
return score
m_common = Counter(text).most_common()[:scoring_factor]
for i, data in enumerate(m_common):
(char,_) = data
if char in top_english.keys():
diff = i - top_english[char]
score += (scoring_factor - abs(diff))
return score
# --- Start ---
raw = open(sys.argv[1], 'rb').read()
# First step is to use one of the header values in srand()
# to decrypt the file suffix.
# XORing each byte with rand() yeilds an offset to g_key
# The output is some sort of "suffix", which includes
# the original file name, along with the original
# first 16 bytes of the encrypted block
data_len, data_seed, suffix_lengh, suffix_seed = struct.unpack('iiii', raw[:16])
enc_suffix = raw[data_len:]
assert len(enc_suffix) == suffix_lengh
suffix = xor_with_rand(enc_suffix, suffix_seed)
paths = suffix[72:].split(b'\x00')[:2]
paths.reverse()
print('\n[+] Original: ' + '\\'.join([p.decode() for p in paths]))
# Once we restore the "real" first 16 bytes
# we should now have the encrypted block.
# The size should match the first 4 bytes
# of the file.
packed = struct.unpack('IIII', suffix[56:56+16])
enc_data = struct.pack('>IIII', *packed) + raw[16:data_len]
assert len(enc_data) == data_len
# This encrypted stream is simply our plaintext
# XORed against a keystream. We can use known-plaintext
# attacks or even english analysis to recover the 32
# byte key
blocks = chunkinto(enc_data, KeySize)
tblocks = transpose(blocks)
real = open(sys.argv[1].replace('vtxt', 'txt'), 'rb').read()
rblocks = transpose(chunkinto(real, KeySize))
key = bytearray(KeySize)
for kp, tb in enumerate(tblocks):
scores = []
for k in range(1, 255):
e = bytearray(tb)
for i in range(len(e)):
e[i] = g_key[e[i] ^ k]
if e[:2] == bytearray(rblocks[kp])[:2]:
key[kp] = k
# score = score_english(e)
# scores.append([score, k, e])
# scores = sorted(scores, key=itemgetter(0), reverse=True)
# key[kp] = scores[0][1]
# print(scores[:3], end="\r\n\r\n")
print('[+] Recovered Key : ' + binascii.hexlify(key).decode())
print('\n[+] Dec: ', end='')
print(reverse_xor_k1(enc_data, key))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment