Upgraded to UniFlash, removed unsupported FDLs and fixed a critical HDLC encoding issue
This commit is contained in:
@@ -1,14 +1,16 @@
|
|||||||
# UniDump: an opensource Unisoc/Spreadtrum phone flash reader
|
# UniFlash: an opensource Unisoc/Spreadtrum phone flash reader/writer
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
Cross-platform MediaTek feature phone dumping had been achieved long ago with [MTreader](https://gitlab.com/suborg/mtreader). Now it's time to do the same for Unisoc (ex-Spreadtrum) phones.
|
Cross-platform MediaTek feature phone dumping had been achieved long ago with [MTreader](https://gitlab.com/suborg/mtreader). Now it's time to do the same for Unisoc (ex-Spreadtrum) phones. And not only that, but also to be able to flash them!
|
||||||
|
|
||||||
Unfortunately, the architecture of Unisoc chipset boot ROM doesn't allow us to get away without any loader blobs. So, some FDL binaries are also shipped in this repo.
|
Unfortunately, the architecture of Unisoc chipset boot ROM doesn't allow us to get away without any loader blobs. So, some FDL binaries are also shipped in this repo.
|
||||||
|
|
||||||
The `unidump.py` file is the main script to use. The `unicmd.py` file is the library created for easier command interface encapsulation.
|
UniFlash started out as UniDump (see [this blog post](https://chronovir.us/2021/12/18/Opus-Spreadtrum/)) but had been quickly upgraded to do both tasks.
|
||||||
|
|
||||||
For further dumped firmware unpacking, I recommend [bzpwork](https://github.com/ilyazx/bzpwork) by ilyazx.
|
The `uniflash.py` file is the main script to use. The `unicmd.py` file is the library created for easier command interface encapsulation.
|
||||||
|
|
||||||
|
For further dumped firmware unpacking/repacking, I recommend [bzpwork](https://github.com/ilyazx/bzpwork) by ilyazx.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
@@ -16,33 +18,19 @@ Python 3.8+ and PyUSB.
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Run `python unidump.py -h` to see all parameters. But there are several typical scenarios that UniDump officially supports.
|
Run `python uniflash.py -h` to see all parameters. But there are several typical scenarios that UniFlash officially supports.
|
||||||
|
|
||||||
**Note**: you need to hold a bootkey pressed when connecting the device for it to be detected correctly. This key can vary across devices. Typically it's Call, Soft Left, Soft Right, Center, 9 or #, but it can be anything else.
|
**Note**: you need to hold a bootkey pressed when connecting the device for it to be detected correctly. This key can vary across devices. Typically it's Call, Soft Left, Soft Right, Center, 9 or #, but it can be anything else.
|
||||||
|
|
||||||
### SC6531E/F/M
|
### SC6531E/F/M
|
||||||
|
|
||||||
This is the default target for UniDump, FDLs for it are shipped in the repo and you don't need to configure anything else.
|
This is the default target for UniFlash, FDLs for it are shipped in the repo and you don't need to configure anything else.
|
||||||
|
|
||||||
For SC6531E, firmware is usually 4MB long, so you just need to run `python unidump.py your-output-file.bin`.
|
For SC6531E, firmware is usually 4MB long, so you just need to run `python uniflash.py dump your-output-file.bin` to dump the firmware image and `python uniflash.py flash your-input-file.bin` to flash it.
|
||||||
|
|
||||||
For SC6531F or SC6531M where firmware can be larger, you need to pass in the length. As with any integer parameter here, you can pass it in hexadecimal format as well.
|
For SC6531F or SC6531M where firmware can be larger, you need to pass in the length. As with any integer parameter here, you can pass it in hexadecimal format as well.
|
||||||
|
|
||||||
E.g. for CAT B26 (bootkey is `#`): `python unidump.py -l 0x6b7000 b26.bin`
|
E.g. for CAT B26 (bootkey is `#`) the dumping command is: `python uniflash.py -l 0x6b7000 dump b26.bin`
|
||||||
|
|
||||||
### ~~UMS9117/UMS9117L~~ (NOT SUPPORTED YET)
|
|
||||||
|
|
||||||
For these 4G chipsets you need to use your own FDLs (although you **may** try the ones shipped with UniDump) and the following addresses: 0x6200 for FDL1, 0x80100000 for FDL2.
|
|
||||||
|
|
||||||
E.g. for Nokia 225 4G (if you're in the UniDump directory): `python unidump.py -fdl1 fdls/ums9117/fdl1.bin -addr1 0x6200 -fdl2 fdls/ums9117/fdl2.bin -addr2 0x80100000 225.bin`
|
|
||||||
|
|
||||||
### ~~SC7701/SC7702/SC7703~~ (NOT SUPPORTED YET)
|
|
||||||
|
|
||||||
First, for these 3G chipsets you need to use your own FDLs (although you **may** try the ones shipped with UniDump) and the following addresses: 0x40000000 for FDL1, 0x0 for FDL2.
|
|
||||||
|
|
||||||
E.g. for Nokia 3310 3G (if you're in the UniDump directory): `python unidump.py -fdl1 fdls/sc770x/fdl1.bin -addr1 0x40000000 -fdl2 fdls/sc770x/fdl2.bin -addr2 0 3310-3g.bin`
|
|
||||||
|
|
||||||
Second, these phones are connected in a different way: disconnect the cable, remove the battery, run the command, connect the cable, hold the bootkey and then insert the battery.
|
|
||||||
|
|
||||||
### SC6531D and lower
|
### SC6531D and lower
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -41,14 +41,15 @@ def hdlc_encode(data, fdl = False, nocrc = False):
|
|||||||
else:
|
else:
|
||||||
crc = crc16_xmodem(data)
|
crc = crc16_xmodem(data)
|
||||||
out = []
|
out = []
|
||||||
for c in data:
|
rdata = bytearray(data + pack('>H', crc))
|
||||||
|
for c in rdata:
|
||||||
if c == 0x7e or c == 0x7d:
|
if c == 0x7e or c == 0x7d:
|
||||||
out.append(0x7d)
|
out.append(0x7d)
|
||||||
out.append(c ^ 0x20)
|
out.append(c ^ 0x20)
|
||||||
else:
|
else:
|
||||||
out.append(c)
|
out.append(c)
|
||||||
out = bytes(out)
|
out = bytes(out)
|
||||||
return b'\x7e' + out + pack('>HB', crc, 0x7e)
|
return b'\x7e' + out + b'\x7e'
|
||||||
|
|
||||||
def hdlc_decode(data, fdl = False, ignoreCrc = False): # HDLC bug in Unisoc: CRC is also encoded!!!
|
def hdlc_decode(data, fdl = False, ignoreCrc = False): # HDLC bug in Unisoc: CRC is also encoded!!!
|
||||||
rawdata = bytearray(data[1:-1])
|
rawdata = bytearray(data[1:-1])
|
||||||
@@ -220,6 +221,10 @@ def cmd_data_start(targetAddr, targetLen):
|
|||||||
datahdr = pack('>LL', targetAddr, targetLen)
|
datahdr = pack('>LL', targetAddr, targetLen)
|
||||||
return shape_data_packet(BSL_CMD_START_DATA, datahdr)
|
return shape_data_packet(BSL_CMD_START_DATA, datahdr)
|
||||||
|
|
||||||
|
def cmd_data_start_flash(targetPartId, targetLen, checksum):
|
||||||
|
datahdr = pack('>LLL', targetPartId, targetLen, checksum)
|
||||||
|
return shape_data_packet(BSL_CMD_START_DATA, datahdr)
|
||||||
|
|
||||||
def cmd_data_send(data):
|
def cmd_data_send(data):
|
||||||
return shape_data_packet(BSL_CMD_MIDST_DATA, data)
|
return shape_data_packet(BSL_CMD_MIDST_DATA, data)
|
||||||
|
|
||||||
@@ -255,8 +260,10 @@ def cmd_read_sector_size():
|
|||||||
def cmd_read_flash_type():
|
def cmd_read_flash_type():
|
||||||
return shape_cmd_packet(BSL_CMD_READ_FLASH_TYPE)
|
return shape_cmd_packet(BSL_CMD_READ_FLASH_TYPE)
|
||||||
|
|
||||||
def cmd_enable_flash():
|
def cmd_enable_flash(targetAddr):
|
||||||
return shape_cmd_packet(BSL_CMD_ENABLE_WRITE_FLASH)
|
#datahdr = pack('>L', targetAddr)
|
||||||
|
datahdr = pack('>LL', targetAddr, 1)
|
||||||
|
return shape_data_packet(BSL_CMD_ENABLE_WRITE_FLASH, datahdr)
|
||||||
|
|
||||||
def cmd_read_flash_info():
|
def cmd_read_flash_info():
|
||||||
return shape_cmd_packet(BSL_CMD_READ_FLASH_INFO)
|
return shape_cmd_packet(BSL_CMD_READ_FLASH_INFO)
|
||||||
|
|||||||
+30
-12
@@ -9,6 +9,7 @@ import unicmd
|
|||||||
|
|
||||||
UNISOC_VID = 0x1782
|
UNISOC_VID = 0x1782
|
||||||
UNISOC_PID = 0x4d00
|
UNISOC_PID = 0x4d00
|
||||||
|
UNISOC_FLASH_BASE_ADDR = 0x10000000
|
||||||
MAX_PKT_SIZE = 1024
|
MAX_PKT_SIZE = 1024
|
||||||
bSize = 512 # read block size
|
bSize = 512 # read block size
|
||||||
genTimeout = 4000
|
genTimeout = 4000
|
||||||
@@ -63,13 +64,18 @@ def handshake(fdlBooted = False):
|
|||||||
if len(r):
|
if len(r):
|
||||||
print('>', r.decode())
|
print('>', r.decode())
|
||||||
|
|
||||||
def send_file_to_addr(fname, faddr, fdlBooted = False):
|
def send_file_to_addr(fname, faddr, fdlBooted = False, flashMode = False, fbs = 1024):
|
||||||
pSize = MAX_PKT_SIZE
|
pSize = MAX_PKT_SIZE
|
||||||
f = open(fname, 'rb')
|
f = open(fname, 'rb')
|
||||||
fdata = f.read()
|
fdata = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
flen = len(fdata)
|
flen = len(fdata)
|
||||||
print('Initializing data transfer...')
|
print('Initializing data transfer...')
|
||||||
|
if flashMode:
|
||||||
|
pSize = fbs
|
||||||
|
# faddr is our flash offset in this case
|
||||||
|
resp = reqresp(unicmd.cmd_data_start(UNISOC_FLASH_BASE_ADDR + faddr, flen), fdlBooted)
|
||||||
|
else:
|
||||||
resp = reqresp(unicmd.cmd_data_start(faddr, flen), fdlBooted)
|
resp = reqresp(unicmd.cmd_data_start(faddr, flen), fdlBooted)
|
||||||
rcode, rlen, r = unicmd.resp_decode(resp, fdlBooted)
|
rcode, rlen, r = unicmd.resp_decode(resp, fdlBooted)
|
||||||
assert rcode == unicmd.BSL_REP_ACK, 'Could not start data transfer, response code is %X' % rcode
|
assert rcode == unicmd.BSL_REP_ACK, 'Could not start data transfer, response code is %X' % rcode
|
||||||
@@ -85,12 +91,15 @@ def send_file_to_addr(fname, faddr, fdlBooted = False):
|
|||||||
print('\nEnding data transfer...')
|
print('\nEnding data transfer...')
|
||||||
resp = reqresp(unicmd.cmd_data_end(), fdlBooted)
|
resp = reqresp(unicmd.cmd_data_end(), fdlBooted)
|
||||||
rcode, rlen, r = unicmd.resp_decode(resp, fdlBooted)
|
rcode, rlen, r = unicmd.resp_decode(resp, fdlBooted)
|
||||||
|
if not flashMode or rcode != unicmd.BSL_FLASH_CFG_ERROR: # on flashing, ignore 0xA4 error
|
||||||
assert rcode == unicmd.BSL_REP_ACK, 'Could not finalize data transfer, response code is %X' % rcode
|
assert rcode == unicmd.BSL_REP_ACK, 'Could not finalize data transfer, response code is %X' % rcode
|
||||||
print('Data transfer successful')
|
print('Data transfer successful')
|
||||||
|
|
||||||
def read_partdata(baseaddr, size, offset):
|
# readback code implementation
|
||||||
|
|
||||||
|
def read_partdata(partid, size, offset):
|
||||||
t = b''
|
t = b''
|
||||||
reqonly(unicmd.cmd_read_flash(baseaddr, size, offset), True)
|
reqonly(unicmd.cmd_read_flash(partid, size, offset), True)
|
||||||
while True:
|
while True:
|
||||||
xr = bytes(dev.read(epIn, bSize, genTimeout))
|
xr = bytes(dev.read(epIn, bSize, genTimeout))
|
||||||
t += xr
|
t += xr
|
||||||
@@ -98,16 +107,16 @@ def read_partdata(baseaddr, size, offset):
|
|||||||
break
|
break
|
||||||
return t
|
return t
|
||||||
|
|
||||||
def read_partition(baseaddr, partsize, partoffset, outfile, rbblocksize):
|
def read_partition(partid, partsize, partoffset, outfile, rbblocksize):
|
||||||
outf = open(outfile, 'wb')
|
outf = open(outfile, 'wb')
|
||||||
psize = partsize
|
psize = partsize
|
||||||
offset = partoffset
|
offset = partoffset
|
||||||
print('Dumping %d bytes from partition 0x%X at offset 0x%X to %s...' % (partsize, baseaddr, partoffset, outfile))
|
print('Dumping %d bytes from partition 0x%X at offset 0x%X to %s...' % (partsize, partid, partoffset, outfile))
|
||||||
bufsize = rbblocksize
|
bufsize = rbblocksize
|
||||||
while psize > 0:
|
while psize > 0:
|
||||||
if psize < bufsize:
|
if psize < bufsize:
|
||||||
bufsize = psize
|
bufsize = psize
|
||||||
resp = read_partdata(baseaddr, bufsize, offset)
|
resp = read_partdata(partid, bufsize, offset)
|
||||||
rcode, rlen, r = unicmd.resp_decode(resp, True)
|
rcode, rlen, r = unicmd.resp_decode(resp, True)
|
||||||
outf.write(r)
|
outf.write(r)
|
||||||
sys.stdout.write('.')
|
sys.stdout.write('.')
|
||||||
@@ -125,12 +134,13 @@ def auto_int(x):
|
|||||||
if __name__ == '__main__': # main app start
|
if __name__ == '__main__': # main app start
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
rootdir = os.path.dirname(os.path.realpath(__file__))
|
rootdir = os.path.dirname(os.path.realpath(__file__))
|
||||||
parser = ArgumentParser(description='UniDump: an opensource Unisoc SC6531E/F/M phone dumper', epilog='(c) Luxferre 2021 --- No rights reserved <https://unlicense.org>')
|
parser = ArgumentParser(description='UniFlash: an opensource Unisoc SC6531E/F/M phone flash reader/writer', epilog='(c) Luxferre 2021 --- No rights reserved <https://unlicense.org>')
|
||||||
parser.add_argument('file', help='File to write the dump into')
|
parser.add_argument('mode', help='Operation mode (flash/dump)')
|
||||||
parser.add_argument('-p','--partid', type=auto_int, default=0x80000003, help='partition ID (defaults to 0x80000003 that can address full flash space on SC6531E/F/M)')
|
parser.add_argument('file', help='File to read the flash data from or write the dump into')
|
||||||
|
parser.add_argument('-p','--partid', type=auto_int, default=0x80000003, help='partition ID for readback (defaults to 0x80000003 that can address full flash space on SC6531E/F/M)')
|
||||||
parser.add_argument('-s','--start', type=auto_int, default=0, help='start position (in the partition, defaults to 0)')
|
parser.add_argument('-s','--start', type=auto_int, default=0, help='start position (in the partition, defaults to 0)')
|
||||||
parser.add_argument('-l', '--length', type=auto_int, default=0x400000, help='data length in bytes to read, defaults to 0x400000')
|
parser.add_argument('-l', '--length', type=auto_int, default=0x400000, help='data length in bytes to read/write, defaults to 0x400000')
|
||||||
parser.add_argument('-bs','--block-size', type=auto_int, default=4096, help='Readback block size (in bytes), defaults to 4096')
|
parser.add_argument('-bs','--block-size', type=auto_int, default=4096, help='Readback/write block size (in bytes), defaults to 4096')
|
||||||
parser.add_argument('-dv','--device-vid', type=auto_int, default=UNISOC_VID, help='Override device vendor ID')
|
parser.add_argument('-dv','--device-vid', type=auto_int, default=UNISOC_VID, help='Override device vendor ID')
|
||||||
parser.add_argument('-dp','--device-pid', type=auto_int, default=UNISOC_PID, help='Override device product ID')
|
parser.add_argument('-dp','--device-pid', type=auto_int, default=UNISOC_PID, help='Override device product ID')
|
||||||
parser.add_argument('-fdl1','--fdl1-file', default=rootdir+'/fdls/sc6531efm/nor_fdl1.bin', help='Path to FDL1, defaults to the generic SC6531E/F/M FDL1 shipped with UniDump')
|
parser.add_argument('-fdl1','--fdl1-file', default=rootdir+'/fdls/sc6531efm/nor_fdl1.bin', help='Path to FDL1, defaults to the generic SC6531E/F/M FDL1 shipped with UniDump')
|
||||||
@@ -139,6 +149,10 @@ if __name__ == '__main__': # main app start
|
|||||||
parser.add_argument('-addr2','--fdl2-addr', type=auto_int, default=0x14000000, help='Address to load FDL2 into, defaults to 0x14000000')
|
parser.add_argument('-addr2','--fdl2-addr', type=auto_int, default=0x14000000, help='Address to load FDL2 into, defaults to 0x14000000')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
is_flash = False
|
||||||
|
if args.mode == 'flash':
|
||||||
|
is_flash = True
|
||||||
|
|
||||||
UNISOC_VID = args.device_vid
|
UNISOC_VID = args.device_vid
|
||||||
UNISOC_PID = args.device_pid
|
UNISOC_PID = args.device_pid
|
||||||
fdl1Addr = args.fdl1_addr
|
fdl1Addr = args.fdl1_addr
|
||||||
@@ -192,8 +206,12 @@ if __name__ == '__main__': # main app start
|
|||||||
|
|
||||||
print('FDL2 running, may start interacting with flash memory')
|
print('FDL2 running, may start interacting with flash memory')
|
||||||
|
|
||||||
|
if is_flash:
|
||||||
|
print('Writing flash at offset 0x%X from %s...' % (readoffset, outfile))
|
||||||
|
send_file_to_addr(outfile, readoffset, True, True, readbs)
|
||||||
|
print('Flash memory written, disconnect the device!')
|
||||||
|
else:
|
||||||
read_partition(partitionId, readlen, readoffset, outfile, readbs)
|
read_partition(partitionId, readlen, readoffset, outfile, readbs)
|
||||||
|
|
||||||
resp = reqresp(unicmd.cmd_reset(), True)
|
resp = reqresp(unicmd.cmd_reset(), True)
|
||||||
rcode, rlen, r = unicmd.resp_decode(resp, True)
|
rcode, rlen, r = unicmd.resp_decode(resp, True)
|
||||||
assert rcode == unicmd.BSL_REP_ACK, 'Could not reset the device, response code is %X' % rcode
|
assert rcode == unicmd.BSL_REP_ACK, 'Could not reset the device, response code is %X' % rcode
|
||||||
Reference in New Issue
Block a user