diff --git a/IdaHelper.py b/IdaHelper.py index 9ba408b..6f5cc1d 100644 --- a/IdaHelper.py +++ b/IdaHelper.py @@ -94,7 +94,8 @@ def checkTheSameRegsForPushAndPops(group): pushpops = dict((g.addr,re.search(pushPatter.pattern+'|'+popPattern.pattern,g.line)) for g in group) pushpops = dict((addr, line.group()) for addr, line in pushpops.items() if line) - + if len(pushpops)==0: + return False # parse regs regsSampler = None for addr, line in pushpops.items(): @@ -163,8 +164,11 @@ def getVars(lines): vars = [l for l in lines if pattern.search(l)] for var in vars: items = ' '.join(var.split(' ')[1:]) - name = re.search('[a-z]+_?[0-9a-fr]*\s', items,re.IGNORECASE)\ + try: + name = re.search('[a-z]+_?[0-9a-fr]*\s*', items,re.IGNORECASE)\ .group().strip() + except: + aaa=1 addr = getAddress(var) value = re.search('=\s*-?(0x)?[a-f0-9]+\s',items,re.IGNORECASE)\ .group().replace('=',' ').strip() diff --git a/arm_translate.py b/arm_translate.py index 2f4716f..7ad8596 100755 --- a/arm_translate.py +++ b/arm_translate.py @@ -52,6 +52,13 @@ def code(old_code, mask, s, is_thumb): return c return c[4:] + c[:4] +def changeSubSp(old_code, offset, thumb): + #new = code(old_code, '11110000', new_offset, thumb) + c = hex(int(old_code, 16)+offset)[2:].upper() + if thumb: + return c + return c[4:] + c[:4] + def makeLdrOrStrInner(old_instr, old_code, rx, ry, a, is_thumb, l): # ldr rx, [ry + a] old_instr = old_instr.lower() s = a diff --git a/main_switcher.py b/main_switcher.py index a938330..ecfad76 100755 --- a/main_switcher.py +++ b/main_switcher.py @@ -7,7 +7,7 @@ conditions_pattern = '|'.join(conditions) NEW = True -RETURN_TYPES = True +RETURN_TYPES = False # def findSpSubbed(groups): # containSpSubbedPattern = re.compile('\ssub\s+sp,',re.IGNORECASE) @@ -54,7 +54,16 @@ def run(path, start_group, end_group, DEBUG, config): # check only one push # the same regs for push and pops + + a = list(filter(None,[switcher.checkOnlyOnePush(g) for g in groups])) + print("Only one push:", len(a)) + + a = list(filter(None,[switcher.checkTheSameRegsForPushAndPops(g) for g in groups])) + print("The same regs for push and pop:", len(a)) + + groups = list(filter(None,[switcher.checkSuitable(g) for g in groups])) + print("Suitable groups (push and pop with the same registers, only one push):", len(groups)) groups = switcher.handleExternalJumps(groups, conditions, funcAddrDict) @@ -92,7 +101,7 @@ def run(path, start_group, end_group, DEBUG, config): return_size = function_types[group[0].addr] if RETURN_TYPES else 4 restrictedRegs = restrictedRegsDict[group[0].addr]\ if group[0].addr in restrictedRegsDict else [] - print(','.join(restrictedRegs),'Next') + #print(','.join(restrictedRegs),'Next') new_registers, table = utils.addRegistersToStartAndEnd\ (push.regs, push.bytes, return_size, restrictedRegs) @@ -126,7 +135,7 @@ def run(path, start_group, end_group, DEBUG, config): funcAddr = group[0].addr key = cxxfilt.demangle(addrFuncDict[funcAddr]) \ if addrFuncDict[funcAddr]!='' else push.addr - print(colored.setColored('{0}: '.format(key), colored.OKGREEN) + 'old {0}, new {1}'.format(push.regs, new_registers)) + #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}'\ @@ -145,6 +154,17 @@ def run(path, start_group, end_group, DEBUG, config): .format(len(onlyForContainsSub), len(onlyWithPushes), len(handledGroups) - len(onlyWithPushes)) colored.printColored(output, colored.BOLD) + l = [] + handled = 0 + for item in onlyForContainsSub: + res = parse.getAllSpLinesForSub(item) + if res!=-1 and len(res) > 0: + handled+=1 + l.extend(res) + print ('Handled with SUB SP (only sub sp lines):', handled) + to_write.extend(l) + + #переписываем файл f = open(path+'_old.so', 'br') diff --git a/parse.py b/parse.py index 5bf9139..3c7025a 100755 --- a/parse.py +++ b/parse.py @@ -1,6 +1,6 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- -import re, arm_translate, utils, switcher +import re, arm_translate, utils, switcher, random conditions = ['eq','ne','cs','hs','cc','lo','mi','pl','vs','vc','hi','ls','ge','lt','gt','le','al'] @@ -294,3 +294,168 @@ def changeOffset(offset, cur, table): # offset - общее смещение if offset > max_key: # и новых смещений return cur + table[max_key] return table[offset//4*4] + +def getAllSpLinesForSub(lines): + to_write = [] + + # ldr/str/... rx, [sp], ry - не знаем значение ry, не можем сделать правильное смещение => не обрабатываем такие функции + register_relative = switcher.getRelativeRegs(lines) + if len(register_relative) > 0: + return -1 + + addSubPattern = re.compile('.*(add|sub)(.w)?\s*sp(,\s?sp)?,\s?#[0-9]+', re.IGNORECASE) + sub_add_sp_lines = switcher.searchInLines(addSubPattern, lines) + + subSpLine = sub_add_sp_lines[0] + if len(sub_add_sp_lines) != 0: + sub_ind = lines.index(subSpLine) + a = switcher.getNumber(subSpLine) + if a is None: + return -1 + else: + a = 0 + sub_ind = 0 + + spOffset = random.randint(1, 100) + # меняем сдвиг sub sp, #4 -> sub sp, #4+spOffset + newSupSP = arm_translate.changeSubSp(subSpLine.bytes, spOffset, subSpLine.thumb) + to_write.append((subSpLine.addr, + len(subSpLine.bytes) // 2, utils.toLittleEndian(newSupSP))) + # ищем строки вида [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])) + useSpPattern = re.compile('.*(ldr|str)(b|h|sb|sh|d)?(.w)?.*\[sp,\s?#-?(0x)?[0-9a-f]+.*\].*', re.IGNORECASE) + use_sp_lines = switcher.searchInLines(useSpPattern, lines[sub_ind:]) + + for l in use_sp_lines: + LdrStrPattern = re.compile('v?(ldr|str)(b|h|sb|sh|d)?(.w)?', re.IGNORECASE) + instr = switcher.searchPattern(LdrStrPattern, l).group().lower() + 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))) + + # ищем строки вида 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])) + addSpToRegPattern = re.compile \ + ('.*(add(.w)?|mov)\s*(r[0-9]|r10|r11|r12),\s?sp(,\s?#[1-9]+)?.*', re.IGNORECASE) + add_sp_to_reg = switcher.searchInLines(addSpToRegPattern, lines) + # todo + if any(['!' in l.line for l in add_sp_to_reg]): + return -1 + + if len(add_sp_to_reg) > 0: + for l in add_sp_to_reg: + new = getRxLinesForSubSp(lines, l, a, sub_ind, spOffset) + if new == -1: + return -1 + to_write.extend(new) + + return to_write + + +def getRxLinesForSubSp(lines, line, sp_subbed, sub_ind, spOffset): + llll = [] + + def getJumpIndex(line): + try: + jmpAddr = tmp[commonJMPPattern.search(line).end():].strip().split(';')[0] + except: + aaa = 1 + jmpAddr = re.sub('[a-z]+_', '0x', jmpAddr).lower() + try: + int(jmpAddr, 16) + except: + return None + try_ind = [l for l in lines if l.addr == jmpAddr] + if len(try_ind) == 0: + return None + return lines.index(try_ind[0]) + + def handleLine(index, line): + llll.append((index, line)) + + c = switcher.getNumber(line) + if c is None: + return -1 + regsPattern = re.compile('r11|r10|r12|r[0-9]', re.IGNORECASE) + reg = switcher.searchPattern(regsPattern, line).group() + useRegPattern = re.compile \ + ('.*(ldr|str)(b|h|sb|sh|sw|d)?(.w)?.*\[{0}(,\s?#\-?(0x)?[0-9a-f]+.*\])?'.format(reg), + re.IGNORECASE) + clearRegPattern = re.compile \ + ('.*(mov(eq|ne)?|(v)?ldr(b|h|sb|sh|sw)?|add)(.w)?\s{0},\s?.*'.format(reg), + re.IGNORECASE) + simpleJMPPattern = re.compile('\sbl?s?(\.w)?\s', re.IGNORECASE) + regJumpPattern = re.compile('\sbl?x\s', re.IGNORECASE) + conditionJumpPattern = re.compile('\s((cbz)|(b(l)?({0})))\s'.format('|'.join(conditions)), re.IGNORECASE) + commonJMPPattern = re.compile('\s((cbn?z)|(bl?s?({0})?))(\.w)?\s'.format('|'.join(conditions)), re.IGNORECASE) + # определяем строку, с которой будем искать строки вида [reg, #d] + start_ind = list(lines).index(line) + + ways, old_ways = [start_ind], [start_ind] + while (len(ways) > 0): + i = ways[0] + end_reg = switcher.searchInLines(clearRegPattern, lines[i + 1:]) + # определяем строку, ДО которой будем искать строки вида [reg, #d] (mov затирает sp) + end_ind = list(lines).index(end_reg[0].group()) if len(end_reg) > 0 else len(lines) + for l in lines[i:end_ind]: + tmp = l.line + if useRegPattern.search(tmp): + handleLine(i, l) + if simpleJMPPattern.search(tmp): + index = getJumpIndex(tmp) + if index is not None and index not in old_ways: + old_ways.append(index) + ways.append(index) + break + if conditionJumpPattern.search(tmp): + try: + index = getJumpIndex(tmp) + except: + index = getJumpIndex(tmp) + if index is None: + break + if index not in old_ways: + old_ways.append(index) + ways.append(index) + ways.remove(i) + + # предполагаем, что изменение sp происходит только в начале и конце функции todo + # если sp еще не отнят, тогда не нужно учитывать a + + to_write = [] + + # todo если будет str rx, [sp, #] и уже добавлен в to_write по sp, будет перезаписано? + for item in llll: + start_ind, l = item[0], item[1] + if start_ind < sub_ind: + sp_subbed = 0 + pattern = re.compile('v?(ldr|str)(b|h|sb|sh|d)?(.w)?', re.IGNORECASE) + instr = switcher.searchPattern(pattern, l).group() + d = switcher.getNumber(l) + if d 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(',', '') + 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) + str_reg = switcher.searchInLines(strRegPattern, lines[start_ind:end_ind]) + if len(str_reg) > 0: + return -1 + return to_write + diff --git a/switcher.py b/switcher.py index 5bb2c25..3883442 100644 --- a/switcher.py +++ b/switcher.py @@ -34,6 +34,15 @@ def getPops(group): return IdaHelper.getPops(group) +def checkOnlyOnePush(group): + if isIDA: + return IdaHelper.checkOnlyOnePush(group) + +def checkTheSameRegsForPushAndPops(groups): + if isIDA: + return IdaHelper.checkTheSameRegsForPushAndPops(groups) + + def checkSuitable(group): if isIDA: if not IdaHelper.checkOnlyOnePush(group):