Init
This commit is contained in:
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Brute-force checksum hypotheses for cmtd+0x08 against all known images.
|
||||
|
||||
Tests CRC32/zlib/adler/fletcher/sum/xor variants over multiple spans.
|
||||
A hypothesis must match ALL builds to be reported as candidate.
|
||||
Stdlib only.
|
||||
"""
|
||||
import binascii, os, struct, sys, zlib
|
||||
|
||||
BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
IMAGES = [
|
||||
'Lofi-12XT_v1.1.156/Lofi-12XT_v1.1.156/Lofi-12 XT.bin',
|
||||
'Lofi-12XT_v1.2.179/Lofi-12XT_v1.2.179/Lofi-12 XT.bin',
|
||||
'Lofi-12XT_v1.5.205/Lofi-12XT_v1.5.205/Lofi-12 XT.bin',
|
||||
]
|
||||
|
||||
def fletcher16(data):
|
||||
s1 = s2 = 0
|
||||
for b in data:
|
||||
s1 = (s1 + b) % 255
|
||||
s2 = (s2 + s1) % 255
|
||||
return (s2 << 8) | s1
|
||||
|
||||
def fletcher32(data):
|
||||
s1 = s2 = 0
|
||||
for i in range(0, len(data), 2):
|
||||
w = data[i] | (data[i+1] << 8 if i+1 < len(data) else 0)
|
||||
s1 = (s1 + w) % 0xFFFF
|
||||
s2 = (s2 + s1) % 0xFFFF
|
||||
return (s2 << 16) | s1
|
||||
|
||||
def sum32(data):
|
||||
return sum(data) & 0xFFFFFFFF
|
||||
|
||||
def xor32(data):
|
||||
r = 0
|
||||
for i in range(0, len(data) - 3, 4):
|
||||
(w,) = struct.unpack('<I', data[i:i+4])
|
||||
r ^= w
|
||||
return r & 0xFFFFFFFF
|
||||
|
||||
def load():
|
||||
out = []
|
||||
for rel in IMAGES:
|
||||
p = os.path.join(BASE, rel)
|
||||
d = open(p, 'rb').read()
|
||||
ck = struct.unpack('<I', d[8:12])[0]
|
||||
out.append((rel.split('/')[0], d, ck))
|
||||
return out
|
||||
|
||||
def spans(d):
|
||||
nsect = struct.unpack('<I', d[0x50:0x54])[0]
|
||||
off = 0x54
|
||||
payloads = b''
|
||||
for _ in range(nsect):
|
||||
ln = struct.unpack('<I', d[off+8:off+12])[0]
|
||||
payloads += d[off+12:off+12+ln]
|
||||
off += 12 + ln
|
||||
z8 = bytearray(d); z8[8:12] = b'\0\0\0\0'
|
||||
return {
|
||||
'full': d,
|
||||
'from_0x04': d[0x04:],
|
||||
'from_0x0C': d[0x0C:],
|
||||
'from_0x30': d[0x30:],
|
||||
'from_0x54': d[0x54:],
|
||||
'payload_concat': payloads,
|
||||
'full_ck_zeroed': bytes(z8),
|
||||
'from0x0C_ck_zeroed': bytes(z8)[0x0C:],
|
||||
}
|
||||
|
||||
def main():
|
||||
blobs = load()
|
||||
for name, d, ck in blobs:
|
||||
print(f"{name}: size={len(d)} stored_ck={ck:08X}")
|
||||
algs = {
|
||||
'crc32_le': lambda b: binascii.crc32(b) & 0xFFFFFFFF,
|
||||
'crc32_be_byteswap': lambda b: struct.unpack('>I', struct.pack('<I', binascii.crc32(b) & 0xFFFFFFFF))[0],
|
||||
'crc32_complement': lambda b: (~binascii.crc32(b)) & 0xFFFFFFFF,
|
||||
'adler32': lambda b: zlib.adler32(b) & 0xFFFFFFFF,
|
||||
'fletcher16': fletcher16,
|
||||
'fletcher32': fletcher32,
|
||||
'sum32': sum32,
|
||||
'xor32': xor32,
|
||||
}
|
||||
cands = []
|
||||
total = 0
|
||||
for aname, fn in algs.items():
|
||||
for sname in spans(blobs[0][1]):
|
||||
total += 1
|
||||
ok = True
|
||||
for _, d, ck in blobs:
|
||||
try:
|
||||
v = fn(spans(d)[sname]) & 0xFFFFFFFF
|
||||
except Exception:
|
||||
ok = False
|
||||
break
|
||||
if v != ck:
|
||||
ok = False
|
||||
break
|
||||
status = 'MATCH-ALL' if ok else 'no'
|
||||
if ok:
|
||||
cands.append((aname, sname))
|
||||
# show near-misses? only print matches to stay concise
|
||||
if ok:
|
||||
print(f" {aname} x {sname}: {status}")
|
||||
print(f"tested {total} hypotheses, {len(cands)} full-match candidates")
|
||||
if not cands:
|
||||
print("No match: checksum is not plain CRC32/adler/fletcher/sum/xor over obvious spans.")
|
||||
print("Next: try seeded CRC, TI-AIS style, per-sect accumulate, or mmtd.flags correlation.")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user