Home | History | Annotate | Download | only in veridex
      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 
     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 
     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 
     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 
    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 
    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