Showing
8 changed files
with
374 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
This diff is collapsed. Click to expand it.
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