String.format("AnalysisException: %s", analysisException.getMessage()), analysisException.codeAddress, Integer.MIN_VALUE)); analysisException.printStackTrace(System.err);
private static void checkWidePair(int registerNumber, AnalyzedInstruction analyzedInstruction) { if (registerNumber + 1 >= analyzedInstruction.postRegisterMap.length) { throw new AnalysisException(String.format("v%d cannot be used as the first register in a wide register" + "pair because it is the last register.", registerNumber)); } }
int codeAddress = getInstructionAddress(instructionToAnalyze); ex.codeAddress = codeAddress; ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.getOpcode().name)); ex.addContext(String.format("code address: %d", codeAddress)); ex.addContext(String.format("method: %s", ReferenceUtil.getReferenceString(method))); break;
void handleAnalysisException(AnalysisException ae) { LLog.e("Analysis error in class=" + mCurrentMethod.getDefiningClass() + " method=" + mCurrentMethod.getName() + "\n" + ae.getContext()); final StackTraceElement[] stacks = ae.getCause() == null ? ae.getStackTrace() : ae.getCause().getStackTrace(); if (LLog.VERBOSE || stacks.length < 10) { LLog.ex(ae); final int printLine = 5; StringBuilder sb = new StringBuilder(1024); sb.append(ae.toString()).append("\n"); int i = 0; int s = Math.min(printLine, stacks.length);
private static void checkRegister(RegisterType registerType, int registerNumber, BitSet validCategories) { if (!validCategories.get(registerType.category)) { throw new AnalysisException(String.format("Invalid register type %s for register v%d.", registerType.toString(), registerNumber)); } }
String.format("AnalysisException: %s", analysisException.getMessage()), analysisException.codeAddress, Integer.MIN_VALUE)); analysisException.printStackTrace(System.err);
int codeAddress = getInstructionAddress(instructionToAnalyze); ex.codeAddress = codeAddress; ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.getOpcode().name)); ex.addContext(String.format("code address: %d", codeAddress)); ex.addContext(String.format("method: %s", ReferenceUtil.getReferenceString(method))); break;
@Nonnull public Opcode getAndCheckDeodexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) { FieldOpcode fieldOpcode = opcodeMap[isGet(odexedOpcode)?GET:PUT] [isStatic(odexedOpcode)?STATIC:INSTANCE] [getTypeIndex(fieldType.charAt(0))]; if (!isCompatible(odexedOpcode, fieldOpcode.type)) { throw new AnalysisException(String.format("Incorrect field type \"%s\" for %s", fieldType, odexedOpcode.name)); } return fieldOpcode.normalOpcode; }
String.format("AnalysisException: %s", analysisException.getMessage()), analysisException.codeAddress, Integer.MIN_VALUE)); analysisException.printStackTrace(System.err);
int codeAddress = getInstructionAddress(instructionToAnalyze); ex.codeAddress = codeAddress; ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.getOpcode().name)); ex.addContext(String.format("code address: %d", codeAddress)); ex.addContext(String.format("method: %s", ReferenceUtil.getReferenceString(method))); break;
@Nonnull public static RegisterType getRegisterType(@Nonnull ClassPath classPath, @Nonnull CharSequence type) { switch (type.charAt(0)) { case 'Z': return BOOLEAN_TYPE; case 'B': return BYTE_TYPE; case 'S': return SHORT_TYPE; case 'C': return CHAR_TYPE; case 'I': return INTEGER_TYPE; case 'F': return FLOAT_TYPE; case 'J': return LONG_LO_TYPE; case 'D': return DOUBLE_LO_TYPE; case 'L': case '[': return getRegisterType(REFERENCE, classPath.getClass(type)); default: throw new AnalysisException("Invalid type: " + type); } }
String.format("AnalysisException: %s", analysisException.getMessage()), analysisException.codeAddress, Integer.MIN_VALUE)); analysisException.printStackTrace(System.err);
private void analyzeNewArray(@Nonnull AnalyzedInstruction analyzedInstruction) { ReferenceInstruction instruction = (ReferenceInstruction)analyzedInstruction.instruction; TypeReference type = (TypeReference)instruction.getReference(); if (type.getType().charAt(0) != '[') { throw new AnalysisException("new-array used with non-array type"); } RegisterType arrayType = RegisterType.getRegisterType(classPath, type); setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); }
private void analyzeAgetWide(@Nonnull AnalyzedInstruction analyzedInstruction) { ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); if (arrayRegisterType.category != RegisterType.NULL) { if (arrayRegisterType.category != RegisterType.REFERENCE || !(arrayRegisterType.type instanceof ArrayProto)) { throw new AnalysisException("aget-wide used with non-array register: %s", arrayRegisterType.toString()); } ArrayProto arrayProto = (ArrayProto)arrayRegisterType.type; if (arrayProto.dimensions != 1) { throw new AnalysisException("aget-wide used with multi-dimensional array: %s", arrayRegisterType.toString()); } char arrayBaseType = arrayProto.getElementType().charAt(0); if (arrayBaseType == 'J') { setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.LONG_LO_TYPE); } else if (arrayBaseType == 'D') { setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.DOUBLE_LO_TYPE); } else { throw new AnalysisException("aget-wide used with narrow array: %s", arrayRegisterType); } } else { // If the array register is null, we can assume that the destination register was meant to be a wide type. // This is the same behavior as dalvik's verifier setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.LONG_LO_TYPE); } }
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 analyzeAgetObject(@Nonnull AnalyzedInstruction analyzedInstruction) { ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); if (arrayRegisterType.category != RegisterType.NULL) { if (arrayRegisterType.category != RegisterType.REFERENCE || !(arrayRegisterType.type instanceof ArrayProto)) { throw new AnalysisException("aget-object used with non-array register: %s", arrayRegisterType.toString()); } ArrayProto arrayProto = (ArrayProto)arrayRegisterType.type; String elementType = arrayProto.getImmediateElementType(); setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass(elementType))); } else { // If the array register is null, we can assume that the destination register was meant to be a reference // type, so we set the destination to NULL. This is the same behavior as dalvik's verifier setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.NULL_TYPE); } }
private void analyzeMoveResult(@Nonnull AnalyzedInstruction analyzedInstruction) { AnalyzedInstruction previousInstruction = null; if (analyzedInstruction.instructionIndex > 0) { previousInstruction = analyzedInstructions.valueAt(analyzedInstruction.instructionIndex-1); } if (previousInstruction == null || !previousInstruction.instruction.getOpcode().setsResult()) { throw new AnalysisException(analyzedInstruction.instruction.getOpcode().name + " must occur after an " + "invoke-*/fill-new-array instruction"); } RegisterType resultRegisterType; ReferenceInstruction invokeInstruction = (ReferenceInstruction)previousInstruction.instruction; Reference reference = invokeInstruction.getReference(); if (reference instanceof MethodReference) { resultRegisterType = RegisterType.getRegisterType(classPath, ((MethodReference)reference).getReturnType()); } else { resultRegisterType = RegisterType.getRegisterType(classPath, (TypeReference)reference); } setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType); }
private void analyzeExecuteInlineRange(@Nonnull AnalyzedInstruction analyzedInstruction) { if (inlineResolver == null) { throw new AnalysisException("Cannot analyze an odexed instruction unless we are deodexing"); } Instruction3rmi instruction = (Instruction3rmi)analyzedInstruction.instruction; Method resolvedMethod = inlineResolver.resolveExecuteInline(analyzedInstruction); Opcode deodexedOpcode; int acccessFlags = resolvedMethod.getAccessFlags(); if (AccessFlags.STATIC.isSet(acccessFlags)) { deodexedOpcode = Opcode.INVOKE_STATIC_RANGE; } else if (AccessFlags.PRIVATE.isSet(acccessFlags)) { deodexedOpcode = Opcode.INVOKE_DIRECT_RANGE; } else { deodexedOpcode = Opcode.INVOKE_VIRTUAL_RANGE; } Instruction3rc deodexedInstruction = new ImmutableInstruction3rc(deodexedOpcode, instruction.getStartRegister(), instruction.getRegisterCount(), resolvedMethod); analyzedInstruction.setDeodexedInstruction(deodexedInstruction); analyzeInstruction(analyzedInstruction); }
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); }
throw new AnalysisException("Execution can continue past the last instruction"); instructionCodeAddress + offsetInstruction.getCodeOffset()); if (analyzedSwitchPayload == null) { throw new AnalysisException("Invalid switch payload offset"); switchElement.getOffset()); if (targetInstruction == null) { throw new AnalysisException("Invalid switch target offset");