libredex/DexClass.cpp (1,906 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "DexClass.h"
#include "Debug.h"
#include "DexAccess.h"
#include "DexAnnotation.h"
#include "DexDebugInstruction.h"
#include "DexDefs.h"
#include "DexIdx.h"
#include "DexInstruction.h"
#include "DexMemberRefs.h"
#include "DexOutput.h"
#include "DexPosition.h"
#include "DexUtil.h"
#include "DuplicateClasses.h"
#include "IRCode.h"
#include "IRInstruction.h"
#include "RedexContext.h"
#include "Show.h"
#include "StringBuilder.h"
#include "Util.h"
#include "Walkers.h"
#include "Warning.h"
#include <algorithm>
#include <boost/functional/hash.hpp>
#include <boost/optional.hpp>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <unordered_set>
#define INSTANTIATE(METHOD, TYPE) \
template void METHOD(std::vector<TYPE>&) const; \
template void METHOD(std::unordered_set<TYPE>&) const; \
template void METHOD(std::vector<const TYPE>&) const; \
template void METHOD(std::unordered_set<const TYPE>&) const;
#define INSTANTIATE2(METHOD, TYPE, OTYPE) \
template void METHOD(std::vector<TYPE>&, OTYPE) const; \
template void METHOD(std::unordered_set<TYPE>&, OTYPE) const; \
template void METHOD(std::vector<const TYPE>&, OTYPE) const; \
template void METHOD(std::unordered_set<const TYPE>&, OTYPE) const;
namespace {
template <typename C, typename T>
struct InsertionHelper;
template <typename T>
struct InsertionHelper<std::vector<T>, T> {
void append(std::vector<T>& c, T t) { c.emplace_back(std::move(t)); }
template <typename It>
void append_all(std::vector<T>& c, It first, It last) {
c.insert(c.end(), first, last);
}
};
template <typename T>
struct InsertionHelper<std::unordered_set<T>, T> {
void append(std::unordered_set<T>& c, T t) { c.emplace(std::move(t)); }
template <typename It>
void append_all(std::unordered_set<T>& c, It first, It last) {
c.insert(first, last);
}
};
template <typename C>
void c_append(C& c, typename C::value_type t) {
InsertionHelper<C, typename C::value_type>().append(c, t);
}
template <typename C, typename It>
void c_append_all(C& c, It first, It last) {
InsertionHelper<C, typename C::value_type>().append_all(c, first, last);
}
} // namespace
namespace {
// Why? get_deobfuscated_name and show_deobfuscated are not enough. deobfuscated
// names could be empty, e.g., when Redex-created methods. So we need a better
// job. And proto and type are still obfuscated in some cases. We also implement
// show_deobfuscated for DexProto.
std::string build_fully_deobfuscated_name(const DexMethod* m) {
string_builders::StaticStringBuilder<5> b;
DexClass* cls = type_class(m->get_class());
if (cls == nullptr) {
// Well, just for safety.
b << "<null>";
} else {
b << std::string(cls->get_deobfuscated_name_or_empty().empty()
? cls->get_name()->str()
: cls->get_deobfuscated_name_or_empty());
}
b << "." << m->get_simple_deobfuscated_name() << ":"
<< show_deobfuscated(m->get_proto());
return b.str();
}
// Return just the name of the method/field.
template <typename T>
std::string get_simple_deobf_name(const T* ref) {
const auto& full_name = ref->get_deobfuscated_name_or_empty();
if (full_name.empty()) {
// This comes up for redex-created methods/fields.
return std::string(ref->c_str());
}
auto dot_pos = full_name.find('.');
auto colon_pos = full_name.find(':');
if (dot_pos == std::string::npos || colon_pos == std::string::npos) {
return full_name;
}
return full_name.substr(dot_pos + 1, colon_pos - dot_pos - 1);
}
} // namespace
const std::string DexString::EMPTY;
const DexString* DexString::make_string(std::string_view nstr) {
return g_redex->make_string(nstr);
}
// Return an existing DexString or nullptr if one does not exist.
const DexString* DexString::get_string(std::string_view s) {
return g_redex->get_string(s);
}
int32_t DexString::java_hashcode() const {
return java_hashcode_of_utf8_string(c_str());
}
int DexTypeList::encode(DexOutputIdx* dodx, uint32_t* output) const {
uint16_t* typep = (uint16_t*)(output + 1);
*output = (uint32_t)m_list.size();
for (auto const& type : m_list) {
*typep++ = dodx->typeidx(type);
}
return (int)(((uint8_t*)typep) - (uint8_t*)output);
}
DexTypeList* DexTypeList::push_front(DexType* t) const {
ContainerType new_list;
new_list.push_back(t);
new_list.insert(new_list.end(), m_list.begin(), m_list.end());
return make_type_list(std::move(new_list));
}
DexTypeList* DexTypeList::pop_front() const {
redex_assert(!m_list.empty());
ContainerType new_list{m_list.begin() + 1, m_list.end()};
return make_type_list(std::move(new_list));
}
DexTypeList* DexTypeList::pop_front(size_t n) const {
redex_assert(m_list.size() >= n);
ContainerType new_list{m_list.begin() + n, m_list.end()};
return make_type_list(std::move(new_list));
}
DexTypeList* DexTypeList::push_back(DexType* t) const {
ContainerType new_list{m_list};
new_list.push_back(t);
return make_type_list(std::move(new_list));
}
DexTypeList* DexTypeList::push_back(const std::vector<DexType*>& t) const {
ContainerType new_list{m_list};
new_list.insert(new_list.end(), t.begin(), t.end());
return make_type_list(std::move(new_list));
}
DexTypeList* DexTypeList::replace_head(DexType* new_head) const {
redex_assert(!m_list.empty());
ContainerType new_list{m_list};
new_list[0] = new_head;
return make_type_list(std::move(new_list));
}
DexTypeList* DexTypeList::make_type_list(ContainerType&& p) {
return g_redex->make_type_list(std::move(p));
}
DexTypeList* DexTypeList::get_type_list(const ContainerType& p) {
return g_redex->get_type_list(p);
}
DexField::DexField(DexType* container, const DexString* name, DexType* type)
: DexFieldRef(container, name, type),
m_access(static_cast<DexAccessFlags>(0)),
m_value(nullptr) {}
DexField::~DexField() = default; // For forwarding.
DexField* DexFieldRef::make_concrete(DexAccessFlags access_flags) {
return make_concrete(access_flags, nullptr);
}
DexField* DexFieldRef::make_concrete(DexAccessFlags access_flags,
std::unique_ptr<DexEncodedValue> v) {
// FIXME assert if already concrete
auto that = static_cast<DexField*>(this);
that->m_access = access_flags;
that->m_concrete = true;
if (is_static(access_flags)) {
that->set_value(std::move(v));
} else {
always_assert(v == nullptr);
}
return that;
}
void DexFieldRef::change(const DexFieldSpec& ref, bool rename_on_collision) {
g_redex->mutate_field(this, ref, rename_on_collision);
}
void DexFieldRef::erase_field(DexFieldRef* f) {
return g_redex->erase_field(f);
}
DexFieldRef* DexField::get_field(
const dex_member_refs::FieldDescriptorTokens& fdt) {
auto cls = DexType::get_type(fdt.cls);
auto name = DexString::get_string(fdt.name);
auto type = DexType::get_type(fdt.type);
return DexField::get_field(cls, name, type);
}
DexFieldRef* DexField::get_field(std::string_view full_descriptor) {
return get_field(dex_member_refs::parse_field(full_descriptor));
}
DexFieldRef* DexField::make_field(const DexType* container,
const DexString* name,
const DexType* type) {
return g_redex->make_field(container, name, type);
}
DexFieldRef* DexField::get_field(const DexType* container,
const DexString* name,
const DexType* type) {
return g_redex->get_field(container, name, type);
}
DexFieldRef* DexField::make_field(std::string_view full_descriptor) {
auto fdt = dex_member_refs::parse_field(full_descriptor);
auto cls = DexType::make_type(fdt.cls);
auto name = DexString::make_string(fdt.name);
auto type = DexType::make_type(fdt.type);
return DexField::make_field(cls, name, type);
}
void DexField::set_external() {
always_assert_log(!m_concrete, "Unexpected concrete field %s\n",
self_show().c_str());
m_deobfuscated_name = self_show();
m_external = true;
}
void DexField::set_value(std::unique_ptr<DexEncodedValue> v) {
always_assert_log(
m_concrete,
"Field needs to be concrete to be attached an encoded value.");
always_assert(is_static(m_access));
// The last contiguous block of static fields with null values are not
// represented in the encoded value array. OTOH null-initialized static
// fields that appear earlier in the static field list have explicit values.
// Let's standardize things here.
m_value =
v != nullptr ? std::move(v) : DexEncodedValue::zero_for_type(get_type());
}
void DexField::clear_annotations() { m_anno.reset(); }
void DexField::attach_annotation_set(std::unique_ptr<DexAnnotationSet> aset) {
always_assert_type_log(!m_concrete, RedexError::BAD_ANNOTATION,
"field %s.%s is concrete\n",
m_spec.cls->get_name()->c_str(), m_spec.name->c_str());
always_assert_type_log(!m_anno, RedexError::BAD_ANNOTATION,
"field %s.%s annotation exists\n",
m_spec.cls->get_name()->c_str(), m_spec.name->c_str());
m_anno = std::move(aset);
}
std::unique_ptr<DexAnnotationSet> DexField::release_annotations() {
return std::move(m_anno);
}
DexDebugEntry::DexDebugEntry(uint32_t addr,
std::unique_ptr<DexDebugInstruction> insn)
: type(DexDebugEntryType::Instruction), addr(addr), insn(std::move(insn)) {}
DexDebugEntry::DexDebugEntry(uint32_t addr, std::unique_ptr<DexPosition> pos)
: type(DexDebugEntryType::Position), addr(addr), pos(std::move(pos)) {}
DexDebugEntry::DexDebugEntry(DexDebugEntry&& other) noexcept
: type(other.type), addr(other.addr) {
switch (type) {
case DexDebugEntryType::Position:
new (&pos) std::unique_ptr<DexPosition>(std::move(other.pos));
break;
case DexDebugEntryType::Instruction:
new (&insn) std::unique_ptr<DexDebugInstruction>(std::move(other.insn));
break;
}
}
DexDebugEntry::~DexDebugEntry() {
switch (type) {
case DexDebugEntryType::Position:
pos.~unique_ptr<DexPosition>();
break;
case DexDebugEntryType::Instruction:
insn.~unique_ptr<DexDebugInstruction>();
break;
}
}
void DexDebugEntry::gather_strings(
std::vector<const DexString*>& lstring) const {
if (type == DexDebugEntryType::Instruction) {
insn->gather_strings(lstring);
}
}
void DexDebugEntry::gather_types(std::vector<DexType*>& ltype) const {
if (type == DexDebugEntryType::Instruction) {
insn->gather_types(ltype);
}
}
/*
* Evaluate the debug opcodes to figure out their absolute addresses and line
* numbers.
*/
static std::vector<DexDebugEntry> eval_debug_instructions(
DexDebugItem* dbg,
DexIdx* idx,
const uint8_t** encdata_ptr,
uint32_t absolute_line) {
std::vector<DexDebugEntry> entries;
// Likely overallocate and then shrink down in an effort to avoid the
// resize overhead.
constexpr size_t kReserveSize = 10000;
entries.reserve(kReserveSize);
uint32_t pc = 0;
while (true) {
std::unique_ptr<DexDebugInstruction> opcode(
DexDebugInstruction::make_instruction(idx, encdata_ptr));
if (opcode == nullptr) {
break;
}
auto op = opcode->opcode();
switch (op) {
case DBG_ADVANCE_LINE: {
absolute_line += opcode->value();
continue;
}
case DBG_END_LOCAL:
case DBG_RESTART_LOCAL:
case DBG_START_LOCAL:
case DBG_START_LOCAL_EXTENDED:
case DBG_SET_FILE:
case DBG_END_SEQUENCE:
case DBG_SET_PROLOGUE_END:
case DBG_SET_EPILOGUE_BEGIN: {
entries.emplace_back(pc, std::move(opcode));
break;
}
case DBG_ADVANCE_PC: {
pc += opcode->uvalue();
continue;
}
default: {
uint8_t adjustment = op - DBG_FIRST_SPECIAL;
absolute_line += DBG_LINE_BASE + (adjustment % DBG_LINE_RANGE);
pc += adjustment / DBG_LINE_RANGE;
entries.emplace_back(pc, std::make_unique<DexPosition>(absolute_line));
break;
}
}
}
entries.shrink_to_fit();
return entries;
}
DexDebugItem::DexDebugItem(DexIdx* idx, uint32_t offset)
: m_source_checksum(idx->get_checksum()), m_source_offset(offset) {
const uint8_t* encdata = idx->get_uleb_data(offset);
const uint8_t* base_encdata = encdata;
uint32_t line_start = read_uleb128(&encdata);
uint32_t paramcount = read_uleb128(&encdata);
while (paramcount--) {
// We intentionally drop the parameter string name here because we don't
// have a convenient representation of it, and our internal tooling doesn't
// use this info anyway.
// We emit matching number of nulls as method arguments at the end.
decode_noindexable_string(idx, encdata);
}
m_dbg_entries = eval_debug_instructions(this, idx, &encdata, line_start);
m_on_disk_size = encdata - base_encdata;
}
uint32_t DexDebugItem::get_line_start() const {
for (auto& entry : m_dbg_entries) {
switch (entry.type) {
case DexDebugEntryType::Position: {
return entry.pos->line;
default:
break;
}
}
}
return 0;
}
DexDebugItem::DexDebugItem(const DexDebugItem& that) {
std::unordered_map<DexPosition*, DexPosition*> pos_map;
m_dbg_entries.reserve(that.m_dbg_entries.size());
for (auto& entry : that.m_dbg_entries) {
switch (entry.type) {
case DexDebugEntryType::Position: {
auto pos = std::make_unique<DexPosition>(*entry.pos);
pos_map[entry.pos.get()] = pos.get();
pos->parent = pos_map[pos->parent];
m_dbg_entries.emplace_back(entry.addr, std::move(pos));
break;
}
case DexDebugEntryType::Instruction:
m_dbg_entries.emplace_back(entry.addr, entry.insn->clone());
break;
}
}
}
std::unique_ptr<DexDebugItem> DexDebugItem::get_dex_debug(DexIdx* idx,
uint32_t offset) {
if (offset == 0) return nullptr;
return std::unique_ptr<DexDebugItem>(new DexDebugItem(idx, offset));
}
/*
* Convert DexDebugEntries into debug opcodes.
*/
std::vector<std::unique_ptr<DexDebugInstruction>> generate_debug_instructions(
DexDebugItem* debugitem,
PositionMapper* pos_mapper,
uint32_t* line_start,
std::vector<DebugLineItem>* line_info,
uint32_t line_addin) {
std::vector<std::unique_ptr<DexDebugInstruction>> dbgops;
uint32_t prev_addr = 0;
boost::optional<uint32_t> prev_line;
auto& entries = debugitem->get_entries();
for (auto it = entries.begin(); it != entries.end(); ++it) {
// find all entries that belong to the same address, and group them by type
auto addr = it->addr;
std::vector<DexPosition*> positions;
std::vector<DexDebugInstruction*> insns;
for (; it != entries.end() && it->addr == addr; ++it) {
switch (it->type) {
case DexDebugEntryType::Position:
if (it->pos->file != nullptr) {
positions.push_back(it->pos.get());
}
break;
case DexDebugEntryType::Instruction:
insns.push_back(it->insn.get());
break;
}
}
--it;
auto addr_delta = addr - prev_addr;
prev_addr = addr;
for (auto pos : positions) {
pos_mapper->register_position(pos);
}
// only emit the last position entry for a given address
if (!positions.empty()) {
auto line_base = pos_mapper->position_to_line(positions.back());
auto line = line_base | line_addin;
line_info->emplace_back(DebugLineItem(it->addr, line_base));
int32_t line_delta;
if (prev_line) {
line_delta = line - *prev_line;
} else {
*line_start = line;
line_delta = 0;
}
prev_line = line;
if (line_delta < DBG_LINE_BASE ||
line_delta >= (DBG_LINE_RANGE + DBG_LINE_BASE)) {
dbgops.emplace_back(
new DexDebugInstruction(DBG_ADVANCE_LINE, line_delta));
line_delta = 0;
}
auto special = (line_delta - DBG_LINE_BASE) +
(addr_delta * DBG_LINE_RANGE) + DBG_FIRST_SPECIAL;
if (special & ~0xff) {
dbgops.emplace_back(
new DexDebugInstruction(DBG_ADVANCE_PC, uint32_t(addr_delta)));
special = line_delta - DBG_LINE_BASE + DBG_FIRST_SPECIAL;
}
dbgops.emplace_back(
new DexDebugInstruction(static_cast<DexDebugItemOpcode>(special)));
line_delta = 0;
addr_delta = 0;
}
for (auto insn : insns) {
if (addr_delta != 0) {
dbgops.emplace_back(
new DexDebugInstruction(DBG_ADVANCE_PC, addr_delta));
addr_delta = 0;
}
dbgops.emplace_back(insn->clone());
}
}
return dbgops;
}
int DexDebugItem::encode(
DexOutputIdx* dodx,
uint8_t* output,
uint32_t line_start,
uint32_t num_params,
const std::vector<std::unique_ptr<DexDebugInstruction>>& dbgops) {
uint8_t* encdata = output;
encdata = write_uleb128(encdata, line_start);
encdata = write_uleb128(encdata, num_params);
for (uint32_t i = 0; i < num_params; ++i) {
encdata = write_uleb128p1(encdata, DEX_NO_INDEX);
}
for (auto& dbgop : dbgops) {
dbgop->encode(dodx, encdata);
}
encdata = write_uleb128(encdata, DBG_END_SEQUENCE);
return (int)(encdata - output);
}
void DexDebugItem::bind_positions(DexMethod* method, const DexString* file) {
auto* method_str = DexString::make_string(show(method));
for (auto& entry : m_dbg_entries) {
switch (entry.type) {
case DexDebugEntryType::Position:
entry.pos->bind(method_str, file);
break;
case DexDebugEntryType::Instruction:
break;
}
}
}
void DexDebugItem::gather_types(std::vector<DexType*>& ltype) const {
for (auto& entry : m_dbg_entries) {
entry.gather_types(ltype);
}
}
void DexDebugItem::gather_strings(
std::vector<const DexString*>& lstring) const {
for (auto& entry : m_dbg_entries) {
entry.gather_strings(lstring);
}
}
DexCode::DexCode(const DexCode& that)
: m_registers_size(that.m_registers_size),
m_ins_size(that.m_ins_size),
m_outs_size(that.m_outs_size),
m_insns(that.m_insns ? std::make_optional<std::vector<DexInstruction*>>()
: std::nullopt) {
if (that.m_insns) {
for (auto& insn : *that.m_insns) {
m_insns->emplace_back(insn->clone());
}
}
for (auto& try_ : that.m_tries) {
m_tries.emplace_back(new DexTryItem(*try_));
}
if (that.m_dbg) {
m_dbg.reset(new DexDebugItem(*that.m_dbg));
}
}
DexCode::~DexCode() {
if (m_insns) {
for (auto const& op : *m_insns) {
delete op;
}
}
}
std::unique_ptr<DexCode> DexCode::get_dex_code(DexIdx* idx, uint32_t offset) {
if (offset == 0) return std::unique_ptr<DexCode>();
const dex_code_item* code = (const dex_code_item*)idx->get_uint_data(offset);
std::unique_ptr<DexCode> dc(new DexCode());
dc->m_registers_size = code->registers_size;
dc->m_ins_size = code->ins_size;
dc->m_outs_size = code->outs_size;
dc->m_insns = std::vector<DexInstruction*>();
const uint16_t* cdata = (const uint16_t*)(code + 1);
uint32_t tries = code->tries_size;
if (code->insns_size) {
// On average there seem to be about two code units per instruction
dc->m_insns->reserve(code->insns_size / 2);
const uint16_t* end = cdata + code->insns_size;
while (cdata < end) {
DexInstruction* dop = DexInstruction::make_instruction(idx, &cdata);
always_assert_log(dop != nullptr,
"Failed to parse method at offset 0x%08x", offset);
dc->m_insns->push_back(dop);
}
/*
* Padding, see dex-spec.
* Per my memory, there are dex-files where the padding is
* implemented not according to spec. Just FYI in case
* something weird happens in the future.
*/
if (code->insns_size & 1 && tries) cdata++;
}
if (tries) {
const dex_tries_item* dti = (const dex_tries_item*)cdata;
const uint8_t* handlers = (const uint8_t*)(dti + tries);
for (uint32_t i = 0; i < tries; i++) {
DexTryItem* dextry = new DexTryItem(dti[i].start_addr, dti[i].insn_count);
const uint8_t* handler = handlers + dti[i].handler_off;
int32_t count = read_sleb128(&handler);
bool has_catchall = false;
if (count <= 0) {
count = -count;
has_catchall = true;
}
while (count--) {
uint32_t tidx = read_uleb128(&handler);
uint32_t hoff = read_uleb128(&handler);
DexType* dt = idx->get_typeidx(tidx);
dextry->m_catches.push_back(std::make_pair(dt, hoff));
}
if (has_catchall) {
auto hoff = read_uleb128(&handler);
dextry->m_catches.push_back(std::make_pair(nullptr, hoff));
}
dc->m_tries.emplace_back(dextry);
}
}
dc->m_dbg = DexDebugItem::get_dex_debug(idx, code->debug_info_off);
return dc;
}
int DexCode::encode(DexOutputIdx* dodx, uint32_t* output) {
dex_code_item* code = (dex_code_item*)output;
code->registers_size = m_registers_size;
code->ins_size = m_ins_size;
code->outs_size = m_outs_size;
code->tries_size = 0;
/* Debug info is added later */
code->debug_info_off = 0;
uint16_t* insns = (uint16_t*)(code + 1);
for (auto const& opc : get_instructions()) {
opc->encode(dodx, insns);
}
code->insns_size = (uint32_t)(insns - ((uint16_t*)(code + 1)));
if (m_tries.empty())
return ((code->insns_size * sizeof(uint16_t)) + sizeof(dex_code_item));
/*
* Now the tries..., obscenely messy encoding :(
* Pad tries to uint32_t
*/
if (code->insns_size & 1) insns++;
int tries = code->tries_size = m_tries.size();
dex_tries_item* dti = (dex_tries_item*)insns;
uint8_t* handler_base = (uint8_t*)(dti + tries);
uint8_t* hemit = handler_base;
std::unordered_set<DexCatches, boost::hash<DexCatches>> catches_set;
for (auto& dextry : m_tries) {
catches_set.insert(dextry->m_catches);
}
hemit = write_uleb128(hemit, catches_set.size());
int tryno = 0;
std::unordered_map<DexCatches, uint32_t, boost::hash<DexCatches>> catches_map;
for (auto it = m_tries.begin(); it != m_tries.end(); ++it, ++tryno) {
auto& dextry = *it;
always_assert(dextry->m_start_addr < code->insns_size);
dti[tryno].start_addr = dextry->m_start_addr;
always_assert(dextry->m_start_addr + dextry->m_insn_count <=
code->insns_size);
dti[tryno].insn_count = dextry->m_insn_count;
if (catches_map.find(dextry->m_catches) == catches_map.end()) {
catches_map[dextry->m_catches] = hemit - handler_base;
size_t catchcount = dextry->m_catches.size();
bool has_catchall = dextry->m_catches.back().first == nullptr;
if (has_catchall) {
// -1 because the catch-all address is last (without an address)
catchcount = -(catchcount - 1);
}
hemit = write_sleb128(hemit, (int32_t)catchcount);
for (auto const& cit : dextry->m_catches) {
auto type = cit.first;
auto catch_addr = cit.second;
if (type != nullptr) {
// Assumption: The only catch-all is at the end of the list
hemit = write_uleb128(hemit, dodx->typeidx(type));
}
always_assert(catch_addr < code->insns_size);
hemit = write_uleb128(hemit, catch_addr);
}
}
dti[tryno].handler_off = catches_map.at(dextry->m_catches);
}
return (int)(hemit - ((uint8_t*)output));
}
DexMethod::DexMethod(DexType* type, const DexString* name, DexProto* proto)
: DexMethodRef(type, name, proto) {
m_virtual = false;
m_anno = nullptr;
m_dex_code = nullptr;
m_code = nullptr;
m_access = static_cast<DexAccessFlags>(0);
}
DexMethod::~DexMethod() = default;
void DexMethod::delete_method(DexMethod* m) { m->make_non_concrete(); }
std::string DexMethod::get_fully_deobfuscated_name() const {
if (m_deobfuscated_name != nullptr &&
get_deobfuscated_name().str() == show(this)) {
return get_deobfuscated_name().str();
}
return build_fully_deobfuscated_name(this);
}
void DexMethod::set_code(std::unique_ptr<IRCode> code) {
m_code = std::move(code);
}
void DexMethod::balloon() {
redex_assert(m_code == nullptr);
m_code = std::make_unique<IRCode>(this);
m_dex_code.reset();
}
void DexMethod::sync() {
redex_assert(m_dex_code == nullptr);
m_dex_code = m_code->sync(this);
m_code.reset();
}
size_t hash_value(const DexMethodSpec& r) {
size_t seed = boost::hash<DexType*>()(r.cls);
boost::hash_combine(seed, r.name);
boost::hash_combine(seed, r.proto);
return seed;
}
DexMethodRef* DexMethod::make_method(const DexType* type,
const DexString* name,
const DexProto* proto) {
return g_redex->make_method(type, name, proto);
}
DexMethodRef* DexMethod::make_method(const DexMethodSpec& spec) {
return g_redex->make_method(spec.cls, spec.name, spec.proto);
}
DexMethod* DexMethod::make_method_from(DexMethod* that,
DexType* target_cls,
const DexString* name) {
auto m = static_cast<DexMethod*>(
DexMethod::make_method(target_cls, name, that->get_proto()));
redex_assert(m != that);
if (that->m_anno) {
m->m_anno = std::make_unique<DexAnnotationSet>(*that->m_anno);
}
if (!is_abstract(that)) {
always_assert_log(that->get_code() != nullptr, "%s", vshow(that).c_str());
m->set_code(std::make_unique<IRCode>(*that->get_code()));
} else {
redex_assert(that->get_code() == nullptr);
}
m->m_access = that->m_access;
m->m_concrete = that->m_concrete;
m->m_virtual = that->m_virtual;
m->m_external = that->m_external;
if (that->m_param_anno != nullptr) {
if (m->m_param_anno == nullptr) {
m->m_param_anno = std::make_unique<ParamAnnotations>();
}
for (auto& pair : *that->m_param_anno) {
// note: DexAnnotation's copy ctor only does a shallow copy
m->m_param_anno->emplace(pair.first, new DexAnnotationSet(*pair.second));
}
}
return m;
}
DexMethodRef* DexMethod::get_method(const DexType* type,
const DexString* name,
const DexProto* proto) {
return g_redex->get_method(type, name, proto);
}
DexMethodRef* DexMethod::get_method(const DexMethodSpec& spec) {
return g_redex->get_method(spec.cls, spec.name, spec.proto);
}
DexMethod* DexMethod::make_full_method_from(DexMethod* that,
DexType* target_cls,
const DexString* name) {
auto m = make_method_from(that, target_cls, name);
m->rstate = that->rstate;
return m;
}
DexMethodRef* DexMethod::get_method(
const dex_member_refs::MethodDescriptorTokens& mdt) {
auto cls = DexType::get_type(mdt.cls);
auto name = DexString::get_string(mdt.name);
DexTypeList::ContainerType args;
for (auto& arg_str : mdt.args) {
args.push_back(DexType::get_type(arg_str));
}
auto dtl = DexTypeList::get_type_list(args);
if (dtl == nullptr) {
return nullptr;
}
auto rtype = DexType::get_type(mdt.rtype);
if (rtype == nullptr) {
return nullptr;
}
return DexMethod::get_method(cls, name, DexProto::get_proto(rtype, dtl));
}
template <bool kCheckFormat>
DexMethodRef* DexMethod::get_method(std::string_view full_descriptor) {
return get_method(
dex_member_refs::parse_method<kCheckFormat>(full_descriptor));
}
template DexMethodRef* DexMethod::get_method<false>(std::string_view);
template DexMethodRef* DexMethod::get_method<true>(std::string_view);
DexMethodRef* DexMethod::make_method(std::string_view full_descriptor) {
auto mdt = dex_member_refs::parse_method(full_descriptor);
auto cls = DexType::make_type(mdt.cls);
auto name = DexString::make_string(mdt.name);
DexTypeList::ContainerType args;
for (auto& arg_str : mdt.args) {
args.push_back(DexType::make_type(arg_str));
}
auto dtl = DexTypeList::make_type_list(std::move(args));
auto rtype = DexType::make_type(mdt.rtype);
return DexMethod::make_method(cls, name, DexProto::make_proto(rtype, dtl));
}
DexMethodRef* DexMethod::make_method(
const std::string& class_type,
const std::string& name,
std::initializer_list<std::string> arg_types,
const std::string& return_type) {
DexTypeList::ContainerType dex_types;
for (const std::string& type_str : arg_types) {
dex_types.push_back(DexType::make_type(type_str.c_str()));
}
return DexMethod::make_method(
DexType::make_type(class_type.c_str()),
DexString::make_string(name),
DexProto::make_proto(DexType::make_type(return_type.c_str()),
DexTypeList::make_type_list(std::move(dex_types))));
}
void DexMethod::combine_annotations_with(DexMethod* other) {
auto other_anno_set = other->get_anno_set();
if (other_anno_set != nullptr) {
if (m_anno == nullptr) {
m_anno = std::make_unique<DexAnnotationSet>(*other->m_anno);
} else {
m_anno->combine_with(*other->m_anno);
}
}
if (other->m_param_anno != nullptr) {
if (m_param_anno == nullptr) {
m_param_anno = std::make_unique<ParamAnnotations>();
}
for (auto& pair : *other->m_param_anno) {
if (m_param_anno->count(pair.first) == 0 ||
m_param_anno->at(pair.first) == nullptr) {
m_param_anno->emplace(pair.first, new DexAnnotationSet(*pair.second));
} else {
(*m_param_anno)[pair.first]->combine_with(*pair.second);
}
}
}
}
void DexMethod::clear_annotations() { m_anno.reset(); }
std::unique_ptr<ParamAnnotations> DexMethod::release_param_anno() {
return std::move(m_param_anno);
}
void DexMethod::attach_annotation_set(std::unique_ptr<DexAnnotationSet> aset) {
always_assert_type_log(!m_concrete, RedexError::BAD_ANNOTATION,
"method %s is concrete\n", self_show().c_str());
always_assert_type_log(!m_anno, RedexError::BAD_ANNOTATION,
"method %s annotation exists\n", self_show().c_str());
m_anno = std::move(aset);
}
void DexMethod::attach_param_annotation_set(
int paramno, std::unique_ptr<DexAnnotationSet> aset) {
always_assert_type_log(!m_concrete, RedexError::BAD_ANNOTATION,
"method %s is concrete\n", self_show().c_str());
always_assert_type_log(
m_param_anno == nullptr || m_param_anno->count(paramno) == 0,
RedexError::BAD_ANNOTATION, "param %d annotation to method %s exists\n",
paramno, self_show().c_str());
if (m_param_anno == nullptr) {
m_param_anno = std::make_unique<ParamAnnotations>();
}
(*m_param_anno)[paramno] = std::move(aset);
}
std::unique_ptr<DexAnnotationSet> DexMethod::release_annotations() {
return std::move(m_anno);
}
DexLocation::DexLocation(std::string store_name, std::string file_name)
: m_store_name(std::move(store_name)), m_file_name(std::move(file_name)) {}
const DexLocation* DexLocation::make_location(std::string_view store_name,
std::string_view file_name) {
return g_redex->make_location(store_name, file_name);
}
const DexLocation* DexLocation::get_location(std::string_view store_name,
std::string_view file_name) {
return g_redex->get_location(store_name, file_name);
}
void DexClass::set_deobfuscated_name(const std::string& name) {
// If the class has an old deobfuscated_name which is not equal to
// `show(self)`, erase the name mapping from the global type map.
if (kInsertDeobfuscatedNameLinks && m_deobfuscated_name != nullptr) {
if (m_deobfuscated_name != m_self->get_name()) {
g_redex->remove_type_name(m_deobfuscated_name);
}
}
m_deobfuscated_name = DexString::make_string(name);
if (!kInsertDeobfuscatedNameLinks) {
return;
}
if (m_deobfuscated_name == m_self->get_name()) {
return;
}
auto existing_type = g_redex->get_type(m_deobfuscated_name);
if (existing_type != nullptr) {
TRACE(DC, 5,
"Unable to alias type '%s' to deobfuscated name '%s' because type "
"'%s' already exists.\n",
m_self->c_str(), m_deobfuscated_name->c_str(),
existing_type->c_str());
return;
}
g_redex->alias_type_name(m_self, m_deobfuscated_name);
}
void DexClass::set_deobfuscated_name(const DexString* name) {
// If the class has an old deobfuscated_name which is not equal to
// `show(self)`, erase the name mapping from the global type map.
if (kInsertDeobfuscatedNameLinks && m_deobfuscated_name != nullptr) {
if (m_deobfuscated_name != m_self->get_name()) {
g_redex->remove_type_name(m_deobfuscated_name);
}
}
m_deobfuscated_name = name;
if (!kInsertDeobfuscatedNameLinks) {
return;
}
if (m_deobfuscated_name == m_self->get_name()) {
return;
}
auto existing_type = g_redex->get_type(m_deobfuscated_name);
if (existing_type != nullptr) {
TRACE(DC, 5,
"Unable to alias type '%s' to deobfuscated name '%s' because type "
"'%s' already exists.\n",
m_self->c_str(), m_deobfuscated_name->c_str(),
existing_type->c_str());
return;
}
g_redex->alias_type_name(m_self, m_deobfuscated_name);
}
void DexClass::set_deobfuscated_name(const DexString& name) {
set_deobfuscated_name(&name);
}
void DexClass::set_external() {
m_deobfuscated_name = DexString::make_string(self_show());
m_external = true;
}
void DexClass::remove_method(const DexMethod* m) {
auto& meths = m->is_virtual() ? m_vmethods : m_dmethods;
auto it = std::find(meths.begin(), meths.end(), m);
DEBUG_ONLY bool erased = false;
if (it != meths.end()) {
erased = true;
meths.erase(it);
}
redex_assert(erased);
}
void DexClass::remove_method_definition(DexMethod* m) {
remove_method(m);
// Virtually delete the definition of the method.
m->m_concrete = false;
m->release_code();
}
void DexMethod::become_virtual() {
redex_assert(!m_virtual);
auto cls = type_class(m_spec.cls);
redex_assert(!cls->is_external());
cls->remove_method(this);
m_virtual = true;
auto& vmethods = cls->get_vmethods();
insert_sorted(vmethods, this, compare_dexmethods);
}
DexMethod* DexMethodRef::make_concrete(DexAccessFlags access,
std::unique_ptr<DexCode> dc,
bool is_virtual) {
auto that = static_cast<DexMethod*>(this);
that->m_access = access;
that->m_dex_code = std::move(dc);
that->m_concrete = true;
that->m_virtual = is_virtual;
return that;
}
DexMethod* DexMethodRef::make_concrete(DexAccessFlags access,
std::unique_ptr<IRCode> dc,
bool is_virtual) {
auto that = static_cast<DexMethod*>(this);
that->m_access = access;
that->m_code = std::move(dc);
that->m_concrete = true;
that->m_virtual = is_virtual;
return that;
}
DexMethod* DexMethodRef::make_concrete(DexAccessFlags access, bool is_virtual) {
return make_concrete(access, std::unique_ptr<IRCode>(nullptr), is_virtual);
}
void DexMethodRef::change(const DexMethodSpec& ref, bool rename_on_collision) {
g_redex->mutate_method(this, ref, rename_on_collision);
}
void DexMethod::make_non_concrete() {
m_access = static_cast<DexAccessFlags>(0);
m_concrete = false;
m_code.reset();
m_virtual = false;
m_param_anno.reset();
m_anno.reset();
}
void DexMethod::set_deobfuscated_name(const std::string& name) {
// If the method has an old deobfuscated_name which is not equal to the name,
// erase the mapping using the old (and now invalid) deobfuscated_name from
// the global type map.
if (kInsertDeobfuscatedNameLinks && m_deobfuscated_name != nullptr &&
!m_deobfuscated_name->str().empty()) {
if (m_deobfuscated_name != this->get_name()) {
g_redex->erase_method(this->get_class(), m_deobfuscated_name,
this->get_proto());
}
}
m_deobfuscated_name = DexString::make_string(name);
if (!kInsertDeobfuscatedNameLinks) {
return;
}
if (m_deobfuscated_name == this->get_name()) {
return;
}
auto existing_method = g_redex->get_method(
this->get_class(), m_deobfuscated_name, this->get_proto());
if (existing_method != nullptr) {
TRACE(DC, 5,
"Unable to alias method '%s' to deobfuscated name '%s' because "
"method '%s' already exists.\n ",
this->c_str(), m_deobfuscated_name->c_str(),
existing_method->c_str());
return;
}
g_redex->alias_method_name(this, m_deobfuscated_name);
}
std::string DexMethod::get_simple_deobfuscated_name() const {
return get_simple_deobf_name(this);
}
/*
* See class_data_item in Dex spec.
*/
void DexClass::load_class_data_item(
DexIdx* idx,
uint32_t cdi_off,
std::unique_ptr<DexEncodedValueArray> svalues) {
if (cdi_off == 0) return;
const uint8_t* encd = idx->get_uleb_data(cdi_off);
uint32_t sfield_count = read_uleb128(&encd);
uint32_t ifield_count = read_uleb128(&encd);
uint32_t dmethod_count = read_uleb128(&encd);
uint32_t vmethod_count = read_uleb128(&encd);
uint32_t ndex = 0;
std::vector<std::unique_ptr<DexEncodedValue>> empty{};
std::vector<std::unique_ptr<DexEncodedValue>>& used =
(svalues == nullptr || svalues->evalues() == nullptr)
? empty
: *svalues->evalues();
auto it = used.begin();
m_sfields.reserve(sfield_count);
for (uint32_t i = 0; i < sfield_count; i++) {
ndex += read_uleb128(&encd);
auto access_flags = (DexAccessFlags)read_uleb128(&encd);
DexField* df = static_cast<DexField*>(idx->get_fieldidx(ndex));
std::unique_ptr<DexEncodedValue> ev = nullptr;
if (it != used.end()) {
ev = std::move(*it);
++it;
}
// We are gonna own the element.
df->make_concrete(access_flags, std::move(ev));
m_sfields.push_back(df);
}
ndex = 0;
m_ifields.reserve(ifield_count);
for (uint32_t i = 0; i < ifield_count; i++) {
ndex += read_uleb128(&encd);
auto access_flags = (DexAccessFlags)read_uleb128(&encd);
DexField* df = static_cast<DexField*>(idx->get_fieldidx(ndex));
df->make_concrete(access_flags);
m_ifields.push_back(df);
}
std::unordered_set<DexMethod*> method_pointer_cache;
method_pointer_cache.reserve(dmethod_count + vmethod_count);
auto process_method = [this, &encd, &idx, &method_pointer_cache](
uint32_t& ndex, bool is_virtual) {
ndex += read_uleb128(&encd);
auto access_flags = (DexAccessFlags)read_uleb128(&encd);
uint32_t code_off = read_uleb128(&encd);
// Find method in method index, returns same pointer for same method.
DexMethod* dm = static_cast<DexMethod*>(idx->get_methodidx(ndex));
std::unique_ptr<DexCode> dc = DexCode::get_dex_code(idx, code_off);
if (dc && dc->get_debug_item()) {
dc->get_debug_item()->bind_positions(dm, m_source_file);
}
dm->make_concrete(access_flags, std::move(dc), is_virtual);
const auto& pair = method_pointer_cache.insert(dm);
bool insertion_happened = pair.second;
always_assert_type_log(insertion_happened, RedexError::DUPLICATE_METHODS,
"Found duplicate methods in the same class. %s",
SHOW(dm));
return dm;
};
m_dmethods.reserve(dmethod_count);
ndex = 0;
for (uint32_t i = 0; i < dmethod_count; i++) {
DexMethod* dm = process_method(ndex, false);
m_dmethods.push_back(dm);
}
m_vmethods.reserve(vmethod_count);
ndex = 0;
for (uint32_t i = 0; i < vmethod_count; i++) {
DexMethod* dm = process_method(ndex, true);
m_vmethods.push_back(dm);
}
}
std::unique_ptr<IRCode> DexMethod::release_code() { return std::move(m_code); }
std::vector<DexMethod*> DexClass::get_all_methods() const {
std::vector<DexMethod*> all_methods(m_vmethods.begin(), m_vmethods.end());
all_methods.insert(all_methods.end(), m_dmethods.begin(), m_dmethods.end());
return all_methods;
}
void DexClass::add_method(DexMethod* m) {
always_assert_log(m->is_concrete() || m->is_external(),
"Method %s must be concrete",
SHOW(m));
always_assert(m->get_class() == get_type());
if (m->is_virtual()) {
insert_sorted(m_vmethods, m, compare_dexmethods);
} else {
insert_sorted(m_dmethods, m, compare_dexmethods);
}
}
std::vector<DexField*> DexClass::get_all_fields() const {
std::vector<DexField*> all_fields(m_ifields.begin(), m_ifields.end());
all_fields.insert(all_fields.end(), m_sfields.begin(), m_sfields.end());
return all_fields;
}
void DexClass::add_field(DexField* f) {
always_assert_log(f->is_concrete() || f->is_external(),
"Field %s must be concrete",
SHOW(f));
always_assert(f->get_class() == get_type());
bool is_static = f->get_access() & DexAccessFlags::ACC_STATIC;
if (is_static) {
insert_sorted(m_sfields, f, compare_dexfields);
} else {
insert_sorted(m_ifields, f, compare_dexfields);
}
}
void DexClass::remove_field(const DexField* f) {
bool is_static = f->get_access() & DexAccessFlags::ACC_STATIC;
auto& fields = is_static ? m_sfields : m_ifields;
DEBUG_ONLY bool erase = false;
auto it = std::find(fields.begin(), fields.end(), f);
if (it != fields.end()) {
erase = true;
fields.erase(it);
}
redex_assert(erase);
}
void DexClass::remove_field_definition(DexField* f) {
remove_field(f);
f->m_concrete = false;
}
void DexClass::sort_fields() {
auto& sfields = this->get_sfields();
auto& ifields = this->get_ifields();
std::sort(sfields.begin(), sfields.end(), compare_dexfields);
std::sort(ifields.begin(), ifields.end(), compare_dexfields);
}
void DexClass::sort_methods() {
auto& vmeths = this->get_vmethods();
auto& dmeths = this->get_dmethods();
std::sort(vmeths.begin(), vmeths.end(), compare_dexmethods);
std::sort(dmeths.begin(), dmeths.end(), compare_dexmethods);
}
DexField* DexClass::find_ifield(const char* name,
const DexType* field_type) const {
for (const auto f : m_ifields) {
if (std::strcmp(f->c_str(), name) == 0 && f->get_type() == field_type) {
return f;
}
}
return nullptr;
}
DexField* DexClass::find_sfield(const char* name,
const DexType* field_type) const {
for (const auto f : m_sfields) {
if (std::strcmp(f->c_str(), name) == 0 && f->get_type() == field_type) {
return f;
}
}
return nullptr;
}
bool DexClass::has_class_data() const {
return !m_vmethods.empty() || !m_dmethods.empty() || !m_ifields.empty() ||
!m_sfields.empty();
}
int DexClass::encode(DexOutputIdx* dodx,
dexcode_to_offset& dco,
uint8_t* output) {
if (m_sfields.empty() && m_ifields.empty() && m_dmethods.empty() &&
m_vmethods.empty()) {
opt_warn(PURE_ABSTRACT_CLASS,
"'%s' super '%s' flags 0x%08x\n",
m_self->get_name()->c_str(),
m_super_class->get_name()->c_str(),
m_access_flags);
}
sort_fields();
sort_methods();
uint8_t* encdata = output;
encdata = write_uleb128(encdata, (uint32_t)m_sfields.size());
encdata = write_uleb128(encdata, (uint32_t)m_ifields.size());
encdata = write_uleb128(encdata, (uint32_t)m_dmethods.size());
encdata = write_uleb128(encdata, (uint32_t)m_vmethods.size());
uint32_t idxbase;
idxbase = 0;
for (auto const& f : m_sfields) {
uint32_t idx = dodx->fieldidx(f);
encdata = write_uleb128(encdata, idx - idxbase);
idxbase = idx;
encdata = write_uleb128(encdata, f->get_access());
}
idxbase = 0;
for (auto const& f : m_ifields) {
uint32_t idx = dodx->fieldidx(f);
encdata = write_uleb128(encdata, idx - idxbase);
idxbase = idx;
encdata = write_uleb128(encdata, f->get_access());
}
idxbase = 0;
for (auto const& m : m_dmethods) {
uint32_t idx = dodx->methodidx(m);
always_assert_log(!m->is_virtual(),
"Virtual method in dmethod."
"\nOffending type: %s"
"\nOffending method: %s",
SHOW(this),
SHOW(m));
redex_assert(!m->is_virtual());
encdata = write_uleb128(encdata, idx - idxbase);
idxbase = idx;
encdata = write_uleb128(encdata, m->get_access());
uint32_t code_off = 0;
if (m->get_dex_code() != nullptr && dco.count(m->get_dex_code())) {
code_off = dco[m->get_dex_code()];
}
encdata = write_uleb128(encdata, code_off);
}
idxbase = 0;
for (auto const& m : m_vmethods) {
uint32_t idx = dodx->methodidx(m);
always_assert_log(m->is_virtual(),
"Direct method in vmethod."
"\nOffending type: %s"
"\nOffending method: %s",
SHOW(this),
SHOW(m));
redex_assert(m->is_virtual());
encdata = write_uleb128(encdata, idx - idxbase);
idxbase = idx;
encdata = write_uleb128(encdata, m->get_access());
uint32_t code_off = 0;
if (m->get_dex_code() != nullptr && dco.count(m->get_dex_code())) {
code_off = dco[m->get_dex_code()];
}
encdata = write_uleb128(encdata, code_off);
}
return (int)(encdata - output);
}
void DexClass::load_class_annotations(DexIdx* idx, uint32_t anno_off) {
if (anno_off == 0) return;
const dex_annotations_directory_item* annodir =
(const dex_annotations_directory_item*)idx->get_uint_data(anno_off);
m_anno =
DexAnnotationSet::get_annotation_set(idx, annodir->class_annotations_off);
const uint32_t* annodata = (uint32_t*)(annodir + 1);
for (uint32_t i = 0; i < annodir->fields_size; i++) {
uint32_t fidx = *annodata++;
uint32_t off = *annodata++;
DexField* field = static_cast<DexField*>(idx->get_fieldidx(fidx));
auto aset = DexAnnotationSet::get_annotation_set(idx, off);
field->attach_annotation_set(std::move(aset));
}
for (uint32_t i = 0; i < annodir->methods_size; i++) {
uint32_t midx = *annodata++;
uint32_t off = *annodata++;
DexMethod* method = static_cast<DexMethod*>(idx->get_methodidx(midx));
auto aset = DexAnnotationSet::get_annotation_set(idx, off);
method->attach_annotation_set(std::move(aset));
}
for (uint32_t i = 0; i < annodir->parameters_size; i++) {
uint32_t midx = *annodata++;
uint32_t xrefoff = *annodata++;
if (xrefoff != 0) {
DexMethod* method = static_cast<DexMethod*>(idx->get_methodidx(midx));
const uint32_t* annoxref = idx->get_uint_data(xrefoff);
uint32_t count = *annoxref++;
for (uint32_t j = 0; j < count; j++) {
uint32_t off = annoxref[j];
auto aset = DexAnnotationSet::get_annotation_set(idx, off);
if (aset != nullptr) {
method->attach_param_annotation_set(j, std::move(aset));
redex_assert(method->get_param_anno());
}
}
}
}
}
void DexClass::combine_annotations_with(DexClass* other) {
auto other_anno_set = other->get_anno_set();
if (other_anno_set != nullptr) {
if (m_anno == nullptr) {
m_anno = std::make_unique<DexAnnotationSet>(*other->m_anno);
} else {
m_anno->combine_with(*other->m_anno);
}
}
}
void DexClass::attach_annotation_set(std::unique_ptr<DexAnnotationSet> anno) {
m_anno = std::move(anno);
}
void DexClass::clear_annotations() { m_anno.reset(); }
static std::unique_ptr<DexEncodedValueArray> load_static_values(
DexIdx* idx, uint32_t sv_off) {
if (sv_off == 0) return nullptr;
const uint8_t* encd = idx->get_uleb_data(sv_off);
return get_encoded_value_array(idx, encd);
}
std::unique_ptr<DexEncodedValueArray> DexClass::get_static_values() {
std::deque<std::unique_ptr<DexEncodedValue>> deque;
for (auto it = m_sfields.rbegin(); it != m_sfields.rend(); ++it) {
auto const& f = *it;
DexEncodedValue* ev = f->get_static_value();
if (!ev->is_zero() || !deque.empty()) {
deque.push_front(ev->clone());
}
}
if (deque.empty()) {
return nullptr;
}
auto aev = std::make_unique<std::vector<std::unique_ptr<DexEncodedValue>>>();
aev->reserve(deque.size());
for (auto& d : deque) {
aev->emplace_back(std::move(d));
}
return std::make_unique<DexEncodedValueArray>(aev.release(), true);
}
DexAnnotationDirectory* DexClass::get_annotation_directory() {
/* First scan to see what types of annotations to scan for if any.
*/
std::unique_ptr<DexFieldAnnotations> fanno = nullptr;
std::unique_ptr<DexMethodAnnotations> manno = nullptr;
std::unique_ptr<DexMethodParamAnnotations> mpanno = nullptr;
for (auto const& f : m_sfields) {
if (f->get_anno_set()) {
if (fanno == nullptr) {
fanno = std::make_unique<DexFieldAnnotations>();
}
fanno->push_back(std::make_pair(f, f->get_anno_set()));
}
}
for (auto const& f : m_ifields) {
if (f->get_anno_set()) {
if (fanno == nullptr) {
fanno = std::make_unique<DexFieldAnnotations>();
}
fanno->push_back(std::make_pair(f, f->get_anno_set()));
}
}
for (auto const& m : m_dmethods) {
if (m->get_anno_set()) {
if (manno == nullptr) {
manno = std::make_unique<DexMethodAnnotations>();
}
manno->push_back(std::make_pair(m, m->get_anno_set()));
}
if (m->get_param_anno()) {
if (mpanno == nullptr) {
mpanno = std::make_unique<DexMethodParamAnnotations>();
}
mpanno->push_back(std::make_pair(m, m->get_param_anno()));
}
}
for (auto const& m : m_vmethods) {
if (m->get_anno_set()) {
if (manno == nullptr) {
manno = std::make_unique<DexMethodAnnotations>();
}
manno->push_back(std::make_pair(m, m->get_anno_set()));
}
if (m->get_param_anno()) {
if (mpanno == nullptr) {
mpanno = std::make_unique<DexMethodParamAnnotations>();
}
mpanno->push_back(std::make_pair(m, m->get_param_anno()));
}
}
if (m_anno || fanno || manno || mpanno) {
return new DexAnnotationDirectory(m_anno.get(), std::move(fanno),
std::move(manno), std::move(mpanno));
}
return nullptr;
}
DexClass* DexClass::create(DexIdx* idx,
const dex_class_def* cdef,
const DexLocation* location) {
DexClass* cls = new DexClass(idx, cdef, location);
if (g_redex->class_already_loaded(cls)) {
// FIXME: This isn't deterministic. We're keeping whichever class we loaded
// first, which may not always be from the same dex (if we load them in
// parallel, for example).
delete cls;
return nullptr;
}
cls->load_class_annotations(idx, cdef->annotations_off);
auto deva = std::unique_ptr<DexEncodedValueArray>(
load_static_values(idx, cdef->static_values_off));
cls->load_class_data_item(idx, cdef->class_data_offset, std::move(deva));
g_redex->publish_class(cls);
return cls;
}
DexClass::DexClass(const DexLocation* location) : m_location(location) {}
DexClass::DexClass(DexIdx* idx,
const dex_class_def* cdef,
const DexLocation* location)
: m_super_class(idx->get_typeidx(cdef->super_idx)),
m_self(idx->get_typeidx(cdef->typeidx)),
m_interfaces(idx->get_type_list(cdef->interfaces_off)),
m_source_file(idx->get_nullable_stringidx(cdef->source_file_idx)),
m_anno(nullptr),
m_location(location),
m_access_flags((DexAccessFlags)cdef->access_flags),
m_external(false),
m_perf_sensitive(false) {}
DexClass::~DexClass() = default; // For forwarding.
template <typename C>
void DexTypeList::gather_types(C& ltype) const {
c_append_all(ltype, m_list.begin(), m_list.end());
}
INSTANTIATE(DexTypeList::gather_types, DexType*)
static const DexString* make_shorty(const DexType* rtype,
const DexTypeList* args) {
std::string s;
s.push_back(type::type_shorty(rtype));
if (args != nullptr) {
for (auto arg : *args) {
s.push_back(type::type_shorty(arg));
}
}
return DexString::make_string(s);
}
DexProto* DexProto::make_proto(const DexType* rtype,
const DexTypeList* args,
const DexString* shorty) {
return g_redex->make_proto(rtype, args, shorty);
}
DexProto* DexProto::make_proto(const DexType* rtype, const DexTypeList* args) {
auto shorty = make_shorty(rtype, args);
return DexProto::make_proto(rtype, args, shorty);
}
DexProto* DexProto::get_proto(const DexType* rtype, const DexTypeList* args) {
return g_redex->get_proto(rtype, args);
}
template <typename C>
void DexProto::gather_types(C& ltype) const {
if (m_args) {
m_args->gather_types(ltype);
}
if (m_rtype) {
c_append(ltype, m_rtype);
}
}
INSTANTIATE(DexProto::gather_types, DexType*)
void DexProto::gather_strings(std::vector<const DexString*>& lstring) const {
if (m_shorty) {
c_append(lstring, m_shorty);
}
}
void DexProto::gather_strings(
std::unordered_set<const DexString*>& lstring) const {
if (m_shorty) {
c_append(lstring, m_shorty);
}
}
namespace {
template <typename C>
void maybe_sort_unique(C&) {}
template <>
void maybe_sort_unique(std::vector<DexType*>& vec) {
sort_unique(vec);
}
} // namespace
template <typename C>
void DexClass::gather_types(C& ltype) const {
for (auto const& m : m_dmethods) {
m->gather_types(ltype);
}
for (auto const& m : m_vmethods) {
m->gather_types(ltype);
}
for (auto const& f : m_sfields) {
f->gather_types(ltype);
}
for (auto const& f : m_ifields) {
f->gather_types(ltype);
}
ltype.insert(ltype.end(), m_super_class);
ltype.insert(ltype.end(), m_self);
if (m_interfaces) m_interfaces->gather_types(ltype);
if (m_anno) {
std::vector<DexType*> type_vec;
m_anno->gather_types(type_vec);
c_append_all(ltype, type_vec.begin(), type_vec.end());
}
// We also need to gather types needed for field and method refs.
std::vector<DexFieldRef*> lfield;
gather_fields(lfield);
for (auto const& f : lfield) {
f->gather_types_shallow(ltype);
}
std::vector<DexMethodRef*> lmethod;
gather_methods(lmethod);
for (auto const& m : lmethod) {
m->gather_types_shallow(ltype);
}
// Remove duplicates.
maybe_sort_unique(ltype);
}
INSTANTIATE(DexClass::gather_types, DexType*)
void DexClass::gather_load_types(std::unordered_set<DexType*>& ltype) const {
if (is_external()) {
return;
}
if (ltype.count(m_self) != 0) {
return;
}
ltype.emplace(m_self);
{
auto superclass = type_class_internal(m_super_class);
if (superclass != nullptr) {
superclass->gather_load_types(ltype);
}
}
if (m_interfaces) {
for (auto* itype : *m_interfaces) {
auto iclass = type_class_internal(itype);
if (iclass != nullptr) {
iclass->gather_load_types(ltype);
}
}
}
}
void DexClass::gather_init_classes(std::vector<DexType*>& ltype) const {
for (auto const& m : m_dmethods) {
m->gather_init_classes(ltype);
}
for (auto const& m : m_vmethods) {
m->gather_init_classes(ltype);
}
}
template <typename C>
void DexClass::gather_strings_internal(C& lstring, bool exclude_loads) const {
for (auto const& m : m_dmethods) {
m->gather_strings(lstring, exclude_loads);
}
for (auto const& m : m_vmethods) {
m->gather_strings(lstring, exclude_loads);
}
for (auto const& f : m_sfields) {
f->gather_strings(lstring);
}
for (auto const& f : m_ifields) {
f->gather_strings(lstring);
}
if (m_source_file) c_append(lstring, m_source_file);
if (m_anno) {
std::vector<const DexString*> strings;
m_anno->gather_strings(strings);
c_append_all(lstring, strings.begin(), strings.end());
}
}
void DexClass::gather_strings(std::vector<const DexString*>& lstring,
bool exclude_loads) const {
gather_strings_internal(lstring, exclude_loads);
}
void DexClass::gather_strings(std::unordered_set<const DexString*>& lstring,
bool exclude_loads) const {
gather_strings_internal(lstring, exclude_loads);
}
template <typename C>
void DexClass::gather_fields(C& lfield) const {
for (auto const& m : m_dmethods) {
m->gather_fields(lfield);
}
for (auto const& m : m_vmethods) {
m->gather_fields(lfield);
}
for (auto const& f : m_sfields) {
lfield.insert(lfield.end(), f);
f->gather_fields(lfield);
}
for (auto const& f : m_ifields) {
lfield.insert(lfield.end(), f);
f->gather_fields(lfield);
}
if (m_anno) {
std::vector<DexFieldRef*> fields_vec; // Simplify refactor.
m_anno->gather_fields(fields_vec);
c_append_all(lfield, fields_vec.begin(), fields_vec.end());
}
}
INSTANTIATE(DexClass::gather_fields, DexFieldRef*)
template <typename C>
void DexClass::gather_methods(C& lmethod) const {
for (auto const& m : m_dmethods) {
lmethod.insert(lmethod.end(), m);
m->gather_methods(lmethod);
}
for (auto const& m : m_vmethods) {
lmethod.insert(lmethod.end(), m);
m->gather_methods(lmethod);
}
for (auto const& f : m_sfields) {
f->gather_methods(lmethod);
}
for (auto const& f : m_ifields) {
f->gather_methods(lmethod);
}
if (m_anno) {
std::vector<DexMethodRef*> method_vec; // Simplify refactor.
m_anno->gather_methods(method_vec);
c_append_all(lmethod, method_vec.begin(), method_vec.end());
}
}
INSTANTIATE(DexClass::gather_methods, DexMethodRef*)
const DexField* DexFieldRef::as_def() const {
if (is_def()) {
return static_cast<const DexField*>(this);
} else {
return nullptr;
}
}
DexField* DexFieldRef::as_def() {
if (is_def()) {
return static_cast<DexField*>(this);
} else {
return nullptr;
}
}
// Find methods and fields from a class using its obfuscated name.
DexField* DexClass::find_field_from_simple_deobfuscated_name(
const std::string& field_name) {
for (DexField* f : get_sfields()) {
if (f->get_simple_deobfuscated_name() == field_name) {
return f;
}
}
for (DexField* f : get_ifields()) {
if (f->get_simple_deobfuscated_name() == field_name) {
return f;
}
}
return nullptr;
}
DexMethod* DexClass::find_method_from_simple_deobfuscated_name(
const std::string& method_name) {
for (DexMethod* m : get_dmethods()) {
if (m->get_simple_deobfuscated_name() == method_name) {
return m;
}
}
for (DexMethod* m : get_vmethods()) {
if (m->get_simple_deobfuscated_name() == method_name) {
return m;
}
}
return nullptr;
}
template <typename C>
void DexClass::gather_callsites(C& lcallsite) const {
for (auto const& m : m_dmethods) {
m->gather_callsites(lcallsite);
}
for (auto const& m : m_vmethods) {
m->gather_callsites(lcallsite);
}
}
INSTANTIATE(DexClass::gather_callsites, DexCallSite*)
template <typename C>
void DexClass::gather_methodhandles(C& lmethodhandle) const {
for (auto const& m : m_dmethods) {
m->gather_methodhandles(lmethodhandle);
}
for (auto const& m : m_vmethods) {
m->gather_methodhandles(lmethodhandle);
}
}
INSTANTIATE(DexClass::gather_methodhandles, DexMethodHandle*)
template <typename C>
void DexFieldRef::gather_types_shallow(C& ltype) const {
ltype.insert(ltype.end(), m_spec.cls);
ltype.insert(ltype.end(), m_spec.type);
}
INSTANTIATE(DexFieldRef::gather_types_shallow, DexType*)
void DexFieldRef::gather_strings_shallow(
std::vector<const DexString*>& lstring) const {
c_append(lstring, m_spec.name);
}
void DexFieldRef::gather_strings_shallow(
std::unordered_set<const DexString*>& lstring) const {
c_append(lstring, m_spec.name);
}
template <typename C>
void DexField::gather_types(C& ltype) const {
std::vector<DexType*> type_vec;
if (m_value) m_value->gather_types(type_vec);
if (m_anno) m_anno->gather_types(type_vec);
c_append_all(ltype, type_vec.begin(), type_vec.end());
}
INSTANTIATE(DexField::gather_types, DexType*)
template <typename C>
void DexField::gather_strings_internal(C& lstring) const {
std::vector<const DexString*> string_vec;
if (m_value) m_value->gather_strings(string_vec);
if (m_anno) m_anno->gather_strings(string_vec);
c_append_all(lstring, string_vec.begin(), string_vec.end());
}
void DexField::gather_strings(std::vector<const DexString*>& lstring) const {
gather_strings_internal(lstring);
}
void DexField::gather_strings(
std::unordered_set<const DexString*>& lstring) const {
gather_strings_internal(lstring);
}
template <typename C>
void DexField::gather_fields(C& lfield) const {
std::vector<DexFieldRef*> field_vec;
if (m_value) m_value->gather_fields(field_vec);
if (m_anno) m_anno->gather_fields(field_vec);
c_append_all(lfield, field_vec.begin(), field_vec.end());
}
INSTANTIATE(DexField::gather_fields, DexFieldRef*)
template <typename C>
void DexField::gather_methods(C& lmethod) const {
std::vector<DexMethodRef*> method_vec;
if (m_value) m_value->gather_methods(method_vec);
if (m_anno) m_anno->gather_methods(method_vec);
c_append_all(lmethod, method_vec.begin(), method_vec.end());
}
INSTANTIATE(DexField::gather_methods, DexMethodRef*)
std::string DexField::get_simple_deobfuscated_name() const {
return get_simple_deobf_name(this);
}
void DexMethod::set_external() {
always_assert_log(!m_concrete, "Unexpected concrete method %s\n",
self_show().c_str());
m_deobfuscated_name = DexString::make_string(self_show());
m_external = true;
}
template <typename C>
void DexMethod::gather_types(C& ltype) const {
gather_types_shallow(ltype); // Handle DexMethodRef parts.
std::vector<DexType*> type_vec; // Simplify refactor.
if (m_code) m_code->gather_types(type_vec);
if (m_anno) m_anno->gather_types(type_vec);
auto param_anno = get_param_anno();
if (param_anno) {
for (auto& pair : *param_anno) {
auto& anno_set = pair.second;
anno_set->gather_types(type_vec);
}
}
c_append_all(ltype, type_vec.begin(), type_vec.end());
}
INSTANTIATE(DexMethod::gather_types, DexType*)
void DexMethod::gather_init_classes(std::vector<DexType*>& ltype) const {
if (m_code) m_code->gather_init_classes(ltype);
}
template <typename C>
void DexMethod::gather_callsites(C& lcallsite) const {
// We handle m_spec.cls and proto in the first-layer gather.
if (m_code) {
std::vector<DexCallSite*> callsite_vec; // Simplify refactor.
m_code->gather_callsites(callsite_vec);
c_append_all(lcallsite, callsite_vec.begin(), callsite_vec.end());
}
}
INSTANTIATE(DexMethod::gather_callsites, DexCallSite*)
template <typename C>
void DexMethod::gather_methodhandles(C& lmethodhandle) const {
// We handle m_spec.cls and proto in the first-layer gather.
std::vector<DexMethodHandle*> mhandles_vec; // Simplify refactor.
if (m_code) m_code->gather_methodhandles(mhandles_vec);
c_append_all(lmethodhandle, mhandles_vec.begin(), mhandles_vec.end());
}
INSTANTIATE(DexMethod::gather_methodhandles, DexMethodHandle*)
template <typename C>
void DexMethod::gather_strings_internal(C& lstring, bool exclude_loads) const {
// We handle m_name and proto in the first-layer gather.
std::vector<const DexString*> strings_vec; // Simplify refactor.
if (m_code && !exclude_loads) m_code->gather_strings(strings_vec);
if (m_anno) m_anno->gather_strings(strings_vec);
auto param_anno = get_param_anno();
if (param_anno) {
for (auto& pair : *param_anno) {
auto& anno_set = pair.second;
anno_set->gather_strings(strings_vec);
}
}
c_append_all(lstring, strings_vec.begin(), strings_vec.end());
}
void DexMethod::gather_strings(std::vector<const DexString*>& lstring,
bool exclude_loads) const {
gather_strings_internal(lstring, exclude_loads);
}
void DexMethod::gather_strings(std::unordered_set<const DexString*>& lstring,
bool exclude_loads) const {
gather_strings_internal(lstring, exclude_loads);
}
template <typename C>
void DexMethod::gather_fields(C& lfield) const {
std::vector<DexFieldRef*> fields_vec; // Simplify refactor.
if (m_code) m_code->gather_fields(fields_vec);
if (m_anno) m_anno->gather_fields(fields_vec);
auto param_anno = get_param_anno();
if (param_anno) {
for (auto& pair : *param_anno) {
auto& anno_set = pair.second;
anno_set->gather_fields(fields_vec);
}
}
c_append_all(lfield, fields_vec.begin(), fields_vec.end());
}
INSTANTIATE(DexMethod::gather_fields, DexFieldRef*)
template <typename C>
void DexMethod::gather_methods(C& lmethod) const {
if (m_code) {
std::vector<DexMethodRef*> method_vec; // Simplify refactor.
m_code->gather_methods(method_vec);
c_append_all(lmethod, method_vec.begin(), method_vec.end());
}
gather_methods_from_annos(lmethod);
}
INSTANTIATE(DexMethod::gather_methods, DexMethodRef*)
template <typename C>
void DexMethod::gather_methods_from_annos(C& lmethod) const {
std::vector<DexMethodRef*> method_vec; // Simplify refactor.
if (m_anno) m_anno->gather_methods(method_vec);
auto param_anno = get_param_anno();
if (param_anno) {
for (auto& pair : *param_anno) {
auto& anno_set = pair.second;
anno_set->gather_methods(method_vec);
}
}
c_append_all(lmethod, method_vec.begin(), method_vec.end());
}
INSTANTIATE(DexMethod::gather_methods_from_annos, DexMethodRef*)
const DexMethod* DexMethodRef::as_def() const {
if (is_def()) {
return static_cast<const DexMethod*>(this);
} else {
return nullptr;
}
}
DexMethod* DexMethodRef::as_def() {
if (is_def()) {
return static_cast<DexMethod*>(this);
} else {
return nullptr;
}
}
template <typename C>
void DexMethodRef::gather_types_shallow(C& ltype) const {
ltype.insert(ltype.end(), m_spec.cls);
m_spec.proto->gather_types(ltype);
}
INSTANTIATE(DexMethodRef::gather_types_shallow, DexType*)
void DexMethodRef::gather_strings_shallow(
std::vector<const DexString*>& lstring) const {
lstring.insert(lstring.end(), m_spec.name);
m_spec.proto->gather_strings(lstring);
}
void DexMethodRef::gather_strings_shallow(
std::unordered_set<const DexString*>& lstring) const {
lstring.insert(lstring.end(), m_spec.name);
m_spec.proto->gather_strings(lstring);
}
uint32_t DexCode::size() const {
uint32_t size = 0;
for (auto const& opc : get_instructions()) {
if (!dex_opcode::is_fopcode(opc->opcode())) {
size += opc->size();
}
}
return size;
}
DexType* DexType::make_type(const DexString* dstring) {
return g_redex->make_type(dstring);
}
DexType* DexType::get_type(const DexString* dstring) {
return g_redex->get_type(dstring);
}
void DexType::set_name(const DexString* new_name) {
g_redex->set_type_name(this, new_name);
}
DexProto* DexType::get_non_overlapping_proto(const DexString* method_name,
DexProto* orig_proto) {
auto methodref_in_context =
DexMethod::get_method(this, method_name, orig_proto);
if (!methodref_in_context) {
return orig_proto;
}
DexTypeList::ContainerType new_arg_list;
auto rtype = orig_proto->get_rtype();
for (auto t : *orig_proto->get_args()) {
new_arg_list.push_back(t);
}
new_arg_list.push_back(type::_int());
DexTypeList* new_args =
DexTypeList::make_type_list(DexTypeList::ContainerType{new_arg_list});
DexProto* new_proto = DexProto::make_proto(rtype, new_args);
methodref_in_context = DexMethod::get_method(this, method_name, new_proto);
while (methodref_in_context) {
new_arg_list.push_back(type::_int());
new_args =
DexTypeList::make_type_list(DexTypeList::ContainerType{new_arg_list});
new_proto = DexProto::make_proto(rtype, new_args);
methodref_in_context = DexMethod::get_method(this, method_name, new_proto);
}
return new_proto;
}
void DexMethod::add_load_params(size_t num_add_loads) {
IRCode* code = this->get_code();
always_assert_log(code, "Method don't have IRCode\n");
auto callee_params = code->get_param_instructions();
size_t added_params = 0;
while (added_params < num_add_loads) {
++added_params;
auto temp = code->allocate_temp();
IRInstruction* new_param_load = new IRInstruction(IOPCODE_LOAD_PARAM);
new_param_load->set_dest(temp);
code->insert_before(callee_params.end(), new_param_load);
}
}
void gather_components(std::vector<const DexString*>& lstring,
std::vector<DexType*>& ltype,
std::vector<DexFieldRef*>& lfield,
std::vector<DexMethodRef*>& lmethod,
std::vector<DexCallSite*>& lcallsite,
std::vector<DexMethodHandle*>& lmethodhandle,
const DexClasses& classes,
bool exclude_loads) {
// Gather references reachable from each class.
std::unordered_set<const DexString*> strings;
std::unordered_set<DexType*> types;
std::unordered_set<DexFieldRef*> fields;
std::unordered_set<DexMethodRef*> methods;
std::unordered_set<DexCallSite*> callsites;
std::unordered_set<DexMethodHandle*> methodhandles;
// Inside a lambda to ensure only visibility of the sets.
[&classes, &exclude_loads, &strings, &types, &fields, &methods, &callsites,
&methodhandles]() {
for (auto const& cls : classes) {
cls->gather_strings(strings, exclude_loads);
cls->gather_types(types);
cls->gather_fields(fields);
cls->gather_methods(methods);
cls->gather_callsites(callsites);
cls->gather_methodhandles(methodhandles);
}
// Gather types and strings needed for field and method refs.
for (auto meth : methods) {
meth->gather_types_shallow(types);
meth->gather_strings_shallow(strings);
}
for (auto field : fields) {
field->gather_types_shallow(types);
field->gather_strings_shallow(strings);
}
// Gather strings needed for each type.
for (auto type : types) {
if (type) strings.insert(type->get_name());
}
}();
lstring.insert(lstring.end(), strings.begin(), strings.end());
ltype.insert(ltype.end(), types.begin(), types.end());
lfield.insert(lfield.end(), fields.begin(), fields.end());
lmethod.insert(lmethod.end(), methods.begin(), methods.end());
lcallsite.insert(lcallsite.end(), callsites.begin(), callsites.end());
lmethodhandle.insert(lmethodhandle.end(), methodhandles.begin(),
methodhandles.end());
// This retains pre-set computation behavior.
sort_unique(lstring);
sort_unique(ltype);
sort_unique(lfield);
sort_unique(lmethod);
sort_unique(lcallsite);
sort_unique(lmethodhandle);
}
std::string DexField::self_show() const { return show(this); }
std::string DexMethod::self_show() const { return show(this); }
std::string DexClass::self_show() const { return show(m_self); }
void DexMethodRef::erase_method(DexMethodRef* mref) {
g_redex->erase_method(mref);
if (mref->is_def()) {
auto m = mref->as_def();
if (m->get_name() != m->get_deobfuscated_name_or_null()) {
g_redex->erase_method(m->get_class(), m->get_deobfuscated_name_or_null(),
m->get_proto());
}
}
}
DexClass* type_class(const DexType* t) { return g_redex->type_class(t); }