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