Пример #1
0
/**
 * Walks all the code of the app, finding classes that are reachable from
 * code.
 *
 * Note that as code is changed or removed by Redex, this information will
 * become stale, so this method should be called periodically, for example
 * after each pass.
 */
void recompute_classes_reachable_from_code(const Scope& scope) {
  // Matches methods marked as native
  walk_methods(scope,
               [&](DexMethod* meth) {
                 if (meth->get_access() & DexAccessFlags::ACC_NATIVE) {
                   TRACE(PGR, 3, "native_method: %s\n", SHOW(meth->get_class()));
                   mark_reachable_by_classname(meth->get_class(), true);
                 }
               });
}
Пример #2
0
/**
 * Walks all the code of the app, finding classes that are reachable from
 * code.
 *
 * Note that as code is changed or removed by Redex, this information will
 * become stale, so this method should be called periodically, for example
 * after each pass.
 */
void recompute_classes_reachable_from_code(const Scope& scope) {
  for (auto clazz : scope) {
    clazz->rstate.clear_if_compute();
  }

  std::unordered_set<DexString*> maybetypes;
  walk_annotations(scope,
                   [&](DexAnnotation* anno) {
                     static DexType* dalviksig =
                         DexType::get_type("Ldalvik/annotation/Signature;");
                     // Signature annotations contain strings that Jackson uses
                     // to construct the underlying types.  We capture the
                     // full list here, and mark them later.  (There are many
                     // duplicates, so de-duping before we look it up as a class
                     // makes sense)
                     if (anno->type() == dalviksig) {
                       auto elems = anno->anno_elems();
                       for (auto const& elem : elems) {
                         auto ev = elem.encoded_value;
                         if (ev->evtype() != DEVT_ARRAY) continue;
                         auto arrayev = static_cast<DexEncodedValueArray*>(ev);
                         auto const& evs = arrayev->evalues();
                         for (auto strev : *evs) {
                           if (strev->evtype() != DEVT_STRING) continue;
                           auto stringev =
                               static_cast<DexEncodedValueString*>(strev);
                           maybetypes.insert((DexString*)stringev->string());
                         }
                       }
                       return;
                     }
                     // Class literals in annotations.
                     //
                     // Example:
                     //    @JsonDeserialize(using=MyJsonDeserializer.class)
                     //                                   ^^^^
                     if (anno->runtime_visible()) {
                       auto elems = anno->anno_elems();
                       for (auto const& dae : elems) {
                         auto evalue = dae.encoded_value;
                         std::vector<DexType*> ltype;
                         evalue->gather_types(ltype);
                         if (ltype.size()) {
                           for (auto dextype : ltype) {
                             mark_reachable_directly(dextype);
                           }
                         }
                       }
                     }
                   });

  // Now we process the strings that were in the signature
  // annotations.
  // Note: We do not mark these as ref'd by string, because
  // these cases are handleable for renaming.
  for (auto dstring : maybetypes) {
    const char* cstr = dstring->c_str();
    int len = strlen(cstr);
    if (len < 3) continue;
    if (cstr[0] != 'L') continue;
    if (cstr[len - 1] == ';') {
      auto dtype = DexType::get_type(dstring);
      mark_reachable_directly(dtype);
      continue;
    }
    std::string buf(cstr);
    buf += ';';
    auto dtype = DexType::get_type(buf.c_str());
    mark_reachable_directly(dtype);
  }

  // Matches methods marked as native
  walk_methods(scope,
               [&](DexMethod* meth) {
                 if (meth->get_access() & DexAccessFlags::ACC_NATIVE) {
                   mark_reachable_by_classname(meth->get_class(), true);
                 }
               });

  walk_code(scope,
            [](DexMethod*) { return true; },
            [&](DexMethod* meth, DexCode* code) {
              auto opcodes = code->get_instructions();
              for (const auto& opcode : opcodes) {
                // Matches any stringref that name-aliases a type.
                if (opcode->has_strings()) {
                  auto stringop = static_cast<DexOpcodeString*>(opcode);
                  DexString* dsclzref = stringop->get_string();
                  DexType* dtexclude =
                      get_dextype_from_dotname(dsclzref->c_str());
                  if (dtexclude == nullptr) continue;
                  mark_reachable_by_classname(dtexclude, true);
                }
                if (opcode->has_types()) {
                  // Matches the following instructions (from most to least
                  // common):
                  // check-cast, new-instance, const-class, instance-of
                  // new-instance should not be on this list, and
                  // we should not allow these to operate on themselves.
                  // TODO(snay/dalves)
                  auto typeop = static_cast<DexOpcodeType*>(opcode);
                  mark_reachable_directly(typeop->get_type());
                }
              }
            });
}
Пример #3
0
/*
 * Initializes list of classes that are reachable via reflection, and calls
 * or from code.
 *
 * These include:
 *  - Classes used in the manifest (e.g. activities, services, etc)
 *  - View or Fragment classes used in layouts
 *  - Classes that are in certain packages (specified in the reflected_packages
 *    section of the config) and classes that extend from them
 *  - Classes marked with special annotations (keep_annotations in config)
 *  - Classes reachable from native libraries
 */
static void init_permanently_reachable_classes(const Scope& scope, folly::dynamic& config, const std::vector<KeepRule>& proguard_rules) {

  std::string apk_dir;
  std::vector<std::string> reflected_package_names;

  auto config_apk_dir = config.find("apk_dir");
  if (config_apk_dir != config.items().end()) {
    apk_dir = toStdString(config_apk_dir->second.asString());
  }

  auto config_reflected_package_names = config.find("reflected_packages");
  if (config_reflected_package_names != config.items().end()) {
    for (auto config_pkg_name : config_reflected_package_names->second) {
      std::string pkg_name = toStdString(config_pkg_name.asString());
      reflected_package_names.push_back(pkg_name);
    }
  }

  std::unordered_set<DexType*> keep_annotations;
  auto config_keep_annotations = config.find("keep_annotations");
  if (config_keep_annotations != config.items().end()) {
    for (auto const& config_anno_name : config_keep_annotations->second) {
      std::string anno_name = toStdString(config_anno_name.asString());
      DexType* anno = DexType::get_type(anno_name.c_str());
      if (anno) keep_annotations.insert(anno);
    }
  }
  keep_annotated_classes(scope, keep_annotations);

  std::vector<std::string> keep_pkgs;
  auto config_keep_packages = config.find("keep_packages");
  if (config_keep_packages != config.items().end()) {
    for (auto const& config_pkg : config_keep_packages->second) {
      auto pkg_name = toStdString(config_pkg.asString());
      keep_pkgs.push_back(pkg_name);
    }
  }
  keep_packages(scope, keep_pkgs);

  if (apk_dir.size()) {
    // Classes present in manifest
    std::string manifest = apk_dir + std::string("/AndroidManifest.xml");
    for (std::string classname : get_manifest_classes(manifest)) {
      mark_reachable_by_classname(classname, false);
    }

    // Classes present in XML layouts
    for (std::string classname : get_layout_classes(apk_dir)) {
      mark_reachable_by_classname(classname, false);
    }

    // Classnames present in native libraries (lib/*/*.so)
    for (std::string classname : get_native_classes(apk_dir)) {
      mark_reachable_by_classname(classname, false);
    }
  }

  std::unordered_set<DexClass*> reflected_package_classes;
  for (auto clazz : scope) {
    const char* cname = clazz->get_type()->get_name()->c_str();
    for (auto pkg : reflected_package_names) {
      if (starts_with(cname, pkg.c_str())) {
        reflected_package_classes.insert(clazz);
        continue;
      }
    }
  }
  for (auto clazz : scope) {
    if (in_reflected_pkg(clazz, reflected_package_classes)) {
      reflected_package_classes.insert(clazz);
      /* Note:
       * Some of these are by string, others by type
       * but we have no way in the config to distinguish
       * them currently.  So, we mark with the most
       * conservative sense here.
       */
      mark_reachable_by_classname(clazz, false);
    }
  }

  /* Do only keep class rules for now.
   * '*' and '**' rules are skipped,
   * because those are matching on something else,
   * which we haven't implemented yet.
   * Rules can be "*" or "**" on classname and match
   * on some other attribute. We don't match against
   * all attributes at once, so this prevents us
   * from matching everything.
   */
  std::vector<std::string> cls_patterns;
  for (auto const& r : proguard_rules) {
    if (r.classname != nullptr &&
        r.class_type == keeprules::ClassType::CLASS &&
          strlen(r.classname) > 2) {
      std::string cls_pattern(r.classname);
      std::replace(cls_pattern.begin(), cls_pattern.end(), '.', '/');
      auto prep_pat = 'L' + cls_pattern;
      TRACE(PGR, 1, "adding pattern %s \n", prep_pat.c_str());
      cls_patterns.push_back(prep_pat);
    }
  }
  size_t pg_marked_classes = 0;
  for (auto clazz : scope) {
    auto cname = clazz->get_type()->get_name()->c_str();
    auto cls_len = strlen(cname);
    for (auto const& pat : cls_patterns) {
        int pat_len = pat.size();
        if (type_matches(pat.c_str(), cname, pat_len, cls_len)) {
          mark_reachable_directly(clazz);
          TRACE(PGR, 2, "matched cls %s against pattern %s \n", cname, pat.c_str());
          pg_marked_classes++;
          break;
      }
    }
  }
  TRACE(PGR, 1, "matched on %lu classes with CLASS KEEP proguard rules \n",
      pg_marked_classes);
}