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