1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <fstream> 18 #include <iostream> 19 #include <unordered_set> 20 21 #include "android-base/stringprintf.h" 22 #include "android-base/strings.h" 23 24 #include "base/os.h" 25 #include "base/unix_file/fd_file.h" 26 #include "dex/art_dex_file_loader.h" 27 #include "dex/dex_file-inl.h" 28 #include "dex/hidden_api_access_flags.h" 29 #include "mem_map.h" 30 31 namespace art { 32 33 static int original_argc; 34 static char** original_argv; 35 36 static std::string CommandLine() { 37 std::vector<std::string> command; 38 for (int i = 0; i < original_argc; ++i) { 39 command.push_back(original_argv[i]); 40 } 41 return android::base::Join(command, ' '); 42 } 43 44 static void UsageErrorV(const char* fmt, va_list ap) { 45 std::string error; 46 android::base::StringAppendV(&error, fmt, ap); 47 LOG(ERROR) << error; 48 } 49 50 static void UsageError(const char* fmt, ...) { 51 va_list ap; 52 va_start(ap, fmt); 53 UsageErrorV(fmt, ap); 54 va_end(ap); 55 } 56 57 NO_RETURN static void Usage(const char* fmt, ...) { 58 va_list ap; 59 va_start(ap, fmt); 60 UsageErrorV(fmt, ap); 61 va_end(ap); 62 63 UsageError("Command: %s", CommandLine().c_str()); 64 UsageError("Usage: hiddenapi [options]..."); 65 UsageError(""); 66 UsageError(" --dex=<filename>: specify dex file whose members' access flags are to be set."); 67 UsageError(" At least one --dex parameter must be specified."); 68 UsageError(""); 69 UsageError(" --light-greylist=<filename>:"); 70 UsageError(" --dark-greylist=<filename>:"); 71 UsageError(" --blacklist=<filename>: text files with signatures of methods/fields to be marked"); 72 UsageError(" greylisted/blacklisted respectively. At least one list must be provided."); 73 UsageError(""); 74 UsageError(" --print-hidden-api: dump a list of marked methods/fields to the standard output."); 75 UsageError(" There is no indication which API category they belong to."); 76 UsageError(""); 77 78 exit(EXIT_FAILURE); 79 } 80 81 class DexClass { 82 public: 83 DexClass(const DexFile& dex_file, uint32_t idx) 84 : dex_file_(dex_file), class_def_(dex_file.GetClassDef(idx)) {} 85 86 const DexFile& GetDexFile() const { return dex_file_; } 87 88 const dex::TypeIndex GetClassIndex() const { return class_def_.class_idx_; } 89 90 const uint8_t* GetData() const { return dex_file_.GetClassData(class_def_); } 91 92 const char* GetDescriptor() const { return dex_file_.GetClassDescriptor(class_def_); } 93 94 private: 95 const DexFile& dex_file_; 96 const DexFile::ClassDef& class_def_; 97 }; 98 99 class DexMember { 100 public: 101 DexMember(const DexClass& klass, const ClassDataItemIterator& it) 102 : klass_(klass), it_(it) { 103 DCHECK_EQ(it_.IsAtMethod() ? GetMethodId().class_idx_ : GetFieldId().class_idx_, 104 klass_.GetClassIndex()); 105 } 106 107 // Sets hidden bits in access flags and writes them back into the DEX in memory. 108 // Note that this will not update the cached data of ClassDataItemIterator 109 // until it iterates over this item again and therefore will fail a CHECK if 110 // it is called multiple times on the same DexMember. 111 void SetHidden(HiddenApiAccessFlags::ApiList value) { 112 const uint32_t old_flags = it_.GetRawMemberAccessFlags(); 113 const uint32_t new_flags = HiddenApiAccessFlags::EncodeForDex(old_flags, value); 114 CHECK_EQ(UnsignedLeb128Size(new_flags), UnsignedLeb128Size(old_flags)); 115 116 // Locate the LEB128-encoded access flags in class data. 117 // `ptr` initially points to the next ClassData item. We iterate backwards 118 // until we hit the terminating byte of the previous Leb128 value. 119 const uint8_t* ptr = it_.DataPointer(); 120 if (it_.IsAtMethod()) { 121 ptr = ReverseSearchUnsignedLeb128(ptr); 122 DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), it_.GetMethodCodeItemOffset()); 123 } 124 ptr = ReverseSearchUnsignedLeb128(ptr); 125 DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), old_flags); 126 127 // Overwrite the access flags. 128 UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags); 129 } 130 131 // Returns true if this member's API entry is in `list`. 132 bool IsOnApiList(const std::unordered_set<std::string>& list) const { 133 return list.find(GetApiEntry()) != list.end(); 134 } 135 136 // Constructs a string with a unique signature of this class member. 137 std::string GetApiEntry() const { 138 std::stringstream ss; 139 ss << klass_.GetDescriptor() << "->"; 140 if (it_.IsAtMethod()) { 141 const DexFile::MethodId& mid = GetMethodId(); 142 ss << klass_.GetDexFile().GetMethodName(mid) 143 << klass_.GetDexFile().GetMethodSignature(mid).ToString(); 144 } else { 145 const DexFile::FieldId& fid = GetFieldId(); 146 ss << klass_.GetDexFile().GetFieldName(fid) << ":" 147 << klass_.GetDexFile().GetFieldTypeDescriptor(fid); 148 } 149 return ss.str(); 150 } 151 152 private: 153 inline const DexFile::MethodId& GetMethodId() const { 154 DCHECK(it_.IsAtMethod()); 155 return klass_.GetDexFile().GetMethodId(it_.GetMemberIndex()); 156 } 157 158 inline const DexFile::FieldId& GetFieldId() const { 159 DCHECK(!it_.IsAtMethod()); 160 return klass_.GetDexFile().GetFieldId(it_.GetMemberIndex()); 161 } 162 163 const DexClass& klass_; 164 const ClassDataItemIterator& it_; 165 }; 166 167 class HiddenApi FINAL { 168 public: 169 HiddenApi() : print_hidden_api_(false) {} 170 171 void ParseArgs(int argc, char** argv) { 172 original_argc = argc; 173 original_argv = argv; 174 175 android::base::InitLogging(argv); 176 177 // Skip over the command name. 178 argv++; 179 argc--; 180 181 if (argc == 0) { 182 Usage("No arguments specified"); 183 } 184 185 for (int i = 0; i < argc; ++i) { 186 const StringPiece option(argv[i]); 187 const bool log_options = false; 188 if (log_options) { 189 LOG(INFO) << "hiddenapi: option[" << i << "]=" << argv[i]; 190 } 191 if (option == "--print-hidden-api") { 192 print_hidden_api_ = true; 193 } else if (option.starts_with("--dex=")) { 194 dex_paths_.push_back(option.substr(strlen("--dex=")).ToString()); 195 } else if (option.starts_with("--light-greylist=")) { 196 light_greylist_path_ = option.substr(strlen("--light-greylist=")).ToString(); 197 } else if (option.starts_with("--dark-greylist=")) { 198 dark_greylist_path_ = option.substr(strlen("--dark-greylist=")).ToString(); 199 } else if (option.starts_with("--blacklist=")) { 200 blacklist_path_ = option.substr(strlen("--blacklist=")).ToString(); 201 } else { 202 Usage("Unknown argument '%s'", option.data()); 203 } 204 } 205 } 206 207 bool ProcessDexFiles() { 208 if (dex_paths_.empty()) { 209 Usage("No DEX files specified"); 210 } 211 212 if (light_greylist_path_.empty() && dark_greylist_path_.empty() && blacklist_path_.empty()) { 213 Usage("No API file specified"); 214 } 215 216 if (!light_greylist_path_.empty() && !OpenApiFile(light_greylist_path_, &light_greylist_)) { 217 return false; 218 } 219 220 if (!dark_greylist_path_.empty() && !OpenApiFile(dark_greylist_path_, &dark_greylist_)) { 221 return false; 222 } 223 224 if (!blacklist_path_.empty() && !OpenApiFile(blacklist_path_, &blacklist_)) { 225 return false; 226 } 227 228 MemMap::Init(); 229 if (!OpenDexFiles()) { 230 return false; 231 } 232 233 DCHECK(!dex_files_.empty()); 234 for (auto& dex_file : dex_files_) { 235 CategorizeAllClasses(*dex_file.get()); 236 } 237 238 UpdateDexChecksums(); 239 return true; 240 } 241 242 private: 243 bool OpenApiFile(const std::string& path, std::unordered_set<std::string>* list) { 244 DCHECK(list->empty()); 245 DCHECK(!path.empty()); 246 247 std::ifstream api_file(path, std::ifstream::in); 248 if (api_file.fail()) { 249 LOG(ERROR) << "Unable to open file '" << path << "' " << strerror(errno); 250 return false; 251 } 252 253 for (std::string line; std::getline(api_file, line);) { 254 list->insert(line); 255 } 256 257 api_file.close(); 258 return true; 259 } 260 261 bool OpenDexFiles() { 262 ArtDexFileLoader dex_loader; 263 DCHECK(dex_files_.empty()); 264 265 for (const std::string& filename : dex_paths_) { 266 std::string error_msg; 267 268 File fd(filename.c_str(), O_RDWR, /* check_usage */ false); 269 if (fd.Fd() == -1) { 270 LOG(ERROR) << "Unable to open file '" << filename << "': " << strerror(errno); 271 return false; 272 } 273 274 // Memory-map the dex file with MAP_SHARED flag so that changes in memory 275 // propagate to the underlying file. We run dex file verification as if 276 // the dex file was not in boot claass path to check basic assumptions, 277 // such as that at most one of public/private/protected flag is set. 278 // We do those checks here and skip them when loading the processed file 279 // into boot class path. 280 std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(), 281 /* location */ filename, 282 /* verify */ true, 283 /* verify_checksum */ true, 284 /* mmap_shared */ true, 285 &error_msg)); 286 if (dex_file.get() == nullptr) { 287 LOG(ERROR) << "Open failed for '" << filename << "' " << error_msg; 288 return false; 289 } 290 291 if (!dex_file->IsStandardDexFile()) { 292 LOG(ERROR) << "Expected a standard dex file '" << filename << "'"; 293 return false; 294 } 295 296 // Change the protection of the memory mapping to read-write. 297 if (!dex_file->EnableWrite()) { 298 LOG(ERROR) << "Failed to enable write permission for '" << filename << "'"; 299 return false; 300 } 301 302 dex_files_.push_back(std::move(dex_file)); 303 } 304 return true; 305 } 306 307 void CategorizeAllClasses(const DexFile& dex_file) { 308 for (uint32_t class_idx = 0; class_idx < dex_file.NumClassDefs(); ++class_idx) { 309 DexClass klass(dex_file, class_idx); 310 const uint8_t* klass_data = klass.GetData(); 311 if (klass_data == nullptr) { 312 continue; 313 } 314 315 for (ClassDataItemIterator it(klass.GetDexFile(), klass_data); it.HasNext(); it.Next()) { 316 DexMember member(klass, it); 317 318 // Catagorize member and overwrite its access flags. 319 // Note that if a member appears on multiple API lists, it will be categorized 320 // as the strictest. 321 bool is_hidden = true; 322 if (member.IsOnApiList(blacklist_)) { 323 member.SetHidden(HiddenApiAccessFlags::kBlacklist); 324 } else if (member.IsOnApiList(dark_greylist_)) { 325 member.SetHidden(HiddenApiAccessFlags::kDarkGreylist); 326 } else if (member.IsOnApiList(light_greylist_)) { 327 member.SetHidden(HiddenApiAccessFlags::kLightGreylist); 328 } else { 329 member.SetHidden(HiddenApiAccessFlags::kWhitelist); 330 is_hidden = false; 331 } 332 333 if (print_hidden_api_ && is_hidden) { 334 std::cout << member.GetApiEntry() << std::endl; 335 } 336 } 337 } 338 } 339 340 void UpdateDexChecksums() { 341 for (auto& dex_file : dex_files_) { 342 // Obtain a writeable pointer to the dex header. 343 DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader()); 344 // Recalculate checksum and overwrite the value in the header. 345 header->checksum_ = dex_file->CalculateChecksum(); 346 } 347 } 348 349 // Print signatures of APIs which have been grey-/blacklisted. 350 bool print_hidden_api_; 351 352 // Paths to DEX files which should be processed. 353 std::vector<std::string> dex_paths_; 354 355 // Paths to text files which contain the lists of API members. 356 std::string light_greylist_path_; 357 std::string dark_greylist_path_; 358 std::string blacklist_path_; 359 360 // Opened DEX files. Note that these are opened as `const` but eventually will be written into. 361 std::vector<std::unique_ptr<const DexFile>> dex_files_; 362 363 // Signatures of DEX members loaded from `light_greylist_path_`, `dark_greylist_path_`, 364 // `blacklist_path_`. 365 std::unordered_set<std::string> light_greylist_; 366 std::unordered_set<std::string> dark_greylist_; 367 std::unordered_set<std::string> blacklist_; 368 }; 369 370 } // namespace art 371 372 int main(int argc, char** argv) { 373 art::HiddenApi hiddenapi; 374 375 // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. 376 hiddenapi.ParseArgs(argc, argv); 377 return hiddenapi.ProcessDexFiles() ? EXIT_SUCCESS : EXIT_FAILURE; 378 } 379