diff options
author | 2022-12-01 12:33:42 +0000 | |
---|---|---|
committer | 2022-12-01 12:33:42 +0000 | |
commit | 6757d436d9b6c5eaf4c6e518a0e174d89cd6a8c6 (patch) | |
tree | 18d0e5bc5a5b671425507f0920faec031ec377ab /tools/aapt2/optimize | |
parent | 026072cdb730d27559222937e99daf5950ca1e44 (diff) | |
parent | 8ceb39c94f3269dc91ff80eafdc31c12455bd019 (diff) |
Merge "Use lambda to refactor the obfuscating resource name."
Diffstat (limited to 'tools/aapt2/optimize')
-rw-r--r-- | tools/aapt2/optimize/Obfuscator.cpp | 95 | ||||
-rw-r--r-- | tools/aapt2/optimize/Obfuscator.h | 24 | ||||
-rw-r--r-- | tools/aapt2/optimize/Obfuscator_test.cpp | 94 |
3 files changed, 197 insertions, 16 deletions
diff --git a/tools/aapt2/optimize/Obfuscator.cpp b/tools/aapt2/optimize/Obfuscator.cpp index 1fdd728928df..2533f8079c39 100644 --- a/tools/aapt2/optimize/Obfuscator.cpp +++ b/tools/aapt2/optimize/Obfuscator.cpp @@ -16,6 +16,7 @@ #include "optimize/Obfuscator.h" +#include <map> #include <set> #include <string> #include <unordered_set> @@ -32,7 +33,10 @@ static const char base64_chars[] = namespace aapt { -Obfuscator::Obfuscator(std::map<std::string, std::string>& path_map_out) : path_map_(path_map_out) { +Obfuscator::Obfuscator(OptimizeOptions& optimizeOptions) + : options_(optimizeOptions.table_flattener_options), + shorten_resource_paths_(optimizeOptions.shorten_resource_paths), + collapse_key_stringpool_(optimizeOptions.table_flattener_options.collapse_key_stringpool) { } std::string ShortenFileName(android::StringPiece file_path, int output_length) { @@ -77,7 +81,8 @@ struct PathComparator { } }; -bool Obfuscator::Consume(IAaptContext* context, ResourceTable* table) { +static bool HandleShortenFilePaths(ResourceTable* table, + std::map<std::string, std::string>& shortened_path_map) { // used to detect collisions std::unordered_set<std::string> shortened_paths; std::set<FileReference*, PathComparator> file_refs; @@ -109,10 +114,94 @@ bool Obfuscator::Consume(IAaptContext* context, ResourceTable* table) { shortened_path = GetShortenedPath(shortened_filename, extension, collision_count); } shortened_paths.insert(shortened_path); - path_map_.insert({*file_ref->path, shortened_path}); + shortened_path_map.insert({*file_ref->path, shortened_path}); file_ref->path = table->string_pool.MakeRef(shortened_path, file_ref->path.GetContext()); } return true; } +void Obfuscator::ObfuscateResourceName( + const bool collapse_key_stringpool, const std::set<ResourceName>& name_collapse_exemptions, + const ResourceNamedType& type_name, const ResourceTableEntryView& entry, + const android::base::function_ref<void(Result obfuscatedResult, const ResourceName&)> + onObfuscate) { + ResourceName resource_name({}, type_name, entry.name); + if (!collapse_key_stringpool || + name_collapse_exemptions.find(resource_name) != name_collapse_exemptions.end()) { + onObfuscate(Result::Keep_ExemptionList, resource_name); + } else { + // resource isn't exempt from collapse, add it as obfuscated value + if (entry.overlayable_item) { + // if the resource name of the specific entry is obfuscated and this + // entry is in the overlayable list, the overlay can't work on this + // overlayable at runtime because the name has been obfuscated in + // resources.arsc during flatten operation. + onObfuscate(Result::Keep_Overlayable, resource_name); + } else { + onObfuscate(Result::Obfuscated, resource_name); + } + } +} + +static bool HandleCollapseKeyStringPool( + const ResourceTable* table, const bool collapse_key_string_pool, + const std::set<ResourceName>& name_collapse_exemptions, + std::unordered_map<uint32_t, std::string>& id_resource_map) { + if (!collapse_key_string_pool) { + return true; + } + + int entryResId = 0; + auto onObfuscate = [&entryResId, &id_resource_map](const Obfuscator::Result obfuscatedResult, + const ResourceName& resource_name) { + if (obfuscatedResult == Obfuscator::Result::Obfuscated) { + id_resource_map.insert({entryResId, resource_name.entry}); + } + }; + + for (auto& package : table->packages) { + for (auto& type : package->types) { + for (auto& entry : type->entries) { + if (!entry->id.has_value() || entry->name.empty()) { + continue; + } + entryResId = entry->id->id; + ResourceTableEntryView entry_view{ + .name = entry->name, + .id = entry->id ? entry->id.value().entry_id() : (std::optional<uint16_t>)std::nullopt, + .visibility = entry->visibility, + .allow_new = entry->allow_new, + .overlayable_item = entry->overlayable_item, + .staged_id = entry->staged_id}; + + Obfuscator::ObfuscateResourceName(collapse_key_string_pool, name_collapse_exemptions, + type->named_type, entry_view, onObfuscate); + } + } + } + + return true; +} + +bool Obfuscator::Consume(IAaptContext* context, ResourceTable* table) { + HandleCollapseKeyStringPool(table, options_.collapse_key_stringpool, + options_.name_collapse_exemptions, options_.id_resource_map); + if (shorten_resource_paths_) { + return HandleShortenFilePaths(table, options_.shortened_path_map); + } + return true; +} + +/** + * Tell the optimizer whether it's needed to dump information for de-obfuscating. + * + * There are two conditions need to dump the information for de-obfuscating. + * * the option of shortening file paths is enabled. + * * the option of collapsing resource names is enabled. + * @return true if the information needed for de-obfuscating, otherwise false + */ +bool Obfuscator::IsEnabled() const { + return shorten_resource_paths_ || collapse_key_stringpool_; +} + } // namespace aapt diff --git a/tools/aapt2/optimize/Obfuscator.h b/tools/aapt2/optimize/Obfuscator.h index 1ea32db12815..786bf8c9ed93 100644 --- a/tools/aapt2/optimize/Obfuscator.h +++ b/tools/aapt2/optimize/Obfuscator.h @@ -17,10 +17,14 @@ #ifndef TOOLS_AAPT2_OPTIMIZE_OBFUSCATOR_H_ #define TOOLS_AAPT2_OPTIMIZE_OBFUSCATOR_H_ -#include <map> +#include <set> #include <string> +#include "ResourceTable.h" +#include "android-base/function_ref.h" #include "android-base/macros.h" +#include "cmd/Optimize.h" +#include "format/binary/TableFlattener.h" #include "process/IResourceTableConsumer.h" namespace aapt { @@ -30,12 +34,26 @@ class ResourceTable; // Maps resources in the apk to shortened paths. class Obfuscator : public IResourceTableConsumer { public: - explicit Obfuscator(std::map<std::string, std::string>& path_map_out); + explicit Obfuscator(OptimizeOptions& optimizeOptions); bool Consume(IAaptContext* context, ResourceTable* table) override; + bool IsEnabled() const; + + enum class Result { Obfuscated, Keep_ExemptionList, Keep_Overlayable }; + + // hardcoded string uses characters which make it an invalid resource name + static constexpr char kObfuscatedResourceName[] = "0_resource_name_obfuscated"; + + static void ObfuscateResourceName( + const bool collapse_key_stringpool, const std::set<ResourceName>& name_collapse_exemptions, + const ResourceNamedType& type_name, const ResourceTableEntryView& entry, + const android::base::function_ref<void(Result, const ResourceName&)> onObfuscate); + private: - std::map<std::string, std::string>& path_map_; + TableFlattenerOptions& options_; + const bool shorten_resource_paths_; + const bool collapse_key_stringpool_; DISALLOW_COPY_AND_ASSIGN(Obfuscator); }; diff --git a/tools/aapt2/optimize/Obfuscator_test.cpp b/tools/aapt2/optimize/Obfuscator_test.cpp index a3339d486d4a..17d1e5262b76 100644 --- a/tools/aapt2/optimize/Obfuscator_test.cpp +++ b/tools/aapt2/optimize/Obfuscator_test.cpp @@ -16,6 +16,7 @@ #include "optimize/Obfuscator.h" +#include <map> #include <memory> #include <string> @@ -51,8 +52,9 @@ TEST(ObfuscatorTest, FileRefPathsChangedInResourceTable) { .AddString("android:string/string", "res/should/still/be/the/same.png") .Build(); - std::map<std::string, std::string> path_map; - ASSERT_TRUE(Obfuscator(path_map).Consume(context.get(), table.get())); + OptimizeOptions options{.shorten_resource_paths = true}; + std::map<std::string, std::string>& path_map = options.table_flattener_options.shortened_path_map; + ASSERT_TRUE(Obfuscator(options).Consume(context.get(), table.get())); // Expect that the path map is populated ASSERT_THAT(path_map.find("res/drawables/xmlfile.xml"), Not(Eq(path_map.end()))); @@ -87,8 +89,9 @@ TEST(ObfuscatorTest, SkipColorFileRefPaths) { test::ParseConfigOrDie("mdp-v21")) .Build(); - std::map<std::string, std::string> path_map; - ASSERT_TRUE(Obfuscator(path_map).Consume(context.get(), table.get())); + OptimizeOptions options{.shorten_resource_paths = true}; + std::map<std::string, std::string>& path_map = options.table_flattener_options.shortened_path_map; + ASSERT_TRUE(Obfuscator(options).Consume(context.get(), table.get())); // Expect that the path map to not contain the ColorStateList ASSERT_THAT(path_map.find("res/color/colorlist.xml"), Eq(path_map.end())); @@ -107,8 +110,9 @@ TEST(ObfuscatorTest, KeepExtensions) { .AddFileReference("android:color/pngfile", original_png_path) .Build(); - std::map<std::string, std::string> path_map; - ASSERT_TRUE(Obfuscator(path_map).Consume(context.get(), table.get())); + OptimizeOptions options{.shorten_resource_paths = true}; + std::map<std::string, std::string>& path_map = options.table_flattener_options.shortened_path_map; + ASSERT_TRUE(Obfuscator(options).Consume(context.get(), table.get())); // Expect that the path map is populated ASSERT_THAT(path_map.find("res/drawable/xmlfile.xml"), Not(Eq(path_map.end()))); @@ -133,8 +137,10 @@ TEST(ObfuscatorTest, DeterministicallyHandleCollisions) { test::ResourceTableBuilder builder1; FillTable(builder1, 0, kNumResources); std::unique_ptr<ResourceTable> table1 = builder1.Build(); - std::map<std::string, std::string> expected_mapping; - ASSERT_TRUE(Obfuscator(expected_mapping).Consume(context.get(), table1.get())); + OptimizeOptions options{.shorten_resource_paths = true}; + std::map<std::string, std::string>& expected_mapping = + options.table_flattener_options.shortened_path_map; + ASSERT_TRUE(Obfuscator(options).Consume(context.get(), table1.get())); // We are trying to ensure lack of non-determinism, it is not simple to prove // a negative, thus we must try the test a few times so that the test itself @@ -153,8 +159,10 @@ TEST(ObfuscatorTest, DeterministicallyHandleCollisions) { FillTable(builder2, 0, start_index); std::unique_ptr<ResourceTable> table2 = builder2.Build(); - std::map<std::string, std::string> actual_mapping; - ASSERT_TRUE(Obfuscator(actual_mapping).Consume(context.get(), table2.get())); + OptimizeOptions actualOptimizerOptions{.shorten_resource_paths = true}; + TableFlattenerOptions& actual_options = actualOptimizerOptions.table_flattener_options; + std::map<std::string, std::string>& actual_mapping = actual_options.shortened_path_map; + ASSERT_TRUE(Obfuscator(actualOptimizerOptions).Consume(context.get(), table2.get())); for (auto& item : actual_mapping) { ASSERT_THAT(expected_mapping[item.first], Eq(item.second)); @@ -162,4 +170,70 @@ TEST(ObfuscatorTest, DeterministicallyHandleCollisions) { } } +TEST(ObfuscatorTest, DumpIdResourceMap) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + + OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme")); + overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION; + overlayable_item.policies |= PolicyFlags::SYSTEM_PARTITION; + overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION; + + std::string original_xml_path = "res/drawable/xmlfile.xml"; + std::string original_png_path = "res/drawable/pngfile.png"; + + std::string name = "com.app.test:string/overlayable"; + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .AddFileReference("android:color/xmlfile", original_xml_path) + .AddFileReference("android:color/pngfile", original_png_path) + .AddValue("com.app.test:color/mycolor", aapt::ResourceId(0x7f020000), + aapt::util::make_unique<aapt::BinaryPrimitive>( + uint8_t(android::Res_value::TYPE_INT_COLOR_ARGB8), 0xffaabbcc)) + .AddString("com.app.test:string/mystring", ResourceId(0x7f030000), "hi") + .AddString("com.app.test:string/in_exemption", ResourceId(0x7f030001), "Hi") + .AddString(name, ResourceId(0x7f030002), "HI") + .SetOverlayable(name, overlayable_item) + .Build(); + + OptimizeOptions options{.shorten_resource_paths = true}; + TableFlattenerOptions& flattenerOptions = options.table_flattener_options; + flattenerOptions.collapse_key_stringpool = true; + flattenerOptions.name_collapse_exemptions.insert( + ResourceName({}, ResourceType::kString, "in_exemption")); + auto& id_resource_map = flattenerOptions.id_resource_map; + ASSERT_TRUE(Obfuscator(options).Consume(context.get(), table.get())); + + // Expect that the id resource name map is populated + EXPECT_THAT(id_resource_map.at(0x7f020000), Eq("mycolor")); + EXPECT_THAT(id_resource_map.at(0x7f030000), Eq("mystring")); + EXPECT_THAT(id_resource_map.find(0x7f030001), Eq(id_resource_map.end())); + EXPECT_THAT(id_resource_map.find(0x7f030002), Eq(id_resource_map.end())); +} + +TEST(ObfuscatorTest, IsEnabledWithDefaultOption) { + OptimizeOptions options; + Obfuscator obfuscatorWithDefaultOption(options); + ASSERT_THAT(obfuscatorWithDefaultOption.IsEnabled(), Eq(false)); +} + +TEST(ObfuscatorTest, IsEnabledWithShortenPathOption) { + OptimizeOptions options{.shorten_resource_paths = true}; + Obfuscator obfuscatorWithShortenPathOption(options); + ASSERT_THAT(obfuscatorWithShortenPathOption.IsEnabled(), Eq(true)); +} + +TEST(ObfuscatorTest, IsEnabledWithCollapseStringPoolOption) { + OptimizeOptions options; + options.table_flattener_options.collapse_key_stringpool = true; + Obfuscator obfuscatorWithCollapseStringPoolOption(options); + ASSERT_THAT(obfuscatorWithCollapseStringPoolOption.IsEnabled(), Eq(true)); +} + +TEST(ObfuscatorTest, IsEnabledWithShortenPathAndCollapseStringPoolOption) { + OptimizeOptions options{.shorten_resource_paths = true}; + options.table_flattener_options.collapse_key_stringpool = true; + Obfuscator obfuscatorWithCollapseStringPoolOption(options); + ASSERT_THAT(obfuscatorWithCollapseStringPoolOption.IsEnabled(), Eq(true)); +} + } // namespace aapt |