private void addAnalyzedInstructionMethodItems(List<MethodItem> methodItems) { MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classDef.options.classPath, method, classDef.options.inlineResolver, classDef.options.normalizeVirtualMethods); AnalysisException analysisException = methodAnalyzer.getAnalysisException(); if (analysisException != null) { List<AnalyzedInstruction> instructions = methodAnalyzer.getAnalyzedInstructions();
setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, RegisterType.getRegisterType(RegisterType.UNINIT_THIS, classPath.getClass(method.getDefiningClass()))); } else { setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass(method.getDefiningClass()))); propagateParameterTypes(totalRegisters-parameterRegisters+1); } else { propagateParameterTypes(totalRegisters-parameterRegisters); setPostRegisterTypeAndPropagateChanges(startOfMethod, i, uninit); if (!analyzeInstruction(instructionToAnalyze)) { undeodexedInstructions.set(i); continue; int codeAddress = getInstructionAddress(instructionToAnalyze); ex.codeAddress = codeAddress; ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.getOpcode().name)); switch (instruction.getOpcode().format) { case Format10x: analyzeOdexReturnVoid(analyzedInstruction, false); continue; case Format21c: case Format22c: analyzePutGetVolatile(analyzedInstruction, false);
AnalyzedInstruction instruction = analyzedInstructions.valueAt(i); Opcode instructionOpcode = instruction.instruction.getOpcode(); currentCodeAddress = getInstructionAddress(instruction); currentExceptionHandlers = buildExceptionHandlerArray(tryBlock); BitSet instructionsToProcess = new BitSet(instructions.size()); addPredecessorSuccessor(startOfMethod, analyzedInstructions.valueAt(0), exceptionHandlers, instructionsToProcess); while (!instructionsToProcess.isEmpty()) { int currentInstructionIndex = instructionsToProcess.nextSetBit(0); int instructionCodeAddress = getInstructionAddress(instruction); addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers, instructionsToProcess); addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess); AnalyzedInstruction targetInstruction = analyzedInstructions.get(instructionCodeAddress + targetAddressOffset); addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess);
case MOVE_OBJECT_FROM16: case MOVE_OBJECT_16: analyzeMove(analyzedInstruction); return true; case MOVE_RESULT: case MOVE_RESULT_WIDE: case MOVE_RESULT_OBJECT: analyzeMoveResult(analyzedInstruction); return true; case MOVE_EXCEPTION: analyzeMoveException(analyzedInstruction); return true; case RETURN_VOID: case RETURN_VOID_BARRIER: case RETURN_VOID_NO_BARRIER: analyzeOdexReturnVoid(analyzedInstruction); return true; case CONST_4: case CONST: case CONST_HIGH16: analyzeConst(analyzedInstruction); return true; case CONST_WIDE_16: case CONST_WIDE: case CONST_WIDE_HIGH16: analyzeWideConst(analyzedInstruction);
private void addPredecessorSuccessor(@Nonnull AnalyzedInstruction predecessor, @Nonnull AnalyzedInstruction successor, @Nonnull AnalyzedInstruction[][] exceptionHandlers, @Nonnull BitSet instructionsToProcess, boolean allowMoveException) { if (!allowMoveException && successor.instruction.getOpcode() == Opcode.MOVE_EXCEPTION) { throw new AnalysisException("Execution can pass from the " + predecessor.instruction.getOpcode().name + " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) + " to the move-exception instruction at address 0x" + Integer.toHexString(getInstructionAddress(successor))); } if (!successor.addPredecessor(predecessor)) { return; } predecessor.addSuccessor(successor); instructionsToProcess.set(successor.getInstructionIndex()); //if the successor can throw an instruction, then we need to add the exception handlers as additional //successors to the predecessor (and then apply this same logic recursively if needed) //Technically, we should handle the monitor-exit instruction as a special case. The exception is actually //thrown *after* the instruction executes, instead of "before" the instruction executes, lke for any other //instruction. But since it doesn't modify any registers, we can treat it like any other instruction. AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex]; if (exceptionHandlersForSuccessor != null) { //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction //can throw an exception assert successor.instruction.getOpcode().canThrow(); for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) { addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, instructionsToProcess, true); } } }
public MethodAnalyzer(@Nonnull ClassPath classPath, @Nonnull Method method, @Nullable InlineMethodResolver inlineResolver, boolean normalizeVirtualMethods) { this.classPath = classPath; this.inlineResolver = inlineResolver; this.normalizeVirtualMethods = normalizeVirtualMethods; this.method = method; MethodImplementation methodImpl = method.getImplementation(); if (methodImpl == null) { throw new IllegalArgumentException("The method has no implementation"); } this.methodImpl = methodImpl; // Override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't // have to handle the case this special case of instruction being null, in the main class startOfMethod = new AnalyzedInstruction(this, new ImmutableInstruction10x(Opcode.NOP), -1, methodImpl.getRegisterCount()) { @Override protected boolean addPredecessor(AnalyzedInstruction predecessor) { throw new UnsupportedOperationException(); } @Override @Nonnull public RegisterType getPredecessorRegisterType(@Nonnull AnalyzedInstruction predecessor, int registerNumber) { throw new UnsupportedOperationException(); } }; buildInstructionList(); analyzedState = new BitSet(analyzedInstructions.size()); paramRegisterCount = MethodUtil.getParameterRegisterCount(method); analyze(); }
@Test public void testInstanceOfNarrowingNez_art() throws IOException { MethodImplementationBuilder builder = new MethodImplementationBuilder(2); builder.addInstruction(new BuilderInstruction22c(Opcode.INSTANCE_OF, 0, 1, new ImmutableTypeReference("Lmain;"))); builder.addInstruction(new BuilderInstruction21t(Opcode.IF_NEZ, 0, builder.getLabel("instance_of"))); builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); builder.addLabel("instance_of"); builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); MethodImplementation methodImplementation = builder.getMethodImplementation(); Method method = new ImmutableMethod("Lmain;", "narrowing", Collections.singletonList(new ImmutableMethodParameter("Ljava/lang/Object;", null, null)), "V", AccessFlags.PUBLIC.getValue(), null, methodImplementation); ClassDef classDef = new ImmutableClassDef("Lmain;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null, null, null, null, Collections.singletonList(method)); DexFile dexFile = new ImmutableDexFile(forArtVersion(56), Collections.singletonList(classDef)); ClassPath classPath = new ClassPath(Lists.newArrayList(new DexClassProvider(dexFile)), true, 56); MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, null, false); List<AnalyzedInstruction> analyzedInstructions = methodAnalyzer.getAnalyzedInstructions(); Assert.assertEquals("Ljava/lang/Object;", analyzedInstructions.get(2).getPreInstructionRegisterType(1).type.getType()); Assert.assertEquals("Lmain;", analyzedInstructions.get(3).getPreInstructionRegisterType(1).type.getType()); }
setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, RegisterType.getRegisterType(RegisterType.UNINIT_THIS, classPath.getClass(method.getDefiningClass()))); } else { setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass(method.getDefiningClass()))); propagateParameterTypes(totalRegisters-parameterRegisters+1); } else { propagateParameterTypes(totalRegisters-parameterRegisters); setPostRegisterTypeAndPropagateChanges(startOfMethod, i, uninit); if (!analyzeInstruction(instructionToAnalyze)) { undeodexedInstructions.set(i); continue; int codeAddress = getInstructionAddress(instructionToAnalyze); ex.codeAddress = codeAddress; ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.getOpcode().name)); switch (instruction.getOpcode().format) { case Format10x: analyzeReturnVoidBarrier(analyzedInstruction, false); continue; case Format21c: case Format22c: analyzePutGetVolatile(analyzedInstruction, false);
Iterable<? extends org.jf.dexlib2.iface.instruction.Instruction> deodex() { try { DexFileModule m = myClass.getContainer(); ClassPathResolver path = new ClassPathResolver(Collections.singletonList(m.getFile().getParent() + '/'), Collections.<String>emptyList(), m.getDexFile()); ClassPath cp = new ClassPath(path.getResolvedClassProviders(), false, m.getDexFile().getOpcodes().artVersion); MethodAnalyzer analyzer = new MethodAnalyzer(cp, eMethod, null, false); return analyzer.getInstructions(); } catch (Exception e) { assert false : e; return eMethod.getImplementation().getInstructions(); } }
private void analyzeMoveException(@Nonnull AnalyzedInstruction analyzedInstruction) { int instructionAddress = getInstructionAddress(analyzedInstruction); RegisterType exceptionType = RegisterType.UNKNOWN_TYPE; for (TryBlock<? extends ExceptionHandler> tryBlock: methodImpl.getTryBlocks()) { for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) { if (handler.getHandlerCodeAddress() == instructionAddress) { String type = handler.getExceptionType(); if (type == null) { exceptionType = RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass("Ljava/lang/Throwable;")); } else { exceptionType = RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass(type)) .merge(exceptionType); } } } } if (exceptionType.category == RegisterType.UNKNOWN) { throw new AnalysisException("move-exception must be the first instruction in an exception handler block"); } setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); }
private void writeFullMerge(IndentingWriter writer, int registerNum) throws IOException { registerFormatter.writeTo(writer, registerNum); writer.write('='); analyzedInstruction.getPreInstructionRegisterType(registerNum).writeTo(writer); writer.write(":merge{"); boolean first = true; for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) { RegisterType predecessorRegisterType = analyzedInstruction.getPredecessorRegisterType( predecessor, registerNum); if (!first) { writer.write(','); } if (predecessor.getInstructionIndex() == -1) { //the fake "StartOfMethod" instruction writer.write("Start:"); } else { writer.write("0x"); writer.printUnsignedLongAsHex(methodAnalyzer.getInstructionAddress(predecessor)); writer.write(':'); } predecessorRegisterType.writeTo(writer); first = false; } writer.write('}'); }
analyzedInstruction.getInstructionIndex() + 1); int nextAddress = getInstructionAddress(analyzedInstruction) + ((Instruction21t)analyzedInstruction.instruction).getCodeOffset(); AnalyzedInstruction branchInstruction = analyzedInstructions.get(nextAddress); overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction, analyzedInstruction, register, newType); overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction, register, originalType); } else { overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction, analyzedInstruction, register, originalType); overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction, register, newType);
@Override public void deOdex(DexFile parentFile, Method method, ClassPath cp) { if (!(parentFile instanceof DexBackedOdexFile)) { throw new RuntimeException("ODEX instruction in non-ODEX file"); } DexBackedOdexFile odexFile = (DexBackedOdexFile) parentFile; InlineMethodResolver inlineMethodResolver = InlineMethodResolver.createInlineMethodResolver(odexFile.getOdexVersion()); MethodAnalyzer analyzer = new MethodAnalyzer(cp, method, inlineMethodResolver, false); targetMethod = inlineMethodResolver.resolveExecuteInline(new AnalyzedInstruction(analyzer, instruction, -1, -1)); }
private void addParamRegs(BitSet registers, int registerCount) { int parameterRegisterCount = methodAnalyzer.getParamRegisterCount(); registers.set(registerCount-parameterRegisterCount, registerCount); }
case MOVE_OBJECT_FROM16: case MOVE_OBJECT_16: analyzeMove(analyzedInstruction); return true; case MOVE_RESULT: case MOVE_RESULT_WIDE: case MOVE_RESULT_OBJECT: analyzeMoveResult(analyzedInstruction); return true; case MOVE_EXCEPTION: analyzeMoveException(analyzedInstruction); return true; case RETURN_VOID: return true; case RETURN_VOID_BARRIER: analyzeReturnVoidBarrier(analyzedInstruction); return true; case CONST_4: case CONST: case CONST_HIGH16: analyzeConst(analyzedInstruction); return true; case CONST_WIDE_16: case CONST_WIDE: case CONST_WIDE_HIGH16: analyzeWideConst(analyzedInstruction);
@Test public void testInstanceOfNarrowingEqz_art() throws IOException { MethodImplementationBuilder builder = new MethodImplementationBuilder(2); builder.addInstruction(new BuilderInstruction22c(Opcode.INSTANCE_OF, 0, 1, new ImmutableTypeReference("Lmain;"))); builder.addInstruction(new BuilderInstruction21t(Opcode.IF_EQZ, 0, builder.getLabel("not_instance_of"))); builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); builder.addLabel("not_instance_of"); builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); MethodImplementation methodImplementation = builder.getMethodImplementation(); Method method = new ImmutableMethod("Lmain;", "narrowing", Collections.singletonList(new ImmutableMethodParameter("Ljava/lang/Object;", null, null)), "V", AccessFlags.PUBLIC.getValue(), null, methodImplementation); ClassDef classDef = new ImmutableClassDef("Lmain;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null, null, null, null, Collections.singletonList(method)); DexFile dexFile = new ImmutableDexFile(forArtVersion(56), Collections.singletonList(classDef)); ClassPath classPath = new ClassPath(Lists.newArrayList(new DexClassProvider(dexFile)), true, 56); MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, null, false); List<AnalyzedInstruction> analyzedInstructions = methodAnalyzer.getAnalyzedInstructions(); Assert.assertEquals("Lmain;", analyzedInstructions.get(2).getPreInstructionRegisterType(1).type.getType()); Assert.assertEquals("Ljava/lang/Object;", analyzedInstructions.get(3).getPreInstructionRegisterType(1).type.getType()); }
Iterable<? extends org.jf.dexlib2.iface.instruction.Instruction> deodex() { try { DexFileModule m = myClass.getContainer(); ClassPathResolver path = new ClassPathResolver(Collections.singletonList(m.getFile().getParent() + '/'), Collections.<String>emptyList(), m.getDexFile()); ClassPath cp = new ClassPath(path.getResolvedClassProviders(), false, m.getDexFile().getOpcodes().artVersion); MethodAnalyzer analyzer = new MethodAnalyzer(cp, eMethod, null, false); return analyzer.getInstructions(); } catch (Exception e) { assert false : e; return eMethod.getImplementation().getInstructions(); } }
private void analyzeMoveException(@Nonnull AnalyzedInstruction analyzedInstruction) { int instructionAddress = getInstructionAddress(analyzedInstruction); RegisterType exceptionType = RegisterType.UNKNOWN_TYPE; for (TryBlock<? extends ExceptionHandler> tryBlock: methodImpl.getTryBlocks()) { for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) { if (handler.getHandlerCodeAddress() == instructionAddress) { String type = handler.getExceptionType(); if (type == null) { exceptionType = RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass("Ljava/lang/Throwable;")); } else { exceptionType = RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass(type)) .merge(exceptionType); } } } } if (exceptionType.category == RegisterType.UNKNOWN) { throw new AnalysisException("move-exception must be the first instruction in an exception handler block"); } setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); }
private void addPredecessorSuccessor(@Nonnull AnalyzedInstruction predecessor, @Nonnull AnalyzedInstruction successor, @Nonnull AnalyzedInstruction[][] exceptionHandlers, @Nonnull BitSet instructionsToProcess, boolean allowMoveException) { if (!allowMoveException && successor.instruction.getOpcode() == Opcode.MOVE_EXCEPTION) { throw new AnalysisException("Execution can pass from the " + predecessor.instruction.getOpcode().name + " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) + " to the move-exception instruction at address 0x" + Integer.toHexString(getInstructionAddress(successor))); } if (!successor.addPredecessor(predecessor)) { return; } predecessor.addSuccessor(successor); instructionsToProcess.set(successor.getInstructionIndex()); //if the successor can throw an instruction, then we need to add the exception handlers as additional //successors to the predecessor (and then apply this same logic recursively if needed) //Technically, we should handle the monitor-exit instruction as a special case. The exception is actually //thrown *after* the instruction executes, instead of "before" the instruction executes, lke for any other //instruction. But since it doesn't modify any registers, we can treat it like any other instruction. AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex]; if (exceptionHandlersForSuccessor != null) { //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction //can throw an exception assert successor.instruction.getOpcode().canThrow(); for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) { addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, instructionsToProcess, true); } } }
private void writeFullMerge(IndentingWriter writer, int registerNum) throws IOException { registerFormatter.writeTo(writer, registerNum); writer.write('='); analyzedInstruction.getPreInstructionRegisterType(registerNum).writeTo(writer); writer.write(":merge{"); boolean first = true; for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) { RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum); if (!first) { writer.write(','); } if (predecessor.getInstructionIndex() == -1) { //the fake "StartOfMethod" instruction writer.write("Start:"); } else { writer.write("0x"); writer.printUnsignedLongAsHex(methodAnalyzer.getInstructionAddress(predecessor)); writer.write(':'); } predecessorRegisterType.writeTo(writer); first = false; } writer.write('}'); }