Implemented deodex functionality

git-svn-id: https://smali.googlecode.com/svn/trunk@637 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2010-02-22 00:57:15 +00:00
parent 2ba2d0f16b
commit 0c65e0f4f5
29 changed files with 1796 additions and 2807 deletions

View File

@ -43,16 +43,6 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
protected final StringTemplateGroup stg;
protected final T instruction;
/**
* Instructions that execution could pass on to next
*/
private LinkedList<InstructionMethodItem> successors = new LinkedList<InstructionMethodItem>();
/**
* Instructions that can pass on execution to this one
*/
private LinkedList<InstructionMethodItem> predecessors = new LinkedList<InstructionMethodItem>();
public InstructionMethodItem(CodeItem codeItem, int codeAddress, StringTemplateGroup stg, T instruction) {
super(codeAddress);
this.codeItem = codeItem;
@ -97,6 +87,14 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
if (instruction instanceof InstructionWithReference) {
setInstructionWithReferenceAttributes((InstructionWithReference)instruction, template);
}
if (instruction instanceof OdexedInvokeVirtual) {
setOdexedInvokeVirtualAttributes((OdexedInvokeVirtual)instruction, template);
}
if (instruction instanceof OdexedFieldAccess) {
setOdexedFieldAccessAttributes((OdexedFieldAccess)instruction, template);
}
}
private void setLiteralAttributes(LiteralInstruction instruction, StringTemplate template) {
@ -170,4 +168,12 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
template.setAttribute("Reference", Reference.createReference(template.getGroup(),
instruction.getReferencedItem()));
}
private void setOdexedInvokeVirtualAttributes(OdexedInvokeVirtual instruction, StringTemplate template) {
template.setAttribute("MethodIndex", instruction.getMethodIndex());
}
private void setOdexedFieldAccessAttributes(OdexedFieldAccess instruction, StringTemplate template) {
template.setAttribute("FieldOffset", instruction.getFieldOffset());
}
}

View File

@ -2,6 +2,7 @@ package org.jf.baksmali.Adaptors.Format;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.jf.baksmali.Adaptors.MethodDefinition;
import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
import org.jf.dexlib.Code.Format.*;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.OffsetInstruction;
@ -15,8 +16,8 @@ public class InstructionMethodItemFactory {
CodeItem codeItem,
int codeAddress,
StringTemplateGroup stg,
Instruction instruction) {
Instruction instruction,
boolean isLastInstruction) {
if (instruction instanceof OffsetInstruction) {
return new OffsetInstructionFormatMethodItem(methodDefinition.getLabelCache(), codeItem, codeAddress, stg,
instruction);
@ -32,6 +33,9 @@ public class InstructionMethodItemFactory {
case SparseSwitchData:
return new SparseSwitchMethodItem(methodDefinition, codeItem, codeAddress, stg,
(SparseSwitchDataPseudoInstruction)instruction);
case UnresolvedNullReference:
return new UnresolvedNullReferenceMethodItem(codeItem, codeAddress, stg,
(UnresolvedNullReference)instruction, isLastInstruction);
default:
return new InstructionMethodItem(codeItem, codeAddress, stg, instruction);
}

View File

@ -1,21 +0,0 @@
package org.jf.baksmali.Adaptors.Format;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.CodeItem;
public class OdexInstructionMethodItem<T extends Instruction> extends InstructionMethodItem<T> {
protected Instruction fixedInstruction = null;
public OdexInstructionMethodItem(CodeItem codeItem, int codeAddress, StringTemplateGroup stg, T ins) {
super(codeItem, codeAddress, stg, ins);
}
public Instruction getFixedInstruction() {
return fixedInstruction;
}
public void setFixedInstruction(Instruction fixedInstruction) {
this.fixedInstruction = fixedInstruction;
}
}

View File

@ -26,29 +26,33 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib.Code.Format;
package org.jf.baksmali.Adaptors.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Code.Format.UnresolvedNullReference;
import org.jf.dexlib.CodeItem;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.antlr.stringtemplate.StringTemplate;
public class DeadInstruction extends Instruction {
public final Instruction OriginalInstruction;
public class UnresolvedNullReferenceMethodItem extends InstructionMethodItem<UnresolvedNullReference> {
public final boolean isLastInstruction;
public DeadInstruction(Instruction originalInstruction) {
super(originalInstruction.opcode);
this.OriginalInstruction = originalInstruction;
public UnresolvedNullReferenceMethodItem(CodeItem codeItem, int codeAddress, StringTemplateGroup stg,
UnresolvedNullReference instruction, boolean isLastInstruction) {
super(codeItem, codeAddress, stg, instruction);
this.isLastInstruction = isLastInstruction;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
//don't write anything
}
protected void setAttributes(StringTemplate template) {
template.setAttribute("Register", formatRegister(instruction.ObjectRegisterNum));
switch (instruction.OriginalInstruction.opcode)
{
case INVOKE_VIRTUAL_QUICK_RANGE:
case INVOKE_SUPER_QUICK_RANGE:
template.setAttribute("UseInvokeRange", 1);
if (isLastInstruction) {
template.setAttribute("AddGoto", 1);
}
}
@Override
public int getSize(int codeAddress) {
return OriginalInstruction.getSize(codeAddress);
}
public Format getFormat() {
return Format.DeadInstruction;
}
}

View File

@ -37,6 +37,7 @@ import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
import org.jf.dexlib.Code.Analysis.RegisterType;
import org.jf.dexlib.Code.Analysis.ValidationException;
import org.jf.dexlib.Code.Format.Format;
import org.jf.dexlib.Debug.DebugInstructionIterator;
import org.jf.dexlib.Util.AccessFlags;
import org.antlr.stringtemplate.StringTemplateGroup;
@ -66,7 +67,7 @@ public class MethodDefinition {
//TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
if (encodedMethod.codeItem != null) {
methodAnalyzer = new MethodAnalyzer(encodedMethod);
methodAnalyzer = new MethodAnalyzer(encodedMethod, baksmali.deodex);
AnalyzedInstruction[] instructions = methodAnalyzer.makeInstructionArray();
packedSwitchMap = new SparseIntArray(1);
@ -78,17 +79,17 @@ public class MethodDefinition {
int currentCodeAddress = 0;
for (int i=0; i<instructions.length; i++) {
AnalyzedInstruction instruction = instructions[i];
if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) {
if (instruction.getInstruction().opcode == Opcode.PACKED_SWITCH) {
packedSwitchMap.append(
currentCodeAddress + ((OffsetInstruction)instruction.instruction).getTargetAddressOffset(),
currentCodeAddress + ((OffsetInstruction)instruction.getInstruction()).getTargetAddressOffset(),
currentCodeAddress);
} else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) {
} else if (instruction.getInstruction().opcode == Opcode.SPARSE_SWITCH) {
sparseSwitchMap.append(
currentCodeAddress + ((OffsetInstruction)instruction.instruction).getTargetAddressOffset(),
currentCodeAddress + ((OffsetInstruction)instruction.getInstruction()).getTargetAddressOffset(),
currentCodeAddress);
}
instructionMap.append(currentCodeAddress, i);
currentCodeAddress += instruction.instruction.getSize(currentCodeAddress);
currentCodeAddress += instruction.getInstruction().getSize(currentCodeAddress);
}
} else {
packedSwitchMap = null;
@ -248,7 +249,7 @@ public class MethodDefinition {
}
AnalyzedInstruction[] instructions;
if (baksmali.registerInfo != 0) {
if (baksmali.registerInfo != 0 || baksmali.deodex) {
instructions = methodAnalyzer.analyze();
ValidationException validationException = methodAnalyzer.getValidationException();
@ -261,6 +262,17 @@ public class MethodDefinition {
instructions = methodAnalyzer.makeInstructionArray();
}
AnalyzedInstruction lastInstruction = null;
for (int i=instructions.length-1; i>=0; i--) {
AnalyzedInstruction instruction = instructions[i];
if (!instruction.isDead()) {
lastInstruction = instruction;
break;
}
}
BitSet printPreRegister = new BitSet(registerCount);
BitSet printPostRegister = new BitSet(registerCount);
@ -268,8 +280,20 @@ public class MethodDefinition {
for (int i=0; i<instructions.length; i++) {
AnalyzedInstruction instruction = instructions[i];
methodItems.add(InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
encodedMethod.codeItem, currentCodeAddress, stg, instruction.instruction));
MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
encodedMethod.codeItem, currentCodeAddress, stg, instruction.getInstruction(),
instruction == lastInstruction);
if (instruction.isDead()) {
methodItems.add(new CommentedOutMethodItem(stg, methodItem));
} else {
methodItems.add(methodItem);
}
if (instruction.getInstruction().getFormat() == Format.UnresolvedNullReference) {
methodItems.add(new CommentedOutMethodItem(stg, InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
encodedMethod.codeItem, currentCodeAddress, stg, instruction.getOriginalInstruction(), false)));
}
if (i != instructions.length - 1) {
methodItems.add(new BlankMethodItem(stg, currentCodeAddress));
@ -280,7 +304,7 @@ public class MethodDefinition {
currentCodeAddress, -1000));
}
if (baksmali.registerInfo != 0 && !instruction.instruction.getFormat().variableSizeFormat) {
if (baksmali.registerInfo != 0 && !instruction.getInstruction().getFormat().variableSizeFormat) {
printPreRegister.clear();
printPostRegister.clear();
@ -314,19 +338,21 @@ public class MethodDefinition {
}
if (!printPreRegister.isEmpty()) {
methodItems.add(new CommentMethodItem(stg,
getPreInstructionRegisterString(instruction, printPreRegister),
currentCodeAddress, 99.9));
String comment = getPreInstructionRegisterString(instruction, printPreRegister);
if (comment != null && comment.length() > 0) {
methodItems.add(new CommentMethodItem(stg, comment, currentCodeAddress, 99.9));
}
}
if (!printPostRegister.isEmpty()) {
methodItems.add(new CommentMethodItem(stg,
getPostInstructionRegisterString(instruction, printPostRegister),
currentCodeAddress, 100.1));
String comment = getPostInstructionRegisterString(instruction, printPostRegister);
if (comment != null && comment.length() > 0) {
methodItems.add(new CommentMethodItem(stg, comment, currentCodeAddress, 100.1));
}
}
}
currentCodeAddress += instruction.instruction.getSize(currentCodeAddress);
currentCodeAddress += instruction.getInstruction().getSize(currentCodeAddress);
}
addTries(methodItems);
@ -351,13 +377,13 @@ public class MethodDefinition {
}
private void addArgsRegs(BitSet printPreRegister, AnalyzedInstruction analyzedInstruction) {
if (analyzedInstruction.instruction instanceof RegisterRangeInstruction) {
RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
if (analyzedInstruction.getInstruction() instanceof RegisterRangeInstruction) {
RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.getInstruction();
printPreRegister.set(instruction.getStartRegister(),
instruction.getStartRegister() + instruction.getRegCount());
} else if (analyzedInstruction.instruction instanceof FiveRegisterInstruction) {
FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
} else if (analyzedInstruction.getInstruction() instanceof FiveRegisterInstruction) {
FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.getInstruction();
int regCount = instruction.getRegCount();
switch (regCount) {
case 5:
@ -375,17 +401,17 @@ public class MethodDefinition {
case 1:
printPreRegister.set(instruction.getRegisterD());
}
} else if (analyzedInstruction.instruction instanceof ThreeRegisterInstruction) {
ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
} else if (analyzedInstruction.getInstruction() instanceof ThreeRegisterInstruction) {
ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.getInstruction();
printPreRegister.set(instruction.getRegisterA());
printPreRegister.set(instruction.getRegisterB());
printPreRegister.set(instruction.getRegisterC());
} else if (analyzedInstruction.instruction instanceof TwoRegisterInstruction) {
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
} else if (analyzedInstruction.getInstruction() instanceof TwoRegisterInstruction) {
TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.getInstruction();
printPreRegister.set(instruction.getRegisterA());
printPreRegister.set(instruction.getRegisterB());
} else if (analyzedInstruction.instruction instanceof SingleRegisterInstruction) {
SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
} else if (analyzedInstruction.getInstruction() instanceof SingleRegisterInstruction) {
SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.getInstruction();
printPreRegister.set(instruction.getRegisterA());
}
}
@ -414,7 +440,7 @@ public class MethodDefinition {
sb.append(instruction.getPreInstructionRegisterType(registerNum));
sb.append(":merge{");
RegisterType mergedRegisterType = instruction.getPreInstructionRegisterType(registerNum);
RegisterType mergedRegisterType = null;
boolean addRegister = false;
boolean first = true;
@ -422,10 +448,15 @@ public class MethodDefinition {
RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
if (!first) {
sb.append(',');
if (!addRegister && mergedRegisterType != predecessorRegisterType) {
addRegister = true;
if (!addRegister) {
sb.append(',');
if (mergedRegisterType != predecessorRegisterType) {
addRegister = true;
}
mergedRegisterType = mergedRegisterType.merge(predecessorRegisterType);
}
} else {
mergedRegisterType = predecessorRegisterType;
}
if (predecessor.getInstructionIndex() == -1) {
@ -436,7 +467,7 @@ public class MethodDefinition {
sb.append(Integer.toHexString(methodAnalyzer.getInstructionAddress(predecessor)));
sb.append(':');
}
sb.append(predecessor.getPostInstructionRegisterType(registerNum).toString());
sb.append(predecessorRegisterType.toString());
first = false;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
package org.jf.baksmali.Deodex;
public class DeodexUtil2 {
}

View File

@ -1,590 +0,0 @@
/*
* [The "BSD licence"]
* Copyright (c) 2009 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.baksmali.Deodex;
import org.jf.dexlib.*;
import org.jf.dexlib.Util.SparseArray;
import java.net.Socket;
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
/**
* This class handles communication with the deodexerant helper binary,
* as well as caching the results of any deodexerant lookups
*/
public class Deodexerant {
private final String host;
private final int port;
private final HashMap<String, ClassData> vtableMap = new HashMap<String, ClassData>();
private final HashMap<CommonSuperclassLookup, String> cachedCommonSuperclassLookup =
new HashMap<CommonSuperclassLookup, String>();
private InlineMethod[] inlineMethods;
public final DexFile dexFile;
private Socket socket = null;
private PrintWriter out = null;
private BufferedReader in = null;
public Deodexerant(DexFile dexFile, String host, int port) {
this.dexFile = dexFile;
this.host = host;
this.port = port;
}
private void loadInlineMethods() {
List<String> responseLines = sendMultilineCommand("I");
inlineMethods = new InlineMethod[responseLines.size()];
for (int i=0; i<inlineMethods.length; i++) {
String response = responseLines.get(i);
if (!response.startsWith("inline: ")) {
throw new RuntimeException("Invalid response from deodexerant");
}
String[] parts = response.substring(8).split(" ");
if (parts.length != 2) {
throw new RuntimeException("Invalid response from deodexerant");
}
String methodType = parts[0];
InlineMethodType type;
if (methodType.equals("virtual")) {
type = InlineMethodType.Virtual;
} else if (methodType.equals("direct")) {
type = InlineMethodType.Direct;
} else if (methodType.equals("static")) {
type = InlineMethodType.Static;
} else {
throw new RuntimeException("Invalid response from deodexerant");
}
String methodDescriptor = parts[1];
inlineMethods[i] = new InlineMethod(methodDescriptor, type);
}
}
public InlineMethod lookupInlineMethod(int inlineMethodIndex) {
if (inlineMethods == null) {
loadInlineMethods();
}
if (inlineMethodIndex >= inlineMethods.length) {
throw new RuntimeException("Invalid inline method index " + inlineMethodIndex + ". Too big.");
}
return inlineMethods[inlineMethodIndex];
}
private TypeIdItem resolveTypeOrSupertype(String type) {
TypeIdItem typeItem = TypeIdItem.internTypeIdItem(dexFile, type);
while (typeItem == null) {
type = lookupSuperclass(type);
if (type == null) {
throw new RuntimeException("Could not find the type or a supertype of " + type + " in the dex file");
}
typeItem = TypeIdItem.internTypeIdItem(dexFile, type);
}
return typeItem;
}
public FieldIdItem lookupField(String type, int fieldOffset) {
ClassData classData = getClassData(type);
return classData.lookupField(fieldOffset);
}
private ClassData getClassData(String type) {
ClassData classData = vtableMap.get(type);
if (classData == null) {
classData = new ClassData(type);
vtableMap.put(type, classData);
}
return classData;
}
public MethodIdItem lookupVirtualMethod(String classType, int methodIndex, boolean lookupSuper) {
if (lookupSuper) {
classType = lookupSuperclass(classType);
}
ClassData classData = getClassData(classType);
return classData.lookupMethod(methodIndex);
}
public String lookupSuperclass(String typeDescriptor) {
connectIfNeeded();
String response = sendCommand("P " + typeDescriptor);
int colon = response.indexOf(':');
if (colon == -1) {
throw new RuntimeException("Invalid response from deodexerant");
}
String type = response.substring(colon+2);
if (type.length() == 0) {
return null;
}
return type;
}
public String lookupCommonSuperclass(String typeDescriptor1, String typeDescriptor2) {
CommonSuperclassLookup lookup = new CommonSuperclassLookup(typeDescriptor1, typeDescriptor2);
String result = cachedCommonSuperclassLookup.get(lookup);
if (result == null) {
String response = sendCommand("C " + typeDescriptor1 + " " + typeDescriptor2);
int colon = response.indexOf(':');
if (colon == -1) {
return null;
}
result = response.substring(colon+2);
cachedCommonSuperclassLookup.put(lookup, result);
}
return result;
}
private String sendCommand(String cmd) {
try {
connectIfNeeded();
out.println(cmd);
out.flush();
String response = in.readLine();
if (response.startsWith("err")) {
String error = response.substring(5);
throw new RuntimeException(error);
}
return response;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
//The command is still just a single line, but we're expecting a multi-line
//response. The repsonse is considered finished when a line starting with "err"
//or with "done" is encountered
private List<String> sendMultilineCommand(String cmd) {
try {
connectIfNeeded();
out.println(cmd);
out.flush();
ArrayList<String> responseLines = new ArrayList<String>();
String response = in.readLine();
if (response == null) {
throw new RuntimeException("Error talking to deodexerant");
}
while (!response.startsWith("done"))
{
if (response.startsWith("err")) {
throw new RuntimeException(response.substring(5));
}
responseLines.add(response);
response = in.readLine();
}
return responseLines;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private void connectIfNeeded() {
try {
if (socket != null) {
return;
}
socket = new Socket(host, port);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private MethodIdItem parseAndResolveMethod(String classType, String methodName, String methodParams,
String methodRet) {
TypeIdItem classTypeItem = resolveTypeOrSupertype(classType);
StringIdItem methodNameItem = StringIdItem.internStringIdItem(dexFile, methodName);
if (methodNameItem == null) {
return null;
}
LinkedList<TypeIdItem> paramList = new LinkedList<TypeIdItem>();
for (int i=0; i<methodParams.length(); i++) {
switch (methodParams.charAt(i)) {
case 'Z':
case 'B':
case 'S':
case 'C':
case 'I':
case 'J':
case 'F':
case 'D':
paramList.add(getType(Character.toString(methodParams.charAt(i))));
break;
case 'L':
{
int end = methodParams.indexOf(';', i);
if (end == -1) {
throw new RuntimeException("invalid parameter in the method");
}
paramList.add(getType(methodParams.substring(i, end+1)));
i = end;
break;
}
case '[':
{
int end;
int typeStart = i+1;
while (typeStart < methodParams.length() && methodParams.charAt(typeStart) == '[') {
typeStart++;
}
switch (methodParams.charAt(typeStart)) {
case 'Z':
case 'B':
case 'S':
case 'C':
case 'I':
case 'J':
case 'F':
case 'D':
end = typeStart;
break;
case 'L':
end = methodParams.indexOf(';', typeStart);
if (end == -1) {
throw new RuntimeException("invalid parameter in the method");
}
break;
default:
throw new RuntimeException("invalid parameter in the method");
}
paramList.add(getType(methodParams.substring(i, end+1)));
i = end;
break;
}
default:
throw new RuntimeException("invalid parameter in the method");
}
}
TypeListItem paramListItem = null;
if (paramList.size() > 0) {
paramListItem = TypeListItem.internTypeListItem(dexFile, paramList);
if (paramListItem == null) {
throw new RuntimeException("Could not find type list item in dex file");
}
}
TypeIdItem retType = getType(methodRet);
ProtoIdItem protoItem = ProtoIdItem.internProtoIdItem(dexFile, retType, paramListItem);
if (protoItem == null) {
return null;
}
MethodIdItem methodIdItem;
do {
methodIdItem = MethodIdItem.internMethodIdItem(dexFile, classTypeItem, protoItem, methodNameItem);
if (methodIdItem != null) {
return methodIdItem;
}
String superclassDescriptor = lookupSuperclass(classTypeItem.getTypeDescriptor());
if (superclassDescriptor == null) {
return null;
}
classTypeItem = TypeIdItem.internTypeIdItem(dexFile, superclassDescriptor);
while (classTypeItem == null && superclassDescriptor != null) {
superclassDescriptor = lookupSuperclass(superclassDescriptor);
classTypeItem = TypeIdItem.internTypeIdItem(dexFile, superclassDescriptor);
}
} while (true);
}
private static final Pattern fullMethodPattern = Pattern.compile("(\\[*(?:L[^;]+;|[ZBSCIJFD]))->([^(]+)\\(([^)]*)\\)(.+)");
private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)");
//private static final Pattern fieldPattern = Pattern.compile("(\\[*L[^;]+;)->([^:]+):(.+)");
private FieldIdItem parseAndResolveField(String classType, String field) {
//expecting a string like someField:Lfield/type;
String[] parts = field.split(":");
if (parts.length != 2) {
throw new RuntimeException("Invalid field descriptor " + field);
}
TypeIdItem classTypeItem = resolveTypeOrSupertype(classType);
if (classTypeItem == null) {
return null;
}
String fieldName = parts[0];
String fieldType = parts[1];
StringIdItem fieldNameItem = StringIdItem.internStringIdItem(dexFile, fieldName);
if (fieldNameItem == null) {
return null;
}
TypeIdItem fieldTypeItem = TypeIdItem.internTypeIdItem(dexFile, fieldType);
if (fieldTypeItem == null) {
return null;
}
FieldIdItem fieldIdItem;
do {
fieldIdItem = FieldIdItem.internFieldIdItem(dexFile, classTypeItem, fieldTypeItem, fieldNameItem);
if (fieldIdItem != null) {
return fieldIdItem;
}
String superclassDescriptor = lookupSuperclass(classTypeItem.getTypeDescriptor());
classTypeItem = TypeIdItem.internTypeIdItem(dexFile, superclassDescriptor);
while (classTypeItem == null && superclassDescriptor != null) {
superclassDescriptor = lookupSuperclass(superclassDescriptor);
classTypeItem = TypeIdItem.internTypeIdItem(dexFile, superclassDescriptor);
}
} while (classTypeItem != null);
throw new RuntimeException("Could not find field in dex file");
}
public enum InlineMethodType {
Virtual,
Direct,
Static
}
public class InlineMethod {
public final String inlineMethodDescriptor;
private final InlineMethodType methodType;
private MethodIdItem methodIdItem = null;
public InlineMethod(String inlineMethodDescriptor, InlineMethodType methodType) {
this.inlineMethodDescriptor = inlineMethodDescriptor;
this.methodType = methodType;
}
public MethodIdItem getMethodIdItem() {
if (methodIdItem == null) {
loadMethod();
}
return methodIdItem;
}
public InlineMethodType getMethodType() {
return methodType;
}
private void loadMethod() {
Matcher m = fullMethodPattern.matcher(inlineMethodDescriptor);
if (!m.matches()) {
throw new RuntimeException("Invalid method descriptor: " + inlineMethodDescriptor);
}
String classType = m.group(1);
String methodName = m.group(2);
String methodParams = m.group(3);
String methodRet = m.group(4);
MethodIdItem method = parseAndResolveMethod(classType, methodName, methodParams, methodRet);
if (method == null) {
throw new RuntimeException("Could not resolve method " + inlineMethodDescriptor);
}
this.methodIdItem = method;
}
}
private TypeIdItem getType(String typeDescriptor) {
TypeIdItem type = TypeIdItem.internTypeIdItem(dexFile, typeDescriptor);
if (type == null) {
throw new RuntimeException("Could not find type \"" + typeDescriptor + "\" in dex file");
}
return type;
}
private class ClassData {
private final String ClassType;
private boolean vtableLoaded = false;
private String[] methodNames;
private String[] methodParams;
private String[] methodRets;
private MethodIdItem[] resolvedMethods;
private boolean fieldsLoaded = false;
private SparseArray<String> instanceFields;
private SparseArray<FieldIdItem> resolvedFields;
public ClassData(String classType) {
this.ClassType = classType;
}
public MethodIdItem lookupMethod(int index) {
if (!vtableLoaded) {
loadvtable();
}
if (index >= resolvedMethods.length) {
throw new RuntimeException("Invalid vtable index " + index + ". Too large.");
}
if (resolvedMethods[index] == null) {
resolvedMethods[index] = parseAndResolveMethod(ClassType, methodNames[index], methodParams[index],
methodRets[index]);
}
return resolvedMethods[index];
}
public FieldIdItem lookupField(int fieldOffset) {
if (!fieldsLoaded) {
loadFields();
}
FieldIdItem fieldIdItem = resolvedFields.get(fieldOffset);
if (fieldIdItem == null) {
String field = instanceFields.get(fieldOffset);
if (field == null) {
throw new RuntimeException("Invalid field offset " + fieldOffset);
}
fieldIdItem = parseAndResolveField(ClassType, field);
if (fieldIdItem != null) {
resolvedFields.put(fieldOffset, fieldIdItem);
}
}
return fieldIdItem;
}
private void loadvtable() {
List<String> responseLines = sendMultilineCommand("V " + ClassType);
methodNames = new String[responseLines.size()];
methodParams = new String[responseLines.size()];
methodRets = new String[responseLines.size()];
resolvedMethods = new MethodIdItem[responseLines.size()];
int index = 0;
for (String vtableEntry: responseLines) {
if (!vtableEntry.startsWith("vtable: ")) {
throw new RuntimeException("Invalid response from deodexerant");
}
String method = vtableEntry.substring(8);
Matcher m = shortMethodPattern.matcher(method);
if (!m.matches()) {
throw new RuntimeException("invalid method string: " + method);
}
methodNames[index] = m.group(1);
methodParams[index] = m.group(2);
methodRets[index] = m.group(3);
index++;
}
vtableLoaded = true;
}
private void loadFields() {
List<String> responseLines = sendMultilineCommand("F " + ClassType);
instanceFields = new SparseArray<String>(responseLines.size());
resolvedFields = new SparseArray<FieldIdItem>(responseLines.size());
for (String fieldLine: responseLines) {
if (!fieldLine.startsWith("field: ")) {
throw new RuntimeException("Invalid response from deodexerant");
}
String field = fieldLine.substring(7);
String[] parts = field.split(" ");
if (parts.length != 2) {
throw new RuntimeException("Invalid response from deodexerant");
}
int fieldOffset = Integer.parseInt(parts[0]);
instanceFields.put(fieldOffset, parts[1]);
}
fieldsLoaded = true;
}
}
private static class CommonSuperclassLookup {
public final String Type1;
public final String Type2;
public CommonSuperclassLookup(String type1, String type2) {
this.Type1 = type1;
this.Type2 = type2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CommonSuperclassLookup that = (CommonSuperclassLookup) o;
return Type1.equals(that.Type1) && Type2.equals(that.Type2);
}
@Override
public int hashCode() {
return Type1.hashCode() + 31 * Type2.hashCode();
}
}
}

View File

@ -31,7 +31,6 @@ package org.jf.baksmali;
import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.baksmali.Deodex.*;
import org.jf.baksmali.Renderers.*;
import org.jf.dexlib.Code.Analysis.ClassPath;
import org.jf.dexlib.DexFile;
@ -46,11 +45,11 @@ public class baksmali {
public static boolean useSequentialLabels = false;
public static boolean outputDebugInfo = true;
public static boolean addCodeOffsets = false;
public static boolean deodex = false;
public static int registerInfo = 0;
public static String bootClassPath;
public static DeodexUtil deodexUtil = null;
public static void disassembleDexFile(DexFile dexFile, Deodexerant deodexerant, String outputDirectory,
public static void disassembleDexFile(DexFile dexFile, boolean deodex, String outputDirectory,
String bootClassPathDir, String bootClassPath, boolean noParameterRegisters,
boolean useLocalsDirective, boolean useSequentialLabels,
boolean outputDebugInfo, boolean addCodeOffsets, int registerInfo)
@ -60,17 +59,14 @@ public class baksmali {
baksmali.useSequentialLabels = useSequentialLabels;
baksmali.outputDebugInfo = outputDebugInfo;
baksmali.addCodeOffsets = addCodeOffsets;
baksmali.deodex = deodex;
baksmali.registerInfo = registerInfo;
baksmali.bootClassPath = bootClassPath;
if (registerInfo != 0) {
if (registerInfo != 0 || deodex) {
ClassPath.InitializeClassPath(bootClassPathDir, bootClassPath==null?null:bootClassPath.split(":"), dexFile);
}
if (deodexerant != null) {
baksmali.deodexUtil = new DeodexUtil(deodexerant);
}
File outputDirectoryFile = new File(outputDirectory);
if (!outputDirectoryFile.exists()) {
if (!outputDirectoryFile.mkdirs()) {

View File

@ -0,0 +1,150 @@
package org.jf.baksmali;
import org.apache.commons.cli.*;
import org.jf.dexlib.Code.Analysis.ClassPath;
public class deodexCheck {
public static void main(String[] args) {
CommandLineParser parser = new PosixParser();
CommandLine commandLine;
Options options = buildOptions();
try {
commandLine = parser.parse(options, args);
} catch (ParseException ex) {
usage(options);
return;
}
String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
String bootClassPathDir = ".";
String deodexerantHost = null;
int deodexerantPort = 0;
int classStartIndex = 0;
String[] remainingArgs = commandLine.getArgs();
if (commandLine.hasOption("v")) {
main.version();
return;
}
if (commandLine.hasOption("?")) {
usage(options);
return;
}
if (remainingArgs.length > 0) {
usage(options);
return;
}
if (commandLine.hasOption("c")) {
String bcp = commandLine.getOptionValue("c");
if (bcp.charAt(0) == ':') {
bootClassPath = bootClassPath + bcp;
} else {
bootClassPath = bcp;
}
}
if (commandLine.hasOption("i")) {
try {
classStartIndex = Integer.parseInt(commandLine.getOptionValue("i"));
} catch (Exception ex) {
}
}
if (commandLine.hasOption("C")) {
bootClassPathDir = commandLine.getOptionValue("C");
}
if (commandLine.hasOption("x")) {
String deodexerantAddress = commandLine.getOptionValue("x");
String[] parts = deodexerantAddress.split(":");
if (parts.length != 2) {
System.err.println("Invalid deodexerant address. Expecting :<port> or <host>:<port>");
System.exit(1);
}
deodexerantHost = parts[0];
if (deodexerantHost.length() == 0) {
deodexerantHost = "localhost";
}
try {
deodexerantPort = Integer.parseInt(parts[1]);
} catch (NumberFormatException ex) {
System.err.println("Invalid port \"" + deodexerantPort + "\" for deodexerant address");
System.exit(1);
}
}
ClassPath.InitializeClassPath(bootClassPathDir, bootClassPath==null?null:bootClassPath.split(":"), null);
ClassPath.validateAgainstDeodexerant(deodexerantHost, deodexerantPort, classStartIndex);
}
/**
* Prints the usage message.
*/
private static void usage(Options options) {
HelpFormatter formatter = new HelpFormatter();
formatter.setWidth(100);
formatter.printHelp("java -classpath baksmali.jar deodexCheck -x HOST:PORT [options]",
"disassembles and/or dumps a dex file", options, "");
}
private static Options buildOptions() {
Options options = new Options();
Option versionOption = OptionBuilder.withLongOpt("version")
.withDescription("prints the version then exits")
.create("v");
Option helpOption = OptionBuilder.withLongOpt("help")
.withDescription("prints the help message then exits")
.create("?");
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
.withDescription("the bootclasspath jars to use, for analysis. Defaults to " +
"core.jar:ext.jar:framework.jar:android.policy.jar:services.jar. If you specify a value that " +
"begins with a :, it will be appended to the default bootclasspath")
.hasOptionalArg()
.withArgName("BOOTCLASSPATH")
.create("c");
Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir")
.withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " +
"directory")
.hasArg()
.withArgName("DIR")
.create("C");
Option deodexerantOption = OptionBuilder.withLongOpt("deodexerant")
.isRequired()
.withDescription("connect to deodexerant on the specified HOST:PORT, and validate the virtual method " +
"indexes, field offsets and inline methods against what dexlib calculates")
.hasArg()
.withArgName("HOST:PORT")
.create("x");
Option classStartOption = OptionBuilder.withLongOpt("class-start-index")
.withDescription("Start checking classes at the given class index")
.hasArg()
.withArgName("CLASSINDEX")
.create("i");
options.addOption(versionOption);
options.addOption(helpOption);
options.addOption(deodexerantOption);
options.addOption(classPathOption);
options.addOption(classPathDirOption);
options.addOption(classStartOption);
return options;
}
}

View File

@ -17,7 +17,6 @@
package org.jf.baksmali;
import org.apache.commons.cli.*;
import org.jf.baksmali.Deodex.Deodexerant;
import org.jf.dexlib.DexFile;
import java.io.File;
@ -88,6 +87,7 @@ public class main {
boolean useSequentialLabels = false;
boolean outputDebugInfo = true;
boolean addCodeOffsets = false;
boolean deodex = false;
int registerInfo = 0;
@ -95,10 +95,9 @@ public class main {
String dumpFileName = null;
String outputDexFileName = null;
String inputDexFileName = null;
String deodexerantHost = null;
String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
String bootClassPathDir = ".";
int deodexerantPort = 0;
String[] remainingArgs = commandLine.getArgs();
@ -176,30 +175,14 @@ public class main {
break;
case 'c':
String bcp = commandLine.getOptionValue("c");
if (bcp.charAt(0) == ':') {
if (bcp != null && bcp.charAt(0) == ':') {
bootClassPath = bootClassPath + bcp;
} else {
bootClassPath = bcp;
}
break;
case 'x':
String deodexerantAddress = commandLine.getOptionValue("x");
String[] parts = deodexerantAddress.split(":");
if (parts.length != 2) {
System.err.println("Invalid deodexerant address. Expecting :<port> or <host>:<port>");
System.exit(1);
}
deodexerantHost = parts[0];
if (deodexerantHost.length() == 0) {
deodexerantHost = "localhost";
}
try {
deodexerantPort = Integer.parseInt(parts[1]);
} catch (NumberFormatException ex) {
System.err.println("Invalid port \"" + deodexerantPort + "\" for deodexerant address");
System.exit(1);
}
deodex = true;
break;
case 'N':
disassemble = false;
@ -240,16 +223,6 @@ public class main {
//Read in and parse the dex file
DexFile dexFile = new DexFile(dexFileFile, !fixRegisters, false);
Deodexerant deodexerant = null;
if (deodexerantHost != null) {
if (!dexFile.isOdex()) {
System.err.println("-x cannot be used with a normal dex file. Ignoring -x");
}
deodexerant = new Deodexerant(dexFile, deodexerantHost, deodexerantPort);
}
if (dexFile.isOdex()) {
if (doDump) {
System.err.println("-d cannot be used with on odex file. Ignoring -d");
@ -257,15 +230,17 @@ public class main {
if (write) {
System.err.println("-w cannot be used with an odex file. Ignoring -w");
}
if (deodexerant == null) {
if (!deodex) {
System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
System.err.println("won't be able to re-assemble the results unless you use deodexerant, and");
System.err.println("the -x option for baksmali");
System.err.println("won't be able to re-assemble the results unless you deodex it with the -x");
System.err.println("option");
}
} else {
deodex = false;
}
if (disassemble) {
baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, bootClassPathDir, bootClassPath,
baksmali.disassembleDexFile(dexFile, deodex, outputDirectory, bootClassPathDir, bootClassPath,
noParameterRegisters, useLocalsDirective, useSequentialLabels, outputDebugInfo, addCodeOffsets,
registerInfo);
}
@ -317,7 +292,7 @@ public class main {
/**
* Prints the version message.
*/
private static void version() {
protected static void version() {
System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)");
System.out.println("Copyright (C) 2009 Ben Gruver");
System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
@ -366,15 +341,13 @@ public class main {
.create("F");
Option noParameterRegistersOption = OptionBuilder.withLongOpt("no-parameter-registers")
.withDescription("use the v<n> syntax instead of the p<n> syntax for registers mapped to method" +
" parameters")
.withDescription("use the v<n> syntax instead of the p<n> syntax for registers mapped to method " +
"parameters")
.create("p");
Option deodexerantOption = OptionBuilder.withLongOpt("deodexerant")
.withDescription("connect to deodexerant on the specified HOST:PORT, and deodex the input odex"
+ " file. This option is ignored if the input file is a dex file instead of an odex file")
.hasArg()
.withArgName("HOST:PORT")
Option deodexerantOption = OptionBuilder.withLongOpt("deodex")
.withDescription("deodex the given odex file. This option is ignored if the input file is not an " +
"odex file.")
.create("x");
Option useLocalsOption = OptionBuilder.withLongOpt("use-locals")

View File

@ -188,11 +188,6 @@ Format22cs(Opcode, RegisterA, RegisterB, FieldOffset) ::=
<Opcode> <RegisterA>, <RegisterB>, field@<FieldOffset>
>>
Format22csf(Opcode, RegisterA, RegisterB, Reference) ::=
<<
<Opcode> <RegisterA>, <RegisterB>, <Reference>
>>
Format22s(Opcode, RegisterA, RegisterB, Literal) ::=
<<
<Opcode> <RegisterA>, <RegisterB>, <Literal>
@ -248,21 +243,11 @@ Format35s(Opcode, Registers, Reference) ::=
<Opcode> {<Registers; separator=", ">}, <Reference>
>>
Format35sf(Opcode, Registers, Reference) ::=
<<
<Opcode> {<Registers; separator=", ">}, <Reference>
>>
Format35ms(Opcode, Registers, MethodIndex) ::=
<<
<Opcode> {<Registers; separator=", ">}, vtable@<MethodIndex>
>>
Format35msf(Opcode, Registers, Reference) ::=
<<
<Opcode> {<Registers; separator=", ">}, <Reference>
>>
Format3rc(Opcode, StartRegister, LastRegister, Reference) ::=
<<
<Opcode> {<StartRegister> .. <LastRegister>}, <Reference>
@ -273,11 +258,6 @@ Format3rms(Opcode, StartRegister, LastRegister, MethodIndex) ::=
<Opcode> {<StartRegister> .. <LastRegister>}, vtable@<MethodIndex>
>>
Format3rmsf(Opcode, StartRegister, LastRegister, Reference) ::=
<<
<Opcode> {<StartRegister> .. <LastRegister>}, <Reference>
>>
Format51l(Opcode, RegisterA, Literal) ::=
<<
<Opcode> <RegisterA>, <Literal>
@ -401,7 +381,6 @@ TypeReference(TypeDescriptor) ::=
>>
SimpleEncodedValue(Value) ::=
<<
<Value>

View File

@ -20,11 +20,7 @@
#and this will always throw an exception. Everything below
#here, until the here2: label is dead code, and should be
#commented out. This instruction itself should be be replaced
#with a call to Ljava/lang/Object;->hashCode()I, followed
#by a goto/32 0, which is just there to prevent dexopt from
#thinking that execution will fall off the end of the method
#i.e. if all the code following this was dead (and thus commented
#out)
#with a call to Ljava/lang/Object;->hashCode()I
invoke-virtual {v0}, Lrandomclass;->getSuperclass()Lsuperclass;
move-result-object v1

View File

@ -6,15 +6,13 @@ import org.jf.dexlib.ItemType;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.Util.ExceptionWithContext;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.*;
public class AnalyzedInstruction {
public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
/**
* The actual instruction
*/
public final Instruction instruction;
protected Instruction instruction;
/**
* The index of the instruction, where the first instruction in the method is at index 0, and so on
@ -24,35 +22,59 @@ public class AnalyzedInstruction {
/**
* Instructions that can pass on execution to this one during normal execution
*/
protected final LinkedList<AnalyzedInstruction> predecessors = new LinkedList<AnalyzedInstruction>();
protected final TreeSet<AnalyzedInstruction> predecessors = new TreeSet<AnalyzedInstruction>();
/**
* Instructions that can execution could pass on to next during normal execution
*/
protected final LinkedList<AnalyzedInstruction> successors = new LinkedList<AnalyzedInstruction>();
/**
* This contains the register types *before* the instruction has executed
*/
protected final RegisterType[] preRegisterMap;
/**
* This contains the register types *after* the instruction has executed
*/
protected final RegisterType[] postRegisterMap;
/**
* This is set to true when this instruction follows an odexed instruction that couldn't be deodexed. In this case
* the unodexable instruction is guaranteed to throw an NPE, so anything following it is dead, up until a non-dead
* code path merges in. And more importantly, the code following the unodexable instruction isn't verifiable in
* some cases, if it depends on the return/field type of the unodexeable instruction. Meaning that if the "dead"
* code was left in, dalvik would reject it because it couldn't verify the register types. In some cases, this
* dead code could be left in without ill-effect, but it's easier to always remove it, which is always valid. Since
* it is dead code, removing it won't have any effect.
* When deodexing, we might need to deodex this instruction multiple times, when we merge in new register
* information. When this happens, we need to restore the original (odexed) instruction, so we can deodex it again
*/
protected final Instruction originalInstruction;
/**
* A dead instruction is one that is unreachable because it follows an odexed instruction that can't be deodexed
* because it's object register is always null. In the non-odexed code that the odex was generated from, we would
* have technically considered this code reachable and could verify it, even though the instruction that ended up
* being odexed was always null, because we would assume both "paths" out of the instruction are valid - the one
* where execution proceeds normally to the next instruction, and the one where an exception occurs and execution
* either goes to a catch block, or out of the method.
*
* However, in the odexed case, we can't verify the code following an undeodexable instruction because we lack
* the register information from the undeodexable instruction - because we don't know the actual method or field
* that is being accessed.
*
* The undeodexable instruction is guaranteed to throw an NPE, so the following code is effectivetly unreachable.
* Once we detect an undeodexeable instruction, the following code is marked as dead up until a non-dead execution
* path merges in. Additionally, we remove the predecessors/successors of any dead instruction. For example, if
* there is a dead goto instruction, then we would remove the target instruction as a successor, and we would
* also remove the dead goto instruction as a predecessor to the target.
*/
protected boolean dead = false;
public AnalyzedInstruction(Instruction instruction, int instructionIndex, int registerCount) {
this.instruction = instruction;
this.originalInstruction = instruction;
this.instructionIndex = instructionIndex;
this.postRegisterMap = new RegisterType[registerCount];
this.preRegisterMap = new RegisterType[registerCount];
RegisterType unknown = RegisterType.getRegisterType(RegisterType.Category.Unknown, null);
for (int i=0; i<registerCount; i++) {
preRegisterMap[i] = unknown;
postRegisterMap[i] = unknown;
}
}
@ -65,38 +87,26 @@ public class AnalyzedInstruction {
return predecessors.size();
}
public List<AnalyzedInstruction> getPredecessors() {
return Collections.unmodifiableList(predecessors);
public SortedSet<AnalyzedInstruction> getPredecessors() {
return Collections.unmodifiableSortedSet(predecessors);
}
private boolean checkPredecessorSorted(AnalyzedInstruction predecessor) {
if (predecessors.size() == 0) {
return true;
}
if (predecessor.getInstructionIndex() <= predecessors.getLast().getInstructionIndex()) {
return false;
}
return true;
protected boolean addPredecessor(AnalyzedInstruction predecessor) {
return predecessors.add(predecessor);
}
protected void addPredecessor(AnalyzedInstruction predecessor) {
assert checkPredecessorSorted(predecessor);
predecessors.add(predecessor);
}
/**
* @return true if the successor was added or false if it wasn't added because it already existed
*/
protected boolean addSuccessor(AnalyzedInstruction successor) {
for (AnalyzedInstruction instruction: successors) {
if (instruction == successor) {
return false;
}
}
protected void addSuccessor(AnalyzedInstruction successor) {
successors.add(successor);
return true;
}
protected void setDeodexedInstruction(Instruction instruction) {
assert originalInstruction.opcode.odexOnly();
this.instruction = instruction;
}
protected void restoreOdexedInstruction() {
assert originalInstruction.opcode.odexOnly();
instruction = originalInstruction;
}
public int getSuccessorCount() {
@ -107,6 +117,18 @@ public class AnalyzedInstruction {
return Collections.unmodifiableList(successors);
}
public Instruction getInstruction() {
return instruction;
}
public Instruction getOriginalInstruction() {
return originalInstruction;
}
public boolean isDead() {
return dead;
}
/**
* Is this instruction a "beginning instruction". A beginning instruction is defined to be an instruction
* that can be the first successfully executed instruction in the method. The first instruction is always a
@ -114,6 +136,9 @@ public class AnalyzedInstruction {
* the first instruction of any exception handler for that try block is also a beginning instruction. And likewise,
* if any of those instructions can throw an exception and are covered by try blocks, the first instruction of the
* corresponding exception handler is a beginning instruction, etc.
*
* To determine this, we simply check if the first predecessor is the fake "StartOfMethod" instruction, which has
* an instruction index of -1.
* @return a boolean value indicating whether this instruction is a beginning instruction
*/
public boolean isBeginningInstruction() {
@ -121,34 +146,50 @@ public class AnalyzedInstruction {
return false;
}
if (predecessors.getFirst().instructionIndex == -1) {
if (predecessors.first().instructionIndex == -1) {
return true;
}
return false;
}
/*
* Sets the "post-instruction" register type as indicated. This should only be used to set
* the method parameter types for the "start of method" instruction, or to set the register
* type of the destination register during verification. The change to the register type
* will
* Merges the given register type into the specified pre-instruction register, and also sets the post-instruction
* register type accordingly if it isn't a destination register for this instruction
* @param registerNumber Which register to set
* @param registerType The "post-instruction" register type
* @param registerType The register type
* @returns true If the post-instruction register type was changed. This might be false if either the specified
* register is a destination register for this instruction, or if the pre-instruction register type didn't change
* after merging in the given register type
*/
protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) {
protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions) {
assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
assert registerType != null;
RegisterType oldRegisterType = postRegisterMap[registerNumber];
if (oldRegisterType == registerType) {
RegisterType oldRegisterType = preRegisterMap[registerNumber];
RegisterType mergedRegisterType = oldRegisterType.merge(registerType);
if (mergedRegisterType == oldRegisterType) {
return false;
}
postRegisterMap[registerNumber] = registerType;
return true;
preRegisterMap[registerNumber] = mergedRegisterType;
verifiedInstructions.clear(instructionIndex);
if (!setsRegister(registerNumber)) {
postRegisterMap[registerNumber] = mergedRegisterType;
return true;
}
return false;
}
protected RegisterType getMergedRegisterTypeFromPredecessors(int registerNumber) {
/**
* Iterates over the predecessors of this instruction, and merges all the post-instruction register types for the
* given register. Any dead, unreachable, or odexed predecessor is ignored
* @param registerNumber
* @return The register type resulting from merging the post-instruction register types from all predecessors
*/
protected RegisterType mergePreRegisterTypeFromPredecessors(int registerNumber) {
RegisterType mergedRegisterType = null;
for (AnalyzedInstruction predecessor: predecessors) {
RegisterType predecessorRegisterType = predecessor.postRegisterMap[registerNumber];
@ -158,9 +199,30 @@ public class AnalyzedInstruction {
return mergedRegisterType;
}
/*
* Sets the "post-instruction" register type as indicated.
* @param registerNumber Which register to set
* @param registerType The "post-instruction" register type
* @returns true if the given register type is different than the existing post-instruction register type
*/
protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) {
assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
assert registerType != null;
RegisterType oldRegisterType = postRegisterMap[registerNumber];
if (oldRegisterType == registerType) {
return false;
}
postRegisterMap[registerNumber] = registerType;
return true;
}
protected boolean isInvokeInit() {
if (instruction == null ||
(instruction.opcode != Opcode.INVOKE_DIRECT && instruction.opcode != Opcode.INVOKE_DIRECT_RANGE)) {
(instruction.opcode != Opcode.INVOKE_DIRECT && instruction.opcode != Opcode.INVOKE_DIRECT_RANGE &&
instruction.opcode != Opcode.INVOKE_DIRECT_EMPTY)) {
return false;
}
@ -187,7 +249,6 @@ public class AnalyzedInstruction {
}
public boolean setsRegister(int registerNumber) {
//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
@ -207,14 +268,14 @@ public class AnalyzedInstruction {
if (registerNumber == destinationRegister) {
return true;
}
RegisterType preInstructionDestRegisterType = getMergedRegisterTypeFromPredecessors(registerNumber);
RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(registerNumber);
if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef &&
preInstructionDestRegisterType.category != RegisterType.Category.UninitThis) {
return false;
}
//check if the uninit ref has been copied to another register
if (getMergedRegisterTypeFromPredecessors(registerNumber) == preInstructionDestRegisterType) {
if (getPreInstructionRegisterType(registerNumber) == preInstructionDestRegisterType) {
return true;
}
return false;
@ -251,14 +312,17 @@ public class AnalyzedInstruction {
}
public RegisterType getPreInstructionRegisterType(int registerNumber) {
//if the specific register is not a destination register, then the stored post-instruction register type will
//be the same as the pre-instruction regsiter type, so we can use that.
//otherwise, we need to merge the predecessor's post-instruction register types
return preRegisterMap[registerNumber];
}
if (this.setsRegister(registerNumber)) {
return getMergedRegisterTypeFromPredecessors(registerNumber);
public int compareTo(AnalyzedInstruction analyzedInstruction) {
//TODO: out of curiosity, check the disassembly of this to see if it retrieves the value of analyzedInstruction.instructionIndex for every access. It should, because the field is final. What about if we set the field to non-final?
if (instructionIndex < analyzedInstruction.instructionIndex) {
return -1;
} else if (instructionIndex == analyzedInstruction.instructionIndex) {
return 0;
} else {
return postRegisterMap[registerNumber];
return 1;
}
}
}

View File

@ -17,6 +17,24 @@ public class ClassPath {
private final HashMap<String, ClassDef> classDefs;
protected ClassDef javaLangObjectClassDef; //Ljava/lang/Object;
private final static String[][] inlineMethods =
new String[][] {
{ "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod()V"},
{ "Ljava/lang/String;", "charAt(I)C"},
{ "Ljava/lang/String;", "compareTo(Ljava/lang/String;)I"},
{ "Ljava/lang/String;", "equals(Ljava/lang/Object;)Z"},
{ "Ljava/lang/String;", "length()I"},
{ "Ljava/lang/Math;", "abs(I)I"},
{ "Ljava/lang/Math;", "abs(J)J"},
{ "Ljava/lang/Math;", "abs(F)F"},
{ "Ljava/lang/Math;", "abs(D)D"},
{ "Ljava/lang/Math;", "min(I)I"},
{ "Ljava/lang/Math;", "max(II)I"},
{ "Ljava/lang/Math;", "sqrt(D)D"},
{ "Ljava/lang/Math;", "cos(D)D"},
{ "Ljava/lang/Math;", "sin(D)D"}
};
public static void InitializeClassPath(String bootClassPathDir, String[] bootClassPath, DexFile dexFile) {
if (theClassPath != null) {
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
@ -37,7 +55,9 @@ public class ClassPath {
}
}
loadDexFile(dexFile);
if (dexFile != null) {
loadDexFile(dexFile);
}
for (String primitiveType: new String[]{"Z", "B", "S", "C", "I", "J", "F", "D"}) {
ClassDef classDef = new PrimitiveClassDef(primitiveType);
@ -76,8 +96,6 @@ public class ClassPath {
if (classDefItem.getClassType().getTypeDescriptor().equals("Ljava/lang/Object;")) {
theClassPath.javaLangObjectClassDef = classDef;
}
/*classDef.dumpVtable();
classDef.dumpFields();*/
}
}
@ -87,15 +105,23 @@ public class ClassPath {
}
}
public static ClassDef getClassDef(String classType) {
public static ClassDef getClassDef(String classType) {
return getClassDef(classType, true);
}
public static ClassDef getClassDef(String classType, boolean createUnresolvedClassDef) {
ClassDef classDef = theClassPath.classDefs.get(classType);
if (classDef == null) {
//if it's an array class, try to create it
if (classType.charAt(0) == '[') {
return theClassPath.createArrayClassDef(classType);
} else {
//TODO: we should output a warning
return theClassPath.createUnresolvedClassDef(classType);
if (createUnresolvedClassDef) {
//TODO: we should output a warning
return theClassPath.createUnresolvedClassDef(classType);
} else {
return null;
}
}
}
return classDef;
@ -105,6 +131,10 @@ public class ClassPath {
return getClassDef(classType.getTypeDescriptor());
}
public static ClassDef getClassDef(TypeIdItem classType, boolean creatUnresolvedClassDef) {
return getClassDef(classType.getTypeDescriptor(), creatUnresolvedClassDef);
}
//256 [ characters
private static final String arrayPrefix = "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" +
"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" +
@ -149,14 +179,14 @@ public class ClassPath {
//TODO: do we want to handle primitive types here? I don't think so.. (if not, add assert)
if (!class1.isInterface && class2.isInterface) {
if (class2.isInterface) {
if (class1.implementsInterface(class2)) {
return class2;
}
return theClassPath.javaLangObjectClassDef;
}
if (!class2.isInterface && class1.isInterface) {
if (class1.isInterface) {
if (class2.implementsInterface(class1)) {
return class1;
}
@ -220,6 +250,15 @@ public class ClassPath {
return getArrayClassDefByElementClassAndDimension(theClassPath.javaLangObjectClassDef, dimensions);
}
public static String[] getInlineMethod(int inlineIndex) {
if (inlineIndex < 0 || inlineIndex >= inlineMethods.length) {
return null;
}
return inlineMethods[inlineIndex];
}
public static class ArrayClassDef extends ClassDef {
private final ClassDef elementClass;
private final int arrayDimensions;
@ -382,6 +421,18 @@ public class ClassPath {
public final static int PrimitiveClassDef = 1;
public final static int UnresolvedClassDef = 2;
/**
* The following fields are used only during the initial loading of classes, and are set to null afterwards
* TODO: free these
*/
//This is only the virtual methods that this class declares itself.
private String[] virtualMethods;
//this is a list of all the interfaces that the class implements directory, or any super interfaces of those
//interfaces. It is generated in such a way that it is ordered in the same way as dalvik's ClassObject.iftable,
private LinkedHashMap<String, ClassDef> interfaceTable;
/**
* This constructor is used for the ArrayClassDef, PrimitiveClassDef and UnresolvedClassDef subclasses
* @param classType the class type
@ -403,6 +454,9 @@ public class ClassPath {
instanceFields = superclass.instanceFields;
instanceFieldLookup = superclass.instanceFieldLookup;
classDepth = 1; //1 off from java.lang.Object
virtualMethods = null;
interfaceTable = null;
} else if (classFlavor == PrimitiveClassDef) {
//primitive type
assert classType.charAt(0) != '[' && classType.charAt(0) != 'L';
@ -416,6 +470,9 @@ public class ClassPath {
instanceFields = null;
instanceFieldLookup = null;
classDepth = 0; //TODO: maybe use -1 to indicate not applicable?
virtualMethods = null;
interfaceTable = null;
} else /*if (classFlavor == UnresolvedClassDef)*/ {
assert classType.charAt(0) == 'L';
this.classType = classType;
@ -429,6 +486,9 @@ public class ClassPath {
instanceFields = superclass.instanceFields;
instanceFieldLookup = superclass.instanceFieldLookup;
classDepth = 1; //1 off from java.lang.Object
virtualMethods = null;
interfaceTable = null;
}
}
@ -446,6 +506,9 @@ public class ClassPath {
implementedInterfaces = loadAllImplementedInterfaces(classDefItem);
//TODO: we can probably get away with only creating the interface table for interface types
interfaceTable = loadInterfaceTable(classDefItem);
virtualMethods = loadVirtualMethods(classDefItem);
vtable = loadVtable(classDefItem);
virtualMethodLookup = new HashMap<String, Integer>((int)Math.ceil(vtable.length / .7f), .75f);
for (int i=0; i<vtable.length; i++) {
@ -513,6 +576,17 @@ public class ClassPath {
return virtualMethodLookup.containsKey(method);
}
public String getInstanceField(int fieldOffset) {
return this.instanceFields.get(fieldOffset, null);
}
public String getVirtualMethod(int vtableIndex) {
if (vtableIndex < 0 || vtableIndex >= vtable.length) {
return null;
}
return this.vtable[vtableIndex];
}
private void swap(byte[] fieldTypes, String[] fields, int position1, int position2) {
byte tempType = fieldTypes[position1];
fieldTypes[position1] = fieldTypes[position2];
@ -586,6 +660,56 @@ public class ClassPath {
return implementedInterfaceSet;
}
private LinkedHashMap<String, ClassDef> loadInterfaceTable(ClassDefItem classDefItem) {
TypeListItem typeListItem = classDefItem.getInterfaces();
if (typeListItem == null) {
return null;
}
LinkedHashMap<String, ClassDef> interfaceTable = new LinkedHashMap<String, ClassDef>();
for (TypeIdItem interfaceType: typeListItem.getTypes()) {
if (!interfaceTable.containsKey(interfaceType.getTypeDescriptor())) {
ClassDef classDef = getClassDef(interfaceType);
if (classDef == null) {
throw new ValidationException(String.format(
"Could not resolve type %s", interfaceType.getTypeDescriptor()));
}
interfaceTable.put(interfaceType.getTypeDescriptor(), classDef);
if (classDef.interfaceTable != null) {
for (ClassDef superInterface: classDef.interfaceTable.values()) {
if (!interfaceTable.containsKey(superInterface.classType)) {
interfaceTable.put(superInterface.classType, superInterface);
}
}
}
}
}
return interfaceTable;
}
private String[] loadVirtualMethods(ClassDefItem classDefItem) {
ClassDataItem classDataItem = classDefItem.getClassData();
if (classDataItem == null) {
return null;
}
EncodedMethod[] virtualEncodedMethods = classDataItem.getVirtualMethods();
if (virtualEncodedMethods == null) {
return null;
}
String[] virtualMethods = new String[virtualEncodedMethods.length];
for (int i=0; i<virtualEncodedMethods.length; i++) {
virtualMethods[i] = virtualEncodedMethods[i].method.getVirtualMethodString();
}
return virtualMethods;
}
private String[] loadVtable(ClassDefItem classDefItem) {
//TODO: it might be useful to keep track of which class's implementation is used for each virtual method. In other words, associate the implementing class type with each vtable entry
List<String> virtualMethodList = new LinkedList<String>();
@ -607,14 +731,32 @@ public class ClassPath {
//iterate over the virtual methods in the current class, and only add them when we don't already have the
//method (i.e. if it was implemented by the superclass)
ClassDataItem classDataItem = classDefItem.getClassData();
if (classDataItem != null) {
EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods();
if (virtualMethods != null) {
for (EncodedMethod virtualMethod: virtualMethods) {
String methodString = virtualMethod.method.getVirtualMethodString();
if (tempVirtualMethodLookup.get(methodString) == null) {
virtualMethodList.add(methodString);
if (!this.isInterface) {
ClassDataItem classDataItem = classDefItem.getClassData();
if (classDataItem != null) {
EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods();
if (virtualMethods != null) {
for (EncodedMethod virtualMethod: virtualMethods) {
String methodString = virtualMethod.method.getVirtualMethodString();
if (tempVirtualMethodLookup.get(methodString) == null) {
virtualMethodList.add(methodString);
tempVirtualMethodLookup.put(methodString, methodIndex++);
}
}
}
}
if (interfaceTable != null) {
for (ClassDef interfaceDef: interfaceTable.values()) {
if (interfaceDef.virtualMethods == null) {
continue;
}
for (String virtualMethod: interfaceDef.virtualMethods) {
if (tempVirtualMethodLookup.get(virtualMethod) == null) {
virtualMethodList.add(virtualMethod);
tempVirtualMethodLookup.put(virtualMethod, methodIndex++);
}
}
}
}
@ -628,9 +770,30 @@ public class ClassPath {
return vtable;
}
private int getNextFieldOffset() {
if (instanceFields == null || instanceFields.size() == 0) {
return 8;
}
int lastItemIndex = instanceFields.size()-1;
int fieldOffset = instanceFields.keyAt(lastItemIndex);
String lastField = instanceFields.valueAt(lastItemIndex);
int fieldTypeIndex = lastField.indexOf(":") + 1;
switch (lastField.charAt(fieldTypeIndex)) {
case 'J':
case 'D':
return fieldOffset + 8;
default:
return fieldOffset + 4;
}
}
private SparseArray<String> loadFields(ClassDefItem classDefItem) {
//This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to
//arrange fields, so that we end up with the same field offsets (which is needed for deodexing).
//See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
final byte REFERENCE = 0;
final byte WIDE = 1;
@ -684,11 +847,24 @@ public class ClassPath {
}
}
int startFieldOffset = 8;
if (this.superclass != null) {
startFieldOffset = this.superclass.getNextFieldOffset();
}
int fieldIndexMod;
if ((startFieldOffset % 8) == 0) {
fieldIndexMod = 0;
} else {
fieldIndexMod = 1;
}
//next, we need to group all the wide fields after the reference fields. But the wide fields have to be
//8-byte aligned. If we're on an odd field index, we need to insert a 32-bit field. If the next field
//is already a 32-bit field, use that. Otherwise, find the first 32-bit field from the end and swap it in.
//If there are no 32-bit fields, do nothing for now. We'll add padding when calculating the field offsets
if (front < fields.length && (front % 2) != 0) {
if (front < fields.length && (front % 2) != fieldIndexMod) {
if (fieldTypes[front] == WIDE) {
//we need to swap in a 32-bit field, so the wide fields will be correctly aligned
back = fields.length - 1;
@ -725,7 +901,7 @@ public class ClassPath {
int superFieldCount = 0;
if (superclass != null) {
superclass.instanceFields.size();
superFieldCount = superclass.instanceFields.size();
}
//now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets
@ -743,7 +919,7 @@ public class ClassPath {
String lastSuperField = superclass.instanceFields.valueAt(superFieldCount-1);
assert lastSuperField.indexOf(':') >= 0;
assert lastSuperField.indexOf(':') < superFieldCount-1; //the ':' shouldn't be the last char
assert lastSuperField.indexOf(':') < lastSuperField.length()-1; //the ':' shouldn't be the last char
char fieldType = lastSuperField.charAt(lastSuperField.indexOf(':') + 1);
if (fieldType == 'J' || fieldType == 'D') {
fieldOffset += 8;
@ -819,4 +995,95 @@ public class ClassPath {
return classType.compareTo(classDef.classType);
}
}
public static void validateAgainstDeodexerant(String host, int port, int skipClasses) {
Deodexerant deodexerant = new Deodexerant(host, port);
int count = 0;
try {
String[] inlineMethods = deodexerant.getInlineMethods();
(new DeodexUtil(null)).checkInlineMethods(inlineMethods);
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex, "Error while checking inline methods");
}
try {
for (ClassDef classDef: theClassPath.classDefs.values()) {
if (count < skipClasses) {
count++;
continue;
}
if (classDef instanceof UnresolvedClassDef || classDef instanceof ArrayClassDef ||
classDef instanceof PrimitiveClassDef) {
continue;
}
String[] vtable = deodexerant.getVirtualMethods(classDef.classType);
if (vtable.length != classDef.vtable.length) {
throw new ValidationException(String.format("virtual table size mismatch for class %s",
classDef.classType));
}
for (int i=0; i<classDef.vtable.length; i++) {
if (!classDef.vtable[i].equals(vtable[i])) {
throw new ValidationException(String.format("virtual method mismatch for class %s at index %d",
classDef.classType, i));
}
}
String[] fields = deodexerant.getInstanceFields(classDef.classType);
if (fields.length != classDef.instanceFields.size()) {
throw new ValidationException(String.format("field count mismatch for class %s",
classDef.classType));
}
for (int i=0; i<classDef.instanceFields.size(); i++) {
String[] fieldValues = fields[i].split(" ");
if (fieldValues.length != 2) {
throw new ValidationException("Could not parse field");
}
int fieldOffset = Integer.parseInt(fieldValues[0]);
String field = classDef.instanceFields.get(fieldOffset);
if (field == null) {
throw new ValidationException("Could not find a field at the given offset");
}
if (!field.equals(fieldValues[1])) {
throw new ValidationException(String.format("field offset mismatch for class %s at index %d",
classDef.classType, i));
}
}
count++;
}
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex, String.format("Error while checking class #%d", count));
}
}
public static String join(String[] vals)
{
StringBuilder sb = new StringBuilder();
for (String val: vals) {
sb.append(val);
sb.append('\n');
}
return sb.toString();
}
public static String join(List<String> vals)
{
StringBuilder sb = new StringBuilder();
for (String val: vals) {
sb.append(val);
sb.append('\n');
}
return sb.toString();
}
}

View File

@ -0,0 +1,323 @@
package org.jf.dexlib.Code.Analysis;
import org.jf.dexlib.*;
import org.jf.dexlib.Util.ExceptionWithContext;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DeodexUtil {
public static final int Virtual = 0;
public static final int Direct = 1;
public static final int Static = 2;
private InlineMethod[] inlineMethods = new InlineMethod[] {
new InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"),
new InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"),
new InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"),
new InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"),
new InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"),
new InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"),
new InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"),
new InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"),
new InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"),
new InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"),
new InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"),
new InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"),
new InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"),
new InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D")
};
public final DexFile dexFile;
public DeodexUtil(DexFile dexFile) {
this.dexFile = dexFile;
}
public InlineMethod lookupInlineMethod(int inlineMethodIndex) {
if (inlineMethodIndex >= inlineMethods.length) {
throw new RuntimeException("Invalid inline method index " + inlineMethodIndex + ".");
}
return inlineMethods[inlineMethodIndex];
}
private TypeIdItem resolveTypeOrSupertype(ClassPath.ClassDef classDef) {
ClassPath.ClassDef originalClassDef = classDef;
do {
TypeIdItem typeItem = TypeIdItem.lookupTypeIdItem(dexFile, classDef.getClassType());
if (typeItem != null) {
return typeItem;
}
classDef = classDef.getSuperclass();
} while (classDef != null);
throw new ExceptionWithContext(String.format("Cannot find type %s in the dex file",
originalClassDef.getClassType()));
}
public FieldIdItem lookupField(ClassPath.ClassDef classDef, int fieldOffset) {
String field = classDef.getInstanceField(fieldOffset);
if (field == null) {
return null;
}
return parseAndResolveField(classDef, field);
}
private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)");
public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef classDef, int methodIndex) {
String method = classDef.getVirtualMethod(methodIndex);
Matcher m = shortMethodPattern.matcher(method);
if (!m.matches()) {
assert false;
throw new RuntimeException("Invalid method descriptor: " + method);
}
String methodName = m.group(1);
String methodParams = m.group(2);
String methodRet = m.group(3);
if (classDef.isInterface()) {
classDef = classDef.getSuperclass();
assert classDef != null;
}
return parseAndResolveMethod(classDef, methodName, methodParams, methodRet);
}
private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef classDef, String methodName, String methodParams,
String methodRet) {
StringIdItem methodNameItem = StringIdItem.lookupStringIdItem(dexFile, methodName);
if (methodNameItem == null) {
return null;
}
LinkedList<TypeIdItem> paramList = new LinkedList<TypeIdItem>();
for (int i=0; i<methodParams.length(); i++) {
TypeIdItem typeIdItem;
switch (methodParams.charAt(i)) {
case 'Z':
case 'B':
case 'S':
case 'C':
case 'I':
case 'J':
case 'F':
case 'D':
typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i,i+1));
break;
case 'L':
{
int end = methodParams.indexOf(';', i);
if (end == -1) {
throw new RuntimeException("invalid parameter in the method");
}
typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i, end+1));
i = end;
break;
}
case '[':
{
int end;
int typeStart = i+1;
while (typeStart < methodParams.length() && methodParams.charAt(typeStart) == '[') {
typeStart++;
}
switch (methodParams.charAt(typeStart)) {
case 'Z':
case 'B':
case 'S':
case 'C':
case 'I':
case 'J':
case 'F':
case 'D':
end = typeStart;
break;
case 'L':
end = methodParams.indexOf(';', typeStart);
if (end == -1) {
throw new RuntimeException("invalid parameter in the method");
}
break;
default:
throw new RuntimeException("invalid parameter in the method");
}
typeIdItem = TypeIdItem.lookupTypeIdItem(dexFile, methodParams.substring(i, end+1));
i = end;
break;
}
default:
throw new RuntimeException("invalid parameter in the method");
}
if (typeIdItem == null) {
return null;
}
paramList.add(typeIdItem);
}
TypeListItem paramListItem = null;
if (paramList.size() > 0) {
paramListItem = TypeListItem.lookupTypeListItem(dexFile, paramList);
if (paramListItem == null) {
return null;
}
}
TypeIdItem retType = TypeIdItem.lookupTypeIdItem(dexFile, methodRet);
if (retType == null) {
return null;
}
ProtoIdItem protoItem = ProtoIdItem.lookupProtoIdItem(dexFile, retType, paramListItem);
if (protoItem == null) {
return null;
}
ClassPath.ClassDef methodClassDef = classDef;
do {
TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, methodClassDef.getClassType());
if (classTypeItem != null) {
MethodIdItem methodIdItem = MethodIdItem.lookupMethodIdItem(dexFile, classTypeItem, protoItem, methodNameItem);
if (methodIdItem != null) {
return methodIdItem;
}
}
methodClassDef = methodClassDef.getSuperclass();
} while (methodClassDef != null);
return null;
}
private FieldIdItem parseAndResolveField(ClassPath.ClassDef classDef, String field) {
//expecting a string like someField:Lfield/type;
String[] parts = field.split(":");
if (parts.length != 2) {
throw new RuntimeException("Invalid field descriptor " + field);
}
String fieldName = parts[0];
String fieldType = parts[1];
StringIdItem fieldNameItem = StringIdItem.lookupStringIdItem(dexFile, fieldName);
if (fieldNameItem == null) {
return null;
}
TypeIdItem fieldTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldType);
if (fieldTypeItem == null) {
return null;
}
ClassPath.ClassDef fieldClass = classDef;
do {
TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldClass.getClassType());
if (classTypeItem == null) {
continue;
}
FieldIdItem fieldIdItem = FieldIdItem.lookupFieldIdItem(dexFile, classTypeItem, fieldTypeItem, fieldNameItem);
if (fieldIdItem != null) {
return fieldIdItem;
}
fieldClass = fieldClass.getSuperclass();
} while (fieldClass != null);
return null;
}
/**
* Compare the inline methods that we have against the given set of inline methods from deodexerant.
* We want to make sure that each inline method in inlineMethods matches the method we have at the same
* index. We may have more inline methods than we are given in inlineMethods - this shouldn't be a problem.
* Newer versions of dalvik add additional inline methods, but (so far) have changed any existing ones.
*
* If anything doesn't look right, we just throw an exception
* @param inlineMethods
*/
protected void checkInlineMethods(String[] inlineMethods) {
if (inlineMethods.length > this.inlineMethods.length) {
throw new ValidationException("Inline method count mismatch");
}
for (int i=0; i<inlineMethods.length; i++) {
String inlineMethod = inlineMethods[i];
int methodType;
if (inlineMethod.startsWith("static")) {
methodType = Static;
inlineMethod = inlineMethod.substring(7);
} else if (inlineMethod.startsWith("direct")) {
methodType = Direct;
inlineMethod = inlineMethod.substring(7);
} else if (inlineMethod.startsWith("virtual")) {
methodType = Virtual;
inlineMethod = inlineMethod.substring(8);
} else {
throw new ValidationException("Could not parse inline method");
}
if (!inlineMethod.equals(this.inlineMethods[i].getMethodString())) {
throw new ValidationException(String.format("Inline method mismatch. %s vs. %s", inlineMethod,
this.inlineMethods[i].getMethodString()));
}
if (methodType != this.inlineMethods[i].methodType) {
throw new ValidationException(String.format("Inline method type mismatch. %d vs. %d", methodType,
this.inlineMethods[i].methodType));
}
}
}
public class InlineMethod {
public final int methodType;
public final String classType;
public final String methodName;
public final String parameters;
public final String returnType;
private MethodIdItem methodIdItem = null;
protected InlineMethod(int methodType, String classType, String methodName, String parameters,
String returnType) {
this.methodType = methodType;
this.classType = classType;
this.methodName = methodName;
this.parameters = parameters;
this.returnType = returnType;
}
public MethodIdItem getMethodIdItem() {
if (methodIdItem == null) {
loadMethod();
}
return methodIdItem;
}
private void loadMethod() {
ClassPath.ClassDef classDef = ClassPath.getClassDef(classType);
this.methodIdItem = parseAndResolveMethod(classDef, methodName, parameters, returnType);
}
public String getMethodString() {
return String.format("%s->%s(%s)%s", classType, methodName, parameters, returnType);
}
}
}

View File

@ -0,0 +1,139 @@
/*
* [The "BSD licence"]
* Copyright (c) 2009 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib.Code.Analysis;
import java.net.Socket;
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;
import java.util.ArrayList;
/**
* This class handles communication with the deodexerant helper binary,
* as well as caching the results of any deodexerant lookups
*/
public class Deodexerant {
private final String host;
private final int port;
private Socket socket = null;
private PrintWriter out = null;
private BufferedReader in = null;
public Deodexerant(String host, int port) {
this.host = host;
this.port = port;
}
public String[] getInlineMethods() {
return sendMultilineCommand("I");
}
public String[] getVirtualMethods(String classType) {
return sendMultilineCommand(String.format("V %s", classType));
}
public String[] getInstanceFields(String classType) {
return sendMultilineCommand(String.format("F %s", classType));
}
private String sendCommand(String cmd) {
try {
connectIfNeeded();
out.println(cmd);
out.flush();
String response = in.readLine();
if (response.startsWith("err")) {
String error = response.substring(5);
throw new RuntimeException(error);
}
return response;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
//The command is still just a single line, but we're expecting a multi-line
//response. The repsonse is considered finished when a line starting with "err"
//or with "done" is encountered
private String[] sendMultilineCommand(String cmd) {
try {
connectIfNeeded();
out.println(cmd);
out.flush();
ArrayList<String> responseLines = new ArrayList<String>();
String response = in.readLine();
if (response == null) {
throw new RuntimeException("Error talking to deodexerant");
}
while (!response.startsWith("done"))
{
if (response.startsWith("err")) {
throw new RuntimeException(response.substring(5));
}
int pos = response.indexOf(':') + 1;
responseLines.add(response.substring(pos+1));
response = in.readLine();
}
String[] lines = new String[responseLines.size()];
for (int i=0; i<lines.length; i++) {
lines[i] = responseLines.get(i);
}
return lines;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private void connectIfNeeded() {
try {
if (socket != null) {
return;
}
socket = new Socket(host, port);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}

View File

@ -44,7 +44,6 @@ public enum Format {
Format22b(Instruction22b.Factory, 4),
Format22c(Instruction22c.Factory, 4),
Format22cs(Instruction22cs.Factory, 4),
Format22csf(null, 4),
Format22s(Instruction22s.Factory, 4),
Format22t(Instruction22t.Factory, 4),
Format22x(Instruction22x.Factory, 4),
@ -56,18 +55,14 @@ public enum Format {
Format32x(Instruction32x.Factory, 6),
Format35c(Instruction35c.Factory, 6),
Format35s(Instruction35s.Factory, 6),
Format35sf(null, 6),
Format35ms(Instruction35ms.Factory, 6),
Format35msf(null, 6),
Format3rc(Instruction3rc.Factory, 6),
Format3rms(Instruction3rms.Factory, 6),
Format3rmsf(null, 6),
Format51l(Instruction51l.Factory, 10),
ArrayData(null, -1, true),
PackedSwitchData(null, -1, true),
SparseSwitchData(null, -1, true),
UnresolvedNullReference(null, -1, false),
DeadInstruction(null, -1, false)
;
public final Instruction.InstructionFactory Factory;

View File

@ -29,13 +29,14 @@
package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.OdexedFieldAccess;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.DexFile;
public class Instruction22cs extends Instruction implements TwoRegisterInstruction {
public class Instruction22cs extends Instruction implements TwoRegisterInstruction, OdexedFieldAccess {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private byte regB;

View File

@ -1,66 +0,0 @@
/*
* [The "BSD licence"]
* Copyright (c) 2009 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.FieldIdItem;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction22csf extends InstructionWithReference implements TwoRegisterInstruction {
private final Instruction22cs unfixedInstruction;
public Instruction22csf(Opcode opcode, Instruction22cs unfixedInstruction, FieldIdItem field) {
//the opcode should be the "fixed" opcode. i.e. iget-object, etc. (NOT the "quick" version)
super(opcode, field);
this.unfixedInstruction = unfixedInstruction;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
byte regA = (byte)getRegisterA();
byte regB = (byte)getRegisterB();
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
out.writeShort(this.getReferencedItem().getIndex());
}
public Format getFormat() {
return Format.Format22csf;
}
public int getRegisterA() {
return unfixedInstruction.getRegisterA();
}
public int getRegisterB() {
return unfixedInstruction.getRegisterB();
}
}

View File

@ -30,13 +30,14 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.FiveRegisterInstruction;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.OdexedInvokeVirtual;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.DexFile;
public class Instruction35ms extends Instruction implements FiveRegisterInstruction {
public class Instruction35ms extends Instruction implements FiveRegisterInstruction, OdexedInvokeVirtual {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regCount;
private byte regA;

View File

@ -1,88 +0,0 @@
/*
* [The "BSD licence"]
* Copyright (c) 2009 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.FiveRegisterInstruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction35msf extends InstructionWithReference implements FiveRegisterInstruction {
private final Instruction35ms unfixedInstruction;
public Instruction35msf(Opcode opcode, Instruction35ms unfixedInstruction, MethodIdItem method) {
//the opcode should be the "fixed" opcode. i.e. iget-object, etc. (NOT the "quick" version)
super(opcode, method);
this.unfixedInstruction = unfixedInstruction;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
byte regA = getRegisterA();
byte regCount = getRegCount();
byte regD = getRegisterD();
byte regE = getRegisterE();
byte regF = getRegisterF();
byte regG = getRegisterG();
out.writeByte(opcode.value);
out.writeByte((regCount << 4) | regA);
out.writeShort(this.getReferencedItem().getIndex());
out.writeByte((regE << 4) | regD);
out.writeByte((regG << 4) | regF);
}
public Format getFormat() {
return Format.Format35msf;
}
public byte getRegCount() {
return unfixedInstruction.getRegCount();
}
public byte getRegisterA() {
return unfixedInstruction.getRegisterA();
}
public byte getRegisterD() {
return unfixedInstruction.getRegisterD();
}
public byte getRegisterE() {
return unfixedInstruction.getRegisterE();
}
public byte getRegisterF() {
return unfixedInstruction.getRegisterF();
}
public byte getRegisterG() {
return unfixedInstruction.getRegisterG();
}
}

View File

@ -1,86 +0,0 @@
/*
* [The "BSD licence"]
* Copyright (c) 2009 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.FiveRegisterInstruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction35sf extends InstructionWithReference implements FiveRegisterInstruction {
private final Instruction35s unfixedInstruction;
public Instruction35sf(Instruction35s unfixedInstruction) {
super(Opcode.INVOKE_DIRECT, unfixedInstruction.getReferencedItem());
this.unfixedInstruction = unfixedInstruction;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
byte regA = getRegisterA();
byte regCount = getRegCount();
byte regD = getRegisterD();
byte regE = getRegisterE();
byte regF = getRegisterF();
byte regG = getRegisterG();
out.writeByte(opcode.value);
out.writeByte((regCount << 4) | regA);
out.writeShort(getReferencedItem().getIndex());
out.writeByte((regE << 4) | regD);
out.writeByte((regG << 4) | regF);
}
public Format getFormat() {
return Format.Format35sf;
}
public byte getRegCount() {
return unfixedInstruction.getRegCount();
}
public byte getRegisterA() {
return unfixedInstruction.getRegisterA();
}
public byte getRegisterD() {
return unfixedInstruction.getRegisterD();
}
public byte getRegisterE() {
return unfixedInstruction.getRegisterE();
}
public byte getRegisterF() {
return unfixedInstruction.getRegisterF();
}
public byte getRegisterG() {
return unfixedInstruction.getRegisterG();
}
}

View File

@ -29,13 +29,14 @@
package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.OdexedInvokeVirtual;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.RegisterRangeInstruction;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.DexFile;
public class Instruction3rms extends Instruction implements RegisterRangeInstruction {
public class Instruction3rms extends Instruction implements RegisterRangeInstruction, OdexedInvokeVirtual {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regCount;
private short startReg;

View File

@ -1,67 +0,0 @@
/*
* [The "BSD licence"]
* Copyright (c) 2009 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.Code.RegisterRangeInstruction;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction3rmsf extends InstructionWithReference implements RegisterRangeInstruction {
private final Instruction3rms unfixedInstruction;
public Instruction3rmsf(Opcode opcode, Instruction3rms unfixedInstruction, MethodIdItem method) {
//the opcode should be the "fixed" opcode. i.e. iget-object, etc. (NOT the "quick" version)
super(opcode, method);
this.unfixedInstruction = unfixedInstruction;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
short regCount = getRegCount();
int startReg = getStartRegister();
out.writeByte(opcode.value);
out.writeByte(regCount);
out.writeShort(this.getReferencedItem().getIndex());
out.writeShort(startReg);
}
public Format getFormat() {
return Format.Format3rmsf;
}
public int getStartRegister() {
return unfixedInstruction.getStartRegister();
}
public short getRegCount() {
return unfixedInstruction.getRegCount();
}
}

View File

@ -0,0 +1,5 @@
package org.jf.dexlib.Code;
public interface OdexedFieldAccess {
int getFieldOffset();
}

View File

@ -0,0 +1,5 @@
package org.jf.dexlib.Code;
public interface OdexedInvokeVirtual {
int getMethodIndex();
}

View File

@ -253,13 +253,12 @@ public enum Opcode
SHR_INT_LIT8((byte)0xe1, "shr-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
USHR_INT_LIT8((byte)0xe2, "ushr-int/lit8", ReferenceType.none, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
EXECUTE_INLINE((byte)0xee, "execute-inline", ReferenceType.none, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
EXECUTE_INLINE_RANGE((byte)0xef, "execute-inline/range", ReferenceType.none, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
EXECUTE_INLINE((byte)0xee, "execute-inline", ReferenceType.none, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
EXECUTE_INLINE_RANGE((byte)0xef, "execute-inline/range", ReferenceType.none, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
INVOKE_DIRECT_EMPTY((byte)0xf0, "invoke-direct-empty", ReferenceType.method, Format.Format35s, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
IGET_QUICK((byte)0xf2, "iget-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
IGET_WIDE_QUICK((byte)0xf3, "iget-wide-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_WIDE_REGISTER),
IGET_OBJECT_QUICK((byte)0xf4, "iget-object-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
IGET_QUICK((byte)0xf2, "iget-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
IGET_WIDE_QUICK((byte)0xf3, "iget-wide-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
IGET_OBJECT_QUICK((byte)0xf4, "iget-object-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
IPUT_QUICK((byte)0xf5, "iput-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
IPUT_WIDE_QUICK((byte)0xf6, "iput-wide-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
IPUT_OBJECT_QUICK((byte)0xf7, "iput-object-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),