· 6 years ago · Jul 26, 2019, 10:44 PM
1#!/usr/bin/env python
2"""Memory Readback Tool
3The number of bytes you read must be a multiple of 16!
4"""
5
6import serial
7import struct
8import sys
9import argparse
10import random
11import argparse
12import shutil
13import struct
14import json
15import zlib
16import os
17import random
18from Crypto.Cipher import AES
19from Crypto.Random.random import StrongRandom
20
21
22from intelhex import IntelHex
23def CMACHash(key,inBytes):
24 encryptor = AES.new(key,AES.MODE_CBC,b'\x00'*16,segment_size=128)
25 if len(inBytes) % 16 != 0:
26 block = inBytes + b'\x00'*(len(inBytes)%16)
27 else:
28 block = inBytes
29 output = (encryptor.encrypt(block))
30 return output[-16:]
31
32def encryptCBC(key, iv, inBytes,outfile):
33 """ Takes in a key, initialization vector, and a file location of the input, and location of the output"""
34 encryptor = AES.new(key, AES.MODE_CBC, iv,segment_size=128)
35 if len(inBytes) % 16 != 0:
36 block = inBytes + b'\x00'*(len(inBytes)%16)
37 else:
38 block = inBytes
39 outfile.write(encryptor.encrypt(block))
40
41def encryptAES(key, iv, inBytes):
42 """ Takes in a key, initialization vector, and a file location of the input, and location of the output"""
43 encryptor = AES.new(key, AES.MODE_CFB, iv,segment_size=128)
44 if len(inBytes) % 16 != 0:
45 block = inBytes + b'\x00'*(len(inBytes)%16)
46 else:
47 block = inBytes
48 return encryptor.encrypt(block)
49
50def decryptAES(key, iv, inBytes):
51 """ Takes in a key, initialization vector, and a file location of the input, and location of the output"""
52 encryptor = AES.new(key, AES.MODE_CFB, iv,segment_size=128)
53# if len(inBytes) % 16 != 0:
54# block = inBytes + b'\x00'*(len(inBytes)%16)
55# else:
56# block = inBytes
57 block = inBytes
58 return encryptor.decrypt(block)
59
60
61def readSecrets():
62 with open("secret_configure_output.txt",'r') as keyFile:
63 y = keyFile.readline()
64 keyValues = {}
65 while len(y) > 5:
66 y = y.split(" ")
67 z = y[2]
68 z=z[1:-2]
69 key = []
70 for i in range(len(z)//4):
71 key.append(struct.pack(">B",int(z[4*i+2:4*i+4],16)))
72 key = b''.join(key)
73 keyValues[y[1]] = key
74 y = keyFile.readline()
75 return keyValues
76
77def construct_request(start_addr, num_bytes,PASSWORD):
78 """Construct a request frame to send the the AVR.
79 The frame consists of a 24 byte password followed by the start address (4
80 bytes) and then the number of bytes to read (4 bytes).
81 """
82 # yeah, this function depends on variables defined outside of it. Sorry.
83 formatstring = '>' + str(len(PASSWORD)) + 'sII'
84 return struct.pack(formatstring, PASSWORD, start_addr, num_bytes)
85
86if __name__ == '__main__':
87 parser = argparse.ArgumentParser(description='Memory Readback Tool')
88
89 parser.add_argument("--port", help="Serial port to send update over.",
90 required=True)
91 parser.add_argument("--address", help="First address to read from.",
92 required=True)
93 parser.add_argument("--num-bytes", help="Number of bytes to read.",
94 required=True)
95 parser.add_argument("--datafile", help="File to write data to (optional).")
96 args = parser.parse_args()
97
98 secrets = readSecrets()
99 request = construct_request(int(args.address), int(args.num_bytes),secrets["PC_RB_PW"])
100 # Read in secret key from file.
101 SECRET_KEY = secrets['PC_RB_KEY']
102 IV = secrets['RB_IV']
103 HASH_KEY = secrets['PC_RBH_KEY']
104
105 request = encryptAES(SECRET_KEY, IV, request)
106 request_hash = CMACHash(HASH_KEY, request)
107 request = struct.pack('>' + str(len(request)) + 's' +
108 str(len(request_hash)) + 's', request, request_hash)
109 # Open serial port. Set baudrate to 115200. Set timeout to 3.7 seconds.
110 ser = serial.Serial(args.port, baudrate=115200,timeout=3.7)
111
112 # Wait for bootloader to reset/enter readback mode.
113 while ser.read(1) != 'R':
114 pass
115
116 # Send the request.
117 ser.write(request)
118
119 addr = int(args.address)
120 sz = int(args.num_bytes)
121
122 # Read in apropriate amount of data.
123 # Reading is done by the page.
124 # sz - 1 is used because address is 0 indexed
125 # while sz is implicitly 1 indexed
126 for i in range(addr//256,(addr+sz-1)//256+1):
127 data += ser.read(256)
128
129 data = decryptAES(SECRET_KEY, IV, data)
130 #Slice off excess data
131 data = data[addr%256:][:sz]
132 printable = ["{:02x}".format(ord(x)) for x in data]
133 print(":".join(printable))