source/Redex.cpp (259 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 <json/json.h> #include <Creators.h> #include <DexAccess.h> #include <DexClass.h> #include <DexUtil.h> #include <IRAssembler.h> #include <ProguardConfiguration.h> #include <ProguardMap.h> #include <ProguardMatcher.h> #include <ProguardParser.h> #include <Reachability.h> #include <RedexContext.h> #include <Resolver.h> #include <mariana-trench/Assert.h> #include <mariana-trench/JsonValidation.h> #include <mariana-trench/Log.h> #include <mariana-trench/Options.h> #include <mariana-trench/Redex.h> namespace marianatrench { DexClass* redex::get_class(const std::string& class_name) { auto* type = get_type(class_name); if (type) { return type_class(type); } return nullptr; } DexMethod* redex::get_method(const std::string& signature) { auto method_reference = DexMethod::get_method(signature); if (!method_reference) { return nullptr; } return method_reference->as_def(); } DexFieldRef* redex::get_field(const std::string& field) { return DexField::get_field(field); } DexType* redex::get_type(const std::string& type) { return DexType::get_type(type); } void redex::process_proguard_configurations( const Options& options, const DexStoresVector& stores) { /* Parse and register proguard configuration contents to redex global state. Used in global type analysis and removing unreachable paths. */ const std::vector<std::string>& proguard_configuration_paths = options.proguard_configuration_paths(); if (proguard_configuration_paths.empty()) { return; } keep_rules::ProguardConfiguration proguard_configuration; for (const auto& proguard_configuration_path : proguard_configuration_paths) { keep_rules::proguard_parser::parse_file( proguard_configuration_path, &proguard_configuration); } ProguardMap empty_map; for (auto& store : stores) { apply_deobfuscated_names(store.get_dexen(), empty_map); } keep_rules::process_proguard_rules( empty_map, build_class_scope(stores), g_redex->external_classes(), proguard_configuration, false); } void redex::remove_unreachable( const Options& options, DexStoresVector& stores) { const std::vector<std::string>& proguard_configuration_paths = options.proguard_configuration_paths(); const std::optional<boost::filesystem::path>& removed_symbols_path = options.removed_symbols_output_path(); keep_rules::ProguardConfiguration proguard_configuration; if (proguard_configuration_paths.empty()) { return; } auto reachables = reachability::compute_reachable_objects( stores, /* empty ignore sets */ reachability::IgnoreSets(), /* number of ignore check strings */ nullptr, /* emit_graph_this_run */ false); reachability::ObjectCounts before = reachability::count_objects(stores); LOG(1, "Removing unreachable code in {} classes, {} fields, {} methods.", before.num_classes, before.num_fields, before.num_methods); ConcurrentSet<std::string> removed_symbols; reachability::sweep( stores, *reachables, removed_symbols_path ? &removed_symbols : nullptr); if (removed_symbols_path) { auto value = Json::Value(Json::arrayValue); for (const auto& symbol : removed_symbols) { value.append(symbol); } JsonValidation::write_json_file(*removed_symbols_path, value); } reachability::ObjectCounts after = reachability::count_objects(stores); LOG(1, "Unreachables removed. {} classes, {} fields, {} methods are left.", after.num_classes, after.num_fields, after.num_methods); } std::vector<DexMethod*> redex::create_methods( Scope& scope, const std::string& class_name, const std::vector<DexMethodSpecification>& methods, const DexType* super) { std::vector<DexMethod*> dex_methods; auto* type = DexType::make_type(DexString::make_string(class_name)); ClassCreator creator(type); if (super) { creator.set_super(const_cast<DexType*>(super)); } else { creator.set_super(type::java_lang_Object()); } for (const auto& method : methods) { auto* dex_method = assembler::method_from_string(method.body); if (!method.annotations.empty()) { dex_method->make_non_concrete(); dex_method->set_external(); dex_method->attach_annotation_set( create_annotation_set(method.annotations)); } if (method.abstract) { dex_method->set_code(nullptr); } dex_methods.push_back(dex_method); creator.add_method(dex_method); } scope.push_back(creator.create()); return dex_methods; } std::vector<DexMethod*> redex::create_methods( Scope& scope, const std::string& class_name, const std::vector<std::string>& bodies, const DexType* super) { std::vector<DexMethodSpecification> methods; for (const auto& body : bodies) { methods.push_back(DexMethodSpecification{body}); } return create_methods(scope, class_name, methods, super); } DexMethod* redex::create_method( Scope& scope, const std::string& class_name, const std::string& body, const DexType* super, const bool abstract, const std::vector<std::string>& annotations) { auto method = DexMethodSpecification{ body, abstract, annotations, }; return create_methods(scope, class_name, {method}, super).front(); } DexMethod* redex::create_void_method( Scope& scope, const std::string& class_name, const std::string& method_name, const std::string& parameter_types, const std::string& return_type, const DexType* super, bool is_static, bool is_private, bool is_native, bool is_abstract, const std::vector<std::string>& annotations) { std::string access = is_private ? "private" : "public"; if (is_static) { access.append(" static"); } if (is_native) { access.append(" native"); } std::string return_statement = "(return-void)"; if (return_type != "V") { return_statement = R"( (new-instance "Ljava/lang/Object;") (move-result-pseudo-object v0) (return-object v0) )"; } auto body = fmt::format( R"( (method ({}) "{}.{}:({}){}" ( {} ) ) )", access, class_name, method_name, parameter_types, return_type, return_statement); auto* dex_method = create_method(scope, class_name, body, super, is_abstract, annotations); // Sanity checks if (!dex_method->is_external()) { mt_assert(::is_static(dex_method) == is_static); mt_assert(::is_private(dex_method) == is_private); mt_assert(::is_public(dex_method) == !is_private); mt_assert(::is_native(dex_method) == is_native); } return dex_method; } std::unique_ptr<DexAnnotationSet> redex::create_annotation_set( const std::vector<std::string>& annotations) { auto dannoset = std::make_unique<DexAnnotationSet>(); for (const std::string& anno : annotations) { const DexString* dstring = DexString::make_string(anno); DexType* dtype = DexType::make_type(dstring); dannoset->add_annotation(std::make_unique<DexAnnotation>( dtype, DexAnnotationVisibility::DAV_RUNTIME)); } return dannoset; } const DexField* redex::create_field( Scope& scope, const std::string& class_name, const DexFieldSpecification& field, const DexType* super, bool is_static) { return redex::create_fields(scope, class_name, {field}, super, is_static)[0]; } std::vector<const DexField*> redex::create_fields( Scope& scope, const std::string& class_name, const std::vector<DexFieldSpecification>& fields, const DexType* super, bool is_static) { std::vector<const DexField*> created_fields; auto* klass = DexType::make_type(DexString::make_string(class_name)); ClassCreator creator(klass); if (super) { creator.set_super(const_cast<DexType*>(super)); } else { creator.set_super(type::java_lang_Object()); } for (const auto& [field_name, field_type, field_annotations] : fields) { // Cast to DexField so that we can add annotations to it auto* field = static_cast<DexField*>(DexField::make_field( /* container */ klass, /* name */ DexString::make_string(field_name), /* type */ field_type)); field->attach_annotation_set( redex::create_annotation_set(field_annotations)); auto* concrete_field = field->make_concrete( is_static ? DexAccessFlags::ACC_STATIC : DexAccessFlags::ACC_PUBLIC, nullptr); creator.add_field(concrete_field); created_fields.push_back(concrete_field); } scope.push_back(creator.create()); return created_fields; } } // namespace marianatrench