From 4416af09f983931597e6d7b1f4e5ad3fd5dcf1ba Mon Sep 17 00:00:00 2001 From: DoroninaD Date: Wed, 18 Oct 2017 23:31:23 +0700 Subject: [PATCH] prepared asm opcode for arm_translate --- r2/arm_translate.py | 103 +++++++++++ r2/main.py | 332 ++++++++++++++++++++++++++++++++++ r2/parse.py | 138 ++++++++++++++ r2Helper.py => r2/r2Helper.py | 27 ++- r2/run.py | 39 ++++ r2/utils.py | 129 +++++++++++++ 6 files changed, 754 insertions(+), 14 deletions(-) create mode 100755 r2/arm_translate.py create mode 100755 r2/main.py create mode 100755 r2/parse.py rename r2Helper.py => r2/r2Helper.py (51%) create mode 100755 r2/run.py create mode 100755 r2/utils.py diff --git a/r2/arm_translate.py b/r2/arm_translate.py new file mode 100755 index 0000000..6ac0835 --- /dev/null +++ b/r2/arm_translate.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- + +from utils import calcRegisters, getCode + +lo_registers = ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7'] + +def convertOneToMany(code, is_thumb, is_pop): + reg = (int(code, 16) >> 12) & 0xF + if not is_thumb: + mask = int('11' + '0' * 26, 2) if not is_pop else int('1100001' + '0' * 21, 2) + return getCode((int(code, 16) ^ mask) & 0xFFFF0000 + pow(2, reg)) + if len(code) == 4: + return code + return getCode((int(code, 16) ^ int('10001011'+'0'*21,2)) & 0xFFFF0000 + pow(2, reg)) #todo проверить! + + + +def pushpopToCode(registers, code, is_thumb, real_reg_count, is_pop): + if real_reg_count == 0: + code = convertOneToMany(code, is_thumb, is_pop) + # считаем сумму регистров + s = calcRegisters(registers) + #mask = int('1'*8, 2) if is_thumb else int('1'*13, 2) + if is_thumb: + mask = int('1' * 8, 2) if len(code)==4 else int('1' * 13, 2) + else: + mask = int('1' * 13, 2) #берем только биты для регистров r0-r12, т.к. sp, lr, pc ге должны быть в списке регистров + c = getCode((int(code, 16) & ~mask) + s) + if is_thumb: + return c + return c[4:]+c[:4] + + +def makeLdrOrStr(old_instr, old_code, rx, ry, a, is_thumb, l): + #return getCode(makeLdrOrStrInner(old_instr, old_code, rx, ry, a, is_thumb)) + return makeLdrOrStrInner(old_instr, old_code, rx, ry, a, is_thumb, l) + + +extended = {'str.w':0xF8C, 'ldr.w':0xF8D, 'strb.w': 0xF88, 'ldrb.w': 0xF89, 'strh.w':0xF8A, 'ldrh.w':0xF8B, 'ldrsh.w':0xF9B, 'ldrsb.w': 0xF99} +basic_arm = {'ldr': 0xE59, 'str':0xE58, 'ldrb': 0xE5D, 'strb': 0xE5C} +basic_thumb = {'ldr': 0x68, 'str':0x60, 'ldrb': 0x78, 'strb': 0x70} +sp_thumb = {'ldr': 0x98, 'str': 0x90} +more_arm = {'ldrh': 0x00B0E1D0, 'strh': 0x00B0E1C0, 'ldrsb':0x00D0E1D0, 'ldrsh':0x00F0E1C0} +more_thumb = {'ldrh': 0x88, 'strh': 0x80} +vcommon = {'vstr': (0xB00ED80, 0xA00ED80), 'vldr':(0xB00ED90, 0xA00ED90)} + +regs = {'sp':13, 'lr':14, 'pc':15} + +def code(old_code, mask, s, is_thumb): + c = getCode((int(old_code, 16) & ~mask) + s) + if is_thumb: + return c + return c[4:] + c[:4] + +def makeLdrOrStrInner(old_instr, old_code, rx, ry, a, is_thumb, l): # ldr rx, [ry + a] + + s = a + # 11-0 + if old_instr.endswith('.w') \ + or old_instr in ['ldr', 'str', 'ldrb', 'strb'] and not is_thumb\ + or old_instr in ['ldrsh', 'ldrsb'] and is_thumb: + mask = int('1' * 12, 2) + return code(old_code, mask, s, is_thumb) + + # 10-6 + if is_thumb and (old_instr in ['str', 'ldr'] and ry!='sp' or old_instr in ['ldrb', 'strb', 'ldrh', 'strh']): + mask = int('1'*5 + '0'*6, 2) + #s = a * int('1000000',2) + s = a * int('10000', 2) + return code(old_code, mask, s, is_thumb) + + # 11-8 3-0 + if not is_thumb and old_instr in ['ldrh', 'strh', 'ldrsb', 'ldrsh', 'ldrd', 'strd']: + mask = int('F0F', 16) + s = a%0x10 + a//0x10 * 0x100 + return code(old_code, mask, s, is_thumb) + + # 7-0 + if is_thumb and (old_instr in ['ldrd', 'strd'] or len(old_instr) == 8 + or (old_instr in ['str', 'ldr'] and ry == 'sp')): + mask = int('1'*8, 2) + s = a//4 + return code(old_code, mask, s, is_thumb) + + ry = regs[ry] if ry in regs else int(ry[1:]) + + if old_instr in vcommon: + if rx[0] == 'd': #d0-d31 + #return vcommon[old_instr][0] + ry + a//4 * 0x10000 + int(rx[1:])%0x10 * 0x10000000 + int(rx[1:])//0x10 * 0x40 + x = vcommon[old_instr][0] + ry + a//4 * 0x10000 + int(rx[1:])%0x10 * 0x10000000 + int(rx[1:])//0x10 * 0x40 + ss = getCode(x) + if is_thumb: + return ss[4:]+ss[:4] + return ss + elif rx[0] == 's': #s0-s31 + #return vcommon[old_instr][1] + ry + a//4 * 0x10000 + int(rx[1:])//2 * 0x10000000 + int(rx[1:])%2 * 0x40 + x = vcommon[old_instr][1] + ry + a//4 * 0x10000 + int(rx[1:])//2 * 0x10000000 + int(rx[1:])%2 * 0x40 + ss = getCode(x) + if is_thumb: + return ss[4:]+ss[:4] + return ss + + diff --git a/r2/main.py b/r2/main.py new file mode 100755 index 0000000..2b4e9f5 --- /dev/null +++ b/r2/main.py @@ -0,0 +1,332 @@ +# -*- coding: utf-8 -*- +import re, utils, r2.arm_translate,parse_functions_utils, static_functions_helper, config_parser, os +import cxxfilt, colored, sys, r2.parse, r2.utils +from r2.r2Helper import r2Helper + +conditions = ['eq','ne','cs','hs','cc','lo','mi','pl','vs','vc','hi','ls','ge','lt','gt','le','al'] +conditions_pattern = '|'.join(conditions) + +NEW = False +RETURN_TYPES = True + +def getPushAndRet(function): + types = ['push','ret', 'pop'] + legal_pushpop_regs = ['lr','pc'] + new_rows = [] + push_added = False + push_pattern = re.compile('(push|stmdb[a-z]*\s*sp!).*lr}') + pop_pattern = re.compile('(pop|ldmia[a-z]*\s*sp!).*(pc|lr)}') + ret_pattern = re.compile('bx.*lr') + pushpop_pattern = re.compile('|'.join(x.pattern for x in [push_pattern,pop_pattern])) + combined_pattern = re.compile('|'.join(x.pattern for x in [pushpop_pattern,ret_pattern])) + for row in function.rows: + # убираем строки до push - они не входят в рандомизаицю + if push_pattern.search(row.opcode): + push_added = True + if not push_added: + continue + if pushpop_pattern.search(row.opcode): + #if row.opcode.startswith('push') or row.opcode.startswith('pop'): + row.opcode = row.opcode \ + .replace('sb', 'r9') \ + .replace('sl', 'r10') \ + .replace('fp', 'r11') + regs = re.search('{.*}', row.opcode).group()[1:-1].replace(' ', '').split(',') + row.regs = regs[:-1] + row.reg = regs[-1] + if combined_pattern.search(row.opcode): + new_rows.append(row) + + if len(new_rows) == 0: + return None + return r2Helper.func_model(function.address, function.name, new_rows) + +def checkOnlyOnePush(opcodes): + return len([op for op in opcodes if op.type=='push'])==1 + +def checkPushAndPopsHaveTheSameRegs(opcodes): + push = '' + for op in opcodes: + if op.type == 'push': + push = op + if op.opcode.startswith('pop') and op.regs!=push.regs: + return False + return True + + +def run(path, start_group, end_group, DEBUG, config): + helper = r2Helper(path) + functions = helper.getFunctions() + + #проверяем, можно ли сделать рандомизацию + # есть ли push/pop с одинаковыми регистрами + + filtered_functions = {} + for addr, function in functions.items(): + filtered_func = getPushAndRet(function) + if filtered_func is None or \ + not checkOnlyOnePush(filtered_func.rows) or \ + not checkPushAndPopsHaveTheSameRegs(filtered_func.rows) or \ + len(filtered_func.rows)<2: + continue + filtered_functions[addr]=filtered_func + + + #bx lr? + #jumps + + init_group_len = len(filtered_functions) + + #difference = [g for g in groups if g not in f] + + if NEW: + have_external_jumps = {} + ext_jumps_list = {} + external_jumps = [] + + # убираем b, которые внутри функции + for index, group in enumerate(groups): + clear = [not group[i][2].startswith('b') for i in range(len(group)) if len(group[i]) > 2] + if all(clear): + continue + first, last = group[0], group[-1] + if len(first) == 2: + first = group[1] + if first[2].startswith('push') or first[2].startswith('stmdb'): + #and last[2].startswith('pop') or last[2].startswith('ldmia'): + first_addr, last_addr = int(first[0], 16), int(last[0], 16) + has_ext_jumps = False + jumps = [] + for g in [group[i] for i in range(len(group)) if group[i][2].startswith('b')]: + if re.search('lr|r[0-9|10|11|12]',g[3]) is not None: + continue + addr = int(g[3],16) + if g[3] == '5dec': + aaa=1 + #if addr < first_addr or addr > last_addr: + if index!=len(groups)-1: + last_addr = int(groups[index+1][0][0],16) + if addr < first_addr or addr > last_addr: + has_ext_jumps = True + jumps.append(hex(addr)) + #break + if has_ext_jumps: + ext_jumps_list[index] = jumps + external_jumps.extend(jumps) + have_external_jumps[index] = group + + external_jumps = set(external_jumps) + external_jumps_res = {} + + for jump in list(external_jumps): + for index, row in enumerate(lines): + address = utils.getAddressFromLine(row) + # нашли строку, на которую jump + if address == jump[2:]: + #проверяем, может прыгнули на push + push_method = re.search('push|stmdb', row) + if push_method is not None: + external_jumps_res[jump] = 'push' + continue + #идем вниз, ищем push/pop/b + for r in lines[index+1:]: + push_method = re.search('push|stmdb', r) + if push_method is not None: + #external_jumps.remove(jump) + external_jumps_res[jump] = 'push' + break + pop_method = re.search('pop|ldmia', r) + if pop_method is not None: + # опасно! надо что-то сделать! + # либо не обрабатывать эту функцию и все, которые на нее ссылаются по addr + # либо связать их и обрабатывать вместе + external_jumps_res[jump] = 'pop' + break + jump_method = re.search('\sb({0})?\s'.format(conditions_pattern), r) + if jump_method is not None: + # опасно! надо что-то сделать! + # сделать рекурсию? + external_jumps_res[jump] = 'jump' + break + + for key, value in ext_jumps_list.items(): + for i in list(value): + if i in external_jumps_res.keys() and external_jumps_res[i] == 'push': + ext_jumps_list[key].remove(i) + + for key, value in ext_jumps_list.items(): + if len(value) == 0: + have_external_jumps.pop(key) + + # фильтруем группы - убираем те, в которых последний pop нe pc + print ('GROUPS:', len(filtered_functions)) + + if NEW: + external_jumps_res = [addr for addr in external_jumps_res if external_jumps_res[addr]!='push'] + # убираем те, которые в have_external_jumps + gr = groups + groups = [] + removed_gr = 0 + for index, group in enumerate(gr): + # если jump в этой группе, то ее тоже не обрабатываем + first_addr, last_addr = group[0][0], gr[index+1][0][0] if index != len(gr)-1 else 0xFFFFFFFF + handle = True + for jump in external_jumps_res: + if int(jump, 16) >= int(first_addr, 16) and int(jump, 16) <= int(last_addr, 16): + handle = False + removed_gr+=1 + break + if handle and index not in have_external_jumps.keys(): + #убираем b/beq/... + group = [g for g in group if not g[2].startswith('b')] + groups.append(group) + + print("Groups after jumps removing", len(filtered_functions)) + + + #добавляем в to_write (адрес, количество старых байт, новые байты) для перезаписи + groups_count = 0 + to_write = [] + l = 0 + full_registers_count = 0 + #1935-36 + print(start_group, ":", end_group) + + regs_added = 0 + for addr, func in filtered_functions.items(): # 66 libcrypto - pop lr => bl - перезапись регистров + push, last = func.rows[0], func.rows[-1] + l+=1 + big_regs = ['sp', 'ip', 'lr', 'pc'] + try: + if any(big_regs[i] in push.regs for i in range(len(big_regs))): #and (str(first[2]).startswith('push') or str(first[2]).startswith('stm')): + continue + except: + aaa=1 + + # добавляем регистры в начало, считаем их количество + real_reg_count = len(push.regs) + #return_size = function_types[func.addr] if RETURN_TYPES else 4 + new_registers, table = utils.addRegistersToStartAndEnd(push.regs, push.opcode, 4) + if new_registers == -1: + full_registers_count+=1 + continue + # меняем втутренние строки, взаимодействующие с sp + #inner_lines = r2.parse.getAllSpLinesForLow(functions[addr].rows, table) todo uncomment + inner_lines = [] + if inner_lines == -1: + continue + groups_count+=1 + # добавляем в to_write (адрес, количество старых байт, новые байты) push + #print (first[0]) + + new_code = r2.arm_translate.pushpopToCode(new_registers, r2.utils.prepare(push.bytes), push.size==2, real_reg_count, False) + to_write.append((push.offset, push.size,r2.utils.toLittleEndian(new_code))) # добавляем новый push + + # добавлаем все pop + pops = [row for row in func.rows if row.opcode.startswith('pop')] + for pop in pops: + new_code = r2.arm_translate.pushpopToCode(new_registers, r2.utils.prepare(pop.bytes), pop.size==2, real_reg_count, True) + to_write.append((pop.offset, pop.size,r2.utils.toLittleEndian(new_code))) # добавляем новый pop + + if len(inner_lines) > 0: + to_write.extend(inner_lines) + key = functions[addr].name + print(colored.setColored('{0}: '.format(key), colored.OKGREEN) + 'old {0}, new {1}'.format(push.regs, new_registers)) + regs_added += len(new_registers) - len(push.regs) + secured = groups_count/init_group_len*100 + # output = 'End:{0}, full regs:{1}, secured:{2}%, average randomness:{3}'\ + # .format(groups_count, full_registers_count, secured, regs_added/groups_count) + + output = 'End:{0}, full regs:{1}, secured:{2}%'\ + .format(groups_count, full_registers_count, secured) + if groups_count>0: + output += ", average randomness:{0}".format(regs_added/groups_count) + + colored.printColored(output, colored.BOLD) + + #переписываем файл + f = open(path, 'br') + text = f.read() + f.close() + + for line in to_write: + offset = int(line[0],16) + text = text[:offset] + line[2] + text[offset+line[1]:] + + f = open(path+'.so', 'bw') + f.write(text) + f.close() + + + + + +def combineFunction(stack_lines): + functions = [] + items = [] + pops = ['pop','pop.w','ldmia','ldmia.w'] + + def is_function_start(line): + return len(line)==2 or line[2].startswith('push') or line[2].startswith('stmdb') + + for index, line in enumerate(stack_lines): + if is_function_start(line): + if line[0]=='ac28': + aaa=1 + functions.append(items) + items = [] + items.append(line) + continue + + if (line[1]!='andeq'): + items.append(line) + + if (line[2] in pops and line[6]=='pc')\ + or (line[2]=='bx' and line[3]=='lr') or line[1]=='andeq': #предполагаемый конец функции + # посмотреть, есть ли прыжки дальше (до начала следующей функции) + # прыжки = b(eq/...) + # также конец функции только если неусловный pop и bx\ + + #находим следующий push/stmdb + ind, next_func_address = index+1, sys.maxsize + while True: + if ind >= len(stack_lines): + break + if is_function_start(stack_lines[ind]): + next_func_address = int(stack_lines[ind][0],16) + break + ind+=1 + + jumps, func_end = [], int(line[0],16) + for item in [i for i in items if len(i)>2]: + if item[2].startswith('b') and not re.match('lr|pc|(r[0-9]*)',item[3]): + jump_to = int(item[3],16) + if func_end < jump_to < next_func_address: + jumps.append(item[3]) + if len(jumps)==0: # если нет, то конец функции + functions.append(items) + items = [] + else: #todo содержит прыжки, посмотерть, выходят ли они за пределы pop pc до push + # если выходят, то не конец фнукции, идем дальше + jumps = [] + continue + + filtered = [] + for f in functions: + if len(f)<2 or len(f[0])<3 or not (f[0][2].startswith('push') or f[0][2].startswith('stmdb')): + continue + push_regs, success = f[0][3], True + for pop in f: + #убираем функции, в котрых регистры в push и pop разные + if (pop[2].startswith('pop') or pop[2].startswith('ldmia')) and pop[3]!=push_regs: + success = False + break + if success: + filtered.append(f) + return filtered + + + + + + diff --git a/r2/parse.py b/r2/parse.py new file mode 100755 index 0000000..6f75a84 --- /dev/null +++ b/r2/parse.py @@ -0,0 +1,138 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +import re, r2.arm_translate, r2.utils + + +def getRxLines(lines, line, table, sp_subbed, sub_ind): + #line = add_sp_to_reg[0].group() + c = re.search('#[0-9]+', line) + c = 0 if c is None else int(c.group()[1:]) + reg = re.search('r11|r10|r12|r[0-9]', line).group() + + #определяем строку, с которой будем искать строки вида [reg, #d] + start_ind = list(lines).index(line+'\n') + + # предполагаем, что изменение sp происходит только в начале и конце функции todo + # если sp еще не отнят, тогда не нужно учитывать a + if start_ind < sub_ind: + sp_subbed = 0 + + # end_reg = list(filter(None, + # [re.search('.*(mov(eq|ne)?|(v)?ldr(b|h|sb|sh|sw)?|add)(.w)?\s{0}, .*'.format(reg), line) for + # line in lines[start_ind+1:]])) + end_reg = r2.utils.searchRe('.*(mov(eq|ne)?|(v)?ldr(b|h|sb|sh|sw)?|add)(.w)?\s{0}, .*'.format(reg),lines[start_ind+1:]) + #определяем строку, ДО которой будем искать строки вида [reg, #d] (mov затирает sp) + end_ind = list(lines).index(end_reg[0].group()+'\n') if len(end_reg) > 0 else len(lines) + + to_write = [] + # Ищем строки вида [reg, #d] + # use_reg = list(filter(None, + # [re.search('.*(ldr|str)(b|h|sb|sh|sw|d)?(.w)?.*\[{0}(, #\-?[0-9]+\])?'.format(reg), line) for + # line in lines[start_ind:end_ind]])) + + # ... [rx] + use_reg = r2.utils.searchRe('.*(ldr|str)(b|h|sb|sh|sw|d)?(.w)?.*\[{0}(, #\-?[0-9]+\])?'.format(reg), lines[start_ind:end_ind]) + + d = 0 + #todo если будет str rx, [sp, #] и уже добавлен в to_write по sp, будет перезаписано? + for l in use_reg: + instr = re.search('v?(ldr|str)(b|h|sb|sh|d)?(.w)?', l.group()).group() + try_d = re.search('#-?[0-9]+', l.group()) + d = int(try_d.group().replace('#', '')) if try_d is not None else 0 + # если d < 0 => если c-d<0 =>[reg, #d-new_regs_count*4] + n = c + d - sp_subbed + if n >= 0: # todo а что если будет sub rx, sp? + rx = re.search('(\s+r10|r11|r12|sp|lr|pc|r[0-9]|((d|s)(([1-2][0-9])|3[0-1]|[0-9]))),', + l.group()).group().strip().replace(',', '') + code, is_thumb = r2.utils.getCodeFromLine(l.group()) + offset = changeOffset(n, d, table) + #offset = d + before_reg_count * 4 + #if n >= real_reg_count * 4: + # offset += after_reg_count * 4 + new_instr_code = r2.arm_translate.makeLdrOrStr(instr, code, rx, reg, offset, is_thumb, l.group()) + # to_write ... [reg, #d-new_regs_count*4] + + to_write.append((r2.utils.getAddressFromLine(l.group()), len(code) // 2, r2.utils.toLittleEndian(new_instr_code))) + + # str rx, [...] + str_reg = list(filter(None,[re.search('.*str(b|h|sb|sh|sw|d)?(.w)?\s{0}.*'.format(reg), line)for line in lines[start_ind:end_ind]])) + if len(str_reg) > 0: + return -1 + return to_write + + +def getAllSpLinesForLow(lines, table): + to_write = [] + opcodes = [line.opcode for line in lines] + + # ldr/str/... rx, [sp], ry - не знаем значение ry, не можем сделать правильное смещение => не обрабатываем такие функции + register_relative = r2.utils.searchRe('(ldr|str).*\[.*\], ', opcodes) + if len(register_relative) > 0: + return -1 + + # ищем строки sub (add) sp, #a => sub (add) sp, #a+new_regs_count*4 => to_write + #sub_add_sp_lines = list(filter(None,[re.search('.*(add|sub)(.w)?\s*sp(, sp)?, #[0-9]+', line) for line in lines])) + sub_add_sp_lines = r2.utils.searchRe('.*(add|sub)(.w)?\s*sp(, sp)?, #[0-9]+', opcodes) + # если строки нет, выходим (потом подумать,как сделать) todo + #if len(sub_add_sp_lines) < 2: #не нашли sub и add + #return [] + + + if len(sub_add_sp_lines) != 0: + try: + sub_ind = lines.index([s for s in lines if str(s).startswith(sub_add_sp_lines[0].group())][0]) + except: + sub_ind = lines.index(sub_add_sp_lines[0].group() + '\n') + a = int(sub_add_sp_lines[0].group().split('#')[-1]) + else: + a = 0 + sub_ind = 0 + + #ищем строки вида [sp, #b] + #use_sp_lines = list(filter(None,[re.search('.*(ldr|str)(b|h|sb|sh|d)?(.w)?.*\[sp, #[0-9]+\].*', line) for line in lines])) + use_sp_lines = r2.utils.searchRe('.*(ldr|str)(b|h|sb|sh|d)?(.w)?.*\[sp, #[0-9]+\].*', opcodes) + #todo + #for i in use_sp_lines: + #print(i.group()) + if len([s for s in use_sp_lines if '!' in str(s.group())])>0: + return -1 + + + + for l in use_sp_lines: + instr = re.search('v?(ldr|str)(b|h|sb|sh|d)?(.w)?',l.group()).group() + b = int(re.search('#[0-9]+', l.group()).group().replace('#','')) + if b-a >= 0: + rx = re.search('(\s+r10|r11|r12|sp|lr|pc|r[0-9]|((d|s)(([1-2][0-9])|3[0-1]|[0-9]))),', l.group()).group().strip().replace(',','') + code, is_thumb = r2.utils.getCodeFromLine(l.group()) + offset = changeOffset(b-a, b, table) + new_instr_code = r2.arm_translate.makeLdrOrStr(instr,code , rx, 'sp', offset, is_thumb, l.group()) + # to_write ... [sp, #b + new_regs_count*4] + to_write.append((r2.utils.getAddressFromLine(l.group()), len(code) // 2, r2.utils.toLittleEndian(new_instr_code))) + + #ищем строки вида add rx, sp, (#c) - должна быть одна ? todo + #add_sp_to_reg = list(filter(None, [re.search('.*(add(.w)?|mov)\s*(r[0-9]|r10|r11|r12), sp(, #[1-9]+)?.*', line) for line in lines])) + add_sp_to_reg = r2.utils.searchRe('.*(add(.w)?|mov)\s*(r[0-9]|r10|r11|r12), sp(, #[1-9]+)?.*', opcodes) + #todo + #for i in add_sp_to_reg: + # print(i.group()) + if len([s for s in add_sp_to_reg if '!' in str(s.group())])>0: + return -1 + + if len(add_sp_to_reg) > 0: + for l in add_sp_to_reg: + new = getRxLines(lines, l.group(), table, a, sub_ind) + if new == -1: + return -1 + to_write.extend(new) + + return to_write + + +def changeOffset(offset, cur, table): # offset - общее смещение + if offset < 0: # cur - локальное смещение + return cur # table - переменная типа словарь, + max_key = max(table.keys()) # в котором хранятся соотвествия старых + if offset > max_key: # и новых смещений + return cur + table[max_key] + return table[offset//4*4] diff --git a/r2Helper.py b/r2/r2Helper.py similarity index 51% rename from r2Helper.py rename to r2/r2Helper.py index 792a6ff..89b9d23 100644 --- a/r2Helper.py +++ b/r2/r2Helper.py @@ -1,15 +1,17 @@ -from collections import namedtuple +from recordclass import recordclass import r2pipe, json -func_model = namedtuple('funcModel','address name rows') -opcode_model = namedtuple('opcodeModel','offset bytes type opcode size') class r2Helper: + func_model = recordclass('funcModel', 'address name rows') + opcode_model = recordclass('opcodeModel', 'offset bytes type opcode size regs reg') + opcode_model.__new__.__defaults__=(None, None, None, None, None, None, None) + def __init__(self, filename): self.r2 = r2pipe.open(filename) - self.cmd('e arm.lines=false arm.varsub=false arm.comments=false') + self.cmd('e arm.lines=false arm.varsub=false arm.comments=false cfg.bigendian=false') self.cmd('aaa') def open(self,filename): @@ -26,22 +28,19 @@ def cmdJson(self, s, *args): def getFunctions(self): funcList = self.cmdJson('aflj') - funcTypes = ['fcn','sub','aav'] #todo - return [self.getFuncInfo(f) for f in funcList if f['type'] in funcTypes] + funcTypes = ['fcn','sub','aav', 'sym'] #todo + return dict((hex(f['offset']),self.getFuncInfo(f)) for f in funcList if f['type'] in funcTypes) - def getFuncInfo(self,funcname): - func_info = self.cmdJson('pdfj @ {0}'.format(funcname['name'])) - print(funcname['name']) + def getFuncInfo(self,func): + func_info = self.cmdJson('pdfj @ {0}'.format(func['offset'])) + print(func['name']) opcodes_info = func_info['ops'] - opcodes = [opcode_model(oc['offset'], oc['bytes'], oc['type'], oc['opcode'], oc['size']) + opcodes = [self.opcode_model(hex(oc['offset']), oc['bytes'], oc['type'], oc['opcode'], oc['size']) for oc in opcodes_info if self.valid(oc['type']) and self.valid(oc['opcode'])] - return func_model(func_info["addr"], func_info["name"], opcodes) + return self.func_model(hex(func_info["addr"]), func_info["name"], opcodes) def valid(self, item): return item!='invalid' -r2 = r2Helper('/home/daria/Documents/dimplom/apps/audio/lib/armeabi-v7a/libnative-audio-jni.so') -functions = r2.getFunctions() - diff --git a/r2/run.py b/r2/run.py new file mode 100755 index 0000000..1c55e16 --- /dev/null +++ b/r2/run.py @@ -0,0 +1,39 @@ +from os import listdir +from os.path import isfile, join +import sys, colored, config_parser,r2.main + + +#path = 'data/com.instagram.android/lib/armeabi-v7a/' +#path = 'data/noassertTel/lib/armeabi-v7a/' +#path = 'apps/telegram/lib/armeabi-v7a/' +#harded_path = 'apps/app-debug/lib/armeabi-v7a/' +harded_path = '../apps/curl/lib/armeabi-v7a/' +DEBUG=1 +path = sys.argv[1] if len(sys.argv) > 1 else harded_path +config_path = sys.argv[2] if len(sys.argv)>2 else '../config.ini' +start_group = sys.argv[3] if len(sys.argv)>3 else 0 +end_group = sys.argv[4] if len(sys.argv)>4 else sys.maxsize #todo LAST ELEMENT!!! + +#todo debug only +# start_group = 387 +# end_group = 388 #388 +#2314 + +# 1-путь к либами,2-конфиг, 3-начало, 4-конец + + + +files = [f for f in listdir(path) if isfile(join(path, f)) and f.endswith('_old.so')] +config = config_parser.ConfigParser(config_path) +for file in files: + # if file[:-7]=='libcurl': + # start_group = 0 + # #end_group = 387 + # end_group = -1 + # else: + # start_group = 0 + # end_group = 1 + + + colored.printColored (file[:-7], colored.HEADER) + r2.main.run(join(path, file), int(start_group), int(end_group), DEBUG, config) \ No newline at end of file diff --git a/r2/utils.py b/r2/utils.py new file mode 100755 index 0000000..4209438 --- /dev/null +++ b/r2/utils.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- +import re, array, binascii, random + + +def getFunctionStackCode(lines): + # ищем add(sub) sp, #число + lines_with_sp = [] + for line in lines: + # stack_line = re.match('\s*[0-9a-f]+\:.*sp.*', line) + stack_line = re.match('.*(add|sub)(.w)?\s*sp, #.*', line) + if stack_line is not None: + lines_with_sp.append(stack_line.group()) + return lines_with_sp + + +def getCode(s): + a = hex(s)[2:] + if len(a) == 4 or len(a) == 8: + return a + if len(a) < 4: + return a.rjust(4, '0') + return a.rjust(8, '0') + + +def toLittleEndian(a): + y = array.array('h', binascii.unhexlify(a)) + y.byteswap() + return bytes.fromhex(binascii.hexlify(y).decode()) + +def prepare(a): + y = array.array('h', binascii.unhexlify(a)) + y.byteswap() + res = binascii.hexlify(y).decode() + return res[4:]+res[:4] + + +def calcRegisters(registers): + s = 0 + for reg in registers: + s += pow(2, int(reg[1:])) + return s + +def getAllUsedRegisters(lines): + matches = searchRe('.*(ldr|str|mov|add|sub).*r(10|11|[0-9])', lines) + regs = [re.findall('r(10|11|[0-9])', match.group()) for match in matches] + r = set(sum(regs, [])) + return list(r) + + +registers = ['r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11'] + + +# добавляем все недостающие регистры в начало +# возвращаем итоговый список регистров и количество добавленных - нужно для изменения add/sub sp, #... +def addRegistersToStart(current_registers): + if 'r1' in current_registers: + return current_registers, 0 # в начало уже нечего добавить + + i, new_registers = 1, [] + + while 'r{0}'.format(i) not in current_registers and i < len(registers): + new_registers.append('r{0}'.format(i)) + i += 1 + + return new_registers + current_registers, len(new_registers) + + +def addRegistersToEnd(current_registers): + if 'r11' in current_registers: + return current_registers # в начало уже нечего добавить + + i = 1 + if len(current_registers) > 0: + i = int(current_registers[-1][1:])+1 + if i > 7: + return current_registers + new_registers = [] + while 'r{0}'.format(i) not in current_registers and i <= 7: + new_registers.append('r{0}'.format(i)) + i += 1 + + return current_registers + new_registers + +def addRegistersToStartAndEnd(current_registers, code, returnValueSize): + minreg = returnValueSize + #minreg = 4 + start = min(minreg, int(current_registers[0][1:])) if len(current_registers) != 0 else minreg + end = 12 if len(code) > 4 else 8 + new_regs = ['r{0}'.format(i) for i in range(start, end) if 'r{0}'.format(i) not in current_registers] + if len(new_regs) == 0: + return -1, -1 + # todo - uncomment for randomization + #random.shuffle(new_regs) + #new_regs = new_regs[:random.randint(1, len(new_regs))] + table = {} + for i in range(len(current_registers)): + offset = len([k for k in new_regs if int(k[1:]) < int(current_registers[i][1:])]) + table[i*4] = offset*4 + table[len(current_registers)*4] = len(new_regs)*4 + new_regs.extend(current_registers) + return sorted(new_regs, key = lambda item: int(item[1:])), table + + + +def getRandomNumberOfItems(start, end): + if start > end: + return [] + if start == end: + return [start] + #return [] + r = list(range(start, end+1)) + random.shuffle(r) + #return r[:random.randint(1,len(r))] + return r + #return r[:1] + + +def getAddressFromLine(line): + return line.split(':')[0].split('<')[0].replace(' ', '') + +def getCodeFromLine(line): + code = str(line.split('\t')[1].replace('\t', '')).strip() + if len(code)!=4 and len(code)!=9: + i = 1 + return code.replace(' ', ''), len(code) in [4, 9] + + +def searchRe(regex, lines): + return list(filter(None, [re.search(regex, line) for line in lines])) \ No newline at end of file