From 4ad527a43c8f5f268e827a2342f53ec01ccc51d9 Mon Sep 17 00:00:00 2001 From: DoroninaD Date: Mon, 16 Oct 2017 00:24:51 +0700 Subject: [PATCH] Update to latest --- config.ini | 4 +- config_parser.py | 11 +- main_insert_border_regs.py | 330 +++++++++++++++++++++++++++++++------ parse_functions_utils.py | 146 ++++++++-------- r2Helper.py | 47 ++++++ run.py | 29 +++- scripts/run.sh | 4 +- utils.py | 2 +- 8 files changed, 432 insertions(+), 141 deletions(-) create mode 100644 r2Helper.py diff --git a/config.ini b/config.ini index 923012f..fe53e21 100644 --- a/config.ini +++ b/config.ini @@ -1,9 +1,9 @@ [app_source] # путь к папке, в которой содержатся все нативные файлы (.cpp, .c, .h) -native_directory = /home/daria/Desktop/hello-libs (copy)/app/src/main/cpp/ +native_directory = /home/daria/Desktop/bionic/app/src/main/cpp/ # путь к папке, в которой содержатся все java файлы (.java) -java_directory = /home/daria/Desktop/hello-libs (copy)/app/src/main/java/ +java_directory = /home/daria/Desktop/bionic/app/src/main/java/ # путь к папке с декомпилированным приложением (unpack.h), в котором все статики убраны nonstatic_app_directory = /home/daria/Documents/dimplom/apps/nonstatic_orig/lib/armeabi-v7a/ diff --git a/config_parser.py b/config_parser.py index 84293cd..75daf5f 100644 --- a/config_parser.py +++ b/config_parser.py @@ -1,14 +1,15 @@ import configparser, os -config_path = os.path.join(os.path.dirname(__file__),'config.ini') +#config_path = os.path.join(os.path.dirname(__file__),'config.ini') class ConfigParser: - def __init__(self): - if not os.path.isfile(config_path): - raise Exception('No config.ini at {0}!'.format(config_path)) + def __init__(self, path): + self.config_path = path + if not os.path.isfile(self.config_path): + raise Exception('No config.ini at {0}!'.format(self.config_path)) self.config = configparser.ConfigParser() - self.config.read(config_path) + self.config.read(self.config_path) def get(self, property): return self.config.get('app_source',property) diff --git a/main_insert_border_regs.py b/main_insert_border_regs.py index 17cad24..b5e2d25 100755 --- a/main_insert_border_regs.py +++ b/main_insert_border_regs.py @@ -1,9 +1,13 @@ # -*- coding: utf-8 -*- import re, utils, arm_translate, parse,parse_functions_utils, static_functions_helper, config_parser, os -import cxxfilt, colored +import cxxfilt, colored, sys +conditions = ['eq','ne','cs','hs','cc','lo','mi','pl','vs','vc','hi','ls','ge','lt','gt','le','al'] +conditions_pattern = '|'.join(conditions) -def run(path, start_group, end_group, DEBUG): +NEW = True +RETURN_TYPES = True +def run(path, start_group, end_group, DEBUG, config): print('DEBUG ', DEBUG) f = open(path+'.txt', 'r') lines = f.readlines() @@ -11,8 +15,6 @@ def run(path, start_group, end_group, DEBUG): lines = lines[indices[0]:] f.close() - config = config_parser.ConfigParser() - stack_lines = [] index = 0 @@ -24,9 +26,29 @@ def run(path, start_group, end_group, DEBUG): is_function_name = re.search('<.*>:', line) if is_function_name is not None: function_name = is_function_name.group()[1:-2] - stack_line = re.match('.*((push(.w)?|stmdb(.w)?\s*sp!).*lr}|(pop(.w)?|ldmia(.w)?\s*sp!).*(pc|lr)}).*', line) + if NEW: + address = utils.getAddressFromLine(line) + stack_lines.append((address,function_name)) + if NEW: + andeq = re.search('andeq', line) + if andeq is not None: + stack_lines.append((address, "andeq", "null")) + + #stack_line = re.match('.*((push(.w)?|stmdb(.w)?\s*sp!).*lr}|(pop[a-z]*|ldmia[a-z]*\s*sp!).*(pc|lr)}).*', line) + stack_line = re.match('.*((push|stmdb[a-z]*\s*sp!).*lr}|(pop|ldmia[a-z]*\s*sp!).*(pc|lr)}).*', line) + + if NEW: + branch_line = re.match('.*\s(bx|b)({0})?\s.*'.format(conditions_pattern), line) + if branch_line is not None: + address = utils.getAddressFromLine(line) + if address == '3db8': + aaa = 1 + code, is_thumb = utils.getCodeFromLine(line) + jumpto = re.search('\s(bx|b)({0})?\s[0-9a-z]+'.format(conditions_pattern),branch_line.group()).group().split()[-1] + method = re.search('\s(bx|b)({0})?'.format(conditions_pattern),branch_line.group()).group().strip() + stack_lines.append((address, code, method, jumpto, is_thumb, index)) if stack_line is not None: - method = re.search('push(.w)?|stmdb(.w)?|pop(.w)?|ldmia(.w)?', line).group() + method = re.search('(push|stmdb|pop|ldmia)[a-z]*', line).group() # берем все регистры в {} и убираем последний (это lr или pc) # в дальнешем будем исключать строки, в которых есть регистры > r11 #registers = re.search('{.*}', line).group().replace('}','').replace('{','').replace(' ','').split(',')[:-1] @@ -45,71 +67,190 @@ def run(path, start_group, end_group, DEBUG): index += 1 # выделяем функции, для которых нет имени - noname_functions = dict((addr, func) for addr, func in functions.items() if func == '') - if len(noname_functions) > 0: - nonstatic_folder = config.get('nonstatic_app_directory') - nonstatic_file = os.path.join(nonstatic_folder, os.path.basename(path)+'.txt') - - newNames = dict((addr, func) for addr, func in noname_functions.items() if func!='') - - while True: - noname_len = len(noname_functions) - for addr in list(noname_functions): - name = static_functions_helper.getName(lines, addr, nonstatic_file, newNames) - if name != '': - newNames[addr] = name - noname_functions.pop(addr) - if len(noname_functions) == noname_len: - break + if RETURN_TYPES: + noname_functions = dict((addr, func) for addr, func in functions.items() if func == '') + if len(noname_functions) > 0: + nonstatic_folder = config.get('nonstatic_app_directory') + nonstatic_file = os.path.join(nonstatic_folder, os.path.basename(path)+'.txt') + + newNames = dict((addr, func) for addr, func in noname_functions.items() if func!='') - for addr in newNames: - functions[addr] = newNames[addr] + while True and os.path.exists(nonstatic_file): + noname_len = len(noname_functions) + for addr in list(noname_functions): + name = static_functions_helper.getName(lines, addr, nonstatic_file, newNames) + if name != '': + newNames[addr] = name + noname_functions.pop(addr) + if len(noname_functions) == noname_len: + break + for addr in newNames: + functions[addr] = newNames[addr] + + groups = combineFunction(stack_lines) #находим тип функций function_types = [] - if DEBUG: + if RETURN_TYPES: print('FUNCTIONS') - function_types = parse_functions_utils.getFunctionsReturnTypeSize(functions) + function_types = parse_functions_utils.getFunctionsReturnTypeSize(functions, config) #todo использовать числа при рандомизации # all_registers = ['r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11'] - # ищем push, записываем для него все pop с теми же регистрами, пока не встретим новый push + #ищем push, записываем для него все pop с теми же регистрами, пока не встретим новый push + #или название функции <..> i = 0 - groups = [] - while i < len(stack_lines)-1: - probably_push = str(stack_lines[i][2]) - if probably_push.startswith('push') or probably_push.startswith('stmdb'): - lst = [stack_lines[i]] - j = i + 1 - while (str(stack_lines[j][2]).startswith('pop') or str(stack_lines[j][2]).startswith('ldmia')) \ - and stack_lines[j][3] == stack_lines[i][3]: - lst.append(stack_lines[j]) - j += 1 - if j >= len(stack_lines): - break - if j - i > 1: - groups.append(lst) - i = j + # groups = [] + # lst = [] + # while i < len(stack_lines)-1: + # if len(stack_lines[i]) == 2: + # #lst.append(stack_lines[i]) + # i+=1 + # continue + # probably_push = str(stack_lines[i][2]) + # if probably_push.startswith('push') or probably_push.startswith('stmdb'): + # #lst = [stack_lines[i]] + # lst.append(stack_lines[i]) + # j = i + 1 + # while len(stack_lines[j]) > 2 \ + # and (str(stack_lines[j][2]).startswith("b") or ((str(stack_lines[j][2]).startswith('pop') or str(stack_lines[j][2]).startswith('ldmia')) \ + # and stack_lines[j][3] == stack_lines[i][3])): + # lst.append(stack_lines[j]) + # j += 1 + # if j >= len(stack_lines): + # break + # if j - i > 1: + # groups.append(lst.copy()) + # lst.clear() + # i = j + # lst.clear() + # else: + # i += 1 + init_group_len = len(groups) + + #difference = [g for g in groups if g not in f] + + if NEW: + have_external_jumps = {} + ext_jumps_list = {} + external_jumps = [] - else: - i += 1 + # убираем 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(groups)) + 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(groups)) + gr = groups groups = [] for group in gr: - if all(g[6]=='pc' for g in group[1:]): + #if all(g[6]=='pc' for g in group[1:]): + #берем только те функции, в которых нет pop lr + #if len(group) > 1 and all(g[6]=='pc' for g in group[1:]): + if len(group) > 1: groups.append(group) #if group[-1][6] == 'pc': # groups.append(group) + + #groups = [group for group in groups if group[i][6]=='pc' for i in range(1,len(group))] - print ('New_GROUPS:', len(groups)) + print ('Groups after pop lr removing:', len(groups)) #добавляем в to_write (адрес, количество старых байт, новые байты) для перезаписи groups_count = 0 @@ -130,13 +271,16 @@ def run(path, start_group, end_group, DEBUG): # добавляем регистры в начало, считает их количество real_reg_count = len(first[3]) - return_size = function_types[addr] if DEBUG else 4 + return_size = function_types[addr] if RETURN_TYPES else 4 new_registers, table = utils.addRegistersToStartAndEnd(first[3], first[1], return_size) if new_registers == -1: full_registers_count+=1 continue # меняем втутренние строки, взаимодействующие с sp - inner_lines = parse.getAllSpLinesForLow(lines[first[5] + 1:last[5]], table) + try: + inner_lines = parse.getAllSpLinesForLow(lines[first[5] + 1:last[5]], table) + except: + aaa=1 if inner_lines == -1: continue groups_count+=1 @@ -154,11 +298,17 @@ def run(path, start_group, end_group, DEBUG): if len(inner_lines) > 0: to_write.extend(inner_lines) - print(colored.setColored('{0}: '.format(cxxfilt.demangle(functions[addr])), colored.OKGREEN) + 'old {0}, new {1}'.format(first[3], new_registers)) + key = cxxfilt.demangle(functions[addr]) if functions[addr]!='' else addr + print(colored.setColored('{0}: '.format(key), colored.OKGREEN) + 'old {0}, new {1}'.format(first[3], new_registers)) regs_added += len(new_registers) - len(first[3]) - secured = groups_count/len(groups)*100 - output = 'End:{0}, full regs:{1}, secured:{2}%, average randomness:{3}'\ - .format(groups_count, full_registers_count, secured, regs_added/groups_count) + 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) @@ -173,4 +323,78 @@ def run(path, start_group, end_group, DEBUG): f = open(path+'.so', 'bw') f.write(text) - f.close() \ No newline at end of file + 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/parse_functions_utils.py b/parse_functions_utils.py index 44826aa..cc0f699 100644 --- a/parse_functions_utils.py +++ b/parse_functions_utils.py @@ -1,12 +1,8 @@ import re, os.path, cxxfilt, glob, codecs, config_parser from timeit import timeit -configParser = config_parser.ConfigParser() -java_dir = configParser.get('java_directory') # todo set directory for java -jni_dir = configParser.get('native_directory') -#jnih_path = '/home/daria/Android/Sdk/ndk-bundle/sysroot/usr/include/jni.h' -system_paths = configParser.getSection('system_directories') + # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html @@ -16,12 +12,22 @@ void_type = ['void'] C_types32 = ['int32_t','int16_t', 'int8_t', 'int', 'unsigned', 'float', 'char', 'bool', 'long int', 'wchar_t', - 'jbyte', 'jchar', 'jboolean', 'jshort', 'jint', 'jfloat'] #unsigned не учитываем + 'jbyte', 'jchar', 'jboolean', 'jshort', 'jint', 'jfloat', 'size_t'] #unsigned не учитываем jni_objects_types = ['jclass', 'jstring', 'jcharArray'] #todo add other Arrays C_types64 = ['long long', 'double', 'int64_t', 'jlong', 'jdouble'] C_types128 = ['long double'] JNI_types = [''] +def init_config(config): + global java_dir + global jni_dir + global system_paths + configParser = config + java_dir = configParser.get('java_directory') # todo set directory for java + jni_dir = configParser.get('native_directory') + # jnih_path = '/home/daria/Android/Sdk/ndk-bundle/sysroot/usr/include/jni.h' + system_paths = configParser.getSection('system_directories') + #for native functions declared in Java code def getJavafunctionType(JNI_function_name): function_name = str(JNI_function_name) @@ -47,13 +53,13 @@ def getJavafunctionType(JNI_function_name): if not os.path.isfile(path): print('NO FILE {0}!'.format(path)) - return -1 #todo + return None #todo with codecs.open(path, "r", encoding='utf-8', errors='ignore') as f: data = f.read() func_declaration = re.search('native .* {0}'.format(short_func_name), data) if func_declaration is None: - print('NONE DECLARATION in {0}'.format(path)) - return -1 #todo + print('NONE DECLARATION in {0} for {1}'.format(path, short_func_name)) + return None #todo # все типы односложные, нет указателей type = func_declaration.group().split('(')[0] #убираем параметры type = type.split(' ')[-2] @@ -80,6 +86,8 @@ def getTypeSize(type, isJNI): return 4 if isJNI and type in Java_types64 or not isJNI and type in C_types64: return 2 + if type !='': + aaa = 1 if '*' in type or '[]' in type \ or isJNI and type in Java_types32 \ or not isJNI and type in C_types32\ @@ -91,8 +99,8 @@ def getTypeSize(type, isJNI): return 4 -def getFunctionsReturnTypeSize(functions): - +def getFunctionsReturnTypeSize(functions, config): + init_config(config) #function_types = dict() function_types = dict.fromkeys(functions.keys(), '') #адрес - найденнная функция @@ -118,11 +126,8 @@ def getFunctionsReturnTypeSize(functions): # demangle mangled functions for address, function in C_functions.items(): - if function.startswith('_Z'): - n = cxxfilt.demangle(function) - if n.startswith('_JNIEnv'): - n = n[9:] - C_functions[address] = n + C_functions[address] = demangleNativeFunctionName(function) + # отдельно выносим функции из jni.h @@ -142,23 +147,24 @@ def getFunctionsReturnTypeSize(functions): for address, func in function_types.items(): if address not in return_sizes: - if address == '362c': + if func==-1: aaa=1 return_sizes[address] = getTypeSize(func, False) - print('4 bytes: ', len([f for f in return_sizes if return_sizes[f]==4])) - print('2 bytes: ', len([f for f in return_sizes if return_sizes[f]==2])) - print('1 bytes: ', len([f for f in return_sizes if return_sizes[f]==1])) - print('0 bytes: ', len([f for f in return_sizes if return_sizes[f]==0])) + # print('4 bytes: ', len([f for f in return_sizes if return_sizes[f]==4])) + # print('2 bytes: ', len([f for f in return_sizes if return_sizes[f]==2])) + # print('1 bytes: ', len([f for f in return_sizes if return_sizes[f]==1])) + # print('0 bytes: ', len([f for f in return_sizes if return_sizes[f]==0])) notfound = dict((f, backup[f]) for f in return_sizes if return_sizes[f]==4) return return_sizes -# def getJNIFunctionsTypes(functions): -# with codecs.open(jnih_path, "r", encoding='utf-8', errors='ignore') as f: -# data = f.read() -# for func in functions: -# func_declaration = re.search('native .* {0}'.format(func), data) -# +def demangleNativeFunctionName(function): + # demangle mangled functions + if function.startswith('_Z'): + function = cxxfilt.demangle(function) + if function.startswith('_JNIEnv'): + function = function[9:] + return function def searchInFile(patterns_dict,func_dict, file): @@ -194,6 +200,47 @@ def searchInFile(patterns_dict,func_dict, file): 'double': 'jdouble', } + +#pattern for C/C++ functions +#for jni functions in native code no need to think about params (they are in the name already) +def makePattern(func): + params = re.search('\((.|\n)*\)', func) + params_regex = '[^;]*' + if params is not None: # есть параметры + params_list = params.group()[1:-1].split(',') + # tmp = params_list.copy() + for i in range(len(params_list)): + p = params_list[i].strip() + p = p.replace(' const', '') # const меняет место, уберем его + if p.startswith('_j') and p[-1] == '*': # _jobject*->jobject + p = p[1:-1] + if p.startswith('_J'): + p = p[1:] + non_escaped = p.strip('&').strip('*').strip(' ') # запоминаем nonescaped параметры + p = re.escape(p) # escape + # todo unsigned int -> unsigned// int + if non_escaped in types_patterns: + p = p.replace(p, types_patterns[non_escaped]) + # p = '\s*j?'+p #для jni + # todo make this regexp more effetive!!! + p = '(\\s*(const)?\\s*' + p + '\\s*(const)?\\s*)' + params_list[i] = p.replace('\\*', '\\s*\\*\\s*').replace('\\&', '\\s*\\&\\s*') + + # params_regex = '(.|\n)*,(const)?\\s*(const)?'.join(params_list) #todo escape? + params_regex = '(.|\n)*\\s*'.join(params_list) # todo escape? + + # для jni и void* -> void * + params_regex = params_regex \ + .replace('_J', 'J').replace('_j', 'j') + + result_pattern = re.compile('\n\s*([a-zA-Z0-9_\"\*\[\]]+\s+){0,3}' + + '\*?{0}\s*\({1}.*\)(\s[a-zA_Z_]+)?\s' + .format(re.escape(func.split('(')[0]), params_regex) + + '*(;|{)', re.MULTILINE) + return result_pattern + + + #открываем файл, ищем все функции def findFunctionsInFiles(functions): # functions = address:functions @@ -203,50 +250,7 @@ def findFunctionsInFiles(functions): patterns = dict() #выделяем типы входных параметров for address, func in functions.items(): - params = re.search('\((.|\n)*\)', func) - #params_regex = '(.|\n)*' - #params_regex = '[^;\)]* - params_regex = '[^;]*' - if address == '362c': - aaa=1 - if params is not None: # есть параметры - params_list = params.group()[1:-1].split(',') - #tmp = params_list.copy() - for i in range(len(params_list)): - p = params_list[i].strip() - p = p.replace(' const', '') # const меняет место, уберем его - if p.startswith('_j') and p[-1]=='*': #_jobject*->jobject - p = p[1:-1] - if p.startswith('_J'): - p = p[1:] - non_escaped = p.strip('&').strip('*').strip(' ') #запоминаем nonescaped параметры - p = re.escape(p) #escape - #todo unsigned int -> unsigned// int - if non_escaped in types_patterns: - p = p.replace(p, types_patterns[non_escaped]) - #p = '\s*j?'+p #для jni - #todo make this regexp more effetive!!! - p = '(\\s*(const)?\\s*' + p + '\\s*(const)?\\s*)' - params_list[i] = p.replace('\\*', '\\s*\\*\\s*').replace('\\&', '\\s*\\&\\s*') - - #params_regex = '(.|\n)*,(const)?\\s*(const)?'.join(params_list) #todo escape? - params_regex = '(.|\n)*\\s*'.join(params_list) #todo escape? - - # для jni и void* -> void * - params_regex= params_regex\ - .replace('_J', 'J').replace('_j', 'j') - - # result_pattern = re.compile('\n.*([a-zA-Z0-9_\*]+\s){0,3}' - # +'\*?{0}\({1}\)(\s[a-zA_Z_]+)?\s' - # .format(re.escape(func.split('(')[0]), params_regex) - # +'{0,};', re.MULTILINE) - - result_pattern = re.compile('\n\s*([a-zA-Z0-9_\"\*\[\]]+\s+){0,3}' - +'\*?{0}\s*\({1}.*\)(\s[a-zA_Z_]+)?\s' - .format(re.escape(func.split('(')[0]), params_regex) - +'*(;|{)', re.MULTILINE) - - + result_pattern = makePattern(func) patterns[address] = result_pattern #patterns = dict((address, '\n.*{0}(.*);'.format(re.escape(func))) diff --git a/r2Helper.py b/r2Helper.py new file mode 100644 index 0000000..792a6ff --- /dev/null +++ b/r2Helper.py @@ -0,0 +1,47 @@ +from collections import namedtuple + +import r2pipe, json + +func_model = namedtuple('funcModel','address name rows') +opcode_model = namedtuple('opcodeModel','offset bytes type opcode size') + + +class r2Helper: + def __init__(self, filename): + self.r2 = r2pipe.open(filename) + self.cmd('e arm.lines=false arm.varsub=false arm.comments=false') + self.cmd('aaa') + + def open(self,filename): + self.cmd('o--') + self.cmd('o {0}'.format(filename)) + self.cmd('e arm.lines=false arm.varsub=false arm.comments=false') + self.cmd('aaa') + + def cmd(self, s): + return self.r2.cmd(s) + + def cmdJson(self, s, *args): + return json.loads(self.cmd(s)) + + 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] + + def getFuncInfo(self,funcname): + func_info = self.cmdJson('pdfj @ {0}'.format(funcname['name'])) + print(funcname['name']) + opcodes_info = func_info['ops'] + opcodes = [opcode_model(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) + + 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/run.py b/run.py index 58acc56..6cca2ea 100755 --- a/run.py +++ b/run.py @@ -1,23 +1,38 @@ from os import listdir from os.path import isfile, join -import main_insert_border_regs, sys, colored +import main_insert_border_regs, sys, colored, config_parser #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/test2/lib/armeabi-v7a/' +harded_path = 'apps/curl/lib/armeabi-v7a/' DEBUG=1 path = sys.argv[1] if len(sys.argv) > 1 else harded_path -start_group = sys.argv[2] if len(sys.argv)>2 else 0 -end_group = sys.argv[3] if len(sys.argv)>3 else -1 #todo LAST ELEMENT!!! +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 = 0 -#end_group = 1000 +# 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) - main_insert_border_regs.run(join(path, file)[:-7], int(start_group), int(end_group), DEBUG) \ No newline at end of file + main_insert_border_regs.run(join(path, file)[:-7], int(start_group), int(end_group), DEBUG, config) \ No newline at end of file diff --git a/scripts/run.sh b/scripts/run.sh index 24816b5..1aa34c6 100644 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -1,7 +1,7 @@ #!/bin/bash # -*- coding: utf-8 -*-java -jar apktool.jar b -o $2 ${1%.apk} -# 1 = apk name, 2 = new apk name -/usr/bin/python3.5 ../run.py ${1%.apk}/lib/armeabi-v7a/ $3 $4 +# 1 = apk name, 2 = new apk name, 3 = config.ini, 4 - start, 5 - end +/usr/bin/python3.5 ../run.py ${1%.apk}/lib/armeabi-v7a/ $3 $4 $5 java -jar apktool.jar b -o $2 ${1%.apk} jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore -storepass 111111 $2 alias_name diff --git a/utils.py b/utils.py index 8d8703f..62f12c8 100755 --- a/utils.py +++ b/utils.py @@ -110,7 +110,7 @@ def getRandomNumberOfItems(start, end): def getAddressFromLine(line): - return line.split(':')[0].replace(' ', '') + return line.split(':')[0].split('<')[0].replace(' ', '') def getCodeFromLine(line): code = str(line.split('\t')[1].replace('\t', '')).strip()