void init_seed_classes(const std::string seeds_filename) { TRACE(PGR, 1, "Reading seed classes from %s\n", seeds_filename.c_str()); auto start = std::chrono::high_resolution_clock::now(); std::ifstream seeds_file(seeds_filename); uint count = 0; if (!seeds_file) { TRACE(PGR, 1, "Seeds file %s was not found (ignoring error).", seeds_filename.c_str()); } else { std::string line; while (getline(seeds_file, line)) { if (line.find(":") == std::string::npos && line.find("$") == std::string::npos) { auto dex_type = get_dextype_from_dotname(line.c_str()); if (dex_type != nullptr) { mark_reachable_by_seed(dex_type); count++; } else { TRACE(PGR, 2, "Seed file contains class for which " "Dex type can't be found: %s\n", line.c_str()); } } } seeds_file.close(); } auto end = std::chrono::high_resolution_clock::now(); TRACE(PGR, 1, "Read %d seed classes in %.1lf seconds\n", count, std::chrono::duration<double>(end - start).count()); }
/** * Walks all the code of the app, finding classes that are reachable from * code. * * Note that as code is changed or removed by Redex, this information will * become stale, so this method should be called periodically, for example * after each pass. */ void recompute_classes_reachable_from_code(const Scope& scope) { for (auto clazz : scope) { clazz->rstate.clear_if_compute(); } std::unordered_set<DexString*> maybetypes; walk_annotations(scope, [&](DexAnnotation* anno) { static DexType* dalviksig = DexType::get_type("Ldalvik/annotation/Signature;"); // Signature annotations contain strings that Jackson uses // to construct the underlying types. We capture the // full list here, and mark them later. (There are many // duplicates, so de-duping before we look it up as a class // makes sense) if (anno->type() == dalviksig) { auto elems = anno->anno_elems(); for (auto const& elem : elems) { auto ev = elem.encoded_value; if (ev->evtype() != DEVT_ARRAY) continue; auto arrayev = static_cast<DexEncodedValueArray*>(ev); auto const& evs = arrayev->evalues(); for (auto strev : *evs) { if (strev->evtype() != DEVT_STRING) continue; auto stringev = static_cast<DexEncodedValueString*>(strev); maybetypes.insert((DexString*)stringev->string()); } } return; } // Class literals in annotations. // // Example: // @JsonDeserialize(using=MyJsonDeserializer.class) // ^^^^ if (anno->runtime_visible()) { auto elems = anno->anno_elems(); for (auto const& dae : elems) { auto evalue = dae.encoded_value; std::vector<DexType*> ltype; evalue->gather_types(ltype); if (ltype.size()) { for (auto dextype : ltype) { mark_reachable_directly(dextype); } } } } }); // Now we process the strings that were in the signature // annotations. // Note: We do not mark these as ref'd by string, because // these cases are handleable for renaming. for (auto dstring : maybetypes) { const char* cstr = dstring->c_str(); int len = strlen(cstr); if (len < 3) continue; if (cstr[0] != 'L') continue; if (cstr[len - 1] == ';') { auto dtype = DexType::get_type(dstring); mark_reachable_directly(dtype); continue; } std::string buf(cstr); buf += ';'; auto dtype = DexType::get_type(buf.c_str()); mark_reachable_directly(dtype); } // Matches methods marked as native walk_methods(scope, [&](DexMethod* meth) { if (meth->get_access() & DexAccessFlags::ACC_NATIVE) { mark_reachable_by_classname(meth->get_class(), true); } }); walk_code(scope, [](DexMethod*) { return true; }, [&](DexMethod* meth, DexCode* code) { auto opcodes = code->get_instructions(); for (const auto& opcode : opcodes) { // Matches any stringref that name-aliases a type. if (opcode->has_strings()) { auto stringop = static_cast<DexOpcodeString*>(opcode); DexString* dsclzref = stringop->get_string(); DexType* dtexclude = get_dextype_from_dotname(dsclzref->c_str()); if (dtexclude == nullptr) continue; mark_reachable_by_classname(dtexclude, true); } if (opcode->has_types()) { // Matches the following instructions (from most to least // common): // check-cast, new-instance, const-class, instance-of // new-instance should not be on this list, and // we should not allow these to operate on themselves. // TODO(snay/dalves) auto typeop = static_cast<DexOpcodeType*>(opcode); mark_reachable_directly(typeop->get_type()); } } }); }