/* * Copyright (C) 2012 Sony Mobile Communications AB * * This file is part of ApkAnalyser. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jerl.bcm.inj; import java.util.Vector; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class InjectionMethodAdapter extends MethodAdapter { private int byteCodeCounter = 0; private final Vector<Injection> offsetInjections = new Vector<Injection>(); private final Vector<Injection> callInjections = new Vector<Injection>(); private final Vector<Injection> entryInjections = new Vector<Injection>(); private final Vector<Injection> exitInjections = new Vector<Injection>(); private final Vector<Injection> exHandlerInjections = new Vector<Injection>(); private final Vector<Label> handlerList = new Vector<Label>(); private boolean isDebugMode = false; public InjectionMethodAdapter(MethodVisitor mv, InjectionMethod[] injections) { super(mv); for (int i = 0; i < injections.length; i++) { switch (injections[i].getInjectionType()) { case Injection.METHOD_CALL_INJECTION: callInjections.add(injections[i]); break; case Injection.METHOD_ENTRY_INJECTION: entryInjections.add(injections[i]); break; case Injection.METHOD_OFFSET_INJECTION: offsetInjections.add(injections[i]); break; case Injection.METHOD_EXIT_INJECTION: exitInjections.add(injections[i]); break; case Injection.METHOD_EXCEPTION_HANDLER_INJECTION: exHandlerInjections.add(injections[i]); break; } } } public InjectionMethodAdapter(MethodVisitor mv, InjectionMethod[] injections, boolean debugMode) { this(mv, injections); setDebugMode(debugMode); } /** * @param on */ public void setDebugMode(boolean on) { isDebugMode = on; } @Override public void visitCode() { // inject method entry for (int i = 0; i < entryInjections.size(); i++) { InjectionMethodEntry ime = (InjectionMethodEntry) entryInjections.elementAt(i); ime.inject(mv); if (isDebugMode) { System.out.println("\t**Inject: " + ime); } } } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc) { incByteCodeCounter(); String methodID = owner + "." + name + desc; boolean keepExistingCall = true; // inject pre method call for (int i = 0; i < callInjections.size(); i++) { InjectionMethodCall imc = (InjectionMethodCall) callInjections.elementAt(i); if (imc.getCallSignature().equals(methodID) && imc.isPreInjection()) { imc.visitMethodInsn(opcode, owner, name, desc); imc.inject(mv); keepExistingCall = imc.keepExistingCall(); if (isDebugMode) { System.out.println("\t**Inject: " + imc); } } } if (keepExistingCall) { mv.visitMethodInsn(opcode, owner, name, desc); } // inject post method call for (int i = 0; i < callInjections.size(); i++) { InjectionMethodCall imc = (InjectionMethodCall) callInjections.elementAt(i); if (imc.getCallSignature().equals(methodID) && !imc.isPreInjection()) { imc.visitMethodInsn(opcode, owner, name, desc); imc.inject(mv); if (isDebugMode) { System.out.println("\t**Inject: " + imc); } } } } @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { incByteCodeCounter(); mv.visitFieldInsn(opcode, owner, name, desc); } @Override public void visitIincInsn(int var, int increment) { incByteCodeCounter(); mv.visitIincInsn(var, increment); } @Override public void visitInsn(int opcode) { incByteCodeCounter(); if (opcode == Opcodes.IRETURN || opcode == Opcodes.LRETURN || opcode == Opcodes.FRETURN || opcode == Opcodes.DRETURN || opcode == Opcodes.ARETURN || opcode == Opcodes.RETURN) { for (int i = 0; i < exitInjections.size(); i++) { InjectionMethodExit ime = (InjectionMethodExit) exitInjections.elementAt(i); ime.inject(mv); if (isDebugMode) { System.out.println("\t**Inject: " + ime); } } } mv.visitInsn(opcode); } @Override public void visitIntInsn(int opcode, int operand) { incByteCodeCounter(); mv.visitIntInsn(opcode, operand); } @Override public void visitJumpInsn(int opcode, Label label) { incByteCodeCounter(); mv.visitJumpInsn(opcode, label); } @Override public void visitLdcInsn(Object cst) { incByteCodeCounter(); mv.visitLdcInsn(cst); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { incByteCodeCounter(); mv.visitLookupSwitchInsn(dflt, keys, labels); } @Override public void visitMultiANewArrayInsn(String desc, int dims) { incByteCodeCounter(); mv.visitMultiANewArrayInsn(desc, dims); } @Override public void visitTypeInsn(int opcode, String desc) { incByteCodeCounter(); mv.visitTypeInsn(opcode, desc); } @Override public void visitVarInsn(int opcode, int var) { incByteCodeCounter(); mv.visitVarInsn(opcode, var); } private void incByteCodeCounter() { for (int i = 0; i < offsetInjections.size(); i++) { // check if injection at this offset InjectionMethodOffset imo = (InjectionMethodOffset) offsetInjections.elementAt(i); if (imo.getByteCodeOffset() == byteCodeCounter) { imo.inject(mv); if (isDebugMode) { System.out.println("\t**Inject: " + imo); } } } byteCodeCounter++; } @Override public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { mv.visitFrame(type, nLocal, local, nStack, stack); } @Override public void visitLabel(Label label) { mv.visitLabel(label); if (handlerList.remove(label)) { for (int i = 0; i < exHandlerInjections.size(); i++) { InjectionMethodExceptionHandler ime = (InjectionMethodExceptionHandler) exHandlerInjections.elementAt(i); ime.inject(mv); if (isDebugMode) { System.out.println("\t**Inject: " + ime); } } } } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { handlerList.add(handler); mv.visitTryCatchBlock(start, end, handler, type); } }