1 /*
2  * Copyright (C) 2018 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 "veridex.h"
18 
19 #include <android-base/file.h>
20 
21 #include "dex/dex_file.h"
22 #include "dex/dex_file_loader.h"
23 #include "hidden_api.h"
24 #include "hidden_api_finder.h"
25 #include "precise_hidden_api_finder.h"
26 #include "resolver.h"
27 
28 #include <cstdlib>
29 #include <sstream>
30 
31 namespace art {
32 
33 static VeriClass z_(Primitive::Type::kPrimBoolean, 0, nullptr);
34 static VeriClass b_(Primitive::Type::kPrimByte, 0, nullptr);
35 static VeriClass c_(Primitive::Type::kPrimChar, 0, nullptr);
36 static VeriClass s_(Primitive::Type::kPrimShort, 0, nullptr);
37 static VeriClass i_(Primitive::Type::kPrimInt, 0, nullptr);
38 static VeriClass f_(Primitive::Type::kPrimFloat, 0, nullptr);
39 static VeriClass d_(Primitive::Type::kPrimDouble, 0, nullptr);
40 static VeriClass j_(Primitive::Type::kPrimLong, 0, nullptr);
41 static VeriClass v_(Primitive::Type::kPrimVoid, 0, nullptr);
42 
43 VeriClass* VeriClass::boolean_ = &z_;
44 VeriClass* VeriClass::byte_ = &b_;
45 VeriClass* VeriClass::char_ = &c_;
46 VeriClass* VeriClass::short_ = &s_;
47 VeriClass* VeriClass::integer_ = &i_;
48 VeriClass* VeriClass::float_ = &f_;
49 VeriClass* VeriClass::double_ = &d_;
50 VeriClass* VeriClass::long_ = &j_;
51 VeriClass* VeriClass::void_ = &v_;
52 
53 // Will be set after boot classpath has been resolved.
54 VeriClass* VeriClass::object_ = nullptr;
55 VeriClass* VeriClass::class_ = nullptr;
56 VeriClass* VeriClass::class_loader_ = nullptr;
57 VeriClass* VeriClass::string_ = nullptr;
58 VeriClass* VeriClass::throwable_ = nullptr;
59 VeriMethod VeriClass::forName_ = nullptr;
60 VeriMethod VeriClass::getField_ = nullptr;
61 VeriMethod VeriClass::getDeclaredField_ = nullptr;
62 VeriMethod VeriClass::getMethod_ = nullptr;
63 VeriMethod VeriClass::getDeclaredMethod_ = nullptr;
64 VeriMethod VeriClass::getClass_ = nullptr;
65 VeriMethod VeriClass::loadClass_ = nullptr;
66 VeriField VeriClass::sdkInt_ = nullptr;
67 
68 static const char* kDexFileOption = "--dex-file=";
69 static const char* kStubsOption = "--core-stubs=";
70 static const char* kFlagsOption = "--api-flags=";
71 static const char* kImprecise = "--imprecise";
72 static const char* kTargetSdkVersion = "--target-sdk-version=";
73 static const char* kOnlyReportSdkUses = "--only-report-sdk-uses";
74 
75 struct VeridexOptions {
76   const char* dex_file = nullptr;
77   const char* core_stubs = nullptr;
78   const char* flags_file = nullptr;
79   bool precise = true;
80   int target_sdk_version = 28; /* P */
81   bool only_report_sdk_uses = false;
82 };
83 
Substr(const char * str,int index)84 static const char* Substr(const char* str, int index) {
85   return str + index;
86 }
87 
StartsWith(const char * str,const char * val)88 static bool StartsWith(const char* str, const char* val) {
89   return strlen(str) >= strlen(val) && memcmp(str, val, strlen(val)) == 0;
90 }
91 
ParseArgs(VeridexOptions * options,int argc,char ** argv)92 static void ParseArgs(VeridexOptions* options, int argc, char** argv) {
93   // Skip over the command name.
94   argv++;
95   argc--;
96 
97   for (int i = 0; i < argc; ++i) {
98     if (StartsWith(argv[i], kDexFileOption)) {
99       options->dex_file = Substr(argv[i], strlen(kDexFileOption));
100     } else if (StartsWith(argv[i], kStubsOption)) {
101       options->core_stubs = Substr(argv[i], strlen(kStubsOption));
102     } else if (StartsWith(argv[i], kFlagsOption)) {
103       options->flags_file = Substr(argv[i], strlen(kFlagsOption));
104     } else if (strcmp(argv[i], kImprecise) == 0) {
105       options->precise = false;
106     } else if (StartsWith(argv[i], kTargetSdkVersion)) {
107       options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion)));
108     } else if (strcmp(argv[i], kOnlyReportSdkUses) == 0) {
109       options->only_report_sdk_uses = true;
110     }
111   }
112 }
113 
Split(const std::string & str,char sep)114 static std::vector<std::string> Split(const std::string& str, char sep) {
115   std::vector<std::string> tokens;
116   std::string tmp;
117   std::istringstream iss(str);
118   while (std::getline(iss, tmp, sep)) {
119     tokens.push_back(tmp);
120   }
121   return tokens;
122 }
123 
124 class Veridex {
125  public:
Run(int argc,char ** argv)126   static int Run(int argc, char** argv) {
127     VeridexOptions options;
128     ParseArgs(&options, argc, argv);
129     android::base::InitLogging(argv);
130 
131     if (!options.dex_file) {
132       LOG(ERROR) << "Required argument '" << kDexFileOption << "' not provided.";
133       return 1;
134     }
135 
136     gTargetSdkVersion = options.target_sdk_version;
137 
138     std::vector<std::string> boot_content;
139     std::vector<std::string> app_content;
140     std::vector<std::unique_ptr<const DexFile>> boot_dex_files;
141     std::vector<std::unique_ptr<const DexFile>> app_dex_files;
142     std::string error_msg;
143 
144     // Read the boot classpath.
145     std::vector<std::string> boot_classpath = Split(options.core_stubs, ':');
146     boot_content.resize(boot_classpath.size());
147     uint32_t i = 0;
148     for (const std::string& str : boot_classpath) {
149       if (!Load(str, boot_content[i++], &boot_dex_files, &error_msg)) {
150         LOG(ERROR) << error_msg;
151         return 1;
152       }
153     }
154 
155     // Read the apps dex files.
156     std::vector<std::string> app_files = Split(options.dex_file, ':');
157     app_content.resize(app_files.size());
158     i = 0;
159     for (const std::string& str : app_files) {
160       if (!Load(str, app_content[i++], &app_dex_files, &error_msg)) {
161         LOG(ERROR) << error_msg;
162         return 1;
163       }
164     }
165 
166     // Resolve classes/methods/fields defined in each dex file.
167 
168     // Cache of types we've seen, for quick class name lookups.
169     TypeMap type_map;
170     // Add internally defined primitives.
171     type_map["Z"] = VeriClass::boolean_;
172     type_map["B"] = VeriClass::byte_;
173     type_map["S"] = VeriClass::short_;
174     type_map["C"] = VeriClass::char_;
175     type_map["I"] = VeriClass::integer_;
176     type_map["F"] = VeriClass::float_;
177     type_map["D"] = VeriClass::double_;
178     type_map["J"] = VeriClass::long_;
179     type_map["V"] = VeriClass::void_;
180 
181     // Cache of resolvers, to easily query address in memory to VeridexResolver.
182     DexResolverMap resolver_map;
183 
184     std::vector<std::unique_ptr<VeridexResolver>> boot_resolvers;
185     Resolve(boot_dex_files, resolver_map, type_map, &boot_resolvers);
186 
187     // Now that boot classpath has been resolved, fill classes and reflection
188     // methods.
189     VeriClass::object_ = type_map["Ljava/lang/Object;"];
190     VeriClass::class_ = type_map["Ljava/lang/Class;"];
191     VeriClass::class_loader_ = type_map["Ljava/lang/ClassLoader;"];
192     VeriClass::string_ = type_map["Ljava/lang/String;"];
193     VeriClass::throwable_ = type_map["Ljava/lang/Throwable;"];
194     VeriClass::forName_ = boot_resolvers[0]->LookupDeclaredMethodIn(
195         *VeriClass::class_, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
196     VeriClass::getField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
197         *VeriClass::class_, "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
198     VeriClass::getDeclaredField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
199         *VeriClass::class_, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
200     VeriClass::getMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
201         *VeriClass::class_,
202         "getMethod",
203         "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
204     VeriClass::getDeclaredMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
205         *VeriClass::class_,
206         "getDeclaredMethod",
207         "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
208     VeriClass::getClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
209         *VeriClass::object_, "getClass", "()Ljava/lang/Class;");
210     VeriClass::loadClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
211         *VeriClass::class_loader_, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
212 
213     VeriClass* version = type_map["Landroid/os/Build$VERSION;"];
214     if (version != nullptr) {
215       VeriClass::sdkInt_ = boot_resolvers[0]->LookupFieldIn(*version, "SDK_INT", "I");
216     }
217 
218     std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
219     Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
220 
221     // Find and log uses of hidden APIs.
222     HiddenApi hidden_api(options.flags_file, options.only_report_sdk_uses);
223     HiddenApiStats stats;
224 
225     HiddenApiFinder api_finder(hidden_api);
226     api_finder.Run(app_resolvers);
227     api_finder.Dump(std::cout, &stats, !options.precise);
228 
229     if (options.precise) {
230       PreciseHiddenApiFinder precise_api_finder(hidden_api);
231       precise_api_finder.Run(app_resolvers);
232       precise_api_finder.Dump(std::cout, &stats);
233     }
234 
235     DumpSummaryStats(std::cout, stats, options);
236 
237     if (options.precise) {
238       std::cout << "To run an analysis that can give more reflection accesses, " << std::endl
239                 << "but could include false positives, pass the --imprecise flag. " << std::endl;
240     }
241 
242     return 0;
243   }
244 
245  private:
DumpSummaryStats(std::ostream & os,const HiddenApiStats & stats,const VeridexOptions & options)246   static void DumpSummaryStats(std::ostream& os,
247                                const HiddenApiStats& stats,
248                                const VeridexOptions& options) {
249     static const char* kPrefix = "       ";
250     if (options.only_report_sdk_uses) {
251       os << stats.api_counts[hiddenapi::ApiList::Whitelist().GetIntValue()]
252          << " SDK API uses." << std::endl;
253     } else {
254       os << stats.count << " hidden API(s) used: "
255          << stats.linking_count << " linked against, "
256          << stats.reflection_count << " through reflection" << std::endl;
257       for (size_t i = 0; i < hiddenapi::ApiList::kValueCount; ++i) {
258         hiddenapi::ApiList api_list = hiddenapi::ApiList(i);
259         if (api_list != hiddenapi::ApiList::Whitelist()) {
260           os << kPrefix << stats.api_counts[i] << " in " << api_list << std::endl;
261         }
262       }
263     }
264   }
265 
Load(const std::string & filename,std::string & content,std::vector<std::unique_ptr<const DexFile>> * dex_files,std::string * error_msg)266   static bool Load(const std::string& filename,
267                    std::string& content,
268                    std::vector<std::unique_ptr<const DexFile>>* dex_files,
269                    std::string* error_msg) {
270     if (filename.empty()) {
271       *error_msg = "Missing file name";
272       return false;
273     }
274 
275     // TODO: once added, use an api to android::base to read a std::vector<uint8_t>.
276     if (!android::base::ReadFileToString(filename.c_str(), &content)) {
277       *error_msg = "ReadFileToString failed for " + filename;
278       return false;
279     }
280 
281     const DexFileLoader dex_file_loader;
282     DexFileLoaderErrorCode error_code;
283     static constexpr bool kVerifyChecksum = true;
284     static constexpr bool kRunDexFileVerifier = true;
285     if (!dex_file_loader.OpenAll(reinterpret_cast<const uint8_t*>(content.data()),
286                                  content.size(),
287                                  filename.c_str(),
288                                  kRunDexFileVerifier,
289                                  kVerifyChecksum,
290                                  &error_code,
291                                  error_msg,
292                                  dex_files)) {
293       if (error_code == DexFileLoaderErrorCode::kEntryNotFound) {
294         LOG(INFO) << "No .dex found, skipping analysis.";
295         return true;
296       }
297       return false;
298     }
299 
300     return true;
301   }
302 
Resolve(const std::vector<std::unique_ptr<const DexFile>> & dex_files,DexResolverMap & resolver_map,TypeMap & type_map,std::vector<std::unique_ptr<VeridexResolver>> * resolvers)303   static void Resolve(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
304                       DexResolverMap& resolver_map,
305                       TypeMap& type_map,
306                       std::vector<std::unique_ptr<VeridexResolver>>* resolvers) {
307     for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
308       VeridexResolver* resolver =
309           new VeridexResolver(*dex_file.get(), resolver_map, type_map);
310       resolvers->emplace_back(resolver);
311       resolver_map[reinterpret_cast<uintptr_t>(dex_file->Begin())] = resolver;
312     }
313 
314     for (const std::unique_ptr<VeridexResolver>& resolver : *resolvers) {
315       resolver->Run();
316     }
317   }
318 };
319 
320 }  // namespace art
321 
main(int argc,char ** argv)322 int main(int argc, char** argv) {
323   return art::Veridex::Run(argc, argv);
324 }
325