2020-07-28 20:47:20 +03:00
#!/usr/bin/env python3
import os , sys , struct , time
from serial import Serial
class MTreader :
2020-07-29 18:26:20 +03:00
def __init__ ( self , devfile ):
2020-07-28 20:47:20 +03:00
while True :
try :
self . s = Serial ( devfile , 115200 )
sys . stdout . write ( " \n " )
break
except OSError as e :
sys . stdout . write ( "." )
sys . stdout . flush ()
time . sleep ( 0.1 )
def send ( self , data , sz = 0 ):
2020-07-29 18:26:20 +03:00
r = ''
2020-07-28 20:47:20 +03:00
if len ( data ):
self . s . write ( data )
if sz > 0 :
r = self . s . read ( sz )
return r
def cmd ( self , cmd , sz = 0 ):
2020-07-29 18:26:20 +03:00
r = ''
2020-07-28 20:47:20 +03:00
cmdlen = len ( cmd )
if cmdlen > 0 :
assert ( self . send ( cmd , cmdlen ) == cmd )
if sz > 0 :
r = self . s . read ( sz )
return r
2020-07-29 18:26:20 +03:00
def read16 ( self , addr , sz = 1 , endianness = '>' ):
r = self . cmd ( b ' \xA2 ' + struct . pack ( '>II' , addr , sz ), sz * 2 )
return struct . unpack ( endianness + sz * 'H' , r )
2020-07-28 20:47:20 +03:00
2020-07-29 18:26:20 +03:00
def write16 ( self , addr , val ):
2020-07-28 20:47:20 +03:00
r = self . cmd ( b ' \xD2 ' + struct . pack ( ">II" , addr , 1 ), 2 )
assert ( r == b " \0\1 " )
r = self . cmd ( struct . pack ( ">H" , val ), 2 )
assert ( r == b " \0\1 " )
2020-07-29 18:26:20 +03:00
def read32 ( self , addr , sz = 1 , endianness = ">" ):
r = self . cmd ( b ' \xD1 ' + struct . pack ( ">II" , addr , sz ), ( sz * 4 ) + 4 )
return struct . unpack ( endianness + "H" + ( sz * 'I' ) + "H" , r )
def write32 ( self , addr , val ):
2020-07-28 20:47:20 +03:00
r = self . cmd ( b ' \xD4 ' + struct . pack ( ">II" , addr , 1 ), 2 )
assert ( r == b " \0\1 " )
r = self . cmd ( struct . pack ( ">I" , val ), 2 )
assert ( r == b " \0\1 " )
def connect ( self , timeout = 9.0 ):
2020-07-29 18:26:20 +03:00
self . s . timeout = 0.1
2020-07-28 20:47:20 +03:00
while True :
2020-07-29 18:26:20 +03:00
self . s . write ( b ' \xA0 ' )
r = self . s . read ( 1 )
if r == b ' \x5F ' :
2020-07-28 20:47:20 +03:00
break
2020-07-29 18:26:20 +03:00
self . s . write ( b ' \x0A\x50\x05 ' )
r = self . s . read ( 3 )
assert ( r == b ' \xF5\xAF\xFA ' )
self . chip = self . read16 ( 0x80000008 )[ 0 ]
self . write16 ( 0xa0030000 , 0x2200 ) # disable system watchdog
self . write16 ( 0xa0710074 , 0x0001 ) # enable transfers from core to RTC
self . write16 ( 0xa0700a28 , 0x8000 ) # enable USB download mode
self . write16 ( 0xa0700a24 , 2 ) # disable battery watchdog
self . write32 ( 0xa0510000 , 3 ) # enter memory map mode 3
# now all flash is mapped as little-endian 32-bit chunks at 0x10000000
2020-07-28 20:47:20 +03:00
2020-07-29 18:26:20 +03:00
def read_flash ( self , outfile , start , size , blk_size = 1024 ):
2020-07-28 20:47:20 +03:00
outf = open ( outfile , "wb" )
2020-07-29 18:26:20 +03:00
offset = 0
addr = 0x10000000 + start
2020-07-28 20:47:20 +03:00
while size > 0 :
2020-07-29 18:26:20 +03:00
rsize = min ( size , blk_size )
chunk = self . read32 ( addr , rsize >> 2 , '<' )
outchunk = struct . pack ( '>' + ( len ( chunk ) - 2 ) * 'L' , * chunk [ 1 : - 1 ])
size -= rsize
addr += rsize
outf . write ( outchunk )
2020-07-28 20:47:20 +03:00
sys . stdout . write ( "." )
sys . stdout . flush ()
2020-07-29 18:26:20 +03:00
outf . close ()
2020-07-28 20:47:20 +03:00
def da_reset ( self ):
r = self . send ( b ' \xB9 ' , 1 )
r = self . send ( b ' \xC9\x00 ' , 1 )
r = self . send ( b ' \xDB\x01\x40\x00\x00\x00\x00 ' , 1 )
if __name__ == "__main__" : # main app start
from argparse import ArgumentParser
2020-07-29 18:39:00 +03:00
parser = ArgumentParser ( description = "MTreader: a simple, no-nonsense MediaTek MT6261 phone ROM reader" , epilog = "(c) Luxferre 2020 --- No rights reserved <https://unlicense.org>" )
2020-07-28 20:47:20 +03:00
parser . add_argument ( 'port' , help = 'Serial port to connect to (/dev/ttyUSB0, /dev/tty.usbmodem14100 etc.)' )
parser . add_argument ( 'file' , help = 'File to write the dump into' )
parser . add_argument ( 'start' , type = int , help = 'start position (in the phone flash memory)' )
parser . add_argument ( 'length' , type = int , help = 'data length' )
parser . add_argument ( '-bs' , '--block-size' , type = int , default = 1024 , help = 'Readback block size (in bytes), defaults to 1024' )
args = parser . parse_args ()
if ( args . length < 1 ):
2020-07-28 20:57:09 +03:00
print ( 'Zero length of data, exiting' )
2020-07-28 20:47:20 +03:00
exit ( 1 )
print ( "Turn the phone off, hold the boot key and connect the cable" )
2020-07-29 18:26:20 +03:00
m = MTreader ( args . port )
print ( "Sending BROM handshake..." )
2020-07-28 20:47:20 +03:00
m . connect ()
2020-07-29 18:26:20 +03:00
print ( "Connected, chip ID: %04x " % m . chip )
2020-07-28 20:47:20 +03:00
print ( "Dumping, don't disconnect..." )
2020-07-29 18:26:20 +03:00
m . read_flash ( args . file , args . start , args . length , args . block_size )
2020-07-28 20:47:20 +03:00
print ( " \n ROM dumped, disconnect the cable" )
m . da_reset ()