Esempio n. 1
0
/*
 * Matches the pattern:
 *   sget-TYPE vA, FIELD
 *   return-TYPE vA
 */
DexField* trivial_get_static_field_wrapper(DexMethod* m) {
  DexCode* code = m->get_code();
  if (code == nullptr) return nullptr;

  auto& insns = code->get_instructions();
  auto it = insns.begin();
  if (!is_sget((*it)->opcode())) return nullptr;

  auto sget = static_cast<DexOpcodeField*>(*it);
  uint16_t sget_dest = sget->dest();
  ++it;

  if (!is_return_value((*it)->opcode())) return nullptr;

  uint16_t ret_reg = (*it)->src(0);
  if (ret_reg != sget_dest) return nullptr;
  ++it;

  if (it != insns.end()) return nullptr;

  // Check to make sure we have a concrete field reference.
  auto def = resolve_field(sget->field(), FieldSearch::Static);
  if (def == nullptr) return nullptr;
  if (!def->is_concrete()) {
    return nullptr;
  }

  return def;
}
Esempio n. 2
0
/*
 * Matches the pattern:
 *   invoke-(direct|static) {vA, ..., vB} METHOD
 *   (  move-result-TYPE v0
 *      return-TYPE v0
 *    | return-void )
 */
DexMethod* trivial_method_wrapper(DexMethod* m) {
  DexCode* code = m->get_code();
  if (code == nullptr) return nullptr;
  auto& insns = code->get_instructions();
  auto it = insns.begin();

  bool is_direct = (*it)->opcode() == OPCODE_INVOKE_DIRECT;
  bool is_static = (*it)->opcode() == OPCODE_INVOKE_STATIC;
  if (!is_direct && !is_static) return nullptr;

  auto invoke = static_cast<DexOpcodeMethod*>(*it);
  auto method = invoke->get_method();
  if (is_static) {
    method = resolve_static(type_class(method->get_class()),
        method->get_name(), method->get_proto());
  }
  if (!method) return nullptr;
  if (!method->is_concrete()) return nullptr;

  auto collision = find_collision_excepting(method,
                                            method->get_name(),
                                            method->get_proto(),
                                            type_class(method->get_class()),
                                            true,
                                            true);
  if (collision) {
    TRACE(SYNT,
          5,
          "wrapper blocked:%s\nwrapped method:%s\nconflicts with:%s\n",
          SHOW(m),
          SHOW(method),
          SHOW(collision));
    return nullptr;
  }
  if (!passes_args_through(invoke, code)) return nullptr;
  if (++it == insns.end()) return nullptr;

  if (is_move_result((*it)->opcode())) {
    if (++it == insns.end()) return nullptr;
    if (!is_return_value((*it)->opcode())) return nullptr;
    if (++it != insns.end()) return nullptr; // exception handling code
    return method;
  }
  if ((*it)->opcode() == OPCODE_RETURN_VOID) {
    if (++it != insns.end()) return nullptr; // exception handling code
    return method;
  }
  return nullptr;
}
Esempio n. 3
0
/*
 * Matches the pattern:
 *   invoke-direct {v0...} Lclass;.<init>
 *   return-void
 */
DexMethod* trivial_ctor_wrapper(DexMethod* m) {
  DexCode* code = m->get_code();
  if (code == nullptr) return nullptr;
  auto& insns = code->get_instructions();
  auto it = insns.begin();
  if ((*it)->opcode() != OPCODE_INVOKE_DIRECT) {
    TRACE(SYNT, 5, "Rejecting, not direct: %s\n", SHOW(m));
    return nullptr;
  }
  auto invoke = static_cast<DexOpcodeMethod*>(*it);
  if (!passes_args_through(invoke, code, 1)) {
    TRACE(SYNT, 5, "Rejecting, not passthrough: %s\n", SHOW(m));
    return nullptr;
  }
  if (++it == insns.end()) return nullptr;
  if ((*it)->opcode() != OPCODE_RETURN_VOID) return nullptr;
  auto method = invoke->get_method();
  if (!method->is_concrete() || !is_constructor(method)) return nullptr;
  return method;
}
Esempio n. 4
0
 void exclude_referenced_bridgee(DexMethod* code_method, const DexCode& code) {
   auto const& insts = code.get_instructions();
   for (auto inst : insts) {
     if (!is_invoke(inst->opcode())) continue;
     auto method = static_cast<DexOpcodeMethod*>(inst)->get_method();
     auto range = m_potential_bridgee_refs.equal_range(
         MethodRef(method->get_class(), method->get_name(),
             method->get_proto()));
     for (auto it = range.first; it != range.second; ++it) {
       auto referenced_bridge = it->second;
       // Don't count the bridge itself
       if (referenced_bridge == code_method) continue;
       TRACE(BRIDGE,
             5,
             "Rejecting, reference `%s.%s.%s' in `%s' blocks `%s'\n",
             SHOW(method->get_class()),
             SHOW(method->get_name()),
             SHOW(method->get_proto()),
             SHOW(code_method),
             SHOW(referenced_bridge));
       m_bridges_to_bridgees.erase(referenced_bridge);
     }
   }
 }
Esempio n. 5
0
void replace_wrappers(DexMethod* caller_method,
                      DexCode& code,
                      WrapperMethods& ssms) {
  std::vector<std::tuple<DexOpcodeMethod*, DexInstruction*, DexField*>> getter_calls;
  std::vector<std::pair<DexOpcodeMethod*, DexMethod*>> wrapper_calls;
  std::vector<std::pair<DexOpcodeMethod*, DexMethod*>> wrapped_calls;
  std::vector<std::pair<DexOpcodeMethod*, DexMethod*>> ctor_calls;

  TRACE(SYNT, 4, "Replacing wrappers in %s\n", SHOW(caller_method));
  auto& insns = code.get_instructions();
  for (auto it = insns.begin(); it != insns.end(); ++it) {
    auto insn = *it;
    if (insn->opcode() == OPCODE_INVOKE_STATIC) {
      // Replace calls to static getters and wrappers
      auto const meth_insn = static_cast<DexOpcodeMethod*>(insn);
      auto const callee = resolve_method(
          meth_insn->get_method(), MethodSearch::Static);
      if (callee == nullptr) continue;

      auto const found_get = ssms.getters.find(callee);
      if (found_get != ssms.getters.end()) {
        auto const move_result = *(std::next(it));
        if (!is_move_result(move_result->opcode())) {
          ssms.keepers.emplace(callee);
          continue;
        }
        auto field = found_get->second;
        getter_calls.emplace_back(meth_insn, move_result, field);
        continue;
      }

      auto const found_wrap = ssms.wrappers.find(callee);
      if (found_wrap != ssms.wrappers.end()) {
        auto method = found_wrap->second;
        wrapper_calls.emplace_back(meth_insn, method);
        continue;
      }
      always_assert_log(
        ssms.wrapped.find(callee) == ssms.wrapped.end(),
        "caller: %s\ncallee: %s\ninsn: %s\n",
        SHOW(caller_method), SHOW(callee), SHOW(insn));

      ssms.keepers.emplace(callee);
    } else if (insn->opcode() == OPCODE_INVOKE_DIRECT ||
               insn->opcode() == OPCODE_INVOKE_DIRECT_RANGE) {
      auto const meth_insn = static_cast<DexOpcodeMethod*>(insn);
      auto const callee =
          resolve_method(meth_insn->get_method(), MethodSearch::Direct);
      if (callee == nullptr) continue;

      auto const found_get = ssms.getters.find(callee);
      if (found_get != ssms.getters.end()) {
        auto const move_result = *(std::next(it));
        if (!is_move_result(move_result->opcode())) {
          ssms.keepers.emplace(callee);
          continue;
        }
        auto field = found_get->second;
        getter_calls.emplace_back(meth_insn, move_result, field);
        continue;
      }

      auto const found_wrap = ssms.wrappers.find(callee);
      if (found_wrap != ssms.wrappers.end()) {
        auto method = found_wrap->second;
        wrapper_calls.emplace_back(meth_insn, method);
        continue;
      }

      auto const found_wrappee = ssms.wrapped.find(callee);
      if (found_wrappee != ssms.wrapped.end()) {
        auto wrapper = found_wrappee->second.first;
        wrapped_calls.emplace_back(meth_insn, wrapper);
        continue;
      }

      auto const found_ctor = ssms.ctors.find(callee);
      if (found_ctor != ssms.ctors.end()) {
        auto ctor = found_ctor->second;
        ctor_calls.emplace_back(meth_insn, ctor);
        continue;
      }
    } else if (insn->opcode() == OPCODE_INVOKE_STATIC_RANGE) {
      // We don't handle this yet, but it's not hard.
      auto const meth_insn = static_cast<DexOpcodeMethod*>(insn);
      auto const callee = resolve_method(
          meth_insn->get_method(), MethodSearch::Static);
      if (callee == nullptr) continue;
      ssms.keepers.emplace(callee);
    }
  }
  // Prune out wrappers that are invalid due to naming conflicts.
  std::unordered_set<DexMethod*> bad_wrappees;
  std::unordered_multimap<DexMethod*, DexMethod*> wrappees_to_wrappers;
  for (auto wpair : wrapper_calls) {
    auto call_inst = wpair.first;
    auto wrapper = call_inst->get_method();
    auto wrappee = wpair.second;
    wrappees_to_wrappers.emplace(wrappee, wrapper);
    if (!can_update_wrappee(wrappee, wrapper)) {
      bad_wrappees.emplace(wrappee);
    }
  }
  for (auto wpair : wrapped_calls) {
    auto call_inst = wpair.first;
    auto wrapper = wpair.second;
    auto wrappee = call_inst->get_method();
    wrappees_to_wrappers.emplace(wrappee, wrapper);
    if (!can_update_wrappee(wrappee, wrapper)) {
      bad_wrappees.emplace(wrappee);
    }
  }
  for (auto bw : bad_wrappees) {
    auto range = wrappees_to_wrappers.equal_range(bw);
    for (auto it = range.first; it != range.second; ++it) {
      ssms.keepers.emplace(it->second);
    }
  }
  wrapper_calls.erase(
    std::remove_if(
      wrapper_calls.begin(), wrapper_calls.end(),
      [&](const std::pair<DexOpcodeMethod*, DexMethod*>& p) {
        return bad_wrappees.count(p.second);
      }),
    wrapper_calls.end()
  );
  wrapped_calls.erase(
    std::remove_if(
      wrapped_calls.begin(), wrapped_calls.end(),
      [&](const std::pair<DexOpcodeMethod*, DexMethod*>& p) {
        return bad_wrappees.count(p.first->get_method());
      }),
    wrapped_calls.end()
  );
  // Fix up everything left.
  if (getter_calls.empty() && wrapper_calls.empty() && ctor_calls.empty() &&
      wrapped_calls.empty()) {
    return;
  }
  MethodTransformer transform(caller_method);
  for (auto g : getter_calls) {
    using std::get;
    if (!replace_getter_wrapper(transform, get<0>(g), get<1>(g), get<2>(g))) {
      ssms.keepers.emplace(get<0>(g)->get_method());
    }
  }
  for (auto wpair : wrapper_calls) {
    auto call_inst = wpair.first;
    auto wrapper = call_inst->get_method();
    auto wrappee = wpair.second;
    auto success =
      replace_method_wrapper(
        transform,
        call_inst,
        wrapper,
        wrappee,
        ssms);
    if (!success) {
      ssms.keepers.emplace(wpair.first->get_method());
    }
  }
  for (auto wpair : wrapped_calls) {
    auto call_inst = wpair.first;
    auto wrapper = wpair.second;
    auto wrappee = call_inst->get_method();
    auto success =
      replace_method_wrapper(
        transform,
        call_inst,
        wrapper,
        wrappee,
        ssms);
    if (!success) {
      ssms.keepers.emplace(wpair.first->get_method());
    }
  }
  for (auto cpair : ctor_calls) {
    replace_ctor_wrapper(transform, cpair.first, cpair.second);
  }
}
Esempio n. 6
0
void DexLoader::gather_input_stats(dex_stats_t* stats, const dex_header* dh) {
  stats->num_types += dh->type_ids_size;
  stats->num_classes += dh->class_defs_size;
  stats->num_method_refs += dh->method_ids_size;
  stats->num_field_refs += dh->field_ids_size;
  stats->num_strings += dh->string_ids_size;
  stats->num_protos += dh->proto_ids_size;
  stats->num_bytes += dh->file_size;

  std::unordered_set<DexEncodedValueArray,
                     boost::hash<DexEncodedValueArray>>
    enc_arrays;
  std::set<DexTypeList*, dextypelists_comparator> type_lists;
  std::unordered_set<uint32_t> anno_offsets;
  for (uint32_t cidx = 0; cidx < dh->class_defs_size; ++cidx) {
    auto* clz = m_classes->at(cidx);
    auto* class_def = &m_class_defs[cidx];
    auto anno_off = class_def->annotations_off;
    if (anno_off) {
      const dex_annotations_directory_item* anno_dir =
        (const dex_annotations_directory_item*)m_idx->get_uint_data(anno_off);
      auto class_anno_off = anno_dir->class_annotations_off;
      if (class_anno_off) {
        const uint32_t* anno_data = m_idx->get_uint_data(class_anno_off);
        uint32_t count = *anno_data++;
        for (uint32_t aidx = 0; aidx < count; ++aidx) {
          anno_offsets.insert(anno_data[aidx]);
        }
      }
      const uint32_t* anno_data = (uint32_t*)(anno_dir + 1);
      for (uint32_t fidx = 0; fidx < anno_dir->fields_size; ++fidx) {
        anno_data++;
        anno_offsets.insert(*anno_data++);
      }
      for (uint32_t midx = 0; midx < anno_dir->methods_size; ++midx) {
        anno_data++;
        anno_offsets.insert(*anno_data++);
      }
      for (uint32_t pidx = 0; pidx < anno_dir->parameters_size; ++pidx) {
        anno_data++;
        uint32_t xrefoff = *anno_data++;
        if (xrefoff != 0) {
          const uint32_t* annoxref = m_idx->get_uint_data(xrefoff);
          uint32_t count = *annoxref++;
          for (uint32_t j = 0; j < count; j++) {
            uint32_t off = annoxref[j];
            anno_offsets.insert(off);
          }
        }
      }
    }
    auto* interfaces_type_list = clz->get_interfaces();
    type_lists.insert(interfaces_type_list);
    std::unique_ptr<DexEncodedValueArray> deva(clz->get_static_values());
    if (deva) {
      if (!enc_arrays.count(*deva)) {
        enc_arrays.emplace(std::move(*deva.release()));
        stats->num_static_values++;
      }
    }
    stats->num_fields +=
        clz->get_ifields().size() + clz->get_sfields().size();
    stats->num_methods +=
        clz->get_vmethods().size() + clz->get_dmethods().size();
    for (auto* meth : clz->get_vmethods()) {
      DexCode* code = meth->get_dex_code();
      if (code) {
        stats->num_instructions += code->get_instructions().size();
      }
    }
    for (auto* meth : clz->get_dmethods()) {
      DexCode* code = meth->get_dex_code();
      if (code) {
        stats->num_instructions += code->get_instructions().size();
      }
    }
  }
  for (uint32_t meth_idx = 0; meth_idx < dh->method_ids_size; ++meth_idx) {
    auto* meth = m_idx->get_methodidx(meth_idx);
    DexProto* proto = meth->get_proto();
    type_lists.insert(proto->get_args());
  }
  stats->num_annotations += anno_offsets.size();
  stats->num_type_lists += type_lists.size();
}