Exemple #1
0
/**
 * Rewrite annotations that are referring to update methods or deleted interfaces.
 */
void OptimizationImpl::rewrite_annotations(Scope& scope, const SingleImplConfig& config) {
  // TODO: this is a hack to fix a problem with enclosing methods only.
  //       There are more dalvik annotations to review.
  //       The infrastructure is here but the code for all cases not yet
  auto enclosingMethod = DexType::get_type("Ldalvik/annotation/EnclosingMethod;");
  if (enclosingMethod == nullptr) return; // nothing to do
  if (!must_set_method_annotations(config)) return;
  for (const auto& cls : scope) {
    auto anno_set = cls->get_anno_set();
    if (anno_set == nullptr) continue;
    for (auto& anno : anno_set->get_annotations()) {
      if (anno->type() != enclosingMethod) continue;
      const auto& elems = anno->anno_elems();
      for (auto& elem : elems) {
        auto value = elem.encoded_value;
        if (value->evtype() == DexEncodedValueTypes::DEVT_METHOD) {
          auto method_value = static_cast<DexEncodedValueMethod*>(value);
          const auto& meth_it = new_methods.find(method_value->method());
          if (meth_it == new_methods.end()) continue;
          TRACE(INTF, 4, "REWRITE: %s\n", SHOW(anno));
          method_value->set_method(meth_it->second);
          TRACE(INTF, 4, "TO: %s\n", SHOW(anno));
        }
      }
    }
  }
}
/**
 * Check collisions in method definition.
 */
EscapeReason OptimizationImpl::check_method_collision(DexType* intf,
                                                      SingleImplData& data) {
  for (auto method : data.methoddefs) {
    auto meth_it = new_methods.find(method);
    if (meth_it != new_methods.end()) method = meth_it->second;
    auto proto = get_or_make_proto(intf, data.cls, method->get_proto());
    assert(proto != method->get_proto());
    DexMethod* collision = find_collision(method->get_name(),
                                          proto,
                                          type_class(method->get_class()),
                                          method->is_virtual());
    if (collision) return SIG_COLLISION;
  }
  return NO_ESCAPE;
}
Exemple #3
0
/**
 * Rewrite all methods sigs by creating new ones. Remove old methods and push
 * the new to the proper class method list.
 */
void OptimizationImpl::set_method_defs(DexType* intf,
                                       SingleImplData& data) {
  for (auto method : data.methoddefs) {
    TRACE(INTF, 3, "(MDEF) %s\n", SHOW(method));
    auto meth = method;
    auto meth_it = new_methods.find(meth);
    if (meth_it != new_methods.end()) {
      // if a method rewrite existed it must not be on a single impl
      // given we have escaped to next pass all collisions
      always_assert(meth_it->second->is_def());
      meth = static_cast<DexMethod*>(meth_it->second);
      TRACE(INTF, 4, "(MDEF) current: %s\n", SHOW(meth));
      assert(!single_impls->is_single_impl(method->get_class()) ||
             single_impls->is_escaped(method->get_class()));
      assert(!single_impls->is_single_impl(meth->get_class()) ||
             single_impls->is_escaped(meth->get_class()));
    }
    auto proto = get_or_make_proto(intf, data.cls, meth->get_proto());
    TRACE(INTF,
          5,
          "(MDEF) make_method: %s.%s - %s => %s\n",
          SHOW(meth->get_class()),
          SHOW(meth->get_name()),
          SHOW(meth->get_proto()),
          SHOW(proto));
    assert(proto != meth->get_proto());
    auto new_meth = static_cast<DexMethod*>(DexMethod::make_method(
        meth->get_class(), meth->get_name(), proto));
    // new_meth may have already existed in RedexContext, so
    // we need to make sure it isn't concrete.
    // TODO: this is horrible. After we remove methods, we shouldn't
    // have these zombies lying around.
    new_meth->clear_annotations();
    new_meth->make_non_concrete();
    new_meth->set_deobfuscated_name(meth->get_deobfuscated_name());
    new_meth->rstate = meth->rstate;
    assert(new_meth != meth);
    setup_method(meth, new_meth);
    new_methods[method] = new_meth;
    auto owner = type_class(new_meth->get_class());
    owner->remove_method(meth);
    owner->add_method(new_meth);
    TRACE(INTF, 3, "(MDEF)\t=> %s\n", SHOW(new_meth));
  }
}
Exemple #4
0
/**
 * Rewrite all method refs.
 */
void OptimizationImpl::set_method_refs(DexType* intf,
                                       SingleImplData& data) {
  for (auto mrefit : data.methodrefs) {
    auto method = mrefit.first;
    TRACE(INTF, 3, "(MREF)  %s\n", SHOW(method));
    auto new_meth = method;
    auto meth_it = new_methods.find(new_meth);
    if (meth_it != new_methods.end()) {
      // if a method rewrite existed it must not be on a single impl
      // given we have escaped to "next pass" all collisions
      new_meth = meth_it->second;
      TRACE(INTF, 4, "current: %s\n", SHOW(new_meth));
      assert(!single_impls->is_single_impl(method->get_class()) ||
             single_impls->is_escaped(method->get_class()));
      assert(!single_impls->is_single_impl(new_meth->get_class()) ||
             single_impls->is_escaped(new_meth->get_class()));
    }
    // next 2 lines will generate no new proto or method when the ref matches
    // a def, which should be very common.
    // However it does not seem too much of an overkill and more
    // straightforward that any logic that manages that.
    // Both creation of protos or methods is "interned" and so the same
    // method would be returned if there is nothing to change and create.
    // Still we need to change the opcodes where we assert the new method
    // to go in the opcode is in fact different than what was there.
    auto proto = get_or_make_proto(intf, data.cls, new_meth->get_proto());
    auto created_meth = DexMethod::make_method(
            new_meth->get_class(), new_meth->get_name(), proto);
    if (created_meth->is_def() && method->is_def()) {
      static_cast<DexMethod*>(created_meth)->set_deobfuscated_name(
          static_cast<DexMethod*>(method)->get_deobfuscated_name());
      static_cast<DexMethod*>(created_meth)->rstate =
          static_cast<DexMethod*>(method)->rstate;
    }
    new_methods[method] = created_meth;
    TRACE(INTF, 3, "(MREF)\t=> %s\n", SHOW(created_meth));
    for (auto opcode : mrefit.second) {
      TRACE(INTF, 3, "(MREF) %s\n", SHOW(opcode));
      assert(opcode->get_method() != created_meth);
      opcode->set_method(created_meth);
      TRACE(INTF, 3, "(MREF) \t=> %s\n", SHOW(opcode));
    }
  }
}
/**
 * Rewrite all methods sigs by creating new ones. Remove old methods and push
 * the new to the proper class method list.
 */
void OptimizationImpl::rewrite_method_defs(DexType* intf,
                                           SingleImplData& data) {
  for (auto method : data.methoddefs) {
    TRACE(INTF, 3, "(MDEF) %s\n", SHOW(method));
    auto meth = method;
    auto meth_it = new_methods.find(meth);
    if (meth_it != new_methods.end()) {
      // if a method rewrite existed it must not be on a single impl
      // given we have escaped to next pass all collisions
      meth = meth_it->second;
      TRACE(INTF, 4, "(MDEF) current: %s\n", SHOW(meth));
      assert(!single_impls->is_single_impl(method->get_class()) ||
             single_impls->is_escaped(method->get_class()));
      assert(!single_impls->is_single_impl(meth->get_class()) ||
             single_impls->is_escaped(meth->get_class()));
    }
    auto proto = get_or_make_proto(intf, data.cls, meth->get_proto());
    TRACE(INTF,
          5,
          "(MDEF) make_method: %s.%s - %s => %s\n",
          SHOW(meth->get_class()),
          SHOW(meth->get_name()),
          SHOW(meth->get_proto()),
          SHOW(proto));
    assert(proto != meth->get_proto());
    auto new_meth = DexMethod::make_method(
        meth->get_class(), meth->get_name(), proto);
    assert(new_meth != meth);
    setup_method(meth, new_meth);
    new_methods[method] = new_meth;
    auto owner = type_class(new_meth->get_class());
    owner->remove_method(meth);
    owner->add_method(new_meth);
    TRACE(INTF, 3, "(MDEF)\t=> %s\n", SHOW(new_meth));
  }
}
/**
 * Move all methods of the interface to the concrete (if not there already)
 * and rewrite all refs that were calling to the interface
 * (invoke-interface* -> invoke-virtual*).
 */
void OptimizationImpl::rewrite_interface_methods(DexType* intf,
                                                 SingleImplData& data) {
  auto intf_cls = type_class(intf);
  for (auto method : intf_cls->get_vmethods()) {
    auto meth = method;
    auto meth_it = new_methods.find(meth);
    if (meth_it != new_methods.end()) {
      meth = meth_it->second;
    }
    drop_single_impl_collision(intf, data, meth);
    auto impl = type_class(data.cls);
    // Given an interface method and a class determine whether the method
    // is already defined in the class and use it if so.
    // An interface method can be defined in some base class for "convenience"
    // even though the base class does not implement the interface so we walk
    // the chain looking for the method.
    // NOTICE: if we have interfaces that have methods defined up the chain
    // in some java, android, google or other library we are screwed.
    // We'll not find the method and introduce a possible abstract one that
    // will break things.
    // Hopefully we'll find that out during verification and correct things.

    // get the new method if one was created (interface method with a single
    // impl in signature)
    TRACE(INTF, 3, "(MITF) interface method %s\n", SHOW(meth));
    auto new_meth = resolve_virtual(impl, meth->get_name(), meth->get_proto());
    if (!new_meth) {
      new_meth = DexMethod::make_method(impl->get_type(), meth->get_name(),
          meth->get_proto());
      TRACE(INTF, 5, "(MITF) created impl method %s\n", SHOW(new_meth));
      setup_method(meth, new_meth);
      assert(new_meth->is_virtual());
      impl->add_method(new_meth);
      TRACE(INTF, 3, "(MITF) moved interface method %s\n", SHOW(new_meth));
    } else {
      TRACE(INTF, 3, "(MITF) found method impl %s\n", SHOW(new_meth));
    }
    assert(new_methods.find(meth) == new_methods.end());
    new_methods[method] = new_meth;
  }

  // rewrite invoke-interface to invoke-virtual
  for (const auto& mref_it : data.intf_methodrefs) {
    auto m = mref_it.first;
    assert(new_methods.find(m) != new_methods.end());
    auto new_m = new_methods[m];
    assert(new_m && new_m != m);
    TRACE(INTF, 3, "(MITFOP) %s\n", SHOW(new_m));
    for (auto mop : mref_it.second) {
      TRACE(INTF, 3, "(MITFOP) %s\n", SHOW(mop));
      mop->rewrite_method(new_m);
      auto op = mop->opcode();
      if (op == OPCODE_INVOKE_INTERFACE) {
        mop->set_opcode(OPCODE_INVOKE_VIRTUAL);
      } else if (op == OPCODE_INVOKE_INTERFACE_RANGE) {
        mop->set_opcode(OPCODE_INVOKE_VIRTUAL_RANGE);
      }
      SingleImplPass::s_invoke_intf_count++;
      TRACE(INTF, 3, "(MITFOP)\t=>%s\n", SHOW(mop));
    }
  }
}