libredex/Show.cpp (1,568 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 "Show.h"
#include <iomanip>
#include <boost/version.hpp>
// Quoted was accepted into public components as of 1.73. The `detail`
// header was removed in 1.74.
#if BOOST_VERSION < 107400
#include <boost/io/detail/quoted_manip.hpp>
#else
#include <boost/io/quoted.hpp>
#endif
#include "ControlFlow.h"
#include "CppUtil.h"
#include "Creators.h"
#include "DexAnnotation.h"
#include "DexCallSite.h"
#include "DexClass.h"
#include "DexDebugInstruction.h"
#include "DexIdx.h"
#include "DexInstruction.h"
#include "DexMethodHandle.h"
#include "DexPosition.h"
#include "DexUtil.h"
#include "IRCode.h"
#include "IROpcode.h"
#include "ShowCFG.h"
#include "StringBuilder.h"
namespace {
std::string humanize(std::string const& type) {
if (type.compare("B") == 0) {
return "byte";
} else if (type.compare("C") == 0) {
return "char";
} else if (type.compare("D") == 0) {
return "double";
} else if (type.compare("F") == 0) {
return "float";
} else if (type.compare("I") == 0) {
return "int";
} else if (type.compare("J") == 0) {
return "long";
} else if (type.compare("S") == 0) {
return "short";
} else if (type.compare("V") == 0) {
return "void";
} else if (type.compare("Z") == 0) {
return "boolean";
} else if (type[0] == '[') {
std::ostringstream ss;
ss << humanize(type.substr(1)) << "[]";
return ss.str();
} else if (type[0] == 'L') {
return java_names::internal_to_external(type);
}
return "unknown";
}
// TODO: make sure names reported handles collisions correctly.
// i.e. ACC_VOLATILE and ACC_BRIDGE etc.
std::string accessibility(uint32_t acc, bool method = false) {
std::ostringstream ss;
if (acc & DexAccessFlags::ACC_PUBLIC) {
ss << "public ";
}
if (acc & DexAccessFlags::ACC_PRIVATE) {
ss << "private ";
}
if (acc & DexAccessFlags::ACC_PROTECTED) {
ss << "protected ";
}
if (acc & DexAccessFlags::ACC_STATIC) {
ss << "static ";
}
if (acc & DexAccessFlags::ACC_FINAL) {
ss << "final ";
}
if (acc & DexAccessFlags::ACC_INTERFACE) {
ss << "interface ";
} else if (acc & DexAccessFlags::ACC_ABSTRACT) {
ss << "abstract ";
}
if (acc & DexAccessFlags::ACC_ENUM) {
ss << "enum ";
}
if (acc & DexAccessFlags::ACC_SYNCHRONIZED) {
ss << "synchronized ";
}
if (acc & DexAccessFlags::ACC_VOLATILE) {
if (method)
ss << "bridge ";
else
ss << "volatile ";
}
if (acc & DexAccessFlags::ACC_NATIVE) {
ss << "native ";
}
if (acc & DexAccessFlags::ACC_TRANSIENT) {
if (method)
ss << "vararg ";
else
ss << "transient ";
}
return ss.str();
}
std::string show(DexAnnotationVisibility vis) {
switch (vis) {
case DAV_BUILD:
return "build";
case DAV_RUNTIME:
return "runtime";
case DAV_SYSTEM:
return "system";
}
}
std::string show_type(const DexType* t, bool deobfuscated) {
return self_recursive_fn(
[&](auto self, const DexType* t) -> std::string {
if (t == nullptr) {
return std::string("");
}
auto name = t->get_name()->str();
if (!deobfuscated) {
return name;
}
if (name[0] == 'L') {
auto cls = type_class(t);
if (cls != nullptr &&
!cls->get_deobfuscated_name_or_empty().empty()) {
return cls->get_deobfuscated_name_or_empty();
}
return name;
} else if (name[0] == '[') {
std::ostringstream ss;
ss << '[' << self(self, DexType::get_type(name.substr(1)));
return ss.str();
}
return name;
},
t);
}
std::string show_field(const DexFieldRef* ref, bool deobfuscated) {
if (ref == nullptr) {
return "";
}
if (deobfuscated && ref->is_def()) {
const auto& name = ref->as_def()->get_deobfuscated_name_or_empty();
if (!name.empty()) {
return name;
}
}
string_builders::StaticStringBuilder<5> b;
b << show_type(ref->get_class(), deobfuscated) << "." << show(ref->get_name())
<< ":" << show_type(ref->get_type(), deobfuscated);
return b.str();
}
std::string show_type_list(const DexTypeList* l, bool deobfuscated) {
if (l == nullptr) {
return "";
}
string_builders::DynamicStringBuilder b(l->size());
for (const auto& type : *l) {
b << show_type(type, deobfuscated);
}
return b.str();
}
std::string show_proto(const DexProto* p, bool deobfuscated) {
if (p == nullptr) {
return "";
}
string_builders::StaticStringBuilder<4> b;
b << "(" << show_type_list(p->get_args(), deobfuscated) << ")"
<< show_type(p->get_rtype(), deobfuscated);
return b.str();
}
std::string show_method(const DexMethodRef* ref, bool deobfuscated) {
if (ref == nullptr) {
return "";
}
if (deobfuscated && ref->is_def()) {
const auto& name = ref->as_def()->get_deobfuscated_name_or_empty();
if (!name.empty()) {
return name;
}
}
string_builders::StaticStringBuilder<5> b;
b << show_type(ref->get_class(), deobfuscated) << "." << show(ref->get_name())
<< ":" << show_proto(ref->get_proto(), deobfuscated);
return b.str();
}
std::string show_opcode(const DexInstruction* insn, bool deobfuscated = false) {
if (!insn) return "";
std::ostringstream ss;
auto opcode = insn->opcode();
switch (opcode) {
case DOPCODE_NOP:
return "nop";
case DOPCODE_MOVE:
return "move";
case DOPCODE_MOVE_WIDE:
return "move-wide";
case DOPCODE_MOVE_OBJECT:
return "move-object";
case DOPCODE_MOVE_RESULT:
return "move-result";
case DOPCODE_MOVE_RESULT_WIDE:
return "move-result-wide";
case DOPCODE_MOVE_RESULT_OBJECT:
return "move-result-object";
case DOPCODE_MOVE_EXCEPTION:
return "move-exception";
case DOPCODE_RETURN_VOID:
return "return-void";
case DOPCODE_RETURN:
return "return";
case DOPCODE_RETURN_WIDE:
return "return-wide";
case DOPCODE_RETURN_OBJECT:
return "return-object";
case DOPCODE_CONST_4:
return "const/4";
case DOPCODE_MONITOR_ENTER:
return "monitor-enter";
case DOPCODE_MONITOR_EXIT:
return "monitor-exit";
case DOPCODE_THROW:
return "throw";
case DOPCODE_GOTO:
return "goto";
case DOPCODE_NEG_INT:
return "neg-int";
case DOPCODE_NOT_INT:
return "not-int";
case DOPCODE_NEG_LONG:
return "neg-long";
case DOPCODE_NOT_LONG:
return "not-long";
case DOPCODE_NEG_FLOAT:
return "neg-float";
case DOPCODE_NEG_DOUBLE:
return "neg-double";
case DOPCODE_INT_TO_LONG:
return "int-to-long";
case DOPCODE_INT_TO_FLOAT:
return "int-to-float";
case DOPCODE_INT_TO_DOUBLE:
return "int-to-double";
case DOPCODE_LONG_TO_INT:
return "long-to-int";
case DOPCODE_LONG_TO_FLOAT:
return "long-to-float";
case DOPCODE_LONG_TO_DOUBLE:
return "long-to-double";
case DOPCODE_FLOAT_TO_INT:
return "float-to-int";
case DOPCODE_FLOAT_TO_LONG:
return "float-to-long";
case DOPCODE_FLOAT_TO_DOUBLE:
return "float-to-double";
case DOPCODE_DOUBLE_TO_INT:
return "double-to-int";
case DOPCODE_DOUBLE_TO_LONG:
return "double-to-long";
case DOPCODE_DOUBLE_TO_FLOAT:
return "double-to-float";
case DOPCODE_INT_TO_BYTE:
return "int-to-byte";
case DOPCODE_INT_TO_CHAR:
return "int-to-char";
case DOPCODE_INT_TO_SHORT:
return "int-to-short";
case DOPCODE_ARRAY_LENGTH:
return "array-length";
case DOPCODE_MOVE_FROM16:
return "move/from16";
case DOPCODE_MOVE_WIDE_FROM16:
return "move-wide/from16";
case DOPCODE_MOVE_OBJECT_FROM16:
return "move-object/from16";
case DOPCODE_CONST_16:
return "const/16";
case DOPCODE_CONST_HIGH16:
return "const/high16";
case DOPCODE_CONST_WIDE_16:
return "const-wide/16";
case DOPCODE_CONST_WIDE_HIGH16:
return "const-wide/high16";
case DOPCODE_GOTO_16:
return "goto/16";
case DOPCODE_CMPL_FLOAT:
return "cmpl-float";
case DOPCODE_CMPG_FLOAT:
return "cmpg-float";
case DOPCODE_CMPL_DOUBLE:
return "cmpl-double";
case DOPCODE_CMPG_DOUBLE:
return "cmpg-double";
case DOPCODE_CMP_LONG:
return "cmp-long";
case DOPCODE_IF_EQ:
return "if-eq";
case DOPCODE_IF_NE:
return "if-ne";
case DOPCODE_IF_LT:
return "if-lt";
case DOPCODE_IF_GE:
return "if-ge";
case DOPCODE_IF_GT:
return "if-gt";
case DOPCODE_IF_LE:
return "if-le";
case DOPCODE_IF_EQZ:
return "if-eqz";
case DOPCODE_IF_NEZ:
return "if-nez";
case DOPCODE_IF_LTZ:
return "if-ltz";
case DOPCODE_IF_GEZ:
return "if-gez";
case DOPCODE_IF_GTZ:
return "if-gtz";
case DOPCODE_IF_LEZ:
return "if-lez";
case DOPCODE_AGET:
return "aget";
case DOPCODE_AGET_WIDE:
return "aget-wide";
case DOPCODE_AGET_OBJECT:
return "aget-object";
case DOPCODE_AGET_BOOLEAN:
return "aget-boolean";
case DOPCODE_AGET_BYTE:
return "aget-byte";
case DOPCODE_AGET_CHAR:
return "aget-char";
case DOPCODE_AGET_SHORT:
return "aget-short";
case DOPCODE_APUT:
return "aput";
case DOPCODE_APUT_WIDE:
return "aput-wide";
case DOPCODE_APUT_OBJECT:
return "aput-object";
case DOPCODE_APUT_BOOLEAN:
return "aput-boolean";
case DOPCODE_APUT_BYTE:
return "aput-byte";
case DOPCODE_APUT_CHAR:
return "aput-char";
case DOPCODE_APUT_SHORT:
return "aput-short";
case DOPCODE_ADD_INT:
return "add-int";
case DOPCODE_SUB_INT:
return "sub-int";
case DOPCODE_MUL_INT:
return "mul-int";
case DOPCODE_DIV_INT:
return "div-int";
case DOPCODE_REM_INT:
return "rem-int";
case DOPCODE_AND_INT:
return "and-int";
case DOPCODE_OR_INT:
return "or-int";
case DOPCODE_XOR_INT:
return "xor-int";
case DOPCODE_SHL_INT:
return "shl-int";
case DOPCODE_SHR_INT:
return "shr-int";
case DOPCODE_USHR_INT:
return "ushr-int";
case DOPCODE_ADD_LONG:
return "add-long";
case DOPCODE_SUB_LONG:
return "sub-long";
case DOPCODE_MUL_LONG:
return "mul-long";
case DOPCODE_DIV_LONG:
return "div-long";
case DOPCODE_REM_LONG:
return "rem-long";
case DOPCODE_AND_LONG:
return "and-long";
case DOPCODE_OR_LONG:
return "or-long";
case DOPCODE_XOR_LONG:
return "xor-long";
case DOPCODE_SHL_LONG:
return "shl-long";
case DOPCODE_SHR_LONG:
return "shr-long";
case DOPCODE_USHR_LONG:
return "ushr-long";
case DOPCODE_ADD_FLOAT:
return "add-float";
case DOPCODE_SUB_FLOAT:
return "sub-float";
case DOPCODE_MUL_FLOAT:
return "mul-float";
case DOPCODE_DIV_FLOAT:
return "div-float";
case DOPCODE_REM_FLOAT:
return "rem-float";
case DOPCODE_ADD_DOUBLE:
return "add-double";
case DOPCODE_SUB_DOUBLE:
return "sub-double";
case DOPCODE_MUL_DOUBLE:
return "mul-double";
case DOPCODE_DIV_DOUBLE:
return "div-double";
case DOPCODE_REM_DOUBLE:
return "rem-double";
case DOPCODE_ADD_INT_LIT16:
return "add-int/lit16";
case DOPCODE_RSUB_INT:
return "rsub-int";
case DOPCODE_MUL_INT_LIT16:
return "mul-int/lit16";
case DOPCODE_DIV_INT_LIT16:
return "div-int/lit16";
case DOPCODE_REM_INT_LIT16:
return "rem-int/lit16";
case DOPCODE_AND_INT_LIT16:
return "and-int/lit16";
case DOPCODE_OR_INT_LIT16:
return "or-int/lit16";
case DOPCODE_XOR_INT_LIT16:
return "xor-int/lit16";
case DOPCODE_ADD_INT_LIT8:
return "add-int/lit8";
case DOPCODE_RSUB_INT_LIT8:
return "rsub-int/lit8";
case DOPCODE_MUL_INT_LIT8:
return "mul-int/lit8";
case DOPCODE_DIV_INT_LIT8:
return "div-int/lit8";
case DOPCODE_REM_INT_LIT8:
return "rem-int/lit8";
case DOPCODE_AND_INT_LIT8:
return "and-int/lit8";
case DOPCODE_OR_INT_LIT8:
return "or-int/lit8";
case DOPCODE_XOR_INT_LIT8:
return "xor-int/lit8";
case DOPCODE_SHL_INT_LIT8:
return "shl-int/lit8";
case DOPCODE_SHR_INT_LIT8:
return "shr-int/lit8";
case DOPCODE_USHR_INT_LIT8:
return "ushr-int/lit8";
case DOPCODE_MOVE_16:
return "move/16";
case DOPCODE_MOVE_WIDE_16:
return "move-wide/16";
case DOPCODE_MOVE_OBJECT_16:
return "move-object/16";
case DOPCODE_CONST:
return "const";
case DOPCODE_CONST_WIDE_32:
return "const-wide/32";
case DOPCODE_FILL_ARRAY_DATA:
return "fill-array-data";
case DOPCODE_GOTO_32:
return "goto/32";
case DOPCODE_PACKED_SWITCH:
return "packed-switch";
case DOPCODE_SPARSE_SWITCH:
return "sparse-switch";
case DOPCODE_CONST_WIDE:
return "const-wide";
// field opcode
case DOPCODE_IGET:
ss << "iget ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_WIDE:
ss << "iget-wide ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_OBJECT:
ss << "iget-object ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_BOOLEAN:
ss << "iget-boolean ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_BYTE:
ss << "iget-byte ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_CHAR:
ss << "iget-char ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_SHORT:
ss << "iget-short ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT:
ss << "iput ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_WIDE:
ss << "iput-wide ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_OBJECT:
ss << "iput-object ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_BOOLEAN:
ss << "iput-boolean ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_BYTE:
ss << "iput-byte ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_CHAR:
ss << "iput-char ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_SHORT:
ss << "iput-short ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SGET:
ss << "sget ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SGET_WIDE:
ss << "sget-wide ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SGET_OBJECT:
ss << "sget-object ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SGET_BOOLEAN:
ss << "sget-boolean ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SGET_BYTE:
ss << "sget-byte ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SGET_CHAR:
ss << "sget-char ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SGET_SHORT:
ss << "sget-short ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SPUT:
ss << "sput ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SPUT_WIDE:
ss << "sput-wide ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SPUT_OBJECT:
ss << "sput-object ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SPUT_BOOLEAN:
ss << "sput-boolean ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SPUT_BYTE:
ss << "sput-byte ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SPUT_CHAR:
ss << "sput-char ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_SPUT_SHORT:
ss << "sput-short ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_VIRTUAL:
ss << "invoke-virtual ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_SUPER:
ss << "invoke-super ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_DIRECT:
ss << "invoke-direct ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_STATIC:
ss << "invoke-static ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_INTERFACE:
ss << "invoke-interface ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_VIRTUAL_RANGE:
ss << "invoke-virtual/range ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_SUPER_RANGE:
ss << "invoke-super/range ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_DIRECT_RANGE:
ss << "invoke-direct/range ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_STATIC_RANGE:
ss << "invoke-static/range ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_INTERFACE_RANGE:
ss << "invoke-interface/range ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_CONST_STRING:
ss << "const-string "
<< show(static_cast<const DexOpcodeString*>(insn)->get_string());
return ss.str();
case DOPCODE_CONST_STRING_JUMBO:
ss << "const-string/jumbo "
<< show(static_cast<const DexOpcodeString*>(insn)->get_string());
return ss.str();
case DOPCODE_CONST_CLASS:
ss << "const-class ";
ss << show_type(static_cast<const DexOpcodeType*>(insn)->get_type(),
deobfuscated);
return ss.str();
case DOPCODE_CHECK_CAST:
ss << "check-cast ";
ss << show_type(static_cast<const DexOpcodeType*>(insn)->get_type(),
deobfuscated);
return ss.str();
case DOPCODE_INSTANCE_OF:
ss << "instance-of ";
ss << show_type(static_cast<const DexOpcodeType*>(insn)->get_type(),
deobfuscated);
return ss.str();
case DOPCODE_NEW_INSTANCE:
ss << "new-instance ";
ss << show_type(static_cast<const DexOpcodeType*>(insn)->get_type(),
deobfuscated);
return ss.str();
case DOPCODE_NEW_ARRAY:
ss << "new-array ";
ss << show_type(static_cast<const DexOpcodeType*>(insn)->get_type(),
deobfuscated);
return ss.str();
case DOPCODE_FILLED_NEW_ARRAY:
ss << "filled-new-array ";
ss << show_type(static_cast<const DexOpcodeType*>(insn)->get_type(),
deobfuscated);
return ss.str();
case FOPCODE_PACKED_SWITCH:
return "packed-switch-payload";
case FOPCODE_SPARSE_SWITCH:
return "sparse-switch-payload";
case FOPCODE_FILLED_ARRAY:
return "fill-array-data-payload";
case DOPCODE_FILLED_NEW_ARRAY_RANGE:
return "filled-new-array/range";
case DOPCODE_RETURN_VOID_NO_BARRIER:
return "return-void-no-barrier";
case DOPCODE_ADD_INT_2ADDR:
return "add-int/2addr";
case DOPCODE_SUB_INT_2ADDR:
return "sub-int/2addr";
case DOPCODE_MUL_INT_2ADDR:
return "mult-int/2addr";
case DOPCODE_DIV_INT_2ADDR:
return "div-int/2addr";
case DOPCODE_REM_INT_2ADDR:
return "rem-int/2addr";
case DOPCODE_AND_INT_2ADDR:
return "and-int/2addr";
case DOPCODE_OR_INT_2ADDR:
return "or-int/2addr";
case DOPCODE_XOR_INT_2ADDR:
return "xor-int/2addr";
case DOPCODE_SHL_INT_2ADDR:
return "shl-int/2addr";
case DOPCODE_SHR_INT_2ADDR:
return "shr-int/2addr";
case DOPCODE_USHR_INT_2ADDR:
return "ushr-int/2addr";
case DOPCODE_ADD_LONG_2ADDR:
return "add-long/2addr";
case DOPCODE_SUB_LONG_2ADDR:
return "sub-long/2addr";
case DOPCODE_MUL_LONG_2ADDR:
return "mul-long/2addr";
case DOPCODE_DIV_LONG_2ADDR:
return "div-long/2addr";
case DOPCODE_REM_LONG_2ADDR:
return "rem-long/2addr";
case DOPCODE_AND_LONG_2ADDR:
return "and-long/2addr";
case DOPCODE_OR_LONG_2ADDR:
return "or-long/2addr";
case DOPCODE_XOR_LONG_2ADDR:
return "xor-long/2addr";
case DOPCODE_SHL_LONG_2ADDR:
return "shl-long/2addr";
case DOPCODE_SHR_LONG_2ADDR:
return "shr-long/2addr";
case DOPCODE_USHR_LONG_2ADDR:
return "ushr-long/2addr";
case DOPCODE_ADD_FLOAT_2ADDR:
return "add-float/2addr";
case DOPCODE_SUB_FLOAT_2ADDR:
return "sub-float/2addr";
case DOPCODE_MUL_FLOAT_2ADDR:
return "mul-float/2addr";
case DOPCODE_DIV_FLOAT_2ADDR:
return "div-float/2addr";
case DOPCODE_REM_FLOAT_2ADDR:
return "rem-float/2addr";
case DOPCODE_ADD_DOUBLE_2ADDR:
return "add-double/2addr";
case DOPCODE_SUB_DOUBLE_2ADDR:
return "sub-double/2addr";
case DOPCODE_MUL_DOUBLE_2ADDR:
return "mul-double/2addr";
case DOPCODE_DIV_DOUBLE_2ADDR:
return "div-double/2addr";
case DOPCODE_REM_DOUBLE_2ADDR:
return "rem-double/2addr";
case DOPCODE_IGET_QUICK:
return "add-double/2addr";
case DOPCODE_IGET_WIDE_QUICK:
ss << "iget-wide-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_OBJECT_QUICK:
ss << "iget-object-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_QUICK:
ss << "iput-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_WIDE_QUICK:
ss << "iput-wide-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_OBJECT_QUICK:
ss << "iput-object-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_VIRTUAL_QUICK:
ss << "invoke-virtual-quick ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_VIRTUAL_RANGE_QUICK:
ss << "invoke-virtual/range-quick ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_BOOLEAN_QUICK:
ss << "iput-boolean-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_BYTE_QUICK:
ss << "iput-byte-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_CHAR_QUICK:
ss << "iput-char-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IPUT_SHORT_QUICK:
ss << "iput-short-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_BOOLEAN_QUICK:
ss << "iget-boolean-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_BYTE_QUICK:
ss << "iget-byte-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_CHAR_QUICK:
ss << "iget-char-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_IGET_SHORT_QUICK:
ss << "iget-short-quick ";
ss << show_field(static_cast<const DexOpcodeField*>(insn)->get_field(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_POLYMORPHIC:
ss << "invoke-polymorphic ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_POLYMORPHIC_RANGE:
ss << "invoke-polymorphic/range ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_CUSTOM:
ss << "invoke-custom ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
case DOPCODE_INVOKE_CUSTOM_RANGE:
ss << "invoke-custom/range ";
ss << show_method(static_cast<const DexOpcodeMethod*>(insn)->get_method(),
deobfuscated);
return ss.str();
}
}
std::string show_insn(const IRInstruction* insn, bool deobfuscated) {
if (!insn) return "";
std::ostringstream ss;
ss << show(insn->opcode()) << " ";
bool first = true;
if (insn->has_dest()) {
ss << "v" << insn->dest();
first = false;
}
for (unsigned i = 0; i < insn->srcs_size(); ++i) {
if (!first) ss << ", ";
ss << "v" << insn->src(i);
first = false;
}
if (opcode::ref(insn->opcode()) != opcode::Ref::None && !first) {
ss << ", ";
}
switch (opcode::ref(insn->opcode())) {
case opcode::Ref::None:
break;
case opcode::Ref::String:
ss << boost::io::quoted(show(insn->get_string()));
break;
case opcode::Ref::Type:
ss << (deobfuscated ? show_deobfuscated(insn->get_type())
: show(insn->get_type()));
break;
case opcode::Ref::Field:
if (deobfuscated) {
ss << show_deobfuscated(insn->get_field());
} else {
ss << show(insn->get_field());
}
break;
case opcode::Ref::Method:
if (deobfuscated) {
ss << show_deobfuscated(insn->get_method());
} else {
ss << show(insn->get_method());
}
break;
case opcode::Ref::Literal:
ss << insn->get_literal();
break;
case opcode::Ref::Data:
ss << "<data>"; // TODO: print something more informative
break;
case opcode::Ref::CallSite:
if (deobfuscated) {
ss << show_deobfuscated(insn->get_callsite());
} else {
ss << show(insn->get_callsite());
}
break;
case opcode::Ref::MethodHandle:
if (deobfuscated) {
ss << show_deobfuscated(insn->get_methodhandle());
} else {
ss << show(insn->get_methodhandle());
}
break;
}
return ss.str();
}
std::string show_helper(const DexAnnotation* anno, bool deobfuscated) {
if (!anno) {
return "";
}
std::ostringstream ss;
ss << "type:" << show(anno->type()) << " visibility:" << show(anno->viz())
<< " annotations:";
if (deobfuscated) {
ss << show_deobfuscated(&anno->anno_elems());
} else {
ss << show(&anno->anno_elems());
}
return ss.str();
}
} // namespace
std::ostream& operator<<(std::ostream& o, const DexString& str) {
o << str.c_str();
return o;
}
// This format must match the proguard map format because it's used to look up
// in the proguard map
std::ostream& operator<<(std::ostream& o, const DexType& type) {
o << *type.get_name();
return o;
}
inline std::string show(const DexString* p) {
if (!p) return "";
return p->str();
}
inline std::string show(const DexType* t) { return show_type(t, false); }
// This format must match the proguard map format because it's used to look up
// in the proguard map
std::string show(const DexFieldRef* ref) { return show_field(ref, false); }
std::ostream& operator<<(std::ostream& o, const DexFieldRef& p) {
o << show(&p);
return o;
}
std::string vshow(const DexField* p) {
if (!p) return "";
std::ostringstream ss;
ss << accessibility(p->get_access()) << humanize(show(p->get_type())) << " "
<< humanize(show(p->get_class())) << "." << show(p->get_name());
if (p->get_anno_set()) {
ss << "\n annotations:" << show(p->get_anno_set());
}
return ss.str();
}
std::string vshow(const DexTypeList* p) {
if (!p) return "";
std::ostringstream ss;
bool first = true;
for (auto const& type : *p) {
if (!first) {
ss << ", ";
} else {
first = false;
}
ss << humanize(show(type));
}
return ss.str();
}
std::string vshow(const DexProto* p, bool include_ret_type = true) {
if (!p) return "";
std::ostringstream ss;
ss << "(" << vshow(p->get_args()) << ")";
if (include_ret_type) {
ss << humanize(show(p->get_rtype()));
}
return ss.str();
}
// This format must match the proguard map format because it's used to look up
// in the proguard map
std::string show(const DexTypeList* l) { return show_type_list(l, false); }
// This format must match the proguard map format because it's used to look up
// in the proguard map
std::string show(const DexProto* p) { return show_proto(p, false); }
std::string show(const DexCode* code) {
if (!code) return "";
std::ostringstream ss;
ss << "regs: " << code->get_registers_size()
<< ", ins: " << code->get_ins_size() << ", outs: " << code->get_outs_size()
<< "\n";
if (code->m_insns) {
for (auto const& insn : code->get_instructions()) {
ss << show(insn) << "\n";
}
}
return ss.str();
}
// This format must match the proguard map format because it's used to look up
// in the proguard map
std::string show(const DexMethodRef* ref) { return show_method(ref, false); }
std::string vshow(uint32_t acc, bool is_method) {
return accessibility(acc, is_method);
}
std::string vshow(const DexType* t) { return humanize(show(t)); }
std::string vshow(const DexMethod* p, bool include_annotations /*=true*/) {
if (!p) return "";
std::ostringstream ss;
ss << (p->is_def() ? accessibility(p->get_access(), true)
: std::string("(?)"))
<< vshow(p->get_proto()->get_rtype()) << " "
<< humanize(show(p->get_class())) << "." << show(p->get_name())
<< vshow(p->get_proto(), false);
if (include_annotations) {
if (p->get_anno_set()) {
ss << "\n annotations:" << show(p->get_anno_set());
}
bool first = true;
if (p->get_param_anno() != nullptr) {
for (auto const& pair : *p->get_param_anno()) {
if (first) {
ss << "\n param annotations:"
<< "\n";
first = false;
}
ss << " " << pair.first << ": " << show(pair.second) << "\n";
}
}
}
return ss.str();
}
// This format must match the proguard map format because it's used to look up
// in the proguard map
std::ostream& operator<<(std::ostream& o, const DexClass& cls) {
o << *cls.get_type();
return o;
}
std::string vshow(const DexClass* p) {
if (!p) return "";
std::ostringstream ss;
ss << accessibility(p->get_access()) << humanize(show(p->get_type()))
<< " extends " << humanize(show(p->get_super_class()));
if (p->get_interfaces()) {
ss << " implements ";
bool first = true;
for (auto const type : *p->get_interfaces()) {
if (first)
first = false;
else
ss << ", ";
ss << humanize(show(type));
}
}
if (p->get_anno_set()) {
ss << "\n annotations:" << show(p->get_anno_set());
}
return ss.str();
}
std::string show(const DexEncodedValue* value) {
if (!value) return "";
return value->show();
}
std::string show(const DexAnnotation* anno) { return show_helper(anno, false); }
std::string show_deobfuscated(const DexAnnotation* anno) {
return show_helper(anno, true);
}
std::string show(const DexAnnotationSet* p) {
if (!p) return "";
std::ostringstream ss;
bool first = true;
for (auto const& anno : p->get_annotations()) {
if (!first) ss << ", ";
ss << show(anno.get());
first = false;
}
return ss.str();
}
std::string show(const DexAnnotationDirectory* p) {
if (!p) return "";
std::ostringstream ss;
if (p->m_class) {
ss << "class annotations:\n" << show(p->m_class) << "\n";
}
if (p->m_field) {
ss << "field annotations:\n";
for (auto const& pair : *p->m_field) {
ss << show(pair.first->get_name()) << ": " << show(pair.second) << "\n";
}
}
if (p->m_method) {
ss << "method annotations:\n";
for (auto const& pair : *p->m_method) {
ss << show(pair.first->get_name()) << ": " << show(pair.second) << "\n";
}
}
if (p->m_method_param) {
ss << "method parameter annotations:\n";
for (auto const& pair : *p->m_method_param) {
ss << show(pair.first->get_name());
for (auto const& parampair : *pair.second) {
ss << " " << parampair.first << ": " << show(parampair.second) << "\n";
}
}
}
return ss.str();
}
std::string show(IROpcode opcode) {
switch (opcode) {
#define OP(op, ...) \
case OPCODE_##op: \
return #op;
#define IOP(op, ...) \
case IOPCODE_##op: \
return "IOPCODE_" #op;
#define OPRANGE(...)
#include "IROpcodes.def"
}
not_reached_log("Unknown opcode 0x%x", opcode);
}
std::string show(DexOpcode opcode) {
switch (opcode) {
#define OP(op, ...) \
case DOPCODE_##op: \
return #op;
DOPS
#undef OP
case FOPCODE_PACKED_SWITCH : return "PACKED_SWITCH_DATA";
case FOPCODE_SPARSE_SWITCH:
return "SPARSE_SWITCH_DATA";
case FOPCODE_FILLED_ARRAY:
return "FILLED_ARRAY_DATA";
SWITCH_FORMAT_QUICK_FIELD_REF
SWITCH_FORMAT_QUICK_METHOD_REF
SWITCH_FORMAT_RETURN_VOID_NO_BARRIER { not_reached(); }
}
}
// Read n_bytes bytes from data into an integral value of type
// IntType while also incrementing the data pointer by n_bytes bytes.
// n_bytes should be less than sizeof(IntType)
template <typename IntType>
static IntType read(const uint8_t*& data, uint16_t n_bytes) {
static_assert(std::is_integral<IntType>::value,
"Only read into integral values.");
always_assert_log(sizeof(IntType) >= n_bytes,
"Should not read more bytes than sizeof(IntType)");
IntType result;
memcpy(&result, data, n_bytes);
data += n_bytes;
return result;
}
std::string show(const DexOpcodeData* insn) {
if (!insn) return "";
std::ostringstream ss;
ss << "{ ";
const auto* data = insn->data();
switch (insn->opcode()) {
case FOPCODE_SPARSE_SWITCH: {
// See format at
// https://source.android.com/devices/tech/dalvik/dalvik-bytecode#sparse-switch
const uint16_t entries = *data++;
const uint16_t* tdata = data + 2 * entries;
const uint8_t* data_ptr = (uint8_t*)data;
const uint8_t* tdata_ptr = (uint8_t*)tdata;
for (size_t i = 0; i < entries; i++) {
if (i != 0) {
ss << ", ";
}
int32_t case_key = read<int32_t>(data_ptr, sizeof(int32_t));
uint32_t target_offset = read<uint32_t>(tdata_ptr, sizeof(uint32_t));
ss << case_key << "->" << target_offset;
}
break;
}
case FOPCODE_PACKED_SWITCH: {
// See format at
// https://source.android.com/devices/tech/dalvik/dalvik-bytecode#packed-switch
const uint16_t entries = *data++;
const uint8_t* data_ptr = (uint8_t*)data;
int32_t case_key = read<int32_t>(data_ptr, sizeof(int32_t));
for (size_t i = 0; i < entries; i++) {
if (i != 0) {
ss << ", ";
}
uint32_t target_offset = read<uint32_t>(data_ptr, sizeof(uint32_t));
ss << case_key++ << "->" << target_offset;
}
break;
}
case FOPCODE_FILLED_ARRAY: {
// See format at
// https://source.android.com/devices/tech/dalvik/dalvik-bytecode#fill-array
const uint16_t ewidth = *data++;
const uint32_t size = *((uint32_t*)data);
ss << "[" << size << " x " << ewidth << "] ";
// escape size
data += 2;
const uint8_t* data_ptr = (uint8_t*)data;
ss << "{ ";
for (size_t i = 0; i < size; i++) {
if (i != 0) {
ss << ", ";
}
ss << std::hex << read<uint64_t>(data_ptr, ewidth);
}
ss << " }";
break;
}
default:
// should not get here
ss << "unknown_payload";
break;
}
ss << " }";
return ss.str();
}
std::string show_insn(const DexInstruction* insn, bool deobfuscated) {
if (!insn) return "";
std::ostringstream ss;
ss << show_opcode(insn, deobfuscated);
if (dex_opcode::is_fopcode(insn->opcode())) {
ss << " " << show(static_cast<const DexOpcodeData*>(insn));
return ss.str();
}
bool first = true;
if (insn->has_dest()) {
ss << " v" << insn->dest();
first = false;
}
for (unsigned i = 0; i < insn->srcs_size(); ++i) {
if (!first) ss << ",";
ss << " v" << insn->src(i);
first = false;
}
if (dex_opcode::has_literal(insn->opcode())) {
if (!first) ss << ",";
ss << " " << insn->get_literal();
first = false;
}
return ss.str();
}
std::string show(const IRInstruction* insn) { return show_insn(insn, false); }
std::string show(const DexInstruction* insn) { return show_insn(insn, false); }
std::ostream& operator<<(std::ostream& o, const IRInstruction& insn) {
o << show(&insn);
return o;
}
std::string show(const DexDebugInstruction* insn) {
if (!insn) return "";
std::ostringstream ss;
switch (insn->opcode()) {
case DBG_END_SEQUENCE:
ss << "DBG_END_SEQUENCE";
break;
case DBG_ADVANCE_PC:
ss << "DBG_ADVANCE_PC " << insn->uvalue();
break;
case DBG_ADVANCE_LINE:
ss << "DBG_ADVANCE_LINE " << insn->value();
break;
case DBG_START_LOCAL: {
auto sl = static_cast<const DexDebugOpcodeStartLocal*>(insn);
ss << "DBG_START_LOCAL v" << sl->uvalue() << " " << show(sl->name()) << ":"
<< show(sl->type());
break;
}
case DBG_START_LOCAL_EXTENDED: {
auto sl = static_cast<const DexDebugOpcodeStartLocal*>(insn);
ss << "DBG_START_LOCAL v" << sl->uvalue() << " " << show(sl->name()) << ":"
<< show(sl->type()) << ";" << show(sl->sig());
break;
}
case DBG_END_LOCAL:
ss << "DBG_END_LOCAL v" << insn->uvalue();
break;
case DBG_RESTART_LOCAL:
ss << "DBG_RESTART_LOCAL v" << insn->uvalue();
break;
case DBG_SET_PROLOGUE_END:
ss << "DBG_SET_PROLOGUE_END";
break;
case DBG_SET_EPILOGUE_BEGIN:
ss << "DBG_SET_EPILOGUE_BEGIN";
break;
case DBG_SET_FILE: {
auto sf = static_cast<const DexDebugOpcodeSetFile*>(insn);
ss << "DBG_SET_FILE " << show(sf->file());
break;
}
default: {
auto adjusted_opcode = insn->opcode() - DBG_FIRST_SPECIAL;
auto line = DBG_LINE_BASE + (adjusted_opcode % DBG_LINE_RANGE);
auto address = (adjusted_opcode / DBG_LINE_RANGE);
ss << "DBG_SPECIAL line+=" << line << " addr+=" << address;
}
}
return ss.str();
}
std::ostream& operator<<(std::ostream& o, const DexPosition& pos) {
if (pos.method != nullptr) {
o << *pos.method;
} else {
o << "Unknown method";
}
o << "(";
if (pos.file == nullptr) {
o << "Unknown source";
} else {
o << *pos.file;
}
o << ":" << pos.line << ")";
if (pos.parent != nullptr) {
o << " [parent: " << pos.parent << "]";
}
return o;
}
std::string show(const DexDebugEntry* entry) {
std::ostringstream ss;
ss << std::hex;
switch (entry->type) {
case DexDebugEntryType::Instruction:
ss << "INSTRUCTION: [0x" << entry->addr << "] " << show(entry->insn);
break;
case DexDebugEntryType::Position:
ss << "POSITION: [0x" << entry->addr << "] " << show(entry->pos);
break;
}
return ss.str();
}
std::string show(TryEntryType t) {
switch (t) {
case TRY_START:
return "TRY_START";
case TRY_END:
return "TRY_END";
}
}
std::string show(const SwitchIndices& si) {
std::ostringstream ss;
for (auto index : si) {
ss << index << " ";
}
return ss.str();
}
std::ostream& operator<<(std::ostream& o, const MethodItemEntry& mie) {
o << "[" << &mie << "] ";
switch (mie.type) {
case MFLOW_OPCODE:
o << "OPCODE: " << show(mie.insn);
break;
case MFLOW_DEX_OPCODE:
o << "DEX_OPCODE: " << show(mie.dex_insn);
break;
case MFLOW_TARGET:
if (mie.target->type == BRANCH_MULTI) {
o << "TARGET: MULTI " << mie.target->case_key << " ";
} else {
o << "TARGET: SIMPLE ";
}
o << mie.target->src;
break;
case MFLOW_TRY:
o << "TRY: " << show(mie.tentry->type) << " " << mie.tentry->catch_start;
break;
case MFLOW_CATCH:
o << "CATCH: " << show(mie.centry->catch_type);
if (mie.centry->next != nullptr) {
o << " (next " << mie.centry->next << ")";
}
break;
case MFLOW_DEBUG:
o << "DEBUG: " << show(mie.dbgop);
break;
case MFLOW_POSITION:
o << "POSITION: " << *mie.pos;
break;
case MFLOW_SOURCE_BLOCK:
o << "SOURCE-BLOCKS: " << mie.src_block->show();
break;
case MFLOW_FALLTHROUGH:
o << "FALLTHROUGH";
break;
}
return o;
}
std::ostream& operator<<(std::ostream& o, const DexMethodHandle& mh) {
o << "[" << &mh << "] ";
o << "METHODHANDLE: TYPE=" << show(mh.type());
o << " FIELD_OR_METHOD_ID=";
if (DexMethodHandle::isInvokeType(mh.type())) {
o << show(mh.methodref());
} else {
o << show(mh.fieldref());
}
return o;
}
std::ostream& operator<<(std::ostream& o, const DexCallSite& cs) {
o << "[" << &cs << "] ";
o << "CALLSITE: METHODHANDLE=" << show(cs.method_handle());
o << " METHODNAME=" << show(cs.method_name());
o << " METHODTYPE=" << show(cs.method_type());
return o;
}
std::string show(const IRList* ir) {
std::string ret;
for (auto const& mei : *ir) {
ret += show(mei);
ret += "\n";
}
return ret;
}
namespace {
struct NoneSpecial {
void mie_before(std::ostream&, const MethodItemEntry&) {}
void mie_after(std::ostream&, const MethodItemEntry&) {}
void start_block(std::ostream&, const cfg::Block*) {}
void end_block(std::ostream&, const cfg::Block*) {}
};
} // namespace
std::string show(const cfg::Block* block) {
NoneSpecial nothing{};
return show(block, nothing);
}
std::string show(const cfg::ControlFlowGraph& cfg) {
NoneSpecial nothing{};
return show(cfg, nothing);
}
std::string show(const MethodCreator* mc) {
if (!mc) return "";
std::ostringstream ss;
ss << "MethodCode for " << SHOW(mc->method) << "\n";
ss << "locals: ";
for (auto& loc : mc->locals) {
ss << "[" << loc.get_reg() << "] " << SHOW(loc.get_type());
}
ss << "\ninstructions:\n";
ss << show(mc->main_block);
return ss.str();
}
std::string show(const MethodBlock* block) {
if (!block) return "";
std::ostringstream ss;
return ss.str();
}
std::string show(DexIdx* p) {
std::ostringstream ss;
ss << "----------------------------------------\n"
<< "strings\n"
<< "----------------------------------------\n";
for (uint32_t i = 0; i < p->m_string_ids_size; i++) {
ss << show(p->m_string_cache[i]) << "\n";
}
ss << "----------------------------------------\n"
<< "types\n"
<< "----------------------------------------\n";
for (uint32_t i = 0; i < p->m_type_ids_size; i++) {
ss << show(p->m_type_cache[i]) << "\n";
}
ss << "----------------------------------------\n"
<< "fields\n"
<< "----------------------------------------\n";
for (uint32_t i = 0; i < p->m_field_ids_size; i++) {
ss << show(p->m_field_cache[i]) << "\n";
}
ss << "----------------------------------------\n"
<< "methods\n"
<< "----------------------------------------\n";
for (uint32_t i = 0; i < p->m_method_ids_size; i++) {
ss << show(p->m_method_cache[i]) << "\n";
}
return ss.str();
}
std::string show(const IRCode* mt) { return show(mt->m_ir_list); }
std::string show(const ir_list::InstructionIterable& it) {
std::ostringstream ss;
for (auto& mei : it) {
ss << show(mei.insn) << "\n";
}
return ss.str();
}
std::string show_context(IRCode const* code, IRInstruction const* insn) {
std::ostringstream ss;
auto iter = code->begin();
while ((*iter).insn != insn) {
always_assert(iter != code->end());
iter++;
}
for (int i = 0; i < 6 && iter != code->begin(); i++) {
iter--;
}
for (int i = 0; i < 11 && iter != code->end(); i++) {
ss << SHOW(*iter++) << std::endl;
}
return ss.str();
}
std::string show_deobfuscated(const DexClass* cls) {
if (!cls) {
return "";
}
const auto& deob = cls->get_deobfuscated_name_or_empty();
if (!deob.empty()) {
return deob;
}
return cls->get_name() ? cls->get_name()->str() : show(cls);
}
std::string show_deobfuscated(const DexFieldRef* ref) {
return show_field(ref, true);
}
std::string show_deobfuscated(const DexMethodRef* ref) {
return show_method(ref, true);
}
std::string show_deobfuscated(const IRInstruction* insn) {
return show_insn(insn, true);
}
std::string show_deobfuscated(const DexInstruction* insn) {
return show_insn(insn, true);
}
std::string show_deobfuscated(const DexEncodedValue* ev) {
if (ev == nullptr) {
return "";
}
return ev->show_deobfuscated();
}
std::string show_deobfuscated(const DexType* t) { return show_type(t, true); }
std::string show_deobfuscated(const DexTypeList* l) {
return show_type_list(l, true);
}
std::string show_deobfuscated(const DexProto* p) { return show_proto(p, true); }
std::string show_deobfuscated(const DexCallSite* callsite) {
if (!callsite) {
return "";
}
// TODO(T58570881) - actually deobfuscate
return SHOW(callsite);
}
std::string show_deobfuscated(const DexMethodHandle* methodhandle) {
if (!methodhandle) {
return "";
}
// TODO(T58570881) - actually deobfuscate
return SHOW(methodhandle);
}
std::string pretty_bytes(uint64_t val) {
size_t divisions = 0;
double d_val = val;
while (d_val > 1024 && divisions < 3) {
d_val /= 1024;
++divisions;
}
const char* modifier;
switch (divisions) {
case 0:
modifier = "";
break;
case 1:
modifier = "k";
break;
case 2:
modifier = "M";
break;
case 3:
modifier = "G";
break;
default:
modifier = "Error";
break;
}
std::ostringstream oss;
oss << std::setiosflags(std::ios::fixed) << std::setprecision(2) << d_val
<< " " << modifier << "B";
return oss.str();
}