795 lines
20 KiB
C
795 lines
20 KiB
C
// SPDX-FileCopyrightText: 2021-2022 deroad <wargio@libero.it>
|
|
// SPDX-FileCopyrightText: 2010-2019 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_types.h>
|
|
#include <rz_lib.h>
|
|
#include <rz_asm.h>
|
|
#include <rz_analysis.h>
|
|
|
|
#include "../../asm/arch/dalvik/opcode.h"
|
|
#include "../../bin/format/dex/dex.h"
|
|
|
|
static const char *getCond(ut8 cond) {
|
|
switch (cond) {
|
|
case 0x32: // if-eq
|
|
return "$z";
|
|
case 0x33: // if-ne
|
|
return "$z,!";
|
|
case 0x34: // if-lt
|
|
return "63,$c,!";
|
|
case 0x35: // if-ge
|
|
return "63,$c,$z,|";
|
|
case 0x36: // if-gt
|
|
return "63,$c";
|
|
case 0x37: // if-le
|
|
return "63,$c,!,$z,|";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static const char *getCondz(ut8 cond) {
|
|
switch (cond) {
|
|
case 0x38: // if-eqz
|
|
return "NOP";
|
|
case 0x39: // if-nez
|
|
return "!";
|
|
case 0x3a: // if-ltz
|
|
return "0,==,63,$c,!";
|
|
case 0x3b: // if-gez
|
|
return "0,==,63,$c,$z,|";
|
|
case 0x3c: // if-gtz
|
|
return "0,==,63,$c";
|
|
case 0x3d: // if-lez
|
|
return "0,==,63,$c,!";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static int dalvik_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *data, int len, RzAnalysisOpMask mask) {
|
|
int sz = dalvik_opcodes[data[0]].len;
|
|
if (!op || sz >= len) {
|
|
if (op && (mask & RZ_ANALYSIS_OP_MASK_DISASM)) {
|
|
op->mnemonic = strdup("invalid");
|
|
}
|
|
return -1;
|
|
}
|
|
op->size = sz;
|
|
op->nopcode = 1; // Necessary??
|
|
op->id = data[0];
|
|
|
|
ut32 vA = 0;
|
|
ut32 vB = 0;
|
|
ut32 vC = 0;
|
|
if (len > 3) {
|
|
vA = data[1];
|
|
vB = data[2];
|
|
vC = data[3];
|
|
}
|
|
switch (data[0]) {
|
|
case 0xca: // rem-float:
|
|
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
|
|
// fallthrough
|
|
case 0x1b: // const-string/jumbo
|
|
case 0x14: // const
|
|
case 0x15: // const
|
|
case 0x16: // const
|
|
case 0x17: // const
|
|
case 0x42: // const
|
|
case 0x12: // const/4
|
|
{
|
|
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
|
ut32 vB = (data[1] & 0x0f);
|
|
ut32 vA = (data[1] & 0xf0) >> 4;
|
|
ut32 vC = (len > 4) ? rz_read_le32(data + 2) : 0x22;
|
|
// op->stackop = RZ_ANALYSIS_STACK_SET;
|
|
// op->ptr = vC; // why
|
|
ut64 val = vC ? vC : vA;
|
|
op->val = val;
|
|
// op->reg = vB;
|
|
op->nopcode = 2;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "0x%" PFMT64x ",v%d,=", val, vB);
|
|
}
|
|
} break;
|
|
case 0x01: // move
|
|
case 0x07: // move-object
|
|
case 0x04: // mov-wide
|
|
{
|
|
ut32 vB = (data[1] & 0x0f);
|
|
ut32 vA = (data[1] & 0xf0) >> 4;
|
|
if (vA == vB) {
|
|
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, ",");
|
|
}
|
|
} else {
|
|
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
|
// op->stackop = RZ_ANALYSIS_STACK_SET;
|
|
// op->ptr = -vA;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "v%d,v%d,=", vA, vB);
|
|
}
|
|
}
|
|
} break;
|
|
case 0x02: // move/from16
|
|
case 0x03: // move/16
|
|
case 0x05: // move-wide/from16
|
|
case 0x06: // mov-wide&17
|
|
case 0x08: // move-object/from16
|
|
case 0x09: // move-object/16
|
|
case 0x13: // const/16
|
|
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
|
if (len > 2) {
|
|
int vA = (int)data[1];
|
|
ut32 vB = (data[3] << 8) | data[2];
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "v%d,v%d,=", vA, vB);
|
|
}
|
|
op->val = vB;
|
|
}
|
|
break;
|
|
case 0x18: // const-wide
|
|
case 0x19: // const-wide
|
|
// 180001000101. const-wide v0:v1, 0x18201cd01010001
|
|
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
|
break;
|
|
case 0x0a: // move-result
|
|
case 0x0d: // move-exception
|
|
case 0x0c: // move-result-object
|
|
case 0x0b: // move-result-wide
|
|
// TODO: add MOVRET OP TYPE ??
|
|
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = data[1];
|
|
esilprintf(op, "sp,v%d,=[8],8,sp,+=,8", vA);
|
|
}
|
|
break;
|
|
case 0x1a: // const-string
|
|
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
|
op->datatype = RZ_ANALYSIS_DATATYPE_STRING;
|
|
if (len > 2) {
|
|
ut32 vA = data[1];
|
|
ut32 vB = (data[3] << 8) | data[2];
|
|
ut64 offset = RZ_ANALYSIS_GET_OFFSET(analysis, 's', vB);
|
|
op->ptr = offset;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
// op->refptr = 0;
|
|
esilprintf(op, "0x%" PFMT64x ",v%d,=", offset, vA);
|
|
}
|
|
}
|
|
break;
|
|
case 0x1c: // const-class
|
|
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
|
op->datatype = RZ_ANALYSIS_DATATYPE_CLASS;
|
|
break;
|
|
case 0x89: // float-to-double
|
|
case 0x8a: // double-to-int
|
|
case 0x87: // double-to-int
|
|
case 0x8c: // double-to-float
|
|
case 0x8b: // double-to-long
|
|
case 0x88: // float-to-long
|
|
case 0x86: // long-to-double
|
|
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
|
|
// fallthrough
|
|
case 0x81: // int-to-long
|
|
case 0x82: // int-to-float
|
|
case 0x85: // long-to-float
|
|
case 0x83: // int-to-double
|
|
case 0x8d: // int-to-byte
|
|
case 0x8e: // int-to-char
|
|
op->type = RZ_ANALYSIS_OP_TYPE_CAST;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = (data[1] & 0x0f);
|
|
ut32 vB = (data[1] & 0xf0) >> 4;
|
|
esilprintf(op, "v%d,0xff,&,v%d,=", vB, vA);
|
|
}
|
|
break;
|
|
case 0x8f: // int-to-short
|
|
op->type = RZ_ANALYSIS_OP_TYPE_CAST;
|
|
// op->datatype = RZ_ANALYSIS_DATATYPE_INT32 | RZ_ANALYSIS_DATATYPE_INT16;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = (data[1] & 0x0f);
|
|
ut32 vB = (data[1] & 0xf0) >> 4;
|
|
esilprintf(op, "v%d,0xffff,&,v%d,=", vB, vA);
|
|
}
|
|
break;
|
|
case 0x84: // long-to-int
|
|
op->type = RZ_ANALYSIS_OP_TYPE_CAST;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = (data[1] & 0x0f);
|
|
ut32 vB = (data[1] & 0xf0) >> 4;
|
|
esilprintf(op, "v%d,0xffffffff,&,v%d,=", vB, vA);
|
|
}
|
|
break;
|
|
case 0x20: // instance-of
|
|
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "%d,instanceof,%d,-,!,v%d,=", vC, vB, vA);
|
|
}
|
|
break;
|
|
case 0x21: // array-length
|
|
op->type = RZ_ANALYSIS_OP_TYPE_LENGTH;
|
|
op->datatype = RZ_ANALYSIS_DATATYPE_ARRAY;
|
|
break;
|
|
case 0x44: // aget
|
|
case 0x45: // aget-bool
|
|
case 0x46:
|
|
case 0x47: // aget-bool
|
|
case 0x48: // aget-byte
|
|
case 0x49: // aget-char
|
|
case 0x4a: // aget-short
|
|
case 0x52: // iget
|
|
case 0x58: // iget-short
|
|
case 0x53: // iget-wide
|
|
case 0x56: // iget-byte
|
|
case 0x57: // iget-char
|
|
case 0xea: // sget-wide-volatile
|
|
case 0xf4: // iget-byte
|
|
case 0x66: // sget-short
|
|
case 0xfd: // sget-object
|
|
case 0x55: // iget-bool
|
|
case 0x60: // sget
|
|
case 0x61: //
|
|
case 0x64: // sget-byte
|
|
case 0x65: // sget-char
|
|
case 0xe3: // iget-volatile
|
|
case 0xe4: //
|
|
case 0xe5: // sget
|
|
case 0xe6: // sget
|
|
case 0xe7: // iget-object-volatile
|
|
case 0xe8: // iget-bool
|
|
case 0xf3: // iget-bool
|
|
case 0xf8: // iget-bool
|
|
case 0xf2: // iget-quick
|
|
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
|
|
break;
|
|
case 0x54: // iget-object
|
|
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = (data[1] & 0x0f);
|
|
ut32 vB = (data[1] & 0xf0) >> 4;
|
|
ut32 vC = (data[2] & 0x0f);
|
|
esilprintf(op, "%d,v%d,iget,v%d,=", vC, vB, vA);
|
|
}
|
|
break;
|
|
case 0x63: // sget-boolean
|
|
op->datatype = RZ_ANALYSIS_DATATYPE_BOOLEAN;
|
|
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = (data[1] & 0x0f);
|
|
ut32 vB = (data[1] & 0xf0) >> 4;
|
|
ut32 vC = (data[2] & 0x0f);
|
|
const char *vT = "-boolean";
|
|
esilprintf(op, "%d,%d,sget%s,v%d,=", vC, vB, vT, vA);
|
|
}
|
|
break;
|
|
case 0x62: // sget-object
|
|
{
|
|
op->datatype = RZ_ANALYSIS_DATATYPE_OBJECT;
|
|
op->type = RZ_ANALYSIS_OP_TYPE_LOAD;
|
|
ut32 vC = len > 3 ? (data[3] << 8) | data[2] : 0;
|
|
op->ptr = analysis->binb.get_offset(analysis->binb.bin, 'f', vC);
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = (data[1] & 0x0f);
|
|
esilprintf(op, "%" PFMT64d ",v%d,=", op->ptr, vA);
|
|
}
|
|
} break;
|
|
case 0x6b: // sput-byte
|
|
case 0x6d: // sput-short
|
|
case 0xeb: // sput-wide-volatile
|
|
case 0x4b: // aput
|
|
case 0x4c: // aput-wide
|
|
case 0x4d: // aput-object
|
|
case 0x4e: // aput-bool
|
|
case 0x4f: //
|
|
case 0x5e: // iput-char
|
|
case 0xfc: // iput-object-volatile
|
|
case 0xf5: // iput-quick
|
|
case 0x5c: // iput-bool
|
|
case 0x69: // sput-object
|
|
case 0x5f: // iput-wide
|
|
case 0xe9: // iput-wide-volatile
|
|
case 0xf6: // iput-wide
|
|
case 0xf7: // iput-wide
|
|
case 0x67: // iput-wide
|
|
case 0x59: // iput-wide
|
|
case 0x5a: // iput-wide
|
|
case 0x5b: // iput-wide
|
|
case 0x5d: // iput-wide
|
|
case 0x50: //
|
|
case 0x51: // aput-short
|
|
case 0x68: // sput-wide
|
|
case 0x6a: // sput-boolean
|
|
case 0x6c: // sput-wide
|
|
case 0xfe: // sput
|
|
{
|
|
op->type = RZ_ANALYSIS_OP_TYPE_STORE;
|
|
ut32 vC = len > 3 ? (data[3] << 8) | data[2] : 0;
|
|
op->ptr = analysis->binb.get_offset(analysis->binb.bin, 'f', vC);
|
|
}
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = (data[1] & 0x0f);
|
|
esilprintf(op, "%" PFMT64d ",v%d,=", op->ptr, vA);
|
|
}
|
|
break;
|
|
case 0xad: // mul-double
|
|
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
|
|
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "v%d,v%d,*,v%d,=", vC, vB, vA);
|
|
}
|
|
break;
|
|
case 0x9d:
|
|
case 0xc8: // mul-float
|
|
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
|
|
/* fall through */
|
|
case 0xcd:
|
|
case 0xd2: // mul-int/lit16
|
|
case 0x92:
|
|
case 0xb2:
|
|
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = (data[1] & 0x0f);
|
|
ut32 vB = (data[1] & 0xf0) >> 4;
|
|
ut32 vC = (data[2] << 8) | data[3];
|
|
esilprintf(op, "%d,v%d,*,v%d,=", vC, vB, vA);
|
|
op->val = vC;
|
|
}
|
|
break;
|
|
case 0x7c: // not-int
|
|
case 0x7e: // not-long
|
|
op->type = RZ_ANALYSIS_OP_TYPE_NOT;
|
|
break;
|
|
case 0xa4: // shr-long
|
|
case 0xba: // ushr-int/2addr
|
|
case 0xe2: // ushr-int
|
|
case 0xa5: // ushr-long
|
|
case 0x9a: // ushr-long
|
|
case 0xc5: // ushr-long/2addr
|
|
case 0xc4: // shr-long/2addr
|
|
case 0xe1: // shr-int/lit8
|
|
case 0x99: // shr-int
|
|
op->type = RZ_ANALYSIS_OP_TYPE_SHR;
|
|
break;
|
|
case 0xaa: // rem-float
|
|
case 0xcf: // rem-double
|
|
case 0xaf: // rem-double
|
|
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
|
|
// fallthrough
|
|
case 0xb4: // rem-int/2addr
|
|
case 0xdc: // rem-int/lit8
|
|
case 0xd4: // rem-int
|
|
case 0xbf: // rem-long/2addr
|
|
case 0x9f: // rem-long
|
|
case 0x94: // rem-int
|
|
op->type = RZ_ANALYSIS_OP_TYPE_MOD; // mod = rem
|
|
break;
|
|
case 0xd7:
|
|
case 0xd9:
|
|
case 0xda:
|
|
case 0xde:
|
|
|
|
case 0x95: // and-int
|
|
case 0x96: // or-int
|
|
op->type = RZ_ANALYSIS_OP_TYPE_OR;
|
|
break;
|
|
case 0xc2: // xor-long
|
|
case 0x97: // xor-int
|
|
case 0xdf: // xor-int/lit16
|
|
case 0xa2: // xor-long
|
|
op->type = RZ_ANALYSIS_OP_TYPE_XOR;
|
|
break;
|
|
case 0xc9: // div-float
|
|
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
|
|
// fallthrough
|
|
case 0x93: // div-int
|
|
case 0xd3: // div-int/lit16
|
|
case 0xdb: // div-int/lit8
|
|
case 0xce: // div-double
|
|
case 0x9e: // div-double
|
|
case 0xbe: // div-double
|
|
case 0xae: // div-double
|
|
case 0xa9: // div-float
|
|
case 0xb3: // div-int/2addr
|
|
op->type = RZ_ANALYSIS_OP_TYPE_DIV;
|
|
break;
|
|
case 0x0e: // return-void
|
|
case 0x0f: // return
|
|
case 0x10: // return-wide
|
|
case 0x11: // return-object
|
|
case 0xf1: // return-void-barrier
|
|
op->type = RZ_ANALYSIS_OP_TYPE_RET;
|
|
op->eob = true;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
if (data[0] == 0x0e) { // return-void
|
|
esilprintf(op, "sp,[8],ip,=,8,sp,+=");
|
|
} else {
|
|
ut32 vA = data[1];
|
|
esilprintf(op, "sp,[8],ip,=,8,sp,+=,8,sp,-=,v%d,sp,=[8]", vA);
|
|
}
|
|
}
|
|
break;
|
|
case 0x28: // goto
|
|
if (len > 1) {
|
|
st32 rel = (signed char)data[1];
|
|
op->jump = addr + (rel * 2);
|
|
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "0x%" PFMT64x ",ip,=", op->jump);
|
|
}
|
|
}
|
|
break;
|
|
case 0x29: // goto/16
|
|
if (len > 3) {
|
|
st32 rel = (short)(data[3] << 8 | data[2]);
|
|
op->jump = addr + (rel * 2);
|
|
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "0x%" PFMT64x ",ip,=", op->jump);
|
|
}
|
|
}
|
|
break;
|
|
case 0x2a: // goto/32
|
|
if (len > 5) {
|
|
st32 rel = (st32)(data[2] | (data[3] << 8) | (data[4] << 16) | (data[5] << 24));
|
|
op->jump = addr + (rel * 2);
|
|
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "0x%" PFMT64x ",ip,=", op->jump);
|
|
}
|
|
}
|
|
break;
|
|
case 0x2c:
|
|
case 0x2b:
|
|
op->type = RZ_ANALYSIS_OP_TYPE_SWITCH;
|
|
break;
|
|
case 0x3e: // glitch 0 width instruction .. invalid instruction
|
|
case 0x43:
|
|
op->type = RZ_ANALYSIS_OP_TYPE_ILL;
|
|
op->size = 1;
|
|
op->eob = true;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, ",");
|
|
}
|
|
break;
|
|
case 0x2d: // cmpl-float
|
|
case 0x2e: // cmpg-float
|
|
case 0x3f: // cmpg-float // ???? wrong disasm imho 2e0f12003f0f
|
|
case 0x2f: // cmpl-double
|
|
case 0x30: // cmlg-double
|
|
case 0x31: // cmp-long
|
|
case 0x1f: // check-cast
|
|
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
|
break;
|
|
case 0x32: // if-eq
|
|
case 0x33: // if-ne
|
|
case 0x34: // if-lt
|
|
case 0x35: // if-ge
|
|
case 0x36: // if-gt
|
|
case 0x37: // if-le
|
|
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
|
if (len > 3) {
|
|
int rel = (int)(data[3] << 8 | data[2]);
|
|
op->jump = addr + (rel * 2);
|
|
op->fail = addr + sz;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = data[1];
|
|
ut32 vB = data[2];
|
|
const char *cond = getCond(data[0]);
|
|
esilprintf(op, "v%d,v%d,==,%s,?{,%" PFMT64d ",ip,=}", vB, vA, cond, op->jump);
|
|
}
|
|
}
|
|
break;
|
|
case 0x38: // if-eqz
|
|
case 0x39: // if-nez
|
|
case 0x3a: // if-ltz
|
|
case 0x3b: // if-gez
|
|
case 0x3c: // if-gtz
|
|
case 0x3d: // if-lez
|
|
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
|
if (len > 3) {
|
|
int rel = (int)(data[3] << 8 | data[2]);
|
|
op->jump = addr + (rel * 2);
|
|
op->fail = addr + sz;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = data[1];
|
|
const char *cond = getCondz(data[0]);
|
|
esilprintf(op, "v%d,%s,?{,%" PFMT64d ",ip,=}", vA, cond, op->jump);
|
|
}
|
|
}
|
|
break;
|
|
case 0xec: // breakpoint
|
|
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "TRAP");
|
|
}
|
|
break;
|
|
case 0x1d: // monitor-enter
|
|
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
|
|
op->stackop = RZ_ANALYSIS_STACK_INC;
|
|
op->stackptr = 1;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, ",");
|
|
}
|
|
break;
|
|
case 0x1e: // monitor-exit /// wrong type?
|
|
op->type = RZ_ANALYSIS_OP_TYPE_POP;
|
|
op->stackop = RZ_ANALYSIS_STACK_INC;
|
|
op->stackptr = -1;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, ",");
|
|
}
|
|
break;
|
|
case 0x73: // invalid
|
|
break;
|
|
case 0x6f: // invoke-super
|
|
case 0x70: // invoke-direct
|
|
case 0x71: // invoke-static
|
|
case 0x72: // invoke-interface
|
|
case 0x77: //
|
|
case 0xb9: // invokeinterface
|
|
case 0xb7: // invokespecial
|
|
case 0xb6: // invokevirtual
|
|
case 0x6e: // invoke-virtual
|
|
if (len > 3) {
|
|
ut32 vB = (data[3] << 8) | data[2];
|
|
ut64 dst = analysis->binb.get_offset(analysis->binb.bin, 'm', vB);
|
|
if (dst == UT64_MAX) {
|
|
op->type = RZ_ANALYSIS_OP_TYPE_UCALL;
|
|
} else {
|
|
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
|
op->jump = dst;
|
|
}
|
|
op->fail = addr + sz;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "8,sp,-=,0x%" PFMT64x ",sp,=[8],0x%" PFMT64x ",ip,=", op->fail, op->jump);
|
|
}
|
|
}
|
|
break;
|
|
case 0x78: // invokeinterface/range
|
|
case 0xf0: // invoke-object-init-range
|
|
case 0xf9: // invoke-virtual-quick/range
|
|
case 0xfb: // invoke-super-quick/range
|
|
case 0x74: // invoke-virtual/range
|
|
case 0x75: // invoke-super/range
|
|
case 0x76: // invoke-direct/range
|
|
case 0xfa: // invoke-super-quick // invoke-polymorphic
|
|
if (len > 3) {
|
|
ut32 vB = (data[3] << 8) | data[2];
|
|
ut64 dst = analysis->binb.get_offset(analysis->binb.bin, 'm', vB);
|
|
op->fail = addr + sz;
|
|
if (dst == UT64_MAX) {
|
|
op->type = RZ_ANALYSIS_OP_TYPE_UCALL;
|
|
} else {
|
|
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
|
op->jump = dst;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "8,sp,-=,0x%" PFMT64x ",sp,=[8],0x%" PFMT64x ",ip,=", op->fail, op->jump);
|
|
}
|
|
}
|
|
} else {
|
|
rz_warn_if_reached();
|
|
}
|
|
break;
|
|
case 0x27: // throw
|
|
{
|
|
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = data[1];
|
|
esilprintf(op, "v%d,TRAP", vA);
|
|
}
|
|
} break;
|
|
case 0xee: // execute-inline
|
|
case 0xef: // execute-inline/range
|
|
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
|
|
break;
|
|
case 0xed: // throw-verification-error
|
|
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
|
break;
|
|
case 0x22: // new-instance
|
|
op->type = RZ_ANALYSIS_OP_TYPE_NEW;
|
|
if (len > 2) {
|
|
// resolve class name for vB
|
|
int vB = (data[3] << 8) | data[2];
|
|
ut64 off = RZ_ANALYSIS_GET_OFFSET(analysis, 't', vB);
|
|
op->ptr = off;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
int vA = (int)data[1];
|
|
esilprintf(op, "%" PFMT64d ",new,v%d,=", off, vA);
|
|
}
|
|
}
|
|
break;
|
|
case 0x23: // new-array
|
|
op->type = RZ_ANALYSIS_OP_TYPE_NEW;
|
|
// 0x1c, 0x1f, 0x22
|
|
if (len > 2 && mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = (data[1] & 0x0f);
|
|
ut32 vB = (data[1] & 0xf0) >> 4;
|
|
ut32 vC = (int)data[2] | (data[3] << 8);
|
|
esilprintf(op, "%d,%d,new-array,v%d,=", vC, vB, vA);
|
|
}
|
|
break;
|
|
case 0x24: // filled-new-array
|
|
case 0x25: // filled-new-array-range
|
|
case 0x26: // filled-new-array-data
|
|
op->type = RZ_ANALYSIS_OP_TYPE_NEW;
|
|
// 0x1c, 0x1f, 0x22
|
|
if (len > 2) {
|
|
// int vA = (int) data[1];
|
|
int vB = (data[3] << 8) | data[2];
|
|
// resolve class name for vB
|
|
ut64 off = RZ_ANALYSIS_GET_OFFSET(analysis, 't', vB);
|
|
op->ptr = off;
|
|
}
|
|
break;
|
|
case 0x00: // nop
|
|
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, ",");
|
|
}
|
|
break;
|
|
case 0x90: // add-int
|
|
case 0x9b: // add-long
|
|
case 0xa6: // add-float
|
|
case 0xac: // add-double
|
|
case 0xb0: // add-int/2addr
|
|
case 0xbb: // add-long/2addr
|
|
case 0xc6: // add-float/2addr
|
|
case 0xcb: // add-double/2addr
|
|
case 0xd0: // add-int/lit16
|
|
case 0xd8: // add-int/lit8
|
|
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vB = (data[1] & 0x0f);
|
|
ut32 vA = (data[1] & 0xf0) >> 4;
|
|
esilprintf(op, "v%d,v%d,+=", vB, vA);
|
|
}
|
|
break;
|
|
case 0xa7: // sub-float
|
|
case 0xcc: // sub-double
|
|
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
|
|
/* fall through */
|
|
case 0xc7:
|
|
case 0xbc:
|
|
case 0x91:
|
|
case 0xb1: // sub-int/2addr
|
|
case 0xd1: // sub-int/2addr
|
|
case 0x9c: // sub-long
|
|
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
esilprintf(op, "v%d,v%d,-,v%d,=", vC, vB, vA);
|
|
}
|
|
break;
|
|
case 0x7b: // neg-int
|
|
case 0x7d: // neg-long
|
|
case 0x7f: // neg-float
|
|
case 0x80: // neg-double
|
|
op->type = RZ_ANALYSIS_OP_TYPE_NOT;
|
|
break;
|
|
case 0xa0: // and-long
|
|
case 0xc0: // and-long
|
|
case 0xdd: // and-long
|
|
case 0xd5: // and-long
|
|
case 0xb5: // and-int
|
|
op->type = RZ_ANALYSIS_OP_TYPE_AND;
|
|
break;
|
|
case 0xd6: // orint/lit16
|
|
case 0xc1: // or-long/2addr
|
|
case 0xa1: // or-long
|
|
op->type = RZ_ANALYSIS_OP_TYPE_OR;
|
|
if (mask & RZ_ANALYSIS_OP_MASK_ESIL) {
|
|
ut32 vA = (data[1] & 0x0f);
|
|
ut32 vB = (data[1] & 0xf0) >> 4;
|
|
ut32 vC = (data[2] << 8) | data[3];
|
|
esilprintf(op, "%d,v%d,|,v%d,=", vC, vB, vA);
|
|
op->val = vC;
|
|
}
|
|
break;
|
|
case 0xe0: // lshl
|
|
case 0xc3: // lshl
|
|
case 0xa3: // shl-long
|
|
case 0x98: // shl-long
|
|
case 0xb8: // shl-int/2addr
|
|
op->type = RZ_ANALYSIS_OP_TYPE_SHL;
|
|
break;
|
|
}
|
|
|
|
if ((op->type == RZ_ANALYSIS_OP_TYPE_JMP || op->type == RZ_ANALYSIS_OP_TYPE_RET) &&
|
|
len > op->size && data[op->size] == 0x0d) {
|
|
// if the return/goto is followed by a move-exception then the analysis should keep going.
|
|
// this is because the function is supposed to have a try-catch
|
|
op->eob = false;
|
|
op->cond = RZ_TYPE_COND_EXCEPTION;
|
|
}
|
|
|
|
return sz;
|
|
}
|
|
|
|
static char *get_reg_profile(RzAnalysis *analysis) {
|
|
const char *p =
|
|
"=PC ip\n"
|
|
"=SP sp\n"
|
|
"=BP bp\n"
|
|
"=A0 v0\n"
|
|
"=A1 v1\n"
|
|
"=A2 v2\n"
|
|
"=A3 v3\n"
|
|
"=SN v0\n"
|
|
"gpr v0 .32 0 0\n"
|
|
"gpr v1 .32 4 0\n"
|
|
"gpr v2 .32 8 0\n"
|
|
"gpr v3 .32 12 0\n"
|
|
"gpr v4 .32 16 0\n"
|
|
"gpr v5 .32 20 0\n"
|
|
"gpr v6 .32 24 0\n"
|
|
"gpr v7 .32 28 0\n"
|
|
"gpr v8 .32 32 0\n"
|
|
"gpr v9 .32 36 0\n"
|
|
"gpr v10 .32 40 0\n"
|
|
"gpr v11 .32 44 0\n"
|
|
"gpr v12 .32 48 0\n"
|
|
"gpr v13 .32 52 0\n"
|
|
"gpr v14 .32 56 0\n"
|
|
"gpr v15 .32 60 0\n"
|
|
"gpr v16 .32 40 0\n"
|
|
"gpr v17 .32 44 0\n"
|
|
"gpr v18 .32 48 0\n"
|
|
"gpr v19 .32 52 0\n"
|
|
"gpr v20 .32 56 0\n"
|
|
"gpr v21 .32 60 0\n"
|
|
"gpr v22 .32 64 0\n"
|
|
"gpr v23 .32 68 0\n"
|
|
"gpr v24 .32 72 0\n"
|
|
"gpr v25 .32 76 0\n"
|
|
"gpr v26 .32 80 0\n"
|
|
"gpr v27 .32 84 0\n"
|
|
"gpr v28 .32 88 0\n"
|
|
"gpr v29 .32 92 0\n"
|
|
"gpr v30 .32 96 0\n"
|
|
"gpr v31 .32 100 0\n"
|
|
"gpr v32 .32 104 0\n"
|
|
"gpr v33 .32 108 0\n"
|
|
"gpr v34 .32 112 0\n"
|
|
"gpr ip .32 116 0\n"
|
|
"gpr sp .32 120 0\n"
|
|
"gpr bp .32 124 0\n";
|
|
return strdup(p);
|
|
}
|
|
|
|
static int archinfo(RzAnalysis *a, RzAnalysisInfoType query) {
|
|
switch (query) {
|
|
case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE:
|
|
return 1;
|
|
case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE:
|
|
return 32;
|
|
case RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN:
|
|
/* fall-thru */
|
|
case RZ_ANALYSIS_ARCHINFO_DATA_ALIGN:
|
|
return -1;
|
|
case RZ_ANALYSIS_ARCHINFO_CAN_USE_POINTERS:
|
|
return false;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
RzAnalysisPlugin rz_analysis_plugin_dalvik = {
|
|
.name = "dalvik",
|
|
.arch = "dalvik",
|
|
.get_reg_profile = &get_reg_profile,
|
|
.license = "LGPL3",
|
|
.bits = 32,
|
|
.esil = false,
|
|
.desc = "Dalvik (Android VM) bytecode analysis plugin",
|
|
.op = &dalvik_op,
|
|
.archinfo = archinfo,
|
|
};
|
|
|
|
#ifndef RZ_PLUGIN_INCORE
|
|
RZ_API RzLibStruct rizin_plugin = {
|
|
.type = RZ_LIB_TYPE_ANALYSIS,
|
|
.data = &rz_analysis_plugin_dalvik,
|
|
.version = RZ_VERSION
|
|
};
|
|
#endif
|