self& trim() { //annotation if(is_annotation()) { array<self> content=array_(); //empty if(content.is_empty()) { clear(); return *this; } //paranoid check(atom(content.join("")).type()==type()); //update update(type().me(),content); return *this; } return *this; }
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; }
bool is_as_atomic(expr const & e) { return is_annotation(e, *g_as_atomic_name); }
bool is_as_is(expr const & e) { return is_annotation(e, *g_as_is_name); }
bool is_partial_explicit(expr const & e) { return is_annotation(e, *g_partial_explicit_name); }
bool is_consume_args(expr const & e) { return is_annotation(e, *g_consume_args_name); }
void RemoveBuildersPass::run_pass(DexStoresVector& stores, ConfigFiles&, PassManager& mgr) { if (mgr.no_proguard_rules()) { TRACE(BUILDERS, 1, "RemoveBuildersPass did not run because no Proguard configuration " "was provided."); return; } // Initialize couters. b_counter = {0, 0, 0, 0}; auto obj_type = get_object_type(); auto scope = build_class_scope(stores); for (DexClass* cls : scope) { if (is_annotation(cls) || is_interface(cls) || cls->get_super_class() != obj_type) { continue; } if (has_builder_name(cls->get_type())) { m_builders.emplace(cls->get_type()); } } std::unordered_set<DexType*> escaped_builders; walk::methods(scope, [&](DexMethod* m) { auto builders = created_builders(m); for (DexType* builder : builders) { if (escapes_stack(builder, m)) { TRACE(BUILDERS, 3, "%s escapes in %s\n", SHOW(builder), m->get_deobfuscated_name().c_str()); escaped_builders.emplace(builder); } } }); std::unordered_set<DexType*> stack_only_builders; for (DexType* builder : m_builders) { if (escaped_builders.find(builder) == escaped_builders.end()) { stack_only_builders.emplace(builder); } } std::unordered_set<DexType*> builders_and_supers; for (DexType* builder : stack_only_builders) { DexType* cls = builder; while (cls != nullptr && cls != obj_type) { builders_and_supers.emplace(cls); cls = type_class(cls)->get_super_class(); } } std::unordered_set<DexType*> this_escapes; for (DexType* cls_ty : builders_and_supers) { DexClass* cls = type_class(cls_ty); if (cls->is_external() || this_arg_escapes(cls, m_enable_buildee_constr_change)) { this_escapes.emplace(cls_ty); } } // set of builders that neither escape the stack nor pass their 'this' arg // to another function std::unordered_set<DexType*> no_escapes; for (DexType* builder : stack_only_builders) { DexType* cls = builder; bool hierarchy_has_escape = false; while (cls != nullptr) { if (this_escapes.find(cls) != this_escapes.end()) { hierarchy_has_escape = true; break; } cls = type_class(cls)->get_super_class(); } if (!hierarchy_has_escape) { no_escapes.emplace(builder); } } size_t dmethod_count = 0; size_t vmethod_count = 0; size_t build_count = 0; for (DexType* builder : no_escapes) { auto cls = type_class(builder); auto buildee = get_buildee(builder); dmethod_count += cls->get_dmethods().size(); vmethod_count += cls->get_vmethods().size(); for (DexMethod* m : cls->get_vmethods()) { if (m->get_proto()->get_rtype() == buildee) { build_count++; } } } std::unordered_set<DexClass*> trivial_builders = get_trivial_builders(m_builders, no_escapes); std::unordered_set<DexClass*> kept_builders = get_builders_with_subclasses(scope); PassConfig pc(mgr.get_config()); BuilderTransform b_transform(pc, scope, stores, false); // Inline non init methods. std::unordered_set<DexClass*> removed_builders; walk::methods(scope, [&](DexMethod* method) { auto builders = created_builders(method); for (DexType* builder : builders) { if (method->get_class() == builder) { continue; } DexClass* builder_cls = type_class(builder); // Filter out builders that we cannot remove. if (kept_builders.find(builder_cls) != kept_builders.end()) { continue; } // Check it is a trivial one. if (trivial_builders.find(builder_cls) != trivial_builders.end()) { DexMethod* method_copy = DexMethod::make_method_from( method, method->get_class(), DexString::make_string(method->get_name()->str() + "$redex_builders")); bool was_not_removed = !b_transform.inline_methods( method, builder, &get_non_init_methods) || !remove_builder_from(method, builder_cls, b_transform); if (was_not_removed) { kept_builders.emplace(builder_cls); method->set_code(method_copy->release_code()); } else { b_counter.methods_cleared++; removed_builders.emplace(builder_cls); } DexMethod::erase_method(method_copy); } } }); // No need to remove the builders here, since `RemoveUnreachable` will // take care of it. gather_removal_builder_stats(removed_builders, kept_builders); mgr.set_metric("total_builders", m_builders.size()); mgr.set_metric("stack_only_builders", stack_only_builders.size()); mgr.set_metric("no_escapes", no_escapes.size()); mgr.incr_metric(METRIC_CLASSES_REMOVED, b_counter.classes_removed); mgr.incr_metric(METRIC_METHODS_REMOVED, b_counter.methods_removed); mgr.incr_metric(METRIC_FIELDS_REMOVED, b_counter.fields_removed); mgr.incr_metric(METRIC_METHODS_CLEARED, b_counter.methods_cleared); TRACE(BUILDERS, 1, "Total builders: %d\n", m_builders.size()); TRACE(BUILDERS, 1, "Stack-only builders: %d\n", stack_only_builders.size()); TRACE(BUILDERS, 1, "Stack-only builders that don't let `this` escape: %d\n", no_escapes.size()); TRACE(BUILDERS, 1, "Stats for unescaping builders:\n"); TRACE(BUILDERS, 1, "\tdmethods: %d\n", dmethod_count); TRACE(BUILDERS, 1, "\tvmethods: %d\n", vmethod_count); TRACE(BUILDERS, 1, "\tbuild methods: %d\n", build_count); TRACE(BUILDERS, 1, "Trivial builders: %d\n", trivial_builders.size()); TRACE(BUILDERS, 1, "Classes removed: %d\n", b_counter.classes_removed); TRACE(BUILDERS, 1, "Methods removed: %d\n", b_counter.methods_removed); TRACE(BUILDERS, 1, "Fields removed: %d\n", b_counter.fields_removed); TRACE(BUILDERS, 1, "Methods cleared: %d\n", b_counter.methods_cleared); }
bool is_by(expr const & e) { return is_annotation(e, *g_by_name); }
ClassSpecification parse_class_specification( std::vector<unique_ptr<Token>>::iterator* it, bool* ok) { ClassSpecification class_spec; *ok = true; class_spec.annotationType = parse_annotation_type(it); if (!parse_access_flags( it, class_spec.setAccessFlags, class_spec.unsetAccessFlags)) { // There was a problem parsing the access flags. Return an empty class spec // for now. std::cerr << "Problem parsing access flags for class specification.\n"; *ok = false; return class_spec; } // According to the ProGuard grammer the next token could be a '!' to express // a rule that // says !class or !interface or !enum. We choose to not implement this // feature. if ((**it)->type == token::notToken) { cerr << "Keep rules that match the negation of class, interface or enum " "are not supported.\n"; *ok = false; return class_spec; } bool match_annotation_class = is_annotation(class_spec.setAccessFlags); if (!match_annotation_class) { // Make sure the next keyword is interface, class, enum. if (!(((**it)->type == token::interface) || ((**it)->type == token::classToken) || ((**it)->type == token::enumToken || ((**it)->type == token::annotation)))) { cerr << "Expected interface, class or enum but got " << (**it)->show() << " at line number " << (**it)->line << endl; *ok = false; return class_spec; } // Restrict matches to interface classes if ((**it)->type == token::interface) { set_access_flag(class_spec.setAccessFlags, ACC_INTERFACE); } // Restrict matches to enum classes if ((**it)->type == token::enumToken) { set_access_flag(class_spec.setAccessFlags, ACC_ENUM); } ++(*it); } // Parse the class name. if ((**it)->type != token::identifier) { cerr << "Expected class name but got " << (**it)->show() << " at line " << (**it)->line << endl; *ok = false; return class_spec; } class_spec.className = static_cast<Identifier*>((*it)->get())->ident; ++(*it); // Parse extends/implements if present, treating implements like extends. if (((**it)->type == token::extends) || ((**it)->type == token::implements)) { ++(*it); class_spec.extendsAnnotationType = parse_annotation_type(it); if ((**it)->type != token::identifier) { cerr << "Expecting a class name after extends/implements but got " << (**it)->show() << " at line " << (**it)->line << endl; *ok = false; class_spec.extendsClassName = ""; } else { class_spec.extendsClassName = static_cast<Identifier*>((*it)->get())->ident; } ++(*it); } // Parse the member specifications, if there are any parse_member_specifications(it, &class_spec, ok); std::sort(class_spec.fieldSpecifications.begin(), class_spec.fieldSpecifications.end(), member_comparison); std::sort(class_spec.methodSpecifications.begin(), class_spec.methodSpecifications.end(), member_comparison); return class_spec; }