# HG changeset patch # User Thinker K.F. Li # Date 1312208635 -28800 # Node ID 3820379b34e8495fd64a088ece081f139104356a # Parent 6380730a80b43e79069bfc3e375c28d0c6f3dd90 Add dexfile_insert_class_relative() diff -r 6380730a80b4 -r 3820379b34e8 paraspace/dexfile.py --- a/paraspace/dexfile.py Mon Aug 01 15:00:29 2011 +0800 +++ b/paraspace/dexfile.py Mon Aug 01 22:23:55 2011 +0800 @@ -1925,6 +1925,17 @@ classdata.virtualMethods.items return methods + ## \brief Find all method IDs that is part of given type. + # + # \param typeid is ID of type that IDs of its methods will be returned. + # \return a list of method IDs. + # + def find_methodids_typeid(self, typeid): + methodids = [methodid + for methodid in self.methodIds.items + if methodid.classIdx == typeid] + return methodids + ## \brief Dump content of a proto ID. @staticmethod def dump_protoid(protoid): diff -r 6380730a80b4 -r 3820379b34e8 paraspace/injection.py --- a/paraspace/injection.py Mon Aug 01 15:00:29 2011 +0800 +++ b/paraspace/injection.py Mon Aug 01 22:23:55 2011 +0800 @@ -94,12 +94,12 @@ pass -## \brief Clone a class definition item +## \brief Clone a composite object. # -# \param dex is the DEXFile that clazz is cloning for. -# \param clazz is the class definition item that is cloning. +# \param dex is the DEXFile that the composite object is cloning for. +# \param comobj is composite object that is cloning. # -def _clone_classdef(dex, clazz): +def _clone_composite(dex, comobj): from copy import copy from paraspace.dexfile import _DEX_StringDataItem, _DEX_StringId from paraspace.dexfile import _DEX_TypeId @@ -175,12 +175,7 @@ pass pass - def has_classdef(clazz): - class_typeIds = set([classdef.classIdx - for classdef in dex.classDefs.items]) - return clazz.classIdx in class_typeIds - - _travel_desc_relocatable(clazz, cloner, visit_log) + _travel_desc_relocatable(comobj, cloner, visit_log) merge_unique_strdata() merge_unique_strid() @@ -195,12 +190,35 @@ dex_append_obj_list(dex, obj) pass + clone = visit_log[id(comobj)] + return clone + + +## \brief Clone a class definition item +# +# \param dex is the DEXFile that clazz is cloning for. +# \param clazz is the class definition item that is cloning. +# \return the cloning _DEX_ClassDef. +# +def _clone_classdef(dex, clazz): + from paraspace.dexfile import DEXFile_linked, _DEX_ClassDef + + def has_classdef(clazz): + classname = DEXFile_linked.get_classdef_name(clazz) + try: + dex.find_class_name(classname) + except ValueError: + return False + return True + + assert isinstance(clazz, _DEX_ClassDef) + if has_classdef(clazz): raise RuntimeError, \ 'clone a class \'%s\'that is already in the DEXFile' % \ classdef_name(clazz) - clone = visit_log[id(clazz)] + clone = _clone_composite(dex, clazz) return clone @@ -212,11 +230,131 @@ # \param dex is a DEXFile_linked to insert the clone. # \param class_def is a class definition going to be cloned. # -def dexfile_insert_class(dex, class_def): - clone = _clone_classdef(dex, class_def) +def dexfile_insert_class(dex, classdef): + clone = _clone_classdef(dex, classdef) return clone +## \brief Collect info of classes mentioned by the code of given class. +def _find_class_relative(dex, classdef): + def classify_typeids_defined(dex, typeids): + classdefs = [] + undef_typeids = [] + for typeid in typeids: + try: + classdef = dex.find_class_typeid(typeid) + except ValueError: + undef_typeids.append(typeid) + else: + classdefs.append(classdef) + pass + pass + return classdefs, undef_typeids + + typeidxs = collect_typeidxs_mentioned_by_class(dex, classdef) + typeids = [dex.find_typeid_idx(typeidx) + for typeidx in typeidxs] + + classdefs, typeids = classify_typeids_defined(dex, typeids) + + return classdefs, typeids + + +def dexfile_insert_classdefs(dex_dst, dex_src, classdefs): + for classdef in classdefs: + dexfile_insert_class(dex_dst, classdef) + pass + pass + + +## \brief Clone and insert a _DEX_TypeId to another DEXFile_linked. +# +# \param dex_dst is a DEXFile_linked where the cloning one is inserted. +# \param dex_src is a DEXFile_linked where the cloned one is from. +# \param typeid is a _DEX_TypeId that is cloned. +# \return the cloning _DEX_TypeId. +# +def dexfile_insert_typeid(dex_dst, dex_src, typeid): + from paraspace.dexfile import _DEX_TypeId, DEXFile_linked + + assert isinstance(typeid, _DEX_TypeId) + + cloning = _clone_composite(dex_dst, typeid) + + methodids = dex_src.find_methodids_typeid(dex_src, typeid) + for methodid in methodids: + _clone_composite(dex_dst, methodid) + pass + + return cloning + + +## \brief Clone and insert a list of _DEX_TypeId objects to a DEXFile_linked. +def dexfile_insert_typeids(dex_dst, dex_src, typeids): + for typeid in typeids: + dexfile_insert_typeid(dex_dst, dex_src, typeid) + pass + pass + + +## \brief Collects relative type IDs and classes definition for given class. +def collect_classdef_relative(dex, classdef): + rel_classdefs = set([classdef]) + rel_typeids = set() + + classdef_queue = [classdef] + while classdef_queue: + cur_classdef = classdef_queue.pop(0) + + classdefs, typeids = _find_class_relative(dex, classdef) + rel_typeids.update(typeids) + new_classdefs = list(set(classdefs) - rel_classdefs) + classdef_queue = classdef_queue + new_classdefs + rel_classdefs.update(new_classdefs) + pass + return rel_classdefs, rel_typeids + + +## \brief Clone and insert given and relative classes into another DEXFile. +# +# \param dex_dst is a DEXFile_linked where the class will be inserted. +# \param dex_src is a DEXFile_linked where the cloned class is from. +# \param classdef is a _DEX_ClassDef that will be cloned. +# +def dexfile_insert_class_relative(dex_dst, dex_src, classdef): + from paraspace.dexfile import DEXFile_linked + + def classdef_not_in_dst(classdef): + classname = DEXFile_linked.get_classdef_name(classdef) + try: + dex_dst.find_class_name(classname) + except ValueError: + return True + return False + + def typeid_not_in_dst(typeid): + typename = DEXFile_linked.get_typeid_name(typeid) + try: + dex_dst.find_typeid_name(typename) + except ValueError: + return True + return False + + relative_classdefs, relative_typeids = \ + collect_classdef_relative(dex_src, classdef) + + inserting_classdefs = filter(classdef_not_in_dst, relative_classdefs) + inserting_typeids = filter(typeid_not_in_dst, relative_typeids) + + dexfile_insert_classdefs(dex_dst, dex_src, inserting_classdefs) + dexfile_insert_typeids(dex_dst, dex_src, inserting_typeids) + + classname = DEXFile_linked.get_classdef_name(classdef) + cloning = dex_dst.find_class_name(classname) + + return cloning + + ## \brief Redirect types and methods for the code of given method. def method_redirect_types(dex, method, types_redir, methods_redir): from paraspace.dalvik_opcodes import decode_insn_blk, all_opcodes diff -r 6380730a80b4 -r 3820379b34e8 paraspace/tests/injection_test.py --- a/paraspace/tests/injection_test.py Mon Aug 01 15:00:29 2011 +0800 +++ b/paraspace/tests/injection_test.py Mon Aug 01 22:23:55 2011 +0800 @@ -213,3 +213,76 @@ typeid_name = DEXFile_linked.get_typeid_name(typeid) assert typeid_name == 'Ljava/io/File;' pass + + +def dexfile_insert_class_relative_test(): + from paraspace.dex_deptracker import prepare_dep_decls + from paraspace.injection import dexfile_insert_class_relative + + _install_dexfile_4_deptracker() + + all_dep_decls = prepare_dep_decls() + + srcdir = os.path.dirname(__file__) + srcroot = os.path.join(srcdir, '..', '..') + + helloworld_fn = os.path.join(srcroot, 'data', 'helloworld.dex') + helloworld_dex = dexfile.DEXFile.open(helloworld_fn) + + classdef_map = _find_map(helloworld_dex, 0x0006) + saved_classdef_map_sz = classdef_map.size + saved_methodids_sz = len(helloworld_dex.methodIds.items) + + codeitems_map = _find_map(helloworld_dex, 0x2001) + saved_codeitems_sz = codeitems_map.size + + helloworld_linked = \ + dexfile.DEXFile_linked.build_dependencies(helloworld_dex, + all_dep_decls) + + 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) + + fakefile_def = fakefile_linked. \ + find_class_name('Lcom/codemud/fakefile/fakefile;') + + fakefile_data = fakefile_def.classDataOffRef.value + assert len(fakefile_data.directMethods.items) == 1 + assert len(fakefile_data.virtualMethods.items) == 0 + fakefile_dataheader = fakefile_data.header + assert fakefile_dataheader.directMethodsSize == 1 + assert fakefile_dataheader.virtualMethodsSize == 0 + + clone = dexfile_insert_class_relative(helloworld_linked, + fakefile_linked, fakefile_def) + assert clone + assert clone != fakefile_def + + helloworld_unlinked = helloworld_linked.get_unlinked() + assert helloworld_unlinked + + # map size for classdef must be increased by 1 + classdef_map = _find_map(helloworld_unlinked, 0x0006) + assert classdef_map.size == saved_classdef_map_sz + 1 + + classdata_map = _find_map(helloworld_unlinked, 0x2000) + assert classdata_map.size == classdef_map.size + + # Check strings + strdatas = helloworld_unlinked.stringDataItems.items + strs = sorted([strdata.data.data for strdata in strdatas]) + assert len(strs) == len(set(strs)) # uniquely + assert 'Lcom/codemud/fakefile/fakefile;' in strs + + # Check Method List + methodids_map = _find_map(helloworld_unlinked, 0x0005) # method ids + assert methodids_map.size == len(helloworld_unlinked.methodIds.items) + assert methodids_map.size == saved_methodids_sz + 1 + + # Check Code item List + codeitems_map = _find_map(helloworld_unlinked, 0x2001) + assert codeitems_map.size == saved_codeitems_sz + 1 + pass