diff --git a/apktool-1.4.1.jar b/apktool-1.4.1.jar new file mode 100755 index 0000000..de24750 Binary files /dev/null and b/apktool-1.4.1.jar differ diff --git a/apktool.jar b/apktool.jar new file mode 100755 index 0000000..17feaf0 Binary files /dev/null and b/apktool.jar differ diff --git a/arm_translate.py b/arm_translate.py new file mode 100755 index 0000000..edb886b --- /dev/null +++ b/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/arm_translate_old.py b/arm_translate_old.py new file mode 100755 index 0000000..7b74703 --- /dev/null +++ b/arm_translate_old.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- + +from utils import calcRegisters, getCode + +lo_registers = ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7'] +arm_push_prefix = 'e92d' +thumb_push_prefix = 'b5' +arm_pop_prefix = 'E8BD' +thumb_pop_prefix = 'bd' + + +def pushToCode(registers, code, is_thumb): + # считаем сумму регистров + s = calcRegisters(registers) + if not is_thumb: + return getCode(int('4000', 16) + s) + arm_push_prefix + #return arm_push_prefix + getCode(int('4000', 16) + s) + if any(registers[i] not in lo_registers for i in range(len(registers))): + return arm_push_prefix + getCode(int('4000', 16) + s) + # все младшие + return thumb_push_prefix + getCode(s) + + +def popToCode(registers, code, is_thumb): + s = calcRegisters(registers) + + if len(code) > 4 and code[4] == '4': # pop {lr} + #return arm_pop_prefix + getCode(int('4000', 16) + s) + return getCode(int('4000', 16) + s) + arm_pop_prefix + if not is_thumb: + return getCode(int('8000', 16) + s) + arm_pop_prefix + if any(registers[i] not in lo_registers for i in range(len(registers))): + return arm_pop_prefix + getCode(int('8000', 16) + s) + # все младшие + return thumb_pop_prefix + getCode(s) + + +arm_addsp255_prefix = "e28dd0" +arm_addsp508_prefix = "e28ddf" +arm_addsp100_prefix = "e28ddc" +thumb_addsubsp_prefix = 'b0' + + +# add sp, #number +def addSpToCode(number, short=True): + # thumb is only for less than 508 + if short and number <= 508: + return thumb_addsubsp_prefix + getCode(number // 4) + + # for arm + if number > 0x400: + raise Exception("Tried to convert add sp, #", number) + + if number in [0x100, 0x200, 0x300, 0x400]: + return arm_addsp100_prefix + getCode(number // 0x100) + + if number < 256: + return arm_addsp255_prefix + getCode(number) + + return arm_addsp508_prefix + getCode(number // 4) + + +arm_subsp255_prefix = "e24dd0" +arm_subsp508_prefix = "e24ddf" +arm_subsp100_prefix = "e24ddc" + + +def subSpToCode(number, short=True): + # thumb is only for less than 512 + if short and number <= 508: + return thumb_addsubsp_prefix + getCode(0x80 + number // 4) + + # for arm + if number > 0x400: + raise Exception("Tried to convert add sp, #", number) + + if number in [0x100, 0x200, 0x300, 0x400]: + return arm_subsp100_prefix + getCode(number // 0x100) + + if number < 256: + return arm_subsp255_prefix + getCode(number) + + return arm_subsp508_prefix + getCode(number // 4) + +def makeLdrOrStr(old_instr, old_code, rx, ry, a, is_thumb): + return getCode(makeLdrOrStrInner(old_instr, old_code, rx, ry, a, is_thumb)) + + +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 makeLdrOrStrInner(old_instr, old_code, rx, ry, a, is_thumb): # ldr rx, [ry + a] + + #is_not_thumb = len(old_code) > 4 + is_sp = ry == 'sp' + + 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) + #return int(ss[4:]+ss[:4],16) + return int(ss, 16) + 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) + #return int(ss[4:]+ss[:4],16) + return int(ss, 16) + + + rx = regs[rx] if rx in regs else int(rx[1:]) + + if str(old_instr).endswith('.w'): + return extended[old_instr]*0x100000 + rx * 0x1000 + ry*0x10000 + a + + if not is_thumb: + if old_instr in basic_arm: + #return basic_arm[old_instr] * 0x100000 + ry * 0x10000 + rx * 0x1000 + a + return basic_arm[old_instr] * 0x10 + ry + (rx * 0x1000 + a) * 0x10000 + if old_instr in more_arm: + #return more_arm[old_instr] + ry * 0x10000 + rx * 0x1000 + a // 0x10 * 0x100 + a % 0x10 + return more_arm[old_instr] + ry + (rx * 0x1000 + a // 0x10 * 0x100 + a % 0x10)*0x10000 + if old_instr in ['ldrd', 'strd']: + x = int(old_code, 16) & 0xfffff0f0 + a % 16 + a // 16 * 0x100 + ss = getCode(x) + return int(ss[4:] + ss[:4], 16) + else: + if old_instr in ['ldrd', 'strd']: + x = (int(old_code, 16) & 0xffffff00) + a//4 + ss = getCode(x) + return int(ss[4:] + ss[:4], 16) + if is_sp and old_instr in sp_thumb: + return sp_thumb[old_instr] * 0x100 + a // 4 + rx * 0x100 + if old_instr in basic_thumb: + return basic_thumb[old_instr] * 0x100 + rx + ry * 8 + a * 0x10 + if old_instr in more_thumb: + return more_thumb[old_instr] * 0x100 + rx + ry * 8 + 0x20 * a + + + + +def pushpopToCode(registers, code, is_thumb): + # считаем сумму регистров + s = calcRegisters(registers) + mask = int('1'*8, 2) if is_thumb else int('1'*13, 2) + c = getCode((int(code, 16) & ~mask) + s) + if is_thumb: + return c + return c[4:] + c[:4] diff --git a/generate_key.sh b/generate_key.sh new file mode 100755 index 0000000..22fc81c --- /dev/null +++ b/generate_key.sh @@ -0,0 +1,2 @@ +#!/bin/bash +keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000 diff --git a/go.sh b/go.sh new file mode 100755 index 0000000..02f19ad --- /dev/null +++ b/go.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# -*- coding: utf-8 -*- +echo 'start' +echo $1 +java -jar apktool.jar d -f -r -s $1 +echo 'java done' + +FILES=${1%.apk}/lib/armeabi-v7a/*.so + +echo ${1%.apk}/lib/armeabi-v7a + +echo $path + +for f in $FILES + +do + +arm-none-eabi-objdump -d -m arm -M reg-names-std $f > ${f%.so}.txt +mv $f ${f%.so}_old.so + +done + + + +echo 'Unpacked, running security...' +python3 ../run.py ${1%.apk}/lib/armeabi-v7a/ +echo 'Security done, packing...' +sudo rm ${1%.apk}/lib/armeabi-v7a/*.txt + +sudo rm ${1%.apk}/lib/armeabi-v7a/*_old.so + + +java -jar apktool.jar b -o $2 ${1%.apk} + +jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore $2 alias_name + + diff --git a/main.py b/main.py new file mode 100755 index 0000000..8af5869 --- /dev/null +++ b/main.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +import re, utils, arm_translate, sys + +# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489e/Babefbce.html +# http://www.davespace.co.uk/arm/introduction-to-arm/registers.html + +#lr = r14, pc = r15, sp = r12 + +# push {, lr(r14)} +# stmdb sp!, {, lr} +# stmdb.w sp!, {, lr} + +# pop {, pc(r15)} или lr +# ldmia.w sp!, {, pc} +# ldmia sp!, {, pc} + + +# print(arm_translate.pushToCode(['r1', 'r2', 'r3'])) +# print(arm_translate.pushToCode(['r1', 'r2', 'r3', 'r8'])) +# +# print(arm_translate.popToCode(['r1', 'r2', 'r3'])) +# print(arm_translate.popToCode(['r1', 'r2', 'r3', 'r8'])) +# +# print(arm_translate.addSpToCode(500)) +# print(arm_translate.addSpToCode(256)) +# print(arm_translate.subSpToCode(256)) +# +# retcode = subprocess.call(". unpack.sh", shell=True) +# if retcode == 0: +# print("success") +# else: +# print("failure") +# raise Exception("UNABLE TO UNPACK ") +#print(sys.argv[1]) +path = sys.argv[1] #название файла без расширения + +if 'libwhatsapp' in path: + exit() + #'data/com.whatsapp/lib/armeabi-v7a/libwhatsapp' +f = open(path+'.txt', 'r') +lines = f.readlines() +indices = [i for i, s in enumerate(lines) if '.text' in s] +lines = lines[indices[0]:] +f.close() + +stack_lines = [] + +index = 0 +# выбираем только строки с push/pop, разбираем их на составляющие +for line in lines: + stack_line = re.match('.*((push(.w)?|stmdb(.w)?\s*sp!).*lr}|(pop(.w)?|ldmia(.w)?\s*sp!).*(pc|lr)}).*', line) + if stack_line is not None: + method = re.search('push(.w)?|stmdb(.w)?|pop(.w)?|ldmia(.w)?', line).group() + #reg = re.search('{.*}', line).group().strip('}').strip('{').replace(',', '').split() + registers = re.findall("r11|r10|r[1-9]", stack_line.group()) + address = re.match('.*:', line).group().replace(' ', '').replace(':', '') + code = line.split('\t')[1].replace(' ', '').replace('\t', '') + stack_lines.append((address, code, method, registers, index)) + index += 1 + +# all_registers = ['r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11'] + + +# ищем 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 + + else: + i += 1 + +#добавляем в to_write (адрес, количество старых байт, новые байты) для перезаписи +to_write = [] + +for group in groups[:2]: + first, last = group[0], group[-1] + code_with_sp = utils.getFunctionStackCode(lines[first[-1] + 1:last[-1]]) + + #print(first) + if len(code_with_sp) > 0: + #print(first) + #print("START") + + new_registers, count = utils.addRegistersToStart(first[3]) + + flag = False + for aa in code_with_sp: + number = str(re.search('#[0-9]+', aa).group()).strip(' ').strip('#') + new_number = int(number) + count * 4 + if new_number > 508: + flag = True + break + if 'add' in aa: + instr = arm_translate.addSpToCode(new_number) + elif 'sub' in aa: + instr = arm_translate.subSpToCode(new_number) + address = re.match('.*:', aa).group().replace(' ', '').replace(':', '') + code = aa.split('\t')[1].replace(' ', '').replace('\t', '') + + # добавляем в to_write (адрес, количество старых байт, новые байты) add/sup sp + to_write.append((address, len(code)//2, utils.toLittleEndian(instr))) # добавляем add/sub sp + + if flag is False: + # добавляем в to_write (адрес, количество старых байт, новые байты) push + to_write.append((first[0], len(first[1]) // 2, + utils.toLittleEndian(arm_translate.pushToCode(new_registers)))) # добавляем новый push + # добавлаем все pop + for a in group[1:]: + to_write.append((a[0], len(a[1]) // 2, + utils.toLittleEndian( + arm_translate.popToCode(new_registers, a[1])))) # добавляем новый pop + + + + #print(last) + #print("END") + + +# for i in to_write: +# if i[1]!=len(i[2]): +# to_write.remove(i) + +#переписываем файл + +f = open(path+'_old.so', '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]:] + #print(to_write) + +f = open(path+'.so', 'bw') +f.write(text) +f.close() diff --git a/main_add_older_regs.py b/main_add_older_regs.py new file mode 100755 index 0000000..32b8d07 --- /dev/null +++ b/main_add_older_regs.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +import re, utils, arm_translate, sys + +# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489e/Babefbce.html +# http://www.davespace.co.uk/arm/introduction-to-arm/registers.html +path = sys.argv[1] #название файла без расширения +#path = 'data/com.whatsapp/lib/armeabi-v7a/libcurve25519' +#if 'libwhatsapp' in path: + # exit() + #'data/com.whatsapp/lib/armeabi-v7a/libwhatsapp' +f = open(path+'.txt', 'r') +lines = f.readlines() +indices = [i for i, s in enumerate(lines) if '.text' in s] +lines = lines[indices[0]:] +f.close() + +stack_lines = [] + +index = 0 +# выбираем только строки с push/pop, разбираем их на составляющие +for line in lines: + stack_line = re.match('.*((push(.w)?|stmdb(.w)?\s*sp!).*lr}|(pop(.w)?|ldmia(.w)?\s*sp!).*(pc|lr)}).*', line) + if stack_line is not None: + method = re.search('push(.w)?|stmdb(.w)?|pop(.w)?|ldmia(.w)?', line).group() + + # берем все регистры в {} и убираем последний (это lr или pc) + # в дальнешем будем исключать строки, в которых есть регистры > r11 + registers = re.search('{.*}', line).group().replace('}','').replace('{','').replace(' ','').split(',')[:-1] + #registers = re.findall("r11|r10|r[1-9]|sp", stack_line.group()) + # убираем лишний sp (sp!) + #if (method.startswith('stm') or method.startswith('ldm')) and 'sp' in registers: + # registers.remove('sp') + address = re.match('.*:', line).group().replace(' ', '').replace(':', '') + code = line.split('\t')[1].replace(' ', '').replace('\t', '') + stack_lines.append((address, code, method, registers, index)) + index += 1 + +# all_registers = ['r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11'] + + +# ищем 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 + + else: + i += 1 + +#добавляем в to_write (адрес, количество старых байт, новые байты) для перезаписи +to_write = [] +l = 0 +for group in groups[:10]: + first, last = group[0], group[-1] + l+=1 + if l == 6026: + jj=0 + #print(first) + big_regs = ['sp', 'ip', 'lr', 'pc', 'r12'] + if any(big_regs[i] in first[3] for i in range(len(big_regs))): #and (str(first[2]).startswith('push') or str(first[2]).startswith('stm')): + continue + new_registers= utils.addRegistersToEnd(first[3]) + + # добавляем в to_write (адрес, количество старых байт, новые байты) push + to_write.append((first[0], len(first[1]) // 2, + utils.toLittleEndian(arm_translate.pushToCode(new_registers, first[1])))) # добавляем новый push + # добавлаем все pop + for a in group[1:]: + to_write.append((a[0], len(a[1]) // 2, + utils.toLittleEndian( + arm_translate.popToCode(new_registers, a[1])))) # добавляем новый pop + + + +#переписываем файл + +f = open(path+'_old.so', '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]:] + print(to_write) + +f = open(path+'.so', 'bw') +f.write(text) +f.close() diff --git a/main_insert_border_regs.py b/main_insert_border_regs.py new file mode 100755 index 0000000..552c2e0 --- /dev/null +++ b/main_insert_border_regs.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +import re, utils, arm_translate, parse,parse_functions_utils + + + +def run(path, start_group, end_group): + f = open(path+'.txt', 'r') + lines = f.readlines() + indices = [i for i, s in enumerate(lines) if '.text' in s] + lines = lines[indices[0]:] + f.close() + + stack_lines = [] + + index = 0 + # выбираем только строки с push/pop, разбираем их на составляющие + # Также ищем названия функций : + function_name = '' + functions = dict() # адрес - функция + for line in lines: + 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 stack_line is not None: + method = re.search('push(.w)?|stmdb(.w)?|pop(.w)?|ldmia(.w)?', line).group() + # берем все регистры в {} и убираем последний (это lr или pc) + # в дальнешем будем исключать строки, в которых есть регистры > r11 + #registers = re.search('{.*}', line).group().replace('}','').replace('{','').replace(' ','').split(',')[:-1] + registers = re.search('{.*}', line).group().replace('}','').replace('{','').replace(' ','').split(',') + last_reg = registers[-1] + #registers = re.findall("r11|r10|r[1-9]|sp", stack_line.group()) + # убираем лишний sp (sp!) + #if (method.startswith('stm') or method.startswith('ldm')) and 'sp' in registers: + # registers.remove('sp') + address = utils.getAddressFromLine(line) + code, is_thumb = utils.getCodeFromLine(line) + stack_lines.append((address, code, method, registers[:-1], is_thumb, index, last_reg)) + functions[address] = function_name.split('@')[0] + function_name = '' + index += 1 + + #находим тип функций + function_types = parse_functions_utils.getFunctionsReturnTypeSize(functions) + #todo использовать числа при рандомизации + # all_registers = ['r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11'] + + + # ищем 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 + + else: + i += 1 + + + # фильтруем группы - убираем те, в которых последний pop нe pc + print ('GROUPS:', len(groups)) + + # gr = groups + # groups = [] + # for group in gr: +# if all(g[6]=='pc' for g in 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)) + + #добавляем в to_write (адрес, количество старых байт, новые байты) для перезаписи + groups_count = 0 + to_write = [] + l = 0 + full_registers_count = 0 + #1935-36 + print(start_group, ":", end_group) + for group in groups: # 66 libcrypto - pop lr => bl - перезапись регистров + first, last = group[0], group[-1] + #print(first) + return_size = function_types[first[0]] + print(return_size) + #print(parse_functions_utils.getFunctionReturnTypeSize(first[-1])) + #print(last[0]) + #print(first, last) + l+=1 + big_regs = ['sp', 'ip', 'lr', 'pc', 'r12'] + if any(big_regs[i] in first[3] for i in range(len(big_regs))): #and (str(first[2]).startswith('push') or str(first[2]).startswith('stm')): + continue + + # добавляем регистры в начало, считает их количество + real_reg_count = len(first[3]) + 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) + if inner_lines == -1: + continue + groups_count+=1 + # добавляем в to_write (адрес, количество старых байт, новые байты) push + #print (first[0]) + + to_write.append((first[0], len(first[1]) // 2, + utils.toLittleEndian(arm_translate.pushpopToCode(new_registers, first[1], first[4], real_reg_count, False)))) # добавляем новый push + + # добавлаем все pop + for a in group[1:]: + to_write.append((a[0], len(a[1]) // 2, + utils.toLittleEndian( + arm_translate.pushpopToCode(new_registers, a[1], a[4], real_reg_count, True)))) # добавляем новый pop + + + #print("POP AT:", last[0]) + #for i in inner_lines: + #print(i) + if len(inner_lines) > 0: + to_write.extend(inner_lines) + + print('End: ', groups_count, 'full registers: ', full_registers_count) + #переписываем файл + f = open(path+'_old.so', '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() \ No newline at end of file diff --git a/main_insert_lower_regs.py b/main_insert_lower_regs.py new file mode 100755 index 0000000..0289715 --- /dev/null +++ b/main_insert_lower_regs.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +import re, utils, arm_translate, parse, sys + +# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489e/Babefbce.html +# http://www.davespace.co.uk/arm/introduction-to-arm/registers.html +#path = sys.argv[1] #название файла без расширения +path = 'data/telegram/lib/armeabi-v7a/libtmessages.25' +#path = 'data/app-debug/lib/armeabi-v7a/libhello-libs' +#if 'libwhatsapp' in path: + # exit() + #'data/com.whatsapp/lib/armeabi-v7a/libwhatsapp' +f = open(path+'.txt', 'r') +lines = f.readlines() +indices = [i for i, s in enumerate(lines) if '.text' in s] +lines = lines[indices[0]:] +f.close() + +stack_lines = [] + +index = 0 +# выбираем только строки с push/pop, разбираем их на составляющие +for line in lines: + stack_line = re.match('.*((push(.w)?|stmdb(.w)?\s*sp!).*lr}|(pop(.w)?|ldmia(.w)?\s*sp!).*(pc|lr)}).*', line) + if stack_line is not None: + method = re.search('push(.w)?|stmdb(.w)?|pop(.w)?|ldmia(.w)?', line).group() + + # берем все регистры в {} и убираем последний (это lr или pc) + # в дальнешем будем исключать строки, в которых есть регистры > r11 + registers = re.search('{.*}', line).group().replace('}','').replace('{','').replace(' ','').split(',')[:-1] + #registers = re.findall("r11|r10|r[1-9]|sp", stack_line.group()) + # убираем лишний sp (sp!) + #if (method.startswith('stm') or method.startswith('ldm')) and 'sp' in registers: + # registers.remove('sp') + address = utils.getAddressFromLine(line) + code, is_thumb = utils.getCodeFromLine(line) + stack_lines.append((address, code, method, registers, is_thumb, index)) + index += 1 + +# all_registers = ['r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11'] + + +# ищем 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 + + else: + i += 1 + +#добавляем в to_write (адрес, количество старых байт, новые байты) для перезаписи +to_write = [] +l = 0 +for group in groups[:6700]: + first, last = group[0], group[-1] + #print(last[0]) + #print(first, last) + l+=1 + big_regs = ['sp', 'ip', 'lr', 'pc', 'r12'] + if any(big_regs[i] in first[3] for i in range(len(big_regs))): #and (str(first[2]).startswith('push') or str(first[2]).startswith('stm')): + continue + + # добавляем регистры в начало, считает их количество + new_registers, count = utils.addRegistersToStart(first[3]) + + # добавляем в to_write (адрес, количество старых байт, новые байты) push + to_write.append((first[0], len(first[1]) // 2, + utils.toLittleEndian(arm_translate.pushToCode(new_registers, first[1], first[4])))) # добавляем новый push + # добавлаем все pop + for a in group[1:]: + to_write.append((a[0], len(a[1]) // 2, + utils.toLittleEndian( + arm_translate.popToCode(new_registers, a[1], a[4])))) # добавляем новый pop + + #меняем втутренние строки, взаимодействующие с sp + inner_lines = parse.getAllSpLinesForLow(lines[first[-1] + 1:last[-1]], count) + print("POP AT:", last[0]) + for i in inner_lines: + print(i) + if len(inner_lines) > 0: + to_write.extend(inner_lines) + + +#переписываем файл + +f = open(path+'_old.so', 'br') +text = f.read() +f.close() + +for line in to_write: + # print(line) + offset = int(line[0],16) + text = text[:offset] + line[2] + text[offset+line[1]:] + +f = open(path+'.so', 'bw') +f.write(text) +f.close() diff --git a/my-release-key.keystore b/my-release-key.keystore new file mode 100755 index 0000000..38a2fe2 Binary files /dev/null and b/my-release-key.keystore differ diff --git a/pack.sh b/pack.sh new file mode 100755 index 0000000..c54f32e --- /dev/null +++ b/pack.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# folder name.apk + + + +sudo rm $1/lib/armeabi-v7a/*.txt +sudo rm $1/lib/armeabi-v7a/*_old.so + +java -jar apktool.jar b -o $2 $1 + +jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore $2 alias_name + diff --git a/parse.py b/parse.py new file mode 100755 index 0000000..f2e81ba --- /dev/null +++ b/parse.py @@ -0,0 +1,144 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +import re, arm_translate, 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 = 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 = 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 = 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 = arm_translate.makeLdrOrStr(instr, code, rx, reg, offset, is_thumb, l.group()) + # to_write ... [reg, #d-new_regs_count*4] + + to_write.append((utils.getAddressFromLine(l.group()), len(code) // 2, 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 = [] + + # ldr/str/... rx, [sp], ry - не знаем значение ry, не можем сделать правильное смещение => не обрабатываем такие функции + register_relative = utils.searchRe('.*(ldr|str).*\[.*\], ', lines) + if len(register_relative) > 0: + return -1 + + + # не обрабатываем функции, в которых есть дополнительные push + more_pushes = utils.searchRe('.*\d}', lines) + if len(more_pushes) > 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 = utils.searchRe('.*(add|sub)(.w)?\s*sp(, sp)?, #[0-9]+', lines) + # если строки нет, выходим (потом подумать,как сделать) 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 = utils.searchRe('.*(ldr|str)(b|h|sb|sh|d)?(.w)?.*\[sp, #[0-9]+\].*', lines) + #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 = utils.getCodeFromLine(l.group()) + offset = changeOffset(b-a, b, table) + new_instr_code = arm_translate.makeLdrOrStr(instr,code , rx, 'sp', offset, is_thumb, l.group()) + # to_write ... [sp, #b + new_regs_count*4] + to_write.append((utils.getAddressFromLine(l.group()), len(code) // 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])) + add_sp_to_reg = utils.searchRe('.*(add(.w)?|mov)\s*(r[0-9]|r10|r11|r12), sp(, #[1-9]+)?.*', lines) + #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/parse_functions_utils.py b/parse_functions_utils.py new file mode 100644 index 0000000..06e44f2 --- /dev/null +++ b/parse_functions_utils.py @@ -0,0 +1,221 @@ +import re, os.path, cxxfilt, glob, codecs +from timeit import timeit +#java_dir = '/home/daria/Documents/android-ndk-master/hello-libs/app/src/main/java/' # todo set directory for java +java_dir = '/home/daria/Downloads/Telegram-FOSS-master/TMessagesProj/src/main/java/' +jni_dir = '/home/daria/Downloads/Telegram-FOSS-master/TMessagesProj/jni' +# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html +# http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2 +Java_types64 = ['long', 'double'] # 64 bits +#Java_types32 = ['int', 'byte', 'short', 'char', 'float', 'boolean'] #32 bits +C_types64 = ['long long', 'double', 'int64'] +C_types128 = ['long double', 'int128'] + +#for native functions declared in Java code +def getJNIfunctionType(JNI_function_name): + function_name = str(JNI_function_name) + if not function_name.startswith('Java'): + return -1 #Not right JNI function name + #ищем escaped символы + #if re.search('_[0-3]', function_name) is not None: + #todo заменить на исходные символы + + splitted_path = function_name.split('_') + short_func_name = splitted_path[-1] + path = java_dir+'/'.join(splitted_path[1:-1])+'.java' #remove Java and func name + if not os.path.isfile(path): + print('NO FILE {0}!'.format(path)) + return -1 #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('TYPE:'+func_declaration) + #type = re.search('{0}'.format('|'.join(small_Java_types.extend(big_Java_types))), func_declaration.group()) + type = func_declaration.group().split(' ')[-2] + return type + +def getCFunctionType(func_name): + #name = str(func_name) + #name_without_params = name.split('(')[0] + #print(name_without_params) + # if func_name.startswith('std::') or func_name.startswith('__gnu_cxx'): + # return Noneunc + #found_func = findFInFiles(name_without_params) + if func_name == '': # нет функции -> не можем определить тип ->None + return None + pattern = C_types128 + C_types64 + ['void'] + type = re.search('{0}'.format('|'.join(pattern)), func_name) + if type is None: #функция есть, но тип не 128 и не 64 и не void -> 32 + return '' + return type.group() + +#def getFunctionReturnTypeSize(function_name): + + # if function_name == '': + # return 4 #не знаем функцию -> максимальный размер + # name = str(function_name).split('@')[0] + # if name.startswith('Java'): + # return getTypeSize(getJNIfunctionType(name), True) + # if name.startswith('_ZN') and not name.startswith('_ZNSt'): + # name = cxxfilt.demangle(name) + # return(getTypeSize(getCFunctionType(name))) + #print("FOUND: {0} : {1}".format(name,findFuncInFiles(name))) + +def getTypeSize(type, isJNI): + if type == None: + return 4 + if type == 'void': + return 0 + if isJNI and type in Java_types64 or not isJNI and type in C_types64: + return 2 + if not isJNI and type in C_types128: + return 4 + return 1 + + +def getFunctionsReturnTypeSize(functions): + + #function_types = dict() + function_types = dict.fromkeys(functions.keys(), '') #адрес - найденнная функция + + functions = dict(functions) # адрес - функция + + # обрабатываем JNI функции + Java_functions = dict((address, func) for address, func in functions.items() + if func.startswith('Java')) + for address, function in Java_functions.items(): + function_types[address] = getJNIfunctionType(function) + + # отпределяем размер для JNI + return_sizes = dict() + for address, func in function_types.items(): + if func!='': + return_sizes[address] = getTypeSize(func, True) + + # обрабатываем C фукнции + C_functions = dict((address, func) for address, func in functions.items() + if not func.startswith('Java') and not func == '') + + # demangle mangled functions + for address, function in C_functions.items(): + if function.startswith('_Z'): + C_functions[address] = cxxfilt.demangle(function) + + # ищем определение функций в файлах + c_found_funcs = findFunctionsInFiles(C_functions) #находим С-функции в h/c(pp) файлах + for address, function in c_found_funcs.items(): + # убираем параметры + function_types[address] = getCFunctionType(function.split('(')[0]) + + for address, func in function_types.items(): + return_sizes[address] = getTypeSize(func, False) + return return_sizes + + + +def searchInFile(patterns_dict,func_dict, file): + results = dict((key, '') for key in patterns_dict.keys()) #address - pattern + with codecs.open(file, "r", encoding='utf-8', errors='ignore') as f: + data = f.read() + for address, func in func_dict.items(): + if func.split('(')[0] in data: + result = re.search(patterns_dict[address], data) + results[address] = result.group() if result is not None else '' + results = dict((key, value) for key, value in results.items() if value!='') + return results + +types_equals = {'uint32': 'unsigned int', + 'uint64': 'unsigned long long', + 'uint32_t': 'unsigned int', + 'uint64_t': 'unsigned long long', + 'int32': 'int', + 'int64': 'long long', + 'int32_t': 'int', + 'int64_t': 'long long' + } + +types_patterns = {'int':'(int32(_t)?)|(j?int)', + 'unsigned int':'uint32(_t)?', + 'long long': '(int64(_t)?)|(j?long( long)?)', + 'unsigned long long': 'uint64(_t)?', + 'unsigned char': 'jboolean|(uint8(_t)?)|bool', + 'signed char': '(j?byte)|int8(_t)?', + 'short': '(j?short)|int16(_t)?', + 'float': 'j?float' + } + +#открываем файл, ищем все функции +def findFunctionsInFiles(functions): + # functions = address:functions + f_backup = functions.copy() + result = dict((key,'') for key, value in functions.items()) # адрес - найденная функция + + patterns = dict() + #выделяем типы входных параметров + for address, func in functions.items(): + params = re.search('\((.|\n)*\)', func) + #params_regex = '(.|\n)*' + #params_regex = '[^;\)]* + params_regex = '[^;]*' + if address == '14ef8c': + 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() + 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('*') #запоминаем 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 + params_list[i] = p.replace('\\*', '\\s*\\*\\s*').replace('\\&', '\\s*\\&\\s*') + + params_regex = '.*,\\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}\({1}.*\)(\s[a-zA_Z_]+)?\s' + .format(re.escape(func.split('(')[0]), params_regex) + +'*;', re.MULTILINE) + + + patterns[address] = result_pattern + + #patterns = dict((address, '\n.*{0}(.*);'.format(re.escape(func))) + #for address, func in functions.items()) + def find(path): + p = dict((address, pattern) for address,pattern in patterns.items()) + for file in glob.iglob(path, recursive=True): + print(file) + found_func = searchInFile(p, functions, file) + result.update(found_func) + #p = dict((key, value) for key, value in patterns.items() if result[key] == '') + for f in found_func.keys(): + functions.pop(f) + p.pop(f) + return p + + + patterns = find(jni_dir+'/**/*.h') + patterns = dict((address, re.compile(p.pattern[:-1]+re.escape('{'), re.MULTILINE)) + for address,p in patterns.items()) + jni_res = dict((addr, f) for addr, f in result.items() if f!='') + result.clear() + # + patterns = find(jni_dir+'/**/*.c*') + + return result \ No newline at end of file diff --git a/parse_one_r.py b/parse_one_r.py new file mode 100755 index 0000000..22d6ab4 --- /dev/null +++ b/parse_one_r.py @@ -0,0 +1,77 @@ +import re, arm_translate, utils + +def getAllSpLinesForLow(lines, before_reg_count, after_reg_count, real_reg_count): + to_write = [] + + # ищем строки 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, #[0-9]+', line) for line in lines])) + + # если строки нет, выходим (потом подумать,как сделать) todo + if len(sub_add_sp_lines) !=2: #не нашли sub и add + return [] + + a = int(sub_add_sp_lines[0].group().split('#')[-1]) + + #ищем строки вида [sp, #b] + use_sp_lines = list(filter(None,[re.search('.*(ldr|str)(b|h|sb|sh)?(.w)?.*\[sp, #[0-9]+\].*', line) for line in lines])) + + #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)?(.w)?',l.group()).group() + b = int(re.search('#[0-9]+', l.group()).group().replace('#','')) + # если b-a >= -new_regs_count*4 => [sp, #b + new_regs_count*4] => to_write - иначе ничего + 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 = utils.getCodeFromLine(l.group()) + if b-a < real_reg_count * 4: + offset = b + before_reg_count*4 + if b - a >= real_reg_count * 4: + offset = b + before_reg_count*4 + after_reg_count*4 + new_instr_code = arm_translate.makeLdrOrStr(instr,code , rx, 'sp', offset, is_thumb) + # to_write ... [sp, #b + new_regs_count*4] + to_write.append((utils.getAddressFromLine(l.group()), len(code) // 2, utils.toLittleEndian(new_instr_code))) + + #ищем строки вида add rx, sp, (#c) - должна быть одна ? todo + add_sp_to_reg = list(filter(None, [re.search('.*(add|mov)\s*(r[1-9]|r10|r11|r12), sp(, #[1-9]+)?.*', line) for line in lines])) + + #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: + if len(add_sp_to_reg) > 1: + print ('Больше одной строки add rx, sp, (#c)') + line = add_sp_to_reg[0].group() + c = re.search('#[1-9]+', line) + c = 0 if c is None else int(c.group()[1:]) + reg = re.search('r11|r10|r12|r[1-9]',line).group() + + # Ищем строки вида [reg, #d] + use_reg = list(filter(None, [re.search('.*(ldr|str)(b|h|sb|sh|sw)?(.w)?.*\\[{0}, #\\-?[0-9]+\\]'.format(reg), line) for line in lines])) + for l in use_reg: + instr = re.search('v?(ldr|str)(b|h|sb|sh)?(.w)?',l.group()).group() + d = int(re.search('#-?[0-9]+', l.group()).group().replace('#','')) + # если d < 0 => если c-d<0 =>[reg, #d-new_regs_count*4] + if c + d >= 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 = utils.getCodeFromLine(l.group()) + if c+d < real_reg_count * 4: + offset = d + before_reg_count * 4 + if c+d >= real_reg_count * 4: + offset = d + before_reg_count * 4 + after_reg_count * 4 + new_instr_code = arm_translate.makeLdrOrStr(instr, code, rx, reg, offset, is_thumb) + #to_write ... [reg, #d-new_regs_count*4] + to_write.append((utils.getAddressFromLine(l.group()), len(code) // 2, utils.toLittleEndian(new_instr_code))) + + #for i in to_write: + #print(i) + return to_write \ No newline at end of file diff --git a/parse_only_low.py b/parse_only_low.py new file mode 100755 index 0000000..bf203db --- /dev/null +++ b/parse_only_low.py @@ -0,0 +1,70 @@ +import re, arm_translate, utils + +def getAllSpLinesForLow(lines, new_regs_count): + to_write = [] + + # ищем строки 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, #[0-9]+', line) for line in lines])) + + # если строки нет, выходим (потом подумать,как сделать) todo + if len(sub_add_sp_lines) !=2: #не нашли sub и add + return [] + + a = int(sub_add_sp_lines[0].group().split('#')[-1]) + # new_a = int(a)+new_regs_count*4 + # + # #вычитаем new_regs_count*4 из sub + # sub_instr = arm_translate.subSpToCode(new_a) + # sub_line = sub_add_sp_lines[0].group() + # sub_address = utils.getAddressFromLine(sub_line) + # sub_code = utils.getCodeFromLine(sub_line) + # to_write.append((sub_address, len(sub_code) // 2, utils.toLittleEndian(sub_instr))) + # + # # добавляем new_regs_count*4 в add + # add_instr = arm_translate.addSpToCode(new_a) + # add_line = sub_add_sp_lines[1].group() + # add_address = utils.getAddressFromLine(add_line) + # add_code = utils.getCodeFromLine(add_line) + # to_write.append((add_address, len(add_code) // 2, utils.toLittleEndian(add_instr))) + + #ищем строки вида [sp, #b] + use_sp_lines = list(filter(None,[re.search('.*(ldr|str)(b|h|sb|sh)?(.w)?.*\[sp, #[0-9]+\]', line) for line in lines])) + for l in use_sp_lines: + instr = re.search('v?(ldr|str)(b|h|sb|sh)?(.w)?',l.group()).group() + b = int(re.search('#[0-9]+', l.group()).group().replace('#','')) + # если b-a >= -new_regs_count*4 => [sp, #b + new_regs_count*4] => to_write - иначе ничего + if b >= a: + 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 = utils.getCodeFromLine(l.group()) + new_instr_code = arm_translate.makeLdrOrStr(instr,code , rx, 'sp', b+new_regs_count*4, is_thumb) + # to_write ... [sp, #b + new_regs_count*4] + to_write.append((utils.getAddressFromLine(l.group()), len(code) // 2, utils.toLittleEndian(new_instr_code))) + + #ищем строки вида add rx, sp, (#c) - должна быть одна ? todo + add_sp_to_reg = list(filter(None, [re.search('.*(add|mov)\s*(r[1-9]|r10|r11|r12), sp(, #[1-9]+)?', line) for line in lines])) + if len(add_sp_to_reg) > 0: + if len(add_sp_to_reg) > 1: + #raise Exception('Больше одной строки add rx, sp, (#c)') + return [] + line = add_sp_to_reg[0].group() + c = re.search('#[1-9]+', line) + c = 0 if c is None else int(c.group()[1:]) + reg = re.search('r[1-9]|r11|r10|r12',line).group() + + # Ищем строки вида [reg, #-d] + use_reg = list(filter(None, [re.search('.*(ldr|str)(b|h|sb|sh|sw)?(.w)?.*\\[{0}, #\\-?[0-9]+\\]'.format(reg), line) for line in lines])) + for l in use_reg: + instr = re.search('v?(ldr|str)(b|h|sb|sh)?(.w)?',l.group()).group() + d = int(re.search('#-?[0-9]+', l.group()).group().replace('#','')) + # если d < 0 => если c-d<0 =>[reg, #d-new_regs_count*4] + if c + d >= 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 = utils.getCodeFromLine(l.group()) + new_instr_code = arm_translate.makeLdrOrStr(instr, code, rx, reg, d + new_regs_count * 4, is_thumb) + #to_write ... [reg, #d-new_regs_count*4] + to_write.append((utils.getAddressFromLine(l.group()), len(code) // 2, utils.toLittleEndian(new_instr_code))) + + #for i in to_write: + #print(i) + return to_write \ No newline at end of file diff --git a/release.keystore b/release.keystore new file mode 100755 index 0000000..a6ff608 Binary files /dev/null and b/release.keystore differ diff --git a/run.py b/run.py new file mode 100755 index 0000000..86a8048 --- /dev/null +++ b/run.py @@ -0,0 +1,20 @@ +from os import listdir +from os.path import isfile, join +import main_insert_border_regs, sys + +#path = 'data/com.instagram.android/lib/armeabi-v7a/' +#path = 'data/noassertTel/lib/armeabi-v7a/' +path = 'apps/telegram/lib/armeabi-v7a/' + +#path = sys.argv[1] #путь к папке todo uncomment +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!!! + +#todo debug only +#start_group = 2314 +#end_group = 2315 + +files = [f for f in listdir(path) if isfile(join(path, f)) and f.endswith('_old.so')] +for file in files: + print (file[:-7]) + main_insert_border_regs.run(join(path, file)[:-7], int(start_group), int(end_group)) \ No newline at end of file diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..2de32c1 --- /dev/null +++ b/run.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# -*- coding: utf-8 -*- + + +# 1 - +python3 ../run.py ${1%.apk}/lib/armeabi-v7a/ \ No newline at end of file diff --git a/scripts/apktool-1.4.1.jar b/scripts/apktool-1.4.1.jar new file mode 100755 index 0000000..de24750 Binary files /dev/null and b/scripts/apktool-1.4.1.jar differ diff --git a/scripts/apktool.jar b/scripts/apktool.jar new file mode 100755 index 0000000..17feaf0 Binary files /dev/null and b/scripts/apktool.jar differ diff --git a/scripts/run.sh b/scripts/run.sh new file mode 100644 index 0000000..24816b5 --- /dev/null +++ b/scripts/run.sh @@ -0,0 +1,11 @@ +#!/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 + +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 +adb shell pm uninstall -k org.telegram.messenger.beta +adb install $2 +spd-say done + diff --git a/scripts/unpack.sh b/scripts/unpack.sh new file mode 100644 index 0000000..5fa5ab1 --- /dev/null +++ b/scripts/unpack.sh @@ -0,0 +1,20 @@ +#!/bin/bash +echo 'start unpacking' +echo $1 +java -jar apktool.jar d -f -r -s $1 +echo 'unpacking done' +echo 'prepare folder' +FILES=${1%.apk}/lib/armeabi-v7a/*.so + +echo ${1%.apk}/lib/armeabi-v7a + +echo $path + +for f in $FILES + +do + +arm-none-eabi-objdump -d -m arm -M reg-names-std $f > ${f%.so}.txt +mv $f ${f%.so}_old.so + +done diff --git a/utils.py b/utils.py new file mode 100755 index 0000000..8d8703f --- /dev/null +++ b/utils.py @@ -0,0 +1,123 @@ +# -*- 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 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].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