578 lines
15 KiB
C++
578 lines
15 KiB
C++
/*
|
|
* Interactive disassembler (IDA)
|
|
* Copyright (c) 1990-98 by Ilfak Guilfanov.
|
|
* E-mail: ig@datarescue.com
|
|
* JVM module.
|
|
* Copyright (c) 1995-2006 by Iouri Kharon.
|
|
* E-mail: yjh@styx.cabel.net
|
|
*
|
|
* ALL RIGHTS RESERVED.
|
|
*
|
|
*/
|
|
|
|
#include "java.hpp"
|
|
|
|
//----------------------------------------------------------------------
|
|
int java_t::LoadIndex(insn_t &insn)
|
|
{
|
|
ushort top;
|
|
|
|
insn.Op1.type = o_mem;
|
|
// insn.Op1.ref = 0;
|
|
insn.Op1.offb = char(insn.size);
|
|
top = insn.wid ? insn.get_next_word() : insn.get_next_byte();
|
|
insn.Op1.addr = top;
|
|
if ( ((insn.Op1.dtype == dt_qword || insn.Op1.dtype == dt_double) && !++top)
|
|
|| top >= curSeg.DataSize )
|
|
{
|
|
if ( !debugmode )
|
|
return 0;
|
|
++insn.Op1.ref;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void copy_const_to_opnd(op_t &x, const const_desc_t &co)
|
|
{
|
|
x.addr = co.value2;
|
|
x.value = co.value;
|
|
#ifdef __EA64__
|
|
// in 64-bit version of IDA the 'value' field is 64bit, so copy the value
|
|
// there
|
|
x.value = make_ulonglong((uint32)x.value, (uint32)x.addr);
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int java_t::ConstLoad(insn_t &insn, CIC_param ctype)
|
|
{
|
|
const_desc_t cntopis;
|
|
|
|
insn.Op1.type = o_cpool;
|
|
// insn.Op1.ref = 0;
|
|
|
|
if ( !insn.Op1.cp_ind )
|
|
goto dmpchk; // NULL Ptr
|
|
|
|
if ( !LoadOpis(lm_normal, insn.Op1.cp_ind, 0, &cntopis) )
|
|
goto dmpchk;
|
|
|
|
CASSERT(offsetof(const_desc_t,flag) == (offsetof(const_desc_t,type) + sizeof(uchar) )
|
|
&& (sizeof(cntopis.type) == sizeof(uchar))
|
|
&& (sizeof(cntopis.flag) == sizeof(uchar))
|
|
&& (sizeof(insn.Op1.cp_type) >= (2*sizeof(uchar)))
|
|
&& (sizeof(ushort) == sizeof(insn.Op1.cp_type)));
|
|
insn.Op1.cp_type = *((ushort *)&cntopis.type);
|
|
|
|
switch ( ctype )
|
|
{
|
|
case C_Class:
|
|
if ( cntopis.type != CONSTANT_Class )
|
|
break;
|
|
// no break
|
|
case C_4byte: // ldc/ldcw
|
|
switch ( cntopis.type )
|
|
{
|
|
case CONSTANT_Class:
|
|
if ( !(cntopis.flag & HAS_CLSNAME) )
|
|
goto wrnret;
|
|
insn.Op1.addr = 0x10001ul * (ushort)fmt_fullname;
|
|
loadref1:
|
|
insn.xtrn_ip = cntopis.ref_ip;
|
|
// no break
|
|
case CONSTANT_Integer:
|
|
case CONSTANT_Float:
|
|
case CONSTANT_String:
|
|
insn.Op1.value = cntopis.value; // for string index to Utf8
|
|
return 1; // or TWO index for other
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case C_8byte:
|
|
if ( cntopis.type == CONSTANT_Long || cntopis.type == CONSTANT_Double )
|
|
goto load2;
|
|
break;
|
|
|
|
case C_Field:
|
|
if ( cntopis.type != CONSTANT_Fieldref )
|
|
break;
|
|
if ( (cntopis.flag & NORM_FIELD) != NORM_FIELD )
|
|
goto wrnret;
|
|
loadref2:
|
|
insn.xtrn_ip = cntopis.ref_ip;
|
|
load2:
|
|
copy_const_to_opnd(insn.Op1, cntopis); // for string index to Utf8
|
|
return 1;
|
|
|
|
case C_Interface:
|
|
if ( cntopis.type == CONSTANT_InterfaceMethodref )
|
|
goto methodchk;
|
|
break;
|
|
case C_Method:
|
|
if ( cntopis.type != CONSTANT_Methodref )
|
|
break;
|
|
methodchk:
|
|
if ( (cntopis.flag & NORM_METOD) == NORM_METOD )
|
|
goto loadref2; // load 3 ind. & xtrn_ref
|
|
goto wrnret;
|
|
|
|
case C_CallSite:
|
|
if ( cntopis.type != CONSTANT_InvokeDynamic )
|
|
break;
|
|
goto wrnret;
|
|
|
|
case C_Type:
|
|
if ( cntopis.type != CONSTANT_Class )
|
|
break;
|
|
if ( !(cntopis.flag & HAS_TYPEDSCR) )
|
|
goto wrnret;
|
|
insn.Op1.addr = ((uint32)fmt_FieldDescriptor << 16) | (ushort)fmt_ClassName;
|
|
goto loadref1; // load 1 ind.
|
|
|
|
case C_TypeName:
|
|
if ( cntopis.type != CONSTANT_Class )
|
|
break;
|
|
if ( !(cntopis.flag & (HAS_TYPEDSCR | HAS_CLSNAME)) )
|
|
goto wrnret;
|
|
insn.Op1.addr = ((uint32)fmt_ClassName_or_Array << 16)
|
|
| (ushort)((cntopis.flag & HAS_CLSNAME)
|
|
? fmt_fullname
|
|
: fmt_ClassName);
|
|
goto loadref1; // load 1 ind.
|
|
|
|
default:
|
|
warning("Illegal CIC call (%x)", ctype);
|
|
return 0;
|
|
}
|
|
dmpchk:
|
|
if ( !debugmode )
|
|
return 0;
|
|
++insn.Op1.ref;
|
|
wrnret:
|
|
++insn.Op1.ref;
|
|
insn.Op1.addr_shorts.low = insn.Op1.cp_ind; // for dmp out
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
int java_t::ana(insn_t *_insn)
|
|
{
|
|
insn_t &insn = *_insn;
|
|
|
|
CIC_param ctype;
|
|
segment_t *s = getMySeg(insn.ea); // also set curSeg
|
|
|
|
if ( s->type != SEG_CODE || insn.ip >= curSeg.CodeSize )
|
|
{
|
|
warning("Can't decode non-code fragment!");
|
|
return 0;
|
|
}
|
|
|
|
insn.Op1.dtype = dt_void;
|
|
insn.wid = insn.swit = 0;
|
|
insn.Op1.ref = 0;
|
|
|
|
insn.itype = insn.get_next_byte();
|
|
if ( insn.itype == j_wide )
|
|
{
|
|
insn.itype = insn.get_next_byte();
|
|
if ( insn.itype == j_iinc
|
|
|| (insn.itype >= j_iload && insn.itype <= j_aload)
|
|
|| (insn.itype >= j_istore && insn.itype <= j_astore)
|
|
|| insn.itype == j_ret )
|
|
{
|
|
insn.wid = 1; // _w
|
|
}
|
|
else
|
|
{
|
|
if ( !debugmode )
|
|
return 0;
|
|
insn.size = 1;
|
|
insn.itype = j_wide;
|
|
}
|
|
}
|
|
|
|
if ( insn.itype >= j_lastnorm )
|
|
{
|
|
if ( !debugmode )
|
|
return 0;
|
|
if ( insn.itype < j_quick_last )
|
|
{
|
|
static const uchar redefcmd[j_quick_last - j_lastnorm] =
|
|
{
|
|
j_ldc, // j_ldc_quick
|
|
j_ldcw, // j_ldcw_quick
|
|
j_ldc2w, // j_ldc2w_quick
|
|
j_getfield, // j_getfield_quick
|
|
j_putfield, // j_putfield_quick
|
|
j_getfield, // j_getfield2_quick
|
|
j_putfield, // j_putfield2_quick
|
|
j_getstatic, // j_getstatic_quick
|
|
j_putstatic, // j_putstatic_quick
|
|
j_getstatic, // j_getstatic2_quick
|
|
j_putstatic, // j_putstatic2_quick
|
|
j_invokevirtual, // j_invokevirtual_quick
|
|
j_invokespecial, // j_invokenonvirtual_quick
|
|
j_a_invokesuper, // j_invokesuper_quick
|
|
j_invokestatic, // j_invokestatic_quick
|
|
j_invokeinterface, // j_invokeinterface_quick
|
|
j_a_invokevirtualobject, // j_invokevirtualobject_quick
|
|
j_a_invokeignored, // j_invokeignored_quick
|
|
j_new, // j_new_quick
|
|
j_anewarray, // j_anewarray_quick
|
|
j_multianewarray, // j_multianewarray_quick
|
|
j_checkcast, // j_checkcast_quick
|
|
j_instanceof, // j_instanceof_quick
|
|
j_invokevirtual, // j_invokevirtual_quick_w
|
|
j_getfield, // j_getfield_quick_w
|
|
j_putfield // j_putfield_quick_w
|
|
};
|
|
|
|
insn.wid = 2; // _quick;
|
|
switch ( insn.itype )
|
|
{
|
|
case j_getstatic2_quick:
|
|
case j_putstatic2_quick:
|
|
case j_getfield2_quick:
|
|
case j_putfield2_quick:
|
|
insn.wid = 3; // 2_quick
|
|
break;
|
|
case j_invokevirtual_quick_w:
|
|
case j_getfield_quick_w:
|
|
case j_putfield_quick_w:
|
|
insn.wid = 4; // _quick_w
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
insn.itype = redefcmd[insn.itype - j_lastnorm];
|
|
}
|
|
else if ( insn.itype < j_software )
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
insn.itype -= (j_software - j_a_software);
|
|
}
|
|
}
|
|
//---
|
|
switch ( insn.itype )
|
|
{
|
|
default:
|
|
{
|
|
uint refs, ref2f;
|
|
|
|
if ( insn.itype >= j_iload_0 && insn.itype <= j_aload_3 )
|
|
{
|
|
refs = (insn.itype - j_iload_0) % 4;
|
|
ref2f = (insn.itype - j_iload_0) / 4;
|
|
ref2f = ref2f == ((j_lload_0 - j_iload_0) / 4)
|
|
|| ref2f == ((j_dload_0 - j_iload_0) / 4);
|
|
goto refer;
|
|
}
|
|
if ( insn.itype >= j_istore_0 && insn.itype <= j_astore_3 )
|
|
{
|
|
refs = (insn.itype - j_istore_0) % 4;
|
|
ref2f = (insn.itype - j_istore_0) / 4;
|
|
ref2f = ref2f == ((j_lstore_0 - j_istore_0) / 4)
|
|
|| ref2f == ((j_dstore_0 - j_istore_0) / 4);
|
|
refer:
|
|
insn.Op1.addr = curSeg.DataBase + (ushort)refs;
|
|
insn.Op1.ref = (uchar)(ref2f + 1);
|
|
if ( (ushort)(refs + ref2f) >= curSeg.DataSize )
|
|
insn.Op1.ref |= 0x80;
|
|
break;
|
|
}
|
|
} // end refs/refx
|
|
if ( insn.itype < j_ifeq || insn.itype > j_jsr )
|
|
break;
|
|
case j_ifnull:
|
|
case j_ifnonnull:
|
|
insn.Op1.addr = (short)insn.get_next_word();
|
|
b_near:
|
|
insn.Op1.type = o_near;
|
|
insn.Op1.offb = 1;
|
|
insn.Op1.addr += insn.ip;
|
|
if ( insn.Op1.addr >= curSeg.CodeSize )
|
|
goto set_bad_ref;
|
|
break;
|
|
|
|
case j_goto_w:
|
|
case j_jsr_w:
|
|
insn.Op1.addr = insn.get_next_dword();
|
|
goto b_near;
|
|
|
|
case j_bipush:
|
|
insn.Op1.dtype = dt_byte;
|
|
insn.Op1.value = (char)insn.get_next_byte();
|
|
goto setdat;
|
|
case j_sipush:
|
|
insn.Op1.dtype = dt_word;
|
|
insn.Op1.value = (short)insn.get_next_word();
|
|
setdat:
|
|
insn.Op1.type = o_imm;
|
|
insn.Op1.offb = 1;
|
|
break;
|
|
|
|
case j_ldc:
|
|
insn.Op1.cp_ind = insn.get_next_byte();
|
|
ctype = C_4byte;
|
|
goto constchk;
|
|
case j_ldcw:
|
|
ctype = C_4byte;
|
|
goto const2w;
|
|
case j_ldc2w:
|
|
ctype = C_8byte;
|
|
const2w:
|
|
insn.Op1.cp_ind = insn.get_next_word();
|
|
constchk:
|
|
if ( !ConstLoad(insn, ctype) )
|
|
return 0;
|
|
break;
|
|
|
|
case j_getstatic:
|
|
case j_putstatic:
|
|
case j_getfield:
|
|
case j_putfield:
|
|
if ( insn.wid > 1 ) // _quick form
|
|
{
|
|
insn.Op1.type = o_imm;
|
|
insn.Op1.ref = 2; // #data
|
|
insn.Op1.offb = 1;
|
|
if ( insn.wid == 4 )
|
|
{
|
|
insn.Op1.dtype = dt_word;
|
|
insn.Op1.value = insn.get_next_word();
|
|
}
|
|
else
|
|
{
|
|
insn.Op1.dtype = dt_byte;
|
|
insn.Op1.value = insn.get_next_byte();
|
|
++insn.size; // SKIP
|
|
}
|
|
break;
|
|
}
|
|
ctype = C_Field;
|
|
goto const2w;
|
|
|
|
case j_new:
|
|
ctype = C_Class;
|
|
goto const2w;
|
|
|
|
case j_anewarray:
|
|
//\\ ?/
|
|
case j_checkcast:
|
|
case j_instanceof:
|
|
ctype = C_TypeName;
|
|
goto const2w;
|
|
|
|
case j_a_invokesuper:
|
|
case j_a_invokeignored:
|
|
goto fictarg;
|
|
case j_invokevirtual:
|
|
case j_a_invokevirtualobject:
|
|
insn.Op2.dtype = dt_void;
|
|
if ( insn.wid > 1 )
|
|
{
|
|
if ( insn.wid == 4 )
|
|
{
|
|
fictarg:
|
|
insn.Op1.value = insn.get_next_word(); //???
|
|
insn.Op1.dtype = dt_word;
|
|
}
|
|
else
|
|
{
|
|
insn.Op2.type = o_imm;
|
|
insn.Op1.ref = 2; // #data
|
|
insn.Op1.dtype = insn.Op2.dtype = dt_byte;
|
|
insn.Op1.value = insn.get_next_byte();
|
|
insn.Op2.offb = 2;
|
|
insn.Op2.value = insn.get_next_byte();
|
|
}
|
|
insn.Op1.offb = 1;
|
|
insn.Op1.type = o_imm;
|
|
insn.Op1.ref = 2; // #data
|
|
break;
|
|
}
|
|
// fallthrough
|
|
case j_invokespecial:
|
|
case j_invokestatic:
|
|
ctype = C_Method;
|
|
goto const2w;
|
|
case j_invokedynamic:
|
|
ctype = C_CallSite;
|
|
insn.Op1.cp_ind = insn.get_next_word();
|
|
if ( !ConstLoad(insn, ctype) )
|
|
return 0;
|
|
insn.get_next_word(); // eat two mandatory 0's
|
|
insn.Op1.ref = 0;
|
|
break;
|
|
case j_invokeinterface:
|
|
ctype = C_Interface;
|
|
insn.Op1.cp_ind = insn.get_next_word();
|
|
insn.Op2.type = o_imm;
|
|
insn.Op2.ref = 1; // not descriptor
|
|
insn.Op2.dtype = dt_byte;
|
|
insn.Op2.value = insn.get_next_byte();
|
|
if ( insn.wid > 1 )
|
|
{
|
|
insn.Op3.type = o_imm;
|
|
insn.Op3.ref = 2; // #data
|
|
insn.Op3.value = insn.get_next_byte();
|
|
insn.Op3.offb = 4;
|
|
insn.Op3.dtype = dt_byte;
|
|
}
|
|
else
|
|
{
|
|
++insn.size; // reserved
|
|
insn.Op3.dtype = dt_void;
|
|
}
|
|
goto constchk;
|
|
|
|
case j_multianewarray:
|
|
insn.Op1.cp_ind = insn.get_next_word();
|
|
insn.Op2.type = o_imm;
|
|
insn.Op2.ref = 1; // not descriptor
|
|
insn.Op2.dtype = dt_byte;
|
|
insn.Op2.value = insn.get_next_byte();
|
|
if ( insn.Op2.value == 0 && !debugmode )
|
|
return 0;
|
|
ctype = C_Type;
|
|
goto constchk;
|
|
|
|
case j_iinc:
|
|
case j_iload:
|
|
case j_istore:
|
|
insn.Op1.dtype = dt_dword;
|
|
goto memref;
|
|
case j_lload:
|
|
case j_lstore:
|
|
insn.Op1.dtype = dt_qword;
|
|
goto memref;
|
|
case j_fload:
|
|
case j_fstore:
|
|
insn.Op1.dtype = dt_float;
|
|
goto memref;
|
|
case j_dload:
|
|
case j_dstore:
|
|
insn.Op1.dtype = dt_double;
|
|
goto memref;
|
|
case j_aload:
|
|
case j_astore:
|
|
insn.Op1.dtype = dt_string;
|
|
goto memref;
|
|
case j_ret:
|
|
insn.Op1.dtype = dt_code;
|
|
memref:
|
|
if ( !LoadIndex(insn) )
|
|
return 0;
|
|
if ( insn.itype == j_iinc )
|
|
{
|
|
insn.Op2.type = o_imm;
|
|
insn.Op2.ref = 0;
|
|
insn.Op2.offb = (uchar)insn.size;
|
|
if ( insn.wid )
|
|
{
|
|
insn.Op2.dtype = dt_word;
|
|
insn.Op2.value = (short)insn.get_next_word();
|
|
}
|
|
else
|
|
{
|
|
insn.Op2.dtype = dt_byte;
|
|
insn.Op2.value = (char)insn.get_next_byte();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case j_tableswitch:
|
|
case j_lookupswitch:
|
|
{
|
|
int32 count;
|
|
uint32 top;
|
|
|
|
insn.swit = 1;
|
|
for ( top = (4 - uint32((insn.ip + insn.size) % 4)) & 3; top; top-- )
|
|
{
|
|
if ( insn.get_next_byte() )
|
|
{
|
|
if ( !debugmode )
|
|
return 0;
|
|
insn.swit |= 0100;
|
|
}
|
|
}
|
|
insn.Op3.type = o_near;
|
|
insn.Op3.offb = (uchar)insn.size;
|
|
insn.Op3.addr = insn.get_next_dword();
|
|
insn.Op3.addr += insn.ip;
|
|
insn.Op3.ref = 0;
|
|
|
|
if ( insn.Op3.addr >= curSeg.CodeSize )
|
|
{
|
|
if ( !debugmode )
|
|
return 0;
|
|
++insn.Op3.ref;
|
|
}
|
|
|
|
insn.swit |= 2; // start out arguments
|
|
|
|
count = insn.get_next_dword();
|
|
if ( insn.itype == j_tableswitch )
|
|
{
|
|
insn.Op1.type = o_imm;
|
|
insn.Op1.dtype = dt_dword;
|
|
insn.Op1.value = count; // minimal value
|
|
insn.Op2.ref = 0;
|
|
insn.Op2.type = o_imm;
|
|
insn.Op2.dtype = dt_dword;
|
|
count = (uint32(insn.Op2.value = insn.get_next_dword()) - count + 1);
|
|
}
|
|
insn.Op3.value = count;
|
|
insn.Op2.addr = insn.ip + insn.size;
|
|
top = uint32(curSeg.CodeSize - insn.ip);
|
|
while ( count-- )
|
|
{
|
|
if ( insn.itype == j_lookupswitch )
|
|
insn.get_next_dword(); // skip pairs;
|
|
if ( (insn.ip + insn.get_next_dword()) >= curSeg.CodeSize )
|
|
{
|
|
if ( !debugmode )
|
|
return 0;
|
|
insn.swit |= 0200;
|
|
}
|
|
if ( (uint32)insn.size >= top )
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case j_newarray:
|
|
insn.Op1.type = o_array; // type!
|
|
insn.Op1.offb = 1;
|
|
insn.Op1.cp_type = insn.get_next_byte();
|
|
if ( insn.Op1.cp_type < T_BOOLEAN || (uchar)insn.Op1.cp_type > T_LONG )
|
|
{
|
|
set_bad_ref:
|
|
if ( !debugmode )
|
|
return 0;
|
|
++insn.Op1.ref;
|
|
}
|
|
break;
|
|
} // switch ( insn.itype )
|
|
|
|
return insn.size;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool idaapi can_have_type(const op_t &x)
|
|
{
|
|
if ( x.type == o_cpool )
|
|
return (uchar)x.cp_type == CONSTANT_Integer
|
|
|| (uchar)x.cp_type == CONSTANT_Long;
|
|
return x.type == o_imm;
|
|
}
|