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 struct VeridexOptions {
69 const char* dex_file = nullptr;
70 const char* core_stubs = nullptr;
71 const char* blacklist = nullptr;
72 const char* light_greylist = nullptr;
73 const char* dark_greylist = nullptr;
74 bool precise = true;
75 int target_sdk_version = 28; /* P */
76 };
77
Substr(const char * str,int index)78 static const char* Substr(const char* str, int index) {
79 return str + index;
80 }
81
StartsWith(const char * str,const char * val)82 static bool StartsWith(const char* str, const char* val) {
83 return strlen(str) >= strlen(val) && memcmp(str, val, strlen(val)) == 0;
84 }
85
ParseArgs(VeridexOptions * options,int argc,char ** argv)86 static void ParseArgs(VeridexOptions* options, int argc, char** argv) {
87 // Skip over the command name.
88 argv++;
89 argc--;
90
91 static const char* kDexFileOption = "--dex-file=";
92 static const char* kStubsOption = "--core-stubs=";
93 static const char* kBlacklistOption = "--blacklist=";
94 static const char* kDarkGreylistOption = "--dark-greylist=";
95 static const char* kLightGreylistOption = "--light-greylist=";
96 static const char* kImprecise = "--imprecise";
97 static const char* kTargetSdkVersion = "--target-sdk-version=";
98
99 for (int i = 0; i < argc; ++i) {
100 if (StartsWith(argv[i], kDexFileOption)) {
101 options->dex_file = Substr(argv[i], strlen(kDexFileOption));
102 } else if (StartsWith(argv[i], kStubsOption)) {
103 options->core_stubs = Substr(argv[i], strlen(kStubsOption));
104 } else if (StartsWith(argv[i], kBlacklistOption)) {
105 options->blacklist = Substr(argv[i], strlen(kBlacklistOption));
106 } else if (StartsWith(argv[i], kDarkGreylistOption)) {
107 options->dark_greylist = Substr(argv[i], strlen(kDarkGreylistOption));
108 } else if (StartsWith(argv[i], kLightGreylistOption)) {
109 options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption));
110 } else if (strcmp(argv[i], kImprecise) == 0) {
111 options->precise = false;
112 } else if (StartsWith(argv[i], kTargetSdkVersion)) {
113 options->target_sdk_version = atoi(Substr(argv[i], strlen(kTargetSdkVersion)));
114 }
115 }
116 }
117
Split(const std::string & str,char sep)118 static std::vector<std::string> Split(const std::string& str, char sep) {
119 std::vector<std::string> tokens;
120 std::string tmp;
121 std::istringstream iss(str);
122 while (std::getline(iss, tmp, sep)) {
123 tokens.push_back(tmp);
124 }
125 return tokens;
126 }
127
128 class Veridex {
129 public:
Run(int argc,char ** argv)130 static int Run(int argc, char** argv) {
131 VeridexOptions options;
132 ParseArgs(&options, argc, argv);
133 gTargetSdkVersion = options.target_sdk_version;
134
135 std::vector<std::string> boot_content;
136 std::vector<std::string> app_content;
137 std::vector<std::unique_ptr<const DexFile>> boot_dex_files;
138 std::vector<std::unique_ptr<const DexFile>> app_dex_files;
139 std::string error_msg;
140
141 // Read the boot classpath.
142 std::vector<std::string> boot_classpath = Split(options.core_stubs, ':');
143 boot_content.resize(boot_classpath.size());
144 uint32_t i = 0;
145 for (const std::string& str : boot_classpath) {
146 if (!Load(str, boot_content[i++], &boot_dex_files, &error_msg)) {
147 LOG(ERROR) << error_msg;
148 return 1;
149 }
150 }
151
152 // Read the apps dex files.
153 std::vector<std::string> app_files = Split(options.dex_file, ':');
154 app_content.resize(app_files.size());
155 i = 0;
156 for (const std::string& str : app_files) {
157 if (!Load(str, app_content[i++], &app_dex_files, &error_msg)) {
158 LOG(ERROR) << error_msg;
159 return 1;
160 }
161 }
162
163 // Resolve classes/methods/fields defined in each dex file.
164
165 // Cache of types we've seen, for quick class name lookups.
166 TypeMap type_map;
167 // Add internally defined primitives.
168 type_map["Z"] = VeriClass::boolean_;
169 type_map["B"] = VeriClass::byte_;
170 type_map["S"] = VeriClass::short_;
171 type_map["C"] = VeriClass::char_;
172 type_map["I"] = VeriClass::integer_;
173 type_map["F"] = VeriClass::float_;
174 type_map["D"] = VeriClass::double_;
175 type_map["J"] = VeriClass::long_;
176 type_map["V"] = VeriClass::void_;
177
178 // Cache of resolvers, to easily query address in memory to VeridexResolver.
179 DexResolverMap resolver_map;
180
181 std::vector<std::unique_ptr<VeridexResolver>> boot_resolvers;
182 Resolve(boot_dex_files, resolver_map, type_map, &boot_resolvers);
183
184 // Now that boot classpath has been resolved, fill classes and reflection
185 // methods.
186 VeriClass::object_ = type_map["Ljava/lang/Object;"];
187 VeriClass::class_ = type_map["Ljava/lang/Class;"];
188 VeriClass::class_loader_ = type_map["Ljava/lang/ClassLoader;"];
189 VeriClass::string_ = type_map["Ljava/lang/String;"];
190 VeriClass::throwable_ = type_map["Ljava/lang/Throwable;"];
191 VeriClass::forName_ = boot_resolvers[0]->LookupDeclaredMethodIn(
192 *VeriClass::class_, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
193 VeriClass::getField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
194 *VeriClass::class_, "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
195 VeriClass::getDeclaredField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
196 *VeriClass::class_, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
197 VeriClass::getMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
198 *VeriClass::class_,
199 "getMethod",
200 "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
201 VeriClass::getDeclaredMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
202 *VeriClass::class_,
203 "getDeclaredMethod",
204 "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
205 VeriClass::getClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
206 *VeriClass::object_, "getClass", "()Ljava/lang/Class;");
207 VeriClass::loadClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
208 *VeriClass::class_loader_, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
209
210 VeriClass* version = type_map["Landroid/os/Build$VERSION;"];
211 if (version != nullptr) {
212 VeriClass::sdkInt_ = boot_resolvers[0]->LookupFieldIn(*version, "SDK_INT", "I");
213 }
214
215 std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
216 Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
217
218 // Find and log uses of hidden APIs.
219 HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist);
220 HiddenApiStats stats;
221
222 HiddenApiFinder api_finder(hidden_api);
223 api_finder.Run(app_resolvers);
224 api_finder.Dump(std::cout, &stats, !options.precise);
225
226 if (options.precise) {
227 PreciseHiddenApiFinder precise_api_finder(hidden_api);
228 precise_api_finder.Run(app_resolvers);
229 precise_api_finder.Dump(std::cout, &stats);
230 }
231
232 DumpSummaryStats(std::cout, stats);
233
234 if (options.precise) {
235 std::cout << "To run an analysis that can give more reflection accesses, " << std::endl
236 << "but could include false positives, pass the --imprecise flag. " << std::endl;
237 }
238
239 return 0;
240 }
241
242 private:
DumpSummaryStats(std::ostream & os,const HiddenApiStats & stats)243 static void DumpSummaryStats(std::ostream& os, const HiddenApiStats& stats) {
244 static const char* kPrefix = " ";
245 os << stats.count << " hidden API(s) used: "
246 << stats.linking_count << " linked against, "
247 << stats.reflection_count << " through reflection" << std::endl;
248 os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kBlacklist]
249 << " in blacklist" << std::endl;
250 os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kDarkGreylist]
251 << " in dark greylist" << std::endl;
252 os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kLightGreylist]
253 << " in light greylist" << std::endl;
254 }
255
Load(const std::string & filename,std::string & content,std::vector<std::unique_ptr<const DexFile>> * dex_files,std::string * error_msg)256 static bool Load(const std::string& filename,
257 std::string& content,
258 std::vector<std::unique_ptr<const DexFile>>* dex_files,
259 std::string* error_msg) {
260 if (filename.empty()) {
261 *error_msg = "Missing file name";
262 return false;
263 }
264
265 // TODO: once added, use an api to android::base to read a std::vector<uint8_t>.
266 if (!android::base::ReadFileToString(filename.c_str(), &content)) {
267 *error_msg = "ReadFileToString failed for " + filename;
268 return false;
269 }
270
271 const DexFileLoader dex_file_loader;
272 static constexpr bool kVerifyChecksum = true;
273 static constexpr bool kRunDexFileVerifier = true;
274 if (!dex_file_loader.OpenAll(reinterpret_cast<const uint8_t*>(content.data()),
275 content.size(),
276 filename.c_str(),
277 kRunDexFileVerifier,
278 kVerifyChecksum,
279 error_msg,
280 dex_files)) {
281 return false;
282 }
283
284 return true;
285 }
286
Resolve(const std::vector<std::unique_ptr<const DexFile>> & dex_files,DexResolverMap & resolver_map,TypeMap & type_map,std::vector<std::unique_ptr<VeridexResolver>> * resolvers)287 static void Resolve(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
288 DexResolverMap& resolver_map,
289 TypeMap& type_map,
290 std::vector<std::unique_ptr<VeridexResolver>>* resolvers) {
291 for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
292 VeridexResolver* resolver =
293 new VeridexResolver(*dex_file.get(), resolver_map, type_map);
294 resolvers->emplace_back(resolver);
295 resolver_map[reinterpret_cast<uintptr_t>(dex_file->Begin())] = resolver;
296 }
297
298 for (const std::unique_ptr<VeridexResolver>& resolver : *resolvers) {
299 resolver->Run();
300 }
301 }
302 };
303
304 } // namespace art
305
main(int argc,char ** argv)306 int main(int argc, char** argv) {
307 return art::Veridex::Run(argc, argv);
308 }
309
310