/*
* 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 org.jf.dexlib.CodeItem;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.StringIdItem;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.Format.Instruction10x;
import org.jf.dexlib.Code.Format.Instruction11x;
import org.jf.dexlib.Code.Format.Instruction12x;
import org.jf.dexlib.Code.Format.Instruction21c;
import org.jf.dexlib.Code.Format.Instruction22x;
import org.jf.dexlib.Code.Format.Instruction31c;
import org.jf.dexlib.Code.Format.Instruction32x;
import org.jf.dexlib.Code.Format.Instruction3rc;
public class InstructionCreator {
ItemCreator ic;
AlterRegister alterRegister = null;
public void setAlterRegister(AlterRegister alterRegister) {
this.alterRegister = alterRegister;
}
public void resetAlterRegister() {
alterRegister = null;
}
private short getRegister(ArrayList<Instruction> instructions, short reg, boolean isWide, boolean isObject) {
short register = reg;
if (alterRegister != null) {
short low = -1;
short high = -1;
Opcode op = Opcode.NOP;
if (isWide) {
if (alterRegister.checkWide() == false) {
throw new RuntimeException("Register is out of range but AlterRegister WIDE is not available");
}
low = alterRegister.lowWideRegister;
high = alterRegister.highWideRegister;
op = Opcode.MOVE_WIDE_16;
} else if (isObject) {
if (alterRegister.checkObject() == true) {
low = alterRegister.lowObjRegister;
high = alterRegister.highObjRegister;
op = Opcode.MOVE_OBJECT_16;
} else if (alterRegister.checkNormal() == true) {
low = alterRegister.lowRegister;
high = alterRegister.highRegister;
op = Opcode.MOVE_16;
}
} else if (alterRegister.checkNormal() == true) {
low = alterRegister.lowRegister;
high = alterRegister.highRegister;
op = Opcode.MOVE_16;
} else if (alterRegister.checkObject() == true) {
low = alterRegister.lowObjRegister;
high = alterRegister.highObjRegister;
op = Opcode.MOVE_OBJECT_16;
} else {
throw new RuntimeException("Register is out of range but AlterRegister is not available");
}
if (high != AlterRegister.INVALID_REG) {
instructions.add(getMoveInstruction(op,
high,
low).setInject()); //65536
}
register = low;
} else if (reg >= 256) {
throw new RuntimeException("Register is out of range but AlterRegister is null");
}
return register;
}
private void restoreRegister(ArrayList<Instruction> instructions, short reg, boolean isWide, boolean isObject) {
if (alterRegister != null) {
short low = -1;
short high = -1;
Opcode op = Opcode.NOP;
if (isWide) {
if (alterRegister.checkWide() == false) {
throw new RuntimeException("Register is out of range but AlterRegister WIDE is not available");
}
low = alterRegister.lowWideRegister;
high = alterRegister.highWideRegister;
op = Opcode.MOVE_WIDE_16;
} else if (isObject) {
if (alterRegister.checkObject() == true) {
low = alterRegister.lowObjRegister;
high = alterRegister.highObjRegister;
op = Opcode.MOVE_OBJECT_16;
} else if (alterRegister.checkNormal() == true) {
low = alterRegister.lowRegister;
high = alterRegister.highRegister;
op = Opcode.MOVE_16;
}
} else if (alterRegister.checkNormal() == true) {
low = alterRegister.lowRegister;
high = alterRegister.highRegister;
op = Opcode.MOVE_16;
} else if (alterRegister.checkObject() == true) {
low = alterRegister.lowObjRegister;
high = alterRegister.highObjRegister;
op = Opcode.MOVE_OBJECT_16;
} else {
throw new RuntimeException("Register is out of range but AlterRegister is not available");
}
Opcode copyresult = Opcode.MOVE_16;
if (isObject) {
copyresult = Opcode.MOVE_OBJECT_16;
}
else if (isWide) {
copyresult = Opcode.MOVE_WIDE_16;
}
instructions.add(getMoveInstruction(copyresult,
reg,
low).setInject()); //65536
if (high != AlterRegister.INVALID_REG) {
instructions.add(getMoveInstruction(op,
low,
high).setInject()); //65536
}
} else if (reg >= 256) {
throw new RuntimeException("Register is out of range but AlterRegister is null");
}
}
InstructionCreator(DexFile dexfile) {
ic = new ItemCreator(dexfile);
}
public short addMoveResultBefore(ArrayList<Instruction> instructions, short reg, char type) {
short register = reg;
switch (type) {
case 'I'://int
case 'Z'://boolean
case 'B'://byte
case 'C'://char
case 'F'://float
case 'S'://short
register = getRegister(instructions, reg, false, false);
break;
case 'D'://double
case 'J'://long
register = getRegister(instructions, reg, true, false);
break;
case 'L'://object
default:
register = getRegister(instructions, reg, false, true);
break;
}
return register;
}
public void addMoveResultAfter(ArrayList<Instruction> instructions, short reg, char type) {
switch (type) {
case 'I'://int
case 'Z'://boolean
case 'B'://byte
case 'C'://char
case 'F'://float
case 'S'://short
restoreRegister(instructions, reg, false, false);
break;
case 'D'://double
case 'J'://long
restoreRegister(instructions, reg, true, false);
break;
case 'L'://object
default:
restoreRegister(instructions, reg, false, true);
break;
}
}
public void addMoveResult(ArrayList<Instruction> instructions, short reg, char type) {
short register = reg;
if (register >= 256) {
throw new RuntimeException("Register is out of range in addMoveResult");
}
switch (type) {
case 'I'://int
case 'Z'://boolean
case 'B'://byte
case 'C'://char
case 'F'://float
case 'S'://short
instructions.add(new Instruction11x(Opcode.MOVE_RESULT, register).setInject()); //256
break;
case 'D'://double
case 'J'://long
instructions.add(new Instruction11x(Opcode.MOVE_RESULT_WIDE, register).setInject()); //256
break;
case 'L'://object
default:
instructions.add(new Instruction11x(Opcode.MOVE_RESULT_OBJECT, register).setInject()); //256
break;
}
}
public void addMove(ArrayList<Instruction> instructions, short regDest, short regSrc, char type) {
switch (type) {
case 'I'://int
case 'Z'://boolean
case 'B'://byte
case 'C'://char
case 'F'://float
case 'S'://short
instructions.add(getMoveInstruction(Opcode.MOVE_16, regDest, regSrc).setInject()); //65536
break;
case 'D'://double
case 'J'://long
instructions.add(getMoveInstruction(Opcode.MOVE_WIDE_16, regDest, regSrc).setInject()); //65536
break;
case 'L'://object
default:
instructions.add(getMoveInstruction(Opcode.MOVE_OBJECT_16, regDest, regSrc).setInject()); //65536
break;
}
}
public void addNewInstance(ArrayList<Instruction> instructions, short reg, String type) {
short register = getRegister(instructions, reg, false, true);
instructions.add(new Instruction21c(Opcode.NEW_INSTANCE, register, ic.addTypeIdItem(type)).setInject()); //256
restoreRegister(instructions, reg, false, true);
}
public void addInvokeWithReturnInt(ArrayList<Instruction> instructions, DebugMethod method, short startReg, CodeItem codeItem, short returnReg) {
short register = getRegister(instructions, returnReg, false, true);
addInvoke(instructions, method, startReg, codeItem);
addMoveResult(instructions, register, 'I'); //256
restoreRegister(instructions, returnReg, false, true);
}
public void addInvokeWithReturnObject(ArrayList<Instruction> instructions, DebugMethod method, short startReg, CodeItem codeItem, short returnReg) {
short register = getRegister(instructions, returnReg, false, true);
addInvoke(instructions, method, startReg, codeItem);
addMoveResult(instructions, register, 'L'); //256
restoreRegister(instructions, returnReg, false, true);
}
public void addInvoke(ArrayList<Instruction> instructions, DebugMethod method, short startReg, CodeItem codeItem) {
short registerCount = 0;
if (method.params.length != 0) {
registerCount = (short) method.params.length;
for (String param : method.params) {
if (param.equals("J") || param.equals("D")) {
registerCount++;
}
}
}
if (!(method.opcode.name.equals(Opcode.INVOKE_STATIC.name)
|| method.opcode.name.equals(Opcode.INVOKE_STATIC_RANGE.name))) {
registerCount++;
}
instructions.add(new Instruction3rc(method.opcode, //65536
registerCount,
startReg,
ic.prepareMethodIdItem(method)).setInject());
if (codeItem.outWords < registerCount) {
codeItem.outWords = registerCount;
}
}
public void addConstString(ArrayList<Instruction> instructions, short reg, String string) {
short register = getRegister(instructions, reg, false, true);
StringIdItem stringIdItem = ic.prepareStringIdItem(string);
if (stringIdItem.getIndex() > 0xFFFF) {
instructions.add(new Instruction31c(Opcode.CONST_STRING_JUMBO, register, stringIdItem).setInject()); //256
} else {
instructions.add(new Instruction21c(Opcode.CONST_STRING, register, stringIdItem).setInject()); //256
}
restoreRegister(instructions, reg, false, true);
}
public void addReturn(ArrayList<Instruction> instructions, short reg, char type) {
short register;
switch (type) {
case 'V':
instructions.add(new Instruction10x(Opcode.RETURN_VOID).setInject());
break;
case 'I'://int
case 'Z'://boolean
case 'B'://byte
case 'C'://char
case 'F'://float
case 'S'://short
register = getRegister(instructions, reg, false, false);
instructions.add(new Instruction11x(Opcode.RETURN, register).setInject()); //256
restoreRegister(instructions, reg, false, false);
break;
case 'D'://double
case 'J'://long
register = getRegister(instructions, reg, true, false);
instructions.add(new Instruction11x(Opcode.RETURN_WIDE, register).setInject()); //256
restoreRegister(instructions, reg, true, false);
break;
case 'L'://object
default:
register = getRegister(instructions, reg, false, true);
instructions.add(new Instruction11x(Opcode.RETURN_OBJECT, register).setInject()); //256
restoreRegister(instructions, reg, false, true);
break;
}
}
private Instruction getMoveInstruction(Opcode op, short regDest, short regSrc) {
Instruction ins = null;
if (op == Opcode.MOVE_16) {
if (regDest < 16 && regSrc < 16) {
ins = new Instruction12x(Opcode.MOVE, (byte) regDest, (byte) regSrc);
} else if (regDest < 256) {
ins = new Instruction22x(Opcode.MOVE_FROM16, (byte) regDest, regSrc);
} else {
ins = new Instruction32x(op, regDest, regSrc);
}
} else if (op == Opcode.MOVE_OBJECT_16) {
if (regDest < 16 && regSrc < 16) {
ins = new Instruction12x(Opcode.MOVE_OBJECT, (byte) regDest, (byte) regSrc);
} else if (regDest < 16) {
ins = new Instruction22x(Opcode.MOVE_OBJECT_FROM16, (byte) regDest, regSrc);
} else {
ins = new Instruction32x(op, regDest, regSrc);
}
} else if (op == Opcode.MOVE_WIDE_16) {
if (regDest < 16 && regSrc < 16) {
ins = new Instruction12x(Opcode.MOVE_WIDE, (byte) regDest, (byte) regSrc);
} else if (regDest < 256) {
ins = new Instruction22x(Opcode.MOVE_WIDE_FROM16, (byte) regDest, regSrc);
} else {
ins = new Instruction32x(op, regDest, regSrc);
}
}
return ins;
}
}