Skip to content

Commit

Permalink
Update to latest
Browse files Browse the repository at this point in the history
  • Loading branch information
DoroninaD committed Oct 15, 2017
1 parent ff932e1 commit 4ad527a
Show file tree
Hide file tree
Showing 8 changed files with 432 additions and 141 deletions.
4 changes: 2 additions & 2 deletions config.ini
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@

[app_source]
# путь к папке, в которой содержатся все нативные файлы (.cpp, .c, .h)
native_directory = /home/daria/Desktop/hello-libs (copy)/app/src/main/cpp/
native_directory = /home/daria/Desktop/bionic/app/src/main/cpp/
# путь к папке, в которой содержатся все java файлы (.java)
java_directory = /home/daria/Desktop/hello-libs (copy)/app/src/main/java/
java_directory = /home/daria/Desktop/bionic/app/src/main/java/
# путь к папке с декомпилированным приложением (unpack.h), в котором все статики убраны
nonstatic_app_directory = /home/daria/Documents/dimplom/apps/nonstatic_orig/lib/armeabi-v7a/

Expand Down
11 changes: 6 additions & 5 deletions config_parser.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import configparser, os

config_path = os.path.join(os.path.dirname(__file__),'config.ini')
#config_path = os.path.join(os.path.dirname(__file__),'config.ini')

class ConfigParser:

def __init__(self):
if not os.path.isfile(config_path):
raise Exception('No config.ini at {0}!'.format(config_path))
def __init__(self, path):
self.config_path = path
if not os.path.isfile(self.config_path):
raise Exception('No config.ini at {0}!'.format(self.config_path))
self.config = configparser.ConfigParser()
self.config.read(config_path)
self.config.read(self.config_path)

def get(self, property):
return self.config.get('app_source',property)
Expand Down
330 changes: 277 additions & 53 deletions main_insert_border_regs.py

Large diffs are not rendered by default.

146 changes: 75 additions & 71 deletions parse_functions_utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import re, os.path, cxxfilt, glob, codecs, config_parser
from timeit import timeit

configParser = config_parser.ConfigParser()

java_dir = configParser.get('java_directory') # todo set directory for java
jni_dir = configParser.get('native_directory')
#jnih_path = '/home/daria/Android/Sdk/ndk-bundle/sysroot/usr/include/jni.h'
system_paths = configParser.getSection('system_directories')



# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html
Expand All @@ -16,12 +12,22 @@
void_type = ['void']
C_types32 = ['int32_t','int16_t', 'int8_t', 'int', 'unsigned',
'float', 'char', 'bool', 'long int', 'wchar_t',
'jbyte', 'jchar', 'jboolean', 'jshort', 'jint', 'jfloat'] #unsigned не учитываем
'jbyte', 'jchar', 'jboolean', 'jshort', 'jint', 'jfloat', 'size_t'] #unsigned не учитываем
jni_objects_types = ['jclass', 'jstring', 'jcharArray'] #todo add other Arrays
C_types64 = ['long long', 'double', 'int64_t', 'jlong', 'jdouble']
C_types128 = ['long double']
JNI_types = ['']

def init_config(config):
global java_dir
global jni_dir
global system_paths
configParser = config
java_dir = configParser.get('java_directory') # todo set directory for java
jni_dir = configParser.get('native_directory')
# jnih_path = '/home/daria/Android/Sdk/ndk-bundle/sysroot/usr/include/jni.h'
system_paths = configParser.getSection('system_directories')

#for native functions declared in Java code
def getJavafunctionType(JNI_function_name):
function_name = str(JNI_function_name)
Expand All @@ -47,13 +53,13 @@ def getJavafunctionType(JNI_function_name):

if not os.path.isfile(path):
print('NO FILE {0}!'.format(path))
return -1 #todo
return None #todo
with codecs.open(path, "r", encoding='utf-8', errors='ignore') as f:
data = f.read()
func_declaration = re.search('native .* {0}'.format(short_func_name), data)
if func_declaration is None:
print('NONE DECLARATION in {0}'.format(path))
return -1 #todo
print('NONE DECLARATION in {0} for {1}'.format(path, short_func_name))
return None #todo
# все типы односложные, нет указателей
type = func_declaration.group().split('(')[0] #убираем параметры
type = type.split(' ')[-2]
Expand All @@ -80,6 +86,8 @@ def getTypeSize(type, isJNI):
return 4
if isJNI and type in Java_types64 or not isJNI and type in C_types64:
return 2
if type !='':
aaa = 1
if '*' in type or '[]' in type \
or isJNI and type in Java_types32 \
or not isJNI and type in C_types32\
Expand All @@ -91,8 +99,8 @@ def getTypeSize(type, isJNI):
return 4


def getFunctionsReturnTypeSize(functions):

def getFunctionsReturnTypeSize(functions, config):
init_config(config)
#function_types = dict()
function_types = dict.fromkeys(functions.keys(), '') #адрес - найденнная функция

Expand All @@ -118,11 +126,8 @@ def getFunctionsReturnTypeSize(functions):

# demangle mangled functions
for address, function in C_functions.items():
if function.startswith('_Z'):
n = cxxfilt.demangle(function)
if n.startswith('_JNIEnv'):
n = n[9:]
C_functions[address] = n
C_functions[address] = demangleNativeFunctionName(function)



# отдельно выносим функции из jni.h
Expand All @@ -142,23 +147,24 @@ def getFunctionsReturnTypeSize(functions):

for address, func in function_types.items():
if address not in return_sizes:
if address == '362c':
if func==-1:
aaa=1
return_sizes[address] = getTypeSize(func, False)
print('4 bytes: ', len([f for f in return_sizes if return_sizes[f]==4]))
print('2 bytes: ', len([f for f in return_sizes if return_sizes[f]==2]))
print('1 bytes: ', len([f for f in return_sizes if return_sizes[f]==1]))
print('0 bytes: ', len([f for f in return_sizes if return_sizes[f]==0]))
# print('4 bytes: ', len([f for f in return_sizes if return_sizes[f]==4]))
# print('2 bytes: ', len([f for f in return_sizes if return_sizes[f]==2]))
# print('1 bytes: ', len([f for f in return_sizes if return_sizes[f]==1]))
# print('0 bytes: ', len([f for f in return_sizes if return_sizes[f]==0]))

notfound = dict((f, backup[f]) for f in return_sizes if return_sizes[f]==4)
return return_sizes

# def getJNIFunctionsTypes(functions):
# with codecs.open(jnih_path, "r", encoding='utf-8', errors='ignore') as f:
# data = f.read()
# for func in functions:
# func_declaration = re.search('native .* {0}'.format(func), data)
#
def demangleNativeFunctionName(function):
# demangle mangled functions
if function.startswith('_Z'):
function = cxxfilt.demangle(function)
if function.startswith('_JNIEnv'):
function = function[9:]
return function


def searchInFile(patterns_dict,func_dict, file):
Expand Down Expand Up @@ -194,6 +200,47 @@ def searchInFile(patterns_dict,func_dict, file):
'double': 'jdouble',
}


#pattern for C/C++ functions
#for jni functions in native code no need to think about params (they are in the name already)
def makePattern(func):
params = re.search('\((.|\n)*\)', func)
params_regex = '[^;]*'
if params is not None: # есть параметры
params_list = params.group()[1:-1].split(',')
# tmp = params_list.copy()
for i in range(len(params_list)):
p = params_list[i].strip()
p = p.replace(' const', '') # const меняет место, уберем его
if p.startswith('_j') and p[-1] == '*': # _jobject*->jobject
p = p[1:-1]
if p.startswith('_J'):
p = p[1:]
non_escaped = p.strip('&').strip('*').strip(' ') # запоминаем nonescaped параметры
p = re.escape(p) # escape
# todo unsigned int -> unsigned// int
if non_escaped in types_patterns:
p = p.replace(p, types_patterns[non_escaped])
# p = '\s*j?'+p #для jni
# todo make this regexp more effetive!!!
p = '(\\s*(const)?\\s*' + p + '\\s*(const)?\\s*)'
params_list[i] = p.replace('\\*', '\\s*\\*\\s*').replace('\\&', '\\s*\\&\\s*')

# params_regex = '(.|\n)*,(const)?\\s*(const)?'.join(params_list) #todo escape?
params_regex = '(.|\n)*\\s*'.join(params_list) # todo escape?

# для jni и void* -> void *
params_regex = params_regex \
.replace('_J', 'J').replace('_j', 'j')

result_pattern = re.compile('\n\s*([a-zA-Z0-9_\"\*\[\]]+\s+){0,3}'
+ '\*?{0}\s*\({1}.*\)(\s[a-zA_Z_]+)?\s'
.format(re.escape(func.split('(')[0]), params_regex)
+ '*(;|{)', re.MULTILINE)
return result_pattern



#открываем файл, ищем все функции
def findFunctionsInFiles(functions):
# functions = address:functions
Expand All @@ -203,50 +250,7 @@ def findFunctionsInFiles(functions):
patterns = dict()
#выделяем типы входных параметров
for address, func in functions.items():
params = re.search('\((.|\n)*\)', func)
#params_regex = '(.|\n)*'
#params_regex = '[^;\)]*
params_regex = '[^;]*'
if address == '362c':
aaa=1
if params is not None: # есть параметры
params_list = params.group()[1:-1].split(',')
#tmp = params_list.copy()
for i in range(len(params_list)):
p = params_list[i].strip()
p = p.replace(' const', '') # const меняет место, уберем его
if p.startswith('_j') and p[-1]=='*': #_jobject*->jobject
p = p[1:-1]
if p.startswith('_J'):
p = p[1:]
non_escaped = p.strip('&').strip('*').strip(' ') #запоминаем nonescaped параметры
p = re.escape(p) #escape
#todo unsigned int -> unsigned// int
if non_escaped in types_patterns:
p = p.replace(p, types_patterns[non_escaped])
#p = '\s*j?'+p #для jni
#todo make this regexp more effetive!!!
p = '(\\s*(const)?\\s*' + p + '\\s*(const)?\\s*)'
params_list[i] = p.replace('\\*', '\\s*\\*\\s*').replace('\\&', '\\s*\\&\\s*')

#params_regex = '(.|\n)*,(const)?\\s*(const)?'.join(params_list) #todo escape?
params_regex = '(.|\n)*\\s*'.join(params_list) #todo escape?

# для jni и void* -> void *
params_regex= params_regex\
.replace('_J', 'J').replace('_j', 'j')

# result_pattern = re.compile('\n.*([a-zA-Z0-9_\*]+\s){0,3}'
# +'\*?{0}\({1}\)(\s[a-zA_Z_]+)?\s'
# .format(re.escape(func.split('(')[0]), params_regex)
# +'{0,};', re.MULTILINE)

result_pattern = re.compile('\n\s*([a-zA-Z0-9_\"\*\[\]]+\s+){0,3}'
+'\*?{0}\s*\({1}.*\)(\s[a-zA_Z_]+)?\s'
.format(re.escape(func.split('(')[0]), params_regex)
+'*(;|{)', re.MULTILINE)


result_pattern = makePattern(func)
patterns[address] = result_pattern

#patterns = dict((address, '\n.*{0}(.*);'.format(re.escape(func)))
Expand Down
47 changes: 47 additions & 0 deletions r2Helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from collections import namedtuple

import r2pipe, json

func_model = namedtuple('funcModel','address name rows')
opcode_model = namedtuple('opcodeModel','offset bytes type opcode size')


class r2Helper:
def __init__(self, filename):
self.r2 = r2pipe.open(filename)
self.cmd('e arm.lines=false arm.varsub=false arm.comments=false')
self.cmd('aaa')

def open(self,filename):
self.cmd('o--')
self.cmd('o {0}'.format(filename))
self.cmd('e arm.lines=false arm.varsub=false arm.comments=false')
self.cmd('aaa')

def cmd(self, s):
return self.r2.cmd(s)

def cmdJson(self, s, *args):
return json.loads(self.cmd(s))

def getFunctions(self):
funcList = self.cmdJson('aflj')
funcTypes = ['fcn','sub','aav'] #todo
return [self.getFuncInfo(f) for f in funcList if f['type'] in funcTypes]

def getFuncInfo(self,funcname):
func_info = self.cmdJson('pdfj @ {0}'.format(funcname['name']))
print(funcname['name'])
opcodes_info = func_info['ops']
opcodes = [opcode_model(oc['offset'], oc['bytes'], oc['type'], oc['opcode'], oc['size'])
for oc in opcodes_info
if self.valid(oc['type']) and self.valid(oc['opcode'])]
return func_model(func_info["addr"], func_info["name"], opcodes)

def valid(self, item):
return item!='invalid'


r2 = r2Helper('/home/daria/Documents/dimplom/apps/audio/lib/armeabi-v7a/libnative-audio-jni.so')
functions = r2.getFunctions()

29 changes: 22 additions & 7 deletions run.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
from os import listdir
from os.path import isfile, join
import main_insert_border_regs, sys, colored
import main_insert_border_regs, sys, colored, config_parser

#path = 'data/com.instagram.android/lib/armeabi-v7a/'
#path = 'data/noassertTel/lib/armeabi-v7a/'
#path = 'apps/telegram/lib/armeabi-v7a/'
#harded_path = 'apps/app-debug/lib/armeabi-v7a/'
harded_path = 'apps/test2/lib/armeabi-v7a/'
harded_path = 'apps/curl/lib/armeabi-v7a/'
DEBUG=1
path = sys.argv[1] if len(sys.argv) > 1 else harded_path
start_group = sys.argv[2] if len(sys.argv)>2 else 0
end_group = sys.argv[3] if len(sys.argv)>3 else -1 #todo LAST ELEMENT!!!
config_path = sys.argv[2] if len(sys.argv)>2 else 'config.ini'
start_group = sys.argv[3] if len(sys.argv)>3 else 0
end_group = sys.argv[4] if len(sys.argv)>4 else sys.maxsize #todo LAST ELEMENT!!!

#todo debug only
#start_group = 0
#end_group = 1000
# start_group = 387
# end_group = 388 #388
#2314

# 1-путь к либами,2-конфиг, 3-начало, 4-конец



files = [f for f in listdir(path) if isfile(join(path, f)) and f.endswith('_old.so')]
config = config_parser.ConfigParser(config_path)
for file in files:
# if file[:-7]=='libcurl':
# start_group = 0
# #end_group = 387
# end_group = -1
# else:
# start_group = 0
# end_group = 1


colored.printColored (file[:-7], colored.HEADER)
main_insert_border_regs.run(join(path, file)[:-7], int(start_group), int(end_group), DEBUG)
main_insert_border_regs.run(join(path, file)[:-7], int(start_group), int(end_group), DEBUG, config)
4 changes: 2 additions & 2 deletions scripts/run.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
# -*- coding: utf-8 -*-java -jar apktool.jar b -o $2 ${1%.apk}
# 1 = apk name, 2 = new apk name
/usr/bin/python3.5 ../run.py ${1%.apk}/lib/armeabi-v7a/ $3 $4
# 1 = apk name, 2 = new apk name, 3 = config.ini, 4 - start, 5 - end
/usr/bin/python3.5 ../run.py ${1%.apk}/lib/armeabi-v7a/ $3 $4 $5

java -jar apktool.jar b -o $2 ${1%.apk}
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore -storepass 111111 $2 alias_name
Expand Down
2 changes: 1 addition & 1 deletion utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def getRandomNumberOfItems(start, end):


def getAddressFromLine(line):
return line.split(':')[0].replace(' ', '')
return line.split(':')[0].split('<')[0].replace(' ', '')

def getCodeFromLine(line):
code = str(line.split('\t')[1].replace('\t', '')).strip()
Expand Down

0 comments on commit 4ad527a

Please sign in to comment.