mirror of
https://github.com/revanced/Apktool.git
synced 2024-09-05 05:55:29 +02:00
fixes #396, update to smali v1.4.2
This commit is contained in:
parent
881bb56b4d
commit
626d7e7ecd
2
CHANGES
2
CHANGES
@ -1,4 +1,6 @@
|
||||
v1.5.3 (TBA)
|
||||
-Updated to smali/baksmali to v1.4.2
|
||||
-Fixed (issue #396) - Correctly handle android:debuggable while in debug mode.
|
||||
|
||||
v1.5.2 (Released February 2 - 2013) Codename: Bug Fixes
|
||||
-Fixed (issue #299) - output smali filename errors to screen during rebuild instead of filestream
|
||||
|
@ -44,8 +44,6 @@ import static org.jf.dexlib.ClassDataItem.EncodedField;
|
||||
import static org.jf.dexlib.ClassDataItem.EncodedMethod;
|
||||
|
||||
public class ClassPath {
|
||||
public static boolean dontLoadClassPath = false;
|
||||
|
||||
private static ClassPath theClassPath = null;
|
||||
|
||||
/**
|
||||
@ -263,10 +261,6 @@ public class ClassPath {
|
||||
|
||||
@Nonnull
|
||||
public static ClassDef getClassDef(String classType, boolean createUnresolvedClassDef) {
|
||||
if (dontLoadClassPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassDef classDef = theClassPath.classDefs.get(classType);
|
||||
if (classDef == null) {
|
||||
//if it's an array class, try to create it
|
||||
@ -543,7 +537,7 @@ public class ClassPath {
|
||||
}
|
||||
|
||||
public ClassDef getSuperclass() {
|
||||
throw unresolvedValidationException();
|
||||
return theClassPath.javaLangObjectClassDef;
|
||||
}
|
||||
|
||||
public int getClassDepth() {
|
||||
@ -599,6 +593,10 @@ public class ClassPath {
|
||||
|
||||
private final int classDepth;
|
||||
|
||||
// classes can only be public or package-private. Internally, any private/protected inner class is actually
|
||||
// package-private.
|
||||
private final boolean isPublic;
|
||||
|
||||
private final VirtualMethod[] vtable;
|
||||
|
||||
//this maps a method name of the form method(III)Ljava/lang/String; to an integer
|
||||
@ -641,6 +639,7 @@ public class ClassPath {
|
||||
implementedInterfaces.add(ClassPath.getClassDef("Ljava/lang/Cloneable;"));
|
||||
implementedInterfaces.add(ClassPath.getClassDef("Ljava/io/Serializable;"));
|
||||
isInterface = false;
|
||||
isPublic = true;
|
||||
|
||||
vtable = superclass.vtable;
|
||||
methodLookup = superclass.methodLookup;
|
||||
@ -658,6 +657,7 @@ public class ClassPath {
|
||||
this.superclass = null;
|
||||
implementedInterfaces = null;
|
||||
isInterface = false;
|
||||
isPublic = true;
|
||||
vtable = null;
|
||||
methodLookup = null;
|
||||
instanceFields = null;
|
||||
@ -671,6 +671,7 @@ public class ClassPath {
|
||||
this.superclass = ClassPath.getClassDef("Ljava/lang/Object;");
|
||||
implementedInterfaces = new TreeSet<ClassDef>();
|
||||
isInterface = false;
|
||||
isPublic = true;
|
||||
|
||||
vtable = superclass.vtable;
|
||||
methodLookup = superclass.methodLookup;
|
||||
@ -685,6 +686,7 @@ public class ClassPath {
|
||||
|
||||
protected ClassDef(UnresolvedClassInfo classInfo) {
|
||||
classType = classInfo.classType;
|
||||
isPublic = classInfo.isPublic;
|
||||
isInterface = classInfo.isInterface;
|
||||
|
||||
superclass = loadSuperclass(classInfo);
|
||||
@ -738,6 +740,10 @@ public class ClassPath {
|
||||
return this.isInterface;
|
||||
}
|
||||
|
||||
public boolean isPublic() {
|
||||
return this.isPublic;
|
||||
}
|
||||
|
||||
public boolean extendsClass(ClassDef superclassDef) {
|
||||
if (superclassDef == null) {
|
||||
return false;
|
||||
@ -1226,6 +1232,7 @@ public class ClassPath {
|
||||
private static class UnresolvedClassInfo {
|
||||
public final String dexFilePath;
|
||||
public final String classType;
|
||||
public final boolean isPublic;
|
||||
public final boolean isInterface;
|
||||
public final String superclassType;
|
||||
public final String[] interfaces;
|
||||
@ -1239,6 +1246,7 @@ public class ClassPath {
|
||||
|
||||
classType = classDefItem.getClassType().getTypeDescriptor();
|
||||
|
||||
isPublic = (classDefItem.getAccessFlags() & AccessFlags.PUBLIC.getValue()) != 0;
|
||||
isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
|
||||
|
||||
TypeIdItem superclassType = classDefItem.getSuperclass();
|
||||
|
@ -64,19 +64,21 @@ public class DeodexUtil {
|
||||
return inlineMethodResolver.resolveExecuteInline(instruction);
|
||||
}
|
||||
|
||||
public FieldIdItem lookupField(ClassPath.ClassDef classDef, int fieldOffset) {
|
||||
ClassPath.FieldDef field = classDef.getInstanceField(fieldOffset);
|
||||
public FieldIdItem lookupField(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
|
||||
int fieldOffset) {
|
||||
ClassPath.FieldDef field = instanceClass.getInstanceField(fieldOffset);
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parseAndResolveField(classDef, field);
|
||||
return parseAndResolveField(accessingClass, instanceClass, field);
|
||||
}
|
||||
|
||||
private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)");
|
||||
|
||||
public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef classDef, int methodIndex) {
|
||||
String method = classDef.getVirtualMethod(methodIndex);
|
||||
public MethodIdItem lookupVirtualMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
|
||||
int methodIndex) {
|
||||
String method = instanceClass.getVirtualMethod(methodIndex);
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
@ -91,20 +93,20 @@ public class DeodexUtil {
|
||||
String methodParams = m.group(2);
|
||||
String methodRet = m.group(3);
|
||||
|
||||
if (classDef instanceof ClassPath.UnresolvedClassDef) {
|
||||
if (instanceClass instanceof ClassPath.UnresolvedClassDef) {
|
||||
//if this is an unresolved class, the only way getVirtualMethod could have found a method is if the virtual
|
||||
//method being looked up was a method on java.lang.Object.
|
||||
classDef = ClassPath.getClassDef("Ljava/lang/Object;");
|
||||
} else if (classDef.isInterface()) {
|
||||
classDef = classDef.getSuperclass();
|
||||
assert classDef != null;
|
||||
instanceClass = ClassPath.getClassDef("Ljava/lang/Object;");
|
||||
} else if (instanceClass.isInterface()) {
|
||||
instanceClass = instanceClass.getSuperclass();
|
||||
assert instanceClass != null;
|
||||
}
|
||||
|
||||
return parseAndResolveMethod(classDef, methodName, methodParams, methodRet);
|
||||
return parseAndResolveMethod(accessingClass, instanceClass, methodName, methodParams, methodRet);
|
||||
}
|
||||
|
||||
private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef classDef, String methodName, String methodParams,
|
||||
String methodRet) {
|
||||
private MethodIdItem parseAndResolveMethod(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass,
|
||||
String methodName, String methodParams, String methodRet) {
|
||||
StringIdItem methodNameItem = StringIdItem.lookupStringIdItem(dexFile, methodName);
|
||||
if (methodNameItem == null) {
|
||||
return null;
|
||||
@ -197,14 +199,14 @@ public class DeodexUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassPath.ClassDef methodClassDef = classDef;
|
||||
ClassPath.ClassDef methodClassDef = definingClass;
|
||||
|
||||
do {
|
||||
TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, methodClassDef.getClassType());
|
||||
|
||||
if (classTypeItem != null) {
|
||||
MethodIdItem methodIdItem = MethodIdItem.lookupMethodIdItem(dexFile, classTypeItem, protoItem, methodNameItem);
|
||||
if (methodIdItem != null) {
|
||||
if (methodIdItem != null && checkClassAccess(accessingClass, methodClassDef)) {
|
||||
return methodIdItem;
|
||||
}
|
||||
}
|
||||
@ -214,7 +216,28 @@ public class DeodexUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
private FieldIdItem parseAndResolveField(ClassPath.ClassDef classDef, ClassPath.FieldDef field) {
|
||||
private static boolean checkClassAccess(ClassPath.ClassDef accessingClass, ClassPath.ClassDef definingClass) {
|
||||
return definingClass.isPublic() ||
|
||||
getPackage(accessingClass.getClassType()).equals(getPackage(definingClass.getClassType()));
|
||||
}
|
||||
|
||||
private static String getPackage(String classRef) {
|
||||
int lastSlash = classRef.lastIndexOf('/');
|
||||
if (lastSlash < 0) {
|
||||
return "";
|
||||
}
|
||||
return classRef.substring(1, lastSlash);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param accessingClass The class that contains the field reference. I.e. the class being deodexed
|
||||
* @param instanceClass The inferred class type of the object that the field is being accessed on
|
||||
* @param field The field being accessed
|
||||
* @return The FieldIdItem of the resolved field
|
||||
*/
|
||||
private FieldIdItem parseAndResolveField(ClassPath.ClassDef accessingClass, ClassPath.ClassDef instanceClass,
|
||||
ClassPath.FieldDef field) {
|
||||
String definingClass = field.definingClass;
|
||||
String fieldName = field.name;
|
||||
String fieldType = field.type;
|
||||
@ -229,7 +252,7 @@ public class DeodexUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassPath.ClassDef fieldClass = classDef;
|
||||
ClassPath.ClassDef fieldClass = instanceClass;
|
||||
|
||||
ArrayList<ClassPath.ClassDef> parents = new ArrayList<ClassPath.ClassDef>();
|
||||
parents.add(fieldClass);
|
||||
@ -248,7 +271,7 @@ public class DeodexUtil {
|
||||
}
|
||||
|
||||
FieldIdItem fieldIdItem = FieldIdItem.lookupFieldIdItem(dexFile, classTypeItem, fieldTypeItem, fieldNameItem);
|
||||
if (fieldIdItem != null) {
|
||||
if (fieldIdItem != null && checkClassAccess(accessingClass, fieldClass)) {
|
||||
return fieldIdItem;
|
||||
}
|
||||
}
|
||||
@ -283,7 +306,8 @@ public class DeodexUtil {
|
||||
private void loadMethod(DeodexUtil deodexUtil) {
|
||||
ClassPath.ClassDef classDef = ClassPath.getClassDef(classType);
|
||||
|
||||
this.methodIdItem = deodexUtil.parseAndResolveMethod(classDef, methodName, parameters, returnType);
|
||||
this.methodIdItem = deodexUtil.parseAndResolveMethod(classDef, classDef, methodName, parameters,
|
||||
returnType);
|
||||
}
|
||||
|
||||
public String getMethodString() {
|
||||
|
@ -3578,7 +3578,14 @@ public class MethodAnalyzer {
|
||||
return false;
|
||||
}
|
||||
|
||||
FieldIdItem fieldIdItem = deodexUtil.lookupField(objectRegisterType.type, fieldOffset);
|
||||
ClassPath.ClassDef accessingClass =
|
||||
ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
|
||||
if (accessingClass == null) {
|
||||
throw new ExceptionWithContext(String.format("Could not find ClassDef for current class: %s",
|
||||
this.encodedMethod.method.getContainingClass()));
|
||||
}
|
||||
|
||||
FieldIdItem fieldIdItem = deodexUtil.lookupField(accessingClass, objectRegisterType.type, fieldOffset);
|
||||
if (fieldIdItem == null) {
|
||||
throw new ValidationException(String.format("Could not resolve the field in class %s at offset %d",
|
||||
objectRegisterType.type.getClassType(), fieldOffset));
|
||||
@ -3621,12 +3628,16 @@ public class MethodAnalyzer {
|
||||
}
|
||||
|
||||
MethodIdItem methodIdItem = null;
|
||||
ClassPath.ClassDef accessingClass =
|
||||
ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
|
||||
if (accessingClass == null) {
|
||||
throw new ExceptionWithContext(String.format("Could not find ClassDef for current class: %s",
|
||||
this.encodedMethod.method.getContainingClass()));
|
||||
}
|
||||
if (isSuper) {
|
||||
ClassPath.ClassDef classDef = ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
|
||||
assert classDef != null;
|
||||
|
||||
if (classDef.getSuperclass() != null) {
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(classDef.getSuperclass(), methodIndex);
|
||||
if (accessingClass.getSuperclass() != null) {
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, accessingClass.getSuperclass(),
|
||||
methodIndex);
|
||||
}
|
||||
|
||||
if (methodIdItem == null) {
|
||||
@ -3634,10 +3645,10 @@ public class MethodAnalyzer {
|
||||
//of from the superclass (although the superclass method is still what would actually be called).
|
||||
//And so the MethodIdItem for the superclass method may not be in the dex file. Let's try to get the
|
||||
//MethodIdItem for the method in the current class instead
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(classDef, methodIndex);
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, accessingClass, methodIndex);
|
||||
}
|
||||
} else{
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(objectRegisterType.type, methodIndex);
|
||||
methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, objectRegisterType.type, methodIndex);
|
||||
}
|
||||
|
||||
if (methodIdItem == null) {
|
||||
|
@ -40,6 +40,9 @@ public class ByteArrayInput
|
||||
/** >= 0; current read cursor */
|
||||
private int cursor;
|
||||
|
||||
/* buffer for reading UTF-8 strings */
|
||||
private char[] buffer = null;
|
||||
|
||||
/**
|
||||
* Constructs an instance with the given data
|
||||
*
|
||||
@ -291,7 +294,11 @@ public class ByteArrayInput
|
||||
//skip the terminating null
|
||||
cursor++;
|
||||
|
||||
return Utf8Utils.utf8BytesToString(data, startPosition, byteCount);
|
||||
if (buffer == null || buffer.length < byteCount) {
|
||||
buffer = new char[byteCount];
|
||||
}
|
||||
|
||||
return Utf8Utils.utf8BytesToString(buffer, data, startPosition, byteCount);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
@ -68,23 +68,16 @@ public final class Utf8Utils {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static char[] tempBuffer = null;
|
||||
|
||||
/**
|
||||
* Converts an array of UTF-8 bytes into a string.
|
||||
*
|
||||
* This method uses a global buffer to avoid having to allocate one every time, so it is *not* thread-safe
|
||||
*
|
||||
* @param buffer a buffer to hold the chars as they are read. Make sure the length of the array is at least 'length'
|
||||
* @param bytes non-null; the bytes to convert
|
||||
* @param start the start index of the utf8 string to convert
|
||||
* @param length the length of the utf8 string to convert, not including any null-terminator that might be present
|
||||
* @return non-null; the converted string
|
||||
*/
|
||||
public static String utf8BytesToString(byte[] bytes, int start, int length) {
|
||||
if (tempBuffer == null || tempBuffer.length < length) {
|
||||
tempBuffer = new char[length];
|
||||
}
|
||||
char[] chars = tempBuffer;
|
||||
public static String utf8BytesToString(char[] buffer, byte[] bytes, int start, int length) {
|
||||
int outAt = 0;
|
||||
|
||||
for (int at = start; length > 0; /*at*/) {
|
||||
@ -157,11 +150,11 @@ public final class Utf8Utils {
|
||||
return throwBadUtf8(v0, at);
|
||||
}
|
||||
}
|
||||
chars[outAt] = out;
|
||||
buffer[outAt] = out;
|
||||
outAt++;
|
||||
}
|
||||
|
||||
return new String(chars, 0, outAt);
|
||||
return new String(buffer, 0, outAt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,6 +36,7 @@ options {
|
||||
@header {
|
||||
package org.jf.smali;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.regex.*;
|
||||
@ -1616,23 +1617,26 @@ annotation_element returns[StringIdItem elementName, EncodedValue elementValue]
|
||||
};
|
||||
|
||||
subannotation returns[TypeIdItem annotationType, StringIdItem[\] elementNames, EncodedValue[\] elementValues]
|
||||
: {ArrayList<StringIdItem> elementNamesList = new ArrayList<StringIdItem>();
|
||||
ArrayList<EncodedValue> elementValuesList = new ArrayList<EncodedValue>();}
|
||||
: {ImmutableSortedMap.Builder<StringIdItem, EncodedValue> elementBuilder =
|
||||
ImmutableSortedMap.<StringIdItem, EncodedValue>naturalOrder();}
|
||||
^(I_SUBANNOTATION
|
||||
class_type_descriptor
|
||||
(annotation_element
|
||||
{
|
||||
elementNamesList.add($annotation_element.elementName);
|
||||
elementValuesList.add($annotation_element.elementValue);
|
||||
elementBuilder.put($annotation_element.elementName, $annotation_element.elementValue);
|
||||
}
|
||||
)*
|
||||
)
|
||||
{
|
||||
ImmutableSortedMap<StringIdItem, EncodedValue> elementMap = elementBuilder.build();
|
||||
|
||||
$annotationType = $class_type_descriptor.type;
|
||||
$elementNames = new StringIdItem[elementNamesList.size()];
|
||||
elementNamesList.toArray($elementNames);
|
||||
$elementValues = new EncodedValue[elementValuesList.size()];
|
||||
elementValuesList.toArray($elementValues);
|
||||
|
||||
$elementNames = new StringIdItem[elementMap.size()];
|
||||
$elementValues = new EncodedValue[elementMap.size()];
|
||||
|
||||
elementMap.keySet().toArray($elementNames);
|
||||
elementMap.values().toArray($elementValues);
|
||||
};
|
||||
|
||||
field_literal returns[FieldIdItem value]
|
||||
|
@ -228,9 +228,6 @@ public class Androlib {
|
||||
if (!working.exists()) {
|
||||
return false;
|
||||
}
|
||||
if (flags.get("debug")) {
|
||||
LOGGER.warning("Debug mode not available.");
|
||||
}
|
||||
File stored = new File(appDir, APK_DIRNAME + "/classes.dex");
|
||||
if (flags.get("forceBuildAll") || isModified(working, stored)) {
|
||||
LOGGER.info("Copying classes.dex file...");
|
||||
@ -379,7 +376,13 @@ public class Androlib {
|
||||
if (!flags.get("forceBuildAll")) {
|
||||
LOGGER.info("Checking whether resources has changed...");
|
||||
}
|
||||
|
||||
File apkDir = new File(appDir, APK_DIRNAME);
|
||||
|
||||
if (flags.get("debug")) {
|
||||
mAndRes.remove_application_debug(new File(apkDir,"AndroidManifest.xml").getAbsolutePath());
|
||||
}
|
||||
|
||||
if (flags.get("forceBuildAll")
|
||||
|| isModified(newFiles(APK_MANIFEST_FILENAMES, appDir),
|
||||
newFiles(APK_MANIFEST_FILENAMES, apkDir))) {
|
||||
|
@ -38,6 +38,7 @@ import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
@ -46,6 +47,7 @@ import javax.xml.transform.stream.StreamResult;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.xml.sax.SAXException;
|
||||
@ -151,6 +153,48 @@ final public class AndrolibResources {
|
||||
}
|
||||
}
|
||||
|
||||
public void remove_application_debug(String filePath)
|
||||
throws AndrolibException {
|
||||
|
||||
// change application:debug to true
|
||||
try {
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory
|
||||
.newInstance();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
Document doc = docBuilder.parse(filePath.toString());
|
||||
|
||||
Node application = doc.getElementById("application");
|
||||
|
||||
// load attr
|
||||
NamedNodeMap attr = application.getAttributes();
|
||||
Node debugAttr = attr.getNamedItem("debug");
|
||||
|
||||
// remove application:debug
|
||||
if (debugAttr != null) {
|
||||
attr.removeNamedItem("debug");
|
||||
}
|
||||
|
||||
// save manifest
|
||||
TransformerFactory transformerFactory = TransformerFactory
|
||||
.newInstance();
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
DOMSource source = new DOMSource(doc);
|
||||
StreamResult result = new StreamResult(new File(filePath));
|
||||
transformer.transform(source, result);
|
||||
|
||||
} catch (ParserConfigurationException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
} catch (SAXException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
} catch (IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
} catch (TransformerConfigurationException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
} catch (TransformerException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void adjust_package_manifest(ResTable resTable, String filePath)
|
||||
throws AndrolibException {
|
||||
|
||||
@ -182,7 +226,6 @@ final public class AndrolibResources {
|
||||
nodeAttr.setNodeValue(packageInfo.get("cur_package"));
|
||||
|
||||
// re-save manifest.
|
||||
// fancy an auto-sort :p
|
||||
TransformerFactory transformerFactory = TransformerFactory
|
||||
.newInstance();
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
|
Loading…
Reference in New Issue
Block a user