/** * 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); } }); }
/** * 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()); } } }); }
/* * 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); }