mirror of
https://github.com/radareorg/radare2.git
synced 2025-02-19 13:53:07 +00:00
474 lines
13 KiB
C
474 lines
13 KiB
C
/* radare - LGPL - Copyright 2007-2013 - pancake */
|
|
|
|
#include <string.h>
|
|
#include <r_types.h>
|
|
#include <r_lib.h>
|
|
#include <r_asm.h>
|
|
#include <r_anal.h>
|
|
|
|
/* DEPRECATE ?? */
|
|
#include "arm.h"
|
|
#include "../asm/arch/arm/arm.h"
|
|
#include "../asm/arch/arm/winedbg/be_arm.h"
|
|
|
|
static unsigned int disarm_branch_offset (unsigned int pc, unsigned int insoff) {
|
|
unsigned int add = insoff << 2;
|
|
/* zero extend if higher is 1 (0x02000000) */
|
|
if ((add & 0x02000000) == 0x02000000)
|
|
add |= 0xFC000000;
|
|
return add + pc + 8;
|
|
}
|
|
|
|
#define IS_BRANCH(x) ((x&ARM_BRANCH_I_MASK) == ARM_BRANCH_I)
|
|
#define IS_BRANCHL(x) (IS_BRANCH(x) && (x&ARM_BRANCH_LINK) == ARM_BRANCH_LINK)
|
|
#define IS_RETURN(x) ((x&(ARM_DTM_I_MASK|ARM_DTM_LOAD|(1<<15))) == (ARM_DTM_I|ARM_DTM_LOAD|(1<<15)))
|
|
//if ( (inst & ( ARM_DTX_I_MASK | ARM_DTX_LOAD | ( ARM_DTX_RD_MASK ) ) ) == ( ARM_DTX_LOAD | ARM_DTX_I | ( ARM_PC << 12 ) ) )
|
|
#define IS_UNKJMP(x) (( (( ARM_DTX_RD_MASK ) ) ) == ( ARM_DTX_LOAD | ARM_DTX_I | ( ARM_PC << 12 ) ))
|
|
#define IS_LOAD(x) ((x&ARM_DTX_LOAD) == (ARM_DTX_LOAD))
|
|
#define IS_CONDAL(x) ((x&ARM_COND_MASK)==ARM_COND_AL)
|
|
#define IS_EXITPOINT(x) (IS_BRANCH (x) || IS_RETURN (x) || IS_UNKJMP (x))
|
|
|
|
#define API static
|
|
|
|
static int op_thumb(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int len) {
|
|
int op_code;
|
|
ut16 *_ins = (ut16*)data;
|
|
ut16 ins = *_ins;
|
|
ut32 *_ins32 = (ut32*)data;
|
|
ut32 ins32 = *_ins32;
|
|
|
|
struct arm_insn *arminsn = arm_new();
|
|
arm_set_thumb(arminsn, R_TRUE);
|
|
arm_set_input_buffer(arminsn, data);
|
|
arm_set_pc(arminsn, addr);
|
|
op->delay = 0;
|
|
op->size = arm_disasm_one_insn(arminsn);
|
|
op->jump = arminsn->jmp;
|
|
op->fail = arminsn->fail;
|
|
arm_free(arminsn);
|
|
|
|
// TODO: handle 32bit instructions (branches are not correctly decoded //
|
|
|
|
/* CMP */
|
|
if (((ins & B4(B1110,0,0,0)) == B4(B0010,0,0,0) )
|
|
&& (1 == (ins & B4(1,B1000,0,0)) >> 11)) { // dp3
|
|
op->type = R_ANAL_OP_TYPE_CMP;
|
|
return op->size;
|
|
}
|
|
if ( (ins & B4(B1111,B1100,0,0)) == B4(B0100,0,0,0) ) {
|
|
op_code = (ins & B4(0,B0011,B1100,0)) >> 6;
|
|
if (op_code == 8 || op_code == 10) { // dp5
|
|
op->type = R_ANAL_OP_TYPE_CMP;
|
|
return op->size;
|
|
}
|
|
}
|
|
if ( (ins & B4(B1111,B1100,0,0)) == B4(B0100,B0100,0,0) ) {
|
|
op_code = (ins & B4(0,B0011,0,0)) >> 8; // dp8
|
|
if (op_code== 1) {
|
|
op->type = R_ANAL_OP_TYPE_CMP;
|
|
return op->size;
|
|
}
|
|
}
|
|
if (ins == 0xbf) {
|
|
// TODO: add support for more NOP instructions
|
|
op->type = R_ANAL_OP_TYPE_NOP;
|
|
} else if (((op_code = ((ins & B4(B1111,B1000,0,0)) >> 11)) >= 12 && op_code <= 17 )) {
|
|
if (op_code%2)
|
|
op->type = R_ANAL_OP_TYPE_LOAD;
|
|
else op->type = R_ANAL_OP_TYPE_STORE;
|
|
} else if ( (ins & B4(B1111,0,0,0)) == B4(B0101,0,0,0) ) {
|
|
op_code = (ins & B4(0,B1110,0,0)) >> 9;
|
|
if (op_code%2)
|
|
op->type = R_ANAL_OP_TYPE_LOAD;
|
|
else op->type = R_ANAL_OP_TYPE_STORE;
|
|
} else if ( (ins & B4(B1111,0,0,0)) == B4(B1101,0,0,0) ) {
|
|
// BNE..
|
|
int delta = (ins & B4(0,0,B1111,B1111));
|
|
op->type = R_ANAL_OP_TYPE_CJMP;
|
|
op->jump = addr+4+(delta<<1);
|
|
op->fail = addr+4;
|
|
} else if ( (ins & B4(B1111,B1000,0,0)) == B4(B1110,0,0,0) ) {
|
|
// B
|
|
int delta = (ins & B4(0,0,B1111,B1111));
|
|
op->type = R_ANAL_OP_TYPE_JMP;
|
|
op->jump = addr+4+(delta<<1);
|
|
op->fail = addr+4;
|
|
op->eob = 1;
|
|
} else if ( (ins & B4(B1111,B1111,B1000,0)) == B4(B0100,B0111,B1000,0) ) {
|
|
// BLX
|
|
op->type = R_ANAL_OP_TYPE_UCALL;
|
|
op->fail = addr+4;
|
|
} else if ( (ins & B4(B1111,B1111,B1000,0)) == B4(B0100,B0111,0,0) ) {
|
|
// BX
|
|
op->type = R_ANAL_OP_TYPE_UJMP;
|
|
op->fail = addr+4;
|
|
} else if ( (ins & B4(B1111,B1000,0,0)) == B4(B1111,0,0,0) ) {
|
|
// BL The long branch with link, it's in 2 instructions:
|
|
// prefix: 11110[offset]
|
|
// suffix: 11111[offset] (11101[offset] for blx)
|
|
ut16 nextins = (ins32 & 0xFFFF0000) >> 16;
|
|
ut32 high = (ins & B4(0, B0111, B1111, B1111)) << 12;
|
|
if (ins & B4(0, B0100, 0, 0)) {
|
|
high |= B4(B1111, B1000, 0, 0) << 16;
|
|
}
|
|
int delta = high + ((nextins & B4(0, B0111, B1111, B1111))*2);
|
|
op->jump = (int)(addr+4+(delta));
|
|
op->type = R_ANAL_OP_TYPE_CALL;
|
|
op->fail = addr+4;
|
|
} else if ( (ins & B4(B1111,B1111,0,0)) == B4(B1011,B1110,0,0) ) {
|
|
op->type = R_ANAL_OP_TYPE_TRAP;
|
|
op->val = (ut64)(ins>>8);
|
|
} else if ( (ins & B4(B1111,B1111,0,0)) == B4(B1101,B1111,0,0)) {
|
|
op->type = R_ANAL_OP_TYPE_SWI;
|
|
op->val = (ut64)(ins>>8);
|
|
}
|
|
return op->size;
|
|
}
|
|
|
|
#if 0
|
|
"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
|
|
"hi", "ls", "ge", "lt", "gt", "le", "al", "nv",
|
|
#endif
|
|
static int iconds[] = {
|
|
R_ANAL_COND_EQ,
|
|
R_ANAL_COND_NE,
|
|
0, // cs
|
|
0, // cc
|
|
0, // mi
|
|
0, // pl
|
|
0, // vs
|
|
0, // vc
|
|
|
|
0, // hi
|
|
0, // ls
|
|
R_ANAL_COND_GE,
|
|
R_ANAL_COND_LT,
|
|
R_ANAL_COND_GT,
|
|
R_ANAL_COND_LE,
|
|
R_ANAL_COND_AL,
|
|
R_ANAL_COND_NV,
|
|
};
|
|
|
|
static int op_cond (const ut8 *data) {
|
|
ut8 b = data[3] >>4;
|
|
if (b==0xf) return 0;
|
|
return iconds[b];
|
|
}
|
|
|
|
static int arm_op32(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int len) {
|
|
const ut8 *b = (ut8 *)data;
|
|
ut8 ndata[4];
|
|
ut32 branch_dst_addr, i = 0;
|
|
ut32* code = (ut32 *)data;
|
|
struct arm_insn *arminsn;
|
|
|
|
if (data == NULL)
|
|
return 0;
|
|
memset (op, '\0', sizeof (RAnalOp));
|
|
arminsn = arm_new();
|
|
arm_set_thumb (arminsn, R_FALSE);
|
|
arm_set_input_buffer (arminsn, data);
|
|
arm_set_pc (arminsn, addr);
|
|
op->addr = addr;
|
|
op->type = R_ANAL_OP_TYPE_UNK;
|
|
|
|
if (anal->big_endian) {
|
|
b = data = ndata;
|
|
ndata[0]=data[3];
|
|
ndata[1]=data[2];
|
|
ndata[2]=data[1];
|
|
ndata[3]=data[0];
|
|
}
|
|
#if 0
|
|
op->jump = op->fail = -1;
|
|
op->ptr = op->val = -1;
|
|
#endif
|
|
if (anal->bits==16) {
|
|
arm_free(arminsn);
|
|
return op_thumb (anal, op, addr, data, len);
|
|
}
|
|
op->size = 4;
|
|
#if 0
|
|
fprintf(stderr, "CODE %02x %02x %02x %02x\n",
|
|
codeA[0], codeA[1], codeA[2], codeA[3]);
|
|
#endif
|
|
op->cond = op_cond (data);
|
|
if (b[2]==0x8f && b[3]==0xe2) {
|
|
op->type = R_ANAL_OP_TYPE_ADD;
|
|
#define ROR(x,y) ((int)((x)>>(y)) | (((x)<<(32-(y)))))
|
|
op->ptr = addr + ROR (b[0], (b[1]&0xf)<<1) + 8;
|
|
} else
|
|
if (b[2]>=0x9c && b[2]<= 0x9f) { // load instruction
|
|
char ch = b[3]&0xf;
|
|
switch (ch) {
|
|
case 5:
|
|
if ((b[3]&0xf) == 5) {
|
|
op->ptr = 8+addr+b[0]+((b[1]&0xf)<<8);
|
|
op->refptr = R_TRUE;
|
|
}
|
|
case 4:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
op->type = R_ANAL_OP_TYPE_LOAD;
|
|
}
|
|
} else
|
|
// 0x000037b8 00:0000 0 800000ef svc 0x00000080
|
|
if (b[2]==0xa0 && b[3]==0xe1) {
|
|
int n = (b[0]<<16) + b[1];
|
|
op->type = R_ANAL_OP_TYPE_MOV;
|
|
switch (n) {
|
|
case 0:
|
|
case 0x0110: case 0x0220: case 0x0330: case 0x0440:
|
|
case 0x0550: case 0x0660: case 0x0770: case 0x0880:
|
|
case 0x0990: case 0x0aa0: case 0x0bb0: case 0x0cc0:
|
|
op->type = R_ANAL_OP_TYPE_NOP;
|
|
break;
|
|
}
|
|
} else
|
|
if (b[3]==0xef) {
|
|
op->type = R_ANAL_OP_TYPE_SWI;
|
|
op->val = (b[0] | (b[1]<<8) | (b[2]<<2));
|
|
} else
|
|
#if 0
|
|
0x00000000 a4a09fa4 ldrge sl, [pc], 0xa4
|
|
0x00000000 a4a09fa5 ldrge sl, [pc, 0xa4]
|
|
0x00000000 a4a09fa6 ldrge sl, [pc], r4, lsr 1
|
|
0x00000000 a4a09fa7 ldrge sl, [pc, r4, lsr 1]
|
|
0x00000000 a4a09fe8 ldm pc, {r2, r5, r7, sp, pc}; <UNPREDICT
|
|
#endif
|
|
if ((b[3]&0xf)==5) { // [reg,0xa4]
|
|
if ((b[1]&0xf0) == 0xf0) {
|
|
//ldr pc, [pc, #1] ;
|
|
op->type = R_ANAL_OP_TYPE_UJMP;
|
|
op->type = R_ANAL_OP_TYPE_RET; // FAKE FOR FUN
|
|
//op->stackop = R_ANAL_STACK_SET;
|
|
op->jump = 1234;
|
|
//op->ptr = 4+addr+b[0]; // sure? :)
|
|
//op->ptrptr = R_TRUE;
|
|
}
|
|
} else
|
|
//eprintf("0x%08x\n", code[i] & ARM_DTX_LOAD);
|
|
// 0x0001B4D8, 1eff2fe1 bx lr
|
|
if (b[3]==0xe2 && b[2]==0x8d && b[1]==0xd0) {
|
|
// ADD SP, SP, ...
|
|
op->type = R_ANAL_OP_TYPE_ADD;
|
|
op->stackop = R_ANAL_STACK_INC;
|
|
op->val = -b[0];
|
|
} else
|
|
if (b[3]==0xe2 && b[2]==0x4d && b[1]==0xd0) {
|
|
// SUB SP, SP, ..
|
|
op->type = R_ANAL_OP_TYPE_SUB;
|
|
op->stackop = R_ANAL_STACK_INC;
|
|
op->val = b[0];
|
|
} else
|
|
if (b[3]==0xe2 && b[2]==0x4c && b[1]==0xb0) {
|
|
// SUB SP, FP, ..
|
|
op->type = R_ANAL_OP_TYPE_SUB;
|
|
op->stackop = R_ANAL_STACK_INC;
|
|
op->val = -b[0];
|
|
} else
|
|
if (b[3]==0xe2 && b[2]==0x4b && b[1]==0xd0) {
|
|
// SUB SP, IP, ..
|
|
op->type = R_ANAL_OP_TYPE_SUB;
|
|
op->stackop = R_ANAL_STACK_INC;
|
|
op->val = -b[0];
|
|
} else
|
|
if ( (code[i] == 0x1eff2fe1) ||(code[i] == 0xe12fff1e)) { // bx lr
|
|
op->type = R_ANAL_OP_TYPE_RET;
|
|
op->eob = 1;
|
|
} else
|
|
if ((code[i] & ARM_DTX_LOAD)) { //IS_LOAD(code[i])) {
|
|
ut32 ptr = 0;
|
|
op->type = R_ANAL_OP_TYPE_MOV;
|
|
if (b[2]==0x1b) {
|
|
/* XXX pretty incomplete */
|
|
op->stackop = R_ANAL_STACK_GET;
|
|
op->ptr = b[0];
|
|
//var_add_access(addr, -b[0], 1, 0); // TODO: set/get (the last 0)
|
|
} else {
|
|
//ut32 oaddr = addr+8+b[0];
|
|
//XXX TODO ret = radare_read_at(oaddr, (ut8*)&ptr, 4);
|
|
if (anal->bits == 32) {
|
|
b = (ut8*)&ptr;
|
|
op->ptr = b[0] + (b[1]<<8) + (b[2]<<16) + (b[3]<<24);
|
|
//XXX data_xrefs_add(oaddr, op->ptr, 1);
|
|
//TODO change data type to pointer
|
|
} else op->ptr = 0;
|
|
}
|
|
}
|
|
|
|
if (IS_LOAD (code[i])) {
|
|
op->type = R_ANAL_OP_TYPE_LOAD;
|
|
}
|
|
if (
|
|
(( ((code[i]&0xff)>=0x10 && (code[i]&0xff)<0x20)
|
|
) && ((code[i]&0xffffff00) == 0xe12fff00))
|
|
||
|
|
IS_EXITPOINT (code[i])) {
|
|
//if (IS_EXITPOINT (code[i])) {
|
|
b=data;
|
|
branch_dst_addr = disarm_branch_offset (addr, b[0] | (b[1]<<8) | (b[2]<<16)); //code[i]&0x00FFFFFF);
|
|
op->ptr = 0;
|
|
if (
|
|
( ((code[i]&0xff)>=0x10 && (code[i]&0xff)<0x20)
|
|
) && ((code[i]&0xffffff00) == 0xe12fff00)
|
|
) {
|
|
op->type = R_ANAL_OP_TYPE_UJMP;
|
|
op->eob = 1;
|
|
} else
|
|
if (IS_BRANCHL (code[i])) {
|
|
if (IS_BRANCH (code[i])) {
|
|
op->type = R_ANAL_OP_TYPE_CALL;
|
|
op->jump = branch_dst_addr;
|
|
op->fail = addr + 4 ;
|
|
op->eob = 1;
|
|
} else {
|
|
op->type = R_ANAL_OP_TYPE_RET;
|
|
op->eob = 1;
|
|
}
|
|
} else if (IS_BRANCH (code[i])) {
|
|
if (IS_CONDAL (code[i])) {
|
|
op->type = R_ANAL_OP_TYPE_JMP;
|
|
op->jump = branch_dst_addr;
|
|
op->fail = UT64_MAX;
|
|
op->eob = 1;
|
|
} else {
|
|
op->type = R_ANAL_OP_TYPE_CJMP;
|
|
op->jump = branch_dst_addr;
|
|
op->fail = addr + 4;
|
|
op->eob = 1;
|
|
}
|
|
} else {
|
|
//unknown jump o return
|
|
//op->type = R_ANAL_OP_TYPE_UJMP;
|
|
//op->type = R_ANAL_OP_TYPE_NOP;
|
|
// op->eob = 1;
|
|
op->eob = 0;
|
|
}
|
|
}
|
|
//op->jump = arminsn->jmp;
|
|
//op->fail = arminsn->fail;
|
|
arm_free(arminsn);
|
|
return op->size;
|
|
}
|
|
|
|
|
|
static ut64 getaddr (ut64 addr, const ut8 *d) {
|
|
if (d[2]>>7) {
|
|
st32 n = (d[0] + (d[1]<<8) + (d[2]<<16) + (0xff<<24));
|
|
n = -n;
|
|
return addr - (n*4);
|
|
}
|
|
return addr + (4*(d[0] + (d[1]<<8) + (d[2]<<16)));
|
|
}
|
|
|
|
static int arm_op64(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *d, int len) {
|
|
memset (op, 0, sizeof (RAnalOp));
|
|
if (d[3]==0) return -1; // invalid
|
|
op->size = 4;
|
|
op->type = R_ANAL_OP_TYPE_NULL;
|
|
if (d[0]==0xc0 && d[3]==0xd6) {
|
|
// defaults to x30 reg. but can be different
|
|
op->type = R_ANAL_OP_TYPE_RET;
|
|
}
|
|
switch (d[3]) {
|
|
case 0x71:
|
|
case 0xeb:
|
|
op->type = R_ANAL_OP_TYPE_CMP;
|
|
break;
|
|
case 0xb8:
|
|
case 0xb9:
|
|
case 0xf8:
|
|
case 0xa9: // ldp/stp
|
|
case 0xf9: // ldr/str
|
|
op->type = R_ANAL_OP_TYPE_LOAD;
|
|
break;
|
|
case 0x91: // mov
|
|
case 0x52: // mov
|
|
case 0x94: // bl A
|
|
case 0x97: // bl A
|
|
op->type = R_ANAL_OP_TYPE_CALL;
|
|
op->jump = getaddr (addr, d);
|
|
op->fail = addr+4;
|
|
break;
|
|
case 0x54: // beq A
|
|
op->type = R_ANAL_OP_TYPE_CJMP;
|
|
op->jump = addr + (4*((d[0]>>4) | (d[1]<<8) | (d[2]<<16)));
|
|
op->fail = addr+4;
|
|
break;
|
|
case 0x17: // b A
|
|
case 0x14: // b A
|
|
op->type = R_ANAL_OP_TYPE_JMP;
|
|
op->jump = getaddr (addr, d);
|
|
op->fail = addr+4;
|
|
break;
|
|
}
|
|
return op->size;
|
|
}
|
|
|
|
static int arm_op(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int len) {
|
|
if (anal->bits==64)
|
|
return arm_op64(anal, op, addr, data, len);
|
|
return arm_op32(anal, op, addr, data, len);
|
|
}
|
|
|
|
static int set_reg_profile(RAnal *anal) {
|
|
// TODO: support 64bit profile
|
|
/* XXX Dupped Profiles */
|
|
return r_reg_set_profile_string (anal->reg,
|
|
"=pc r15\n"
|
|
"=sp r14\n" // XXX
|
|
"=bp r14\n" // XXX
|
|
"=a0 r0\n"
|
|
"=a1 r1\n"
|
|
"=a2 r2\n"
|
|
"=a3 r3\n"
|
|
"gpr lr .32 56 0\n" // r14
|
|
"gpr pc .32 60 0\n" // r15
|
|
|
|
"gpr r0 .32 0 0\n"
|
|
"gpr r1 .32 4 0\n"
|
|
"gpr r2 .32 8 0\n"
|
|
"gpr r3 .32 12 0\n"
|
|
"gpr r4 .32 16 0\n"
|
|
"gpr r5 .32 20 0\n"
|
|
"gpr r6 .32 24 0\n"
|
|
"gpr r7 .32 28 0\n"
|
|
"gpr r8 .32 32 0\n"
|
|
"gpr r9 .32 36 0\n"
|
|
"gpr r10 .32 40 0\n"
|
|
"gpr r11 .32 44 0\n"
|
|
"gpr r12 .32 48 0\n"
|
|
"gpr r13 .32 52 0\n"
|
|
"gpr r14 .32 56 0\n"
|
|
"gpr r15 .32 60 0\n"
|
|
"gpr r16 .32 64 0\n"
|
|
"gpr r17 .32 68 0\n");
|
|
}
|
|
|
|
struct r_anal_plugin_t r_anal_plugin_arm = {
|
|
.name = "arm",
|
|
.arch = R_SYS_ARCH_ARM,
|
|
.license = "LGPL3",
|
|
.bits = 32 | 64,
|
|
.desc = "ARM code analysis plugin",
|
|
.init = NULL,
|
|
.fini = NULL,
|
|
.op = &arm_op,
|
|
.set_reg_profile = set_reg_profile,
|
|
.fingerprint_bb = NULL,
|
|
.fingerprint_fcn = NULL,
|
|
.diff_bb = NULL,
|
|
.diff_fcn = NULL,
|
|
.diff_eval = NULL
|
|
};
|
|
|
|
#ifndef CORELIB
|
|
struct r_lib_struct_t radare_plugin = {
|
|
.type = R_LIB_TYPE_ANAL,
|
|
.data = &r_anal_plugin_arm
|
|
};
|
|
#endif
|