Skip to content

Instantly share code, notes, and snippets.

@danny8376
Last active March 10, 2025 16:51
Show Gist options
  • Save danny8376/fd7104396827adeabb8c13780a6f65c6 to your computer and use it in GitHub Desktop.
Save danny8376/fd7104396827adeabb8c13780a6f65c6 to your computer and use it in GitHub Desktop.
Put corrupted OTP (named otp.mem, same as GodMode9 name) in the same location, run it, then the fixed OTP will be named otp_fixed.mem (will still write it even nothing is fixed)
from pyctr.crypto import CryptoEngine
from Cryptodome.Cipher import AES
from hashlib import sha256
import itertools
a = CryptoEngine()
def print_diff(ori, enc, dec, postxt, *suffix):
if postxt == None:
return
if type(postxt) is tuple:
s = len(enc) // len(postxt)
for x in range(len(enc) // s):
b, e = x * s, (x + 1) * s
xori, xenc, xdec = ori[b:e], enc[b:e], dec[b:e]
if xori != xenc:
print(f"{postxt[x]} ori:", xori.hex(), *suffix)
print(f"{postxt[x]} fix:", xenc.hex(), xdec.hex(), *suffix)
elif ori != enc:
print(f"{postxt} ori:", ori.hex(), *suffix)
print(f"{postxt} fix:", enc.hex(), dec.hex(), *suffix)
def decrypt_otp_block(data, iv):
cipher = AES.new(a.otp_key, AES.MODE_CBC, iv)
return cipher.decrypt(data)
def crack_singlebit(indata, iv, check, postxt):
#if check(decrypt_otp_block(indata, iv)):
# return indata
ret = None
ints = bytearray(indata)
for x in range(len(indata)):
orig = ints[x]
for bit in range(8):
new = orig ^ (1 << bit)
ints[x] = new
data = decrypt_otp_block(ints, iv)
if check(data):
print_diff(indata, ints, data, postxt, "(single bit)")
ret = ints
return ret
ints[x] = orig
return ret
def crack_dataline(indata, iv, bit, check, postxt):
#if check(decrypt_otp_block(indata, iv)):
# return indata
set_size = len(indata) // 4
ret = None
for combi in range(pow(2, set_size)):
ints = bytearray(indata)
for addr in range(set_size):
if (combi >> addr) & 1:
byte = 4 * addr + bit // 8
orig = ints[byte]
ints[byte] = orig ^ (1 << (bit % 8))
cipher = AES.new(a.otp_key, AES.MODE_CBC, iv)
data = cipher.decrypt(ints)
if check(data):
print_diff(indata, ints, data, postxt, "(data line)")
ret = ints
return ret
return ret
def crack_singlebit_dataline(indata, extract, check, postxt, bit = -1):
indata, iv = extract(indata)
ret = (-1, b"\x00" * len(indata))
if check(decrypt_otp_block(indata, iv)):
if bit == -1:
bit = -2
return (bit, indata)
if bit == -1 or bit == 32:
res = crack_singlebit(indata, iv, check, postxt)
if res != None:
ret = (32, res)
return ret
#if bit == 32: # force sigle bit -> fail
# return ret
if bit < 0: # -1: default, -2: no need to fix previously
for bit in range(32):
res = crack_dataline(indata, iv, bit, check, postxt)
if res != None:
ret = (bit, res)
return ret
else:
res = crack_dataline(indata, iv, bit, check, postxt)
if res != None:
ret = (bit, res)
return ret
return ret
def crack0x00(otp2fix):
def extract(data):
return data[0:0x10], a.otp_iv
def check(data):
return data[0:4] == b"\x0f\xb0\xad\xde" and data[0xD:0x10] == b"\x00\x00\x00" and data[0xC] in (0, 2)
ret = crack_singlebit_dataline(otp2fix, extract, check, "0x00")
if ret[0] == -1:
print("Can't fix 0x00")
exit(1)
return ret
def crack0x10(otp2fix, bit):
def extract(data):
return data[0x10:0x20], data[0:0x10]
def check(data):
return data[0x8:0xA] == b"\x05\x00"
ret = crack_singlebit_dataline(otp2fix, extract, check, "0x10", bit)
if ret[0] == -1:
print("Can't fix 0x10")
exit(1)
return ret
def crack0x70x80(otp2fix, bit):
def extract(data):
return data[0x70:0x90], data[0x60:0x70]
def check(data):
return data[0x10:0x20] == b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
ret = crack_singlebit_dataline(otp2fix, extract, check, ("0x70", "0x80"), bit)
if ret[0] == -1:
print("can't fix 0x70, 0x80")
#exit(1)
return ret
def showbits(bit):
for addr in range(0x100 // 4):
byte = 4 * addr + bit // 8
orig = otp[byte]
print((orig >> (bit % 8)) & 1, end="\t")
if addr % 4 == 3:
print()
def verify(ints):
cipher = AES.new(a.otp_key, AES.MODE_CBC, a.otp_iv)
data = cipher.decrypt(ints)
ohash = data[0xE0:]
before_hash = data[0:0xE0]
hash_before_hash = sha256(before_hash).hexdigest()
return data[0:4].hex() == "0fb0adde" and hash_before_hash == ohash.hex()
def crack_bits_by_hash(otp2fix, byte_range, bit_count=1):
#[forked_iter] = itertools.tee(byte_range, 1)
#for bytei in forked_iter:
for bytei in byte_range:
ogbyte=otp2fix[bytei]
for biti in range(8):
otp2fix[bytei] = ogbyte ^ (1 << biti)
if bit_count > 1:
crack_bits_by_hash(otp2fix, byte_range, bit_count - 1)
if verify(otp2fix):
return otp2fix
otp2fix[bytei] = ogbyte
with open('otp.mem', 'rb') as f:
content = f.read(0x100)
otp = bytearray(content)
bit, otp[0:0x10] = crack0x00(otp)
#showbits(bit)
bit, otp[0x10:0x20] = crack0x10(otp, bit)
bit, otp[0x70:0x90] = crack0x70x80(otp, bit)
#showbits(bit)
#remaining_range = itertools.chain(range(0x20, 0x70), range(0x90, 0x100))
remaining_range = list(range(0x20, 0x70)) + list(range(0x90, 0x100))
bit_count = 0
while not verify(otp):
bit_count += 1
if bit_count <= 1:
print(f"Can't be fixed by just pattern.")
print(f"Brute forcing non-patterned part for all possible {bit_count} bit flip...")
crack_bits_by_hash(otp, remaining_range, bit_count)
for x in range(0x10):
print("--------------------------------")
start = 0x10 * x
end = start + 0x10
og=content[start:end]
inmem=otp[start:end]
print(og.hex())
print(inmem.hex())
print("same" if og == inmem else "fixed")
with open('otp_fixed.mem', 'wb') as f:
f.write(otp)
#for bit in range(32):
# ints = bytearray(content[0:0x10])
# for combi in range(pow(2, 0x10 // 4)):
# for addr in range(0, 0x10, 4):
# if (combi >> addr) & 1:
# byte = addr + bit // 8
# orig = ints[byte]
# ints[byte] = orig ^ (1 << (bit % 8))
# cipher = AES.new(a.otp_key, AES.MODE_CBC, a.otp_iv)
# data = cipher.decrypt(ints)
# ohash = data[0xE0:]
# before_hash = data[0:0xE0]
# hash_before_hash = sha256(before_hash).hexdigest()
# if data[0:4].hex() == "0fb0adde":
# print(data[0:4].hex(), bit, combi)
# #print(bit, hash_before_hash, ohash.hex(), hash_before_hash == ohash.hex())
# if hash_before_hash == ohash.hex():
# print(beginning + before_hash + ohash)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment