• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CommandLine()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 
UsageErrorV(const char * fmt,va_list ap)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 
UsageError(const char * fmt,...)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 
Usage(const char * fmt,...)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:
DexClass(const DexFile & dex_file,uint32_t idx)83   DexClass(const DexFile& dex_file, uint32_t idx)
84       : dex_file_(dex_file), class_def_(dex_file.GetClassDef(idx)) {}
85 
GetDexFile() const86   const DexFile& GetDexFile() const { return dex_file_; }
87 
GetClassIndex() const88   const dex::TypeIndex GetClassIndex() const { return class_def_.class_idx_; }
89 
GetData() const90   const uint8_t* GetData() const { return dex_file_.GetClassData(class_def_); }
91 
GetDescriptor() const92   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:
DexMember(const DexClass & klass,const ClassDataItemIterator & it)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.
SetHidden(HiddenApiAccessFlags::ApiList value)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`.
IsOnApiList(const std::unordered_set<std::string> & list) const132   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.
GetApiEntry() const137   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:
GetMethodId() const153   inline const DexFile::MethodId& GetMethodId() const {
154     DCHECK(it_.IsAtMethod());
155     return klass_.GetDexFile().GetMethodId(it_.GetMemberIndex());
156   }
157 
GetFieldId() const158   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:
HiddenApi()169   HiddenApi() : print_hidden_api_(false) {}
170 
ParseArgs(int argc,char ** argv)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 
ProcessDexFiles()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:
OpenApiFile(const std::string & path,std::unordered_set<std::string> * list)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 
OpenDexFiles()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 
CategorizeAllClasses(const DexFile & dex_file)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 
UpdateDexChecksums()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 
main(int argc,char ** argv)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