Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e50588e authored by Luke Nicholson's avatar Luke Nicholson Committed by Android (Google) Code Review
Browse files

Merge "Enable obfuscation of resource names, with whitelisting support."

parents e888a8c6 b0643302
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <memory>
#include <vector>

#include "android-base/file.h"
#include "android-base/stringprintf.h"

#include "androidfw/ResourceTypes.h"
@@ -47,6 +48,7 @@ using ::aapt::configuration::Artifact;
using ::aapt::configuration::PostProcessingConfiguration;
using ::android::ResTable_config;
using ::android::StringPiece;
using ::android::base::ReadFileToString;
using ::android::base::StringAppendF;
using ::android::base::StringPrintf;

@@ -279,6 +281,20 @@ class OptimizeCommand {
  OptimizeContext* context_;
};

bool ExtractWhitelistFromConfig(const std::string& path, OptimizeContext* context,
                                OptimizeOptions* options) {
  std::string contents;
  if (!ReadFileToString(path, &contents, true)) {
    context->GetDiagnostics()->Error(DiagMessage()
                                     << "failed to parse whitelist from config file: " << path);
    return false;
  }
  for (const StringPiece& resource_name : util::Tokenize(contents, ',')) {
    options->table_flattener_options.whitelisted_resources.insert(resource_name.to_string());
  }
  return true;
}

bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
                                OptimizeOptions* out_options) {
  const xml::XmlResource* manifest = apk->GetManifest();
@@ -302,6 +318,7 @@ int Optimize(const std::vector<StringPiece>& args) {
  OptimizeContext context;
  OptimizeOptions options;
  Maybe<std::string> config_path;
  Maybe<std::string> whitelist_path;
  Maybe<std::string> target_densities;
  Maybe<std::string> target_abis;
  std::vector<std::string> configs;
@@ -320,6 +337,10 @@ int Optimize(const std::vector<StringPiece>& args) {
              "All the resources that would be unused on devices of the given densities will be \n"
              "removed from the APK.",
              &target_densities)
          .OptionalFlag("--whitelist-config-path",
                        "Path to the whitelist.cfg file containing whitelisted resources \n"
                        "whose names should not be altered in final resource tables.",
                        &whitelist_path)
          .OptionalFlag(
              "--target-abis",
              "Comma separated list of the CPU ABIs that the APK will be optimized for.\n"
@@ -339,6 +360,9 @@ int Optimize(const std::vector<StringPiece>& args) {
                          "Enables encoding sparse entries using a binary search tree.\n"
                          "This decreases APK size at the cost of resource retrieval performance.",
                          &options.table_flattener_options.use_sparse_entries)
          .OptionalSwitch("--enable-resource-obfuscation",
                          "Enables obfuscation of key string pool to single value",
                          &options.table_flattener_options.collapse_key_stringpool)
          .OptionalSwitch("-v", "Enables verbose logging", &verbose);

  if (!flags.Parse("aapt2 optimize", args, &std::cerr)) {
@@ -425,6 +449,15 @@ int Optimize(const std::vector<StringPiece>& args) {
    return 1;
  }

  if (options.table_flattener_options.collapse_key_stringpool) {
    if (whitelist_path) {
      std::string& path = whitelist_path.value();
      if (!ExtractWhitelistFromConfig(path, &context, &options)) {
        return 1;
      }
    }
  }

  if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
    return 1;
  }
+22 −6
Original line number Diff line number Diff line
@@ -220,12 +220,15 @@ class MapFlattenVisitor : public ValueVisitor {
class PackageFlattener {
 public:
  PackageFlattener(IAaptContext* context, ResourceTablePackage* package,
                   const std::map<size_t, std::string>* shared_libs, bool use_sparse_entries)
                   const std::map<size_t, std::string>* shared_libs, bool use_sparse_entries,
                   bool collapse_key_stringpool, const std::set<std::string>& whitelisted_resources)
      : context_(context),
        diag_(context->GetDiagnostics()),
        package_(package),
        shared_libs_(shared_libs),
        use_sparse_entries_(use_sparse_entries) {
        use_sparse_entries_(use_sparse_entries),
        collapse_key_stringpool_(collapse_key_stringpool),
        whitelisted_resources_(whitelisted_resources) {
  }

  bool FlattenPackage(BigBuffer* buffer) {
@@ -494,13 +497,23 @@ class PackageFlattener {
      // configuration available. Here we reverse this to match the binary
      // table.
      std::map<ConfigDescription, std::vector<FlatEntry>> config_to_entry_list_map;
      for (ResourceEntry* entry : sorted_entries) {
        const uint32_t key_index = (uint32_t)key_pool_.MakeRef(entry->name).index();

      // hardcoded string uses characters which make it an invalid resource name
      const std::string obfuscated_resource_name = "0_resource_name_obfuscated";

      for (ResourceEntry* entry : sorted_entries) {
        uint32_t local_key_index;
        if (!collapse_key_stringpool_ ||
            whitelisted_resources_.find(entry->name) != whitelisted_resources_.end()) {
          local_key_index = (uint32_t)key_pool_.MakeRef(entry->name).index();
        } else {
          // resource isn't whitelisted, add it as obfuscated value
          local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
        }
        // Group values by configuration.
        for (auto& config_value : entry->values) {
          config_to_entry_list_map[config_value->config].push_back(
              FlatEntry{entry, config_value->value.get(), key_index});
              FlatEntry{entry, config_value->value.get(), local_key_index});
        }
      }

@@ -549,6 +562,8 @@ class PackageFlattener {
  bool use_sparse_entries_;
  StringPool type_pool_;
  StringPool key_pool_;
  bool collapse_key_stringpool_;
  const std::set<std::string>& whitelisted_resources_;
};

}  // namespace
@@ -593,7 +608,8 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
    }

    PackageFlattener flattener(context, package.get(), &table->included_packages_,
                               options_.use_sparse_entries);
                               options_.use_sparse_entries, options_.collapse_key_stringpool,
                               options_.whitelisted_resources);
    if (!flattener.FlattenPackage(&package_buffer)) {
      return false;
    }
+8 −0
Original line number Diff line number Diff line
@@ -35,6 +35,14 @@ struct TableFlattenerOptions {
  // This is only available on platforms O+ and will only be respected when
  // minSdk is O+.
  bool use_sparse_entries = false;

  // When true, the key string pool in the final ResTable
  // is collapsed to a single entry. All resource entries
  // have name indices that point to this single value
  bool collapse_key_stringpool = false;

  // Set of whitelisted resource names to avoid altering in key stringpool
  std::set<std::string> whitelisted_resources;
};

class TableFlattener : public IResourceTableConsumer {
+118 −0
Original line number Diff line number Diff line
@@ -127,6 +127,15 @@ class TableFlattenerTest : public ::testing::Test {
             << StringPiece16(actual_name.name, actual_name.nameLen) << "'";
    }

    ResourceName actual_res_name(resName.value());

    if (expected_res_name.entry != actual_res_name.entry ||
        expected_res_name.package != actual_res_name.package ||
        expected_res_name.type != actual_res_name.type) {
      return ::testing::AssertionFailure() << "expected resource '" << expected_res_name.to_string()
                                           << "' but got '" << actual_res_name.to_string() << "'";
    }

    if (expected_config != config) {
      return ::testing::AssertionFailure() << "expected config '" << expected_config
                                           << "' but got '" << ConfigDescription(config) << "'";
@@ -450,4 +459,113 @@ TEST_F(TableFlattenerTest, LongSharedLibraryPackageNameIsIllegal) {
  ASSERT_FALSE(Flatten(context.get(), {}, table.get(), &result));
}

TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) {
  std::unique_ptr<ResourceTable> table =
      test::ResourceTableBuilder()
          .SetPackageId("com.app.test", 0x7f)
          .AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
          .AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
          .AddValue("com.app.test:id/three", ResourceId(0x7f020002),
                    test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
          .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
          .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
                    ResourceId(0x7f030000),
                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
          .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
          .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
          .Build();

  TableFlattenerOptions options;
  options.collapse_key_stringpool = true;

  ResTable res_table;

  ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));

  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
                     ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));

  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
                     ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));

  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
                     ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));

  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
                     ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
                     ResTable_config::CONFIG_VERSION));

  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
                     ResourceId(0x7f030000), test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC,
                     2u, ResTable_config::CONFIG_VERSION));

  std::u16string foo_str = u"foo";
  ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
  ASSERT_GE(idx, 0);
  EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated",
                     ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));

  std::u16string bar_path = u"res/layout/bar.xml";
  idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
  ASSERT_GE(idx, 0);
  EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
                     ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
}

TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) {
  std::unique_ptr<ResourceTable> table =
      test::ResourceTableBuilder()
          .SetPackageId("com.app.test", 0x7f)
          .AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
          .AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
          .AddValue("com.app.test:id/three", ResourceId(0x7f020002),
                    test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
          .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
          .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
                    ResourceId(0x7f030000),
                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
          .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
          .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
          .Build();

  TableFlattenerOptions options;
  options.collapse_key_stringpool = true;
  options.whitelisted_resources.insert("test");
  options.whitelisted_resources.insert("three");
  ResTable res_table;

  ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));

  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
                     ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));

  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
                     ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));

  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {},
                     Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));

  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
                     ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
                     ResTable_config::CONFIG_VERSION));

  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
                     ResourceId(0x7f030000), test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC,
                     2u, ResTable_config::CONFIG_VERSION));

  std::u16string foo_str = u"foo";
  ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
  ASSERT_GE(idx, 0);
  EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
                     Res_value::TYPE_STRING, (uint32_t)idx, 0u));

  std::u16string bar_path = u"res/layout/bar.xml";
  idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
  ASSERT_GE(idx, 0);
  EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated",
                     ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
}

}  // namespace aapt
+1 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ TEST(UnicodeTest, IsValidResourceEntryName) {
  EXPECT_FALSE(IsValidResourceEntryName("Føø/Bar"));
  EXPECT_FALSE(IsValidResourceEntryName("Føø:Bar"));
  EXPECT_FALSE(IsValidResourceEntryName("Føø;Bar"));
  EXPECT_FALSE(IsValidResourceEntryName("0_resource_name_obfuscated"));
}

}  // namespace text