Skip to content

Commit

Permalink
Add SUB SP, static_functions for ida
Browse files Browse the repository at this point in the history
  • Loading branch information
DoroninaD committed Nov 12, 2017
1 parent 25f27a2 commit 474b7ad
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 62 deletions.
11 changes: 8 additions & 3 deletions IdaHelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
25 changes: 21 additions & 4 deletions arm_translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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]
Expand Down
17 changes: 16 additions & 1 deletion main_switcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down
59 changes: 40 additions & 19 deletions parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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]))
Expand All @@ -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]))
Expand Down Expand Up @@ -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)
Expand Down
88 changes: 54 additions & 34 deletions static_functions_helper.py
Original file line number Diff line number Diff line change
@@ -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]
Expand All @@ -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] #убираем <>:
Expand All @@ -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):
Expand All @@ -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):
Expand Down
30 changes: 29 additions & 1 deletion switcher.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import IdaHelper, ObjdumpHelper, re
import IdaHelper, ObjdumpHelper, re, os, static_functions_helper
from recordclass import recordclass

isIDA = True
Expand All @@ -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)
Expand Down

0 comments on commit 474b7ad

Please sign in to comment.