use of org.jf.dexlib2.analysis.AnalyzedInstruction in project smali by JesusFreke.
the class MethodAnalyzer method analyzeMoveResult.
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);
}
use of org.jf.dexlib2.analysis.AnalyzedInstruction in project smali by JesusFreke.
the class MethodAnalyzer method analyzeInvokeVirtualQuick.
private boolean analyzeInvokeVirtualQuick(@Nonnull AnalyzedInstruction analyzedInstruction, boolean isSuper, boolean isRange) {
int methodIndex;
int objectRegister;
if (isRange) {
Instruction3rms instruction = (Instruction3rms) analyzedInstruction.instruction;
methodIndex = instruction.getVtableIndex();
objectRegister = instruction.getStartRegister();
} else {
Instruction35ms instruction = (Instruction35ms) analyzedInstruction.instruction;
methodIndex = instruction.getVtableIndex();
objectRegister = instruction.getRegisterC();
}
RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, objectRegister, ReferenceOrUninitCategories);
TypeProto objectRegisterTypeProto = objectRegisterType.type;
if (objectRegisterType.category == RegisterType.NULL) {
return false;
}
assert objectRegisterTypeProto != null;
MethodReference resolvedMethod;
if (isSuper) {
// invoke-super is only used for the same class that we're currently in
TypeProto typeProto = classPath.getClass(method.getDefiningClass());
TypeProto superType;
String superclassType = typeProto.getSuperclass();
if (superclassType != null) {
superType = classPath.getClass(superclassType);
} else {
// This is either java.lang.Object, or an UnknownClassProto
superType = typeProto;
}
resolvedMethod = superType.getMethodByVtableIndex(methodIndex);
} else {
resolvedMethod = objectRegisterTypeProto.getMethodByVtableIndex(methodIndex);
}
if (resolvedMethod == null) {
throw new AnalysisException("Could not resolve the method in class %s at index %d", objectRegisterType.type.getType(), methodIndex);
}
// no need to check class access for invoke-super. A class can obviously access its superclass.
ClassDef thisClass = classPath.getClassDef(method.getDefiningClass());
if (classPath.getClass(resolvedMethod.getDefiningClass()).isInterface()) {
resolvedMethod = new ReparentedMethodReference(resolvedMethod, objectRegisterTypeProto.getType());
} else if (!isSuper && !TypeUtils.canAccessClass(thisClass.getType(), classPath.getClassDef(resolvedMethod.getDefiningClass()))) {
// the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different
// than resolvedMethod.getDefiningClass()), and walk up the class hierarchy.
ClassDef methodClass = classPath.getClassDef(objectRegisterTypeProto.getType());
while (!TypeUtils.canAccessClass(thisClass.getType(), methodClass)) {
String superclass = methodClass.getSuperclass();
if (superclass == null) {
throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s", ReferenceUtil.getMethodDescriptor(resolvedMethod, true));
}
methodClass = classPath.getClassDef(superclass);
}
// methodClass is now the first accessible class found. Now. we need to make sure that the method is
// actually valid for this class
MethodReference newResolvedMethod = classPath.getClass(methodClass.getType()).getMethodByVtableIndex(methodIndex);
if (newResolvedMethod == null) {
throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s", ReferenceUtil.getMethodDescriptor(resolvedMethod, true));
}
resolvedMethod = newResolvedMethod;
resolvedMethod = new ImmutableMethodReference(methodClass.getType(), resolvedMethod.getName(), resolvedMethod.getParameterTypes(), resolvedMethod.getReturnType());
}
if (normalizeVirtualMethods) {
MethodReference replacementMethod = normalizeMethodReference(resolvedMethod);
if (replacementMethod != null) {
resolvedMethod = replacementMethod;
}
}
Instruction deodexedInstruction;
if (isRange) {
Instruction3rms instruction = (Instruction3rms) analyzedInstruction.instruction;
Opcode opcode;
if (isSuper) {
opcode = Opcode.INVOKE_SUPER_RANGE;
} else {
opcode = Opcode.INVOKE_VIRTUAL_RANGE;
}
deodexedInstruction = new ImmutableInstruction3rc(opcode, instruction.getStartRegister(), instruction.getRegisterCount(), resolvedMethod);
} else {
Instruction35ms instruction = (Instruction35ms) analyzedInstruction.instruction;
Opcode opcode;
if (isSuper) {
opcode = Opcode.INVOKE_SUPER;
} else {
opcode = Opcode.INVOKE_VIRTUAL;
}
deodexedInstruction = new ImmutableInstruction35c(opcode, instruction.getRegisterCount(), instruction.getRegisterC(), instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(), instruction.getRegisterG(), resolvedMethod);
}
analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
analyzeInstruction(analyzedInstruction);
return true;
}
use of org.jf.dexlib2.analysis.AnalyzedInstruction in project smali by JesusFreke.
the class MethodAnalyzer method buildInstructionList.
private void buildInstructionList() {
int registerCount = methodImpl.getRegisterCount();
ImmutableList<Instruction> instructions = ImmutableList.copyOf(methodImpl.getInstructions());
analyzedInstructions.ensureCapacity(instructions.size());
//first, create all the instructions and populate the instructionAddresses array
int currentCodeAddress = 0;
for (int i = 0; i < instructions.size(); i++) {
Instruction instruction = instructions.get(i);
analyzedInstructions.append(currentCodeAddress, new AnalyzedInstruction(this, instruction, i, registerCount));
assert analyzedInstructions.indexOfKey(currentCodeAddress) == i;
currentCodeAddress += instruction.getCodeUnits();
}
//next, populate the exceptionHandlers array. The array item for each instruction that can throw an exception
//and is covered by a try block should be set to a list of the first instructions of each exception handler
//for the try block covering the instruction
List<? extends TryBlock<? extends ExceptionHandler>> tries = methodImpl.getTryBlocks();
tries = TryListBuilder.massageTryBlocks(tries);
int triesIndex = 0;
TryBlock currentTry = null;
AnalyzedInstruction[] currentExceptionHandlers = null;
AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[instructions.size()][];
if (tries != null) {
for (int i = 0; i < analyzedInstructions.size(); i++) {
AnalyzedInstruction instruction = analyzedInstructions.valueAt(i);
Opcode instructionOpcode = instruction.instruction.getOpcode();
currentCodeAddress = getInstructionAddress(instruction);
//check if we have gone past the end of the current try
if (currentTry != null) {
if (currentTry.getStartCodeAddress() + currentTry.getCodeUnitCount() <= currentCodeAddress) {
currentTry = null;
triesIndex++;
}
}
//check if the next try is applicable yet
if (currentTry == null && triesIndex < tries.size()) {
TryBlock<? extends ExceptionHandler> tryBlock = tries.get(triesIndex);
if (tryBlock.getStartCodeAddress() <= currentCodeAddress) {
assert (tryBlock.getStartCodeAddress() + tryBlock.getCodeUnitCount() > currentCodeAddress);
currentTry = tryBlock;
currentExceptionHandlers = buildExceptionHandlerArray(tryBlock);
}
}
//for the current instruction
if (currentTry != null && instructionOpcode.canThrow()) {
exceptionHandlers[i] = currentExceptionHandlers;
}
}
}
//and no reachable code will have an unreachable predessor or successor
assert analyzedInstructions.size() > 0;
BitSet instructionsToProcess = new BitSet(instructions.size());
addPredecessorSuccessor(startOfMethod, analyzedInstructions.valueAt(0), exceptionHandlers, instructionsToProcess);
while (!instructionsToProcess.isEmpty()) {
int currentInstructionIndex = instructionsToProcess.nextSetBit(0);
instructionsToProcess.clear(currentInstructionIndex);
AnalyzedInstruction instruction = analyzedInstructions.valueAt(currentInstructionIndex);
Opcode instructionOpcode = instruction.instruction.getOpcode();
int instructionCodeAddress = getInstructionAddress(instruction);
if (instruction.instruction.getOpcode().canContinue()) {
if (currentInstructionIndex == analyzedInstructions.size() - 1) {
throw new AnalysisException("Execution can continue past the last instruction");
}
AnalyzedInstruction nextInstruction = analyzedInstructions.valueAt(currentInstructionIndex + 1);
addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers, instructionsToProcess);
}
if (instruction.instruction instanceof OffsetInstruction) {
OffsetInstruction offsetInstruction = (OffsetInstruction) instruction.instruction;
if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
AnalyzedInstruction analyzedSwitchPayload = analyzedInstructions.get(instructionCodeAddress + offsetInstruction.getCodeOffset());
if (analyzedSwitchPayload == null) {
throw new AnalysisException("Invalid switch payload offset");
}
SwitchPayload switchPayload = (SwitchPayload) analyzedSwitchPayload.instruction;
for (SwitchElement switchElement : switchPayload.getSwitchElements()) {
AnalyzedInstruction targetInstruction = analyzedInstructions.get(instructionCodeAddress + switchElement.getOffset());
if (targetInstruction == null) {
throw new AnalysisException("Invalid switch target offset");
}
addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess);
}
} else if (instructionOpcode != Opcode.FILL_ARRAY_DATA) {
int targetAddressOffset = offsetInstruction.getCodeOffset();
AnalyzedInstruction targetInstruction = analyzedInstructions.get(instructionCodeAddress + targetAddressOffset);
addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess);
}
}
}
}
use of org.jf.dexlib2.analysis.AnalyzedInstruction in project smali by JesusFreke.
the class AnalyzedInstruction method setsRegister.
/**
* Determines if this instruction sets the given register, or alters its type
*
* @param registerNumber The register to check
* @return true if this instruction sets the given register or alters its type
*/
public boolean setsRegister(int registerNumber) {
if (isInvokeInit()) {
// When constructing a new object, the register type will be an uninitialized reference after the
// new-instance instruction, but becomes an initialized reference once the <init> method is called. So even
// though invoke instructions don't normally change any registers, calling an <init> method will change the
// type of its object register. If the uninitialized reference has been copied to other registers, they will
// be initialized as well, so we need to check for that too
int destinationRegister;
if (instruction instanceof FiveRegisterInstruction) {
assert ((FiveRegisterInstruction) instruction).getRegisterCount() > 0;
destinationRegister = ((FiveRegisterInstruction) instruction).getRegisterC();
} else {
assert instruction instanceof RegisterRangeInstruction;
RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction) instruction;
assert rangeInstruction.getRegisterCount() > 0;
destinationRegister = rangeInstruction.getStartRegister();
}
RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(destinationRegister);
if (preInstructionDestRegisterType.category == RegisterType.UNKNOWN) {
// We never let an uninitialized reference propagate past an invoke-init if the object register type is
// unknown This is because the uninitialized reference may be an alias to the reference being
// initialized, but we can't know that until the object register's type is known
RegisterType preInstructionRegisterType = getPreInstructionRegisterType(registerNumber);
if (preInstructionRegisterType.category == RegisterType.UNINIT_REF || preInstructionRegisterType.category == RegisterType.UNINIT_THIS) {
return true;
}
}
if (preInstructionDestRegisterType.category != RegisterType.UNINIT_REF && preInstructionDestRegisterType.category != RegisterType.UNINIT_THIS) {
return false;
}
if (registerNumber == destinationRegister) {
return true;
}
//check if the uninit ref has been copied to another register
return preInstructionDestRegisterType.equals(getPreInstructionRegisterType(registerNumber));
}
// branch of the following if-eqz/if-nez
if (instructionIndex > 0 && methodAnalyzer.getClassPath().isArt() && getPredecessorCount() == 1 && (instruction.getOpcode() == Opcode.IF_EQZ || instruction.getOpcode() == Opcode.IF_NEZ)) {
AnalyzedInstruction prevInstruction = predecessors.first();
if (prevInstruction.instruction.getOpcode() == Opcode.INSTANCE_OF && MethodAnalyzer.canPropagateTypeAfterInstanceOf(prevInstruction, this, methodAnalyzer.getClassPath())) {
Instruction22c instanceOfInstruction = (Instruction22c) prevInstruction.instruction;
if (registerNumber == instanceOfInstruction.getRegisterB()) {
return true;
}
// TODO: do we need to do some sort of additional check that these multiple move-object predecessors actually refer to the same value?
if (instructionIndex > 1) {
int originalSourceRegister = -1;
RegisterType newType = null;
for (AnalyzedInstruction prevPrevAnalyzedInstruction : prevInstruction.predecessors) {
Opcode opcode = prevPrevAnalyzedInstruction.instruction.getOpcode();
if (opcode == Opcode.MOVE_OBJECT || opcode == Opcode.MOVE_OBJECT_16 || opcode == Opcode.MOVE_OBJECT_FROM16) {
TwoRegisterInstruction moveInstruction = ((TwoRegisterInstruction) prevPrevAnalyzedInstruction.instruction);
RegisterType originalType = prevPrevAnalyzedInstruction.getPostInstructionRegisterType(moveInstruction.getRegisterB());
if (moveInstruction.getRegisterA() != instanceOfInstruction.getRegisterB()) {
originalSourceRegister = -1;
break;
}
if (originalType.type == null) {
originalSourceRegister = -1;
break;
}
if (newType == null) {
newType = RegisterType.getRegisterType(methodAnalyzer.getClassPath(), (TypeReference) instanceOfInstruction.getReference());
}
if (MethodAnalyzer.isNotWideningConversion(originalType, newType)) {
if (originalSourceRegister != -1) {
if (originalSourceRegister != moveInstruction.getRegisterB()) {
originalSourceRegister = -1;
break;
}
} else {
originalSourceRegister = moveInstruction.getRegisterB();
}
}
} else {
originalSourceRegister = -1;
break;
}
}
if (originalSourceRegister != -1 && registerNumber == originalSourceRegister) {
return true;
}
}
}
}
if (!instruction.getOpcode().setsRegister()) {
return false;
}
int destinationRegister = getDestinationRegister();
if (registerNumber == destinationRegister) {
return true;
}
if (instruction.getOpcode().setsWideRegister() && registerNumber == (destinationRegister + 1)) {
return true;
}
return false;
}
use of org.jf.dexlib2.analysis.AnalyzedInstruction in project smali by JesusFreke.
the class MethodAnalyzer method analyzeIgetSgetWideObject.
private void analyzeIgetSgetWideObject(@Nonnull AnalyzedInstruction analyzedInstruction) {
ReferenceInstruction referenceInstruction = (ReferenceInstruction) analyzedInstruction.instruction;
FieldReference fieldReference = (FieldReference) referenceInstruction.getReference();
RegisterType fieldType = RegisterType.getRegisterType(classPath, fieldReference.getType());
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
}
Aggregations