/* * 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" #include #include #include #include "npooluti.hpp" #include "oututil.hpp" #define _CUR_IDP_VER IDP_JDK16 static const char constant_pool[] = "$ Constant Pool ~"; //------------------------------------------------------------------------- nodeidx_t bootstrap_methods_get_node(bool assert, bool can_create) { // if ( curClass.bmNode != 0 ) // return curClass.bmNode; netnode n("$ BootstrapMethods ~"); if ( can_create && !netnode_exist(n) ) n.create("$ BootstrapMethods ~"); if ( assert ) QASSERT(10333, netnode_exist(n)); return n; } //------------------------------------------------------------------------- nodeidx_t bootstrap_methods_get_count() { return netnode(bootstrap_methods_get_node()).altval(0); } //------------------------------------------------------------------------- void bootstrap_methods_set_count(nodeidx_t cnt) { netnode(bootstrap_methods_get_node()).altset(0, cnt); } //------------------------------------------------------------------------- bool bootstrap_methods_get_method( bootstrap_method_def_t *out, nodeidx_t idx) { netnode n = bootstrap_methods_get_node(); bytevec_t blob; if ( n.getblob(&blob, idx, stag) < 2 ) return false; const uchar *p = blob.begin(); out->method_ref = *(ushort *) p; p += sizeof(ushort); const size_t nargs = size_t(blob.end() - p) / sizeof(ushort); out->args.resize(nargs); if ( nargs > 0 ) memcpy(out->args.begin(), p, nargs * sizeof(ushort)); return true; } //------------------------------------------------------------------------- static bool bootstrap_methods_set_method( const bootstrap_method_def_t &bmd, nodeidx_t idx) { netnode n = bootstrap_methods_get_node(); bytevec_t blob; blob.append(&bmd.method_ref, sizeof(bmd.method_ref)); if ( !bmd.args.empty() ) blob.append(bmd.args.begin(), bmd.args.size() * sizeof(ushort)); return n.setblob(blob.begin(), blob.size(), idx, stag); } //----------------------------------------------------------------------- void java_t::database_loaded(const char *file) { int i, v; ssize_t size; if ( file == NULL ) // load old file { if ( ConstantNode.create(constant_pool) ) goto BADIDB; i = sizeof(curClass); v = (int)ConstantNode.altval(CNA_VERSION); switch ( v ) { default: error("Very old database format. Cannot convert it"); case IDP_JDK15: error("Intermediate (developer) database format. Cannot convert it."); case IDP_JDK12: i -= sizeof(curClass) - offsetof(ClassInfo, MajVers); // no break case _CUR_IDP_VER: // IDP_JDK16 break; } if ( i != (int)ConstantNode.supval(CNS_CLASS, NULL, (size_t)-1) ) goto BADIDB; size = ConstantNode.supval(CNS_CLASS, &curClass, sizeof(curClass)); if ( size < (int32)offsetof(ClassInfo, MajVers) || !curClass.ClassNode ) goto BADIDB; if ( curClass.xtrnNode ) { if ( !curClass.xtrnCnt ) goto BADIDB; XtrnNode = curClass.xtrnNode; } else if ( curClass.xtrnCnt ) { goto BADIDB; } ClassNode = curClass.ClassNode; if ( v != IDP_JDK12 ) // current JDK format { if ( curClass.MajVers < JDK_MIN_MAJOR ) goto BADIDB; if ( curClass.MajVers > JDK_MAX_MAJOR ) goto BADIDB; if ( curClass.MajVers == JDK_MIN_MAJOR ) // JDK1.0/1.1 { if ( curClass.JDKsubver != (curClass.MinVers >= JDK_1_1_MINOR) ) goto BADIDB; } else { if ( curClass.MinVers ) goto BADIDB; if ( curClass.MajVers - (JDK_MIN_MAJOR-1) != curClass.JDKsubver ) goto BADIDB; } } make_NameChars(/*on_load=*/ false); // initialize and set enableExr_NameChar for upgrade if ( v != _CUR_IDP_VER ) { ResW_init(); // prepare RW-find v = upgrade_db_format(v, ConstantNode); if ( v == 0 ) { BADIDB: DESTROYED("database_loaded"); } QASSERT(10134, v == _CUR_IDP_VER); ConstantNode.supset(CNS_CLASS, &curClass, sizeof(curClass)); ConstantNode.altset(CNA_VERSION, _CUR_IDP_VER); } else { v = curClass.maxStrSz; if ( v != 0 ) tsPtr = (ushort*)myAlloc(sizeof(ushort)*(v+1)); v = curClass.maxSMsize; if ( v != 0 ) smBuf = (uchar*)myAlloc(v+1); v = curClass.maxAnnSz; if ( v != 0 ) annBuf = (uchar*)myAlloc(v+1); idpflags = (ushort)ConstantNode.altval(CNA_IDPFLAGS); user_limiter = (uchar)ConstantNode.altval(CNA_LIMITER); if ( !ResW_oldbase() ) goto BADIDB; } // disableExt_NameChar(); // set standart extension if ( curClass.extflg & XFL_C_DONE ) sm_node = smn_ok; if ( curClass.MajVers >= JDK_SMF_MAJOR_MIN ) SMF_mode = 1; } else { // new base if ( !ConstantNode ) INTERNAL("ConstantNode"); char str[MAXSPECSIZE]; char *ps = str; i = qstrlen((char *) file); if ( i >= sizeof(str) ) { ps = (char *) file + i - (sizeof(str) - 3); for ( i = sizeof(str) - 1 - 3; i; i-- ) if ( *--ps == '/' ) break; if ( i == 0 ) error("notify: illegal file name parameter"); file = ps; memcpy(str, "...", 3); ps = &str[3]; } memcpy(ps, file, i); if ( ps != str ) i += 3; ConstantNode.supset(CNS_SOURCE, str, i); user_limiter = inf_get_limiter(); ConstantNode.altset(CNA_LIMITER, user_limiter); } inf_set_limiter(0); } //------------------------------------------------------------------------- void java_t::dump_floating_constants( const char *problem, const char *what, const intvec_t &ks) { const size_t kss = ks.size(); if ( kss > 0 ) { static const char *const emc = "Number of %s CONSTANT_%s: %" FMT_Z "\n"; load_msg(emc, problem, what, kss); for ( size_t i = 0; i < kss; ++i ) { qstrvec_t lines; const_desc_t cd; ushort cid = ks[i]; if ( ConstantNode.supval(cid, &cd, sizeof(cd)) == sizeof(cd) ) { print_constant(&lines, cd, cid); if ( lines.empty() ) lines.push_back(""); } else { cd.type = 0; lines.push_back(""); } for ( size_t lidx = 0, lcnt = lines.size(); lidx < lcnt; ++lidx ) { char sfxbuf[MAXSTR]; if ( lcnt > 1 ) qsnprintf(sfxbuf, sizeof(sfxbuf), ".%" FMT_Z, lidx); else sfxbuf[0] = '\0'; load_msg(" #%" FMT_Z "%s, index=%d, type=%s: %s", i, sfxbuf, cid, constant_type_to_str(cd.type), lines[lidx].c_str()); } } } } //----------------------------------------------------------------------- bool java_t::LoadOpis(load_mode_t load_mode, ushort index, uchar _op, const_desc_t *p) { const uchar op = _op; #define LoadAnyString(lmod, index) LoadOpis(lmod, index, CONSTANT_Utf8, NULL) const_desc_t tmp; if ( p == NULL ) p = &tmp; if ( !index || index > curClass.maxCPindex || ConstantNode.supval(index, p, sizeof(*p)) != sizeof(*p) ) { return false; } if ( load_mode == lm_lenient || load_mode == lm_no_set_ref ) return true; if ( !p->is_referenced() ) { p->mark_referenced(); StoreOpis(index, *p); } CASSERT(MAX_CONSTANT_TYPE < 0x20); if ( op < 0x20 ) return op == 0 || op == p->type; #define LoadNamedClass(lmod, index, p) LoadOpis(lmod, index, uchar(-1), p) if ( op == uchar(-1) ) return p->type == CONSTANT_Class && (p->flag & HAS_CLSNAME); if ( p->type != CONSTANT_Utf8 ) return false; #define OP_TYPE_MASK 0x1F #define OP_CHECK_SHIFT 5 const uchar _type = op & OP_TYPE_MASK; const uchar _check = op >> OP_CHECK_SHIFT; switch ( _type ) { default: INTERNAL("LoadOpis"); case 0: case CONSTANT_Utf8: break; } { static const uchar chk[8] = { 0x00, // align #define CheckAnyDscr(lmod, index, p) LoadOpis(lmod, index, (1 << OP_CHECK_SHIFT), p) (HAS_TYPEDSCR | HAS_CALLDSCR), #define CheckFieldDscr(lmod, index, p) LoadOpis(lmod, index, (2 << OP_CHECK_SHIFT), p) #define LoadFieldDscr(lmod, index, p) LoadOpis(lmod, index, (2 << OP_CHECK_SHIFT) | CONSTANT_Utf8, p) HAS_TYPEDSCR, #define CheckFieldName(lmod, index, p) LoadOpis(lmod, index, (3 << OP_CHECK_SHIFT), p) #define LoadFieldName(lmod, index) LoadOpis(lmod, index, (3 << OP_CHECK_SHIFT) | CONSTANT_Utf8, NULL) HAS_FLDNAME, //#define Check...(lmod, index, p) LoadOpis(lmod, index, (4 << OP_CHECK_SHIFT), p) 0x00, #define CheckClass(lmod, index, p) LoadOpis(lmod, index, (5 << OP_CHECK_SHIFT), p) (HAS_TYPEDSCR | HAS_CLSNAME), #define CheckCallDscr(lmod, index, p) LoadOpis(lmod, index, (6 << OP_CHECK_SHIFT), p) #define LoadCallDscr(lmod, index) LoadOpis(lmod, index, (6 << OP_CHECK_SHIFT) | CONSTANT_Utf8, NULL) HAS_CALLDSCR, #define CheckClassName(lmod, index, p) LoadOpis(lmod, index, (7 << OP_CHECK_SHIFT), p) HAS_CLSNAME }; if ( p->flag & chk[_check] ) return true; } if ( _type != 0 ) return false; if ( load_mode == lm_need_cr ) msg("\n"); load_msg("Illegal reference type to Utf8#%u\n", index); return lm_need_cr >= 2; // true when load_constants_pool, false after } //----------------------------------------------------------------------- // for annotation bool java_t::isSingleClass(ushort val) { const_desc_t co; return LoadFieldDscr(lm_normal, val, &co) && (co._Sflags & (_OP_VALPOS | _OP_ONECLS)) == _OP_ONECLS; } //----------------------------------------------------------------------- uchar java_t::attribute_type_from_str(ushort index, attr_parent_kind_t apk, char str[MAX_ATTR_NMSZ]) { static const char *const name[] = { #define attr_LineNumberTable 0 // 0x000001: code "LineNumberTable", #define attr_LocalVariableTable 1 // 0x000002: code "LocalVariableTable", #define attr_LocalVariableTypeTable 2 // 0x000004: code "LocalVariableTypeTable", #define attr_StackMap 3 // 0x000008: code (J2ME CLDC) "StackMap", #define attr_StackMapTable 4 // 0x000010: code (>=JDK1.6) "StackMapTable", #define attr_CODE_TOP 5 #define attr_ConstantValue 5 // 0x000020: fld "ConstantValue", #define attr_FLD_TOP 6 #define attr_Code 6 // 0x000040: met "Code", #define attr_Exceptions 7 // 0x000080: met "Exceptions", #define attr_RuntimeVisibleParameterAnnotations 8 // 0x000100: met "RuntimeVisibleParameterAnnotations", #define attr_RuntimeInvisibleParameterAnnotations 9 // 0x000200: met "RuntimeInvisibleParameterAnnotations", #define attr_AnnotationDefault 10 // 0x000400: met "AnnotationDefault", #define attr_MET_TOP 11 #define attr_SourceFile 11 // 0x000800: file "SourceFile", #define attr_InnerClasses 12 // 0x001000: file "InnerClasses", #define attr_EnclosingMethod 13 // 0x002000: file "EnclosingMethod", #define attr_SourceDebugExtension 14 // 0x004000: file "SourceDebugExtension", #define attr_BootstrapMethods 15 // 0x008000: file "BootstrapMethods", #define attr_FILE_TOP 16 #define attr_Signature 16 // 0x010000: all !code "Signature", #define attr_Synthetic 17 // 0x020000: all !code "Synthetic", #define attr_Deprecated 18 // 0x040000: all !code "Deprecated", #define attr_RuntimeVisibleAnnotations 19 // 0x080000: all !code "RuntimeVisibleAnnotations", #define attr_RuntimeInvisibleAnnotations 20 // 0x100000: all !code "RuntimeInvisibleAnnotations", // next field for check pool ONLY (must be in last position) #define attr_LocalVariables 21 // 0x200000: obsolete "LocalVariables" #define attr_CHECK_MASK 0x3FFFFF }; #define attr_UNKNOWN 32 #define attr_TRUNCATED 33 #define attr_NONAME 34 //#if sizeof("RuntimeInvisibleParameterAnnotations") > MAX_ATTR_NMSZ //#error //#endif str[0] = '\0'; if ( !LoadFieldName(lm_normal, index) ) return attr_NONAME; if ( !fmtName(index, str, MAX_ATTR_NMSZ, fmt_UnqualifiedName) ) return attr_TRUNCATED; uchar i = 0, top = (uchar)(qnumber(name) - 1); switch ( apk ) { case attr_parent_kind_code: top = attr_CODE_TOP; break; case attr_parent_kind_field: i = attr_CODE_TOP; top = attr_FLD_TOP; break; case attr_parent_kind_method: i = attr_FLD_TOP; top = attr_MET_TOP; break; case attr_parent_kind_class_file: i = attr_MET_TOP; top = attr_FILE_TOP; break; default: // case ARQ_CHECK: ++top; break; } repeat: do { if ( !strcmp(name[i], str) ) return i; } while ( ++i < top ); if ( apk != attr_parent_kind_code && i < (uchar)(qnumber(name)-1) ) { i = attr_FILE_TOP; top = (uchar)(qnumber(name) - 1); goto repeat; } return attr_UNKNOWN; } //------------------------------------------------------------------------- static NORETURN void badref_loader_failure(ushort i, ushort k) { loader_failure("Bad reference (from %u to %u) in constant pool", i, k); } //----------------------------------------------------------------------- uint java_t::load_constants_pool(void) { ushort k; uint ui = 0; const_desc_t cd; // prepare jasmin reserved word checking ResW_init(); msg("\nLoading constant pool..."); FOR_EACH_CONSTANT_POOL_INDEX(i) { memset(&cd, 0, sizeof(cd)); cd.type = read1(); switch ( cd.type ) { case CONSTANT_Long: case CONSTANT_Double: cd.value2 = read4(); // fallthrough case CONSTANT_Integer: case CONSTANT_Float: cd.value = read4(); break; case CONSTANT_NameAndType: case CONSTANT_Fieldref: case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: k = read2(); if ( k == 0 || k > curClass.maxCPindex ) { badindex: loader_failure("Bad record in constant pool.\n" "Record %u have reference to %u\n" "(maxnum %u, file offset after the read is 0x%" FMT_64 "X)", i, k, curClass.maxCPindex, qftell(myFile)); } cd._class = k; // _subnam for name & type // fallthrough. Yes, we will let 'name_and_type_index' be read by the CONSTANT_String block below... case CONSTANT_Class: cd.ref_ip = 0; // fallthrough case CONSTANT_String: k = read2(); if ( k == 0 || k > curClass.maxCPindex ) goto badindex; cd._name = k; // _dscr for name & type break; case CONSTANT_Unicode: loader_failure("File contains CONSTANT_Unicode, which was removed from " "the standard in 1996, and is not supported by IDA"); case CONSTANT_Utf8: cd._name = (ushort)i; // for xtrnRef_dscr if ( LoadUtf8((ushort)i, &cd) ) parse_const_desc((ushort)i, &cd); break; case CONSTANT_MethodHandle: cd._mhr_kind = read1(); cd._mhr_index = read2(); break; case CONSTANT_MethodType: k = read2(); // descriptor_index if ( k == 0 || k > curClass.maxCPindex ) goto badindex; cd._mtd_index = k; break; case CONSTANT_InvokeDynamic: { cd._bmidx = read2(); // bootstrap_method_attr_index k = read2(); // name_and_type_index if ( k == 0 || k > curClass.maxCPindex ) goto badindex; cd._name = k; } break; default: loader_failure("Bad constant type 0x%x (%u)", cd.type, i); } // end switch StoreOpis(i, cd); if ( cd.type == CONSTANT_Long || cd.type == CONSTANT_Double ) { if ( curClass.maxCPindex == (ushort)i ) loader_failure("Premature end of constant pool"); ++i; } } // end for ResW_free(); // free mem - this set not needed later msg("checking..."); FOR_EACH_CONSTANT_POOL_INDEX(i) { const_desc_t cr; ConstantNode.supval(i, &cd, sizeof(cd)); switch ( cd.type ) { case CONSTANT_String: if ( !LoadAnyString(lm_need_cr, cd._name) ) badref_loader_failure(i, cd._name); continue; case CONSTANT_Long: case CONSTANT_Double: ++i; default: continue; case CONSTANT_NameAndType: if ( !CheckFieldName(lm_need_cr, cd._class, &cr) ) badref_loader_failure(i, cd._class); cd.flag |= ((cr.flag & HAS_FLDNAME) << SUB_SHIFT); if ( !CheckAnyDscr(lm_need_cr, cd._name, &cr) ) badref_loader_failure(i, cd._name); cd.flag |= ((cr.flag<> 16); goto found; } } XtrnNode.altset(j, (i << 16) | cd._name); ++curClass.xtrnCnt; cd._subnam = (ushort)i; found: StoreOpis(i, cd); } } // end for if ( loadMode & MLD_EXTREF ) XtrnNode.altdel(); // delete all msg("referencing..."); FOR_EACH_CONSTANT_POOL_INDEX(i) { const_desc_t cr; uint32 sav; ConstantNode.supval(i, &cd, sizeof(cd)); switch ( cd.type ) { case CONSTANT_Long: case CONSTANT_Double: ++i; default: continue; case CONSTANT_Class: continue; case CONSTANT_InterfaceMethodref: case CONSTANT_Fieldref: case CONSTANT_Methodref: if ( !LoadOpis(lm_need_cr, cd._class, CONSTANT_Class, &cr) ) badref_loader_failure(i, cd._class); //\\VALID NULL ??? go twos if any... (reorder cur ind to null) cd.flag |= (cr.flag & HAS_CLSNAME); k = cd._name; sav = errload; cd.ref_ip = cr._subnam; CheckClassName(lm_need_cr, cd._name = cr._name, NULL); //lint !e530 likely using an uninitialized value if ( !LoadOpis(lm_need_cr, k, CONSTANT_NameAndType, &cr) ) { badref_loader_failure(i, k); } cd._dscr = cr._name; cd._subnam = cr._class; cd.flag |=(cr.flag & (SUB_FLDNAME | SUB_TYPEDSCR | SUB_CALLDSCR)); if ( cd.type != CONSTANT_Fieldref ) CheckCallDscr(lm_need_cr, cd._dscr, NULL); else CheckFieldDscr(lm_need_cr, cd._dscr, NULL); if ( !LoadFieldName(lm_need_cr, cd._subnam) ) --sav; if ( (loadMode & MLD_EXTREF) && errload == sav ) { XtrnNode.altset(++ui, i, '0'); ++curClass.xtrnCnt; } else { cd.ref_ip = 0; } break; case CONSTANT_MethodHandle: if ( !LoadOpis(lm_need_cr, cd._mhr_index, 0, &cr) ) badref_loader_failure(i, cd._mhr_index); break; case CONSTANT_MethodType: if ( !LoadOpis(lm_need_cr, cd._mtd_index, CONSTANT_Utf8, &cr) ) badref_loader_failure(i, cd._mtd_index); break; case CONSTANT_InvokeDynamic: // We can resolve the name_and_type_index, right away but the // bootstrap_method_attr_index, will have to wait after the // BootstrapMethods array has been loaded (in load_attributes()) if ( !LoadOpis(lm_need_cr, cd._name, CONSTANT_NameAndType, &cr) ) badref_loader_failure(i, cd._mtd_index); break; } // end switch StoreOpis(i, cd); } // end for msg("complete\n"); // { // debug // intvec_t tmp; // FOR_EACH_CONSTANT_POOL_INDEX(i) // tmp.push_back(i); // dump_floating_constants( // "NO PROBLEM", // "NOTHING", // tmp); // } return ui; } //----------------------------------------------------------------------- void java_t::setPoolReference(void) { char str[MAXNAMELEN]; const_desc_t co; uint ic, ii, ui = 0; msg("Sorting external references..."); for ( uint i = 1; (ushort)i <= curClass.xtrnCnt; i++ ) { uint j = (uint)XtrnNode.altval(i, '0'); if ( j == 0 ) continue; show_addr(curClass.xtrnCnt - (ushort)i); ConstantNode.supval(j, &co, sizeof(co)); if ( co._class == curClass.This.Dscr ) { co.ref_ip = 0; StoreOpis(j, co); continue; } const_desc_t cr; ConstantNode.supval(ic = co.ref_ip, &cr, sizeof(cr)); xtrnSet(ic, &cr, ++ui, str, sizeof(str), true); xtrnSet(j, &co, ++ui, str, sizeof(str), false); deltry(ii = i + 1, ic, ui, co); for ( ; (ushort)ii <= curClass.xtrnCnt; ii++ ) { j = (uint)XtrnNode.altval(ii, '0'); if ( j == 0 ) continue; ConstantNode.supval(j, &cr, sizeof(cr)); if ( cr.ref_ip != (ushort)ic ) continue; xtrnSet(j, &cr, ++ui, str, sizeof(str), false); XtrnNode.altdel(ii, '0'); deltry(ii + 1, ic, ui, co); } } XtrnNode.altdel_all('0'); FOR_EACH_CONSTANT_POOL_INDEX(i) { ConstantNode.supval(i, &co, sizeof(co)); switch ( co.type ) { case CONSTANT_Long: case CONSTANT_Double: ++i; default: break; case CONSTANT_Class: if ( co._subnam == (ushort)i && (ushort)i != curClass.This.Dscr && !co.ref_ip ) { xtrnSet(i, &co, ++ui, str, sizeof(str), true); } break; } } FOR_EACH_CONSTANT_POOL_INDEX(i) { ConstantNode.supval(i, &co, sizeof(co)); switch ( co.type ) { case CONSTANT_Long: case CONSTANT_Double: ++i; default: continue; case CONSTANT_Class: break; } if ( co._subnam && co._subnam != (ushort)i ) { const_desc_t tmp; ConstantNode.supval(co._subnam, &tmp, sizeof(tmp)); co.ref_ip = tmp.ref_ip; StoreOpis(i, co); } } curClass.xtrnCnt = (ushort)ui; if ( curClass.xtrnCnt != 0 ) { set_segm_end(curClass.xtrnEA, curClass.xtrnEA + curClass.xtrnCnt + 1, SEGMOD_KILL); create_byte(curClass.xtrnEA, 1); } else { XtrnNode.kill(); curClass.xtrnNode = 0; del_segm(curClass.xtrnEA, SEGMOD_KILL); curClass.xtrnEA = 0; } msg("OK\n"); } //----------------------------------------------------------------------- void java_t::CheckPoolReference(bool insns_created) { char str[MAX_ATTR_NMSZ]; const_desc_t co; intvec_t k1s; intvec_t k2s; intvec_t k3s; uint mask = attr_CHECK_MASK; msg("Checking references, %s creating instructions...\n", insns_created ? "after" : "before"); FOR_EACH_CONSTANT_POOL_INDEX(i) { ConstantNode.supval(i, &co, sizeof(co)); if ( co.type == CONSTANT_Long || co.type == CONSTANT_Double ) ++i; if ( co.is_referenced() ) continue; switch ( co.type ) { case CONSTANT_Utf8: if ( !insns_created ) { uchar j; k2s.push_back(i); CASSERT((1 << (attr_LocalVariables+1)) - 1 == attr_CHECK_MASK); if ( (co.flag & HAS_FLDNAME) && mask && (j = attribute_type_from_str((ushort)i, attr_parent_kind_CHECK, str)) <= attr_LocalVariables && (mask & (1 << j)) ) { mask ^= (1 << j); } else if ( co._Ssize ) { k3s.push_back(i); // unnotify empty } } break; case CONSTANT_NameAndType: if ( !insns_created ) k1s.push_back(i); break; case CONSTANT_Class: if ( insns_created ) { k2s.push_back(i); if ( !(co.flag & HAS_CLSNAME) ) k3s.push_back(i); } break; default: if ( insns_created ) k1s.push_back(i); break; } // switch } // for if ( !k1s.empty() ) { dump_floating_constants( "unused", insns_created ? "(any except Class/Type/String)" : "NameAndType", k1s); } if ( !k2s.empty() ) { if ( !k3s.empty() ) { dump_floating_constants( insns_created ? "unnamed" : "unreferenced", insns_created ? "Class" : "Utf8", k3s); for ( size_t i = 0, n = k3s.size(); i < n; ++i ) k2s.del(k3s[i]); } if ( !k2s.empty() ) dump_floating_constants("unused", insns_created ? "Class" : "Utf8", k2s); } } //----------------------------------------------------------------------- void java_t::ValidateStoreLocVar(ushort slot, LocVar & lv) { netnode temp; uint32 cnt, id; bool dble; LocVar vals[(qmin(MAXSTR, MAXSPECSIZE)/sizeof(LocVar))]; const char *txt = "Invalid declaration"; lv.ScopeTop = (ushort)(id = (uint32)lv.ScopeBeg + lv.ScopeTop); if ( slot >= curSeg.DataSize || id > curSeg.CodeSize ) goto BADDECL; dble = false; if ( curSeg.varNode ) { temp = curSeg.varNode; cnt = (uint32)temp.altval(slot); if ( cnt != 0 ) { if ( (int32)cnt < 0 ) { cnt = -(int32)cnt; dble = true; } if ( (cnt % sizeof(LocVar)) || cnt >= sizeof(vals) || temp.supval(slot, vals, cnt+1) != cnt ) { goto interr; } cnt /= sizeof(LocVar); } } else { temp.create(); curSeg.varNode = temp; cnt = 0; } if ( !lv.utsign ) // base declaration { const_desc_t opis; CASSERT(offsetof(LocVar, utsign)+sizeof(lv.utsign) == sizeof(LocVar)); //lint -esym(645, vals) Symbol may not have been initialized for ( id = 0; id < cnt; id++ ) // skip full duplication if ( memcmp(&lv, &vals[id], offsetof(LocVar, utsign)) == 0 ) return; if ( !LoadFieldName(lm_normal, lv.var.Name) ) goto BADDECL; if ( !LoadFieldDscr(lm_normal, lv.var.Dscr, &opis) ) goto BADDECL; CASSERT(offsetof(const_desc_t, _Sflags) - offsetof(const_desc_t, _Ssize) == 2); if ( !dble && opis._Sopstr == (1 | (_OP_UTF8_ << 16)) ) { uchar tmp[sizeof(ushort)+1]; if ( ConstantNode.supval((uint32)lv.var.Dscr << 16, tmp, sizeof(tmp), BLOB_TAG) != sizeof(ushort) ) goto interr; switch ( tmp[0] ) { case j_double: case j_long: dble = true; if ( slot+1 == curSeg.DataSize ) goto BADDECL; default: break; } } txt = "Too many variants"; if ( cnt == qnumber(vals)-1 ) goto BADDECL; if ( !lv.ScopeBeg ) curSeg.id.extflg |= XFL_M_LABSTART; // special label at entry if ( lv.ScopeTop == curSeg.CodeSize ) curSeg.id.extflg |= XFL_M_LABEND; // special label at end { ea_t dea = curSeg.DataBase + slot; add_dref(dea, curSeg.start_ea + lv.ScopeBeg, dr_I); add_dref(dea, curSeg.start_ea + lv.ScopeTop, dr_I); } xtrnRef_dscr(curSeg.start_ea + lv.ScopeBeg, &opis); if ( !cnt ) set_lv_name(lv.var.Name, curSeg.DataBase + slot, (loadMode & MLD_LOCVAR) ? 3 : 0); // if not rename_on_load ONLY mark vals[cnt++] = lv; } else { // signature declaration CASSERT(offsetof(LocVar, var.Dscr)+sizeof(lv.var.Dscr)+sizeof(lv.utsign) == sizeof(LocVar)); for ( id = 0; id < cnt; id++ ) if ( memcmp(&lv, &vals[id], offsetof(LocVar, var.Dscr)) == 0 ) { if ( !vals[id].utsign ) { if ( !CheckSignature(lv.utsign, attr_parent_kind_code) ) goto BADDECL; vals[id].utsign = lv.utsign; goto store; } if ( vals[id].utsign == lv.utsign ) return; txt = "Different signature"; goto BADDECL; } txt = "Signature without type"; goto BADDECL; } store: cnt *= sizeof(LocVar); temp.supset(slot, vals, cnt); if ( !lv.utsign ) { if ( dble ) cnt = -(int32)cnt; temp.altset(slot, cnt); } return; BADDECL: load_msg("%s LocVar#%u Method#%u (name#%u dsc#%u sgn#%u scope:%u-%u)\n", txt, slot, curSeg.id.Number, lv.var.Name, lv.var.Dscr, lv.utsign, lv.ScopeBeg, lv.ScopeTop); return; interr: INTERNAL("StoreLocVar"); } //----------------------------------------------------------------------- inline void java_t::BadRefFile(const char *to, ushort id) { BadRef(BADADDR, to, id, attr_parent_kind_class_file); } //----------------------------------------------------------------------- uchar *java_t::annotation(uchar *p, uint32 *psize) { if ( *psize < 2 ) { bad: return NULL; } *psize -= 2; uint pairs= read2(); *(ushort *)p = (ushort)pairs; p += sizeof(ushort); if ( pairs ) { do { if ( *psize < 2 ) goto bad; *psize -= 2; ushort id = read2(); if ( !LoadFieldName(lm_normal, id) ) goto bad; *(ushort *)p = id; p = annot_elm(p+sizeof(ushort), psize); if ( p == NULL ) goto bad; } while ( --pairs ); } return p; } //--------------------------------------------------------------------------- uchar *java_t::annot_elm(uchar *ptr, uint32 *psize, uchar is_array) { if ( *psize < 1+2 ) { bad: return NULL; } *psize -= 1+2; union { uchar *p1; ushort *p2; }; p1 = ptr; uchar tag = read1(); ushort val = read2(); *p1++ = tag; *p2++ = val; switch ( tag ) { case j_annotation: if ( isSingleClass(val) && (p1 = annotation(p1, psize)) != NULL ) { goto done; } default: goto bad; case j_array: if ( val && !is_array ) // multidimensional array is not valid (javac ) { uchar *ps = p1; tag = 0; do { p1 = annot_elm(p1, psize, 1); if ( p1 == NULL ) goto bad; if ( !tag ) { if ( val == 1 ) break; tag = *ps; ps = p1; } else if ( tag != (uchar)-1 ) { if ( tag != *ps ) goto bad; tag = (uchar)-1; } } while ( --val ); goto done; } goto bad; case j_enumconst: if ( LoadFieldDscr(lm_normal, val, NULL) ) { if ( *psize < 2 ) goto bad; *psize -= 2; *p2++ = val = read2(); if ( LoadFieldName(lm_normal, val) ) goto done; } goto bad; case j_class_ret: if ( isSingleClass(val) ) goto done; goto bad; //### in 'classfile.pdf' j_void_ret also remebemered? case j_string: tag = CONSTANT_Utf8; break; case j_float: tag = CONSTANT_Float; break; case j_long: tag = CONSTANT_Long; break; case j_double: tag = CONSTANT_Double; break; case j_int: case j_byte: case j_char: case j_short: case j_bool: tag = CONSTANT_Integer; break; } if ( !LoadOpis(lm_normal, val, tag, NULL) ) goto bad; done: return p1; } //----------------------------------------------------------------------- bool java_t::sm_chkargs(uchar **pptr, uint32 *pDopSize, ushort cnt) { union { uchar *p1; ushort *p2; }; p1 = *pptr; uint32 dopsize = *pDopSize; bool result = false; do { if ( !dopsize ) goto declerr_w; --dopsize; uchar tag = read1(); if ( tag > ITEM_Uninitialized ) goto declerr_w; *p1++ = tag; CASSERT(ITEM_Object+1 == ITEM_Uninitialized); if ( tag >= ITEM_Object ) { if ( dopsize < 2 ) goto declerr_w; dopsize -= 2; ushort var = read2(); if ( tag == ITEM_Object ) { const_desc_t opis; if ( var == curClass.This.Dscr ) { var = curClass.This.Name; p1[-1] = ITEM_CURCLASS; } else if ( LoadNamedClass(lm_normal, var, &opis) ) { var = opis._name; } else { p1[-1] = ITEM_BADOBJECT; } } else { // Uninitialized (offset to new instruction) if ( !var ) curSeg.id.extflg |= XFL_M_LABSTART; // PARANOYA else if ( var >= curSeg.CodeSize ) goto declerr_w; } *p2++ = var; } } while ( --cnt ); result = true; *pptr = p1; declerr_w: *pDopSize = dopsize; return result; } //----------------------------------------------------------------------- int java_t::sm_load(ushort declcnt, uint32 *pDopSize) { union { uchar *p1; ushort *p2; }; sm_info_t smr; uint32 dopsize = *pDopSize; netnode temp(curSeg.smNode); int result = 0; uint prevoff = (uint)-1; p1 = sm_realloc(dopsize); dopsize -= 2; // skip READED counter *p2++ = declcnt; // counter smr.noff = (uint32)(p1 - smBuf); smr.fcnt = 0; do { ea_t refea; uint nxcnt; uchar rectype = SMT_FULL_FRAME; if ( SMF_mode ) // >=JDK6 { if ( !dopsize ) goto declerr_w; --dopsize; rectype = read1(); *p1++ = rectype; } { uint off; if ( rectype < SMT_SAME_FRM_S1 ) { if ( rectype > SMT_SAME_FRM_S1_max ) goto declerr_w; // reserved off = rectype; if ( rectype >= SMT_SAME_FRM_S1_min ) off -= SMT_SAME_FRM_S1_min; } else { if ( dopsize < 2 ) goto declerr_w; dopsize -= 2; off = read2(); *p2++ = (ushort)off; } if ( SMF_mode ) off += (prevoff + 1); // >=JDK6 if ( (uint32)off >= curSeg.CodeSize ) goto declerr_w; prevoff = off; refea = curSeg.start_ea + off; } if ( temp.supval(refea, NULL, 0) != -1 ) // for CLDC only { --result; goto declerr_w; } nxcnt = smr.fcnt; if ( rectype == SMT_FULL_FRAME ) { for ( int pass = 0; pass < 2; pass++ ) { ushort cnt; if ( dopsize < 2 ) goto declerr_w; dopsize -= 2; *p2++ = cnt = read2(); // number of locals / number of stacks if ( !pass ) nxcnt = cnt; if ( cnt && !sm_chkargs(&p1, &dopsize, cnt) ) goto declerr_w; } } else if ( rectype > SMT_SAME_FRM_S0 ) { rectype -= SMT_SAME_FRM_S0; if ( !sm_chkargs(&p1, &dopsize, rectype) ) goto declerr_w; nxcnt += rectype; } else if ( rectype >= SMT_SAME_FRM_S1 ) { rectype = (uchar)SMT_SAME_FRM_S0 - rectype; nxcnt -= rectype; if ( int(nxcnt) < 0 ) goto declerr_w; if ( rectype == 4 /*i.e., was: SMT_SAME_FRM_S1 */ ) { // same_locals_1_stack_item_frame_extended // http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.4 if ( !sm_chkargs(&p1, &dopsize, 1) ) goto declerr_w; } } else if ( rectype >= SMT_SAME_FRM_S1_min ) { if ( !sm_chkargs(&p1, &dopsize, 1) ) goto declerr_w; } smr.eoff = (uint32)(p1 - smBuf); temp.supset(refea, &smr, sizeof(smr)); smr.noff = smr.eoff; smr.fcnt = nxcnt; } while ( --declcnt ); // temp.altset(-1, smr.noff); temp.setblob(smBuf, smr.noff, 0, BLOB_TAG); ++result; declerr_w: *pDopSize = dopsize; return result; } //------------------------------------------------------------------------- NORETURN void java_t::loader_failure_bad_attr_decl_size(attr_parent_kind_t apk) const { char diastr[128]; loader_failure("Illegal declaration size%s", mk_diag(apk, diastr)); } //----------------------------------------------------------------------- void java_t::load_attributes(attr_parent_kind_t apk) { char atrs[MAX_ATTR_NMSZ+2], diastr[128]; // ushort k; uint i, j, r; netnode temp; uval_t *pann; uchar eflg, lvtb = 0, lntb = 0, fatal = (loadMode & MLD_FORCE) == 0; const_desc_t opis; opis._name = 0; // for vc j = read2(); for ( i = 0; i < j; i++ ) { if ( savesize >= 0 && (savesize -= 6) < 0 ) loader_failure_bad_attr_decl_size(apk); const ushort attribute_name_index = read2(); uint attribute_length = read4(); if ( savesize >= 0 ) { if ( (uint)savesize < attribute_length ) loader_failure_bad_attr_decl_size(apk); savesize -= attribute_length; } eflg = 0; // flag(additional attributes/locvars/annotation/stackmap) atrs[0] = ' '; // for additinal attibutes (mark SourceDebugExtension) switch ( attribute_type_from_str(attribute_name_index, apk, &atrs[1]) ) { case attr_SourceDebugExtension: if ( sde ) goto duplerr_w; ++sde; if ( attribute_length < 0x10000 ) { if ( attribute_length == 0 ) { deb(IDA_DEBUG_LDR, "Ignore zero length SourceDebugExtension attribute\n"); break; } qoff64_t pos = qftell(myFile), ssz = FileSize; if ( LoadUtf8((ushort)-1, (const_desc_t*)(size_t)attribute_length) ) { curClass.extflg |= XFL_C_DEBEXT; break; } qfseek(myFile, pos, SEEK_SET); FileSize = ssz; } if ( ask_yn(ASKBTN_YES, "HIDECANCEL\n" "SourceDebugExtension attribute have a non standard encoding\n" "or large size and cannot be represented in assembler.\n\n" "\3Do you want to store it in external file?") > ASKBTN_NO ) goto attr2file; goto skipAttr; case attr_BootstrapMethods: { if ( bootstrap_methods_get_node(/*assert=*/ false) != BADNODE ) loader_failure("Duplicate 'BootstrapMethods' attribute definition"); bootstrap_methods_get_node(/*assert=*/ true, /*can_create=*/ true); if ( attribute_length < 2 ) goto declerr_w; const ushort num_bootstrap_methods = read2(); bootstrap_methods_set_count(num_bootstrap_methods); attribute_length -= 2; const qoff64_t end = qftell(myFile) + attribute_length; for ( ushort bmidx = 0; bmidx < num_bootstrap_methods; ++bmidx ) { if ( qftell(myFile) >= end ) goto declerr_w; const ushort bootstrap_method_ref = read2(); if ( qftell(myFile) >= end ) goto declerr_w; const ushort num_bootstrap_arguments = read2(); bootstrap_method_def_t mdef; mdef.method_ref = bootstrap_method_ref; for ( ushort baidx = 0; baidx < num_bootstrap_arguments; ++baidx ) { if ( qftell(myFile) >= end ) goto declerr_w; const ushort bootstrap_argument = read2(); mdef.args.push_back(bootstrap_argument); } bootstrap_methods_set_method(mdef, bmidx); } // Now that all bootstrap methods are loaded, try and resolve // the constant pool's references to it FOR_EACH_CONSTANT_POOL_INDEX(cpidx) { const_desc_t cd; cd.type = 0; ConstantNode.supval(cpidx, &cd, sizeof(cd)); if ( cd.type == CONSTANT_InvokeDynamic ) { bootstrap_method_def_t mdef; if ( bootstrap_methods_get_method(&mdef, cd._bmidx) ) LoadOpis(lm_normal, mdef.method_ref, CONSTANT_MethodHandle, NULL); } } } break; case attr_UNKNOWN: atrs[0] = uchar(apk); if ( loadMode & MLD_EXTATR ) { attr2file: const char *p = CopyAttrToFile(atrs, attribute_length, attribute_name_index); if ( p == NULL ) break; loader_failure("%s %sattribute '%s'%s", p, atrs[0] == ' ' ? "" : "additional ", &atrs[1], mk_diag(apk, diastr)); } if ( idpflags & IDM_REQUNK ) { idpflags &= ~IDM_REQUNK; if ( ask_yn(ASKBTN_YES, "HIDECANCEL\n" "File contains unknown attribute(s).\n" "Do you want store it in external files?\n\n") > ASKBTN_NO ) { loadMode |= MLD_EXTATR; goto attr2file; } } eflg = 1; goto unkAttr; case attr_NONAME: qsnprintf(atrs, sizeof(atrs), "(with index %u)", attribute_name_index); goto notify; case attr_TRUNCATED: qstrncat(atrs, "...", sizeof(atrs)); unkAttr: atrs[0] = '"'; qstrncat(atrs, "\"", sizeof(atrs)); notify: #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4191) #endif if ( eflg && !(idpflags & IDM_WARNUNK) ) msg( "Ignore%s %s attribute (size %u)%s\n", (uchar)atrs[0] == ' ' ? "" : " unknown", atrs, attribute_length, mk_diag(apk, diastr)); else load_msg( "Ignore%s %s attribute (size %u)%s\n", (uchar)atrs[0] == ' ' ? "" : " unknown", atrs, attribute_length, mk_diag(apk, diastr)); #ifdef _MSC_VER #pragma warning(pop) #endif skipAttr: if ( attribute_length ) { real_skipAttr: skipData(attribute_length); } break; case attr_ConstantValue: { ushort constantvalue_index; if ( attribute_length != 2 || !LoadOpis(lm_normal, constantvalue_index = read2(), 0, &opis) ) { declerr: ++fatal; declerr_w: load_msg("Illegal declaration of %s%s\n", &atrs[1], mk_diag(apk, diastr)); goto skipAttr; } temp = curField.valNode; if ( !temp ) { temp.create(); curField.valNode = temp; r = 0; } else { r = (uint)temp.altval(0); } switch ( opis.type ) { case CONSTANT_Integer: case CONSTANT_Long: case CONSTANT_Float: case CONSTANT_Double: case CONSTANT_String: temp.supset(++r, &opis, sizeof(opis)); break; default: BadRef(curClass.start_ea + curField.id.Number, "value", constantvalue_index, apk); temp.altset(++r, (((uint32) constantvalue_index) << 16) | 0xFFFF); break; } temp.altset(0, r); } break; case attr_Code: if ( curSeg.CodeSize ) { // duplerr: ++fatal; duplerr_w: if ( fatal ) loader_failure("Duplicate %s attribute declaration%s", &atrs[1], mk_diag(apk, diastr)); else load_msg("Duplicate %s attribute declaration%s", &atrs[1], mk_diag(apk, diastr)); goto skipAttr; } r = curClass.JDKsubver ? 12 : 8; if ( attribute_length < r ) goto declerr; attribute_length -= r; r -= 8; curSeg.stacks = r ? read2() : read1(); curSeg.DataSize = r ? read2() : read1(); // max_locals curSeg.CodeSize = r ? read4() : read2(); if ( curSeg.CodeSize != 0 ) { if ( attribute_length < curSeg.CodeSize ) goto declerr; attribute_length -= curSeg.CodeSize; if ( FileSize < curSeg.CodeSize ) errtrunc(); FileSize -= curSeg.CodeSize; { segment_t *S = _add_seg(-1); // expand size if ( curSeg.DataSize ) set_default_sreg_value(S, rVds, _add_seg(2)->sel); // create data segment } } { const ushort exception_table_length = read2(); if ( exception_table_length != 0 ) // except. table { if ( !curSeg.CodeSize ) goto declerr; r = ((uint32) exception_table_length) * (2*4); if ( attribute_length < r ) goto declerr; attribute_length -= r; temp.create(); curSeg.excNode = temp; temp.altset(0, exception_table_length); ea_t ea = curSeg.start_ea + curSeg.CodeSize; r = 1; uchar err = 0; do { Exception exc; exc.start_pc = read2(); exc.end_pc = read2(); exc.handler_pc = read2(); exc.filter.Ref &= 0; // for 'finally' exc.filter.Dscr = read2(); if ( exc.filter.Dscr != 0 ) { if ( !LoadNamedClass(lm_normal, exc.filter.Dscr, &opis) ) { BadRef(ea, ".catch", exc.filter.Dscr, apk); } else { exc.filter.Name = opis._name; xtrnRef(ea, opis); } } temp.supset(r++, &exc, sizeof(exc)); err |= set_exception_xref(&curSeg, exc, ea); // as procedure for base converter } while ( (ushort)r <= exception_table_length ); if ( err ) { remember_problem(PR_ILLADDR, ea); load_msg("Invalid address(es) in .catch%s\n", mk_diag(apk, diastr)); } } }// exception table savesize = attribute_length; load_attributes(attr_parent_kind_code); // Additional attr savesize = -1; break; case attr_Exceptions: if ( curSeg.thrNode ) goto duplerr_w; if ( attribute_length < (2+2) || (attribute_length % 2) ) goto declerr_w; attribute_length -= 2; if ( (attribute_length / 2) != (uint32)read2() ) goto declerr_w; attribute_length /= 2; temp.create(); curSeg.thrNode = temp; r = 0; do { const ushort exception_index = read2(); if ( exception_index == 0 ) { load_msg("Ignore zero exception index%s\n", mk_diag(apk, diastr)); } else { uint32 refd = (uint32)exception_index << 16; if ( !LoadNamedClass(lm_normal, exception_index, &opis) ) { BadRef(curSeg.start_ea, ".throws", exception_index, apk); } else { refd |= opis._name; xtrnRef(curSeg.start_ea, opis); } temp.altset(++r, refd); } } while ( --attribute_length ); temp.altset(0, r); break; case attr_LineNumberTable: if ( lntb ) goto duplerr_w; ++lntb; r = attribute_length - 2; if ( attribute_length < 2 || (r % 4) ) goto declerr_w; attribute_length -= 2; if ( (r /= 4) != (uint32)read2() ) goto declerr_w; if ( attribute_length == 0 ) { // Symantec error (strip) #1 deb(IDA_DEBUG_LDR, "Stripped declaration of LineNumber table%s\n", mk_diag(apk, diastr)); } while ( r-- ) { const ushort start_pc = read2(); const ushort line_number = read2(); if ( uint32(start_pc) < curSeg.CodeSize ) set_source_linnum(curSeg.start_ea + start_pc, line_number); else load_msg("Illegal address (%u) of source line %u%s\n", start_pc, line_number, mk_diag(apk, diastr)); } break; case attr_LocalVariableTypeTable: if ( !(lvtb & 1) ) // ATT: my be can before LocalVariableTable? { if ( fatal ) loader_failure("%s before LocalVariableTable%s", atrs, mk_diag(apk, diastr)); else load_msg("%s before LocalVariableTable%s", atrs, mk_diag(apk, diastr)); goto skipAttr; } ++eflg; // 2 <= 1 + 1 // no break case attr_LocalVariableTable: ++eflg; // 1 if ( lvtb & eflg ) goto duplerr_w; lvtb |= eflg; --eflg; // unification (0/1) r = attribute_length - 2; if ( attribute_length < 2 || (r % 10) ) goto declerr_w; attribute_length -= 2; if ( (r /= 10) != (uint32)read2() ) goto declerr_w; while ( r-- ) { LocVar lv; lv.ScopeBeg = read2(); // start_pc lv.ScopeTop = read2(); // length lv.var.Name = read2(); // name_index CASSERT(offsetof(LocVar,utsign) == offsetof(LocVar,var.Dscr)+sizeof(lv.var.Dscr) && sizeof(lv.var.Dscr)*2 == sizeof(uint32)); *(uint32 *)&lv.var.Dscr &= 0; if ( eflg == 0 ) // LocalVariableTable/LocalVariableTypeTable lv.var.Dscr = read2(); // descriptor_index else lv.utsign = read2(); // descriptor_index const ushort index = read2(); // index if ( !eflg ) // normal table { if ( index == 0 && !lv.var.Name && !lv.ScopeBeg && lv.ScopeTop <= 1 ) { // Symantec error (strip) #2 deb(IDA_DEBUG_LDR, "Stripped declaration of local variables%s\n", mk_diag(apk, diastr)); continue; } if ( (short)lv.ScopeBeg == -1 ) { // Microsoft VisualJ++ error (purge?) LoadAnyString(lm_normal, lv.var.Name); LoadAnyString(lm_normal, lv.var.Dscr); deb(IDA_DEBUG_LDR, "Purged declaration of LocVar#%u%s\n", index, mk_diag(apk, diastr)); continue; } } ValidateStoreLocVar(index, lv); } // while break; case attr_SourceFile: { if ( attribute_length != 2 ) goto declerr_w; const ushort sourcefile_index = read2(); if ( LoadAnyString(lm_normal, sourcefile_index) ) curClass.SourceName = sourcefile_index; else BadRef(BADADDR, "source file name", sourcefile_index, apk); } break; case attr_InnerClasses: r = attribute_length - 2; if ( attribute_length < 2 || (r % 8) ) goto declerr_w; attribute_length -= 2; if ( (r /= 8) != (uint32)read2() ) goto declerr_w; if ( !r ) { deb(IDA_DEBUG_LDR, "Stripped declaration of InnerClasses\n"); break; } attribute_length = r; { ushort flags = 0; while ( attribute_length-- ) { InnerClass ic; *(uchar *)&flags = 0; ic.inner = read2(); // inner_class_info_index if ( ic.inner && !LoadNamedClass(lm_normal, ic.inner, &opis) ) flags |= 0x101; else ic.inner = opis._name; ic.outer = read2(); // outer_class_info_index if ( ic.outer && !LoadNamedClass(lm_normal, ic.outer, &opis) ) flags |= 0x101; else ic.outer = opis._name; ic.name = read2(); // inner_class_name_index if ( ic.name && !LoadFieldName(lm_normal, ic.name) ) flags |= 0x101; ic.access = read2(); // inner_class_access_flags r = ic.access & ACC_ACCESS_MASK; if ( r & (r-1) ) flags |= 0x101; if ( uchar(flags) == 0 ) { temp = curClass.innerNode; if ( !temp ) { temp.create(); curClass.innerNode = temp; r = 0; } else { r = (uint)temp.altval(0); } temp.supset(++r, &ic, sizeof(ic)); temp.altset(0, r); } } if ( flags != 0 ) load_msg("Error declaration(s) in Inner Classes\n"); } break; case attr_EnclosingMethod: if ( curClass.encClass ) goto duplerr_w; if ( attribute_length != 4 ) goto declerr_w; curClass.encClass = LoadNamedClass(lm_normal, read2(), &opis) ? opis._name : 0xFFFF; { const ushort method_index = read2(); if ( curClass.encClass == 0xFFFF ) { bad_encl: msg("Invalid EnclosingMethod description\n"); } else if ( method_index != 0 ) { if ( !LoadOpis(lm_normal, method_index, CONSTANT_NameAndType, &opis) || !(opis.flag & SUB_FLDNAME) || !LoadCallDscr(lm_normal, opis._name) ) { goto bad_encl; } curClass.encMethod = method_index; } } break; case attr_Synthetic: if ( attribute_length != 0 ) goto declerr_w; switch ( apk ) { default: // attr_parent_kind_code goto declerr_w; // paranoya case attr_parent_kind_field: curField.id.access |= ACC_SYNTHETIC; break; case attr_parent_kind_method: curSeg.id.access |= ACC_SYNTHETIC; break; case attr_parent_kind_class_file: curClass.AccessFlag |= ACC_SYNTHETIC; break; } break; case attr_Deprecated: if ( attribute_length != 0 ) goto declerr_w; switch ( apk ) { default: // attr_parent_kind_code goto declerr_w; // paranoya case attr_parent_kind_field: curField.id.extflg |= XFL_DEPRECATED; break; case attr_parent_kind_method: curSeg.id.extflg |= XFL_DEPRECATED; break; case attr_parent_kind_class_file: curClass.extflg |= XFL_DEPRECATED; break; } break; case attr_Signature: if ( attribute_length != 2 || apk == attr_parent_kind_code ) goto declerr_w; { const ushort signature_index = read2(); if ( CheckSignature(signature_index, apk) ) { switch ( apk ) { case attr_parent_kind_field: curField.id.utsign = signature_index; break; case attr_parent_kind_method: curSeg.id.utsign = signature_index; break; case attr_parent_kind_class_file: curClass.utsign = signature_index; break; default: break; } } } break; case attr_StackMapTable: ++eflg; // fallthrough case attr_StackMap: if ( !eflg != (curClass.JDKsubver < 6) ) { if ( fatal ) loader_failure("JDK1.%u incompatible with attribute%s%s", curClass.JDKsubver, atrs, mk_diag(apk, diastr)); else load_msg("JDK1.%u incompatible with attribute%s%s", curClass.JDKsubver, atrs, mk_diag(apk, diastr)); goto skipAttr; } if ( curSeg.smNode ) goto duplerr_w; if ( attribute_length < 2 ) goto declerr_w; { const ushort number_of_entries = read2(); if ( number_of_entries == 0 ) { attribute_length -= 2; curSeg.smNode = BADNODE; deb(IDA_DEBUG_LDR, "Empty%s attribute%s\n", atrs, mk_diag(apk, diastr)); curSeg.id.extflg |= XFL_M_EMPTYSM; } else { temp.create(); curSeg.smNode = temp; r = sm_load(number_of_entries, &attribute_length); if ( int(r) <= 0 ) { temp.kill(); curSeg.smNode = BADNODE; if ( !r ) goto declerr_w; if ( fatal ) loader_failure("Inconsistent declaration of %s%s", &atrs[1], mk_diag(apk, diastr)); else load_msg("Inconsistent declaration of %s%s", &atrs[1], mk_diag(apk, diastr)); goto skipAttr; //-V779 Unreachable code detected } } } skip_excess: if ( attribute_length ) { deb(IDA_DEBUG_LDR, "Excess %u bytes in%s attribute%s\n", attribute_length, atrs, mk_diag(apk, diastr)); goto real_skipAttr; } break; case attr_AnnotationDefault: pann = &curSeg.annNodes[2]; eflg = 4; // as flag goto do_annot1; case attr_RuntimeInvisibleParameterAnnotations: ++eflg; // no break case attr_RuntimeVisibleParameterAnnotations: pann = &curSeg.annNodes[3]; eflg |= 2; // flag of secondary loop do_annot1: if ( apk != attr_parent_kind_method ) goto declerr_w; // paranoya goto do_annot; case attr_RuntimeInvisibleAnnotations: ++eflg; // no break case attr_RuntimeVisibleAnnotations: switch ( apk ) { default: // attr_parent_kind_code goto declerr_w; // paranoya case attr_parent_kind_class_file: pann = curClass.annNodes; break; case attr_parent_kind_field: pann = curField.annNodes; break; case attr_parent_kind_method: pann = curSeg.annNodes; break; } do_annot: if ( eflg & 1 ) ++pann; // invisible if ( *pann ) goto duplerr_w; temp.create(); *pann = temp; if ( attribute_length == 0 ) goto declerr_w; { uchar *p = annotation_realloc(attribute_length); r = 1; // no paramsloop if ( eflg & 2 ) // Parameters { r = read1(); *p++ = (uchar)r; --attribute_length; if ( !r ) goto annot_err; } if ( eflg & 4 ) // defalut { p = annot_elm(p, &attribute_length); if ( p != NULL ) goto annot_done; annot_err: temp.kill(); *pann = (uval_t)-1; // as flag for duplicates goto declerr_w; } do // loop for Parameters { if ( attribute_length < 2 ) goto annot_err; uint cnt = read2(); attribute_length -= 2; *(ushort *)p = (ushort)cnt; p += sizeof(ushort); if ( !cnt ) { if ( !(eflg & 2) ) goto annot_err; // no parameters continue; } eflg |= 8; // flag for parameters do { if ( attribute_length < 2 ) goto annot_err; attribute_length -= 2; ushort id = read2(); if ( !isSingleClass(id) ) goto annot_err; *(ushort*)p = id; p = annotation(p + sizeof(ushort), &attribute_length); if ( p == NULL ) goto annot_err; } while ( --cnt ); } while ( --r ); // parameters loop if ( eflg == 2 ) goto annot_err; // empty Parameters annotation annot_done: r = (uint)(p - annBuf); } // local variable block temp.setblob(annBuf, r, 0, BLOB_TAG); temp.altset(0, r); goto skip_excess; default: INTERNAL("load_attributes"); } // switch } // for } //----------------------------------------------------------------------- uchar java_t::CheckSignature(ushort index, attr_parent_kind_t apk) { char diastr[128]; const_desc_t opis; if ( !LoadOpis(lm_normal, index, CONSTANT_Utf8, &opis) ) { bad: if ( apk != attr_parent_kind_code ) // not debug variable load_msg("Invalid signature (#%u)%s\n", index, mk_diag(apk, diastr)); return 0; } CASSERT(HAS_TYPEDSCR < 0x100 && HAS_CALLDSCR < 0x100); //-V590 expression is excessive if ( !((uchar)opis.flag & ((apk == attr_parent_kind_method ) ? HAS_CALLDSCR : HAS_TYPEDSCR)) ) { if ( !opis._Ssize ) goto bad; // PARANOYA if ( opis._Sflags & (_OP_EXTSYM_ | _OP_NOSIGN) ) goto bad; opis._Sflags &= (_OP_VALPOS | _OP_METSIGN | _OP_CLSSIGN); switch ( apk ) { case attr_parent_kind_method: if ( (opis._Sflags &= ~_OP_METSIGN) != _OP_VALPOS ) goto bad; break; case attr_parent_kind_class_file: opis._Sflags &= ~_OP_CLSSIGN; // no break default: // FIELD & .var if ( opis._Sflags ) goto bad; break; } } return 1; } //----------------------------------------------------------------------- // // This function should read the input file (it is opened in binary mode) // analyze it and: // - loading segment and offset are in inf.BaseAddr & // - load it into the database using file2base(),mem2base() // or allocate addresses by enable_flags() and fill them // with values using putByte(), putWord(), putLong() // - (if createsegs) create segments using // add_segm(segment_t *,const char *name,const char *sclass,int flags) // or // add_segm(uint short,ea_t,ea_t,char *,char *) // see segment.hpp for explanations // - set up inf_get_start_ip(),startCS to the starting address // - set up inf_get_min_ea(),inf_get_max_ea() // // void java_t::loader(FILE *fp, bool manual) { ushort j; const_desc_t opis; memset(&curClass, 0, sizeof(curClass)); qfseek(fp, 0, SEEK_SET); // rewind FileSize = qfsize(fp); myFile = fp; enableExt_NameChar(); if ( read4() != MAGICNUMBER ) error("Illegal magic number"); curClass.MinVers = read2(); curClass.MajVers = read2(); if ( curClass.MajVers <= JDK_MIN_MAJOR ) { if ( curClass.MajVers < JDK_MIN_MAJOR ) goto BAD_VERSION; curClass.JDKsubver = (uchar)(curClass.MinVers >= JDK_1_1_MINOR); } else if ( curClass.MajVers > JDK_MAX_MAJOR || curClass.MinVers ) { BAD_VERSION: loader_failure("Unsupported file format (version %u.%u)", curClass.MajVers, curClass.MinVers); } else { curClass.JDKsubver = (uchar)(curClass.MajVers - (JDK_MIN_MAJOR-1)); } if ( curClass.MajVers >= JDK_SMF_MAJOR_MIN ) { SMF_mode = 1; } else if ( curClass.JDKsubver <= 1 ) { switch ( curClass.MinVers ) { default: ask_for_feedback( "Class file with version %u.%u (JDK1.%u?) is not tested!", curClass.MajVers, curClass.MinVers, curClass.JDKsubver); // no break case JDK_1_02_MINOR: case JDK_1_1_MINOR: break; } } //-- curClass.maxCPindex = read2(); if ( curClass.maxCPindex <= 2 ) loader_failure("Empty constant pool"); loadMode = loadDialog(manual); ConstantNode.create(constant_pool); --curClass.maxCPindex; // last valid number XtrnNode.create(); make_NameChars(/*on_load=*/ true); // initialize and set 'load extension' const uint nconstants = load_constants_pool(); if ( !_add_seg(0) ) { XtrnNode.kill(); } else { curClass.xtrnCnt = ushort(nconstants); curClass.xtrnNode = XtrnNode; } //-- curClass.AccessFlag = read2(); if ( curClass.AccessFlag & ~ACC_THIS_MASK ) load_msg("Illegal class access bits (0x%X)\n", curClass.AccessFlag); curClass.This.Dscr = read2(); if ( LoadNamedClass(lm_normal, curClass.This.Dscr, &opis) ) { curClass.This.Name = opis._name; } else { BadRefFile("'this' class", curClass.This.Dscr); CASSERT(offsetof(ClassInfo, This.Ref)+2 == offsetof(ClassInfo, This.Dscr) && offsetof(ClassInfo, This.Ref) == offsetof(ClassInfo, This.Name)); curClass.This.Ref >>= 16; // curClass.This.Name = curClass.This.Dscr; // curClass.This.Dscr = 0; } //-- if ( curClass.xtrnNode ) setPoolReference(); curClass.super.Dscr = read2(); uint i = read2(); // interface counter i *= 2; if ( FileSize < i ) errtrunc(); qfseek(fp, i, SEEK_CUR); curClass.FieldCnt = read2(); qfseek(fp, -2 - qoff64_t(i), SEEK_CUR); _add_seg(3); // class segment enableExt_NameChar(); if ( curClass.This.Dscr ) { curSeg.id.Number = 0; SetName(curClass.This.Name, curClass.start_ea, curClass.AccessFlag, 0); hide_name(curClass.start_ea); } if ( curClass.super.Ref ) { if ( !LoadNamedClass(lm_normal, curClass.super.Dscr, &opis) ) { BadRefFile("parent class", curClass.super.Dscr); } else { curClass.super.Name = opis._name; xtrnRef(curClass.start_ea, opis); } } //-- if ( (i /= 2) != 0 ) // InterfaceCount { netnode temp; uint r = 0; do { j = read2(); if ( j == 0 ) { load_msg("Ignore zero interface index\n"); continue; } if ( !r ) temp.create(); uint32 refd = (uint32)j << 16; if ( !LoadNamedClass(lm_normal, j, &opis) ) { BadRefFile("interface", j); } else { xtrnRef(curClass.start_ea, opis); refd |= opis._name; } temp.altset(++r, refd); } while ( --i ); if ( r ) { temp.altset(0, r); curClass.impNode = temp; } } //--- ClassNode.create(); curClass.ClassNode = ClassNode; qfseek(fp, 2, SEEK_CUR); if ( errload ) { int f = (curClass.AccessFlag & ~ACC_THIS_MASK) ? curClass.AccessFlag : 0; mark_access(curClass.start_ea, f); } //--- for ( i = 1; (ushort)i <= curClass.FieldCnt; i++ ) { memset(&curField, 0, sizeof(curField)); curField.id.Number = (ushort)i; curField.id.access = read2(); j = curField.id.access & ACC_ACCESS_MASK; if ( (curField.id.access & ~ACC_FIELD_MASK) || (j & (j-1)) ) { load_msg("Illegal Field#%u Attribute 0x%04x\n", i, curField.id.access); // curField.id.extflg |= EFL_ACCESS; mark_access(curClass.start_ea + i, curField.id.access); } curField.id.name = read2(); if ( !CheckFieldName(lm_normal, curField.id.name, NULL) ) curField.id.extflg |= EFL_NAME; curField.id.dscr = read2(); if ( !CheckFieldDscr(lm_normal, curField.id.dscr, &opis) ) curField.id.extflg |= EFL_TYPE; else xtrnRef_dscr(curClass.start_ea + curField.id.Number, &opis); if ( curField.id.extflg & EFL_NAMETYPE ) load_msg("Illegal NameAndType of field %u\n", i); load_attributes(attr_parent_kind_field); for ( int n = 0; n < qnumber(curField.annNodes); n++ ) if ( curField.annNodes[n] == (uval_t)-1 ) ++curField.annNodes[n]; ClassNode.supset(i, &curField, sizeof(curField)); if ( !(curField.id.extflg & EFL_NAME) ) SetName(curField.id.name, curClass.start_ea + i, curField.id.access, i); } //-- curClass.MethodCnt = read2(); for ( i = 1; i <= curClass.MethodCnt; i++ ) { memset(&curSeg, 0, sizeof(curSeg)); curSeg.id.Number = (ushort)i; curSeg.id.access = read2(); j = curSeg.id.access & ACC_ACCESS_MASK; if ( !(j & ~ACC_METHOD_MASK) && !(j & (j-1)) ) j = 0; else load_msg("Illegal Method#%u Attribute 0x%04x\n", i, curSeg.id.access); // curSeg.id.extflg |= EFL_ACCESS; _add_seg(1); // create code segment // this for strnRef_dscr curSeg.id.name = read2(); if ( !CheckFieldName(lm_normal, curSeg.id.name, NULL) ) curSeg.id.extflg |= EFL_NAME; curSeg.id.dscr = read2(); if ( !CheckCallDscr(lm_normal, curSeg.id.dscr, &opis) ) curSeg.id.extflg |= EFL_TYPE; else xtrnRef_dscr(curSeg.start_ea, &opis, 1); if ( curSeg.id.extflg & EFL_NAMETYPE ) load_msg("Illegal NameAndType of method %u\n", i); // if ( curSeg.id.extflg & EFL_ACCESS ) if ( j ) mark_access(curSeg.start_ea, curSeg.id.access); load_attributes(attr_parent_kind_method); if ( curSeg.smNode == BADNODE ) curSeg.smNode = 0; // remove 'flagged' value for ( int n = 0; n < qnumber(curSeg.annNodes); n++ ) if ( curSeg.annNodes[n] == (uval_t)-1 ) ++curSeg.annNodes[n]; if ( curSeg.varNode ) resizeLocVars(); if ( curSeg.thrNode ) { netnode tnode(curSeg.thrNode); if ( !tnode.altval(0) ) { tnode.kill(); curSeg.thrNode = 0; } } ClassNode.supset(-(int)i, &curSeg, sizeof(curSeg)); } //-- load_attributes(attr_parent_kind_class_file); // Source File for ( int n = 0; n < qnumber(curClass.annNodes); n++ ) if ( curClass.annNodes[n] == (uval_t)-1 ) ++curClass.annNodes[n]; myFile = NULL; if ( curClass.encClass == 0xFFFF ) ++curClass.encClass; // unification in out if ( FileSize ) warning("This file has extra information (pos=0x%" FMT_64 "x)", qftell(fp)); //--- CheckPoolReference(0); endLoad_NameChar(); // set 'standart extension' if ( !set_parent_object() && (curClass.AccessFlag & ACC_INTERFACE) ) { load_msg("This is interface, but superclass is not java.lang.Object!\n"); mark_and_comment(curClass.start_ea, "Interface have nonstandart parent"); } if ( curClass.impNode && !curClass.super.Ref ) { load_msg("This have implements without superclass!\n"); mark_and_comment(curClass.start_ea, "Empty supperclass not for Object"); } if ( errload ) curClass.extflg |= XFL_C_ERRLOAD; ConstantNode.supset(CNS_CLASS, &curClass, sizeof(curClass)); // load end! //-- debugmode = 1; // full pass... for ( i = 1; i <= curClass.MethodCnt; i++ ) { msg("Analysing method %u...\n", i); ClassNode.supval(-(int)i, &curSeg, sizeof(curSeg)); ea_t ea = curSeg.start_ea; show_addr(ea); ea_t end = ea + curSeg.CodeSize; segment_t *s = getseg(ea); if ( s == NULL || end < ea || end > s->end_ea ) loader_failure("Bad method %u code size 0x%X", i, curSeg.CodeSize); if ( !curSeg.CodeSize ) { create_byte(ea, 0x10); } else { do { int sz = create_insn(ea); if ( sz == 0 ) ++sz; ea += sz; } while ( ea < end ); } if ( (curSeg.id.extflg & EFL_NAME) == 0 ) SetName(curSeg.id.name, curSeg.start_ea, curSeg.id.access, curClass.FieldCnt + i); add_func(curSeg.start_ea, end + 1); } debugmode = 0; // all references setting... CheckPoolReference(1); ConstantNode.altset(CNA_VERSION, _CUR_IDP_VER); ResW_newbase(); create_filename_cmt(); }