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 "hidden_api_finder.h"
18
19 #include "dex/class_accessor-inl.h"
20 #include "dex/code_item_accessors-inl.h"
21 #include "dex/dex_instruction-inl.h"
22 #include "dex/dex_file.h"
23 #include "dex/method_reference.h"
24 #include "hidden_api.h"
25 #include "resolver.h"
26 #include "veridex.h"
27
28 #include <iostream>
29
30 namespace art {
31
CheckMethod(uint32_t method_id,VeridexResolver * resolver,MethodReference ref)32 void HiddenApiFinder::CheckMethod(uint32_t method_id,
33 VeridexResolver* resolver,
34 MethodReference ref) {
35 // Note: we always query whether a method is in a list, as the app
36 // might define blacklisted APIs (which won't be used at runtime).
37 std::string name = HiddenApi::GetApiMethodName(resolver->GetDexFile(), method_id);
38 if (hidden_api_.IsInAnyList(name)) {
39 method_locations_[name].push_back(ref);
40 }
41 }
42
CheckField(uint32_t field_id,VeridexResolver * resolver,MethodReference ref)43 void HiddenApiFinder::CheckField(uint32_t field_id,
44 VeridexResolver* resolver,
45 MethodReference ref) {
46 // Note: we always query whether a field is in a list, as the app
47 // might define blacklisted APIs (which won't be used at runtime).
48 std::string name = HiddenApi::GetApiFieldName(resolver->GetDexFile(), field_id);
49 if (hidden_api_.IsInAnyList(name)) {
50 field_locations_[name].push_back(ref);
51 }
52 }
53
CollectAccesses(VeridexResolver * resolver)54 void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) {
55 const DexFile& dex_file = resolver->GetDexFile();
56 // Look at all types referenced in this dex file. Any of these
57 // types can lead to being used through reflection.
58 for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) {
59 std::string name(dex_file.StringByTypeIdx(dex::TypeIndex(i)));
60 if (hidden_api_.IsInAnyList(name)) {
61 classes_.insert(name);
62 }
63 }
64 // Note: we collect strings constants only referenced in code items as the string table
65 // contains other kind of strings (eg types).
66 for (ClassAccessor accessor : dex_file.GetClasses()) {
67 for (const ClassAccessor::Method& method : accessor.GetMethods()) {
68 for (const DexInstructionPcPair& inst : method.GetInstructions()) {
69 switch (inst->Opcode()) {
70 case Instruction::CONST_STRING: {
71 dex::StringIndex string_index(inst->VRegB_21c());
72 std::string name = std::string(dex_file.StringDataByIdx(string_index));
73 // Cheap filtering on the string literal. We know it cannot be a field/method/class
74 // if it contains a space.
75 if (name.find(' ') == std::string::npos) {
76 // Class names at the Java level are of the form x.y.z, but the list encodes
77 // them of the form Lx/y/z;. Inner classes have '$' for both Java level class
78 // names in strings, and hidden API lists.
79 std::string str = HiddenApi::ToInternalName(name);
80 // Note: we can query the lists directly, as HiddenApi added classes that own
81 // private methods and fields in them.
82 // We don't add class names to the `strings_` set as we know method/field names
83 // don't have '.' or '/'. All hidden API class names have a '/'.
84 if (hidden_api_.IsInAnyList(str)) {
85 classes_.insert(str);
86 } else if (hidden_api_.IsInAnyList(name)) {
87 // Could be something passed to JNI.
88 classes_.insert(name);
89 } else {
90 // We only keep track of the location for strings, as these will be the
91 // field/method names the user is interested in.
92 strings_.insert(name);
93 reflection_locations_[name].push_back(method.GetReference());
94 }
95 }
96 break;
97 }
98 case Instruction::INVOKE_DIRECT:
99 case Instruction::INVOKE_INTERFACE:
100 case Instruction::INVOKE_STATIC:
101 case Instruction::INVOKE_SUPER:
102 case Instruction::INVOKE_VIRTUAL: {
103 CheckMethod(inst->VRegB_35c(), resolver, method.GetReference());
104 break;
105 }
106
107 case Instruction::INVOKE_DIRECT_RANGE:
108 case Instruction::INVOKE_INTERFACE_RANGE:
109 case Instruction::INVOKE_STATIC_RANGE:
110 case Instruction::INVOKE_SUPER_RANGE:
111 case Instruction::INVOKE_VIRTUAL_RANGE: {
112 CheckMethod(inst->VRegB_3rc(), resolver, method.GetReference());
113 break;
114 }
115
116 case Instruction::IGET:
117 case Instruction::IGET_WIDE:
118 case Instruction::IGET_OBJECT:
119 case Instruction::IGET_BOOLEAN:
120 case Instruction::IGET_BYTE:
121 case Instruction::IGET_CHAR:
122 case Instruction::IGET_SHORT: {
123 CheckField(inst->VRegC_22c(), resolver, method.GetReference());
124 break;
125 }
126
127 case Instruction::IPUT:
128 case Instruction::IPUT_WIDE:
129 case Instruction::IPUT_OBJECT:
130 case Instruction::IPUT_BOOLEAN:
131 case Instruction::IPUT_BYTE:
132 case Instruction::IPUT_CHAR:
133 case Instruction::IPUT_SHORT: {
134 CheckField(inst->VRegC_22c(), resolver, method.GetReference());
135 break;
136 }
137
138 case Instruction::SGET:
139 case Instruction::SGET_WIDE:
140 case Instruction::SGET_OBJECT:
141 case Instruction::SGET_BOOLEAN:
142 case Instruction::SGET_BYTE:
143 case Instruction::SGET_CHAR:
144 case Instruction::SGET_SHORT: {
145 CheckField(inst->VRegB_21c(), resolver, method.GetReference());
146 break;
147 }
148
149 case Instruction::SPUT:
150 case Instruction::SPUT_WIDE:
151 case Instruction::SPUT_OBJECT:
152 case Instruction::SPUT_BOOLEAN:
153 case Instruction::SPUT_BYTE:
154 case Instruction::SPUT_CHAR:
155 case Instruction::SPUT_SHORT: {
156 CheckField(inst->VRegB_21c(), resolver, method.GetReference());
157 break;
158 }
159
160 default:
161 break;
162 }
163 }
164 }
165 }
166 }
167
Run(const std::vector<std::unique_ptr<VeridexResolver>> & resolvers)168 void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
169 for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
170 CollectAccesses(resolver.get());
171 }
172 }
173
Dump(std::ostream & os,HiddenApiStats * stats,bool dump_reflection)174 void HiddenApiFinder::Dump(std::ostream& os,
175 HiddenApiStats* stats,
176 bool dump_reflection) {
177 stats->linking_count = method_locations_.size() + field_locations_.size();
178
179 // Dump methods from hidden APIs linked against.
180 for (const std::pair<const std::string,
181 std::vector<MethodReference>>& pair : method_locations_) {
182 hiddenapi::ApiList api_list = hidden_api_.GetApiList(pair.first);
183 CHECK(api_list.IsValid());
184 stats->api_counts[api_list.GetIntValue()]++;
185 os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
186 os << std::endl;
187 HiddenApiFinder::DumpReferences(os, pair.second);
188 os << std::endl;
189 }
190
191 // Dump fields from hidden APIs linked against.
192 for (const std::pair<const std::string,
193 std::vector<MethodReference>>& pair : field_locations_) {
194 hiddenapi::ApiList api_list = hidden_api_.GetApiList(pair.first);
195 CHECK(api_list.IsValid());
196 stats->api_counts[api_list.GetIntValue()]++;
197 os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
198 os << std::endl;
199 HiddenApiFinder::DumpReferences(os, pair.second);
200 os << std::endl;
201 }
202
203 if (dump_reflection) {
204 // Dump potential reflection uses.
205 for (const std::string& cls : classes_) {
206 for (const std::string& name : strings_) {
207 std::string full_name = cls + "->" + name;
208 hiddenapi::ApiList api_list = hidden_api_.GetApiList(full_name);
209 if (api_list.IsValid()) {
210 stats->api_counts[api_list.GetIntValue()]++;
211 stats->reflection_count++;
212 os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
213 << " potential use(s):";
214 os << std::endl;
215 HiddenApiFinder::DumpReferences(os, reflection_locations_[name]);
216 os << std::endl;
217 }
218 }
219 }
220 }
221 }
222
DumpReferences(std::ostream & os,const std::vector<MethodReference> & references)223 void HiddenApiFinder::DumpReferences(std::ostream& os,
224 const std::vector<MethodReference>& references) {
225 static const char* kPrefix = " ";
226
227 // Count number of occurrences of each reference, to make the output clearer.
228 std::map<std::string, size_t> counts;
229 for (const MethodReference& ref : references) {
230 std::string ref_string = HiddenApi::GetApiMethodName(ref);
231 if (!counts.count(ref_string)) {
232 counts[ref_string] = 0;
233 }
234 counts[ref_string]++;
235 }
236
237 for (const std::pair<const std::string, size_t>& pair : counts) {
238 os << kPrefix << pair.first;
239 if (pair.second > 1) {
240 os << " (" << pair.second << " occurrences)";
241 }
242 os << std::endl;
243 }
244 }
245
246 } // namespace art
247