void reportReachableClasses(const Scope& scope, std::string reportFileName) { TRACE(PGR, 4, "Total numner of classes: %d\n", scope.size()); // Report classes that the reflection filter says can't be deleted. std::ofstream reportFileCanDelete(reportFileName + ".cant_delete"); for (auto const& cls : scope) { if (!can_delete(cls)) { reportFileCanDelete << cls->get_name()->c_str() << "\n"; } } reportFileCanDelete.close(); // Report classes that the reflection filter says can't be renamed. std::ofstream reportFileCanRename(reportFileName + ".cant_rename"); for (auto const& cls : scope) { if (!can_rename(cls)) { reportFileCanRename << cls->get_name()->c_str() << "\n"; } } reportFileCanRename.close(); // Report classes marked for keep from ProGuard flat file list. std::ofstream reportFileKeep(reportFileName + ".must_keep"); for (auto const& cls : scope) { if (is_seed(cls)) { reportFileKeep << cls->get_name()->c_str() << "\n"; } } reportFileKeep.close(); }
bool can_rename_scope(const VirtualScope* scope) { for (const auto& vmeth : scope->methods) { if (!can_rename(vmeth.first) || (vmeth.second & ESCAPED) != 0) { return false; } } return true; }
bool should_rename(DexClass *clazz, std::vector<std::string>& pre_patterns, std::vector<std::string>& post_patterns, std::unordered_set<const DexType*>& untouchables, bool rename_annotations, PassManager& mgr) { if (!rename_annotations && is_annotation(clazz)) { mgr.incr_metric(METRIC_CANT_RENAME_ANNOTATION, 1); return false; } if (untouchables.count(clazz->get_type())) { mgr.incr_metric(METRIC_CANT_RENAME_UNTOUCHABLE, 1); return false; } auto chstring = clazz->get_type()->get_name()->c_str(); /* We're assuming anonymous classes are safe always safe to rename. */ auto last_cash = strrchr(chstring, '$'); if (last_cash != nullptr) { auto val = *++last_cash; if (val >= '0' && val <= '9') { match_inner++; return true; } } /* Check for more aggressive, but finer grained filters first */ for (auto p : pre_patterns) { auto substr = strstr(chstring, p.c_str()); if (substr != nullptr) { if (p.length() > 1) { match_long++; } else { match_short++; } return true; } } if (!can_rename(clazz) && !can_delete(clazz)) { mgr.incr_metric(METRIC_CANT_RENAME_AND_CANT_DELETE, 1); return false; } /* Check for wider, less precise filters */ for (auto p : post_patterns) { auto substr = strstr(chstring, p.c_str()); if (substr != nullptr) { if (p.length() > 1) { match_long++; } else { match_short++; } return true; } } mgr.incr_metric(METRIC_NOT_WHITELISTED, 1); return false; }
bool should_rename(DexClass *clazz, std::vector<std::string>& pre_patterns, std::vector<std::string>& post_patterns, std::unordered_set<const DexType*>& untouchables, bool rename_annotations) { if (!rename_annotations && is_annotation(clazz)) return false; if (untouchables.count(clazz->get_type())) return false; auto chstring = clazz->get_type()->get_name()->c_str(); /* We're assuming anonymous classes are safe always safe to rename. */ auto substr = strrchr(chstring, '$'); if (substr != nullptr) { auto val = *++substr; if (val >= '0' && val <= '9') { match_inner++; return true; } } /* Check for more aggressive, but finer grained filters first */ for (auto p : pre_patterns) { auto substr = strstr(chstring, p.c_str()); if (substr != nullptr) { if (p.length() > 1) { match_long++; } else { match_short++; } return true; } } if (!can_rename(clazz) && !can_delete(clazz)) { return false; } /* Check for wider, less precise filters */ for (auto p : post_patterns) { auto substr = strstr(chstring, p.c_str()); if (substr != nullptr) { if (p.length() > 1) { match_long++; } else { match_short++; } return true; } } return false; }
/** * Remove any chance for collisions. */ void OptimizationImpl::rename_possible_collisions( DexType* intf, SingleImplData& data) { const auto& rename = [](DexMethodRef* meth, DexString* name) { DexMethodSpec spec; spec.cls = meth->get_class(); spec.name = name; spec.proto = meth->get_proto(); meth->change(spec, false); }; TRACE(INTF, 9, "Changing name related to %s\n", SHOW(intf)); for (const auto& meth : data.methoddefs) { if (!can_rename(meth)) { TRACE(INTF, 9, "Changing name but cannot rename %s, give up\n", SHOW(meth)); return; } } std::unordered_map<DexString*, DexString*> names; const auto new_name = [&](DexString* name) { const auto& name_it = names.find(name); if (name_it != names.end()) { return name_it->second; } DexString* possible_name = nullptr; static std::string sufx("$r"); static int intf_id = 0; while(true) { const auto& str = name->str() + sufx + std::to_string(intf_id++); possible_name = DexString::get_string(str.c_str()); if (possible_name == nullptr) { possible_name = DexString::make_string(str.c_str()); break; } } names[name] = possible_name; return possible_name; }; for (const auto& meth : data.methoddefs) { if (is_constructor(meth)) continue; auto name = new_name(meth->get_name()); TRACE(INTF, 9, "Changing name for %s to %s\n", SHOW(meth), SHOW(name)); rename(meth, name); } for (const auto& refs_it : data.methodrefs) { if (refs_it.first->is_def()) continue; static auto init = DexString::make_string("<init>"); always_assert(refs_it.first->get_name() != init); auto name = new_name(refs_it.first->get_name()); TRACE(INTF, 9, "Changing name for %s to %s\n", SHOW(refs_it.first), SHOW(name)); if (names.count(refs_it.first->get_name()) == 0) { TRACE(INTF, 9, "Changing name on missing method def %s", SHOW(refs_it.first)); } rename(refs_it.first, name); } }