# StoneD (Stone Depacker) - unpack Unisoc SC6531 stone images (part of UniFlash) # Created by Luxferre in 2021, released into public domain import os import sys import struct import lzma from custlzma.frenchlzma import DecodeurLZMASPD # (for LZMA_SPD decompression) # common utils def readFile(fname): f = open(fname, 'rb') fdata = f.read() f.close() return fdata def writeFile(fname, fdata): outf = open(fname, 'wb') outf.write(fdata) outf.close() # unpack part CMP_NONE=0 CMP_LZMA_SPRD=1 CMP_LZMA=2 def getCompType(data): if (data[0] == 0x5d or data[0] == 0x67) and data[1] == 0: return CMP_LZMA elif data[0] == 0x5a and data[1] == 0: return CMP_LZMA_SPRD else: return CMP_NONE def getTblOffset(blocksOffTbl, index): tind = index << 2 return struct.unpack(' kernel image targetFile = targetDir + '/kern.bin' elif blkId == 0x75736572: # resu -> user image targetFile = targetDir + '/user.bin' elif blkId == 0x7253736F: # resources targetFile = targetDir + '/rsrc.bin' else: targetFile = targetDir + ('/blk_%X.bin' % blkId) unpack_block(sectionData[blkDataOffset:], blkPacSize, targetFile) def unpack_stone(fname, targetDir): fdata = readFile(fname) flen = len(fdata) assert flen >= 0x10, 'Input file %s is too small' % fname # check for security header sectionOffset = 0 if fdata[0:15] == b'SPRD-SECUREFLAG': sectionOffset = 1024 print('Signed image detected, using section offset %d' % sectionOffset) # look for TRAPGAMI header startPos = -1 for i in range(flen): if fdata[i:i+8] == b'TRAPGAMI': startPos = i break assert startPos > 0, 'No stone header found in %s' % fname print('Stone header found at 0x%X' % startPos) psImageEnd = 0xffffffff # PS (protocol station) image is the first in the flash backup and not compressed dfcStruct = fdata[startPos+8:startPos+120] for i in range(0,112,4): targetAddr = struct.unpack(' 0: psPath = targetDir + '/ps.bin' writeFile(psPath, fdata[:psImageEnd]) print('Protocol station image %s written!' % psPath) # main code start if __name__ == '__main__': # main app start from argparse import ArgumentParser rootdir = os.path.dirname(os.path.realpath(__file__)) parser = ArgumentParser(description='StoneD: an opensource Unisoc/Spreadtrum stone image unpacker', epilog='(c) Luxferre 2021 --- No rights reserved ') parser.add_argument('file', help='Stone image file to unpack') parser.add_argument('-d','--directory', default=None, help='Directory where component files will be written to (defaults to the same where the main stone file resides)') args = parser.parse_args() imgfile = args.file imgdir = os.path.dirname(os.path.realpath(imgfile)) if args.directory is not None: imgdir = os.path.realpath(args.directory) print('Unpacking %s to %s' % (imgfile, imgdir)) unpack_stone(imgfile, imgdir)