opt/renameclasses/RenameClassesV2.cpp (692 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include "RenameClassesV2.h" #include <algorithm> #include <boost/algorithm/string/predicate.hpp> #include <boost/regex.hpp> #include <map> #include <string> #include <unordered_map> #include <unordered_set> #include <vector> #include "ConfigFiles.h" #include "DexClass.h" #include "DexUtil.h" #include "IRInstruction.h" #include "KeepReason.h" #include "PassManager.h" #include "ReachableClasses.h" #include "RedexResources.h" #include "Show.h" #include "TypeStringRewriter.h" #include "Walkers.h" #include "Warning.h" #include "Trace.h" #include <locator.h> using facebook::Locator; #define MAX_DESCRIPTOR_LENGTH (1024) static const char* METRIC_CLASSES_IN_SCOPE = "num_classes_in_scope"; static const char* METRIC_RENAMED_CLASSES = "**num_renamed**"; static const char* METRIC_FORCE_RENAMED_CLASSES = "num_force_renamed"; static const char* METRIC_REWRITTEN_CONST_STRINGS = "num_rewritten_const_strings"; static const char* METRIC_MISSING_HIERARCHY_TYPES = "num_missing_hierarchy_types"; static const char* METRIC_MISSING_HIERARCHY_CLASSES = "num_missing_hierarchy_classes"; static RenameClassesPassV2 s_pass; namespace { const char* dont_rename_reason_to_metric(DontRenameReasonCode reason) { switch (reason) { case DontRenameReasonCode::Annotated: return "num_dont_rename_annotated"; case DontRenameReasonCode::Annotations: return "num_dont_rename_annotations"; case DontRenameReasonCode::Specific: return "num_dont_rename_specific"; case DontRenameReasonCode::Packages: return "num_dont_rename_packages"; case DontRenameReasonCode::Hierarchy: return "num_dont_rename_hierarchy"; case DontRenameReasonCode::Resources: return "num_dont_rename_resources"; case DontRenameReasonCode::ClassNameLiterals: return "num_dont_rename_class_name_literals"; case DontRenameReasonCode::Canaries: return "num_dont_rename_canaries"; case DontRenameReasonCode::NativeBindings: return "num_dont_rename_native_bindings"; case DontRenameReasonCode::ClassForTypesWithReflection: return "num_dont_rename_class_for_types_with_reflection"; case DontRenameReasonCode::ProguardCantRename: return "num_dont_rename_pg_cant_rename"; case DontRenameReasonCode::SerdeRelationships: return "num_dont_rename_serde_relationships"; default: not_reached_log("Unexpected DontRenameReasonCode: %d", reason); } } bool dont_rename_reason_to_metric_per_rule(DontRenameReasonCode reason) { switch (reason) { case DontRenameReasonCode::Annotated: case DontRenameReasonCode::Packages: case DontRenameReasonCode::Hierarchy: // Set to true to add more detailed metrics for renamer if needed return false; case DontRenameReasonCode::ProguardCantRename: return keep_reason::Reason::record_keep_reasons(); default: return false; } } } // namespace // Returns idx of the vector of packages if the given class name matches, or -1 // if not found. ssize_t find_matching_package( const std::string& classname, const std::vector<std::string>& allowed_packages) { for (size_t i = 0; i < allowed_packages.size(); i++) { if (classname.rfind("L" + allowed_packages[i]) == 0) { return i; } } return -1; } bool referenced_by_layouts(const DexClass* clazz) { return clazz->rstate.is_referenced_by_resource_xml(); } // Returns true if this class is a layout, and allowed for renaming via config. bool is_allowed_layout_class( const DexClass* clazz, const std::vector<std::string>& allow_layout_rename_packages) { always_assert(referenced_by_layouts(clazz)); auto idx = find_matching_package(clazz->get_name()->str(), allow_layout_rename_packages); return idx != -1; } std::unordered_set<std::string> RenameClassesPassV2::build_dont_rename_class_name_literals(Scope& scope) { using namespace boost::algorithm; std::vector<const DexString*> all_strings; for (auto clazz : scope) { clazz->gather_strings(all_strings); } sort_unique(all_strings); std::unordered_set<std::string> result; boost::regex external_name_regex{ "((org)|(com)|(android(x|\\.support)))\\." "([a-zA-Z][a-zA-Z\\d_$]*\\.)*" "[a-zA-Z][a-zA-Z\\d_$]*"}; for (auto dex_str : all_strings) { const std::string& s = dex_str->str(); if (!ends_with(s, ".java") && boost::regex_match(s, external_name_regex)) { const std::string& internal_name = java_names::external_to_internal(s); auto cls = type_class(DexType::get_type(internal_name)); if (cls != nullptr && !cls->is_external()) { result.insert(internal_name); TRACE(RENAME, 4, "Found %s in string pool before renaming", s.c_str()); } } } return result; } std::unordered_set<std::string> RenameClassesPassV2::build_dont_rename_for_types_with_reflection( Scope& scope, const ProguardMap& pg_map) { std::unordered_set<std::string> dont_rename_class_for_types_with_reflection; std::unordered_set<DexType*> refl_map; for (auto const& refl_type_str : m_dont_rename_types_with_reflection) { auto deobf_cls_string = pg_map.translate_class(refl_type_str); TRACE(RENAME, 4, "%s got translated to %s", refl_type_str.c_str(), deobf_cls_string.c_str()); if (deobf_cls_string.empty()) { deobf_cls_string = refl_type_str; } DexType* type_with_refl = DexType::get_type(deobf_cls_string.c_str()); if (type_with_refl != nullptr) { TRACE(RENAME, 4, "got DexType %s", SHOW(type_with_refl)); refl_map.insert(type_with_refl); } } walk::opcodes( scope, [](DexMethod*) { return true; }, [&](DexMethod* m, IRInstruction* insn) { if (insn->has_method()) { auto callee = insn->get_method(); if (callee == nullptr || !callee->is_concrete()) return; auto callee_method_cls = callee->get_class(); if (refl_map.count(callee_method_cls) == 0) return; std::string classname = m->get_class()->get_name()->str(); TRACE(RENAME, 4, "Found %s with known reflection usage. marking reachable", classname.c_str()); dont_rename_class_for_types_with_reflection.insert(classname); } }); return dont_rename_class_for_types_with_reflection; } std::unordered_set<std::string> RenameClassesPassV2::build_dont_rename_canaries( Scope& scope) { std::unordered_set<std::string> dont_rename_canaries; // Gather canaries for (auto clazz : scope) { if (strstr(clazz->get_name()->c_str(), "/Canary")) { dont_rename_canaries.insert(clazz->get_name()->str()); } } return dont_rename_canaries; } std::unordered_map<const DexType*, std::string> RenameClassesPassV2::build_force_rename_hierarchies( PassManager& mgr, Scope& scope, const ClassHierarchy& class_hierarchy) { std::unordered_map<const DexType*, std::string> force_rename_hierarchies; std::vector<DexClass*> base_classes; for (const auto& base : m_force_rename_hierarchies) { // skip comments if (base.c_str()[0] == '#') continue; auto base_type = DexType::get_type(base.c_str()); if (base_type != nullptr) { DexClass* base_class = type_class(base_type); if (!base_class) { TRACE(RENAME, 2, "Can't find class for force_rename_hierachy rule %s", base.c_str()); mgr.incr_metric(METRIC_MISSING_HIERARCHY_CLASSES, 1); } else { base_classes.emplace_back(base_class); } } else { TRACE(RENAME, 2, "Can't find type for force_rename_hierachy rule %s", base.c_str()); mgr.incr_metric(METRIC_MISSING_HIERARCHY_TYPES, 1); } } for (const auto& base_class : base_classes) { auto base_name = base_class->get_name()->c_str(); force_rename_hierarchies[base_class->get_type()] = base_name; TypeSet children_and_implementors; get_all_children_or_implementors(class_hierarchy, scope, base_class, children_and_implementors); for (const auto& cls : children_and_implementors) { force_rename_hierarchies[cls] = base_name; } } return force_rename_hierarchies; } std::unordered_map<const DexType*, std::string> RenameClassesPassV2::build_dont_rename_hierarchies( PassManager& mgr, Scope& scope, const ClassHierarchy& class_hierarchy) { std::unordered_map<const DexType*, std::string> dont_rename_hierarchies; std::vector<DexClass*> base_classes; for (const auto& base : m_dont_rename_hierarchies) { // skip comments if (base.c_str()[0] == '#') continue; auto base_type = DexType::get_type(base.c_str()); if (base_type != nullptr) { DexClass* base_class = type_class(base_type); if (!base_class) { TRACE(RENAME, 2, "Can't find class for dont_rename_hierachy rule %s", base.c_str()); mgr.incr_metric(METRIC_MISSING_HIERARCHY_CLASSES, 1); } else { base_classes.emplace_back(base_class); } } else { TRACE(RENAME, 2, "Can't find type for dont_rename_hierachy rule %s", base.c_str()); mgr.incr_metric(METRIC_MISSING_HIERARCHY_TYPES, 1); } } for (const auto& base_class : base_classes) { auto base_name = base_class->get_name()->c_str(); dont_rename_hierarchies[base_class->get_type()] = base_name; TypeSet children_and_implementors; get_all_children_or_implementors(class_hierarchy, scope, base_class, children_and_implementors); for (const auto& cls : children_and_implementors) { dont_rename_hierarchies[cls] = base_name; } } return dont_rename_hierarchies; } std::unordered_set<const DexType*> RenameClassesPassV2::build_dont_rename_serde_relationships(Scope& scope) { std::unordered_set<const DexType*> dont_rename_serde_relationships; for (const auto& cls : scope) { klass::Serdes cls_serdes = klass::get_serdes(cls); const char* rawname = cls->get_name()->c_str(); std::string name = std::string(rawname); name.pop_back(); // Look for a class that matches one of the two deserializer patterns DexType* deser = cls_serdes.get_deser(); DexType* flatbuf_deser = cls_serdes.get_flatbuf_deser(); bool has_deser_finder = false; if (deser || flatbuf_deser) { for (const auto& method : cls->get_dmethods()) { if (!strcmp("$$getDeserializerClass", method->get_name()->c_str())) { has_deser_finder = true; break; } } } // Look for a class that matches one of the two serializer patterns DexType* ser = cls_serdes.get_ser(); DexType* flatbuf_ser = cls_serdes.get_flatbuf_ser(); bool has_ser_finder = false; if (ser || flatbuf_ser) { for (const auto& method : cls->get_dmethods()) { if (!strcmp("$$getSerializerClass", method->get_name()->c_str())) { has_ser_finder = true; break; } } } bool dont_rename = ((deser || flatbuf_deser) && !has_deser_finder) || ((ser || flatbuf_ser) && !has_ser_finder); if (dont_rename) { dont_rename_serde_relationships.insert(cls->get_type()); if (deser) dont_rename_serde_relationships.insert(deser); if (flatbuf_deser) dont_rename_serde_relationships.insert(flatbuf_deser); if (ser) dont_rename_serde_relationships.insert(ser); if (flatbuf_ser) dont_rename_serde_relationships.insert(flatbuf_ser); } } return dont_rename_serde_relationships; } std::unordered_set<const DexType*> RenameClassesPassV2::build_dont_rename_native_bindings(Scope& scope) { std::unordered_set<const DexType*> dont_rename_native_bindings; // find all classes with native methods, and all types mentioned // in protos of native methods for (auto clazz : scope) { for (auto meth : clazz->get_dmethods()) { if (is_native(meth)) { dont_rename_native_bindings.insert(clazz->get_type()); auto proto = meth->get_proto(); auto rtype = proto->get_rtype(); dont_rename_native_bindings.insert(rtype); for (auto ptype : *proto->get_args()) { dont_rename_native_bindings.insert( type::get_element_type_if_array(ptype)); } } } for (auto meth : clazz->get_vmethods()) { if (is_native(meth)) { dont_rename_native_bindings.insert(clazz->get_type()); auto proto = meth->get_proto(); auto rtype = proto->get_rtype(); dont_rename_native_bindings.insert(rtype); for (auto ptype : *proto->get_args()) { dont_rename_native_bindings.insert( type::get_element_type_if_array(ptype)); } } } } return dont_rename_native_bindings; } std::unordered_set<const DexType*> RenameClassesPassV2::build_dont_rename_annotated() { std::unordered_set<const DexType*> dont_rename_annotated; for (const auto& annotation : m_dont_rename_annotated) { DexType* anno = DexType::get_type(annotation); if (anno) { dont_rename_annotated.insert(anno); } } return dont_rename_annotated; } static void sanity_check(const Scope& scope, const rewriter::TypeStringMap& name_mapping) { std::unordered_set<std::string> external_names; // Class.forName() expects strings of the form "foo.bar.Baz". We should be // very suspicious if we see these strings in the string pool that // correspond to the old name of a class that we have renamed... for (const auto& it : name_mapping.get_class_map()) { external_names.emplace(java_names::internal_to_external(it.first->c_str())); } std::vector<const DexString*> all_strings; for (auto clazz : scope) { clazz->gather_strings(all_strings); } sort_unique(all_strings); int sketchy_strings = 0; for (auto s : all_strings) { if (external_names.find(s->str()) != external_names.end() || name_mapping.get_new_type_name(s)) { TRACE(RENAME, 2, "Found %s in string pool after renaming", s->c_str()); sketchy_strings++; } } if (sketchy_strings > 0) { fprintf(stderr, "WARNING: Found a number of sketchy class-like strings after class " "renaming. Re-run with TRACE=RENAME:2 for more details.\n"); } } std::string get_keep_rule(const DexClass* clazz) { if (keep_reason::Reason::record_keep_reasons()) { const auto& keep_reasons = clazz->rstate.keep_reasons(); for (const auto* reason : keep_reasons) { if (reason->type == keep_reason::KEEP_RULE && !reason->keep_rule->allowobfuscation) { return show(*reason); } } } return ""; } void RenameClassesPassV2::eval_classes(Scope& scope, const ClassHierarchy& class_hierarchy, ConfigFiles& conf, bool rename_annotations, PassManager& mgr) { std::unordered_map<const DexType*, std::string> force_rename_hierarchies; std::unordered_set<const DexType*> dont_rename_serde_relationships; std::unordered_set<std::string> dont_rename_class_name_literals; std::unordered_set<std::string> dont_rename_class_for_types_with_reflection; std::unordered_set<std::string> dont_rename_canaries; std::unordered_map<const DexType*, std::string> dont_rename_hierarchies; std::unordered_set<const DexType*> dont_rename_native_bindings; std::unordered_set<const DexType*> dont_rename_annotated; std::vector<std::function<void()>> fns{ [&] { force_rename_hierarchies = build_force_rename_hierarchies(mgr, scope, class_hierarchy); }, [&] { dont_rename_serde_relationships = build_dont_rename_serde_relationships(scope); }, [&] { dont_rename_class_name_literals = build_dont_rename_class_name_literals(scope); }, [&] { dont_rename_class_for_types_with_reflection = build_dont_rename_for_types_with_reflection( scope, conf.get_proguard_map()); }, [&] { dont_rename_canaries = build_dont_rename_canaries(scope); }, [&] { dont_rename_hierarchies = build_dont_rename_hierarchies(mgr, scope, class_hierarchy); }, [&] { dont_rename_native_bindings = build_dont_rename_native_bindings(scope); }, [&] { dont_rename_annotated = build_dont_rename_annotated(); }}; workqueue_run<std::function<void()>>([](std::function<void()>& fn) { fn(); }, fns); std::string norule; for (auto clazz : scope) { // Short circuit force renames if (force_rename_hierarchies.count(clazz->get_type())) { m_force_rename_classes.insert(clazz); continue; } // Don't rename annotations if (!rename_annotations && is_annotation(clazz)) { m_dont_rename_reasons[clazz] = {DontRenameReasonCode::Annotations, norule}; continue; } // Don't rename types annotated with anything in dont_rename_annotated bool annotated = false; for (const auto& anno : dont_rename_annotated) { if (has_anno(clazz, anno)) { m_dont_rename_reasons[clazz] = {DontRenameReasonCode::Annotated, anno->str()}; annotated = true; break; } } if (annotated) continue; const char* clsname = clazz->get_name()->c_str(); std::string strname = std::string(clsname); // Don't rename anything mentioned in resources. Two variants of checks here // to cover both configuration options (either we're relying on aapt to // compute resource reachability, or we're doing it ourselves). if (referenced_by_layouts(clazz) && !is_allowed_layout_class(clazz, m_allow_layout_rename_packages)) { m_dont_rename_reasons[clazz] = {DontRenameReasonCode::Resources, norule}; continue; } // Don't rename anythings in the direct name blocklist (hierarchy ignored) if (m_dont_rename_specific.count(clsname)) { m_dont_rename_reasons[clazz] = {DontRenameReasonCode::Specific, strname}; continue; } // Don't rename anything if it falls in an excluded package bool package_blocklisted = false; for (const auto& pkg : m_dont_rename_packages) { if (strname.rfind("L" + pkg) == 0) { TRACE(RENAME, 2, "%s excluded by pkg rule %s", clsname, pkg.c_str()); m_dont_rename_reasons[clazz] = {DontRenameReasonCode::Packages, pkg}; package_blocklisted = true; break; } } if (package_blocklisted) continue; if (dont_rename_class_name_literals.count(clsname)) { m_dont_rename_reasons[clazz] = {DontRenameReasonCode::ClassNameLiterals, norule}; continue; } if (dont_rename_class_for_types_with_reflection.count(clsname)) { m_dont_rename_reasons[clazz] = { DontRenameReasonCode::ClassForTypesWithReflection, norule}; continue; } if (dont_rename_canaries.count(clsname)) { m_dont_rename_reasons[clazz] = {DontRenameReasonCode::Canaries, norule}; continue; } if (dont_rename_native_bindings.count(clazz->get_type())) { m_dont_rename_reasons[clazz] = {DontRenameReasonCode::NativeBindings, norule}; continue; } if (dont_rename_hierarchies.count(clazz->get_type())) { std::string rule = dont_rename_hierarchies[clazz->get_type()]; m_dont_rename_reasons[clazz] = {DontRenameReasonCode::Hierarchy, rule}; continue; } if (dont_rename_serde_relationships.count(clazz->get_type())) { m_dont_rename_reasons[clazz] = {DontRenameReasonCode::SerdeRelationships, norule}; continue; } if (!can_rename_if_also_renaming_xml(clazz)) { const auto& keep_reasons = clazz->rstate.keep_reasons(); auto rule = !keep_reasons.empty() ? show(*keep_reasons.begin()) : ""; m_dont_rename_reasons[clazz] = {DontRenameReasonCode::ProguardCantRename, get_keep_rule(clazz)}; continue; } } } /** * We re-evaluate a number of config rules again at pass running time. * The reason is that the types specified in those rules can be created in * previous Redex passes and did not exist when the initial evaluation happened. */ void RenameClassesPassV2::eval_classes_post( Scope& scope, const ClassHierarchy& class_hierarchy, PassManager& mgr) { auto dont_rename_hierarchies = build_dont_rename_hierarchies(mgr, scope, class_hierarchy); for (auto clazz : scope) { if (m_dont_rename_reasons.find(clazz) != m_dont_rename_reasons.end()) { continue; } const char* clsname = clazz->get_name()->c_str(); std::string strname = std::string(clsname); // Don't rename anythings in the direct name blocklist (hierarchy ignored) if (m_dont_rename_specific.count(clsname)) { m_dont_rename_reasons[clazz] = {DontRenameReasonCode::Specific, strname}; continue; } // Don't rename anything if it falls in an excluded package bool package_blocklisted = false; for (const auto& pkg : m_dont_rename_packages) { if (strname.rfind("L" + pkg) == 0) { TRACE(RENAME, 2, "%s excluded by pkg rule %s", clsname, pkg.c_str()); m_dont_rename_reasons[clazz] = {DontRenameReasonCode::Packages, pkg}; package_blocklisted = true; break; } } if (package_blocklisted) continue; if (dont_rename_hierarchies.count(clazz->get_type())) { std::string rule = dont_rename_hierarchies[clazz->get_type()]; m_dont_rename_reasons[clazz] = {DontRenameReasonCode::Hierarchy, rule}; continue; } // Don't rename anything if something changed and the class cannot be // renamed anymore. if (!can_rename_if_also_renaming_xml(clazz)) { m_dont_rename_reasons[clazz] = {DontRenameReasonCode::ProguardCantRename, get_keep_rule(clazz)}; } } } void RenameClassesPassV2::eval_pass(DexStoresVector& stores, ConfigFiles& conf, PassManager& mgr) { const auto& json = conf.get_json_config(); json.get("apk_dir", "", m_apk_dir); TRACE(RENAME, 3, "APK Dir: %s", m_apk_dir.c_str()); auto scope = build_class_scope(stores); ClassHierarchy class_hierarchy = build_type_hierarchy(scope); eval_classes(scope, class_hierarchy, conf, m_rename_annotations, mgr); } std::unordered_set<DexClass*> RenameClassesPassV2::get_renamable_classes( Scope& scope) { std::unordered_set<DexClass*> renamable_classes; for (auto clazz : scope) { if (m_force_rename_classes.count(clazz) || !m_dont_rename_reasons.count(clazz)) { renamable_classes.insert(clazz); } } return renamable_classes; } void RenameClassesPassV2::rename_classes( Scope& scope, const std::unordered_set<DexClass*>& renamable_classes, PassManager& mgr) { rewriter::TypeStringMap name_mapping; uint32_t sequence = 0; for (auto clazz : scope) { auto dtype = clazz->get_type(); auto oldname = dtype->get_name(); if (m_force_rename_classes.count(clazz)) { mgr.incr_metric(METRIC_FORCE_RENAMED_CLASSES, 1); TRACE(RENAME, 2, "Forced renamed: '%s'", oldname->c_str()); } else if (m_dont_rename_reasons.find(clazz) != m_dont_rename_reasons.end()) { auto reason = m_dont_rename_reasons[clazz]; std::string metric = dont_rename_reason_to_metric(reason.code); mgr.incr_metric(metric, 1); if (dont_rename_reason_to_metric_per_rule(reason.code)) { std::string str = metric + "::" + std::string(reason.rule); mgr.incr_metric(str, 1); TRACE(RENAME, 2, "'%s' NOT RENAMED due to %s'", oldname->c_str(), str.c_str()); } else { TRACE(RENAME, 2, "'%s' NOT RENAMED due to %s'", oldname->c_str(), metric.c_str()); } sequence++; always_assert(!renamable_classes.count(clazz)); continue; } always_assert(renamable_classes.count(clazz)); mgr.incr_metric(METRIC_RENAMED_CLASSES, 1); char descriptor[Locator::encoded_global_class_index_max]; always_assert(sequence != Locator::invalid_global_class_index); Locator::encodeGlobalClassIndex(sequence, m_digits, descriptor); always_assert_log(facebook::Locator::decodeGlobalClassIndex(descriptor) == sequence, "global class index didn't roundtrip; %s generated from " "%u parsed to %u", descriptor, sequence, facebook::Locator::decodeGlobalClassIndex(descriptor)); sequence++; std::string prefixed_descriptor = prepend_package_prefix(descriptor); TRACE(RENAME, 2, "'%s' -> %s (%u)'", oldname->c_str(), prefixed_descriptor.c_str(), sequence); auto dstring = DexString::make_string(prefixed_descriptor); always_assert_log(!DexType::get_type(dstring), "Type name collision detected. %s already exists.", prefixed_descriptor.c_str()); name_mapping.add_type_name(clazz->get_name(), dstring); dtype->set_name(dstring); std::string old_str(oldname->c_str()); // std::string new_str(descriptor); // proguard_map.update_class_mapping(old_str, new_str); m_base_strings_size += strlen(oldname->c_str()); m_ren_strings_size += strlen(dstring->c_str()); while (1) { std::string arrayop("["); arrayop += oldname->c_str(); oldname = DexString::get_string(arrayop); if (oldname == nullptr) { break; } auto arraytype = DexType::get_type(oldname); if (arraytype == nullptr) { break; } std::string newarraytype("["); newarraytype += dstring->c_str(); dstring = DexString::make_string(newarraytype); arraytype->set_name(dstring); } } /* Now rewrite all const-string strings for force renamed classes. */ rewriter::TypeStringMap force_rename_map; for (const auto& pair : name_mapping.get_class_map()) { auto type = DexType::get_type(pair.first); if (!type) { continue; } auto clazz = type_class(type); if (clazz && m_force_rename_classes.count(clazz)) { force_rename_map.add_type_name(pair.first, pair.second); } } auto updated_instructions = rewriter::rewrite_string_literal_instructions(scope, force_rename_map); mgr.incr_metric(METRIC_REWRITTEN_CONST_STRINGS, updated_instructions); /* Now we need to re-write the Signature annotations. They use * Strings rather than Type's, so they have to be explicitly * handled. */ rewrite_dalvik_annotation_signature(scope, name_mapping); rename_classes_in_layouts(name_mapping, mgr); sanity_check(scope, name_mapping); } void RenameClassesPassV2::rename_classes_in_layouts( const rewriter::TypeStringMap& name_mapping, PassManager& mgr) { // Sync up ResStringPool entries in XML layouts. Class names should appear in // their "external" name, i.e. java.lang.String instead of Ljava/lang/String; std::map<std::string, std::string> aliases_for_layouts; for (const auto& apair : name_mapping.get_class_map()) { aliases_for_layouts.emplace( java_names::internal_to_external(apair.first->str()), java_names::internal_to_external(apair.second->str())); } auto resources = create_resource_reader(m_apk_dir); resources->rename_classes_in_layouts(aliases_for_layouts); } std::string RenameClassesPassV2::prepend_package_prefix( const char* descriptor) { always_assert_log(*descriptor == 'L', "Class descriptor \"%s\" did not start with L!\n", descriptor); descriptor++; // drop letter 'L' std::stringstream ss; ss << "L" << m_package_prefix << descriptor; return ss.str(); } std::unordered_set<DexClass*> RenameClassesPassV2::get_renamable_classes( Scope& scope, ConfigFiles& conf, PassManager& mgr) { if (mgr.no_proguard_rules()) { TRACE(RENAME, 1, "RenameClassesPassV2 not run because no ProGuard configuration was " "provided."); return {}; } if (!m_package_prefix.empty()) { always_assert_log( !(conf.get_json_config().get("emit_locator_strings", false)), "Rename classes package_prefix doesn't work together with " "emit_locator_strings.\n"); } ClassHierarchy class_hierarchy = build_type_hierarchy(scope); eval_classes_post(scope, class_hierarchy, mgr); always_assert_log(scope.size() < std::pow(Locator::global_class_index_digits_base, Locator::global_class_index_digits_max), "scope size %zu too large", scope.size()); int total_classes = scope.size(); mgr.incr_metric(METRIC_CLASSES_IN_SCOPE, total_classes); // encode the whole sequence as base 62: [0 - 9], [A - Z], [a - z] m_digits = std::ceil(std::log(total_classes) / std::log(Locator::global_class_index_digits_base)); TRACE(RENAME, 1, "Total classes in scope for renaming: %d chosen number of digits: %d", total_classes, m_digits); return get_renamable_classes(scope); } void RenameClassesPassV2::run_pass(DexStoresVector& stores, ConfigFiles& conf, PassManager& mgr) { auto scope = build_class_scope(stores); rename_classes(scope, get_renamable_classes(scope, conf, mgr), mgr); TRACE(RENAME, 1, "String savings, at least %d-%d = %d bytes ", m_base_strings_size, m_ren_strings_size, m_base_strings_size - m_ren_strings_size); }