├── .gitignore ├── README.md ├── brntool.py ├── cfenand.py ├── cfenandzyx.py ├── cfetool.py ├── en751221tool.py ├── rt63365tool.py ├── rtl8186tool.py ├── rtl867xtool.py ├── zyx1tool.py └── zyx2tool.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.dump 3 | *.bin 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bootloader-dump tools 2 | Collection of tools for dumping the memory or backing up the flash chip using the **memory read** native 3 | command present in bootloaders from some devices (not all) like routers. 4 | 5 | Usually you access to the bootloader command line using an UART adapter, and then if the command is 6 | available dump small portions of the memory in plain text, these tools automate the process for getting 7 | a full backup in binary format. It deals through the serial port with the bootloader by sending the command 8 | **memory read** and captures the output dumping it into a binary file. 9 | 10 | # Prerequisites 11 | - All scripts require Python 2 to be installed. 12 | - "pyserial" package needs to be installed via pip. 13 | - If using Windows, change the source serial from "/dev/ttyUSB0" to "COM3" or whtever COM number is attached to your UART adapter in Device Manager. 14 | - If using multiple Python versions, replace "python2" command with "py -2". 15 | 16 | 17 | ## brntool 18 | This tool can, so far, given a serial port connected to a device with **brnboot / amazonboot**, dump its flash into a file. 19 | Homepage: https://github.com/rvalles/brntool 20 | 21 | **Example:** 22 | `python3 brntool.py --read=AR4518PW_whole.dump --addr=0xB0000000 --verbose --size=0x400000` 23 | 24 | --addr: Memory Address 25 | --size: Memory Size 26 | --block: Buffer size (Default: 10240 -> 10Kb) 27 | 28 | ## cfetool 29 | This tool can dump the flash of a device with CFE bootloader into a file. 30 | It's compatible with all CFE bootloaders with "dm" command usually found in **BCM63xx SoCs**. 31 | 32 | **Example:** 33 | `python2 cfetool.py --read=test.bin --addr=0xB8000000 --size=0x20000 --block=0x10000` 34 | 35 | --addr: Memory Address 36 | --size: Memory Size 37 | --block: Buffer size (Default: 10240 -> 10Kb) 38 | 39 | **Zyxel variants:** 40 | zyx1tool.py, zyx2tool.py 41 | 42 | ## cfenand 43 | This tool can dump the NAND flash of a device with CFE bootloader into a file. 44 | It's compatible with all CFE bootloaders with "dn" command usually found in **BCM63xx SoCs**. 45 | Homepage: https://github.com/Depau/bcm-cfedump 46 | 47 | **Example:** 48 | `python -m cfenand -D /dev/ttyUSB0 -O nand.bin -t 0.05 nand` 49 | 50 | Tested with a BCM63167 Sercomm router (128MB flash). 51 | 52 | ## cfenandzyx 53 | For broadcom NAND devices with a CFE bootloader modded by Zyxel with the **ATDF** command available. Tested on Mitrastar GPT-2541GNAC 54 | 55 | **Example:** 56 | * enable the ATDF command: open the console with minicom and execute: 57 | `ATEN 1 10F0A563` 58 | * dump the flash: close minicom and execute on the computer: 59 | `python cfenandzyx.py --verbose --blkn 0 --size 0x8000000 --read mitrastardump.bin` 60 | 61 | --size: Memory Size 62 | --blkn: flash block position (first is 0) 63 | 64 | ## rt63365tool 65 | For **Ralink RT63365** (Trendchip) based SoCs running the tcboot bootloader. Tested on Huawei HG532s 66 | 67 | **Example:** 68 | `python2 rt63365tool.py --read=test.bin --addr=0xB0000000 --size=0x800000 --block=0x10000` 69 | 70 | --addr: Memory Address 71 | --size: Memory Size 72 | --block: Buffer size 73 | 74 | ## en751221tool 75 | For **Econet EN751221** based SoCs running the tcboot bootloader. Tested on ZTE H367A 76 | 77 | **Example:** 78 | * open minicom in the bootloader CLI execute: 79 | `readflash 80020000 0 1a00000` 80 | * close minicom, at the pc execute: 81 | `python2 rt63365tool.py --read=dump1.bin --addr=0x80020000 --size=0x1a00000 --block=0x10000` 82 | 83 | ## rtl8186tool 84 | 85 | This tool can dump the flash memory of a **Realtek RTL8186** based device running the btcode bootloader 86 | 87 | **Example:** 88 | `python2 rtl8186tool.py --read=test.bin --addr=0xbfc00000 --size=0x200000 --block=0x10000` 89 | 90 | --addr: Memory Address 91 | --size: Memory Size 92 | --block: Buffer size (Default: 10240 -> 10Kb) 93 | 94 | **Note:** the default baud rate used by the tool is 38400 bps 95 | 96 | 97 | ## rtl867xtool 98 | 99 | This tool was tested on a **Realtek RTL8676** based device (ZTE H298N) 100 | 101 | **Example:** 102 | `python2 rtl867xtool.py --read=test.bin --addr=0x80000000 --size=0x8000000 --block=0x10000` 103 | 104 | --addr: Memory Address 105 | --size: Memory Size 106 | --block: Buffer size (Default: 10240 -> 10Kb) 107 | 108 | 109 | --- 110 | 111 | All tools are based/inspired on the original brntool (@rvalles): Homepage: https://github.com/rvalles/brntool 112 | -------------------------------------------------------------------------------- /brntool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | #------------- Bootloader dump sample: ----------------------# 5 | #[VR9 Boot]:r 6 | # 7 | #Enter the Start Address to Read....0x B0000000 8 | #Data Length is (1) 4 Bytes (2) 2 Bytes (3) 1 Byte... 3 9 | #Enter the Count to Read....(Maximun 10000) 160 10 | # 11 | #---------------------------------------------------------- 12 | # Address 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 13 | #---------------------------------------------------------- 14 | #0xB0000000 10 00 00 0B 00 00 00 00 00 00 00 00 00 00 00 00 15 | #0xB0000010 68 8C 68 8C 00 00 00 00 31 2E 31 2E 30 00 00 00 16 | #0xB0000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 17 | #0xB0000030 40 80 90 00 40 80 98 00 40 80 68 00 40 1B 78 00 18 | #0xB0000040 3C 08 00 FF 35 08 FF 00 03 68 D8 24 3C 08 00 01 19 | #0xB0000050 35 08 95 00 17 68 00 19 00 00 00 00 40 08 80 00 20 | #0xB0000060 3C 09 80 00 35 29 FF FF 01 09 40 24 3C 09 36 04 21 | #0xB0000070 01 09 40 25 00 00 00 00 40 88 80 00 00 00 00 40 22 | #0xB0000080 00 00 00 40 00 00 00 40 00 00 00 C0 40 08 60 00 23 | #0xB0000090 3C 09 FF FC 35 29 FF FF 01 09 40 24 24 09 00 00 24 | # 25 | #[VR9 Boot]: 26 | #-------------------------------------------------------------# 27 | 28 | #Keep python2 working 29 | from __future__ import division #1/2 = float, 1//2 = integer 30 | from __future__ import print_function #print("blah", file=whatever) 31 | #Keep python2 working end 32 | from optparse import OptionParser 33 | import serial 34 | import sys 35 | #import struct 36 | import re 37 | lineregex = re.compile(r'0x(?:[0-9A-F]{8})((?: [0-9A-F]{2}){1,16})') 38 | def get2menu(ser,verbose): 39 | if verbose: 40 | print("Waiting for a prompt...", file=sys.stderr) 41 | while True: 42 | ser.write(" !".encode()) 43 | if(ser.read(1)==b']' and ser.read(1)==b':'): 44 | while ser.read(256): 45 | pass 46 | if verbose: 47 | print("Ok.", file=sys.stderr) 48 | return 49 | def memreadblock(ser,addr,size): 50 | while ser.read(1): 51 | pass 52 | ser.write('r'.encode()) 53 | while not (ser.read(1)==b'0' and ser.read(1)==b'x'): 54 | pass 55 | ser.write(("%x"%addr).encode()) 56 | ser.write('\r'.encode()) 57 | while not (ser.read(1)==b'.' and ser.read(1)==b'.' and ser.read(1)==b'.'): 58 | pass 59 | ser.write('3'.encode()) 60 | while not ser.read(1)==b')': 61 | pass 62 | ser.write(str(size).encode()) 63 | ser.write('\r'.encode()) 64 | buf=b'' 65 | m = False 66 | while not m: 67 | m = lineregex.match(ser.readline().decode().strip()) 68 | while m: 69 | if sys.version_info >= (3, 0): 70 | buf+=bytes.fromhex(m.group(1)) 71 | else: 72 | buf+=''.join([chr(int(x, 16)) for x in m.group(1)[1:].split(' ')]) 73 | m = lineregex.match(ser.readline().decode().strip()) 74 | return buf 75 | def memreadblock2file(ser,fd,addr,size): 76 | while True: 77 | buf=memreadblock(ser,addr,size) 78 | if len(buf)==size: 79 | break 80 | sys.stderr.write('!') 81 | fd.write(buf) 82 | return 83 | def memread(ser,path,addr,size,verbose): 84 | #bs=1024 85 | bs=10000 #10000 is usually the maximum size for an hexdump on brnboot. 86 | get2menu(ser,verbose) 87 | if path == "-": 88 | # get sys.stdout in Python 2 or sys.stdout.buffer in Python 3 89 | fd=getattr(sys.stdout, 'buffer', sys.stdout) 90 | else: 91 | fd=open(path,"wb") 92 | while 0bs: 94 | memreadblock2file(ser,fd,addr,bs) 95 | size-=bs 96 | addr+=bs 97 | print("Addr: " + hex(addr), file=sys.stderr) 98 | print("Size: " + str(size), file=sys.stderr) 99 | else: 100 | memreadblock2file(ser,fd,addr,size) 101 | size=0 102 | fd.close() 103 | return 104 | def main(): 105 | optparser = OptionParser("usage: %prog [options]",version="%prog 0.1") 106 | optparser.add_option("--verbose", action="store_true", dest="verbose", help="be verbose", default=False) 107 | optparser.add_option("--serial", dest="serial", help="specify serial port", default="/dev/ttyUSB0", metavar="dev") 108 | optparser.add_option("--read", dest="read", help="read mem to file", metavar="path") 109 | #optparser.add_option("--write", dest="write",help="write mem from file", metavar="path") 110 | optparser.add_option("--addr", dest="addr",help="mem address", metavar="addr") 111 | optparser.add_option("--size", dest="size",help="size to copy", metavar="bytes") 112 | (options, args) = optparser.parse_args() 113 | if len(args) != 0: 114 | optparser.error("incorrect number of arguments") 115 | ser = serial.Serial(options.serial, 115200, timeout=1) 116 | if options.read: 117 | memread(ser,options.read,int(options.addr,0),int(options.size,0),options.verbose) 118 | return 119 | if __name__ == '__main__': 120 | main() 121 | -------------------------------------------------------------------------------- /cfenand.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # This tool expects data from the serial terminal in a format like this 5 | # CFE> dn 0 0 1 6 | # ------------------ block: 0, page: 0 ------------------ 7 | # 00000000: 10000027 00000000 80005fb0 80005fb0 ...'......_..._. 8 | # 00000010: 80006600 800070a0 80000000 80006600 ..f...p.......f. 9 | # 00000020: 8000e5d0 00000000 00000000 00000000 ................ 10 | # 00000030: 80002d70 80002e84 80002e7c 80004688 ..-p.......|..F. 11 | # 00000040: 80004774 80004774 80004774 8000477c ..Gt..Gt..Gt..G| 12 | # 00000050: 800033d8 80004784 8000483c 80002620 ..3...G...H<..& 13 | # 00000060: 800025e8 800025f0 80002f44 800025d4 ..%...%.../D..%. 14 | # 00000070: 80003088 80002d68 80002ed0 80004774 ..0...-h......Gt 15 | # 00000080: 80004774 80004774 80002f18 800045a8 ..Gt..Gt../...E. 16 | # 00000090: 800045b4 80004774 80004774 80004774 ..E...Gt..Gt..Gt 17 | # 000000a0: 0000e021 04110001 00000000 00000000 ...!............ 18 | # 000000b0: 3c151fff 02bfa824 3c168000 26d600c8 <......$<...&... 19 | # 000000c0: 04110001 00000000 03f6b022 02a02021 ...........".. ! 20 | # 000000d0: 3c1b8000 277b0030 0376d821 3c01a000 <...'{.0.v.!<... 21 | # 000000e0: 0361d825 8f7b0000 0376d821 3c01a000 .a.%.{...v.!<... 22 | # 000000f0: 0361d825 0360f809 00000000 3c044845 .a.%.`......<.HE 23 | # 00000100: 34844c4f 3c1b8000 277b0030 0376d821 4.LO<...'{.0.v.! 24 | # 00000110: 3c01a000 0361d825 8f7b0004 0376d821 <....a.%.{...v.! 25 | # 00000120: 3c01a000 0361d825 0360f809 00000000 <....a.%.`...... 26 | # 00000130: 3c1b8000 277b0030 0376d821 3c01a000 <...'{.0.v.!<... 27 | # 00000140: 0361d825 8f7b000c 0376d821 3c01a000 .a.%.{...v.!<... 28 | # 00000150: 0361d825 0360f809 00000000 041102f2 .a.%.`.......... 29 | # 00000160: 00000000 3c168000 26d60174 04110001 ....<...&..t.... 30 | # 00000170: 00000000 03f6b022 3c1b8000 277b0030 ......."<...'{.0 31 | # 00000180: 0376d821 8f7b0040 0376d821 0360f809 .v.!.{.@.v.!.`.. 32 | # 00000190: 00000000 3c1b8000 277b0030 0376d821 ....<...'{.0.v.! 33 | # 000001a0: 8f7b005c 0376d821 0360f809 00000000 .{.\.v.!.`...... 34 | # 000001b0: 14400009 00000000 00000000 3c1b8000 .@..........<... 35 | # 000001c0: 277b0030 0376d821 8f7b0038 0376d821 '{.0.v.!.{.8.v.! 36 | # 000001d0: 0360f809 00000000 00000000 3c048000 .`..........<... 37 | # 000001e0: 24840d44 00962021 3c1b8000 277b0030 $..D.. !<...'{.0 38 | # 000001f0: 0376d821 8f7b0048 0376d821 0360f809 .v.!.{.H.v.!.`.. 39 | # 00000200: 00000000 04110259 00000000 00000000 .......Y........ 40 | # 00000210: 3c044452 3484414d 3c1b8000 277b0030 <.DR4.AM<...'{.0 41 | # 00000220: 0376d821 8f7b0004 0376d821 0360f809 .v.!.{...v.!.`.. 42 | # 00000230: 00000000 3c1b8000 277b0030 0376d821 ....<...'{.0.v.! 43 | # 00000240: 8f7b0008 0376d821 0360f809 00000000 .{...v.!.`...... 44 | # 00000250: 00402021 3c1b8000 277b0030 0376d821 .@ !<...'{.0.v.! 45 | # 00000260: 8f7b0020 0376d821 0360f809 00000000 .{. .v.!.`...... 46 | # 00000270: 3c1b8000 277b0030 0376d821 8f7b003c <...'{.0.v.!.{.< 47 | # 00000280: 0376d821 0360f809 00000000 0040d021 .v.!.`.......@.! 48 | # 00000290: 1740000c 00000000 3c045241 34844d58 .@......<.RA4.MX 49 | # 000002a0: 3c1b8000 277b0030 0376d821 8f7b0004 <...'{.0.v.!.{.. 50 | # 000002b0: 0376d821 0360f809 00000000 1000ffff .v.!.`.......... 51 | # 000002c0: 00000000 24180100 031a082a 14200002 ....$......*. .. 52 | # 000002d0: 00000000 0340c021 0018c500 241e0000 .....@.!....$... 53 | # 000002e0: 24190000 3c048000 24840008 00962021 $...<...$..... ! 54 | # 000002f0: 8c9c0018 039ee020 3c045a42 34845353 ....... <.ZB4.SS 55 | # 00000300: 3c1b8000 277b0030 0376d821 8f7b0004 <...'{.0.v.!.{.. 56 | # 00000310: 0376d821 0360f809 00000000 3c048000 .v.!.`......<... 57 | # 00000320: 24840008 00962021 8c820014 8c83000c $..... !........ 58 | # 00000330: 005e1020 007e1820 ac400000 ac400004 .^. .~. .@...@.. 59 | # 00000340: ac400008 ac40000c 20420010 0043082a .@...@.. B...C.* 60 | # 00000350: 1420fff9 00000000 3c04434f 34844445 . ......<.CO4.DE 61 | # 00000360: 3c1b8000 277b0030 0376d821 8f7b0004 <...'{.0.v.!.{.. 62 | # 00000370: 0376d821 0360f809 00000000 3c048000 .v.!.`......<... 63 | # 00000380: 24840008 00962021 8c890010 0120b821 $..... !..... .! 64 | # 00000390: 8c8a0010 01565021 8c8b0000 01765821 .....VP!.....vX! 65 | # 000003a0: 8d4c0000 8d4d0004 8d4e0008 8d4f000c .L...M...N...O.. 66 | # 000003b0: ad2c0000 ad2d0004 ad2e0008 ad2f000c .,...-......./.. 67 | # 000003c0: 21290010 214a0010 014b082b 1420fff4 !)..!J...K.+. .. 68 | # 000003d0: 00000000 3c044441 34845441 3c1b8000 ....<.DA4.TA<... 69 | # 000003e0: 277b0030 0376d821 8f7b0004 0376d821 '{.0.v.!.{...v.! 70 | # 000003f0: 0360f809 00000000 3c048000 24840008 .`......<...$... 71 | # 00000400: 00962021 8c890004 01364821 2408000f .. !.....6H!$... 72 | # 00000410: 01284820 01004027 01284824 8c8a0004 .(H ..@'.(H$.... 73 | # 00000420: 8c8b0008 015e5020 017e5820 8d2c0000 .....^P .~X .,.. 74 | # 00000430: 8d2d0004 8d2e0008 8d2f000c ad4c0000 .-......./...L.. 75 | # 00000440: ad4d0004 ad4e0008 ad4f000c 21290010 .M...N...O..!).. 76 | # 00000450: 214a0010 014b082b 1420fff4 00000000 !J...K.+. ...... 77 | # 00000460: 10000148 00000000 00000000 00000000 ...H............ 78 | # 00000470: 00000000 00000000 00000000 00000000 ................ 79 | # 00000480: 00000000 00000000 00000000 00000000 ................ 80 | # 00000490: 00000000 00000000 00000000 00000000 ................ 81 | # 000004a0: 00000000 00000000 00000000 00000000 ................ 82 | # 000004b0: 00000000 00000000 00000000 00000000 ................ 83 | # 000004c0: 00000000 00000000 00000000 00000000 ................ 84 | # 000004d0: 00000000 00000000 00000000 00000000 ................ 85 | # 000004e0: 00000000 00000000 00000000 00000000 ................ 86 | # 000004f0: 00000000 00000000 00000000 00000000 ................ 87 | # 00000500: 00000000 00000000 00000000 00000000 ................ 88 | # 00000510: 00000000 00000000 00000000 00000000 ................ 89 | # 00000520: 00000000 00000000 00000000 00000000 ................ 90 | # 00000530: 00000000 00000000 00000000 00000000 ................ 91 | # 00000540: 00000000 00000000 00000000 00000000 ................ 92 | # 00000550: 00000000 00000000 00000000 00000000 ................ 93 | # 00000560: 00000000 00000000 00000000 00006600 ..............f. 94 | # 00000570: 6366652d 76010026 76030000 00000000 cfe-v..&v....... 95 | # 00000580: 00000006 653d3139 322e3136 382e312e ....e=192.168.1. 96 | # 00000590: 313a6666 66666666 30302068 3d313932 1:ffffff00 h=192 97 | # 000005a0: 2e313638 2e312e31 30302067 3d20723d .168.1.100 g= r= 98 | # 000005b0: 6620663d 766d6c69 6e757820 693d6263 f f=vmlinux i=bc 99 | # 000005c0: 6d393633 78785f66 735f6b65 726e656c m963xx_fs_kernel 100 | # 000005d0: 20643d31 20703d30 20633d20 613d20ff d=1 p=0 c= a= . 101 | # 000005e0: ffffffff ffffffff ffffffff ffffffff ................ 102 | # 000005f0: ffffffff ffffffff ffffffff ffffffff ................ 103 | # 00000600: ffffffff ffffffff ffffffff ffffffff ................ 104 | # 00000610: ffffffff ffffffff ffffffff ffffffff ................ 105 | # 00000620: ffffffff ffffffff ffffffff ffffffff ................ 106 | # 00000630: ffffffff ffffffff ffffffff ffffffff ................ 107 | # 00000640: ffffffff ffffffff ffffffff ffffffff ................ 108 | # 00000650: ffffffff ffffffff ffffffff ffffffff ................ 109 | # 00000660: ffffffff ffffffff ffffffff ffffffff ................ 110 | # 00000670: ffffffff ffffffff ffffffff ffffffff ................ 111 | # 00000680: ffffffff 39363331 36375245 46330000 ....963167REF3.. 112 | # 00000690: 00000000 00000000 00000018 00000010 ................ 113 | # 000006a0: 78810261 deadff00 ffffffff 00000000 x..a............ 114 | # 000006b0: 00000000 00000000 00000000 00000000 ................ 115 | # 000006c0: 00000000 ffffffff ffffffff ffffffff ................ 116 | # 000006d0: ffffffff ffffffff ffffffff ffffffff ................ 117 | # 000006e0: ffffffff ffffffff ffffffff ffffffff ................ 118 | # 000006f0: ffffffff ffffffff ffffffff ffffffff ................ 119 | # 00000700: ffffffff ffffffff ffffffff ffffffff ................ 120 | # 00000710: ffffffff ffffffff ffffffff ffffffff ................ 121 | # 00000720: ffffffff ffffffff ffffffff ffffffff ................ 122 | # 00000730: ffffffff ffffffff ffffffff ffffffff ................ 123 | # 00000740: ffffffff ffffffff ffffffff ffffffff ................ 124 | # 00000750: ffffffff ffffffff ffffffff ffffffff ................ 125 | # 00000760: ffffffff ffffffff ffffffff ffffffff ................ 126 | # 00000770: ffffffff ffffffff ffffffff ffffffff ................ 127 | # 00000780: ffffffff ffffffff ffffffff ffffffff ................ 128 | # 00000790: ffffffff ffffffff ffffffff ffffffff ................ 129 | # 000007a0: ffffffff ffffffff ffffffff ffffffff ................ 130 | # 000007b0: ffffffff ffffffff ffffffff ffffffff ................ 131 | # 000007c0: ffffffff ffffffff ffffff00 00000000 ................ 132 | # 000007d0: 00000000 00000080 0000f600 0001ec00 ................ 133 | # 000007e0: 0001fc00 00000080 0000f580 0000f580 ................ 134 | # 000007f0: 00001000 00000400 4c453936 37325f5a ........LE9672_Z 135 | # 136 | # ----------- spare area for block 0, page 0 ----------- 137 | # 00000800: ff198520 03000000 080a18d8 7a75874a ... ........zu.J 138 | # 00000810: ffffffff ffffffff ff0b4028 13dc2db8 ..........@(..-. 139 | # 00000820: ffffffff ffffffff ff0e4785 37c474e0 ..........G.7.t. 140 | # 00000830: ffffffff ffffffff ff0c332c 4063cef7 ..........3,@c.. 141 | # 142 | # *** command status = 1 143 | # CFE> 144 | 145 | import argparse 146 | import re 147 | import sys 148 | import time 149 | import traceback 150 | from typing import Generator, TextIO 151 | 152 | import serial 153 | 154 | MAX_RETRIES = 5 155 | NAND_SIZE = 131072 * 1024 # 128MiB 156 | BLOCK_SIZE = 128 * 1024 157 | PAGE_SIZE = 2048 158 | 159 | line_regex = re.compile(r'(?P[0-9a-fA-F]{8}):(?P(?: [0-9a-fA-F]{8}){4})(?:\s+.{16})?') 160 | #Correctable ECC Error detected: addr=0x00203600, intrCtrl=0x00000090, accessCtrl=0xE3441010 161 | 162 | def parse_hex_byte_string(hexbytes: str) -> bytes: 163 | assert len(hexbytes) % 2 == 0 164 | return int(hexbytes, 16).to_bytes(len(hexbytes) // 2, 'big') 165 | 166 | 167 | def parse_serial_line(line: str) -> Generator[bytes, None, None]: 168 | m = line_regex.match(line) 169 | 170 | try: 171 | for chunk in m.group('data').split(): 172 | yield parse_hex_byte_string(chunk) 173 | except Exception: 174 | print("\n\nError caused by line: '{}'".format(line)) 175 | raise 176 | 177 | 178 | def format_size(size: int) -> str: 179 | units = ('', 'K', 'M', 'G', 'T') 180 | count = 0 181 | 182 | while size > 1500: 183 | size /= 1024 184 | count += 1 185 | 186 | return "{}{}B".format(round(size, 1), units[count]) 187 | 188 | 189 | def format_time(time: int) -> str: 190 | if time < 60: 191 | return "{}s".format(time) 192 | 193 | s = time % 60 194 | time //= 60 195 | if time < 60: 196 | return "{}m {}s".format(time, s) 197 | 198 | m = time % 60 199 | time //= 60 200 | if time < 24: 201 | return "{}h {}m {}s".format(time, m, s) 202 | 203 | h = time % 24 204 | time //= 24 205 | return "{}d {}h {}m {}s".format(time, h, m, s) 206 | 207 | 208 | class PrettyPrinter: 209 | def __init__(self, out: TextIO): 210 | self.out = out 211 | self._lastline_len = 0 212 | 213 | def clear_line(self): 214 | print('\r' + ' ' * self._lastline_len, file=self.out) 215 | 216 | def print(self, string): 217 | if len(string) > 100000: 218 | print(string, file=self.out, end='') 219 | else: 220 | lines = string.split("\n") 221 | self._lastline_len = len(lines[-1]) 222 | 223 | for l in lines[:-1]: 224 | print(string, file=self.out) 225 | 226 | if lines[-1] != '': 227 | print(string, file=self.out, end='') 228 | 229 | self.out.flush() 230 | 231 | def msg(self, string): 232 | self.clear_line() 233 | self.print(string) 234 | print('\n\n', end='', file=self.out) 235 | 236 | def error(self, msg): 237 | self.msg(msg) 238 | 239 | def exc(self): 240 | string = traceback.format_exc() 241 | self.error(string) 242 | 243 | 244 | class ProgressPrinter(PrettyPrinter): 245 | chars = "⡏⠟⠻⢹⣸⣴⣦⣇" 246 | 247 | def __init__(self, out: TextIO, item_size: int, item_name: str): 248 | super().__init__(out) 249 | self.item_size = item_size 250 | self.item_name = item_name 251 | self._chars_step = 0 252 | self._last_done = -1 253 | self._last_total = -1 254 | self._clean = True 255 | self._last_time = -1 256 | 257 | def clear_line(self): 258 | super().clear_line() 259 | self._clean = True 260 | 261 | def print_progress(self, done, total): 262 | if self._last_total != total: 263 | self.clear_line() 264 | 265 | string = "\r {} ".format(self.chars[self._chars_step]) 266 | 267 | string += "[{}/{} {}] ".format(done, total, self.item_name) 268 | string += "[{}/{}] ".format(format_size(done * self.item_size), format_size(total * self.item_size)) 269 | 270 | if self._last_time > 0: 271 | delta_t = time.time() - self._last_time 272 | delta_b = done - self._last_done 273 | speed = delta_b / delta_t 274 | 275 | string += "[{}/s] ".format(format_size(speed)) 276 | 277 | remaining = total - done 278 | try: 279 | eta = int(remaining // speed) 280 | except ZeroDivisionError: 281 | eta = int(0) 282 | 283 | string += "[ETA: {}]".format(format_time(eta)) 284 | 285 | self._chars_step = (self._chars_step + 1) % len(self.chars) 286 | self._last_done = done 287 | self._last_total = total 288 | self._last_time = time.time() 289 | 290 | self.print(string) 291 | 292 | 293 | class CFECommunicator: 294 | # noinspection PyShadowingNames 295 | def __init__(self, serial: serial.Serial, block_size: int = BLOCK_SIZE, page_size: int = PAGE_SIZE, 296 | nand_size: int = NAND_SIZE, max_retries: int = MAX_RETRIES, printer: PrettyPrinter = None): 297 | self.max_retries = max_retries 298 | self.block_size = block_size 299 | self.page_size = page_size 300 | self.nand_size = nand_size 301 | self.ser = serial 302 | self.printer = printer or PrettyPrinter(sys.stdout) 303 | 304 | def eat_junk(self) -> None: 305 | while self.ser.read(1): 306 | pass 307 | 308 | def wait_for_prompt(self) -> None: 309 | self.printer.msg("Waiting for a prompt...") 310 | while True: 311 | self.ser.write(b"\r\n") 312 | if self.ser.read(1) == b'C' and self.ser.read(1) == b'F' \ 313 | and self.ser.read(1) == b'E' and self.ser.read(1) == b'>': 314 | self.eat_junk() 315 | return 316 | 317 | def parse_pages_bulk(self) -> Generator[bytes, None, None]: 318 | while not self.ser.readline().startswith(b"-----"): 319 | pass 320 | buf = b'' 321 | 322 | while True: 323 | line = self.ser.readline().strip() 324 | 325 | if len(line) == 0: 326 | continue 327 | 328 | # Spare area. Yield and skip to next page 329 | if line.startswith(b"-----"): 330 | yield buf 331 | buf = b'' 332 | 333 | while not self.ser.readline().startswith(b"-----"): 334 | pass 335 | continue 336 | 337 | try: 338 | for b in parse_serial_line(line.decode()): 339 | buf += b 340 | except UnicodeDecodeError: 341 | traceback.print_exc() 342 | 343 | def read_page(self, block: int, page: int) -> bytes: 344 | buf = b'' 345 | main_area_read = False 346 | 347 | self.ser.write("dn {block} {page} 1\r\n".format(block=block, page=page).encode()) 348 | self.ser.readline() # remove echo 349 | 350 | while True: 351 | line = self.ser.readline().strip() 352 | 353 | if line.startswith(b"-----"): 354 | if main_area_read: 355 | break 356 | main_area_read = True 357 | continue 358 | 359 | #danitool begin: eat crashing line 360 | if line.startswith(b"Correctable ECC Error detected"): 361 | continue 362 | #danitool end 363 | 364 | if len(line) == 0: 365 | continue 366 | 367 | try: 368 | for b in parse_serial_line(line.decode()): 369 | buf += b 370 | except UnicodeDecodeError: 371 | traceback.print_exc() 372 | 373 | if len(buf) != self.page_size: 374 | raise IOError("Read page size ({}) different from expected size ({})" 375 | .format(len(buf), self.page_size)) 376 | 377 | self.eat_junk() 378 | 379 | return buf 380 | 381 | def read_pages(self, block: int, page_start: int, number: int) -> Generator[bytes, None, None]: 382 | for page in range(page_start, page_start + number): 383 | retries = 0 384 | 385 | while retries < self.max_retries: 386 | try: 387 | yield self.read_page(block, page) 388 | break 389 | except Exception: 390 | print("Block {} page {} read failed, retrying.".format(block, page)) 391 | retries += 1 392 | self.printer.exc() 393 | else: 394 | raise IOError("Max number of page read retries exceeded") 395 | 396 | def read_pages_bulk(self, block: int, page_start: int, number: int) -> Generator[bytes, None, None]: 397 | self.ser.write("dn {block} {page} {number}\r\n".format(block=block, page=page_start, number=number).encode()) 398 | yield from self.parse_pages_bulk() 399 | 400 | def read_block(self, block: int) -> Generator[bytes, None, None]: 401 | count = 0 402 | for i in self.read_pages(block, 0, self.block_size // self.page_size): 403 | yield i 404 | count += 1 405 | 406 | expected = self.block_size // self.page_size 407 | if count != expected: 408 | raise IOError("Read block size ({}) different from expected size ({})" 409 | .format(count, expected)) 410 | 411 | def read_blocks(self, block: int, number: int) -> Generator[bytes, None, None]: 412 | for block in range(block, block + number): 413 | yield from self.read_block(block) 414 | 415 | def read_nand(self) -> Generator[bytes, None, None]: 416 | for block in range(self.nand_size // self.block_size): 417 | yield from self.read_block(block) 418 | 419 | def read_nand_bulk(self) -> Generator[bytes, None, None]: 420 | yield from self.read_pages_bulk(0, 0, self.nand_size // self.page_size) 421 | 422 | 423 | def main(): 424 | parser = argparse.ArgumentParser(description="Broadcom CFE dumper") 425 | parser.add_argument('-N', '--nand-size', type=int, help="NAND size", default=NAND_SIZE) 426 | parser.add_argument('-B', '--block-size', type=int, help="Block size", default=BLOCK_SIZE) 427 | parser.add_argument('-P', '--page-size', type=int, help="Page size", default=PAGE_SIZE) 428 | parser.add_argument('-D', '--device', type=str, help="Serial port", required=True) 429 | parser.add_argument('-b', '--baudrate', type=str, help="Baud rate", default=115200) 430 | parser.add_argument('-t', '--timeout', type=float, help="Serial port timeout", default=0.1) 431 | parser.add_argument('-O', '--output', type=str, help="Output file, '-' for stdout", default='-') 432 | parser.add_argument('-r', '--max-retries', type=int, help="Max retries per page on failure", default=MAX_RETRIES) 433 | 434 | subparsers = parser.add_subparsers(help="Available commands", dest='command') 435 | 436 | readpage_parser = subparsers.add_parser('page', help="Read one or more pages") 437 | readpage_parser.add_argument('block', type=int, help="Block to read pages from") 438 | readpage_parser.add_argument('page', type=int, help="Page to read") 439 | readpage_parser.add_argument('number', type=int, help="Number of subsequent pages to read (if more than 1)", 440 | default=1) 441 | 442 | readpage_parser = subparsers.add_parser('pages_bulk', help="Read one or more pages in bulk") 443 | readpage_parser.add_argument('block', type=int, help="Block to read pages from") 444 | readpage_parser.add_argument('page', type=int, help="Page to read") 445 | readpage_parser.add_argument('number', type=int, help="Number of subsequent pages to read (if more than 1)", 446 | default=1) 447 | 448 | readblock_parser = subparsers.add_parser('block', help="Read one or more blocks") 449 | readblock_parser.add_argument('block', type=int, help="Block to read") 450 | readblock_parser.add_argument('number', type=int, help="Number of subsequent blocks to read (if more than 1)", 451 | default=1) 452 | 453 | subparsers.add_parser('nand', help="Read the entire NAND") 454 | subparsers.add_parser('nand_bulk', help="Read the entire NAND in bulk") 455 | 456 | args = parser.parse_args() 457 | printer = ProgressPrinter(sys.stdout if args.output != "-" else sys.stderr, args.page_size, "pages") 458 | ser = serial.Serial(args.device, args.baudrate, timeout=args.timeout) 459 | c = CFECommunicator(ser, args.block_size, args.page_size, args.nand_size, args.max_retries, printer) 460 | 461 | if args.command == 'page': 462 | gen = c.read_pages(args.block, args.page, args.number) 463 | pages = args.number 464 | elif args.command == 'pages_bulk': 465 | gen = c.read_pages_bulk(args.block, args.page, args.number) 466 | pages = args.number 467 | elif args.command == 'block': 468 | gen = c.read_blocks(args.block, args.number) 469 | pages = args.block_size // args.page_size * args.number 470 | elif args.command == 'nand': 471 | gen = c.read_nand() 472 | pages = args.nand_size // args.page_size 473 | elif args.command == 'nand_bulk': 474 | gen = c.read_nand_bulk() 475 | pages = args.nand_size // args.page_size 476 | else: 477 | raise RuntimeError 478 | 479 | pages_read = 0 480 | 481 | c.wait_for_prompt() 482 | 483 | with open(args.output, 'wb') as output: 484 | try: 485 | for page in gen: 486 | pages_read += 1 487 | output.write(page) 488 | if type(c) == CFECommunicator or pages_read % 100 == 0: 489 | printer.print_progress(pages_read, pages) 490 | except Exception: 491 | printer.print_progress(pages_read, pages) 492 | raise 493 | 494 | printer.print("\n\n") 495 | 496 | 497 | if __name__ == "__main__": 498 | main() 499 | -------------------------------------------------------------------------------- /cfenandzyx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | #enable the ATDF command: 5 | #CFE> ATEN 1 10F0A563 6 | # 7 | #dump the block 0: 8 | #CFE> ATDF 0 9 | # 10 | #10 00 02 81 00 00 00 00 00 00 00 00 00 00 00 00 11 | #00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 | #00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 13 | #00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 | #00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 15 | #00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 | #00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 17 | #00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18 | # ----- trimmed lines ----- 19 | #ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 20 | #ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 21 | #ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 22 | #ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 23 | #ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 24 | #ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 25 | #ff ff ff ff ff ff ff ff 00 00 00 00 00 01 ca ff 26 | #*** command status = 0 27 | #CFE> 28 | 29 | # example, dump 128MB NAND flash: 30 | # python cfenandzyx.py --verbose --blkn 0 --size 0x8000000 --read mitrastardump.bin 31 | 32 | #Keep python2 working 33 | from __future__ import division #1/2 = float, 1//2 = integer 34 | from __future__ import print_function #print("blah", file=whatever) 35 | #Keep python2 working end 36 | from optparse import OptionParser 37 | import serial 38 | import sys 39 | #import struct 40 | import re 41 | lineregex = re.compile(r'((?:[0-9a-f]{2} ){16})') 42 | def get2menu(ser,verbose): 43 | if verbose: 44 | print("Waiting for a prompt...", file=sys.stderr) 45 | while True: 46 | ser.write('\r'.encode()) 47 | if(ser.read(1)==b'C' and ser.read(1)==b'F' and ser.read(1)==b'E' and ser.read(1)==b'>'): 48 | while ser.read(256): 49 | pass 50 | if verbose: 51 | print("Ok.", file=sys.stderr) 52 | return 53 | 54 | def memreadblock(ser,blkn): 55 | while ser.read(1): 56 | pass 57 | ser.write(("ATDF %d"%blkn).encode()) 58 | ser.write('\r'.encode()) 59 | buf = b'' 60 | m = False 61 | while not m: 62 | m = lineregex.match(ser.readline().decode()) 63 | while m: 64 | #print(m.groups()) 65 | if sys.version_info >= (3, 0): 66 | buf += bytes.fromhex(m.group(1)) 67 | else: 68 | buf += ''.join([chr(int(x, 16)) for x in m.group(1)[1:].split(' ')]) 69 | m = lineregex.match(ser.readline().decode()) 70 | return buf 71 | 72 | def memreadblock2file(ser,fd,blkn,size): 73 | while True: 74 | buf = memreadblock(ser,blkn) 75 | if len(buf) == size: 76 | break 77 | sys.stderr.write('!') 78 | fd.write(buf) 79 | return 80 | 81 | def memread(ser,path,blkn,size,verbose): 82 | #block size should be 131072 = 128kB = 0x20000 83 | bs = 131072 84 | get2menu(ser,verbose) 85 | if path == "-": 86 | # get sys.stdout in Python 2 or sys.stdout.buffer in Python 3 87 | fd = getattr(sys.stdout, 'buffer', sys.stdout) 88 | else: 89 | fd = open(path,"wb") 90 | while size > 0: 91 | if size > bs: 92 | print("Block: " + str(blkn), file=sys.stderr) 93 | print("Size: " + str(size), file=sys.stderr) 94 | memreadblock2file(ser,fd,blkn,bs) 95 | size -= bs 96 | blkn += 1 97 | else: 98 | print("Block: " + str(blkn), file=sys.stderr) 99 | print("Size: " + str(size), file=sys.stderr) 100 | memreadblock2file(ser,fd,blkn,size) 101 | size = 0 102 | fd.close() 103 | return 104 | 105 | def main(): 106 | optparser = OptionParser("usage: %prog [options]",version="%prog 0.1") 107 | optparser.add_option("--verbose", action="store_true", dest="verbose", help="be verbose", default=False) 108 | optparser.add_option("--serial", dest="serial", help="specify serial port", default="/dev/ttyUSB0", metavar="dev") 109 | optparser.add_option("--read", dest="read", help="read mem to file", metavar="path") 110 | optparser.add_option("--blkn", dest="blkn",help="block position", metavar="blkn") 111 | optparser.add_option("--size", dest="size",help="size to copy", metavar="bytes") 112 | (options, args) = optparser.parse_args() 113 | if len(args) != 0: 114 | optparser.error("incorrect number of arguments") 115 | ser = serial.Serial(options.serial, 115200, timeout=1) 116 | if options.read: 117 | memread(ser,options.read,int(options.blkn,0),int(options.size,0),options.verbose) 118 | return 119 | if __name__ == '__main__': 120 | main() 121 | -------------------------------------------------------------------------------- /cfetool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # This tool expects data from the serial terminal in a format like this 5 | # CFE> dm b8020000 160 6 | # b8020000: 36 00 00 00 42 72 6f 61 64 63 6f 6d 20 43 6f 72 6...Broadcom Cor 7 | # b8020010: 70 6f 72 61 74 69 6f 00 76 65 72 2e 20 32 2e 30 poratio.ver. 2.0 8 | # b8020020: 00 00 00 00 00 00 36 33 36 38 00 00 39 36 33 36 ......6368..9636 9 | # b8020030: 38 4d 56 57 47 00 00 00 00 00 00 00 31 00 34 30 8MVWG.......1.40 10 | # b8020040: 36 32 39 38 30 00 00 00 30 00 00 00 00 00 00 00 62980...0....... 11 | # b8020050: 00 00 00 00 30 00 00 00 00 00 00 00 00 00 33 32 ....0.........32 12 | # b8020060: 31 37 31 36 32 34 39 36 00 00 32 36 30 31 35 32 17162496..260152 13 | # b8020070: 38 00 00 00 33 32 31 37 31 36 32 34 39 36 00 00 8...3217162496.. 14 | # b8020080: 31 34 36 31 34 35 32 00 00 00 32 00 00 00 00 00 1461452...2..... 15 | # b8020090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 16 | # 17 | # *** command status = 0 18 | # CFE> 19 | 20 | from __future__ import division 21 | from optparse import OptionParser 22 | 23 | import serial 24 | import sys 25 | import re 26 | 27 | lineregex = re.compile(r'(?:[0-9a-f]{8})(?:[:])((?: [0-9a-f]{2}){1,16})') 28 | #lineregex = re.compile(r'(?:[0-9a-f]{8})(?:[:])((?: [0-9a-f]{2}){1,16})(?:\s{4})(?:.{16})') 29 | 30 | def printf(string): 31 | sys.stdout.write(string) 32 | sys.stdout.flush() 33 | 34 | def skip_prompt(ser): 35 | while ser.read(1): 36 | pass 37 | 38 | def wait_prompt(ser): 39 | printf("Waiting for a prompt...") 40 | while True: 41 | ser.write("\x03") 42 | if(ser.read(1) == 'C' and ser.read(1) == 'F' and ser.read(1) == 'E' and ser.read(1) == '>'): 43 | skip_prompt(ser) 44 | printf(" OK\n") 45 | return 46 | 47 | def memreadblock(ser, addr, size): 48 | skip_prompt(ser) 49 | ser.write("dm %x %d\r" %(addr, size)) 50 | buf='' 51 | m = False 52 | while not m: 53 | m = lineregex.match(ser.readline().strip()) 54 | while m: 55 | bytes = [chr(int(x, 16)) for x in m.group(1)[1:].split(' ')] 56 | buf+=''.join(bytes) 57 | m = lineregex.match(ser.readline().strip()) 58 | return buf 59 | 60 | def memreadblock2file(ser, fd, addr, size): 61 | while True: 62 | buf = memreadblock(ser, addr, size) 63 | if len(buf) == size: 64 | break 65 | printf(' [!]\n') 66 | printf(' [.]\n') 67 | fd.write(buf) 68 | return 69 | 70 | def memread(ser, path, addr, size, block): 71 | wait_prompt(ser) 72 | total_size = size 73 | fd = open(path, "wb") 74 | while size > 0: 75 | cur_size = (total_size - size) 76 | printf('%d%% (%d/%d)' %((cur_size / total_size) * 100, cur_size, total_size)) 77 | if size > block: 78 | memreadblock2file(ser, fd, addr, block) 79 | size -= block 80 | addr += block 81 | else: 82 | memreadblock2file(ser, fd, addr, size) 83 | size = 0 84 | fd.close() 85 | return 86 | 87 | def main(): 88 | optparser = OptionParser("usage: %prog [options]",version="%prog 0.1") 89 | optparser.add_option("--block", dest="block", help="buffer block size", default="10240",metavar="block") 90 | optparser.add_option("--serial", dest="serial", help="specify serial port", default="/dev/ttyUSB0", metavar="dev") 91 | optparser.add_option("--read", dest="read", help="read mem to file", metavar="path") 92 | optparser.add_option("--addr", dest="addr",help="mem address", metavar="addr") 93 | optparser.add_option("--size", dest="size",help="size to copy", metavar="bytes") 94 | (options, args) = optparser.parse_args() 95 | if len(args) != 0: 96 | optparser.error("incorrect number of arguments") 97 | ser = serial.Serial(options.serial, 115200, timeout=1) 98 | if options.read: 99 | memread(ser, options.read, int(options.addr, 0), int(options.size, 0), int(options.block, 0)) 100 | return 101 | 102 | if __name__ == '__main__': 103 | main() 104 | -------------------------------------------------------------------------------- /en751221tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # This tool expects data from the serial terminal in a format like this 5 | # bldr> dump b0000000 a0 6 | # b0000000 0b f0 00 0a.00 00 00 00.00 00 00 00.00 00 00 00 |................| 7 | # b0000010 00 00 00 00.00 00 00 00.00 00 00 00.00 00 00 00 |................| 8 | # b0000020 00 00 00 00.00 00 00 00.40 80 90 00.40 80 98 00 |........@...@...| 9 | # b0000030 40 1a 60 00.24 1b ff e6.03 5b d0 24.40 9a 60 00 |@.`.$....[.$@.`.| 10 | # b0000040 3c 1a 00 80.40 9a 68 00.0f f0 00 a2.00 00 00 00 |<...@.h.........| 11 | # b0000050 0f f0 01 60.00 00 00 00.3c 1a bf b0.8f 5b 00 64 |...`....<....[.d| 12 | # b0000060 00 00 00 00.00 1b dc 02.23 7b ff fd.07 60 00 05 |........#{...`..| 13 | # b0000070 00 00 00 00.0f f0 01 e1.00 00 00 00.0b f0 00 23 |...............#| 14 | # b0000080 00 00 00 00.0f f0 03 7e.00 00 00 00.0f f0 01 7c |.......~.......|| 15 | # b0000090 00 00 00 00.3c 02 bf bf.34 42 02 00.24 04 00 00 |....<...4B..$...| 16 | # bldr> 17 | 18 | #Example command, backup 128MB of flash, 4 dumps: 19 | #1st dump 20 | #open minicom in the bootloader CLI execute: 21 | #readflash 80020000 0 1a00000 22 | #close minicom, at the pc execute: 23 | #python2 en751221tool.py --read=dump1.bin --addr=0x80020000 --size=0x1a00000 --block=0x10000 24 | # 25 | #2nd dump 26 | #readflash 80020000 1a00000 1800000 27 | #python2 en751221tool.py --read=dump2.bin --addr=0x80020000 --size=0x1800000 --block=0x10000 28 | # 29 | #3th dump 30 | #readflash 80020000 3200000 3e00000 31 | #python2 en751221tool.py --read=dump3.bin --addr=0x80020000 --size=0x3e00000 --block=0x10000 32 | # 33 | #4th dump 34 | #readflash 80020000 7000000 1000000 35 | #python2 en751221tool.py --read=dump4.bin --addr=0x80020000 --size=0x1000000 --block=0x10000 36 | 37 | from __future__ import division 38 | from optparse import OptionParser 39 | 40 | import serial 41 | import sys 42 | import re 43 | import time 44 | 45 | lineregex = re.compile(r'(?:[0-9a-f]{8} )((?:[ |\\.][0-9a-f]{2}){1,16})') 46 | 47 | def printf(string): 48 | sys.stdout.write(string) 49 | sys.stdout.flush() 50 | 51 | def skip_prompt(ser): 52 | while ser.read(1): 53 | pass 54 | 55 | def wait_prompt(ser): 56 | printf("Waiting for a prompt...") 57 | ser.flush() 58 | while True: 59 | ser.write("\x31") # Press 1 means entering boot mode 60 | ser.write("\x0D") #send carriage return 61 | if(ser.read(1) == 'b' and ser.read(1) == 'l' and ser.read(1) == 'd' and ser.read(1) == 'r' and ser.read(1) == '>'): 62 | ser.write("\x0D") #send carriage return to get a CLI 63 | time.sleep(1) 64 | skip_prompt(ser) 65 | printf(" OK\n") 66 | return 67 | 68 | def memreadblock(ser, addr, size): 69 | skip_prompt(ser) 70 | ser.write("dump %x %x\r" %(addr, size)) 71 | buf='' 72 | m = False 73 | while not m: 74 | line = ser.readline().strip().replace(".", " ", 3) 75 | m = lineregex.match(line) 76 | while m: 77 | bytes = [chr(int(x, 16)) for x in m.group(1)[1:].split(' ')] 78 | buf+=''.join(bytes) 79 | line = ser.readline().strip().replace(".", " ", 3) 80 | m = lineregex.match(line) 81 | return buf 82 | 83 | def memreadblock2file(ser, fd, addr, size): 84 | while True: 85 | buf = memreadblock(ser, addr, size) 86 | if len(buf) == size: 87 | break 88 | printf(' [!]\n') 89 | printf(' [.]\n') 90 | fd.write(buf) 91 | return 92 | 93 | def memread(ser, path, addr, size, block): 94 | wait_prompt(ser) 95 | total_size = size 96 | fd = open(path, "wb") 97 | while size > 0: 98 | cur_size = (total_size - size) 99 | printf('%d%% (%d/%d)' %((cur_size / total_size) * 100, cur_size, total_size)) 100 | if size > block: 101 | memreadblock2file(ser, fd, addr, block) 102 | size -= block 103 | addr += block 104 | else: 105 | memreadblock2file(ser, fd, addr, size) 106 | size = 0 107 | fd.close() 108 | return 109 | 110 | def main(): 111 | optparser = OptionParser("usage: %prog [options]",version="%prog 0.1") 112 | optparser.add_option("--block", dest="block", help="buffer block size", default="10240",metavar="block") 113 | optparser.add_option("--serial", dest="serial", help="specify serial port", default="/dev/ttyUSB0", metavar="dev") 114 | optparser.add_option("--read", dest="read", help="read mem to file", metavar="path") 115 | optparser.add_option("--addr", dest="addr",help="mem address", metavar="addr") 116 | optparser.add_option("--size", dest="size",help="size to copy", metavar="bytes") 117 | (options, args) = optparser.parse_args() 118 | if len(args) != 0: 119 | optparser.error("incorrect number of arguments") 120 | ser = serial.Serial(options.serial, 115200, timeout=1) 121 | if options.read: 122 | memread(ser, options.read, int(options.addr, 0), int(options.size, 0), int(options.block, 0)) 123 | return 124 | 125 | if __name__ == '__main__': 126 | main() 127 | -------------------------------------------------------------------------------- /rt63365tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # This tool expects data from the serial terminal in a format like this 5 | # bldr> dump b0000000 a0 6 | # b0000000 0b f0 00 0a.00 00 00 00.00 00 00 00.00 00 00 00 |................| 7 | # b0000010 00 00 00 00.00 00 00 00.00 00 00 00.00 00 00 00 |................| 8 | # b0000020 00 00 00 00.00 00 00 00.40 80 90 00.40 80 98 00 |........@...@...| 9 | # b0000030 40 1a 60 00.24 1b ff e6.03 5b d0 24.40 9a 60 00 |@.`.$....[.$@.`.| 10 | # b0000040 3c 1a 00 80.40 9a 68 00.0f f0 00 a2.00 00 00 00 |<...@.h.........| 11 | # b0000050 0f f0 01 60.00 00 00 00.3c 1a bf b0.8f 5b 00 64 |...`....<....[.d| 12 | # b0000060 00 00 00 00.00 1b dc 02.23 7b ff fd.07 60 00 05 |........#{...`..| 13 | # b0000070 00 00 00 00.0f f0 01 e1.00 00 00 00.0b f0 00 23 |...............#| 14 | # b0000080 00 00 00 00.0f f0 03 7e.00 00 00 00.0f f0 01 7c |.......~.......|| 15 | # b0000090 00 00 00 00.3c 02 bf bf.34 42 02 00.24 04 00 00 |....<...4B..$...| 16 | # bldr> 17 | 18 | #Example command, backup 8MB of flash: 19 | # python2 rt63365tool.py --read=test.bin --addr=0xB0000000 --size=0x800000 --block=0x10000 20 | 21 | #Example command, dump 32MB of RAM content: 22 | # python2 rt63365tool.py --read=test.bin --addr=0x80000000 --size=0x2000000 --block=0x10000 23 | 24 | from __future__ import division 25 | from optparse import OptionParser 26 | 27 | import serial 28 | import sys 29 | import re 30 | import time 31 | 32 | lineregex = re.compile(r'(?:[0-9a-f]{8} )((?:[ |\\.][0-9a-f]{2}){1,16})') 33 | 34 | def printf(string): 35 | sys.stdout.write(string) 36 | sys.stdout.flush() 37 | 38 | def skip_prompt(ser): 39 | while ser.read(1): 40 | pass 41 | 42 | def wait_prompt(ser): 43 | printf("Waiting for a prompt...") 44 | ser.flush() 45 | while True: 46 | ser.write("\x74") #send t (required on Tplink routers) 47 | ser.write("\x0D") #send carriage return 48 | ser.write("\x0D") #send 2nd carriage return for getting a clean CLI 49 | if(ser.read(1) == 'b' and ser.read(1) == 'l' and ser.read(1) == 'd' and ser.read(1) == 'r' and ser.read(1) == '>'): 50 | skip_prompt(ser) 51 | printf(" OK\n") 52 | return 53 | 54 | def memreadblock(ser, addr, size): 55 | skip_prompt(ser) 56 | ser.write("dump %x %x\r" %(addr, size)) 57 | buf='' 58 | m = False 59 | while not m: 60 | line = ser.readline().strip().replace(".", " ", 3) 61 | m = lineregex.match(line) 62 | while m: 63 | bytes = [chr(int(x, 16)) for x in m.group(1)[1:].split(' ')] 64 | buf+=''.join(bytes) 65 | line = ser.readline().strip().replace(".", " ", 3) 66 | m = lineregex.match(line) 67 | return buf 68 | 69 | def memreadblock2file(ser, fd, addr, size): 70 | while True: 71 | buf = memreadblock(ser, addr, size) 72 | if len(buf) == size: 73 | break 74 | printf(' [!]\n') 75 | printf(' [.]\n') 76 | fd.write(buf) 77 | return 78 | 79 | def memread(ser, path, addr, size, block): 80 | wait_prompt(ser) 81 | total_size = size 82 | fd = open(path, "wb") 83 | while size > 0: 84 | cur_size = (total_size - size) 85 | printf('%d%% (%d/%d)' %((cur_size / total_size) * 100, cur_size, total_size)) 86 | if size > block: 87 | memreadblock2file(ser, fd, addr, block) 88 | size -= block 89 | addr += block 90 | else: 91 | memreadblock2file(ser, fd, addr, size) 92 | size = 0 93 | fd.close() 94 | return 95 | 96 | def main(): 97 | optparser = OptionParser("usage: %prog [options]",version="%prog 0.1") 98 | optparser.add_option("--block", dest="block", help="buffer block size", default="10240",metavar="block") 99 | optparser.add_option("--serial", dest="serial", help="specify serial port", default="/dev/ttyUSB0", metavar="dev") 100 | optparser.add_option("--read", dest="read", help="read mem to file", metavar="path") 101 | optparser.add_option("--addr", dest="addr",help="mem address", metavar="addr") 102 | optparser.add_option("--size", dest="size",help="size to copy", metavar="bytes") 103 | (options, args) = optparser.parse_args() 104 | if len(args) != 0: 105 | optparser.error("incorrect number of arguments") 106 | ser = serial.Serial(options.serial, 115200, timeout=1) 107 | if options.read: 108 | memread(ser, options.read, int(options.addr, 0), int(options.size, 0), int(options.block, 0)) 109 | return 110 | 111 | if __name__ == '__main__': 112 | main() 113 | -------------------------------------------------------------------------------- /rtl8186tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | #This tool expects data from the serial terminal in a format like this 5 | #D 0x80300000 40 6 | #80300000: 60C34A37 72783B1B FA3DBF73 B5A5BE6F 7 | #80300010: 4E01C8DF AEF3DCFD A796CC4F 6FB33EA6 8 | #80300020: 4DE6CCBB B43B07EF EFD5FCB0 3732D4F7 9 | #80300030: 7EDBB4F8 AD7DE946 2643A9B9 3DF31547 10 | #80300040: 4B3BFC3B CDEF9A97 938CFA51 FC448F43 11 | #80300050: E657FEF9 FA73DFCC 46FDD345 D5AB7959 12 | #80300060: 6FAE73D4 9C8BDCC9 B2AB1E71 AF94EED5 13 | #80300070: 552B5FBF C6DFB259 6F6ECED1 8E7AB710 14 | #80300080: 22FAEF57 6A5B303F 3FD57C76 5B614BDE 15 | #80300090: 2FF4976B 6EB9E52F D7BF8DBD AB88B676 16 | 17 | #Example command, backup 2MB of flash: 18 | # python2 cfetool.py --read=test.bin --addr=0xbfc00000 --size=0x200000 --block=0x10000 19 | 20 | from __future__ import division 21 | from optparse import OptionParser 22 | 23 | import serial 24 | import sys 25 | import re 26 | 27 | lineregex = re.compile(r'(?:[0-9A-F]{8})(?:[:])((?: [0-9A-F]{8}){1,4})') 28 | 29 | def printf(string): 30 | sys.stdout.write(string) 31 | sys.stdout.flush() 32 | 33 | def skip_prompt(ser): 34 | while ser.read(1): 35 | pass 36 | 37 | def wait_prompt(ser): 38 | printf("Waiting for a prompt...") 39 | while True: 40 | ser.write('\r'.encode()) 41 | if(ser.read(1)=='<' and ser.read(1)=='R' and ser.read(1)=='e' and ser.read(1)=='a' and ser.read(1)=='l' and ser.read(1)=='T' and ser.read(1)=='e' and ser.read(1)=='k' and ser.read(1)=='>'): 42 | skip_prompt(ser) 43 | printf(" OK\n") 44 | return 45 | 46 | def memreadblock(ser, addr, size): 47 | skip_prompt(ser) 48 | ser.write("D %x %d\r" %(addr, size/4)) 49 | buf='' 50 | m = False 51 | while not m: 52 | s = ser.readline().strip() 53 | m = lineregex.match(re.sub('\s+', ' ', s)) #trim repeated spaces to 1 and then apply lineregex 54 | while m: 55 | bytes = [chr(int(x[0:2],16))+chr(int(x[2:4],16))+chr(int(x[4:6],16))+chr(int(x[6:8],16)) for x in m.group(1)[1:].split(' ')] 56 | buf+=''.join(bytes) 57 | s = ser.readline().strip() 58 | m = lineregex.match(re.sub('\s+', ' ', s)) 59 | return buf 60 | 61 | def memreadblock2file(ser, fd, addr, size): 62 | while True: 63 | buf = memreadblock(ser, addr, size) 64 | if len(buf) == size: 65 | break 66 | printf(' [!]\n') 67 | printf(' [.]\n') 68 | fd.write(buf) 69 | return 70 | 71 | def memread(ser, path, addr, size, block): 72 | wait_prompt(ser) 73 | total_size = size 74 | fd = open(path, "wb") 75 | while size > 0: 76 | cur_size = (total_size - size) 77 | printf('%d%% (%d/%d) Address: %s' %((cur_size / total_size) * 100, cur_size, total_size,hex(addr))) 78 | if size > block: 79 | memreadblock2file(ser, fd, addr, block) 80 | size -= block 81 | addr += block 82 | else: 83 | memreadblock2file(ser, fd, addr, size) 84 | size = 0 85 | printf("100%\n") 86 | fd.close() 87 | return 88 | 89 | def main(): 90 | optparser = OptionParser("usage: %prog [options]",version="%prog 0.1") 91 | optparser.add_option("--block", dest="block", help="buffer block size", default="10240",metavar="block") 92 | optparser.add_option("--serial", dest="serial", help="specify serial port", default="/dev/ttyUSB0", metavar="dev") 93 | optparser.add_option("--read", dest="read", help="read mem to file", metavar="path") 94 | optparser.add_option("--addr", dest="addr",help="mem address", metavar="addr") 95 | optparser.add_option("--size", dest="size",help="size to copy", metavar="bytes") 96 | (options, args) = optparser.parse_args() 97 | if len(args) != 0: 98 | optparser.error("incorrect number of arguments") 99 | ser = serial.Serial(options.serial, 38400, timeout=1) 100 | if options.read: 101 | memread(ser, options.read, int(options.addr, 0), int(options.size, 0), int(options.block, 0)) 102 | return 103 | 104 | if __name__ == '__main__': 105 | main() 106 | -------------------------------------------------------------------------------- /rtl867xtool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # This tool expects data from the serial terminal in a format like this 5 | # d 0x80000000 160 6 | # 0x80000000: 10 00 00 FF 00 00 00 00 10 00 01 05 00 00 00 00 ................ 7 | # 0x80000010: 00 00 00 00 00 00 00 00 10 00 FF F9 00 00 00 00 ................ 8 | # 0x80000020: 10 00 FF F7 00 00 00 00 10 00 FF F5 00 00 00 00 ................ 9 | # 0x80000030: 10 00 FF F3 00 00 00 00 10 00 FF F1 00 00 00 00 ................ 10 | # 0x80000040: 10 00 FF EF 00 00 00 00 10 00 FF ED 00 00 00 00 ................ 11 | # 0x80000050: 10 00 FF EB 00 00 00 00 10 00 FF E9 00 00 00 00 ................ 12 | # 0x80000060: 10 00 FF E7 00 00 00 00 10 00 FF E5 00 00 00 00 ................ 13 | # 0x80000070: 10 00 FF E3 00 00 00 00 10 00 FF E1 00 00 00 00 ................ 14 | # 0x80000080: 10 00 FF DF 00 00 00 00 10 00 FF DD 00 00 00 00 ................ 15 | # 0x80000090: 10 00 FF DB 00 00 00 00 10 00 FF D9 00 00 00 00 ................ 16 | # 17 | 18 | # Example, dump 128MB, NAND flash? 19 | # python2 rtl867xtool.py --read=test.bin --addr=0x80000000 --size=0x8000000 --block=0x10000 20 | 21 | from __future__ import division 22 | from optparse import OptionParser 23 | 24 | import serial 25 | import sys 26 | import re 27 | import time 28 | 29 | lineregex = re.compile(r'0x(?:[0-9A-F]{8})(?:[:])((?: [0-9A-F]{2}){1,16})') 30 | 31 | def printf(string): 32 | sys.stdout.write(string) 33 | sys.stdout.flush() 34 | 35 | def skip_prompt(ser): 36 | while ser.read(1): 37 | pass 38 | 39 | def wait_prompt(ser): 40 | printf("Waiting for a prompt...") 41 | while True: 42 | ser.write("\x31") # Press 1 means entering boot mode 43 | ser.write("\x0D") # send carriage return 44 | if(ser.read(1) == '<' and ser.read(1) == 'R' and ser.read(1) == 'T' and ser.read(1) == 'L' and ser.read(1) == '8' and ser.read(1) == '6' and ser.read(1) == '7' and ser.read(1) == 'X' and ser.read(1) == '>'): 45 | time.sleep(1) 46 | ser.write("\x0D") # send carriage return to get a clean CLI 47 | skip_prompt(ser) 48 | printf(" OK\n") 49 | return 50 | 51 | def memreadblock(ser, addr, size): 52 | skip_prompt(ser) 53 | ser.write("d 0x%x %d\r" %(addr, size)) 54 | buf='' 55 | m = False 56 | while not m: 57 | m = lineregex.match(ser.readline().strip()) 58 | while m: 59 | bytes = [chr(int(x, 16)) for x in m.group(1)[1:].split(' ')] 60 | buf+=''.join(bytes) 61 | m = lineregex.match(ser.readline().strip()) 62 | return buf 63 | 64 | def memreadblock2file(ser, fd, addr, size): 65 | while True: 66 | buf = memreadblock(ser, addr, size) 67 | if len(buf) == size: 68 | break 69 | printf(' [!]\n') 70 | printf(' [.]\n') 71 | fd.write(buf) 72 | return 73 | 74 | def memread(ser, path, addr, size, block): 75 | wait_prompt(ser) 76 | total_size = size 77 | fd = open(path, "wb") 78 | while size > 0: 79 | cur_size = (total_size - size) 80 | printf('%d%% (%d/%d) Address: %s' %((cur_size / total_size) * 100, cur_size, total_size,hex(addr))) 81 | if size > block: 82 | memreadblock2file(ser, fd, addr, block) 83 | size -= block 84 | addr += block 85 | else: 86 | memreadblock2file(ser, fd, addr, size) 87 | size = 0 88 | printf("100%\n") 89 | fd.close() 90 | return 91 | 92 | def main(): 93 | optparser = OptionParser("usage: %prog [options]",version="%prog 0.1") 94 | optparser.add_option("--block", dest="block", help="buffer block size", default="10240",metavar="block") 95 | optparser.add_option("--serial", dest="serial", help="specify serial port", default="/dev/ttyUSB0", metavar="dev") 96 | optparser.add_option("--read", dest="read", help="read mem to file", metavar="path") 97 | optparser.add_option("--addr", dest="addr",help="mem address", metavar="addr") 98 | optparser.add_option("--size", dest="size",help="size to copy", metavar="bytes") 99 | (options, args) = optparser.parse_args() 100 | if len(args) != 0: 101 | optparser.error("incorrect number of arguments") 102 | ser = serial.Serial(options.serial, 115200, timeout=1) 103 | if options.read: 104 | memread(ser, options.read, int(options.addr, 0), int(options.size, 0), int(options.block, 0)) 105 | return 106 | 107 | if __name__ == '__main__': 108 | main() 109 | -------------------------------------------------------------------------------- /zyx1tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import division 4 | from optparse import OptionParser 5 | 6 | import serial 7 | import sys 8 | import re 9 | 10 | lineregex = re.compile(r'(?:[0-9A-F]{8})(?:[:])((?: [0-9A-F]{2}){1,16})') 11 | 12 | def printf(string): 13 | sys.stdout.write(string) 14 | sys.stdout.flush() 15 | 16 | def skip_prompt(ser): 17 | while ser.read(1): 18 | pass 19 | 20 | def wait_esc(ser): 21 | while True: 22 | if(ser.read(1) == 'E' and ser.read(1) == 'S' and ser.read(1) == 'C'): 23 | return 24 | 25 | def wait_prompt(ser): 26 | printf("Waiting for a prompt...") 27 | wait_esc(ser) 28 | ser.flush() 29 | while True: 30 | ser.write('\033') 31 | if(ser.read(1) == 'A' and ser.read(1) == 'T' and ser.read(1) == 'C' and ser.read(1) == 'm' and ser.read(1) == 'd' and ser.read(1) == '>'): 32 | skip_prompt(ser) 33 | printf(" OK\n") 34 | return 35 | 36 | def memreadblock(ser, addr, size): 37 | pause_str = "< Press any key to Continue, ESC to Quit >" 38 | skip_prompt(ser) 39 | ser.write("ATDU%x,%x\r" %(addr, size)) 40 | buf='' 41 | m = False 42 | while not m: 43 | line = ser.readline().strip().replace("-", " ", 1) 44 | m = lineregex.match(line) 45 | while m: 46 | bytes = [chr(int(x, 16)) for x in m.group(1)[1:].split(' ')] 47 | buf+=''.join(bytes) 48 | line = ser.readline().strip().replace("-", " ", 1) 49 | m = lineregex.match(line) 50 | if line.find(pause_str, 0, len(pause_str)) == 0: 51 | ser.write('\r') 52 | line = ser.readline().strip().replace("-", " ", 1) 53 | m = lineregex.match(line) 54 | return buf 55 | 56 | def memreadblock2file(ser, fd, addr, size): 57 | while True: 58 | buf = memreadblock(ser, addr, size) 59 | if len(buf) == size: 60 | break 61 | printf(' [!]\n') 62 | printf(' [.]\n') 63 | fd.write(buf) 64 | return 65 | 66 | def memread(ser, path, addr, size, block): 67 | wait_prompt(ser) 68 | total_size = size 69 | fd = open(path, "wb") 70 | while size > 0: 71 | cur_size = (total_size - size) 72 | printf('%d%% (%d/%d)' %((cur_size / total_size) * 100, cur_size, total_size)) 73 | if size > block: 74 | memreadblock2file(ser, fd, addr, block) 75 | size -= block 76 | addr += block 77 | else: 78 | memreadblock2file(ser, fd, addr, size) 79 | size = 0 80 | fd.close() 81 | return 82 | 83 | def main(): 84 | optparser = OptionParser("usage: %prog [options]",version="%prog 0.1") 85 | optparser.add_option("--block", dest="block", help="buffer block size", default="10240",metavar="block") 86 | optparser.add_option("--serial", dest="serial", help="specify serial port", default="/dev/ttyUSB0", metavar="dev") 87 | optparser.add_option("--read", dest="read", help="read mem to file", metavar="path") 88 | optparser.add_option("--addr", dest="addr",help="mem address", metavar="addr") 89 | optparser.add_option("--size", dest="size",help="size to copy", metavar="bytes") 90 | (options, args) = optparser.parse_args() 91 | if len(args) != 0: 92 | optparser.error("incorrect number of arguments") 93 | ser = serial.Serial(options.serial, 115200, timeout=1) 94 | if options.read: 95 | memread(ser, options.read, int(options.addr, 0), int(options.size, 0), int(options.block, 0)) 96 | return 97 | 98 | if __name__ == '__main__': 99 | main() 100 | -------------------------------------------------------------------------------- /zyx2tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import division 4 | from optparse import OptionParser 5 | 6 | import serial 7 | import sys 8 | import re 9 | 10 | lineregex = re.compile(r'(?:[0-9a-f]{8})(?:[:])((?: [0-9a-f]{2}){1,16})') 11 | #lineregex = re.compile(r'(?:[0-9a-f]{8})(?:[:])((?: [0-9a-f]{2}){1,16})(?:\s{4})(?:.{16})') 12 | 13 | def printf(string): 14 | sys.stdout.write(string) 15 | sys.stdout.flush() 16 | 17 | def skip_prompt(ser): 18 | while ser.read(1): 19 | pass 20 | 21 | def wait_prompt(ser): 22 | printf("Waiting for a prompt...") 23 | while True: 24 | ser.write("?") 25 | if(ser.read(1) == 'C' and ser.read(1) == 'F' and ser.read(1) == 'E' and ser.read(1) == '>'): 26 | skip_prompt(ser) 27 | printf(" OK\n") 28 | return 29 | 30 | def memreadblock(ser, addr, size): 31 | skip_prompt(ser) 32 | ser.write("ATDU %x %d\r" %(addr, size)) 33 | buf='' 34 | m = False 35 | while not m: 36 | m = lineregex.match(ser.readline().strip()) 37 | while m: 38 | bytes = [chr(int(x, 16)) for x in m.group(1)[1:].split(' ')] 39 | buf+=''.join(bytes) 40 | m = lineregex.match(ser.readline().strip()) 41 | return buf 42 | 43 | def memreadblock2file(ser, fd, addr, size): 44 | while True: 45 | buf = memreadblock(ser, addr, size) 46 | if len(buf) == size: 47 | break 48 | printf(' [!]\n') 49 | printf(' [.]\n') 50 | fd.write(buf) 51 | return 52 | 53 | def memread(ser, path, addr, size, block): 54 | wait_prompt(ser) 55 | total_size = size 56 | fd = open(path, "wb") 57 | while size > 0: 58 | cur_size = (total_size - size) 59 | printf('%d%% (%d/%d)' %((cur_size / total_size) * 100, cur_size, total_size)) 60 | if size > block: 61 | memreadblock2file(ser, fd, addr, block) 62 | size -= block 63 | addr += block 64 | else: 65 | memreadblock2file(ser, fd, addr, size) 66 | size = 0 67 | fd.close() 68 | return 69 | 70 | def main(): 71 | optparser = OptionParser("usage: %prog [options]",version="%prog 0.1") 72 | optparser.add_option("--block", dest="block", help="buffer block size", default="10240",metavar="block") 73 | optparser.add_option("--serial", dest="serial", help="specify serial port", default="/dev/ttyUSB0", metavar="dev") 74 | optparser.add_option("--read", dest="read", help="read mem to file", metavar="path") 75 | optparser.add_option("--addr", dest="addr",help="mem address", metavar="addr") 76 | optparser.add_option("--size", dest="size",help="size to copy", metavar="bytes") 77 | (options, args) = optparser.parse_args() 78 | if len(args) != 0: 79 | optparser.error("incorrect number of arguments") 80 | ser = serial.Serial(options.serial, 115200, timeout=1) 81 | if options.read: 82 | memread(ser, options.read, int(options.addr, 0), int(options.size, 0), int(options.block, 0)) 83 | return 84 | 85 | if __name__ == '__main__': 86 | main() 87 | --------------------------------------------------------------------------------