Skip to content

Instantly share code, notes, and snippets.

@Decencies
Forked from castleberrysam/fw_decrypt.py
Last active November 19, 2023 02:33
Show Gist options
  • Save Decencies/0136e02cb5e9ccb06a1cd3a2c8639bbf to your computer and use it in GitHub Desktop.
Save Decencies/0136e02cb5e9ccb06a1cd3a2c8639bbf to your computer and use it in GitHub Desktop.
Image extractor/decrypter for Beiying K8xx/K9xx (sinowealth SH6xFxxx/SH7xFxxx) firmware update files
#!/usr/bin/env python3
from struct import *
from argparse import *
# Royal Kludge vendor ID
rk_venor = 0x258a
# Update File magic bytes
upf_magic = b'\x5a\xa5SINO'
k1 = 0xc6ed3720
k2 = 0x61c89647
def decrypt8(cw: int, nw: int, k1: int, k2: int, key: bytes) -> tuple[int, int]:
"""
Decrypt the 32 bits within the low and high words, using the high, low & firmware keys.
@param cw the current (low) word
@param nw the next (high) word
@param k1 the first (high) key
@param k1 the second (low) key
@param key the 4 byte firmware key
@return the new high and low words after decryption
"""
# accumulator for rolling key
acc = k1
for _ in range(32):
# Round 1 (high)
diff = (key[3] + (cw >> 5)) & 0xffffffff
xor = (key[2] + (cw << 4)) & 0xffffffff
diff ^= xor
diff ^= (acc + cw) & 0xffffffff
nw = (nw - diff) & 0xffffffff
# Round 2 (low)
diff = (key[0] + (nw << 4)) & 0xffffffff
xor = (key[1] + (nw >> 5)) & 0xffffffff
diff ^= xor
diff ^= (acc + nw) & 0xffffffff
cw = (cw - diff) & 0xffffffff
acc = (acc + k2) & 0xffffffff
return cw, nw
def decrypt(data: bytes, k1: int, k2: int, key: bytes) -> bytes:
"""
Decrypt the firmware data in chunks, using the high, low & firmware keys.
@param data the firmware data
@param k1 the first (high) key
@param k1 the second (low) key
@param key the 4 byte firmware key
@return the decrypted firmware bytes
"""
# unpack the data as uints
words = [unpack_from('>I', data, i * 4)[0] for i in range(len(data) // 4)]
print(f'decrypting {len(data) // 8} chunks...', end=' ')
# read & decrypt the words
for i in range(len(data) // 8):
lo, hi = i * 2, i * 2 + 1
# define, decrypt & swap the current and next words
cw, nw = words[lo], words[hi]
cw, nw = decrypt8(cw, nw, k1, k2, key)
words[lo], words[hi] = cw, nw
print('done!')
return b''.join(pack('>I', val) for val in words)
def main(upf, out, k1: int, k2: int, ven: int):
magic = upf.read(len(upf_magic))
# todo: some firmware starts with 5aa5424c45
assert magic == upf_magic, f'invalid upf magic {magic:06x}'
ven_id, pid, fw_size = unpack('>HHH', upf.read(6))
# todo: can it be different?
assert ven_id == rk_venor, f'invalid ven_id {ven_id:04x}'
# todo: can it be larger than 64K?
assert fw_size <= 0x10000, f'fw too big! ({fw_size:04x}>0xf000)'
fw_data = upf.read(fw_size)
key = upf.read(4)
print('-' * 32)
print(f'pid: {pid:04x}')
print(f'size: {fw_size:04x}')
print(f"key: {unpack('<I', key)[0]:08x}")
print('-' * 32)
out.write(decrypt(fw_data, k1, k2, key))
print('-' * 32)
print(f'wrote firmware to {out.name}')
if __name__ == '__main__':
p = ArgumentParser('sinodump', add_help=False)
p.add_argument('upf', type=FileType('rb'))
p.add_argument('out', type=FileType('wb'))
p.add_argument('--k1', type=int, default=k1)
p.add_argument('--k2', type=int, default=k2)
args = p.parse_args()
main(args.upf, args.out, args.k1, args.k2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment