PROLEAD
A Probing-Based Leakage Detection Tool for Hardware and Software
Loading...
Searching...
No Matches
parse_args.py
Go to the documentation of this file.
1START_SYMBOL = "cipher"
2MAIN_SYMBOL = "main"
3HALT_SYMBOLS = ["report_done"]
4IGNORE_SYMBOLS = []
5
6RELATIVE_PATH = ""
7
10
11from subprocess import Popen, PIPE
12import os
13import sys
14
15def is_int(s):
16 try:
17 int(s, 16)
18 except:
19 return False
20 return True
21
22def run(cmd):
23 p = Popen(cmd.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE)
24 output, err = p.communicate()
25 output = output.decode('UTF-8')
26 err = err.decode('UTF-8')
27 if p.returncode != 0:
28 print(output)
29 print(err)
30 sys.exit(1)
31 return output, err
32
33def num(s):
34 return hex(int(s, 16))
35
36
37def extract_args(FolderName):
38
39 args = ""
40
41 input_parameter_list = list()
42 functionContainingCipher = sys.argv[0]
43
44
45 argv = sys.argv[1:] #remove function name, inputs remain
46
47 for i in range(len(argv)):
48 input_parameter_list.append(str(argv[i]))
49
50 # if str(FolderName).rpartition("/")[0]:
51 # RELATIVE_PATH = str(FolderName).rpartition("/")[0] + str(FolderName).rpartition("/")[1]#"../example_sw/"
52 # else:
53 # RELATIVE_PATH = "./"
54
55 RELATIVE_PATH = str(FolderName).rpartition("results")[0] + "binary"
56
57 armV = None
58 out,err = run("arm-none-eabi-readelf -A " + RELATIVE_PATH + "/binary.elf")
59 tmp,err = run("arm-none-eabi-readelf -A " + RELATIVE_PATH + "/binary.elf")
60 # extract/verify supported architecture
61 if "Tag_CPU_name:" in out:
62 out = out[out.find("Tag_CPU_name:"):]
63 out = out[out.find(":")+1 : out.find("\n")].strip()
64 if out == '"6-M"':
65 armV = "6-M"
66 elif out == '"7-M"':
67 armV = "7-M"
68 else:
69 if "Tag_CPU_arch:" in tmp:
70 tmp = tmp[tmp.find("Tag_CPU_arch:"):]
71 tmp = tmp[tmp.find(":")+1 : tmp.find("\n")].strip()
72 if tmp == "v7E-M":
73 armV = "7E-M"
74 elif tmp == "v7-M":
75 armV = "v7-M"
76 elif tmp == "v6-M":
77 armV = "v6-M"
78 else:
79 print("ERROR: {} is not an ARMv6-M, ARMv7-M or ARMv7E-M binary.".format(tmp))
80 sys.exit(1)
81 else:
82 print("ERROR: 'Tag_CPU_arch' is missing in the output of readelf.")
83 sys.exit(1)
84 else:
85 print("ERROR: 'Tag_CPU_name' is missing in the output of readelf.")
86 sys.exit(1)
87
88
89
90 # extract addresses of symbols
91
92 with open(RELATIVE_PATH + "/binary.map", "rt") as file:
93 lines = file.readlines()
94
95 flash = (0,0)
96 end_of_flash = None
97 ram = (0,0)
98 start_address = None
99 main_address = None
100 functionContainingCipher_address = None
101 halt_addresses = list()
102 ignore_ranges = list()
103 following_function_cipher_address = None
104 following_function_cipher_found = False
105 ram_edata_address = None
106 rand_section_start = None
107 rand_section_end = None
108 rand_size_detected = False
109 input_parameter_addresses = dict.fromkeys(input_parameter_list)#stores ram addresses for parameters passed to emulator
110 inputs_found = False
111
112 for line in lines:
113 parts = line.split()
114 if len(parts) < 2: continue
115
116 if ".data" in parts and inputs_found == False:
117 lines_idx = lines.index(line)
118 data_parts = lines[lines_idx].split() #first occurance of .data
119 # go through data segment of .map file until data segment ends and extract all addresses for the inputs
120 while(not ("_edata" in data_parts)):
121 for input_param in input_parameter_list:
122 if input_param in data_parts:
123 input_parameter_addresses[input_param] = data_parts[data_parts.index(input_param)-1]
124 lines_idx += 1
125 data_parts = lines[lines_idx].split()
126 inputs_found = True
127
128 if "_edata" in parts:
129 ram_edata_address = num(parts[0])
130 if ".randomness" in parts and not rand_size_detected:
131 rand_section_start = int(parts[1], 0)
132 rand_section_end = rand_section_start + int(parts[2], 0)
133 rand_size_detected = True
134 if parts[0].upper() == "FLASH":
135 flash = (num(parts[1]), num(parts[2])) # origin, length
136 end_of_flash = int(parts[1], 0) + int(parts[2],0)
137 elif parts[0].upper() == "RAM":
138 ram = (num(parts[1]), num(parts[2])) # origin, length
139
140 if (not following_function_cipher_found) and ("cipher" in parts):
141 following_function_cipher_found = True
142 following_function_cipher_address = (lines[lines.index(line) + 1].split())[0]
143 # extract all symbols
144
145 begin_ignore = None
146 sections = list()
147 out,err = run("arm-none-eabi-objdump " + RELATIVE_PATH + "/binary.elf -t")
148 lines = sorted([l for l in out.split("\n")])
149 for l in lines:
150 parts = l.split()
151
152 if len(parts) < 3: continue
153
154 if begin_ignore != None and parts[2] == "F" and not (parts[-1] in IGNORE_SYMBOLS):
155 if is_int(parts[0]):
156 ignore_ranges.append((begin_ignore, hex(min(int(parts[0],16), end_of_flash))))
157 begin_ignore = None
158
159 if parts[-1] == START_SYMBOL:
160 start_address = num(parts[0])
161 elif parts[-1] == MAIN_SYMBOL:
162 main_address = num(parts[0])
163 elif parts[-1] in HALT_SYMBOLS:
164 halt_addresses.append((parts[-1], num(parts[0])))
165 elif (parts[-1] in IGNORE_SYMBOLS or parts[2] == "O") and begin_ignore == None:
166 begin_ignore = num(parts[0])
167
168 if parts[-1] == functionContainingCipher:
169 functionContainingCipher_address = parts[0]
170
171 if begin_ignore != None:
172 ignore_ranges.append((begin_ignore, hex(end_of_flash)))
173
174 if flash == (0,0) or ram == (0,0):
175 print("flash or ram section could not be found in map file")
176 sys.exit(1)
177
178 if start_address == None:
179 print("{} symbol could not be found in map file".format(START_SYMBOL))
180 sys.exit(1)
181
182 if functionContainingCipher_address == None:
183 print("Could not find {} function in map file".format(functionContainingCipher))
184
185 if ram_edata_address == None:
186 print("No symbol '_edata' found in linker script in data segment. This is important for placing user input in the correct memory addresses.")
187 sys.exit(1)
188
189 if (rand_section_start == None) or (rand_section_end == None):
190 print("No section for emulator randomness detected. If you need no randomness proceed otherwise reconfigure linker script.")
191
192 for key, val in input_parameter_addresses.items():
193 if val == None:
194 print("No symbol {} could be found in .map file! Check in test source files if {} is declared as global variable and stored in .data section.".format(key, key))
195 sys.exit(1)
196
197 #extract all sections to binary files
198
199 sections = list()
200 out,err = run("arm-none-eabi-objdump " + RELATIVE_PATH + "/binary.elf -h")
201 lines = [l for l in out.split("\n")]
202 while len(lines) > 0:
203 l = lines.pop(0)
204 parts = l.split()
205 if len(parts) == 7 and parts[1].startswith("."):
206 attr = lines.pop(0)
207 if "ALLOC" not in attr: continue
208 if int(parts[2], 16) != 0: # size != 0
209 sections.append((parts[1], num(parts[3]))) # name, offset
210
211 for name, _ in sections:
212 run("arm-none-eabi-objcopy -O binary --only-section="+name+" " + RELATIVE_PATH + "/binary.elf " + RELATIVE_PATH + "/code_section"+name)
213
214
215 # check if compiler optimizations removed call to cipher from main (e.g replacing it with cipher.constprop.0)
216 # if so set --start to start address of modified label
217
218 with open(RELATIVE_PATH + "/disassembled.txt", "r") as f:
219 lines = f.readlines()
220 sanitised_lines = []
221 for line in lines:
222 sanitised_lines.append((line.replace("\n", "")).replace("\t", " "))
223 sanitised_lines = list(filter(None, sanitised_lines))
224 cipher_list = []
225 original_cipher_list = []
226 main_list = []
227
228 for line in sanitised_lines:
229 if((functionContainingCipher_address in line) and ("<{}>".format(functionContainingCipher) in line)):
230 main_list = sanitised_lines[sanitised_lines.index(line):]
231 break
232
233 for line in main_list:
234
235 if((not line.startswith(" ")) and (main_list.index(line) != 0)):
236 main_list = main_list[:main_list.index(line)]
237 break
238
239 for line in main_list:
240 if("cipher" in line):
241 cipher_list.append(line)
242 if("<cipher>" in line):
243 original_cipher_list.append(line)
244
245 if(len(original_cipher_list) == 0):
246 print("could not find <cipher> symbol in compiled main-function. Checking for compiler optimized modifications...")
247
248 if(len(cipher_list) == 0):
249 print("could not find <cipher> related functions in main. Please check disassembled.txt. Aborting...")
250 sys.exit(-1)
251 else:
252
253 print("found in {} (order of occurrance):".format(functionContainingCipher))
254 found_cipher_elements = []
255 for line in cipher_list:
256 elements = list(filter(None, line.split(" ")))
257 function_address = elements[-2]
258 function_name = elements[-1]
259 print("\taddress: {}\tfunction: {}\n".format(function_address, function_name))
260 found_cipher_elements.append((function_address, function_name))
261 print("taking {}".format(found_cipher_elements[0][1]))
262 input("press enter to continue with this function to test...")
263 start_address = num(found_cipher_elements[0][0])
264
265 # generate command line args
266
267 args = "--start {}".format(start_address)
268
269 args += " --main {}".format(main_address)
270
271 for x in halt_addresses:
272 args += " --halt {} {}".format(x[0], x[1])
273 for start, end in ignore_ranges:
274 args += " --ignore {} {}".format(start, end)
275
276 args += " --flash {} {} --ram {} {}".format(flash[0], flash[1], ram[0], ram[1])
277
278 args += " --edata_used_ram {}".format(ram_edata_address)
279
280 args += " --randomness_section {} {}".format(rand_section_start, rand_section_end)
281
282 if armV == "6-M":
283 args += " --armv6m"
284 elif armV == "7-M":
285 args += " --armv7m"
286 else:
287 args += " --armv7e-m"
288
289 for name, offset in sections:
290 args += " --section " + RELATIVE_PATH + "/code_section{} {}".format(name, offset)
291
292 args += " --inputs"
293 for key, val in input_parameter_addresses.items():
294 args += " {} {}".format(key,val)
295
296
297
298 return args
299
def run(cmd)
Definition: parse_args.py:22
def num(s)
Definition: parse_args.py:33
def extract_args(FolderName)
Definition: parse_args.py:37
def is_int(s)
Definition: parse_args.py:15