changeset 152:bc213cb88636

Inject and redirect fields
author Thinker K.F. Li <thinker@codemud.net>
date Tue, 16 Aug 2011 18:10:11 +0800
parents 91fabeaffce8
children 9a1677d03417
files paraspace/dexfile.py paraspace/injection.py paraspace/tests/injection_test.py
diffstat 3 files changed, 169 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/paraspace/dexfile.py	Tue Aug 16 16:07:32 2011 +0800
+++ b/paraspace/dexfile.py	Tue Aug 16 18:10:11 2011 +0800
@@ -2358,6 +2358,10 @@
     ## \brief Return _DEX_StringId of given index.
     def find_strid_idx(self, idx):
         return self.stringIds.items[idx]
+
+    ## \brief Return _DEX_FieldId of given index.
+    def find_fieldid_idx(self, idx):
+        return self.fieldIds.items[idx]
     pass
 
 
--- a/paraspace/injection.py	Tue Aug 16 16:07:32 2011 +0800
+++ b/paraspace/injection.py	Tue Aug 16 18:10:11 2011 +0800
@@ -273,21 +273,25 @@
     classdefs = None
     typeids = None
     strids = None
+    fieldids = None
     
-    def __init__(self, classdefs, typeids, strids):
+    def __init__(self, classdefs, typeids, strids, fieldids):
         self.classdefs = set(classdefs)
         self.typeids = set(typeids)
         self.strids = set(strids)
+        self.fieldids = set(fieldids)
         pass
 
     @staticmethod
     def dup(src):
-        return relative_obj_set(src.classdefs, src.typeids, src.strids)
+        return relative_obj_set(src.classdefs, src.typeids,
+                                src.strids, str.fieldids)
 
     def update(self, new_data):
         self.classdefs.update(new_data.classdefs)
         self.typeids.update(new_data.typeids)
         self.strids.update(new_data.strids)
+        self.fieldids.update(new_data.fieldids)
         pass
     pass
 
@@ -297,10 +301,10 @@
 # \param method is a \ref _DEX_Method.
 # \return a list of type indices mentioned in the code.
 #
-def collect_typeidxs_stridxs_in_method(dex, method):
+def collect_typeidxs_stridxs_fieldidxs_in_method(dex, method):
     from paraspace.dexfile import _DEX_Method, DEXFile_linked
     from paraspace.dalvik_opcodes import decode_insn_blk, all_opcodes
-    from itertools import chain
+    from paraspace.dalvik_opcodes import all_opcodes
     
     assert isinstance(method, _DEX_Method)
 
@@ -344,36 +348,77 @@
         
         return None
 
+    field_opcodes = set([all_opcodes.OP_IGET,
+                         all_opcodes.OP_IGET_WIDE,
+                         all_opcodes.OP_IGET_OBJECT,
+                         all_opcodes.OP_IGET_BOOLEAN,
+                         all_opcodes.OP_IGET_BYTE,
+                         all_opcodes.OP_IGET_CHAR,
+                         all_opcodes.OP_IGET_SHORT,
+                         all_opcodes.OP_IPUT,
+                         all_opcodes.OP_IPUT_WIDE,
+                         all_opcodes.OP_IPUT_OBJECT,
+                         all_opcodes.OP_IPUT_BOOLEAN,
+                         all_opcodes.OP_IPUT_BYTE,
+                         all_opcodes.OP_IPUT_CHAR,
+                         all_opcodes.OP_IPUT_SHORT,
+                         all_opcodes.OP_SGET,
+                         all_opcodes.OP_SGET_WIDE,
+                         all_opcodes.OP_SGET_OBJECT,
+                         all_opcodes.OP_SGET_BOOLEAN,
+                         all_opcodes.OP_SGET_BYTE,
+                         all_opcodes.OP_SGET_CHAR,
+                         all_opcodes.OP_SGET_SHORT,
+                         all_opcodes.OP_SPUT,
+                         all_opcodes.OP_SPUT_WIDE,
+                         all_opcodes.OP_SPUT_OBJECT,
+                         all_opcodes.OP_SPUT_BOOLEAN,
+                         all_opcodes.OP_SPUT_BYTE,
+                         all_opcodes.OP_SPUT_CHAR,
+                         all_opcodes.OP_SPUT_SHORT])
+    def collect_fields_in_op_vector(op_vector):
+        code, args = op_vector
+        if code in field_opcodes:
+            fieldidx = args[2]
+            return fieldidx
+        pass
+
     code_blk = DEXFile_linked.get_code_block_method(method)
     op_vectors = decode_insn_blk(code_blk)
     types_insns = [collect_types_in_op_vector(op_vector)
                    for op_vector in op_vectors]
     typeidxs = [idx for idx in types_insns if idx is not None]
 
-    strs_insns = [collect_strings_in_op_vector(op_vectors)
-                  for op_vectors in op_vectors]
+    strs_insns = [collect_strings_in_op_vector(op_vector)
+                  for op_vector in op_vectors]
     stridxs = [idx for idx in strs_insns if idx is not None]
+
+    fields_insns = [collect_fields_in_op_vector(op_vector)
+                    for op_vector in op_vectors]
+    fieldidxs = [idx for idx in fields_insns if idx is not None]
     
-    return typeidxs, stridxs
+    return typeidxs, stridxs, fieldidxs
 
 
 ## \brief Collect all type and string indices mentioned by the class code.
-def collect_typeidxs_stridxs_mentioned_by_class(dex, classdef):
+def collect_typeidxs_stridxs_fieldidxs_mentioned_by_class(dex, classdef):
     from paraspace.dexfile import DEXFile_linked
 
     assert isinstance(dex, DEXFile_linked)
     
     typeidxs = set()
     stridxs = set()
+    fieldidxs = set()
     methods = DEXFile_linked.get_methods_classdef(classdef)
     for method in methods:
-        method_typeidxs, method_stridxs = \
-            collect_typeidxs_stridxs_in_method(dex, method)
+        method_typeidxs, method_stridxs, method_fieldidxs = \
+            collect_typeidxs_stridxs_fieldidxs_in_method(dex, method)
         typeidxs.update(method_typeidxs)
         stridxs.update(method_stridxs)
+        fieldidxs.update(method_fieldidxs)
         pass
     
-    return list(typeidxs), list(stridxs)
+    return list(typeidxs), list(stridxs), list(fieldidxs)
 
 
 ## \brief Collect info of classes mentioned by the code of given class.
@@ -392,15 +437,16 @@
             pass
         return classdefs, undef_typeids
 
-    typeidxs, stridxs = \
-        collect_typeidxs_stridxs_mentioned_by_class(dex, classdef)
+    typeidxs, stridxs, fieldidxs = \
+        collect_typeidxs_stridxs_fieldidxs_mentioned_by_class(dex, classdef)
     typeids = [dex.find_typeid_idx(typeidx)
                for typeidx in typeidxs]
     strids = [dex.find_strid_idx(idx) for idx in stridxs]
+    fieldids = [dex.find_fieldid_idx(idx) for idx in fieldidxs]
     
     classdefs, typeids = classify_typeids_defined(dex, typeids)
     
-    rel_set = classdef_rel_set(classdefs, typeids, strids)
+    rel_set = classdef_rel_set(classdefs, typeids, strids, fieldids)
     return rel_set
 
 
@@ -423,6 +469,19 @@
     return clones
 
 
+## \brief Insert _DEX_FieldId into another DEX.
+def dexfile_insert_fieldid(dex_dst, dex_src, fieldid):
+    clone = _clone_composite(dex_dst, fieldid)
+    return clone
+
+
+## \brief Insert _DEX_FieldIds into another DEX.
+def dexfile_insert_fieldids(dex_dst, dex_src, fieldids):
+    clones = [dexfile_insert_fieldid(dex_dst, dex_src, fieldid)
+              for fieldid in fieldids]
+    return clones
+
+
 ## \brief Clone and insert a _DEX_TypeId to another DEXFile_linked.
 #
 # \param dex_dst is a DEXFile_linked where the cloning one is inserted.
@@ -478,7 +537,7 @@
 
 ## \brief Collects relative type IDs and classes definition for given class.
 def collect_classdefs_relative(dex, classdefs):
-    rel_set = classdef_rel_set(classdefs, [], [])
+    rel_set = classdef_rel_set(classdefs, [], [], [])
     
     classdef_queue = list(classdefs)
     while classdef_queue:
@@ -524,15 +583,17 @@
         dexfile_insert_or_merge_typeids(dex_dst, dex_src, rel_set.typeids)
     cloning_strids = \
         dexfile_insert_stringids(dex_dst, dex_src, rel_set.strids)
+    cloning_fieldids = \
+        dexfile_insert_fieldids(dex_dst, dex_src, rel_set.fieldids)
 
     cloning = classdef_rel_set(cloning_classdefs, cloning_typeids,
-                               cloning_strids)
+                               cloning_strids, cloning_fieldids)
     return cloning
 
 
 ## \brief Redirect types, methods and strings for the code of given method.
 def method_redirect_indices(dex, method, typeidxs_redir, methods_redir,
-                            stridxs_redir):
+                            stridxs_redir, fieldidxs_redir):
     from paraspace.dalvik_opcodes import decode_insn_blk, all_opcodes
     from paraspace.dalvik_opcodes import encode_opcode_vectors
     from paraspace.dexfile import DEXFile_linked
@@ -544,6 +605,34 @@
     insns_blk = code.insns.data
     op_vectors = decode_insn_blk(insns_blk)
 
+    field_opcodes = set([all_opcodes.OP_IGET,
+                         all_opcodes.OP_IGET_WIDE,
+                         all_opcodes.OP_IGET_OBJECT,
+                         all_opcodes.OP_IGET_BOOLEAN,
+                         all_opcodes.OP_IGET_BYTE,
+                         all_opcodes.OP_IGET_CHAR,
+                         all_opcodes.OP_IGET_SHORT,
+                         all_opcodes.OP_IPUT,
+                         all_opcodes.OP_IPUT_WIDE,
+                         all_opcodes.OP_IPUT_OBJECT,
+                         all_opcodes.OP_IPUT_BOOLEAN,
+                         all_opcodes.OP_IPUT_BYTE,
+                         all_opcodes.OP_IPUT_CHAR,
+                         all_opcodes.OP_IPUT_SHORT,
+                         all_opcodes.OP_SGET,
+                         all_opcodes.OP_SGET_WIDE,
+                         all_opcodes.OP_SGET_OBJECT,
+                         all_opcodes.OP_SGET_BOOLEAN,
+                         all_opcodes.OP_SGET_BYTE,
+                         all_opcodes.OP_SGET_CHAR,
+                         all_opcodes.OP_SGET_SHORT,
+                         all_opcodes.OP_SPUT,
+                         all_opcodes.OP_SPUT_WIDE,
+                         all_opcodes.OP_SPUT_OBJECT,
+                         all_opcodes.OP_SPUT_BOOLEAN,
+                         all_opcodes.OP_SPUT_BYTE,
+                         all_opcodes.OP_SPUT_CHAR,
+                         all_opcodes.OP_SPUT_SHORT])
     def redirect(opcode, args):
         if opcode == all_opcodes.OP_NEW_INSTANCE:
             typeidx = args[1]
@@ -578,6 +667,11 @@
                 return opcode, args
 
             return opcode, (args[0], stridxs_redir[stridx])
+        elif opcode in field_opcodes:
+            fieldidx = args[2]
+            if fieldidx not in fieldidxs_redir:
+                return opcode, args
+            return opcode, (args[0], args[1], fieldidxs_redir[fieldidx])
         return opcode, args
     
     new_op_vectors = [redirect(opcode, args) for opcode, args in op_vectors]
@@ -625,22 +719,23 @@
 # \param dex is a DEXFile_linked.
 # \param classdef is a class definition.
 # \param typeidxs_redir is a map of types.
+# \param methods_redir is a map of methods.
 # \param stridxs_redir is a map of strings.
-# \param methods_redir is a map of methods.
+# \param fieldidxs_redir is a map of fields.
 #
 def class_redirect_indices(dex, classdef, typeidxs_redir, methods_redir,
-                           stridxs_redir):
+                           stridxs_redir, fieldidxs_redir):
     if not classdef.classDataOffRef.is_true:
         return
     
     classdata = classdef.classDataOffRef.value
     for method in classdata.directMethods.items:
         method_redirect_indices(dex, method, typeidxs_redir, methods_redir,
-                                stridxs_redir)
+                                stridxs_redir, fieldidxs_redir)
         pass
     for method in classdata.virtualMethods.items:
         method_redirect_indices(dex, method, typeidxs_redir, methods_redir,
-                                stridxs_redir)
+                                stridxs_redir, fieldidxs_redir)
         pass
     pass
 
@@ -688,23 +783,36 @@
     return stridxs_map
 
 
+def make_fieldidxs_redir_map(dex_src, dex_dst):
+    fieldidxs_map = {}
+    for idx, fieldid in enumerate(dex_src.fieldIds.items):
+        try:
+            tgt_idx = dex_dst.fieldIds.items.index(fieldid)
+        except ValueError:
+            continue
+        fieldidxs_map[idx] = tgt_idx
+        pass
+    return fieldidxs_map
+
+
 ## \biref Redirect types of all code in given DEXFile_linked.
 def dexfile_redirect_indices(dex, typeidxs_redir, methods_redir, stridxs_redir,
-                             excludes=set([])):
+                             fieldidxs_redir, excludes=set([])):
     for classdef in dex.classDefs.items:
         typeid = classdef.classIdx
         idx = dex.get_idx_typeid(typeid)
         if idx in excludes:
             continue
         class_redirect_indices(dex, classdef, typeidxs_redir, methods_redir,
-                               stridxs_redir)
+                               stridxs_redir, fieldidxs_redir)
         pass
     pass
 
 
 ## \brief Redirect types for code of types specified by given indices.
 def dexfile_redirect_indices_typeidxs(dex, typeidxs_redir, methodidxs_redir,
-                                      stridxs_redir, typeidxs):
+                                      stridxs_redir, fieldidxs_redir,
+                                      typeidxs):
     typeidxs = set(typeidxs)
     for classdef in dex.classDefs.items:
         typeid = classdef.classIdx
@@ -712,7 +820,8 @@
         if idx not in typeidxs:
             continue
         class_redirect_indices(dex, classdef, typeidxs_redir,
-                               methodidxs_redir, stridxs_redir)
+                               methodidxs_redir, stridxs_redir,
+                               fieldidxs_redir)
         pass
     pass
 
@@ -757,10 +866,14 @@
 
     stridxs_redir = \
         make_stridxs_redir_map(dex_src, dex_dst)
+
+    fieldidxs_redir = \
+        make_fieldidxs_redir_map(dex_src, dex_dst)
     
     dexfile_redirect_indices_typeidxs(dex_dst, typeidxs_redir,
                                       methodidxs_redir,
                                       stridxs_redir,
+                                      fieldidxs_redir,
                                       typeidxs_redir.values())
     pass
 
@@ -799,13 +912,15 @@
         make_methodidxs_redir_map(dex, dex, typeidxs_redir)
 
     stridxs_redir = {}
+    fieldidxs_redir = {}
     
     ex_typeids = [dex.find_typeid_name(name)
                    for name in exclude_classnames]
     ex_typeidxs = [dex.get_idx_typeid(typeid)
                    for typeid in ex_typeids]
     dexfile_redirect_indices(dex, typeidxs_redir, methodidxs_redir,
-                             stridxs_redir, excludes=ex_typeidxs)
+                             stridxs_redir, fieldidxs_redir,
+                             excludes=ex_typeidxs)
     pass
 
 
@@ -833,16 +948,18 @@
 # injection, this function create maps for remapping indices mentioned
 # in the code.
 # 
-def _make_idx_map(saved_typeids, saved_methodids, saved_strids,
-                  new_typeids, new_methodids, new_strids):
+def _make_idx_map(saved_typeids, saved_methodids, saved_strids, saved_fieldids,
+                  new_typeids, new_methodids, new_strids, new_fieldids):
     methodidxs_map = dict([(idx, new_methodids.index(methodid))
                            for idx, methodid in enumerate(saved_methodids)])
     typeidxs_map = dict([(idx, new_typeids.index(typeid))
                          for idx, typeid in enumerate(saved_typeids)])
     stridxs_map = dict([(idx, new_strids.index(strid))
                         for idx, strid in enumerate(saved_strids)])
+    fieldidxs_map = dict([(idx, new_fieldids.index(fieldid))
+                          for idx, fieldid in enumerate(saved_fieldids)])
     
-    return typeidxs_map, methodidxs_map, stridxs_map
+    return typeidxs_map, methodidxs_map, stridxs_map, fieldidxs_map
 
 
 ## \brief Sort sorted arrays and remapping indices for code blocks.
@@ -856,18 +973,22 @@
     saved_typeids = list(dex_linked.typeIds.items)
     saved_methodids = list(dex_linked.methodIds.items)
     saved_strids = list(dex_linked.stringIds.items)
+    saved_fieldids = list(dex_linked.fieldIds.items)
     
     dex_sort_sorted_arrays(dex_linked)
 
     new_typeids = dex_linked.typeIds.items
     new_methodids = dex_linked.methodIds.items
     new_strids = dex_linked.stringIds.items
-    typeidxs_map, methodidxs_map, stridxs_map = \
+    new_fieldids = dex_linked.fieldIds.items
+    typeidxs_map, methodidxs_map, stridxs_map, fieldidxs_map = \
         _make_idx_map(saved_typeids, saved_methodids, saved_strids, \
-                          new_typeids, new_methodids, new_strids)
+                          saved_fieldids, \
+                          new_typeids, new_methodids, new_strids, \
+                          new_fieldids)
     
     dexfile_redirect_indices(dex_linked, typeidxs_map, methodidxs_map,
-                             stridxs_map)
+                             stridxs_map, fieldidxs_map)
     pass
 
 
--- a/paraspace/tests/injection_test.py	Tue Aug 16 16:07:32 2011 +0800
+++ b/paraspace/tests/injection_test.py	Tue Aug 16 18:10:11 2011 +0800
@@ -161,7 +161,7 @@
                                               helloworld_linked,
                                               types_redir)
     dexfile_redirect_indices(helloworld_linked, types_redir,
-                             methods_redir, excludes)
+                             methods_redir, {}, excludes)
 
     for code in helloworld_linked.codeItems.items:
         op_vectors = dalvik_opcodes.decode_insn_blk(code.insns.data)
@@ -181,8 +181,10 @@
 
 def collect_types_in_method_test():
     from paraspace.dex_deptracker import prepare_dep_decls
-    from paraspace.injection import collect_typeidxs_stridxs_in_method
-    from paraspace.injection import collect_typeidxs_stridxs_mentioned_by_class
+    from paraspace.injection import \
+        collect_typeidxs_stridxs_fieldidxs_in_method
+    from paraspace.injection import \
+        collect_typeidxs_stridxs_fieldidxs_mentioned_by_class
     from paraspace.dexfile import DEXFile_linked
     
     _install_dexfile_4_deptracker()
@@ -203,17 +205,18 @@
 
     init_method = fakefile_linked.find_method_name('<init>', fakefile_def)
 
-    typeidxs, stridxs = \
-        collect_typeidxs_stridxs_in_method(fakefile_linked, init_method)
+    typeidxs, stridxs, fieldidxs = \
+        collect_typeidxs_stridxs_fieldidxs_in_method(fakefile_linked, \
+                                                         init_method)
     assert len(typeidxs) == 1
 
     typeid = fakefile_linked.find_typeid_idx(typeidxs[0])
     typeid_name = DEXFile_linked.get_typeid_name(typeid)
     assert typeid_name == 'Ljava/io/File;'
     
-    typeidxs, stridxs = \
-        collect_typeidxs_stridxs_mentioned_by_class(fakefile_linked, \
-                                                        fakefile_def)
+    typeidxs, stridxs, fieldidxs = \
+        collect_typeidxs_stridxs_fieldidxs_mentioned_by_class(\
+        fakefile_linked, fakefile_def)
     assert len(typeidxs) == 1
 
     typeid = fakefile_linked.find_typeid_idx(typeidxs[0])
@@ -370,7 +373,7 @@
                                       helloworld_linked, \
                                       typeidxs_redir_map)
     dexfile_redirect_indices(helloworld_linked, typeidxs_redir_map,
-                             methodidxs_redir_map, {},
+                             methodidxs_redir_map, {}, {},
                              excludes=[fakefile_typeidx])
 
     cloning_initdef = \