/** * 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; }
/** * 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)); } }
/** * 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)); } } }