Mercurial > paraspace
changeset 104:61cef1662035
Redirect types
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Thu, 28 Jul 2011 00:06:54 +0800 |
parents | 8a53e6f7f517 |
children | f14c32108164 |
files | paraspace/dalvik_opcodes.py paraspace/dexfile.py paraspace/injection.py paraspace/tests/dexfile_test.py |
diffstat | 4 files changed, 200 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/paraspace/dalvik_opcodes.py Wed Jul 27 12:09:19 2011 +0800 +++ b/paraspace/dalvik_opcodes.py Thu Jul 28 00:06:54 2011 +0800 @@ -1270,6 +1270,13 @@ return decoded_insns +## \brief Encode a list of opcode vectors to a instruction block. +def encode_opcode_vectors(op_vectors): + insns = [encode_inst(op_vector) for op_vector in op_vectors] + insns_blk = ''.join(insns) + return insns_blk + + del name del _names del _opcode
--- a/paraspace/dexfile.py Wed Jul 27 12:09:19 2011 +0800 +++ b/paraspace/dexfile.py Thu Jul 28 00:06:54 2011 +0800 @@ -1031,7 +1031,7 @@ class _DEX_TypeList(composite): num = uint32 - typeItems = array('num', uint16) + typeItems = array('num', depend_idx('DEXFile.typeIds')(uint16)) child_names = 'num typeItems'.split() pass @@ -1693,7 +1693,29 @@ ## \brief Return name string of a linked class definition item @staticmethod def get_classdef_name(classdef): - return classdef.classIdx.descriptorIdx.stringDataOff.data.data + return DEXFile_linked.get_typeid_name(classdef.classIdx) + + ## \brief Return name string of a linked type ID item. + @staticmethod + def get_typeid_name(typeid): + return typeid.descriptorIdx.stringDataOff.data.data + + ## \brief Get index of given type ID. + def get_idx_typeid(self, typeid): + return self.typeIds.items.index(typeid) + + ## \brief Find type ID item with given name. + def find_typeid_name(self, name): + for typeid in self.typeIds.items: + typeid_name = DEXFile_linked.get_typeid_name(typeid) + if typeid_name == name: + return typeid + pass + pass + + ## \brief Return type ID item with given index. + def find_typeid_idx(self, idx): + return self.typeIds.items[idx] def find_class_name(self, name): for classdef in self.classDefs.items: @@ -1703,6 +1725,15 @@ pass raise ValueError, 'can not find class definition for \'%s\'' % (name) + ## \brief Return a class definition corresponding for give type ID. + def find_class_typeid(self, typeid): + for classdef in self.classDefs.items: + if classdef.classIdx == typeid: + return classdef + pass + raise ValueError, \ + 'can not find class definition for typeid %s' % (repr(typeid)) + ## \brief Update size of map items. # # Corresponding data lists of maps may be changed, it should be updated @@ -1769,6 +1800,61 @@ return wmethod pass pass + + ## \brief Get name of given method ID. + @staticmethod + def get_methodid_name(methoid): + return methoid.nameIdx.stringDataOff.data.data + + ## \brief Find the method ID item of given index. + def find_methodid_idx(self, idx): + methodid = self.methodIds.items[idx] + return methodid + + ## \brief Find a method definition with an index to method ID. + def find_method_idx(self, idx): + methodid = self.find_methodid_idx(idx) + method_name = DEXFile_linked.get_methoid_name(methodid) + method_proto = methodid.protoIdx + method_typeid = methodid.classIdx + classdef = self.find_class_typeid(method_typeid) + + method = self.find_method_name_proto(method, method_proto, classdef) + + return method + + ## \brief Test if prototype of two methods are compatible. + @staticmethod + def _proto_is_compatible(proto1, proto2): + if proto1.returnTypeIdx != proto2.returnTypeIdx: + return False + typelist1 = proto1.parametersOffRef.value + typelist2 = proto2.parametersOffRef.value + if len(typelist1.typeItems.items) != len(typelist2.typeItems.items): + return False + + for typeid1, typeid2 in map(None, + typelist1.typeItems.items, + typelist2.typeItems.items): + if typeid1 != typeid2: + return False + pass + return True + + def find_method_name_proto(self, method_name, proto, classdef): + if not classdef.classDataOffRef.is_true: + return + + classdata = classdef.classDataOffRef.value + for wmethod in classdata.directMethods.items + \ + classdata.virtualMethods.items: + wmethod_name = DEXFile_linked.get_method_name(wmethod) + if method_name != wmethod_name: + continue + if DEXFile_linked._proto_is_compatible(wmethod.protoIdx, proto): + return wmethod + pass + raise ValueError, 'can not find a method for given name and prototype' pass
--- a/paraspace/injection.py Wed Jul 27 12:09:19 2011 +0800 +++ b/paraspace/injection.py Thu Jul 28 00:06:54 2011 +0800 @@ -209,3 +209,79 @@ def dexfile_insert_class(dex, class_def): clone = _clone_classdef(dex, class_def) return clone + + +def method_redirect_types(dex, method, redirect_map): + from paraspace.dalvik_opcodes import decode_insn_blk, all_opcodes + from paraspace.dalvik_opcodes import encode_opcode_vectors + from paraspace.dexfile import DEXFile_linked + + if not method.codeOffRef.is_true: + return + + code = method.codeOffRef.value + insns_blk = code.insns.data + op_vectors = decode_insn_blk(insns_blk) + + def redirect(opcode, args): + if opcode == all_opcodes.OP_NEW_INSTANCE: + typeidx = args[1] + if typeidx in redirect_map: + to_type = redirect_map[typeidx] + return opcode, (args[0], to_type) + pass + elif opcode == all_opcodes.OP_INVOKE_DIRECT: + methodidx = args[2] + methodid = dex.find_methodid_idx(methodidx) + method_typeid = methodid.classIdx + method_typeidx = dex.get_idx_typeid(method_typeidx) + if method_typeidx not in redirect_map: + return opcode, args + + new_method_typeidx = redirect_map[method_typeidx] + new_method_typeid = dex.find_typeid_idx(new_method_typeidx) + classdef = dex.find_class_typeid(new_method_typeid) + method_name = DEXFile_linked.get_methodid_name(methodid) + method_proto = methodid.protoIdx + + try: + new_method = dex.find_method_name_proto(method_name, + method_proto, + classdef) + except: + return opcode, args + new_method_idx = dex.get_index_method(new_method) + return opcode, (args[0], args[1], new_method_idx, + args[3], args[4], args[5], args[6]) + return opcode, args + + new_op_vectors = [redirect(opcode, args) for opcode, args in op_vectors] + new_insns_blk = encode_opcode_vectors(new_op_vectors) + + code.insns.data = new_insns_blk + pass + + +def class_redirect_types(dex, classdef, redirect_map): + if not classdef.classDataOffRef.is_true: + return + + classdata = classdef.classDataOffRef.value + for method in classdata.directMethods.items: + method_redirect_types(dex, method, redirect_map) + pass + for method in classdata.virtualMethods.items: + method_redirect_types(dex, method, redirect_map) + pass + pass + + +def dexfile_redirect_types(dex, redirect_map, excludes=set([])): + for classdef in dex.classDefs.items: + typeid = classdef.classIdx + idx = dex.get_index_typeid(typeid) + if idx in excludes: + continue + class_redirect_types(dex, classdef, redirect_map) + pass + pass
--- a/paraspace/tests/dexfile_test.py Wed Jul 27 12:09:19 2011 +0800 +++ b/paraspace/tests/dexfile_test.py Thu Jul 28 00:06:54 2011 +0800 @@ -401,3 +401,32 @@ method_name = dexfile.DEXFile_linked.get_method_name(fakefile_cstr) assert method_name == '<init>' pass + + +def find_typeid_test(): + from paraspace.dex_deptracker import prepare_dep_decls + + _install_dexfile_4_deptracker() + + all_dep_decls = prepare_dep_decls() + + srcdir = os.path.dirname(__file__) + srcroot = os.path.join(srcdir, '..', '..') + + fakefile_fn = os.path.join(srcroot, 'data', 'fakefile.dex') + fakefile_dex = dexfile.DEXFile.open(fakefile_fn) + fakefile_linked = \ + dexfile.DEXFile_linked. \ + build_dependencies(fakefile_dex, all_dep_decls) + + File_typeid = fakefile_linked.find_typeid_name('Ljava/io/File;') + assert File_typeid + + File_name = dexfile.DEXFile_linked.get_typeid_name(File_typeid) + assert File_name == 'Ljava/io/File;' + + idx = fakefile_linked.get_idx_typeid(File_typeid) + assert idx >= 0 + File_typeid_idx = fakefile_linked.find_typeid_idx(idx) + assert File_typeid == File_typeid_idx + pass