From 474b7ad55d291016fdcfa20a9a1b8838d0a3ab8b Mon Sep 17 00:00:00 2001 From: DoroninaD Date: Mon, 13 Nov 2017 00:52:27 +0700 Subject: [PATCH] Add SUB SP, static_functions for ida --- IdaHelper.py | 11 +++-- arm_translate.py | 25 +++++++++-- main_switcher.py | 17 +++++++- parse.py | 59 +++++++++++++++++-------- static_functions_helper.py | 88 +++++++++++++++++++++++--------------- switcher.py | 30 ++++++++++++- 6 files changed, 168 insertions(+), 62 deletions(-) diff --git a/IdaHelper.py b/IdaHelper.py index 6f5cc1d..5739d99 100644 --- a/IdaHelper.py +++ b/IdaHelper.py @@ -148,11 +148,16 @@ def getNumber(line): a = line.line for key, value in [(key, value) for key, value in varsDict.items() if key[0]==line.funcAddr]: a = a.replace(key[1], value) - number = re.search('#-?(0x)?[0-9a-f\-\+x]+', a, re.IGNORECASE) - if not number: + number = re.search('#-?(0x)?[0-9a-f\-\+x]+\]?!?\s', a, re.IGNORECASE) + if not number and '#' not in a: return 0 + if not number: + # return 0 + return None try: - return ast.literal_eval(number.group()[1:]) + return ast.literal_eval(number.group() + .replace(']','') + .replace('!','')[1:]) except: return None diff --git a/arm_translate.py b/arm_translate.py index 7ad8596..a0f3025 100755 --- a/arm_translate.py +++ b/arm_translate.py @@ -13,8 +13,6 @@ def convertOneToMany(code, is_thumb, is_pop): 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) @@ -54,9 +52,28 @@ def code(old_code, mask, s, is_thumb): def changeSubSp(old_code, offset, thumb): #new = code(old_code, '11110000', new_offset, thumb) - c = hex(int(old_code, 16)+offset)[2:].upper() + #sum = offset+int(old_code, 16) if thumb: - return c + return old_code[:2]+hex(offset//4+0x80)[2:].upper() + leftPart = old_code[:5] + if offset in [256,512,768]: + rightPart = 0xC00 + offset/256 + elif offset == 1024: + rightPart = 0xb01 + elif offset < 256: + rightPart = offset + elif offset < 512: + rightPart = 0xf41 + (offset%260)//4 + elif offset < 768: + rightPart = 0xf81 + (offset%516)//4 + elif offset < 1024: + rightPart = 0xfc1 + (offset%768)//4 + elif offset < 4080: + rightPart = 0xe41 + (offset%1040)//16 + else: + aaa=1 + c = leftPart + hex(rightPart)[2:].upper().rjust(3,'0') + # c = hex(int(old_code, 16)+offset)[2:].upper() return c[4:] + c[:4] def makeLdrOrStrInner(old_instr, old_code, rx, ry, a, is_thumb, l): # ldr rx, [ry + a] diff --git a/main_switcher.py b/main_switcher.py index ecfad76..7ca9bc1 100755 --- a/main_switcher.py +++ b/main_switcher.py @@ -36,6 +36,7 @@ def run(path, start_group, end_group, DEBUG, config): f.close() groups, addrFuncDict = switcher.getFunctions(lines) + switcher.resolveStaticFunctions(addrFuncDict, config, path,groups) funcAddrDict = dict(zip(addrFuncDict.values(),addrFuncDict.keys())) init_group_len = len(groups) print('GROUPS:', len(groups)) @@ -92,7 +93,8 @@ def run(path, start_group, end_group, DEBUG, config): regs_added = 0 handledGroups = [] - for group in groups[start_group:end_group]: # 66 libcrypto - pop lr => bl - перезапись регистров + handledFuncs = [] + for index,group in enumerate(groups[start_group:end_group]): # 66 libcrypto - pop lr => bl - перезапись регистров #first, last = group[0], group[-1] push, pops = switcher.getPushes(group)[0], switcher.getPops(group) l+=1 @@ -154,13 +156,26 @@ def run(path, start_group, end_group, DEBUG, config): .format(len(onlyForContainsSub), len(onlyWithPushes), len(handledGroups) - len(onlyWithPushes)) colored.printColored(output, colored.BOLD) + + notHandledPushes = [item for item in groups if item not in onlyWithPushes + and item in containSpSubbed] + #onlyForContainsSub.extend(notHandledPushes) + s = sorted(onlyForContainsSub,key=lambda x: x[0].addr) l = [] handled = 0 + print('SUB SP:::') for item in onlyForContainsSub: + if item[0].addr == 0x4a60: + continue res = parse.getAllSpLinesForSub(item) + if res == -1 or len(res)==0: + aaa=1 if res!=-1 and len(res) > 0: handled+=1 l.extend(res) + key = cxxfilt.demangle(addrFuncDict[item[0].addr]) \ + if addrFuncDict[item[0].addr] != '' else item[0].addr + print(colored.setColored('{0}: '.format(key), colored.OKGREEN)) print ('Handled with SUB SP (only sub sp lines):', handled) to_write.extend(l) diff --git a/parse.py b/parse.py index 3c7025a..33070b9 100755 --- a/parse.py +++ b/parse.py @@ -306,6 +306,9 @@ def getAllSpLinesForSub(lines): addSubPattern = re.compile('.*(add|sub)(.w)?\s*sp(,\s?sp)?,\s?#[0-9]+', re.IGNORECASE) sub_add_sp_lines = switcher.searchInLines(addSubPattern, lines) + if len(sub_add_sp_lines)!=2: + return -1 + subSpLine = sub_add_sp_lines[0] if len(sub_add_sp_lines) != 0: sub_ind = lines.index(subSpLine) @@ -316,11 +319,26 @@ def getAllSpLinesForSub(lines): a = 0 sub_ind = 0 - spOffset = random.randint(1, 100) + spOffset = random.randint(1, 50)*4 + if spOffset+a > 1024: + spOffset += 16-(spOffset+a)%16 + if subSpLine.thumb and len(subSpLine.bytes)==4 and spOffset+a > 508: + spOffset = 508-a + + print('old {0}, new {1}'.format(a,spOffset+a)) # меняем сдвиг sub sp, #4 -> sub sp, #4+spOffset - newSupSP = arm_translate.changeSubSp(subSpLine.bytes, spOffset, subSpLine.thumb) - to_write.append((subSpLine.addr, + newSupSP = arm_translate.changeSubSp(subSpLine.bytes, spOffset+a, subSpLine.thumb) + try: + to_write.append((subSpLine.addr, len(subSpLine.bytes) // 2, utils.toLittleEndian(newSupSP))) + except: + aaa=1 + + for addSpLine in sub_add_sp_lines[1:]: + newAddSP = arm_translate.changeSubSp(addSpLine.bytes, spOffset+a, addSpLine.thumb) + to_write.append((addSpLine.addr, + len(addSpLine.bytes) // 2, utils.toLittleEndian(newAddSP))) + # ищем строки вида [sp, #b] - но только после вычитания (sub_ind) # от всех отнимаем spOffset # use_sp_lines = list(filter(None,[re.search('.*(ldr|str)(b|h|sb|sh|d)?(.w)?.*\[sp, #[0-9]+\].*', line) for line in lines])) @@ -333,17 +351,17 @@ def getAllSpLinesForSub(lines): b = switcher.getNumber(l) if b is None: return -1 - - pattern = re.compile \ - ('(\s+r10|r11|r12|sp|lr|pc|r[0-9]|((d|s)(([1-2][0-9])|3[0-1]|[0-9]))),', re.IGNORECASE) - rx = switcher.searchPattern(pattern, l).group().strip().replace(',', '') - # code, is_thumb = utils.getCodeFromLine(l.group()) - offset = b+spOffset - new_instr_code = arm_translate. \ - makeLdrOrStr(instr, l.bytes, rx.lower(), 'sp', offset, l.thumb, l.line) - # to_write ... [sp, #b + new_regs_count*4] - to_write.append((l.addr, - len(l.bytes) // 2, utils.toLittleEndian(new_instr_code))) + if b-a<0: #смотрим только на регистры и входные параметры + pattern = re.compile \ + ('(\s+r10|r11|r12|sp|lr|pc|r[0-9]|((d|s)(([1-2][0-9])|3[0-1]|[0-9]))),', re.IGNORECASE) + rx = switcher.searchPattern(pattern, l).group().strip().replace(',', '') + # code, is_thumb = utils.getCodeFromLine(l.group()) + offset = b+spOffset + new_instr_code = arm_translate. \ + makeLdrOrStr(instr, l.bytes, rx.lower(), 'sp', offset, l.thumb, l.line) + # to_write ... [sp, #b + new_regs_count*4] + to_write.append((l.addr, + len(l.bytes) // 2, 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])) @@ -445,12 +463,15 @@ def handleLine(index, line): instr = switcher.searchPattern(pattern, l).group() d = switcher.getNumber(l) if d is None: + switcher.getNumber(l) return -1 - pattern = re.compile('(\s+r10|r11|r12|sp|lr|pc|r[0-9]|((d|s)(([1-2][0-9])|3[0-1]|[0-9]))),', re.IGNORECASE) - rx = switcher.searchPattern(pattern, l).group().strip().replace(',', '') - offset = d+spOffset - new_instr_code = arm_translate.makeLdrOrStr(instr, l.bytes, rx, reg, offset, l.thumb, l.line) - to_write.append((l.addr, len(l.bytes) // 2, utils.toLittleEndian(new_instr_code))) + n = c + d - sp_subbed + if n < 0: # todo а что если будет sub rx, sp? + pattern = re.compile('(\s+r10|r11|r12|sp|lr|pc|r[0-9]|((d|s)(([1-2][0-9])|3[0-1]|[0-9]))),', re.IGNORECASE) + rx = switcher.searchPattern(pattern, l).group().strip().replace(',', '') + offset = d+spOffset + new_instr_code = arm_translate.makeLdrOrStr(instr, l.bytes, rx, reg, offset, l.thumb, l.line) + to_write.append((l.addr, len(l.bytes) // 2, utils.toLittleEndian(new_instr_code))) # str rx, [...] strRegPattern = re.compile('.*str(b|h|sb|sh|sw|d)?(.w)?\s{0}.*'.format(reg), re.IGNORECASE) diff --git a/static_functions_helper.py b/static_functions_helper.py index 509024a..703d436 100644 --- a/static_functions_helper.py +++ b/static_functions_helper.py @@ -1,20 +1,26 @@ -import re, utils, os, parse_functions_utils +import re, utils, os, parse_functions_utils, switcher, codecs #1. Cоздать {name}_nonstatic.txt - руками или сгенерить #file = {name}.txt - открытый #address = адрес push, у которого нет имени -def getName(static_file_lines, address, nonstatic_file, newNames): +def getName(static_file_lines, address, nonstatic_file, newNames, funcAddr): + pattern = re.compile('(:?bl|b|bx|b\.n)\s*{0}'.format(address),re.IGNORECASE) + line_where_called = None + for g in static_file_lines: + l = switcher.searchInLines(pattern, g) + if len(l)!=0: + line_where_called = l[0] # Найти, где в исходном файле вызывается, т.е. bl|b|bx address - line_where_called, line_index = searchInLines\ - ('(:?bl|b|bx|b\.n)\s*{0}\s*<.*>'.format(address), static_file_lines) + # line_where_called, line_index = searchInLines\ + # ('(:?bl|b|bx|b\.n)\s*{0}\s*<.*>'.format(address), static_file_lines) if line_where_called is None: return '' #raise Exception('{0} is not called in static {1}'.format(address, nonstatic_file)) - a1 = utils.getAddressFromLine(static_file_lines[line_index]) #адрес, где вызывается функций + a1 = line_where_called.addr #адрес, где вызывается функций #извлекаем имя функции из line_where_called #function_name = re.search('<.*@@', line_where_called).group()[1:-2] #delta = re.search('Base\+.*>', line_where_called).group()[5:-1] @@ -25,24 +31,25 @@ def getName(static_file_lines, address, nonstatic_file, newNames): # if result is not None: # break # a2 = utils.getAddressFromLine(static_file_lines[line_index]) - a2, index = getFuncBegin(line_index, static_file_lines) + #a2, index = getFuncBegin(line_where_called, static_file_lines) + a2 = line_where_called.funcAddr if a2 is None: return '' #теперь в a2 лежит индекс начала функции #todo parse strings to ints #result, index = searchInLines('<{0}@@Base>:'.format(function_name), static_file_lines) #a2 = utils.getAddressFromLine(static_file_lines[index+1]) - delta = hexstringToInt(a1) - hexstringToInt(a2) # смещение вызова искомой функции относительно начала той, в которой вызывается + delta = int(a1,16) - int(a2,16) # смещение вызова искомой функции относительно начала той, в которой вызывается # определяем имя функции по адресу a2 - function_name = re.search('<.*>:', static_file_lines[index-1]) - if function_name is None: - newname_address = utils.getAddressFromLine(static_file_lines[index]) + #function_name = re.search('<.*>:', static_file_lines[index-1])] + function_name = funcAddr[a2] + if re.search('((sub)|(local))_[0-9a-f]+',function_name,re.IGNORECASE): + newname_address = a2 if newname_address not in newNames: return '' + function_name = newNames[newname_address] #function_name = re.sub('plt','@Base',newNames[newname_address])+':' - function_name = '<{0}@@Base>:'.format(newNames[newname_address]) - else: - function_name = function_name.group() + #ищем, в каком файле находится определение функции function_name # demangled_function_name = function_name[1:-2] #убираем <>: @@ -61,27 +68,36 @@ def getName(static_file_lines, address, nonstatic_file, newNames): # break #открываем nonstatic_file, ищем начало функции function_name - with open(nonstatic_file) as f: + with codecs.open(nonstatic_file, 'r', 'utf-8', errors="ignore") as f: data = f.readlines() - result, index = searchInLines(function_name, data) - if result is None: - return '' + data = [l for l in data if '.text' in l] + ns_funcs,ns_dict = switcher.getFunctions(data) + #result, index = searchInLines(function_name, data) + ns_funcAddr = dict(zip(ns_dict.values(), ns_dict.keys())) + addr = ns_funcAddr[function_name] + group = [f for f in ns_funcs if f[0].addr == addr][0] + # if result is None: + # return '' #raise Exception('{0} is not called in nonstatic {1}'.format(address, nonstatic_file)) - b1 = utils.getAddressFromLine(data[index+1]) # адрес начала функции function_name - b2 = hexstringToInt(b1) + delta # адрес, где вызывается искомая функция + #b1 = utils.getAddressFromLine(data[index+1]) # адрес начала функции function_name + b1 = group[0].addr + b2 = int(b1,16) + delta # адрес, где вызывается искомая функция # ищем строку в data, у которой этот адрес - row, ind = searchInLines('.*{0}:.*'.format(hex(b2)[2:]), data) - if row is None: - return '' + #row, ind = searchInLines('.*{0}:.*'.format(hex(b2)[2:]), data) + row = [l for l in group if l.addr == hex(b2)][0] + # if row is None: + # return '' #raise Exception('{0} address is not found in nonstatic {1}'.format(b2, nonstatic_file)) - row = data[ind] - found_name = re.search('<.*>', row) - if found_name is None: - #raise Exception('No func name at {0} in {1}', b2, nonstatic_file) - return '' - if re.search('(:?@@Base|@plt)>',found_name.group()) is None: - return '' - return found_name.group().split('@')[0][1:] + #row = data[ind] + #found_name = re.search('<.*>', row) + found_name = row.line.split(';')[0].strip().split(' ')[-1] + return found_name + # if found_name is None: + # #raise Exception('No func name at {0} in {1}', b2, nonstatic_file) + # return '' + # if re.search('(:?@@Base|@plt)>',found_name.group()) is None: + # return '' + #return found_name.group().split('@')[0][1:] def searchInLines(regex, lines): for index, line in enumerate(lines): @@ -94,22 +110,26 @@ def hexstringToInt(str): return int('0x'+str, 16) -def getFuncBegin(start_index, static_file_lines): +def getFuncBegin(line, static_file_lines): + group = [g for g in static_file_lines if g[0].addr == line.funcAddr][0] + start_index = group.index(line) pop_count = 0 while True: if start_index == 0: return None, None start_index -= 1 - some_func_end = re.search('pop|ldmia', static_file_lines[start_index]) + popPattern, pushPattern = re.compile('pop|ldmia|ldmfd',re.IGNORECASE),\ + re.compile('push|stmdb|stmfd', re.IGNORECASE) + some_func_end = switcher.searchPattern(popPattern,group[start_index]) if some_func_end is not None: pop_count+=1 continue - result = re.search('push|stmdb', static_file_lines[start_index]) + result = switcher.searchPattern(pushPattern,group[start_index]) if result is not None: if pop_count == 0: break pop_count-=1 - return utils.getAddressFromLine(static_file_lines[start_index]), start_index + return group[start_index].addr, start_index def generateNonStaticFile(in_file, path): diff --git a/switcher.py b/switcher.py index 3883442..3032c82 100644 --- a/switcher.py +++ b/switcher.py @@ -1,4 +1,4 @@ -import IdaHelper, ObjdumpHelper, re +import IdaHelper, ObjdumpHelper, re, os, static_functions_helper from recordclass import recordclass isIDA = True @@ -25,6 +25,34 @@ def getFunctions(lines): return IdaHelper.getFunctions(lines) +def resolveStaticFunctions(funcdict, config, fileName, groups): + # выделяем функции, для которых нет имени + nonamePattern = re.compile('(sub)|(local)_[0-9a-f]+',re.IGNORECASE) + noname_functions = dict((addr, func) for addr, func in funcdict.items() + if nonamePattern.search(func)) + if len(noname_functions) > 0: + nonstatic_folder = config.get('nonstatic_app_directory') + nonstatic_file = os.path.join(nonstatic_folder, os.path.basename(fileName)+'.txt') + + newNames = dict((addr, func) for addr, func in noname_functions.items() + if not nonamePattern.search(func)) + + while True and os.path.exists(nonstatic_file): + noname_len = len(noname_functions) + for addr in list(noname_functions): + name = static_functions_helper.getName(groups, noname_functions[addr], nonstatic_file, newNames, funcdict) + if name!='': + if name.startswith('j_'): + name = name[2:] + newNames[addr] = name + noname_functions.pop(addr) + if len(noname_functions) == noname_len: + break + + for addr in newNames: + funcdict[addr] = newNames[addr] + + def getPushes(group): if isIDA: return IdaHelper.getPush(group)