Skip to content

Instantly share code, notes, and snippets.

@ubuntor
Created August 19, 2021 23:46
Show Gist options
  • Save ubuntor/8bce07490a4cba5bad81ab45e094b55a to your computer and use it in GitHub Desktop.
Save ubuntor/8bce07490a4cba5bad81ab45e094b55a to your computer and use it in GitHub Desktop.
CONS decompression

run each part in order

  • part1.py: carve lzma from firmware
  • part2.html: decompress lzma
    • can access via python -m http.server
    • make sure compressed.lzma from part1.py is in the same directory
    • check console for progress
  • part3.py: decode SPARC BCJ filter
    • make sure extracted from part2.html is in the same directory
import struct
def u32(b, endian='big'):
e = '>' if endian == 'big' else '<'
return struct.unpack(e+'I', b)[0]
with open('DMR-EZ485V firmware.bin','rb') as f:
firmware = f.read()
cons = firmware[0x40000:] # start of CONS
assert cons[0:4] == b'CONS' # CONS header
assert cons[12:14] == b'\xba\xbe' # "Bal Header"
compression_type = cons[20]
'''
compression types:
0x21: INFLATE
0x22: LZMA
0x24: LZMA (SPARC)
'''
# assuming LZMA (SPARC)
assert compression_type == 0x24, f"compression type {hex(compression_type)} not supported"
compressed_size = u32(cons[8:12])
print(hex(compressed_size))
compressed = cons[0x20:0x20+compressed_size]
open("compressed.lzma","wb").write(compressed)
<!doctype html>
<head>
<meta charset="utf-8">
<title>LZMA</title>
<script src="https://cdn.jsdelivr.net/npm/lzma@2.3.2/src/lzma-d.js" integrity="sha256-WZWlL+5I6wmZdtdU3o3aHV0rjwQAwjk/rvrMS79+b3s=" crossorigin="anonymous"></script>
</head>
<body>
<script>
const downloadURL = (data, fileName) => {
const a = document.createElement('a')
a.href = data
a.download = fileName
document.body.appendChild(a)
a.style.display = 'none'
a.click()
a.remove()
}
const downloadBlob = (data, fileName, mimeType) => {
const blob = new Blob([data], {
type: mimeType
})
const url = window.URL.createObjectURL(blob)
downloadURL(url, fileName)
setTimeout(() => window.URL.revokeObjectURL(url), 1000)
}
async function main() {
const r = await fetch("compressed.lzma");
const data = await r.arrayBuffer();
const arr = new Uint8Array(data);
console.log(arr);
LZMA.decompress(arr, (result, error) => {
console.log(result);
console.log(error);
downloadBlob(new Uint8Array(result), "extracted", "application/octet-stream");
}, (percent) => {
console.log(percent);
});
}
main();
</script>
</body>
</html>
import struct
def u32(b, endian='big'):
e = '>' if endian == 'big' else '<'
return struct.unpack(e+'I', b)[0]
def p32(x, endian='big'):
e = '>' if endian == 'big' else '<'
return struct.pack(e+'I', x)
with open('extracted','rb') as f:
data = bytearray(f.read())
# SPARC BCJ filter decode
for i in range(0, len(data)-3, 4):
if (data[i] == 0x40 and data[i+1] & 0xC0 == 0) or (data[i] == 0x7F and data[i+1] & 0xC0 == 0xC0):
old = data[i:i+4]
src = u32(old)
src <<= 2
dest = src - i
dest >>= 2
dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000
data[i:i+4] = p32(dest)
print(f'patched {i:x}: {old} -> {data[i:i+4]}')
with open("out_final","wb") as f:
f.write(data)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment