DexInstructionParser extends DexParser { public void parse() throws IOException,UnknownInstructionException { long instrBase = file.getFilePointer(); int instrCode = read8Bit(); InstructionType instrType = instructionTypes[ instrCode ]; StringBuilder instrText = new StringBuilder(); instrText.append( instructionNames[ instrCode ] ); instrText.append( "\t" ); forkStatus = initialForkStatus( instrCode ); forkData = null; affectedRegisters = null; boolean generateText = true; switch( instrType ) { case UNKNOWN_INSTRUCTION: throw new UnknownInstructionException( "Unknown instruction 0x"+ dumpByte( instrCode )+ " at offset "+ dumpLong( file.getFilePointer() - 1L ) ); case REGCONST4: { int b1 = read8Bit(); int reg = b1 & 0x0F; int constant = ( b1 & 0xF0 ) >> 4; instrText.append( "v"+reg ); instrText.append( "," ); instrText.append( constant ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),TYPE_SINGLE_LENGTH ); } break; case REGSTRINGCONST: { int reg = read8Bit(); int stringidx = read16Bit(); instrText.append( "v"+reg ); instrText.append( "," ); instrText.append( "\""+ dexStringIdsBlock.getString( stringidx )+ "\"" ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),"Ljava/lang/String;" ); } break; case REGSTRINGCONST_JUMBO: { int reg = read8Bit(); int stringidx = (int)read32Bit(); instrText.append( "v"+reg ); instrText.append( "," ); instrText.append( "\""+ dexStringIdsBlock.getString( stringidx )+ "\"" ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),"Ljava/lang/String;" ); } break; case METHODINVOKE: { int b2 = read8Bit(); int regno = ( b2 & 0xF0 ) >> 4; int origRegNo = regno; int lastreg = -1; if( ( regno > 4 ) && ( regno % 4 ) == 1 ) { --regno; lastreg = b2 & 0x0F; } int methodidx = read16Bit(); String proto = dexMethodIdsBlock.getProto( methodidx ); String resultType = DexMethodIdsBlock.getResultType( proto ); if( "V".equals( resultType ) ) regMap.remove( REGMAP_RESULT_KEY ); else regMap.put( REGMAP_RESULT_KEY, convertJavaTypeToInternal( resultType ) ); instrText.append( "{" ); int regByte = 0; int byteCounter = 0; ArrayList<Integer> registerList = new ArrayList<Integer>(); for( int i = 0 ; i < regno ; ++i ) { if( i > 0 ) instrText.append( "," ); int reg = 0; if( ( i % 2 ) == 0 ) { regByte = read8Bit(); ++byteCounter; reg = regByte & 0x0F; } else reg = ( regByte & 0xF0 ) >> 4; instrText.append( "v"+reg ); registerList.add( new Integer( reg ) ); } if( lastreg >= 0 ) { instrText.append( ",v"+lastreg ); registerList.add( new Integer( lastreg ) ); } if( ( byteCounter % 2 ) != 0 ) read8Bit(); instrText.append( "}," ); instrText.append( dexMethodIdsBlock.getMethod( methodidx ) ); instrText.append( "\t; "+proto ); affectedRegisters = getAffectedRegistersForRegList( registerList, proto, 1 ); } break; case METHODINVOKE_STATIC: { int b2 = read8Bit(); int regno = ( b2 & 0xF0 ) >> 4; int origRegNo = regno; int lastreg = -1; if( ( regno > 4 ) && ( regno % 4 ) == 1 ) { --regno; lastreg = b2 & 0x0F; } int methodidx = read16Bit(); String proto = dexMethodIdsBlock.getProto( methodidx ); String resultType = DexMethodIdsBlock.getResultType( proto ); if( "V".equals( resultType ) ) regMap.remove( REGMAP_RESULT_KEY ); else regMap.put( REGMAP_RESULT_KEY, convertJavaTypeToInternal( resultType ) ); instrText.append( "{" ); int regByte = 0; int byteCounter = 0; ArrayList<Integer> registerList = new ArrayList<Integer>(); for( int i = 0 ; i < regno ; ++i ) { if( i > 0 ) instrText.append( "," ); int reg = 0; if( ( i % 2 ) == 0 ) { regByte = read8Bit(); ++byteCounter; reg = regByte & 0x0F; } else reg = ( regByte & 0xF0 ) >> 4; instrText.append( "v"+reg ); registerList.add( new Integer( reg ) ); } if( lastreg >= 0 ) { instrText.append( ",v"+lastreg ); registerList.add( new Integer( lastreg ) ); } if( ( byteCounter % 2 ) != 0 ) read8Bit(); instrText.append( "}," ); instrText.append( dexMethodIdsBlock.getMethod( methodidx ) ); instrText.append( "\t; "+proto ); affectedRegisters = getAffectedRegistersForRegList( registerList, proto, 0 ); } break; case QUICKMETHODINVOKE: { int b2 = read8Bit(); int regno = ( b2 & 0xF0 ) >> 4; int origRegNo = regno; int lastreg = -1; if( ( regno > 4 ) && ( regno % 4 ) == 1 ) { --regno; lastreg = b2 & 0x0F; } int vtableOffset = read16Bit(); instrText.append( "{" ); int regByte = 0; int byteCounter = 0; String baseClass = null; ArrayList<Integer> registerList = new ArrayList<Integer>(); for( int i = 0 ; i < regno ; ++i ) { if( i > 0 ) instrText.append( "," ); int reg = 0; if( ( i % 2 ) == 0 ) { regByte = read8Bit(); ++byteCounter; reg = regByte & 0x0F; } else reg = ( regByte & 0xF0 ) >> 4; instrText.append( "v"+reg ); registerList.add( new Integer( reg ) ); if( ( !secondPass && ( dexOffsetResolver != null ) ) && ( i == 0 ) ) { baseClass = regMap.get( new Integer( reg ) ); if( ( baseClass == null ) || ( TYPE_SINGLE_LENGTH.equals( baseClass ) ) || ( TYPE_DOUBLE_LENGTH.equals( baseClass ) ) ) baseClass = getLocalVariableType( instrBase,reg ); if( baseClass != null ) baseClass = DexTypeIdsBlock.LTypeToJava( baseClass ); } } if( lastreg >= 0 ) { instrText.append( "v"+lastreg ); registerList.add( new Integer( lastreg ) ); } if( ( byteCounter % 2 ) != 0 ) read8Bit(); instrText.append( "}," ); boolean offsetResolved = false; if( dexOffsetResolver != null ) { if( secondPass ) { Long key = new Long( file.getFilePointer() ); String parameter = quickParameterMap.get( key ); if( parameter != null ) { instrText.append( parameter ); offsetResolved = true; } } else { if( baseClass != null ) { String methodProto = dexOffsetResolver.getMethodNameFromOffset( baseClass, vtableOffset ); if( methodProto != null ) { String proto = ""; int idx = methodProto.indexOf( ',' ); if( idx >= 0 ) { proto = methodProto.substring( idx+1 ); methodProto = methodProto.substring( 0,idx ); } String parameter = methodProto+ "\t; "+proto+ " , vtable #0x"+ Integer.toHexString( vtableOffset ); Long key = new Long( file.getFilePointer() ); quickParameterMap.put( key,parameter ); instrText.append( parameter ); String resultType = DexMethodIdsBlock.getResultType( proto ); if( "V".equals( resultType ) ) regMap.remove( REGMAP_RESULT_KEY ); else regMap.put( REGMAP_RESULT_KEY, convertJavaTypeToInternal( resultType ) ); affectedRegisters = getAffectedRegistersForRegList( registerList, proto, 1 ); offsetResolved = true; } } } } if( !offsetResolved ) { instrText.append( "vtable #0x"+ Integer.toHexString( vtableOffset ) ); affectedRegisters = new int[ registerList.size() ]; for( int i = 0 ; i < registerList.size() ; ++i ) affectedRegisters[i] = registerList.get( i ).intValue(); } } break; case INLINEMETHODINVOKE: { int b2 = read8Bit(); int regno = ( b2 & 0xF0 ) >> 4; int origRegNo = regno; int lastreg = -1; ArrayList<Integer> registerList = new ArrayList<Integer>(); if( ( regno > 4 ) && ( regno % 4 ) == 1 ) { --regno; lastreg = b2 & 0x0F; } int inlineOffset = read16Bit(); instrText.append( "{" ); int regByte = 0; int byteCounter = 0; for( int i = 0 ; i < regno ; ++i ) { if( i > 0 ) instrText.append( "," ); int reg = 0; if( ( i % 2 ) == 0 ) { regByte = read8Bit(); ++byteCounter; reg = regByte & 0x0F; } else reg = ( regByte & 0xF0 ) >> 4; instrText.append( "v"+reg ); registerList.add( new Integer( reg ) ); } if( lastreg >= 0 ) { instrText.append( "v"+lastreg ); registerList.add( new Integer( lastreg ) ); } if( ( byteCounter % 2 ) != 0 ) read8Bit(); instrText.append( "}," ); boolean offsetResolved = false; if( secondPass ) { Long key = new Long( file.getFilePointer() ); String parameter = quickParameterMap.get( key ); if( parameter != null ) { instrText.append( parameter ); offsetResolved = true; } } else { String methodProto = DexOffsetResolver .getInlineMethodNameFromIndex( inlineOffset, dexSignatureBlock.getOptVersion() ); if( methodProto != null ) { String proto = ""; int idx = methodProto.indexOf( ',' ); if( idx >= 0 ) { proto = methodProto.substring( idx+1 ); methodProto = methodProto.substring( 0,idx ); } String parameter = methodProto+"\t; "+proto+ " , inline #0x"+ Integer.toHexString( inlineOffset ); Long key = new Long( file.getFilePointer() ); quickParameterMap.put( key,parameter ); instrText.append( parameter ); String resultType = DexMethodIdsBlock.getResultType( proto ); if( "V".equals( resultType ) ) regMap.remove( REGMAP_RESULT_KEY ); else regMap.put( REGMAP_RESULT_KEY, convertJavaTypeToInternal( resultType ) ); affectedRegisters = getAffectedRegistersForRegList( registerList, proto, 1 ); offsetResolved = true; } } if( !offsetResolved ) instrText.append( "inline #0x"+ Integer.toHexString( inlineOffset ) ); } break; case FILLEDARRAY: { int b2 = read8Bit(); int regno = ( b2 & 0xF0 ) >> 4; int lastreg = -1; if( ( regno > 4 ) && ( regno % 4 ) == 1 ) { --regno; lastreg = b2 & 0x0F; } int typeidx = read16Bit(); instrText.append( "{" ); int regByte = 0; int byteCounter = 0; affectedRegisters = new int[ regno ]; for( int i = 0 ; i < regno ; ++i ) { if( i > 0 ) instrText.append( "," ); int reg = 0; if( ( i % 2 ) == 0 ) { regByte = read8Bit(); ++byteCounter; reg = regByte & 0x0F; } else reg = ( regByte & 0xF0 ) >> 4; instrText.append( "v"+reg ); affectedRegisters[ i ] = reg; } if( lastreg >= 0 ) { instrText.append( "v"+lastreg ); affectedRegisters[ regno - 1 ] = lastreg; } if( ( byteCounter % 2 ) != 0 ) read8Bit(); instrText.append( "}," ); String arrayType = dexTypeIdsBlock.getType( typeidx ); instrText.append( arrayType ); regMap.put( REGMAP_RESULT_KEY, convertJavaTypeToInternal( arrayType ) ); } break; case METHODINVOKE_RANGE: { int regno = read8Bit(); int methodidx = read16Bit(); int rangestart = read16Bit(); int rangeend = rangestart + regno - 1; String proto = dexMethodIdsBlock.getProto( methodidx ); String resultType = DexMethodIdsBlock.getResultType( proto ); if( "V".equals( resultType ) ) regMap.remove( REGMAP_RESULT_KEY ); else regMap.put( REGMAP_RESULT_KEY, convertJavaTypeToInternal( resultType ) ); if( regno == 1 ) instrText.append( "{v"+ rangestart+ "}" ); else instrText.append( "{v"+ rangestart+ "..v"+ rangeend+ "}" ); instrText.append( ","+ dexMethodIdsBlock.getMethod( methodidx )+ ";\t"+ proto ); affectedRegisters = getAffectedRegistersForRange( proto, rangestart,1 ); } break; case METHODINVOKE_RANGE_STATIC: { int regno = read8Bit(); int methodidx = read16Bit(); int rangestart = read16Bit(); int rangeend = rangestart + regno - 1; String proto = dexMethodIdsBlock.getProto( methodidx ); String resultType = DexMethodIdsBlock.getResultType( proto ); if( "V".equals( resultType ) ) regMap.remove( REGMAP_RESULT_KEY ); else regMap.put( REGMAP_RESULT_KEY, convertJavaTypeToInternal( resultType ) ); if( regno == 1 ) instrText.append( "{v"+ rangestart+ "}" ); else instrText.append( "{v"+ rangestart+ "..v"+ rangeend+ "}" ); instrText.append( ","+ dexMethodIdsBlock.getMethod( methodidx )+ ";\t"+ proto ); affectedRegisters = getAffectedRegistersForRange( proto, rangestart,0 ); } break; case QUICKMETHODINVOKE_RANGE: { int regno = read8Bit(); int vtableOffset = read16Bit(); int rangestart = read16Bit(); int rangeend = rangestart + regno - 1; if( regno == 1 ) instrText.append( "{v"+ rangestart+ "}" ); else instrText.append( "{v"+ rangestart+ "..v"+ rangeend+ "}" ); boolean offsetResolved = false; if( dexOffsetResolver != null ) { if( secondPass ) { Long key = new Long( file.getFilePointer() ); String parameter = quickParameterMap.get( key ); if( parameter != null ) { instrText.append( parameter ); offsetResolved = true; } } else { String baseClass = null; if( dexOffsetResolver != null ) { baseClass = regMap.get( new Integer( rangestart ) ); if( baseClass != null ) { baseClass = DexTypeIdsBlock.LTypeToJava( baseClass ); String methodProto = dexOffsetResolver.getMethodNameFromOffset( baseClass, vtableOffset ); if( methodProto != null ) { String proto = ""; int idx = methodProto.indexOf( ',' ); if( idx >= 0 ) { proto = methodProto.substring( idx+1 ); methodProto = methodProto.substring( 0,idx ); } String parameter = baseClass+ "/"+methodProto+ "\t; "+proto+ " , vtable #0x"+ Integer.toHexString( vtableOffset ); instrText.append( parameter ); Long key = new Long( file.getFilePointer() ); quickParameterMap.put( key,parameter ); String resultType = DexMethodIdsBlock.getResultType( proto ); if( "V".equals( resultType ) ) regMap.remove( REGMAP_RESULT_KEY ); else regMap.put( REGMAP_RESULT_KEY, convertJavaTypeToInternal( resultType ) ); affectedRegisters = getAffectedRegistersForRange( proto, rangestart,1 ); offsetResolved = true; } else { affectedRegisters = new int[ regno ]; for( int i = 0 ; i < regno ; ++i ) affectedRegisters[i] = rangestart+i; } } } } } if( !offsetResolved ) instrText.append( ",vtable #0x"+ Integer.toHexString( vtableOffset ) ); } break; case INLINEMETHODINVOKE_RANGE: { int regno = read8Bit(); int inlineOffset = read16Bit(); int rangestart = read16Bit(); int rangeend = rangestart + regno - 1; if( regno == 1 ) instrText.append( "{v"+ rangestart+ "}," ); else instrText.append( "{v"+ rangestart+ "..v"+ rangeend+ "}," ); boolean offsetResolved = false; if( secondPass ) { Long key = new Long( file.getFilePointer() ); String parameter = quickParameterMap.get( key ); if( parameter != null ) { instrText.append( parameter ); offsetResolved = true; } } else { String methodProto = DexOffsetResolver .getInlineMethodNameFromIndex( inlineOffset, dexSignatureBlock.getOptVersion() ); if( methodProto != null ) { String proto = ""; int idx = methodProto.indexOf( ',' ); if( idx >= 0 ) { proto = methodProto.substring( idx+1 ); methodProto = methodProto.substring( 0,idx ); } String parameter = methodProto+"\t; "+proto+ " , inline #0x"+ Integer.toHexString( inlineOffset ); Long key = new Long( file.getFilePointer() ); quickParameterMap.put( key,parameter ); instrText.append( parameter ); String resultType = DexMethodIdsBlock.getResultType( proto ); if( "V".equals( resultType ) ) regMap.remove( REGMAP_RESULT_KEY ); else regMap.put( REGMAP_RESULT_KEY, convertJavaTypeToInternal( resultType ) ); affectedRegisters = getAffectedRegistersForRange( proto, rangestart,1 ); offsetResolved = true; } } if( !offsetResolved ) instrText.append( "inline #0x"+ Integer.toHexString( inlineOffset ) ); } break; case FILLEDARRAY_RANGE: { int regno = read8Bit(); int typeidx = read16Bit(); int rangestart = read16Bit(); int rangeend = rangestart + regno - 1; if( regno == 1 ) instrText.append( "{v"+ rangestart+ "}" ); else instrText.append( "{v"+ rangestart+ "..v"+ rangeend+ "}" ); affectedRegisters = new int[ regno ]; for( int i = 0 ; i < regno ; ++i ) affectedRegisters[i] = rangestart + i; String arrayType = dexTypeIdsBlock.getType( typeidx ); instrText.append( ","+arrayType ); regMap.put( REGMAP_RESULT_KEY, convertJavaTypeToInternal( arrayType ) ); } break; case NEWARRAY: { int regs = read8Bit(); int typeidx = read16Bit(); int targetreg = regs & 0xF; int sizereg = ( regs & 0xF0 ) >> 4; String arrayType = dexTypeIdsBlock.getType( typeidx ); regMap.put( new Integer( targetreg ),arrayType ); affectedRegisters = new int[2]; affectedRegisters[0] = targetreg; affectedRegisters[1] = sizereg; instrText.append( "v"+ targetreg+ ",v"+ sizereg+ ","+ arrayType ); } break; case FILLARRAYDATA: { int reg = read8Bit(); int offset = readSigned32Bit(); long target = instrBase + ( (long)offset * 2L ); instrText.append( "v"+ reg+ ","+ "l"+Long.toHexString( target ) ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; if( !secondPass ) { FillArrayTask fillArrayTask = new FillArrayTask( this, instrBase, target ); tasks.add( fillArrayTask ); } updateLowestDataBlock( target ); } break; case ONEREGFIELD_READ: { int reg = read8Bit(); int fieldidx = read16Bit(); instrText.append( "v"+reg+","+ dexFieldIdsBlock.getField( fieldidx ) ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),TYPE_SINGLE_LENGTH ); } break; case ONEREGFIELD_READ_WIDE: { int reg = read8Bit(); int fieldidx = read16Bit(); instrText.append( "v"+reg+","+ dexFieldIdsBlock.getField( fieldidx ) ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),TYPE_DOUBLE_LENGTH ); } break; case ONEREGFIELD_READ_OBJECT: { int reg = read8Bit(); int fieldidx = read16Bit(); instrText.append( "v"+reg+","+ dexFieldIdsBlock.getField( fieldidx ) ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ), dexFieldIdsBlock.getFieldType( fieldidx ) ); } break; case ONEREGFIELD_WRITE: { int reg = read8Bit(); int fieldidx = read16Bit(); instrText.append( "v"+reg+","+ dexFieldIdsBlock.getField( fieldidx ) ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ), dexFieldIdsBlock.getFieldType( fieldidx ) ); } break; case TWOREGSFIELD_READ: { int b1 = read8Bit(); int reg1 = b1 & 0xF; int reg2 = ( b1 & 0xF0 ) >> 4; int fieldidx = read16Bit(); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; instrText.append( "v"+reg1+",v"+reg2+","+ dexFieldIdsBlock.getField( fieldidx ) ); regMap.put( new Integer( reg1 ),TYPE_SINGLE_LENGTH ); } break; case TWOREGSFIELD_READ_WIDE: { int b1 = read8Bit(); int reg1 = b1 & 0xF; int reg2 = ( b1 & 0xF0 ) >> 4; int fieldidx = read16Bit(); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; instrText.append( "v"+reg1+",v"+reg2+","+ dexFieldIdsBlock.getField( fieldidx ) ); regMap.put( new Integer( reg1 ),TYPE_DOUBLE_LENGTH ); } break; case TWOREGSFIELD_READ_OBJECT: { int b1 = read8Bit(); int reg1 = b1 & 0xF; int reg2 = ( b1 & 0xF0 ) >> 4; int fieldidx = read16Bit(); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; instrText.append( "v"+reg1+",v"+reg2+","+ dexFieldIdsBlock.getField( fieldidx ) ); regMap.put( new Integer( reg1 ), dexFieldIdsBlock.getFieldType( fieldidx ) ); } break; case TWOREGSFIELD_WRITE: { int b1 = read8Bit(); int reg1 = b1 & 0xF; int reg2 = ( b1 & 0xF0 ) >> 4; int fieldidx = read16Bit(); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; instrText.append( "v"+reg1+",v"+reg2+","+ dexFieldIdsBlock.getField( fieldidx ) ); } break; case NOPARAMETER: { int b = read8Bit(); } break; case REGCONST16: { int targetreg = read8Bit(); int constant = read16Bit(); affectedRegisters = new int[1]; affectedRegisters[0] = targetreg; instrText.append( "v"+targetreg+","+ Integer.toString( constant ) ); regMap.put( new Integer( targetreg ),TYPE_SINGLE_LENGTH ); } break; case REGCONST16_WIDE: { int targetreg = read8Bit(); int constant = read16Bit(); affectedRegisters = new int[1]; affectedRegisters[0] = targetreg; instrText.append( "v"+targetreg+","+ Integer.toString( constant ) ); regMap.put( new Integer( targetreg ),TYPE_DOUBLE_LENGTH ); } break; case THREEREGS: { int reg1 = read8Bit(); int reg2 = read8Bit(); int reg3 = read8Bit(); instrText.append( "v"+reg1+",v"+reg2+",v"+reg3 ); affectedRegisters = new int[3]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; affectedRegisters[2] = reg3; regMap.put( new Integer( reg1 ),TYPE_SINGLE_LENGTH ); } break; case THREEREGS_WIDE: { int reg1 = read8Bit(); int reg2 = read8Bit(); int reg3 = read8Bit(); instrText.append( "v"+reg1+",v"+reg2+",v"+reg3 ); affectedRegisters = new int[3]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; affectedRegisters[2] = reg3; regMap.put( new Integer( reg1 ),TYPE_DOUBLE_LENGTH ); } break; case AGET: { int reg1 = read8Bit(); int reg2 = read8Bit(); int reg3 = read8Bit(); instrText.append( "v"+reg1+",v"+reg2+",v"+reg3 ); affectedRegisters = new int[3]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; affectedRegisters[2] = reg3; String arrayType = regMap.get( new Integer( reg2 ) ); String elementType = TYPE_UNKNOWN; if( ( arrayType != null ) && arrayType.startsWith( "[" ) ) elementType = convertJavaTypeToInternal( arrayType.substring( 1 ) ); regMap.put( new Integer( reg1 ),elementType ); } break; case APUT: { int reg1 = read8Bit(); int reg2 = read8Bit(); int reg3 = read8Bit(); instrText.append( "v"+reg1+",v"+reg2+",v"+reg3 ); affectedRegisters = new int[3]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; affectedRegisters[2] = reg3; } break; case PACKEDSWITCH: { int reg = read8Bit(); int offset = readSigned32Bit(); long target = instrBase + ( (long)offset * 2L ); instrText.append( "v"+reg ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; generateText = false; if( !secondPass ) { PackedSwitchTask packedSwitchTask = new PackedSwitchTask( this, instrBase, target ); packedSwitchTask.setReg( reg ); tasks.add( packedSwitchTask ); forkData = packedSwitchTask.readJumpTable(); forkStatus = ForkStatus.FORK_AND_CONTINUE; } updateLowestDataBlock( target ); } break; case SPARSESWITCH: { int reg = read8Bit(); int offset = readSigned32Bit(); long target = instrBase + ( (long)offset * 2L ); instrText.append( "v"+reg ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; generateText = false; if( !secondPass ) { SparseSwitchTask sparseSwitchTask = new SparseSwitchTask( this, instrBase, target ); sparseSwitchTask.setReg( reg ); tasks.add( sparseSwitchTask ); forkData = sparseSwitchTask.readJumpTable(); forkStatus = ForkStatus.FORK_AND_CONTINUE; } updateLowestDataBlock( target ); } break; case MOVERESULT: { int reg = read8Bit(); instrText.append( "v"+reg ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),regMap.get( REGMAP_RESULT_KEY ) ); } break; case ONEREG: { int reg = read8Bit(); instrText.append( "v"+reg ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; } break; case OFFSET8: { long target = calculateTarget( instrBase ); instrText.append( labelForAddress( target ) ); forkData = new long[1]; forkData[0] = target; forkStatus = ForkStatus.FORK_UNCONDITIONALLY; } break; case CHECKCAST: { int reg = read8Bit(); int typeidx = read16Bit(); String castType = dexTypeIdsBlock.getClassName( typeidx ); instrText.append( "v"+reg+ ","+castType ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; if( !castType.startsWith( "[" ) ) castType = "L" + castType + ";"; regMap.put( new Integer( reg ),castType ); } break; case NEWINSTANCE: { int reg = read8Bit(); int typeidx = read16Bit(); String type = dexTypeIdsBlock.getClassName( typeidx ); instrText.append( "v"+reg+ ","+type ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),"L"+type+";" ); } break; case TWOREGSTYPE: { int b1 = read8Bit(); int reg1 = b1 & 0xF; int reg2 = ( b1 & 0xF0 ) >> 4; int typeidx = read16Bit(); instrText.append( "v"+reg1+ ",v"+reg2+ ","+dexTypeIdsBlock.getClassName( typeidx ) ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),TYPE_SINGLE_LENGTH ); } break; case REGOFFSET16: { int reg = read8Bit(); long target = calculateTarget16Bit( instrBase ); instrText.append( "v"+reg+ ","+labelForAddress( target ) ); forkData = new long[1]; forkData[0] = target; forkStatus = ForkStatus.FORK_AND_CONTINUE; affectedRegisters = new int[1]; affectedRegisters[0] = reg; } break; case OFFSET16: { int padding = read8Bit(); long target = calculateTarget16Bit( instrBase ); instrText.append( labelForAddress( target ) ); forkData = new long[1]; forkData[0] = target; forkStatus = ForkStatus.FORK_UNCONDITIONALLY; } break; case TWOREGSOFFSET16: { int b1 = read8Bit(); int reg1 = b1 & 0xF; int reg2 = ( b1 & 0xF0 ) >> 4; long target = calculateTarget16Bit( instrBase ); instrText.append( "v"+reg1+",v"+reg2+ ","+labelForAddress( target ) ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; forkData = new long[1]; forkData[0] = target; forkStatus = ForkStatus.FORK_AND_CONTINUE; } break; case MOVE: { int b1 = read8Bit(); int reg1 = b1 & 0xF; int reg2 = ( b1 & 0xF0 ) >> 4; instrText.append( "v"+reg1+",v"+reg2 ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),regMap.get( new Integer( reg2 ) ) ); } break; case MOVE_OBJECT: { int b1 = read8Bit(); int reg1 = b1 & 0xF; int reg2 = ( b1 & 0xF0 ) >> 4; instrText.append( "v"+reg1+",v"+reg2 ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),getRegType( instrBase,reg2 ) ); } break; case TWOREGSPACKED_SINGLE: { int b1 = read8Bit(); int reg1 = b1 & 0xF; int reg2 = ( b1 & 0xF0 ) >> 4; instrText.append( "v"+reg1+",v"+reg2 ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),TYPE_SINGLE_LENGTH ); } break; case TWOREGSPACKED_DOUBLE: { int b1 = read8Bit(); int reg1 = b1 & 0xF; int reg2 = ( b1 & 0xF0 ) >> 4; instrText.append( "v"+reg1+",v"+reg2 ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),TYPE_DOUBLE_LENGTH ); } break; case TWOREGSCONST8: { int reg1 = read8Bit(); int reg2 = read8Bit(); int constant = read8Bit(); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),TYPE_SINGLE_LENGTH ); instrText.append( "v"+reg1+",v"+reg2+","+constant ); } break; case REGCLASSCONST: { int reg = read8Bit(); int typeidx = read16Bit(); String type = dexTypeIdsBlock.getClassName( typeidx ); instrText.append( "v"+ reg+ ","+ type ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),"Ljava/lang/Class;" ); } break; case REGCONST32: { int reg = read8Bit(); long constant = read32Bit(); instrText.append( "v"+ reg+ ","+ constant+ "\t; 0x"+ Long.toHexString( constant ) ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),TYPE_SINGLE_LENGTH ); } break; case REGCONST32_WIDE: { int reg = read8Bit(); long constant = read32Bit(); instrText.append( "v"+ reg+ ","+ constant+ "\t; 0x"+ Long.toHexString( constant ) ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),TYPE_DOUBLE_LENGTH ); } break; case REGCONST64: { int reg = read8Bit(); long const1 = read32Bit(); long const2 = read32Bit(); long constant = const2 << 32 | const1; instrText.append( "v"+ reg+ ","+ constant+ "\t; 0x"+ Long.toHexString( constant ) ); affectedRegisters = new int[1]; affectedRegisters[0] = reg; regMap.put( new Integer( reg ),TYPE_DOUBLE_LENGTH ); } break; case REG8REG16: { int reg1 = read8Bit(); int reg2 = read16Bit(); instrText.append( "v"+ reg1+ ",v"+ reg2 ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),regMap.get( new Integer( reg2 ) ) ); } break; case REG8REG16_OBJECT: { int reg1 = read8Bit(); int reg2 = read16Bit(); instrText.append( "v"+ reg1+ ",v"+ reg2 ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),getRegType( instrBase,reg2 ) ); } break; case TWOREGSPACKEDCONST16: { int reg = read8Bit(); int reg1 = reg & 0xF; int reg2 = ( reg & 0xF0 ) >> 4; int constant = read16Bit(); instrText.append( "v"+reg1+ ",v"+reg2+ ","+constant ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),TYPE_SINGLE_LENGTH ); } break; case TWOREGSQUICKOFFSET: { int reg = read8Bit(); int reg1 = reg & 0xF; int reg2 = ( reg & 0xF0 ) >> 4; int constant = read16Bit(); String baseClass = null; if( dexOffsetResolver != null ) baseClass = regMap.get( new Integer( reg2 ) ); if( baseClass != null ) baseClass = DexTypeIdsBlock.LTypeToJava( baseClass ); instrText.append( "v"+reg1+ ",v"+reg2+ "," ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),TYPE_SINGLE_LENGTH ); boolean offsetResolved = false; if( secondPass ) { Long key = new Long( file.getFilePointer() ); String parameter = quickParameterMap.get( key ); if( parameter != null ) { instrText.append( parameter ); offsetResolved = true; } } else { if( baseClass != null ) { String fieldName = dexOffsetResolver.getFieldNameFromOffset( baseClass, constant ); if( fieldName != null ) { Long key = new Long( file.getFilePointer() ); fieldName += "\t;[obj+0x"+ Integer.toHexString( constant )+ "]"; quickParameterMap.put( key,fieldName ); instrText.append( fieldName ); offsetResolved = true; } } } if( !offsetResolved ) instrText.append( "[obj+0x"+ Integer.toHexString( constant )+ "]" ); } break; case TWOREGSQUICKOFFSET_WIDE: { int reg = read8Bit(); int reg1 = reg & 0xF; int reg2 = ( reg & 0xF0 ) >> 4; int constant = read16Bit(); String baseClass = null; if( dexOffsetResolver != null ) baseClass = regMap.get( new Integer( reg2 ) ); if( baseClass != null ) baseClass = DexTypeIdsBlock.LTypeToJava( baseClass ); instrText.append( "v"+reg1+ ",v"+reg2+ "," ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),TYPE_DOUBLE_LENGTH ); boolean offsetResolved = false; if( secondPass ) { Long key = new Long( file.getFilePointer() ); String parameter = quickParameterMap.get( key ); if( parameter != null ) { instrText.append( parameter ); offsetResolved = true; } } else { if( baseClass != null ) { String fieldName = dexOffsetResolver.getFieldNameFromOffset( baseClass, constant ); if( fieldName != null ) { Long key = new Long( file.getFilePointer() ); fieldName += "\t;[obj+0x"+ Integer.toHexString( constant )+ "]"; quickParameterMap.put( key,fieldName ); instrText.append( fieldName ); offsetResolved = true; } } } if( !offsetResolved ) instrText.append( "[obj+0x"+ Integer.toHexString( constant )+ "]" ); } break; case TWOREGSQUICKOFFSET_OBJECT: { int reg = read8Bit(); int reg1 = reg & 0xF; int reg2 = ( reg & 0xF0 ) >> 4; int constant = read16Bit(); String baseClass = null; if( dexOffsetResolver != null ) baseClass = regMap.get( new Integer( reg2 ) ); if( baseClass != null ) baseClass = DexTypeIdsBlock.LTypeToJava( baseClass ); instrText.append( "v"+reg1+ ",v"+reg2+ "," ); boolean offsetResolved = false; String resultType = "L<unknown>;"; if( secondPass ) { Long key = new Long( file.getFilePointer() ); String parameter = quickParameterMap.get( key ); if( parameter != null ) { instrText.append( parameter ); offsetResolved = true; } } else { if( baseClass != null ) { String fieldName = dexOffsetResolver.getFieldNameFromOffset( baseClass, constant ); if( fieldName != null ) { int idx = fieldName.indexOf( ' ' ); if( idx >= 0 ); resultType = fieldName.substring( idx+1 ); fieldName += "\t;[obj+0x"+ Integer.toHexString( constant )+ "]"; Long key = new Long( file.getFilePointer() ); quickParameterMap.put( key,fieldName ); instrText.append( fieldName ); offsetResolved = true; } } } if( !offsetResolved ) instrText.append( "[obj+0x"+ Integer.toHexString( constant )+ "]" ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; regMap.put( new Integer( reg1 ),resultType ); } break; case TWOREGSQUICKOFFSET_WRITE: { int reg = read8Bit(); int reg1 = reg & 0xF; int reg2 = ( reg & 0xF0 ) >> 4; int constant = read16Bit(); String baseClass = null; if( dexOffsetResolver != null ) baseClass = regMap.get( new Integer( reg2 ) ); if( baseClass != null ) baseClass = DexTypeIdsBlock.LTypeToJava( baseClass ); instrText.append( "v"+reg1+ ",v"+reg2+ "," ); affectedRegisters = new int[2]; affectedRegisters[0] = reg1; affectedRegisters[1] = reg2; boolean offsetResolved = false; if( secondPass ) { Long key = new Long( file.getFilePointer() ); String parameter = quickParameterMap.get( key ); if( parameter != null ) { instrText.append( parameter ); offsetResolved = true; } } else { if( baseClass != null ) { String fieldName = dexOffsetResolver.getFieldNameFromOffset( baseClass, constant ); if( fieldName != null ) { Long key = new Long( file.getFilePointer() ); fieldName += "\t;[obj+0x"+ Integer.toHexString( constant )+ "]"; quickParameterMap.put( key,fieldName ); instrText.append( fieldName ); offsetResolved = true; } } } if( !offsetResolved ) instrText.append( "[obj+0x"+ Integer.toHexString( constant )+ "]" ); } break; } if( generateText ) dump( "\t"+instrText ); }  void initializePass( boolean secondPass ); void parse(); void setDexSignatureBlock( DexSignatureBlock dexSignatureBlock ); void setDexStringIdsBlock( DexStringIdsBlock dexStringIdsBlock ); void setDexTypeIdsBlock( DexTypeIdsBlock dexTypeIdsBlock ); void setDexFieldIdsBlock( DexFieldIdsBlock dexFieldIdsBlock ); void setDexOffsetResolver( DexOffsetResolver dexOffsetResolver ); void setDexMethodIdsBlock( DexMethodIdsBlock dexMethodIdsBlock ); void setPass( boolean secondPass ); void setRegTraces( ArrayList<RegisterTraces> regTraces ); DedexerTask getTaskForAddress( long address ); void postPassProcessing( boolean secondPass ); void setCodeGenerator( CodeGenerator codeGenerator ); CodeGenerator getCodeGenerator(); long getLowestDataBlock(); static String labelForAddress( long address ); void placeTask( long target, DedexerTask label ); void placeLabel( long target, String labelName ); ForkStatus getForkStatus(); long[] getForkData(); HashMap<Integer,String> getRegisterMap(); void setRegisterMap( HashMap<Integer,String> regMap ); int[] getAffectedRegisters(); static String convertJavaTypeToInternal( String javaType ); static final Integer REGMAP_RESULT_KEY; static final String TYPE_SINGLE_LENGTH; static final String TYPE_DOUBLE_LENGTH; static final String TYPE_UNKNOWN; }
@Test public void testInstrParser() throws IOException,UnknownInstructionException { String lineseparator = System.getProperty( "line.separator" ); initOutputStream(); dib.parse( 0x374L ); assertEquals( "\tconst/4\tv1,0"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse(); assertEquals( "\tconst-string\tv0,\"hello.txt\""+lineseparator,getOutputStream() ); initOutputStream(); dib.parse(); assertEquals( "\tinvoke-direct\t{v2,v0},java/io/File/<init>\t; <init>(Ljava/lang/String;)V"+lineseparator, getOutputStream() ); initOutputStream(); dib.parse( 0x382L ); assertEquals( "\tnew-array\tv0,v0,[I"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse(); assertEquals( "\tfill-array-data\tv0,l3a0"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse(); assertEquals( "\tiput-object\tv0,v2,Test3.array [I"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x39eL ); assertEquals( "\treturn-void\t"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x3c6L ); assertEquals( "\tconst/16\tv0,10"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x3ceL ); assertEquals( "\taput-boolean\tv1,v0,v1"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x420L ); assertEquals( "\treturn\tv0"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x424L ); assertEquals( "\tgoto\tl420"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x49cL ); assertEquals( "\tinstance-of\tv0,v4,Test3"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x4a0L ); assertEquals( "\tif-nez\tv0,l4aa"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse(); assertEquals( "\tmove\tv0,v2"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x4aaL ); assertEquals( "\tcheck-cast\tv4,Test3"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x4b6L ); assertEquals( "\tif-ne\tv0,v1,l4be"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x4ecL ); assertEquals( "\tnew-instance\tv0,java/io/IOException"+lineseparator, getOutputStream() ); initOutputStream(); dib.parse( 0x51cL ); assertEquals( "\tadd-int/lit8\tv0,v0,1"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x56cL ); assertEquals( "\tconst-class\tv0,Test3"+lineseparator,getOutputStream() ); initOutputStream(); dib.parse( 0x588L ); assertEquals( "\tsget\tv0,Test3.is1 I"+lineseparator,getOutputStream() ); }