Fixing the mess-ups that was apktool v1.4.8 :(

This commit is contained in:
Connor Tumbleson 2012-07-08 17:46:02 -05:00
parent b5a476bb1b
commit 97451619b8
6 changed files with 169 additions and 187 deletions

View File

@ -149,10 +149,9 @@ final public class AndrolibResources {
inApk = apkFile.getDirectory(); inApk = apkFile.getDirectory();
out = new FileDirectory(outDir); out = new FileDirectory(outDir);
LOGGER.info("Decoding AndroidManifest.xml with resources..."); fileDecoder.decode(
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml",
fileDecoder.decodeManifest( "xml");
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml");
if (inApk.containsDir("res")) { if (inApk.containsDir("res")) {
in = inApk.getDir("res"); in = inApk.getDir("res");

View File

@ -51,15 +51,7 @@ public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializ
} }
serializer.startTag(null, "item"); serializer.startTag(null, "item");
serializer.attribute(null, "quantity", QUANTITY_MAP[i]); serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
serializer.text(item.encodeAsResXmlValue());
String rawValue = item.encodeAsResXmlValueExt();
//AAPT don`t parse formatted for item tag(only for string and string-array tag),
// so adding "formatted='fasle'" is useless.
/*if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
serializer.attribute(null, "formatted", "false");
}*/
serializer.text(rawValue);
serializer.endTag(null, "item"); serializer.endTag(null, "item");
} }
serializer.endTag(null, "plurals"); serializer.endTag(null, "plurals");

View File

@ -52,7 +52,7 @@ public abstract class ResScalarValue extends ResValue
if (mRawValue != null) { if (mRawValue != null) {
return mRawValue; return mRawValue;
} }
return encodeAsResXml(); return encodeAsResXmlValueExt();
} }
public String encodeAsResXmlValueExt() throws AndrolibException { public String encodeAsResXmlValueExt() throws AndrolibException {
@ -60,7 +60,7 @@ public abstract class ResScalarValue extends ResValue
if (rawValue != null) { if (rawValue != null) {
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) { if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
int count = 1; int count = 1;
StringBuffer result = new StringBuffer(); StringBuilder result = new StringBuilder();
String tmp1[] = rawValue.split("%%", -1); String tmp1[] = rawValue.split("%%", -1);
int tmp1_sz = tmp1.length; int tmp1_sz = tmp1.length;
for(int i=0;i<tmp1_sz;i++) { for(int i=0;i<tmp1_sz;i++) {

View File

@ -247,13 +247,13 @@ public class ARSCDecoder {
byte keyboard = mIn.readByte(); byte keyboard = mIn.readByte();
byte navigation = mIn.readByte(); byte navigation = mIn.readByte();
byte inputFlags = mIn.readByte(); byte inputFlags = mIn.readByte();
/*inputPad0*/ mIn.skipBytes(1); mIn.skipBytes(1);
short screenWidth = mIn.readShort(); short screenWidth = mIn.readShort();
short screenHeight = mIn.readShort(); short screenHeight = mIn.readShort();
short sdkVersion = mIn.readShort(); short sdkVersion = mIn.readShort();
/*minorVersion, now must always be 0*/ mIn.skipBytes(2); mIn.skipBytes(2);
byte screenLayout = 0; byte screenLayout = 0;
byte uiMode = 0; byte uiMode = 0;

View File

@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package brut.androlib.res.decoder; package brut.androlib.res.decoder;
import android.content.res.XmlResourceParser; import android.content.res.XmlResourceParser;
@ -40,14 +39,13 @@ import java.util.logging.Logger;
* *
* Binary xml files parser. * Binary xml files parser.
* *
* Parser has only two states: * Parser has only two states: (1) Operational state, which parser obtains after
* (1) Operational state, which parser obtains after first successful call * first successful call to next() and retains until open(), close(), or failed
* to next() and retains until open(), close(), or failed call to next(). * call to next(). (2) Closed state, which parser obtains after open(), close(),
* (2) Closed state, which parser obtains after open(), close(), or failed * or failed call to next(). In this state methods return invalid values or
* call to next(). In this state methods return invalid values or throw exceptions. * throw exceptions.
* *
* TODO: * TODO: * check all methods in closed state
* * check all methods in closed state
* *
*/ */
public class AXmlResourceParser implements XmlResourceParser { public class AXmlResourceParser implements XmlResourceParser {
@ -324,15 +322,14 @@ public class AXmlResourceParser implements XmlResourceParser {
} catch (AndrolibException ex) { } catch (AndrolibException ex) {
setFirstError(ex); setFirstError(ex);
LOGGER.log(Level.WARNING, String.format( LOGGER.log(Level.WARNING, String.format(
"Could not decode attr value, using undecoded value " + "Could not decode attr value, using undecoded value "
"instead: ns=%s, name=%s, value=0x%08x", + "instead: ns=%s, name=%s, value=0x%08x",
getAttributePrefix(index), getAttributeName(index), getAttributePrefix(index), getAttributeName(index),
valueData valueData), ex);
), ex);
} }
} else { } else {
if (valueType == TypedValue.TYPE_STRING) { if (valueType == TypedValue.TYPE_STRING) {
return m_strings.getString(valueRaw); return ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw));
} }
} }
@ -493,20 +490,16 @@ public class AXmlResourceParser implements XmlResourceParser {
///////////////////////////////////////////// implementation ///////////////////////////////////////////// implementation
/** /**
* Namespace stack, holds prefix+uri pairs, as well as * Namespace stack, holds prefix+uri pairs, as well as depth information.
* depth information. * All information is stored in one int[] array. Array consists of depth
* All information is stored in one int[] array. * frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count;
* Array consists of depth frames: * Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable
* Data=DepthFrame*; * bottom-up traversal. increaseDepth adds depth frame, decreaseDepth
* DepthFrame=Count+[Prefix+Uri]*+Count; * removes it. push/pop operations operate only in current depth frame.
* Count='count of Prefix+Uri pairs'; * decreaseDepth removes any remaining (not pop'ed) namespace pairs. findXXX
* Yes, count is stored twice, to enable bottom-up traversal. * methods search all depth frames starting from the last namespace pair of
* increaseDepth adds depth frame, decreaseDepth removes it. * current depth frame. All functions that operate with int, use -1 as
* push/pop operations operate only in current depth frame. * 'invalid value'.
* decreaseDepth removes any remaining (not pop'ed) namespace pairs.
* findXXX methods search all depth frames starting
* from the last namespace pair of current depth frame.
* All functions that operate with int, use -1 as 'invalid value'.
* *
* !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !! * !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !!
* *
@ -806,7 +799,9 @@ public class AXmlResourceParser implements XmlResourceParser {
// Delayed initialization. // Delayed initialization.
if (m_strings == null) { if (m_strings == null) {
m_reader.skipCheckInt(CHUNK_AXML_FILE); m_reader.skipCheckInt(CHUNK_AXML_FILE);
/*chunkSize*/ m_reader.skipInt(); /*
* chunkSize
*/ m_reader.skipInt();
m_strings = StringBlock.read(m_reader); m_strings = StringBlock.read(m_reader);
m_namespaces.increaseDepth(); m_namespaces.increaseDepth();
m_operational = true; m_operational = true;
@ -856,9 +851,13 @@ public class AXmlResourceParser implements XmlResourceParser {
} }
// Common header. // Common header.
/*chunkSize*/ m_reader.skipInt(); /*
* chunkSize
*/ m_reader.skipInt();
int lineNumber = m_reader.readInt(); int lineNumber = m_reader.readInt();
/*0xFFFFFFFF*/ m_reader.skipInt(); /*
* 0xFFFFFFFF
*/ m_reader.skipInt();
// Fake START_DOCUMENT event. // Fake START_DOCUMENT event.
if (chunkType == CHUNK_XML_START_TAG && event == -1) { if (chunkType == CHUNK_XML_START_TAG && event == -1) {
@ -874,8 +873,12 @@ public class AXmlResourceParser implements XmlResourceParser {
int uri = m_reader.readInt(); int uri = m_reader.readInt();
m_namespaces.push(prefix, uri); m_namespaces.push(prefix, uri);
} else { } else {
/*prefix*/ m_reader.skipInt(); /*
/*uri*/ m_reader.skipInt(); * prefix
*/ m_reader.skipInt();
/*
* uri
*/ m_reader.skipInt();
m_namespaces.pop(); m_namespaces.pop();
} }
continue; continue;
@ -888,7 +891,9 @@ public class AXmlResourceParser implements XmlResourceParser {
if (chunkType == CHUNK_XML_START_TAG) { if (chunkType == CHUNK_XML_START_TAG) {
m_namespaceUri = m_reader.readInt(); m_namespaceUri = m_reader.readInt();
m_name = m_reader.readInt(); m_name = m_reader.readInt();
/*flags?*/ m_reader.skipInt(); /*
* flags?
*/ m_reader.skipInt();
int attributeCount = m_reader.readInt(); int attributeCount = m_reader.readInt();
m_idAttribute = (attributeCount >>> 16) - 1; m_idAttribute = (attributeCount >>> 16) - 1;
attributeCount &= 0xFFFF; attributeCount &= 0xFFFF;
@ -922,8 +927,12 @@ public class AXmlResourceParser implements XmlResourceParser {
if (chunkType == CHUNK_XML_TEXT) { if (chunkType == CHUNK_XML_TEXT) {
m_name = m_reader.readInt(); m_name = m_reader.readInt();
/*?*/ m_reader.skipInt(); /*
/*?*/ m_reader.skipInt(); * ?
*/ m_reader.skipInt();
/*
* ?
*/ m_reader.skipInt();
m_event = TEXT; m_event = TEXT;
break; break;
} }
@ -931,10 +940,12 @@ public class AXmlResourceParser implements XmlResourceParser {
} }
private static String formatArray(int[] array, int min, int max) { private static String formatArray(int[] array, int min, int max) {
if(max > array.length) if (max > array.length) {
max = array.length; max = array.length;
if(min < 0) }
if (min < 0) {
min = 0; min = 0;
}
StringBuffer sb = new StringBuffer("["); StringBuffer sb = new StringBuffer("[");
int i = min; int i = min;
while (true) { while (true) {
@ -953,21 +964,20 @@ public class AXmlResourceParser implements XmlResourceParser {
private boolean compareAttr(int[] attr1, int[] attr2) { private boolean compareAttr(int[] attr1, int[] attr2) {
//TODO: sort Attrs //TODO: sort Attrs
/* /*
* ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING * ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING :
* : ATTRIBUTE_IX_NAMESPACE_URI * ATTRIBUTE_IX_NAMESPACE_URI ATTRIBUTE_IX_NAMESPACE_URI :
* ATTRIBUTE_IX_NAMESPACE_URI : ATTRIBUTE_IX_NAME * ATTRIBUTE_IX_NAME id
* id
* *
*/ */
if(attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING && if (attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING
attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE] && && attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE]
//(m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) || && //(m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) ||
// m_strings.touch(attr2[ATTRIBUTE_IX_VALUE_STRING], m_name)) && // m_strings.touch(attr2[ATTRIBUTE_IX_VALUE_STRING], m_name)) &&
//m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) && //m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) &&
attr1[ATTRIBUTE_IX_VALUE_STRING] != attr2[ATTRIBUTE_IX_VALUE_STRING]) { attr1[ATTRIBUTE_IX_VALUE_STRING] != attr2[ATTRIBUTE_IX_VALUE_STRING]) {
return (attr1[ATTRIBUTE_IX_VALUE_STRING] < attr2[ATTRIBUTE_IX_VALUE_STRING]); return (attr1[ATTRIBUTE_IX_VALUE_STRING] < attr2[ATTRIBUTE_IX_VALUE_STRING]);
} else if ((attr1[ATTRIBUTE_IX_NAMESPACE_URI] == attr2[ATTRIBUTE_IX_NAMESPACE_URI]) && (attr1[ATTRIBUTE_IX_NAMESPACE_URI] != -1) && } else if ((attr1[ATTRIBUTE_IX_NAMESPACE_URI] == attr2[ATTRIBUTE_IX_NAMESPACE_URI]) && (attr1[ATTRIBUTE_IX_NAMESPACE_URI] != -1)
//(m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) || && //(m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) ||
// m_strings.touch(attr2[ATTRIBUTE_IX_NAME], m_name)) && // m_strings.touch(attr2[ATTRIBUTE_IX_NAME], m_name)) &&
//m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) && //m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) &&
(attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) { (attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) {
@ -994,10 +1004,10 @@ public class AXmlResourceParser implements XmlResourceParser {
if (dbgOut == null) { if (dbgOut == null) {
dbgOut = new BufferedWriter(new FileWriter("C:\\res.log", false)); dbgOut = new BufferedWriter(new FileWriter("C:\\res.log", false));
} }
dbgOut.write("Namespace: " + getAttributeNamespace (i) + dbgOut.write("Namespace: " + getAttributeNamespace(i)
", Name: " + getAttributeName (i)+ + ", Name: " + getAttributeName(i)
", Value: " + getAttributeValue (i) + ", Array: " + + ", Value: " + getAttributeValue(i) + ", Array: "
formatArray(tmp1[i], 0, ATTRIBUTE_LENGHT) + "\n"); + formatArray(tmp1[i], 0, ATTRIBUTE_LENGHT) + "\n");
dbgOut.flush(); dbgOut.flush();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -1025,16 +1035,14 @@ public class AXmlResourceParser implements XmlResourceParser {
mFirstError = error; mFirstError = error;
} }
} }
/////////////////////////////////// data /////////////////////////////////// data
/* /*
* All values are essentially indices, e.g. m_name is * All values are essentially indices, e.g. m_name is an index of name in
* an index of name in m_strings. * m_strings.
*/ */
private ExtDataInput m_reader; private ExtDataInput m_reader;
private ResAttrDecoder mAttrDecoder; private ResAttrDecoder mAttrDecoder;
private AndrolibException mFirstError; private AndrolibException mFirstError;
private boolean m_operational = false; private boolean m_operational = false;
private StringBlock m_strings; private StringBlock m_strings;
private int[] m_resourceIDs; private int[] m_resourceIDs;
@ -1048,20 +1056,16 @@ public class AXmlResourceParser implements XmlResourceParser {
private int m_idAttribute; private int m_idAttribute;
private int m_classAttribute; private int m_classAttribute;
private int m_styleAttribute; private int m_styleAttribute;
private final static Logger LOGGER = private final static Logger LOGGER =
Logger.getLogger(AXmlResourceParser.class.getName()); Logger.getLogger(AXmlResourceParser.class.getName());
private static final String E_NOT_SUPPORTED = "Method is not supported."; private static final String E_NOT_SUPPORTED = "Method is not supported.";
private static final int private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0,
ATTRIBUTE_IX_NAMESPACE_URI = 0,
ATTRIBUTE_IX_NAME = 1, ATTRIBUTE_IX_NAME = 1,
ATTRIBUTE_IX_VALUE_STRING = 2, ATTRIBUTE_IX_VALUE_STRING = 2,
ATTRIBUTE_IX_VALUE_TYPE = 3, ATTRIBUTE_IX_VALUE_TYPE = 3,
ATTRIBUTE_IX_VALUE_DATA = 4, ATTRIBUTE_IX_VALUE_DATA = 4,
ATTRIBUTE_LENGHT = 5; ATTRIBUTE_LENGHT = 5;
private static final int CHUNK_AXML_FILE = 0x00080003,
private static final int
CHUNK_AXML_FILE = 0x00080003,
CHUNK_RESOURCEIDS = 0x00080180, CHUNK_RESOURCEIDS = 0x00080180,
CHUNK_XML_FIRST = 0x00100100, CHUNK_XML_FIRST = 0x00100100,
CHUNK_XML_START_NAMESPACE = 0x00100100, CHUNK_XML_START_NAMESPACE = 0x00100100,

View File

@ -156,38 +156,25 @@ public final class ResXmlEncoders {
return out.toString(); return out.toString();
} }
/**
* It searches for "%", but not "%%" nor "%(\d)+\$"
*/
private static List<Integer> findNonPositionalSubstitutions(String str, private static List<Integer> findNonPositionalSubstitutions(String str,
int max) { int max) {
int pos = 0; int pos = 0;
int pos2 = 0;
int count = 0; int count = 0;
int length = str.length(); int length = str.length();
List<Integer> ret = new ArrayList<Integer>(); List<Integer> ret = new ArrayList<Integer>();
while((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) { while((pos = str.indexOf('%', pos)) != -1) {
if (pos2 == length) { if (pos + 1 == length) {
break; break;
} }
char c = str.charAt(pos2++); char c = str.charAt(pos + 1);
if (c == '%') { if (c >= 'a' && c <= 'z') {
continue;
}
if (c >= '0' && c <= '9' && pos2 < length) {
do {
c = str.charAt(pos2++);
} while (c >= '0' && c <= '9' && pos2 < length);
if (c == '$') {
continue;
}
}
ret.add(pos); ret.add(pos);
if (max != -1 && ++count >= max) { if (max != -1 && ++count >= max) {
break; break;
} }
} }
pos += 2;
}
return ret; return ret;
} }