/* * 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 andreflect.injection; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.jf.dexlib.CodeItem; import org.jf.dexlib.DexFile; import org.jf.dexlib.MethodIdItem; import org.jf.dexlib.TypeIdItem; import org.jf.dexlib.TypeListItem; import org.jf.dexlib.Code.FiveRegisterInstruction; import org.jf.dexlib.Code.Instruction; import org.jf.dexlib.Code.RegisterRangeInstruction; import org.jf.dexlib.Code.SingleRegisterInstruction; import org.jf.dexlib.Util.AccessFlags; import andreflect.DexMethod; import andreflect.Util; public class DalvikInjectCollection { InstructionCreator insCreator; public static final String APK_TAG = "APKANALYSER"; public static final short INVALID_REG = -1; DalvikInjectCollection(DexFile dexFile) { insCreator = new InstructionCreator(dexFile); logRegisterMap = new HashMap<DexMethod, LogRegister>(); } public ArrayList<Instruction> injectCopyParamRegs(CodeItem codeItem, int regDest, int regSrc, boolean isStatic, String paramShortDesc) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); if (!isStatic) { insCreator.addMove(instructions, (short) regDest++, (short) regSrc++, 'L'); } if (paramShortDesc != null) { int regCount = paramShortDesc.length(); for (int i = 0; i < regCount; i++) { insCreator.addMove(instructions, (short) regDest++, (short) regSrc++, paramShortDesc.charAt(i)); if (paramShortDesc.charAt(i) == 'D' || paramShortDesc.charAt(i) == 'J') { regSrc++; //never use from16 opcodes regDest++; } } } return instructions; } public ArrayList<Instruction> injectInstanceHash(DexMethod method, short register, String str, Instruction ins, boolean isThis) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; LogRegister logReg = getLogRegister(method); short reg = (short) (codeItem.getRegisterCount()); short logTagReg = logReg.logTagReg() ? logReg.logTagReg : (logReg.logTagReg = reg++); short logTextReg = logReg.logTextReg() ? logReg.logTextReg : (logReg.logTextReg = reg++); short stringBufferInstanceReg = logReg.stringBufferInstanceReg() ? logReg.stringBufferInstanceReg : (logReg.stringBufferInstanceReg = reg++); short appendValueReg = logReg.appendValueReg() ? logReg.appendValueReg : (logReg.appendValueReg = reg++); int alter = prepareRegister(reg, method, ins, false); if (alter == -1) { return instructions; } reg += alter; insCreator.addConstString(instructions, logTagReg, APK_TAG); insCreator.addNewInstance(instructions, stringBufferInstanceReg, DebugMethod.STRINGBUFFER_INIT.className); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_INIT, stringBufferInstanceReg, codeItem); insCreator.addConstString(instructions, appendValueReg, str); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); if (isThis == false) { insCreator.addInvokeWithReturnInt(instructions, DebugMethod.OBJECT_HASHCODE, register, codeItem, logTextReg); } else { insCreator.addInvokeWithReturnInt(instructions, DebugMethod.OBJECT_HASHCODE_SUPER, register, codeItem, logTextReg); } insCreator.addMove(instructions, appendValueReg, logTextReg, 'I'); stringbuffer_append(stringBufferInstanceReg, stringBufferInstanceReg, 'I', DebugMethod.STRINGBUFFER_APPEND_OBJECT.params[0], codeItem, instructions); insCreator.addInvokeWithReturnObject(instructions, DebugMethod.STRINGBUFFER_TOSTRING, stringBufferInstanceReg, codeItem, logTextReg); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); codeItem.registerCount = reg; resetRegister(); return instructions; } public ArrayList<Instruction> injectRegPrintAsObject(DexMethod method, short register, String str, Instruction ins) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; LogRegister logReg = getLogRegister(method); short reg = (short) (codeItem.getRegisterCount()); short logTagReg = logReg.logTagReg() ? logReg.logTagReg : (logReg.logTagReg = reg++); short logTextReg = logReg.logTextReg() ? logReg.logTextReg : (logReg.logTextReg = reg++); short stringBufferInstanceReg = logReg.stringBufferInstanceReg() ? logReg.stringBufferInstanceReg : (logReg.stringBufferInstanceReg = reg++); short appendValueReg = logReg.appendValueReg() ? logReg.appendValueReg : (logReg.appendValueReg = reg++); int alter = prepareRegister(reg, method, ins, false); if (alter == -1) { return instructions; } reg += alter; insCreator.addConstString(instructions, logTagReg, APK_TAG); insCreator.addNewInstance(instructions, stringBufferInstanceReg, DebugMethod.STRINGBUFFER_INIT.className); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_INIT, stringBufferInstanceReg, codeItem); insCreator.addConstString(instructions, appendValueReg, str); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); insCreator.addMove(instructions, appendValueReg, register, 'L'); stringbuffer_append(stringBufferInstanceReg, stringBufferInstanceReg, 'L', DebugMethod.STRINGBUFFER_APPEND_OBJECT.params[0], codeItem, instructions); insCreator.addInvokeWithReturnObject(instructions, DebugMethod.STRINGBUFFER_TOSTRING, stringBufferInstanceReg, codeItem, logTextReg); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); codeItem.registerCount = reg; resetRegister(); return instructions; } public ArrayList<Instruction> injectRegPrintWithTypeIdItem(DexMethod method, short register, String str, Instruction ins, TypeIdItem type) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; String shortDesc = type.toShorty(); String typeDesc = type.getTypeDescriptor(); LogRegister logReg = getLogRegister(method); short reg = (short) (codeItem.getRegisterCount()); short logTagReg = logReg.logTagReg() ? logReg.logTagReg : (logReg.logTagReg = reg++); short logTextReg = logReg.logTextReg() ? logReg.logTextReg : (logReg.logTextReg = reg++); short stringBufferInstanceReg = INVALID_REG; short appendValueReg = INVALID_REG; if (hasWideRegister(shortDesc)) { stringBufferInstanceReg = logReg.stringBufferInstanceReg_Wide() ? logReg.stringBufferInstanceReg_Wide : (logReg.stringBufferInstanceReg_Wide = reg++); appendValueReg = logReg.appendValueReg_Wide() ? logReg.appendValueReg_Wide : (logReg.appendValueReg_Wide = reg++); reg++; } else { stringBufferInstanceReg = logReg.stringBufferInstanceReg() ? logReg.stringBufferInstanceReg : (logReg.stringBufferInstanceReg = reg++); appendValueReg = logReg.appendValueReg() ? logReg.appendValueReg : (logReg.appendValueReg = reg++); } int alter = prepareRegister(reg, method, ins, hasWideRegister(shortDesc)); if (alter == -1) { return instructions; } reg += alter; insCreator.addConstString(instructions, logTagReg, APK_TAG); insCreator.addNewInstance(instructions, stringBufferInstanceReg, DebugMethod.STRINGBUFFER_INIT.className); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_INIT, stringBufferInstanceReg, codeItem); insCreator.addConstString(instructions, appendValueReg, str + " = "); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); insCreator.addMove(instructions, appendValueReg, register, shortDesc.charAt(0)); stringbuffer_append(stringBufferInstanceReg, stringBufferInstanceReg, shortDesc.charAt(0), typeDesc, codeItem, instructions); insCreator.addInvokeWithReturnObject(instructions, DebugMethod.STRINGBUFFER_TOSTRING, stringBufferInstanceReg, codeItem, logTextReg); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); codeItem.registerCount = reg; resetRegister(); return instructions; } public ArrayList<Instruction> injectLog(DexMethod method, String str, Instruction ins) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; LogRegister logReg = getLogRegister(method); short reg = (short) (codeItem.getRegisterCount()); short logTagReg = logReg.logTagReg() ? logReg.logTagReg : (logReg.logTagReg = reg++); short logTextReg = logReg.logTextReg() ? logReg.logTextReg : (logReg.logTextReg = reg++); int alter = prepareRegister(reg, method, ins, false); if (alter == -1) { return instructions; } reg += alter; insCreator.addConstString(instructions, logTagReg, APK_TAG); insCreator.addConstString(instructions, logTextReg, str); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); codeItem.registerCount = reg; resetRegister(); return instructions; } public ArrayList<Instruction> injectCurThread(DexMethod method, String str, Instruction ins) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; LogRegister logReg = getLogRegister(method); short reg = (short) (codeItem.getRegisterCount()); short logTagReg = logReg.logTagReg() ? logReg.logTagReg : (logReg.logTagReg = reg++); short logTextReg = logReg.logTextReg() ? logReg.logTextReg : (logReg.logTextReg = reg++); short stringBufferInstanceReg = logReg.stringBufferInstanceReg() ? logReg.stringBufferInstanceReg : (logReg.stringBufferInstanceReg = reg++); short appendValueReg = logReg.appendValueReg() ? logReg.appendValueReg : (logReg.appendValueReg = reg++); int alter = prepareRegister(reg, method, ins, false); if (alter == -1) { return instructions; } reg += alter; insCreator.addConstString(instructions, logTagReg, APK_TAG); insCreator.addNewInstance(instructions, stringBufferInstanceReg, DebugMethod.STRINGBUFFER_INIT.className); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_INIT, stringBufferInstanceReg, codeItem); insCreator.addConstString(instructions, appendValueReg, str + " = "); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); insCreator.addInvokeWithReturnObject(instructions, DebugMethod.THREAD_CURRENTTHREAD, (short) 0, codeItem, appendValueReg); insCreator.addInvokeWithReturnObject(instructions, DebugMethod.THREAD_GETNAME, appendValueReg, codeItem, appendValueReg); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); insCreator.addInvokeWithReturnObject(instructions, DebugMethod.STRINGBUFFER_TOSTRING, stringBufferInstanceReg, codeItem, logTextReg); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); codeItem.registerCount = reg; resetRegister(); return instructions; } public ArrayList<Instruction> injectGC(DexMethod method) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; insCreator.addInvoke(instructions, DebugMethod.GC, (short) 0, codeItem); return instructions; } public ArrayList<Instruction> injectPrintStackTrace(DexMethod method, String str, Instruction ins) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; LogRegister logReg = getLogRegister(method); short reg = (short) (codeItem.getRegisterCount()); short logTagReg = logReg.logTagReg() ? logReg.logTagReg : (logReg.logTagReg = reg++); short logTextReg = logReg.logTextReg() ? logReg.logTextReg : (logReg.logTextReg = reg++); short stringBufferInstanceReg = logReg.stringBufferInstanceReg() ? logReg.stringBufferInstanceReg : (logReg.stringBufferInstanceReg = reg++); short appendValueReg = logReg.appendValueReg() ? logReg.appendValueReg : (logReg.appendValueReg = reg++); int alter = prepareRegister(reg, method, ins, false); if (alter == -1) { return instructions; } reg += alter; insCreator.addConstString(instructions, logTagReg, APK_TAG); insCreator.addNewInstance(instructions, stringBufferInstanceReg, DebugMethod.STRINGBUFFER_INIT.className); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_INIT, stringBufferInstanceReg, codeItem); insCreator.addConstString(instructions, appendValueReg, str + " = "); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); insCreator.addNewInstance(instructions, logTextReg, DebugMethod.THROWABLE_INIT.className); insCreator.addInvoke(instructions, DebugMethod.THROWABLE_INIT, logTextReg, codeItem); insCreator.addInvokeWithReturnObject(instructions, DebugMethod.LOG_GETSTACKTRACE, logTextReg, codeItem, appendValueReg); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); insCreator.addInvokeWithReturnObject(instructions, DebugMethod.STRINGBUFFER_TOSTRING, stringBufferInstanceReg, codeItem, logTextReg); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); codeItem.registerCount = reg; resetRegister(); return instructions; } public ArrayList<Instruction> injectInvokeReturn(DexMethod method, String str, Instruction invokeIns, Instruction moveResultIns, Instruction ins, MethodIdItem methodIdItem) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; String returnShortDesc = methodIdItem.getPrototype().getReturnType().toShorty(); String typeDesc = methodIdItem.getPrototype().getReturnType().getTypeDescriptor(); if (returnShortDesc.charAt(0) == 'V') { return injectLog(method, str, ins); } LogRegister logReg = getLogRegister(method); short reg = (short) (codeItem.getRegisterCount()); short logTagReg = logReg.logTagReg() ? logReg.logTagReg : (logReg.logTagReg = reg++); short logTextReg = logReg.logTextReg() ? logReg.logTextReg : (logReg.logTextReg = reg++); short stringBufferInstanceReg2 = logReg.stringBufferInstanceReg() ? logReg.stringBufferInstanceReg : (logReg.stringBufferInstanceReg = reg++); short appendValueReg2 = logReg.appendValueReg() ? logReg.appendValueReg : (logReg.appendValueReg = reg++); short stringBufferInstanceReg = INVALID_REG; short appendValueReg = INVALID_REG; if (hasWideRegister(returnShortDesc)) { stringBufferInstanceReg = logReg.stringBufferInstanceReg_Wide() ? logReg.stringBufferInstanceReg_Wide : (logReg.stringBufferInstanceReg_Wide = reg++); appendValueReg = logReg.appendValueReg_Wide() ? logReg.appendValueReg_Wide : (logReg.appendValueReg_Wide = reg++); reg++; } else { stringBufferInstanceReg = logReg.stringBufferInstanceReg2() ? logReg.stringBufferInstanceReg2 : (logReg.stringBufferInstanceReg2 = reg++); appendValueReg = logReg.appendValueReg2() ? logReg.appendValueReg2 : (logReg.appendValueReg2 = reg++); } int alter = prepareRegister(reg, method, ins, hasWideRegister(returnShortDesc)); if (alter == -1) { return instructions; } reg += alter; if (moveResultIns != null) { insCreator.addMove(instructions, appendValueReg, (short) (((SingleRegisterInstruction) moveResultIns).getRegisterA()), returnShortDesc.charAt(0)); } else { //workaround to inject the register save before the invokation ArrayList<Instruction> saveRegForMoveResult = new ArrayList<Instruction>(); short moveResultAlterReg = insCreator.addMoveResultBefore(saveRegForMoveResult, appendValueReg, returnShortDesc.charAt(0)); if (moveResultAlterReg != appendValueReg) { DalvikBytecodeModificationMediator.injectInsturctionsAtInstruction(method, invokeIns, saveRegForMoveResult); } insCreator.addMoveResult(instructions, moveResultAlterReg, returnShortDesc.charAt(0)); if (moveResultAlterReg != appendValueReg) { insCreator.addMoveResultAfter(instructions, appendValueReg, returnShortDesc.charAt(0)); } } insCreator.addConstString(instructions, logTagReg, APK_TAG); insCreator.addConstString(instructions, logTextReg, str); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); insCreator.addNewInstance(instructions, stringBufferInstanceReg, DebugMethod.STRINGBUFFER_INIT.className); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_INIT, stringBufferInstanceReg, codeItem); insCreator.addMove(instructions, stringBufferInstanceReg2, stringBufferInstanceReg, 'L'); insCreator.addConstString(instructions, appendValueReg2, " return: " + Util.getProtoString(typeDesc) + " = "); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg2, codeItem); stringbuffer_append(stringBufferInstanceReg, stringBufferInstanceReg, returnShortDesc.charAt(0), typeDesc, codeItem, instructions); insCreator.addInvokeWithReturnObject(instructions, DebugMethod.STRINGBUFFER_TOSTRING, stringBufferInstanceReg, codeItem, logTextReg); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); codeItem.registerCount = reg; resetRegister(); return instructions; } public ArrayList<Instruction> injectReturnValue(DexMethod method, String str, Instruction ins) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; String returnShortDesc = codeItem.getParent().method.getPrototype().getReturnType().toShorty(); String typeDesc = codeItem.getParent().method.getPrototype().getReturnType().getTypeDescriptor(); if (!(returnShortDesc.charAt(0) != 'V' && ins instanceof SingleRegisterInstruction)) { return injectLog(method, str, ins); } LogRegister logReg = getLogRegister(method); short reg = (short) (codeItem.getRegisterCount()); short logTagReg = logReg.logTagReg() ? logReg.logTagReg : (logReg.logTagReg = reg++); short logTextReg = logReg.logTextReg() ? logReg.logTextReg : (logReg.logTextReg = reg++); short stringBufferInstanceReg = INVALID_REG; short appendValueReg = INVALID_REG; if (hasWideRegister(returnShortDesc)) { stringBufferInstanceReg = logReg.stringBufferInstanceReg_Wide() ? logReg.stringBufferInstanceReg_Wide : (logReg.stringBufferInstanceReg_Wide = reg++); appendValueReg = logReg.appendValueReg_Wide() ? logReg.appendValueReg_Wide : (logReg.appendValueReg_Wide = reg++); reg++; } else { stringBufferInstanceReg = logReg.stringBufferInstanceReg() ? logReg.stringBufferInstanceReg : (logReg.stringBufferInstanceReg = reg++); appendValueReg = logReg.appendValueReg() ? logReg.appendValueReg : (logReg.appendValueReg = reg++); } int alter = prepareRegister(reg, method, ins, hasWideRegister(returnShortDesc)); if (alter == -1) { return instructions; } reg += alter; insCreator.addConstString(instructions, logTagReg, APK_TAG); insCreator.addConstString(instructions, logTextReg, str); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); insCreator.addNewInstance(instructions, stringBufferInstanceReg, DebugMethod.STRINGBUFFER_INIT.className); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_INIT, stringBufferInstanceReg, codeItem); insCreator.addConstString(instructions, appendValueReg, " return: " + Util.getProtoString(typeDesc) + " = "); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); insCreator.addMove(instructions, appendValueReg, (short) (((SingleRegisterInstruction) ins).getRegisterA()), returnShortDesc.charAt(0)); stringbuffer_append(stringBufferInstanceReg, stringBufferInstanceReg, returnShortDesc.charAt(0), typeDesc, codeItem, instructions); insCreator.addInvokeWithReturnObject(instructions, DebugMethod.STRINGBUFFER_TOSTRING, stringBufferInstanceReg, codeItem, logTextReg); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); codeItem.registerCount = reg; resetRegister(); return instructions; } public ArrayList<Instruction> injectParams(DexMethod method, String str, String[] paramStrings) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; TypeListItem params = codeItem.getParent().method.getPrototype().getParameters(); String paramShortDesc = params == null ? null : params.getShortyString(); if (params == null || paramShortDesc == null) { return injectLog(method, str, null); //always at beginning } LogRegister logReg = getLogRegister(method); short reg = (short) (codeItem.getRegisterCount()); short logTagReg = logReg.logTagReg() ? logReg.logTagReg : (logReg.logTagReg = reg++); short logTextReg = logReg.logTextReg() ? logReg.logTextReg : (logReg.logTextReg = reg++); short stringBufferInstanceReg = logReg.stringBufferInstanceReg() ? logReg.stringBufferInstanceReg : (logReg.stringBufferInstanceReg = reg++); short appendValueReg = logReg.appendValueReg() ? logReg.appendValueReg : (logReg.appendValueReg = reg++); short stringBufferInstanceReg_Wide = INVALID_REG; short appendValueReg_Wide = INVALID_REG; if (hasWideRegister(paramShortDesc)) { stringBufferInstanceReg_Wide = logReg.stringBufferInstanceReg_Wide() ? logReg.stringBufferInstanceReg_Wide : (logReg.stringBufferInstanceReg_Wide = reg++); appendValueReg_Wide = logReg.appendValueReg_Wide() ? logReg.appendValueReg_Wide : (logReg.appendValueReg_Wide = reg++); reg++; } int alter = prepareRegister(reg, method, null, hasWideRegister(paramShortDesc)); if (alter == -1) { return instructions; } reg += alter; insCreator.addConstString(instructions, logTagReg, APK_TAG); insCreator.addConstString(instructions, logTextReg, str); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); insCreator.addNewInstance(instructions, stringBufferInstanceReg, DebugMethod.STRINGBUFFER_INIT.className); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_INIT, stringBufferInstanceReg, codeItem); if (stringBufferInstanceReg_Wide != INVALID_REG) { insCreator.addMove(instructions, stringBufferInstanceReg_Wide, stringBufferInstanceReg, 'L'); } int paramCount = paramShortDesc.length(); int parameterRegisterCount = codeItem.getParent().method.getPrototype().getParameterRegisterCount() + (((codeItem.getParent().accessFlags & AccessFlags.STATIC.getValue()) == 0) ? 1 : 0); short regSrc = (short) (codeItem.registerOriginalCount - parameterRegisterCount + (((codeItem.getParent().accessFlags & AccessFlags.STATIC.getValue()) == 0) ? 1 : 0)); for (int i = 0; i < paramCount; i++) { insCreator.addConstString(instructions, appendValueReg, " parameter[" + i + "]: " + paramStrings[i] + " = "); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); if (paramShortDesc.charAt(i) != 'D' && paramShortDesc.charAt(i) != 'J') { insCreator.addMove(instructions, appendValueReg, regSrc++, paramShortDesc.charAt(i)); } else { insCreator.addMove(instructions, appendValueReg_Wide, regSrc++, paramShortDesc.charAt(i)); regSrc++; //never use from16 opcodes } stringbuffer_append(stringBufferInstanceReg, stringBufferInstanceReg_Wide, paramShortDesc.charAt(i), codeItem.getParent().method.getPrototype().getParameters().getTypeIdItem(i).getTypeDescriptor(), codeItem, instructions); insCreator.addConstString(instructions, appendValueReg, "\n"); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); } insCreator.addInvokeWithReturnObject(instructions, DebugMethod.STRINGBUFFER_TOSTRING, stringBufferInstanceReg, codeItem, logTextReg); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); codeItem.registerCount = reg; resetRegister(); return instructions; } public ArrayList<Instruction> injectInvokeParams(DexMethod method, String str, Instruction invokeIns, MethodIdItem methodIdItem) { ArrayList<Instruction> instructions = new ArrayList<Instruction>(); CodeItem codeItem = method.getEncodedMethod().codeItem; if (!(invokeIns instanceof FiveRegisterInstruction || invokeIns instanceof RegisterRangeInstruction)) { return injectLog(method, str, invokeIns); } TypeListItem params = methodIdItem.getPrototype().getParameters(); String paramShortDesc = params == null ? null : params.getShortyString(); if (params == null || paramShortDesc == null) { return injectLog(method, str, invokeIns); } LogRegister logReg = getLogRegister(method); short reg = (short) (codeItem.getRegisterCount()); short logTagReg = logReg.logTagReg() ? logReg.logTagReg : (logReg.logTagReg = reg++); short logTextReg = logReg.logTextReg() ? logReg.logTextReg : (logReg.logTextReg = reg++); short stringBufferInstanceReg = logReg.stringBufferInstanceReg() ? logReg.stringBufferInstanceReg : (logReg.stringBufferInstanceReg = reg++); short appendValueReg = logReg.appendValueReg() ? logReg.appendValueReg : (logReg.appendValueReg = reg++); short stringBufferInstanceReg_Wide = INVALID_REG; short appendValueReg_Wide = INVALID_REG; if (hasWideRegister(paramShortDesc)) { stringBufferInstanceReg_Wide = logReg.stringBufferInstanceReg_Wide() ? logReg.stringBufferInstanceReg_Wide : (logReg.stringBufferInstanceReg_Wide = reg++); appendValueReg_Wide = logReg.appendValueReg_Wide() ? logReg.appendValueReg_Wide : (logReg.appendValueReg_Wide = reg++); reg++; } int alter = prepareRegister(reg, method, invokeIns, hasWideRegister(paramShortDesc)); if (alter == -1) { return instructions; } reg += alter; String[] paramStrings = new String[params.getTypeCount()]; for (int i = 0; i < params.getTypeCount(); i++) { paramStrings[i] = Util.getProtoString(params.getTypeIdItem(i).getTypeDescriptor()); } insCreator.addConstString(instructions, logTagReg, APK_TAG); insCreator.addConstString(instructions, logTextReg, str); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); insCreator.addNewInstance(instructions, stringBufferInstanceReg, DebugMethod.STRINGBUFFER_INIT.className); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_INIT, stringBufferInstanceReg, codeItem); if (stringBufferInstanceReg_Wide != INVALID_REG) { insCreator.addMove(instructions, stringBufferInstanceReg_Wide, stringBufferInstanceReg, 'L'); } int paramCount = paramShortDesc.length(); short[] paramRegs = null; int parameterRegisterCount = 0; if (invokeIns instanceof FiveRegisterInstruction) { parameterRegisterCount = ((FiveRegisterInstruction) invokeIns).getRegCount(); paramRegs = new short[5]; paramRegs[0] = ((FiveRegisterInstruction) invokeIns).getRegisterD(); paramRegs[1] = ((FiveRegisterInstruction) invokeIns).getRegisterE(); paramRegs[2] = ((FiveRegisterInstruction) invokeIns).getRegisterF(); paramRegs[3] = ((FiveRegisterInstruction) invokeIns).getRegisterG(); paramRegs[4] = ((FiveRegisterInstruction) invokeIns).getRegisterA(); } else {//RegisterRangeInstruction parameterRegisterCount = ((RegisterRangeInstruction) invokeIns).getRegCount(); paramRegs = new short[parameterRegisterCount]; for (int i = 0; i < ((RegisterRangeInstruction) invokeIns).getRegCount(); i++) { paramRegs[i] = (short) (((RegisterRangeInstruction) invokeIns).getStartRegister() + i); } } short regParamIndex = (short) parameterRegisterCount; for (int i = 0; i < paramCount; i++) { if (paramShortDesc.charAt(i) == 'D' || paramShortDesc.charAt(i) == 'J') { regParamIndex--; } regParamIndex--; } for (int i = 0; i < paramCount; i++) { insCreator.addConstString(instructions, appendValueReg, " parameter[" + i + "]: " + paramStrings[i] + " = "); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); if (paramShortDesc.charAt(i) != 'D' && paramShortDesc.charAt(i) != 'J') { insCreator.addMove(instructions, appendValueReg, paramRegs[regParamIndex++], paramShortDesc.charAt(i)); } else { insCreator.addMove(instructions, appendValueReg_Wide, paramRegs[regParamIndex++], paramShortDesc.charAt(i)); regParamIndex++; //never use from16 opcodes } stringbuffer_append(stringBufferInstanceReg, stringBufferInstanceReg_Wide, paramShortDesc.charAt(i), methodIdItem.getPrototype().getParameters().getTypeIdItem(i).getTypeDescriptor(), codeItem, instructions); insCreator.addConstString(instructions, appendValueReg, "\n"); insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_STRING, stringBufferInstanceReg, codeItem); } insCreator.addInvokeWithReturnObject(instructions, DebugMethod.STRINGBUFFER_TOSTRING, stringBufferInstanceReg, codeItem, logTextReg); insCreator.addInvoke(instructions, DebugMethod.LOG, logTagReg, codeItem); codeItem.registerCount = reg; resetRegister(); return instructions; } private void stringbuffer_append(short stringBufferInstanceReg, short stringBufferInstanceReg_Wide, char shortTypeDesc, String typeDesc, CodeItem codeItem, ArrayList<Instruction> instructions) { switch (shortTypeDesc) { case 'I'://int case 'B'://byte case 'S'://short insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_IBS, stringBufferInstanceReg, codeItem); break; case 'Z'://boolean insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_Z, stringBufferInstanceReg, codeItem); break; case 'C'://char insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_C, stringBufferInstanceReg, codeItem); break; case 'F'://float insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_F, stringBufferInstanceReg, codeItem); break; case 'D'://double insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_D, stringBufferInstanceReg_Wide, codeItem); break; case 'J'://long insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_J, stringBufferInstanceReg_Wide, codeItem); break; case 'L'://object insCreator.addInvoke(instructions, DebugMethod.STRINGBUFFER_APPEND_OBJECT, stringBufferInstanceReg, codeItem); break; default: break; } } private LogRegister getLogRegister(DexMethod method) { if (logRegisterMap.containsKey(method)) { return logRegisterMap.get(method); } else { LogRegister logRegister = new LogRegister(); logRegisterMap.put(method, logRegister); return logRegister; } } private final Map<DexMethod, LogRegister> logRegisterMap; private boolean hasWideRegister(String shortTypeDesc) { boolean ret = false; for (int i = 0; i < shortTypeDesc.length(); i++) { if (shortTypeDesc.charAt(i) == 'D' || shortTypeDesc.charAt(i) == 'J') { ret = true; } } return ret; } private void resetRegister() { insCreator.resetAlterRegister(); } //offsetIns can be null private int prepareRegister(short reg, DexMethod method, Instruction offsetIns, boolean needWide) { if (method.getEncodedMethod().codeItem.getRegisterCount() == reg) { return 0; } int parameterRegisterCount = method.getEncodedMethod().codeItem.getParent().method.getPrototype().getParameterRegisterCount() + (((method.getEncodedMethod().codeItem.getParent().accessFlags & AccessFlags.STATIC.getValue()) == 0) ? 1 : 0); if (reg + parameterRegisterCount < 255) { //just an assumption return 0; } System.out.println("[prepareRegister] method ignored, does not have enough register " + method.getMEClass().getName() + "." + method.getName()); //TODO analyse and set alter registers return -1; } }