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