Files
2021-10-31 21:20:46 +02:00

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;
}