Example #1
0
    public void addClassTreeHook(HookClass origin, HookClass to) {
      addSuperclassHook(origin, to);

      Set<HookClass> superclassHookSet = new HashSet<HookClass>();
      superclassHookSet.add(origin);

      for (ClassDefItem classDef : mDexFile.ClassDefsSection.getItems()) {
        HookClass superclass = new HookClass(classDef.getSuperclass());

        if (superclassHookSet.contains(superclass)) {
          TypeIdItem classType = classDef.getClassType();
          HookClass newHook = new HookClass(classType);
          superclassHookSet.add(newHook);
          mHooks.put(newHook, classType);
        }
      }

      HookClass[] superclassHooks = superclassHookSet.toArray(new HookClass[0]);

      HashSet<EncodedMethod> newClassMethods = getClassMethodsMap().get(to);

      if (newClassMethods != null) {
        for (EncodedMethod newVirtual : newClassMethods) {
          if (newVirtual.isDirect()) {
            continue;
          }

          for (HookClass superclass : superclassHooks) {
            HookMethod deadMethod = new HookMethod(newVirtual.method);
            deadMethod.mClasspath = superclass.mClasspath;
            mDead.add(deadMethod);
          }
        }
      }
    }
Example #2
0
  public void hookSuperclass(HookMap hooks, ClassDefItem classDef) {
    HookItem oldTarget = HookItem.getInstance(classDef.getSuperclass());

    if (oldTarget == null) {
      return;
    }

    if (!hooks.containsKey(oldTarget)) {
      return;
    }

    TypeIdItem newTarget = (TypeIdItem) hooks.get(oldTarget);

    assert (newTarget != null);

    if (newTarget == classDef.getClassType()) {
      return;
    }

    classDef.setSuperclass(newTarget);

    HookClass classType = new HookClass(classDef.getClassType());

    if (hooks.mIgnoreMethods.contains(classType)) {
      return;
    }

    if (hooks.mDead.isEmpty()) {
      return;
    }

    // get rid of virtual methods so the hooked class method is called instead

    String className = newTarget.getTypeDescriptor();

    List<EncodedMethod> newDirects = new LinkedList<EncodedMethod>();
    List<EncodedMethod> newVirtuals = new LinkedList<EncodedMethod>();

    ClassDataItem classData = classDef.getClassData();

    for (EncodedMethod encodedMethod : classData.getDirectMethods()) {
      newDirects.add(encodedMethod);
    }

    for (EncodedMethod encodedMethod : classData.getVirtualMethods()) {
      HookMethod method = new HookMethod(encodedMethod.method);
      method.mClasspath = className;

      if (!hooks.mDead.contains(method)) {
        newVirtuals.add(encodedMethod);
      }
    }

    classData.update(newDirects, newVirtuals);
  }
Example #3
0
    public static HookItem getInstance(Item item) {
      if (item instanceof MethodIdItem) {
        return new HookMethod((MethodIdItem) item);
      }

      if (item instanceof TypeIdItem) {
        return new HookClass((TypeIdItem) item);
      }

      if (item instanceof ClassDefItem) {
        ClassDefItem classDef = (ClassDefItem) item;
        return new HookClass(classDef.getClassType());
      }

      return null;
    }
Example #4
0
  private void initClassMaps() {
    mClasses = new HashMap<HookClass, TypeIdItem>();
    mClassMethods = new HashMap<HookClass, HashSet<EncodedMethod>>();
    mSubclasses = new HashMap<String, Set<String>>();

    for (ClassDefItem item : mDexFile.ClassDefsSection.getItems()) {
      TypeIdItem classType = item.getClassType();
      HookClass classHook = new HookClass(classType);
      mClasses.put(classHook, classType);

      String superclassType = item.getSuperclass().getTypeDescriptor();
      Set<String> subclassSet = mSubclasses.get(superclassType);

      if (subclassSet == null) {
        subclassSet = new TreeSet<String>();
        mSubclasses.put(superclassType, subclassSet);
      }

      subclassSet.add(classType.getTypeDescriptor());

      ClassDataItem classData = item.getClassData();

      if (classData == null) {
        continue;
      }

      HashSet<EncodedMethod> classMethods = new HashSet<EncodedMethod>();

      for (EncodedMethod method : classData.getDirectMethods()) {
        classMethods.add(method);
      }

      for (EncodedMethod method : classData.getVirtualMethods()) {
        classMethods.add(method);
      }

      mClassMethods.put(classHook, classMethods);
    }
  }
Example #5
0
  public static void disassembleDexFile(
      String dexFilePath,
      DexFile dexFile,
      boolean deodex,
      String outputDirectory,
      String[] classPathDirs,
      String bootClassPath,
      String extraBootClassPath,
      boolean noParameterRegisters,
      boolean useLocalsDirective,
      boolean useSequentialLabels,
      boolean outputDebugInfo,
      boolean addCodeOffsets,
      boolean noAccessorComments,
      int registerInfo,
      boolean verify,
      boolean ignoreErrors,
      String inlineTable) {
    baksmali.noParameterRegisters = noParameterRegisters;
    baksmali.useLocalsDirective = useLocalsDirective;
    baksmali.useSequentialLabels = useSequentialLabels;
    baksmali.outputDebugInfo = outputDebugInfo;
    baksmali.addCodeOffsets = addCodeOffsets;
    baksmali.noAccessorComments = noAccessorComments;
    baksmali.deodex = deodex;
    baksmali.registerInfo = registerInfo;
    baksmali.bootClassPath = bootClassPath;
    baksmali.verify = verify;

    ClassPath.ClassPathErrorHandler classPathErrorHandler = null;
    if (ignoreErrors) {
      classPathErrorHandler =
          new ClassPath.ClassPathErrorHandler() {
            public void ClassPathError(String className, Exception ex) {
              System.err.println(String.format("Skipping %s", className));
              ex.printStackTrace(System.err);
            }
          };
    }

    if (registerInfo != 0 || deodex || verify) {
      try {
        String[] extraBootClassPathArray = null;
        if (extraBootClassPath != null && extraBootClassPath.length() > 0) {
          assert extraBootClassPath.charAt(0) == ':';
          extraBootClassPathArray = extraBootClassPath.substring(1).split(":");
        }

        if (dexFile.isOdex() && bootClassPath == null) {
          // ext.jar is a special case - it is typically the 2nd jar in the boot class path, but it
          // also
          // depends on classes in framework.jar (typically the 3rd jar in the BCP). If the user
          // didn't
          // specify a -c option, we should add framework.jar to the boot class path by default, so
          // that it
          // "just works"
          if (extraBootClassPathArray == null && isExtJar(dexFilePath)) {
            extraBootClassPathArray = new String[] {"framework.jar"};
          }
          ClassPath.InitializeClassPathFromOdex(
              classPathDirs, extraBootClassPathArray, dexFilePath, dexFile, classPathErrorHandler);
        } else {
          String[] bootClassPathArray = null;
          if (bootClassPath != null) {
            bootClassPathArray = bootClassPath.split(":");
          }
          ClassPath.InitializeClassPath(
              classPathDirs,
              bootClassPathArray,
              extraBootClassPathArray,
              dexFilePath,
              dexFile,
              classPathErrorHandler);
        }

        if (inlineTable != null) {
          inlineResolver = new CustomInlineMethodResolver(inlineTable);
        }
      } catch (Exception ex) {
        System.err.println("\n\nError occured while loading boot class path files. Aborting.");
        ex.printStackTrace(System.err);
        System.exit(1);
      }
    }

    File outputDirectoryFile = new File(outputDirectory);
    if (!outputDirectoryFile.exists()) {
      if (!outputDirectoryFile.mkdirs()) {
        System.err.println("Can't create the output directory " + outputDirectory);
        System.exit(1);
      }
    }

    if (!noAccessorComments) {
      syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile);
    }

    // sort the classes, so that if we're on a case-insensitive file system and need to handle
    // classes with file
    // name collisions, then we'll use the same name for each class, if the dex file goes through
    // multiple
    // baksmali/smali cycles for some reason. If a class with a colliding name is added or removed,
    // the filenames
    // may still change of course
    ArrayList<ClassDefItem> classDefItems =
        new ArrayList<ClassDefItem>(dexFile.ClassDefsSection.getItems());
    Collections.sort(
        classDefItems,
        new Comparator<ClassDefItem>() {
          public int compare(ClassDefItem classDefItem1, ClassDefItem classDefItem2) {
            return classDefItem1
                .getClassType()
                .getTypeDescriptor()
                .compareTo(classDefItem1.getClassType().getTypeDescriptor());
          }
        });

    ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali");

    for (ClassDefItem classDefItem : classDefItems) {
      /**
       * The path for the disassembly file is based on the package name The class descriptor will
       * look something like: Ljava/lang/Object; Where the there is leading 'L' and a trailing ';',
       * and the parts of the package name are separated by '/'
       */
      if (registerInfo != 0 || deodex || verify) {
        // If we are analyzing the bytecode, make sure that this class is loaded into the ClassPath.
        // If it isn't
        // then there was some error while loading it, and we should skip it
        ClassPath.ClassDef classDef = ClassPath.getClassDef(classDefItem.getClassType(), false);
        if (classDef == null || classDef instanceof ClassPath.UnresolvedClassDef) {
          continue;
        }
      }

      String classDescriptor = classDefItem.getClassType().getTypeDescriptor();

      // validate that the descriptor is formatted like we expect
      if (classDescriptor.charAt(0) != 'L'
          || classDescriptor.charAt(classDescriptor.length() - 1) != ';') {
        System.err.println(
            "Unrecognized class descriptor - " + classDescriptor + " - skipping class");
        continue;
      }

      File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor);

      // create and initialize the top level string template
      ClassDefinition classDefinition = new ClassDefinition(classDefItem);

      // write the disassembly
      Writer writer = null;
      try {
        File smaliParent = smaliFile.getParentFile();
        if (!smaliParent.exists()) {
          if (!smaliParent.mkdirs()) {
            System.err.println(
                "Unable to create directory " + smaliParent.toString() + " - skipping class");
            continue;
          }
        }

        if (!smaliFile.exists()) {
          if (!smaliFile.createNewFile()) {
            System.err.println(
                "Unable to create file " + smaliFile.toString() + " - skipping class");
            continue;
          }
        }

        BufferedWriter bufWriter =
            new BufferedWriter(new OutputStreamWriter(new FileOutputStream(smaliFile), "UTF8"));

        writer = new IndentingWriter(bufWriter);
        classDefinition.writeTo((IndentingWriter) writer);
      } catch (Exception ex) {
        System.err.println(
            "\n\nError occured while disassembling class "
                + classDescriptor.replace('/', '.')
                + " - skipping class");
        ex.printStackTrace();
      } finally {
        if (writer != null) {
          try {
            writer.close();
          } catch (Throwable ex) {
            System.err.println("\n\nError occured while closing file " + smaliFile.toString());
            ex.printStackTrace();
          }
        }
      }

      if (!ignoreErrors && classDefinition.hadValidationErrors()) {
        System.exit(1);
      }
    }
  }