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