diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 68fd5959..00000000 --- a/NOTICE +++ /dev/null @@ -1,11 +0,0 @@ -apktool -Copyright 2011 Ryszard Wiśniewski - -This product includes software developed by: - - * Ryszard Wiśniewski (brut.alll@gmail.com) - * JesusFreke (http://code.google.com/p/smali/) - * Dmitry Skiba (http://code.google.com/p/android4me/) - * Tahseen Ur Rehman (http://code.google.com/p/radixtree/) - * Android Open Source Project (http://source.android.com/) - * The Apache Software Foundation (http://www.apache.org/) diff --git a/apktool-cli/pom.xml b/apktool-cli/pom.xml deleted file mode 100644 index 8390d68d..00000000 --- a/apktool-cli/pom.xml +++ /dev/null @@ -1,71 +0,0 @@ - - 4.0.0 - - brut.apktool - apktool-cli - 1.5.1-SNAPSHOT - jar - - - brut.apktool - apktool-project - 1.0-SNAPSHOT - - - apktool cli - - - ${project.parent.basedir} - - - - - - maven-assembly-plugin - - - src/main/assembly/jar-with-deps.xml - - - - brut.apktool.Main - - - - - - package - - single - - - - - - - - - - ${project.groupId} - apktool-lib - ${project.version} - - - common - brut.j - jar - 1.0 - - - brut.apktool.smali - dexlib - 1.3.4-ibot8 - jar - - - brut.apktool.smali - baksmali - 1.3.4-ibot8 - - - \ No newline at end of file diff --git a/apktool-cli/src/main/assembly/jar-with-deps.xml b/apktool-cli/src/main/assembly/jar-with-deps.xml deleted file mode 100644 index cfea65c5..00000000 --- a/apktool-cli/src/main/assembly/jar-with-deps.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - jar-with-deps - - jar - - false - - - / - true - runtime - - org.jf.* - brut.* - xpp3:xpp3 - org.yaml:snakeyaml - commons-io:* - org.antlr:antlr-runtime - com.google.* - org.apache.* - net.lingala.* - - - - diff --git a/apktool-cli/src/main/java/brut/apktool/Main.java b/apktool-cli/src/main/java/brut/apktool/Main.java deleted file mode 100644 index 64b8a4a5..00000000 --- a/apktool-cli/src/main/java/brut/apktool/Main.java +++ /dev/null @@ -1,333 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.apktool; - -import brut.androlib.Androlib; -import brut.androlib.AndrolibException; -import brut.androlib.ApkDecoder; -import brut.androlib.ApktoolProperties; -import brut.androlib.err.CantFindFrameworkResException; -import brut.androlib.err.InFileNotFoundException; -import brut.androlib.err.OutDirExistsException; -import brut.androlib.res.util.ExtFile; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.logging.*; - -/** - * @author Ryszard Wiśniewski - */ -public class Main { - public static void main(String[] args) - throws IOException, AndrolibException, InterruptedException { - try { - Verbosity verbosity = Verbosity.NORMAL; - int i; - for (i = 0; i < args.length; i++) { - String opt = args[i]; - if (! opt.startsWith("-")) { - break; - } - if ("-v".equals(opt) || "--verbose".equals(opt)) { - if (verbosity != Verbosity.NORMAL) { - throw new InvalidArgsError(); - } - verbosity = Verbosity.VERBOSE; - } else if ("-q".equals(opt) || "--quiet".equals(opt)) { - if (verbosity != Verbosity.NORMAL) { - throw new InvalidArgsError(); - } - verbosity = Verbosity.QUIET; - } else { - throw new InvalidArgsError(); - } - } - setupLogging(verbosity); - - if (args.length <= i) { - throw new InvalidArgsError(); - } - String cmd = args[i]; - args = Arrays.copyOfRange(args, i + 1, args.length); - - if ("d".equals(cmd) || "decode".equals(cmd)) { - cmdDecode(args); - } else if ("b".equals(cmd) || "build".equals(cmd)) { - cmdBuild(args); - } else if ("if".equals(cmd) || "install-framework".equals(cmd)) { - cmdInstallFramework(args); - } else if ("publicize-resources".equals(cmd)) { - cmdPublicizeResources(args); - } else { - throw new InvalidArgsError(); - } - } catch (InvalidArgsError ex) { - usage(); - System.exit(1); - } - } - - private static void cmdDecode(String[] args) throws InvalidArgsError, - AndrolibException { - ApkDecoder decoder = new ApkDecoder(); - - int i; - for (i = 0; i < args.length; i++) { - String opt = args[i]; - if (! opt.startsWith("-")) { - break; - } - if ("-s".equals(opt) || "--no-src".equals(opt)) { - decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_NONE); - } else if ("-d".equals(opt) || "--debug".equals(opt)) { - decoder.setDebugMode(true); - } else if ("-b".equals(opt) || "--no-debug-info".equals(opt)) { - decoder.setBaksmaliDebugMode(false); - } else if ("-t".equals(opt) || "--frame-tag".equals(opt)) { - i++; - if (i >= args.length) { - throw new InvalidArgsError(); - } - decoder.setFrameworkTag(args[i]); - } else if ("-f".equals(opt) || "--force".equals(opt)) { - decoder.setForceDelete(true); - } else if ("-r".equals(opt) || "--no-res".equals(opt)) { - decoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE); - } else if ("--keep-broken-res".equals(opt)) { - decoder.setKeepBrokenResources(true); - } else { - throw new InvalidArgsError(); - } - } - - String outName = null; - if (args.length == i + 2) { - outName = args[i + 1]; - } else if (args.length == i + 1) { - outName = args[i]; - outName = outName.endsWith(".apk") ? - outName.substring(0, outName.length() - 4) : outName + ".out"; - outName = new File(outName).getName(); - } else { - throw new InvalidArgsError(); - } - File outDir = new File(outName); - decoder.setOutDir(outDir); - decoder.setApkFile(new File(args[i])); - - try { - decoder.decode(); - } catch (OutDirExistsException ex) { - System.out.println( - "Destination directory (" + outDir.getAbsolutePath() + ") " + - "already exists. Use -f switch if you want to overwrite it."); - System.exit(1); - } catch (InFileNotFoundException ex) { - System.out.println( - "Input file (" + args[i] + ") " + - "was not found or was not readable."); - System.exit(1); - } catch (CantFindFrameworkResException ex) { - System.out.println( - "Can't find framework resources for package of id: " + - String.valueOf(ex.getPkgId()) + ". You must install proper " + - "framework files, see project website for more info."); - System.exit(1); - } - } - - private static void cmdBuild(String[] args) throws InvalidArgsError, - AndrolibException { - - // hold all the fields - HashMap flags = new HashMap(); - flags.put("forceBuildAll", false); - flags.put("debug", false); - flags.put("verbose", false); - flags.put("injectOriginal", false); - flags.put("framework", false); - flags.put("update", false); - - int i; - int skip = 0; - ExtFile mOrigApk = null; - for (i = 0; i < args.length; i++) { - String opt = args[i]; - if (! opt.startsWith("-")) { - break; - } - if ("-f".equals(opt) || "--force-all".equals(opt)) { - flags.put("forceBuildAll", true); - } else if ("-d".equals(opt) || "--debug".equals(opt)) { - flags.put("debug", true); - } else if ("-v".equals(opt) || "--verbose".equals(opt)) { - flags.put("verbose", true); - } else if ("-o".equals(opt) || "--original".equals(opt)) { - if (args.length >= 4) { - throw new InvalidArgsError(); - } else { - flags.put("injectOriginal", true); - mOrigApk = new ExtFile(args[i + 1]); - skip = 1; - } - } else { - throw new InvalidArgsError(); - } - } - - String appDirName; - File outFile = null; - switch (args.length - i - skip) { - case 0: - appDirName = "."; - break; - case 2: - outFile = new File(args[i + 1 + skip]); - case 1: - appDirName = args[i + skip]; - break; - default: - throw new InvalidArgsError(); - } - - new Androlib().build(new File(appDirName), outFile, flags, mOrigApk); - } - - private static void cmdInstallFramework(String[] args) - throws AndrolibException { - String tag = null; - switch (args.length) { - case 2: - tag = args[1]; - case 1: - new Androlib().installFramework(new File(args[0]), tag); - return; - } - - throw new InvalidArgsError(); - } - - private static void cmdPublicizeResources(String[] args) - throws InvalidArgsError, AndrolibException { - if (args.length != 1) { - throw new InvalidArgsError(); - } - - new Androlib().publicizeResources(new File(args[0])); - } - - private static void usage() { - System.out.println( - "Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" + - "Copyright 2010 Ryszard Wiśniewski \n" + - "with smali v" + ApktoolProperties.get("smaliVersion") + - ", and baksmali v" + ApktoolProperties.get("baksmaliVersion") + "\n" + - "Updated by iBotPeaches \n" + - "Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" + - "\n" + - "Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n" + - "\n" + - "COMMANDs are:\n" + - "\n" + - " d[ecode] [OPTS] []\n" + - " Decode to .\n" + - "\n" + - " OPTS:\n" + - "\n" + - " -s, --no-src\n" + - " Do not decode sources.\n" + - " -r, --no-res\n" + - " Do not decode resources.\n" + - " -d, --debug\n" + - " Decode in debug mode. Check project page for more info.\n" + - " -b, --no-debug-info\n" + - " Baksmali -- don't write out debug info (.local, .param, .line, etc.)\n" + - " -f, --force\n" + - " Force delete destination directory.\n" + - " -t , --frame-tag \n" + - " Try to use framework files tagged by .\n" + - " --keep-broken-res\n" + - " Use if there was an error and some resources were dropped, e.g.:\n" + - " \"Invalid config flags detected. Dropping resources\", but you\n" + - " want to decode them anyway, even with errors. You will have to\n" + - " fix them manually before building." + - "\n\n" + - " b[uild] [OPTS] [] []\n" + - " Build an apk from already decoded application located in .\n" + - "\n" + - " It will automatically detect, whether files was changed and perform\n" + - " needed steps only.\n" + - "\n" + - " If you omit then current directory will be used.\n" + - " If you omit then /dist/\n" + - " will be used.\n" + - "\n" + - " OPTS:\n" + - "\n" + - " -f, --force-all\n" + - " Skip changes detection and build all files.\n" + - " -d, --debug\n" + - " Build in debug mode. Check project page for more info.\n" + - " -o, --original\n" + - " Build resources into original APK. Retains signature." + - "\n" + - " if|install-framework []\n" + - " Install framework file to your system.\n" + - "\n" + - "For additional info, see: https://github.com/iBotPeaches/brut.apktool" + - "\n" + - "For smali/baksmali info, see: http://code.google.com/p/smali/" - ); - } - - private static void setupLogging(Verbosity verbosity) { - Logger logger = Logger.getLogger(""); - for (Handler handler : logger.getHandlers()) { - logger.removeHandler(handler); - } - if (verbosity == Verbosity.QUIET) { - return; - } - - Handler handler = new ConsoleHandler(); - logger.addHandler(handler); - - if (verbosity == Verbosity.VERBOSE) { - handler.setLevel(Level.ALL); - logger.setLevel(Level.ALL); - } else { - handler.setFormatter(new Formatter() { - @Override - public String format(LogRecord record) { - return record.getLevel().toString().charAt(0) + ": " - + record.getMessage() - + System.getProperty("line.separator"); - } - }); - } - } - - private static enum Verbosity { - NORMAL, VERBOSE, QUIET; - } - - static class InvalidArgsError extends AndrolibException { - - } -} diff --git a/apktool-lib/pom.xml b/apktool-lib/pom.xml deleted file mode 100644 index 1a9bed02..00000000 --- a/apktool-lib/pom.xml +++ /dev/null @@ -1,153 +0,0 @@ - - 4.0.0 - brut.apktool - apktool-lib - 1.5.1-SNAPSHOT - jar - - - brut.apktool - apktool-project - 1.0-SNAPSHOT - - - apktool library - - - 1.3.4-ibot7 - ${project.parent.basedir} - - - - - - src/main/resources - true - - brut/androlib/apktool.properties - - - - src/main/resources - - brut/androlib/android-framework.jar - - - - - - com.mycila.maven-license-plugin - maven-license-plugin - - - src/main/java/com/mindprod/** - src/main/java/android/** - src/main/java/org/xmlpull/** - src/main/resources/brut/androlib/apktool.properties - src/test/resources/brut/apktool/testapp/** - - - - - pl.project13.maven - git-commit-id-plugin - 1.5 - - - - revision - - - - - - - - - - brut.j - dir - 1.0 - - - brut.j - util - 1.0 - - - brut.j - common - 1.0 - - - brut.apktool.smali - util - ${smaliVersion} - - - org.yaml - snakeyaml - 1.7 - - - xpp3 - xpp3 - 1.1.4c - - - junit - junit - 4.10 - - - hamcrest-core - org.hamcrest - - - - - xmlunit - xmlunit - 1.3 - - - com.google.guava - guava - 12.0 - jar - - - org.apache.commons - commons-lang3 - 3.1 - jar - - - brut.apktool.smali - baksmali - 1.3.4-ibot8 - - - brut.apktool.smali - dexlib - 1.3.4-ibot8 - - - brut.apktool.smali - smali - 1.3.4-ibot8 - - - net.lingala.zip4j - zip4j - 1.3.1 - - - - - sonatype-releases - Sonatype Releases - https://oss.sonatype.org/content/repositories/releases/ - - - \ No newline at end of file diff --git a/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java b/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java deleted file mode 100644 index c59e6d4e..00000000 --- a/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.res; - -import org.xmlpull.v1.XmlPullParser; - -import android.util.AttributeSet; - -/** - * The XML parsing interface returned for an XML resource. This is a standard - * XmlPullParser interface, as well as an extended AttributeSet interface and - * an additional close() method on this interface for the client to indicate - * when it is done reading the resource. - */ -public interface XmlResourceParser extends XmlPullParser, AttributeSet { - /** - * Close this interface to the resource. Calls on the interface are no - * longer value after this call. - */ - public void close(); -} - diff --git a/apktool-lib/src/main/java/android/util/AttributeSet.java b/apktool-lib/src/main/java/android/util/AttributeSet.java deleted file mode 100644 index 1da422fd..00000000 --- a/apktool-lib/src/main/java/android/util/AttributeSet.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2008 Android4ME - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.util; - -/** - * @author Dmitry Skiba - * - */ -public interface AttributeSet { - int getAttributeCount(); - String getAttributeName(int index); - String getAttributeValue(int index); - String getPositionDescription(); - int getAttributeNameResource(int index); - int getAttributeListValue(int index,String options[],int defaultValue); - boolean getAttributeBooleanValue(int index,boolean defaultValue); - int getAttributeResourceValue(int index,int defaultValue); - int getAttributeIntValue(int index,int defaultValue); - int getAttributeUnsignedIntValue(int index,int defaultValue); - float getAttributeFloatValue(int index,float defaultValue); - String getIdAttribute(); - String getClassAttribute(); - int getIdAttributeResourceValue(int index); - int getStyleAttribute(); - String getAttributeValue(String namespace, String attribute); - int getAttributeListValue(String namespace,String attribute,String options[],int defaultValue); - boolean getAttributeBooleanValue(String namespace,String attribute,boolean defaultValue); - int getAttributeResourceValue(String namespace,String attribute,int defaultValue); - int getAttributeIntValue(String namespace,String attribute,int defaultValue); - int getAttributeUnsignedIntValue(String namespace,String attribute,int defaultValue); - float getAttributeFloatValue(String namespace,String attribute,float defaultValue); - - //TODO: remove - int getAttributeValueType(int index); - int getAttributeValueData(int index); -} \ No newline at end of file diff --git a/apktool-lib/src/main/java/android/util/TypedValue.java b/apktool-lib/src/main/java/android/util/TypedValue.java deleted file mode 100644 index d7b5295d..00000000 --- a/apktool-lib/src/main/java/android/util/TypedValue.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.util; - -/** - * Container for a dynamically typed data value. Primarily used with - * {@link android.content.res.Resources} for holding resource values. - */ -public class TypedValue { - /** The value contains no data. */ - public static final int TYPE_NULL = 0x00; - - /** The data field holds a resource identifier. */ - public static final int TYPE_REFERENCE = 0x01; - /** The data field holds an attribute resource - * identifier (referencing an attribute in the current theme - * style, not a resource entry). */ - public static final int TYPE_ATTRIBUTE = 0x02; - /** The string field holds string data. In addition, if - * data is non-zero then it is the string block - * index of the string and assetCookie is the set of - * assets the string came from. */ - public static final int TYPE_STRING = 0x03; - /** The data field holds an IEEE 754 floating point number. */ - public static final int TYPE_FLOAT = 0x04; - /** The data field holds a complex number encoding a - * dimension value. */ - public static final int TYPE_DIMENSION = 0x05; - /** The data field holds a complex number encoding a fraction - * of a container. */ - public static final int TYPE_FRACTION = 0x06; - - /** Identifies the start of plain integer values. Any type value - * from this to {@link #TYPE_LAST_INT} means the - * data field holds a generic integer value. */ - public static final int TYPE_FIRST_INT = 0x10; - - /** The data field holds a number that was - * originally specified in decimal. */ - public static final int TYPE_INT_DEC = 0x10; - /** The data field holds a number that was - * originally specified in hexadecimal (0xn). */ - public static final int TYPE_INT_HEX = 0x11; - /** The data field holds 0 or 1 that was originally - * specified as "false" or "true". */ - public static final int TYPE_INT_BOOLEAN = 0x12; - - /** Identifies the start of integer values that were specified as - * color constants (starting with '#'). */ - public static final int TYPE_FIRST_COLOR_INT = 0x1c; - - /** The data field holds a color that was originally - * specified as #aarrggbb. */ - public static final int TYPE_INT_COLOR_ARGB8 = 0x1c; - /** The data field holds a color that was originally - * specified as #rrggbb. */ - public static final int TYPE_INT_COLOR_RGB8 = 0x1d; - /** The data field holds a color that was originally - * specified as #argb. */ - public static final int TYPE_INT_COLOR_ARGB4 = 0x1e; - /** The data field holds a color that was originally - * specified as #rgb. */ - public static final int TYPE_INT_COLOR_RGB4 = 0x1f; - - /** Identifies the end of integer values that were specified as color - * constants. */ - public static final int TYPE_LAST_COLOR_INT = 0x1f; - - /** Identifies the end of plain integer values. */ - public static final int TYPE_LAST_INT = 0x1f; - - /* ------------------------------------------------------------ */ - - /** Complex data: bit location of unit information. */ - public static final int COMPLEX_UNIT_SHIFT = 0; - /** Complex data: mask to extract unit information (after shifting by - * {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as - * defined below. */ - public static final int COMPLEX_UNIT_MASK = 0xf; - - /** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */ - public static final int COMPLEX_UNIT_PX = 0; - /** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent - * Pixels. */ - public static final int COMPLEX_UNIT_DIP = 1; - /** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */ - public static final int COMPLEX_UNIT_SP = 2; - /** {@link #TYPE_DIMENSION} complex unit: Value is in points. */ - public static final int COMPLEX_UNIT_PT = 3; - /** {@link #TYPE_DIMENSION} complex unit: Value is in inches. */ - public static final int COMPLEX_UNIT_IN = 4; - /** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */ - public static final int COMPLEX_UNIT_MM = 5; - - /** {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall - * size. */ - public static final int COMPLEX_UNIT_FRACTION = 0; - /** {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. */ - public static final int COMPLEX_UNIT_FRACTION_PARENT = 1; - - /** Complex data: where the radix information is, telling where the decimal - * place appears in the mantissa. */ - public static final int COMPLEX_RADIX_SHIFT = 4; - /** Complex data: mask to extract radix information (after shifting by - * {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point - * representations as defined below. */ - public static final int COMPLEX_RADIX_MASK = 0x3; - - /** Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 */ - public static final int COMPLEX_RADIX_23p0 = 0; - /** Complex data: the mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn */ - public static final int COMPLEX_RADIX_16p7 = 1; - /** Complex data: the mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn */ - public static final int COMPLEX_RADIX_8p15 = 2; - /** Complex data: the mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn */ - public static final int COMPLEX_RADIX_0p23 = 3; - - /** Complex data: bit location of mantissa information. */ - public static final int COMPLEX_MANTISSA_SHIFT = 8; - /** Complex data: mask to extract mantissa information (after shifting by - * {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision; - * the top bit is the sign. */ - public static final int COMPLEX_MANTISSA_MASK = 0xffffff; - - /* ------------------------------------------------------------ */ - - /** - * If {@link #density} is equal to this value, then the density should be - * treated as the system's default density value: {@link DisplayMetrics#DENSITY_DEFAULT}. - */ - public static final int DENSITY_DEFAULT = 0; - - /** - * If {@link #density} is equal to this value, then there is no density - * associated with the resource and it should not be scaled. - */ - public static final int DENSITY_NONE = 0xffff; - - /* ------------------------------------------------------------ */ - - /** The type held by this value, as defined by the constants here. - * This tells you how to interpret the other fields in the object. */ - public int type; - - private static final float MANTISSA_MULT = - 1.0f / (1<>TypedValue.COMPLEX_RADIX_SHIFT) - & TypedValue.COMPLEX_RADIX_MASK]; - } - - private static final String[] DIMENSION_UNIT_STRS = new String[] { - "px", "dip", "sp", "pt", "in", "mm" - }; - private static final String[] FRACTION_UNIT_STRS = new String[] { - "%", "%p" - }; - - /** - * Perform type conversion as per {@link #coerceToString()} on an - * explicitly supplied type and data. - * - * @param type The data type identifier. - * @param data The data value. - * - * @return String The coerced string value. If the value is - * null or the type is not known, null is returned. - */ - public static final String coerceToString(int type, int data) - { - switch (type) { - case TYPE_NULL: - return null; - case TYPE_REFERENCE: - return "@" + data; - case TYPE_ATTRIBUTE: - return "?" + data; - case TYPE_FLOAT: - return Float.toString(Float.intBitsToFloat(data)); - case TYPE_DIMENSION: - return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[ - (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; - case TYPE_FRACTION: - return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[ - (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; - case TYPE_INT_HEX: - return "0x" + Integer.toHexString(data); - case TYPE_INT_BOOLEAN: - return data != 0 ? "true" : "false"; - } - - if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) { - String res =String.format("%08x", data); - char[] vals = res.toCharArray(); - switch (type) { - default: - case TYPE_INT_COLOR_ARGB8://#AaRrGgBb - break; - case TYPE_INT_COLOR_RGB8://#FFRrGgBb->#RrGgBb - res = res.substring(2); - break; - case TYPE_INT_COLOR_ARGB4://#AARRGGBB->#ARGB - res = new StringBuffer().append(vals[0]).append(vals[2]).append(vals[4]).append(vals[6]).toString(); - break; - case TYPE_INT_COLOR_RGB4://#FFRRGGBB->#RGB - res = new StringBuffer().append(vals[2]).append(vals[4]).append(vals[6]).toString(); - break; - } - return "#" + res; - } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) { - String res; - switch (type) { - default: - case TYPE_INT_DEC: - res = Integer.toString(data); - break; - //defined before - /*case TYPE_INT_HEX: - res = "0x" + Integer.toHexString(data); - break; - case TYPE_INT_BOOLEAN: - res = (data != 0) ? "true":"false"; - break;*/ - } - return res; - } - - return null; - } - -}; diff --git a/apktool-lib/src/main/java/brut/androlib/Androlib.java b/apktool-lib/src/main/java/brut/androlib/Androlib.java deleted file mode 100644 index 3ea6b496..00000000 --- a/apktool-lib/src/main/java/brut/androlib/Androlib.java +++ /dev/null @@ -1,575 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib; - -import brut.androlib.err.InFileNotFoundException; -import brut.androlib.java.AndrolibJava; -import brut.androlib.res.AndrolibResources; -import brut.androlib.res.data.ResPackage; -import brut.androlib.res.data.ResTable; -import brut.androlib.res.util.ExtFile; -import brut.androlib.src.SmaliBuilder; -import brut.androlib.src.SmaliDecoder; -import brut.common.BrutException; -import brut.directory.*; -import brut.util.BrutIO; -import brut.util.OS; -import java.io.*; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; -import net.lingala.zip4j.core.ZipFile; -import net.lingala.zip4j.exception.ZipException; -import net.lingala.zip4j.model.ZipParameters; -import net.lingala.zip4j.util.Zip4jConstants; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.Yaml; - -/** - * @author Ryszard Wiśniewski - */ -public class Androlib { - private final AndrolibResources mAndRes = new AndrolibResources(); - - public ResTable getResTable(ExtFile apkFile) throws AndrolibException { - return mAndRes.getResTable(apkFile, true); - } - - public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) throws AndrolibException { - return mAndRes.getResTable(apkFile, loadMainPkg); - } - - public void decodeSourcesRaw(ExtFile apkFile, File outDir, boolean debug) - throws AndrolibException { - try { - if (debug) { - LOGGER.warning("Debug mode not available."); - } - Directory apk = apkFile.getDirectory(); - LOGGER.info("Copying raw classes.dex file..."); - apkFile.getDirectory().copyToDir(outDir, "classes.dex"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public void decodeSourcesSmali(File apkFile, File outDir, boolean debug, boolean bakdeb) - throws AndrolibException { - try { - File smaliDir = new File(outDir, SMALI_DIRNAME); - OS.rmdir(smaliDir); - smaliDir.mkdirs(); - LOGGER.info("Baksmaling..."); - SmaliDecoder.decode(apkFile, smaliDir, debug, bakdeb); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } - - public void decodeSourcesJava(ExtFile apkFile, File outDir, boolean debug) - throws AndrolibException { - LOGGER.info("Decoding Java sources..."); - new AndrolibJava().decode(apkFile, outDir); - } - - public void decodeManifestRaw(ExtFile apkFile, File outDir) - throws AndrolibException { - try { - Directory apk = apkFile.getDirectory(); - LOGGER.info("Copying raw manifest..."); - apkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public void decodeManifestFull(ExtFile apkFile, File outDir, - ResTable resTable) throws AndrolibException { - mAndRes.decodeManifest(resTable, apkFile, outDir); - } - - public void decodeResourcesRaw(ExtFile apkFile, File outDir) - throws AndrolibException { - try { - Directory apk = apkFile.getDirectory(); - LOGGER.info("Copying raw resources..."); - apkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public void decodeResourcesFull(ExtFile apkFile, File outDir, - ResTable resTable) throws AndrolibException { - mAndRes.decode(resTable, apkFile, outDir); - } - - public void decodeRawFiles(ExtFile apkFile, File outDir) - throws AndrolibException { - LOGGER.info("Copying assets and libs..."); - try { - Directory in = apkFile.getDirectory(); - if (in.containsDir("assets")) { - in.copyToDir(outDir, "assets"); - } - if (in.containsDir("lib")) { - in.copyToDir(outDir, "lib"); - } - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public void writeMetaFile(File mOutDir, Map meta) - throws AndrolibException { - DumperOptions options = new DumperOptions(); - options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); -// options.setIndent(4); - Yaml yaml = new Yaml(options); - - FileWriter writer = null; - try { - writer = new FileWriter(new File(mOutDir, "apktool.yml")); - yaml.dump(meta, writer); - } catch (IOException ex) { - throw new AndrolibException(ex); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException ex) {} - } - } - } - - public Map readMetaFile(ExtFile appDir) - throws AndrolibException { - InputStream in = null; - try { - in = appDir.getDirectory().getFileInput("apktool.yml"); - Yaml yaml = new Yaml(); - return (Map) yaml.load(in); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) {} - } - } - } - - public void build(File appDir, File outFile, - HashMap flags, ExtFile origApk) throws AndrolibException { - build(new ExtFile(appDir), outFile, flags, origApk); - } - - public void build(ExtFile appDir, File outFile, - HashMap flags, ExtFile origApk) throws AndrolibException { - Map meta = readMetaFile(appDir); - Object t1 = meta.get("isFrameworkApk"); - flags.put("framework", t1 == null ? false : (Boolean) t1); - mAndRes.setSdkInfo((Map) meta.get("sdkInfo")); - - // check the orig apk - if (flags.get("injectOriginal")) { - if (!origApk.isFile() || !origApk.canRead()) { - throw new InFileNotFoundException(); - } else { - mOrigApkFile = origApk; - } - } - - if (outFile == null) { - String outFileName = (String) meta.get("apkFileName"); - outFile = new File(appDir, "dist" + File.separator + - (outFileName == null ? "out.apk" : outFileName)); - } - - new File(appDir, APK_DIRNAME).mkdirs(); - buildSources(appDir, flags); - buildResources(appDir, flags, - (Map) meta.get("usesFramework")); - buildLib(appDir, flags); - buildApk(appDir, outFile,flags); - } - - public void buildSources(File appDir, HashMap flags) - throws AndrolibException { - if (! buildSourcesRaw(appDir, flags) - && ! buildSourcesSmali(appDir, flags) - && ! buildSourcesJava(appDir, flags) - ) { - LOGGER.warning("Could not find sources"); - } - } - - public boolean buildSourcesRaw(File appDir, - HashMap flags) throws AndrolibException { - try { - File working = new File(appDir, "classes.dex"); - 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..."); - BrutIO.copyAndClose(new FileInputStream(working), - new FileOutputStream(stored)); - } - return true; - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } - - public boolean buildSourcesSmali(File appDir, - HashMap flags) throws AndrolibException { - ExtFile smaliDir = new ExtFile(appDir, "smali"); - if (! smaliDir.exists()) { - return false; - } - File dex = new File(appDir, APK_DIRNAME + "/classes.dex"); - if (! flags.get("forceBuildAll")) { - LOGGER.info("Checking whether sources has changed..."); - } - if (flags.get("forceBuildAll") || isModified(smaliDir, dex)) { - LOGGER.info("Smaling..."); - dex.delete(); - SmaliBuilder.build(smaliDir, dex, flags); - } - return true; - } - - public boolean buildSourcesJava(File appDir, - HashMap flags) throws AndrolibException { - File javaDir = new File(appDir, "src"); - if (! javaDir.exists()) { - return false; - } - File dex = new File(appDir, APK_DIRNAME + "/classes.dex"); - if (! flags.get("forceBuildAll")) { - LOGGER.info("Checking whether sources has changed..."); - } - if (flags.get("forceBuildAll") || isModified(javaDir, dex)) { - LOGGER.info("Building java sources..."); - dex.delete(); - new AndrolibJava().build(javaDir, dex); - } - return true; - } - - public void buildResources(ExtFile appDir, HashMap flags, - Map usesFramework) - throws AndrolibException { - if (! buildResourcesRaw(appDir, flags) - && ! buildResourcesFull(appDir, flags, usesFramework) - && ! buildManifest(appDir, flags, usesFramework)) { - LOGGER.warning("Could not find resources"); - } - } - - public boolean buildResourcesRaw(ExtFile appDir, HashMap flags) - throws AndrolibException { - try { - if (! new File(appDir, "resources.arsc").exists()) { - return false; - } - File apkDir = new File(appDir, APK_DIRNAME); - if (! flags.get("forceBuildAll")) { - LOGGER.info("Checking whether resources has changed..."); - } - if (flags.get("forceBuildAll") || isModified( - newFiles(APK_RESOURCES_FILENAMES, appDir), - newFiles(APK_RESOURCES_FILENAMES, apkDir))) { - LOGGER.info("Copying raw resources..."); - appDir.getDirectory() - .copyToDir(apkDir, APK_RESOURCES_FILENAMES); - } - return true; - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public boolean buildResourcesFull(File appDir, HashMap flags, - Map usesFramework) - throws AndrolibException { - try { - if (! new File(appDir, "res").exists()) { - return false; - } - if (! flags.get("forceBuildAll")) { - LOGGER.info("Checking whether resources has changed..."); - } - File apkDir = new File(appDir, APK_DIRNAME); - if (flags.get("forceBuildAll") || isModified( - newFiles(APP_RESOURCES_FILENAMES, appDir), - newFiles(APK_RESOURCES_FILENAMES, apkDir))) { - LOGGER.info("Building resources..."); - - File apkFile = File.createTempFile("APKTOOL", null); - apkFile.delete(); - - File ninePatch = new File(appDir, "9patch"); - if (! ninePatch.exists()) { - ninePatch = null; - } - mAndRes.aaptPackage( - apkFile, - new File(appDir, "AndroidManifest.xml"), - new File(appDir, "res"), - ninePatch, null, parseUsesFramework(usesFramework), - flags - ); - - Directory tmpDir = new ExtFile(apkFile).getDirectory(); - tmpDir.copyToDir(apkDir, - tmpDir.containsDir("res") ? APK_RESOURCES_FILENAMES : - APK_RESOURCES_WITHOUT_RES_FILENAMES); - } - return true; - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public boolean buildManifestRaw(ExtFile appDir, HashMap flags) - throws AndrolibException { - try { - File apkDir = new File(appDir, APK_DIRNAME); - LOGGER.info("Copying raw AndroidManifest.xml..."); - appDir.getDirectory() - .copyToDir(apkDir, APK_MANIFEST_FILENAMES); - return true; - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public boolean buildManifest(ExtFile appDir, HashMap flags, - Map usesFramework) - throws AndrolibException { - try { - if (! new File(appDir, "AndroidManifest.xml").exists()) { - return false; - } - if (! flags.get("forceBuildAll")) { - LOGGER.info("Checking whether resources has changed..."); - } - File apkDir = new File(appDir, APK_DIRNAME); - if (flags.get("forceBuildAll") || isModified( - newFiles(APK_MANIFEST_FILENAMES, appDir), - newFiles(APK_MANIFEST_FILENAMES, apkDir))) { - LOGGER.info("Building AndroidManifest.xml..."); - - File apkFile = File.createTempFile("APKTOOL", null); - apkFile.delete(); - - File ninePatch = new File(appDir, "9patch"); - if (! ninePatch.exists()) { - ninePatch = null; - } - - mAndRes.aaptPackage( - apkFile, - new File(appDir, "AndroidManifest.xml"), - null, - ninePatch, null, parseUsesFramework(usesFramework), - flags - ); - - Directory tmpDir = new ExtFile(apkFile).getDirectory(); - tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES); - } - return true; - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } catch (AndrolibException ex) { - LOGGER.warning("Parse AndroidManifest.xml failed, treat it as raw file."); - return buildManifestRaw(appDir, flags); - } - } - - public void buildLib(File appDir, HashMap flags) - throws AndrolibException { - File working = new File(appDir, "lib"); - if (! working.exists()) { - return; - } - File stored = new File(appDir, APK_DIRNAME + "/lib"); - if (flags.get("forceBuildAll") || isModified(working, stored)) { - LOGGER.info("Copying libs..."); - try { - OS.rmdir(stored); - OS.cpdir(working, stored); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } - } - - public void buildApk(File appDir, File outApk, HashMap flags) - throws AndrolibException { - LOGGER.info("Building apk file..."); - if (outApk.exists()) { - outApk.delete(); - } else { - File outDir = outApk.getParentFile(); - if (outDir != null && ! outDir.exists()) { - outDir.mkdirs(); - } - } - File assetDir = new File(appDir, "assets"); - if (! assetDir.exists()) { - assetDir = null; - } - mAndRes.aaptPackage(outApk, null, null, - new File(appDir, APK_DIRNAME), assetDir, null, flags); - - /* check for re-insert */ - if (flags.get("injectOriginal")) { - try { - LOGGER.info("Building resources into original apk file..."); - ZipFile editOrig = new ZipFile(mOrigApkFile.getAbsoluteFile()); - - // no compression levels, paras - ZipParameters parameters = new ZipParameters(); - parameters.setCompressionMethod(Zip4jConstants.COMP_STORE); - parameters.setCompressionLevel(0); - parameters.setIncludeRootFolder(true); - parameters.setRootFolderInZip("/"); - - // add res folder - editOrig.addFolder(new File(appDir, APK_DIRNAME + "/res").getAbsolutePath(), parameters); - - // add assets, if there - if (assetDir != null) { - //editOrig.addFolder(new File(appDir, APK_DIRNAME + "/assets").getAbsolutePath(), parameters); - } - - // add resources.arsc - parameters.setFileNameInZip("resources.arsc"); - // editOrig.addFile(new File(appDir, "resources.arsc"), parameters); - } catch(ZipException ex) { - throw new AndrolibException(ex); - } - } - } - - public void publicizeResources(File arscFile) throws AndrolibException { - mAndRes.publicizeResources(arscFile); - } - - public void installFramework(File frameFile, String tag) - throws AndrolibException { - mAndRes.installFramework(frameFile, tag); - } - - public boolean isFrameworkApk(ResTable resTable) { - for (ResPackage pkg : resTable.listMainPackages()) { - if (pkg.getId() < 64) { - return true; - } - } - return false; - } - - public static String getVersion() { - String version = ApktoolProperties.get("version"); - return version.endsWith("-SNAPSHOT") ? - version.substring(0, version.length() - 9) + '.' + - ApktoolProperties.get("git.commit.id.abbrev") - : version; - } - - private File[] parseUsesFramework(Map usesFramework) - throws AndrolibException { - if (usesFramework == null) { - return null; - } - - List ids = (List) usesFramework.get("ids"); - if (ids == null || ids.isEmpty()) { - return null; - } - - String tag = (String) usesFramework.get("tag"); - File[] files = new File[ids.size()]; - int i = 0; - for (int id : ids) { - files[i++] = mAndRes.getFrameworkApk(id, tag); - } - return files; - } - - private boolean isModified(File working, File stored) { - if (! stored.exists()) { - return true; - } - return BrutIO.recursiveModifiedTime(working) > - BrutIO.recursiveModifiedTime(stored); - } - - private boolean isModified(File[] working, File[] stored) { - for (int i = 0; i < stored.length; i++) { - if (! stored[i].exists()) { - return true; - } - } - return BrutIO.recursiveModifiedTime(working) > - BrutIO.recursiveModifiedTime(stored); - } - - private File[] newFiles(String[] names, File dir) { - File[] files = new File[names.length]; - for (int i = 0; i < names.length; i++) { - files[i] = new File(dir, names[i]); - } - return files; - } - - public void setApkFile(File apkFile) { - mOrigApkFile = new ExtFile(apkFile); - } - - - private ExtFile mOrigApkFile = null; - - private final static Logger LOGGER = - Logger.getLogger(Androlib.class.getName()); - - private final static String SMALI_DIRNAME = "smali"; - private final static String APK_DIRNAME = "build/apk"; - private final static String[] APK_RESOURCES_FILENAMES = - new String[]{"resources.arsc", "AndroidManifest.xml", "res"}; - private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = - new String[]{"resources.arsc", "AndroidManifest.xml"}; - private final static String[] APP_RESOURCES_FILENAMES = - new String[]{"AndroidManifest.xml", "res"}; - private final static String[] APK_MANIFEST_FILENAMES = - new String[]{"AndroidManifest.xml"}; -} diff --git a/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java b/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java deleted file mode 100644 index e1ca29a4..00000000 --- a/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java +++ /dev/null @@ -1,275 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib; - -import brut.androlib.err.InFileNotFoundException; -import brut.androlib.err.OutDirExistsException; -import brut.androlib.res.AndrolibResources; -import brut.androlib.res.data.ResPackage; -import brut.androlib.res.data.ResTable; -import brut.androlib.res.util.ExtFile; -import brut.common.BrutException; -import brut.directory.DirectoryException; -import brut.util.OS; -import java.io.File; -import java.util.*; - -/** - * @author Ryszard Wiśniewski - */ -public class ApkDecoder { - public ApkDecoder() { - this(new Androlib()); - } - - public ApkDecoder(Androlib androlib) { - mAndrolib = androlib; - } - - public ApkDecoder(File apkFile) { - this(apkFile, new Androlib()); - } - - public ApkDecoder(File apkFile, Androlib androlib) { - mAndrolib = androlib; - setApkFile(apkFile); - } - - public void setApkFile(File apkFile) { - mApkFile = new ExtFile(apkFile); - mResTable = null; - } - - public void setOutDir(File outDir) throws AndrolibException { - mOutDir = outDir; - } - - public void decode() throws AndrolibException { - File outDir = getOutDir(); - - if (! mForceDelete && outDir.exists()) { - throw new OutDirExistsException(); - } - - if (! mApkFile.isFile() || ! mApkFile.canRead() ) { - throw new InFileNotFoundException(); - } - - try { - OS.rmdir(outDir); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - outDir.mkdirs(); - - if (hasSources()) { - switch (mDecodeSources) { - case DECODE_SOURCES_NONE: - mAndrolib.decodeSourcesRaw(mApkFile, outDir, mDebug); - break; - case DECODE_SOURCES_SMALI: - mAndrolib.decodeSourcesSmali(mApkFile, outDir, mDebug, mBakDeb); - break; - case DECODE_SOURCES_JAVA: - mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug); - break; - } - } - - if (hasResources()) { - switch (mDecodeResources) { - case DECODE_RESOURCES_NONE: - mAndrolib.decodeResourcesRaw(mApkFile, outDir); - break; - case DECODE_RESOURCES_FULL: - mAndrolib.decodeResourcesFull(mApkFile, outDir, - getResTable()); - break; - } - } else { - // if there's no resources.asrc, decode the manifest without looking up - // attribute references - if (hasManifest()) { - switch (mDecodeResources) { - case DECODE_RESOURCES_NONE: - mAndrolib.decodeManifestRaw(mApkFile, outDir); - break; - case DECODE_RESOURCES_FULL: - mAndrolib.decodeManifestFull(mApkFile, outDir, - getResTable()); - break; - } - } - } - - mAndrolib.decodeRawFiles(mApkFile, outDir); - writeMetaFile(); - } - - public void setDecodeSources(short mode) throws AndrolibException { - if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI - && mode != DECODE_SOURCES_JAVA) { - throw new AndrolibException("Invalid decode sources mode: " + mode); - } - mDecodeSources = mode; - } - - public void setDecodeResources(short mode) throws AndrolibException { - if (mode != DECODE_RESOURCES_NONE && mode != DECODE_RESOURCES_FULL) { - throw new AndrolibException("Invalid decode resources mode"); - } - mDecodeResources = mode; - } - - public void setDebugMode(boolean debug) { - mDebug = debug; - } - - public void setBaksmaliDebugMode(boolean bakdeb) { - mBakDeb = bakdeb; - } - - public void setForceDelete(boolean forceDelete) { - mForceDelete = forceDelete; - } - - public void setFrameworkTag(String tag) throws AndrolibException { - mFrameTag = tag; - if (mResTable != null) { - getResTable().setFrameTag(tag); - } - } - - public void setKeepBrokenResources(boolean keepBrokenResources) { - mKeepBrokenResources = keepBrokenResources; - } - - public ResTable getResTable() throws AndrolibException { - if (mResTable == null) { - boolean hasResources = hasResources(); - boolean hasManifest = hasManifest(); - if (! (hasManifest || hasResources)) { - throw new AndrolibException( - "Apk doesn't contain either AndroidManifest.xml file or resources.arsc file"); - } - AndrolibResources.sKeepBroken = mKeepBrokenResources; - mResTable = mAndrolib.getResTable(mApkFile, hasResources); - mResTable.setFrameTag(mFrameTag); - } - return mResTable; - } - - public boolean hasSources() throws AndrolibException { - try { - return mApkFile.getDirectory().containsFile("classes.dex"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public boolean hasManifest() throws AndrolibException { - try { - return mApkFile.getDirectory().containsFile("AndroidManifest.xml"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public boolean hasResources() throws AndrolibException { - try { - return mApkFile.getDirectory().containsFile("resources.arsc"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public final static short DECODE_SOURCES_NONE = 0x0000; - public final static short DECODE_SOURCES_SMALI = 0x0001; - public final static short DECODE_SOURCES_JAVA = 0x0002; - - public final static short DECODE_RESOURCES_NONE = 0x0100; - public final static short DECODE_RESOURCES_FULL = 0x0101; - - - private File getOutDir() throws AndrolibException { - if (mOutDir == null) { - throw new AndrolibException("Out dir not set"); - } - return mOutDir; - } - - private void writeMetaFile() throws AndrolibException { - Map meta = new LinkedHashMap(); - meta.put("version", Androlib.getVersion()); - meta.put("apkFileName", mApkFile.getName()); - - if (mDecodeResources != DECODE_RESOURCES_NONE && (hasManifest() || hasResources())) { - meta.put("isFrameworkApk", - Boolean.valueOf(mAndrolib.isFrameworkApk(getResTable()))); - putUsesFramework(meta); - putSdkInfo(meta); - } - - mAndrolib.writeMetaFile(mOutDir, meta); - } - - private void putUsesFramework(Map meta) - throws AndrolibException { - Set pkgs = getResTable().listFramePackages(); - if (pkgs.isEmpty()) { - return; - } - - Integer[] ids = new Integer[pkgs.size()]; - int i = 0; - for (ResPackage pkg : pkgs) { - ids[i++] = pkg.getId(); - } - Arrays.sort(ids); - - Map uses = new LinkedHashMap(); - uses.put("ids", ids); - - if (mFrameTag != null) { - uses.put("tag", mFrameTag); - } - - meta.put("usesFramework", uses); - } - - private void putSdkInfo(Map meta) - throws AndrolibException { - Map info = getResTable().getSdkInfo(); - if (info.size() > 0) { - meta.put("sdkInfo", info); - } - - } - - private final Androlib mAndrolib; - - private ExtFile mApkFile; - private File mOutDir; - private ResTable mResTable; - private short mDecodeSources = DECODE_SOURCES_SMALI; - private short mDecodeResources = DECODE_RESOURCES_FULL; - private boolean mDebug = false; - private boolean mForceDelete = false; - private String mFrameTag; - private boolean mKeepBrokenResources = false; - private boolean mBakDeb = true; -} diff --git a/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java b/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java deleted file mode 100644 index 00c13a06..00000000 --- a/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; -import java.util.logging.Logger; -import org.jf.baksmali.baksmali; -import org.jf.smali.main; - -/** - * @author Ryszard Wiśniewski - */ -public class ApktoolProperties { - public static String get(String key) { - return get().getProperty(key); - } - - public static Properties get() { - if (sProps == null) { - loadProps(); - } - return sProps; - } - - private static void loadProps() { - InputStream in = ApktoolProperties.class - .getResourceAsStream("apktool.properties"); - sProps = new Properties(); - try { - sProps.load(in); - in.close(); - } catch (IOException ex) { - LOGGER.warning("Can't load properties."); - } - - InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties"); - Properties properties = new Properties(); - String version = "(unknown)"; - try { - properties.load(templateStream); - version = properties.getProperty("application.version"); - } catch (IOException ex) { - } - sProps.put("baksmaliVersion", version); - templateStream = main.class.getClassLoader().getResourceAsStream("smali.properties"); - properties = new Properties(); - version = "(unknown)"; - try { - properties.load(templateStream); - version = properties.getProperty("application.version"); - } catch (IOException ex) { - } - sProps.put("smaliVersion", version); - } - - private static Properties sProps; - - private static final Logger LOGGER = - Logger.getLogger(ApktoolProperties.class.getName()); -} \ No newline at end of file diff --git a/apktool-lib/src/main/java/brut/androlib/err/CantFindFrameworkResException.java b/apktool-lib/src/main/java/brut/androlib/err/CantFindFrameworkResException.java deleted file mode 100644 index 0c0c10e3..00000000 --- a/apktool-lib/src/main/java/brut/androlib/err/CantFindFrameworkResException.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.err; - -import brut.androlib.AndrolibException; - -/** - * @author Ryszard Wiśniewski - */ -public class CantFindFrameworkResException extends AndrolibException { - - public CantFindFrameworkResException(Throwable cause, int id) { - super(cause); - mPkgId = id; - } - - public CantFindFrameworkResException(int id) { - mPkgId = id; - } - - public int getPkgId() { - return mPkgId; - } - - private final int mPkgId; -} diff --git a/apktool-lib/src/main/java/brut/androlib/err/OutDirExistsException.java b/apktool-lib/src/main/java/brut/androlib/err/OutDirExistsException.java deleted file mode 100644 index 59b26acd..00000000 --- a/apktool-lib/src/main/java/brut/androlib/err/OutDirExistsException.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.err; - -import brut.androlib.AndrolibException; - -/** - * @author Ryszard Wiśniewski - */ -public class OutDirExistsException extends AndrolibException { - - public OutDirExistsException(Throwable cause) { - super(cause); - } - - public OutDirExistsException(String message, Throwable cause) { - super(message, cause); - } - - public OutDirExistsException(String message) { - super(message); - } - - public OutDirExistsException() { - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/java/AndrolibJava.java b/apktool-lib/src/main/java/brut/androlib/java/AndrolibJava.java deleted file mode 100644 index f3be8f7e..00000000 --- a/apktool-lib/src/main/java/brut/androlib/java/AndrolibJava.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.java; - -import brut.androlib.res.util.ExtFile; -import java.io.File; - -/** - * @author Ryszard Wiśniewski - */ -public class AndrolibJava { - - public void decode(ExtFile apkFile, File outDir) { - throw new UnsupportedOperationException("Not yet implemented"); - } - - public void build(File javaDir, File dex) { - throw new UnsupportedOperationException("Not yet implemented"); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/mod/IndentingWriter.java b/apktool-lib/src/main/java/brut/androlib/mod/IndentingWriter.java deleted file mode 100644 index 99b1ce21..00000000 --- a/apktool-lib/src/main/java/brut/androlib/mod/IndentingWriter.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.mod; - -import java.io.Writer; - -/** - * @author Ryszard Wiśniewski - */ -public class IndentingWriter extends org.jf.util.IndentingWriter { - - public IndentingWriter(Writer writer) { - super(writer); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java b/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java deleted file mode 100644 index 8927026c..00000000 --- a/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.mod; - -import java.io.*; -import org.antlr.runtime.*; -import org.antlr.runtime.tree.CommonTree; -import org.antlr.runtime.tree.CommonTreeNodeStream; -import org.jf.dexlib.DexFile; -import org.jf.smali.*; - -/** - * @author Ryszard Wiśniewski - */ -public class SmaliMod { - - public static boolean assembleSmaliFile(InputStream smaliStream, - String name, DexFile dexFile, boolean verboseErrors, - boolean oldLexer, boolean printTokens) - throws IOException, RecognitionException { - CommonTokenStream tokens; - - - boolean lexerErrors = false; - LexerErrorInterface lexer; - - if (oldLexer) { - ANTLRInputStream input = new ANTLRInputStream(smaliStream, "UTF-8"); - input.name = name; - - lexer = new smaliLexer(input); - tokens = new CommonTokenStream((TokenSource)lexer); - } else { - InputStreamReader reader = - new InputStreamReader(smaliStream, "UTF-8"); - - lexer = new smaliFlexLexer(reader); - tokens = new CommonTokenStream((TokenSource)lexer); - } - - if (printTokens) { - tokens.getTokens(); - - for (int i=0; i 0 || lexer.getNumberOfSyntaxErrors() > 0) { - return false; - } - - CommonTree t = (CommonTree) result.getTree(); - - CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); - treeStream.setTokenStream(tokens); - - smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); - - dexGen.dexFile = dexFile; - dexGen.smali_file(); - - if (dexGen.getNumberOfSyntaxErrors() > 0) { - return false; - } - - return true; - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java b/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java deleted file mode 100644 index 06219ef8..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java +++ /dev/null @@ -1,583 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res; - -import brut.androlib.AndrolibException; -import brut.androlib.err.CantFindFrameworkResException; -import brut.androlib.res.data.*; -import brut.androlib.res.decoder.*; -import brut.androlib.res.decoder.ARSCDecoder.ARSCData; -import brut.androlib.res.decoder.ARSCDecoder.FlagsOffset; -import brut.androlib.res.util.*; -import brut.androlib.res.xml.ResValuesXmlSerializable; -import brut.common.BrutException; -import brut.directory.*; -import brut.util.*; -import java.io.*; -import java.util.*; -import java.util.logging.Logger; -import java.util.zip.*; -import org.apache.commons.io.IOUtils; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -final public class AndrolibResources { - public ResTable getResTable(ExtFile apkFile) throws AndrolibException { - return getResTable(apkFile, true); - } - - public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) throws AndrolibException { - ResTable resTable = new ResTable(this); - if (loadMainPkg) { - loadMainPkg(resTable, apkFile); - } - return resTable; - } - - public ResPackage loadMainPkg(ResTable resTable, ExtFile apkFile) - throws AndrolibException { - LOGGER.info("Loading resource table..."); - ResPackage[] pkgs = getResPackagesFromApk( - apkFile, resTable, sKeepBroken); - ResPackage pkg = null; - - switch (pkgs.length) { - case 1: - pkg = pkgs[0]; - break; - case 2: - if (pkgs[0].getName().equals("android")) { - LOGGER.warning("Skipping \"android\" package group"); - pkg = pkgs[1]; - } else if (pkgs[0].getName().equals("com.htc")) { - LOGGER.warning("Skipping \"htc\" stupid package group"); - pkg = pkgs[1]; - } - break; - } - - if (pkg == null) { - throw new AndrolibException( - "Arsc files with zero or multiple packages"); - } - - resTable.addPackage(pkg, true); - LOGGER.info("Loaded."); - return pkg; - } - - public ResPackage loadFrameworkPkg(ResTable resTable, int id, - String frameTag) throws AndrolibException { - File apk = getFrameworkApk(id, frameTag); - - LOGGER.info("Loading resource table from file: " + apk); - ResPackage[] pkgs = getResPackagesFromApk( - new ExtFile(apk), resTable, true); - - if (pkgs.length != 1) { - throw new AndrolibException( - "Arsc files with zero or multiple packages"); - } - - ResPackage pkg = pkgs[0]; - if (pkg.getId() != id) { - throw new AndrolibException("Expected pkg of id: " + - String.valueOf(id) + ", got: " + pkg.getId()); - } - - resTable.addPackage(pkg, false); - LOGGER.info("Loaded."); - return pkg; - } - - public void decodeManifest(ResTable resTable, ExtFile apkFile, File outDir) - throws AndrolibException { - - Duo duo = getManifestFileDecoder(); - ResFileDecoder fileDecoder = duo.m1; - - // Set ResAttrDecoder - duo.m2.setAttrDecoder(new ResAttrDecoder()); - ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder(); - - // Fake ResPackage - attrDecoder.setCurrentPackage(new ResPackage(resTable, 0, null)); - - Directory inApk, out; - try { - inApk = apkFile.getDirectory(); - out = new FileDirectory(outDir); - - LOGGER.info("Decoding AndroidManifest.xml with only framework resources..."); - fileDecoder.decodeManifest( - inApk, "AndroidManifest.xml", out, "AndroidManifest.xml"); - - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public void decode(ResTable resTable, ExtFile apkFile, File outDir) - throws AndrolibException { - Duo duo = getResFileDecoder(); - ResFileDecoder fileDecoder = duo.m1; - ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder(); - - attrDecoder.setCurrentPackage( - resTable.listMainPackages().iterator().next()); - - Directory inApk, in = null, out; - try { - inApk = apkFile.getDirectory(); - out = new FileDirectory(outDir); - - LOGGER.info("Decoding AndroidManifest.xml with resources..."); - - fileDecoder.decodeManifest( - inApk, "AndroidManifest.xml", out, "AndroidManifest.xml"); - - if (inApk.containsDir("res")) { - in = inApk.getDir("res"); - } - out = out.createDir("res"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - - ExtMXSerializer xmlSerializer = getResXmlSerializer(); - for (ResPackage pkg : resTable.listMainPackages()) { - attrDecoder.setCurrentPackage(pkg); - - LOGGER.info("Decoding file-resources..."); - for (ResResource res : pkg.listFiles()) { - fileDecoder.decode(res, in, out); - } - - LOGGER.info("Decoding values */* XMLs..."); - for (ResValuesFile valuesFile : pkg.listValuesFiles()) { - generateValuesFile(valuesFile, out, xmlSerializer); - } - generatePublicXml(pkg, out, xmlSerializer); - LOGGER.info("Done."); - } - - AndrolibException decodeError = duo.m2.getFirstError(); - if (decodeError != null) { - throw decodeError; - } - } - - public void setSdkInfo(Map map) { - if(map != null) { - mMinSdkVersion = map.get("minSdkVersion"); - mTargetSdkVersion = map.get("targetSdkVersion"); - mMaxSdkVersion = map.get("maxSdkVersion"); - } - } - - public void aaptPackage(File apkFile, File manifest, File resDir, - File rawDir, File assetDir, File[] include, HashMap flags) - throws AndrolibException { - List cmd = new ArrayList(); - - cmd.add("aapt"); - cmd.add("p"); - - if (flags.get("verbose")) { - cmd.add("-v"); - } - - if (flags.get("update")) { - cmd.add("-u"); - } - if (mMinSdkVersion != null) { - cmd.add("--min-sdk-version"); - cmd.add(mMinSdkVersion); - } - if (mTargetSdkVersion != null) { - cmd.add("--target-sdk-version"); - cmd.add(mTargetSdkVersion); - } - if (mMaxSdkVersion != null) { - cmd.add("--max-sdk-version"); - cmd.add(mMaxSdkVersion); - } - cmd.add("-F"); - cmd.add(apkFile.getAbsolutePath()); - - if (flags.get("framework")) { - cmd.add("-x"); -// cmd.add("-0"); -// cmd.add("arsc"); - } - - if (include != null) { - for (File file : include) { - cmd.add("-I"); - cmd.add(file.getPath()); - } - } - if (resDir != null) { - cmd.add("-S"); - cmd.add(resDir.getAbsolutePath()); - } - if (manifest != null) { - cmd.add("-M"); - cmd.add(manifest.getAbsolutePath()); - } - if (assetDir != null) { - cmd.add("-A"); - cmd.add(assetDir.getAbsolutePath()); - } - if (rawDir != null) { - cmd.add(rawDir.getAbsolutePath()); - } - - try { - OS.exec(cmd.toArray(new String[0])); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } - - public boolean detectWhetherAppIsFramework(File appDir) - throws AndrolibException { - File publicXml = new File(appDir, "res/values/public.xml"); - if (! publicXml.exists()) { - return false; - } - - Iterator it; - try { - it = IOUtils.lineIterator( - new FileReader(new File(appDir, "res/values/public.xml"))); - } catch (FileNotFoundException ex) { - throw new AndrolibException( - "Could not detect whether app is framework one", ex); - } - it.next(); - it.next(); - return it.next().contains("0x01"); - } - - public void tagSmaliResIDs(ResTable resTable, File smaliDir) - throws AndrolibException { - new ResSmaliUpdater().tagResIDs(resTable, smaliDir); - } - - public void updateSmaliResIDs(ResTable resTable, File smaliDir) throws AndrolibException { - new ResSmaliUpdater().updateResIDs(resTable, smaliDir); - } - - public Duo getResFileDecoder() { - ResStreamDecoderContainer decoders = - new ResStreamDecoderContainer(); - decoders.setDecoder("raw", new ResRawStreamDecoder()); - decoders.setDecoder("9patch", new Res9patchStreamDecoder()); - - AXmlResourceParser axmlParser = new AXmlResourceParser(); - axmlParser.setAttrDecoder(new ResAttrDecoder()); - decoders.setDecoder("xml", - new XmlPullStreamDecoder(axmlParser, getResXmlSerializer())); - - return new Duo( - new ResFileDecoder(decoders), axmlParser); - } - - public Duo getManifestFileDecoder() { - ResStreamDecoderContainer decoders = - new ResStreamDecoderContainer(); - - AXmlResourceParser axmlParser = new AXmlResourceParser(); - - decoders.setDecoder("xml", - new XmlPullStreamDecoder(axmlParser, getResXmlSerializer())); - - return new Duo( - new ResFileDecoder(decoders), axmlParser); - } - - public ExtMXSerializer getResXmlSerializer() { - ExtMXSerializer serial = new ExtMXSerializer(); - serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION - , " "); - serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR, - System.getProperty("line.separator")); - serial.setProperty(ExtMXSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8"); - serial.setDisabledAttrEscape(true); - return serial; - } - - private void generateValuesFile(ResValuesFile valuesFile, Directory out, - ExtXmlSerializer serial) throws AndrolibException { - try { - OutputStream outStream = out.getFileOutput(valuesFile.getPath()); - serial.setOutput((outStream), null); - serial.startDocument(null, null); - serial.startTag(null, "resources"); - - for (ResResource res : valuesFile.listResources()) { - if (valuesFile.isSynthesized(res)) { - continue; - } - ((ResValuesXmlSerializable) res.getValue()) - .serializeToResValuesXml(serial, res); - } - - serial.endTag(null, "resources"); - serial.newLine(); - serial.endDocument(); - serial.flush(); - outStream.close(); - } catch (IOException ex) { - throw new AndrolibException( - "Could not generate: " + valuesFile.getPath(), ex); - } catch (DirectoryException ex) { - throw new AndrolibException( - "Could not generate: " + valuesFile.getPath(), ex); - } - } - - private void generatePublicXml(ResPackage pkg, Directory out, - XmlSerializer serial) throws AndrolibException { -// try { -// OutputStream outStream = out.getFileOutput("values/public.xml"); -// serial.setOutput(outStream, null); -// serial.startDocument(null, null); -// serial.startTag(null, "resources"); -// -// for (ResResSpec spec : pkg.listResSpecs()) { -// serial.startTag(null, "public"); -// serial.attribute(null, "type", spec.getType().getName()); -// serial.attribute(null, "name", spec.getName()); -// serial.attribute(null, "id", String.format( -// "0x%08x", spec.getId().id)); -// serial.endTag(null, "public"); -// } -// -// serial.endTag(null, "resources"); -// serial.endDocument(); -// serial.flush(); -// outStream.close(); -// } catch (IOException ex) { -// throw new AndrolibException( -// "Could not generate public.xml file", ex); -// } catch (DirectoryException ex) { -// throw new AndrolibException( -// "Could not generate public.xml file", ex); -// } - } - - private ResPackage[] getResPackagesFromApk(ExtFile apkFile, - ResTable resTable, boolean keepBroken) throws AndrolibException { - try { - return ARSCDecoder.decode( - apkFile.getDirectory().getFileInput("resources.arsc"), false, - keepBroken, resTable).getPackages(); - } catch (DirectoryException ex) { - throw new AndrolibException( - "Could not load resources.arsc from file: " + apkFile, ex); - } - } - - public File getFrameworkApk(int id, String frameTag) - throws AndrolibException { - File dir = getFrameworkDir(); - File apk; - - if (frameTag != null) { - apk = new File(dir, String.valueOf(id) + '-' + frameTag + ".apk"); - if (apk.exists()) { - return apk; - } - } - - apk = new File(dir, String.valueOf(id) + ".apk"); - if (apk.exists()) { - return apk; - } - - if (id == 1) { - InputStream in = null; - OutputStream out = null; - try { - in = AndrolibResources.class.getResourceAsStream( - "/brut/androlib/android-framework.jar"); - out = new FileOutputStream(apk); - IOUtils.copy(in, out); - return apk; - } catch (IOException ex) { - throw new AndrolibException(ex); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) {} - } - if (out != null) { - try { - out.close(); - } catch (IOException ex) {} - } - } - } - - throw new CantFindFrameworkResException(id); - } - - public void installFramework(File frameFile, String tag) - throws AndrolibException { - InputStream in = null; - ZipOutputStream out = null; - try { - ZipFile zip = new ZipFile(frameFile); - ZipEntry entry = zip.getEntry("resources.arsc"); - - if (entry == null) { - throw new AndrolibException("Can't find resources.arsc file"); - } - - in = zip.getInputStream(entry); - byte[] data = IOUtils.toByteArray(in); - - ARSCData arsc = ARSCDecoder.decode( - new ByteArrayInputStream(data), true, true); - publicizeResources(data, arsc.getFlagsOffsets()); - - File outFile = new File(getFrameworkDir(), - String.valueOf(arsc.getOnePackage().getId()) + - (tag == null ? "" : '-' + tag) + ".apk"); - - out = new ZipOutputStream(new FileOutputStream(outFile)); - out.setMethod(ZipOutputStream.STORED); - CRC32 crc = new CRC32(); - crc.update(data); - entry = new ZipEntry("resources.arsc"); - entry.setSize(data.length); - entry.setCrc(crc.getValue()); - out.putNextEntry(entry); - out.write(data); - - LOGGER.info("Framework installed to: " + outFile); - } catch (ZipException ex) { - throw new AndrolibException(ex); - } catch (IOException ex) { - throw new AndrolibException(ex); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) {} - } - if (out != null) { - try { - out.close(); - } catch (IOException ex) {} - } - } - } - - public void publicizeResources(File arscFile) throws AndrolibException { - byte[] data = new byte[(int) arscFile.length()]; - - InputStream in = null; - OutputStream out = null; - try { - in = new FileInputStream(arscFile); - in.read(data); - - publicizeResources(data); - - out = new FileOutputStream(arscFile); - out.write(data); - } catch (IOException ex) { - throw new AndrolibException(ex); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) {} - } - if (out != null) { - try { - out.close(); - } catch (IOException ex) {} - } - } - } - - public void publicizeResources(byte[] arsc) throws AndrolibException { - publicizeResources(arsc, - ARSCDecoder.decode(new ByteArrayInputStream(arsc), true, true) - .getFlagsOffsets()); - } - - public void publicizeResources(byte[] arsc, FlagsOffset[] flagsOffsets) - throws AndrolibException { - for (FlagsOffset flags : flagsOffsets) { - int offset = flags.offset + 3; - int end = offset + 4 * flags.count; - while(offset < end) { - arsc[offset] |= (byte) 0x40; - offset += 4; - } - } - } - - private File getFrameworkDir() throws AndrolibException { - String path; - - /* store in user-home, for Mac OS X */ - if (System.getProperty("os.name").equals("Mac OS X")) { - path = System.getProperty("user.home") + File.separatorChar + - "Library/Application Support/apktool/framework"; } - else { - path = System.getProperty("user.home") + File.separatorChar + - "apktool" + File.separatorChar + "framework"; - } - File dir = new File(path); - if (! dir.exists()) { - if (! dir.mkdirs()) { - throw new AndrolibException("Can't create directory: " + dir); - } - } - return dir; - } - - public File getAndroidResourcesFile() throws AndrolibException { - try { - return Jar.getResourceAsFile("/brut/androlib/android-framework.jar"); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } - - - // TODO: dirty static hack. I have to refactor decoding mechanisms. - public static boolean sKeepBroken = false; - - - private final static Logger LOGGER = - Logger.getLogger(AndrolibResources.class.getName()); - - private String mMinSdkVersion = null; - private String mMaxSdkVersion = null; - private String mTargetSdkVersion = null; - -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java b/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java deleted file mode 100644 index d2cd8eb5..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res; - -import brut.androlib.AndrolibException; -import brut.androlib.err.UndefinedResObject; -import brut.androlib.res.data.ResResSpec; -import brut.androlib.res.data.ResTable; -import brut.directory.Directory; -import brut.directory.DirectoryException; -import brut.directory.FileDirectory; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Iterator; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; - -/** - * @author Ryszard Wiśniewski - */ -public class ResSmaliUpdater { - public void tagResIDs(ResTable resTable, File smaliDir) - throws AndrolibException { - Directory dir = null; - try { - dir = new FileDirectory(smaliDir); - } catch (DirectoryException ex) { - throw new AndrolibException( - "Could not tag res IDs", ex); - } - for (String fileName : dir.getFiles(true)) { - try { - tagResIdsForFile(resTable, dir, fileName); - } catch (IOException ex) { - throw new AndrolibException( - "Could not tag resIDs for file: " + fileName, ex); - } catch (DirectoryException ex) { - throw new AndrolibException( - "Could not tag resIDs for file: " + fileName, ex); - } catch (AndrolibException ex) { - throw new AndrolibException( - "Could not tag resIDs for file: " + fileName, ex); - } - } - } - - public void updateResIDs(ResTable resTable, File smaliDir) - throws AndrolibException { - try { - Directory dir = new FileDirectory(smaliDir); - for (String fileName : dir.getFiles(true)) { - Iterator it = - IOUtils.readLines(dir.getFileInput(fileName)).iterator(); - PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); - while (it.hasNext()) { - String line = it.next(); - out.println(line); - Matcher m1 = RES_NAME_PATTERN.matcher(line); - if (! m1.matches()) { - continue; - } - Matcher m2 = RES_ID_PATTERN.matcher(it.next()); - if (! m2.matches()) { - throw new AndrolibException(); - } - int resID = resTable.getPackage(m1.group(1)) - .getType(m1.group(2)).getResSpec(m1.group(3)) - .getId().id; - if (m2.group(1) != null) { - out.println(String.format( - RES_ID_FORMAT_FIELD, m2.group(1), resID)); - } else { - out.println(String.format( - RES_ID_FORMAT_CONST, m2.group(2), resID)); - } - } - out.close(); - } - } catch (IOException ex) { - throw new AndrolibException( - "Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex); - } catch (DirectoryException ex) { - throw new AndrolibException( - "Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex); - } - } - - private void tagResIdsForFile(ResTable resTable, Directory dir, - String fileName) throws IOException, DirectoryException, - AndrolibException { - Iterator it = - IOUtils.readLines(dir.getFileInput(fileName)).iterator(); - PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); - while (it.hasNext()) { - String line = it.next(); - if (RES_NAME_PATTERN.matcher(line).matches()) { - out.println(line); - out.println(it.next()); - continue; - } - Matcher m = RES_ID_PATTERN.matcher(line); - if (m.matches()) { - int resID = parseResID(m.group(3)); - if (resID != -1) { - try { - ResResSpec spec = resTable.getResSpec(resID); - out.println(String.format( - RES_NAME_FORMAT, spec.getFullName())); - } catch (UndefinedResObject ex) { - if (! R_FILE_PATTERN.matcher(fileName).matches()) { - LOGGER.warning(String.format( - "Undefined resource spec in %s: 0x%08x" - , fileName, resID)); - } - } - } - } - out.println(line); - } - out.close(); - } - - private int parseResID(String resIDHex) { - if (resIDHex.endsWith("ff")) { - return -1; - } - int resID = Integer.valueOf(resIDHex, 16); - if (resIDHex.length() == 4) { - resID = resID << 16; - } - return resID; - } - - private final static String RES_ID_FORMAT_FIELD = - ".field %s:I = 0x%08x"; - private final static String RES_ID_FORMAT_CONST = - " const %s, 0x%08x"; - private final static Pattern RES_ID_PATTERN = Pattern.compile( - "^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$"); - private final static String RES_NAME_FORMAT = - "# APKTOOL/RES_NAME: %s"; - private final static Pattern RES_NAME_PATTERN = Pattern.compile( - "^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$"); - - private final static Pattern R_FILE_PATTERN = Pattern.compile( - ".*R\\$[a-z]+\\.smali$"); - - private final static Logger LOGGER = - Logger.getLogger(ResSmaliUpdater.class.getName()); -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java b/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java deleted file mode 100644 index 917ec504..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data; - -import brut.androlib.AndrolibException; -import brut.androlib.err.UndefinedResObject; -import java.util.*; - -/** - * @author Ryszard Wiśniewski - */ -public class ResConfig { - private final ResConfigFlags mFlags; - private final Map mResources = - new LinkedHashMap(); - - public ResConfig(ResConfigFlags flags) { - this.mFlags = flags; - } - - public Set listResources() { - return new LinkedHashSet(mResources.values()); - } - - public ResResource getResource(ResResSpec spec) throws AndrolibException { - ResResource res = mResources.get(spec); - if (res == null) { - throw new UndefinedResObject(String.format( - "resource: spec=%s, config=%s", spec, this)); - } - return res; - } - - public Set listResSpecs() { - return mResources.keySet(); - } - - public ResConfigFlags getFlags() { - return mFlags; - } - - public void addResource(ResResource res) - throws AndrolibException { - addResource(res, false); - } - - public void addResource(ResResource res, boolean overwrite) - throws AndrolibException { - ResResSpec spec = res.getResSpec(); - if (mResources.put(spec, res) != null && ! overwrite) { - throw new AndrolibException(String.format( - "Multiple resources: spec=%s, config=%s", spec, this)); - } - } - - @Override - public String toString() { - return mFlags.toString(); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java b/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java deleted file mode 100644 index 751564fd..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java +++ /dev/null @@ -1,443 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data; - -import java.util.logging.Logger; - -/** - * @author Ryszard Wiśniewski - */ -public class ResConfigFlags { - public final short mcc; - public final short mnc; - - public final char[] language; - public final char[] country; - - public final byte orientation; - public final byte touchscreen; - public final short density; - - public final byte keyboard; - public final byte navigation; - public final byte inputFlags; - - public final short screenWidth; - public final short screenHeight; - - public final short sdkVersion; - - public final byte screenLayout; - public final byte uiMode; - public final short smallestScreenWidthDp; - - public final short screenWidthDp; - public final short screenHeightDp; - - public final boolean isInvalid; - - private final String mQualifiers; - - public ResConfigFlags() { - mcc = 0; - mnc = 0; - language = new char[]{'\00', '\00'}; - country = new char[]{'\00', '\00'}; - orientation = ORIENTATION_ANY; - touchscreen = TOUCHSCREEN_ANY; - density = DENSITY_DEFAULT; - keyboard = KEYBOARD_ANY; - navigation = NAVIGATION_ANY; - inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY; - screenWidth = 0; - screenHeight = 0; - sdkVersion = 0; - screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY; - uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY; - smallestScreenWidthDp = 0; - screenWidthDp = 0; - screenHeightDp = 0; - isInvalid = false; - mQualifiers = ""; - } - - public ResConfigFlags(short mcc, short mnc, char[] language, char[] country, - byte orientation, byte touchscreen, short density, byte keyboard, - byte navigation, byte inputFlags, short screenWidth, - short screenHeight, short sdkVersion, byte screenLayout, - byte uiMode, short smallestScreenWidthDp, short screenWidthDp, - short screenHeightDp, boolean isInvalid) { - if (orientation < 0 || orientation > 3) { - LOGGER.warning("Invalid orientation value: " + orientation); - orientation = 0; - isInvalid = true; - } - if (touchscreen < 0 || touchscreen > 3) { - LOGGER.warning("Invalid touchscreen value: " + touchscreen); - touchscreen = 0; - isInvalid = true; - } - if (density < -1) { - LOGGER.warning("Invalid density value: " + density); - density = 0; - isInvalid = true; - } - if (keyboard < 0 || keyboard > 3) { - LOGGER.warning("Invalid keyboard value: " + keyboard); - keyboard = 0; - isInvalid = true; - } - if (navigation < 0 || navigation > 4) { - LOGGER.warning("Invalid navigation value: " + navigation); - navigation = 0; - isInvalid = true; - } - - this.mcc = mcc; - this.mnc = mnc; - this.language = language; - this.country = country; - this.orientation = orientation; - this.touchscreen = touchscreen; - this.density = density; - this.keyboard = keyboard; - this.navigation = navigation; - this.inputFlags = inputFlags; - this.screenWidth = screenWidth; - this.screenHeight = screenHeight; - this.sdkVersion = sdkVersion; - this.screenLayout = screenLayout; - this.uiMode = uiMode; - this.smallestScreenWidthDp = smallestScreenWidthDp; - this.screenWidthDp = screenWidthDp; - this.screenHeightDp = screenHeightDp; - this.isInvalid = isInvalid; - mQualifiers = generateQualifiers(); - } - - public String getQualifiers() { - return mQualifiers; - } - - private String generateQualifiers() { - StringBuilder ret = new StringBuilder(); - if (mcc != 0) { - ret.append("-mcc").append(String.format("%03d", mcc)); - if (mnc != 0) { - ret.append("-mnc").append(mnc); - } - } - if (language[0] != '\00') { - ret.append('-').append(language); - if (country[0] != '\00') { - ret.append("-r").append(country); - } - } - if (smallestScreenWidthDp != 0) { - ret.append("-sw").append(smallestScreenWidthDp).append("dp"); - } - if (screenWidthDp != 0) { - ret.append("-w").append(screenWidthDp).append("dp"); - } - if (screenHeightDp != 0) { - ret.append("-h").append(screenHeightDp).append("dp"); - } - switch (screenLayout & MASK_SCREENSIZE) { - case SCREENSIZE_SMALL: - ret.append("-small"); - break; - case SCREENSIZE_NORMAL: - ret.append("-normal"); - break; - case SCREENSIZE_LARGE: - ret.append("-large"); - break; - case SCREENSIZE_XLARGE: - ret.append("-xlarge"); - break; - } - switch (screenLayout & MASK_SCREENLONG) { - case SCREENLONG_YES: - ret.append("-long"); - break; - case SCREENLONG_NO: - ret.append("-notlong"); - break; - } - switch (orientation) { - case ORIENTATION_PORT: - ret.append("-port"); - break; - case ORIENTATION_LAND: - ret.append("-land"); - break; - case ORIENTATION_SQUARE: - ret.append("-square"); - break; - } - switch (uiMode & MASK_UI_MODE_TYPE) { - case UI_MODE_TYPE_CAR: - ret.append("-car"); - break; - case UI_MODE_TYPE_DESK: - ret.append("-desk"); - break; - case UI_MODE_TYPE_TELEVISION: - ret.append("-television"); - break; - case UI_MODE_TYPE_SMALLUI: - ret.append("-smallui"); - break; - case UI_MODE_TYPE_MEDIUMUI: - ret.append("-mediumui"); - break; - case UI_MODE_TYPE_LARGEUI: - ret.append("-largeui"); - break; - case UI_MODE_TYPE_HUGEUI: - ret.append("-hugeui"); - break; - case UI_MODE_TYPE_APPLIANCE: - ret.append("-appliance"); - break; - } - switch (uiMode & MASK_UI_MODE_NIGHT) { - case UI_MODE_NIGHT_YES: - ret.append("-night"); - break; - case UI_MODE_NIGHT_NO: - ret.append("-notnight"); - break; - } - switch (density) { - case DENSITY_DEFAULT: - break; - case DENSITY_LOW: - ret.append("-ldpi"); - break; - case DENSITY_MEDIUM: - ret.append("-mdpi"); - break; - case DENSITY_HIGH: - ret.append("-hdpi"); - break; - case DENSITY_TV: - ret.append("-tvdpi"); - break; - case DENSITY_XHIGH: - ret.append("-xhdpi"); - break; - case DENSITY_XXHIGH: - ret.append("-xxhdpi"); - break; - case DENSITY_NONE: - ret.append("-nodpi"); - break; - default: - ret.append('-').append(density).append("dpi"); - } - switch (touchscreen) { - case TOUCHSCREEN_NOTOUCH: - ret.append("-notouch"); - break; - case TOUCHSCREEN_STYLUS: - ret.append("-stylus"); - break; - case TOUCHSCREEN_FINGER: - ret.append("-finger"); - break; - } - switch (inputFlags & MASK_KEYSHIDDEN) { - case KEYSHIDDEN_NO: - ret.append("-keysexposed"); - break; - case KEYSHIDDEN_YES: - ret.append("-keyshidden"); - break; - case KEYSHIDDEN_SOFT: - ret.append("-keyssoft"); - break; - } - switch (keyboard) { - case KEYBOARD_NOKEYS: - ret.append("-nokeys"); - break; - case KEYBOARD_QWERTY: - ret.append("-qwerty"); - break; - case KEYBOARD_12KEY: - ret.append("-12key"); - break; - } - switch (inputFlags & MASK_NAVHIDDEN) { - case NAVHIDDEN_NO: - ret.append("-navexposed"); - break; - case NAVHIDDEN_YES: - ret.append("-navhidden"); - break; - } - switch (navigation) { - case NAVIGATION_NONAV: - ret.append("-nonav"); - break; - case NAVIGATION_DPAD: - ret.append("-dpad"); - break; - case NAVIGATION_TRACKBALL: - ret.append("-trackball"); - break; - case NAVIGATION_WHEEL: - ret.append("-wheel"); - break; - } - if (screenWidth != 0 && screenHeight != 0) { - if (screenWidth > screenHeight) { - ret.append(String.format("-%dx%d", screenWidth, screenHeight)); - } else { - ret.append(String.format("-%dx%d", screenHeight, screenWidth)); - } - } - if (sdkVersion > getNaturalSdkVersionRequirement()) { - ret.append("-v").append(sdkVersion); - } - if (isInvalid) { - ret.append("-ERR" + sErrCounter++); - } - - return ret.toString(); - } - - private short getNaturalSdkVersionRequirement() { - if (smallestScreenWidthDp != 0 || screenWidthDp != 0 - || screenHeightDp != 0) { - return 13; - } - if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != 0) { - return 8; - } - if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != 0 - || density != DENSITY_DEFAULT) { - return 4; - } - return 0; - } - - @Override - public String toString() { - return ! getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]"; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResConfigFlags other = (ResConfigFlags) obj; - return this.mQualifiers.equals(other.mQualifiers); - } - - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + this.mQualifiers.hashCode(); - return hash; - } - - - // TODO: Dirty static hack. This counter should be a part of ResPackage, - // but it would be hard right now and this feature is very rarely used. - private static int sErrCounter = 0; - - - public final static byte ORIENTATION_ANY = 0; - public final static byte ORIENTATION_PORT = 1; - public final static byte ORIENTATION_LAND = 2; - public final static byte ORIENTATION_SQUARE = 3; - - public final static byte TOUCHSCREEN_ANY = 0; - public final static byte TOUCHSCREEN_NOTOUCH = 1; - public final static byte TOUCHSCREEN_STYLUS = 2; - public final static byte TOUCHSCREEN_FINGER = 3; - - public final static short DENSITY_DEFAULT = 0; - public final static short DENSITY_LOW = 120; - public final static short DENSITY_MEDIUM = 160; - public final static short DENSITY_TV = 213; - public final static short DENSITY_HIGH = 240; - public final static short DENSITY_XHIGH = 320; - public final static short DENSITY_XXHIGH = 480; - public final static short DENSITY_NONE = -1; - - public final static byte KEYBOARD_ANY = 0; - public final static byte KEYBOARD_NOKEYS = 1; - public final static byte KEYBOARD_QWERTY = 2; - public final static byte KEYBOARD_12KEY = 3; - - public final static byte NAVIGATION_ANY = 0; - public final static byte NAVIGATION_NONAV = 1; - public final static byte NAVIGATION_DPAD = 2; - public final static byte NAVIGATION_TRACKBALL = 3; - public final static byte NAVIGATION_WHEEL = 4; - - public final static byte MASK_KEYSHIDDEN = 0x3; - public final static byte KEYSHIDDEN_ANY = 0x0; - public final static byte KEYSHIDDEN_NO = 0x1; - public final static byte KEYSHIDDEN_YES = 0x2; - public final static byte KEYSHIDDEN_SOFT = 0x3; - - public final static byte MASK_NAVHIDDEN = 0xc; - public final static byte NAVHIDDEN_ANY = 0x0; - public final static byte NAVHIDDEN_NO = 0x4; - public final static byte NAVHIDDEN_YES = 0x8; - - public final static byte MASK_SCREENSIZE = 0x0f; - public final static byte SCREENSIZE_ANY = 0x00; - public final static byte SCREENSIZE_SMALL = 0x01; - public final static byte SCREENSIZE_NORMAL = 0x02; - public final static byte SCREENSIZE_LARGE = 0x03; - public final static byte SCREENSIZE_XLARGE = 0x04; - - public final static byte MASK_SCREENLONG = 0x30; - public final static byte SCREENLONG_ANY = 0x00; - public final static byte SCREENLONG_NO = 0x10; - public final static byte SCREENLONG_YES = 0x20; - - public final static byte MASK_UI_MODE_TYPE = 0x0f; - public final static byte UI_MODE_TYPE_ANY = 0x00; - public final static byte UI_MODE_TYPE_NORMAL = 0x01; - public final static byte UI_MODE_TYPE_DESK = 0x02; - public final static byte UI_MODE_TYPE_CAR = 0x03; - public final static byte UI_MODE_TYPE_TELEVISION = 0x04; - public final static byte UI_MODE_TYPE_APPLIANCE = 0x05; - public final static byte UI_MODE_TYPE_SMALLUI = 0x0c; - public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d; - public final static byte UI_MODE_TYPE_LARGEUI = 0x0e; - public final static byte UI_MODE_TYPE_HUGEUI = 0x0f; - - public final static byte MASK_UI_MODE_NIGHT = 0x30; - public final static byte UI_MODE_NIGHT_ANY = 0x00; - public final static byte UI_MODE_NIGHT_NO = 0x10; - public final static byte UI_MODE_NIGHT_YES = 0x20; - - - private static final Logger LOGGER = - Logger.getLogger(ResConfigFlags.class.getName()); -} \ No newline at end of file diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java b/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java deleted file mode 100644 index 2fee2bc2..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java +++ /dev/null @@ -1,220 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data; - -import brut.androlib.AndrolibException; -import brut.androlib.err.UndefinedResObject; -import brut.androlib.res.data.value.ResFileValue; -import brut.androlib.res.data.value.ResValueFactory; -import brut.androlib.res.xml.ResValuesXmlSerializable; -import brut.util.Duo; -import java.util.*; - -/** - * @author Ryszard Wiśniewski - */ -public class ResPackage { - private final ResTable mResTable; - private final int mId; - private final String mName; - private final Map mResSpecs = - new LinkedHashMap(); - private final Map mConfigs = - new LinkedHashMap(); - private final Map mTypes = - new LinkedHashMap(); - private final Set mSynthesizedRes = new HashSet(); - - private ResValueFactory mValueFactory; - - public ResPackage(ResTable resTable, int id, String name) { - this.mResTable = resTable; - this.mId = id; - this.mName = name; - } - - public List listResSpecs() { - return new ArrayList(mResSpecs.values()); - } - - public boolean hasResSpec(ResID resID) { - return mResSpecs.containsKey(resID); - } - - public ResResSpec getResSpec(ResID resID) throws UndefinedResObject { - ResResSpec spec = mResSpecs.get(resID); - if (spec == null) { - throw new UndefinedResObject("resource spec: " + resID.toString()); - } - return spec; - } - - public List getConfigs() { - return new ArrayList(mConfigs.values()); - } - - public boolean hasConfig(ResConfigFlags flags) { - return mConfigs.containsKey(flags); - } - - public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException { - ResConfig config = mConfigs.get(flags); - if (config == null) { - throw new UndefinedResObject("config: " + flags); - } - return config; - } - - public ResConfig getOrCreateConfig(ResConfigFlags flags) - throws AndrolibException { - ResConfig config = mConfigs.get(flags); - if (config == null) { - config = new ResConfig(flags); - mConfigs.put(flags, config); - } - return config; - } - - public List listTypes() { - return new ArrayList(mTypes.values()); - } - - public boolean hasType(String typeName) { - return mTypes.containsKey(typeName); - } - - public ResType getType(String typeName) throws AndrolibException { - ResType type = mTypes.get(typeName); - if (type == null) { - throw new UndefinedResObject("type: " + typeName); - } - return type; - } - - public Set listFiles() { - Set ret = new HashSet(); - for (ResResSpec spec : mResSpecs.values()) { - for (ResResource res : spec.listResources()) { - if (res.getValue() instanceof ResFileValue) { - ret.add(res); - } - } - } - return ret; - } - - public Collection listValuesFiles() { - Map, ResValuesFile> ret = - new HashMap, ResValuesFile>(); - for (ResResSpec spec : mResSpecs.values()) { - for (ResResource res : spec.listResources()) { - if (res.getValue() instanceof ResValuesXmlSerializable) { - ResType type = res.getResSpec().getType(); - ResConfig config = res.getConfig(); - Duo key = - new Duo(type, config); - ResValuesFile values = ret.get(key); - if (values == null) { - values = new ResValuesFile(this, type, config); - ret.put(key, values); - } - values.addResource(res); - } - } - } - return ret.values(); - } - - public ResTable getResTable() { - return mResTable; - } - - public int getId() { - return mId; - } - - public String getName() { - return mName; - } - - boolean isSynthesized(ResID resId) { - return mSynthesizedRes.contains(resId); - } - - public void addResSpec(ResResSpec spec) throws AndrolibException { - if (mResSpecs.put(spec.getId(), spec) != null) { - throw new AndrolibException("Multiple resource specs: " + spec); - } - } - - public void addConfig(ResConfig config) throws AndrolibException { - if (mConfigs.put(config.getFlags(), config) != null) { - throw new AndrolibException("Multiple configs: " + config); - } - } - - public void addType(ResType type) throws AndrolibException { - if (mTypes.put(type.getName(), type) != null) { - throw new AndrolibException("Multiple types: " + type); - } - } - - public void addResource(ResResource res) { - } - - public void addSynthesizedRes(int resId) { - mSynthesizedRes.add(new ResID(resId)); - } - - @Override - public String toString() { - return mName; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResPackage other = (ResPackage) obj; - if (this.mResTable != other.mResTable && (this.mResTable == null || !this.mResTable.equals(other.mResTable))) { - return false; - } - if (this.mId != other.mId) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + (this.mResTable != null ? this.mResTable.hashCode() : 0); - hash = 31 * hash + this.mId; - return hash; - } - - public ResValueFactory getValueFactory() { - if (mValueFactory == null) { - mValueFactory = new ResValueFactory(this); - } - return mValueFactory; - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java b/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java deleted file mode 100755 index f39f561e..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data; - -import brut.androlib.AndrolibException; -import brut.androlib.err.UndefinedResObject; -import java.util.*; -import org.apache.commons.lang3.StringUtils; - -/** - * @author Ryszard Wiśniewski - */ -public class ResResSpec { - private final ResID mId; - private final String mName; - private final ResPackage mPackage; - private final ResType mType; - private final Map mResources = - new LinkedHashMap(); - - public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) { - this.mId = id; - this.mName = name; - this.mPackage = pkg; - this.mType = type; - } - - public Set listResources() { - return new LinkedHashSet(mResources.values()); - } - - public ResResource getResource(ResConfig config) throws AndrolibException { - return getResource(config.getFlags()); - } - - public ResResource getResource(ResConfigFlags config) - throws AndrolibException { - ResResource res = mResources.get(config); - if (res == null) { - throw new UndefinedResObject(String.format( - "resource: spec=%s, config=%s", this, config)); - } - return res; - } - - public boolean hasResource(ResConfig config) { - return hasResource(config.getFlags()); - } - - private boolean hasResource(ResConfigFlags flags) { - return mResources.containsKey(flags); - } - - public ResResource getDefaultResource() throws AndrolibException { - return getResource(new ResConfigFlags()); - } - - public boolean hasDefaultResource() { - return mResources.containsKey(new ResConfigFlags()); - } - - public String getFullName() { - return getFullName(false, false); - } - - public String getFullName(ResPackage relativeToPackage, - boolean excludeType) { - return getFullName( - getPackage().equals(relativeToPackage), excludeType); - } - - public String getFullName(boolean excludePackage, boolean excludeType) { - return - (excludePackage ? "" : getPackage().getName() + ":") + - (excludeType ? "" : getType().getName() + "/") + getName(); - } - - public ResID getId() { - return mId; - } - - public String getName() { - return StringUtils.replace(mName, "\"", "q"); - } - - public ResPackage getPackage() { - return mPackage; - } - - public ResType getType() { - return mType; - } - - public void addResource(ResResource res) - throws AndrolibException { - addResource(res, false); - } - - public void addResource(ResResource res, boolean overwrite) - throws AndrolibException { - ResConfigFlags flags = res.getConfig().getFlags(); - if (mResources.put(flags, res) != null && ! overwrite) { - throw new AndrolibException(String.format("Multiple resources: spec=%s, config=%s", this, flags)); - } - } - - @Override - public String toString() { - return mId.toString() + " " + mType.toString() + "/" + mName; - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java b/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java deleted file mode 100644 index 2351b3c8..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.value.ResValue; - -/** - * @author Ryszard Wiśniewski - */ -public class ResResource { - private final ResConfig mConfig; - private final ResResSpec mResSpec; - private final ResValue mValue; - - public ResResource(ResConfig config, ResResSpec spec, - ResValue value) { - this.mConfig = config; - this.mResSpec = spec; - this.mValue = value; - } - - public String getFilePath() { - return mResSpec.getType().getName() + - mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName(); - } - - public ResConfig getConfig() { - return mConfig; - } - - public ResResSpec getResSpec() { - return mResSpec; - } - - public ResValue getValue() { - return mValue; - } - - public void replace(ResValue value) throws AndrolibException { - ResResource res = new ResResource(mConfig, mResSpec, value); - mConfig.addResource(res, true); - mResSpec.addResource(res, true); - } - - @Override - public String toString() { - return getFilePath(); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java b/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java deleted file mode 100644 index 58949285..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data; - -import brut.androlib.AndrolibException; -import brut.androlib.err.UndefinedResObject; -import brut.androlib.res.AndrolibResources; -import brut.androlib.res.data.value.ResValue; -import java.util.*; - -/** - * @author Ryszard Wiśniewski - */ -public class ResTable { - private final AndrolibResources mAndRes; - - private final Map mPackagesById = - new HashMap(); - private final Map mPackagesByName = - new HashMap(); - private final Set mMainPackages = - new LinkedHashSet(); - private final Set mFramePackages = - new LinkedHashSet(); - - private String mFrameTag; - - private Map mSdkInfo = new LinkedHashMap(); - - public ResTable() { - mAndRes = null; - } - - public ResTable(AndrolibResources andRes) { - mAndRes = andRes; - } - - public ResResSpec getResSpec(int resID) throws AndrolibException { - return getResSpec(new ResID(resID)); - } - - public ResResSpec getResSpec(ResID resID) throws AndrolibException { - return getPackage(resID.package_).getResSpec(resID); - } - - public Set listMainPackages() { - return mMainPackages; - } - - public Set listFramePackages() { - return mFramePackages; - } - - public ResPackage getPackage(int id) throws AndrolibException { - ResPackage pkg = mPackagesById.get(id); - if (pkg != null) { - return pkg; - } - if (mAndRes != null) { - return mAndRes.loadFrameworkPkg(this, id, mFrameTag); - } - throw new UndefinedResObject(String.format("package: id=%d", id)); - } - - public ResPackage getPackage(String name) throws AndrolibException { - ResPackage pkg = mPackagesByName.get(name); - if (pkg == null) { - throw new UndefinedResObject("package: name=" + name); - } - return pkg; - } - - public boolean hasPackage(int id) { - return mPackagesById.containsKey(id); - } - - public boolean hasPackage(String name) { - return mPackagesByName.containsKey(name); - } - - public ResValue getValue(String package_, String type, String name) - throws AndrolibException { - return getPackage(package_).getType(type).getResSpec(name) - .getDefaultResource().getValue(); - } - - public void addPackage(ResPackage pkg, boolean main) - throws AndrolibException { - Integer id = pkg.getId(); - if (mPackagesById.containsKey(id)) { - throw new AndrolibException( - "Multiple packages: id=" + id.toString()); - } - String name = pkg.getName(); - if (mPackagesByName.containsKey(name)) { - throw new AndrolibException("Multiple packages: name=" + name); - } - - mPackagesById.put(id, pkg); - mPackagesByName.put(name, pkg); - if (main) { - mMainPackages.add(pkg); - } else { - mFramePackages.add(pkg); - } - } - - public void setFrameTag(String tag) { - mFrameTag = tag; - } - - public Map getSdkInfo() { - return mSdkInfo; - } - - public void addSdkInfo(String key, String value) { - mSdkInfo.put(key, value); - } - - public void clearSdkInfo() { - mSdkInfo.clear(); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java b/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java deleted file mode 100644 index c73b7f8f..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data; - -import brut.androlib.AndrolibException; -import brut.androlib.err.UndefinedResObject; -import java.util.*; - -/** - * @author Ryszard Wiśniewski - */ -public final class ResType { - private final String mName; - private final Map mResSpecs = - new LinkedHashMap(); - - private final ResTable mResTable; - private final ResPackage mPackage; - - public ResType(String name, ResTable resTable, - ResPackage package_) { - this.mName = name; - this.mResTable = resTable; - this.mPackage = package_; - } - - public String getName() { - return mName; - } - - public Set listResSpecs() { - return new LinkedHashSet(mResSpecs.values()); - } - - public ResResSpec getResSpec(String name) throws AndrolibException { - ResResSpec spec = mResSpecs.get(name); - if (spec == null) { - throw new UndefinedResObject(String.format( - "resource spec: %s/%s", getName(), name)); - } - return spec; - } - - public void addResSpec(ResResSpec spec) - throws AndrolibException { - if (mResSpecs.put(spec.getName(), spec) != null) { - throw new AndrolibException(String.format( - "Multiple res specs: %s/%s", getName(), spec.getName())); - } - } - - @Override - public String toString() { - return mName; - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java b/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java deleted file mode 100644 index 0b7946a8..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data; - -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * @author Ryszard Wiśniewski - */ -public class ResValuesFile { - private final ResPackage mPackage; - private final ResType mType; - private final ResConfig mConfig; - private final Set mResources = - new LinkedHashSet(); - - public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) { - this.mPackage = pkg; - this.mType = type; - this.mConfig = config; - } - - public String getPath() { - return "values" + mConfig.getFlags().getQualifiers() - + "/" + mType.getName() - + (mType.getName().endsWith("s") ? "" : "s") - + ".xml"; - } - - public Set listResources() { - return mResources; - } - - public ResType getType() { - return mType; - } - - public ResConfig getConfig() { - return mConfig; - } - - public boolean isSynthesized(ResResource res) { - return mPackage.isSynthesized(res.getResSpec().getId()); - } - - public void addResource(ResResource res) { - mResources.add(res); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResValuesFile other = (ResValuesFile) obj; - if (this.mType != other.mType && (this.mType == null || !this.mType.equals(other.mType))) { - return false; - } - if (this.mConfig != other.mConfig && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0); - hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0); - return hash; - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java deleted file mode 100644 index 595e286f..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResResource; -import brut.androlib.res.xml.ResValuesXmlSerializable; -import brut.util.Duo; -import java.io.IOException; -import org.xmlpull.v1.XmlSerializer; -import org.apache.commons.lang3.StringUtils; - -/** - * @author Ryszard Wiśniewski - */ -public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializable { - private String mRawItems; - ResArrayValue(ResReferenceValue parent, - Duo[] items) { - super(parent); - - mItems = new ResScalarValue[items.length]; - for (int i = 0; i < items.length; i++) { - mItems[i] = items[i].m2; - } - } - - public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) { - super(parent); - mItems = items; - } - - @Override - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException { - String type = getType(); - type = (type == null ? "" : type + "-") + "array"; - - serializer.startTag(null, type); - serializer.attribute(null, "name", res.getResSpec().getName()); - for (int i = 0; i < mItems.length; i++) { - serializer.startTag(null, "item"); - serializer.text(mItems[i].encodeAsResXmlItemValue()); - serializer.endTag(null, "item"); - } - serializer.endTag(null, type); - } - - public String getType() throws AndrolibException { - if (mItems.length == 0) { - return null; - } - String type = mItems[0].getType(); - for (int i = 1; i < mItems.length; i++) { - - if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) { - return "string"; - } else if (mItems[i].encodeAsResXmlItemValue().startsWith("@drawable")) { - return null; - } else if (!"string".equals(type) && !"integer".equals(type)) { - return null; - } else if (!type.equals(mItems[i].getType())) { - return null; - } - } - return type; - } - - private final ResScalarValue[] mItems; - - - public static final int BAG_KEY_ARRAY_START = 0x02000000; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java deleted file mode 100644 index 24282d31..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResPackage; -import brut.androlib.res.data.ResResource; -import brut.androlib.res.xml.ResValuesXmlSerializable; -import brut.util.Duo; -import java.io.IOException; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public class ResAttr extends ResBagValue implements ResValuesXmlSerializable { - ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, - Boolean l10n) { - super(parentVal); - mType = type; - mMin = min; - mMax = max; - mL10n = l10n; - } - - public String convertToResXmlFormat(ResScalarValue value) - throws AndrolibException { - return null; - } - - @Override - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException { - String type = getTypeAsString(); - - serializer.startTag(null, "attr"); - serializer.attribute(null, "name", res.getResSpec().getName()); - if (type != null) { - serializer.attribute(null, "format", type); - } - if (mMin != null) { - serializer.attribute(null, "min", mMin.toString()); - } - if (mMax != null) { - serializer.attribute(null, "max", mMax.toString()); - } - if (mL10n != null && mL10n) { - serializer.attribute(null, "localization", "suggested"); - } - serializeBody(serializer, res); - serializer.endTag(null, "attr"); - } - - - public static ResAttr factory(ResReferenceValue parent, - Duo[] items, ResValueFactory factory, - ResPackage pkg) throws AndrolibException { - - int type = ((ResIntValue) items[0].m2).getValue(); - int scalarType = type & 0xffff; - Integer min = null, max = null; - Boolean l10n = null; - int i; - for (i = 1; i < items.length; i++) { - switch (items[i].m1) { - case BAG_KEY_ATTR_MIN: - min = ((ResIntValue) items[i].m2).getValue(); - continue; - case BAG_KEY_ATTR_MAX: - max = ((ResIntValue) items[i].m2).getValue(); - continue; - case BAG_KEY_ATTR_L10N: - l10n = ((ResIntValue) items[i].m2).getValue() != 0; - continue; - } - break; - } - - if (i == items.length) { - return new ResAttr(parent, scalarType, min, max, l10n); - } - Duo[] attrItems = - new Duo[items.length - i]; - int j = 0; - for (; i < items.length; i++) { - int resId = items[i].m1; - pkg.addSynthesizedRes(resId); - attrItems[j++] = new Duo( - factory.newReference(resId, null), (ResIntValue) items[i].m2); - } - switch (type & 0xff0000) { - case TYPE_ENUM: - return new ResEnumAttr( - parent, scalarType, min, max, l10n, attrItems); - case TYPE_FLAGS: - return new ResFlagsAttr( - parent, scalarType, min, max, l10n, attrItems); - } - - throw new AndrolibException("Could not decode attr value"); - } - - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException {} - - protected String getTypeAsString() { - String s = ""; - if ((mType & TYPE_REFERENCE) != 0) { - s += "|reference"; - } - if ((mType & TYPE_STRING) != 0) { - s += "|string"; - } - if ((mType & TYPE_INT) != 0) { - s += "|integer"; - } - if ((mType & TYPE_BOOL) != 0) { - s += "|boolean"; - } - if ((mType & TYPE_COLOR) != 0) { - s += "|color"; - } - if ((mType & TYPE_FLOAT) != 0) { - s += "|float"; - } - if ((mType & TYPE_DIMEN) != 0) { - s += "|dimension"; - } - if ((mType & TYPE_FRACTION) != 0) { - s += "|fraction"; - } - if (s.isEmpty()) { - return null; - } - return s.substring(1); - } - - private final int mType; - private final Integer mMin; - private final Integer mMax; - private final Boolean mL10n; - - - public static final int BAG_KEY_ATTR_TYPE = 0x01000000; - private static final int BAG_KEY_ATTR_MIN = 0x01000001; - private static final int BAG_KEY_ATTR_MAX = 0x01000002; - private static final int BAG_KEY_ATTR_L10N = 0x01000003; - - private final static int TYPE_REFERENCE = 0x01; - private final static int TYPE_STRING = 0x02; - private final static int TYPE_INT = 0x04; - private final static int TYPE_BOOL = 0x08; - private final static int TYPE_COLOR = 0x10; - private final static int TYPE_FLOAT = 0x20; - private final static int TYPE_DIMEN = 0x40; - private final static int TYPE_FRACTION = 0x80; - private final static int TYPE_ANY_STRING = 0xee; - - private static final int TYPE_ENUM = 0x00010000; - private static final int TYPE_FLAGS = 0x00020000; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java deleted file mode 100644 index f8bdeed8..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResResource; -import brut.androlib.res.xml.ResValuesXmlSerializable; -import brut.util.Duo; -import java.io.IOException; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public class ResBagValue extends ResValue implements ResValuesXmlSerializable { - protected final ResReferenceValue mParent; - - public ResBagValue(ResReferenceValue parent) { - this.mParent = parent; - } - - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException { - String type = res.getResSpec().getType().getName(); - if ("style".equals(type)) { - new ResStyleValue(mParent, new Duo[0], null) - .serializeToResValuesXml(serializer, res); - return; - } - if ("array".equals(type)) { - new ResArrayValue(mParent, new Duo[0]) - .serializeToResValuesXml(serializer, res); - return; - } - if ("plurals".equals(type)) { - new ResPluralsValue(mParent, new Duo[0]) - .serializeToResValuesXml(serializer, res); - return; - } - - serializer.startTag(null, "item"); - serializer.attribute(null, "type", type); - serializer.attribute(null, "name", res.getResSpec().getName()); - serializer.endTag(null, "item"); - } - - public ResReferenceValue getParent() { - return mParent; - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java deleted file mode 100644 index 807196fe..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -/** - * @author Ryszard Wiśniewski - */ -public class ResBoolValue extends ResScalarValue { - private final boolean mValue; - - public ResBoolValue(boolean value, String rawValue) { - super("bool", rawValue); - this.mValue = value; - } - - public boolean getValue() { - return mValue; - } - - protected String encodeAsResXml() { - return mValue ? "true" : "false"; - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java deleted file mode 100644 index 224186f3..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -/** - * @author Ryszard Wiśniewski - */ -public class ResColorValue extends ResIntValue { - public ResColorValue(int value, String rawValue) { - super(value, rawValue, "color"); - } - - @Override - protected String encodeAsResXml() { - return String.format("#%08x", mValue); - } -} \ No newline at end of file diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java deleted file mode 100644 index d00a6077..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import android.util.TypedValue; -import brut.androlib.AndrolibException; - -/** - * @author Ryszard Wiśniewski - */ -public class ResDimenValue extends ResIntValue { - public ResDimenValue(int value, String rawValue) { - super(value, rawValue, "dimen"); - } - - @Override - protected String encodeAsResXml() throws AndrolibException { - return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java deleted file mode 100644 index bf550d3f..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResResource; -import brut.util.Duo; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public class ResEnumAttr extends ResAttr { - ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max, - Boolean l10n, Duo[] items) { - super(parent, type, min, max, l10n); - mItems = items; - } - - @Override - public String convertToResXmlFormat(ResScalarValue value) - throws AndrolibException { - if (value instanceof ResIntValue) { - String ret = decodeValue(((ResIntValue) value).getValue()); - if (ret != null) { - return ret; - } - } - return super.convertToResXmlFormat(value); - } - - @Override - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException { - for (Duo duo : mItems) { - int intVal = duo.m2.getValue(); - - serializer.startTag(null, "enum"); - serializer.attribute(null, "name", duo.m1.getReferent().getName()); - serializer.attribute(null, "value", String.valueOf(intVal)); - serializer.endTag(null, "enum"); - } - } - - private String decodeValue(int value) throws AndrolibException { - String value2 = mItemsCache.get(value); - if (value2 == null) { - ResReferenceValue ref = null; - for (Duo duo : mItems) { - if (duo.m2.getValue() == value) { - ref = duo.m1; - break; - } - } - if (ref != null) { - value2 = ref.getReferent().getName(); - mItemsCache.put(value, value2); - } - } - return value2; - } - - - private final Duo[] mItems; - private final Map mItemsCache = - new HashMap(); -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java deleted file mode 100644 index e6ddff64..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; - -/** - * @author Ryszard Wiśniewski - */ -public class ResFileValue extends ResValue { - private final String mPath; - - public ResFileValue(String path) { - this.mPath = path; - } - - public String getPath() { - return mPath; - } - - public String getStrippedPath() throws AndrolibException { - if (! mPath.startsWith("res/")) { - throw new AndrolibException( - "File path does not start with \"res/\": " + mPath); - } - return mPath.substring(4); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java deleted file mode 100644 index 7761562c..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResResource; -import brut.util.Duo; -import java.io.IOException; -import java.util.Arrays; -import java.util.Comparator; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public class ResFlagsAttr extends ResAttr { - ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, Boolean l10n, Duo[] items) { - super(parent, type, min, max, l10n); - - mItems = new FlagItem[items.length]; - for (int i = 0; i < items.length; i++) { - mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue()); - } - } - - @Override - public String convertToResXmlFormat(ResScalarValue value) - throws AndrolibException { - if (! (value instanceof ResIntValue)) { - return super.convertToResXmlFormat(value); - } - loadFlags(); - int intVal = ((ResIntValue) value).getValue(); - - if (intVal == 0) { - return renderFlags(mZeroFlags); - } - - FlagItem[] flagItems = new FlagItem[mFlags.length]; - int[] flags = new int[mFlags.length]; - int flagsCount = 0; - for (int i = 0; i < mFlags.length; i++) { - FlagItem flagItem = mFlags[i]; - int flag = flagItem.flag; - - if ((intVal & flag) != flag) { - continue; - } - - if (! isSubpartOf(flag, flags)) { - flags[flagsCount] = flag; - flagItems[flagsCount++] = flagItem; - } - } - return renderFlags(Arrays.copyOf(flagItems, flagsCount)); - } - - @Override - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException { - for (int i = 0; i < mItems.length; i++) { - FlagItem item = mItems[i]; - - serializer.startTag(null, "flag"); - serializer.attribute(null, "name", item.getValue()); - serializer.attribute(null, "value", - String.format("0x%08x", item.flag)); - serializer.endTag(null, "flag"); - } - } - - private boolean isSubpartOf(int flag, int[] flags) { - for (int i = 0; i < flags.length; i++) { - if ((flags[i] & flag) == flag) { - return true; - } - } - return false; - } - - private String renderFlags(FlagItem[] flags) throws AndrolibException { - String ret = ""; - for (int i = 0; i < flags.length; i++) { - ret += "|" + flags[i].getValue(); - } - if (ret.isEmpty()) { - return ret; - } - return ret.substring(1); - } - - private void loadFlags() { - if (mFlags != null) { - return; - } - - FlagItem[] zeroFlags = new FlagItem[mItems.length]; - int zeroFlagsCount = 0; - FlagItem[] flags = new FlagItem[mItems.length]; - int flagsCount = 0; - - for (int i = 0; i < mItems.length; i++) { - FlagItem item = mItems[i]; - if (item.flag == 0) { - zeroFlags[zeroFlagsCount++] = item; - } else { - flags[flagsCount++] = item; - } - } - - mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount); - mFlags = Arrays.copyOf(flags, flagsCount); - - Arrays.sort(mFlags, new Comparator() { - public int compare(FlagItem o1, FlagItem o2) { - return Integer.valueOf(Integer.bitCount(o2.flag)) - .compareTo(Integer.bitCount(o1.flag)); - } - }); - } - - - private final FlagItem[] mItems; - - private FlagItem[] mZeroFlags; - private FlagItem[] mFlags; - - - private static class FlagItem { - public final ResReferenceValue ref; - public final int flag; - public String value; - - public FlagItem(ResReferenceValue ref, int flag) { - this.ref = ref; - this.flag = flag; - } - - public String getValue() throws AndrolibException { - if (value == null) { - value = ref.getReferent().getName(); - } - return value; - } - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java deleted file mode 100644 index bbd77219..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -/** - * @author Ryszard Wiśniewski - */ -public class ResFloatValue extends ResScalarValue { - private final float mValue; - - public ResFloatValue(float value, String rawValue) { - super("float", rawValue); - this.mValue = value; - } - - public float getValue() { - return mValue; - } - - protected String encodeAsResXml() { - return String.valueOf(mValue); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java deleted file mode 100644 index c3325834..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import android.util.TypedValue; -import brut.androlib.AndrolibException; - -/** - * @author Ryszard Wiśniewski - */ -public class ResFractionValue extends ResIntValue { - public ResFractionValue(int value, String rawValue) { - super(value, rawValue, "fraction"); - } - - @Override - protected String encodeAsResXml() throws AndrolibException { - return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java deleted file mode 100644 index cb08ae89..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResResource; -import brut.androlib.res.xml.ResValuesXmlSerializable; -import java.io.IOException; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public class ResIdValue extends ResValue implements ResValuesXmlSerializable { - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) throws IOException, AndrolibException { - serializer.startTag(null, "item"); - serializer.attribute(null, "type", res.getResSpec().getType().getName()); - serializer.attribute(null, "name", res.getResSpec().getName()); - serializer.endTag(null, "item"); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java deleted file mode 100644 index d1139995..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import android.util.TypedValue; -import brut.androlib.AndrolibException; - -/** - * @author Ryszard Wiśniewski - */ -public class ResIntValue extends ResScalarValue { - protected final int mValue; - private int type; - - public ResIntValue(int value, String rawValue, int type) { - this(value, rawValue, "integer"); - this.type = type; - } - - public ResIntValue(int value, String rawValue, String type) { - super(type, rawValue); - this.mValue = value; - } - - public int getValue() { - return mValue; - } - - protected String encodeAsResXml() throws AndrolibException { - return TypedValue.coerceToString(type, mValue); - } -} \ No newline at end of file diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java deleted file mode 100644 index b5902924..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResResource; -import brut.androlib.res.xml.ResValuesXmlSerializable; -import brut.androlib.res.xml.ResXmlEncoders; -import brut.util.Duo; -import java.io.IOException; -import org.apache.commons.lang3.StringUtils; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializable { - ResPluralsValue(ResReferenceValue parent, - Duo[] items) { - super(parent); - - mItems = new ResScalarValue[6]; - for (int i = 0; i < items.length; i++) { - mItems[items[i].m1 - BAG_KEY_PLURALS_START] = - (ResScalarValue) items[i].m2; - } - } - - @Override - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException { - serializer.startTag(null, "plurals"); - serializer.attribute(null, "name", res.getResSpec().getName()); - for (int i = 0; i < mItems.length; i++) { - ResScalarValue item = mItems[i]; - if (item == null) { - continue; - } - - ResScalarValue rawValue = item; - - serializer.startTag(null, "item"); - serializer.attribute(null, "quantity", QUANTITY_MAP[i]); - if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue.encodeAsResXmlValue())) { - serializer.text(item.encodeAsResXmlValueExt()); - } else { - String recode = item.encodeAsResXmlValue(); - //Dirty, but working fix @miuirussia - for (int j = 0; j < 10; j++) { - recode = StringUtils.replace(recode, "%" + Integer.toString(j) + "$" + Integer.toString(j) + "$", "%" + Integer.toString(j) + "$"); - } - serializer.text(recode); - } - serializer.endTag(null, "item"); - } - serializer.endTag(null, "plurals"); - } - - - private final ResScalarValue[] mItems; - - - public static final int BAG_KEY_PLURALS_START = 0x01000004; - public static final int BAG_KEY_PLURALS_END = 0x01000009; - private static final String[] QUANTITY_MAP = - new String[] {"other", "zero", "one", "two", "few", "many"}; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java deleted file mode 100644 index d626e039..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResPackage; -import brut.androlib.res.data.ResResSpec; - -/** - * @author Ryszard Wiśniewski - */ -public class ResReferenceValue extends ResIntValue { - private final ResPackage mPackage; - private final boolean mTheme; - - public ResReferenceValue(ResPackage package_, int value, String rawValue) { - this(package_, value, rawValue, false); - } - - public ResReferenceValue(ResPackage package_, int value, String rawValue, - boolean theme) { - super(value, rawValue, "reference"); - mPackage = package_; - mTheme = theme; - } - - protected String encodeAsResXml() throws AndrolibException { - if (isNull()) { - return "@null"; - } - - ResResSpec spec = getReferent(); - boolean newId = - spec.hasDefaultResource() && - spec.getDefaultResource().getValue() instanceof ResIdValue; - - /* generate the beginning to fix @android */ - String mStart = (mTheme ? '?' : '@') + (newId ? "+" : ""); - // mStart = mStart.replace("@android", "@*android"); - - /* now dump back */ - return mStart + - spec.getFullName(mPackage, - mTheme && spec.getType().getName().equals("attr")); - } - - public ResResSpec getReferent() throws AndrolibException { - return mPackage.getResTable().getResSpec(getValue()); - } - - public boolean isNull() { - return mValue == 0; - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java deleted file mode 100644 index 9569d88d..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResResource; -import brut.androlib.res.xml.ResValuesXmlSerializable; -import brut.androlib.res.xml.ResXmlEncodable; -import brut.androlib.res.xml.ResXmlEncoders; -import java.io.IOException; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public abstract class ResScalarValue extends ResValue - implements ResXmlEncodable, ResValuesXmlSerializable { - protected final String mType; - protected final String mRawValue; - - protected ResScalarValue(String type, String rawValue) { - mType = type; - mRawValue = rawValue; - } - - public String encodeAsResXmlAttr() throws AndrolibException { - if (mRawValue != null) { - return mRawValue; - } - return encodeAsResXml().replace("@android:", "@*android:"); - } - - public String encodeAsResXmlItemValue() throws AndrolibException { - return encodeAsResXmlValue().replace("@android:", "@*android:"); - } - - public String encodeAsResXmlValue() throws AndrolibException { - if (mRawValue != null) { - return mRawValue; - } - return encodeAsResXmlValueExt().replace("@android:", "@*android:"); - } - - public String encodeAsResXmlValueExt() throws AndrolibException { - String rawValue = mRawValue; - if (rawValue != null) { - if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) { - int count = 1; - StringBuilder result = new StringBuilder(); - String tmp1[] = rawValue.split("%%", -1); - int tmp1_sz = tmp1.length; - for(int i=0;i - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResResource; -import brut.androlib.res.xml.ResXmlEncoders; -import java.io.IOException; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public class ResStringValue extends ResScalarValue { - - public ResStringValue(String value) { - this(value, "string"); - } - - public ResStringValue(String value, String type) { - super(type, value); - } - - @Override - public String encodeAsResXmlAttr() { - return ResXmlEncoders.encodeAsResXmlAttr(mRawValue); - } - - @Override - public String encodeAsResXmlItemValue() { - return ResXmlEncoders.enumerateNonPositionalSubstitutions( - ResXmlEncoders.encodeAsXmlValue(mRawValue)); - } - - @Override - public String encodeAsResXmlValue() { - return ResXmlEncoders.encodeAsXmlValue(mRawValue); - } - - @Override - protected String encodeAsResXml() throws AndrolibException { - throw new UnsupportedOperationException(); - } - - @Override - protected void serializeExtraXmlAttrs(XmlSerializer serializer, - ResResource res) throws IOException { - if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(mRawValue)) { - serializer.attribute(null, "formatted", "false"); - } - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java deleted file mode 100644 index 99331f95..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResResSpec; -import brut.androlib.res.data.ResResource; -import brut.androlib.res.xml.ResValuesXmlSerializable; -import brut.util.Duo; -import java.io.IOException; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializable { - ResStyleValue(ResReferenceValue parent, - Duo[] items, ResValueFactory factory) { - super(parent); - - mItems = new Duo[items.length]; - for (int i = 0; i < items.length; i++) { - mItems[i] = new Duo( - factory.newReference(items[i].m1, null), items[i].m2); - } - } - - @Override - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException { - serializer.startTag(null, "style"); - serializer.attribute(null, "name", res.getResSpec().getName()); - if (! mParent.isNull()) { - serializer.attribute(null, "parent", mParent.encodeAsResXmlAttr()); - } - for (int i = 0; i < mItems.length; i++) { - ResResSpec spec = mItems[i].m1.getReferent(); - ResAttr attr = (ResAttr) spec.getDefaultResource().getValue(); - String value = attr.convertToResXmlFormat(mItems[i].m2); - - if (value == null) { - value = mItems[i].m2.encodeAsResXmlValue(); - } - - if (value == null) { - continue; - } - - serializer.startTag(null, "item"); - serializer.attribute(null, "name", - spec.getFullName(res.getResSpec().getPackage(), true)); - serializer.text(value); - serializer.endTag(null, "item"); - } - serializer.endTag(null, "style"); - } - - - private final Duo[] mItems; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java deleted file mode 100644 index fd07436d..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValue.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -/** - * @author Ryszard Wiśniewski - */ -public class ResValue { - -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java b/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java deleted file mode 100644 index a123002a..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.data.value; - -import android.util.TypedValue; -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResPackage; -import brut.util.Duo; - -/** - * @author Ryszard Wiśniewski - */ -public class ResValueFactory { - private final ResPackage mPackage; - - public ResValueFactory(ResPackage pakage_) { - this.mPackage = pakage_; - } - - public ResScalarValue factory(int type, int value, String rawValue) - throws AndrolibException { - switch (type) { - case TypedValue.TYPE_REFERENCE: - return newReference(value, rawValue); - case TypedValue.TYPE_ATTRIBUTE: - return newReference(value, rawValue, true); - case TypedValue.TYPE_STRING: - return new ResStringValue(rawValue); - case TypedValue.TYPE_FLOAT: - return new ResFloatValue(Float.intBitsToFloat(value), rawValue); - case TypedValue.TYPE_DIMENSION: - return new ResDimenValue(value, rawValue); - case TypedValue.TYPE_FRACTION: - return new ResFractionValue(value, rawValue); - case TypedValue.TYPE_INT_BOOLEAN: - return new ResBoolValue(value != 0, rawValue); - } - - if (type >= TypedValue.TYPE_FIRST_COLOR_INT - && type <= TypedValue.TYPE_LAST_COLOR_INT) { - return new ResColorValue(value, rawValue); - } - if (type >= TypedValue.TYPE_FIRST_INT - && type <= TypedValue.TYPE_LAST_INT) { - return new ResIntValue(value, rawValue, type); - } - - throw new AndrolibException("Invalid value type: "+ type); - } - - public ResValue factory(String value) { - if (value.startsWith("res/")) { - return new ResFileValue(value); - } - return new ResStringValue(value); - } - - public ResBagValue bagFactory(int parent, - Duo[] items) throws AndrolibException { - ResReferenceValue parentVal = newReference(parent, null); - - if (items.length == 0) { - return new ResBagValue(parentVal); - } - int key = items[0].m1; - if (key == ResAttr.BAG_KEY_ATTR_TYPE) { - return ResAttr.factory(parentVal, items, this, mPackage); - } - if (key == ResArrayValue.BAG_KEY_ARRAY_START) { - return new ResArrayValue(parentVal, items); - } - if (key >= ResPluralsValue.BAG_KEY_PLURALS_START - && key <= ResPluralsValue.BAG_KEY_PLURALS_END) { - return new ResPluralsValue(parentVal, items); - } - return new ResStyleValue(parentVal, items, this); - } - - public ResReferenceValue newReference(int resID, String rawValue) { - return newReference(resID, rawValue, false); - } - - public ResReferenceValue newReference(int resID, String rawValue, - boolean theme) { - return new ResReferenceValue(mPackage, resID, rawValue, theme); - } -} \ No newline at end of file diff --git a/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java b/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java deleted file mode 100644 index cc5bf6dd..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java +++ /dev/null @@ -1,440 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.decoder; - -import android.util.TypedValue; -import brut.androlib.AndrolibException; -import brut.androlib.res.data.*; -import brut.androlib.res.data.value.*; -import brut.util.Duo; -import brut.androlib.res.data.ResTable; -import brut.util.ExtDataInput; -import com.mindprod.ledatastream.LEDataInputStream; -import java.io.*; -import java.math.BigInteger; -import java.util.*; -import java.util.logging.Logger; -import org.apache.commons.io.input.CountingInputStream; - -/** - * @author Ryszard Wiśniewski - */ -public class ARSCDecoder { - public static ARSCData decode(InputStream arscStream, - boolean findFlagsOffsets, boolean keepBroken) - throws AndrolibException { - return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable()); - } - - public static ARSCData decode(InputStream arscStream, - boolean findFlagsOffsets, boolean keepBroken, ResTable resTable) - throws AndrolibException { - try { - ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable, - findFlagsOffsets, keepBroken); - ResPackage[] pkgs = decoder.readTable(); - return new ARSCData( - pkgs, - decoder.mFlagsOffsets == null ? null : - decoder.mFlagsOffsets.toArray(new FlagsOffset[0]), - resTable); - } catch (IOException ex) { - throw new AndrolibException("Could not decode arsc file", ex); - } - } - - private ARSCDecoder(InputStream arscStream, ResTable resTable, - boolean storeFlagsOffsets, boolean keepBroken) { - if (storeFlagsOffsets) { - arscStream = mCountIn = new CountingInputStream(arscStream); - mFlagsOffsets = new ArrayList(); - } else { - mCountIn = null; - mFlagsOffsets = null; - } - mIn = new ExtDataInput(new LEDataInputStream(arscStream)); - mResTable = resTable; - mKeepBroken = keepBroken; - } - - private ResPackage[] readTable() throws IOException, AndrolibException { - nextChunkCheckType(Header.TYPE_TABLE); - int packageCount = mIn.readInt(); - - mTableStrings = StringBlock.read(mIn); - ResPackage[] packages = new ResPackage[packageCount]; - - nextChunk(); - for (int i = 0; i < packageCount; i++) { - packages[i] = readPackage(); - } - - return packages; - } - - private ResPackage readPackage() throws IOException, AndrolibException { - checkChunkType(Header.TYPE_PACKAGE); - int id = (byte) mIn.readInt(); - String name = mIn.readNulEndedString(128, true); - /*typeNameStrings*/ mIn.skipInt(); - /*typeNameCount*/ mIn.skipInt(); - /*specNameStrings*/ mIn.skipInt(); - /*specNameCount*/ mIn.skipInt(); - - mTypeNames = StringBlock.read(mIn); - mSpecNames = StringBlock.read(mIn); - - mResId = id << 24; - mPkg = new ResPackage(mResTable, id, name); - - nextChunk(); - while (mHeader.type == Header.TYPE_TYPE) { - readType(); - } - - return mPkg; - } - - private ResType readType() throws AndrolibException, IOException { - checkChunkType(Header.TYPE_TYPE); - byte id = mIn.readByte(); - mIn.skipBytes(3); - int entryCount = mIn.readInt(); - - mMissingResSpecs = new boolean[entryCount]; - Arrays.fill(mMissingResSpecs, true); - - if (mFlagsOffsets != null) { - mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount)); - } - /*flags*/ mIn.skipBytes(entryCount * 4); - - mResId = (0xff000000 & mResId) | id << 16; - mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg); - mPkg.addType(mType); - - while (nextChunk().type == Header.TYPE_CONFIG) { - readConfig(); - } - - addMissingResSpecs(); - - return mType; - } - - private ResConfig readConfig() throws IOException, AndrolibException { - checkChunkType(Header.TYPE_CONFIG); - /*typeId*/ mIn.skipInt(); - int entryCount = mIn.readInt(); - /*entriesStart*/ mIn.skipInt(); - - ResConfigFlags flags = readConfigFlags(); - int[] entryOffsets = mIn.readIntArray(entryCount); - - if (flags.isInvalid) { - String resName = mType.getName() + flags.getQualifiers(); - if (mKeepBroken) { - LOGGER.warning( - "Invalid config flags detected: " + resName); - } else { - LOGGER.warning( - "Invalid config flags detected. Dropping resources: " + resName); - } - } - - mConfig = flags.isInvalid && ! mKeepBroken ? - null : mPkg.getOrCreateConfig(flags); - - for (int i = 0; i < entryOffsets.length; i++) { - if (entryOffsets[i] != -1) { - mMissingResSpecs[i] = false; - mResId = (mResId & 0xffff0000) | i; - readEntry(); - } - } - - return mConfig; - } - - private void readEntry() throws IOException, AndrolibException { - /*size*/ mIn.skipBytes(2); - short flags = mIn.readShort(); - int specNamesId = mIn.readInt(); - - ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? - readValue() : readComplexEntry(); - - if (mConfig == null) { - return; - } - - ResID resId = new ResID(mResId); - ResResSpec spec; - if (mPkg.hasResSpec(resId)) { - spec = mPkg.getResSpec(resId); - } else { - spec = new ResResSpec( - resId, mSpecNames.getString(specNamesId), mPkg, mType); - mPkg.addResSpec(spec); - mType.addResSpec(spec); - } - ResResource res = new ResResource(mConfig, spec, value); - - mConfig.addResource(res); - spec.addResource(res); - mPkg.addResource(res); - } - - private ResBagValue readComplexEntry() throws IOException, - AndrolibException { - int parent = mIn.readInt(); - int count = mIn.readInt(); - - ResValueFactory factory = mPkg.getValueFactory(); - Duo[] items = new Duo[count]; - for (int i = 0; i < count; i++) { - items[i] = new Duo( - mIn.readInt(), (ResScalarValue) readValue()); - } - - return factory.bagFactory(parent, items); - } - - private ResValue readValue() throws IOException, AndrolibException { - /*size*/ mIn.skipCheckShort((short) 8); - /*zero*/ mIn.skipCheckByte((byte) 0); - byte type = mIn.readByte(); - int data = mIn.readInt(); - - return type == TypedValue.TYPE_STRING ? - mPkg.getValueFactory().factory(mTableStrings.getHTML(data)) : - mPkg.getValueFactory().factory(type, data, null); - } - - private ResConfigFlags readConfigFlags() throws IOException, AndrolibException { - int size = mIn.readInt(); - if (size < 28) { - throw new AndrolibException("Config size < 28"); - } - - boolean isInvalid = false; - - short mcc = mIn.readShort(); - short mnc = mIn.readShort(); - - char[] language = new char[]{ - (char) mIn.readByte(), (char) mIn.readByte()}; - char[] country = new char[]{ - (char) mIn.readByte(), (char) mIn.readByte()}; - - byte orientation = mIn.readByte(); - byte touchscreen = mIn.readByte(); - short density = mIn.readShort(); - - byte keyboard = mIn.readByte(); - byte navigation = mIn.readByte(); - byte inputFlags = mIn.readByte(); - /*inputPad0*/ mIn.skipBytes(1); - - short screenWidth = mIn.readShort(); - short screenHeight = mIn.readShort(); - - short sdkVersion = mIn.readShort(); - /*minorVersion, now must always be 0*/ mIn.skipBytes(2); - - byte screenLayout = 0; - byte uiMode = 0; - short smallestScreenWidthDp = 0; - if (size >= 32) { - screenLayout = mIn.readByte(); - uiMode = mIn.readByte(); - smallestScreenWidthDp = mIn.readShort(); - } - - short screenWidthDp = 0; - short screenHeightDp = 0; - - if (size >= 36) { - screenWidthDp = mIn.readShort(); - screenHeightDp = mIn.readShort(); - } - - if (size >= 40) { - // mIn.skipBytes(2); - } - - int exceedingSize = size - KNOWN_CONFIG_BYTES; - if (exceedingSize > 0) { - byte[] buf = new byte[exceedingSize]; - mIn.readFully(buf); - BigInteger exceedingBI = new BigInteger(1, buf); - - if (exceedingBI.equals(BigInteger.ZERO)) { - LOGGER.fine(String.format( - "Config flags size > %d, but exceeding bytes are all zero, so it should be ok.", - KNOWN_CONFIG_BYTES)); - } else { - LOGGER.warning(String.format( - "Config flags size > %d. Exceeding bytes: 0x%X.", - KNOWN_CONFIG_BYTES, exceedingBI)); - isInvalid = true; - } - } - - return new ResConfigFlags(mcc, mnc, language, country, orientation, - touchscreen, density, keyboard, navigation, inputFlags, - screenWidth, screenHeight, sdkVersion, screenLayout, uiMode, - smallestScreenWidthDp, screenWidthDp, screenHeightDp, isInvalid); - } - - private void addMissingResSpecs() throws AndrolibException { - int resId = mResId & 0xffff0000; - - for (int i = 0; i < mMissingResSpecs.length; i++) { - if (! mMissingResSpecs[i]) { - continue; - } - - ResResSpec spec = new ResResSpec(new ResID(resId | i), - String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType); - mPkg.addResSpec(spec); - mType.addResSpec(spec); - - ResValue value = new ResBoolValue(false, null); - ResResource res = new ResResource( - mPkg.getOrCreateConfig(new ResConfigFlags()), spec, value); - mPkg.addResource(res); - mConfig.addResource(res); - spec.addResource(res); - } - } - - private Header nextChunk() throws IOException { - return mHeader = Header.read(mIn); - } - - private void checkChunkType(int expectedType) throws AndrolibException { - if (mHeader.type != expectedType) { - throw new AndrolibException(String.format( - "Invalid chunk type: expected=0x%08x, got=0x%08x", - expectedType, mHeader.type)); - } - } - - private void nextChunkCheckType(int expectedType) - throws IOException, AndrolibException { - nextChunk(); - checkChunkType(expectedType); - } - - private final ExtDataInput mIn; - private final ResTable mResTable; - private final CountingInputStream mCountIn; - private final List mFlagsOffsets; - private final boolean mKeepBroken; - - private Header mHeader; - private StringBlock mTableStrings; - private StringBlock mTypeNames; - private StringBlock mSpecNames; - private ResPackage mPkg; - private ResType mType; - private ResConfig mConfig; - private int mResId; - private boolean[] mMissingResSpecs; - - - private final static short ENTRY_FLAG_COMPLEX = 0x0001; - - - public static class Header { - public final short type; - public final int chunkSize; - - public Header(short type, int size) { - this.type = type; - this.chunkSize = size; - } - - public static Header read(ExtDataInput in) throws IOException { - short type; - try { - type = in.readShort(); - } catch (EOFException ex) { - return new Header(TYPE_NONE, 0); - } - in.skipBytes(2); - return new Header(type, in.readInt()); - } - - public final static short - TYPE_NONE = -1, - TYPE_TABLE = 0x0002, - TYPE_PACKAGE = 0x0200, - TYPE_TYPE = 0x0202, - TYPE_CONFIG = 0x0201; - } - - public static class FlagsOffset { - public final int offset; - public final int count; - - public FlagsOffset(int offset, int count) { - this.offset = offset; - this.count = count; - } - } - - private static final Logger LOGGER = - Logger.getLogger(ARSCDecoder.class.getName()); - private static final int KNOWN_CONFIG_BYTES = 36; - - - public static class ARSCData { - - public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets, - ResTable resTable) { - mPackages = packages; - mFlagsOffsets = flagsOffsets; - mResTable = resTable; - } - - public FlagsOffset[] getFlagsOffsets() { - return mFlagsOffsets; - } - - public ResPackage[] getPackages() { - return mPackages; - } - - public ResPackage getOnePackage() throws AndrolibException { - if (mPackages.length != 1) { - throw new AndrolibException( - "Arsc file contains zero or multiple packages"); - } - return mPackages[0]; - } - - public ResTable getResTable() { - return mResTable; - } - - private final ResPackage[] mPackages; - private final FlagsOffset[] mFlagsOffsets; - private final ResTable mResTable; - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java b/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java deleted file mode 100644 index 91197bcf..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java +++ /dev/null @@ -1,1013 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package brut.androlib.res.decoder; - -import android.content.res.XmlResourceParser; -import android.util.TypedValue; -import brut.androlib.AndrolibException; -import brut.androlib.res.xml.ResXmlEncoders; -import brut.util.ExtDataInput; -import com.mindprod.ledatastream.LEDataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.xmlpull.v1.XmlPullParserException; - -/** - * @author Ryszard Wiśniewski - * @author Dmitry Skiba - * - * Binary xml files parser. - * - * Parser has only two states: (1) Operational state, which parser obtains after - * first successful call to next() and retains until open(), close(), or failed - * call to next(). (2) Closed state, which parser obtains after open(), close(), - * or failed call to next(). In this state methods return invalid values or - * throw exceptions. - * - * TODO: * check all methods in closed state - * - */ -public class AXmlResourceParser implements XmlResourceParser { - - public AXmlResourceParser() { - resetEventInfo(); - } - - public AXmlResourceParser(InputStream stream) { - this(); - open(stream); - } - - public AndrolibException getFirstError() { - return mFirstError; - } - - public ResAttrDecoder getAttrDecoder() { - return mAttrDecoder; - } - - public void setAttrDecoder(ResAttrDecoder attrDecoder) { - mAttrDecoder = attrDecoder; - } - - public void open(InputStream stream) { - close(); - if (stream != null) { - m_reader = new ExtDataInput( - new LEDataInputStream(stream)); - } - } - - public void close() { - if (!m_operational) { - return; - } - m_operational = false; -// m_reader.close(); - m_reader = null; - m_strings = null; - m_resourceIDs = null; - m_namespaces.reset(); - resetEventInfo(); - } - - /////////////////////////////////// iteration - public int next() throws XmlPullParserException, IOException { - if (m_reader == null) { - throw new XmlPullParserException("Parser is not opened.", this, null); - } - try { - doNext(); - return m_event; - } catch (IOException e) { - close(); - throw e; - } - } - - public int nextToken() throws XmlPullParserException, IOException { - return next(); - } - - public int nextTag() throws XmlPullParserException, IOException { - int eventType = next(); - if (eventType == TEXT && isWhitespace()) { - eventType = next(); - } - if (eventType != START_TAG && eventType != END_TAG) { - throw new XmlPullParserException("Expected start or end tag.", this, null); - } - return eventType; - } - - public String nextText() throws XmlPullParserException, IOException { - if (getEventType() != START_TAG) { - throw new XmlPullParserException("Parser must be on START_TAG to read next text.", this, null); - } - int eventType = next(); - if (eventType == TEXT) { - String result = getText(); - eventType = next(); - if (eventType != END_TAG) { - throw new XmlPullParserException("Event TEXT must be immediately followed by END_TAG.", this, null); - } - return result; - } else if (eventType == END_TAG) { - return ""; - } else { - throw new XmlPullParserException("Parser must be on START_TAG or TEXT to read text.", this, null); - } - } - - public void require(int type, String namespace, String name) throws XmlPullParserException, IOException { - if (type != getEventType() - || (namespace != null && !namespace.equals(getNamespace())) - || (name != null && !name.equals(getName()))) { - throw new XmlPullParserException(TYPES[type] + " is expected.", this, null); - } - } - - public int getDepth() { - return m_namespaces.getDepth() - 1; - } - - public int getEventType() throws XmlPullParserException { - return m_event; - } - - public int getLineNumber() { - return m_lineNumber; - } - - public String getName() { - if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) { - return null; - } - return m_strings.getString(m_name); - } - - public String getText() { - if (m_name == -1 || m_event != TEXT) { - return null; - } - return m_strings.getString(m_name); - } - - public char[] getTextCharacters(int[] holderForStartAndLength) { - String text = getText(); - if (text == null) { - return null; - } - holderForStartAndLength[0] = 0; - holderForStartAndLength[1] = text.length(); - char[] chars = new char[text.length()]; - text.getChars(0, text.length(), chars, 0); - return chars; - } - - public String getNamespace() { - return m_strings.getString(m_namespaceUri); - } - - public String getPrefix() { - int prefix = m_namespaces.findPrefix(m_namespaceUri); - return m_strings.getString(prefix); - } - - public String getPositionDescription() { - return "XML line #" + getLineNumber(); - } - - public int getNamespaceCount(int depth) throws XmlPullParserException { - return m_namespaces.getAccumulatedCount(depth); - } - - public String getNamespacePrefix(int pos) throws XmlPullParserException { - int prefix = m_namespaces.getPrefix(pos); - return m_strings.getString(prefix); - } - - public String getNamespaceUri(int pos) throws XmlPullParserException { - int uri = m_namespaces.getUri(pos); - return m_strings.getString(uri); - } - - /////////////////////////////////// attributes - public String getClassAttribute() { - if (m_classAttribute == -1) { - return null; - } - int offset = getAttributeOffset(m_classAttribute); - int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - return m_strings.getString(value); - } - - public String getIdAttribute() { - if (m_idAttribute == -1) { - return null; - } - int offset = getAttributeOffset(m_idAttribute); - int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - return m_strings.getString(value); - } - - public int getIdAttributeResourceValue(int defaultValue) { - if (m_idAttribute == -1) { - return defaultValue; - } - int offset = getAttributeOffset(m_idAttribute); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType != TypedValue.TYPE_REFERENCE) { - return defaultValue; - } - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - public int getStyleAttribute() { - if (m_styleAttribute == -1) { - return 0; - } - int offset = getAttributeOffset(m_styleAttribute); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - public int getAttributeCount() { - if (m_event != START_TAG) { - return -1; - } - return m_attributes.length / ATTRIBUTE_LENGHT; - } - - public String getAttributeNamespace(int index) { - int offset = getAttributeOffset(index); - int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; - if (namespace == -1) { - return ""; - } - return m_strings.getString(namespace); - } - - public String getAttributePrefix(int index) { - int offset = getAttributeOffset(index); - int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; - int prefix = m_namespaces.findPrefix(uri); - if (prefix == -1) { - return ""; - } - return m_strings.getString(prefix); - } - - public String getAttributeName(int index) { - int offset = getAttributeOffset(index); - int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; - if (name == -1) { - return ""; - } - return m_strings.getString(name); - } - - public int getAttributeNameResource(int index) { - int offset = getAttributeOffset(index); - int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; - if (m_resourceIDs == null - || name < 0 || name >= m_resourceIDs.length) { - return 0; - } - return m_resourceIDs[name]; - } - - public int getAttributeValueType(int index) { - int offset = getAttributeOffset(index); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - } - - public int getAttributeValueData(int index) { - int offset = getAttributeOffset(index); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - public String getAttributeValue(int index) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - int valueRaw = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - - if (mAttrDecoder != null) { - try { - return mAttrDecoder.decode(valueType, valueData, - valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars( - m_strings.getString(valueRaw)), - getAttributeNameResource(index)); - } catch (AndrolibException ex) { - setFirstError(ex); - LOGGER.log(Level.WARNING, String.format( - "Could not decode attr value, using undecoded value " - + "instead: ns=%s, name=%s, value=0x%08x", - getAttributePrefix(index), getAttributeName(index), - valueData), ex); - } - } else { - if (valueType == TypedValue.TYPE_STRING) { - return ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw)); - } - } - - return TypedValue.coerceToString(valueType, valueData); - } - - public boolean getAttributeBooleanValue(int index, boolean defaultValue) { - return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0; - } - - public float getAttributeFloatValue(int index, float defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_FLOAT) { - int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - return Float.intBitsToFloat(valueData); - } - return defaultValue; - } - - public int getAttributeIntValue(int index, int defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType >= TypedValue.TYPE_FIRST_INT - && valueType <= TypedValue.TYPE_LAST_INT) { - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - return defaultValue; - } - - public int getAttributeUnsignedIntValue(int index, int defaultValue) { - return getAttributeIntValue(index, defaultValue); - } - - public int getAttributeResourceValue(int index, int defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_REFERENCE) { - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - return defaultValue; - } - - public String getAttributeValue(String namespace, String attribute) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return ""; - } - return getAttributeValue(index); - } - - public boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeBooleanValue(index, defaultValue); - } - - public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeFloatValue(index, defaultValue); - } - - public int getAttributeIntValue(String namespace, String attribute, int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeIntValue(index, defaultValue); - } - - public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeUnsignedIntValue(index, defaultValue); - } - - public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeResourceValue(index, defaultValue); - } - - public int getAttributeListValue(int index, String[] options, int defaultValue) { - // TODO implement - return 0; - } - - public int getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue) { - // TODO implement - return 0; - } - - public String getAttributeType(int index) { - return "CDATA"; - } - - public boolean isAttributeDefault(int index) { - return false; - } - - /////////////////////////////////// dummies - public void setInput(InputStream stream, String inputEncoding) throws XmlPullParserException { - open(stream); - } - - public void setInput(Reader reader) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - public String getInputEncoding() { - return null; - } - - public int getColumnNumber() { - return -1; - } - - public boolean isEmptyElementTag() throws XmlPullParserException { - return false; - } - - public boolean isWhitespace() throws XmlPullParserException { - return false; - } - - public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - public String getNamespace(String prefix) { - throw new RuntimeException(E_NOT_SUPPORTED); - } - - public Object getProperty(String name) { - return null; - } - - public void setProperty(String name, Object value) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - public boolean getFeature(String feature) { - return false; - } - - public void setFeature(String name, boolean value) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - ///////////////////////////////////////////// implementation - /** - * Namespace stack, holds prefix+uri pairs, as well as depth information. - * All information is stored in one int[] array. Array consists of depth - * frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count; - * Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable - * bottom-up traversal. increaseDepth adds depth frame, decreaseDepth - * removes it. push/pop operations operate only in current depth frame. - * 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' !! - * - */ - private static final class NamespaceStack { - - public NamespaceStack() { - m_data = new int[32]; - } - - public final void reset() { - m_dataLength = 0; - m_count = 0; - m_depth = 0; - } - - public final int getTotalCount() { - return m_count; - } - - public final int getCurrentCount() { - if (m_dataLength == 0) { - return 0; - } - int offset = m_dataLength - 1; - return m_data[offset]; - } - - public final int getAccumulatedCount(int depth) { - if (m_dataLength == 0 || depth < 0) { - return 0; - } - if (depth > m_depth) { - depth = m_depth; - } - int accumulatedCount = 0; - int offset = 0; - for (; depth != 0; --depth) { - int count = m_data[offset]; - accumulatedCount += count; - offset += (2 + count * 2); - } - return accumulatedCount; - } - - public final void push(int prefix, int uri) { - if (m_depth == 0) { - increaseDepth(); - } - ensureDataCapacity(2); - int offset = m_dataLength - 1; - int count = m_data[offset]; - m_data[offset - 1 - count * 2] = count + 1; - m_data[offset] = prefix; - m_data[offset + 1] = uri; - m_data[offset + 2] = count + 1; - m_dataLength += 2; - m_count += 1; - } - - public final boolean pop(int prefix, int uri) { - if (m_dataLength == 0) { - return false; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - for (int i = 0, o = offset - 2; i != count; ++i, o -= 2) { - if (m_data[o] != prefix || m_data[o + 1] != uri) { - continue; - } - count -= 1; - if (i == 0) { - m_data[o] = count; - o -= (1 + count * 2); - m_data[o] = count; - } else { - m_data[offset] = count; - offset -= (1 + 2 + count * 2); - m_data[offset] = count; - System.arraycopy( - m_data, o + 2, - m_data, o, - m_dataLength - o); - } - m_dataLength -= 2; - m_count -= 1; - return true; - } - return false; - } - - public final boolean pop() { - if (m_dataLength == 0) { - return false; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - if (count == 0) { - return false; - } - count -= 1; - offset -= 2; - m_data[offset] = count; - offset -= (1 + count * 2); - m_data[offset] = count; - m_dataLength -= 2; - m_count -= 1; - return true; - } - - public final int getPrefix(int index) { - return get(index, true); - } - - public final int getUri(int index) { - return get(index, false); - } - - public final int findPrefix(int uri) { - return find(uri, false); - } - - public final int findUri(int prefix) { - return find(prefix, true); - } - - public final int getDepth() { - return m_depth; - } - - public final void increaseDepth() { - ensureDataCapacity(2); - int offset = m_dataLength; - m_data[offset] = 0; - m_data[offset + 1] = 0; - m_dataLength += 2; - m_depth += 1; - } - - public final void decreaseDepth() { - if (m_dataLength == 0) { - return; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - if ((offset - 1 - count * 2) == 0) { - return; - } - m_dataLength -= 2 + count * 2; - m_count -= count; - m_depth -= 1; - } - - private void ensureDataCapacity(int capacity) { - int available = (m_data.length - m_dataLength); - if (available > capacity) { - return; - } - int newLength = (m_data.length + available) * 2; - int[] newData = new int[newLength]; - System.arraycopy(m_data, 0, newData, 0, m_dataLength); - m_data = newData; - } - - private final int find(int prefixOrUri, boolean prefix) { - if (m_dataLength == 0) { - return -1; - } - int offset = m_dataLength - 1; - for (int i = m_depth; i != 0; --i) { - int count = m_data[offset]; - offset -= 2; - for (; count != 0; --count) { - if (prefix) { - if (m_data[offset] == prefixOrUri) { - return m_data[offset + 1]; - } - } else { - if (m_data[offset + 1] == prefixOrUri) { - return m_data[offset]; - } - } - offset -= 2; - } - } - return -1; - } - - private final int get(int index, boolean prefix) { - if (m_dataLength == 0 || index < 0) { - return -1; - } - int offset = 0; - for (int i = m_depth; i != 0; --i) { - int count = m_data[offset]; - if (index >= count) { - index -= count; - offset += (2 + count * 2); - continue; - } - offset += (1 + index * 2); - if (!prefix) { - offset += 1; - } - return m_data[offset]; - } - return -1; - } - private int[] m_data; - private int m_dataLength; - private int m_count; - private int m_depth; - } - - /////////////////////////////////// package-visible -// final void fetchAttributes(int[] styleableIDs,TypedArray result) { -// result.resetIndices(); -// if (m_attributes==null || m_resourceIDs==null) { -// return; -// } -// boolean needStrings=false; -// for (int i=0,e=styleableIDs.length;i!=e;++i) { -// int id=styleableIDs[i]; -// for (int o=0;o!=m_attributes.length;o+=ATTRIBUTE_LENGHT) { -// int name=m_attributes[o+ATTRIBUTE_IX_NAME]; -// if (name>=m_resourceIDs.length || -// m_resourceIDs[name]!=id) -// { -// continue; -// } -// int valueType=m_attributes[o+ATTRIBUTE_IX_VALUE_TYPE]; -// int valueData; -// int assetCookie; -// if (valueType==TypedValue.TYPE_STRING) { -// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_STRING]; -// assetCookie=-1; -// needStrings=true; -// } else { -// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_DATA]; -// assetCookie=0; -// } -// result.addValue(i,valueType,valueData,assetCookie,id,0); -// } -// } -// if (needStrings) { -// result.setStrings(m_strings); -// } -// } - final StringBlock getStrings() { - return m_strings; - } - - /////////////////////////////////// - private final int getAttributeOffset(int index) { - if (m_event != START_TAG) { - throw new IndexOutOfBoundsException("Current event is not START_TAG."); - } - int offset = index * ATTRIBUTE_LENGHT; - if (offset >= m_attributes.length) { - throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ")."); - } - return offset; - } - - private final int findAttribute(String namespace, String attribute) { - if (m_strings == null || attribute == null) { - return -1; - } - int name = m_strings.find(attribute); - if (name == -1) { - return -1; - } - int uri = (namespace != null) - ? m_strings.find(namespace) - : -1; - for (int o = 0; o != m_attributes.length; ++o) { - if (name == m_attributes[o + ATTRIBUTE_IX_NAME] - && (uri == -1 || uri == m_attributes[o + ATTRIBUTE_IX_NAMESPACE_URI])) { - return o / ATTRIBUTE_LENGHT; - } - } - return -1; - } - - private final void resetEventInfo() { - m_event = -1; - m_lineNumber = -1; - m_name = -1; - m_namespaceUri = -1; - m_attributes = null; - m_idAttribute = -1; - m_classAttribute = -1; - m_styleAttribute = -1; - } - - private final void doNext() throws IOException { - // Delayed initialization. - if (m_strings == null) { - m_reader.skipCheckInt(CHUNK_AXML_FILE); - /* - * chunkSize - */ m_reader.skipInt(); - m_strings = StringBlock.read(m_reader); - m_namespaces.increaseDepth(); - m_operational = true; - } - - if (m_event == END_DOCUMENT) { - return; - } - - int event = m_event; - resetEventInfo(); - - while (true) { - if (m_decreaseDepth) { - m_decreaseDepth = false; - m_namespaces.decreaseDepth(); - } - - // Fake END_DOCUMENT event. - if (event == END_TAG - && m_namespaces.getDepth() == 1 - && m_namespaces.getCurrentCount() == 0) { - m_event = END_DOCUMENT; - break; - } - - int chunkType; - if (event == START_DOCUMENT) { - // Fake event, see CHUNK_XML_START_TAG handler. - chunkType = CHUNK_XML_START_TAG; - } else { - chunkType = m_reader.readInt(); - } - - if (chunkType == CHUNK_RESOURCEIDS) { - int chunkSize = m_reader.readInt(); - if (chunkSize < 8 || (chunkSize % 4) != 0) { - throw new IOException("Invalid resource ids size (" + chunkSize + ")."); - } - m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2); - continue; - } - - if (chunkType < CHUNK_XML_FIRST || chunkType > CHUNK_XML_LAST) { - throw new IOException("Invalid chunk type (" + chunkType + ")."); - } - - // Fake START_DOCUMENT event. - if (chunkType == CHUNK_XML_START_TAG && event == -1) { - m_event = START_DOCUMENT; - break; - } - - // Common header. - /*chunkSize*/ m_reader.skipInt(); - int lineNumber = m_reader.readInt(); - /*0xFFFFFFFF*/ m_reader.skipInt(); - - if (chunkType == CHUNK_XML_START_NAMESPACE - || chunkType == CHUNK_XML_END_NAMESPACE) { - if (chunkType == CHUNK_XML_START_NAMESPACE) { - int prefix = m_reader.readInt(); - int uri = m_reader.readInt(); - m_namespaces.push(prefix, uri); - } else { - /*prefix*/ m_reader.skipInt(); - /*uri*/ m_reader.skipInt(); - m_namespaces.pop(); - } - continue; - } - - m_lineNumber = lineNumber; - - if (chunkType == CHUNK_XML_START_TAG) { - m_namespaceUri = m_reader.readInt(); - m_name = m_reader.readInt(); - /*flags?*/ m_reader.skipInt(); - int attributeCount = m_reader.readInt(); - m_idAttribute = (attributeCount >>> 16) - 1; - attributeCount &= 0xFFFF; - m_classAttribute = m_reader.readInt(); - m_styleAttribute = (m_classAttribute >>> 16) - 1; - m_classAttribute = (m_classAttribute & 0xFFFF) - 1; - m_attributes = m_reader.readIntArray(attributeCount * ATTRIBUTE_LENGHT); - for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length;) { - m_attributes[i] = (m_attributes[i] >>> 24); - i += ATTRIBUTE_LENGHT; - } - m_namespaces.increaseDepth(); - m_event = START_TAG; - break; - } - - if (chunkType == CHUNK_XML_END_TAG) { - m_namespaceUri = m_reader.readInt(); - m_name = m_reader.readInt(); - m_event = END_TAG; - m_decreaseDepth = true; - break; - } - - if (chunkType == CHUNK_XML_TEXT) { - m_name = m_reader.readInt(); - /*?*/ m_reader.skipInt(); - /*?*/ m_reader.skipInt(); - m_event = TEXT; - break; - } - } - } - - private static String formatArray(int[] array, int min, int max) { - if (max > array.length) { - max = array.length; - } - if (min < 0) { - min = 0; - } - StringBuffer sb = new StringBuffer("["); - int i = min; - while (true) { - sb.append(array[i]); - i++; - if (i < max) { - sb.append(", "); - } else { - sb.append("]"); - break; - } - } - return sb.toString(); - } - - private boolean compareAttr(int[] attr1, int[] attr2) { - //TODO: sort Attrs - /* - * ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING : - * ATTRIBUTE_IX_NAMESPACE_URI ATTRIBUTE_IX_NAMESPACE_URI : - * ATTRIBUTE_IX_NAME id - * - */ - if (attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING - && attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE] - && //(m_strings.touch(attr1[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) && - 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) - && //(m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) || - // m_strings.touch(attr2[ATTRIBUTE_IX_NAME], m_name)) && - //m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) && - (attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) { - return (attr1[ATTRIBUTE_IX_NAME] < attr2[ATTRIBUTE_IX_NAME]); - //} else if (attr1[ATTRIBUTE_IX_NAMESPACE_URI] < attr2[ATTRIBUTE_IX_NAMESPACE_URI]) { - // return true; - } else { - return false; - } - } - - - private void setFirstError(AndrolibException error) { - if (mFirstError == null) { - mFirstError = error; - } - } - - /////////////////////////////////// data - /* - * All values are essentially indices, e.g. m_name is - * an index of name in m_strings. - */ - private ExtDataInput m_reader; - private ResAttrDecoder mAttrDecoder; - private AndrolibException mFirstError; - - private boolean m_operational = false; - private StringBlock m_strings; - private int[] m_resourceIDs; - private NamespaceStack m_namespaces = new NamespaceStack(); - private boolean m_decreaseDepth; - private int m_event; - private int m_lineNumber; - private int m_name; - private int m_namespaceUri; - private int[] m_attributes; - private int m_idAttribute; - private int m_classAttribute; - private int m_styleAttribute; - - private final static Logger LOGGER = - Logger.getLogger(AXmlResourceParser.class.getName()); - private static final String E_NOT_SUPPORTED = "Method is not supported."; - private static final int - ATTRIBUTE_IX_NAMESPACE_URI = 0, - ATTRIBUTE_IX_NAME = 1, - ATTRIBUTE_IX_VALUE_STRING = 2, - ATTRIBUTE_IX_VALUE_TYPE = 3, - ATTRIBUTE_IX_VALUE_DATA = 4, - ATTRIBUTE_LENGHT = 5; - - private static final int - CHUNK_AXML_FILE = 0x00080003, - CHUNK_RESOURCEIDS = 0x00080180, - CHUNK_XML_FIRST = 0x00100100, - CHUNK_XML_START_NAMESPACE = 0x00100100, - CHUNK_XML_END_NAMESPACE = 0x00100101, - CHUNK_XML_START_TAG = 0x00100102, - CHUNK_XML_END_TAG = 0x00100103, - CHUNK_XML_TEXT = 0x00100104, - CHUNK_XML_LAST = 0x00100104; -} \ No newline at end of file diff --git a/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java b/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java deleted file mode 100644 index 53b1b4a3..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.decoder; - -import brut.androlib.AndrolibException; -import brut.androlib.err.CantFind9PatchChunk; -import brut.util.ExtDataInput; -import java.awt.image.BufferedImage; -import java.io.*; -import javax.imageio.ImageIO; -import org.apache.commons.io.IOUtils; - -/** - * @author Ryszard Wiśniewski - */ -public class Res9patchStreamDecoder implements ResStreamDecoder { - public void decode(InputStream in, OutputStream out) - throws AndrolibException { - try { - byte[] data = IOUtils.toByteArray(in); - - BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); - int w = im.getWidth(), h = im.getHeight(); - - BufferedImage im2 = new BufferedImage( - w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR); - if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) { - im2.getRaster().setRect(1, 1, im.getRaster()); - } else { - im2.getGraphics().drawImage(im, 1, 1, null); - } - - NinePatch np = getNinePatch(data); - drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); - drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom); - - int[] xDivs = np.xDivs; - for (int i = 0; i < xDivs.length; i += 2) { - drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]); - } - - int[] yDivs = np.yDivs; - for (int i = 0; i < yDivs.length; i += 2) { - drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); - } - - ImageIO.write(im2, "png", out); - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } - - private NinePatch getNinePatch(byte[] data) - throws AndrolibException, IOException { - ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); - find9patchChunk(di); - return NinePatch.decode(di); - } - - private void find9patchChunk(DataInput di) - throws AndrolibException, IOException { - di.skipBytes(8); - while (true) { - int size; - try { - size = di.readInt(); - } catch (IOException ex) { - throw new CantFind9PatchChunk("Cant find nine patch chunk", ex); - } - if (di.readInt() == NP_CHUNK_TYPE) { - return; - } - di.skipBytes(size + 4); - } - } - - private void drawHLine(BufferedImage im, int y, int x1, int x2) { - for (int x = x1; x <= x2; x++) { - im.setRGB(x, y, NP_COLOR); - } - } - - private void drawVLine(BufferedImage im, int x, int y1, int y2) { - for (int y = y1; y <= y2; y++) { - im.setRGB(x, y, NP_COLOR); - } - } - - private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc - private static final int NP_COLOR = 0xff000000; - - - private static class NinePatch { - public final int padLeft, padRight, padTop, padBottom; - public final int[] xDivs, yDivs; - - public NinePatch(int padLeft, int padRight, int padTop, int padBottom, - int[] xDivs, int[] yDivs) { - this.padLeft = padLeft; - this.padRight = padRight; - this.padTop = padTop; - this.padBottom = padBottom; - this.xDivs = xDivs; - this.yDivs = yDivs; - } - - public static NinePatch decode(ExtDataInput di) throws IOException { - di.skipBytes(1); - byte numXDivs = di.readByte(); - byte numYDivs = di.readByte(); - di.skipBytes(1); - di.skipBytes(8); - int padLeft = di.readInt(); - int padRight = di.readInt(); - int padTop = di.readInt(); - int padBottom = di.readInt(); - di.skipBytes(4); - int[] xDivs = di.readIntArray(numXDivs); - int[] yDivs = di.readIntArray(numYDivs); - - return new NinePatch(padLeft, padRight, padTop, padBottom, - xDivs, yDivs); - } - } -} \ No newline at end of file diff --git a/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java b/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java deleted file mode 100644 index 75c601e1..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.decoder; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResPackage; -import brut.androlib.res.data.value.ResAttr; -import brut.androlib.res.data.value.ResScalarValue; - -/** - * @author Ryszard Wiśniewski - */ -public class ResAttrDecoder { - public String decode(int type, int value, String rawValue, int attrResId) - throws AndrolibException { - ResScalarValue resValue = mCurrentPackage.getValueFactory() - .factory(type, value, rawValue); - - String decoded = null; - if (attrResId != 0) { - ResAttr attr = (ResAttr) getCurrentPackage().getResTable() - .getResSpec(attrResId).getDefaultResource().getValue(); - decoded = attr.convertToResXmlFormat(resValue); - } - - return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); - } - - public ResPackage getCurrentPackage() throws AndrolibException { - if (mCurrentPackage == null) { - throw new AndrolibException("Current package not set"); - } - return mCurrentPackage; - } - - public void setCurrentPackage(ResPackage currentPackage) { - mCurrentPackage = currentPackage; - } - - private ResPackage mCurrentPackage; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java b/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java deleted file mode 100644 index e16f9b59..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.decoder; - -import brut.androlib.AndrolibException; -import brut.androlib.err.CantFind9PatchChunk; -import brut.androlib.res.data.ResResource; -import brut.androlib.res.data.value.ResBoolValue; -import brut.androlib.res.data.value.ResFileValue; -import brut.directory.Directory; -import brut.directory.DirectoryException; -import java.io.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Ryszard Wiśniewski - */ -public class ResFileDecoder { - private final ResStreamDecoderContainer mDecoders; - - public ResFileDecoder(ResStreamDecoderContainer decoders) { - this.mDecoders = decoders; - } - - public void decode(ResResource res, Directory inDir, Directory outDir) - throws AndrolibException { - - ResFileValue fileValue = (ResFileValue) res.getValue(); - String inFileName = fileValue.getStrippedPath(); - String outResName = res.getFilePath(); - String typeName = res.getResSpec().getType().getName(); - - String ext = null; - String outFileName; - int extPos = inFileName.lastIndexOf("."); - if (extPos == -1) { - outFileName = outResName; - } else { - ext = inFileName.substring(extPos); - outFileName = outResName + ext; - } - - try { - if (typeName.equals("raw")) { - decode(inDir, inFileName, outDir, outFileName, "raw"); - return; - } - if (typeName.equals("drawable") || typeName.equals("mipmap")) { - if (inFileName.toLowerCase().endsWith(".9.png")) { - outFileName = outResName + ".9" + ext; - - try { - decode( - inDir, inFileName, outDir, outFileName, "9patch"); - return; - } catch (CantFind9PatchChunk ex) { - LOGGER.log(Level.WARNING, String.format( - "Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.", - inFileName - ), ex); - outDir.removeFile(outFileName); - outFileName = outResName + ext; - } - } - if (! ".xml".equals(ext)) { - decode(inDir, inFileName, outDir, outFileName, "raw"); - return; - } - } - - decode(inDir, inFileName, outDir, outFileName, "xml"); - } catch (AndrolibException ex) { - LOGGER.log(Level.SEVERE, String.format( - "Could not decode file, replacing by FALSE value: %s", - inFileName, outFileName), ex); - res.replace(new ResBoolValue(false, null)); - } - } - - public void decode(Directory inDir, String inFileName, Directory outDir, - String outFileName, String decoder) throws AndrolibException { - InputStream in = null; - OutputStream out = null; - try { - in = inDir.getFileInput(inFileName); - out = outDir.getFileOutput(outFileName); - mDecoders.decode(in, out, decoder); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } finally { - try{ - if (in != null) { - in.close(); - } - if (out != null) { - out.close(); - } - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } - } - - public void decodeManifest(Directory inDir, String inFileName, Directory outDir, - String outFileName) throws AndrolibException { - InputStream in = null; - OutputStream out = null; - try { - in = inDir.getFileInput(inFileName); - out = outDir.getFileOutput(outFileName); - ((XmlPullStreamDecoder)mDecoders.getDecoder("xml")).decodeManifest(in, out); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } finally { - try{ - if (in != null) { - in.close(); - } - if (out != null) { - out.close(); - } - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } - } - - private final static Logger LOGGER = - Logger.getLogger(ResFileDecoder.class.getName()); -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java b/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java deleted file mode 100644 index 6c5d0e3a..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.decoder; - -import brut.androlib.AndrolibException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import org.apache.commons.io.IOUtils; - -/** - * @author Ryszard Wiśniewski - */ -public class ResRawStreamDecoder implements ResStreamDecoder { - public void decode(InputStream in, OutputStream out) - throws AndrolibException { - try { - IOUtils.copy(in, out); - } catch (IOException ex) { - throw new AndrolibException("Could not decode raw stream", ex); - } - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java b/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java deleted file mode 100644 index e7bbcd68..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.decoder; - -import brut.androlib.AndrolibException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @author Ryszard Wiśniewski - */ -public interface ResStreamDecoder { - public void decode(InputStream in, OutputStream out) - throws AndrolibException; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java b/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java deleted file mode 100644 index d478bf98..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.decoder; - -import brut.androlib.AndrolibException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; - -/** - * @author Ryszard Wiśniewski - */ -public class ResStreamDecoderContainer { - private final Map mDecoders = - new HashMap(); - - public void decode(InputStream in, OutputStream out, String decoderName) - throws AndrolibException { - getDecoder(decoderName).decode(in, out); - } - - public ResStreamDecoder getDecoder(String name) throws AndrolibException { - ResStreamDecoder decoder = mDecoders.get(name); - if (decoder == null) { - throw new AndrolibException("Undefined decoder: " + name); - } - return decoder; - } - - public void setDecoder(String name, ResStreamDecoder decoder) { - mDecoders.put(name, decoder); - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java b/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java deleted file mode 100644 index 36ecda52..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java +++ /dev/null @@ -1,347 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.decoder; - -import brut.androlib.res.xml.ResXmlEncoders; -import brut.util.ExtDataInput; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * @author Ryszard Wiśniewski - * @author Dmitry Skiba - * - * Block of strings, used in binary xml and arsc. - * - * TODO: - * - implement get() - * - */ -public class StringBlock { - - /** - * Reads whole (including chunk type) string block from stream. - * Stream must be at the chunk type. - */ - public static StringBlock read(ExtDataInput reader) throws IOException { - reader.skipCheckInt(CHUNK_TYPE); - int chunkSize = reader.readInt(); - int stringCount = reader.readInt(); - int styleOffsetCount = reader.readInt(); - int flags = reader.readInt(); - int stringsOffset = reader.readInt(); - int stylesOffset = reader.readInt(); - - StringBlock block = new StringBlock(); - block.m_isUTF8 = (flags & UTF8_FLAG) != 0; - block.m_stringOffsets = reader.readIntArray(stringCount); - block.m_stringOwns = new int[stringCount]; - for (int i=0;i= m_stringOffsets.length) { - return null; - } - int offset = m_stringOffsets[index]; - int length; - - if (! m_isUTF8) { - length = getShort(m_strings, offset) * 2; - offset += 2; - } else { - offset += getVarint(m_strings, offset)[1]; - int[] varint = getVarint(m_strings, offset); - offset += varint[1]; - length = varint[0]; - } - return decodeString(offset, length); - } - - /** - * Not yet implemented. - * - * Returns string with style information (if any). - */ - public CharSequence get(int index) { - return getString(index); - } - - /** - * Returns string with style tags (html-like). - */ - public String getHTML(int index) { - String raw = getString(index); - if (raw == null) { - return raw; - } - int[] style = getStyle(index); - if (style == null) { - return ResXmlEncoders.escapeXmlChars(raw); - } - StringBuilder html = new StringBuilder(raw.length() + 32); - int[] opened = new int[style.length / 3]; - int offset = 0, depth = 0; - while (true) { - int i = -1, j; - for (j = 0; j != style.length; j += 3) { - if (style[j + 1] == -1) { - continue; - } - if (i == -1 || style[i + 1] > style[j + 1]) { - i = j; - } - } - int start = ((i != -1) ? style[i + 1] : raw.length()); - for (j = depth - 1; j >= 0; j--) { - int last = opened[j]; - int end = style[last + 2]; - if (end >= start) { - break; - } - if (offset <= end) { - html.append(ResXmlEncoders.escapeXmlChars( - raw.substring(offset, end + 1))); - offset = end + 1; - } - outputStyleTag(getString(style[last]), html, true); - } - depth = j + 1; - if (offset < start) { - html.append(ResXmlEncoders.escapeXmlChars( - raw.substring(offset, start))); - offset = start; - } - if (i == -1) { - break; - } - outputStyleTag(getString(style[i]), html, false); - style[i + 1] = -1; - opened[depth++] = i; - } - return html.toString(); - } - - private void outputStyleTag(String tag, StringBuilder builder, - boolean close) { - builder.append('<'); - if (close) { - builder.append('/'); - } - - int pos = tag.indexOf(';'); - if (pos == -1) { - builder.append(tag); - } else { - builder.append(tag.substring(0, pos)); - if (! close) { - boolean loop = true; - while (loop) { - int pos2 = tag.indexOf('=', pos + 1); - builder.append(' ').append(tag.substring(pos + 1, pos2)) - .append("=\""); - pos = tag.indexOf(';', pos2 + 1); - - String val; - if (pos != -1) { - val = tag.substring(pos2 + 1, pos); - } else { - loop = false; - val = tag.substring(pos2 + 1); - } - - builder.append(ResXmlEncoders.escapeXmlChars(val)) - .append('"'); - } - } - } - builder.append('>'); - } - - /** - * Finds index of the string. - * Returns -1 if the string was not found. - */ - public int find(String string) { - if (string == null) { - return -1; - } - for (int i = 0; i != m_stringOffsets.length; ++i) { - int offset = m_stringOffsets[i]; - int length = getShort(m_strings, offset); - if (length != string.length()) { - continue; - } - int j = 0; - for (; j != length; ++j) { - offset += 2; - if (string.charAt(j) != getShort(m_strings, offset)) { - break; - } - } - if (j == length) { - return i; - } - } - return -1; - } - - ///////////////////////////////////////////// implementation - private StringBlock() { - } - - /** - * Returns style information - array of int triplets, - * where in each triplet: - * * first int is index of tag name ('b','i', etc.) - * * second int is tag start index in string - * * third int is tag end index in string - */ - private int[] getStyle(int index) { - if (m_styleOffsets == null || m_styles == null - || index >= m_styleOffsets.length) { - return null; - } - int offset = m_styleOffsets[index] / 4; - int style[]; - { - int count = 0; - for (int i = offset; i < m_styles.length; ++i) { - if (m_styles[i] == -1) { - break; - } - count += 1; - } - if (count == 0 || (count % 3) != 0) { - return null; - } - style = new int[count]; - } - for (int i = offset, j = 0; i < m_styles.length;) { - if (m_styles[i] == -1) { - break; - } - style[j++] = m_styles[i++]; - } - return style; - } - - private String decodeString(int offset, int length) { - try { - return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode( - ByteBuffer.wrap(m_strings, offset, length)).toString(); - } catch (CharacterCodingException ex) { - LOGGER.log(Level.WARNING, null, ex); - return null; - } - } - - private static final int getShort(byte[] array, int offset) { - return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff; - } - - private static final int getShort(int[] array, int offset) { - int value = array[offset / 4]; - if ((offset % 4) / 2 == 0) { - return (value & 0xFFFF); - } else { - return (value >>> 16); - } - } - - private static final int[] getVarint(byte[] array, int offset) { - int val = array[offset]; - boolean more = (val & 0x80) != 0; - val &= 0x7f; - - if (! more) { - return new int[]{val, 1}; - } else { - return new int[]{val << 8 | array[offset + 1] & 0xff, 2}; - } - } - - public boolean touch(int index, int own) { - if (index < 0 - || m_stringOwns == null - || index >= m_stringOwns.length) { - return false; - } - if(m_stringOwns[index] == -1) { - m_stringOwns[index] = own; - return true; - } else if (m_stringOwns[index] == own) { - return true; - } else { - return false; - } - } - - private int[] m_stringOffsets; - private byte[] m_strings; - private int[] m_styleOffsets; - private int[] m_styles; - private boolean m_isUTF8; - private int[] m_stringOwns; - private static final CharsetDecoder UTF16LE_DECODER = - Charset.forName("UTF-16LE").newDecoder(); - private static final CharsetDecoder UTF8_DECODER = - Charset.forName("UTF-8").newDecoder(); - private static final Logger LOGGER = - Logger.getLogger(StringBlock.class.getName()); - private static final int CHUNK_TYPE = 0x001C0001; - private static final int UTF8_FLAG = 0x00000100; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java b/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java deleted file mode 100644 index c7f21466..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.decoder; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResTable; -import brut.androlib.res.util.ExtXmlSerializer; -import java.io.*; -import java.util.logging.Logger; -import org.xmlpull.v1.*; -import org.xmlpull.v1.wrapper.*; -import org.xmlpull.v1.wrapper.classic.StaticXmlSerializerWrapper; - -/** - * @author Ryszard Wiśniewski - */ -public class XmlPullStreamDecoder implements ResStreamDecoder { - public XmlPullStreamDecoder(XmlPullParser parser, - ExtXmlSerializer serializer) { - this.mParser = parser; - this.mSerial = serializer; - } - - public void decode(InputStream in, OutputStream out) - throws AndrolibException { - try { - XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); - XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); - final ResTable resTable = ((AXmlResourceParser)mParser).getAttrDecoder().getCurrentPackage().getResTable(); - - XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory){ - boolean hideSdkInfo = false; - @Override - public void event(XmlPullParser pp) throws XmlPullParserException, IOException { - int type = pp.getEventType(); - - if (type == XmlPullParser.START_TAG) { - if ("uses-sdk".equalsIgnoreCase(pp.getName())) { - try { - hideSdkInfo = parseAttr(pp); - if(hideSdkInfo) { - return; - } - } catch (AndrolibException e) {} - } - } else if (hideSdkInfo && type == XmlPullParser.END_TAG && - "uses-sdk".equalsIgnoreCase(pp.getName())) { - return; - } - super.event(pp); - } - - private boolean parseAttr(XmlPullParser pp) throws AndrolibException { - ResTable restable = resTable; - for (int i = 0; i < pp.getAttributeCount(); i++) { - final String a_ns = "http://schemas.android.com/apk/res/android"; - String ns = pp.getAttributeNamespace (i); - if (a_ns.equalsIgnoreCase(ns)) { - String name = pp.getAttributeName (i); - String value = pp.getAttributeValue (i); - if (name != null && value != null) { - if (name.equalsIgnoreCase("minSdkVersion") || - name.equalsIgnoreCase("targetSdkVersion") || - name.equalsIgnoreCase("maxSdkVersion")) { - restable.addSdkInfo(name, value); - } else { - restable.clearSdkInfo(); - return false;//Found unknown flags - } - } - } else { - resTable.clearSdkInfo(); - return false;//Found unknown flags - } - } - return true; - } - }; - - par.setInput(in, null); - ser.setOutput(out, null); - - while (par.nextToken() != XmlPullParser.END_DOCUMENT) { - ser.event(par); - } - ser.flush(); - } catch (XmlPullParserException ex) { - throw new AndrolibException("Could not decode XML", ex); - } catch (IOException ex) { - throw new AndrolibException("Could not decode XML", ex); - } - } - - public void decodeManifest(InputStream in, OutputStream out) - throws AndrolibException { - mOptimizeForManifest = true; - try { - decode(in, out); - } finally { - mOptimizeForManifest = false; - } - } - - private final XmlPullParser mParser; - private final ExtXmlSerializer mSerial; - - private boolean mOptimizeForManifest = false; - - private final static Logger LOGGER = - Logger.getLogger(XmlPullStreamDecoder.class.getName()); -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java b/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java deleted file mode 100644 index 1d042b10..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.util; - -import brut.directory.Directory; -import brut.directory.DirectoryException; -import brut.directory.FileDirectory; -import brut.directory.ZipRODirectory; -import java.io.File; -import java.net.URI; - -/** - * @author Ryszard Wiśniewski - */ -public class ExtFile extends File { - public ExtFile(File file) { - super(file.getPath()); - } - - public ExtFile(URI uri) { - super(uri); - } - - public ExtFile(File parent, String child) { - super(parent, child); - } - - public ExtFile(String parent, String child) { - super(parent, child); - } - - public ExtFile(String pathname) { - super(pathname); - } - - public Directory getDirectory() throws DirectoryException { - if (mDirectory == null) { - if (isDirectory()) { - mDirectory = new FileDirectory(this); - } else { - mDirectory = new ZipRODirectory(this); - } - } - return mDirectory; - } - - - private Directory mDirectory; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java b/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java deleted file mode 100644 index 4fd001da..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.util; - -import java.io.*; -import org.xmlpull.mxp1_serializer.MXSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer { - @Override - public void startDocument(String encoding, Boolean standalone) throws - IOException, IllegalArgumentException, IllegalStateException { - super.startDocument(encoding != null ? encoding : mDefaultEncoding, - standalone); - this.newLine(); - } - - @Override - protected void writeAttributeValue(String value, Writer out) - throws IOException { - if (mIsDisabledAttrEscape) { - out.write(value); - return; - } - super.writeAttributeValue(value, out); - } - - @Override - public void setOutput(OutputStream os, String encoding) throws IOException { - super.setOutput(os, encoding != null ? encoding : mDefaultEncoding); - } - - @Override - public Object getProperty(String name) throws IllegalArgumentException { - if (PROPERTY_DEFAULT_ENCODING.equals(name)) { - return mDefaultEncoding; - } - return super.getProperty(name); - } - - @Override - public void setProperty(String name, Object value) - throws IllegalArgumentException, IllegalStateException { - if (PROPERTY_DEFAULT_ENCODING.equals(name)) { - mDefaultEncoding = (String) value; - } else { - super.setProperty(name, value); - } - } - - public ExtXmlSerializer newLine() throws IOException { - super.out.write(lineSeparator); - return this; - } - - public void setDisabledAttrEscape(boolean disabled) { - mIsDisabledAttrEscape = disabled; - } - - private String mDefaultEncoding; - private boolean mIsDisabledAttrEscape = false; - -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java b/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java deleted file mode 100644 index 3c8ffed6..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.util; - -import java.io.IOException; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public interface ExtXmlSerializer extends XmlSerializer { - - public ExtXmlSerializer newLine() throws IOException; - public void setDisabledAttrEscape(boolean disabled); - - public static final String PROPERTY_SERIALIZER_INDENTATION = - "http://xmlpull.org/v1/doc/properties.html#serializer-indentation"; - public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = - "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator"; - public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING"; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java b/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java deleted file mode 100644 index 00d6a0ca..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.xml; - -import brut.androlib.AndrolibException; -import brut.androlib.res.data.ResResource; -import java.io.IOException; -import org.xmlpull.v1.XmlSerializer; - -/** - * @author Ryszard Wiśniewski - */ -public interface ResValuesXmlSerializable { - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java b/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java deleted file mode 100644 index 86d40a50..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.xml; - -import brut.androlib.AndrolibException; - -/** - * @author Ryszard Wiśniewski - */ -public interface ResXmlEncodable { - public String encodeAsResXmlAttr() throws AndrolibException; - public String encodeAsResXmlValue() throws AndrolibException; -} diff --git a/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java b/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java deleted file mode 100644 index 46226e39..00000000 --- a/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java +++ /dev/null @@ -1,202 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.res.xml; - -import java.awt.event.KeyEvent; -import java.util.ArrayList; -import java.util.List; - -/** - * @author Ryszard Wiśniewski - */ -public final class ResXmlEncoders { - - public static String escapeXmlChars(String str) { - return str.replace("&", "&").replace("<", "<"); - } - - public static String encodeAsResXmlAttr(String str) { - if (str.isEmpty()) { - return str; - } - - char[] chars = str.toCharArray(); - StringBuilder out = new StringBuilder(str.length() + 10); - - switch (chars[0]) { - case '#': - case '@': - case '?': - out.append('\\'); - } - - for (char c : chars) { - switch (c) { - case '\\': - out.append('\\'); - break; - case '"': - out.append("""); - continue; - case '\n': - out.append("\\n"); - continue; - default: - if (!isPrintableChar(c)) { - out.append(String.format("\\u%04x", (int) c)); - continue; - } - } - out.append(c); - } - - return out.toString(); - } - - public static String encodeAsXmlValue(String str) { - if (str.isEmpty()) { - return str; - } - - char[] chars = str.toCharArray(); - StringBuilder out = new StringBuilder(str.length() + 10); - - switch (chars[0]) { - case '#': - case '@': - case '?': - out.append('\\'); - } - - boolean isInStyleTag = false; - int startPos = 0; - boolean enclose = false; - boolean wasSpace = true; - for (char c : chars) { - if (isInStyleTag) { - if (c == '>') { - isInStyleTag = false; - startPos = out.length() + 1; - enclose = false; - } - } else if (c == ' ') { - if (wasSpace) { - enclose = true; - } - wasSpace = true; - } else { - wasSpace = false; - switch (c) { - case '\\': - out.append('\\'); - break; - case '\'': - case '\n': - enclose = true; - break; - case '"': - out.append('\\'); - break; - case '<': - isInStyleTag = true; - if (enclose) { - out.insert(startPos, '"').append('"'); - } - break; - default: - if (!isPrintableChar(c)) { - out.append(String.format("\\u%04x", (int) c)); - continue; - } - } - } - out.append(c); - } - - if (enclose || wasSpace) { - out.insert(startPos, '"').append('"'); - } - - return out.toString(); - } - - public static boolean hasMultipleNonPositionalSubstitutions(String str) { - return findNonPositionalSubstitutions(str, 2).size() > 1; - } - - public static String enumerateNonPositionalSubstitutions(String str) { - List subs = findNonPositionalSubstitutions(str, -1); - if (subs.size() < 2) { - return str; - } - - StringBuilder out = new StringBuilder(); - int pos = 0; - int count = 0; - for (Integer sub : subs) { - out.append(str.substring(pos, ++sub)).append(++count).append('$'); - pos = sub; - } - out.append(str.substring(pos)); - - return out.toString(); - } - - /** - * It searches for "%", but not "%%" nor "%(\d)+\$" - */ - private static List findNonPositionalSubstitutions(String str, - int max) { - int pos = 0; - int pos2 = 0; - int count = 0; - int length = str.length(); - List ret = new ArrayList(); - while((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) { - if (pos2 == length) { - break; - } - char c = str.charAt(pos2++); - if (c == '%') { - 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); - if (max != -1 && ++count >= max) { - break; - } - } - - return ret; - } - - private static boolean isPrintableChar(char c) { - Character.UnicodeBlock block = Character.UnicodeBlock.of(c); - return !Character.isISOControl(c) - && c != KeyEvent.CHAR_UNDEFINED - && block != null - && block != Character.UnicodeBlock.SPECIALS; - } -} diff --git a/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java b/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java deleted file mode 100644 index 86c89366..00000000 --- a/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java +++ /dev/null @@ -1,227 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.src; - -import brut.androlib.AndrolibException; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.jf.dexlib.Code.Analysis.RegisterType; -import org.jf.dexlib.Code.Opcode; - -/** - * @author Ryszard Wiśniewski - */ -public class DebugInjector { - - public static void inject(ListIterator it, StringBuilder out) - throws AndrolibException { - new DebugInjector(it, out).inject(); - } - - private DebugInjector(ListIterator it, StringBuilder out) { - mIt = it; - mOut = out; - } - - private void inject() throws AndrolibException { - String definition = nextAndAppend(); - if ( - definition.contains(" abstract ") || - definition.contains(" native ") - ) { - nextAndAppend(); - return; - } - injectParameters(definition); - - boolean end = false; - while (!end) { - end = step(); - } - } - - private void injectParameters(String definition) throws AndrolibException { - int pos = definition.indexOf('('); - if (pos == -1) { - throw new AndrolibException(); - } - int pos2 = definition.indexOf(')', pos); - if (pos2 == -1) { - throw new AndrolibException(); - } - String params = definition.substring(pos + 1, pos2); - - int i = definition.contains(" static ") ? 0 : 1; - int argc = TypeName.listFromInternalName(params).size() + i; - while(i < argc) { - mOut.append(".parameter \"p").append(i).append("\"\n"); - i++; - } - } - - private boolean step() { - String line = next(); - if (line.isEmpty()) { - return false; - } - - switch (line.charAt(0)) { - case '#': - return processComment(line); - case ':': - append(line); - return false; - case '.': - return processDirective(line); - default: - return processInstruction(line); - } - } - - private boolean processComment(String line) { - if (mFirstInstruction) { - return false; - } - - Matcher m = REGISTER_INFO_PATTERN.matcher(line); - - while (m.find()) { - String localName = m.group(1); - String localType = null; - switch (RegisterType.Category.valueOf(m.group(2))) { - case Reference: - case Null: - case UninitRef: - case UninitThis: - localType = "Ljava/lang/Object;"; - break; - case Boolean: - localType = "Z"; - break; - case Integer: - case One: - case Unknown: - localType = "I"; - break; - case Uninit: - case Conflicted: - if (mInitializedRegisters.remove(localName)) { - mOut.append(".end local ").append(localName) - .append('\n'); - } - continue; - case Short: - case PosShort: - localType = "S"; - break; - case Byte: - case PosByte: - localType = "B"; - break; - case Char: - localType = "C"; - break; - case Float: - localType = "F"; - break; - case LongHi: - case LongLo: - localType = "J"; - break; - case DoubleHi: - case DoubleLo: - localType = "D"; - break; - default: - assert false; - } - - mInitializedRegisters.add(localName); - mOut.append(".local ").append(localName).append(", ") - .append(localName).append(':').append(localType).append('\n'); - } - - return false; - } - - private boolean processDirective(String line) { - String line2 = line.substring(1); - if ( - line2.startsWith("line ") || - line2.equals("prologue") || - line2.startsWith("parameter") || - line2.startsWith("local ") || - line2.startsWith("end local ") - ) { - return false; - } - - append(line); - if (line2.equals("end method")) { - return true; - } - if ( - line2.startsWith("annotation ") || - line2.equals("sparse-switch") || - line2.startsWith("packed-switch ") || - line2.startsWith("array-data ") - ) { - while(true) { - line2 = nextAndAppend(); - if (line2.startsWith(".end ")) { - break; - } - } - } - return false; - } - - private boolean processInstruction(String line) { - if (mFirstInstruction) { - mOut.append(".prologue\n"); - mFirstInstruction = false; - } - mOut.append(".line ").append(mIt.nextIndex()).append('\n') - .append(line).append('\n'); - - return false; - } - - private String next() { - return mIt.next().trim(); - } - - private String nextAndAppend() { - String line = next(); - append(line); - return line; - } - - private void append(String append) { - mOut.append(append).append('\n'); - } - - private final ListIterator mIt; - private final StringBuilder mOut; - - private boolean mFirstInstruction = true; - private final Set mInitializedRegisters = new HashSet(); - - private static final Pattern REGISTER_INFO_PATTERN = - Pattern.compile("((?:p|v)\\d+)=\\(([^)]+)\\);"); -} diff --git a/apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java b/apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java deleted file mode 100644 index f8bbf003..00000000 --- a/apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.src; - -import brut.androlib.AndrolibException; -import brut.androlib.mod.SmaliMod; -import java.io.*; -import org.antlr.runtime.RecognitionException; -import org.jf.dexlib.CodeItem; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.ByteArrayAnnotatedOutput; - -/** - * @author Ryszard Wiśniewski - */ -public class DexFileBuilder { - public void addSmaliFile(File smaliFile) throws AndrolibException { - try { - addSmaliFile(new FileInputStream(smaliFile), - smaliFile.getAbsolutePath()); - } catch (FileNotFoundException ex) { - throw new AndrolibException(ex); - } - } - - public void addSmaliFile(InputStream smaliStream, String name) - throws AndrolibException { - try { - if (! SmaliMod.assembleSmaliFile( - smaliStream, name, mDexFile, false, false, false)) { - throw new AndrolibException( - "Could not smali file: " + smaliStream); - } - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (RecognitionException ex) { - throw new AndrolibException(ex); - } - } - - public void writeTo(File dexFile) throws AndrolibException { - try { - OutputStream out = new FileOutputStream(dexFile); - out.write(getAsByteArray()); - out.close(); - } catch (IOException ex) { - throw new AndrolibException( - "Could not write dex to file: " + dexFile, ex); - } - } - - public byte[] getAsByteArray() { - mDexFile.place(); - for (CodeItem codeItem: mDexFile.CodeItemsSection.getItems()) { - codeItem.fixInstructions(true, true); - } - - mDexFile.place(); - - ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); - mDexFile.writeTo(out); - byte[] bytes = out.toByteArray(); - - DexFile.calcSignature(bytes); - DexFile.calcChecksum(bytes); - - return bytes; - } - - private final DexFile mDexFile = new DexFile(); -} diff --git a/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java b/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java deleted file mode 100644 index 8fd32808..00000000 --- a/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.src; - -import brut.androlib.AndrolibException; -import brut.androlib.res.util.ExtFile; -import brut.directory.DirectoryException; -import java.io.*; -import java.util.HashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Logger; -import org.apache.commons.io.IOUtils; - -/** - * @author Ryszard Wiśniewski - */ -public class SmaliBuilder { - - public static void build(ExtFile smaliDir, File dexFile, - HashMap flags) - throws AndrolibException { - new SmaliBuilder(smaliDir, dexFile, flags).build(); - } - - private SmaliBuilder(ExtFile smaliDir, File dexFile, HashMap flags) { - mSmaliDir = smaliDir; - mDexFile = dexFile; - mFlags = flags; - } - - private void build() throws AndrolibException { - try { - mDexBuilder = new DexFileBuilder(); - for (String fileName : mSmaliDir.getDirectory().getFiles(true)) { - buildFile(fileName); - } - mDexBuilder.writeTo(mDexFile); - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - private void buildFile(String fileName) throws AndrolibException, - IOException { - File inFile = new File(mSmaliDir, fileName); - InputStream inStream = new FileInputStream(inFile); - - if (fileName.endsWith(".smali")) { - mDexBuilder.addSmaliFile(inFile); - return; - } - if (! fileName.endsWith(".java")) { - LOGGER.warning("Unknown file type, ignoring: " + inFile); - return; - } - - StringBuilder out = new StringBuilder(); - List lines = IOUtils.readLines(inStream); - - if (!mFlags.containsKey("debug")) { - final String[] linesArray = lines.toArray(new String[0]); - for (int i = 2; i < linesArray.length - 2; i++) { - out.append(linesArray[i]).append('\n'); - } - } else { - lines.remove(lines.size() - 1); - lines.remove(lines.size() - 1); - ListIterator it = lines.listIterator(2); - - out.append(".source \"").append(inFile.getName()).append("\"\n"); - while (it.hasNext()) { - String line = it.next().trim(); - if (line.isEmpty() || line.charAt(0) == '#' || - line.startsWith(".source")) { - continue; - } - if (line.startsWith(".method ")) { - it.previous(); - DebugInjector.inject(it, out); - continue; - } - - out.append(line).append('\n'); - } - } - mDexBuilder.addSmaliFile( - IOUtils.toInputStream(out.toString()), fileName); - } - - private final ExtFile mSmaliDir; - private final File mDexFile; - private final HashMap mFlags; - - private DexFileBuilder mDexBuilder; - - - private final static Logger LOGGER = - Logger.getLogger(SmaliBuilder.class.getName()); -} diff --git a/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java b/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java deleted file mode 100644 index 134fb885..00000000 --- a/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.src; - -import brut.androlib.AndrolibException; -import java.io.File; -import java.io.IOException; -import org.jf.baksmali.baksmali; -import org.jf.baksmali.main; -import org.jf.dexlib.Code.Analysis.ClassPath; -import org.jf.dexlib.DexFile; - -/** - * @author Ryszard Wiśniewski - */ -public class SmaliDecoder { - - public static void decode(File apkFile, File outDir, boolean debug, boolean bakdeb) - throws AndrolibException { - new SmaliDecoder(apkFile, outDir, debug, bakdeb).decode(); - } - - private SmaliDecoder(File apkFile, File outDir, boolean debug, boolean bakdeb) { - mApkFile = apkFile; - mOutDir = outDir; - mDebug = debug; - mBakDeb = bakdeb; - } - - private void decode() throws AndrolibException { - if (mDebug) { - ClassPath.dontLoadClassPath = true; - } - try { - baksmali.disassembleDexFile(mApkFile.getAbsolutePath(), - new DexFile(mApkFile), false, mOutDir.getAbsolutePath(), null, - null, null, false, true, true, mBakDeb, false, false, - mDebug ? main.DIFFPRE: 0, false, false, null); - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } - - private final File mApkFile; - private final File mOutDir; - private final boolean mDebug; - private final boolean mBakDeb; -} diff --git a/apktool-lib/src/main/java/brut/androlib/src/TypeName.java b/apktool-lib/src/main/java/brut/androlib/src/TypeName.java deleted file mode 100644 index ffc7bb1a..00000000 --- a/apktool-lib/src/main/java/brut/androlib/src/TypeName.java +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.src; - -import brut.androlib.AndrolibException; -import brut.util.Duo; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -/** - * @author Ryszard Wiśniewski - */ -public class TypeName { - public final String package_; - public final String type; - public final String innerType; - public final int array; - - public TypeName(String type, int array) { - this(null, type, null, array); - } - - public TypeName(String package_, String type, String innerType, int array) { - this.package_ = package_; - this.type = type; - this.innerType = innerType; - this.array = array; - } - - public String getShortenedName() { - return getName("java.lang".equals(package_), isFileOwner()); - } - - public String getName() { - return getName(false, false); - } - - public String getName(boolean excludePackage, boolean separateInner) { - String name = - (package_ == null || excludePackage ? "" : package_ + '.') + - type + - (innerType != null ? (separateInner ? '$' : '.') + innerType : ""); - for (int i = 0; i < array; i++) { - name += "[]"; - } - return name; - } - - public String getJavaFilePath() { - return getFilePath(isFileOwner()) + ".java"; - } - - public String getSmaliFilePath() { - return getFilePath(true) + ".smali"; - } - - public String getFilePath(boolean separateInner) { - return package_.replace('.', File.separatorChar) + File.separatorChar - + type + (separateInner && isInner() ? "$" + innerType : ""); - } - - public boolean isInner() { - return innerType != null; - } - - public boolean isArray() { - return array != 0; - } - - public boolean isFileOwner() { - if (mIsFileOwner == null) { - mIsFileOwner = true; - if (isInner()) { - char c = innerType.charAt(0); - if (c < '0' || c > '9') { - mIsFileOwner = false; - } - } - } - return mIsFileOwner; - } - - @Override - public String toString() { - return getName(); - } - - public static TypeName fromInternalName(String internal) - throws AndrolibException { - Duo duo = fetchFromInternalName(internal); - if (duo.m2 != internal.length()) { - throw new AndrolibException( - "Invalid internal name: " + internal); - } - return duo.m1; - } - - public static List listFromInternalName(String internal) - throws AndrolibException { - List types = new ArrayList(); - while (! internal.isEmpty()) { - Duo duo = fetchFromInternalName(internal); - types.add(duo.m1); - internal = internal.substring(duo.m2); - } - return types; - } - - public static Duo fetchFromInternalName(String internal) - throws AndrolibException { - String origInternal = internal; - int array = 0; - - boolean isArray = false; - do { - if (internal.isEmpty()) { - throw new AndrolibException( - "Invalid internal name: " + origInternal); - } - isArray = internal.charAt(0) == '['; - if (isArray) { - array++; - internal = internal.substring(1); - } - } while (isArray); - - int length = array + 1; - String package_ = null; - String type = null; - String innerType = null; - switch (internal.charAt(0)) { - case 'B': - type = "byte"; - break; - case 'C': - type = "char"; - break; - case 'D': - type = "double"; - break; - case 'F': - type = "float"; - break; - case 'I': - type = "int"; - break; - case 'J': - type = "long"; - break; - case 'S': - type = "short"; - break; - case 'Z': - type = "boolean"; - break; - case 'V': - type = "void"; - break; - case 'L': - int pos = internal.indexOf(';'); - if (pos == -1) { - throw new AndrolibException( - "Invalid internal name: " + origInternal); - } - length += pos; - internal = internal.substring(1, pos); - - pos = internal.lastIndexOf('/'); - if (pos == -1) { - package_ = ""; - type = internal; - } else { - package_ = internal.substring(0, pos).replace('/', '.'); - type = internal.substring(pos + 1); - } - - pos = type.indexOf('$'); - if (pos != -1) { - innerType = type.substring(pos + 1); - type = type.substring(0, pos); - } - break; - default: - throw new AndrolibException( - "Invalid internal name: " + origInternal); - } - - return new Duo( - new TypeName(package_, type, innerType, array), length); - } - - - private Boolean mIsFileOwner; -} diff --git a/apktool-lib/src/main/java/com/mindprod/ledatastream/LEDataInputStream.java b/apktool-lib/src/main/java/com/mindprod/ledatastream/LEDataInputStream.java deleted file mode 100644 index 83656341..00000000 --- a/apktool-lib/src/main/java/com/mindprod/ledatastream/LEDataInputStream.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * @(#)LEDataInputStream.java - * - * Summary: Little-Endian version of DataInputStream. - * - * Copyright: (c) 1998-2010 Roedy Green, Canadian Mind Products, http://mindprod.com - * - * Licence: This software may be copied and used freely for any purpose but military. - * http://mindprod.com/contact/nonmil.html - * - * Requires: JDK 1.1+ - * - * Created with: IntelliJ IDEA IDE. - * - * Version History: - * 1.8 2007-05-24 - */ -package com.mindprod.ledatastream; - -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * Little-Endian version of DataInputStream. - *

- * Very similar to DataInputStream except it reads - * little-endian instead of big-endian binary data. We can't extend - * DataInputStream directly since it has only final methods, though - * DataInputStream itself is not final. This forces us implement - * LEDataInputStream with a DataInputStream object, and use wrapper methods. - * - * @author Roedy Green, Canadian Mind Products - * @version 1.8 2007-05-24 - * @since 1998 - */ -public final class LEDataInputStream implements DataInput - { - // ------------------------------ CONSTANTS ------------------------------ - - /** - * undisplayed copyright notice. - * - * @noinspection UnusedDeclaration - */ - private static final String EMBEDDED_COPYRIGHT = - "copyright (c) 1999-2010 Roedy Green, Canadian Mind Products, http://mindprod.com"; - - // ------------------------------ FIELDS ------------------------------ - - /** - * to get at the big-Endian methods of a basic DataInputStream - * - * @noinspection WeakerAccess - */ - protected final DataInputStream dis; - - /** - * to get at the a basic readBytes method. - * - * @noinspection WeakerAccess - */ - protected final InputStream is; - - /** - * work array for buffering input. - * - * @noinspection WeakerAccess - */ - protected final byte[] work; - // -------------------------- PUBLIC STATIC METHODS -------------------------- - - /** - * Note. This is a STATIC method! - * - * @param in stream to read UTF chars from (endian irrelevant) - * - * @return string from stream - * @throws IOException if read fails. - */ - public static String readUTF( DataInput in ) throws IOException - { - return DataInputStream.readUTF( in ); - } - - // -------------------------- PUBLIC INSTANCE METHODS -------------------------- - - /** - * constructor. - * - * @param in binary inputstream of little-endian data. - */ - public LEDataInputStream( InputStream in ) - { - this.is = in; - this.dis = new DataInputStream( in ); - work = new byte[8]; - } - - /** - * close. - * - * @throws IOException if close fails. - */ - public final void close() throws IOException - { - dis.close(); - } - - /** - * Read bytes. Watch out, read may return fewer bytes than requested. - * - * @param ba where the bytes go. - * @param off offset in buffer, not offset in file. - * @param len count of bytes to read. - * - * @return how many bytes read. - * @throws IOException if read fails. - */ - public final int read( byte ba[], int off, int len ) throws IOException - { - // For efficiency, we avoid one layer of wrapper - return is.read( ba, off, len ); - } - - /** - * read only a one-byte boolean. - * - * @return true or false. - * @throws IOException if read fails. - * @see java.io.DataInput#readBoolean() - */ - public final boolean readBoolean() throws IOException - { - return dis.readBoolean(); - } - - /** - * read byte. - * - * @return the byte read. - * @throws IOException if read fails. - * @see java.io.DataInput#readByte() - */ - public final byte readByte() throws IOException - { - return dis.readByte(); - } - - /** - * Read on char. like DataInputStream.readChar except little endian. - * - * @return little endian 16-bit unicode char from the stream. - * @throws IOException if read fails. - */ - public final char readChar() throws IOException - { - dis.readFully( work, 0, 2 ); - return ( char ) ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) ); - } - - /** - * Read a double. like DataInputStream.readDouble except little endian. - * - * @return little endian IEEE double from the datastream. - * @throws IOException - */ - public final double readDouble() throws IOException - { - return Double.longBitsToDouble( readLong() ); - } - - /** - * Read one float. Like DataInputStream.readFloat except little endian. - * - * @return little endian IEEE float from the datastream. - * @throws IOException if read fails. - */ - public final float readFloat() throws IOException - { - return Float.intBitsToFloat( readInt() ); - } - - /** - * Read bytes until the array is filled. - * - * @see java.io.DataInput#readFully(byte[]) - */ - public final void readFully( byte ba[] ) throws IOException - { - dis.readFully( ba, 0, ba.length ); - } - - /** - * Read bytes until the count is satisfied. - * - * @throws IOException if read fails. - * @see java.io.DataInput#readFully(byte[],int,int) - */ - public final void readFully( byte ba[], - int off, - int len ) throws IOException - { - dis.readFully( ba, off, len ); - } - - /** - * Read an int, 32-bits. Like DataInputStream.readInt except little endian. - * - * @return little-endian binary int from the datastream - * @throws IOException if read fails. - */ - public final int readInt() throws IOException - { - dis.readFully( work, 0, 4 ); - return ( work[ 3 ] ) << 24 - | ( work[ 2 ] & 0xff ) << 16 - | ( work[ 1 ] & 0xff ) << 8 - | ( work[ 0 ] & 0xff ); - } - - /** - * Read a line. - * - * @return a rough approximation of the 8-bit stream as a 16-bit unicode string - * @throws IOException - * @noinspection deprecation - * @deprecated This method does not properly convert bytes to characters. Use a Reader instead with a little-endian - * encoding. - */ - public final String readLine() throws IOException - { - return dis.readLine(); - } - - /** - * read a long, 64-bits. Like DataInputStream.readLong except little endian. - * - * @return little-endian binary long from the datastream. - * @throws IOException - */ - public final long readLong() throws IOException - { - dis.readFully( work, 0, 8 ); - return ( long ) ( work[ 7 ] ) << 56 - | - /* long cast needed or shift done modulo 32 */ - ( long ) ( work[ 6 ] & 0xff ) << 48 - | ( long ) ( work[ 5 ] & 0xff ) << 40 - | ( long ) ( work[ 4 ] & 0xff ) << 32 - | ( long ) ( work[ 3 ] & 0xff ) << 24 - | ( long ) ( work[ 2 ] & 0xff ) << 16 - | ( long ) ( work[ 1 ] & 0xff ) << 8 - | ( long ) ( work[ 0 ] & 0xff ); - } - - /** - * Read short, 16-bits. Like DataInputStream.readShort except little endian. - * - * @return little endian binary short from stream. - * @throws IOException if read fails. - */ - public final short readShort() throws IOException - { - dis.readFully( work, 0, 2 ); - return ( short ) ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) ); - } - - /** - * Read UTF counted string. - * - * @return String read. - */ - public final String readUTF() throws IOException - { - return dis.readUTF(); - } - - /** - * Read an unsigned byte. Note: returns an int, even though says Byte (non-Javadoc) - * - * @throws IOException if read fails. - * @see java.io.DataInput#readUnsignedByte() - */ - public final int readUnsignedByte() throws IOException - { - return dis.readUnsignedByte(); - } - - /** - * Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort except little endian. Note, returns int - * even though it reads a short. - * - * @return little-endian int from the stream. - * @throws IOException if read fails. - */ - public final int readUnsignedShort() throws IOException - { - dis.readFully( work, 0, 2 ); - return ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) ); - } - - /** - * Skip over bytes in the stream. See the general contract of the skipBytes method of - * DataInput. - *

- * Bytes for this operation are read from the contained input stream. - * - * @param n the number of bytes to be skipped. - * - * @return the actual number of bytes skipped. - * @throws IOException if an I/O error occurs. - */ - public final int skipBytes( int n ) throws IOException - { - return dis.skipBytes( n ); - } - } \ No newline at end of file diff --git a/apktool-lib/src/main/java/org/xmlpull/mxp1_serializer/MXSerializer.java b/apktool-lib/src/main/java/org/xmlpull/mxp1_serializer/MXSerializer.java deleted file mode 100644 index 7a4f881b..00000000 --- a/apktool-lib/src/main/java/org/xmlpull/mxp1_serializer/MXSerializer.java +++ /dev/null @@ -1,1144 +0,0 @@ -package org.xmlpull.mxp1_serializer; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; - -import org.xmlpull.v1.XmlSerializer; - -/** - * Implementation of XmlSerializer interface from XmlPull V1 API. - * This implementation is optimzied for performance and low memory footprint. - * - *

Implemented features:

    - *
  • FEATURE_NAMES_INTERNED - when enabled all returned names - * (namespaces, prefixes) will be interned and it is required that - * all names passed as arguments MUST be interned - *
  • FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE - *
- *

Implemented properties:

    - *
  • PROPERTY_SERIALIZER_INDENTATION - *
  • PROPERTY_SERIALIZER_LINE_SEPARATOR - *
- * - */ -public class MXSerializer implements XmlSerializer { - protected final static String XML_URI = "http://www.w3.org/XML/1998/namespace"; - protected final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; - private static final boolean TRACE_SIZING = false; - private static final boolean TRACE_ESCAPING = false; - - protected final String FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE = - "http://xmlpull.org/v1/doc/features.html#serializer-attvalue-use-apostrophe"; - protected final String FEATURE_NAMES_INTERNED = - "http://xmlpull.org/v1/doc/features.html#names-interned"; - protected final String PROPERTY_SERIALIZER_INDENTATION = - "http://xmlpull.org/v1/doc/properties.html#serializer-indentation"; - protected final String PROPERTY_SERIALIZER_LINE_SEPARATOR = - "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator"; - protected final static String PROPERTY_LOCATION = - "http://xmlpull.org/v1/doc/properties.html#location"; - - // properties/features - protected boolean namesInterned; - protected boolean attributeUseApostrophe; - protected String indentationString = null; //" "; - protected String lineSeparator = "\n"; - - protected String location; - protected Writer out; - - protected int autoDeclaredPrefixes; - - protected int depth = 0; - - // element stack - protected String elNamespace[] = new String[ 2 ]; - protected String elName[] = new String[ elNamespace.length ]; - protected String elPrefix[] = new String[ elNamespace.length ]; - protected int elNamespaceCount[] = new int[ elNamespace.length ]; - - //namespace stack - protected int namespaceEnd = 0; - protected String namespacePrefix[] = new String[ 8 ]; - protected String namespaceUri[] = new String[ namespacePrefix.length ]; - - protected boolean finished; - protected boolean pastRoot; - protected boolean setPrefixCalled; - protected boolean startTagIncomplete; - - protected boolean doIndent; - protected boolean seenTag; - - protected boolean seenBracket; - protected boolean seenBracketBracket; - - // buffer output if neede to write escaped String see text(String) - private static final int BUF_LEN = Runtime.getRuntime().freeMemory() > 1000000L ? 8*1024 : 256; - protected char buf[] = new char[ BUF_LEN ]; - - - protected static final String precomputedPrefixes[]; - - static { - precomputedPrefixes = new String[32]; //arbitrary number ... - for (int i = 0; i < precomputedPrefixes.length; i++) - { - precomputedPrefixes[i] = ("n"+i).intern(); - } - } - - private boolean checkNamesInterned = false; - - private void checkInterning(String name) { - if(namesInterned && name != name.intern()) { - throw new IllegalArgumentException( - "all names passed as arguments must be interned" - +"when NAMES INTERNED feature is enabled"); - } - } - - protected void reset() { - location = null; - out = null; - autoDeclaredPrefixes = 0; - depth = 0; - - // nullify references on all levels to allow it to be GCed - for (int i = 0; i < elNamespaceCount.length; i++) - { - elName[ i ] = null; - elPrefix[ i ] = null; - elNamespace[ i ] = null; - elNamespaceCount[ i ] = 2; - } - - - namespaceEnd = 0; - - - //NOTE: no need to intern() as all literal strings and string-valued constant expressions - //are interned. String literals are defined in 3.10.5 of the Java Language Specification - // just checking ... - //assert "xmlns" == "xmlns".intern(); - //assert XMLNS_URI == XMLNS_URI.intern(); - - //TODO: how to prevent from reporting this namespace? - // this is special namespace declared for consistensy with XML infoset - namespacePrefix[ namespaceEnd ] = "xmlns"; - namespaceUri[ namespaceEnd ] = XMLNS_URI; - ++namespaceEnd; - - namespacePrefix[ namespaceEnd ] = "xml"; - namespaceUri[ namespaceEnd ] = XML_URI; - ++namespaceEnd; - - finished = false; - pastRoot = false; - setPrefixCalled = false; - startTagIncomplete = false; - //doIntent is not changed - seenTag = false; - - seenBracket = false; - seenBracketBracket = false; - } - - - protected void ensureElementsCapacity() { - final int elStackSize = elName.length; - //assert (depth + 1) >= elName.length; - // we add at least one extra slot ... - final int newSize = (depth >= 7 ? 2 * depth : 8) + 2; // = lucky 7 + 1 //25 - if(TRACE_SIZING) { - System.err.println( - getClass().getName()+" elStackSize "+elStackSize+" ==> "+newSize); - } - final boolean needsCopying = elStackSize > 0; - String[] arr = null; - // reuse arr local variable slot - arr = new String[newSize]; - if(needsCopying) System.arraycopy(elName, 0, arr, 0, elStackSize); - elName = arr; - - arr = new String[newSize]; - if(needsCopying) System.arraycopy(elPrefix, 0, arr, 0, elStackSize); - elPrefix = arr; - - arr = new String[newSize]; - if(needsCopying) System.arraycopy(elNamespace, 0, arr, 0, elStackSize); - elNamespace = arr; - - final int[] iarr = new int[newSize]; - if(needsCopying) { - System.arraycopy(elNamespaceCount, 0, iarr, 0, elStackSize); - } else { - // special initialization - iarr[0] = 0; - } - elNamespaceCount = iarr; - } - - protected void ensureNamespacesCapacity() { //int size) { - //int namespaceSize = namespacePrefix != null ? namespacePrefix.length : 0; - //assert (namespaceEnd >= namespacePrefix.length); - - //if(size >= namespaceSize) { - //int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25 - final int newSize = namespaceEnd > 7 ? 2 * namespaceEnd : 8; - if(TRACE_SIZING) { - System.err.println( - getClass().getName()+" namespaceSize "+namespacePrefix.length+" ==> "+newSize); - } - final String[] newNamespacePrefix = new String[newSize]; - final String[] newNamespaceUri = new String[newSize]; - if(namespacePrefix != null) { - System.arraycopy( - namespacePrefix, 0, newNamespacePrefix, 0, namespaceEnd); - System.arraycopy( - namespaceUri, 0, newNamespaceUri, 0, namespaceEnd); - } - namespacePrefix = newNamespacePrefix; - namespaceUri = newNamespaceUri; - - // TODO use hashes for quick namespace->prefix lookups - // if( ! allStringsInterned ) { - // int[] newNamespacePrefixHash = new int[newSize]; - // if(namespacePrefixHash != null) { - // System.arraycopy( - // namespacePrefixHash, 0, newNamespacePrefixHash, 0, namespaceEnd); - // } - // namespacePrefixHash = newNamespacePrefixHash; - // } - //prefixesSize = newSize; - // ////assert nsPrefixes.length > size && nsPrefixes.length == newSize - //} - } - - - public void setFeature(String name, - boolean state) throws IllegalArgumentException, IllegalStateException - { - if(name == null) { - throw new IllegalArgumentException("feature name can not be null"); - } - if(FEATURE_NAMES_INTERNED.equals(name)) { - namesInterned = state; - } else if(FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) { - attributeUseApostrophe = state; - } else { - throw new IllegalStateException("unsupported feature "+name); - } - } - - public boolean getFeature(String name) throws IllegalArgumentException - { - if(name == null) { - throw new IllegalArgumentException("feature name can not be null"); - } - if(FEATURE_NAMES_INTERNED.equals(name)) { - return namesInterned; - } else if(FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) { - return attributeUseApostrophe; - } else { - return false; - } - } - - // precomputed variables to simplify writing indentation - protected int offsetNewLine; - protected int indentationJump; - protected char[] indentationBuf; - protected int maxIndentLevel; - protected boolean writeLineSepartor; //should end-of-line be written - protected boolean writeIndentation; // is indentation used? - - /** - * For maximum efficiency when writing indents the required output is pre-computed - * This is internal function that recomputes buffer after user requested chnages. - */ - protected void rebuildIndentationBuf() { - if(doIndent == false) return; - final int maxIndent = 65; //hardcoded maximum indentation size in characters - int bufSize = 0; - offsetNewLine = 0; - if(writeLineSepartor) { - offsetNewLine = lineSeparator.length(); - bufSize += offsetNewLine; - } - maxIndentLevel = 0; - if(writeIndentation) { - indentationJump = indentationString.length(); - maxIndentLevel = maxIndent / indentationJump; - bufSize += maxIndentLevel * indentationJump; - } - if(indentationBuf == null || indentationBuf.length < bufSize) { - indentationBuf = new char[bufSize + 8]; - } - int bufPos = 0; - if(writeLineSepartor) { - for (int i = 0; i < lineSeparator.length(); i++) - { - indentationBuf[ bufPos++ ] = lineSeparator.charAt(i); - } - } - if(writeIndentation) { - for (int i = 0; i < maxIndentLevel; i++) - { - for (int j = 0; j < indentationString.length(); j++) - { - indentationBuf[ bufPos++ ] = indentationString.charAt(j); - } - } - } - } - - // if(doIndent) writeIndent(); - protected void writeIndent() throws IOException { - final int start = writeLineSepartor ? 0 : offsetNewLine; - final int level = (depth > maxIndentLevel) ? maxIndentLevel : depth; - out.write( indentationBuf, start, ( (level - 1) * indentationJump) + offsetNewLine); - } - - public void setProperty(String name, - Object value) throws IllegalArgumentException, IllegalStateException - { - if(name == null) { - throw new IllegalArgumentException("property name can not be null"); - } - if(PROPERTY_SERIALIZER_INDENTATION.equals(name)) { - indentationString = (String)value; - } else if(PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) { - lineSeparator = (String)value; - } else if(PROPERTY_LOCATION.equals(name)) { - location = (String) value; - } else { - throw new IllegalStateException("unsupported property "+name); - } - writeLineSepartor = lineSeparator != null && lineSeparator.length() > 0; - writeIndentation = indentationString != null && indentationString.length() > 0; - // optimize - do not write when nothing to write ... - doIndent = indentationString != null && (writeLineSepartor || writeIndentation); - //NOTE: when indentationString == null there is no indentation - // (even though writeLineSeparator may be true ...) - rebuildIndentationBuf(); - seenTag = false; // for consistency - } - - public Object getProperty(String name) throws IllegalArgumentException - { - if(name == null) { - throw new IllegalArgumentException("property name can not be null"); - } - if(PROPERTY_SERIALIZER_INDENTATION.equals(name)) { - return indentationString; - } else if(PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) { - return lineSeparator; - } else if(PROPERTY_LOCATION.equals(name)) { - return location; - } else { - return null; - } - } - - private String getLocation() { - return location != null ? " @"+location : ""; - } - - // this is special method that can be accessed directly to retrieve Writer serializer is using - public Writer getWriter() - { - return out; - } - - public void setOutput(Writer writer) - { - reset(); - out = writer; - } - - public void setOutput(OutputStream os, String encoding) throws IOException - { - if(os == null) throw new IllegalArgumentException("output stream can not be null"); - reset(); - if(encoding != null) { - out = new OutputStreamWriter(os, encoding); - } else { - out = new OutputStreamWriter(os); - } - } - - public void startDocument (String encoding, Boolean standalone) throws IOException - { - char apos = attributeUseApostrophe ? '\'' : '"'; - if(attributeUseApostrophe) { - out.write(""); - } - - public void endDocument() throws IOException - { - // close all unclosed tag; - while(depth > 0) { - endTag(elNamespace[ depth ], elName[ depth ]); - } - //assert depth == 0; - //assert startTagIncomplete == false; - finished = pastRoot = startTagIncomplete = true; - out.flush(); - } - - public void setPrefix(String prefix, String namespace) throws IOException - { - if(startTagIncomplete) closeStartTag(); - //assert prefix != null; - //assert namespace != null; - if (prefix == null) { - prefix = ""; - } - if(!namesInterned) { - prefix = prefix.intern(); //will throw NPE if prefix==null - } else if(checkNamesInterned) { - checkInterning(prefix); - } else if(prefix == null) { - throw new IllegalArgumentException("prefix must be not null"+getLocation()); - } - - //check that prefix is not duplicated ... - for (int i = elNamespaceCount[ depth ]; i < namespaceEnd; i++) - { - if(prefix == namespacePrefix[ i ]) { - throw new IllegalStateException("duplicated prefix "+printable(prefix)+getLocation()); - } - } - - if(!namesInterned) { - namespace = namespace.intern(); - } else if(checkNamesInterned) { - checkInterning(namespace); - } else if(namespace == null) { - throw new IllegalArgumentException("namespace must be not null"+getLocation()); - } - - if(namespaceEnd >= namespacePrefix.length) { - ensureNamespacesCapacity(); - } - namespacePrefix[ namespaceEnd ] = prefix; - namespaceUri[ namespaceEnd ] = namespace; - ++namespaceEnd; - setPrefixCalled = true; - } - - protected String lookupOrDeclarePrefix( String namespace ) { - return getPrefix(namespace, true); - } - - public String getPrefix(String namespace, boolean generatePrefix) - { - return getPrefix(namespace, generatePrefix, false); - } - - protected String getPrefix(String namespace, boolean generatePrefix, boolean nonEmpty) - { - //assert namespace != null; - if(!namesInterned) { - // when String is interned we can do much faster namespace stack lookups ... - namespace = namespace.intern(); - } else if(checkNamesInterned) { - checkInterning(namespace); - //assert namespace != namespace.intern(); - } - if(namespace == null) { - throw new IllegalArgumentException("namespace must be not null"+getLocation()); - } else if(namespace.length() == 0) { - throw new IllegalArgumentException("default namespace cannot have prefix"+getLocation()); - } - - // first check if namespace is already in scope - for (int i = namespaceEnd - 1; i >= 0 ; --i) - { - if(namespace == namespaceUri[ i ]) { - final String prefix = namespacePrefix[ i ]; - if(nonEmpty && prefix.length() == 0) continue; - // now check that prefix is still in scope - for (int p = namespaceEnd - 1; p > i ; --p) - { - if(prefix == namespacePrefix[ p ]) - continue; // too bad - prefix is redeclared with different namespace - } - return prefix; - } - } - - // so not found it ... - if(!generatePrefix) { - return null; - } - return generatePrefix(namespace); - } - - private String generatePrefix(String namespace) { - //assert namespace == namespace.intern(); - while(true) { - ++autoDeclaredPrefixes; - //fast lookup uses table that was pre-initialized in static{} .... - final String prefix = autoDeclaredPrefixes < precomputedPrefixes.length - ? precomputedPrefixes[autoDeclaredPrefixes] : ("n"+autoDeclaredPrefixes).intern(); - // make sure this prefix is not declared in any scope (avoid hiding in-scope prefixes)! - for (int i = namespaceEnd - 1; i >= 0 ; --i) - { - if(prefix == namespacePrefix[ i ]) { - continue; // prefix is already declared - generate new and try again - } - } - // declare prefix - - if(namespaceEnd >= namespacePrefix.length) { - ensureNamespacesCapacity(); - } - namespacePrefix[ namespaceEnd ] = prefix; - namespaceUri[ namespaceEnd ] = namespace; - ++namespaceEnd; - - return prefix; - } - } - - public int getDepth() - { - return depth; - } - - public String getNamespace () - { - return elNamespace[depth]; - } - - public String getName() - { - return elName[depth]; - } - - public XmlSerializer startTag (String namespace, String name) throws IOException - { - if(startTagIncomplete) { - closeStartTag(); - } - seenBracket = seenBracketBracket = false; - ++depth; - if(doIndent && depth > 0 && seenTag) { - writeIndent(); - } - seenTag = true; - setPrefixCalled = false; - startTagIncomplete = true; - if( (depth + 1) >= elName.length) { - ensureElementsCapacity(); - } - ////assert namespace != null; - - if(checkNamesInterned && namesInterned) checkInterning(namespace); - elNamespace[ depth ] = (namesInterned || namespace == null) ? namespace : namespace.intern(); - //assert name != null; - //elName[ depth ] = name; - if(checkNamesInterned && namesInterned) checkInterning(name); - elName[ depth ] = (namesInterned || name == null) ? name : name.intern(); - if(out == null) { - throw new IllegalStateException("setOutput() must called set before serialization can start"); - } - out.write('<'); - if(namespace != null) { - if(namespace.length() > 0) { - //ALEK: in future make this algo a feature on serializer - String prefix = null; - if(depth > 0 && (namespaceEnd - elNamespaceCount[depth-1]) == 1) { - // if only one prefix was declared un-declare it if the prefix is already declared on parent el with the same URI - String uri = namespaceUri[namespaceEnd-1]; - if(uri == namespace || uri.equals(namespace)) { - String elPfx = namespacePrefix[namespaceEnd-1]; - // 2 == to skip predefined namesapces (xml and xmlns ...) - for(int pos = elNamespaceCount[depth-1] - 1; pos >= 2; --pos ) { - String pf = namespacePrefix[pos]; - if(pf == elPfx || pf.equals(elPfx)) { - String n = namespaceUri[pos]; - if(n == uri || n.equals(uri)) { - --namespaceEnd; //un-declare namespace: this is kludge! - prefix = elPfx; - } - break; - } - } - } - } - if(prefix == null) { - prefix = lookupOrDeclarePrefix( namespace ); - } - //assert prefix != null; - // make sure that default ("") namespace to not print ":" - if(prefix.length() > 0) { - elPrefix[ depth ] = prefix; - out.write(prefix); - out.write(':'); - } else { - elPrefix[ depth ] = ""; - } - } else { - // make sure that default namespace can be declared - for (int i = namespaceEnd - 1; i >= 0 ; --i) { - if(namespacePrefix[ i ] == "") { - final String uri = namespaceUri[ i ]; - if(uri == null) { - // declare default namespace - setPrefix("", ""); - } else if(uri.length() > 0) { - throw new IllegalStateException( - "start tag can not be written in empty default namespace "+ - "as default namespace is currently bound to '"+uri+"'"+getLocation()); - } - break; - } - } - elPrefix[ depth ] = ""; - } - } else { - elPrefix[ depth ] = ""; - } - out.write(name); - return this; - } - - public XmlSerializer attribute (String namespace, String name, - String value) throws IOException - { - if(!startTagIncomplete) { - throw new IllegalArgumentException("startTag() must be called before attribute()"+getLocation()); - } - //assert setPrefixCalled == false; - out.write(' '); - ////assert namespace != null; - if(namespace != null && namespace.length() > 0) { - //namespace = namespace.intern(); - if(!namesInterned) { - namespace = namespace.intern(); - } else if(checkNamesInterned) { - checkInterning(namespace); - } - String prefix = getPrefix( namespace, false, true ); - //assert( prefix != null); - //if(prefix.length() == 0) { - if(prefix == null) { - // needs to declare prefix to hold default namespace - //NOTE: attributes such as a='b' are in NO namespace - prefix = generatePrefix(namespace); - } - out.write(prefix); - out.write(':'); - // if(prefix.length() > 0) { - // out.write(prefix); - // out.write(':'); - // } - } - //assert name != null; - out.write(name); - out.write('='); - //assert value != null; - out.write( attributeUseApostrophe ? '\'' : '"'); - writeAttributeValue(value, out); - out.write( attributeUseApostrophe ? '\'' : '"'); - return this; - } - - protected void closeStartTag() throws IOException { - if(finished) { - throw new IllegalArgumentException("trying to write past already finished output"+getLocation()); - } - if(seenBracket) { - seenBracket = seenBracketBracket = false; - } - if( startTagIncomplete || setPrefixCalled ) { - if(setPrefixCalled) { - throw new IllegalArgumentException( - "startTag() must be called immediately after setPrefix()"+getLocation()); - } - if(!startTagIncomplete) { - throw new IllegalArgumentException("trying to close start tag that is not opened"+getLocation()); - } - - // write all namespace delcarations! - writeNamespaceDeclarations(); - out.write('>'); - elNamespaceCount[ depth ] = namespaceEnd; - startTagIncomplete = false; - } - } - - protected void writeNamespaceDeclarations() throws IOException - { - //int start = elNamespaceCount[ depth - 1 ]; - for (int i = elNamespaceCount[ depth - 1 ]; i < namespaceEnd; i++) - { - if(doIndent && namespaceUri[ i ].length() > 40) { - writeIndent(); - out.write(" "); - } - if(namespacePrefix[ i ] != "") { - out.write(" xmlns:"); - out.write(namespacePrefix[ i ]); - out.write('='); - } else { - out.write(" xmlns="); - } - out.write( attributeUseApostrophe ? '\'' : '"'); - - //NOTE: escaping of namespace value the same way as attributes!!!! - writeAttributeValue(namespaceUri[ i ], out); - - out.write( attributeUseApostrophe ? '\'' : '"'); - } - } - - public XmlSerializer endTag(String namespace, String name) throws IOException - { - // check that level is valid - ////assert namespace != null; - //if(namespace != null) { - // namespace = namespace.intern(); - //} - seenBracket = seenBracketBracket = false; - if(namespace != null) { - if(!namesInterned) { - namespace = namespace.intern(); - } else if(checkNamesInterned) { - checkInterning(namespace); - } - } - - if(namespace != elNamespace[ depth ]) - { - throw new IllegalArgumentException( - "expected namespace "+printable(elNamespace[ depth ]) - +" and not "+printable(namespace)+getLocation()); - } - if(name == null) { - throw new IllegalArgumentException("end tag name can not be null"+getLocation()); - } - if(checkNamesInterned && namesInterned) { - checkInterning(name); - } - String startTagName = elName[ depth ]; - if((!namesInterned && !name.equals(startTagName)) - || (namesInterned && name != startTagName )) - { - throw new IllegalArgumentException( - "expected element name "+printable(elName[ depth ])+" and not "+printable(name)+getLocation()); - } - if(startTagIncomplete) { - writeNamespaceDeclarations(); - out.write(" />"); //space is added to make it easier to work in XHTML!!! - --depth; - } else { - //assert startTagIncomplete == false; - if(doIndent && seenTag) { writeIndent(); } - out.write(" 0) { - out.write(startTagPrefix); - out.write(':'); - } - - // if(namespace != null && namespace.length() > 0) { - // //TODO prefix should be alredy known from matching start tag ... - // final String prefix = lookupOrDeclarePrefix( namespace ); - // //assert( prefix != null); - // if(prefix.length() > 0) { - // out.write(prefix); - // out.write(':'); - // } - // } - out.write(name); - out.write('>'); - --depth; - } - namespaceEnd = elNamespaceCount[ depth ]; - startTagIncomplete = false; - seenTag = true; - return this; - } - - public XmlSerializer text (String text) throws IOException - { - //assert text != null; - if(startTagIncomplete || setPrefixCalled) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - writeElementContent(text, out); - return this; - } - - public XmlSerializer text (char [] buf, int start, int len) throws IOException - { - if(startTagIncomplete || setPrefixCalled) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - writeElementContent(buf, start, len, out); - return this; - } - - public void cdsect (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - out.write(""); - } - - public void entityRef (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - out.write('&'); - out.write(text); //escape? - out.write(';'); - } - - public void processingInstruction (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - out.write(""); - } - - public void comment (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - out.write(""); - } - - public void docdecl (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - out.write(""); - } - - public void ignorableWhitespace (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - if(text.length() == 0) { - throw new IllegalArgumentException( - "empty string is not allowed for ignorable whitespace"+getLocation()); - } - out.write(text); //no escape? - } - - public void flush () throws IOException - { - if(!finished && startTagIncomplete) closeStartTag(); - out.flush(); - } - - // --- utility methods - - protected void writeAttributeValue(String value, Writer out) throws IOException - { - //.[apostrophe and <, & escaped], - final char quot = attributeUseApostrophe ? '\'' : '"'; - final String quotEntity = attributeUseApostrophe ? "'" : """; - - int pos = 0; - for (int i = 0; i < value.length(); i++) - { - char ch = value.charAt(i); - if(ch == '&') { - if(i > pos) out.write(value.substring(pos, i)); - out.write("&"); - pos = i + 1; - } if(ch == '<') { - if(i > pos) out.write(value.substring(pos, i)); - out.write("<"); - pos = i + 1; - }else if(ch == quot) { - if(i > pos) out.write(value.substring(pos, i)); - out.write(quotEntity); - pos = i + 1; - } else if(ch < 32) { - //in XML 1.0 only legal character are #x9 | #xA | #xD - // and they must be escaped otherwise in attribute value they are normalized to spaces - if(ch == 13 || ch == 10 || ch == 9) { - if(i > pos) out.write(value.substring(pos, i)); - out.write("&#"); - out.write(Integer.toString(ch)); - out.write(';'); - pos = i + 1; - } else { - if(TRACE_ESCAPING) System.err.println(getClass().getName()+" DEBUG ATTR value.len="+value.length()+" "+printable(value)); - - throw new IllegalStateException( - //"character "+Integer.toString(ch)+" is not allowed in output"+getLocation()); - "character "+printable(ch)+" ("+Integer.toString(ch)+") is not allowed in output"+getLocation() - +" (attr value="+printable(value)+")"); - // in XML 1.1 legal are [#x1-#xD7FF] - // if(ch > 0) { - // if(i > pos) out.write(text.substring(pos, i)); - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - // } else { - // throw new IllegalStateException( - // "character zero is not allowed in XML 1.1 output"+getLocation()); - // } - } - } - } - if(pos > 0) { - out.write(value.substring(pos)); - } else { - out.write(value); // this is shortcut to the most common case - } - - } - - protected void writeElementContent(String text, Writer out) throws IOException - { - // esccape '<', '&', ']]>', <32 if necessary - int pos = 0; - for (int i = 0; i < text.length(); i++) - { - //TODO: check if doing char[] text.getChars() would be faster than getCharAt(i) ... - char ch = text.charAt(i); - if(ch == ']') { - if(seenBracket) { - seenBracketBracket = true; - } else { - seenBracket = true; - } - } else { - if(ch == '&') { - if(i > pos) out.write(text.substring(pos, i)); - out.write("&"); - pos = i + 1; - } else if(ch == '<') { - if(i > pos) out.write(text.substring(pos, i)); - out.write("<"); - pos = i + 1; - } else if(seenBracketBracket && ch == '>') { - if(i > pos) out.write(text.substring(pos, i)); - out.write(">"); - pos = i + 1; - } else if(ch < 32) { - //in XML 1.0 only legal character are #x9 | #xA | #xD - if( ch == 9 || ch == 10 || ch == 13) { - // pass through - - // } else if(ch == 13) { //escape - // if(i > pos) out.write(text.substring(pos, i)); - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - } else { - if(TRACE_ESCAPING) System.err.println(getClass().getName()+" DEBUG TEXT value.len="+text.length()+" "+printable(text)); - throw new IllegalStateException( - "character "+Integer.toString(ch)+" is not allowed in output"+getLocation() - +" (text value="+printable(text)+")"); - // in XML 1.1 legal are [#x1-#xD7FF] - // if(ch > 0) { - // if(i > pos) out.write(text.substring(pos, i)); - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - // } else { - // throw new IllegalStateException( - // "character zero is not allowed in XML 1.1 output"+getLocation()); - // } - } - } - if(seenBracket) { - seenBracketBracket = seenBracket = false; - } - - } - } - if(pos > 0) { - out.write(text.substring(pos)); - } else { - out.write(text); // this is shortcut to the most common case - } - - - - } - - protected void writeElementContent(char[] buf, int off, int len, Writer out) throws IOException - { - // esccape '<', '&', ']]>' - final int end = off + len; - int pos = off; - for (int i = off; i < end; i++) - { - final char ch = buf[i]; - if(ch == ']') { - if(seenBracket) { - seenBracketBracket = true; - } else { - seenBracket = true; - } - } else { - if(ch == '&') { - if(i > pos) { - out.write(buf, pos, i - pos); - } - out.write("&"); - pos = i + 1; - } else if(ch == '<') { - if(i > pos) { - out.write(buf, pos, i - pos); - } - out.write("<"); - pos = i + 1; - - } else if(seenBracketBracket && ch == '>') { - if(i > pos) { - out.write(buf, pos, i - pos); - } - out.write(">"); - pos = i + 1; - } else if(ch < 32) { - //in XML 1.0 only legal character are #x9 | #xA | #xD - if( ch == 9 || ch == 10 || ch == 13) { - // pass through - - - // } else if(ch == 13 ) { //if(ch == '\r') { - // if(i > pos) { - // out.write(buf, pos, i - pos); - // } - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - } else { - if(TRACE_ESCAPING) System.err.println( - getClass().getName()+" DEBUG TEXT value.len=" - +len+" "+printable(new String(buf,off,len))); - throw new IllegalStateException( - "character "+printable(ch)+" ("+Integer.toString(ch)+") is not allowed in output"+getLocation()); - // in XML 1.1 legal are [#x1-#xD7FF] - // if(ch > 0) { - // if(i > pos) out.write(text.substring(pos, i)); - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - // } else { - // throw new IllegalStateException( - // "character zero is not allowed in XML 1.1 output"+getLocation()); - // } - } - } - if(seenBracket) { - seenBracketBracket = seenBracket = false; - } - // assert seenBracketBracket == seenBracket == false; - } - } - if(end > pos) { - out.write(buf, pos, end - pos); - } - } - - /** simple utility method -- good for debugging */ - protected static final String printable(String s) { - if(s == null) return "null"; - StringBuffer retval = new StringBuffer(s.length() + 16); - retval.append("'"); - char ch; - for (int i = 0; i < s.length(); i++) { - addPrintable(retval, s.charAt(i)); - } - retval.append("'"); - return retval.toString(); - } - - protected static final String printable(char ch) { - StringBuffer retval = new StringBuffer(); - addPrintable(retval, ch); - return retval.toString(); - } - - private static void addPrintable(StringBuffer retval, char ch) - { - switch (ch) - { - case '\b': - retval.append("\\b"); - break; - case '\t': - retval.append("\\t"); - break; - case '\n': - retval.append("\\n"); - break; - case '\f': - retval.append("\\f"); - break; - case '\r': - retval.append("\\r"); - break; - case '\"': - retval.append("\\\""); - break; - case '\'': - retval.append("\\\'"); - break; - case '\\': - retval.append("\\\\"); - break; - default: - if (ch < 0x20 || ch > 0x7e) { - final String ss = "0000" + Integer.toString(ch, 16); - retval.append("\\u" + ss.substring(ss.length() - 4, ss.length())); - } else { - retval.append(ch); - } - } - } - -} - diff --git a/apktool-lib/src/main/resources/brut/androlib/android-framework.jar b/apktool-lib/src/main/resources/brut/androlib/android-framework.jar deleted file mode 100644 index 8b9be231..00000000 Binary files a/apktool-lib/src/main/resources/brut/androlib/android-framework.jar and /dev/null differ diff --git a/apktool-lib/src/main/resources/brut/androlib/apktool.properties b/apktool-lib/src/main/resources/brut/androlib/apktool.properties deleted file mode 100644 index 684afd27..00000000 --- a/apktool-lib/src/main/resources/brut/androlib/apktool.properties +++ /dev/null @@ -1,2 +0,0 @@ -version = ${project.version} -git.commit.id.abbrev = ${git.commit.id.abbrev} diff --git a/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java b/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java deleted file mode 100644 index 13355d0d..00000000 --- a/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib; - -import brut.androlib.res.util.ExtFile; -import brut.common.BrutException; -import brut.util.OS; -import java.io.*; -import java.util.HashMap; -import java.util.logging.Logger; -import org.custommonkey.xmlunit.*; -import org.junit.*; -import static org.junit.Assert.*; -import org.xml.sax.SAXException; - - -/** - * @author Ryszard Wiśniewski - */ -public class BuildAndDecodeTest { - - @BeforeClass - public static void beforeClass() throws BrutException { - sTmpDir = new ExtFile(OS.createTempDirectory()); - sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig"); - sTestNewDir = new ExtFile(sTmpDir, "testapp-new"); - File testApk = new File(sTmpDir, "testapp.apk"); - - LOGGER.info("Unpacking testapp..."); - TestUtils.copyResourceDir(BuildAndDecodeTest.class, - "brut/apktool/testapp/", sTestOrigDir); - - LOGGER.info("Building testapp.apk..."); - ExtFile blank = null; - new Androlib().build(sTestOrigDir, testApk, BuildAndDecodeTest.returnStock(),blank); - - LOGGER.info("Decoding testapp.apk..."); - ApkDecoder apkDecoder = new ApkDecoder(testApk); - apkDecoder.setOutDir(sTestNewDir); - apkDecoder.decode(); - } - - @AfterClass - public static void afterClass() throws BrutException { - OS.rmdir(sTmpDir); - } - - @Test - public void valuesArraysTest() throws BrutException { - compareValuesFiles("values-mcc001/arrays.xml"); - compareValuesFiles("values-mcc002/arrays.xml"); - } - - @Test - public void valuesBoolsTest() throws BrutException { - compareValuesFiles("values-mcc001/bools.xml"); - } - - @Test - public void valuesColorsTest() throws BrutException { - compareValuesFiles("values-mcc001/colors.xml"); - } - - @Test - public void valuesDimensTest() throws BrutException { - compareValuesFiles("values-mcc001/dimens.xml"); - } - - @Test - public void valuesIdsTest() throws BrutException { - compareValuesFiles("values-mcc001/ids.xml"); - } - - @Test - public void valuesIntegersTest() throws BrutException { - compareValuesFiles("values-mcc001/integers.xml"); - } - - @Test - public void valuesStringsTest() throws BrutException { - compareValuesFiles("values-mcc001/strings.xml"); - } - - @Test - public void valuesReferencesTest() throws BrutException { - compareValuesFiles("values-mcc002/strings.xml"); - } - - @Test - public void crossTypeTest() throws BrutException { - compareValuesFiles("values-mcc003/strings.xml"); - compareValuesFiles("values-mcc003/integers.xml"); - compareValuesFiles("values-mcc003/bools.xml"); - } - - @Test - public void xmlLiteralsTest() throws BrutException { - compareXmlFiles("res/xml/literals.xml"); - } - - @Test - public void xmlReferencesTest() throws BrutException { - compareXmlFiles("res/xml/references.xml"); - } - - @Test - public void qualifiersTest() throws BrutException { - compareValuesFiles("values-mcc004-mnc4-en-rUS-sw100dp-w200dp-h300dp" + - "-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key" + - "-navhidden-dpad/strings.xml"); - } - - private void compareValuesFiles(String path) throws BrutException { - compareXmlFiles("res/" + path, - new ElementNameAndAttributeQualifier("name")); - } - - private void compareXmlFiles(String path) throws BrutException { - compareXmlFiles(path, null); - } - - private void compareXmlFiles(String path, - ElementQualifier qualifier) throws BrutException { - DetailedDiff diff; - try { - Reader control = new FileReader( - new File(sTestOrigDir, path)); - Reader test = new FileReader(new File(sTestNewDir, path)); - - diff = new DetailedDiff(new Diff(control, test)); - } catch (SAXException ex) { - throw new BrutException(ex); - } catch (IOException ex) { - throw new BrutException(ex); - } - - if (qualifier != null) { - diff.overrideElementQualifier(qualifier); - } - - assertTrue(path + ": " + - diff.getAllDifferences().toString(), diff.similar()); - } - - private static HashMap returnStock() throws BrutException { - HashMap tmp = new HashMap(); - tmp.put("forceBuildAll", false); - tmp.put("debug", false); - tmp.put("verbose", false); - tmp.put("injectOriginal", false); - tmp.put("framework", false); - tmp.put("update", false); - - return tmp; - } - - private static ExtFile sTmpDir; - private static ExtFile sTestOrigDir; - private static ExtFile sTestNewDir; - - private final static Logger LOGGER = - Logger.getLogger(BuildAndDecodeTest.class.getName()); -} diff --git a/apktool-lib/src/test/java/brut/androlib/TestUtils.java b/apktool-lib/src/test/java/brut/androlib/TestUtils.java deleted file mode 100644 index d6efc275..00000000 --- a/apktool-lib/src/test/java/brut/androlib/TestUtils.java +++ /dev/null @@ -1,138 +0,0 @@ - /** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib; - -import brut.common.BrutException; -import brut.directory.*; -import java.io.*; -import java.net.URL; -import java.net.URLDecoder; -import java.util.HashMap; -import java.util.Map; -import org.custommonkey.xmlunit.ElementQualifier; -import org.w3c.dom.Element; -import org.xmlpull.v1.*; - -/** - * @author Ryszard Wiśniewski - */ -public abstract class TestUtils { - - public static Map parseStringsXml(File file) - throws BrutException { - try { - XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser(); - xpp.setInput(new FileReader(file)); - - int eventType; - String key = null; - Map map = new HashMap(); - while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) { - switch (eventType) { - case XmlPullParser.START_TAG: - if ("string".equals(xpp.getName())) { - int attrCount = xpp.getAttributeCount(); - for (int i = 0; i < attrCount; i++) { - if ("name".equals(xpp.getAttributeName(i))) { - key = xpp.getAttributeValue(i); - break; - } - } - } - break; - case XmlPullParser.END_TAG: - if ("string".equals(xpp.getName())) { - key = null; - } - break; - case XmlPullParser.TEXT: - if (key != null) { - map.put(key, xpp.getText()); - } - break; - } - } - - return map; - } catch (IOException ex) { - throw new BrutException(ex); - } catch (XmlPullParserException ex) { - throw new BrutException(ex); - } - } - - /* TODO: move to brut.util.Jar - it's not possible for now, because below - * implementation uses brut.dir. I think I should merge all my projects to - * single brut.common . - */ - public static void copyResourceDir(Class class_, String dirPath, File out) - throws BrutException { - if (! out.exists()) { - out.mkdirs(); - } - copyResourceDir(class_, dirPath, new FileDirectory(out)); - } - - public static void copyResourceDir(Class class_, String dirPath, - Directory out) throws BrutException { - if (class_ == null) { - class_ = Class.class; - } - - URL dirURL = class_.getClassLoader().getResource(dirPath); - if (dirURL != null && dirURL.getProtocol().equals("file")) { - DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out); - return; - } - - if (dirURL == null) { - String className = class_.getName().replace(".", "/") + ".class"; - dirURL = class_.getClassLoader().getResource(className); - } - - - if (dirURL.getProtocol().equals("jar")) { - String jarPath; - try { - jarPath = URLDecoder.decode(dirURL.getPath().substring( - 5, dirURL.getPath().indexOf("!")), "UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new BrutException(ex); - } - DirUtil.copyToDir(new FileDirectory(jarPath), out); - } - } - - - public static class ResValueElementQualifier implements ElementQualifier { - - public boolean qualifyForComparison(Element control, Element test) { - String controlType = control.getTagName(); - if ("item".equals(controlType)) { - controlType = control.getAttribute("type"); - } - - String testType = test.getTagName(); - if ("item".equals(testType)) { - testType = test.getAttribute("type"); - } - - return controlType.equals(testType) && control.getAttribute("name") - .equals(test.getAttribute("name")); - } - } -} diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/AndroidManifest.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/AndroidManifest.xml deleted file mode 100644 index d79f6186..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/apktool.yml b/apktool-lib/src/test/resources/brut/apktool/testapp/apktool.yml deleted file mode 100644 index dbe200bc..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/apktool.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: 1.5.0 -apkFileName: testapp.apk -isFrameworkApk: false -usesFramework: - ids: - - 1 diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/arrays.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/arrays.xml deleted file mode 100644 index 8cc3d152..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/arrays.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - TEST1 - TEST2 - TEST3 - %2$s foo %1$d - - - -1 - 0 - 1 - - - - true - TEST - 5 - 5.5 - 10.0sp - #ff123456 - - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/bools.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/bools.xml deleted file mode 100644 index 787bfcad..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/bools.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - false - true - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/colors.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/colors.xml deleted file mode 100644 index 00dfdb0f..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/colors.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #ff123456 - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/dimens.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/dimens.xml deleted file mode 100644 index 2a3a6440..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/dimens.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - 10.0dip - 10.0sp - 10.0pt - 10.0px - 10.0mm - 10.0in - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/ids.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/ids.xml deleted file mode 100644 index 1d2724aa..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/ids.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/integers.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/integers.xml deleted file mode 100644 index d672d18a..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/integers.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - -1 - 0 - 1 - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml deleted file mode 100644 index 6beed51c..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - Lorem ipsum... - \@ - \? - \#ff123456 - & - "'" - \" - \u0005 - " foo bar " - "foo -bar" - " foo"bar baz foo - foobarbaz - foobar"b - az"foo - %d of %d - foo %d bar % - %2$s foo %1$d - %-e foo %,d - %2$-e foo %1$,d - %02d foo %01d - %d foo %1 - %1% foo %2% - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc002/arrays.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc002/arrays.xml deleted file mode 100644 index 96e27014..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc002/arrays.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - @string/test_string1 - @string/test_string2 - - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc002/strings.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc002/strings.xml deleted file mode 100644 index 2017d36e..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc002/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - @string/test1 - @*android:string/ok - ?android:textStyle - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/bools.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/bools.xml deleted file mode 100644 index cf6de945..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/bools.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - TEST - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/integers.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/integers.xml deleted file mode 100644 index 2540c7b4..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/integers.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - TEST - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/strings.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/strings.xml deleted file mode 100644 index b97cd2f7..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc003/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - true - 5 - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc004-mnc4-en-rUS-sw100dp-w200dp-h300dp-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key-navhidden-dpad/strings.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc004-mnc4-en-rUS-sw100dp-w200dp-h300dp-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key-navhidden-dpad/strings.xml deleted file mode 100644 index 00ec9a40..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc004-mnc4-en-rUS-sw100dp-w200dp-h300dp-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key-navhidden-dpad/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values/strings.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/values/strings.xml deleted file mode 100644 index 00ec9a40..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/xml/literals.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/xml/literals.xml deleted file mode 100644 index aa360a75..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/xml/literals.xml +++ /dev/null @@ -1,16 +0,0 @@ - - diff --git a/apktool-lib/src/test/resources/brut/apktool/testapp/res/xml/references.xml b/apktool-lib/src/test/resources/brut/apktool/testapp/res/xml/references.xml deleted file mode 100644 index ac200d2b..00000000 --- a/apktool-lib/src/test/resources/brut/apktool/testapp/res/xml/references.xml +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/brut.apktool b/brut.apktool new file mode 160000 index 00000000..f7d1e370 --- /dev/null +++ b/brut.apktool @@ -0,0 +1 @@ +Subproject commit f7d1e37031f32285ac368e38b21da4fc7574a99d diff --git a/brut.apktool.smali/.gitignore b/brut.apktool.smali/.gitignore new file mode 100644 index 00000000..be4d8cad --- /dev/null +++ b/brut.apktool.smali/.gitignore @@ -0,0 +1,6 @@ +/baksmali/target/ +/dexlib/target/ +/maven-smali-plugin/target/ +/smali/target/ +/util/target/ +*~ diff --git a/NOTICE-smali b/brut.apktool.smali/NOTICE similarity index 100% rename from NOTICE-smali rename to brut.apktool.smali/NOTICE diff --git a/brut.apktool.smali/baksmali/.gitignore b/brut.apktool.smali/baksmali/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/brut.apktool.smali/baksmali/.gitignore @@ -0,0 +1 @@ +/target diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/AnnotationFormatter.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/AnnotationFormatter.java new file mode 100644 index 00000000..edb4fcee --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/AnnotationFormatter.java @@ -0,0 +1,64 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.baksmali.Adaptors.EncodedValue.AnnotationEncodedValueAdaptor; +import org.jf.util.IndentingWriter; +import org.jf.dexlib.AnnotationItem; +import org.jf.dexlib.AnnotationSetItem; + +import java.io.IOException; + + +public class AnnotationFormatter { + + public static void writeTo(IndentingWriter writer, AnnotationSetItem annotationSet) throws IOException { + boolean first = true; + for (AnnotationItem annotationItem: annotationSet.getAnnotations()) { + if (!first) { + writer.write('\n'); + } + first = false; + + writeTo(writer, annotationItem); + } + } + + public static void writeTo(IndentingWriter writer, AnnotationItem annotationItem) throws IOException { + writer.write(".annotation "); + writer.write(annotationItem.getVisibility().visibility); + writer.write(' '); + ReferenceFormatter.writeTypeReference(writer, annotationItem.getEncodedAnnotation().annotationType); + writer.write('\n'); + + AnnotationEncodedValueAdaptor.writeElementsTo(writer, annotationItem.getEncodedAnnotation()); + + writer.write(".end annotation\n"); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/BlankMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/BlankMethodItem.java new file mode 100644 index 00000000..d0078493 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/BlankMethodItem.java @@ -0,0 +1,48 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.util.IndentingWriter; + +//a "spacer" between instructions +public class BlankMethodItem extends MethodItem { + public BlankMethodItem(int codeAddress) { + super(codeAddress); + } + + public double getSortOrder() { + return Integer.MAX_VALUE; + } + + public boolean writeTo(IndentingWriter writer) { + //we didn't technically print something, but returning true indicates that a newline should be printed + //after this method item, which is the intended functionality + return true; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java new file mode 100644 index 00000000..2c5541f4 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java @@ -0,0 +1,94 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.util.IndentingWriter; +import org.jf.dexlib.TypeIdItem; + +import java.io.IOException; + +public class CatchMethodItem extends MethodItem { + private final TypeIdItem exceptionType; + + private final LabelMethodItem tryStartLabel; + private final LabelMethodItem tryEndLabel; + private final LabelMethodItem handlerLabel; + + public CatchMethodItem(MethodDefinition.LabelCache labelCache, int codeAddress, TypeIdItem exceptionType, + int startAddress, int endAddress, int handlerAddress) { + super(codeAddress); + this.exceptionType = exceptionType; + + tryStartLabel = labelCache.internLabel(new LabelMethodItem(startAddress, "try_start_")); + + //use the address from the last covered instruction, but make the label + //name refer to the address of the next instruction + tryEndLabel = labelCache.internLabel(new EndTryLabelMethodItem(codeAddress, endAddress)); + + if (exceptionType == null) { + handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catchall_")); + } else { + handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catch_")); + } + } + + public LabelMethodItem getTryStartLabel() { + return tryStartLabel; + } + + public LabelMethodItem getTryEndLabel() { + return tryEndLabel; + } + + public LabelMethodItem getHandlerLabel() { + return handlerLabel; + } + + public double getSortOrder() { + //sort after instruction and end_try label + return 102; + } + + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + if (exceptionType == null) { + writer.write(".catchall"); + } else { + writer.write(".catch "); + ReferenceFormatter.writeTypeReference(writer, exceptionType); + } + writer.write(" {"); + tryStartLabel.writeTo(writer); + writer.write(" .. "); + tryEndLabel.writeTo(writer); + writer.write("} "); + handlerLabel.writeTo(writer); + return true; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java new file mode 100644 index 00000000..781e70b1 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java @@ -0,0 +1,336 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.dexlib.Util.Utf8Utils; +import org.jf.util.CommentingIndentingWriter; +import org.jf.util.IndentingWriter; +import org.jf.dexlib.*; +import org.jf.dexlib.Code.Analysis.ValidationException; +import org.jf.dexlib.Code.Format.Instruction21c; +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.EncodedValue.EncodedValue; +import org.jf.dexlib.Util.AccessFlags; +import org.jf.dexlib.Util.SparseArray; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.List; + +public class ClassDefinition { + private ClassDefItem classDefItem; + @Nullable + private ClassDataItem classDataItem; + + private SparseArray fieldsSetInStaticConstructor; + + protected boolean validationErrors; + + public ClassDefinition(ClassDefItem classDefItem) { + this.classDefItem = classDefItem; + this.classDataItem = classDefItem.getClassData(); + findFieldsSetInStaticConstructor(); + } + + public boolean hadValidationErrors() { + return validationErrors; + } + + private void findFieldsSetInStaticConstructor() { + fieldsSetInStaticConstructor = new SparseArray(); + + if (classDataItem == null) { + return; + } + + for (ClassDataItem.EncodedMethod directMethod: classDataItem.getDirectMethods()) { + if (directMethod.method.getMethodName().getStringValue().equals("") && + directMethod.codeItem != null) { + for (Instruction instruction: directMethod.codeItem.getInstructions()) { + switch (instruction.opcode) { + case SPUT: + case SPUT_BOOLEAN: + case SPUT_BYTE: + case SPUT_CHAR: + case SPUT_OBJECT: + case SPUT_SHORT: + case SPUT_WIDE: { + Instruction21c ins = (Instruction21c)instruction; + FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem(); + fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem); + break; + } + } + } + } + } + } + + public void writeTo(IndentingWriter writer) throws IOException { + writeClass(writer); + writeSuper(writer); + writeSourceFile(writer); + writeInterfaces(writer); + writeAnnotations(writer); + writeStaticFields(writer); + writeInstanceFields(writer); + writeDirectMethods(writer); + writeVirtualMethods(writer); + } + + private void writeClass(IndentingWriter writer) throws IOException { + writer.write(".class "); + writeAccessFlags(writer); + writer.write(classDefItem.getClassType().getTypeDescriptor()); + writer.write('\n'); + } + + private void writeAccessFlags(IndentingWriter writer) throws IOException { + for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDefItem.getAccessFlags())) { + writer.write(accessFlag.toString()); + writer.write(' '); + } + } + + private void writeSuper(IndentingWriter writer) throws IOException { + TypeIdItem superClass = classDefItem.getSuperclass(); + if (superClass != null) { + writer.write(".super "); + writer.write(superClass.getTypeDescriptor()); + writer.write('\n'); + } + } + + private void writeSourceFile(IndentingWriter writer) throws IOException { + StringIdItem sourceFile = classDefItem.getSourceFile(); + if (sourceFile != null) { + writer.write(".source \""); + Utf8Utils.writeEscapedString(writer, sourceFile.getStringValue()); + writer.write("\"\n"); + } + } + + private void writeInterfaces(IndentingWriter writer) throws IOException { + TypeListItem interfaceList = classDefItem.getInterfaces(); + if (interfaceList == null) { + return; + } + + List interfaces = interfaceList.getTypes(); + if (interfaces == null || interfaces.size() == 0) { + return; + } + + writer.write('\n'); + writer.write("# interfaces\n"); + for (TypeIdItem typeIdItem: interfaceList.getTypes()) { + writer.write(".implements "); + writer.write(typeIdItem.getTypeDescriptor()); + writer.write('\n'); + } + } + + private void writeAnnotations(IndentingWriter writer) throws IOException { + AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations(); + if (annotationDirectory == null) { + return; + } + + AnnotationSetItem annotationSet = annotationDirectory.getClassAnnotations(); + if (annotationSet == null) { + return; + } + + writer.write("\n\n"); + writer.write("# annotations\n"); + AnnotationFormatter.writeTo(writer, annotationSet); + } + + private void writeStaticFields(IndentingWriter writer) throws IOException { + if (classDataItem == null) { + return; + } + //if classDataItem is not null, then classDefItem won't be null either + assert(classDefItem != null); + + EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticFieldInitializers(); + + EncodedValue[] staticInitializers; + if (encodedStaticInitializers != null) { + staticInitializers = encodedStaticInitializers.getEncodedArray().values; + } else { + staticInitializers = new EncodedValue[0]; + } + + List encodedFields = classDataItem.getStaticFields(); + if (encodedFields.size() == 0) { + return; + } + + writer.write("\n\n"); + writer.write("# static fields\n"); + + for (int i=0; i 0) { + writer.write('\n'); + } + + ClassDataItem.EncodedField field = encodedFields.get(i); + EncodedValue encodedValue = null; + if (i < staticInitializers.length) { + encodedValue = staticInitializers[i]; + } + AnnotationSetItem fieldAnnotations = null; + AnnotationDirectoryItem annotations = classDefItem.getAnnotations(); + if (annotations != null) { + fieldAnnotations = annotations.getFieldAnnotations(field.field); + } + + IndentingWriter fieldWriter = writer; + // the encoded fields are sorted, so we just have to compare with the previous one to detect duplicates + if (i > 0 && field.equals(encodedFields.get(i-1))) { + fieldWriter = new CommentingIndentingWriter(writer, "#"); + fieldWriter.write("Ignoring field with duplicate signature\n"); + System.err.println(String.format("Warning: class %s has duplicate static field %s, Ignoring.", + classDefItem.getClassType().getTypeDescriptor(), field.field.getShortFieldString())); + } + + boolean setInStaticConstructor = + fieldsSetInStaticConstructor.get(field.field.getIndex()) != null; + + FieldDefinition.writeTo(fieldWriter, field, encodedValue, fieldAnnotations, setInStaticConstructor); + } + } + + private void writeInstanceFields(IndentingWriter writer) throws IOException { + if (classDataItem == null) { + return; + } + + List encodedFields = classDataItem.getInstanceFields(); + if (encodedFields.size() == 0) { + return; + } + + writer.write("\n\n"); + writer.write("# instance fields\n"); + for (int i=0; i 0) { + writer.write('\n'); + } + + AnnotationSetItem fieldAnnotations = null; + AnnotationDirectoryItem annotations = classDefItem.getAnnotations(); + if (annotations != null) { + fieldAnnotations = annotations.getFieldAnnotations(field.field); + } + + IndentingWriter fieldWriter = writer; + // the encoded fields are sorted, so we just have to compare with the previous one to detect duplicates + if (i > 0 && field.equals(encodedFields.get(i-1))) { + fieldWriter = new CommentingIndentingWriter(writer, "#"); + fieldWriter.write("Ignoring field with duplicate signature\n"); + System.err.println(String.format("Warning: class %s has duplicate instance field %s, Ignoring.", + classDefItem.getClassType().getTypeDescriptor(), field.field.getShortFieldString())); + } + + FieldDefinition.writeTo(fieldWriter, field, null, fieldAnnotations, false); + } + } + + private void writeDirectMethods(IndentingWriter writer) throws IOException { + if (classDataItem == null) { + return; + } + + List directMethods = classDataItem.getDirectMethods(); + if (directMethods.size() == 0) { + return; + } + + writer.write("\n\n"); + writer.write("# direct methods\n"); + writeMethods(writer, directMethods); + } + + private void writeVirtualMethods(IndentingWriter writer) throws IOException { + if (classDataItem == null) { + return; + } + + List virtualMethods = classDataItem.getVirtualMethods(); + + if (virtualMethods.size() == 0) { + return; + } + + writer.write("\n\n"); + writer.write("# virtual methods\n"); + writeMethods(writer, virtualMethods); + } + + private void writeMethods(IndentingWriter writer, List methods) throws IOException { + for (int i=0; i 0) { + writer.write('\n'); + } + + AnnotationSetItem methodAnnotations = null; + AnnotationSetRefList parameterAnnotations = null; + AnnotationDirectoryItem annotations = classDefItem.getAnnotations(); + if (annotations != null) { + methodAnnotations = annotations.getMethodAnnotations(method.method); + parameterAnnotations = annotations.getParameterAnnotations(method.method); + } + + IndentingWriter methodWriter = writer; + // the encoded methods are sorted, so we just have to compare with the previous one to detect duplicates + if (i > 0 && method.equals(methods.get(i-1))) { + methodWriter = new CommentingIndentingWriter(writer, "#"); + methodWriter.write("Ignoring method with duplicate signature\n"); + System.err.println(String.format("Warning: class %s has duplicate method %s, Ignoring.", + classDefItem.getClassType().getTypeDescriptor(), method.method.getShortMethodString())); + } + + MethodDefinition methodDefinition = new MethodDefinition(method); + methodDefinition.writeTo(methodWriter, methodAnnotations, parameterAnnotations); + + ValidationException validationException = methodDefinition.getValidationException(); + if (validationException != null) { + System.err.println(String.format("Error while disassembling method %s. Continuing.", + method.method.getMethodString())); + validationException.printStackTrace(System.err); + this.validationErrors = true; + } + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentMethodItem.java new file mode 100644 index 00000000..8ac43962 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentMethodItem.java @@ -0,0 +1,55 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public class CommentMethodItem extends MethodItem { + //private final StringTemplate template; + private final String comment; + private final double sortOrder; + + public CommentMethodItem(String comment, int codeAddress, double sortOrder) { + super(codeAddress); + this.comment = comment; + this.sortOrder = sortOrder; + } + + public double getSortOrder() { + return sortOrder; + } + + public boolean writeTo(IndentingWriter writer) throws IOException { + writer.write('#'); + writer.write(comment); + return true; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentedOutMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentedOutMethodItem.java new file mode 100644 index 00000000..aaeb6a60 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentedOutMethodItem.java @@ -0,0 +1,52 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public class CommentedOutMethodItem extends MethodItem { + private final MethodItem commentedOutMethodItem; + + public CommentedOutMethodItem(MethodItem commentedOutMethodItem) { + super(commentedOutMethodItem.getCodeAddress()); + this.commentedOutMethodItem = commentedOutMethodItem; + } + + public double getSortOrder() { + return commentedOutMethodItem.getSortOrder() + .001; + } + + public boolean writeTo(IndentingWriter writer) throws IOException { + writer.write('#'); + commentedOutMethodItem.writeTo(writer); + return true; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/DebugMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/DebugMethodItem.java new file mode 100644 index 00000000..858c5251 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/DebugMethodItem.java @@ -0,0 +1,123 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.dexlib.Util.Utf8Utils; +import org.jf.util.IndentingWriter; +import org.jf.dexlib.CodeItem; +import org.jf.dexlib.StringIdItem; +import org.jf.dexlib.TypeIdItem; + +import java.io.IOException; + +public abstract class DebugMethodItem extends MethodItem { + private final double sortOrder; + + public DebugMethodItem(int codeAddress, double sortOrder) { + super(codeAddress); + this.sortOrder = sortOrder; + } + + public double getSortOrder() { + return sortOrder; + } + + protected static void writeLine(IndentingWriter writer, int line) throws IOException { + writer.write(".line "); + writer.printSignedIntAsDec(line); + } + + protected static void writeEndPrologue(IndentingWriter writer) throws IOException { + writer.write(".prologue"); + } + + protected static void writeBeginEpilogue(IndentingWriter writer) throws IOException { + writer.write(".epilogue"); + } + + protected static void writeStartLocal(IndentingWriter writer, CodeItem codeItem, int register, + StringIdItem name, TypeIdItem type, StringIdItem signature) + throws IOException { + writer.write(".local "); + RegisterFormatter.writeTo(writer, codeItem, register); + writer.write(", "); + writer.write(name.getStringValue()); + writer.write(':'); + writer.write(type.getTypeDescriptor()); + if (signature != null) { + writer.write(",\""); + writer.write(signature.getStringValue()); + writer.write('"'); + } + } + + protected static void writeEndLocal(IndentingWriter writer, CodeItem codeItem, int register, StringIdItem name, + TypeIdItem type, StringIdItem signature) throws IOException { + writer.write(".end local "); + RegisterFormatter.writeTo(writer, codeItem, register); + + if (name != null) { + writer.write(" #"); + writer.write(name.getStringValue()); + writer.write(':'); + writer.write(type.getTypeDescriptor()); + if (signature != null) { + writer.write(",\""); + writer.write(signature.getStringValue()); + writer.write('"'); + } + } + } + + + protected static void writeRestartLocal(IndentingWriter writer, CodeItem codeItem, int register, + StringIdItem name, TypeIdItem type, StringIdItem signature) + throws IOException { + writer.write(".restart local "); + RegisterFormatter.writeTo(writer, codeItem, register); + + if (name != null) { + writer.write(" #"); + writer.write(name.getStringValue()); + writer.write(':'); + writer.write(type.getTypeDescriptor()); + if (signature != null) { + writer.write(",\""); + writer.write(signature.getStringValue()); + writer.write('"'); + } + } + } + + protected static void writeSetFile(IndentingWriter writer, String fileName) throws IOException { + writer.write(".source \""); + Utf8Utils.writeEscapedString(writer, fileName); + writer.write('"'); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/EncodedValue/AnnotationEncodedValueAdaptor.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/EncodedValue/AnnotationEncodedValueAdaptor.java new file mode 100644 index 00000000..dd514d97 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/EncodedValue/AnnotationEncodedValueAdaptor.java @@ -0,0 +1,61 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors.EncodedValue; + +import org.jf.baksmali.Adaptors.ReferenceFormatter; +import org.jf.util.IndentingWriter; +import org.jf.dexlib.EncodedValue.AnnotationEncodedSubValue; + +import java.io.IOException; + +public abstract class AnnotationEncodedValueAdaptor { + + public static void writeTo(IndentingWriter writer, AnnotationEncodedSubValue encodedAnnotation) + throws IOException { + writer.write(".subannotation "); + ReferenceFormatter.writeTypeReference(writer, encodedAnnotation.annotationType); + writer.write('\n'); + + writeElementsTo(writer, encodedAnnotation); + writer.write(".end subannotation"); + } + + public static void writeElementsTo(IndentingWriter writer, AnnotationEncodedSubValue encodedAnnotation) + throws IOException { + writer.indent(4); + for (int i=0; i { + public ArrayDataMethodItem(CodeItem codeItem, int codeAddress, ArrayDataPseudoInstruction instruction) { + super(codeItem, codeAddress, instruction); + } + + public boolean writeTo(IndentingWriter writer) throws IOException { + writer.write(".array-data 0x"); + writer.printUnsignedLongAsHex(instruction.getElementWidth()); + writer.write('\n'); + + writer.indent(4); + Iterator iterator = instruction.getElements(); + while (iterator.hasNext()) { + ArrayDataPseudoInstruction.ArrayElement element = iterator.next(); + + for (int i=0; i extends MethodItem { + protected final CodeItem codeItem; + protected final T instruction; + + public InstructionMethodItem(CodeItem codeItem, int codeAddress, T instruction) { + super(codeAddress); + this.codeItem = codeItem; + this.instruction = instruction; + } + + public double getSortOrder() { + //instructions should appear after everything except an "end try" label and .catch directive + return 100; + } + + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + switch (instruction.getFormat()) { + case Format10t: + writeOpcode(writer); + writer.write(' '); + writeTargetLabel(writer); + return true; + case Format10x: + if (instruction instanceof UnknownInstruction) { + writer.write("#unknown opcode: 0x"); + writer.printUnsignedLongAsHex(((UnknownInstruction) instruction).getOriginalOpcode() & 0xFFFF); + writer.write('\n'); + } + writeOpcode(writer); + return true; + case Format11n: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeLiteral(writer); + return true; + case Format11x: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + return true; + case Format12x: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeSecondRegister(writer); + return true; + case Format20bc: + writeOpcode(writer); + writer.write(' '); + writeVerificationErrorType(writer); + writer.write(", "); + writeReference(writer); + return true; + case Format20t: + case Format30t: + writeOpcode(writer); + writer.write(' '); + writeTargetLabel(writer); + return true; + case Format21c: + case Format31c: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeReference(writer); + return true; + case Format21h: + case Format21s: + case Format31i: + case Format51l: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeLiteral(writer); + return true; + case Format21t: + case Format31t: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeTargetLabel(writer); + return true; + case Format22b: + case Format22s: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeSecondRegister(writer); + writer.write(", "); + writeLiteral(writer); + return true; + case Format22c: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeSecondRegister(writer); + writer.write(", "); + writeReference(writer); + return true; + case Format22cs: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeSecondRegister(writer); + writer.write(", "); + writeFieldOffset(writer); + return true; + case Format22t: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeSecondRegister(writer); + writer.write(", "); + writeTargetLabel(writer); + return true; + case Format22x: + case Format32x: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeSecondRegister(writer); + return true; + case Format23x: + writeOpcode(writer); + writer.write(' '); + writeFirstRegister(writer); + writer.write(", "); + writeSecondRegister(writer); + writer.write(", "); + writeThirdRegister(writer); + return true; + case Format35c: + writeOpcode(writer); + writer.write(' '); + writeInvokeRegisters(writer); + writer.write(", "); + writeReference(writer); + return true; + case Format35mi: + writeOpcode(writer); + writer.write(' '); + writeInvokeRegisters(writer); + writer.write(", "); + writeInlineIndex(writer); + return true; + case Format35ms: + writeOpcode(writer); + writer.write(' '); + writeInvokeRegisters(writer); + writer.write(", "); + writeVtableIndex(writer); + return true; + case Format3rc: + writeOpcode(writer); + writer.write(' '); + writeInvokeRangeRegisters(writer); + writer.write(", "); + writeReference(writer); + return true; + case Format3rmi: + writeOpcode(writer); + writer.write(' '); + writeInvokeRangeRegisters(writer); + writer.write(", "); + writeInlineIndex(writer); + return true; + case Format3rms: + writeOpcode(writer); + writer.write(' '); + writeInvokeRangeRegisters(writer); + writer.write(", "); + writeVtableIndex(writer); + return true; + } + assert false; + return false; + } + + protected void writeOpcode(IndentingWriter writer) throws IOException { + writer.write(instruction.opcode.name); + } + + protected void writeTargetLabel(IndentingWriter writer) throws IOException { + //this method is overrided by OffsetInstructionMethodItem, and should only be called for the formats that + //have a target + throw new RuntimeException(); + } + + protected void writeRegister(IndentingWriter writer, int registerNumber) throws IOException { + RegisterFormatter.writeTo(writer, codeItem, registerNumber); + } + + protected void writeFirstRegister(IndentingWriter writer) throws IOException { + writeRegister(writer, ((SingleRegisterInstruction)instruction).getRegisterA()); + } + + protected void writeSecondRegister(IndentingWriter writer) throws IOException { + writeRegister(writer, ((TwoRegisterInstruction)instruction).getRegisterB()); + } + + protected void writeThirdRegister(IndentingWriter writer) throws IOException { + writeRegister(writer, ((ThreeRegisterInstruction)instruction).getRegisterC()); + } + + protected void writeInvokeRegisters(IndentingWriter writer) throws IOException { + FiveRegisterInstruction instruction = (FiveRegisterInstruction)this.instruction; + final int regCount = instruction.getRegCount(); + + writer.write('{'); + switch (regCount) { + case 1: + writeRegister(writer, instruction.getRegisterD()); + break; + case 2: + writeRegister(writer, instruction.getRegisterD()); + writer.write(", "); + writeRegister(writer, instruction.getRegisterE()); + break; + case 3: + writeRegister(writer, instruction.getRegisterD()); + writer.write(", "); + writeRegister(writer, instruction.getRegisterE()); + writer.write(", "); + writeRegister(writer, instruction.getRegisterF()); + break; + case 4: + writeRegister(writer, instruction.getRegisterD()); + writer.write(", "); + writeRegister(writer, instruction.getRegisterE()); + writer.write(", "); + writeRegister(writer, instruction.getRegisterF()); + writer.write(", "); + writeRegister(writer, instruction.getRegisterG()); + break; + case 5: + writeRegister(writer, instruction.getRegisterD()); + writer.write(", "); + writeRegister(writer, instruction.getRegisterE()); + writer.write(", "); + writeRegister(writer, instruction.getRegisterF()); + writer.write(", "); + writeRegister(writer, instruction.getRegisterG()); + writer.write(", "); + writeRegister(writer, instruction.getRegisterA()); + break; + } + writer.write('}'); + } + + protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException { + RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction; + + int regCount = instruction.getRegCount(); + if (regCount == 0) { + writer.write("{}"); + } else { + int startRegister = instruction.getStartRegister(); + RegisterFormatter.writeRegisterRange(writer, codeItem, startRegister, startRegister+regCount-1); + } + } + + protected void writeLiteral(IndentingWriter writer) throws IOException { + LongRenderer.writeSignedIntOrLongTo(writer, ((LiteralInstruction)instruction).getLiteral()); + } + + protected void writeFieldOffset(IndentingWriter writer) throws IOException { + writer.write("field@0x"); + writer.printUnsignedLongAsHex(((OdexedFieldAccess) instruction).getFieldOffset()); + } + + protected void writeInlineIndex(IndentingWriter writer) throws IOException { + writer.write("inline@0x"); + writer.printUnsignedLongAsHex(((OdexedInvokeInline) instruction).getInlineIndex()); + } + + protected void writeVtableIndex(IndentingWriter writer) throws IOException { + writer.write("vtable@0x"); + writer.printUnsignedLongAsHex(((OdexedInvokeVirtual) instruction).getVtableIndex()); + } + + protected void writeReference(IndentingWriter writer) throws IOException { + Item item = ((InstructionWithReference)instruction).getReferencedItem(); + ReferenceFormatter.writeReference(writer, item); + } + + protected void writeVerificationErrorType(IndentingWriter writer) throws IOException { + VerificationErrorType validationErrorType = ((Instruction20bc)instruction).getValidationErrorType(); + writer.write(validationErrorType.getName()); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItemFactory.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItemFactory.java new file mode 100644 index 00000000..c38b524a --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItemFactory.java @@ -0,0 +1,67 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors.Format; + +import org.jf.baksmali.Adaptors.MethodDefinition; +import org.jf.dexlib.Code.Format.*; +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.OffsetInstruction; +import org.jf.dexlib.CodeItem; + +public class InstructionMethodItemFactory { + private InstructionMethodItemFactory() { + } + + public static InstructionMethodItem makeInstructionFormatMethodItem(MethodDefinition methodDefinition, + CodeItem codeItem, + int codeAddress, + Instruction instruction) { + if (instruction instanceof OffsetInstruction) { + return new OffsetInstructionFormatMethodItem(methodDefinition.getLabelCache(), codeItem, codeAddress, + instruction); + } + + switch (instruction.getFormat()) { + case ArrayData: + return new ArrayDataMethodItem(codeItem, codeAddress, + (ArrayDataPseudoInstruction)instruction); + case PackedSwitchData: + return new PackedSwitchMethodItem(methodDefinition, codeItem, codeAddress, + (PackedSwitchDataPseudoInstruction)instruction); + case SparseSwitchData: + return new SparseSwitchMethodItem(methodDefinition, codeItem, codeAddress, + (SparseSwitchDataPseudoInstruction)instruction); + case UnresolvedOdexInstruction: + return new UnresolvedOdexInstructionMethodItem(codeItem, codeAddress, + (UnresolvedOdexInstruction)instruction); + default: + return new InstructionMethodItem(codeItem, codeAddress, instruction); + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/OffsetInstructionFormatMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/OffsetInstructionFormatMethodItem.java new file mode 100644 index 00000000..d63e43bf --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/OffsetInstructionFormatMethodItem.java @@ -0,0 +1,85 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors.Format; + +import org.jf.baksmali.Adaptors.LabelMethodItem; +import org.jf.baksmali.Adaptors.MethodDefinition; +import org.jf.util.IndentingWriter; +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.OffsetInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.CodeItem; + +import java.io.IOException; + +public class OffsetInstructionFormatMethodItem + extends InstructionMethodItem { + protected LabelMethodItem label; + + public OffsetInstructionFormatMethodItem(MethodDefinition.LabelCache labelCache, CodeItem codeItem, int codeAddress, + T instruction) { + super(codeItem, codeAddress, instruction); + + label = new LabelMethodItem(codeAddress + instruction.getTargetAddressOffset(), getLabelPrefix()); + label = labelCache.internLabel(label); + } + + @Override + protected void writeTargetLabel(IndentingWriter writer) throws IOException { + label.writeTo(writer); + } + + public LabelMethodItem getLabel() { + return label; + } + + private String getLabelPrefix() { + switch (instruction.getFormat()) { + case Format10t: + case Format20t: + case Format30t: + return "goto_"; + case Format21t: + case Format22t: + return "cond_"; + case Format31t: + if (instruction.opcode == Opcode.FILL_ARRAY_DATA) { + return "array_"; + } + if (instruction.opcode == Opcode.PACKED_SWITCH) { + return "pswitch_data_"; + } + assert instruction.opcode == Opcode.SPARSE_SWITCH; + return "sswitch_data_"; + } + + assert false; + return null; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java new file mode 100644 index 00000000..d991d646 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java @@ -0,0 +1,113 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors.Format; + +import org.jf.baksmali.Adaptors.LabelMethodItem; +import org.jf.baksmali.Adaptors.MethodDefinition; +import org.jf.util.IndentingWriter; +import org.jf.baksmali.Renderers.IntegerRenderer; +import org.jf.dexlib.Code.Format.PackedSwitchDataPseudoInstruction; +import org.jf.dexlib.CodeItem; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class PackedSwitchMethodItem extends InstructionMethodItem { + private final List targets; + + public PackedSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress, + PackedSwitchDataPseudoInstruction instruction) { + super(codeItem, codeAddress, instruction); + + int baseCodeAddress = methodDefinition.getPackedSwitchBaseAddress(codeAddress); + + targets = new ArrayList(); + Iterator iterator = instruction.iterateKeysAndTargets(); + + if (baseCodeAddress >= 0) { + while (iterator.hasNext()) { + PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next(); + PackedSwitchLabelTarget packedSwitchLabelTarget = new PackedSwitchLabelTarget(); + + + LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "pswitch_"); + label = methodDefinition.getLabelCache().internLabel(label); + packedSwitchLabelTarget.Target = label; + targets.add(packedSwitchLabelTarget); + } + } else { + while (iterator.hasNext()) { + PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next(); + PackedSwitchOffsetTarget packedSwitchOffsetTarget = new PackedSwitchOffsetTarget(); + + + packedSwitchOffsetTarget.Target = target.targetAddressOffset; + targets.add(packedSwitchOffsetTarget); + } + } + } + + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + writer.write(".packed-switch "); + IntegerRenderer.writeTo(writer, instruction.getFirstKey()); + writer.indent(4); + writer.write('\n'); + for (PackedSwitchTarget target: targets) { + target.writeTargetTo(writer); + writer.write('\n'); + } + writer.deindent(4); + writer.write(".end packed-switch"); + return true; + } + + private static abstract class PackedSwitchTarget { + public abstract void writeTargetTo(IndentingWriter writer) throws IOException; + } + + private static class PackedSwitchLabelTarget extends PackedSwitchTarget { + public LabelMethodItem Target; + public void writeTargetTo(IndentingWriter writer) throws IOException { + Target.writeTo(writer); + } + } + + private static class PackedSwitchOffsetTarget extends PackedSwitchTarget { + public int Target; + public void writeTargetTo(IndentingWriter writer) throws IOException { + if (Target >= 0) { + writer.write('+'); + } + writer.printSignedIntAsDec(Target); + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java new file mode 100644 index 00000000..46f49dc2 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java @@ -0,0 +1,115 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors.Format; + +import org.jf.baksmali.Adaptors.LabelMethodItem; +import org.jf.baksmali.Adaptors.MethodDefinition; +import org.jf.util.IndentingWriter; +import org.jf.baksmali.Renderers.IntegerRenderer; +import org.jf.dexlib.Code.Format.SparseSwitchDataPseudoInstruction; +import org.jf.dexlib.CodeItem; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class SparseSwitchMethodItem extends InstructionMethodItem { + private final List targets; + + public SparseSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress, + SparseSwitchDataPseudoInstruction instruction) { + super(codeItem, codeAddress, instruction); + + int baseCodeAddress = methodDefinition.getSparseSwitchBaseAddress(codeAddress); + + targets = new ArrayList(); + Iterator iterator = instruction.iterateKeysAndTargets(); + if (baseCodeAddress >= 0) { + while (iterator.hasNext()) { + SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next(); + SparseSwitchLabelTarget sparseSwitchLabelTarget = new SparseSwitchLabelTarget(); + sparseSwitchLabelTarget.Key = target.key; + + LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "sswitch_"); + label = methodDefinition.getLabelCache().internLabel(label); + sparseSwitchLabelTarget.Target = label; + + targets.add(sparseSwitchLabelTarget); + } + } else { + //if we couldn't determine a base address, just use relative offsets rather than labels + while (iterator.hasNext()) { + SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next(); + SparseSwitchOffsetTarget sparseSwitchOffsetTarget = new SparseSwitchOffsetTarget(); + sparseSwitchOffsetTarget.Key = target.key; + + sparseSwitchOffsetTarget.Target = target.targetAddressOffset; + targets.add(sparseSwitchOffsetTarget); + } + } + } + + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + writer.write(".sparse-switch\n"); + writer.indent(4); + for (SparseSwitchTarget target: targets) { + IntegerRenderer.writeTo(writer, target.Key); + writer.write(" -> "); + target.writeTargetTo(writer); + writer.write('\n'); + } + writer.deindent(4); + writer.write(".end sparse-switch"); + return true; + } + + private static abstract class SparseSwitchTarget { + public int Key; + public abstract void writeTargetTo(IndentingWriter writer) throws IOException; + } + + private static class SparseSwitchLabelTarget extends SparseSwitchTarget { + public LabelMethodItem Target; + public void writeTargetTo(IndentingWriter writer) throws IOException { + Target.writeTo(writer); + } + } + + private static class SparseSwitchOffsetTarget extends SparseSwitchTarget { + public int Target; + public void writeTargetTo(IndentingWriter writer) throws IOException { + if (Target >= 0) { + writer.write('+'); + } + writer.printSignedIntAsDec(Target); + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedOdexInstructionMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedOdexInstructionMethodItem.java new file mode 100644 index 00000000..5451a203 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedOdexInstructionMethodItem.java @@ -0,0 +1,52 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors.Format; + +import org.jf.util.IndentingWriter; +import org.jf.dexlib.Code.Format.UnresolvedOdexInstruction; +import org.jf.dexlib.CodeItem; + +import java.io.IOException; + +public class UnresolvedOdexInstructionMethodItem extends InstructionMethodItem { + public UnresolvedOdexInstructionMethodItem(CodeItem codeItem, int codeAddress, UnresolvedOdexInstruction instruction) { + super(codeItem, codeAddress, instruction); + } + + public boolean writeTo(IndentingWriter writer) throws IOException { + writeThrowTo(writer); + return true; + } + + private void writeThrowTo(IndentingWriter writer) throws IOException { + writer.write("#Replaced unresolvable odex instruction with a throw\n"); + writer.write("throw "); + writeRegister(writer, instruction.ObjectRegisterNum); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/LabelMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/LabelMethodItem.java new file mode 100644 index 00000000..dc0ace92 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/LabelMethodItem.java @@ -0,0 +1,99 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.util.IndentingWriter; +import org.jf.baksmali.baksmali; + +import java.io.IOException; + +public class LabelMethodItem extends MethodItem { + private final String labelPrefix; + private int labelSequence; + + public LabelMethodItem(int codeAddress, String labelPrefix) { + super(codeAddress); + this.labelPrefix = labelPrefix; + } + + public double getSortOrder() { + return 0; + } + + public int compareTo(MethodItem methodItem) { + int result = super.compareTo(methodItem); + + if (result == 0) { + if (methodItem instanceof LabelMethodItem) { + result = labelPrefix.compareTo(((LabelMethodItem)methodItem).labelPrefix); + } + } + return result; + } + + public int hashCode() { + //force it to call equals when two labels are at the same address + return getCodeAddress(); + } + + public boolean equals(Object o) { + if (!(o instanceof LabelMethodItem)) { + return false; + } + return this.compareTo((MethodItem)o) == 0; + } + + + public boolean writeTo(IndentingWriter writer) throws IOException { + writer.write(':'); + writer.write(labelPrefix); + if (baksmali.useSequentialLabels) { + writer.printUnsignedLongAsHex(labelSequence); + } else { + writer.printUnsignedLongAsHex(this.getLabelAddress()); + } + return true; + } + + public String getLabelPrefix() { + return labelPrefix; + } + + public int getLabelAddress() { + return this.getCodeAddress(); + } + + public int getLabelSequence() { + return labelSequence; + } + + public void setLabelSequence(int labelSequence) { + this.labelSequence = labelSequence; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java new file mode 100644 index 00000000..3e3796e4 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -0,0 +1,662 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory; +import org.jf.dexlib.Code.Analysis.SyntheticAccessorResolver; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.util.IndentingWriter; +import org.jf.baksmali.baksmali; +import org.jf.dexlib.*; +import org.jf.dexlib.Code.Analysis.AnalyzedInstruction; +import org.jf.dexlib.Code.Analysis.MethodAnalyzer; +import org.jf.dexlib.Code.Analysis.ValidationException; +import org.jf.dexlib.Code.Format.Format; +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.OffsetInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Debug.DebugInstructionIterator; +import org.jf.dexlib.Util.AccessFlags; +import org.jf.dexlib.Util.ExceptionWithContext; +import org.jf.dexlib.Util.SparseIntArray; + +import java.io.IOException; +import java.util.*; + +public class MethodDefinition { + private final ClassDataItem.EncodedMethod encodedMethod; + private MethodAnalyzer methodAnalyzer; + + private final LabelCache labelCache = new LabelCache(); + + private final SparseIntArray packedSwitchMap; + private final SparseIntArray sparseSwitchMap; + private final SparseIntArray instructionMap; + + public MethodDefinition(ClassDataItem.EncodedMethod encodedMethod) { + + + try { + this.encodedMethod = encodedMethod; + + //TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh. + + if (encodedMethod.codeItem != null) { + Instruction[] instructions = encodedMethod.codeItem.getInstructions(); + + packedSwitchMap = new SparseIntArray(1); + sparseSwitchMap = new SparseIntArray(1); + instructionMap = new SparseIntArray(instructions.length); + + int currentCodeAddress = 0; + for (int i=0; i instructions, AnalyzedInstruction instruction) { + if (instruction.getInstruction().opcode != Opcode.NOP || + instruction.getInstruction().getFormat().variableSizeFormat) { + + return false; + } + + if (instruction.getInstructionIndex() == instructions.size()-1) { + return false; + } + + AnalyzedInstruction nextInstruction = instructions.get(instruction.getInstructionIndex()+1); + if (nextInstruction.getInstruction().getFormat().variableSizeFormat) { + return true; + } + return false; + } + + private boolean needsAnalyzed() { + for (Instruction instruction: encodedMethod.codeItem.getInstructions()) { + if (instruction.opcode.odexOnly()) { + return true; + } + } + return false; + } + + private List getMethodItems() { + ArrayList methodItems = new ArrayList(); + + if (encodedMethod.codeItem == null) { + return methodItems; + } + + if ((baksmali.registerInfo != 0) || baksmali.verify || + (baksmali.deodex && needsAnalyzed())) { + addAnalyzedInstructionMethodItems(methodItems); + } else { + addInstructionMethodItems(methodItems); + } + + addTries(methodItems); + if (baksmali.outputDebugInfo) { + addDebugInfo(methodItems); + } + + if (baksmali.useSequentialLabels) { + setLabelSequentialNumbers(); + } + + for (LabelMethodItem labelMethodItem: labelCache.getLabels()) { + methodItems.add(labelMethodItem); + } + + Collections.sort(methodItems); + + return methodItems; + } + + private void addInstructionMethodItems(List methodItems) { + Instruction[] instructions = encodedMethod.codeItem.getInstructions(); + + int currentCodeAddress = 0; + for (int i=0; i methodItems) { + methodAnalyzer = new MethodAnalyzer(encodedMethod, baksmali.deodex, baksmali.inlineResolver); + + methodAnalyzer.analyze(); + + ValidationException validationException = methodAnalyzer.getValidationException(); + if (validationException != null) { + methodItems.add(new CommentMethodItem( + String.format("ValidationException: %s" ,validationException.getMessage()), + validationException.getCodeAddress(), Integer.MIN_VALUE)); + } else if (baksmali.verify) { + methodAnalyzer.verify(); + + validationException = methodAnalyzer.getValidationException(); + if (validationException != null) { + methodItems.add(new CommentMethodItem( + String.format("ValidationException: %s" ,validationException.getMessage()), + validationException.getCodeAddress(), Integer.MIN_VALUE)); + } + } + + List instructions = methodAnalyzer.getInstructions(); + + int currentCodeAddress = 0; + for (int i=0; i methodItems) { + if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) { + return; + } + + Instruction[] instructions = encodedMethod.codeItem.getInstructions(); + int lastInstructionAddress = instructionMap.keyAt(instructionMap.size()-1); + int codeSize = lastInstructionAddress + instructions[instructions.length - 1].getSize(lastInstructionAddress); + + for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { + int startAddress = tryItem.getStartCodeAddress(); + int endAddress = tryItem.getStartCodeAddress() + tryItem.getTryLength(); + + if (startAddress >= codeSize) { + throw new RuntimeException(String.format("Try start offset %d is past the end of the code block.", + startAddress)); + } + // Note: not >=. endAddress == codeSize is valid, when the try covers the last instruction + if (endAddress > codeSize) { + throw new RuntimeException(String.format("Try end offset %d is past the end of the code block.", + endAddress)); + } + + /** + * The end address points to the address immediately after the end of the last + * instruction that the try block covers. We want the .catch directive and end_try + * label to be associated with the last covered instruction, so we need to get + * the address for that instruction + */ + + int lastCoveredIndex = instructionMap.getClosestSmaller(endAddress-1); + int lastCoveredAddress = instructionMap.keyAt(lastCoveredIndex); + + //add the catch all handler if it exists + int catchAllAddress = tryItem.encodedCatchHandler.getCatchAllHandlerAddress(); + if (catchAllAddress != -1) { + if (catchAllAddress >= codeSize) { + throw new RuntimeException(String.format( + "Catch all handler offset %d is past the end of the code block.", catchAllAddress)); + } + + CatchMethodItem catchAllMethodItem = new CatchMethodItem(labelCache, lastCoveredAddress, null, + startAddress, endAddress, catchAllAddress); + methodItems.add(catchAllMethodItem); + } + + //add the rest of the handlers + for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { + if (handler.getHandlerAddress() >= codeSize) { + throw new RuntimeException(String.format( + "Exception handler offset %d is past the end of the code block.", catchAllAddress)); + } + + //use the address from the last covered instruction + CatchMethodItem catchMethodItem = new CatchMethodItem(labelCache, lastCoveredAddress, + handler.exceptionType, startAddress, endAddress, handler.getHandlerAddress()); + methodItems.add(catchMethodItem); + } + } + } + + private void addDebugInfo(final List methodItems) { + if (encodedMethod.codeItem == null || encodedMethod.codeItem.getDebugInfo() == null) { + return; + } + + final CodeItem codeItem = encodedMethod.codeItem; + DebugInfoItem debugInfoItem = codeItem.getDebugInfo(); + + DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(), + new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() { + @Override + public void ProcessStartLocal(final int codeAddress, final int length, final int registerNum, + final StringIdItem name, final TypeIdItem type) { + methodItems.add(new DebugMethodItem(codeAddress, -1) { + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + writeStartLocal(writer, codeItem, registerNum, name, type, null); + return true; + } + }); + } + + @Override + public void ProcessStartLocalExtended(final int codeAddress, final int length, + final int registerNum, final StringIdItem name, + final TypeIdItem type, final StringIdItem signature) { + methodItems.add(new DebugMethodItem(codeAddress, -1) { + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + writeStartLocal(writer, codeItem, registerNum, name, type, signature); + return true; + } + }); + } + + @Override + public void ProcessEndLocal(final int codeAddress, final int length, final int registerNum, + final StringIdItem name, final TypeIdItem type, + final StringIdItem signature) { + methodItems.add(new DebugMethodItem(codeAddress, -1) { + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + writeEndLocal(writer, codeItem, registerNum, name, type, signature); + return true; + } + }); + } + + @Override + public void ProcessRestartLocal(final int codeAddress, final int length, final int registerNum, + final StringIdItem name, final TypeIdItem type, + final StringIdItem signature) { + methodItems.add(new DebugMethodItem(codeAddress, -1) { + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + writeRestartLocal(writer, codeItem, registerNum, name, type, signature); + return true; + } + }); + } + + @Override + public void ProcessSetPrologueEnd(int codeAddress) { + methodItems.add(new DebugMethodItem(codeAddress, -4) { + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + writeEndPrologue(writer); + return true; + } + }); + } + + @Override + public void ProcessSetEpilogueBegin(int codeAddress) { + methodItems.add(new DebugMethodItem(codeAddress, -4) { + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + writeBeginEpilogue(writer); + return true; + } + }); + } + + @Override + public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) { + methodItems.add(new DebugMethodItem(codeAddress, -3) { + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + writeSetFile(writer, name.getStringValue()); + return true; + } + }); + } + + @Override + public void ProcessLineEmit(int codeAddress, final int line) { + methodItems.add(new DebugMethodItem(codeAddress, -2) { + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + writeLine(writer, line); + return true; + } + }); + } + }); + } + + private void setLabelSequentialNumbers() { + HashMap nextLabelSequenceByType = new HashMap(); + ArrayList sortedLabels = new ArrayList(labelCache.getLabels()); + + //sort the labels by their location in the method + Collections.sort(sortedLabels); + + for (LabelMethodItem labelMethodItem: sortedLabels) { + Integer labelSequence = nextLabelSequenceByType.get(labelMethodItem.getLabelPrefix()); + if (labelSequence == null) { + labelSequence = 0; + } + labelMethodItem.setLabelSequence(labelSequence); + nextLabelSequenceByType.put(labelMethodItem.getLabelPrefix(), labelSequence + 1); + } + } + + public static class LabelCache { + protected HashMap labels = new HashMap(); + + public LabelCache() { + } + + public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) { + LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem); + if (internedLabelMethodItem != null) { + return internedLabelMethodItem; + } + labels.put(labelMethodItem, labelMethodItem); + return labelMethodItem; + } + + + public Collection getLabels() { + return labels.values(); + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodItem.java new file mode 100644 index 00000000..60358c5d --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodItem.java @@ -0,0 +1,59 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public abstract class MethodItem implements Comparable { + protected final int codeAddress; + + protected MethodItem(int codeAddress) { + this.codeAddress = codeAddress; + } + + public int getCodeAddress() { + return codeAddress; + } + + //return an arbitrary double that determines how this item will be sorted with others at the same address + public abstract double getSortOrder(); + + public int compareTo(MethodItem methodItem) { + int result = ((Integer) codeAddress).compareTo(methodItem.codeAddress); + + if (result == 0){ + return ((Double)getSortOrder()).compareTo(methodItem.getSortOrder()); + } + return result; + } + + public abstract boolean writeTo(IndentingWriter writer) throws IOException; +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PostInstructionRegisterInfoMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PostInstructionRegisterInfoMethodItem.java new file mode 100644 index 00000000..d4179603 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PostInstructionRegisterInfoMethodItem.java @@ -0,0 +1,111 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.Adaptors; + +import org.jf.util.IndentingWriter; +import org.jf.baksmali.baksmali; +import org.jf.baksmali.main; +import org.jf.dexlib.ClassDataItem; +import org.jf.dexlib.Code.Analysis.AnalyzedInstruction; +import org.jf.dexlib.Code.Analysis.MethodAnalyzer; +import org.jf.dexlib.Code.Analysis.RegisterType; + +import java.io.IOException; +import java.util.BitSet; + +public class PostInstructionRegisterInfoMethodItem extends MethodItem { + private final AnalyzedInstruction analyzedInstruction; + private final MethodAnalyzer methodAnalyzer; + + public PostInstructionRegisterInfoMethodItem(AnalyzedInstruction analyzedInstruction, MethodAnalyzer methodAnalyzer, + int codeAddress) { + super(codeAddress); + this.analyzedInstruction = analyzedInstruction; + this.methodAnalyzer = methodAnalyzer; + } + + @Override + public double getSortOrder() { + return 100.1; + } + + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + int registerInfo = baksmali.registerInfo; + int registerCount = analyzedInstruction.getRegisterCount(); + BitSet registers = new BitSet(registerCount); + + if ((registerInfo & main.ALL) != 0) { + registers.set(0, registerCount); + } else { + if ((registerInfo & main.ALLPOST) != 0) { + registers.set(0, registerCount); + } else if ((registerInfo & main.DEST) != 0) { + addDestRegs(registers, registerCount); + } + } + + return writeRegisterInfo(writer, registers); + } + + private void addDestRegs(BitSet printPostRegister, int registerCount) { + for (int registerNum=0; registerNum= 0; registerNum = registers.nextSetBit(registerNum + 1)) { + + RegisterType registerType = analyzedInstruction.getPostInstructionRegisterType(registerNum); + + RegisterFormatter.writeTo(writer, encodedMethod.codeItem, registerNum); + writer.write('='); + + if (registerType == null) { + writer.write("null"); + } else { + registerType.writeTo(writer); + } + writer.write(';'); + } + return true; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java new file mode 100644 index 00000000..bdbfea32 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java @@ -0,0 +1,283 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.Adaptors; + +import org.jf.util.IndentingWriter; +import org.jf.baksmali.baksmali; +import org.jf.baksmali.main; +import org.jf.dexlib.ClassDataItem; +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.*; +import org.jf.dexlib.Util.AccessFlags; + +import java.io.IOException; +import java.util.BitSet; + +public class PreInstructionRegisterInfoMethodItem extends MethodItem { + private static AnalyzedInstruction lastInstruction; + + private final AnalyzedInstruction analyzedInstruction; + private final MethodAnalyzer methodAnalyzer; + + public PreInstructionRegisterInfoMethodItem(AnalyzedInstruction analyzedInstruction, MethodAnalyzer methodAnalyzer, + int codeAddress) { + super(codeAddress); + this.analyzedInstruction = analyzedInstruction; + this.methodAnalyzer = methodAnalyzer; + } + + @Override + public double getSortOrder() { + return 99.9; + } + + @Override + public boolean writeTo(IndentingWriter writer) throws IOException { + int registerInfo = baksmali.registerInfo; + int registerCount = analyzedInstruction.getRegisterCount(); + BitSet registers = new BitSet(registerCount); + + if ((registerInfo & main.ALL) != 0) { + registers.set(0, registerCount); + } else { + if ((registerInfo & main.ALLPRE) != 0) { + registers.set(0, registerCount); + } else { + if ((registerInfo & main.ARGS) != 0) { + addArgsRegs(registers); + } + if ((registerInfo & main.DIFFPRE) != 0) { + addDiffRegs(registers); + } + if ((registerInfo & main.MERGE) != 0) { + addMergeRegs(registers, registerCount); + } else if ((registerInfo & main.FULLMERGE) != 0 && + (analyzedInstruction.isBeginningInstruction())) { + addParamRegs(registers, registerCount); + } + } + } + + boolean printedSomething = false; + if ((registerInfo & main.FULLMERGE) != 0) { + printedSomething = writeFullMergeRegs(writer, registers, registerCount); + } + + printedSomething |= writeRegisterInfo(writer, registers, printedSomething); + + return printedSomething; + } + + private void addArgsRegs(BitSet registers) { + if (analyzedInstruction.getInstruction() instanceof RegisterRangeInstruction) { + RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.getInstruction(); + + registers.set(instruction.getStartRegister(), + instruction.getStartRegister() + instruction.getRegCount()); + } else if (analyzedInstruction.getInstruction() instanceof FiveRegisterInstruction) { + FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.getInstruction(); + int regCount = instruction.getRegCount(); + switch (regCount) { + case 5: + registers.set(instruction.getRegisterA()); + //fall through + case 4: + registers.set(instruction.getRegisterG()); + //fall through + case 3: + registers.set(instruction.getRegisterF()); + //fall through + case 2: + registers.set(instruction.getRegisterE()); + //fall through + case 1: + registers.set(instruction.getRegisterD()); + } + } else if (analyzedInstruction.getInstruction() instanceof ThreeRegisterInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.getInstruction(); + registers.set(instruction.getRegisterA()); + registers.set(instruction.getRegisterB()); + registers.set(instruction.getRegisterC()); + } else if (analyzedInstruction.getInstruction() instanceof TwoRegisterInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.getInstruction(); + registers.set(instruction.getRegisterA()); + registers.set(instruction.getRegisterB()); + } else if (analyzedInstruction.getInstruction() instanceof SingleRegisterInstruction) { + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.getInstruction(); + registers.set(instruction.getRegisterA()); + } + } + + private void addDiffRegs(BitSet registers) { + if (!analyzedInstruction.isBeginningInstruction()) { + for (int i = 0; i < analyzedInstruction.getRegisterCount(); i++) { + + if (lastInstruction.getPreInstructionRegisterType(i).category + != analyzedInstruction.getPreInstructionRegisterType(i).category) { + registers.set(i); + } + } + } + lastInstruction = analyzedInstruction; + } + + private void addMergeRegs(BitSet registers, int registerCount) { + if (analyzedInstruction.isBeginningInstruction()) { + addParamRegs(registers, registerCount); + } + + if (analyzedInstruction.getPredecessorCount() <= 1) { + //in the common case of an instruction that only has a single predecessor which is the previous + //instruction, the pre-instruction registers will always match the previous instruction's + //post-instruction registers + return; + } + + for (int registerNum=0; registerNum= 0; registerNum = registers.nextSetBit(registerNum + 1)) { + + RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNum); + + RegisterFormatter.writeTo(writer, encodedMethod.codeItem, registerNum); + writer.write('='); + + if (registerType == null) { + writer.write("null"); + } else { + registerType.writeTo(writer); + } + writer.write(';'); + } + return true; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java new file mode 100644 index 00000000..7a0ec5a1 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java @@ -0,0 +1,79 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.Adaptors; + +import org.jf.util.IndentingWriter; +import org.jf.dexlib.*; +import org.jf.dexlib.Util.Utf8Utils; + +import java.io.IOException; + +public class ReferenceFormatter { + public static void writeReference(IndentingWriter writer, Item item) throws IOException { + switch (item.getItemType()) { + case TYPE_METHOD_ID_ITEM: + writeMethodReference(writer, (MethodIdItem)item); + return; + case TYPE_FIELD_ID_ITEM: + writeFieldReference(writer, (FieldIdItem)item); + return; + case TYPE_STRING_ID_ITEM: + writeStringReference(writer, (StringIdItem)item); + return; + case TYPE_TYPE_ID_ITEM: + writeTypeReference(writer, (TypeIdItem)item); + return; + } + } + + public static void writeMethodReference(IndentingWriter writer, MethodIdItem item) throws IOException { + writer.write(item.getContainingClass().getTypeDescriptor()); + writer.write("->"); + writer.write(item.getMethodName().getStringValue()); + writer.write(item.getPrototype().getPrototypeString()); + } + + public static void writeFieldReference(IndentingWriter writer, FieldIdItem item) throws IOException { + writer.write(item.getContainingClass().getTypeDescriptor()); + writer.write("->"); + writer.write(item.getFieldName().getStringValue()); + writer.write(':'); + writer.write(item.getFieldType().getTypeDescriptor()); + } + + public static void writeStringReference(IndentingWriter writer, StringIdItem item) throws IOException { + writer.write('"'); + Utf8Utils.writeEscapedString(writer, item.getStringValue()); + writer.write('"'); + } + + public static void writeTypeReference(IndentingWriter writer, TypeIdItem item) throws IOException { + writer.write(item.getTypeDescriptor()); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/RegisterFormatter.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/RegisterFormatter.java new file mode 100644 index 00000000..35c620d5 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/RegisterFormatter.java @@ -0,0 +1,103 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Adaptors; + +import org.jf.util.IndentingWriter; +import org.jf.baksmali.baksmali; +import org.jf.dexlib.CodeItem; +import org.jf.dexlib.Util.AccessFlags; + +import java.io.IOException; + +/** + * This class contains the logic used for formatting registers + */ +public class RegisterFormatter { + + /** + * Write out the register range value used by Format3rc. If baksmali.noParameterRegisters is true, it will always + * output the registers in the v format. But if false, then it will check if *both* registers are parameter + * registers, and if so, use the p format for both. If only the last register is a parameter register, it will + * use the v format for both, otherwise it would be confusing to have something like {v20 .. p1} + * @param writer the IndentingWriter to write to + * @param codeItem the CodeItem that the register is from + * @param startRegister the first register in the range + * @param lastRegister the last register in the range + */ + public static void writeRegisterRange(IndentingWriter writer, CodeItem codeItem, int startRegister, + int lastRegister) throws IOException { + assert lastRegister >= startRegister; + + if (!baksmali.noParameterRegisters) { + int parameterRegisterCount = codeItem.getParent().method.getPrototype().getParameterRegisterCount() + + (((codeItem.getParent().accessFlags & AccessFlags.STATIC.getValue())==0)?1:0); + int registerCount = codeItem.getRegisterCount(); + + assert startRegister <= lastRegister; + + if (startRegister >= registerCount - parameterRegisterCount) { + writer.write("{p"); + writer.printSignedIntAsDec(startRegister - (registerCount - parameterRegisterCount)); + writer.write(" .. p"); + writer.printSignedIntAsDec(lastRegister - (registerCount - parameterRegisterCount)); + writer.write('}'); + return; + } + } + writer.write("{v"); + writer.printSignedIntAsDec(startRegister); + writer.write(" .. v"); + writer.printSignedIntAsDec(lastRegister); + writer.write('}'); + } + + /** + * Writes a register with the appropriate format. If baksmali.noParameterRegisters is true, then it will always + * output a register in the v format. If false, then it determines if the register is a parameter register, + * and if so, formats it in the p format instead. + * + * @param writer the IndentingWriter to write to + * @param codeItem the CodeItem that the register is from + * @param register the register number + */ + public static void writeTo(IndentingWriter writer, CodeItem codeItem, int register) throws IOException { + if (!baksmali.noParameterRegisters) { + int parameterRegisterCount = codeItem.getParent().method.getPrototype().getParameterRegisterCount() + + (((codeItem.getParent().accessFlags & AccessFlags.STATIC.getValue())==0)?1:0); + int registerCount = codeItem.getRegisterCount(); + if (register >= registerCount - parameterRegisterCount) { + writer.write('p'); + writer.printSignedIntAsDec((register - (registerCount - parameterRegisterCount))); + return; + } + } + writer.write('v'); + writer.printSignedIntAsDec(register); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java new file mode 100644 index 00000000..f32d7aa0 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java @@ -0,0 +1,62 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2011 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.Adaptors; + +import org.jf.dexlib.Code.Analysis.SyntheticAccessorResolver; +import static org.jf.dexlib.Code.Analysis.SyntheticAccessorResolver.AccessedMember; +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public class SyntheticAccessCommentMethodItem extends MethodItem { + private final AccessedMember accessedMember; + + public SyntheticAccessCommentMethodItem(AccessedMember accessedMember, int codeAddress) { + super(codeAddress); + this.accessedMember = accessedMember; + } + + public double getSortOrder() { + //just before the pre-instruction register information, if any + return 99.8; + } + + public boolean writeTo(IndentingWriter writer) throws IOException { + writer.write('#'); + if (accessedMember.getAccessedMemberType() == SyntheticAccessorResolver.METHOD) { + writer.write("calls: "); + } else if (accessedMember.getAccessedMemberType() == SyntheticAccessorResolver.GETTER) { + writer.write("getter for: "); + } else { + writer.write("setter for: "); + } + ReferenceFormatter.writeReference(writer, accessedMember.getAccessedMember()); + return true; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/BooleanRenderer.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/BooleanRenderer.java new file mode 100644 index 00000000..f181bb48 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/BooleanRenderer.java @@ -0,0 +1,43 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.Renderers; + +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public class BooleanRenderer { + public static void writeTo(IndentingWriter writer, boolean val) throws IOException { + if (val) { + writer.write("true"); + } else { + writer.write("false"); + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/ByteRenderer.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/ByteRenderer.java new file mode 100644 index 00000000..9c060fd4 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/ByteRenderer.java @@ -0,0 +1,53 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Renderers; + +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public class ByteRenderer { + public static void writeTo(IndentingWriter writer, byte val) throws IOException { + if (val<0) { + writer.write("-0x"); + writer.printUnsignedLongAsHex(-val); + writer.write('t'); + } else { + writer.write("0x"); + writer.printUnsignedLongAsHex(val); + writer.write('t'); + } + } + + public static void writeUnsignedTo(IndentingWriter writer, byte val) throws IOException { + writer.write("0x"); + writer.printUnsignedLongAsHex(val & 0xFF); + writer.write('t'); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/CharRenderer.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/CharRenderer.java new file mode 100644 index 00000000..90854d15 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/CharRenderer.java @@ -0,0 +1,42 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Renderers; + +import org.jf.util.IndentingWriter; +import org.jf.dexlib.Util.Utf8Utils; + +import java.io.IOException; + +public class CharRenderer { + public static void writeTo(IndentingWriter writer, char val) throws IOException { + writer.write('\''); + Utf8Utils.writeEscapedChar(writer, val); + writer.write('\''); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/DoubleRenderer.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/DoubleRenderer.java new file mode 100644 index 00000000..03fff640 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/DoubleRenderer.java @@ -0,0 +1,39 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.Renderers; + +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public class DoubleRenderer { + public static void writeTo(IndentingWriter writer, double val) throws IOException { + writer.write(Double.toString(val)); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/FloatRenderer.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/FloatRenderer.java new file mode 100644 index 00000000..a1de2b9d --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/FloatRenderer.java @@ -0,0 +1,40 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Renderers; + +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public class FloatRenderer { + public static void writeTo(IndentingWriter writer, float val) throws IOException { + writer.write(Float.toString(val)); + writer.write('f'); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/IntegerRenderer.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/IntegerRenderer.java new file mode 100644 index 00000000..22beaacb --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/IntegerRenderer.java @@ -0,0 +1,50 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Renderers; + +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public class IntegerRenderer { + public static void writeTo(IndentingWriter writer, int val) throws IOException { + if (val<0) { + writer.write("-0x"); + writer.printUnsignedLongAsHex(-((long) val)); + } else { + writer.write("0x"); + writer.printUnsignedLongAsHex(val); + } + } + + public static void writeUnsignedTo(IndentingWriter writer, int val) throws IOException { + writer.write("0x"); + writer.printUnsignedLongAsHex(val & 0xFFFFFFFFL); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/LongRenderer.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/LongRenderer.java new file mode 100644 index 00000000..83076e48 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/LongRenderer.java @@ -0,0 +1,63 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Renderers; + +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public class LongRenderer { + public static void writeTo(IndentingWriter writer, long val) throws IOException { + if (val<0) { + writer.write("-0x"); + writer.printUnsignedLongAsHex(-val); + writer.write('L'); + } else { + writer.write("0x"); + writer.printUnsignedLongAsHex(val); + writer.write('L'); + } + } + + public static void writeSignedIntOrLongTo(IndentingWriter writer, long val) throws IOException { + if (val<0) { + writer.write("-0x"); + writer.printUnsignedLongAsHex(-val); + if (val < Integer.MIN_VALUE) { + writer.write('L'); + } + } else { + writer.write("0x"); + writer.printUnsignedLongAsHex(val); + if (val > Integer.MAX_VALUE) { + writer.write('L'); + } + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/ShortRenderer.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/ShortRenderer.java new file mode 100644 index 00000000..3385c6b6 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/ShortRenderer.java @@ -0,0 +1,47 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Renderers; + +import org.jf.util.IndentingWriter; + +import java.io.IOException; + +public class ShortRenderer { + public static void writeTo(IndentingWriter writer, short val) throws IOException { + if (val < 0) { + writer.write("-0x"); + writer.printUnsignedLongAsHex(-val); + writer.write('s'); + } else { + writer.write("0x"); + writer.printUnsignedLongAsHex(val); + writer.write('s'); + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java new file mode 100644 index 00000000..549d8eab --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java @@ -0,0 +1,214 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.baksmali.Adaptors.ClassDefinition; +import org.jf.dexlib.ClassDefItem; +import org.jf.dexlib.Code.Analysis.*; +import org.jf.dexlib.DexFile; +import org.jf.util.ClassFileNameHandler; +import org.jf.util.IndentingWriter; + +import java.io.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class baksmali { + public static boolean noParameterRegisters = false; + public static boolean useLocalsDirective = false; + public static boolean useSequentialLabels = false; + public static boolean outputDebugInfo = true; + public static boolean addCodeOffsets = false; + public static boolean noAccessorComments = false; + public static boolean deodex = false; + public static boolean verify = false; + public static InlineMethodResolver inlineResolver = null; + public static int registerInfo = 0; + public static String bootClassPath; + + public static SyntheticAccessorResolver syntheticAccessorResolver = null; + + public static void disassembleDexFile(String dexFilePath, DexFile dexFile, boolean deodex, String outputDirectory, + String[] classPathDirs, String bootClassPath, String extraBootClassPath, + boolean noParameterRegisters, boolean useLocalsDirective, + boolean useSequentialLabels, boolean outputDebugInfo, boolean addCodeOffsets, + boolean noAccessorComments, int registerInfo, boolean verify, + boolean ignoreErrors, String inlineTable) + { + baksmali.noParameterRegisters = noParameterRegisters; + baksmali.useLocalsDirective = useLocalsDirective; + baksmali.useSequentialLabels = useSequentialLabels; + baksmali.outputDebugInfo = outputDebugInfo; + baksmali.addCodeOffsets = addCodeOffsets; + baksmali.noAccessorComments = noAccessorComments; + baksmali.deodex = deodex; + baksmali.registerInfo = registerInfo; + baksmali.bootClassPath = bootClassPath; + baksmali.verify = verify; + + if (registerInfo != 0 || deodex || verify) { + try { + String[] extraBootClassPathArray = null; + if (extraBootClassPath != null && extraBootClassPath.length() > 0) { + assert extraBootClassPath.charAt(0) == ':'; + extraBootClassPathArray = extraBootClassPath.substring(1).split(":"); + } + + if (dexFile.isOdex() && bootClassPath == null) { + //ext.jar is a special case - it is typically the 2nd jar in the boot class path, but it also + //depends on classes in framework.jar (typically the 3rd jar in the BCP). If the user didn't + //specify a -c option, we should add framework.jar to the boot class path by default, so that it + //"just works" + if (extraBootClassPathArray == null && isExtJar(dexFilePath)) { + extraBootClassPathArray = new String[] {"framework.jar"}; + } + ClassPath.InitializeClassPathFromOdex(classPathDirs, extraBootClassPathArray, dexFilePath, dexFile); + } else { + String[] bootClassPathArray = null; + if (bootClassPath != null) { + bootClassPathArray = bootClassPath.split(":"); + } + ClassPath.InitializeClassPath(classPathDirs, bootClassPathArray, extraBootClassPathArray, + dexFilePath, dexFile); + } + + if (inlineTable != null) { + inlineResolver = new CustomInlineMethodResolver(inlineTable); + } + } catch (Exception ex) { + System.err.println("\n\nError occured while loading boot class path files. Aborting."); + ex.printStackTrace(System.err); + System.exit(1); + } + } + + File outputDirectoryFile = new File(outputDirectory); + if (!outputDirectoryFile.exists()) { + if (!outputDirectoryFile.mkdirs()) { + System.err.println("Can't create the output directory " + outputDirectory); + System.exit(1); + } + } + + if (!noAccessorComments) { + syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile); + } + + //sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file + //name collisions, then we'll use the same name for each class, if the dex file goes through multiple + //baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames + //may still change of course + ArrayList classDefItems = new ArrayList(dexFile.ClassDefsSection.getItems()); + Collections.sort(classDefItems, new Comparator() { + public int compare(ClassDefItem classDefItem1, ClassDefItem classDefItem2) { + return classDefItem1.getClassType().getTypeDescriptor().compareTo(classDefItem1.getClassType().getTypeDescriptor()); + } + }); + + ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali"); + + for (ClassDefItem classDefItem: classDefItems) { + /** + * The path for the disassembly file is based on the package name + * The class descriptor will look something like: + * Ljava/lang/Object; + * Where the there is leading 'L' and a trailing ';', and the parts of the + * package name are separated by '/' + */ + + String classDescriptor = classDefItem.getClassType().getTypeDescriptor(); + + //validate that the descriptor is formatted like we expect + if (classDescriptor.charAt(0) != 'L' || + classDescriptor.charAt(classDescriptor.length()-1) != ';') { + System.err.println("Unrecognized class descriptor - " + classDescriptor + " - skipping class"); + continue; + } + + File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor); + + //create and initialize the top level string template + ClassDefinition classDefinition = new ClassDefinition(classDefItem); + + //write the disassembly + Writer writer = null; + try + { + File smaliParent = smaliFile.getParentFile(); + if (!smaliParent.exists()) { + if (!smaliParent.mkdirs()) { + System.err.println("Unable to create directory " + smaliParent.toString() + " - skipping class"); + continue; + } + } + + if (!smaliFile.exists()){ + if (!smaliFile.createNewFile()) { + System.err.println("Unable to create file " + smaliFile.toString() + " - skipping class"); + continue; + } + } + + BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter( + new FileOutputStream(smaliFile), "UTF8")); + + writer = new IndentingWriter(bufWriter); + classDefinition.writeTo((IndentingWriter)writer); + } catch (Exception ex) { + System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class"); + ex.printStackTrace(); + smaliFile.delete(); + } + finally + { + if (writer != null) { + try { + writer.close(); + } catch (Throwable ex) { + System.err.println("\n\nError occured while closing file " + smaliFile.toString()); + ex.printStackTrace(); + } + } + } + + if (!ignoreErrors && classDefinition.hadValidationErrors()) { + System.exit(1); + } + } + } + + private static final Pattern extJarPattern = Pattern.compile("(?:^|\\\\|/)ext.(?:jar|odex)$"); + private static boolean isExtJar(String dexFilePath) { + Matcher m = extJarPattern.matcher(dexFilePath); + return m.find(); + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/dump.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/dump.java new file mode 100644 index 00000000..f428642e --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/dump.java @@ -0,0 +1,109 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.ByteArrayAnnotatedOutput; + +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; + +public class dump { + public static void dump(DexFile dexFile, String dumpFileName, String outputDexFileName, boolean sort) + throws IOException { + + if (sort) { + //sort all items, to guarantee a unique ordering + dexFile.setSortAllItems(true); + } else { + //don't change the order + dexFile.setInplace(true); + } + + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); + + if (dumpFileName != null) { + out.enableAnnotations(120, true); + } + + dexFile.place(); + dexFile.writeTo(out); + + //write the dump + if (dumpFileName != null) { + out.finishAnnotating(); + FileWriter writer = null; + + + try { + writer = new FileWriter(dumpFileName); + out.writeAnnotationsTo(writer); + } catch (IOException ex) { + System.err.println("\n\nThere was an error while dumping the dex file to " + dumpFileName); + ex.printStackTrace(); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException ex) { + System.err.println("\n\nThere was an error while closing the dump file " + dumpFileName); + ex.printStackTrace(); + } + } + } + } + + //rewrite the dex file + if (outputDexFileName != null) { + byte[] bytes = out.toByteArray(); + + DexFile.calcSignature(bytes); + DexFile.calcChecksum(bytes); + + FileOutputStream fileOutputStream = null; + try { + fileOutputStream = new FileOutputStream(outputDexFileName); + fileOutputStream.write(bytes); + } catch (IOException ex) { + System.err.println("\n\nThere was an error while writing the dex file " + outputDexFileName); + ex.printStackTrace(); + } finally { + if (fileOutputStream != null) { + try { + fileOutputStream.close(); + } catch (IOException ex) { + System.err.println("\n\nThere was an error while closing the dex file " + outputDexFileName); + ex.printStackTrace(); + } + } + } + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java new file mode 100644 index 00000000..693ae166 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java @@ -0,0 +1,512 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.apache.commons.cli.*; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.util.ConsoleUtil; +import org.jf.util.SmaliHelpFormatter; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Properties; + +public class main { + + public static final String VERSION; + + private static final Options basicOptions; + private static final Options debugOptions; + private static final Options options; + + public static final int ALL = 1; + public static final int ALLPRE = 2; + public static final int ALLPOST = 4; + public static final int ARGS = 8; + public static final int DEST = 16; + public static final int MERGE = 32; + public static final int FULLMERGE = 64; + + public static final int DIFFPRE = 128; + + static { + options = new Options(); + basicOptions = new Options(); + debugOptions = new Options(); + buildOptions(); + + InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties"); + Properties properties = new Properties(); + String version = "(unknown)"; + try { + properties.load(templateStream); + version = properties.getProperty("application.version"); + } catch (IOException ex) { + } + VERSION = version; + } + + /** + * This class is uninstantiable. + */ + private main() { + } + + /** + * Run! + */ + public static void main(String[] args) { + Locale locale = new Locale("en", "US"); + Locale.setDefault(locale); + + CommandLineParser parser = new PosixParser(); + CommandLine commandLine; + + try { + commandLine = parser.parse(options, args); + } catch (ParseException ex) { + usage(); + return; + } + + boolean disassemble = true; + boolean doDump = false; + boolean write = false; + boolean sort = false; + boolean fixRegisters = false; + boolean noParameterRegisters = false; + boolean useLocalsDirective = false; + boolean useSequentialLabels = false; + boolean outputDebugInfo = true; + boolean addCodeOffsets = false; + boolean noAccessorComments = false; + boolean deodex = false; + boolean verify = false; + boolean ignoreErrors = false; + + int apiLevel = 14; + + int registerInfo = 0; + + String outputDirectory = "out"; + String dumpFileName = null; + String outputDexFileName = null; + String inputDexFileName = null; + String bootClassPath = null; + StringBuffer extraBootClassPathEntries = new StringBuffer(); + List bootClassPathDirs = new ArrayList(); + bootClassPathDirs.add("."); + String inlineTable = null; + + String[] remainingArgs = commandLine.getArgs(); + + Option[] options = commandLine.getOptions(); + + for (int i=0; i", + "disassembles and/or dumps a dex file", basicOptions, printDebugOptions?debugOptions:null); + } + + private static void usage() { + usage(false); + } + + /** + * Prints the version message. + */ + protected static void version() { + System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)"); + System.out.println("Copyright (C) 2010 Ben Gruver (JesusFreke@JesusFreke.com)"); + System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)"); + System.exit(0); + } + + private static void buildOptions() { + 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. Specify twice for debug options") + .create("?"); + + Option outputDirOption = OptionBuilder.withLongOpt("output") + .withDescription("the directory where the disassembled files will be placed. The default is out") + .hasArg() + .withArgName("DIR") + .create("o"); + + Option noParameterRegistersOption = OptionBuilder.withLongOpt("no-parameter-registers") + .withDescription("use the v syntax instead of the p syntax for registers mapped to method " + + "parameters") + .create("p"); + + 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") + .withDescription("output the .locals directive with the number of non-parameter registers, rather" + + " than the .register directive with the total number of register") + .create("l"); + + Option sequentialLabelsOption = OptionBuilder.withLongOpt("sequential-labels") + .withDescription("create label names using a sequential numbering scheme per label type, rather than " + + "using the bytecode address") + .create("s"); + + Option noDebugInfoOption = OptionBuilder.withLongOpt("no-debug-info") + .withDescription("don't write out debug info (.local, .param, .line, etc.)") + .create("b"); + + Option registerInfoOption = OptionBuilder.withLongOpt("register-info") + .hasOptionalArgs() + .withArgName("REGISTER_INFO_TYPES") + .withValueSeparator(',') + .withDescription("print the specificed type(s) of register information for each instruction. " + + "\"ARGS,DEST\" is the default if no types are specified.\nValid values are:\nALL: all " + + "pre- and post-instruction registers.\nALLPRE: all pre-instruction registers\nALLPOST: all " + + "post-instruction registers\nARGS: any pre-instruction registers used as arguments to the " + + "instruction\nDEST: the post-instruction destination register, if any\nMERGE: Any " + + "pre-instruction register has been merged from more than 1 different post-instruction " + + "register from its predecessors\nFULLMERGE: For each register that would be printed by " + + "MERGE, also show the incoming register types that were merged") + .create("r"); + + 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 the value begins with a " + + ":, it will be appended to the default bootclasspath instead of replacing it") + .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("d"); + + Option codeOffsetOption = OptionBuilder.withLongOpt("code-offsets") + .withDescription("add comments to the disassembly containing the code offset for each address") + .create("f"); + + Option noAccessorCommentsOption = OptionBuilder.withLongOpt("no-accessor-comments") + .withDescription("don't output helper comments for synthetic accessors") + .create("m"); + + Option apiLevelOption = OptionBuilder.withLongOpt("api-level") + .withDescription("The numeric api-level of the file being disassembled. If not " + + "specified, it defaults to 14 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); + + Option dumpOption = OptionBuilder.withLongOpt("dump-to") + .withDescription("dumps the given dex file into a single annotated dump file named FILE" + + " (.dump by default), along with the normal disassembly") + .hasOptionalArg() + .withArgName("FILE") + .create("D"); + + Option ignoreErrorsOption = OptionBuilder.withLongOpt("ignore-errors") + .withDescription("ignores any non-fatal errors that occur while disassembling/deodexing," + + " ignoring the class if needed, and continuing with the next class. The default" + + " behavior is to stop disassembling and exit once an error is encountered") + .create("I"); + + + Option noDisassemblyOption = OptionBuilder.withLongOpt("no-disassembly") + .withDescription("suppresses the output of the disassembly") + .create("N"); + + Option writeDexOption = OptionBuilder.withLongOpt("write-dex") + .withDescription("additionally rewrites the input dex file to FILE") + .hasArg() + .withArgName("FILE") + .create("W"); + + Option sortOption = OptionBuilder.withLongOpt("sort") + .withDescription("sort the items in the dex file into a canonical order before dumping/writing") + .create("S"); + + Option fixSignedRegisterOption = OptionBuilder.withLongOpt("fix-signed-registers") + .withDescription("when dumping or rewriting, fix any registers in the debug info that are encoded as" + + " a signed value") + .create("F"); + + Option verifyDexOption = OptionBuilder.withLongOpt("verify") + .withDescription("perform bytecode verification") + .create("V"); + + Option inlineTableOption = OptionBuilder.withLongOpt("inline-table") + .withDescription("specify a file containing a custom inline method table to use for deodexing") + .hasArg() + .withArgName("FILE") + .create("T"); + + basicOptions.addOption(versionOption); + basicOptions.addOption(helpOption); + basicOptions.addOption(outputDirOption); + basicOptions.addOption(noParameterRegistersOption); + basicOptions.addOption(deodexerantOption); + basicOptions.addOption(useLocalsOption); + basicOptions.addOption(sequentialLabelsOption); + basicOptions.addOption(noDebugInfoOption); + basicOptions.addOption(registerInfoOption); + basicOptions.addOption(classPathOption); + basicOptions.addOption(classPathDirOption); + basicOptions.addOption(codeOffsetOption); + basicOptions.addOption(noAccessorCommentsOption); + basicOptions.addOption(apiLevelOption); + + debugOptions.addOption(dumpOption); + debugOptions.addOption(ignoreErrorsOption); + debugOptions.addOption(noDisassemblyOption); + debugOptions.addOption(writeDexOption); + debugOptions.addOption(sortOption); + debugOptions.addOption(fixSignedRegisterOption); + debugOptions.addOption(verifyDexOption); + debugOptions.addOption(inlineTableOption); + + for (Object option: basicOptions.getOptions()) { + options.addOption((Option)option); + } + for (Object option: debugOptions.getOptions()) { + options.addOption((Option)option); + } + } +} \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/main/resources/properties/baksmali.properties b/brut.apktool.smali/baksmali/src/main/resources/properties/baksmali.properties new file mode 100644 index 00000000..bd3e633f --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/resources/properties/baksmali.properties @@ -0,0 +1 @@ +application.version=1.4.0 \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/main/resources/templates/templates/baksmali.stg b/brut.apktool.smali/baksmali/src/main/resources/templates/templates/baksmali.stg new file mode 100644 index 00000000..8b5b42f7 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/resources/templates/templates/baksmali.stg @@ -0,0 +1,436 @@ +group baksmali; + +smaliFile(AccessFlags, ClassType, SuperType, SourceFile, Interfaces, Annotations, StaticFields, + InstanceFields, DirectMethods, VirtualMethods) ::= +<< +.class }> + +.super + + + +.source "" + + + + + +# interfaces + + + + + + +# annotations + + + + + + +# static fields + + + + + + +# instance fields + + + + + + +# direct methods + + + + + + +# virtual methods + + + +>> + + + + +implement(interface) ::= +<< +.implements +>> + + +annotation(Visibility, AnnotationType, Elements) ::= +<< +.annotation + + + + + + + +.end annotation +>> + + + +field(AccessFlags, FieldName, FieldType, Annotations, InitialValue, Comments) ::= +<< +} ; separator="\n"> + + +.field }>: = + + +.end field + + +>> + + +method(AccessFlags, MethodName, Prototype, HasCode, RegistersDirective, RegisterCount, Parameters, Annotations, + MethodItems) ::= +<< +.method }> + + + + + + + + + + + + + +.end method +>> + +Parameter(ParameterName, Annotations) ::= +<< +.parameter "" + + +.end parameter + +>> + +Format10t(Opcode, TargetLabel) ::= +<< + +>> + +Format10x(Opcode) ::= +<< + +>> + +Format11n(Opcode, RegisterA, Literal) ::= +<< + , +>> + +Format11x(Opcode, RegisterA) ::= +<< + +>> + +Format12x(Opcode, RegisterA, RegisterB) ::= +<< + , +>> + +Format20t(Opcode, TargetLabel) ::= +<< + +>> + +Format21c(Opcode, RegisterA, Reference) ::= +<< + , +>> + +Format21h(Opcode, RegisterA, Literal) ::= +<< + , +>> + +Format21s(Opcode, RegisterA, Literal) ::= +<< + , +>> + +Format21t(Opcode, RegisterA, TargetLabel) ::= +<< + , +>> + +Format22b(Opcode, RegisterA, RegisterB, Literal) ::= +<< + , , +>> + +Format22c(Opcode, RegisterA, RegisterB, Reference) ::= +<< + , , +>> + +Format22cs(Opcode, RegisterA, RegisterB, FieldOffset) ::= +<< + , , field@ +>> + +Format22s(Opcode, RegisterA, RegisterB, Literal) ::= +<< + , , +>> + +Format22t(Opcode, RegisterA, RegisterB, TargetLabel) ::= +<< + , , +>> + +Format22x(Opcode, RegisterA, RegisterB) ::= +<< + , +>> + +Format23x(Opcode, RegisterA, RegisterB, RegisterC) ::= +<< + , , +>> + +Format30t(Opcode, TargetLabel) ::= +<< + +>> + +Format31c(Opcode, RegisterA, Reference) ::= +<< + , +>> + +Format31i(Opcode, RegisterA, Literal) ::= +<< + , +>> + +Format31t(Opcode, RegisterA, TargetLabel) ::= +<< + , +>> + +Format32x(Opcode, RegisterA, RegisterB) ::= +<< + , +>> + +Format35c(Opcode, Registers, Reference) ::= +<< + {}, +>> + +Format35s(Opcode, Registers, Reference) ::= +<< + {}, +>> + +Format35ms(Opcode, Registers, MethodIndex) ::= +<< + {}, vtable@ +>> + +Format3rc(Opcode, StartRegister, LastRegister, Reference) ::= +<< + { .. }, +>> + +Format3rms(Opcode, StartRegister, LastRegister, MethodIndex) ::= +<< + { .. }, vtable@ +>> + +Format51l(Opcode, RegisterA, Literal) ::= +<< + , +>> + +CommentedOutMethodItem(MethodItem) ::= +<< +# +>> + +UnresolvedNullReference(Opcode, Register, UseInvokeRange, AddGoto) ::= +<< + +#Replaced unresolvable optimized invoke-*-range-quick instruction +#with a generic method call that will throw a NullPointerException +invoke-virtual/range { .. }, Ljava/lang/Object;->hashCode()I +goto/32 0 + +#Replaced unresolvable optimized instruction with a throw +throw + +>> + + +ArrayData(Opcode, ElementWidth, Values, Dead) ::= +<< +#.array-data + +}; separator="\n"> + +}; separator="\n"> + + +#.end array-data +>> + +ArrayElement(Bytes) ::= +<< + +>> + +PackedSwitchData(Opcode, FirstKey, Targets, Dead) ::= +<< +#.packed-switch + +}; separator="\n"> + +}; separator="\n"> + + +#.end packed-switch +>> + +SparseSwitchData(Opcode, Targets, Dead) ::= +<< +#.sparse-switch + + -> }; separator="\n"> + + -> }; separator="\n"> + + +#.end sparse-switch +>> + + +Label(Prefix, Suffix) ::= +<< +: +>> + +Line(Line) ::= +<< +.line +>> + +EndPrologue(Prologue) ::= +<< +.prologue +>> + +StartEpilogue(Epilogue) ::= +<< +.epilogue +>> + +StartLocal(Register, Name, Type, Signature) ::= +<< +.local , :,"" +>> + +EndLocal(Register, Name, Type, Signature) ::= +<< +.end local #:,, "" +>> + +RestartLocal(Register, Name, Type, Signature) ::= +<< +.restart local #:,, "" +>> + +SetFile(FileName) ::= +<< +.source "" +>> + +Blank(Blank) ::= +<< + +>> + +Catch(ExceptionType, StartLabel, EndLabel, HandlerLabel) ::= +<< +.catch .catchall { .. } +>> + + +StringReference(EscapedValue) ::= +<< +"" +>> + +FieldReference(ContainingClass, FieldName, FieldType) ::= +<< +->: +>> + +MethodReference(ContainingClass, MethodName, Prototype) ::= +<< +-> +>> + +TypeReference(TypeDescriptor) ::= +<< + +>> + + +SimpleEncodedValue(Value) ::= +<< + +>> + +EncodedIndexedItemReference(Value) ::= +<< + +>> + +ArrayEncodedValue(Value) ::= +<< +{ + +} +>> + +EnumEncodedValue(Value) ::= +<< +.enum +>> + +AnnotationEncodedValue(AnnotationType, Elements) ::= +<< +.subannotation + +.end subannotation +>> + +AnnotationElement(Name, Value) ::= +<< + = +>> + +Comment(Comment) ::= +<< +# +>> \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/baksmali_test_class.smali b/brut.apktool.smali/baksmali/src/test/smali/baksmali_test_class.smali new file mode 100644 index 00000000..903baf48 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/baksmali_test_class.smali @@ -0,0 +1,218 @@ +.class public Lbaksmali/test/class; +.super Ljava/lang/Object; + +.source "baksmali_test_class.smali" + +.implements Lsome/interface; +.implements Lsome/other/interface; + + +.annotation build Lsome/annotation; + value1 = "test" + value2 = .subannotation Lsome/annotation; + value1 = "test2" + value2 = Lsome/enum; + .end subannotation +.end annotation + +.annotation system Lsome/annotation; +.end annotation + + + +.field public static aStaticFieldWithoutAnInitializer:I + +.field public static longStaticField:J = 0x300000000L +.field public static longNegStaticField:J = -0x300000000L + +.field public static intStaticField:I = 0x70000000 +.field public static intNegStaticField:I = -500 + +.field public static shortStaticField:S = 500s +.field public static shortNegStaticField:S = -500s + +.field public static byteStaticField:B = 123t +.field public static byteNegStaticField:B = 0xAAt + +.field public static floatStaticField:F = 3.1415926f + +.field public static doubleStaticField:D = 3.141592653589793 + +.field public static charStaticField:C = 'a' +.field public static charEscapedStaticField:C = '\n' + +.field public static boolTrueStaticField:Z = true +.field public static boolFalseStaticField:Z = false + +.field public static typeStaticField:Ljava/lang/Class; = Lbaksmali/test/class; + +.field public static stringStaticField:Ljava/lang/String; = "test" +.field public static stringEscapedStaticField:Ljava/lang/String; = "test\ntest" + + +.field public static fieldStaticField:Ljava/lang/reflect/Field; = Lbaksmali/test/class;->fieldStaticField:Ljava/lang/reflect/Field; + +.field public static methodStaticField:Ljava/lang/reflect/Method; = Lbaksmali/test/class;->teshMethod(ILjava/lang/String;)Ljava/lang/String; + +.field public static arrayStaticField:[I = {1, 2, 3, {1, 2, 3, 4}} + +.field public static enumStaticField:Lsome/enum; = .enum Lsome/enum;->someEnumValue:Lsome/enum; + +.field public static annotationStaticField:Lsome/annotation; = .subannotation Lsome/annotation; + value1 = "test" + value2 = .subannotation Lsome/annotation; + value1 = "test2" + value2 = Lsome/enum; + .end subannotation +.end subannotation + +.field public static staticFieldWithAnnotation:I + .annotation runtime La/field/annotation; + this = "is" + a = "test" + .end annotation + .annotation runtime Lorg/junit/Test; + .end annotation +.end field + +.field public instanceField:Ljava/lang/String; + + + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public testMethod(ILjava/lang/String;)Ljava/lang/String; + .registers 3 + .annotation runtime Lorg/junit/Test; + .end annotation + .annotation system Lyet/another/annotation; + somevalue = 1234 + anothervalue = 3.14159 + .end annotation + + const-string v0, "testing\n123" + + goto switch: + + sget v0, Lbaksmali/test/class;->staticField:I + + switch: + packed-switch v0, pswitch: + + try_start: + const/4 v0, 7 + const v0, 10 + nop + try_end: + .catch Ljava/lang/Exception; {try_start: .. try_end:} handler: + .catchall {try_start: .. try_end:} handler2: + + handler: + + Label10: + Label11: + Label12: + Label13: + return-object v0 + + + + .array-data 4 + 1 2 3 4 5 6 200 + .end array-data + + pswitch: + .packed-switch 10 + Label10: + Label11: + Label12: + Label13: + .end packed-switch + + handler2: + + return-void + +.end method + +.method public abstract testMethod2()V + .annotation runtime Lsome/annotation; + subannotation = .subannotation Lsome/other/annotation; + value = "value" + .end subannotation + .end annotation + .annotation runtime Lorg/junit/Test; + .end annotation +.end method + + +.method public tryTest()V + .registers 1 + + handler: + nop + + + try_start: + const/4 v0, 7 + const v0, 10 + nop + try_end: + .catch Ljava/lang/Exception; {try_start: .. try_end:} handler: +.end method + + +.method public debugTest(IIIII)V + .registers 10 + + .parameter "Blah" + .parameter + .parameter "BlahWithAnnotations" + .annotation runtime Lsome/annotation; + something = "some value" + somethingelse = 1234 + .end annotation + .annotation runtime La/second/annotation; + .end annotation + .end parameter + .parameter + .annotation runtime Lsome/annotation; + something = "some value" + somethingelse = 1234 + .end annotation + .end parameter + .parameter "LastParam" + + .prologue + + nop + nop + + .source "somefile.java" + .line 101 + + nop + + + .line 50 + + .local v0, aNumber:I + const v0, 1234 + .end local v0 + + .source "someotherfile.java" + .line 900 + + const-string v0, "1234" + + .restart local v0 + const v0, 6789 + .end local v0 + + .epilogue + +.end method \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/main.smali b/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/main.smali new file mode 100644 index 00000000..db88b46c --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/main.smali @@ -0,0 +1,70 @@ +.class public Lmain; + +.super Ljava/lang/Object; + +.method public static main([Ljava/lang/String;)V + .registers 3 + + :here4 + const v0, 0 + + :here3 + + new-instance v2, Lsuperclass; + invoke-direct {v2}, Lsuperclass;->()V + + if-eqz v0, :here + + + #this is the unresolvable instruction. v0 is always null, + #and this will always throw an exception. It should be + #replaced with throw v0. + invoke-virtual {v0}, Lrandomclass;->getSuperclass()Lsuperclass; + move-result-object v1 + + + if-eqz v0, :here + + #this would normally be deodexable, except that it follows + #the above un-deodexeable instruction, which prevents the + #propagation of any register information. It can never be + #reached, and should be replaced with throw v2 + invoke-virtual {v2}, Lsuperclass;->somemethod()V + + #another odexed instruction that uses the result of the + #first unresolveable odex instruction. This should + #be replaced with throw v1 + invoke-virtual {v1}, Lsuperclass;->somemethod()V + + :here + + #and we're back to the non-dead code + invoke-virtual {v2}, Lsuperclass;->somemethod()V + + if-nez v0, :here3 + + return-void +.end method + +.method public static FirstInstructionTest(Lrandomclass;)V + .registers 1 + + :try_start + invoke-virtual/range {p0}, Lrandomclass;->getSuperclass()Lsuperclass; + return-void + :try_end + .catch Ljava/lang/Exception; {:try_start .. :try_end} :handler + :handler + :inner_try_start + #this tests that the parameter register types are correctly propagated to the exception handlers, in the + #case that the first instruction of the method can throw an exception and is in a try black + invoke-virtual/range {p0}, Lrandomclass;->getSuperclass()Lsuperclass; + return-void + :inner_try_end + .catch Ljava/lang/Exception; {:inner_try_start .. :inner_try_end} :inner_handler + :inner_handler + #this additionally tests that the register types are propagated recursively, in the case that the first + #instruction in the exception handler can also throw an exception, and is covered by a try block + invoke-virtual/range {p0}, Lrandomclass;->getSuperclass()Lsuperclass; + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/randomclass.smali b/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/randomclass.smali new file mode 100644 index 00000000..3c02f137 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/randomclass.smali @@ -0,0 +1,18 @@ +.class public Lrandomclass; + +.super Ljava/lang/Object; + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public getSuperclass()Lsuperclass; + .registers 2 + + new-instance v0, Lsuperclass; + invoke-direct {v0}, Lsuperclass;->()V + + return-object v0 +.end method \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/subclass.smali b/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/subclass.smali new file mode 100644 index 00000000..9ffa4de6 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/subclass.smali @@ -0,0 +1,21 @@ +.class public Lsubclass; + +.super Lsuperclass; + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Lsuperclass;->()V + return-void +.end method + +.method public somemethod()V + .registers 2 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v1, "subclass.somemethod" + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/superclass.smali b/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/superclass.smali new file mode 100644 index 00000000..21491034 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/deodex_test1/superclass.smali @@ -0,0 +1,21 @@ +.class public Lsuperclass; + +.super Ljava/lang/Object; + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public somemethod()V + .registers 2 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v1, "superclass.somemethod" + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/app_classes/main.smali b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/app_classes/main.smali new file mode 100644 index 00000000..505312c6 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/app_classes/main.smali @@ -0,0 +1,41 @@ +.class public Lmain; + +.super Ljava/lang/Object; + +.method public static main([Ljava/lang/String;)V + .registers 6 + + const v2, 0 + + + const v3, 1 + const v4, 0 + new-array v1, v3, [Lsubclass1; + new-instance v0, Lsubclass1; + invoke-direct {v0}, Lsubclass1;->()V + aput-object v0, v1, v4 + + goto :here2 + + :here + const v2, 1 + + :here2 + + #this is tricky, because it has to merge two array types, [Lsubclass1; and [Lsubclass2 + #which should result in [Lsuperclass;. However, this dex file won't have a reference + #to [Lsuperclass; + aget-object v5, v1, v4 + + invoke-virtual {v5}, Lsupersuperclass;->somemethod()V + + + new-array v1, v3, [Lsubclass2; + new-instance v0, Lsubclass2; + invoke-direct {v0}, Lsubclass2;->()V + aput-object v0, v1, v4 + + if-eqz v2, :here + + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/randomclass.smali b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/randomclass.smali new file mode 100644 index 00000000..3c02f137 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/randomclass.smali @@ -0,0 +1,18 @@ +.class public Lrandomclass; + +.super Ljava/lang/Object; + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public getSuperclass()Lsuperclass; + .registers 2 + + new-instance v0, Lsuperclass; + invoke-direct {v0}, Lsuperclass;->()V + + return-object v0 +.end method \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/subclass1.smali b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/subclass1.smali new file mode 100644 index 00000000..d7b840f3 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/subclass1.smali @@ -0,0 +1,21 @@ +.class public Lsubclass1; + +.super Lsuperclass; + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Lsuperclass;->()V + return-void +.end method + +.method public somemethod()V + .registers 2 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v1, "subclass1.somemethod" + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/subclass2.smali b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/subclass2.smali new file mode 100644 index 00000000..605cccb7 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/subclass2.smali @@ -0,0 +1,21 @@ +.class public Lsubclass2; + +.super Lsuperclass; + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Lsuperclass;->()V + return-void +.end method + +.method public somemethod()V + .registers 2 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v1, "subclass2.somemethod" + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/superclass.smali b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/superclass.smali new file mode 100644 index 00000000..e44a17a2 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/superclass.smali @@ -0,0 +1,21 @@ +.class public Lsuperclass; + +.super Lsupersuperclass; + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Lsupersuperclass;->()V + return-void +.end method + +.method public somemethod()V + .registers 2 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v1, "superclass.somemethod" + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/supersuperclass.smali b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/supersuperclass.smali new file mode 100644 index 00000000..9621e14c --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/smali/deodex_test2/bootclass_classes/supersuperclass.smali @@ -0,0 +1,21 @@ +.class public Lsupersuperclass; + +.super Ljava/lang/Object; + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public somemethod()V + .registers 2 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v1, "supersuperclass.somemethod" + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/.gitignore b/brut.apktool.smali/dexlib/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/brut.apktool.smali/dexlib/.gitignore @@ -0,0 +1 @@ +/target diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java new file mode 100644 index 00000000..3942ce52 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java @@ -0,0 +1,600 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import com.google.common.base.Preconditions; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.ExceptionWithContext; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.ReadOnlyArrayList; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; + +public class AnnotationDirectoryItem extends Item { + @Nullable + private AnnotationSetItem classAnnotations; + @Nullable + private FieldAnnotation[] fieldAnnotations; + @Nullable + private MethodAnnotation[] methodAnnotations; + @Nullable + private ParameterAnnotation[] parameterAnnotations; + + /** + * typically each AnnotationDirectoryItem will have a distinct parent. The only case that isn't true is when + * the AnnotationDirectoryItem *only* contains class annotations, with no other type of annotation. In that + * case, the same AnnotationDirectoryItem could be referenced from multiple classes. + * This isn't a problem though, because this field is only used in compareTo to determine the sort order, which + * which knows it should handle a null value as a special case + */ + @Nullable + private ClassDefItem parent = null; + + /** + * Creates a new uninitialized AnnotationDirectoryItem + * @param dexFile The DexFile that this item belongs to + */ + protected AnnotationDirectoryItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new AnnotationDirectoryItem with the given values + * @param dexFile The DexFile that this item belongs to + * @param classAnnotations The annotations associated with the overall class + * @param fieldAnnotations A list of FieldAnnotation objects that contain the field annotations for + * this class + * @param methodAnnotations A list of MethodAnnotation objects that contain the method annotations for + * this class + * @param parameterAnnotations A list of ParameterAnnotation objects that contain the parameter + * annotations for the methods in this class + */ + private AnnotationDirectoryItem(DexFile dexFile, @Nullable AnnotationSetItem classAnnotations, + @Nullable List fieldAnnotations, + @Nullable List methodAnnotations, + @Nullable List parameterAnnotations) { + super(dexFile); + this.classAnnotations = classAnnotations; + + if (fieldAnnotations == null || fieldAnnotations.size() == 0) { + this.fieldAnnotations = null; + } else { + this.fieldAnnotations = new FieldAnnotation[fieldAnnotations.size()]; + this.fieldAnnotations = fieldAnnotations.toArray(this.fieldAnnotations); + Arrays.sort(this.fieldAnnotations); + } + + if (methodAnnotations == null || methodAnnotations.size() == 0) { + this.methodAnnotations = null; + } else { + this.methodAnnotations = new MethodAnnotation[methodAnnotations.size()]; + this.methodAnnotations = methodAnnotations.toArray(this.methodAnnotations); + Arrays.sort(this.methodAnnotations); + } + + if (parameterAnnotations == null || parameterAnnotations.size() == 0) { + this.parameterAnnotations = null; + } else { + this.parameterAnnotations = new ParameterAnnotation[parameterAnnotations.size()]; + this.parameterAnnotations = parameterAnnotations.toArray(this.parameterAnnotations); + Arrays.sort(this.parameterAnnotations); + } + } + + /** + * Returns an AnnotationDirectoryItem for the given values, and that has been interned into the given + * DexFile + * @param dexFile The DexFile that this item belongs to + * @param classAnnotations The annotations associated with the class + * @param fieldAnnotations A list of FieldAnnotation objects containing the field annotations + * @param methodAnnotations A list of MethodAnnotation objects containing the method annotations + * @param parameterAnnotations A list of ParameterAnnotation objects containin the parameter + * annotations + * @return an AnnotationItem for the given values, and that has been interned into the given + * DexFile + */ + public static AnnotationDirectoryItem internAnnotationDirectoryItem(DexFile dexFile, + AnnotationSetItem classAnnotations, + List fieldAnnotations, + List methodAnnotations, + List parameterAnnotations) { + AnnotationDirectoryItem annotationDirectoryItem = new AnnotationDirectoryItem(dexFile, classAnnotations, + fieldAnnotations, methodAnnotations, parameterAnnotations); + return dexFile.AnnotationDirectoriesSection.intern(annotationDirectoryItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + classAnnotations = (AnnotationSetItem)readContext.getOptionalOffsettedItemByOffset( + ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt()); + + int fieldAnnotationCount = in.readInt(); + if (fieldAnnotationCount > 0) { + fieldAnnotations = new FieldAnnotation[fieldAnnotationCount]; + } else { + fieldAnnotations = null; + } + + int methodAnnotationCount = in.readInt(); + if (methodAnnotationCount > 0) { + methodAnnotations = new MethodAnnotation[methodAnnotationCount]; + } else { + methodAnnotations = null; + } + + int parameterAnnotationCount = in.readInt(); + if (parameterAnnotationCount > 0) { + parameterAnnotations = new ParameterAnnotation[parameterAnnotationCount]; + } else { + parameterAnnotations = null; + } + + if (fieldAnnotations != null) { + for (int i=0; iAnnotationSetItem containing the annotations associated with this class, or null + * if there are no class annotations + */ + @Nullable + public AnnotationSetItem getClassAnnotations() { + return classAnnotations; + } + + /** + * Get a list of the field annotations in this AnnotationDirectoryItem + * @return A list of FieldAnnotation objects, or null if there are no field annotations + */ + @Nonnull + public List getFieldAnnotations() { + if (fieldAnnotations == null) { + return Collections.emptyList(); + } + return ReadOnlyArrayList.of(fieldAnnotations); + } + + /** + * Get a list of the method annotations in this AnnotationDirectoryItem + * @return A list of MethodAnnotation objects, or null if there are no method annotations + */ + @Nonnull + public List getMethodAnnotations() { + if (methodAnnotations == null) { + return Collections.emptyList(); + } + return ReadOnlyArrayList.of(methodAnnotations); + } + + /** + * Get a list of the parameter annotations in this AnnotationDirectoryItem + * @return A list of ParameterAnnotation objects, or null if there are no parameter annotations + */ + @Nonnull + public List getParameterAnnotations() { + if (parameterAnnotations == null) { + return Collections.emptyList(); + } + return ReadOnlyArrayList.of(parameterAnnotations); + } + + /** + * Gets the field annotations for the given field, or null if no annotations are defined for that field + * @param fieldIdItem The field to get the annotations for + * @return An AnnotationSetItem containing the field annotations, or null if none are found + */ + @Nullable + public AnnotationSetItem getFieldAnnotations(FieldIdItem fieldIdItem) { + if (fieldAnnotations == null) { + return null; + } + int index = Arrays.binarySearch(fieldAnnotations, fieldIdItem); + if (index < 0) { + return null; + } + return fieldAnnotations[index].annotationSet; + } + + /** + * Gets the method annotations for the given method, or null if no annotations are defined for that method + * @param methodIdItem The method to get the annotations for + * @return An AnnotationSetItem containing the method annotations, or null if none are found + */ + @Nullable + public AnnotationSetItem getMethodAnnotations(MethodIdItem methodIdItem) { + if (methodAnnotations == null) { + return null; + } + int index = Arrays.binarySearch(methodAnnotations, methodIdItem); + if (index < 0) { + return null; + } + return methodAnnotations[index].annotationSet; + } + + /** + * Gets the parameter annotations for the given method, or null if no parameter annotations are defined for that + * method + * @param methodIdItem The method to get the parameter annotations for + * @return An AnnotationSetRefList containing the parameter annotations, or null if none are found + */ + @Nullable + public AnnotationSetRefList getParameterAnnotations(MethodIdItem methodIdItem) { + if (parameterAnnotations == null) { + return null; + } + int index = Arrays.binarySearch(parameterAnnotations, methodIdItem); + if (index < 0) { + return null; + } + return parameterAnnotations[index].annotationSet; + } + + /** + * @return The number of field annotations in this AnnotationDirectoryItem + */ + public int getFieldAnnotationCount() { + if (fieldAnnotations == null) { + return 0; + } + return fieldAnnotations.length; + } + + /** + * @return The number of method annotations in this AnnotationDirectoryItem + */ + public int getMethodAnnotationCount() { + if (methodAnnotations == null) { + return 0; + } + return methodAnnotations.length; + } + + /** + * @return The number of parameter annotations in this AnnotationDirectoryItem + */ + public int getParameterAnnotationCount() { + if (parameterAnnotations == null) { + return 0; + } + return parameterAnnotations.length; + } + + /** + * @return true if this AnnotationDirectoryItem is internable. It is only internable if it has + * only class annotations, but no field, method or parameter annotations + */ + private boolean isInternable() { + return classAnnotations != null && + (fieldAnnotations == null || fieldAnnotations.length == 0) && + (methodAnnotations == null || methodAnnotations.length == 0) && + (parameterAnnotations == null || parameterAnnotations.length == 0); + } + + /** + * Sets the ClassDefItem that this AnnotationDirectoryItem is associated with. + * + * @param classDefItem the ClassDefItem that this AnnotationDirectoryItem is associated + * with. + */ + protected void setParent(ClassDefItem classDefItem) { + // If this AnnotationDirectoryItem is internable, then setParent may be called multiple times, because it is + // reused for multiple classes. In this case, the parent field isn't used, so it doesn't matter if we overwrite + // it. + // If, on the other hand, it is not internable, we want to make sure that only a single parent is set. parent + // should either be null, or be equal to the new parent + Preconditions.checkState(this.isInternable() || (parent == null || parent.equals(classDefItem))); + this.parent = classDefItem; + } + + @Override + public int hashCode() { + // An instance is internable only if it has only class annotations, but no other type of annotation + if (!isInternable()) { + return super.hashCode(); + } + return classAnnotations.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + AnnotationDirectoryItem other = (AnnotationDirectoryItem)o; + return (this.compareTo(other) == 0); + } + + public static class FieldAnnotation implements Comparable>, Convertible { + public final FieldIdItem field; + public final AnnotationSetItem annotationSet; + + public FieldAnnotation(FieldIdItem field, AnnotationSetItem annotationSet) { + this.field = field; + this.annotationSet = annotationSet; + } + + public int compareTo(Convertible other) { + return field.compareTo(other.convert()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + return compareTo((FieldAnnotation)o) == 0; + } + + @Override + public int hashCode() { + return field.hashCode() + 31 * annotationSet.hashCode(); + } + + public FieldIdItem convert() { + return field; + } + } + + public static class MethodAnnotation implements Comparable>, Convertible { + public final MethodIdItem method; + public final AnnotationSetItem annotationSet; + + public MethodAnnotation(MethodIdItem method, AnnotationSetItem annotationSet) { + this.method = method; + this.annotationSet = annotationSet; + } + + public int compareTo(Convertible other) { + return method.compareTo(other.convert()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + return compareTo((MethodAnnotation)o) == 0; + } + + @Override + public int hashCode() { + return method.hashCode() + 31 * annotationSet.hashCode(); + } + + public MethodIdItem convert() { + return method; + } + } + + public static class ParameterAnnotation implements Comparable>, + Convertible { + public final MethodIdItem method; + public final AnnotationSetRefList annotationSet; + + public ParameterAnnotation(MethodIdItem method, AnnotationSetRefList annotationSet) { + this.method = method; + this.annotationSet = annotationSet; + } + + public int compareTo(Convertible other) { + return method.compareTo(other.convert()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + return compareTo((ParameterAnnotation)o) == 0; + } + + @Override + public int hashCode() { + return method.hashCode() + 31 * annotationSet.hashCode(); + } + + public MethodIdItem convert() { + return method; + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java new file mode 100644 index 00000000..f35a414b --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java @@ -0,0 +1,162 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.EncodedValue.AnnotationEncodedSubValue; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +public class AnnotationItem extends Item { + private int hashCode = 0; + + private AnnotationVisibility visibility; + private AnnotationEncodedSubValue annotationValue; + + /** + * Creates a new uninitialized AnnotationItem + * @param dexFile The DexFile that this item belongs to + */ + protected AnnotationItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new AnnotationItem with the given values + * @param dexFile The DexFile that this item belongs to + * @param visibility The visibility of this annotation + * @param annotationValue The value of this annotation + */ + private AnnotationItem(DexFile dexFile, AnnotationVisibility visibility, + AnnotationEncodedSubValue annotationValue) { + super(dexFile); + this.visibility = visibility; + this.annotationValue = annotationValue; + } + + /** + * Returns an AnnotationItem for the given values, and that has been interned into the given + * DexFile + * @param dexFile The DexFile that this item belongs to + * @param visibility The visibility of this annotation + * @param annotationValue The value of this annotation + * @return an AnnotationItem for the given values, and that has been interned into the given + * DexFile + */ + public static AnnotationItem internAnnotationItem(DexFile dexFile, AnnotationVisibility visibility, + AnnotationEncodedSubValue annotationValue) { + AnnotationItem annotationItem = new AnnotationItem(dexFile, visibility, annotationValue); + return dexFile.AnnotationsSection.intern(annotationItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + visibility = AnnotationVisibility.fromByte(in.readByte()); + annotationValue = new AnnotationEncodedSubValue(dexFile, in); + } + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + return annotationValue.placeValue(offset + 1); + } + + /** {@inheritDoc} */ + protected void writeItem(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate("visibility: " + visibility.name()); + out.writeByte(visibility.value); + annotationValue.writeValue(out); + }else { + out.writeByte(visibility.value); + annotationValue.writeValue(out); + } + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_ANNOTATION_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "annotation_item @0x" + Integer.toHexString(getOffset()); + } + + /** {@inheritDoc} */ + public int compareTo(AnnotationItem o) { + int comp = visibility.value - o.visibility.value; + if (comp == 0) { + comp = annotationValue.compareTo(o.annotationValue); + } + return comp; + } + + /** + * @return The visibility of this annotation + */ + public AnnotationVisibility getVisibility() { + return visibility; + } + + /** + * @return The encoded annotation value of this annotation + */ + public AnnotationEncodedSubValue getEncodedAnnotation() { + return annotationValue; + } + + /** + * calculate and cache the hashcode + */ + private void calcHashCode() { + hashCode = visibility.value; + hashCode = hashCode * 31 + annotationValue.hashCode(); + } + + @Override + public int hashCode() { + //there's a small possibility that the actual hash code will be 0. If so, we'll + //just end up recalculating it each time + if (hashCode == 0) + calcHashCode(); + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + AnnotationItem other = (AnnotationItem)o; + return visibility == other.visibility && annotationValue.equals(other.annotationValue); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java new file mode 100644 index 00000000..e221ad02 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java @@ -0,0 +1,190 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +public class AnnotationSetItem extends Item { + private int hashCode = 0; + + private AnnotationItem[] annotations; + + /** + * Creates a new uninitialized AnnotationSetItem + * @param dexFile The DexFile that this item belongs to + */ + protected AnnotationSetItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new AnnotationSetItem for the given annotations + * @param dexFile The DexFile that this item belongs to + * @param annotations The annotations for this AnnotationSetItem + */ + private AnnotationSetItem(DexFile dexFile, AnnotationItem[] annotations) { + super(dexFile); + this.annotations = annotations; + } + + /** + * Returns an AnnotationSetItem for the given annotations, and that has been interned into the given + * DexFile + * @param dexFile The DexFile that this item belongs to + * @param annotations The annotations for this AnnotationSetItem + * @return an AnnotationSetItem for the given annotations + */ + public static AnnotationSetItem internAnnotationSetItem(DexFile dexFile, List annotations) { + AnnotationSetItem annotationSetItem; + if (annotations == null) { + annotationSetItem = new AnnotationSetItem(dexFile, new AnnotationItem[0]); + } else { + AnnotationItem[] annotationsArray = new AnnotationItem[annotations.size()]; + annotations.toArray(annotationsArray); + annotationSetItem = new AnnotationSetItem(dexFile, annotationsArray); + } + return dexFile.AnnotationSetsSection.intern(annotationSetItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + annotations = new AnnotationItem[in.readInt()]; + + for (int i=0; i() { + public int compare(AnnotationItem annotationItem, AnnotationItem annotationItem2) { + int annotationItemIndex = annotationItem.getEncodedAnnotation().annotationType.getIndex(); + int annotationItemIndex2 = annotationItem2.getEncodedAnnotation().annotationType.getIndex(); + if (annotationItemIndex < annotationItemIndex2) { + return -1; + } else if (annotationItemIndex == annotationItemIndex2) { + return 0; + } + return 1; + } + }); + + + if (out.annotates()) { + out.annotate(4, "size: 0x" + Integer.toHexString(annotations.length) + " (" + annotations.length + ")"); + for (AnnotationItem annotationItem: annotations) { + out.annotate(4, "annotation_off: 0x" + Integer.toHexString(annotationItem.getOffset()) + " - " + + annotationItem.getEncodedAnnotation().annotationType.getTypeDescriptor()); + } + } + out.writeInt(annotations.length); + for (AnnotationItem annotationItem: annotations) { + out.writeInt(annotationItem.getOffset()); + } + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_ANNOTATION_SET_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "annotation_set_item @0x" + Integer.toHexString(getOffset()); + } + +/** {@inheritDoc} */ + public int compareTo(AnnotationSetItem o) { + if (o == null) { + return 1; + } + + int comp = annotations.length - o.annotations.length; + if (comp == 0) { + for (int i=0; iAnnotationItem objects in this AnnotationSetItem + */ + public AnnotationItem[] getAnnotations() { + return annotations; + } + + /** + * calculate and cache the hashcode + */ + private void calcHashCode() { + hashCode = 0; + for (AnnotationItem annotationItem: annotations) { + hashCode = hashCode * 31 + annotationItem.hashCode(); + } + } + + @Override + public int hashCode() { + //there's a small possibility that the actual hash code will be 0. If so, we'll + //just end up recalculating it each time + if (hashCode == 0) + calcHashCode(); + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + AnnotationSetItem other = (AnnotationSetItem)o; + return (this.compareTo(other) == 0); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java new file mode 100644 index 00000000..e38ce31c --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java @@ -0,0 +1,170 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +import java.util.List; + +public class AnnotationSetRefList extends Item { + private int hashCode = 0; + + private AnnotationSetItem[] annotationSets; + + /** + * Creates a new uninitialized AnnotationSetRefList + * @param dexFile The DexFile that this item belongs to + */ + protected AnnotationSetRefList(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new AnnotationSetRefList for the given annotation sets + * @param dexFile The DexFile that this item belongs to + * @param annotationSets The annotationSets for this AnnotationSetRefList + */ + private AnnotationSetRefList(DexFile dexFile, AnnotationSetItem[] annotationSets) { + super(dexFile); + this.annotationSets = annotationSets; + } + + /** + * Returns an AnnotationSetRefList for the given annotation sets, and that has been interned into the + * given DexFile + * @param dexFile The DexFile that this item belongs to + * @param annotationSets The annotation sets for this AnnotationSetRefList + * @return an AnnotationSetItem for the given annotations + */ + public static AnnotationSetRefList internAnnotationSetRefList(DexFile dexFile, + List annotationSets) { + AnnotationSetItem[] annotationSetsArray = new AnnotationSetItem[annotationSets.size()]; + annotationSets.toArray(annotationSetsArray); + AnnotationSetRefList annotationSetRefList = new AnnotationSetRefList(dexFile, annotationSetsArray); + return dexFile.AnnotationSetRefListsSection.intern(annotationSetRefList); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + annotationSets = new AnnotationSetItem[in.readInt()]; + + for (int i=0; iAnnotationSetItem objects that make up this + * AnnotationSetRefList + */ + public AnnotationSetItem[] getAnnotationSets() { + return annotationSets; + } + + /** + * calculate and cache the hashcode + */ + private void calcHashCode() { + hashCode = 0; + for (AnnotationSetItem annotationSetItem: annotationSets) { + hashCode = hashCode * 31 + annotationSetItem.hashCode(); + } + } + + @Override + public int hashCode() { + //there's a small possibility that the actual hash code will be 0. If so, we'll + //just end up recalculating it each time + if (hashCode == 0) + calcHashCode(); + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + AnnotationSetRefList other = (AnnotationSetRefList)o; + return (this.compareTo(other) == 0); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java new file mode 100644 index 00000000..03ae96f0 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java @@ -0,0 +1,55 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +public enum AnnotationVisibility { + BUILD((byte)0, "build"), + RUNTIME((byte)1, "runtime"), + SYSTEM((byte)2, "system"); + + public final byte value; + public final String visibility; + private AnnotationVisibility(byte value, String visibility) { + this.value = value; + this.visibility = visibility; + } + + public static AnnotationVisibility fromByte(byte value) { + switch (value) { + case (byte)0: + return BUILD; + case (byte)1: + return RUNTIME; + case (byte)2: + return SYSTEM; + default: + throw new RuntimeException("Invalid annotation visibility value: " + value); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java new file mode 100644 index 00000000..33710710 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java @@ -0,0 +1,815 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import com.google.common.base.Preconditions; +import org.jf.dexlib.Util.*; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; + +public class ClassDataItem extends Item { + @Nullable + private EncodedField[] staticFields = null; + @Nullable + private EncodedField[] instanceFields = null; + @Nullable + private EncodedMethod[] directMethods = null; + @Nullable + private EncodedMethod[] virtualMethods = null; + + @Nullable + private ClassDefItem parent = null; + + /** + * Creates a new uninitialized ClassDataItem + * @param dexFile The DexFile that this item belongs to + */ + public ClassDataItem(final DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new ClassDataItem with the given values + * @param dexFile The DexFile that this item belongs to + * @param staticFields The static fields for this class + * @param instanceFields The instance fields for this class + * @param directMethods The direct methods for this class + * @param virtualMethods The virtual methods for this class + */ + private ClassDataItem(DexFile dexFile, @Nullable EncodedField[] staticFields, + @Nullable EncodedField[] instanceFields, @Nullable EncodedMethod[] directMethods, + @Nullable EncodedMethod[] virtualMethods) { + super(dexFile); + this.staticFields = staticFields; + this.instanceFields = instanceFields; + this.directMethods = directMethods; + this.virtualMethods = virtualMethods; + } + + /** + * Creates a new ClassDataItem with the given values + * @param dexFile The DexFile that this item belongs to + * @param staticFields The static fields for this class + * @param instanceFields The instance fields for this class + * @param directMethods The direct methods for this class + * @param virtualMethods The virtual methods for this class + * @return a new ClassDataItem with the given values + */ + public static ClassDataItem internClassDataItem(DexFile dexFile, @Nullable List staticFields, + @Nullable List instanceFields, + @Nullable List directMethods, + @Nullable List virtualMethods) { + EncodedField[] staticFieldsArray = null; + EncodedField[] instanceFieldsArray = null; + EncodedMethod[] directMethodsArray = null; + EncodedMethod[] virtualMethodsArray = null; + + if (staticFields != null && staticFields.size() > 0) { + SortedSet staticFieldsSet = new TreeSet(); + for (EncodedField staticField: staticFields) { + if (staticFieldsSet.contains(staticField)) { + System.err.println(String.format("Ignoring duplicate static field definition: %s", + staticField.field.getFieldString())); + continue; + } + staticFieldsSet.add(staticField); + } + + staticFieldsArray = new EncodedField[staticFieldsSet.size()]; + staticFieldsArray = staticFieldsSet.toArray(staticFieldsArray); + } + + if (instanceFields != null && instanceFields.size() > 0) { + SortedSet instanceFieldsSet = new TreeSet(); + for (EncodedField instanceField: instanceFields) { + if (instanceFieldsSet.contains(instanceField)) { + System.err.println(String.format("Ignoring duplicate instance field definition: %s", + instanceField.field.getFieldString())); + continue; + } + instanceFieldsSet.add(instanceField); + } + + instanceFieldsArray = new EncodedField[instanceFieldsSet.size()]; + instanceFieldsArray = instanceFieldsSet.toArray(instanceFieldsArray); + } + + TreeSet directMethodSet = new TreeSet(); + + if (directMethods != null && directMethods.size() > 0) { + for (EncodedMethod directMethod: directMethods) { + if (directMethodSet.contains(directMethod)) { + System.err.println(String.format("Ignoring duplicate direct method definition: %s", + directMethod.method.getMethodString())); + continue; + } + directMethodSet.add(directMethod); + } + + directMethodsArray = new EncodedMethod[directMethodSet.size()]; + directMethodsArray = directMethodSet.toArray(directMethodsArray); + } + + if (virtualMethods != null && virtualMethods.size() > 0) { + TreeSet virtualMethodSet = new TreeSet(); + for (EncodedMethod virtualMethod: virtualMethods) { + if (directMethodSet.contains(virtualMethod)) { + // If both a direct and virtual definition is present, dalvik's behavior seems to be undefined, + // so we can't gracefully handle this case, like we can if the duplicates are all direct or all + // virtual -- in which case, we ignore all but the first definition + throw new RuntimeException(String.format("Duplicate direct+virtual method definition: %s", + virtualMethod.method.getMethodString())); + } + if (virtualMethodSet.contains(virtualMethod)) { + System.err.println(String.format("Ignoring duplicate virtual method definition: %s", + virtualMethod.method.getMethodString())); + continue; + } + virtualMethodSet.add(virtualMethod); + } + + virtualMethodsArray = new EncodedMethod[virtualMethodSet.size()]; + virtualMethodsArray = virtualMethodSet.toArray(virtualMethodsArray); + } + + ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFieldsArray, instanceFieldsArray, + directMethodsArray, virtualMethodsArray); + return dexFile.ClassDataSection.intern(classDataItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + int staticFieldsCount = in.readUnsignedLeb128(); + int instanceFieldsCount = in.readUnsignedLeb128(); + int directMethodsCount = in.readUnsignedLeb128(); + int virtualMethodsCount = in.readUnsignedLeb128(); + + if (staticFieldsCount > 0) { + staticFields = new EncodedField[staticFieldsCount]; + EncodedField previousEncodedField = null; + for (int i=0; i 0) { + instanceFields = new EncodedField[instanceFieldsCount]; + EncodedField previousEncodedField = null; + for (int i=0; i 0) { + directMethods = new EncodedMethod[directMethodsCount]; + EncodedMethod previousEncodedMethod = null; + for (int i=0; i 0) { + virtualMethods = new EncodedMethod[virtualMethodsCount]; + EncodedMethod previousEncodedMethod = null; + for (int i=0; iClassDefItem that this ClassDataItem is associated with + * @param classDefItem the ClassDefItem that this ClassDataItem is associated with + */ + protected void setParent(ClassDefItem classDefItem) { + Preconditions.checkState(parent == null || parent.compareTo(classDefItem) == 0 || isEmpty()); + parent = classDefItem; + } + + /** + * @return the static fields for this class + */ + @Nonnull + public List getStaticFields() { + if (staticFields == null) { + return Collections.emptyList(); + } + return ReadOnlyArrayList.of(staticFields); + } + + /** + * @return the instance fields for this class + */ + @Nonnull + public List getInstanceFields() { + if (instanceFields == null) { + return Collections.emptyList(); + } + return ReadOnlyArrayList.of(instanceFields); + } + + /** + * @return the direct methods for this class + */ + @Nonnull + public List getDirectMethods() { + if (directMethods == null) { + return Collections.emptyList(); + } + return ReadOnlyArrayList.of(directMethods); + } + + /** + * @return the virtual methods for this class + */ + @Nonnull + public List getVirtualMethods() { + if (virtualMethods == null) { + return Collections.emptyList(); + } + return ReadOnlyArrayList.of(virtualMethods); + } + + /** + * @return The number of static fields in this ClassDataItem + */ + public int getStaticFieldCount() { + if (staticFields == null) { + return 0; + } + return staticFields.length; + } + + /** + * @return The number of instance fields in this ClassDataItem + */ + public int getInstanceFieldCount() { + if (instanceFields == null) { + return 0; + } + return instanceFields.length; + } + + /** + * @return The number of direct methods in this ClassDataItem + */ + public int getDirectMethodCount() { + if (directMethods == null) { + return 0; + } + return directMethods.length; + } + + /** + * @return The number of virtual methods in this ClassDataItem + */ + public int getVirtualMethodCount() { + if (virtualMethods == null) { + return 0; + } + return virtualMethods.length; + } + + /** + * @return true if this is an empty ClassDataItem + */ + public boolean isEmpty() { + return (getStaticFieldCount() + getInstanceFieldCount() + + getDirectMethodCount() + getVirtualMethodCount()) == 0; + } + + /** + * Performs a binary search for the definition of the specified direct method + * @param methodIdItem The MethodIdItem of the direct method to search for + * @return The EncodedMethod for the specified direct method, or null if not found + */ + public EncodedMethod findDirectMethodByMethodId(MethodIdItem methodIdItem) { + return findMethodByMethodIdInternal(methodIdItem.index, directMethods); + } + + /** + * Performs a binary search for the definition of the specified virtual method + * @param methodIdItem The MethodIdItem of the virtual method to search for + * @return The EncodedMethod for the specified virtual method, or null if not found + */ + public EncodedMethod findVirtualMethodByMethodId(MethodIdItem methodIdItem) { + return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods); + } + + /** + * Performs a binary search for the definition of the specified method. It can be either direct or virtual + * @param methodIdItem The MethodIdItem of the virtual method to search for + * @return The EncodedMethod for the specified virtual method, or null if not found + */ + public EncodedMethod findMethodByMethodId(MethodIdItem methodIdItem) { + EncodedMethod encodedMethod = findMethodByMethodIdInternal(methodIdItem.index, directMethods); + if (encodedMethod != null) { + return encodedMethod; + } + + return findMethodByMethodIdInternal(methodIdItem.index, virtualMethods); + } + + private static EncodedMethod findMethodByMethodIdInternal(int methodIdItemIndex, EncodedMethod[] encodedMethods) { + int min = 0; + int max = encodedMethods.length; + + while (min>1; + + EncodedMethod encodedMethod = encodedMethods[index]; + + int encodedMethodIndex = encodedMethod.method.getIndex(); + if (encodedMethodIndex == methodIdItemIndex) { + return encodedMethod; + } else if (encodedMethodIndex < methodIdItemIndex) { + if (min == index) { + break; + } + min = index; + } else { + if (max == index) { + break; + } + max = index; + } + } + + return null; + } + + public static class EncodedField implements Comparable { + /** + * The FieldIdItem that this EncodedField is associated with + */ + public final FieldIdItem field; + + /** + * The access flags for this field + */ + public final int accessFlags; + + /** + * Constructs a new EncodedField with the given values + * @param field The FieldIdItem that this EncodedField is associated with + * @param accessFlags The access flags for this field + */ + public EncodedField(FieldIdItem field, int accessFlags) { + this.field = field; + this.accessFlags = accessFlags; + } + + /** + * This is used internally to construct a new EncodedField while reading in a DexFile + * @param dexFile The DexFile that is being read in + * @param in the Input object to read the EncodedField from + * @param previousEncodedField The previous EncodedField in the list containing this + * EncodedField. + */ + private EncodedField(DexFile dexFile, Input in, @Nullable EncodedField previousEncodedField) { + int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); + field = dexFile.FieldIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex); + accessFlags = in.readUnsignedLeb128(); + } + + /** + * Writes the EncodedField to the given AnnotatedOutput object + * @param out the AnnotatedOutput object to write to + * @param previousEncodedField The previous EncodedField in the list containing this + * EncodedField. + */ + private void writeTo(AnnotatedOutput out, EncodedField previousEncodedField) { + int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); + + if (out.annotates()) { + out.annotate("field: " + field.getFieldString()); + out.writeUnsignedLeb128(field.getIndex() - previousIndex); + out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForField(accessFlags)); + out.writeUnsignedLeb128(accessFlags); + }else { + out.writeUnsignedLeb128(field.getIndex() - previousIndex); + out.writeUnsignedLeb128(accessFlags); + } + } + + /** + * Calculates the size of this EncodedField and returns the offset + * immediately following it + * @param offset the offset of this EncodedField in the DexFile + * @param previousEncodedField The previous EncodedField in the list containing this + * EncodedField. + * @return the offset immediately following this EncodedField + */ + private int place(int offset, EncodedField previousEncodedField) { + int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex(); + + offset += Leb128Utils.unsignedLeb128Size(field.getIndex() - previousIndex); + offset += Leb128Utils.unsignedLeb128Size(accessFlags); + return offset; + } + + /** + * Compares this EncodedField to another, based on the comparison of the associated + * FieldIdItem + * @param other The EncodedField to compare against + * @return a standard integer comparison value indicating the relationship + */ + public int compareTo(EncodedField other) + { + return field.compareTo(other.field); + } + + /** + * Determines if this EncodedField is equal to other, based on the equality of the associated + * FieldIdItem + * @param other The EncodedField to test for equality + * @return true if other is equal to this instance, otherwise false + */ + public boolean equals(Object other) { + if (other instanceof EncodedField) { + return compareTo((EncodedField)other) == 0; + } + return false; + } + + /** + * @return true if this is a static field + */ + public boolean isStatic() { + return (accessFlags & AccessFlags.STATIC.getValue()) != 0; + } + } + + public static class EncodedMethod implements Comparable { + /** + * The MethodIdItem that this EncodedMethod is associated with + */ + public final MethodIdItem method; + + /** + * The access flags for this method + */ + public final int accessFlags; + + /** + * The CodeItem containing the code for this method, or null if there is no code for this method + * (i.e. an abstract method) + */ + public final CodeItem codeItem; + + /** + * Constructs a new EncodedMethod with the given values + * @param method The MethodIdItem that this EncodedMethod is associated with + * @param accessFlags The access flags for this method + * @param codeItem The CodeItem containing the code for this method, or null if there is no code + * for this method (i.e. an abstract method) + */ + public EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem) { + this.method = method; + this.accessFlags = accessFlags; + this.codeItem = codeItem; + if (codeItem != null) { + codeItem.setParent(this); + } + } + + /** + * This is used internally to construct a new EncodedMethod while reading in a DexFile + * @param dexFile The DexFile that is being read in + * @param readContext a ReadContext object to hold information that is only needed while reading + * in a file + * @param in the Input object to read the EncodedMethod from + * @param previousEncodedMethod The previous EncodedMethod in the list containing this + * EncodedMethod. + */ + public EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod) { + int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); + method = dexFile.MethodIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex); + accessFlags = in.readUnsignedLeb128(); + if (dexFile.skipInstructions()) { + in.readUnsignedLeb128(); + codeItem = null; + } else { + codeItem = (CodeItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CODE_ITEM, + in.readUnsignedLeb128()); + } + if (codeItem != null) { + codeItem.setParent(this); + } + } + + /** + * Writes the EncodedMethod to the given AnnotatedOutput object + * @param out the AnnotatedOutput object to write to + * @param previousEncodedMethod The previous EncodedMethod in the list containing this + * EncodedMethod. + */ + private void writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod) { + int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); + + if (out.annotates()) { + out.annotate("method: " + method.getMethodString()); + out.writeUnsignedLeb128(method.getIndex() - previousIndex); + out.annotate("access_flags: " + AccessFlags.formatAccessFlagsForMethod(accessFlags)); + out.writeUnsignedLeb128(accessFlags); + if (codeItem != null) { + out.annotate("code_off: 0x" + Integer.toHexString(codeItem.getOffset())); + out.writeUnsignedLeb128(codeItem.getOffset()); + } else { + out.annotate("code_off: 0x0"); + out.writeUnsignedLeb128(0); + } + }else { + out.writeUnsignedLeb128(method.getIndex() - previousIndex); + out.writeUnsignedLeb128(accessFlags); + out.writeUnsignedLeb128(codeItem==null?0:codeItem.getOffset()); + } + } + + /** + * Calculates the size of this EncodedMethod and returns the offset + * immediately following it + * @param offset the offset of this EncodedMethod in the DexFile + * @param previousEncodedMethod The previous EncodedMethod in the list containing this + * EncodedMethod. + * @return the offset immediately following this EncodedField + */ + private int place(int offset, EncodedMethod previousEncodedMethod) { + int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex(); + + offset += Leb128Utils.unsignedLeb128Size(method.getIndex() - previousIndex); + offset += Leb128Utils.unsignedLeb128Size(accessFlags); + offset += codeItem==null?1:Leb128Utils.unsignedLeb128Size(codeItem.getOffset()); + return offset; + } + + /** + * Compares this EncodedMethod to another, based on the comparison of the associated + * MethodIdItem + * @param other The EncodedMethod to compare against + * @return a standard integer comparison value indicating the relationship + */ + public int compareTo(EncodedMethod other) { + return method.compareTo(other.method); + } + + /** + * Determines if this EncodedMethod is equal to other, based on the equality of the associated + * MethodIdItem + * @param other The EncodedMethod to test for equality + * @return true if other is equal to this instance, otherwise false + */ + public boolean equals(Object other) { + if (other instanceof EncodedMethod) { + return compareTo((EncodedMethod)other) == 0; + } + return false; + } + + /** + * @return true if this is a direct method + */ + public boolean isDirect() { + return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() | + AccessFlags.CONSTRUCTOR.getValue())) != 0); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java new file mode 100644 index 00000000..3737adbc --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java @@ -0,0 +1,389 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import com.google.common.base.Preconditions; +import org.jf.dexlib.EncodedValue.ArrayEncodedSubValue; +import org.jf.dexlib.EncodedValue.EncodedValue; +import org.jf.dexlib.Util.AccessFlags; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.TypeUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; + +public class ClassDefItem extends Item { + private TypeIdItem classType; + private int accessFlags; + private @Nullable TypeIdItem superType; + private @Nullable TypeListItem implementedInterfaces; + private @Nullable StringIdItem sourceFile; + private @Nullable AnnotationDirectoryItem annotations; + private @Nullable ClassDataItem classData; + private @Nullable EncodedArrayItem staticFieldInitializers; + + /** + * Creates a new uninitialized ClassDefItem + * @param dexFile The DexFile that this item belongs to + */ + protected ClassDefItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new ClassDefItem with the given values + * @param dexFile The DexFile that this item belongs to + * @param classType The type of this class + * @param accessFlags The access flags of this class + * @param superType The superclass of this class, or null if none (only valid for java.lang.Object) + * @param implementedInterfaces A list of the interfaces that this class implements, or null if none + * @param sourceFile The main source file that this class is defined in, or null if not available + * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none + * @param classData The ClassDataItem containing the method and field definitions for this class + * @param staticFieldInitializers The initial values for this class's static fields, or null if none. The initial + * values should be in the same order as the static fields in the ClassDataItem. It can contain + * fewer items than static fields, in which case the remaining static fields will be initialized with a default + * value of null/0. The initial value for any fields that don't specifically have a value can be either the + * type-appropriate null/0 encoded value, or null. + */ + private ClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, @Nullable TypeIdItem superType, + @Nullable TypeListItem implementedInterfaces, @Nullable StringIdItem sourceFile, + @Nullable AnnotationDirectoryItem annotations, @Nullable ClassDataItem classData, + @Nullable EncodedArrayItem staticFieldInitializers) { + super(dexFile); + assert classType != null; + this.classType = classType; + this.accessFlags = accessFlags; + this.superType = superType; + this.implementedInterfaces = implementedInterfaces; + this.sourceFile = sourceFile; + this.annotations = annotations; + this.classData = classData; + this.staticFieldInitializers = staticFieldInitializers; + + if (classData != null) { + classData.setParent(this); + } + if (annotations != null) { + annotations.setParent(this); + } + } + + /** + * Returns a ClassDefItem for the given values, and that has been interned into the given + * DexFile + * @param dexFile The DexFile that this item belongs to + * @param classType The type of this class + * @param accessFlags The access flags of this class + * @param superType The superclass of this class, or null if none (only valid for java.lang.Object) + * @param implementedInterfaces A list of the interfaces that this class implements, or null if none + * @param sourceFile The main source file that this class is defined in, or null if not available + * @param annotations The annotations for this class and its fields, methods and method parameters, or null if none + * @param classData The ClassDataItem containing the method and field definitions for this class + * @param staticFieldInitializers The initial values for this class's static fields, or null if none. If it is not + * null, it must contain the same number of items as the number of static fields in this class. The value in the + * StaticFieldInitializer for any field that doesn't have an explicit initial value can either be null + * or be the type-appropriate null/0 value. + * @return a ClassDefItem for the given values, and that has been interned into the given + * DexFile + */ + public static ClassDefItem internClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, + @Nullable TypeIdItem superType, @Nullable TypeListItem implementedInterfaces, + @Nullable StringIdItem sourceFile, @Nullable AnnotationDirectoryItem annotations, + @Nullable ClassDataItem classData, + @Nullable List staticFieldInitializers) { + EncodedArrayItem encodedArrayItem = null; + if(!dexFile.getInplace() && staticFieldInitializers != null && staticFieldInitializers.size() > 0) { + assert classData != null; + assert staticFieldInitializers.size() == classData.getStaticFieldCount(); + encodedArrayItem = makeStaticFieldInitializersItem(dexFile, staticFieldInitializers); + } + + ClassDefItem classDefItem = new ClassDefItem(dexFile, classType, accessFlags, superType, implementedInterfaces, + sourceFile, annotations, classData, encodedArrayItem); + return dexFile.ClassDefsSection.intern(classDefItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + classType = dexFile.TypeIdsSection.getItemByIndex(in.readInt()); + accessFlags = in.readInt(); + superType = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readInt()); + implementedInterfaces = (TypeListItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST, + in.readInt()); + sourceFile = dexFile.StringIdsSection.getOptionalItemByIndex(in.readInt()); + annotations = (AnnotationDirectoryItem)readContext.getOptionalOffsettedItemByOffset( + ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, in.readInt()); + classData = (ClassDataItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_CLASS_DATA_ITEM, in.readInt()); + staticFieldInitializers = (EncodedArrayItem)readContext.getOptionalOffsettedItemByOffset( + ItemType.TYPE_ENCODED_ARRAY_ITEM, in.readInt()); + + if (classData != null) { + classData.setParent(this); + } + if (annotations != null) { + annotations.setParent(this); + } + } + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + return offset + 32; + } + + /** {@inheritDoc} */ + protected void writeItem(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate(4, "class_type: " + classType.getTypeDescriptor()); + out.annotate(4, "access_flags: " + AccessFlags.formatAccessFlagsForClass(accessFlags)); + out.annotate(4, "superclass_type: " + (superType==null?"":superType.getTypeDescriptor())); + out.annotate(4, "interfaces: " + + (implementedInterfaces==null?"":implementedInterfaces.getTypeListString(" "))); + out.annotate(4, "source_file: " + (sourceFile==null?"":sourceFile.getStringValue())); + out.annotate(4, "annotations_off: " + + (annotations==null?"":"0x"+Integer.toHexString(annotations.getOffset()))); + out.annotate(4, "class_data_off:" + + (classData==null?"":"0x"+Integer.toHexString(classData.getOffset()))); + out.annotate(4, "static_values_off: " + + (staticFieldInitializers==null?"":"0x"+Integer.toHexString(staticFieldInitializers.getOffset()))); + } + out.writeInt(classType.getIndex()); + out.writeInt(accessFlags); + out.writeInt(superType==null?-1:superType.getIndex()); + out.writeInt(implementedInterfaces==null?0:implementedInterfaces.getOffset()); + out.writeInt(sourceFile==null?-1:sourceFile.getIndex()); + out.writeInt(annotations==null?0:annotations.getOffset()); + out.writeInt(classData==null?0:classData.getOffset()); + out.writeInt(staticFieldInitializers==null?0:staticFieldInitializers.getOffset()); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_CLASS_DEF_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "class_def_item: " + classType.getTypeDescriptor(); + } + + /** {@inheritDoc} */ + public int compareTo(ClassDefItem o) { + //The actual sorting for this class is done during the placement phase, in ClassDefPlacer. + //This method is just used for sorting the associated ClassDataItem items after the ClassDefItems have been + //placed, so we can just do the comparison based on the offsets + return this.getOffset() - o.getOffset(); + } + + public TypeIdItem getClassType() { + return classType; + } + + public int getAccessFlags() { + return accessFlags; + } + + @Nullable + public TypeIdItem getSuperclass() { + return superType; + } + + @Nullable + public TypeListItem getInterfaces() { + return implementedInterfaces; + } + + @Nullable + public StringIdItem getSourceFile() { + return sourceFile; + } + + @Nullable + public AnnotationDirectoryItem getAnnotations() { + return annotations; + } + + @Nullable + public ClassDataItem getClassData() { + return classData; + } + + @Nullable + public EncodedArrayItem getStaticFieldInitializers() { + return staticFieldInitializers; + } + + public static int placeClassDefItems(IndexedSection section, int offset) { + ClassDefPlacer cdp = new ClassDefPlacer(section); + return cdp.placeSection(offset); + } + + /** + * This class places the items within a ClassDefItem section, such that superclasses and interfaces are + * placed before sub/implementing classes + */ + private static class ClassDefPlacer { + private final IndexedSection section; + private final HashMap unplacedClassDefsByType = + new HashMap(); + + private int currentIndex = 0; + private int currentOffset; + + public ClassDefPlacer(IndexedSection section) { + this.section = section; + + for (ClassDefItem classDefItem: section.items) { + TypeIdItem typeIdItem = classDefItem.classType; + unplacedClassDefsByType.put(typeIdItem, classDefItem); + } + } + + public int placeSection(int offset) { + currentOffset = offset; + + if (section.DexFile.getSortAllItems()) { + //presort the list, to guarantee a unique ordering + Collections.sort(section.items, new Comparator() { + public int compare(ClassDefItem a, ClassDefItem b) { + return a.getClassType().compareTo(b.getClassType()); + } + }); + } + + //we need to initialize the offset for all the classes to -1, so we can tell which ones + //have been placed + for (ClassDefItem classDefItem: section.items) { + classDefItem.offset = -1; + } + + for (ClassDefItem classDefItem: section.items) { + placeClass(classDefItem); + } + + for (ClassDefItem classDefItem: unplacedClassDefsByType.values()) { + section.items.set(classDefItem.getIndex(), classDefItem); + } + + return currentOffset; + } + + private void placeClass(ClassDefItem classDefItem) { + if (!classDefItem.isPlaced()) { + TypeIdItem superType = classDefItem.superType; + ClassDefItem superClassDefItem = unplacedClassDefsByType.get(superType); + + if (superClassDefItem != null) { + placeClass(superClassDefItem); + } + + TypeListItem interfaces = classDefItem.implementedInterfaces; + + if (interfaces != null) { + for (TypeIdItem interfaceType: interfaces.getTypes()) { + ClassDefItem interfaceClass = unplacedClassDefsByType.get(interfaceType); + if (interfaceClass != null) { + placeClass(interfaceClass); + } + } + } + + currentOffset = classDefItem.placeAt(currentOffset, currentIndex++); + unplacedClassDefsByType.remove(classDefItem.classType); + } + } + } + + public static class StaticFieldInitializer implements Comparable { + public final EncodedValue value; + public final ClassDataItem.EncodedField field; + public StaticFieldInitializer(EncodedValue value, ClassDataItem.EncodedField field) { + this.value = value; + this.field = field; + } + + public int compareTo(StaticFieldInitializer other) { + return field.compareTo(other.field); + } + } + + + /** + * A helper method to sort the static field initializers and populate the default values as needed + * @param dexFile the DexFile + * @param staticFieldInitializers the initial values + * @return an interned EncodedArrayItem containing the static field initializers + */ + private static EncodedArrayItem makeStaticFieldInitializersItem(DexFile dexFile, + @Nonnull List staticFieldInitializers) { + if (staticFieldInitializers == null || staticFieldInitializers.size() == 0) { + return null; + } + + int len = staticFieldInitializers.size(); + + // make a copy before sorting. we don't want to modify the list passed to us + staticFieldInitializers = new ArrayList(staticFieldInitializers); + Collections.sort(staticFieldInitializers); + + int lastIndex = -1; + for (int i=len-1; i>=0; i--) { + StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i); + + if (staticFieldInitializer.value != null && + (staticFieldInitializer.value.compareTo(TypeUtils.makeDefaultValueForType( + staticFieldInitializer.field.field.getFieldType())) != 0)) { + lastIndex = i; + break; + } + } + + //we don't have any non-null/non-default values, so we don't need to create an EncodedArrayItem + if (lastIndex == -1) { + return null; + } + + EncodedValue[] values = new EncodedValue[lastIndex+1]; + + for (int i=0; i<=lastIndex; i++) { + StaticFieldInitializer staticFieldInitializer = staticFieldInitializers.get(i); + EncodedValue encodedValue = staticFieldInitializer.value; + if (encodedValue == null) { + encodedValue = TypeUtils.makeDefaultValueForType(staticFieldInitializer.field.field.getFieldType()); + } + + values[i] = encodedValue; + } + + ArrayEncodedSubValue encodedArrayValue = new ArrayEncodedSubValue(values); + return EncodedArrayItem.internEncodedArrayItem(dexFile, encodedArrayValue); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java new file mode 100644 index 00000000..d73abd5a --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java @@ -0,0 +1,343 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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 org.jf.dexlib.Code.*; +import org.jf.dexlib.Item; +import org.jf.dexlib.ItemType; +import org.jf.dexlib.MethodIdItem; +import org.jf.dexlib.Util.ExceptionWithContext; + +import java.util.*; + +public class AnalyzedInstruction implements Comparable { + /** + * The actual instruction + */ + protected Instruction instruction; + + /** + * The index of the instruction, where the first instruction in the method is at index 0, and so on + */ + protected final int instructionIndex; + + /** + * Instructions that can pass on execution to this one during normal execution + */ + protected final TreeSet predecessors = new TreeSet(); + + /** + * Instructions that can execution could pass on to next during normal execution + */ + protected final LinkedList successors = new LinkedList(); + + /** + * 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; + + /** + * 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; + + /** + * An analyzed instruction's "deadness" is set during analysis (i.e. MethodAnalyzer.analyzer()). A dead instruction + * is one that the analyzer never reaches. This occurs either with natural "dead code" - code that simply has no + * code path that can ever reach it, or code that follows an odexed instruction that can't be deodexed. + */ + 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 getPredecessors() { + return Collections.unmodifiableSortedSet(predecessors); + } + + protected boolean addPredecessor(AnalyzedInstruction predecessor) { + return predecessors.add(predecessor); + } + + protected void addSuccessor(AnalyzedInstruction successor) { + successors.add(successor); + } + + protected void setDeodexedInstruction(Instruction instruction) { + assert originalInstruction.opcode.odexOnly(); + this.instruction = instruction; + } + + protected void restoreOdexedInstruction() { + assert originalInstruction.opcode.odexOnly(); + instruction = originalInstruction; + } + + public int getSuccessorCount() { + return successors.size(); + } + + public List getSuccesors() { + 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 + * beginning instruction. If the first instruction can throw an exception, and is covered by a try block, then + * 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() { + //if this instruction has no predecessors, it is either the fake "StartOfMethod" instruction or it is an + //unreachable instruction. + if (predecessors.size() == 0) { + return false; + } + + if (predecessors.first().instructionIndex == -1) { + return true; + } + return false; + } + + /* + * 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 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 mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions) { + assert registerNumber >= 0 && registerNumber < postRegisterMap.length; + assert registerType != null; + + RegisterType oldRegisterType = preRegisterMap[registerNumber]; + RegisterType mergedRegisterType = oldRegisterType.merge(registerType); + + if (mergedRegisterType == oldRegisterType) { + return false; + } + + preRegisterMap[registerNumber] = mergedRegisterType; + verifiedInstructions.clear(instructionIndex); + + if (!setsRegister(registerNumber)) { + postRegisterMap[registerNumber] = mergedRegisterType; + return true; + } + + return false; + } + + /** + * 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 the register number + * @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]; + assert predecessorRegisterType != null; + mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType); + } + 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.canInitializeReference()) { + return false; + } + + //TODO: check access flags instead of name? + + InstructionWithReference instruction = (InstructionWithReference)this.instruction; + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM; + MethodIdItem method = (MethodIdItem)item; + + if (!method.getMethodName().getStringValue().equals("")) { + return false; + } + + return true; + } + + public boolean setsRegister() { + return instruction.opcode.setsRegister(); + } + + public boolean setsWideRegister() { + return instruction.opcode.setsWideRegister(); + } + + 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 method is called. So even though invoke + //instructions don't normally change any registers, calling an method will change the type of its + //object register. If the uninitialized reference has been copied to other registers, they will be initialized + //as well, so we need to check for that too + if (isInvokeInit()) { + int destinationRegister; + if (instruction instanceof FiveRegisterInstruction) { + destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD(); + } else { + assert instruction instanceof RegisterRangeInstruction; + RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction; + assert rangeInstruction.getRegCount() > 0; + destinationRegister = rangeInstruction.getStartRegister(); + } + + if (registerNumber == destinationRegister) { + return true; + } + 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 (getPreInstructionRegisterType(registerNumber) == preInstructionDestRegisterType) { + return true; + } + return false; + } + + if (!setsRegister()) { + return false; + } + int destinationRegister = getDestinationRegister(); + + if (registerNumber == destinationRegister) { + return true; + } + if (setsWideRegister() && registerNumber == (destinationRegister + 1)) { + return true; + } + return false; + } + + public int getDestinationRegister() { + if (!this.instruction.opcode.setsRegister()) { + throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " + + "store a value"); + } + return ((SingleRegisterInstruction)instruction).getRegisterA(); + } + + public int getRegisterCount() { + return postRegisterMap.length; + } + + public RegisterType getPostInstructionRegisterType(int registerNumber) { + return postRegisterMap[registerNumber]; + } + + public RegisterType getPreInstructionRegisterType(int registerNumber) { + return preRegisterMap[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 1; + } + } +} + diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java new file mode 100644 index 00000000..ac1da808 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java @@ -0,0 +1,1282 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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 org.jf.dexlib.*; +import org.jf.dexlib.Util.AccessFlags; +import org.jf.dexlib.Util.ExceptionWithContext; +import org.jf.dexlib.Util.SparseArray; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.File; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +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; + + private final HashMap classDefs; + protected ClassDef javaLangObjectClassDef; //cached ClassDef for Ljava/lang/Object; + + // Contains the classes that we haven't loaded yet + private HashMap unloadedClasses; + + private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$"); + + /** + * Initialize the class path using the dependencies from an odex file + * @param classPathDirs The directories to search for boot class path files + * @param extraBootClassPathEntries any extra entries that should be added after the entries that are read + * from the odex file + * @param dexFilePath The path of the dex file (used for error reporting purposes only) + * @param dexFile The DexFile to load - it must represents an odex file + */ + public static void InitializeClassPathFromOdex(String[] classPathDirs, String[] extraBootClassPathEntries, + String dexFilePath, DexFile dexFile) { + if (!dexFile.isOdex()) { + throw new ExceptionWithContext("Cannot use InitialiazeClassPathFromOdex with a non-odex DexFile"); + } + + if (theClassPath != null) { + throw new ExceptionWithContext("Cannot initialize ClassPath multiple times"); + } + + OdexDependencies odexDependencies = dexFile.getOdexDependencies(); + + String[] bootClassPath = new String[odexDependencies.getDependencyCount()]; + for (int i=0; i(); + } + + private void initClassPath(String[] classPathDirs, String[] bootClassPath, String[] extraBootClassPathEntries, + String dexFilePath, DexFile dexFile) { + unloadedClasses = new LinkedHashMap(); + + if (bootClassPath != null) { + for (String bootClassPathEntry: bootClassPath) { + loadBootClassPath(classPathDirs, bootClassPathEntry); + } + } + + if (extraBootClassPathEntries != null) { + for (String bootClassPathEntry: extraBootClassPathEntries) { + loadBootClassPath(classPathDirs, bootClassPathEntry); + } + } + + if (dexFile != null) { + loadDexFile(dexFilePath, dexFile); + } + + javaLangObjectClassDef = getClassDef("Ljava/lang/Object;", false); + + for (String primitiveType: new String[]{"Z", "B", "S", "C", "I", "J", "F", "D"}) { + ClassDef classDef = new PrimitiveClassDef(primitiveType); + classDefs.put(primitiveType, classDef); + } + } + + private void loadBootClassPath(String[] classPathDirs, String bootClassPathEntry) { + for (String classPathDir: classPathDirs) { + File file = null; + DexFile dexFile = null; + + int extIndex = bootClassPathEntry.lastIndexOf("."); + + String baseEntry; + if (extIndex == -1) { + baseEntry = bootClassPathEntry; + } else { + baseEntry = bootClassPathEntry.substring(0, extIndex); + } + + for (String ext: new String[]{"", ".odex", ".jar", ".apk", ".zip"}) { + if (ext.length() == 0) { + file = new File(classPathDir, bootClassPathEntry); + } else { + file = new File(classPathDir, baseEntry + ext); + } + + if (file.exists()) { + if (!file.canRead()) { + System.err.println(String.format("warning: cannot open %s for reading. Will continue " + + "looking.", file.getPath())); + continue; + } + + try { + dexFile = new DexFile(file, false, true); + } catch (DexFile.NoClassesDexException ex) { + continue; + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, "Error while reading boot class path entry \"" + + bootClassPathEntry + "\"."); + } + } + } + if (dexFile == null) { + continue; + } + + try { + loadDexFile(file.getPath(), dexFile); + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, + String.format("Error while loading boot classpath entry %s", bootClassPathEntry)); + } + return; + } + throw new ExceptionWithContext(String.format("Cannot locate boot class path file %s", bootClassPathEntry)); + } + + private void loadDexFile(String dexFilePath, DexFile dexFile) { + for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) { + try { + UnresolvedClassInfo unresolvedClassInfo = new UnresolvedClassInfo(dexFilePath, classDefItem); + + if (!unloadedClasses.containsKey(unresolvedClassInfo.classType)) { + unloadedClasses.put(unresolvedClassInfo.classType, unresolvedClassInfo); + } + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s", + classDefItem.getClassType().getTypeDescriptor())); + } + } + } + + /** + * This method loads the given class (and any dependent classes, as needed), removing them from unloadedClasses + * @param classType the class to load + * @return the newly loaded ClassDef object for the given class, or null if the class cannot be found + */ + @Nullable + private static ClassDef loadClassDef(String classType) { + ClassDef classDef = null; + + UnresolvedClassInfo classInfo = theClassPath.unloadedClasses.get(classType); + if (classInfo == null) { + return null; + } + + try { + classDef = new ClassDef(classInfo); + theClassPath.classDefs.put(classDef.classType, classDef); + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s from file %s", + classInfo.classType, classInfo.dexFilePath)); + } + theClassPath.unloadedClasses.remove(classType); + + return classDef; + } + + @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 + if (classType.charAt(0) == '[') { + return theClassPath.createArrayClassDef(classType); + } else { + try { + classDef = loadClassDef(classType); + if (classDef == null) { + throw new ExceptionWithContext( + String.format("Could not find definition for class %s", classType)); + } + } catch (Exception ex) { + RuntimeException exWithContext = ExceptionWithContext.withContext(ex, + String.format("Error while loading ClassPath class %s", classType)); + if (createUnresolvedClassDef) { + //TODO: add warning message + return theClassPath.createUnresolvedClassDef(classType); + } else { + throw exWithContext; + } + } + } + } + return classDef; + } + + public static ClassDef getClassDef(String classType) { + return getClassDef(classType, true); + } + + public static ClassDef getClassDef(TypeIdItem classType) { + return getClassDef(classType.getTypeDescriptor()); + } + + public static ClassDef getClassDef(TypeIdItem classType, boolean creatUnresolvedClassDef) { + return getClassDef(classType.getTypeDescriptor(), creatUnresolvedClassDef); + } + + //256 [ characters + private static final String arrayPrefix = "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["; + private static ClassDef getArrayClassDefByElementClassAndDimension(ClassDef classDef, int arrayDimension) { + return getClassDef(arrayPrefix.substring(256 - arrayDimension) + classDef.classType); + } + + private static ClassDef unresolvedObjectClassDef = null; + public static ClassDef getUnresolvedObjectClassDef() { + if (unresolvedObjectClassDef == null) { + unresolvedObjectClassDef = new UnresolvedClassDef("Ljava/lang/Object;"); + } + return unresolvedObjectClassDef; + } + + private ClassDef createUnresolvedClassDef(String classType) { + assert classType.charAt(0) == 'L'; + + UnresolvedClassDef unresolvedClassDef = new UnresolvedClassDef(classType); + classDefs.put(classType, unresolvedClassDef); + return unresolvedClassDef; + } + + private ClassDef createArrayClassDef(String arrayClassName) { + assert arrayClassName != null; + assert arrayClassName.charAt(0) == '['; + + ArrayClassDef arrayClassDef = new ArrayClassDef(arrayClassName); + if (arrayClassDef.elementClass == null) { + return null; + } + + classDefs.put(arrayClassName, arrayClassDef); + return arrayClassDef; + } + + public static ClassDef getCommonSuperclass(ClassDef class1, ClassDef class2) { + if (class1 == class2) { + return class1; + } + + if (class1 == null) { + return class2; + } + + if (class2 == null) { + return class1; + } + + //TODO: do we want to handle primitive types here? I don't think so.. (if not, add assert) + + if (class2.isInterface) { + if (class1.implementsInterface(class2)) { + return class2; + } + return theClassPath.javaLangObjectClassDef; + } + + if (class1.isInterface) { + if (class2.implementsInterface(class1)) { + return class1; + } + return theClassPath.javaLangObjectClassDef; + } + + if (class1 instanceof ArrayClassDef && class2 instanceof ArrayClassDef) { + return getCommonArraySuperclass((ArrayClassDef)class1, (ArrayClassDef)class2); + } + + //we've got two non-array reference types. Find the class depth of each, and then move up the longer one + //so that both classes are at the same class depth, and then move each class up until they match + + //we don't strictly need to keep track of the class depth separately, but it's probably slightly faster + //to do so, rather than calling getClassDepth() many times + int class1Depth = class1.getClassDepth(); + int class2Depth = class2.getClassDepth(); + + while (class1Depth > class2Depth) { + class1 = class1.superclass; + class1Depth--; + } + + while (class2Depth > class1Depth) { + class2 = class2.superclass; + class2Depth--; + } + + while (class1Depth > 0) { + if (class1 == class2) { + return class1; + } + class1 = class1.superclass; + class1Depth--; + class2 = class2.superclass; + class2Depth--; + } + + return class1; + } + + private static ClassDef getCommonArraySuperclass(ArrayClassDef class1, ArrayClassDef class2) { + assert class1 != class2; + + //If one of the arrays is a primitive array, then the only option is to return java.lang.Object + //TODO: might it be possible to merge something like int[] and short[] into int[]? (I don't think so..) + if (class1.elementClass instanceof PrimitiveClassDef || class2.elementClass instanceof PrimitiveClassDef) { + return theClassPath.javaLangObjectClassDef; + } + + //if the two arrays have the same number of dimensions, then we should return an array class with the + //same number of dimensions, for the common superclass of the 2 element classes + if (class1.arrayDimensions == class2.arrayDimensions) { + ClassDef commonElementClass = getCommonSuperclass(class1.elementClass, class2.elementClass); + return getArrayClassDefByElementClassAndDimension(commonElementClass, class1.arrayDimensions); + } + + //something like String[][][] and String[][] should be merged to Object[][] + //this also holds when the element classes aren't the same (but are both reference types) + int dimensions = Math.min(class1.arrayDimensions, class2.arrayDimensions); + return getArrayClassDefByElementClassAndDimension(theClassPath.javaLangObjectClassDef, dimensions); + } + + public static class ArrayClassDef extends ClassDef { + private final ClassDef elementClass; + private final int arrayDimensions; + + protected ArrayClassDef(String arrayClassType) { + super(arrayClassType, ClassDef.ArrayClassDef); + assert arrayClassType.charAt(0) == '['; + + int i=0; + while (arrayClassType.charAt(i) == '[') i++; + + String elementClassType = arrayClassType.substring(i); + + if (i>256) { + throw new ExceptionWithContext("Error while creating array class for element type " + elementClassType + + " with " + i + " dimensions. The maximum number of dimensions is 256"); + } + + try { + elementClass = ClassPath.getClassDef(arrayClassType.substring(i)); + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, "Error while creating array class " + arrayClassType); + } + arrayDimensions = i; + } + + /** + * Returns the "base" element class of the array. + * + * For example, for a multi-dimensional array of strings ([[Ljava/lang/String;), this method would return + * Ljava/lang/String; + * @return the "base" element class of the array + */ + public ClassDef getBaseElementClass() { + return elementClass; + } + + /** + * Returns the "immediate" element class of the array. + * + * For example, for a multi-dimensional array of stings with 2 dimensions ([[Ljava/lang/String;), this method + * would return [Ljava/lang/String; + * @return the immediate element class of the array + */ + public ClassDef getImmediateElementClass() { + if (arrayDimensions == 1) { + return elementClass; + } + return getArrayClassDefByElementClassAndDimension(elementClass, arrayDimensions - 1); + } + + public int getArrayDimensions() { + return arrayDimensions; + } + + @Override + public boolean extendsClass(ClassDef superclassDef) { + if (!(superclassDef instanceof ArrayClassDef)) { + if (superclassDef == ClassPath.theClassPath.javaLangObjectClassDef) { + return true; + } else if (superclassDef.isInterface) { + return this.implementsInterface(superclassDef); + } + return false; + } + + ArrayClassDef arraySuperclassDef = (ArrayClassDef)superclassDef; + if (this.arrayDimensions == arraySuperclassDef.arrayDimensions) { + ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass(); + + if (baseElementClass.isInterface) { + return true; + } + + return baseElementClass.extendsClass(arraySuperclassDef.getBaseElementClass()); + } else if (this.arrayDimensions > arraySuperclassDef.arrayDimensions) { + ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass(); + if (baseElementClass.isInterface) { + return true; + } + + if (baseElementClass == ClassPath.theClassPath.javaLangObjectClassDef) { + return true; + } + return false; + } + return false; + } + } + + public static class PrimitiveClassDef extends ClassDef { + protected PrimitiveClassDef(String primitiveClassType) { + super(primitiveClassType, ClassDef.PrimitiveClassDef); + assert primitiveClassType.charAt(0) != 'L' && primitiveClassType.charAt(0) != '['; + } + } + + public static class UnresolvedClassDef extends ClassDef { + protected UnresolvedClassDef(String unresolvedClassDef) { + super(unresolvedClassDef, ClassDef.UnresolvedClassDef); + assert unresolvedClassDef.charAt(0) == 'L'; + } + + protected ValidationException unresolvedValidationException() { + return new ValidationException(String.format("class %s cannot be resolved.", this.getClassType())); + } + + public ClassDef getSuperclass() { + throw unresolvedValidationException(); + } + + public int getClassDepth() { + throw unresolvedValidationException(); + } + + public boolean isInterface() { + throw unresolvedValidationException(); + } + + public boolean extendsClass(ClassDef superclassDef) { + if (superclassDef != theClassPath.javaLangObjectClassDef && superclassDef != this) { + throw unresolvedValidationException(); + } + return true; + } + + public boolean implementsInterface(ClassDef interfaceDef) { + throw unresolvedValidationException(); + } + + public boolean hasVirtualMethod(String method) { + if (!super.hasVirtualMethod(method)) { + throw unresolvedValidationException(); + } + return true; + } + } + + public static class FieldDef { + public final String definingClass; + public final String name; + public final String type; + + public FieldDef(String definingClass, String name, String type) { + this.definingClass = definingClass; + this.name = name; + this.type = type; + } + } + + public static class ClassDef implements Comparable { + private final String classType; + private final ClassDef superclass; + /** + * This is a list of all of the interfaces that a class implements, either directly or indirectly. It includes + * all interfaces implemented by the superclass, and all super-interfaces of any implemented interface. The + * intention is to make it easier to determine whether the class implements a given interface or not. + */ + private final TreeSet implementedInterfaces; + + private final boolean isInterface; + + private final int classDepth; + + private final String[] vtable; + + //this maps a method name of the form method(III)Ljava/lang/String; to an integer + //If the value is non-negative, it is a vtable index + //If it is -1, it is a non-static direct method, + //If it is -2, it is a static method + private final HashMap methodLookup; + + private final SparseArray instanceFields; + + public final static int ArrayClassDef = 0; + public final static int PrimitiveClassDef = 1; + public final static int UnresolvedClassDef = 2; + + private final static int DirectMethod = -1; + private final static int StaticMethod = -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 interfaceTable; + + /** + * This constructor is used for the ArrayClassDef, PrimitiveClassDef and UnresolvedClassDef subclasses + * @param classType the class type + * @param classFlavor one of ArrayClassDef, PrimitiveClassDef or UnresolvedClassDef + */ + protected ClassDef(String classType, int classFlavor) { + if (classFlavor == ArrayClassDef) { + assert classType.charAt(0) == '['; + this.classType = classType; + this.superclass = ClassPath.theClassPath.javaLangObjectClassDef; + implementedInterfaces = new TreeSet(); + implementedInterfaces.add(ClassPath.getClassDef("Ljava/lang/Cloneable;")); + implementedInterfaces.add(ClassPath.getClassDef("Ljava/io/Serializable;")); + isInterface = false; + + vtable = superclass.vtable; + methodLookup = superclass.methodLookup; + + instanceFields = superclass.instanceFields; + 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'; + + this.classType = classType; + this.superclass = null; + implementedInterfaces = null; + isInterface = false; + vtable = null; + methodLookup = null; + instanceFields = 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; + this.superclass = ClassPath.getClassDef("Ljava/lang/Object;"); + implementedInterfaces = new TreeSet(); + isInterface = false; + + vtable = superclass.vtable; + methodLookup = superclass.methodLookup; + + instanceFields = superclass.instanceFields; + classDepth = 1; //1 off from java.lang.Object + + virtualMethods = null; + interfaceTable = null; + } + } + + protected ClassDef(UnresolvedClassInfo classInfo) { + classType = classInfo.classType; + isInterface = classInfo.isInterface; + + superclass = loadSuperclass(classInfo); + if (superclass == null) { + classDepth = 0; + } else { + classDepth = superclass.classDepth + 1; + } + + implementedInterfaces = loadAllImplementedInterfaces(classInfo); + + //TODO: we can probably get away with only creating the interface table for interface types + interfaceTable = loadInterfaceTable(classInfo); + virtualMethods = classInfo.virtualMethods; + vtable = loadVtable(classInfo); + + int directMethodCount = 0; + if (classInfo.directMethods != null) { + directMethodCount = classInfo.directMethods.length; + } + methodLookup = new HashMap((int)Math.ceil(((vtable.length + directMethodCount)/ .7f)), .75f); + for (int i=0; i 0) { + for (int i=0; i superclassDepth) { + ancestor = ancestor.getSuperclass(); + } + + return ancestor == superclassDef; + } + + /** + * Returns true if this class implements the given interface. This searches the interfaces that this class + * directly implements, any interface implemented by this class's superclasses, and any super-interface of + * any of these interfaces. + * @param interfaceDef the interface + * @return true if this class implements the given interface + */ + public boolean implementsInterface(ClassDef interfaceDef) { + assert !(interfaceDef instanceof UnresolvedClassDef); + return implementedInterfaces.contains(interfaceDef); + } + + public boolean hasVirtualMethod(String method) { + Integer val = methodLookup.get(method); + if (val == null || val < 0) { + return false; + } + return true; + } + + public int getMethodType(String method) { + Integer val = methodLookup.get(method); + if (val == null) { + return -1; + } + if (val >= 0) { + return DeodexUtil.Virtual; + } + if (val == DirectMethod) { + return DeodexUtil.Direct; + } + if (val == StaticMethod) { + return DeodexUtil.Static; + } + throw new RuntimeException("Unexpected method type"); + } + + public FieldDef 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, FieldDef[] fields, int position1, int position2) { + byte tempType = fieldTypes[position1]; + fieldTypes[position1] = fieldTypes[position2]; + fieldTypes[position2] = tempType; + + FieldDef tempField = fields[position1]; + fields[position1] = fields[position2]; + fields[position2] = tempField; + } + + private ClassDef loadSuperclass(UnresolvedClassInfo classInfo) { + if (classInfo.classType.equals("Ljava/lang/Object;")) { + if (classInfo.superclassType != null) { + throw new ExceptionWithContext("Invalid superclass " + + classInfo.superclassType + " for Ljava/lang/Object;. " + + "The Object class cannot have a superclass"); + } + return null; + } else { + String superclassType = classInfo.superclassType; + if (superclassType == null) { + throw new ExceptionWithContext(classInfo.classType + " has no superclass"); + } + + ClassDef superclass; + try { + superclass = ClassPath.getClassDef(superclassType); + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, + String.format("Could not find superclass %s", superclassType)); + } + + if (!isInterface && superclass.isInterface) { + throw new ValidationException("Class " + classType + " has the interface " + superclass.classType + + " as its superclass"); + } + if (isInterface && !superclass.isInterface && superclass != + ClassPath.theClassPath.javaLangObjectClassDef) { + throw new ValidationException("Interface " + classType + " has the non-interface class " + + superclass.classType + " as its superclass"); + } + + return superclass; + } + } + + private TreeSet loadAllImplementedInterfaces(UnresolvedClassInfo classInfo) { + assert classType != null; + assert classType.equals("Ljava/lang/Object;") || superclass != null; + assert classInfo != null; + + TreeSet implementedInterfaceSet = new TreeSet(); + + if (superclass != null) { + for (ClassDef interfaceDef: superclass.implementedInterfaces) { + implementedInterfaceSet.add(interfaceDef); + } + } + + + if (classInfo.interfaces != null) { + for (String interfaceType: classInfo.interfaces) { + ClassDef interfaceDef; + try { + interfaceDef = ClassPath.getClassDef(interfaceType); + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, + String.format("Could not find interface %s", interfaceType)); + } + assert interfaceDef.isInterface(); + implementedInterfaceSet.add(interfaceDef); + + interfaceDef = interfaceDef.getSuperclass(); + while (!interfaceDef.getClassType().equals("Ljava/lang/Object;")) { + assert interfaceDef.isInterface(); + implementedInterfaceSet.add(interfaceDef); + interfaceDef = interfaceDef.getSuperclass(); + } + } + } + + return implementedInterfaceSet; + } + + private LinkedHashMap loadInterfaceTable(UnresolvedClassInfo classInfo) { + if (classInfo.interfaces == null) { + return null; + } + + LinkedHashMap interfaceTable = new LinkedHashMap(); + + for (String interfaceType: classInfo.interfaces) { + if (!interfaceTable.containsKey(interfaceType)) { + ClassDef interfaceDef; + try { + interfaceDef = ClassPath.getClassDef(interfaceType); + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, + String.format("Could not find interface %s", interfaceType)); + } + interfaceTable.put(interfaceType, interfaceDef); + + if (interfaceDef.interfaceTable != null) { + for (ClassDef superInterface: interfaceDef.interfaceTable.values()) { + if (!interfaceTable.containsKey(superInterface.classType)) { + interfaceTable.put(superInterface.classType, superInterface); + } + } + } + } + } + + return interfaceTable; + } + + private String[] loadVtable(UnresolvedClassInfo classInfo) { + //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 virtualMethodList = new LinkedList(); + //use a temp hash table, so that we can construct the final lookup with an appropriate + //capacity, based on the number of virtual methods + HashMap tempVirtualMethodLookup = new HashMap(); + + //copy the virtual methods from the superclass + int methodIndex = 0; + if (superclass != null) { + for (String method: superclass.vtable) { + virtualMethodList.add(method); + tempVirtualMethodLookup.put(method, methodIndex++); + } + + assert superclass.instanceFields != null; + } + + + //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) + if (!this.isInterface) { + if (classInfo.virtualMethods != null) { + for (String virtualMethod: classInfo.virtualMethods) { + if (tempVirtualMethodLookup.get(virtualMethod) == null) { + virtualMethodList.add(virtualMethod); + tempVirtualMethodLookup.put(virtualMethod, 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++); + } + } + } + } + } + + String[] vtable = new String[virtualMethodList.size()]; + for (int i=0; i loadFields(UnresolvedClassInfo classInfo) { + //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; + final byte OTHER = 2; + + FieldDef[] fields = null; + //the "type" for each field in fields. 0=reference,1=wide,2=other + byte[] fieldTypes = null; + + if (classInfo.instanceFields != null) { + fields = new FieldDef[classInfo.instanceFields.length]; + fieldTypes = new byte[fields.length]; + + for (int i=0; i front) { + if (fieldTypes[back] == REFERENCE) { + swap(fieldTypes, fields, front, back--); + break; + } + back--; + } + } + + if (fieldTypes[front] != REFERENCE) { + break; + } + } + + + 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) != 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; + while (back > front) { + if (fieldTypes[back] == OTHER) { + swap(fieldTypes, fields, front++, back); + break; + } + back--; + } + } else { + //there's already a 32-bit field here that we can use + front++; + } + } + + //do the swap thing for wide fields + back = fields.length - 1; + for (; front front) { + if (fieldTypes[back] == WIDE) { + swap(fieldTypes, fields, front, back--); + break; + } + back--; + } + } + + if (fieldTypes[front] != WIDE) { + break; + } + } + + int superFieldCount = 0; + if (superclass != null) { + superFieldCount = superclass.instanceFields.size(); + } + + //now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets + int totalFieldCount = superFieldCount + fields.length; + SparseArray instanceFields = new SparseArray(totalFieldCount); + + int fieldOffset; + + if (superclass != null && superFieldCount > 0) { + for (int i=0; i types = typeList.getTypes(); + if (types != null && types.size() > 0) { + String[] interfaces = new String[types.size()]; + for (int i=0; i encodedMethods = classDataItem.getDirectMethods(); + if (encodedMethods.size() > 0) { + boolean[] staticMethods = new boolean[encodedMethods.size()]; + String[] directMethods = new String[encodedMethods.size()]; + + for (int i=0; i encodedMethods = classDataItem.getVirtualMethods(); + if (encodedMethods.size() > 0) { + String[] virtualMethods = new String[encodedMethods.size()]; + for (int i=0; i encodedFields = classDataItem.getInstanceFields(); + if (encodedFields.size() > 0) { + String[][] instanceFields = new String[encodedFields.size()][2]; + for (int i=0; i lines = new ArrayList(); + + BufferedReader br = new BufferedReader(fr); + + try { + String line = br.readLine(); + + while (line != null) { + if (line.length() > 0) { + lines.add(line); + } + + line = br.readLine(); + } + } catch (IOException ex) { + throw new RuntimeException("Error while reading file: " + inlineTable, ex); + } + + inlineMethods = new DeodexUtil.InlineMethod[lines.size()]; + + for (int i=0; i= inlineMethods.length) { + throw new RuntimeException("Invalid method index: " + methodIndex); + } + return inlineMethods[methodIndex]; + } + + private static final Pattern longMethodPattern = Pattern.compile("(L[^;]+;)->([^(]+)\\(([^)]*)\\)(.+)"); + + private DeodexUtil.InlineMethod parseAndResolveInlineMethod(String inlineMethod) { + Matcher m = longMethodPattern.matcher(inlineMethod); + if (!m.matches()) { + assert false; + throw new RuntimeException("Invalid method descriptor: " + inlineMethod); + } + + String className = m.group(1); + String methodName = m.group(2); + String methodParams = m.group(3); + String methodRet = m.group(4); + + ClassPath.ClassDef classDef = ClassPath.getClassDef(className, false); + int methodType = classDef.getMethodType(String.format("%s(%s)%s", methodName, methodParams, methodRet)); + + if (methodType == -1) { + throw new RuntimeException("Cannot resolve inline method: " + inlineMethod); + } + + return new DeodexUtil.InlineMethod(methodType, className, methodName, methodParams, methodRet); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java new file mode 100644 index 00000000..be689ccd --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DeodexUtil.java @@ -0,0 +1,293 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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 org.jf.dexlib.*; + +import java.util.ArrayList; +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 final InlineMethodResolver inlineMethodResolver; + + public final DexFile dexFile; + + public DeodexUtil(DexFile dexFile) { + this.dexFile = dexFile; + OdexHeader odexHeader = dexFile.getOdexHeader(); + if (odexHeader == null) { + //if there isn't an odex header, why are we creating an DeodexUtil object? + assert false; + throw new RuntimeException("Cannot create a DeodexUtil object for a dex file without an odex header"); + } + inlineMethodResolver = InlineMethodResolver.createInlineMethodResolver(this, odexHeader.version); + } + + public DeodexUtil(DexFile dexFile, InlineMethodResolver inlineMethodResolver) { + this.dexFile = dexFile; + this.inlineMethodResolver = inlineMethodResolver; + } + + public InlineMethod lookupInlineMethod(AnalyzedInstruction instruction) { + return inlineMethodResolver.resolveExecuteInline(instruction); + } + + public FieldIdItem lookupField(ClassPath.ClassDef classDef, int fieldOffset) { + ClassPath.FieldDef 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); + if (method == null) { + return null; + } + + 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 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; + } + + 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 paramList = new LinkedList(); + + for (int i=0; i 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, ClassPath.FieldDef field) { + String definingClass = field.definingClass; + String fieldName = field.name; + String fieldType = field.type; + + 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; + + ArrayList parents = new ArrayList(); + parents.add(fieldClass); + + while (fieldClass != null && !fieldClass.getClassType().equals(definingClass)) { + fieldClass = fieldClass.getSuperclass(); + parents.add(fieldClass); + } + + for (int i=parents.size()-1; i>=0; i--) { + fieldClass = parents.get(i); + + TypeIdItem classTypeItem = TypeIdItem.lookupTypeIdItem(dexFile, fieldClass.getClassType()); + if (classTypeItem == null) { + continue; + } + + FieldIdItem fieldIdItem = FieldIdItem.lookupFieldIdItem(dexFile, classTypeItem, fieldTypeItem, fieldNameItem); + if (fieldIdItem != null) { + return fieldIdItem; + } + } + return null; + } + + public static 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; + + 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(DeodexUtil deodexUtil) { + if (methodIdItem == null) { + loadMethod(deodexUtil); + } + return methodIdItem; + } + + private void loadMethod(DeodexUtil deodexUtil) { + ClassPath.ClassDef classDef = ClassPath.getClassDef(classType); + + this.methodIdItem = deodexUtil.parseAndResolveMethod(classDef, methodName, parameters, returnType); + } + + public String getMethodString() { + return String.format("%s->%s(%s)%s", classType, methodName, parameters, returnType); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DexFileClassMap.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DexFileClassMap.java new file mode 100644 index 00000000..ca442687 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DexFileClassMap.java @@ -0,0 +1,56 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2011 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 org.jf.dexlib.ClassDefItem; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.TypeIdItem; + +import java.util.HashMap; + +/** + * Keeps a simple map of classes defined in a dex file, allowing you to look them up by TypeIdItem or name + */ +public class DexFileClassMap { + private final HashMap definedClasses = new HashMap(); + + public DexFileClassMap(DexFile dexFile) { + for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) { + definedClasses.put(classDefItem.getClassType().getTypeDescriptor(), classDefItem); + } + } + + public ClassDefItem getClassDefByName(String typeName) { + return definedClasses.get(typeName); + } + + public ClassDefItem getClassDefByType(TypeIdItem typeIdItem) { + return definedClasses.get(typeIdItem.getTypeDescriptor()); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java new file mode 100644 index 00000000..33835885 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java @@ -0,0 +1,180 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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 org.jf.dexlib.Code.OdexedInvokeInline; +import org.jf.dexlib.Code.OdexedInvokeVirtual; + +import static org.jf.dexlib.Code.Analysis.DeodexUtil.Static; +import static org.jf.dexlib.Code.Analysis.DeodexUtil.Virtual; +import static org.jf.dexlib.Code.Analysis.DeodexUtil.Direct; + +public abstract class InlineMethodResolver { + public static InlineMethodResolver createInlineMethodResolver(DeodexUtil deodexUtil, int odexVersion) { + if (odexVersion == 35) { + return new InlineMethodResolver_version35(deodexUtil); + } else if (odexVersion == 36) { + return new InlineMethodResolver_version36(deodexUtil); + } else { + throw new RuntimeException(String.format("odex version %d is not supported yet", odexVersion)); + } + } + + protected InlineMethodResolver() { + } + + public abstract DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction instruction); + + private static class InlineMethodResolver_version35 extends InlineMethodResolver + { + private final DeodexUtil.InlineMethod[] inlineMethods; + + public InlineMethodResolver_version35(DeodexUtil deodexUtil) { + inlineMethods = new DeodexUtil.InlineMethod[] { + new DeodexUtil.InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"), + new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"), + new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"), + new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"), + new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D") + }; + } + + @Override + public DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction analyzedInstruction) { + assert analyzedInstruction.instruction instanceof OdexedInvokeInline; + + OdexedInvokeInline instruction = (OdexedInvokeInline)analyzedInstruction.instruction; + int inlineIndex = instruction.getInlineIndex(); + + if (inlineIndex < 0 || inlineIndex >= inlineMethods.length) { + throw new RuntimeException("Invalid inline index: " + inlineIndex); + } + return inlineMethods[inlineIndex]; + } + } + + private static class InlineMethodResolver_version36 extends InlineMethodResolver + { + private final DeodexUtil.InlineMethod[] inlineMethods; + private final DeodexUtil.InlineMethod indexOfIMethod; + private final DeodexUtil.InlineMethod indexOfIIMethod; + private final DeodexUtil.InlineMethod fastIndexOfMethod; + private final DeodexUtil.InlineMethod isEmptyMethod; + + + public InlineMethodResolver_version36(DeodexUtil deodexUtil) { + //The 5th and 6th entries differ between froyo and gingerbread. We have to look at the parameters being + //passed to distinguish between them. + + //froyo + indexOfIMethod = new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "I", "I"); + indexOfIIMethod = new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "II", "I"); + + //gingerbread + fastIndexOfMethod = new DeodexUtil.InlineMethod(Direct, "Ljava/lang/String;", "fastIndexOf", "II", "I"); + isEmptyMethod = new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "isEmpty", "", "Z"); + + inlineMethods = new DeodexUtil.InlineMethod[] { + new DeodexUtil.InlineMethod(Static, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"), + new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "charAt", "I", "C"), + new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"), + new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"), + //froyo: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "I", "I"), + //gingerbread: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "fastIndexOf", "II", "I"), + null, + //froyo: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "indexOf", "II", "I"), + //gingerbread: deodexUtil.new InlineMethod(Virtual, "Ljava/lang/String;", "isEmpty", "", "Z"), + null, + new DeodexUtil.InlineMethod(Virtual, "Ljava/lang/String;", "length", "", "I"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "I", "I"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "J", "J"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "F", "F"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "abs", "D", "D"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "min", "II", "I"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "max", "II", "I"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sqrt", "D", "D"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "cos", "D", "D"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Math;", "sin", "D", "D"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Float;", "floatToIntBits", "F", "I"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Float;", "floatToRawIntBits", "F", "I"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Float;", "intBitsToFloat", "I", "F"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Double;", "doubleToLongBits", "D", "J"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Double;", "doubleToRawLongBits", "D", "J"), + new DeodexUtil.InlineMethod(Static, "Ljava/lang/Double;", "longBitsToDouble", "J", "D") + }; + } + + @Override + public DeodexUtil.InlineMethod resolveExecuteInline(AnalyzedInstruction analyzedInstruction) { + assert analyzedInstruction.instruction instanceof OdexedInvokeInline; + + OdexedInvokeInline instruction = (OdexedInvokeInline)analyzedInstruction.instruction; + int inlineIndex = instruction.getInlineIndex(); + + if (inlineIndex < 0 || inlineIndex >= inlineMethods.length) { + throw new RuntimeException("Invalid method index: " + inlineIndex); + } + + if (inlineIndex == 4) { + int parameterCount = getParameterCount(instruction); + if (parameterCount == 2) { + return indexOfIMethod; + } else if (parameterCount == 3) { + return fastIndexOfMethod; + } else { + throw new RuntimeException("Could not determine the correct inline method to use"); + } + } else if (inlineIndex == 5) { + int parameterCount = getParameterCount(instruction); + if (parameterCount == 3) { + return indexOfIIMethod; + } else if (parameterCount == 1) { + return isEmptyMethod; + } else { + throw new RuntimeException("Could not determine the correct inline method to use"); + } + } + + return inlineMethods[inlineIndex]; + } + + private int getParameterCount(OdexedInvokeInline instruction) { + return instruction.getRegCount(); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java new file mode 100644 index 00000000..3f67d7f5 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java @@ -0,0 +1,3755 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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 org.jf.dexlib.*; +import org.jf.dexlib.Code.*; +import org.jf.dexlib.Code.Format.*; +import org.jf.dexlib.Util.AccessFlags; +import org.jf.dexlib.Util.ExceptionWithContext; +import org.jf.dexlib.Util.SparseArray; + +import java.util.BitSet; +import java.util.EnumSet; +import java.util.List; + +/** + * The MethodAnalyzer performs several functions. It "analyzes" the instructions and infers the register types + * for each register, it can deodex odexed instructions, and it can verify the bytecode. The analysis and verification + * are done in two separate passes, because the analysis has to process instructions multiple times in some cases, and + * there's no need to perform the verification multiple times, so we wait until the method is fully analyzed and then + * verify it. + * + * Before calling the analyze() method, you must have initialized the ClassPath by calling + * ClassPath.InitializeClassPath + */ +public class MethodAnalyzer { + private final ClassDataItem.EncodedMethod encodedMethod; + + private final DeodexUtil deodexUtil; + + private SparseArray instructions; + + private static final int NOT_ANALYZED = 0; + private static final int ANALYZED = 1; + private static final int VERIFIED = 2; + private int analyzerState = NOT_ANALYZED; + + private BitSet analyzedInstructions; + + private ValidationException validationException = null; + + //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the + //register types for this instruction to the parameter types, in order to have them propagate to all of its + //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first + //instruction, etc. + private AnalyzedInstruction startOfMethod; + + public MethodAnalyzer(ClassDataItem.EncodedMethod encodedMethod, boolean deodex, + InlineMethodResolver inlineResolver) { + if (encodedMethod == null) { + throw new IllegalArgumentException("encodedMethod cannot be null"); + } + if (encodedMethod.codeItem == null || encodedMethod.codeItem.getInstructions().length == 0) { + throw new IllegalArgumentException("The method has no code"); + } + this.encodedMethod = encodedMethod; + + if (deodex) { + if (inlineResolver != null) { + this.deodexUtil = new DeodexUtil(encodedMethod.method.getDexFile(), inlineResolver); + } else { + this.deodexUtil = new DeodexUtil(encodedMethod.method.getDexFile()); + } + } else { + this.deodexUtil = null; + } + + //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't + //have to handle the case this special case of instruction being null, in the main class + startOfMethod = new AnalyzedInstruction(null, -1, encodedMethod.codeItem.getRegisterCount()) { + public boolean setsRegister() { + return false; + } + + @Override + public boolean setsWideRegister() { + return false; + } + + @Override + public boolean setsRegister(int registerNumber) { + return false; + } + + @Override + public int getDestinationRegister() { + assert false; + return -1; + }; + }; + + buildInstructionList(); + + analyzedInstructions = new BitSet(instructions.size()); + } + + public boolean isAnalyzed() { + return analyzerState >= ANALYZED; + } + + public boolean isVerified() { + return analyzerState == VERIFIED; + } + + public void analyze() { + assert encodedMethod != null; + assert encodedMethod.codeItem != null; + + if (analyzerState >= ANALYZED) { + //the instructions have already been analyzed, so there is nothing to do + return; + } + + CodeItem codeItem = encodedMethod.codeItem; + MethodIdItem methodIdItem = encodedMethod.method; + + int totalRegisters = codeItem.getRegisterCount(); + int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); + + int nonParameterRegisters = totalRegisters - parameterRegisters; + + for (AnalyzedInstruction instruction: instructions.getValues()) { + instruction.dead = true; + } + + //if this isn't a static method, determine which register is the "this" register and set the type to the + //current class + if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) { + nonParameterRegisters--; + int thisRegister = totalRegisters - parameterRegisters - 1; + + //if this is a constructor, then set the "this" register to an uninitialized reference of the current class + if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) { + setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, + RegisterType.getRegisterType(RegisterType.Category.UninitThis, + ClassPath.getClassDef(methodIdItem.getContainingClass()))); + } else { + setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, + RegisterType.getRegisterType(RegisterType.Category.Reference, + ClassPath.getClassDef(methodIdItem.getContainingClass()))); + } + } + + TypeListItem parameters = methodIdItem.getPrototype().getParameters(); + if (parameters != null) { + RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters); + for (int i=0; i=0; i=instructionsToAnalyze.nextSetBit(i+1)) { + instructionsToAnalyze.clear(i); + if (analyzedInstructions.get(i)) { + continue; + } + AnalyzedInstruction instructionToAnalyze = instructions.valueAt(i); + instructionToAnalyze.dead = false; + try { + if (instructionToAnalyze.originalInstruction.opcode.odexOnly()) { + //if we had deodexed an odex instruction in a previous pass, we might have more specific + //register information now, so let's restore the original odexed instruction and + //re-deodex it + instructionToAnalyze.restoreOdexedInstruction(); + } + + if (!analyzeInstruction(instructionToAnalyze)) { + undeodexedInstructions.set(i); + continue; + } else { + didSomething = true; + undeodexedInstructions.clear(i); + } + } catch (ValidationException ex) { + this.validationException = ex; + int codeAddress = getInstructionAddress(instructionToAnalyze); + ex.setCodeAddress(codeAddress); + ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.opcode.name)); + ex.addContext(String.format("CodeAddress: %d", codeAddress)); + ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString())); + break; + } + + analyzedInstructions.set(instructionToAnalyze.getInstructionIndex()); + + for (AnalyzedInstruction successor: instructionToAnalyze.successors) { + instructionsToAnalyze.set(successor.getInstructionIndex()); + } + } + if (validationException != null) { + break; + } + } + + if (!didSomething) { + break; + } + + if (!undeodexedInstructions.isEmpty()) { + for (int i=undeodexedInstructions.nextSetBit(0); i>=0; i=undeodexedInstructions.nextSetBit(i+1)) { + instructionsToAnalyze.set(i); + } + } + } while (true); + + //Now, go through and fix up any unresolvable odex instructions. These are usually odex instructions + //that operate on a null register, and thus always throw an NPE. They can also be any sort of odex instruction + //that occurs after an unresolvable odex instruction. We deodex if possible, or replace with an + //UnresolvableOdexInstruction + for (int i=0; i=0; i=instructionsToVerify.nextSetBit(i+1)) { + instructionsToVerify.clear(i); + if (verifiedInstructions.get(i)) { + continue; + } + AnalyzedInstruction instructionToVerify = instructions.valueAt(i); + try { + verifyInstruction(instructionToVerify); + } catch (ValidationException ex) { + this.validationException = ex; + int codeAddress = getInstructionAddress(instructionToVerify); + ex.setCodeAddress(codeAddress); + ex.addContext(String.format("opcode: %s", instructionToVerify.instruction.opcode.name)); + ex.addContext(String.format("CodeAddress: %d", codeAddress)); + ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString())); + break; + } + + verifiedInstructions.set(instructionToVerify.getInstructionIndex()); + + for (AnalyzedInstruction successor: instructionToVerify.successors) { + instructionsToVerify.set(successor.getInstructionIndex()); + } + } + if (validationException != null) { + break; + } + } + + analyzerState = VERIFIED; + } + + private int getThisRegister() { + assert (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0; + + CodeItem codeItem = encodedMethod.codeItem; + assert codeItem != null; + + MethodIdItem methodIdItem = encodedMethod.method; + assert methodIdItem != null; + + int totalRegisters = codeItem.getRegisterCount(); + if (totalRegisters == 0) { + throw new ValidationException("A non-static method must have at least 1 register"); + } + + int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); + + return totalRegisters - parameterRegisters - 1; + } + + private boolean isInstanceConstructor() { + return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0 && + (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; + } + + private boolean isStaticConstructor() { + return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0 && + (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; + } + + public AnalyzedInstruction getStartOfMethod() { + return startOfMethod; + } + + /** + * @return a read-only list containing the instructions for tihs method. + */ + public List getInstructions() { + return instructions.getValues(); + } + + public ClassDataItem.EncodedMethod getMethod() { + return this.encodedMethod; + } + + public ValidationException getValidationException() { + return validationException; + } + + private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) { + assert typeListItem != null; + assert parameterRegisterCount == typeListItem.getRegisterCount(); + + RegisterType[] registerTypes = new RegisterType[parameterRegisterCount]; + + int registerNum = 0; + for (TypeIdItem type: typeListItem.getTypes()) { + if (type.getRegisterCount() == 2) { + registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true); + registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false); + } else { + registerTypes[registerNum++] = RegisterType.getRegisterTypeForTypeIdItem(type); + } + } + + return registerTypes; + } + + public int getInstructionAddress(AnalyzedInstruction instruction) { + return instructions.keyAt(instruction.instructionIndex); + } + + private void setDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, + RegisterType registerType) { + setPostRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(), + registerType); + } + + private void setPostRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, int registerNumber, + RegisterType registerType) { + + BitSet changedInstructions = new BitSet(instructions.size()); + + if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) { + return; + } + + propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions); + + //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction + //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on + //the next iteration of the while loop. + //This could also be done recursively, but in large methods it would likely cause very deep recursion, + //which requires the user to specify a larger stack size. This isn't really a problem, but it is slightly + //annoying. + while (!changedInstructions.isEmpty()) { + for (int instructionIndex=changedInstructions.nextSetBit(0); + instructionIndex>=0; + instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) { + + changedInstructions.clear(instructionIndex); + + propagateRegisterToSuccessors(instructions.valueAt(instructionIndex), registerNumber, + changedInstructions); + } + } + + if (registerType.category == RegisterType.Category.LongLo) { + checkWidePair(registerNumber, analyzedInstruction); + setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, + RegisterType.getRegisterType(RegisterType.Category.LongHi, null)); + } else if (registerType.category == RegisterType.Category.DoubleLo) { + checkWidePair(registerNumber, analyzedInstruction); + setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, + RegisterType.getRegisterType(RegisterType.Category.DoubleHi, null)); + } + } + + private void propagateRegisterToSuccessors(AnalyzedInstruction instruction, int registerNumber, + BitSet changedInstructions) { + RegisterType postRegisterType = instruction.getPostInstructionRegisterType(registerNumber); + for (AnalyzedInstruction successor: instruction.successors) { + if (successor.mergeRegister(registerNumber, postRegisterType, analyzedInstructions)) { + changedInstructions.set(successor.instructionIndex); + } + } + } + + private void buildInstructionList() { + assert encodedMethod != null; + assert encodedMethod.codeItem != null; + int registerCount = encodedMethod.codeItem.getRegisterCount(); + + Instruction[] insns = encodedMethod.codeItem.getInstructions(); + + instructions = new SparseArray(insns.length); + + //first, create all the instructions and populate the instructionAddresses array + int currentCodeAddress = 0; + for (int i=0; i currentCodeAddress); + + currentTry = tryItem; + + currentExceptionHandlers = buildExceptionHandlerArray(tryItem); + } + } + + //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers + //for the current instruction + if (currentTry != null && instructionOpcode.canThrow()) { + exceptionHandlers[i] = currentExceptionHandlers; + } + } + } + + //finally, populate the successors and predecessors for each instruction. We start at the fake "StartOfMethod" + //instruction and follow the execution path. Any unreachable code won't have any predecessors or successors, + //and no reachable code will have an unreachable predessor or successor + assert instructions.size() > 0; + BitSet instructionsToProcess = new BitSet(insns.length); + + addPredecessorSuccessor(startOfMethod, instructions.valueAt(0), exceptionHandlers, instructionsToProcess); + while (!instructionsToProcess.isEmpty()) { + int currentInstructionIndex = instructionsToProcess.nextSetBit(0); + instructionsToProcess.clear(currentInstructionIndex); + + AnalyzedInstruction instruction = instructions.valueAt(currentInstructionIndex); + Opcode instructionOpcode = instruction.instruction.opcode; + int instructionCodeAddress = getInstructionAddress(instruction); + + if (instruction.instruction.opcode.canContinue()) { + if (instruction.instruction.opcode != Opcode.NOP || + !instruction.instruction.getFormat().variableSizeFormat) { + + if (currentInstructionIndex == instructions.size() - 1) { + throw new ValidationException("Execution can continue past the last instruction"); + } + + AnalyzedInstruction nextInstruction = instructions.valueAt(currentInstructionIndex+1); + addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers, instructionsToProcess); + } + } + + if (instruction.instruction instanceof OffsetInstruction) { + OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction; + + if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) { + MultiOffsetInstruction switchDataInstruction = + (MultiOffsetInstruction)instructions.get(instructionCodeAddress + + offsetInstruction.getTargetAddressOffset()).instruction; + for (int targetAddressOffset: switchDataInstruction.getTargets()) { + AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress + + targetAddressOffset); + + addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, + instructionsToProcess); + } + } else { + int targetAddressOffset = offsetInstruction.getTargetAddressOffset(); + AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress + + targetAddressOffset); + addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess); + } + } + } + } + + private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor, + AnalyzedInstruction[][] exceptionHandlers, + BitSet instructionsToProcess) { + addPredecessorSuccessor(predecessor, successor, exceptionHandlers, instructionsToProcess, false); + } + + private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor, + AnalyzedInstruction[][] exceptionHandlers, + BitSet instructionsToProcess, boolean allowMoveException) { + + if (!allowMoveException && successor.instruction.opcode == Opcode.MOVE_EXCEPTION) { + throw new ValidationException("Execution can pass from the " + predecessor.instruction.opcode.name + + " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) + + " to the move-exception instruction at address 0x" + + Integer.toHexString(getInstructionAddress(successor))); + } + + if (!successor.addPredecessor(predecessor)) { + return; + } + + predecessor.addSuccessor(successor); + instructionsToProcess.set(successor.getInstructionIndex()); + + + //if the successor can throw an instruction, then we need to add the exception handlers as additional + //successors to the predecessor (and then apply this same logic recursively if needed) + //Technically, we should handle the monitor-exit instruction as a special case. The exception is actually + //thrown *after* the instruction executes, instead of "before" the instruction executes, lke for any other + //instruction. But since it doesn't modify any registers, we can treat it like any other instruction. + AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex]; + if (exceptionHandlersForSuccessor != null) { + //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction + //can throw an exception + assert successor.instruction.opcode.canThrow(); + + for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) { + addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, instructionsToProcess, true); + } + } + } + + private AnalyzedInstruction[] buildExceptionHandlerArray(CodeItem.TryItem tryItem) { + int exceptionHandlerCount = tryItem.encodedCatchHandler.handlers.length; + int catchAllHandler = tryItem.encodedCatchHandler.getCatchAllHandlerAddress(); + if (catchAllHandler != -1) { + exceptionHandlerCount++; + } + + AnalyzedInstruction[] exceptionHandlers = new AnalyzedInstruction[exceptionHandlerCount]; + for (int i=0; i Primitive32BitCategories = EnumSet.of( + RegisterType.Category.Null, + RegisterType.Category.One, + RegisterType.Category.Boolean, + RegisterType.Category.Byte, + RegisterType.Category.PosByte, + RegisterType.Category.Short, + RegisterType.Category.PosShort, + RegisterType.Category.Char, + RegisterType.Category.Integer, + RegisterType.Category.Float); + + private static final EnumSet WideLowCategories = EnumSet.of( + RegisterType.Category.LongLo, + RegisterType.Category.DoubleLo); + + private static final EnumSet WideHighCategories = EnumSet.of( + RegisterType.Category.LongHi, + RegisterType.Category.DoubleHi); + + private static final EnumSet ReferenceCategories = EnumSet.of( + RegisterType.Category.Null, + RegisterType.Category.Reference); + + private static final EnumSet ReferenceOrUninitThisCategories = EnumSet.of( + RegisterType.Category.Null, + RegisterType.Category.UninitThis, + RegisterType.Category.Reference); + + private static final EnumSet ReferenceOrUninitCategories = EnumSet.of( + RegisterType.Category.Null, + RegisterType.Category.UninitRef, + RegisterType.Category.UninitThis, + RegisterType.Category.Reference); + + private static final EnumSet ReferenceAndPrimitive32BitCategories = EnumSet.of( + RegisterType.Category.Null, + RegisterType.Category.One, + RegisterType.Category.Boolean, + RegisterType.Category.Byte, + RegisterType.Category.PosByte, + RegisterType.Category.Short, + RegisterType.Category.PosShort, + RegisterType.Category.Char, + RegisterType.Category.Integer, + RegisterType.Category.Float, + RegisterType.Category.Reference); + + private static final EnumSet BooleanCategories = EnumSet.of( + RegisterType.Category.Null, + RegisterType.Category.One, + RegisterType.Category.Boolean); + + private void analyzeMove(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType); + } + + private void verifyMove(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories); + } + + private void analyzeMoveResult(AnalyzedInstruction analyzedInstruction) { + AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); + if (!previousInstruction.instruction.opcode.setsResult()) { + throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + + "invoke-*/fill-new-array instruction"); + } + + RegisterType resultRegisterType; + InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction; + Item item = invokeInstruction.getReferencedItem(); + + if (item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM) { + resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem( + ((MethodIdItem)item).getPrototype().getReturnType()); + } else { + assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; + resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); + } + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType); + } + + private void verifyMoveResult(AnalyzedInstruction analyzedInstruction, + EnumSet allowedCategories) { + if (analyzedInstruction.instructionIndex == 0) { + throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " + + "instruction in a method. It must occur after an invoke-*/fill-new-array instruction"); + } + + AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); + + if (!previousInstruction.instruction.opcode.setsResult()) { + throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + + "invoke-*/fill-new-array instruction"); + } + + //TODO: does dalvik allow a move-result after an invoke with a void return type? + RegisterType resultRegisterType; + + InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.getInstruction(); + Item item = invokeInstruction.getReferencedItem(); + + if (item instanceof MethodIdItem) { + resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem( + ((MethodIdItem)item).getPrototype().getReturnType()); + } else { + assert item instanceof TypeIdItem; + resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); + } + + if (!allowedCategories.contains(resultRegisterType.category)) { + throw new ValidationException(String.format("Wrong move-result* instruction for return value %s", + resultRegisterType.toString())); + } + } + + private void analyzeMoveException(AnalyzedInstruction analyzedInstruction) { + CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); + int instructionAddress = getInstructionAddress(analyzedInstruction); + + if (tries == null) { + throw new ValidationException("move-exception must be the first instruction in an exception handler block"); + } + + RegisterType exceptionType = null; + + for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { + if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) { + exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference, + ClassPath.getClassDef("Ljava/lang/Throwable;")); + break; + } + for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { + if (handler.getHandlerAddress() == instructionAddress) { + exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType) + .merge(exceptionType); + } + } + } + + if (exceptionType == null) { + throw new ValidationException("move-exception must be the first instruction in an exception handler block"); + } + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); + } + + private void verifyMoveException(AnalyzedInstruction analyzedInstruction) { + CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); + int instructionAddress = getInstructionAddress(analyzedInstruction); + + if (tries == null) { + throw new ValidationException("move-exception must be the first instruction in an exception handler block"); + } + + RegisterType exceptionType = null; + + for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { + if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) { + exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference, + ClassPath.getClassDef("Ljava/lang/Throwable;")); + break; + } + for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { + if (handler.getHandlerAddress() == instructionAddress) { + exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType) + .merge(exceptionType); + } + } + } + + if (exceptionType == null) { + throw new ValidationException("move-exception must be the first instruction in an exception handler block"); + } + + //TODO: check if the type is a throwable. Should we throw a ValidationException or print a warning? (does dalvik validate that it's a throwable? It doesn't in CodeVerify.c, but it might check in DexSwapVerify.c) + if (exceptionType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Exception type %s is not a reference type", + exceptionType.toString())); + } + } + + private void analyzeReturnVoidBarrier(AnalyzedInstruction analyzedInstruction) { + analyzeReturnVoidBarrier(analyzedInstruction, true); + } + + private void analyzeReturnVoidBarrier(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { + Instruction10x instruction = (Instruction10x)analyzedInstruction.instruction; + + Instruction10x deodexedInstruction = new Instruction10x(Opcode.RETURN_VOID); + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + if (analyzeResult) { + analyzeInstruction(analyzedInstruction); + } + } + + private void verifyReturnVoid(AnalyzedInstruction analyzedInstruction) { + TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); + if (returnType.getTypeDescriptor().charAt(0) != 'V') { + //TODO: could add which return-* variation should be used instead + throw new ValidationException("Cannot use return-void with a non-void return type (" + + returnType.getTypeDescriptor() + ")"); + } + } + + private void verifyReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { + /*if (this.isInstanceConstructor()) { + checkConstructorReturn(analyzedInstruction); + }*/ + + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + int returnRegister = instruction.getRegisterA(); + RegisterType returnRegisterType = getAndCheckSourceRegister(analyzedInstruction, returnRegister, + validCategories); + + TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); + if (returnType.getTypeDescriptor().charAt(0) == 'V') { + throw new ValidationException("Cannot use return with a void return type. Use return-void instead"); + } + + RegisterType methodReturnRegisterType = RegisterType.getRegisterTypeForTypeIdItem(returnType); + + if (!validCategories.contains(methodReturnRegisterType.category)) { + //TODO: could add which return-* variation should be used instead + throw new ValidationException(String.format("Cannot use %s with return type %s", + analyzedInstruction.instruction.opcode.name, returnType.getTypeDescriptor())); + } + + if (validCategories == ReferenceCategories) { + if (methodReturnRegisterType.type.isInterface()) { + if (returnRegisterType.category != RegisterType.Category.Null && + !returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) { + //TODO: how to handle warnings? + } + } else { + if (returnRegisterType.category == RegisterType.Category.Reference && + !returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) { + + throw new ValidationException(String.format("The return value in register v%d (%s) is not " + + "compatible with the method's return type %s", returnRegister, + returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType())); + } + } + } + } + + private void analyzeConst(AnalyzedInstruction analyzedInstruction) { + LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; + + RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral()); + + //we assume that the literal value is a valid value for the given instruction type, because it's impossible + //to store an invalid literal with the instruction. so we don't need to check the type of the literal + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); + } + + private void analyzeConstHigh16(AnalyzedInstruction analyzedInstruction) { + //the literal value stored in the instruction is a 16-bit value. When shifted left by 16, it will always be an + //integer + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Integer, null)); + } + + private void analyzeWideConst(AnalyzedInstruction analyzedInstruction) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); + } + + private void analyzeConstString(AnalyzedInstruction analyzedInstruction) { + ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;"); + RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType); + } + + private void analyzeConstClass(AnalyzedInstruction analyzedInstruction) { + ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); + RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType); + } + + + private void verifyConstClass(AnalyzedInstruction analyzedInstruction) { + ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); + RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); + + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; + + //TODO: need to check class access + //make sure the referenced class is resolvable + ClassPath.getClassDef((TypeIdItem)item); + } + + private void verifyMonitor(AnalyzedInstruction analyzedInstruction) { + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), ReferenceCategories); + } + + private void analyzeCheckCast(AnalyzedInstruction analyzedInstruction) { + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; + + RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType); + } + + private void verifyCheckCast(AnalyzedInstruction analyzedInstruction) { + { + //ensure the "source" register is a reference type + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + + RegisterType registerType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), + ReferenceCategories); + } + + { + //resolve and verify the class that we're casting to + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; + + //TODO: need to check class access + RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); + if (castRegisterType.category != RegisterType.Category.Reference) { + //TODO: verify that dalvik allows a non-reference type.. + //TODO: print a warning, but don't re-throw the exception. dalvik allows a non-reference type during validation (but throws an exception at runtime) + } + } + } + + private void analyzeInstanceOf(AnalyzedInstruction analyzedInstruction) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Boolean, null)); + } + + private void verifyInstanceOf(AnalyzedInstruction analyzedInstruction) { + { + //ensure the register that is being checks is a reference type + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), ReferenceCategories); + } + + { + //resolve and verify the class that we're checking against + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; + RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); + if (registerType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use instance-of with a non-reference type %s", + registerType.toString())); + } + + //TODO: is it valid to use an array type? + //TODO: could probably do an even more sophisticated check, where we check the possible register types against the specified type. In some cases, we could determine that it always fails, and print a warning to that effect. + } + } + + private void analyzeArrayLength(AnalyzedInstruction analyzedInstruction) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Integer, null)); + } + + private void verifyArrayLength(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + int arrayRegisterNumber = instruction.getRegisterB(); + RegisterType arrayRegisterType = getAndCheckSourceRegister(analyzedInstruction, arrayRegisterNumber, + ReferenceCategories); + + if (arrayRegisterType.type != null) { + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use array-length with non-array type %s", + arrayRegisterType.type.getClassType())); + } + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + } + } + + private void analyzeNewInstance(AnalyzedInstruction analyzedInstruction) { + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + + int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); + RegisterType destRegisterType = analyzedInstruction.getPostInstructionRegisterType(register); + if (destRegisterType.category != RegisterType.Category.Unknown) { + assert destRegisterType.category == RegisterType.Category.UninitRef; + + //the post-instruction destination register will only be set if we have already analyzed this instruction + //at least once. If this is the case, then the uninit reference has already been propagated to all + //successors and nothing else needs to be done. + return; + } + + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; + + RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getUnitializedReference(classType.type)); + } + + private void verifyNewInstance(AnalyzedInstruction analyzedInstruction) { + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + + int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); + RegisterType destRegisterType = analyzedInstruction.postRegisterMap[register]; + if (destRegisterType.category != RegisterType.Category.Unknown) { + assert destRegisterType.category == RegisterType.Category.UninitRef; + + //the "post-instruction" destination register will only be set if we've gone over + //this instruction at least once before. If this is the case, then we need to check + //all the other registers, and make sure that none of them contain the same + //uninitialized reference that is in the destination register. + + for (int i=0; i= 1<<16) { + throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " + + "is larger than the largest allowed register of v65535.", + instruction.getStartRegister(), + instruction.getStartRegister() + instruction.getRegCount() - 1)); + } + + verifyFilledNewArrayCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction)); + } + + private void verifyFillArrayData(AnalyzedInstruction analyzedInstruction) { + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + + int register = instruction.getRegisterA(); + RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); + assert registerType != null; + + if (registerType.category == RegisterType.Category.Null) { + return; + } + + if (registerType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " + + "type %s", register, registerType.toString())); + } + + assert registerType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type; + + if (arrayClassDef.getArrayDimensions() != 1) { + throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " + + "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); + } + + int elementWidth; + switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) { + case 'Z': + case 'B': + elementWidth = 1; + break; + case 'C': + case 'S': + elementWidth = 2; + break; + case 'I': + case 'F': + elementWidth = 4; + break; + case 'J': + case 'D': + elementWidth = 8; + break; + default: + throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " + + "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); + } + + + int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); + int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset; + AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress); + if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) { + throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x", + arrayDataCodeAddress)); + } + + ArrayDataPseudoInstruction arrayDataPseudoInstruction = + (ArrayDataPseudoInstruction)arrayDataInstruction.instruction; + + if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) { + throw new ValidationException(String.format("The array data at code address 0x%x does not have the " + + "correct element width for array type %s. Expecting element width %d, got element width %d.", + arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth, + arrayDataPseudoInstruction.getElementWidth())); + } + } + + private void verifyThrow(AnalyzedInstruction analyzedInstruction) { + int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); + + RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); + assert registerType != null; + + if (registerType.category == RegisterType.Category.Null) { + return; + } + + if (registerType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d", + registerType.toString(), register)); + } + + assert registerType.type != null; + + if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) { + throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d", + registerType.type.getClassType(), register)); + } + } + + private void analyzeArrayDataOrSwitch(AnalyzedInstruction analyzedInstruction) { + int dataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); + + int dataCodeAddress = this.getInstructionAddress(analyzedInstruction) + dataAddressOffset; + AnalyzedInstruction dataAnalyzedInstruction = instructions.get(dataCodeAddress); + + if (dataAnalyzedInstruction != null) { + dataAnalyzedInstruction.dead = false; + + //if there is a preceding nop, it's deadness should be the same + AnalyzedInstruction priorInstruction = + instructions.valueAt(dataAnalyzedInstruction.getInstructionIndex()-1); + if (priorInstruction.getInstruction().opcode == Opcode.NOP && + !priorInstruction.getInstruction().getFormat().variableSizeFormat) { + + priorInstruction.dead = false; + } + } + } + + private void verifySwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) { + int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); + int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); + + getAndCheckSourceRegister(analyzedInstruction, register, Primitive32BitCategories); + + int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset; + AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress); + + if (switchDataAnalyzedInstruction == null || + switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) { + throw new ValidationException(String.format("There is no %s structure at code address 0x%x", + expectedSwitchDataFormat.name(), switchDataCodeAddress)); + } + } + + private void analyzeFloatWideCmp(AnalyzedInstruction analyzedInstruction) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Byte, null)); + } + + private void verifyFloatWideCmp(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories); + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validCategories); + } + + private void verifyIfEqNe(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); + assert registerType1 != null; + + RegisterType registerType2 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert registerType2 != null; + + if (!( + (ReferenceCategories.contains(registerType1.category) && + ReferenceCategories.contains(registerType2.category)) + || + (Primitive32BitCategories.contains(registerType1.category) && + Primitive32BitCategories.contains(registerType2.category)) + )) { + + throw new ValidationException(String.format("%s cannot be used on registers of dissimilar types %s and " + + "%s. They must both be a reference type or a primitive 32 bit type.", + analyzedInstruction.instruction.opcode.name, registerType1.toString(), registerType2.toString())); + } + } + + private void verifyIf(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories); + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); + } + + private void verifyIfEqzNez(AnalyzedInstruction analyzedInstruction) { + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), + ReferenceAndPrimitive32BitCategories); + } + + private void verifyIfz(AnalyzedInstruction analyzedInstruction) { + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories); + } + + private void analyze32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(instructionCategory, null)); + } + + private void verify32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert arrayRegisterType != null; + + if (arrayRegisterType.category != RegisterType.Category.Null) { + if (arrayRegisterType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use %s with non-array type %s", + analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString())); + } + + assert arrayRegisterType.type != null; + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use %s with non-array type %s", + analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); + } + + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; + + if (arrayClassDef.getArrayDimensions() != 1) { + throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s", + analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); + } + + RegisterType arrayBaseType = + RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); + if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { + throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + arrayRegisterType.type.getClassType())); + } + } + } + + private void analyzeAgetWide(AnalyzedInstruction analyzedInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert arrayRegisterType != null; + + if (arrayRegisterType.category != RegisterType.Category.Null) { + assert arrayRegisterType.type != null; + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", + arrayRegisterType.type.getClassType())); + } + + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; + + char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); + if (arrayBaseType == 'J') { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); + } else if (arrayBaseType == 'D') { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null)); + } else { + throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " + + "array type for the instruction.", arrayRegisterType.type.getClassType())); + } + } else { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); + } + } + + private void verifyAgetWide(AnalyzedInstruction analyzedInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert arrayRegisterType != null; + + if (arrayRegisterType.category != RegisterType.Category.Null) { + if (arrayRegisterType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", + arrayRegisterType.category.toString())); + } + + assert arrayRegisterType.type != null; + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", + arrayRegisterType.type.getClassType())); + } + + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; + + if (arrayClassDef.getArrayDimensions() != 1) { + throw new ValidationException(String.format("Cannot use aget-wide with multi-dimensional array type %s", + arrayRegisterType.type.getClassType())); + } + + char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); + if (arrayBaseType != 'J' && arrayBaseType != 'D') { + throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " + + "array type for the instruction.", arrayRegisterType.type.getClassType())); + } + } + } + + private void analyzeAgetObject(AnalyzedInstruction analyzedInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert arrayRegisterType != null; + + if (arrayRegisterType.category != RegisterType.Category.Null) { + assert arrayRegisterType.type != null; + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", + arrayRegisterType.type.getClassType())); + } + + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; + + ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); + char elementTypePrefix = elementClassDef.getClassType().charAt(0); + if (elementTypePrefix != 'L' && elementTypePrefix != '[') { + throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + + "array type for the instruction.", arrayRegisterType.type.getClassType())); + } + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef)); + } else { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.Category.Null, null)); + } + } + + private void verifyAgetObject(AnalyzedInstruction analyzedInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert arrayRegisterType != null; + + if (arrayRegisterType.category != RegisterType.Category.Null) { + if (arrayRegisterType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", + arrayRegisterType.category.toString())); + } + + assert arrayRegisterType.type != null; + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", + arrayRegisterType.type.getClassType())); + } + + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; + + ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); + char elementTypePrefix = elementClassDef.getClassType().charAt(0); + if (elementTypePrefix != 'L' && elementTypePrefix != '[') { + throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + + "array type for the instruction.", arrayRegisterType.type.getClassType())); + } + } + } + + private void verify32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); + + RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); + assert sourceRegisterType != null; + RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); + if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { + throw new ValidationException(String.format("Cannot use %s with source register type %s.", + analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); + } + + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert arrayRegisterType != null; + + if (arrayRegisterType.category != RegisterType.Category.Null) { + if (arrayRegisterType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use %s with non-array type %s", + analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString())); + } + + assert arrayRegisterType.type != null; + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use %s with non-array type %s", + analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); + } + + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; + + if (arrayClassDef.getArrayDimensions() != 1) { + throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s", + analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); + } + + RegisterType arrayBaseType = + RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); + if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { + throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + arrayRegisterType.type.getClassType())); + } + } + } + + private void verifyAputWide(AnalyzedInstruction analyzedInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert arrayRegisterType != null; + + if (arrayRegisterType.category != RegisterType.Category.Null) { + if (arrayRegisterType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s", + arrayRegisterType.category.toString())); + } + + assert arrayRegisterType.type != null; + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s", + arrayRegisterType.type.getClassType())); + } + + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; + + if (arrayClassDef.getArrayDimensions() != 1) { + throw new ValidationException(String.format("Cannot use aput-wide with multi-dimensional array type %s", + arrayRegisterType.type.getClassType())); + } + + char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); + if (arrayBaseType != 'J' && arrayBaseType != 'D') { + throw new ValidationException(String.format("Cannot use aput-wide with array type %s. Incorrect " + + "array type for the instruction.", arrayRegisterType.type.getClassType())); + } + } + } + + private void verifyAputObject(AnalyzedInstruction analyzedInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); + + RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); + assert sourceRegisterType != null; + + //TODO: ensure sourceRegisterType is a Reference type? + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + assert arrayRegisterType != null; + + if (arrayRegisterType.category != RegisterType.Category.Null) { + //don't check the source type against the array type, just make sure it is an array of reference types + + if (arrayRegisterType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", + arrayRegisterType.category.toString())); + } + + assert arrayRegisterType.type != null; + if (arrayRegisterType.type.getClassType().charAt(0) != '[') { + throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", + arrayRegisterType.type.getClassType())); + } + + assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; + ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; + + ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); + char elementTypePrefix = elementClassDef.getClassType().charAt(0); + if (elementTypePrefix != 'L' && elementTypePrefix != '[') { + throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + + "array type for the instruction.", arrayRegisterType.type.getClassType())); + } + } + } + + private void analyze32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(instructionCategory, null)); + } + + private void verify32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), + ReferenceOrUninitThisCategories); + + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + if (objectRegisterType.category != RegisterType.Category.Null && + !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { + throw new ValidationException(String.format("Cannot access field %s through type %s", + field.getFieldString(), objectRegisterType.type.getClassType())); + } + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + } + + private void analyzeIgetWideObject(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); + } + + private void verifyIgetWide(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), + ReferenceOrUninitThisCategories); + + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + if (objectRegisterType.category != RegisterType.Category.Null && + !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { + throw new ValidationException(String.format("Cannot access field %s through type %s", + field.getFieldString(), objectRegisterType.type.getClassType())); + } + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (!WideLowCategories.contains(fieldType.category)) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + } + + private void verifyIgetObject(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), + ReferenceOrUninitThisCategories); + + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + if (objectRegisterType.category != RegisterType.Category.Null && + !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { + throw new ValidationException(String.format("Cannot access field %s through type %s", + field.getFieldString(), objectRegisterType.type.getClassType())); + } + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (fieldType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + } + + private void verify32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), + ReferenceOrUninitThisCategories); + + RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); + assert sourceRegisterType != null; + + //per CodeVerify.c in dalvik: + //java generates synthetic functions that write byte values into boolean fields + if (sourceRegisterType.category == RegisterType.Category.Byte && + instructionCategory == RegisterType.Category.Boolean) { + + sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null); + } + + RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); + if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { + throw new ValidationException(String.format("Cannot use %s with source register type %s.", + analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); + } + + + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + if (objectRegisterType.category != RegisterType.Category.Null && + !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { + throw new ValidationException(String.format("Cannot access field %s through type %s", + field.getFieldString(), objectRegisterType.type.getClassType())); + } + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + } + + private void verifyIputWide(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), + ReferenceOrUninitThisCategories); + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); + + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + if (objectRegisterType.category != RegisterType.Category.Null && + !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { + throw new ValidationException(String.format("Cannot access field %s through type %s", + field.getFieldString(), objectRegisterType.type.getClassType())); + } + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (!WideLowCategories.contains(fieldType.category)) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + } + + private void verifyIputObject(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), + ReferenceOrUninitThisCategories); + + RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), + ReferenceCategories); + + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + if (objectRegisterType.category != RegisterType.Category.Null && + !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { + throw new ValidationException(String.format("Cannot access field %s through type %s", + field.getFieldString(), objectRegisterType.type.getClassType())); + } + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (fieldType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + + if (sourceRegisterType.category != RegisterType.Category.Null && + !fieldType.type.isInterface() && + !sourceRegisterType.type.extendsClass(fieldType.type)) { + + throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s", + sourceRegisterType.type.getClassType(), fieldType.type.getClassType())); + } + } + + private void analyze32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(instructionCategory, null)); + } + + private void verify32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + } + + private void analyzeSgetWideObject(AnalyzedInstruction analyzedInstruction) { + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); + } + + private void verifySgetWide(AnalyzedInstruction analyzedInstruction) { + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + + if (fieldType.category != RegisterType.Category.LongLo && + fieldType.category != RegisterType.Category.DoubleLo) { + + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + } + + private void verifySgetObject(AnalyzedInstruction analyzedInstruction) { + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (fieldType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + } + + private void verify32BitPrimitiveSput(AnalyzedInstruction analyzedInstruction, + RegisterType.Category instructionCategory) { + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + + RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); + assert sourceRegisterType != null; + + //per CodeVerify.c in dalvik: + //java generates synthetic functions that write byte values into boolean fields + if (sourceRegisterType.category == RegisterType.Category.Byte && + instructionCategory == RegisterType.Category.Boolean) { + + sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null); + } + + RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); + if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { + throw new ValidationException(String.format("Cannot use %s with source register type %s.", + analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); + } + + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + } + + private void verifySputWide(AnalyzedInstruction analyzedInstruction) { + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); + + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (!WideLowCategories.contains(fieldType.category)) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + } + + private void verifySputObject(AnalyzedInstruction analyzedInstruction) { + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + + RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), + ReferenceCategories); + + //TODO: check access + Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); + assert referencedItem instanceof FieldIdItem; + FieldIdItem field = (FieldIdItem)referencedItem; + + RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); + + if (fieldType.category != RegisterType.Category.Reference) { + throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + + "for the instruction.", analyzedInstruction.instruction.opcode.name, + field.getFieldString())); + } + + if (sourceRegisterType.category != RegisterType.Category.Null && + !fieldType.type.isInterface() && + !sourceRegisterType.type.extendsClass(fieldType.type)) { + + throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s", + sourceRegisterType.type.getClassType(), fieldType.type.getClassType())); + } + } + + private void analyzeInvokeDirect(AnalyzedInstruction analyzedInstruction) { + FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; + analyzeInvokeDirectCommon(analyzedInstruction, new Format35cRegisterIterator(instruction)); + } + + private void verifyInvoke(AnalyzedInstruction analyzedInstruction, int invokeType) { + FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; + verifyInvokeCommon(analyzedInstruction, false, invokeType, new Format35cRegisterIterator(instruction)); + } + + private void analyzeInvokeDirectRange(AnalyzedInstruction analyzedInstruction) { + RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; + analyzeInvokeDirectCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction)); + } + + private void verifyInvokeRange(AnalyzedInstruction analyzedInstruction, int invokeType) { + RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; + verifyInvokeCommon(analyzedInstruction, true, invokeType, new Format3rcRegisterIterator(instruction)); + } + + private static final int INVOKE_VIRTUAL = 0x01; + private static final int INVOKE_SUPER = 0x02; + private static final int INVOKE_DIRECT = 0x04; + private static final int INVOKE_INTERFACE = 0x08; + private static final int INVOKE_STATIC = 0x10; + + private void analyzeInvokeDirectCommon(AnalyzedInstruction analyzedInstruction, RegisterIterator registers) { + //the only time that an invoke instruction changes a register type is when using invoke-direct on a + //constructor () method, which changes the uninitialized reference (and any register that the same + //uninit reference has been copied to) to an initialized reference + + InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; + + Item item = instruction.getReferencedItem(); + assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM; + MethodIdItem methodIdItem = (MethodIdItem)item; + + if (!methodIdItem.getMethodName().getStringValue().equals("")) { + return; + } + + RegisterType objectRegisterType; + //the object register is always the first register + int objectRegister = registers.getRegister(); + + objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister); + assert objectRegisterType != null; + + if (objectRegisterType.category != RegisterType.Category.UninitRef && + objectRegisterType.category != RegisterType.Category.UninitThis) { + return; + } + + setPostRegisterTypeAndPropagateChanges(analyzedInstruction, objectRegister, + RegisterType.getRegisterType(RegisterType.Category.Reference, objectRegisterType.type)); + + for (int i=0; i method %s on uninitialized " + + "reference type %s", methodIdItem.getMethodString(), + objectRegisterType.type.getClassType())); + } + } else if (objectRegisterType.category == RegisterType.Category.Reference) { + if (isInit) { + throw new ValidationException(String.format("Cannot invoke %s on initialized reference type %s", + methodIdItem.getMethodString(), objectRegisterType.type.getClassType())); + } + } else if (objectRegisterType.category == RegisterType.Category.Null) { + if (isInit) { + throw new ValidationException(String.format("Cannot invoke %s on a null reference", + methodIdItem.getMethodString())); + } + } + else { + throw new ValidationException(String.format("Cannot invoke %s on non-reference type %s", + methodIdItem.getMethodString(), objectRegisterType.toString())); + } + + if (isInit) { + if (objectRegisterType.type.getSuperclass() == methodClassDef) { + if (!encodedMethod.method.getMethodName().getStringValue().equals("")) { + throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " + + "match the method type exactly", methodIdItem.getMethodString(), + objectRegisterType.type.getClassType())); + } + } + } + + if ((invokeType & INVOKE_INTERFACE) == 0 && objectRegisterType.category != RegisterType.Category.Null && + !objectRegisterType.type.extendsClass(methodClassDef)) { + + throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " + + "does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(), + methodClassDef.getClassType())); + } + } + + if (typeListItem != null) { + List parameterTypes = typeListItem.getTypes(); + int parameterTypeIndex = 0; + while (!registers.pastEnd()) { + assert parameterTypeIndex < parameterTypes.size(); + RegisterType parameterType = + RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex)); + + int register = registers.getRegister(); + + RegisterType parameterRegisterType; + if (WideLowCategories.contains(parameterType.category)) { + parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories); + + if (!registers.moveNext()) { + throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d", + parameterTypeIndex+1)); + } + int nextRegister = registers.getRegister(); + + if (nextRegister != register + 1) { + throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " + + "must be consecutive.", register, nextRegister)); + } + } else { + parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register); + } + + assert parameterRegisterType != null; + + if (!parameterRegisterType.canBeAssignedTo(parameterType)) { + throw new ValidationException( + String.format("Invalid register type %s for parameter %d %s.", + parameterRegisterType.toString(), parameterTypeIndex+1, + parameterType.toString())); + } + + parameterTypeIndex++; + registers.moveNext(); + } + } + } + + private void analyzeUnaryOp(AnalyzedInstruction analyzedInstruction, RegisterType.Category destRegisterCategory) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(destRegisterCategory, null)); + } + + private void verifyUnaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSourceCategories) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSourceCategories); + } + + private void analyzeBinaryOp(AnalyzedInstruction analyzedInstruction, RegisterType.Category destRegisterCategory, + boolean checkForBoolean) { + if (checkForBoolean) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + RegisterType source1RegisterType = + analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + RegisterType source2RegisterType = + analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC()); + + if (BooleanCategories.contains(source1RegisterType.category) && + BooleanCategories.contains(source2RegisterType.category)) { + + destRegisterCategory = RegisterType.Category.Boolean; + } + } + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(destRegisterCategory, null)); + } + + private void verifyBinaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories, + EnumSet validSource2Categories) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSource1Categories); + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validSource2Categories); + } + + private void analyzeBinary2AddrOp(AnalyzedInstruction analyzedInstruction, + RegisterType.Category destRegisterCategory, boolean checkForBoolean) { + if (checkForBoolean) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType source1RegisterType = + analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); + RegisterType source2RegisterType = + analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + + if (BooleanCategories.contains(source1RegisterType.category) && + BooleanCategories.contains(source2RegisterType.category)) { + + destRegisterCategory = RegisterType.Category.Boolean; + } + } + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(destRegisterCategory, null)); + } + + private void verifyBinary2AddrOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories, + EnumSet validSource2Categories) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), validSource1Categories); + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSource2Categories); + } + + private void analyzeLiteralBinaryOp(AnalyzedInstruction analyzedInstruction, + RegisterType.Category destRegisterCategory, boolean checkForBoolean) { + if (checkForBoolean) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType sourceRegisterType = + analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + + if (BooleanCategories.contains(sourceRegisterType.category)) { + long literal = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral(); + if (literal == 0 || literal == 1) { + destRegisterCategory = RegisterType.Category.Boolean; + } + } + } + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(destRegisterCategory, null)); + } + + private void verifyLiteralBinaryOp(AnalyzedInstruction analyzedInstruction) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); + } + + private RegisterType.Category getDestTypeForLiteralShiftRight(AnalyzedInstruction analyzedInstruction, + boolean signedShift) { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), + Primitive32BitCategories); + long literalShift = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral(); + + if (literalShift == 0) { + return sourceRegisterType.category; + } + + RegisterType.Category destRegisterCategory; + if (!signedShift) { + destRegisterCategory = RegisterType.Category.Integer; + } else { + destRegisterCategory = sourceRegisterType.category; + } + + if (literalShift >= 32) { + //TODO: add warning + return destRegisterCategory; + } + + switch (sourceRegisterType.category) { + case Integer: + case Float: + if (!signedShift) { + if (literalShift > 24) { + return RegisterType.Category.PosByte; + } + if (literalShift >= 16) { + return RegisterType.Category.Char; + } + } else { + if (literalShift >= 24) { + return RegisterType.Category.Byte; + } + if (literalShift >= 16) { + return RegisterType.Category.Short; + } + } + break; + case Short: + if (signedShift && literalShift >= 8) { + return RegisterType.Category.Byte; + } + break; + case PosShort: + if (literalShift >= 8) { + return RegisterType.Category.PosByte; + } + break; + case Char: + if (literalShift > 8) { + return RegisterType.Category.PosByte; + } + break; + case Byte: + break; + case PosByte: + return RegisterType.Category.PosByte; + case Null: + case One: + case Boolean: + return RegisterType.Category.Null; + default: + assert false; + } + + return destRegisterCategory; + } + + + private void analyzeExecuteInline(AnalyzedInstruction analyzedInstruction) { + if (deodexUtil == null) { + throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing"); + } + + Instruction35mi instruction = (Instruction35mi)analyzedInstruction.instruction; + + DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction); + MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem(deodexUtil); + if (inlineMethodIdItem == null) { + throw new ValidationException(String.format("Cannot load inline method with index %d", + instruction.getInlineIndex())); + } + + Opcode deodexedOpcode = null; + switch (inlineMethod.methodType) { + case DeodexUtil.Direct: + deodexedOpcode = Opcode.INVOKE_DIRECT; + break; + case DeodexUtil.Static: + deodexedOpcode = Opcode.INVOKE_STATIC; + break; + case DeodexUtil.Virtual: + deodexedOpcode = Opcode.INVOKE_VIRTUAL; + break; + default: + assert false; + } + + Instruction35c deodexedInstruction = new Instruction35c(deodexedOpcode, instruction.getRegCount(), + instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(), + instruction.getRegisterG(), instruction.getRegisterA(), inlineMethodIdItem); + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + analyzeInstruction(analyzedInstruction); + } + + private void analyzeExecuteInlineRange(AnalyzedInstruction analyzedInstruction) { + if (deodexUtil == null) { + throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing"); + } + + Instruction3rmi instruction = (Instruction3rmi)analyzedInstruction.instruction; + + DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction); + MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem(deodexUtil); + if (inlineMethodIdItem == null) { + throw new ValidationException(String.format("Cannot load inline method with index %d", + instruction.getInlineIndex())); + } + + Opcode deodexedOpcode = null; + switch (inlineMethod.methodType) { + case DeodexUtil.Direct: + deodexedOpcode = Opcode.INVOKE_DIRECT_RANGE; + break; + case DeodexUtil.Static: + deodexedOpcode = Opcode.INVOKE_STATIC_RANGE; + break; + case DeodexUtil.Virtual: + deodexedOpcode = Opcode.INVOKE_VIRTUAL_RANGE; + break; + default: + assert false; + } + + Instruction3rc deodexedInstruction = new Instruction3rc(deodexedOpcode, (short)instruction.getRegCount(), + instruction.getStartRegister(), inlineMethodIdItem); + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + analyzeInstruction(analyzedInstruction); + } + + private void analyzeInvokeDirectEmpty(AnalyzedInstruction analyzedInstruction) { + analyzeInvokeDirectEmpty(analyzedInstruction, true); + } + + private void analyzeInvokeDirectEmpty(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { + Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction; + + Instruction35c deodexedInstruction = new Instruction35c(Opcode.INVOKE_DIRECT, instruction.getRegCount(), + instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(), + instruction.getRegisterG(), instruction.getRegisterA(), instruction.getReferencedItem()); + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + if (analyzeResult) { + analyzeInstruction(analyzedInstruction); + } + } + + private void analyzeInvokeObjectInitRange(AnalyzedInstruction analyzedInstruction) { + analyzeInvokeObjectInitRange(analyzedInstruction, true); + } + + private void analyzeInvokeObjectInitRange(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { + Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction; + + Instruction3rc deodexedInstruction = new Instruction3rc(Opcode.INVOKE_DIRECT_RANGE, + (short)instruction.getRegCount(), instruction.getStartRegister(), instruction.getReferencedItem()); + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + if (analyzeResult) { + analyzeInstruction(analyzedInstruction); + } + } + + private boolean analyzeIputIgetQuick(AnalyzedInstruction analyzedInstruction) { + Instruction22cs instruction = (Instruction22cs)analyzedInstruction.instruction; + + int fieldOffset = instruction.getFieldOffset(); + RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), + ReferenceOrUninitCategories); + + if (objectRegisterType.category == RegisterType.Category.Null) { + return false; + } + + FieldIdItem fieldIdItem = deodexUtil.lookupField(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)); + } + + String fieldType = fieldIdItem.getFieldType().getTypeDescriptor(); + + Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType, instruction.opcode); + + Instruction22c deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(), + (byte)instruction.getRegisterB(), fieldIdItem); + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + analyzeInstruction(analyzedInstruction); + + return true; + } + + private boolean analyzeInvokeVirtualQuick(AnalyzedInstruction analyzedInstruction, boolean isSuper, + boolean isRange) { + int methodIndex; + int objectRegister; + + + if (isRange) { + Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction; + methodIndex = instruction.getVtableIndex(); + objectRegister = instruction.getStartRegister(); + } else { + Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; + methodIndex = instruction.getVtableIndex(); + objectRegister = instruction.getRegisterD(); + } + + RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, objectRegister, + ReferenceOrUninitCategories); + + if (objectRegisterType.category == RegisterType.Category.Null) { + return false; + } + + MethodIdItem methodIdItem = null; + 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 (methodIdItem == null) { + //it's possible that the pre-odexed instruction had used the method from the current class instead + //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); + } + } else{ + methodIdItem = deodexUtil.lookupVirtualMethod(objectRegisterType.type, methodIndex); + } + + if (methodIdItem == null) { + throw new ValidationException(String.format("Could not resolve the method in class %s at index %d", + objectRegisterType.type.getClassType(), methodIndex)); + } + + + Instruction deodexedInstruction; + if (isRange) { + Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction; + Opcode opcode; + if (isSuper) { + opcode = Opcode.INVOKE_SUPER_RANGE; + } else { + opcode = Opcode.INVOKE_VIRTUAL_RANGE; + } + + deodexedInstruction = new Instruction3rc(opcode, (short)instruction.getRegCount(), + instruction.getStartRegister(), methodIdItem); + } else { + Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; + Opcode opcode; + if (isSuper) { + opcode = Opcode.INVOKE_SUPER; + } else { + opcode = Opcode.INVOKE_VIRTUAL; + } + + deodexedInstruction = new Instruction35c(opcode, instruction.getRegCount(), + instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(), + instruction.getRegisterG(), instruction.getRegisterA(), methodIdItem); + } + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + analyzeInstruction(analyzedInstruction); + + return true; + } + + private boolean analyzePutGetVolatile(AnalyzedInstruction analyzedInstruction) { + return analyzePutGetVolatile(analyzedInstruction, true); + } + + private boolean analyzePutGetVolatile(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { + FieldIdItem fieldIdItem = + (FieldIdItem)(((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem()); + + String fieldType = fieldIdItem.getFieldType().getTypeDescriptor(); + + Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType, + analyzedInstruction.instruction.opcode); + + Instruction deodexedInstruction; + + if (analyzedInstruction.instruction.opcode.isOdexedStaticVolatile()) { + SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; + deodexedInstruction = new Instruction21c(opcode, (byte)instruction.getRegisterA(), fieldIdItem); + } else { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(), + (byte)instruction.getRegisterB(), fieldIdItem); + } + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + if (analyzeResult) { + analyzeInstruction(analyzedInstruction); + } + return true; + } + + private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory, + RegisterType.Category instructionCategory) { + if (arrayFieldCategory == instructionCategory) { + return true; + } + + if ((arrayFieldCategory == RegisterType.Category.Integer && + instructionCategory == RegisterType.Category.Float) || + (arrayFieldCategory == RegisterType.Category.Float && + instructionCategory == RegisterType.Category.Integer)) { + return true; + } + return false; + } + + private static RegisterType getAndCheckSourceRegister(AnalyzedInstruction analyzedInstruction, int registerNumber, + EnumSet validCategories) { + assert registerNumber >= 0 && registerNumber < analyzedInstruction.postRegisterMap.length; + + RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNumber); + assert registerType != null; + + checkRegister(registerType, registerNumber, validCategories); + + if (validCategories == WideLowCategories) { + checkRegister(registerType, registerNumber, WideLowCategories); + checkWidePair(registerNumber, analyzedInstruction); + + RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNumber + 1); + assert secondRegisterType != null; + checkRegister(secondRegisterType, registerNumber+1, WideHighCategories); + } + + return registerType; + } + + private static void checkRegister(RegisterType registerType, int registerNumber, EnumSet validCategories) { + if (!validCategories.contains(registerType.category)) { + throw new ValidationException(String.format("Invalid register type %s for register v%d.", + registerType.toString(), registerNumber)); + } + } + + private static void checkWidePair(int registerNumber, AnalyzedInstruction analyzedInstruction) { + if (registerNumber + 1 >= analyzedInstruction.postRegisterMap.length) { + throw new ValidationException(String.format("v%d cannot be used as the first register in a wide register" + + "pair because it is the last register.", registerNumber)); + } + } + + private static interface RegisterIterator { + int getRegister(); + boolean moveNext(); + int getCount(); + boolean pastEnd(); + } + + private static class Format35cRegisterIterator implements RegisterIterator { + private final int registerCount; + private final int[] registers; + private int currentRegister = 0; + + public Format35cRegisterIterator(FiveRegisterInstruction instruction) { + registerCount = instruction.getRegCount(); + registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(), + instruction.getRegisterF(), instruction.getRegisterG(), + instruction.getRegisterA()}; + } + + public int getRegister() { + return registers[currentRegister]; + } + + public boolean moveNext() { + currentRegister++; + return !pastEnd(); + } + + public int getCount() { + return registerCount; + } + + public boolean pastEnd() { + return currentRegister >= registerCount; + } + } + + private static class Format3rcRegisterIterator implements RegisterIterator { + private final int startRegister; + private final int registerCount; + private int currentRegister = 0; + + public Format3rcRegisterIterator(RegisterRangeInstruction instruction) { + startRegister = instruction.getStartRegister(); + registerCount = instruction.getRegCount(); + } + + public int getRegister() { + return startRegister + currentRegister; + } + + public boolean moveNext() { + currentRegister++; + return !pastEnd(); + } + + public int getCount() { + return registerCount; + } + + public boolean pastEnd() { + return currentRegister >= registerCount; + } + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/OdexedFieldInstructionMapper.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/OdexedFieldInstructionMapper.java new file mode 100644 index 00000000..f15f0e51 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/OdexedFieldInstructionMapper.java @@ -0,0 +1,235 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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 org.jf.dexlib.Code.Opcode; + +public class OdexedFieldInstructionMapper { + private static Opcode[][][][] opcodeMap = new Opcode[][][][] { + //get opcodes + new Opcode[][][] { + //iget quick + new Opcode[][] { + //odexed + new Opcode[] { + /*Z*/ Opcode.IGET_QUICK, + /*B*/ Opcode.IGET_QUICK, + /*S*/ Opcode.IGET_QUICK, + /*C*/ Opcode.IGET_QUICK, + /*I,F*/ Opcode.IGET_QUICK, + /*J,D*/ Opcode.IGET_WIDE_QUICK, + /*L,[*/ Opcode.IGET_OBJECT_QUICK + }, + //deodexed + new Opcode[] { + /*Z*/ Opcode.IGET_BOOLEAN, + /*B*/ Opcode.IGET_BYTE, + /*S*/ Opcode.IGET_SHORT, + /*C*/ Opcode.IGET_CHAR, + /*I,F*/ Opcode.IGET, + /*J,D*/ Opcode.IGET_WIDE, + /*L,[*/ Opcode.IGET_OBJECT + } + }, + //iget volatile + new Opcode[][] { + //odexed + new Opcode[] { + /*Z*/ Opcode.IGET_VOLATILE, + /*B*/ Opcode.IGET_VOLATILE, + /*S*/ Opcode.IGET_VOLATILE, + /*C*/ Opcode.IGET_VOLATILE, + /*I,F*/ Opcode.IGET_VOLATILE, + /*J,D*/ Opcode.IGET_WIDE_VOLATILE, + /*L,[*/ Opcode.IGET_OBJECT_VOLATILE + }, + //deodexed + new Opcode[] { + /*Z*/ Opcode.IGET_BOOLEAN, + /*B*/ Opcode.IGET_BYTE, + /*S*/ Opcode.IGET_SHORT, + /*C*/ Opcode.IGET_CHAR, + /*I,F*/ Opcode.IGET, + /*J,D*/ Opcode.IGET_WIDE, + /*L,[*/ Opcode.IGET_OBJECT + } + }, + //sget volatile + new Opcode[][] { + //odexed + new Opcode[] { + /*Z*/ Opcode.SGET_VOLATILE, + /*B*/ Opcode.SGET_VOLATILE, + /*S*/ Opcode.SGET_VOLATILE, + /*C*/ Opcode.SGET_VOLATILE, + /*I,F*/ Opcode.SGET_VOLATILE, + /*J,D*/ Opcode.SGET_WIDE_VOLATILE, + /*L,[*/ Opcode.SGET_OBJECT_VOLATILE + }, + //deodexed + new Opcode[] { + /*Z*/ Opcode.SGET_BOOLEAN, + /*B*/ Opcode.SGET_BYTE, + /*S*/ Opcode.SGET_SHORT, + /*C*/ Opcode.SGET_CHAR, + /*I,F*/ Opcode.SGET, + /*J,D*/ Opcode.SGET_WIDE, + /*L,[*/ Opcode.SGET_OBJECT + } + } + }, + //put opcodes + new Opcode[][][] { + //iput quick + new Opcode[][] { + //odexed + new Opcode[] { + /*Z*/ Opcode.IPUT_QUICK, + /*B*/ Opcode.IPUT_QUICK, + /*S*/ Opcode.IPUT_QUICK, + /*C*/ Opcode.IPUT_QUICK, + /*I,F*/ Opcode.IPUT_QUICK, + /*J,D*/ Opcode.IPUT_WIDE_QUICK, + /*L,[*/ Opcode.IPUT_OBJECT_QUICK + }, + //deodexed + new Opcode[] { + /*Z*/ Opcode.IPUT_BOOLEAN, + /*B*/ Opcode.IPUT_BYTE, + /*S*/ Opcode.IPUT_SHORT, + /*C*/ Opcode.IPUT_CHAR, + /*I,F*/ Opcode.IPUT, + /*J,D*/ Opcode.IPUT_WIDE, + /*L,[*/ Opcode.IPUT_OBJECT + } + }, + //iput volatile + new Opcode[][] { + //odexed + new Opcode[] { + /*Z*/ Opcode.IPUT_VOLATILE, + /*B*/ Opcode.IPUT_VOLATILE, + /*S*/ Opcode.IPUT_VOLATILE, + /*C*/ Opcode.IPUT_VOLATILE, + /*I,F*/ Opcode.IPUT_VOLATILE, + /*J,D*/ Opcode.IPUT_WIDE_VOLATILE, + /*L,[*/ Opcode.IPUT_OBJECT_VOLATILE + }, + //deodexed + new Opcode[] { + /*Z*/ Opcode.IPUT_BOOLEAN, + /*B*/ Opcode.IPUT_BYTE, + /*S*/ Opcode.IPUT_SHORT, + /*C*/ Opcode.IPUT_CHAR, + /*I,F*/ Opcode.IPUT, + /*J,D*/ Opcode.IPUT_WIDE, + /*L,[*/ Opcode.IPUT_OBJECT + } + }, + //sput volatile + new Opcode[][] { + //odexed + new Opcode[] { + /*Z*/ Opcode.SPUT_VOLATILE, + /*B*/ Opcode.SPUT_VOLATILE, + /*S*/ Opcode.SPUT_VOLATILE, + /*C*/ Opcode.SPUT_VOLATILE, + /*I,F*/ Opcode.SPUT_VOLATILE, + /*J,D*/ Opcode.SPUT_WIDE_VOLATILE, + /*L,[*/ Opcode.SPUT_OBJECT_VOLATILE + }, + //deodexed + new Opcode[] { + /*Z*/ Opcode.SPUT_BOOLEAN, + /*B*/ Opcode.SPUT_BYTE, + /*S*/ Opcode.SPUT_SHORT, + /*C*/ Opcode.SPUT_CHAR, + /*I,F*/ Opcode.SPUT, + /*J,D*/ Opcode.SPUT_WIDE, + /*L,[*/ Opcode.SPUT_OBJECT + } + } + } + }; + + private static int getTypeIndex(char type) { + switch (type) { + case 'Z': + return 0; + case 'B': + return 1; + case 'S': + return 2; + case 'C': + return 3; + case 'I': + case 'F': + return 4; + case 'J': + case 'D': + return 5; + case 'L': + case '[': + return 6; + default: + } + throw new RuntimeException(String.format("Unknown type %s: ", type)); + } + + private static int getOpcodeSubtype(Opcode opcode) { + if (opcode.isOdexedInstanceQuick()) { + return 0; + } else if (opcode.isOdexedInstanceVolatile()) { + return 1; + } else if (opcode.isOdexedStaticVolatile()) { + return 2; + } + throw new RuntimeException(String.format("Not an odexed field access opcode: %s", opcode.name)); + } + + static Opcode getAndCheckDeodexedOpcodeForOdexedOpcode(String fieldType, Opcode odexedOpcode) { + int opcodeType = odexedOpcode.setsRegister()?0:1; + int opcodeSubType = getOpcodeSubtype(odexedOpcode); + int typeIndex = getTypeIndex(fieldType.charAt(0)); + + Opcode correctOdexedOpcode, deodexedOpcode; + + correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex]; + deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex]; + + if (correctOdexedOpcode != odexedOpcode) { + throw new ValidationException(String.format("Incorrect field type \"%s\" for %s", fieldType, + odexedOpcode.name)); + } + + return deodexedOpcode; + } +} + + diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java new file mode 100644 index 00000000..ed67732a --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java @@ -0,0 +1,322 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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 org.jf.dexlib.TypeIdItem; + +import java.io.IOException; +import java.io.Writer; +import java.util.HashMap; + +import static org.jf.dexlib.Code.Analysis.ClassPath.ClassDef; + +public class RegisterType { + private final static HashMap internedRegisterTypes = + new HashMap(); + + public final Category category; + public final ClassDef type; + + private RegisterType(Category category, ClassDef type) { + assert ((category == Category.Reference || category == Category.UninitRef || category == Category.UninitThis) && + type != null) || + ((category != Category.Reference && category != Category.UninitRef && category != Category.UninitThis) && + type == null); + + this.category = category; + this.type = type; + } + + @Override + public String toString() { + return "(" + category.name() + (type==null?"":("," + type.getClassType())) + ")"; + } + + public void writeTo(Writer writer) throws IOException { + writer.write('('); + writer.write(category.name()); + if (type != null) { + writer.write(','); + writer.write(type.getClassType()); + } + writer.write(')'); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RegisterType that = (RegisterType) o; + + if (category != that.category) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = category.hashCode(); + result = 31 * result + (type != null ? type.hashCode() : 0); + return result; + } + + public static enum Category { + //the Unknown category denotes a register type that hasn't been determined yet + Unknown, + Uninit, + Null, + One, + Boolean, + Byte, + PosByte, + Short, + PosShort, + Char, + Integer, + Float, + LongLo, + LongHi, + DoubleLo, + DoubleHi, + //the UninitRef category is used after a new-instance operation, and before the corresponding is called + UninitRef, + //the UninitThis category is used the "this" register inside an method, before the superclass' + //method is called + UninitThis, + Reference, + //This is used when there are multiple incoming execution paths that have incompatible register types. For + //example if the register's type is an Integer on one incomming code path, but is a Reference type on another + //incomming code path. There is no register type that can hold either an Integer or a Reference. + Conflicted; + + //this table is used when merging register types. For example, if a particular register can be either a Byte + //or a Char, then the "merged" type of that register would be Integer, because it is the "smallest" type can + //could hold either type of value. + protected static Category[][] mergeTable = + { + /* Unknown Uninit Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted*/ + /*Unknown*/ {Unknown, Uninit, Null, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, LongLo, LongHi, DoubleLo, DoubleHi, UninitRef, UninitThis, Reference, Conflicted}, + /*Uninit*/ {Uninit, Uninit, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*Null*/ {Null, Conflicted, Null, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted}, + /*One*/ {One, Conflicted, Boolean, One, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*Boolean*/ {Boolean, Conflicted, Boolean, Boolean, Boolean, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*Byte*/ {Byte, Conflicted, Byte, Byte, Byte, Byte, Byte, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*PosByte*/ {PosByte, Conflicted, PosByte, PosByte, PosByte, Byte, PosByte, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*Short*/ {Short, Conflicted, Short, Short, Short, Short, Short, Short, Short, Integer, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*PosShort*/ {PosShort, Conflicted, PosShort, PosShort, PosShort, Short, PosShort, Short, PosShort, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*Char*/ {Char, Conflicted, Char, Char, Char, Integer, Char, Integer, Char, Char, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*Integer*/ {Integer, Conflicted, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*Float*/ {Float, Conflicted, Float, Float, Float, Float, Float, Float, Float, Float, Integer, Float, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*LongLo*/ {LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, LongLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*LongHi*/ {LongHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, LongHi, Conflicted, Conflicted, Conflicted, Conflicted}, + /*DoubleLo*/ {DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*DoubleHi*/ {DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, DoubleHi, Conflicted, Conflicted, Conflicted, Conflicted}, + /*UninitRef*/ {UninitRef, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}, + /*UninitThis*/ {UninitThis, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, UninitThis, Conflicted, Conflicted}, + /*Reference*/ {Reference, Conflicted, Reference, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted}, + /*Conflicted*/ {Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted} + }; + + //this table is used to denote whether a given value type can be assigned to a "slot" of a certain type. For + //example, to determine if you can assign a Boolean value to a particular array "slot", where the array is an + //array of Integers, you would look up assignmentTable[Boolean.ordinal()][Integer.ordinal()] + //Note that not all slot types in the table are expected to be used. For example, it doesn't make sense to + //check if a value can be assigned to an uninitialized reference slot - because there is no such thing. + protected static boolean[][] assigmentTable = + { + /* Unknown Uninit Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef UninitThis Reference Conflicted |slot type*/ + /*Unknown*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + /*Uninit*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + /*Null*/ {false, false, true, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, true, false}, + /*One*/ {false, false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false}, + /*Boolean*/ {false, false, false, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false}, + /*Byte*/ {false, false, false, false, false, true, false, true, true, false, true, true, false, false, false, false, false, false, false, false}, + /*PosByte*/ {false, false, false, false, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false}, + /*Short*/ {false, false, false, false, false, false, false, true, false, false, true, true, false, false, false, false, false, false, false, false}, + /*PosShort*/ {false, false, false, false, false, false, false, true, true, true, true, true, false, false, false, false, false, false, false, false}, + /*Char*/ {false, false, false, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false, false}, + /*Integer*/ {false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false}, + /*Float*/ {false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false}, + /*LongLo*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false}, + /*LongHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false}, + /*DoubleLo*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false}, + /*DoubleHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false}, + /*UninitRef*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + /*UninitThis*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + /*Reference*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, + /*Conflicted*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false} + /*----------*/ + /*value type*/ + }; + + } + + public static RegisterType getRegisterTypeForType(String type) { + switch (type.charAt(0)) { + case 'V': + throw new ValidationException("The V type can only be used as a method return type"); + case 'Z': + return getRegisterType(Category.Boolean, null); + case 'B': + return getRegisterType(Category.Byte, null); + case 'S': + return getRegisterType(Category.Short, null); + case 'C': + return getRegisterType(Category.Char, null); + case 'I': + return getRegisterType(Category.Integer, null); + case 'F': + return getRegisterType(Category.Float, null); + case 'J': + return getRegisterType(Category.LongLo, null); + case 'D': + return getRegisterType(Category.DoubleLo, null); + case 'L': + case '[': + return getRegisterType(Category.Reference, ClassPath.getClassDef(type)); + default: + throw new RuntimeException("Invalid type: " + type); + } + } + + public static RegisterType getRegisterTypeForTypeIdItem(TypeIdItem typeIdItem) { + return getRegisterTypeForType(typeIdItem.getTypeDescriptor()); + } + + public static RegisterType getWideRegisterTypeForTypeIdItem(TypeIdItem typeIdItem, boolean firstRegister) { + if (typeIdItem.getRegisterCount() == 1) { + throw new RuntimeException("Cannot use this method for non-wide register type: " + + typeIdItem.getTypeDescriptor()); + } + + switch (typeIdItem.getTypeDescriptor().charAt(0)) { + case 'J': + if (firstRegister) { + return getRegisterType(Category.LongLo, null); + } else { + return getRegisterType(Category.LongHi, null); + } + case 'D': + if (firstRegister) { + return getRegisterType(Category.DoubleLo, null); + } else { + return getRegisterType(Category.DoubleHi, null); + } + default: + throw new RuntimeException("Invalid type: " + typeIdItem.getTypeDescriptor()); + } + } + + public static RegisterType getRegisterTypeForLiteral(long literalValue) { + if (literalValue < -32768) { + return getRegisterType(Category.Integer, null); + } + if (literalValue < -128) { + return getRegisterType(Category.Short, null); + } + if (literalValue < 0) { + return getRegisterType(Category.Byte, null); + } + if (literalValue == 0) { + return getRegisterType(Category.Null, null); + } + if (literalValue == 1) { + return getRegisterType(Category.One, null); + } + if (literalValue < 128) { + return getRegisterType(Category.PosByte, null); + } + if (literalValue < 32768) { + return getRegisterType(Category.PosShort, null); + } + if (literalValue < 65536) { + return getRegisterType(Category.Char, null); + } + return getRegisterType(Category.Integer, null); + } + + public RegisterType merge(RegisterType type) { + if (type == null || type == this) { + return this; + } + + Category mergedCategory = Category.mergeTable[this.category.ordinal()][type.category.ordinal()]; + + ClassDef mergedType = null; + if (mergedCategory == Category.Reference) { + if (this.type instanceof ClassPath.UnresolvedClassDef || + type.type instanceof ClassPath.UnresolvedClassDef) { + mergedType = ClassPath.getUnresolvedObjectClassDef(); + } else { + mergedType = ClassPath.getCommonSuperclass(this.type, type.type); + } + } else if (mergedCategory == Category.UninitRef || mergedCategory == Category.UninitThis) { + if (this.category == Category.Unknown) { + return type; + } + assert type.category == Category.Unknown; + return this; + } + return RegisterType.getRegisterType(mergedCategory, mergedType); + } + + public boolean canBeAssignedTo(RegisterType slotType) { + if (Category.assigmentTable[this.category.ordinal()][slotType.category.ordinal()]) { + if (this.category == Category.Reference && slotType.category == Category.Reference) { + if (!slotType.type.isInterface()) { + return this.type.extendsClass(slotType.type); + } + //for verification, we assume all objects implement all interfaces, so we don't verify the type if + //slotType is an interface + } + return true; + } + return false; + } + + public static RegisterType getUnitializedReference(ClassDef classType) { + //We always create a new RegisterType instance for an uninit ref. Each unique uninit RegisterType instance + //is used to track a specific uninitialized reference, so that if multiple registers contain the same + //uninitialized reference, then they can all be upgraded to an initialized reference when the appropriate + // is invoked + return new RegisterType(Category.UninitRef, classType); + } + + public static RegisterType getRegisterType(Category category, ClassDef classType) { + RegisterType newRegisterType = new RegisterType(category, classType); + RegisterType internedRegisterType = internedRegisterTypes.get(newRegisterType); + if (internedRegisterType == null) { + internedRegisterTypes.put(newRegisterType, newRegisterType); + return newRegisterType; + } + return internedRegisterType; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/SyntheticAccessorResolver.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/SyntheticAccessorResolver.java new file mode 100644 index 00000000..3494b070 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/SyntheticAccessorResolver.java @@ -0,0 +1,138 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2011 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 org.jf.dexlib.*; +import org.jf.dexlib.Code.Format.Instruction22c; +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Util.AccessFlags; + +import java.util.HashMap; + +public class SyntheticAccessorResolver { + public static final int METHOD = 0; + public static final int GETTER = 1; + public static final int SETTER = 2; + + private final DexFileClassMap classMap; + private final HashMap resolvedAccessors = new HashMap(); + + public SyntheticAccessorResolver(DexFile dexFile) { + classMap = new DexFileClassMap(dexFile); + } + + public static boolean looksLikeSyntheticAccessor(MethodIdItem methodIdItem) { + return methodIdItem.getMethodName().getStringValue().startsWith("access$"); + } + + public AccessedMember getAccessedMember(MethodIdItem methodIdItem) { + AccessedMember accessedMember = resolvedAccessors.get(methodIdItem); + if (accessedMember != null) { + return accessedMember; + } + + ClassDefItem classDefItem = classMap.getClassDefByType(methodIdItem.getContainingClass()); + if (classDefItem == null) { + return null; + } + + ClassDataItem classDataItem = classDefItem.getClassData(); + if (classDataItem == null) { + return null; + } + + ClassDataItem.EncodedMethod encodedMethod = classDataItem.findDirectMethodByMethodId(methodIdItem); + if (encodedMethod == null) { + return null; + } + + //A synthetic accessor will be marked synthetic + if ((encodedMethod.accessFlags & AccessFlags.SYNTHETIC.getValue()) == 0) { + return null; + } + + Instruction[] instructions = encodedMethod.codeItem.getInstructions(); + + //TODO: add support for odexed formats + switch (instructions[0].opcode.format) { + case Format35c: + case Format3rc: { + //a synthetic method access should be either 2 or 3 instructions, depending on if the method returns + //anything or not + if (instructions.length < 2 || instructions.length > 3) { + return null; + } + InstructionWithReference instruction = (InstructionWithReference)instructions[0]; + MethodIdItem referencedMethodIdItem = (MethodIdItem)instruction.getReferencedItem(); + + accessedMember = new AccessedMember(METHOD, referencedMethodIdItem); + resolvedAccessors.put(methodIdItem, accessedMember); + return accessedMember; + } + case Format22c: { + //a synthetic field access should be exactly 2 instructions. The set/put, and then the return + if (instructions.length != 2) { + return null; + } + Instruction22c instruction = (Instruction22c)instructions[0]; + FieldIdItem referencedFieldIdItem = (FieldIdItem)instruction.getReferencedItem(); + + if (instruction.opcode.setsRegister() || instruction.opcode.setsWideRegister()) { + accessedMember = new AccessedMember(GETTER, referencedFieldIdItem); + } else { + accessedMember = new AccessedMember(SETTER, referencedFieldIdItem); + } + + resolvedAccessors.put(methodIdItem, accessedMember); + return accessedMember; + } + default: + return null; + } + } + + public static class AccessedMember { + private final int accessedMemberType; + private final Item accessedMember; + + public AccessedMember(int accessedMemberType, Item accessedMember) { + this.accessedMemberType = accessedMemberType; + this.accessedMember = accessedMember; + } + + public int getAccessedMemberType() { + return accessedMemberType; + } + + public Item getAccessedMember() { + return accessedMember; + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java new file mode 100644 index 00000000..32c13cb4 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java @@ -0,0 +1,52 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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 org.jf.dexlib.Util.ExceptionWithContext; + +public class ValidationException extends ExceptionWithContext { + private int codeAddress; + + public ValidationException(int codeAddress, String errorMessage) { + super(errorMessage); + this.codeAddress = codeAddress; + } + + public ValidationException(String errorMessage) { + super(errorMessage); + } + + public void setCodeAddress(int codeAddress) { + this.codeAddress = codeAddress; + } + + public int getCodeAddress() { + return codeAddress; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/EncodedLiteralInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/EncodedLiteralInstruction.java new file mode 100644 index 00000000..a772dfea --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/EncodedLiteralInstruction.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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; + +public interface EncodedLiteralInstruction extends LiteralInstruction { + long getDecodedLiteral(); +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/FiveRegisterInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/FiveRegisterInstruction.java new file mode 100644 index 00000000..b9ff0661 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/FiveRegisterInstruction.java @@ -0,0 +1,37 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +public interface FiveRegisterInstruction extends InvokeInstruction { + byte getRegisterA(); + byte getRegisterD(); + byte getRegisterE(); + byte getRegisterF(); + byte getRegisterG(); +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java new file mode 100644 index 00000000..5de88f40 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java @@ -0,0 +1,152 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +import java.util.Iterator; + +public class ArrayDataPseudoInstruction extends Instruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private int elementWidth; + private byte[] encodedValues; + + @Override + public int getSize(int codeAddress) { + return ((encodedValues.length + 1)/2) + 4 + (codeAddress % 2); + } + + public ArrayDataPseudoInstruction(int elementWidth, byte[] encodedValues) { + super(Opcode.NOP); + + if (encodedValues.length % elementWidth != 0) { + throw new RuntimeException("There are not a whole number of " + elementWidth + " byte elements"); + } + + this.elementWidth = elementWidth; + this.encodedValues = encodedValues; + } + + public ArrayDataPseudoInstruction(byte[] buffer, int bufferIndex) { + super(Opcode.NOP); + + byte opcodeByte = buffer[bufferIndex]; + if (opcodeByte != 0x00) { + throw new RuntimeException("Invalid opcode byte for an ArrayData pseudo-instruction"); + } + + byte subopcodeByte = buffer[bufferIndex+1]; + if (subopcodeByte != 0x03) { + throw new RuntimeException("Invalid sub-opcode byte for an ArrayData pseudo-instruction"); + } + + this.elementWidth = NumberUtils.decodeUnsignedShort(buffer, bufferIndex+2); + int elementCount = NumberUtils.decodeInt(buffer, bufferIndex+4); + this.encodedValues = new byte[elementCount * elementWidth]; + System.arraycopy(buffer, bufferIndex+8, encodedValues, 0, elementCount * elementWidth); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.alignTo(4); + + int elementCount = encodedValues.length / elementWidth; + + out.writeByte(0x00); + out.writeByte(0x03); + out.writeShort(elementWidth); + out.writeInt(elementCount); + out.write(encodedValues); + + //make sure we're written out an even number of bytes + out.alignTo(2); + } + + protected void annotateInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.annotate(getSize(currentCodeAddress)*2, "[0x" + Integer.toHexString(currentCodeAddress) + "] " + + "fill-array-data instruction"); + } + + public Format getFormat() { + return Format.ArrayData; + } + + public int getElementWidth() { + return elementWidth; + } + + public int getElementCount() { + return encodedValues.length / elementWidth; + } + + public static class ArrayElement { + public final byte[] buffer; + public int bufferIndex; + public final int elementWidth; + public ArrayElement(byte[] buffer, int elementWidth) { + this.buffer = buffer; + this.elementWidth = elementWidth; + } + } + + public Iterator getElements() { + return new Iterator() { + final int elementCount = getElementCount(); + int i=0; + int position=0; + final ArrayElement arrayElement = new ArrayElement(encodedValues, getElementWidth()); + + public boolean hasNext() { + return i 127) { + throw new RuntimeException("The address offset is out of range. It must be in [-128,-1] or [1, 127]"); + } + + out.writeByte(opcode.value); + out.writeByte(targetAddressOffset); + } + + public void updateTargetAddressOffset(int targetAddressOffset) { + this.targetAddressOffset = targetAddressOffset; + } + + public Format getFormat() { + return Format.Format10t; + } + + public int getTargetAddressOffset() { + return targetAddressOffset; + } + + private static class Factory implements InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction10t(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java new file mode 100644 index 00000000..46ea9686 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java @@ -0,0 +1,64 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; + +public class Instruction10x extends Instruction { + public static final InstructionFactory Factory = new Factory(); + + public Instruction10x(Opcode opcode) { + super(opcode); + } + + public Instruction10x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + assert (buffer[bufferIndex] & 0xFF) == opcode.value; + assert buffer[bufferIndex + 1] == 0x00; + } + + public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(0); + } + + public Format getFormat() { + return Format.Format10x; + } + + private static class Factory implements InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction10x(opcode, buffer, bufferIndex); + } + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java new file mode 100644 index 00000000..7fae04c3 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java @@ -0,0 +1,89 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.LiteralInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.SingleRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction11n extends Instruction implements SingleRegisterInstruction, LiteralInstruction { + public static final InstructionFactory Factory = new Factory(); + private byte regA; + private byte litB; + + public Instruction11n(Opcode opcode, byte regA, byte litB) { + super(opcode); + + if (regA >= 1 << 4) { + throw new RuntimeException("The register number must be less than v16"); + } + + if (litB < -(1 << 3) || + litB >= 1 << 3) { + throw new RuntimeException("The literal value must be between -8 and 7 inclusive"); + } + + this.regA = regA; + this.litB = litB; + } + + private Instruction11n(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + this.litB = NumberUtils.decodeHighSignedNibble(buffer[bufferIndex + 1]); + } + + public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte((litB << 4) | regA); + } + + public Format getFormat() { + return Format.Format11n; + } + + public int getRegisterA() { + return regA; + } + + public long getLiteral() { + return litB; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction11n(opcode, buffer, bufferIndex); + } + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java new file mode 100644 index 00000000..ca30c7b4 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java @@ -0,0 +1,76 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.SingleRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction11x extends Instruction implements SingleRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + + public Instruction11x(Opcode opcode, short regA) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + this.regA = (byte)regA; + } + + private Instruction11x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + } + + public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + } + + public Format getFormat() { + return Format.Format11x; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction11x(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java new file mode 100644 index 00000000..6d8d774d --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java @@ -0,0 +1,83 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.TwoRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction12x extends Instruction implements TwoRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private int regA; + private int regB; + + public Instruction12x(Opcode opcode, byte regA, byte regB) { + super(opcode); + + if (regA >= 1 << 4 || + regB >= 1 << 4) { + throw new RuntimeException("The register number must be less than v16"); + } + + this.regA = regA; + this.regB = regB; + } + + private Instruction12x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + } + + public void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte((regB << 4) | regA); + } + + public Format getFormat() { + return Format.Format12x; + } + + public int getRegisterA() { + return regA; + } + + public int getRegisterB() { + return regB; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction12x(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java new file mode 100644 index 00000000..75af9df7 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java @@ -0,0 +1,100 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2011 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.*; +import org.jf.dexlib.Code.*; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction20bc extends InstructionWithReference { + public static final Instruction.InstructionFactory Factory = new Factory(); + + private VerificationErrorType validationErrorType; + + public Instruction20bc(Opcode opcode, VerificationErrorType validationErrorType, Item referencedItem) { + super(opcode, referencedItem, getReferenceType(referencedItem)); + + this.validationErrorType = validationErrorType; + } + + private static ReferenceType getReferenceType(Item item) { + if (item instanceof TypeIdItem) { + return ReferenceType.type; + } + if (item instanceof FieldIdItem) { + return ReferenceType.field; + } + if (item instanceof MethodIdItem) { + return ReferenceType.method; + } + return null; + } + + private Instruction20bc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + + short val = NumberUtils.decodeUnsignedByte(buffer[bufferIndex+1]); + validationErrorType = VerificationErrorType.getValidationErrorType(val & 0x3f); + } + + protected ReferenceType readReferenceType(Opcode opcode, byte[] buffer, int bufferIndex) { + short val = NumberUtils.decodeUnsignedByte(buffer[bufferIndex+1]); + short referenceType = (short)(val >> 6); + return ReferenceType.fromValidationErrorReferenceType(referenceType); + } + + @Override + public Format getFormat() { + return Format.Format20bc; + } + + @Override + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + if(opcode == Opcode.CONST_STRING && getReferencedItem().getIndex() > 0xFFFF) { + throw new RuntimeException("String offset is too large for const-string. Use string-const/jumbo instead."); + } + + out.writeByte(opcode.value); + out.writeByte((this.getReferenceType().getValidationErrorReferenceType() << 6) & + validationErrorType.getValue()); + + out.writeShort(getReferencedItem().getIndex()); + } + + public VerificationErrorType getValidationErrorType() { + return validationErrorType; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction20bc(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java new file mode 100644 index 00000000..a92e1a8b --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java @@ -0,0 +1,94 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.OffsetInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction20t extends Instruction implements OffsetInstruction { + public static final InstructionFactory Factory = new Factory(); + private int targetAddressOffset; + + public Instruction20t(Opcode opcode, int offA) { + super(opcode); + this.targetAddressOffset = offA; + + if (targetAddressOffset == 0) { + throw new RuntimeException("The address offset cannot be 0. Use goto/32 instead."); + } + + //allow out of range address offsets here, so we have the option of replacing this instruction + //with goto/16 or goto/32 later + } + + private Instruction20t(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + assert buffer[bufferIndex] == opcode.value; + + this.targetAddressOffset = NumberUtils.decodeShort(buffer, bufferIndex+2); + assert targetAddressOffset != 0; + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + if (targetAddressOffset == 0) { + throw new RuntimeException("The address offset cannot be 0. Use goto/32 instead"); + } + + if (targetAddressOffset < -32768 || targetAddressOffset > 32767) { + throw new RuntimeException("The address offset is out of range. It must be in [-32768,-1] or [1, 32768]"); + } + + out.writeByte(opcode.value); + out.writeByte(0x00); + out.writeShort(targetAddressOffset); + } + + public void updateTargetAddressOffset(int targetAddressOffset) { + this.targetAddressOffset = targetAddressOffset; + } + + public Format getFormat() { + return Format.Format20t; + } + + public int getTargetAddressOffset() { + return targetAddressOffset; + } + + private static class Factory implements InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction20t(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java new file mode 100644 index 00000000..411dc724 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java @@ -0,0 +1,111 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.SingleRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.TypeIdItem; +import org.jf.dexlib.Util.AnnotatedOutput; + +public class Instruction21c extends InstructionWithReference implements SingleRegisterInstruction, + InstructionWithJumboVariant { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + + public Instruction21c(Opcode opcode, short regA, Item referencedItem) { + super(opcode, referencedItem); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + if (opcode == Opcode.NEW_INSTANCE) { + assert referencedItem instanceof TypeIdItem; + if (((TypeIdItem)referencedItem).getTypeDescriptor().charAt(0) != 'L') { + throw new RuntimeException("Only class references can be used with the new-instance opcode"); + } + } + + this.regA = (byte)regA; + } + + private Instruction21c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + + if (opcode == Opcode.NEW_INSTANCE && + ((TypeIdItem)this.getReferencedItem()).getTypeDescriptor().charAt(0) != 'L') { + + throw new RuntimeException("Only class references can be used with the new-instance opcode"); + } + + this.regA = buffer[bufferIndex + 1]; + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + if(getReferencedItem().getIndex() > 0xFFFF) { + if (opcode.hasJumboOpcode()) { + throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", + opcode.referenceType.name(), opcode.getJumboOpcode().name)); + } else { + throw new RuntimeException(String.format("%s index is too large", opcode.referenceType.name())); + } + } + + out.writeByte(opcode.value); + out.writeByte(regA); + out.writeShort(getReferencedItem().getIndex()); + } + + public Format getFormat() { + return Format.Format21c; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + public Instruction makeJumbo() { + Opcode jumboOpcode = opcode.getJumboOpcode(); + if (jumboOpcode == null) { + return null; + } + + return new Instruction31c(jumboOpcode, (short)getRegisterA(), getReferencedItem()); + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction21c(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java new file mode 100644 index 00000000..c063b9aa --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java @@ -0,0 +1,94 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedLiteralInstruction; +import org.jf.dexlib.Code.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.SingleRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction21h extends Instruction implements SingleRegisterInstruction, EncodedLiteralInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private short litB; + + public Instruction21h(Opcode opcode, short regA, short litB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + this.regA = (byte)regA; + this.litB = litB; + } + + private Instruction21h(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = buffer[bufferIndex + 1]; + this.litB = NumberUtils.decodeShort(buffer, bufferIndex + 2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + out.writeShort(litB); + } + + public Format getFormat() { + return Format.Format21h; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + public long getLiteral() { + return litB; + } + + public long getDecodedLiteral() { + if (opcode == Opcode.CONST_HIGH16) { + return litB << 16; + } else { + assert opcode == Opcode.CONST_WIDE_HIGH16; + return ((long)litB) << 48; + } + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction21h(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java new file mode 100644 index 00000000..ca507694 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java @@ -0,0 +1,85 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.LiteralInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.SingleRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction21s extends Instruction implements SingleRegisterInstruction, LiteralInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private short litB; + + public Instruction21s(Opcode opcode, short regA, short litB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + this.regA = (byte)regA; + this.litB = litB; + } + + private Instruction21s(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = buffer[bufferIndex + 1]; + this.litB = NumberUtils.decodeShort(buffer, bufferIndex + 2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + out.writeShort(litB); + } + + public Format getFormat() { + return Format.Format21s; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + public long getLiteral() { + return litB; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction21s(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java new file mode 100644 index 00000000..4a839d5a --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java @@ -0,0 +1,103 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.OffsetInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.SingleRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction21t extends Instruction implements OffsetInstruction, SingleRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private short targetAddressOffset; + + public Instruction21t(Opcode opcode, short regA, short offB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + if (offB == 0) { + throw new RuntimeException("The address offset cannot be 0."); + } + + this.regA = (byte)regA; + this.targetAddressOffset = offB; + } + + private Instruction21t(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + assert buffer[bufferIndex] == opcode.value; + + regA = buffer[bufferIndex + 1]; + targetAddressOffset = NumberUtils.decodeShort(buffer, bufferIndex + 2); + assert targetAddressOffset != 0; + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + out.writeShort(targetAddressOffset); + } + + public void updateTargetAddressOffset(int targetAddressOffset) { + if (targetAddressOffset < Short.MIN_VALUE || targetAddressOffset > Short.MAX_VALUE) { + throw new RuntimeException("The address offset " + targetAddressOffset + + " is out of range. It must be in [-32768, 32767]"); + } + if (targetAddressOffset == 0) { + throw new RuntimeException("The address offset cannot be 0"); + } + this.targetAddressOffset = (short) targetAddressOffset; + } + + public Format getFormat() { + return Format.Format21t; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + public int getTargetAddressOffset() { + return targetAddressOffset; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction21t(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java new file mode 100644 index 00000000..19fdbd55 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java @@ -0,0 +1,93 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.LiteralInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.TwoRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; + +public class Instruction22b extends Instruction implements TwoRegisterInstruction, LiteralInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private byte regB; + private byte litC; + + public Instruction22b(Opcode opcode, short regA, short regB, byte litC) { + super(opcode); + + if (regA >= 1 << 8 || + regB >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + this.regA = (byte)regA; + this.regB = (byte)regB; + this.litC = litC; + } + + private Instruction22b(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = buffer[bufferIndex + 1]; + this.regB = buffer[bufferIndex + 2]; + this.litC = buffer[bufferIndex + 3]; + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + out.writeByte(regB); + out.writeByte(litC); + } + + public Format getFormat() { + return Format.Format22b; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + public int getRegisterB() { + return regB & 0xFF; + } + + public long getLiteral() { + return litC; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22b(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java new file mode 100644 index 00000000..500ee39c --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java @@ -0,0 +1,96 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.TwoRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction22c extends InstructionWithReference implements TwoRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private byte regB; + + public Instruction22c(Opcode opcode, byte regA, byte regB, Item referencedItem) { + super(opcode, referencedItem); + + if (regA >= 1 << 4 || + regB >= 1 << 4) { + throw new RuntimeException("The register number must be less than v16"); + } + + this.regA = regA; + this.regB = regB; + } + + private Instruction22c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + + this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + if(getReferencedItem().getIndex() > 0xFFFF) { + if (opcode.hasJumboOpcode()) { + throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", + opcode.referenceType.name(), opcode.getJumboOpcode().name)); + } else { + throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name())); + } + } + + out.writeByte(opcode.value); + out.writeByte((regB << 4) | regA); + out.writeShort(getReferencedItem().getIndex()); + } + + public Format getFormat() { + return Format.Format22c; + } + + public int getRegisterA() { + return regA; + } + + public int getRegisterB() { + return regB; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22c(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java new file mode 100644 index 00000000..9dc62fae --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java @@ -0,0 +1,97 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.OdexedFieldAccess; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.TwoRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction22cs extends Instruction implements TwoRegisterInstruction, OdexedFieldAccess { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private byte regB; + private short fieldOffset; + + public Instruction22cs(Opcode opcode, byte regA, byte regB, int fieldOffset) { + super(opcode); + + if (regA >= 1 << 4 || + regB >= 1 << 4) { + throw new RuntimeException("The register number must be less than v16"); + } + + if (fieldOffset >= 1 << 16) { + throw new RuntimeException("The field offset must be less than 65536"); + } + + this.regA = regA; + this.regB = regB; + this.fieldOffset = (short)fieldOffset; + } + + private Instruction22cs(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + this.fieldOffset = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte((regB << 4) | regA); + out.writeShort(fieldOffset); + } + + public Format getFormat() { + return Format.Format22cs; + } + + public int getRegisterA() { + return regA; + } + + public int getRegisterB() { + return regB; + } + + public int getFieldOffset() { + return fieldOffset & 0xFFFF; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22cs(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java new file mode 100644 index 00000000..434a1b20 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java @@ -0,0 +1,93 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.LiteralInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.TwoRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction22s extends Instruction implements TwoRegisterInstruction, LiteralInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private byte regB; + private short litC; + + public Instruction22s(Opcode opcode, byte regA, byte regB, short litC) { + super(opcode); + + if (regA >= 1 << 4 || + regB >= 1 << 4) { + throw new RuntimeException("The register number must be less than v16"); + } + + this.regA = regA; + this.regB = regB; + this.litC = litC; + } + + private Instruction22s(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + this.litC = NumberUtils.decodeShort(buffer, bufferIndex + 2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte((regB << 4) | regA); + out.writeShort(litC); + } + + public Format getFormat() { + return Format.Format22s; + } + + public int getRegisterA() { + return regA; + } + + public int getRegisterB() { + return regB; + } + + public long getLiteral() { + return litC; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22s(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java new file mode 100644 index 00000000..d5b3a734 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java @@ -0,0 +1,112 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.OffsetInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.TwoRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction22t extends Instruction implements OffsetInstruction, TwoRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private byte regB; + private short targetAddressOffset; + + public Instruction22t(Opcode opcode, byte regA, byte regB, short offC) { + super(opcode); + + if (regA >= 16 || + regB >= 16) { + throw new RuntimeException("The register number must be less than v16"); + } + + if (offC == 0) { + throw new RuntimeException("The address offset cannot be 0."); + } + + this.regA = regA; + this.regB = regB; + this.targetAddressOffset = offC; + } + + private Instruction22t(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + assert buffer[bufferIndex] == opcode.value; + + regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + targetAddressOffset = NumberUtils.decodeShort(buffer, bufferIndex + 2); + + assert targetAddressOffset != 0; + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte((regB << 4) | regA); + out.writeShort(targetAddressOffset); + } + + public void updateTargetAddressOffset(int targetAddressOffset) { + if (targetAddressOffset < -32768 || targetAddressOffset > 32767) { + throw new RuntimeException("The address offset " + targetAddressOffset + + " is out of range. It must be in [-32768, 32767]"); + } + if (targetAddressOffset == 0) { + throw new RuntimeException("The address offset cannot be 0"); + } + this.targetAddressOffset = (short)targetAddressOffset; + } + + public Format getFormat() { + return Format.Format22t; + } + + public int getRegisterA() { + return regA; + } + + public int getRegisterB() { + return regB; + } + + public int getTargetAddressOffset() { + return targetAddressOffset; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22t(opcode, buffer, bufferIndex); + } + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java new file mode 100644 index 00000000..dc262897 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java @@ -0,0 +1,88 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.TwoRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction22x extends Instruction implements TwoRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private short regB; + + public Instruction22x(Opcode opcode, short regA, int regB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v16"); + } + + if (regB >= 1 << 16) { + throw new RuntimeException("The register number must be less than v65536"); + } + + this.regA = (byte)regA; + this.regB = (short)regB; + } + + private Instruction22x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = buffer[bufferIndex + 1]; + this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + out.writeShort(regB); + } + + public Format getFormat() { + return Format.Format22x; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + public int getRegisterB() { + return regB & 0xFFFF; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction22x(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java new file mode 100644 index 00000000..b9695fcc --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java @@ -0,0 +1,93 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.ThreeRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; + +public class Instruction23x extends Instruction implements ThreeRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private byte regB; + private byte regC; + + public Instruction23x(Opcode opcode, short regA, short regB, short regC) { + super(opcode); + + if (regA >= 1 << 8 || + regB >= 1 << 8 || + regC >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + this.regA = (byte)regA; + this.regB = (byte)regB; + this.regC = (byte)regC; + } + + private Instruction23x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = buffer[bufferIndex + 1]; + this.regB = buffer[bufferIndex + 2]; + this.regC = buffer[bufferIndex + 3]; + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + out.writeByte(regB); + out.writeByte(regC); + } + + public Format getFormat() { + return Format.Format23x; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + public int getRegisterB() { + return regB & 0xFF; + } + + public int getRegisterC() { + return regC & 0xFF; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction23x(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java new file mode 100644 index 00000000..7a97903f --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java @@ -0,0 +1,78 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.OffsetInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction30t extends Instruction implements OffsetInstruction { + public static final InstructionFactory Factory = new Factory(); + private int targetAddressOffset; + + public Instruction30t(Opcode opcode, int offA) { + super(opcode); + this.targetAddressOffset = offA; + } + + private Instruction30t(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + assert buffer[bufferIndex] == opcode.value; + + this.targetAddressOffset = NumberUtils.decodeInt(buffer, bufferIndex+2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(0x00); + out.writeInt(targetAddressOffset); + } + + public void updateTargetAddressOffset(int targetAddressOffset) { + this.targetAddressOffset = targetAddressOffset; + } + + public Format getFormat() { + return Format.Format30t; + } + + public int getTargetAddressOffset() { + return targetAddressOffset; + } + + private static class Factory implements InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction30t(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java new file mode 100644 index 00000000..4c62c00a --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java @@ -0,0 +1,79 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.SingleRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction31c extends InstructionWithJumboReference implements SingleRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + + public Instruction31c(Opcode opcode, short regA, Item referencedItem) { + super(opcode, referencedItem); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + this.regA = (byte)regA; + } + + private Instruction31c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + + this.regA = buffer[bufferIndex + 1]; + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + out.writeInt(getReferencedItem().getIndex()); + } + + public Format getFormat() { + return Format.Format31c; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction31c(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java new file mode 100644 index 00000000..5c08ce48 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java @@ -0,0 +1,85 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.LiteralInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.SingleRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction31i extends Instruction implements SingleRegisterInstruction, LiteralInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private int litB; + + public Instruction31i(Opcode opcode, short regA, int litB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + this.regA = (byte)regA; + this.litB = litB; + } + + private Instruction31i(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + this.litB = NumberUtils.decodeInt(buffer, bufferIndex + 2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + out.writeInt(litB); + } + + public Format getFormat() { + return Format.Format31i; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + public long getLiteral() { + return litB; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction31i(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java new file mode 100644 index 00000000..52d6ce66 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java @@ -0,0 +1,90 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.OffsetInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.SingleRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction31t extends Instruction implements OffsetInstruction, SingleRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private int targetAddressOffset; + + public Instruction31t(Opcode opcode, short regA, int offB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + this.regA = (byte)regA; + this.targetAddressOffset = offB; + } + + private Instruction31t(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = buffer[bufferIndex + 1]; + this.targetAddressOffset = NumberUtils.decodeInt(buffer, bufferIndex + 2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + //align the address offset so that the absolute address is aligned on a 4-byte boundary (2 code block boundary) + out.writeInt(targetAddressOffset + ((currentCodeAddress + targetAddressOffset) % 2)); + } + + public void updateTargetAddressOffset(int targetAddressOffset) { + this.targetAddressOffset = targetAddressOffset; + } + + public Format getFormat() { + return Format.Format31t; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + public int getTargetAddressOffset() { + return targetAddressOffset; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction31t(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java new file mode 100644 index 00000000..841ab665 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java @@ -0,0 +1,86 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.TwoRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction32x extends Instruction implements TwoRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private short regA; + private short regB; + + public Instruction32x(Opcode opcode, int regA, int regB) { + super(opcode); + + if (regA >= 1<<16 || + regB >= 1<<16) { + throw new RuntimeException("The register number must be less than v65536"); + } + + this.regA = (short)regA; + this.regB = (short)regB; + } + + private Instruction32x(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regA = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(0); + out.writeShort(regA); + out.writeShort(regB); + } + + public Format getFormat() { + return Format.Format32x; + } + + public int getRegisterA() { + return regA & 0xFFFF; + } + + public int getRegisterB() { + return regB & 0xFFFF; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction32x(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java new file mode 100644 index 00000000..dfe69fec --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java @@ -0,0 +1,170 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.MethodIdItem; +import org.jf.dexlib.TypeIdItem; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +import static org.jf.dexlib.Code.Opcode.*; + +public class Instruction35c extends InstructionWithReference implements FiveRegisterInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regCount; + private byte regA; + private byte regD; + private byte regE; + private byte regF; + private byte regG; + + public Instruction35c(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG, + byte regA, Item referencedItem) { + super(opcode, referencedItem); + + if (regCount > 5) { + throw new RuntimeException("regCount cannot be greater than 5"); + } + + if (regD >= 1 << 4 || + regE >= 1 << 4 || + regF >= 1 << 4 || + regG >= 1 << 4 || + regA >= 1 << 4) { + throw new RuntimeException("All register args must fit in 4 bits"); + } + + checkItem(opcode, referencedItem, regCount); + + this.regCount = (byte)regCount; + this.regA = regA; + this.regD = regD; + this.regE = regE; + this.regF = regF; + this.regG = regG; + } + + protected Instruction35c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + + this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]); + this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]); + this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]); + this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]); + + if (getRegCount() > 5) { + throw new RuntimeException("regCount cannot be greater than 5"); + } + + checkItem(opcode, getReferencedItem(), getRegCount()); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + if(getReferencedItem().getIndex() > 0xFFFF) { + if (opcode.hasJumboOpcode()) { + throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", + opcode.referenceType.name(), opcode.getJumboOpcode().name)); + } else { + throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name())); + } + } + + 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.Format35c; + } + + public int getRegCount() { + return regCount; + } + + public byte getRegisterA() { + return regA; + } + + public byte getRegisterD() { + return regD; + } + + public byte getRegisterE() { + return regE; + } + + public byte getRegisterF() { + return regF; + } + + public byte getRegisterG() { + return regG; + } + + private static void checkItem(Opcode opcode, Item item, int regCount) { + if (opcode == FILLED_NEW_ARRAY) { + //check data for filled-new-array opcode + String type = ((TypeIdItem) item).getTypeDescriptor(); + if (type.charAt(0) != '[') { + throw new RuntimeException("The type must be an array type"); + } + if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { + throw new RuntimeException("The type cannot be an array of longs or doubles"); + } + } else if (opcode.value >= INVOKE_VIRTUAL.value && opcode.value <= INVOKE_INTERFACE.value || + opcode == INVOKE_DIRECT_EMPTY) { + //check data for invoke-* opcodes + MethodIdItem methodIdItem = (MethodIdItem) item; + int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); + if (opcode != INVOKE_STATIC) { + parameterRegisterCount++; + } + if (parameterRegisterCount != regCount) { + throw new RuntimeException("regCount does not match the number of arguments of the method"); + } + } + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction35c(dexFile, opcode, buffer, bufferIndex); + } + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35mi.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35mi.java new file mode 100644 index 00000000..a204cd71 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35mi.java @@ -0,0 +1,136 @@ +/* + * Copyright 2011, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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.*; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + + +public class Instruction35mi extends Instruction implements FiveRegisterInstruction, OdexedInvokeInline { + public static final InstructionFactory Factory = new Factory(); + private byte regCount; + private byte regA; + private byte regD; + private byte regE; + private byte regF; + private byte regG; + private short inlineIndex; + + public Instruction35mi(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG, + byte regA, int inlineIndex) { + super(opcode); + if (regCount > 5) { + throw new RuntimeException("regCount cannot be greater than 5"); + } + + if (regD >= 1 << 4 || + regE >= 1 << 4 || + regF >= 1 << 4 || + regG >= 1 << 4 || + regA >= 1 << 4) { + throw new RuntimeException("All register args must fit in 4 bits"); + } + + if (inlineIndex >= 1 << 16) { + throw new RuntimeException("The method index must be less than 65536"); + } + + this.regCount = (byte)regCount; + this.regA = regA; + this.regD = regD; + this.regE = regE; + this.regF = regF; + this.regG = regG; + this.inlineIndex = (short)inlineIndex; + } + + private Instruction35mi(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]); + this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]); + this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]); + this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]); + this.inlineIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte((regCount << 4) | regA); + out.writeShort(inlineIndex); + out.writeByte((regE << 4) | regD); + out.writeByte((regG << 4) | regF); + } + + public Format getFormat() { + return Format.Format35mi; + } + + public int getRegCount() { + return regCount; + } + + public byte getRegisterA() { + return regA; + } + + public byte getRegisterD() { + return regD; + } + + public byte getRegisterE() { + return regE; + } + + public byte getRegisterF() { + return regF; + } + + public byte getRegisterG() { + return regG; + } + + public int getInlineIndex() { + return inlineIndex & 0xFFFF; + } + + private static class Factory implements InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction35mi(opcode, buffer, bufferIndex); + } + } +} + diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java new file mode 100644 index 00000000..8e9098c1 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java @@ -0,0 +1,136 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.OdexedInvokeVirtual; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + + +public class Instruction35ms extends Instruction implements FiveRegisterInstruction, OdexedInvokeVirtual { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regCount; + private byte regA; + private byte regD; + private byte regE; + private byte regF; + private byte regG; + private short vtableIndex; + + public Instruction35ms(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG, + byte regA, int vtableIndex) { + super(opcode); + if (regCount > 5) { + throw new RuntimeException("regCount cannot be greater than 5"); + } + + if (regD >= 1 << 4 || + regE >= 1 << 4 || + regF >= 1 << 4 || + regG >= 1 << 4 || + regA >= 1 << 4) { + throw new RuntimeException("All register args must fit in 4 bits"); + } + + if (vtableIndex >= 1 << 16) { + throw new RuntimeException("The method index must be less than 65536"); + } + + this.regCount = (byte)regCount; + this.regA = regA; + this.regD = regD; + this.regE = regE; + this.regF = regF; + this.regG = regG; + this.vtableIndex = (short)vtableIndex; + } + + private Instruction35ms(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); + this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); + this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]); + this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]); + this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]); + this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]); + this.vtableIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte((regCount << 4) | regA); + out.writeShort(vtableIndex); + out.writeByte((regE << 4) | regD); + out.writeByte((regG << 4) | regF); + } + + public Format getFormat() { + return Format.Format35ms; + } + + public int getRegCount() { + return regCount; + } + + public byte getRegisterA() { + return regA; + } + + public byte getRegisterD() { + return regD; + } + + public byte getRegisterE() { + return regE; + } + + public byte getRegisterF() { + return regF; + } + + public byte getRegisterG() { + return regG; + } + + public int getVtableIndex() { + return vtableIndex & 0xFFFF; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction35ms(opcode, buffer, bufferIndex); + } + } +} + diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java new file mode 100644 index 00000000..5e4d35cf --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java @@ -0,0 +1,138 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.RegisterRangeInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.MethodIdItem; +import org.jf.dexlib.TypeIdItem; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +import static org.jf.dexlib.Code.Opcode.*; + +public class Instruction3rc extends InstructionWithReference implements RegisterRangeInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regCount; + private short startReg; + + public Instruction3rc(Opcode opcode, short regCount, int startReg, Item referencedItem) { + super(opcode, referencedItem); + + if (regCount >= 1 << 8) { + throw new RuntimeException("regCount must be less than 256"); + } + if (regCount < 0) { + throw new RuntimeException("regCount cannot be negative"); + } + + if (startReg >= 1 << 16) { + throw new RuntimeException("The beginning register of the range must be less than 65536"); + } + if (startReg < 0) { + throw new RuntimeException("The beginning register of the range cannot be negative"); + } + + this.regCount = (byte)regCount; + this.startReg = (short)startReg; + + checkItem(opcode, referencedItem, regCount); + } + + private Instruction3rc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + + this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4); + + checkItem(opcode, getReferencedItem(), getRegCount()); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + if(getReferencedItem().getIndex() > 0xFFFF) { + if (opcode.hasJumboOpcode()) { + throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", + opcode.referenceType.name(), opcode.getJumboOpcode().name)); + } else { + throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name())); + } + } + + out.writeByte(opcode.value); + out.writeByte(regCount); + out.writeShort(this.getReferencedItem().getIndex()); + out.writeShort(startReg); + } + + public Format getFormat() { + return Format.Format3rc; + } + + public int getRegCount() { + return (short)(regCount & 0xFF); + } + + public int getStartRegister() { + return startReg & 0xFFFF; + } + + private static void checkItem(Opcode opcode, Item item, int regCount) { + if (opcode == FILLED_NEW_ARRAY_RANGE) { + //check data for filled-new-array/range opcode + String type = ((TypeIdItem) item).getTypeDescriptor(); + if (type.charAt(0) != '[') { + throw new RuntimeException("The type must be an array type"); + } + if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { + throw new RuntimeException("The type cannot be an array of longs or doubles"); + } + } else if (opcode.value >= INVOKE_VIRTUAL_RANGE.value && opcode.value <= INVOKE_INTERFACE_RANGE.value || + opcode == INVOKE_OBJECT_INIT_RANGE) { + //check data for invoke-*/range opcodes + MethodIdItem methodIdItem = (MethodIdItem) item; + int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); + if (opcode != INVOKE_STATIC_RANGE) { + parameterRegisterCount++; + } + if (parameterRegisterCount != regCount) { + throw new RuntimeException("regCount does not match the number of arguments of the method"); + } + } + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction3rc(dexFile, opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmi.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmi.java new file mode 100644 index 00000000..8499f6f0 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmi.java @@ -0,0 +1,107 @@ +/* + * Copyright 2011, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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.*; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction3rmi extends Instruction implements RegisterRangeInstruction, OdexedInvokeInline { + public static final InstructionFactory Factory = new Factory(); + private byte regCount; + private short startReg; + private short inlineIndex; + + public Instruction3rmi(Opcode opcode, short regCount, int startReg, int inlineIndex) { + super(opcode); + + if (regCount >= 1 << 8) { + throw new RuntimeException("regCount must be less than 256"); + } + if (regCount < 0) { + throw new RuntimeException("regCount cannot be negative"); + } + + if (startReg >= 1 << 16) { + throw new RuntimeException("The beginning register of the range must be less than 65536"); + } + if (startReg < 0) { + throw new RuntimeException("The beginning register of the range cannot be negative"); + } + + if (inlineIndex >= 1 << 16) { + throw new RuntimeException("The method index must be less than 65536"); + } + + this.regCount = (byte)regCount; + this.startReg = (short)startReg; + this.inlineIndex = (short)inlineIndex; + } + + private Instruction3rmi(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + this.inlineIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regCount); + out.writeShort(inlineIndex); + out.writeShort(startReg); + } + + public Format getFormat() { + return Format.Format3rmi; + } + + public int getRegCount() { + return (short)(regCount & 0xFF); + } + + public int getStartRegister() { + return startReg & 0xFFFF; + } + + public int getInlineIndex() { + return inlineIndex & 0xFFFF; + } + + private static class Factory implements InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction3rmi(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java new file mode 100644 index 00000000..a93c9f72 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java @@ -0,0 +1,107 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.OdexedInvokeVirtual; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.RegisterRangeInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction3rms extends Instruction implements RegisterRangeInstruction, OdexedInvokeVirtual { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regCount; + private short startReg; + private short vtableIndex; + + public Instruction3rms(Opcode opcode, short regCount, int startReg, int vtableIndex) { + super(opcode); + + if (regCount >= 1 << 8) { + throw new RuntimeException("regCount must be less than 256"); + } + if (regCount < 0) { + throw new RuntimeException("regCount cannot be negative"); + } + + if (startReg >= 1 << 16) { + throw new RuntimeException("The beginning register of the range must be less than 65536"); + } + if (startReg < 0) { + throw new RuntimeException("The beginning register of the range cannot be negative"); + } + + if (vtableIndex >= 1 << 16) { + throw new RuntimeException("The method index must be less than 65536"); + } + + this.regCount = (byte)regCount; + this.startReg = (short)startReg; + this.vtableIndex = (short)vtableIndex; + } + + private Instruction3rms(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + this.vtableIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regCount); + out.writeShort(vtableIndex); + out.writeShort(startReg); + } + + public Format getFormat() { + return Format.Format3rms; + } + + public int getRegCount() { + return (short)(regCount & 0xFF); + } + + public int getStartRegister() { + return startReg & 0xFFFF; + } + + public int getVtableIndex() { + return vtableIndex & 0xFFFF; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction3rms(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java new file mode 100644 index 00000000..a627c7a8 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java @@ -0,0 +1,85 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.LiteralInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.SingleRegisterInstruction; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +public class Instruction51l extends Instruction implements SingleRegisterInstruction, LiteralInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private byte regA; + private long litB; + + public Instruction51l(Opcode opcode, short regA, long litB) { + super(opcode); + + if (regA >= 1 << 8) { + throw new RuntimeException("The register number must be less than v256"); + } + + this.regA = (byte)regA; + this.litB = litB; + } + + private Instruction51l(Opcode opcode, byte[] buffer, int bufferIndex) { + super(opcode); + + regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); + litB = NumberUtils.decodeLong(buffer, bufferIndex + 2); + } + + protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.writeByte(opcode.value); + out.writeByte(regA); + out.writeLong(litB); + } + + public Format getFormat() { + return Format.Format51l; + } + + public int getRegisterA() { + return regA & 0xFF; + } + + public long getLiteral() { + return litB; + } + + private static class Factory implements Instruction.InstructionFactory { + public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + return new Instruction51l(opcode, buffer, bufferIndex); + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboReference.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboReference.java new file mode 100644 index 00000000..c10e3bd3 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboReference.java @@ -0,0 +1,57 @@ +/* + * Copyright 2011, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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.InstructionWithReference; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.Code.ReferenceType; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Item; +import org.jf.dexlib.Util.NumberUtils; + +public abstract class InstructionWithJumboReference extends InstructionWithReference { + protected InstructionWithJumboReference(Opcode opcode, Item referencedItem) { + super(opcode, referencedItem); + } + + protected InstructionWithJumboReference(Opcode opcode, Item referencedItem, ReferenceType referenceType) { + super(opcode, referencedItem, referenceType); + } + + protected InstructionWithJumboReference(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { + super(dexFile, opcode, buffer, bufferIndex); + } + + protected int getReferencedItemIndex(byte[] buffer, int bufferIndex) { + return NumberUtils.decodeInt(buffer, bufferIndex + 2); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboVariant.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboVariant.java new file mode 100644 index 00000000..9557e696 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/InstructionWithJumboVariant.java @@ -0,0 +1,38 @@ +/* + * Copyright 2011, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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.Instruction; + +public interface InstructionWithJumboVariant { + Instruction makeJumbo(); +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java new file mode 100644 index 00000000..6e88861a --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java @@ -0,0 +1,158 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Instruction; +import org.jf.dexlib.Code.MultiOffsetInstruction; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.NumberUtils; + +import java.util.Iterator; + +public class PackedSwitchDataPseudoInstruction extends Instruction implements MultiOffsetInstruction { + public static final Instruction.InstructionFactory Factory = new Factory(); + private int firstKey; + private int[] targets; + + @Override + public int getSize(int codeAddress) { + return getTargetCount() * 2 + 4 + (codeAddress % 2); + } + + public PackedSwitchDataPseudoInstruction(int firstKey, int[] targets) { + super(Opcode.NOP); + + if (targets.length > 0xFFFF) { + throw new RuntimeException("The packed-switch data contains too many elements. " + + "The maximum number of switch elements is 65535"); + } + + this.firstKey = firstKey; + this.targets = targets; + } + + public PackedSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) { + super(Opcode.NOP); + + byte opcodeByte = buffer[bufferIndex]; + if (opcodeByte != 0x00) { + throw new RuntimeException("Invalid opcode byte for a PackedSwitchData pseudo-instruction"); + } + byte subopcodeByte = buffer[bufferIndex+1]; + if (subopcodeByte != 0x01) { + throw new RuntimeException("Invalid sub-opcode byte for a PackedSwitchData pseudo-instruction"); + } + + int targetCount = NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + this.firstKey = NumberUtils.decodeInt(buffer, bufferIndex + 4); + this.targets = new int[targetCount]; + + for (int i = 0; i iterateKeysAndTargets() { + return new Iterator() { + final int targetCount = getTargetCount(); + int i = 0; + int value = getFirstKey(); + + PackedSwitchTarget packedSwitchTarget = new PackedSwitchTarget(); + + public boolean hasNext() { + return i 0xFFFF) { + throw new RuntimeException("The sparse-switch data contains too many elements. " + + "The maximum number of switch elements is 65535"); + } + + this.keys = keys; + this.targets = targets; + } + + public SparseSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) { + super(Opcode.NOP); + + byte opcodeByte = buffer[bufferIndex]; + if (opcodeByte != 0x00) { + throw new RuntimeException("Invalid opcode byte for a SparseSwitchData pseudo-instruction"); + } + byte subopcodeByte = buffer[bufferIndex+1]; + if (subopcodeByte != 0x02) { + throw new RuntimeException("Invalid sub-opcode byte for a SparseSwitchData pseudo-instruction"); + } + + int targetCount = NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); + keys = new int[targetCount]; + targets = new int[targetCount]; + + for (int i=0; i 0) { + int key = keys[0]; + + out.writeInt(key); + + for (int i = 1; i < keys.length; i++) { + key = keys[i]; + assert key >= keys[i - 1]; + out.writeInt(key); + } + + for (int target : targets) { + out.writeInt(target); + } + } + } + + protected void annotateInstruction(AnnotatedOutput out, int currentCodeAddress) { + out.annotate(getSize(currentCodeAddress)*2, "[0x" + Integer.toHexString(currentCodeAddress) + "] " + + "sparse-switch-data instruction"); + } + + public void updateTarget(int targetIndex, int targetAddressOffset) { + targets[targetIndex] = targetAddressOffset; + } + + public Format getFormat() { + return Format.SparseSwitchData; + } + + public int getTargetCount() { + return targets.length; + } + + public int[] getTargets() { + return targets; + } + + public int[] getKeys() { + return keys; + } + + public static class SparseSwitchTarget { + public int key; + public int targetAddressOffset; + } + + public Iterator iterateKeysAndTargets() { + return new Iterator() { + final int targetCount = getTargetCount(); + int i = 0; + + SparseSwitchTarget sparseSwitchTarget = new SparseSwitchTarget(); + + public boolean hasNext() { + return i opcodesByName; + + //if the instruction can throw an exception + public static final int CAN_THROW = 0x1; + //if the instruction is an odex only instruction + public static final int ODEX_ONLY = 0x2; + //if execution can continue to the next instruction + public static final int CAN_CONTINUE = 0x4; + //if the instruction sets the "hidden" result register + public static final int SETS_RESULT = 0x8; + //if the instruction sets the value of it's first register + public static final int SETS_REGISTER = 0x10; + //if the instruction sets the value of it's first register to a wide type + public static final int SETS_WIDE_REGISTER = 0x20; + //if the instruction is an odexed iget-quick/iput-quick instruction + public static final int ODEXED_INSTANCE_QUICK = 0x40; + //if the instruction is an odexed iget-volatile/iput-volatile instruction + public static final int ODEXED_INSTANCE_VOLATILE = 0x80; + //if the instruction is an odexed sget-volatile/sput-volatile instruction + public static final int ODEXED_STATIC_VOLATILE = 0x100; + //if the instruction is a jumbo instruction + public static final int JUMBO_OPCODE = 0x200; + //if the instruction can initialize an uninitialized object reference + public static final int CAN_INITIALIZE_REFERENCE = 0x400; + + static { + opcodesByValue = new Opcode[256]; + expandedOpcodesByValue = new Opcode[256]; + opcodesByName = new HashMap(); + + for (Opcode opcode: Opcode.values()) { + //INVOKE_DIRECT_EMPTY was changed to INVOKE_OBJECT_INIT_RANGE in ICS + if (opcode != INVOKE_DIRECT_EMPTY) { + if (((opcode.value >> 8) & 0xFF) == 0x00) { + opcodesByValue[opcode.value & 0xFF] = opcode; + } else { + assert ((opcode.value >> 8) & 0xFF) == 0xFF; + expandedOpcodesByValue[opcode.value & 0xFF] = opcode; + } + opcodesByName.put(opcode.name.hashCode(), opcode); + } + } + } + + public static Opcode getOpcodeByName(String opcodeName) { + return opcodesByName.get(opcodeName.toLowerCase().hashCode()); + } + + public static Opcode getOpcodeByValue(short opcodeValue) { + if (((opcodeValue >> 8) & 0xFF) == 0x00) { + return opcodesByValue[opcodeValue & 0xFF]; + } else { + assert ((opcodeValue >> 8) & 0xFF) == 0xFF; + return expandedOpcodesByValue[opcodeValue & 0xFF]; + } + } + + private static void removeOpcodes(Opcode... toRemove) { + for (Opcode opcode: toRemove) { + opcodesByName.remove(opcode.name.toLowerCase().hashCode()); + + if (((opcode.value >> 8) & 0xFF) == 0x00) { + opcodesByValue[opcode.value] = null; + } else { + expandedOpcodesByValue[opcode.value & 0xFF] = null; + } + } + } + + private static void addOpcodes(Opcode... toAdd) { + for (Opcode opcode: toAdd) { + if (((opcode.value >> 8) & 0xFF) == 0x00) { + opcodesByValue[opcode.value & 0xFF] = opcode; + } else { + assert ((opcode.value >> 8) & 0xFF) == 0xFF; + expandedOpcodesByValue[opcode.value & 0xFF] = opcode; + } + opcodesByName.put(opcode.name.hashCode(), opcode); + } + } + + /** + * This will add/remove/replace various opcodes in the value/name maps as needed, + * based on the idiosyncrasies of that api level + * @param apiLevel + */ + public static void updateMapsForApiLevel(int apiLevel) { + if (apiLevel < 5) { + removeOpcodes(THROW_VERIFICATION_ERROR); + } + if (apiLevel < 8) { + removeOpcodes(EXECUTE_INLINE_RANGE); + } + if (apiLevel < 9) { + removeOpcodes(IGET_VOLATILE, IPUT_VOLATILE, SGET_VOLATILE, SPUT_VOLATILE, IGET_OBJECT_VOLATILE, + IGET_WIDE_VOLATILE, IPUT_WIDE_VOLATILE, SGET_WIDE_VOLATILE, SPUT_WIDE_VOLATILE, + IPUT_OBJECT_VOLATILE, SGET_OBJECT_VOLATILE, SPUT_OBJECT_VOLATILE); + } + if (apiLevel < 11) { + removeOpcodes(RETURN_VOID_BARRIER); + } + if (apiLevel < 14) { + removeOpcodes(INVOKE_OBJECT_INIT_RANGE); + addOpcodes(INVOKE_DIRECT_EMPTY); + } + } + + public final short value; + public final String name; + public final ReferenceType referenceType; + public final Format format; + public final int flags; + private final short jumboOpcode; + + Opcode(short opcodeValue, String opcodeName, ReferenceType referenceType, Format format) { + this(opcodeValue, opcodeName, referenceType, format, 0); + } + + Opcode(short opcodeValue, String opcodeName, ReferenceType referenceType, Format format, int flags) { + this(opcodeValue, opcodeName, referenceType, format, flags, (short)-1); + } + + Opcode(short opcodeValue, String opcodeName, ReferenceType referenceType, Format format, int flags, short jumboOpcodeValue) { + this.value = opcodeValue; + this.name = opcodeName; + this.referenceType = referenceType; + this.format = format; + this.flags = flags; + this.jumboOpcode = jumboOpcodeValue; + } + + public final boolean canThrow() { + return (flags & CAN_THROW) != 0; + } + + public final boolean odexOnly() { + return (flags & ODEX_ONLY) != 0; + } + + public final boolean canContinue() { + return (flags & CAN_CONTINUE) != 0; + } + + public final boolean setsResult() { + return (flags & SETS_RESULT) != 0; + } + + public final boolean setsRegister() { + return (flags & SETS_REGISTER) != 0; + } + + public final boolean setsWideRegister() { + return (flags & SETS_WIDE_REGISTER) != 0; + } + + public final boolean isOdexedInstanceQuick() { + return (flags & ODEXED_INSTANCE_QUICK) != 0; + } + + public final boolean isOdexedInstanceVolatile() { + return (flags & ODEXED_INSTANCE_VOLATILE) != 0; + } + + public final boolean isOdexedStaticVolatile() { + return (flags & ODEXED_STATIC_VOLATILE) != 0; + } + + public final boolean isJumboOpcode() { + return (flags & JUMBO_OPCODE) != 0; + } + + public final boolean canInitializeReference() { + return (flags & CAN_INITIALIZE_REFERENCE) != 0; + } + + public final boolean hasJumboOpcode() { + return jumboOpcode != -1 && Opcode.getOpcodeByValue(jumboOpcode) != null; + } + + public final Opcode getJumboOpcode() { + return Opcode.getOpcodeByValue(jumboOpcode); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java new file mode 100644 index 00000000..f6d147ab --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java @@ -0,0 +1,80 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.*; + +public enum ReferenceType +{ + string(-1), + type(0), + field(1), + method(2), + none(-1); + + private int validationErrorReferenceType; + + private ReferenceType(int validationErrorReferenceType) { + this.validationErrorReferenceType = validationErrorReferenceType; + } + + public boolean checkItem(Item item) { + switch (this) { + case string: + return item instanceof StringIdItem; + case type: + return item instanceof TypeIdItem; + case field: + return item instanceof FieldIdItem; + case method: + return item instanceof MethodIdItem; + } + return false; + } + + public static ReferenceType fromValidationErrorReferenceType(int validationErrorReferenceType) { + switch (validationErrorReferenceType) { + case 0: + return type; + case 1: + return field; + case 2: + return method; + } + return null; + } + + public int getValidationErrorReferenceType() { + if (validationErrorReferenceType == -1) { + throw new RuntimeException("This reference type cannot be referenced from a throw-validation-error" + + " instruction"); + } + return validationErrorReferenceType; + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/RegisterRangeInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/RegisterRangeInstruction.java new file mode 100644 index 00000000..21e37196 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/RegisterRangeInstruction.java @@ -0,0 +1,33 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +public interface RegisterRangeInstruction extends InvokeInstruction { + int getStartRegister(); +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/SingleRegisterInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/SingleRegisterInstruction.java new file mode 100644 index 00000000..0cc46561 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/SingleRegisterInstruction.java @@ -0,0 +1,33 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +public interface SingleRegisterInstruction { + int getRegisterA(); +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ThreeRegisterInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ThreeRegisterInstruction.java new file mode 100644 index 00000000..83bbb808 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ThreeRegisterInstruction.java @@ -0,0 +1,33 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +public interface ThreeRegisterInstruction extends TwoRegisterInstruction { + int getRegisterC(); +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/TwoRegisterInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/TwoRegisterInstruction.java new file mode 100644 index 00000000..022a1458 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/TwoRegisterInstruction.java @@ -0,0 +1,35 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +public interface TwoRegisterInstruction extends SingleRegisterInstruction { + int getRegisterA(); + int getRegisterB(); + +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java new file mode 100644 index 00000000..622be5d3 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java @@ -0,0 +1,99 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2011 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; + +import java.util.HashMap; + +public enum VerificationErrorType { + None(0, "no-error"), + Generic(1, "generic-error"), + NoClass(2, "no-such-class"), + NoField(3, "no-such-field"), + NoMethod(4, "no-such-method"), + AccessClass(5, "illegal-class-access"), + AccessField(6, "illegal-field-access"), + AccessMethod(7, "illegal-method-access"), + ClassChange(8, "class-change-error"), + Instantiation(9, "instantiation-error"); + + private static HashMap verificationErrorTypesByName; + + static { + verificationErrorTypesByName = new HashMap(); + + for (VerificationErrorType verificationErrorType: VerificationErrorType.values()) { + verificationErrorTypesByName.put(verificationErrorType.getName(), verificationErrorType); + } + } + + private int value; + private String name; + private VerificationErrorType(int value, String name) { + this.value = value; + this.name = name; + } + + public int getValue() { + return value; + } + + public String getName() { + return name; + } + + public static VerificationErrorType fromString(String validationErrorType) { + return verificationErrorTypesByName.get(validationErrorType); + } + + public static VerificationErrorType getValidationErrorType(int validationErrorType) { + switch (validationErrorType) { + case 0: + return None; + case 1: + return Generic; + case 2: + return NoClass; + case 3: + return NoField; + case 4: + return NoMethod; + case 5: + return AccessClass; + case 6: + return AccessField; + case 7: + return AccessMethod; + case 8: + return ClassChange; + case 9: + return Instantiation; + } + return null; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/CodeItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/CodeItem.java new file mode 100644 index 00000000..e56bfb58 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/CodeItem.java @@ -0,0 +1,1083 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Code.Format.*; +import org.jf.dexlib.Code.*; +import org.jf.dexlib.Debug.DebugInstructionIterator; +import org.jf.dexlib.Debug.DebugOpcode; +import org.jf.dexlib.Util.*; + +import java.util.ArrayList; +import java.util.List; + +public class CodeItem extends Item { + private int registerCount; + private int inWords; + private int outWords; + private DebugInfoItem debugInfo; + private Instruction[] instructions; + private TryItem[] tries; + private EncodedCatchHandler[] encodedCatchHandlers; + + private ClassDataItem.EncodedMethod parent; + + /** + * Creates a new uninitialized CodeItem + * @param dexFile The DexFile that this item belongs to + */ + public CodeItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new CodeItem with the given values. + * @param dexFile The DexFile that this item belongs to + * @param registerCount the number of registers that the method containing this code uses + * @param inWords the number of 2-byte words that the parameters to the method containing this code take + * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code + * @param debugInfo the debug information for this code/method + * @param instructions the instructions for this code item + * @param tries an array of the tries defined for this code/method + * @param encodedCatchHandlers an array of the exception handlers defined for this code/method + */ + private CodeItem(DexFile dexFile, + int registerCount, + int inWords, + int outWords, + DebugInfoItem debugInfo, + Instruction[] instructions, + TryItem[] tries, + EncodedCatchHandler[] encodedCatchHandlers) { + super(dexFile); + + this.registerCount = registerCount; + this.inWords = inWords; + this.outWords = outWords; + this.debugInfo = debugInfo; + if (debugInfo != null) { + debugInfo.setParent(this); + } + + this.instructions = instructions; + this.tries = tries; + this.encodedCatchHandlers = encodedCatchHandlers; + } + + /** + * Returns a new CodeItem with the given values. + * @param dexFile The DexFile that this item belongs to + * @param registerCount the number of registers that the method containing this code uses + * @param inWords the number of 2-byte words that the parameters to the method containing this code take + * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code + * @param debugInfo the debug information for this code/method + * @param instructions the instructions for this code item + * @param tries a list of the tries defined for this code/method or null if none + * @param encodedCatchHandlers a list of the exception handlers defined for this code/method or null if none + * @return a new CodeItem with the given values. + */ + public static CodeItem internCodeItem(DexFile dexFile, + int registerCount, + int inWords, + int outWords, + DebugInfoItem debugInfo, + List instructions, + List tries, + List encodedCatchHandlers) { + TryItem[] triesArray = null; + EncodedCatchHandler[] encodedCatchHandlersArray = null; + Instruction[] instructionsArray = null; + + if (tries != null && tries.size() > 0) { + triesArray = new TryItem[tries.size()]; + tries.toArray(triesArray); + } + + if (encodedCatchHandlers != null && encodedCatchHandlers.size() > 0) { + encodedCatchHandlersArray = new EncodedCatchHandler[encodedCatchHandlers.size()]; + encodedCatchHandlers.toArray(encodedCatchHandlersArray); + } + + if (instructions != null && instructions.size() > 0) { + instructionsArray = new Instruction[instructions.size()]; + instructions.toArray(instructionsArray); + } + + CodeItem codeItem = new CodeItem(dexFile, registerCount, inWords, outWords, debugInfo, instructionsArray, + triesArray, encodedCatchHandlersArray); + return dexFile.CodeItemsSection.intern(codeItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + this.registerCount = in.readShort(); + this.inWords = in.readShort(); + this.outWords = in.readShort(); + int triesCount = in.readShort(); + this.debugInfo = (DebugInfoItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_DEBUG_INFO_ITEM, + in.readInt()); + if (this.debugInfo != null) { + this.debugInfo.setParent(this); + } + + int instructionCount = in.readInt(); + + final ArrayList instructionList = new ArrayList(); + + byte[] encodedInstructions = in.readBytes(instructionCount * 2); + InstructionIterator.IterateInstructions(dexFile, encodedInstructions, + new InstructionIterator.ProcessInstructionDelegate() { + public void ProcessInstruction(int codeAddress, Instruction instruction) { + instructionList.add(instruction); + } + }); + + this.instructions = new Instruction[instructionList.size()]; + instructionList.toArray(instructions); + + if (triesCount > 0) { + in.alignTo(4); + + //we need to read in the catch handlers first, so save the offset to the try items for future reference + int triesOffset = in.getCursor(); + in.setCursor(triesOffset + 8 * triesCount); + + //read in the encoded catch handlers + int encodedHandlerStart = in.getCursor(); + int handlerCount = in.readUnsignedLeb128(); + SparseArray handlerMap = new SparseArray(handlerCount); + encodedCatchHandlers = new EncodedCatchHandler[handlerCount]; + for (int i=0; i 0) { + offset = AlignmentUtils.alignOffset(offset, 4); + + offset += tries.length * 8; + int encodedCatchHandlerBaseOffset = offset; + offset += Leb128Utils.unsignedLeb128Size(encodedCatchHandlers.length); + for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { + offset = encodedCatchHandler.place(offset, encodedCatchHandlerBaseOffset); + } + } + return offset; + } + + /** {@inheritDoc} */ + protected void writeItem(final AnnotatedOutput out) { + int instructionsLength = getInstructionsLength(); + + if (out.annotates()) { + out.annotate(0, parent.method.getMethodString()); + out.annotate(2, "registers_size: 0x" + Integer.toHexString(registerCount) + " (" + registerCount + ")"); + out.annotate(2, "ins_size: 0x" + Integer.toHexString(inWords) + " (" + inWords + ")"); + out.annotate(2, "outs_size: 0x" + Integer.toHexString(outWords) + " (" + outWords + ")"); + int triesLength = tries==null?0:tries.length; + out.annotate(2, "tries_size: 0x" + Integer.toHexString(triesLength) + " (" + triesLength + ")"); + if (debugInfo == null) { + out.annotate(4, "debug_info_off:"); + } else { + out.annotate(4, "debug_info_off: 0x" + Integer.toHexString(debugInfo.getOffset())); + } + out.annotate(4, "insns_size: 0x" + Integer.toHexString(instructionsLength) + " (" + + (instructionsLength) + ")"); + } + + out.writeShort(registerCount); + out.writeShort(inWords); + out.writeShort(outWords); + if (tries == null) { + out.writeShort(0); + } else { + out.writeShort(tries.length); + } + if (debugInfo == null) { + out.writeInt(0); + } else { + out.writeInt(debugInfo.getOffset()); + } + + out.writeInt(instructionsLength); + + int currentCodeAddress = 0; + for (Instruction instruction: instructions) { + currentCodeAddress = instruction.write(out, currentCodeAddress); + } + + if (tries != null && tries.length > 0) { + if (out.annotates()) { + if ((currentCodeAddress % 2) != 0) { + out.annotate("padding"); + out.writeShort(0); + } + + int index = 0; + for (TryItem tryItem: tries) { + out.annotate(0, "[0x" + Integer.toHexString(index++) + "] try_item"); + out.indent(); + tryItem.writeTo(out); + out.deindent(); + } + + out.annotate("handler_count: 0x" + Integer.toHexString(encodedCatchHandlers.length) + "(" + + encodedCatchHandlers.length + ")"); + out.writeUnsignedLeb128(encodedCatchHandlers.length); + + index = 0; + for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { + out.annotate(0, "[" + Integer.toHexString(index++) + "] encoded_catch_handler"); + out.indent(); + encodedCatchHandler.writeTo(out); + out.deindent(); + } + } else { + if ((currentCodeAddress % 2) != 0) { + out.writeShort(0); + } + + for (TryItem tryItem: tries) { + tryItem.writeTo(out); + } + + out.writeUnsignedLeb128(encodedCatchHandlers.length); + + for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { + encodedCatchHandler.writeTo(out); + } + } + } + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_CODE_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + if (this.parent == null) { + return "code_item @0x" + Integer.toHexString(getOffset()); + } + return "code_item @0x" + Integer.toHexString(getOffset()) + " (" + parent.method.getMethodString() + ")"; + } + + /** {@inheritDoc} */ + public int compareTo(CodeItem other) { + if (parent == null) { + if (other.parent == null) { + return 0; + } + return -1; + } + if (other.parent == null) { + return 1; + } + return parent.method.compareTo(other.parent.method); + } + + /** + * @return the register count + */ + public int getRegisterCount() { + return registerCount; + } + + /** + * @return an array of the instructions in this code item + */ + public Instruction[] getInstructions() { + return instructions; + } + + /** + * @return an array of the TryItem objects in this CodeItem + */ + public TryItem[] getTries() { + return tries; + } + + /** + * @return an array of the EncodedCatchHandler objects in this CodeItem + */ + public EncodedCatchHandler[] getHandlers() { + return encodedCatchHandlers; + } + + /** + * @return the DebugInfoItem associated with this CodeItem + */ + public DebugInfoItem getDebugInfo() { + return debugInfo; + } + + /** + * @return the number of 2-byte words that the parameters to the method containing this code take + */ + public int getInWords() { + return inWords; + } + + /** + * @return the maximum number of 2-byte words for the arguments of any method call in this code + */ + public int getOutWords() { + return outWords; + } + + /** + * Sets the MethodIdItem of the method that this CodeItem is associated with + * @param encodedMethod the EncodedMethod of the method that this CodeItem is associated + * with + */ + protected void setParent(ClassDataItem.EncodedMethod encodedMethod) { + this.parent = encodedMethod; + } + + /** + * @return the MethodIdItem of the method that this CodeItem belongs to + */ + public ClassDataItem.EncodedMethod getParent() { + return parent; + } + + /** + * Used by OdexUtil to update this CodeItem with a deodexed version of the instructions + * @param newInstructions the new instructions to use for this code item + */ + public void updateCode(Instruction[] newInstructions) { + this.instructions = newInstructions; + } + + /** + * @return The length of the instructions in this CodeItem, in 2-byte code blocks + */ + private int getInstructionsLength() { + int currentCodeAddress = 0; + for (Instruction instruction: instructions) { + currentCodeAddress += instruction.getSize(currentCodeAddress); + } + return currentCodeAddress; + } + + /** + * Go through the instructions and perform any of the following fixes that are applicable + * - Replace const-string instruction with const-string/jumbo, when the string index is too big + * - Replace goto and goto/16 with a larger version of goto, when the target is too far away + * TODO: we should be able to replace if-* instructions with targets that are too far away with a negated if followed by a goto/32 to the original target + * TODO: remove multiple nops that occur before a switch/array data pseudo instruction. In some cases, multiple smali-baksmali cycles with changes in between could cause nops to start piling up + * TODO: in case of non-range invoke with a jumbo-sized method reference, we could check if the registers are sequential, and replace it with the jumbo variant (which only takes a register range) + * + * The above fixes are applied iteratively, until no more fixes have been performed + */ + public void fixInstructions(boolean fixJumbo, boolean fixGoto) { + try { + boolean didSomething = false; + + do + { + didSomething = false; + + int currentCodeAddress = 0; + for (int i=0; i 0xFFFF) { + + InstructionWithJumboVariant instructionWithJumboVariant = + (InstructionWithJumboVariant)referenceInstruction; + + Instruction jumboInstruction = instructionWithJumboVariant.makeJumbo(); + if (jumboInstruction != null) { + replaceInstructionAtAddress(currentCodeAddress, + instructionWithJumboVariant.makeJumbo()); + didSomething = true; + break; + } + } + } + + currentCodeAddress += instruction.getSize(currentCodeAddress); + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, "Error while attempting to fix " + + instruction.opcode.name + " instruction at address " + currentCodeAddress); + } + } + }while(didSomething); + } catch (Exception ex) { + throw this.addExceptionContext(ex); + } + } + + private void replaceInstructionAtAddress(int codeAddress, Instruction replacementInstruction) { + Instruction originalInstruction = null; + + int[] originalInstructionCodeAddresses = new int[instructions.length+1]; + SparseIntArray originalSwitchAddressByOriginalSwitchDataAddress = new SparseIntArray(); + + int currentCodeAddress = 0; + int instructionIndex = 0; + int i; + for (i=0; i= 0; + int originalAddress = originalAddressByNewAddress.get(currentCodeAddress); + + int originalInstructionTarget = originalAddress + offsetInstruction.getTargetAddressOffset(); + + assert newAddressByOriginalAddress.indexOfKey(originalInstructionTarget) >= 0; + int newInstructionTarget = newAddressByOriginalAddress.get(originalInstructionTarget); + + int newCodeAddress = (newInstructionTarget - currentCodeAddress); + + if (newCodeAddress != offsetInstruction.getTargetAddressOffset()) { + offsetInstruction.updateTargetAddressOffset(newCodeAddress); + } + } else if (instruction instanceof MultiOffsetInstruction) { + MultiOffsetInstruction multiOffsetInstruction = (MultiOffsetInstruction)instruction; + + assert originalAddressByNewAddress.indexOfKey(currentCodeAddress) >= 0; + int originalDataAddress = originalAddressByNewAddress.get(currentCodeAddress); + + int originalSwitchAddress = + originalSwitchAddressByOriginalSwitchDataAddress.get(originalDataAddress, -1); + if (originalSwitchAddress == -1) { + //TODO: maybe we could just remove the unreferenced switch data? + throw new RuntimeException("This method contains an unreferenced switch data block at address " + + + currentCodeAddress + " and can't be automatically fixed."); + } + + assert newAddressByOriginalAddress.indexOfKey(originalSwitchAddress) >= 0; + int newSwitchAddress = newAddressByOriginalAddress.get(originalSwitchAddress); + + int[] targets = multiOffsetInstruction.getTargets(); + for (int t=0; t= 0; + int newTargetCodeAddress = newAddressByOriginalAddress.get(originalTargetCodeAddress); + int newCodeAddress = newTargetCodeAddress - newSwitchAddress; + if (newCodeAddress != targets[t]) { + multiOffsetInstruction.updateTarget(t, newCodeAddress); + } + } + } + currentCodeAddress += instruction.getSize(currentCodeAddress); + } + + if (debugInfo != null) { + final byte[] encodedDebugInfo = debugInfo.getEncodedDebugInfo(); + + ByteArrayInput debugInput = new ByteArrayInput(encodedDebugInfo); + + DebugInstructionFixer debugInstructionFixer = new DebugInstructionFixer(encodedDebugInfo, + newAddressByOriginalAddress); + DebugInstructionIterator.IterateInstructions(debugInput, debugInstructionFixer); + + if (debugInstructionFixer.result != null) { + debugInfo.setEncodedDebugInfo(debugInstructionFixer.result); + } + } + + if (encodedCatchHandlers != null) { + for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { + if (encodedCatchHandler.catchAllHandlerAddress != -1) { + assert newAddressByOriginalAddress.indexOfKey(encodedCatchHandler.catchAllHandlerAddress) >= 0; + encodedCatchHandler.catchAllHandlerAddress = + newAddressByOriginalAddress.get(encodedCatchHandler.catchAllHandlerAddress); + } + + for (EncodedTypeAddrPair handler: encodedCatchHandler.handlers) { + assert newAddressByOriginalAddress.indexOfKey(handler.handlerAddress) >= 0; + handler.handlerAddress = newAddressByOriginalAddress.get(handler.handlerAddress); + } + } + } + + if (this.tries != null) { + for (TryItem tryItem: tries) { + int startAddress = tryItem.startCodeAddress; + int endAddress = tryItem.startCodeAddress + tryItem.tryLength; + + assert newAddressByOriginalAddress.indexOfKey(startAddress) >= 0; + tryItem.startCodeAddress = newAddressByOriginalAddress.get(startAddress); + + assert newAddressByOriginalAddress.indexOfKey(endAddress) >= 0; + tryItem.tryLength = newAddressByOriginalAddress.get(endAddress) - tryItem.startCodeAddress; + } + } + } + + private class DebugInstructionFixer extends DebugInstructionIterator.ProcessRawDebugInstructionDelegate { + private int currentCodeAddress = 0; + private SparseIntArray newAddressByOriginalAddress; + private final byte[] originalEncodedDebugInfo; + public byte[] result = null; + + public DebugInstructionFixer(byte[] originalEncodedDebugInfo, SparseIntArray newAddressByOriginalAddress) { + this.newAddressByOriginalAddress = newAddressByOriginalAddress; + this.originalEncodedDebugInfo = originalEncodedDebugInfo; + } + + + @Override + public void ProcessAdvancePC(int startDebugOffset, int debugInstructionLength, int codeAddressDelta) { + currentCodeAddress += codeAddressDelta; + + if (result != null) { + return; + } + + int newCodeAddress = newAddressByOriginalAddress.get(currentCodeAddress, -1); + + //The address might not point to an actual instruction in some cases, for example, if an AdvancePC + //instruction was inserted just before a "special" instruction, to fix up the addresses for a previous + //instruction replacement. + //In this case, it should be safe to skip, because there will be another AdvancePC/SpecialOpcode that will + //bump up the address to point to a valid instruction before anything (line/local/etc.) is emitted + if (newCodeAddress == -1) { + return; + } + + if (newCodeAddress != currentCodeAddress) { + int newCodeAddressDelta = newCodeAddress - (currentCodeAddress - codeAddressDelta); + assert newCodeAddressDelta > 0; + int codeAddressDeltaLeb128Size = Leb128Utils.unsignedLeb128Size(newCodeAddressDelta); + + //if the length of the new code address delta is the same, we can use the existing buffer + if (codeAddressDeltaLeb128Size + 1 == debugInstructionLength) { + result = originalEncodedDebugInfo; + Leb128Utils.writeUnsignedLeb128(newCodeAddressDelta, result, startDebugOffset+1); + } else { + //The length of the new code address delta is different, so create a new buffer with enough + //additional space to accomodate the new code address delta value. + result = new byte[originalEncodedDebugInfo.length + codeAddressDeltaLeb128Size - + (debugInstructionLength - 1)]; + + System.arraycopy(originalEncodedDebugInfo, 0, result, 0, startDebugOffset); + + result[startDebugOffset] = DebugOpcode.DBG_ADVANCE_PC.value; + Leb128Utils.writeUnsignedLeb128(newCodeAddressDelta, result, startDebugOffset+1); + + System.arraycopy(originalEncodedDebugInfo, startDebugOffset + debugInstructionLength, result, + startDebugOffset + codeAddressDeltaLeb128Size + 1, + originalEncodedDebugInfo.length - (startDebugOffset + codeAddressDeltaLeb128Size + 1)); + } + } + } + + @Override + public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDelta, + int codeAddressDelta) { + currentCodeAddress += codeAddressDelta; + if (result != null) { + return; + } + + int newCodeAddress = newAddressByOriginalAddress.get(currentCodeAddress, -1); + assert newCodeAddress != -1; + + if (newCodeAddress != currentCodeAddress) { + int newCodeAddressDelta = newCodeAddress - (currentCodeAddress - codeAddressDelta); + assert newCodeAddressDelta > 0; + + //if the new code address delta won't fit in the special opcode, we need to insert + //an additional DBG_ADVANCE_PC opcode + if (lineDelta < 2 && newCodeAddressDelta > 16 || lineDelta > 1 && newCodeAddressDelta > 15) { + int additionalCodeAddressDelta = newCodeAddress - currentCodeAddress; + int additionalCodeAddressDeltaLeb128Size = Leb128Utils.signedLeb128Size(additionalCodeAddressDelta); + + //create a new buffer with enough additional space for the new opcode + result = new byte[originalEncodedDebugInfo.length + additionalCodeAddressDeltaLeb128Size + 1]; + + System.arraycopy(originalEncodedDebugInfo, 0, result, 0, startDebugOffset); + result[startDebugOffset] = 0x01; //DBG_ADVANCE_PC + Leb128Utils.writeUnsignedLeb128(additionalCodeAddressDelta, result, startDebugOffset+1); + System.arraycopy(originalEncodedDebugInfo, startDebugOffset, result, + startDebugOffset+additionalCodeAddressDeltaLeb128Size+1, + result.length - (startDebugOffset+additionalCodeAddressDeltaLeb128Size+1)); + } else { + result = originalEncodedDebugInfo; + result[startDebugOffset] = DebugInfoBuilder.calculateSpecialOpcode(lineDelta, + newCodeAddressDelta); + } + } + } + } + + public static class TryItem { + /** + * The address (in 2-byte words) within the code where the try block starts + */ + private int startCodeAddress; + + /** + * The number of 2-byte words that the try block covers + */ + private int tryLength; + + /** + * The associated exception handler + */ + public final EncodedCatchHandler encodedCatchHandler; + + /** + * Construct a new TryItem with the given values + * @param startCodeAddress the code address within the code where the try block starts + * @param tryLength the number of code blocks that the try block covers + * @param encodedCatchHandler the associated exception handler + */ + public TryItem(int startCodeAddress, int tryLength, EncodedCatchHandler encodedCatchHandler) { + this.startCodeAddress = startCodeAddress; + this.tryLength = tryLength; + this.encodedCatchHandler = encodedCatchHandler; + } + + /** + * This is used internally to construct a new TryItem while reading in a DexFile + * @param in the Input object to read the TryItem from + * @param encodedCatchHandlers a SparseArray of the EncodedCatchHandlers for this CodeItem. The + * key should be the offset of the EncodedCatchHandler from the beginning of the encoded_catch_handler_list + * structure. + */ + private TryItem(Input in, SparseArray encodedCatchHandlers) { + startCodeAddress = in.readInt(); + tryLength = in.readShort(); + + encodedCatchHandler = encodedCatchHandlers.get(in.readShort()); + if (encodedCatchHandler == null) { + throw new RuntimeException("Could not find the EncodedCatchHandler referenced by this TryItem"); + } + } + + /** + * Writes the TryItem to the given AnnotatedOutput object + * @param out the AnnotatedOutput object to write to + */ + private void writeTo(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate(4, "start_addr: 0x" + Integer.toHexString(startCodeAddress)); + out.annotate(2, "try_length: 0x" + Integer.toHexString(tryLength) + " (" + tryLength + + ")"); + out.annotate(2, "handler_off: 0x" + Integer.toHexString(encodedCatchHandler.getOffsetInList())); + } + + out.writeInt(startCodeAddress); + out.writeShort(tryLength); + out.writeShort(encodedCatchHandler.getOffsetInList()); + } + + /** + * @return The address (in 2-byte words) within the code where the try block starts + */ + public int getStartCodeAddress() { + return startCodeAddress; + } + + /** + * @return The number of code blocks that the try block covers + */ + public int getTryLength() { + return tryLength; + } + } + + public static class EncodedCatchHandler { + /** + * An array of the individual exception handlers + */ + public final EncodedTypeAddrPair[] handlers; + + /** + * The address within the code (in 2-byte words) for the catch all handler, or -1 if there is no catch all + * handler + */ + private int catchAllHandlerAddress; + + private int baseOffset; + private int offset; + + /** + * Constructs a new EncodedCatchHandler with the given values + * @param handlers an array of the individual exception handlers + * @param catchAllHandlerAddress The address within the code (in 2-byte words) for the catch all handler, or -1 + * if there is no catch all handler + */ + public EncodedCatchHandler(EncodedTypeAddrPair[] handlers, int catchAllHandlerAddress) { + this.handlers = handlers; + this.catchAllHandlerAddress = catchAllHandlerAddress; + } + + /** + * This is used internally to construct a new EncodedCatchHandler while reading in a + * DexFile + * @param dexFile the DexFile that is being read in + * @param in the Input object to read the EncodedCatchHandler from + */ + private EncodedCatchHandler(DexFile dexFile, Input in) { + int handlerCount = in.readSignedLeb128(); + + if (handlerCount < 0) { + handlers = new EncodedTypeAddrPair[-1 * handlerCount]; + } else { + handlers = new EncodedTypeAddrPair[handlerCount]; + } + + for (int i=0; iEncodedCatchHandler, or -1 if there + * is no "Catch All" handler + */ + public int getCatchAllHandlerAddress() { + return catchAllHandlerAddress; + } + + /** + * @return the offset of this EncodedCatchHandler from the beginning of the + * encoded_catch_handler_list structure + */ + private int getOffsetInList() { + return offset-baseOffset; + } + + /** + * Places the EncodedCatchHandler, storing the offset and baseOffset, and returning the offset + * immediately following this EncodedCatchHandler + * @param offset the offset of this EncodedCatchHandler in the DexFile + * @param baseOffset the offset of the beginning of the encoded_catch_handler_list structure in the + * DexFile + * @return the offset immediately following this EncodedCatchHandler + */ + private int place(int offset, int baseOffset) { + this.offset = offset; + this.baseOffset = baseOffset; + + int size = handlers.length; + if (catchAllHandlerAddress > -1) { + size *= -1; + offset += Leb128Utils.unsignedLeb128Size(catchAllHandlerAddress); + } + offset += Leb128Utils.signedLeb128Size(size); + + for (EncodedTypeAddrPair handler: handlers) { + offset += handler.getSize(); + } + return offset; + } + + /** + * Writes the EncodedCatchHandler to the given AnnotatedOutput object + * @param out the AnnotatedOutput object to write to + */ + private void writeTo(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate("size: 0x" + Integer.toHexString(handlers.length) + " (" + handlers.length + ")"); + + int size = handlers.length; + if (catchAllHandlerAddress > -1) { + size = size * -1; + } + out.writeSignedLeb128(size); + + int index = 0; + for (EncodedTypeAddrPair handler: handlers) { + out.annotate(0, "[" + index++ + "] encoded_type_addr_pair"); + out.indent(); + handler.writeTo(out); + out.deindent(); + } + + if (catchAllHandlerAddress > -1) { + out.annotate("catch_all_addr: 0x" + Integer.toHexString(catchAllHandlerAddress)); + out.writeUnsignedLeb128(catchAllHandlerAddress); + } + } else { + int size = handlers.length; + if (catchAllHandlerAddress > -1) { + size = size * -1; + } + out.writeSignedLeb128(size); + + for (EncodedTypeAddrPair handler: handlers) { + handler.writeTo(out); + } + + if (catchAllHandlerAddress > -1) { + out.writeUnsignedLeb128(catchAllHandlerAddress); + } + } + } + + @Override + public int hashCode() { + int hash = 0; + for (EncodedTypeAddrPair handler: handlers) { + hash = hash * 31 + handler.hashCode(); + } + hash = hash * 31 + catchAllHandlerAddress; + return hash; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + EncodedCatchHandler other = (EncodedCatchHandler)o; + if (handlers.length != other.handlers.length || catchAllHandlerAddress != other.catchAllHandlerAddress) { + return false; + } + + for (int i=0; iException that this handler handles + */ + public final TypeIdItem exceptionType; + + /** + * The address (in 2-byte words) in the code of the handler + */ + private int handlerAddress; + + /** + * Constructs a new EncodedTypeAddrPair with the given values + * @param exceptionType the type of the Exception that this handler handles + * @param handlerAddress the address (in 2-byte words) in the code of the handler + */ + public EncodedTypeAddrPair(TypeIdItem exceptionType, int handlerAddress) { + this.exceptionType = exceptionType; + this.handlerAddress = handlerAddress; + } + + /** + * This is used internally to construct a new EncodedTypeAddrPair while reading in a + * DexFile + * @param dexFile the DexFile that is being read in + * @param in the Input object to read the EncodedCatchHandler from + */ + private EncodedTypeAddrPair(DexFile dexFile, Input in) { + exceptionType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128()); + handlerAddress = in.readUnsignedLeb128(); + } + + /** + * @return the size of this EncodedTypeAddrPair + */ + private int getSize() { + return Leb128Utils.unsignedLeb128Size(exceptionType.getIndex()) + + Leb128Utils.unsignedLeb128Size(handlerAddress); + } + + /** + * Writes the EncodedTypeAddrPair to the given AnnotatedOutput object + * @param out the AnnotatedOutput object to write to + */ + private void writeTo(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate("exception_type: " + exceptionType.getTypeDescriptor()); + out.writeUnsignedLeb128(exceptionType.getIndex()); + + out.annotate("handler_addr: 0x" + Integer.toHexString(handlerAddress)); + out.writeUnsignedLeb128(handlerAddress); + } else { + out.writeUnsignedLeb128(exceptionType.getIndex()); + out.writeUnsignedLeb128(handlerAddress); + } + } + + public int getHandlerAddress() { + return handlerAddress; + } + + @Override + public int hashCode() { + return exceptionType.hashCode() * 31 + handlerAddress; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + EncodedTypeAddrPair other = (EncodedTypeAddrPair)o; + return exceptionType == other.exceptionType && handlerAddress == other.handlerAddress; + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Convertible.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Convertible.java new file mode 100644 index 00000000..f227621b --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Convertible.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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; + +/** + * Describes an object that can be converted to a different type + */ +public interface Convertible { + T convert(); +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java new file mode 100644 index 00000000..b61f399d --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java @@ -0,0 +1,364 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Debug; + +import org.jf.dexlib.DebugInfoItem; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.StringIdItem; +import org.jf.dexlib.TypeIdItem; +import org.jf.dexlib.Util.ByteArrayInput; +import org.jf.dexlib.Util.Input; + +public class DebugInstructionIterator { + /** + * This method decodes the debug instructions in the given byte array and iterates over them, calling + * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction + * @param in an Input object that the debug instructions can be read from + * @param processDebugInstruction a ProcessDebugInstructionDelegate object that gets called + * for each instruction that is encountered + */ + public static void IterateInstructions(Input in, ProcessRawDebugInstructionDelegate processDebugInstruction) { + int startDebugOffset; + + while(true) + { + startDebugOffset = in.getCursor(); + byte debugOpcode = in.readByte(); + + switch (debugOpcode) { + case 0x00: + { + processDebugInstruction.ProcessEndSequence(startDebugOffset); + return; + } + case 0x01: + { + int codeAddressDiff = in.readUnsignedLeb128(); + processDebugInstruction.ProcessAdvancePC(startDebugOffset, in.getCursor() - startDebugOffset, + codeAddressDiff); + break; + } + case 0x02: + { + int lineDiff = in.readSignedLeb128(); + processDebugInstruction.ProcessAdvanceLine(startDebugOffset, in.getCursor() - startDebugOffset, + lineDiff); + break; + } + case 0x03: + { + int registerNum = in.readUnsignedOrSignedLeb128(); + boolean isSignedRegister = false; + if (registerNum < 0) { + isSignedRegister = true; + registerNum = ~registerNum; + } + int nameIndex = in.readUnsignedLeb128() - 1; + int typeIndex = in.readUnsignedLeb128() - 1; + processDebugInstruction.ProcessStartLocal(startDebugOffset, in.getCursor() - startDebugOffset, + registerNum, nameIndex, typeIndex, isSignedRegister); + break; + } + case 0x04: + { + int registerNum = in.readUnsignedOrSignedLeb128(); + boolean isSignedRegister = false; + if (registerNum < 0) { + isSignedRegister = true; + registerNum = ~registerNum; + } + int nameIndex = in.readUnsignedLeb128() - 1; + int typeIndex = in.readUnsignedLeb128() - 1; + int signatureIndex = in.readUnsignedLeb128() - 1; + processDebugInstruction.ProcessStartLocalExtended(startDebugOffset, + in.getCursor() - startDebugOffset, registerNum, nameIndex, typeIndex, signatureIndex, + isSignedRegister); + break; + } + case 0x05: + { + int registerNum = in.readUnsignedOrSignedLeb128(); + boolean isSignedRegister = false; + if (registerNum < 0) { + isSignedRegister = true; + registerNum = ~registerNum; + } + processDebugInstruction.ProcessEndLocal(startDebugOffset, in.getCursor() - startDebugOffset, + registerNum, isSignedRegister); + break; + } + case 0x06: + { + int registerNum = in.readUnsignedOrSignedLeb128(); + boolean isSignedRegister = false; + if (registerNum < 0) { + isSignedRegister = true; + registerNum = ~registerNum; + } + processDebugInstruction.ProcessRestartLocal(startDebugOffset, in.getCursor() - startDebugOffset, + registerNum, isSignedRegister); + break; + } + case 0x07: + { + processDebugInstruction.ProcessSetPrologueEnd(startDebugOffset); + break; + } + case 0x08: + { + processDebugInstruction.ProcessSetEpilogueBegin(startDebugOffset); + break; + } + case 0x09: + { + int nameIndex = in.readUnsignedLeb128(); + processDebugInstruction.ProcessSetFile(startDebugOffset, in.getCursor() - startDebugOffset, + nameIndex); + break; + } + default: + { + int base = ((debugOpcode & 0xFF) - 0x0A); + processDebugInstruction.ProcessSpecialOpcode(startDebugOffset, debugOpcode, (base % 15) - 4, base / 15); + } + } + } + } + + /** + * This method decodes the debug instructions in the given byte array and iterates over them, calling + * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction + * @param debugInfoItem the DebugInfoItem to iterate over + * @param registerCount the number of registers in the method that the given debug info is for + * @param processDecodedDebugInstruction a ProcessDebugInstructionDelegate object that gets called + * for each instruction that is encountered + */ + public static void DecodeInstructions(DebugInfoItem debugInfoItem, int registerCount, + ProcessDecodedDebugInstructionDelegate processDecodedDebugInstruction) { + int startDebugOffset; + int currentCodeAddress = 0; + int line = debugInfoItem.getLineStart(); + Input in = new ByteArrayInput(debugInfoItem.getEncodedDebugInfo()); + DexFile dexFile = debugInfoItem.getDexFile(); + + Local[] locals = new Local[registerCount]; + + while(true) + { + startDebugOffset = in.getCursor(); + byte debugOpcode = in.readByte(); + + switch (DebugOpcode.getDebugOpcodeByValue(debugOpcode)) { + case DBG_END_SEQUENCE: + { + return; + } + case DBG_ADVANCE_PC: + { + int codeAddressDiff = in.readUnsignedLeb128(); + currentCodeAddress += codeAddressDiff; + break; + } + case DBG_ADVANCE_LINE: + { + int lineDiff = in.readSignedLeb128(); + line += lineDiff; + break; + } + case DBG_START_LOCAL: + { + int registerNum = in.readUnsignedLeb128(); + StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); + TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); + locals[registerNum] = new Local(registerNum, name, type, null); + processDecodedDebugInstruction.ProcessStartLocal(currentCodeAddress, + in.getCursor() - startDebugOffset, registerNum, name, type); + break; + } + case DBG_START_LOCAL_EXTENDED: + { + int registerNum = in.readUnsignedLeb128(); + StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); + TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); + StringIdItem signature = + dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); + locals[registerNum] = new Local(registerNum, name, type, signature); + processDecodedDebugInstruction.ProcessStartLocalExtended(currentCodeAddress, + in.getCursor() - startDebugOffset, registerNum, name, type, signature); + break; + } + case DBG_END_LOCAL: + { + int registerNum = in.readUnsignedLeb128(); + Local local = locals[registerNum]; + if (local == null) { + processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, + null, null, null); + } else { + processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, + local.name, local.type, local.signature); + } + break; + } + case DBG_RESTART_LOCAL: + { + int registerNum = in.readUnsignedLeb128(); + Local local = locals[registerNum]; + if (local == null) { + processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, + registerNum, null, null, null); + } else { + processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, + registerNum, local.name, local.type, local.signature); + } + + break; + } + case DBG_SET_PROLOGUE_END: + { + processDecodedDebugInstruction.ProcessSetPrologueEnd(currentCodeAddress); + break; + } + case DBG_SET_EPILOGUE_BEGIN: + { + processDecodedDebugInstruction.ProcessSetEpilogueBegin(currentCodeAddress); + break; + } + case DBG_SET_FILE: + { + StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); + processDecodedDebugInstruction.ProcessSetFile(currentCodeAddress, in.getCursor() - startDebugOffset, name); + break; + } + case DBG_SPECIAL_OPCODE: + { + int base = ((debugOpcode & 0xFF) - 0x0A); + currentCodeAddress += base / 15; + line += (base % 15) - 4; + processDecodedDebugInstruction.ProcessLineEmit(currentCodeAddress, line); + } + } + } + } + + public static class ProcessRawDebugInstructionDelegate + { + //TODO: add javadocs + public void ProcessEndSequence(int startDebugOffset) { + ProcessStaticOpcode(DebugOpcode.DBG_END_SEQUENCE, startDebugOffset, 1); + } + + public void ProcessAdvancePC(int startDebugOffset, int length, int codeAddressDiff) { + ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_PC, startDebugOffset, length); + } + + public void ProcessAdvanceLine(int startDebugOffset, int length, int lineDiff) { + ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_LINE, startDebugOffset, length); + } + + public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, int typeIndex, + boolean registerIsSigned) { + } + + public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex, + int typeIndex,int signatureIndex, boolean registerIsSigned) { + } + + public void ProcessEndLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) { + ProcessStaticOpcode(DebugOpcode.DBG_END_LOCAL, startDebugOffset, length); + } + + public void ProcessRestartLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) { + ProcessStaticOpcode(DebugOpcode.DBG_RESTART_LOCAL, startDebugOffset, length); + } + + public void ProcessSetPrologueEnd(int startDebugOffset) { + ProcessStaticOpcode(DebugOpcode.DBG_SET_PROLOGUE_END, startDebugOffset, 1); + } + + public void ProcessSetEpilogueBegin(int startDebugOffset) { + ProcessStaticOpcode(DebugOpcode.DBG_SET_EPILOGUE_BEGIN, startDebugOffset, 1); + } + + public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { + } + + public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDiff, int codeAddressDiff) { + ProcessStaticOpcode(DebugOpcode.DBG_SPECIAL_OPCODE, startDebugOffset, 1); + } + + public void ProcessStaticOpcode(DebugOpcode debugOpcode, int startDebugOffset, int length) { + } + } + + public static class ProcessDecodedDebugInstructionDelegate + { + public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name, + TypeIdItem type) { + } + + public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, StringIdItem name, + TypeIdItem type, StringIdItem signature) { + } + + public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type, + StringIdItem signature) { + } + + public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name, + TypeIdItem type, StringIdItem signature) { + } + + public void ProcessSetPrologueEnd(int codeAddress) { + } + + public void ProcessSetEpilogueBegin(int codeAddress) { + } + + public void ProcessSetFile(int codeAddress, int length, StringIdItem name) { + } + + public void ProcessLineEmit(int codeAddress, int line) { + } + } + + private static class Local { + public final int register; + public final StringIdItem name; + public final TypeIdItem type; + public final StringIdItem signature; + public Local(int register, StringIdItem name, TypeIdItem type, StringIdItem signature) { + this.register = register; + this.name = name; + this.type = type; + this.signature = signature; + } + + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java new file mode 100644 index 00000000..d219ee33 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java @@ -0,0 +1,64 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Debug; + +public enum DebugOpcode { + DBG_END_SEQUENCE((byte)0x00), + DBG_ADVANCE_PC((byte)0x01), + DBG_ADVANCE_LINE((byte)0x02), + DBG_START_LOCAL((byte)0x03), + DBG_START_LOCAL_EXTENDED((byte)0x04), + DBG_END_LOCAL((byte)0x05), + DBG_RESTART_LOCAL((byte)0x06), + DBG_SET_PROLOGUE_END((byte)0x07), + DBG_SET_EPILOGUE_BEGIN((byte)0x08), + DBG_SET_FILE((byte)0x09), + DBG_SPECIAL_OPCODE((byte)0x0A); + + private static DebugOpcode[] opcodesByValue; + + static { + opcodesByValue = new DebugOpcode[11]; + + for (DebugOpcode debugOpcode: DebugOpcode.values()) { + opcodesByValue[debugOpcode.value & 0xFF] = debugOpcode; + } + } + + public static DebugOpcode getDebugOpcodeByValue(byte debugOpcodeValue) { + debugOpcodeValue = (byte)Math.min(debugOpcodeValue & 0xFF, 0x0A); + return opcodesByValue[debugOpcodeValue]; + } + + public final byte value; + + DebugOpcode(byte value) { + this.value = value; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java new file mode 100644 index 00000000..81e023de --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java @@ -0,0 +1,620 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Debug.DebugInstructionIterator; +import org.jf.dexlib.Debug.DebugOpcode; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.ByteArrayInput; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.Leb128Utils; + +import java.util.ArrayList; +import java.util.List; + +public class DebugInfoItem extends Item { + private int lineStart; + private StringIdItem[] parameterNames; + private byte[] encodedDebugInfo; + private Item[] referencedItems; + + private CodeItem parent = null; + + /** + * Creates a new uninitialized DebugInfoInfo + * @param dexFile The DexFile that this item belongs to + */ + public DebugInfoItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new DebugInfoItem with the given values + * @param dexFile The DexFile that this item belongs to + * @param lineStart the initial value for the line number register for the debug info machine + * @param parameterNames an array of the names of the associated method's parameters. The entire parameter + * can be null if no parameter info is available, or any element can be null to indicate no info for that parameter + * @param encodedDebugInfo the debug info, encoded as a byte array + * @param referencedItems an array of the items referenced by instructions, in order of occurance in the encoded + * debug info + */ + private DebugInfoItem(DexFile dexFile, + int lineStart, + StringIdItem[] parameterNames, + byte[] encodedDebugInfo, + Item[] referencedItems) { + super(dexFile); + this.lineStart = lineStart; + this.parameterNames = parameterNames; + this.encodedDebugInfo = encodedDebugInfo; + this.referencedItems = referencedItems; + } + + /** + * Returns a new DebugInfoItem with the given values + * @param dexFile The DexFile that this item belongs to + * @param lineStart the initial value for the line number register for the debug info machine + * @param parameterNames an array of the names of the associated method's parameters. The entire parameter + * can be null if no parameter info is available, or any element can be null to indicate no info for that parameter + * @param encodedDebugInfo the debug info, encoded as a byte array + * @param referencedItems an array of the items referenced by instructions, in order of occurance in the encoded + * debug info + * @return a new DebugInfoItem with the given values + */ + public static DebugInfoItem internDebugInfoItem(DexFile dexFile, + int lineStart, + StringIdItem[] parameterNames, + byte[] encodedDebugInfo, + Item[] referencedItems) { + DebugInfoItem debugInfoItem = new DebugInfoItem(dexFile, lineStart, parameterNames, encodedDebugInfo, + referencedItems); + return dexFile.DebugInfoItemsSection.intern(debugInfoItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + lineStart = in.readUnsignedLeb128(); + parameterNames = new StringIdItem[in.readUnsignedLeb128()]; + IndexedSection stringIdSection = dexFile.StringIdsSection; + for (int i=0; i referencedItemsList = new ArrayList(50); + DebugInstructionIterator.IterateInstructions(in, + new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() { + @Override + public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, + int typeIndex, boolean registerIsSigned) { + if (nameIndex != -1) { + referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex)); + } + if (typeIndex != -1) { + referencedItemsList.add(dexFile.TypeIdsSection.getItemByIndex(typeIndex)); + } + } + + @Override + public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNume, + int nameIndex, int typeIndex, int signatureIndex, + boolean registerIsSigned) { + if (nameIndex != -1) { + referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex)); + } + if (typeIndex != -1) { + referencedItemsList.add(dexFile.TypeIdsSection.getItemByIndex(typeIndex)); + } + if (signatureIndex != -1) { + referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(signatureIndex)); + } + } + + @Override + public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { + if (nameIndex != -1) { + referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex)); + } + } + }); + + referencedItems = new Item[referencedItemsList.size()]; + referencedItemsList.toArray(referencedItems); + + int length = in.getCursor() - start; + in.setCursor(start); + encodedDebugInfo = in.readBytes(length); + } + + + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + offset += Leb128Utils.unsignedLeb128Size(lineStart); + offset += Leb128Utils.unsignedLeb128Size(parameterNames.length); + for (StringIdItem parameterName: parameterNames) { + int indexp1; + if (parameterName == null) { + indexp1 = 0; + } else { + indexp1 = parameterName.getIndex() + 1; + } + offset += Leb128Utils.unsignedLeb128Size(indexp1); + } + + //make a subclass so we can keep track of and access the computed length + class ProcessDebugInstructionDelegateWithLength extends + DebugInstructionIterator.ProcessRawDebugInstructionDelegate { + public int length = 0; + } + ProcessDebugInstructionDelegateWithLength pdidwl; + + //final referencedItems = this.referencedItems; + + DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo), + pdidwl = new ProcessDebugInstructionDelegateWithLength() { + private int referencedItemsPosition = 0; + + @Override + public void ProcessStaticOpcode(DebugOpcode opcode, int startDebugOffset, int length) { + this.length+=length; + } + + @Override + public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, + int typeIndex, boolean registerIsSigned) { + this.length++; + if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { + this.length += Leb128Utils.signedLeb128Size(registerNum); + } else { + this.length+=Leb128Utils.unsignedLeb128Size(registerNum); + } + if (nameIndex != -1) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + if (typeIndex != -1) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + + } + + @Override + public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex, + int typeIndex, int signatureIndex, + boolean registerIsSigned) { + this.length++; + if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { + this.length += Leb128Utils.signedLeb128Size(registerNum); + } else { + this.length+=Leb128Utils.unsignedLeb128Size(registerNum); + } + if (nameIndex != -1) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + if (typeIndex != -1) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + if (signatureIndex != -1) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + } + + @Override + public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { + this.length++; + if (nameIndex != -1) { + this.length+= + Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); + } else { + this.length++; + } + } + }); + return offset + pdidwl.length; + } + + /** {@inheritDoc} */ + protected void writeItem(final AnnotatedOutput out) { + if (out.annotates()) { + writeItemWithAnnotations(out); + } else { + writeItemWithNoAnnotations(out); + } + } + + /** + * Replaces the encoded debug info for this DebugInfoItem. It is expected that the new debug info is compatible + * with the existing information, i.e. lineStart, referencedItems, parameterNames + * @param encodedDebugInfo the new encoded debug info + */ + protected void setEncodedDebugInfo(byte[] encodedDebugInfo) { + //TODO: I would rather replace this method with some way of saying "The (code) instruction at address changed from A bytes to B bytes. Fixup the debug info accordingly" + + this.encodedDebugInfo = encodedDebugInfo; + } + + /** + * Helper method that writes the item, without writing annotations + * @param out the AnnotatedOutput object + */ + private void writeItemWithNoAnnotations(final AnnotatedOutput out) { + out.writeUnsignedLeb128(lineStart); + out.writeUnsignedLeb128(parameterNames.length); + for (StringIdItem parameterName: parameterNames) { + int indexp1; + if (parameterName == null) { + indexp1 = 0; + } else { + indexp1 = parameterName.getIndex() + 1; + } + out.writeUnsignedLeb128(indexp1); + } + + DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo), + new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() { + private int referencedItemsPosition = 0; + + @Override + public void ProcessStaticOpcode(DebugOpcode opcode, int startDebugOffset, int length) { + out.write(encodedDebugInfo, startDebugOffset, length); + } + + @Override + public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, + int typeIndex, boolean registerIsSigned) { + out.writeByte(DebugOpcode.DBG_START_LOCAL.value); + if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { + out.writeSignedLeb128(registerNum); + } else { + out.writeUnsignedLeb128(registerNum); + } + if (nameIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + if (typeIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + } + + @Override + public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex, + int typeIndex, int signatureIndex, + boolean registerIsSigned) { + out.writeByte(DebugOpcode.DBG_START_LOCAL_EXTENDED.value); + if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { + out.writeSignedLeb128(registerNum); + } else { + out.writeUnsignedLeb128(registerNum); + } + if (nameIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + if (typeIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + if (signatureIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + } + + @Override + public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { + out.writeByte(DebugOpcode.DBG_SET_FILE.value); + if (nameIndex != -1) { + out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); + } else { + out.writeByte(0); + } + } + }); + } + + /** + * Helper method that writes and annotates the item + * @param out the AnnotatedOutput object + */ + private void writeItemWithAnnotations(final AnnotatedOutput out) { + out.annotate(0, parent.getParent().method.getMethodString()); + out.annotate("line_start: 0x" + Integer.toHexString(lineStart) + " (" + lineStart + ")"); + out.writeUnsignedLeb128(lineStart); + out.annotate("parameters_size: 0x" + Integer.toHexString(parameterNames.length) + " (" + parameterNames.length + + ")"); + out.writeUnsignedLeb128(parameterNames.length); + int index = 0; + for (StringIdItem parameterName: parameterNames) { + int indexp1; + if (parameterName == null) { + out.annotate("[" + index++ +"] parameterName: "); + indexp1 = 0; + } else { + out.annotate("[" + index++ +"] parameterName: " + parameterName.getStringValue()); + indexp1 = parameterName.getIndex() + 1; + } + out.writeUnsignedLeb128(indexp1); + } + + DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo), + new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() { + private int referencedItemsPosition = 0; + + @Override + public void ProcessEndSequence(int startDebugOffset) { + out.annotate("DBG_END_SEQUENCE"); + out.writeByte(DebugOpcode.DBG_END_SEQUENCE.value); + } + + @Override + public void ProcessAdvancePC(int startDebugOffset, int length, int addressDiff) { + out.annotate("DBG_ADVANCE_PC"); + out.writeByte(DebugOpcode.DBG_ADVANCE_PC.value); + out.indent(); + out.annotate("addr_diff: 0x" + Integer.toHexString(addressDiff) + " (" + addressDiff + ")"); + out.writeUnsignedLeb128(addressDiff); + out.deindent(); + } + + @Override + public void ProcessAdvanceLine(int startDebugOffset, int length, int lineDiff) { + out.annotate("DBG_ADVANCE_LINE"); + out.writeByte(DebugOpcode.DBG_ADVANCE_LINE.value); + out.indent(); + out.annotate("line_diff: 0x" + Integer.toHexString(lineDiff) + " (" + lineDiff + ")"); + out.writeSignedLeb128(lineDiff); + out.deindent(); + } + + @Override + public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, + int typeIndex, boolean registerIsSigned) { + out.annotate("DBG_START_LOCAL"); + out.writeByte(DebugOpcode.DBG_START_LOCAL.value); + out.indent(); + out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")"); + if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { + out.writeSignedLeb128(registerNum); + } else { + out.writeUnsignedLeb128(registerNum); + } + if (nameIndex != -1) { + Item nameItem = referencedItems[referencedItemsPosition++]; + assert nameItem instanceof StringIdItem; + out.annotate("name: " + ((StringIdItem)nameItem).getStringValue()); + out.writeUnsignedLeb128(nameItem.getIndex() + 1); + } else { + out.annotate("name: "); + out.writeByte(0); + } + if (typeIndex != -1) { + Item typeItem = referencedItems[referencedItemsPosition++]; + assert typeItem instanceof TypeIdItem; + out.annotate("type: " + ((TypeIdItem)typeItem).getTypeDescriptor()); + out.writeUnsignedLeb128(typeItem.getIndex() + 1); + } else { + out.annotate("type: "); + out.writeByte(0); + } + out.deindent(); + } + + @Override + public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, + int nameIndex, int typeIndex, int signatureIndex, + boolean registerIsSigned) { + out.annotate("DBG_START_LOCAL_EXTENDED"); + out.writeByte(DebugOpcode.DBG_START_LOCAL_EXTENDED.value); + out.indent(); + out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")"); + if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { + out.writeSignedLeb128(registerNum); + } else { + out.writeUnsignedLeb128(registerNum); + } + if (nameIndex != -1) { + Item nameItem = referencedItems[referencedItemsPosition++]; + assert nameItem instanceof StringIdItem; + out.annotate("name: " + ((StringIdItem)nameItem).getStringValue()); + out.writeUnsignedLeb128(nameItem.getIndex() + 1); + } else { + out.annotate("name: "); + out.writeByte(0); + } + if (typeIndex != -1) { + Item typeItem = referencedItems[referencedItemsPosition++]; + assert typeItem instanceof TypeIdItem; + out.annotate("type: " + ((TypeIdItem)typeItem).getTypeDescriptor()); + out.writeUnsignedLeb128(typeItem.getIndex() + 1); + } else { + out.annotate("type: "); + out.writeByte(0); + } + if (signatureIndex != -1) { + Item signatureItem = referencedItems[referencedItemsPosition++]; + assert signatureItem instanceof StringIdItem; + out.annotate("signature: " + ((StringIdItem)signatureItem).getStringValue()); + out.writeUnsignedLeb128(signatureItem.getIndex() + 1); + } else { + out.annotate("signature: "); + out.writeByte(0); + } + out.deindent(); + } + + @Override + public void ProcessEndLocal(int startDebugOffset, int length, int registerNum, + boolean registerIsSigned) { + out.annotate("DBG_END_LOCAL"); + out.writeByte(DebugOpcode.DBG_END_LOCAL.value); + out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")"); + if (registerIsSigned) { + out.writeSignedLeb128(registerNum); + } else { + out.writeUnsignedLeb128(registerNum); + } + } + + @Override + public void ProcessRestartLocal(int startDebugOffset, int length, int registerNum, + boolean registerIsSigned) { + out.annotate("DBG_RESTART_LOCAL"); + out.writeByte(DebugOpcode.DBG_RESTART_LOCAL.value); + out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")"); + if (registerIsSigned) { + out.writeSignedLeb128(registerNum); + } else { + out.writeUnsignedLeb128(registerNum); + } + } + + @Override + public void ProcessSetPrologueEnd(int startDebugOffset) { + out.annotate("DBG_SET_PROLOGUE_END"); + out.writeByte(DebugOpcode.DBG_SET_PROLOGUE_END.value); + } + + @Override + public void ProcessSetEpilogueBegin(int startDebugOffset) { + out.annotate("DBG_SET_EPILOGUE_BEGIN"); + out.writeByte(DebugOpcode.DBG_SET_EPILOGUE_BEGIN.value); + } + + @Override + public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { + out.annotate("DBG_SET_FILE"); + out.writeByte(DebugOpcode.DBG_SET_FILE.value); + if (nameIndex != -1) { + Item sourceItem = referencedItems[referencedItemsPosition++]; + assert sourceItem instanceof StringIdItem; + out.annotate("source_file: \"" + ((StringIdItem)sourceItem).getStringValue() + "\""); + out.writeUnsignedLeb128(sourceItem.getIndex() + 1); + } else { + out.annotate("source_file: "); + out.writeByte(0); + } + } + + @Override + public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDiff, + int addressDiff) { + out.annotate("DBG_SPECIAL_OPCODE: line_diff=0x" + Integer.toHexString(lineDiff) + "(" + + lineDiff +"),addressDiff=0x" + Integer.toHexString(addressDiff) + "(" + addressDiff + + ")"); + out.writeByte(debugOpcode); + } + }); + } + + + + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_DEBUG_INFO_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "debug_info_item @0x" + Integer.toHexString(getOffset()); + } + + /** {@inheritDoc} */ + public int compareTo(DebugInfoItem other) { + if (parent == null) { + if (other.parent == null) { + return 0; + } + return -1; + } + if (other.parent == null) { + return 1; + } + return parent.compareTo(other.parent); + } + + /** + * Set the CodeItem that this DebugInfoItem is associated with + * @param codeItem the CodeItem that this DebugInfoItem is associated with + */ + protected void setParent(CodeItem codeItem) { + this.parent = codeItem; + } + + /** + * @return the initial value for the line number register for the debug info machine + */ + public int getLineStart() { + return lineStart; + } + + /** + * @return the debug info, encoded as a byte array + */ + public byte[] getEncodedDebugInfo() { + return encodedDebugInfo; + } + + /** + * @return an array of the items referenced by instructions, in order of occurance in the encoded debug info + */ + public Item[] getReferencedItems() { + return referencedItems; + } + + /** + * @return an array of the names of the associated method's parameters. The array can be null if no parameter info + * is available, or any element can be null to indicate no info for that parameter + */ + public StringIdItem[] getParameterNames() { + return parameterNames; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DexFile.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DexFile.java new file mode 100644 index 00000000..80f25f87 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DexFile.java @@ -0,0 +1,901 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.*; + +import java.io.*; +import java.security.DigestException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.zip.Adler32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + *

Main use cases

+ * + *

These are the main use cases that drove the design of this library

+ * + *
    + *
  1. Annotate an existing dex file - In this case, the intent is to document the structure of + * an existing dex file. We want to be able to read in the dex file, and then write out a dex file + * that is exactly the same (while adding annotation information to an AnnotatedOutput object)

  2. + * + *
  3. Canonicalize an existing dex file - In this case, the intent is to rewrite an existing dex file + * so that it is in a canonical form. There is a certain amount of leeway in how various types of + * tems in a dex file are ordered or represented. It is sometimes useful to be able to easily + * compare a disassebled and reassembled dex file with the original dex file. If both dex-files are + * written canonically, they "should" match exactly, barring any explicit changes to the reassembled + * file.

    + * + *

    Currently, there are a couple of pieces of information that probably won't match exactly + *

      + *
    • the order of exception handlers in the EncodedCatchHandlerList for a method
    • + *
    • the ordering of some of the debug info in the {@link org.jf.dexlib.DebugInfoItem} for a method
    • + *

    + * + * + *

    Note that the above discrepancies should typically only be "intra-item" differences. They + * shouldn't change the size of the item, or affect how anything else is placed or laid out

  4. + * + *
  5. Creating a dex file from scratch - In this case, a blank dex file is created and then classes + * are added to it incrementally by calling the {@link org.jf.dexlib.Section#intern intern} method of + * {@link DexFile#ClassDefsSection}, which will add all the information necessary to represent the given + * class. For example, when assembling a dex file from a set of assembly text files.

    + * + *

    In this case, we can choose to write the dex file in a canonical form or not. It is somewhat + * slower to write it in a canonical format, due to the extra sorting and calculations that are + * required.

  6. + * + * + *
  7. Reading in the dex file - In this case, the intent is to read in a dex file and expose all the + * data to the calling application. For example, when disassembling a dex file into a text based + * assembly format, or doing other misc processing of the dex file.

  8. + * + * + *

    Other use cases

    + * + *

    These are other use cases that are possible, but did not drive the design of the library. + * No effort was made to test these use cases or ensure that they work. Some of these could + * probably be better achieved with a disassemble - modify - reassemble type process, using + * smali/baksmali or another assembler/disassembler pair that are compatible with each other

    + * + *
      + *
    • deleting classes/methods/etc. from a dex file
    • + *
    • merging 2 dex files
    • + *
    • splitting a dex file
    • + *
    • moving classes from 1 dex file to another
    • + *
    • removing the debug information from a dex file
    • + *
    • obfustication of a dex file
    • + *
    + */ +public class DexFile +{ + /** + * A mapping from ItemType to the section that contains items of the given type + */ + private final Section[] sectionsByType; + + /** + * Ordered lists of the indexed and offsetted sections. The order of these lists specifies the order + * that the sections will be written in + */ + private final IndexedSection[] indexedSections; + private final OffsettedSection[] offsettedSections; + + /** + * dalvik had a bug where it wrote the registers for certain types of debug info in a signed leb + * format, instead of an unsigned leb format. There are no negative registers of course, but + * certain positive values have a different encoding depending on whether they are encoded as + * an unsigned leb128 or a signed leb128. Specifically, the signed leb128 is 1 byte longer in some cases. + * + * This determine whether we should keep any signed registers as signed, or force all register to + * unsigned. By default we don't keep track of whether they were signed or not, and write them back + * out as unsigned. This option only has an effect when reading an existing dex file. It has no + * effect when a dex file is created from scratch + * + * The 2 main use-cases in play are + * 1. Annotate an existing dex file - In this case, preserveSignedRegisters should be false, so that we keep + * track of any signed registers and write them back out as signed Leb128 values. + * + * 2. Canonicalize an existing dex file - In this case, fixRegisters should be true, so that all + * registers in the debug info are written as unsigned Leb128 values regardless of how they were + * originally encoded + */ + private final boolean preserveSignedRegisters; + + /** + * When true, any instructions in a code item are skipped over instead of being read in. This is useful when + * you only need the information about the classes and their methods, for example, when loading the BOOTCLASSPATH + * jars in order to analyze a dex file + */ + private final boolean skipInstructions; + + /** + * When true, this prevents any sorting of the items during placement of the dex file. This + * should *only* be set to true when this dex file was read in from an existing (valid) dex file, + * and no modifications were made (i.e. no items added or deleted). Otherwise it is likely that + * an invalid dex file will be generated. + * + * This is useful for the first use case (annotating an existing dex file). This ensures the items + * retain the same order as in the original dex file. + */ + private boolean inplace = false; + + /** + * When true, this imposes an full ordering on all the items, to force them into a (possibly + * arbitrary) canonical order. When false, only the items that the dex format specifies + * an order for are sorted. The rest of the items are not ordered. + * + * This is useful for the second use case (canonicalizing an existing dex file) or possibly for + * the third use case (creating a dex file from scratch), if there is a need to write the new + * dex file in a canonical form. + */ + private boolean sortAllItems = false; + + /** + * Is this file an odex file? This is only set when reading in an odex file + */ + private boolean isOdex = false; + + private OdexHeader odexHeader; + private OdexDependencies odexDependencies; + + private int dataOffset; + private int dataSize; + private int fileSize; + + /** + * A private constructor containing common code to initialize the section maps and lists + * @param preserveSignedRegisters If true, keep track of any registers in the debug information + * @param skipInstructions If true, skip the instructions in any code item. + * that are signed, so they will be written in the same format. See + * getPreserveSignedRegisters() + */ + private DexFile(boolean preserveSignedRegisters, boolean skipInstructions) { + this.preserveSignedRegisters = preserveSignedRegisters; + this.skipInstructions = skipInstructions; + + sectionsByType = new Section[] { + StringIdsSection, + TypeIdsSection, + ProtoIdsSection, + FieldIdsSection, + MethodIdsSection, + ClassDefsSection, + TypeListsSection, + AnnotationSetRefListsSection, + AnnotationSetsSection, + ClassDataSection, + CodeItemsSection, + AnnotationDirectoriesSection, + StringDataSection, + DebugInfoItemsSection, + AnnotationsSection, + EncodedArraysSection, + null, + null + }; + + indexedSections = new IndexedSection[] { + StringIdsSection, + TypeIdsSection, + ProtoIdsSection, + FieldIdsSection, + MethodIdsSection, + ClassDefsSection + }; + + offsettedSections = new OffsettedSection[] { + AnnotationSetRefListsSection, + AnnotationSetsSection, + CodeItemsSection, + AnnotationDirectoriesSection, + TypeListsSection, + StringDataSection, + AnnotationsSection, + EncodedArraysSection, + ClassDataSection, + DebugInfoItemsSection + }; + } + + + /** + * Construct a new DexFile instance by reading in the given dex file. + * @param file The dex file to read in + * @throws IOException if an IOException occurs + */ + public DexFile(String file) + throws IOException { + this(new File(file), true, false); + } + + /** + * Construct a new DexFile instance by reading in the given dex file, + * and optionally keep track of any registers in the debug information that are signed, + * so they will be written in the same format. + * @param file The dex file to read in + * @param preserveSignedRegisters If true, keep track of any registers in the debug information + * that are signed, so they will be written in the same format. See + * @param skipInstructions If true, skip the instructions in any code item. + * getPreserveSignedRegisters() + * @throws IOException if an IOException occurs + */ + public DexFile(String file, boolean preserveSignedRegisters, boolean skipInstructions) + throws IOException { + this(new File(file), preserveSignedRegisters, skipInstructions); + } + + /** + * Construct a new DexFile instance by reading in the given dex file. + * @param file The dex file to read in + * @throws IOException if an IOException occurs + */ + public DexFile(File file) + throws IOException { + this(file, true, false); + } + + /** + * Construct a new DexFile instance by reading in the given dex file, + * and optionally keep track of any registers in the debug information that are signed, + * so they will be written in the same format. + * @param file The dex file to read in + * @param preserveSignedRegisters If true, keep track of any registers in the debug information + * that are signed, so they will be written in the same format. + * @param skipInstructions If true, skip the instructions in any code item. + * @see #getPreserveSignedRegisters + * @throws IOException if an IOException occurs + */ + public DexFile(File file, boolean preserveSignedRegisters, boolean skipInstructions) + throws IOException { + this(preserveSignedRegisters, skipInstructions); + + long fileLength; + byte[] magic = FileUtils.readFile(file, 0, 8); + + InputStream inputStream = null; + Input in = null; + ZipFile zipFile = null; + + try { + //do we have a zip file? + if (magic[0] == 0x50 && magic[1] == 0x4B) { + zipFile = new ZipFile(file); + ZipEntry zipEntry = zipFile.getEntry("classes.dex"); + if (zipEntry == null) { + throw new NoClassesDexException("zip file " + file.getName() + " does not contain a classes.dex " + + "file"); + } + fileLength = zipEntry.getSize(); + if (fileLength < 40) { + throw new RuntimeException("The classes.dex file in " + file.getName() + " is too small to be a" + + " valid dex file"); + } else if (fileLength > Integer.MAX_VALUE) { + throw new RuntimeException("The classes.dex file in " + file.getName() + " is too large to read in"); + } + inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry)); + + inputStream.mark(8); + for (int i=0; i<8; i++) { + magic[i] = (byte)inputStream.read(); + } + inputStream.reset(); + } else { + fileLength = file.length(); + if (fileLength < 40) { + throw new RuntimeException(file.getName() + " is too small to be a valid dex file"); + } + if (fileLength < 40) { + throw new RuntimeException(file.getName() + " is too small to be a valid dex file"); + } else if (fileLength > Integer.MAX_VALUE) { + throw new RuntimeException(file.getName() + " is too large to read in"); + } + inputStream = new FileInputStream(file); + } + + byte[] dexMagic, odexMagic; + boolean isDex = false; + this.isOdex = false; + + for (int i=0; i 40) { + FileUtils.readStream(inputStream, odexHeader.dexOffset - 40); + } + + in = new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.dexLength)); + + if (dependencySkip > 0) { + FileUtils.readStream(inputStream, dependencySkip); + } + + odexDependencies = new OdexDependencies( + new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.depsLength))); + } else if (isDex) { + in = new ByteArrayInput(FileUtils.readStream(inputStream, (int)fileLength)); + } else { + StringBuffer sb = new StringBuffer("bad magic value:"); + for (int i=0; i<8; i++) { + sb.append(" "); + sb.append(Hex.u1(magic[i])); + } + throw new RuntimeException(sb.toString()); + } + } finally { + if (inputStream != null) { + inputStream.close(); + } + if (zipFile != null) { + zipFile.close(); + } + } + + ReadContext readContext = new ReadContext(); + + HeaderItem.readFrom(in, 0, readContext); + + //the map offset was set while reading in the header item + int mapOffset = readContext.getSectionOffset(ItemType.TYPE_MAP_LIST); + + in.setCursor(mapOffset); + MapItem.readFrom(in, 0, readContext); + + //the sections are ordered in such a way that the item types + Section sections[] = new Section[] { + StringDataSection, + StringIdsSection, + TypeIdsSection, + TypeListsSection, + ProtoIdsSection, + FieldIdsSection, + MethodIdsSection, + AnnotationsSection, + AnnotationSetsSection, + AnnotationSetRefListsSection, + AnnotationDirectoriesSection, + DebugInfoItemsSection, + CodeItemsSection, + ClassDataSection, + EncodedArraysSection, + ClassDefsSection + }; + + for (Section section: sections) { + if (section == null) { + continue; + } + + if (skipInstructions && (section == CodeItemsSection || section == DebugInfoItemsSection)) { + continue; + } + + int sectionOffset = readContext.getSectionOffset(section.ItemType); + if (sectionOffset > 0) { + int sectionSize = readContext.getSectionSize(section.ItemType); + in.setCursor(sectionOffset); + section.readFrom(sectionSize, in, readContext); + } + } + } + + /** + * Constructs a new, blank dex file. Classes can be added to this dex file by calling + * the Section.intern() method of ClassDefsSection + */ + public DexFile() { + this(true, false); + } + + /** + * Get the Section containing items of the same type as the given item + * @param item Get the Section that contains items of this type + * @param The specific item subclass - inferred from the passed item + * @return the Section containing items of the same type as the given item + */ + public Section getSectionForItem(T item) { + return (Section)sectionsByType[item.getItemType().SectionIndex]; + } + + /** + * Get the Section containing items of the given type + * @param itemType the type of item + * @return the Section containing items of the given type + */ + public Section getSectionForType(ItemType itemType) { + return sectionsByType[itemType.SectionIndex]; + } + + /** + * Get a boolean value indicating whether this dex file preserved any signed + * registers in the debug info as it read the dex file in. By default, the dex file + * doesn't check whether the registers are encoded as unsigned or signed values. + * + * This does *not* affect the actual register value that is read in. The value is + * read correctly regardless + * + * This does affect whether any signed registers will retain the same encoding or be + * forced to the (correct) unsigned encoding when the dex file is written back out. + * + * See the discussion about signed register values in the documentation for + * DexFile + * @return a boolean indicating whether this dex file preserved any signed registers + * as it was read in + */ + public boolean getPreserveSignedRegisters() { + return preserveSignedRegisters; + } + + /** + * Get a boolean value indicating whether to skip any instructions in a code item while reading in the dex file. + * This is useful when you only need the information about the classes and their methods, for example, when + * loading the BOOTCLASSPATH jars in order to analyze a dex file + * @return a boolean value indicating whether to skip any instructions in a code item + */ + public boolean skipInstructions() { + return skipInstructions; + } + + /** + * Get a boolean value indicating whether all items should be placed into a + * (possibly arbitrary) "canonical" ordering. If false, then only the items + * that must be ordered per the dex specification are sorted. + * + * When true, writing the dex file involves somewhat more overhead + * + * If both SortAllItems and Inplace are true, Inplace takes precedence + * @return a boolean value indicating whether all items should be sorted + */ + public boolean getSortAllItems() { + return this.sortAllItems; + } + + /** + * Set a boolean value indicating whether all items should be placed into a + * (possibly arbitrary) "canonical" ordering. If false, then only the items + * that must be ordered per the dex specification are sorted. + * + * When true, writing the dex file involves somewhat more overhead + * + * If both SortAllItems and Inplace are true, Inplace takes precedence + * @param value a boolean value indicating whether all items should be sorted + */ + public void setSortAllItems(boolean value) { + this.sortAllItems = value; + } + + /** + * @return a boolean value indicating whether this dex file was created by reading in an odex file + */ + public boolean isOdex() { + return this.isOdex; + } + + /** + * @return an OdexDependencies object that contains the dependencies for this odex, or null if this + * DexFile represents a dex file instead of an odex file + */ + public OdexDependencies getOdexDependencies() { + return odexDependencies; + } + + /** + * @return An OdexHeader object containing the information from the odex header in this dex file, or null if there + * is no odex header + */ + public OdexHeader getOdexHeader() { + return odexHeader; + } + + /** + * Get a boolean value indicating whether items in this dex file should be + * written back out "in-place", or whether the normal layout logic should be + * applied. + * + * This should only be used for a dex file that has been read from an existing + * dex file, and no modifications have been made to the dex file. Otherwise, + * there is a good chance that the resulting dex file will be invalid due to + * items that aren't placed correctly + * + * If both SortAllItems and Inplace are true, Inplace takes precedence + * @return a boolean value indicating whether items in this dex file should be + * written back out in-place. + */ + public boolean getInplace() { + return this.inplace; + } + + /** + * @return the size of the file, in bytes + */ + public int getFileSize() { + return fileSize; + } + + /** + * @return the size of the data section, in bytes + */ + public int getDataSize() { + return dataSize; + } + + /** + * @return the offset where the data section begins + */ + public int getDataOffset() { + return dataOffset; + } + + /** + * Set a boolean value indicating whether items in this dex file should be + * written back out "in-place", or whether the normal layout logic should be + * applied. + * + * This should only be used for a dex file that has been read from an existing + * dex file, and no modifications have been made to the dex file. Otherwise, + * there is a good chance that the resulting dex file will be invalid due to + * items that aren't placed correctly + * + * If both SortAllItems and Inplace are true, Inplace takes precedence + * @param value a boolean value indicating whether items in this dex file should be + * written back out in-place. + */ + public void setInplace(boolean value) { + this.inplace = value; + } + + /** + * Get an array of Section objects that are sorted by offset. + * @return an array of Section objects that are sorted by offset. + */ + protected Section[] getOrderedSections() { + int sectionCount = 0; + + for (Section section: sectionsByType) { + if (section != null && section.getItems().size() > 0) { + sectionCount++; + } + } + + Section[] sections = new Section[sectionCount]; + sectionCount = 0; + for (Section section: sectionsByType) { + if (section != null && section.getItems().size() > 0) { + sections[sectionCount++] = section; + } + } + + Arrays.sort(sections, new Comparator
    () { + public int compare(Section a, Section b) { + return a.getOffset() - b.getOffset(); + } + }); + + return sections; + } + + /** + * This method should be called before writing a dex file. It sorts the sections + * as needed or as indicated by getSortAllItems() and getInplace(), + * and then performs a pass through all of the items, finalizing the position (i.e. + * index and/or offset) of each item in the dex file. + * + * This step is needed primarily so that the indexes and offsets of all indexed and + * offsetted items are available when writing references to those items elsewhere. + */ + public void place() { + int offset = HeaderItem.placeAt(0, 0); + + int sectionsPosition = 0; + Section[] sections; + if (this.inplace) { + sections = this.getOrderedSections(); + } else { + sections = new Section[indexedSections.length + offsettedSections.length]; + System.arraycopy(indexedSections, 0, sections, 0, indexedSections.length); + System.arraycopy(offsettedSections, 0, sections, indexedSections.length, offsettedSections.length); + } + + while (sectionsPosition < sections.length && sections[sectionsPosition].ItemType.isIndexedItem()) { + Section section = sections[sectionsPosition]; + if (!this.inplace) { + section.sortSection(); + } + + offset = section.placeAt(offset); + + sectionsPosition++; + } + + dataOffset = offset; + + while (sectionsPosition < sections.length) { + Section section = sections[sectionsPosition]; + if (this.sortAllItems && !this.inplace) { + section.sortSection(); + } + offset = section.placeAt(offset); + + sectionsPosition++; + } + + offset = AlignmentUtils.alignOffset(offset, ItemType.TYPE_MAP_LIST.ItemAlignment); + offset = MapItem.placeAt(offset, 0); + + fileSize = offset; + dataSize = offset - dataOffset; + } + + /** + * Writes the dex file to the give AnnotatedOutput object. If + * out.Annotates() is true, then annotations that document the format + * of the dex file are written. + * + * You must call place() on this dex file, before calling this method + * @param out the AnnotatedOutput object to write the dex file and annotations to + * + * After calling this method, you should call calcSignature() and + * then calcChecksum() on the resulting byte array, to calculate the + * signature and checksum in the header + */ + public void writeTo(AnnotatedOutput out) { + + out.annotate(0, "-----------------------------"); + out.annotate(0, "header item"); + out.annotate(0, "-----------------------------"); + out.annotate(0, " "); + HeaderItem.writeTo(out); + + out.annotate(0, " "); + + int sectionsPosition = 0; + Section[] sections; + if (this.inplace) { + sections = this.getOrderedSections(); + } else { + sections = new Section[indexedSections.length + offsettedSections.length]; + System.arraycopy(indexedSections, 0, sections, 0, indexedSections.length); + System.arraycopy(offsettedSections, 0, sections, indexedSections.length, offsettedSections.length); + } + + while (sectionsPosition < sections.length) { + sections[sectionsPosition].writeTo(out); + sectionsPosition++; + } + + out.alignTo(MapItem.getItemType().ItemAlignment); + + out.annotate(0, " "); + out.annotate(0, "-----------------------------"); + out.annotate(0, "map item"); + out.annotate(0, "-----------------------------"); + out.annotate(0, " "); + MapItem.writeTo(out); + } + + public final HeaderItem HeaderItem = new HeaderItem(this); + public final MapItem MapItem = new MapItem(this); + + /** + * The IndexedSection containing StringIdItem items + */ + public final IndexedSection StringIdsSection = + new IndexedSection(this, ItemType.TYPE_STRING_ID_ITEM); + + /** + * The IndexedSection containing TypeIdItem items + */ + public final IndexedSection TypeIdsSection = + new IndexedSection(this, ItemType.TYPE_TYPE_ID_ITEM); + + /** + * The IndexedSection containing ProtoIdItem items + */ + public final IndexedSection ProtoIdsSection = + new IndexedSection(this, ItemType.TYPE_PROTO_ID_ITEM); + + /** + * The IndexedSection containing FieldIdItem items + */ + public final IndexedSection FieldIdsSection = + new IndexedSection(this, ItemType.TYPE_FIELD_ID_ITEM); + + /** + * The IndexedSection containing MethodIdItem items + */ + public final IndexedSection MethodIdsSection = + new IndexedSection(this, ItemType.TYPE_METHOD_ID_ITEM); + + /** + * The IndexedSection containing ClassDefItem items + */ + public final IndexedSection ClassDefsSection = + new IndexedSection(this, ItemType.TYPE_CLASS_DEF_ITEM) { + + public int placeAt(int offset) { + if (DexFile.this.getInplace()) { + return super.placeAt(offset); + } + + int ret = ClassDefItem.placeClassDefItems(this, offset); + + Collections.sort(this.items); + + this.offset = items.get(0).getOffset(); + return ret; + } + + protected void sortSection() { + // Do nothing. Sorting is handled by ClassDefItem.ClassDefPlacer, during placement + } + }; + + /** + * The OffsettedSection containing TypeListItem items + */ + public final OffsettedSection TypeListsSection = + new OffsettedSection(this, ItemType.TYPE_TYPE_LIST); + + /** + * The OffsettedSection containing AnnotationSetRefList items + */ + public final OffsettedSection AnnotationSetRefListsSection = + new OffsettedSection(this, ItemType.TYPE_ANNOTATION_SET_REF_LIST); + + /** + * The OffsettedSection containing AnnotationSetItem items + */ + public final OffsettedSection AnnotationSetsSection = + new OffsettedSection(this, ItemType.TYPE_ANNOTATION_SET_ITEM); + + /** + * The OffsettedSection containing ClassDataItem items + */ + public final OffsettedSection ClassDataSection = + new OffsettedSection(this, ItemType.TYPE_CLASS_DATA_ITEM); + + /** + * The OffsettedSection containing CodeItem items + */ + public final OffsettedSection CodeItemsSection = + new OffsettedSection(this, ItemType.TYPE_CODE_ITEM); + + /** + * The OffsettedSection containing StringDataItem items + */ + public final OffsettedSection StringDataSection = + new OffsettedSection(this, ItemType.TYPE_STRING_DATA_ITEM); + + /** + * The OffsettedSection containing DebugInfoItem items + */ + public final OffsettedSection DebugInfoItemsSection = + new OffsettedSection(this, ItemType.TYPE_DEBUG_INFO_ITEM); + + /** + * The OffsettedSection containing AnnotationItem items + */ + public final OffsettedSection AnnotationsSection = + new OffsettedSection(this, ItemType.TYPE_ANNOTATION_ITEM); + + /** + * The OffsettedSection containing EncodedArrayItem items + */ + public final OffsettedSection EncodedArraysSection = + new OffsettedSection(this, ItemType.TYPE_ENCODED_ARRAY_ITEM); + + /** + * The OffsettedSection containing AnnotationDirectoryItem items + */ + public final OffsettedSection AnnotationDirectoriesSection = + new OffsettedSection(this, ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM); + + + /** + * Calculates the signature for the dex file in the given byte array, + * and then writes the signature to the appropriate location in the header + * containing in the array + * + * @param bytes non-null; the bytes of the file + */ + public static void calcSignature(byte[] bytes) { + MessageDigest md; + + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + + md.update(bytes, 32, bytes.length - 32); + + try { + int amt = md.digest(bytes, 12, 20); + if (amt != 20) { + throw new RuntimeException("unexpected digest write: " + amt + + " bytes"); + } + } catch (DigestException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Calculates the checksum for the .dex file in the + * given array, and modify the array to contain it. + * + * @param bytes non-null; the bytes of the file + */ + public static void calcChecksum(byte[] bytes) { + Adler32 a32 = new Adler32(); + + a32.update(bytes, 12, bytes.length - 12); + + int sum = (int) a32.getValue(); + + bytes[8] = (byte) sum; + bytes[9] = (byte) (sum >> 8); + bytes[10] = (byte) (sum >> 16); + bytes[11] = (byte) (sum >> 24); + } + + public static class NoClassesDexException extends ExceptionWithContext { + public NoClassesDexException(String message) { + super(message); + } + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java new file mode 100644 index 00000000..6eb917cb --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java @@ -0,0 +1,135 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.EncodedValue.ArrayEncodedSubValue; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +public class EncodedArrayItem extends Item { + private int hashCode = 0; + + private ArrayEncodedSubValue encodedArray; + + /** + * Creates a new uninitialized EncodedArrayItem + * @param dexFile The DexFile that this item belongs to + */ + protected EncodedArrayItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new EncodedArrayItem with the given values + * @param dexFile The DexFile that this item belongs to + * @param encodedArray The encoded array value + */ + private EncodedArrayItem(DexFile dexFile, ArrayEncodedSubValue encodedArray) { + super(dexFile); + this.encodedArray = encodedArray; + } + + /** + * Returns an EncodedArrayItem for the given values, and that has been interned into the given + * DexFile + * @param dexFile The DexFile that this item belongs to + * @param encodedArray The encoded array value + * @return an EncodedArrayItem for the given values, and that has been interned into the given + */ + public static EncodedArrayItem internEncodedArrayItem(DexFile dexFile, ArrayEncodedSubValue encodedArray) { + EncodedArrayItem encodedArrayItem = new EncodedArrayItem(dexFile, encodedArray); + return dexFile.EncodedArraysSection.intern(encodedArrayItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + encodedArray = new ArrayEncodedSubValue(dexFile, in); + } + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + return encodedArray.placeValue(offset); + } + + /** {@inheritDoc} */ + protected void writeItem(AnnotatedOutput out) { + encodedArray.writeValue(out); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_ENCODED_ARRAY_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "encoded_array @0x" + Integer.toHexString(getOffset()); + } + + /** {@inheritDoc} */ + public int compareTo(EncodedArrayItem encodedArrayItem) { + return encodedArray.compareTo(encodedArrayItem.encodedArray); + } + + /** + * @return The encoded array value + */ + public ArrayEncodedSubValue getEncodedArray() { + return encodedArray; + } + + /** + * calculate and cache the hashcode + */ + private void calcHashCode() { + hashCode = encodedArray.hashCode(); + } + + @Override + public int hashCode() { + //there's a small possibility that the actual hash code will be 0. If so, we'll + //just end up recalculating it each time + if (hashCode == 0) + calcHashCode(); + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + EncodedArrayItem other = (EncodedArrayItem)o; + return (encodedArray.compareTo(other.encodedArray) == 0); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java new file mode 100644 index 00000000..acd59961 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java @@ -0,0 +1,169 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.StringIdItem; +import org.jf.dexlib.TypeIdItem; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.Leb128Utils; + +/** + * An AnnotationEncodedSubValue is identical to an AnnotationEncodedValue, except that it + * doesn't have the initial valueType/valueArg byte. This is used in the AnnotationItem object + */ +public class AnnotationEncodedSubValue extends EncodedValue { + private int hashCode = 0; + + public final TypeIdItem annotationType; + public final StringIdItem[] names; + public final EncodedValue[] values; + + /** + * Constructs a new AnnotationEncodedSubValue by reading the value from the given Input + * object. + * @param dexFile The DexFile that is being read in + * @param in The Input object to read from + */ + public AnnotationEncodedSubValue(DexFile dexFile, Input in) { + annotationType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128()); + names = new StringIdItem[in.readUnsignedLeb128()]; + values = new EncodedValue[names.length]; + + for (int i=0; iAnnotationEncodedValue with the given values. names and values must be the same + * length, and must be sorted according to the name + * @param annotationType The type of the annotation + * @param names An array of the names of the elements of the annotation + * @param values An array of the values of the elements on the annotation + */ + public AnnotationEncodedSubValue(TypeIdItem annotationType, StringIdItem[] names, EncodedValue[] values) { + this.annotationType = annotationType; + if (names.length != values.length) { + throw new RuntimeException("The names and values parameters must be the same length"); + } + this.names = names; + this.values = values; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + out.annotate("annotation_type: " + annotationType.getTypeDescriptor()); + out.writeUnsignedLeb128(annotationType.getIndex()); + out.annotate("element_count: 0x" + Integer.toHexString(names.length) + " (" + names.length + ")"); + out.writeUnsignedLeb128(names.length); + + for (int i=0; iAnnotationEncodedValue by reading the value from the given Input + * object. The Input's cursor should be set to the 2nd byte of the encoded value + * @param dexFile The DexFile that is being read in + * @param in The Input object to read from + */ + protected AnnotationEncodedValue(DexFile dexFile, Input in) { + super(dexFile, in); + } + + /** + * Constructs a new AnnotationEncodedValue with the given values. names and values must be the same + * length, and must be sorted according to the name + * @param annotationType The type of the annotation + * @param names An array of the names of the elements of the annotation + * @param values An array of the values of the elements on the annotation + */ + public AnnotationEncodedValue(TypeIdItem annotationType, StringIdItem[] names, EncodedValue[] values) { + super(annotationType, names, values); + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate("value_type=" + ValueType.VALUE_ANNOTATION.name() + ",value_arg=0"); + } + out.writeByte(ValueType.VALUE_ANNOTATION.value); + super.writeValue(out); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return super.placeValue(offset + 1); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java new file mode 100644 index 00000000..f79db736 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java @@ -0,0 +1,141 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.Leb128Utils; + +/** + * An ArrayEncodedSubValue is identical to an ArrayEncodedValue, except that it + * doesn't have the initial valueType/valueArg byte. This is used in the EncodedArrayItem object + */ +public class ArrayEncodedSubValue extends EncodedValue { + private int hashCode = 0; + + public final EncodedValue[] values; + + /** + * Constructs a new ArrayEncodedSubValue by reading the value from the given Input object. + * The Input's cursor should be set to the 2nd byte of the encoded value + * @param dexFile The DexFile that is being read in + * @param in The Input object to read from + */ + public ArrayEncodedSubValue(DexFile dexFile, Input in) { + values = new EncodedValue[in.readUnsignedLeb128()]; + + for (int i=0; iArrayEncodedSubValue with the given values + * @param values The array values + */ + public ArrayEncodedSubValue(EncodedValue[] values) { + this.values = values; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + if (out.annotates()) + { + out.annotate("array_size: 0x" + Integer.toHexString(values.length) + " (" + values.length + ")"); + out.writeUnsignedLeb128(values.length); + int index = 0; + for (EncodedValue encodedValue: values) { + out.annotate(0, "[" + index++ + "] array_element"); + out.indent(); + encodedValue.writeValue(out); + out.deindent(); + } + } else { + out.writeUnsignedLeb128(values.length); + for (EncodedValue encodedValue: values) { + encodedValue.writeValue(out); + } + } + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + offset = offset + Leb128Utils.unsignedLeb128Size(values.length); + for (EncodedValue encodedValue: values) { + offset = encodedValue.placeValue(offset); + } + + return offset; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + ArrayEncodedSubValue other = (ArrayEncodedSubValue)o; + + int comp = values.length - other.values.length; + if (comp != 0) { + return comp; + } + + for (int i=0; iArrayEncodedValue by reading the value from the given Input object. + * The Input's cursor should be set to the 2nd byte of the encoded value + * @param dexFile The DexFile that is being read in + * @param in The Input object to read from + */ + protected ArrayEncodedValue(DexFile dexFile, Input in) { + super(dexFile, in); + } + + /** + * Constructs a new ArrayEncodedValue with the given values + * @param values The array values + */ + public ArrayEncodedValue(EncodedValue[] values) { + super(values); + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate("value_type=" + ValueType.VALUE_ARRAY.name() + ",value_arg=0"); + } + out.writeByte(ValueType.VALUE_ARRAY.value); + super.writeValue(out); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return super.placeValue(offset + 1); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java new file mode 100644 index 00000000..507e58fe --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java @@ -0,0 +1,109 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.Util.AnnotatedOutput; + +public class BooleanEncodedValue extends EncodedValue { + /** + * The dupliton values + */ + public static final BooleanEncodedValue TrueValue = new BooleanEncodedValue(true); + public static final BooleanEncodedValue FalseValue = new BooleanEncodedValue(false); + + public final boolean value; + + /** + * Constructs a new BooleanEncodedValue with the given value + * @param value The value + */ + private BooleanEncodedValue(boolean value) { + this.value = value; + } + + /** + * Gets the BooleanEncodedValue for the given valueArg value. The high 3 bits of the first byte should + * be passed as the valueArg parameter + * @param valueArg The high 3 bits of the first byte of this encoded value + * @return the BooleanEncodedValue for the given valueArg value + */ + protected static BooleanEncodedValue getBooleanEncodedValue(byte valueArg) { + if (valueArg == 0) { + return FalseValue; + } else if (valueArg == 1) { + return TrueValue; + } + throw new RuntimeException("valueArg must be either 0 or 1"); + } + + /** + * Gets the BooleanEncodedValue for the given boolean value + * @param value the boolean value + * @return the BooleanEncodedValue for the given boolean value + */ + public static BooleanEncodedValue getBooleanEncodedValue(boolean value) { + if (value) { + return TrueValue; + } + return FalseValue; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate("value_type=" + ValueType.VALUE_BOOLEAN.name() + ",value=" + Boolean.toString(value)); + } + out.writeByte(ValueType.VALUE_BOOLEAN.value | ((value?1:0) << 5)); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + BooleanEncodedValue other = (BooleanEncodedValue)o; + if (value == other.value) + return 0; + if (value) + return 1; + return -1; + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_BOOLEAN; + } + + @Override + public int hashCode() { + return value?1:0; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java new file mode 100644 index 00000000..683d547e --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java @@ -0,0 +1,86 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class ByteEncodedValue extends EncodedValue { + public final byte value; + + /** + * Constructs a new ByteEncodedValue by reading the value from the given Input object. + * The Input's cursor should be set to the 2nd byte of the encoded value + * @param in The Input object to read from + */ + protected ByteEncodedValue(Input in) { + value = (byte)EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(1)); + } + + /** + * Constructs a new ByteEncodedValue with the given value + * @param value The value + */ + public ByteEncodedValue(byte value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_BYTE.name() + ",value_arg=0"); + out.annotate(1, "value: 0x" + Integer.toHexString(value) + " (" + value + ")"); + } + out.writeByte(ValueType.VALUE_BYTE.value); + out.writeByte(value); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + 2; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + ByteEncodedValue other = (ByteEncodedValue)o; + + return (valueother.value?1:0)); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_BYTE; + } + + @Override + public int hashCode() { + return value; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java new file mode 100644 index 00000000..25646612 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java @@ -0,0 +1,93 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class CharEncodedValue extends EncodedValue { + public final char value; + + /** + * Constructs a new CharEncodedValue by reading the value from the given Input object. + * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of + * the first byte should be passed as the valueArg parameter + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected CharEncodedValue(Input in, byte valueArg) { + value = (char)EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); + } + + /** + * Constructs a new CharEncodedValue with the given value + * @param value The value + */ + public CharEncodedValue(char value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_CHAR.name() + ",value_arg=" + (bytes.length - 1)); + char[] c = Character.toChars(value); + assert c.length > 0; + out.annotate(bytes.length, "value: 0x" + Integer.toHexString(value) + " '" + c[0] + "'"); + } + + out.writeByte(ValueType.VALUE_CHAR.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value) + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + CharEncodedValue other = (CharEncodedValue)o; + + return (valueother.value?1:0)); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_CHAR; + } + + @Override + public int hashCode() { + return value; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java new file mode 100644 index 00000000..6b8bcc37 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java @@ -0,0 +1,94 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class DoubleEncodedValue extends EncodedValue { + public final double value; + + /** + * Constructs a new DoubleEncodedValue by reading the value from the given Input object. + * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of + * the first byte should be passed as the valueArg parameter + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected DoubleEncodedValue(Input in, byte valueArg) { + long longValue = EncodedValueUtils.decodeRightZeroExtendedValue(in.readBytes(valueArg + 1)); + value = Double.longBitsToDouble(longValue); + } + + /** + * Constructs a new DoubleEncodedValue with the given value + * @param value The value + */ + public DoubleEncodedValue(double value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeRightZeroExtendedValue(Double.doubleToRawLongBits(value)); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_DOUBLE.name() + ",value_arg=" + (bytes.length - 1)); + out.annotate(bytes.length, "value: " + value); + } + + out.writeByte(ValueType.VALUE_DOUBLE.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + 1 + EncodedValueUtils.getRequiredBytesForRightZeroExtendedValue( + Double.doubleToRawLongBits(value)); + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + DoubleEncodedValue other = (DoubleEncodedValue)o; + + return Double.compare(value, other.value); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_DOUBLE; + } + + @Override + public int hashCode() { + return (int)Double.doubleToRawLongBits(value); + } +} + diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java new file mode 100644 index 00000000..4c00faf2 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java @@ -0,0 +1,125 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +public abstract class EncodedValue implements Comparable { + /** + * Writes this EncodedValue to the given AnnotatedOutput object + * @param out the AnnotatedOutput object to write to + */ + public abstract void writeValue(AnnotatedOutput out); + + /** + * Calculates the size of this encoded value and returns offset + size; + * @param offset The offset to place this encoded value + * @return the offset immediately after this encoded value + */ + public abstract int placeValue(int offset); + + + public static EncodedValue readEncodedValue(DexFile dexFile, Input in) { + Byte b = in.readByte(); + ValueType valueType = ValueType.fromByte((byte)(b & 0x1f)); + byte valueArg = (byte)((b & 0xFF) >> 5); + + switch (valueType) { + case VALUE_BYTE: + return new ByteEncodedValue(in); + case VALUE_SHORT: + return new ShortEncodedValue(in, valueArg); + case VALUE_CHAR: + return new CharEncodedValue(in, valueArg); + case VALUE_INT: + return new IntEncodedValue(in, valueArg); + case VALUE_LONG: + return new LongEncodedValue(in, valueArg); + case VALUE_FLOAT: + return new FloatEncodedValue(in, valueArg); + case VALUE_DOUBLE: + return new DoubleEncodedValue(in, valueArg); + case VALUE_STRING: + return new StringEncodedValue(dexFile, in, valueArg); + case VALUE_TYPE: + return new TypeEncodedValue(dexFile, in, valueArg); + case VALUE_FIELD: + return new FieldEncodedValue(dexFile, in, valueArg); + case VALUE_METHOD: + return new MethodEncodedValue(dexFile, in, valueArg); + case VALUE_ENUM: + return new EnumEncodedValue(dexFile, in, valueArg); + case VALUE_ARRAY: + return new ArrayEncodedValue(dexFile, in); + case VALUE_ANNOTATION: + return new AnnotationEncodedValue(dexFile, in); + case VALUE_NULL: + return NullEncodedValue.NullValue; + case VALUE_BOOLEAN: + return BooleanEncodedValue.getBooleanEncodedValue(valueArg); + } + return null; + } + + /** {@inheritDoc} */ + public int compareTo(EncodedValue o) { + int comp = getValueType().compareTo(o.getValueType()); + if (comp == 0) { + comp = compareValue(o); + } + return comp; + } + + /** + * Compare the value of this EncodedValue against the value of the given , which + * is guaranteed to be of the same type as this EncodedValue + * @param o The EncodedValue to compare against + * @return A standard comparison integer value + */ + protected abstract int compareValue(EncodedValue o); + + /** + * @return the ValueType representing the type of this EncodedValue + */ + public abstract ValueType getValueType(); + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !(o instanceof EncodedValue)) { + return false; + } + + return this.compareTo((EncodedValue)o) == 0; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java new file mode 100644 index 00000000..7cd1f45a --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java @@ -0,0 +1,95 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.FieldIdItem; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class EnumEncodedValue extends EncodedValue { + public final FieldIdItem value; + + /** + * Constructs a new EnumEncodedValue by reading the field index from the given Input + * object. The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits + * of the first byte should be passed as the valueArg parameter + * @param dexFile The DexFile that is being read in + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected EnumEncodedValue(DexFile dexFile, Input in, byte valueArg) { + int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); + value = dexFile.FieldIdsSection.getItemByIndex(index); + } + + /** + * Constructs a new EnumEncodedValue with the given FieldIdItem value + * @param value The FieldIdItem value + */ + public EnumEncodedValue(FieldIdItem value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex()); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_ENUM.name() + ",value_arg=" + (bytes.length - 1)); + out.annotate(bytes.length, "value: " + value.getFieldString()); + } + + out.writeByte(ValueType.VALUE_ENUM.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + EnumEncodedValue other = (EnumEncodedValue)o; + + return value.compareTo(other.value); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_ENUM; + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java new file mode 100644 index 00000000..6aafc621 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java @@ -0,0 +1,95 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.FieldIdItem; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class FieldEncodedValue extends EncodedValue { + public final FieldIdItem value; + + /** + * Constructs a new FieldEncodedValue by reading the field index from the given Input + * object. The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits + * of the first byte should be passed as the valueArg parameter + * @param dexFile The DexFile that is being read in + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected FieldEncodedValue(DexFile dexFile, Input in, byte valueArg) { + int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); + value = dexFile.FieldIdsSection.getItemByIndex(index); + } + + /** + * Constructs a new FieldEncodedValue with the given FieldIdItem value + * @param value The FieldIdItem value + */ + public FieldEncodedValue(FieldIdItem value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex()); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_FIELD.name() + ",value_arg=" + (bytes.length - 1)); + out.annotate(bytes.length, "value: " + value.getFieldString()); + } + + out.writeByte(ValueType.VALUE_FIELD.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + FieldEncodedValue other = (FieldEncodedValue)o; + + return value.compareTo(other.value); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_FIELD; + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java new file mode 100644 index 00000000..af514f4f --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java @@ -0,0 +1,93 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class FloatEncodedValue extends EncodedValue { + public final float value; + + /** + * Constructs a new FloatEncodedValue by reading the value from the given Input object. + * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of + * the first byte should be passed as the valueArg parameter + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected FloatEncodedValue(Input in, byte valueArg) { + long longValue = EncodedValueUtils.decodeRightZeroExtendedValue(in.readBytes(valueArg + 1)); + value = Float.intBitsToFloat((int)((longValue >> 32) & 0xFFFFFFFFL)); + } + + /** + * Constructs a new FloatEncodedValue with the given value + * @param value The value + */ + public FloatEncodedValue(float value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeRightZeroExtendedValue(((long)Float.floatToRawIntBits(value)) << 32); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_FLOAT.name() + ",value_arg=" + (bytes.length - 1)); + out.annotate(bytes.length, "value: " + value); + } + + out.writeByte(ValueType.VALUE_FLOAT.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + 1 + EncodedValueUtils.getRequiredBytesForRightZeroExtendedValue( + ((long)Float.floatToRawIntBits(value)) << 32); + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + FloatEncodedValue other = (FloatEncodedValue)o; + + return Float.compare(value, other.value); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_FLOAT; + } + + @Override + public int hashCode() { + return Float.floatToRawIntBits(value); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java new file mode 100644 index 00000000..7da5b021 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java @@ -0,0 +1,91 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class IntEncodedValue extends EncodedValue { + public final int value; + + /** + * Constructs a new IntEncodedValue by reading the value from the given Input object. + * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of + * the first byte should be passed as the valueArg parameter + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected IntEncodedValue(Input in, byte valueArg) { + value = (int)EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1)); + } + + /** + * Constructs a new IntEncodedValue with the given value + * @param value The value + */ + public IntEncodedValue(int value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_INT.name() + ",value_arg=" + (bytes.length - 1)); + out.annotate(bytes.length, "value: 0x" + Integer.toHexString(value) + " (" + value + ")"); + } + + out.writeByte(ValueType.VALUE_INT.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + IntEncodedValue other = (IntEncodedValue)o; + + return (valueother.value?1:0)); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_INT; + } + + @Override + public int hashCode() { + return value; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java new file mode 100644 index 00000000..7db6ed13 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java @@ -0,0 +1,91 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class LongEncodedValue extends EncodedValue { + public final long value; + + /** + * Constructs a new LongEncodedValue by reading the value from the given Input object. + * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of + * the first byte should be passed as the valueArg parameter + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected LongEncodedValue(Input in, byte valueArg) { + value = EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1)); + } + + /** + * Constructs a new LongEncodedValue with the given value + * @param value The value + */ + public LongEncodedValue(long value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_LONG.name() + ",value_arg=" + (bytes.length - 1)); + out.annotate(bytes.length, "value: 0x" + Long.toHexString(value) + " (" + value + ")"); + } + + out.writeByte(ValueType.VALUE_LONG.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + LongEncodedValue other = (LongEncodedValue)o; + + return (valueother.value?1:0)); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_LONG; + } + + @Override + public int hashCode() { + return (int)value; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java new file mode 100644 index 00000000..e23450a2 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java @@ -0,0 +1,95 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.MethodIdItem; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class MethodEncodedValue extends EncodedValue { + public final MethodIdItem value; + + /** + * Constructs a new MethodEncodedValue by reading the method index from the given Input + * object. The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits + * of the first byte should be passed as the valueArg parameter + * @param dexFile The DexFile that is being read in + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected MethodEncodedValue(DexFile dexFile, Input in, byte valueArg) { + int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); + value = dexFile.MethodIdsSection.getItemByIndex(index); + } + + /** + * Constructs a new MethodEncodedValue with the given MethodIdItem value + * @param value The MethodIdItem value + */ + public MethodEncodedValue(MethodIdItem value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex()); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_METHOD.name() + ",value_arg=" + (bytes.length - 1)); + out.annotate(bytes.length, "value: " + value.getMethodString()); + } + + out.writeByte(ValueType.VALUE_METHOD.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + MethodEncodedValue other = (MethodEncodedValue)o; + + return value.compareTo(other.value); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_METHOD; + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java new file mode 100644 index 00000000..334b82a3 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java @@ -0,0 +1,72 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.Util.AnnotatedOutput; + +public class NullEncodedValue extends EncodedValue { + /** + * The singleton value + */ + public static final NullEncodedValue NullValue = new NullEncodedValue(); + + /** + * Constructs a new NullEncodedValue + */ + private NullEncodedValue() { + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate("value_type=" + ValueType.VALUE_NULL.name() + ",value_arg=0"); + } + out.writeByte(ValueType.VALUE_NULL.value); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + return 0; + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_NULL; + } + + @Override + public int hashCode() { + return 1; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java new file mode 100644 index 00000000..66d80e17 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java @@ -0,0 +1,91 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class ShortEncodedValue extends EncodedValue { + public final short value; + + /** + * Constructs a new ShortEncodedValue by reading the value from the given Input object. + * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of + * the first byte should be passed as the valueArg parameter + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected ShortEncodedValue(Input in, byte valueArg) { + value = (short) EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1)); + } + + /** + * Constructs a new ShortEncodedValue with the given value + * @param value The value + */ + public ShortEncodedValue(short value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_SHORT.name() + ",value_arg=" + (bytes.length - 1)); + out.annotate(bytes.length, "value: 0x" + Integer.toHexString(value) + " (" + value + ")"); + } + + out.writeByte(ValueType.VALUE_SHORT.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + ShortEncodedValue other = (ShortEncodedValue)o; + + return (valueother.value?1:0)); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_SHORT; + } + + @Override + public int hashCode() { + return value; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java new file mode 100644 index 00000000..8b323945 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java @@ -0,0 +1,96 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.StringIdItem; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.Utf8Utils; + +public class StringEncodedValue extends EncodedValue { + public final StringIdItem value; + + /** + * Constructs a new StringEncodedValue by reading the string index from the given Input + * object. The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits + * of the first byte should be passed as the valueArg parameter + * @param dexFile The DexFile that is being read in + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected StringEncodedValue(DexFile dexFile, Input in, byte valueArg) { + int index = (int)EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); + value = dexFile.StringIdsSection.getItemByIndex(index); + } + + /** + * Constructs a new StringEncodedValue with the given StringIdItem value + * @param value The StringIdItem value + */ + public StringEncodedValue(StringIdItem value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex()); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_STRING.name() + ",value_arg=" + (bytes.length - 1)); + out.annotate(bytes.length, "value: \"" + Utf8Utils.escapeString(value.getStringValue()) + "\""); + } + + out.writeByte(ValueType.VALUE_STRING.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + StringEncodedValue other = (StringEncodedValue)o; + + return value.compareTo(other.value); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_STRING; + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java new file mode 100644 index 00000000..335aab65 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java @@ -0,0 +1,95 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.DexFile; +import org.jf.dexlib.TypeIdItem; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.EncodedValueUtils; +import org.jf.dexlib.Util.Input; + +public class TypeEncodedValue extends EncodedValue { + public final TypeIdItem value; + + /** + * Constructs a new TypeEncodedValue by reading the type index from the given Input + * object. The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits + * of the first byte should be passed as the valueArg parameter + * @param dexFile The DexFile that is being read in + * @param in The Input object to read from + * @param valueArg The high 3 bits of the first byte of this encoded value + */ + protected TypeEncodedValue(DexFile dexFile, Input in, byte valueArg) { + int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); + value = dexFile.TypeIdsSection.getItemByIndex(index); + } + + /** + * Constructs a new TypeEncodedValue with the given TypeIdItem value + * @param value The TypeIdItem value + */ + public TypeEncodedValue(TypeIdItem value) { + this.value = value; + } + + /** {@inheritDoc} */ + public void writeValue(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex()); + + if (out.annotates()) { + out.annotate(1, "value_type=" + ValueType.VALUE_TYPE.name() + ",value_arg=" + (bytes.length - 1)); + out.annotate(bytes.length, "value: " + value.getTypeDescriptor()); + } + + out.writeByte(ValueType.VALUE_TYPE.value | ((bytes.length - 1) << 5)); + out.write(bytes); + } + + /** {@inheritDoc} */ + public int placeValue(int offset) { + return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1; + } + + /** {@inheritDoc} */ + protected int compareValue(EncodedValue o) { + TypeEncodedValue other = (TypeEncodedValue)o; + + return value.compareTo(other.value); + } + + /** {@inheritDoc} */ + public ValueType getValueType() { + return ValueType.VALUE_TYPE; + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java new file mode 100644 index 00000000..d33b0ac2 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java @@ -0,0 +1,86 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.EncodedValue; + +import org.jf.dexlib.Util.SparseArray; + +public enum ValueType { + + VALUE_BYTE((byte) 0x00), + VALUE_SHORT((byte) 0x02), + VALUE_CHAR((byte) 0x03), + VALUE_INT((byte) 0x04), + VALUE_LONG((byte) 0x06), + VALUE_FLOAT((byte) 0x10), + VALUE_DOUBLE((byte) 0x11), + VALUE_STRING((byte) 0x17), + VALUE_TYPE((byte) 0x18), + VALUE_FIELD((byte) 0x19), + VALUE_METHOD((byte) 0x1a), + VALUE_ENUM((byte) 0x1b), + VALUE_ARRAY((byte) 0x1c), + VALUE_ANNOTATION((byte) 0x1d), + VALUE_NULL((byte) 0x1e), + VALUE_BOOLEAN((byte) 0x1f); + + /** + * A map to facilitate looking up a ValueType by byte value + */ + private final static SparseArray valueTypeIntegerMap; + + static { + /** build the valueTypeIntegerMap object */ + valueTypeIntegerMap = new SparseArray(16); + + for (ValueType valueType : ValueType.values()) { + valueTypeIntegerMap.put(valueType.value, valueType); + } + } + + /** + * The byte value for this ValueType + */ + public final byte value; + + private ValueType(byte value) { + this.value = value; + } + + /** + * Converts a byte value to the corresponding ValueType enum value, + * or null if the value isn't a valid ValueType value + * + * @param valueType the byte value to convert to a ValueType + * @return the ValueType enum value corresponding to valueType, or null + * if not a valid ValueType value + */ + public static ValueType fromByte(byte valueType) { + return valueTypeIntegerMap.get(valueType); + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java new file mode 100644 index 00000000..c9fe05f0 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java @@ -0,0 +1,263 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +public class FieldIdItem extends Item implements Convertible { + private int hashCode = 0; + + private TypeIdItem classType; + private TypeIdItem fieldType; + private StringIdItem fieldName; + + /** + * Creates a new uninitialized FieldIdItem + * @param dexFile The DexFile that this item belongs to + */ + protected FieldIdItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new FieldIdItem for the given class, type and name + * @param dexFile The DexFile that this item belongs to + * @param classType the class that the field is a member of + * @param fieldType the type of the field + * @param fieldName the name of the field + */ + private FieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType, StringIdItem fieldName) { + this(dexFile); + + assert classType.dexFile == dexFile; + assert fieldType.dexFile == dexFile; + assert fieldName.dexFile == dexFile; + + this.classType = classType; + this.fieldType = fieldType; + this.fieldName = fieldName; + } + + /** + * Returns a FieldIdItem for the given values, and that has been interned into + * the given DexFile + * @param dexFile The DexFile that this item belongs to + * @param classType the class that the field is a member of + * @param fieldType the type of the field + * @param fieldName the name of the field + * @return a FieldIdItem for the given values, and that has been interned into + * the given DexFile + */ + public static FieldIdItem internFieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType, + StringIdItem fieldName) { + FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, fieldType, fieldName); + return dexFile.FieldIdsSection.intern(fieldIdItem); + } + + /** + * Looks up a FieldIdItem from the given DexFile for the given + * values + * @param dexFile The DexFile that this item belongs to + * @param classType the class that the field is a member of + * @param fieldType the type of the field + * @param fieldName the name of the field + * @return a FieldIdItem from the given DexFile for the given + * values, or null if it doesn't exist + */ + public static FieldIdItem lookupFieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType, + StringIdItem fieldName) { + FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, fieldType, fieldName); + return dexFile.FieldIdsSection.getInternedItem(fieldIdItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + classType = dexFile.TypeIdsSection.getItemByIndex(in.readShort()); + fieldType = dexFile.TypeIdsSection.getItemByIndex(in.readShort()); + fieldName = dexFile.StringIdsSection.getItemByIndex(in.readInt()); + } + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + return offset + 8; + } + + /** {@inheritDoc} */ + protected void writeItem(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate(2, "class_type: " + classType.getTypeDescriptor()); + out.annotate(2, "field_type: " + fieldType.getTypeDescriptor()); + out.annotate(4, "field_name: " + fieldName.getStringValue()); + } + + int classIndex = classType.getIndex(); + if (classIndex > 0xffff) { + throw new RuntimeException(String.format("Error writing field_id_item for %s. The type index of " + + "defining class %s is too large", getFieldString(), classType.getTypeDescriptor())); + } + out.writeShort(classIndex); + + int typeIndex = fieldType.getIndex(); + if (typeIndex > 0xffff) { + throw new RuntimeException(String.format("Error writing field_id_item for %s. The type index of field " + + "type %s is too large", getFieldString(), fieldType.getTypeDescriptor())); + } + out.writeShort(typeIndex); + + out.writeInt(fieldName.getIndex()); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_FIELD_ID_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return getFieldString(); + } + + /** {@inheritDoc} */ + public int compareTo(FieldIdItem o) { + int result = classType.compareTo(o.classType); + if (result != 0) { + return result; + } + + result = fieldName.compareTo(o.fieldName); + if (result != 0) { + return result; + } + + return fieldType.compareTo(o.fieldType); + } + + /** + * @return the class that this field is a member of + */ + public TypeIdItem getContainingClass() { + return classType; + } + + /** + * @return the type of this field + */ + public TypeIdItem getFieldType() { + return fieldType; + } + + /** + * @return the field name + */ + public StringIdItem getFieldName() { + return fieldName; + } + + String cachedFieldString = null; + /** + * @return a string formatted like LclassName;->fieldName:fieldType + */ + public String getFieldString() { + if (cachedFieldString == null) { + String typeDescriptor = classType.getTypeDescriptor(); + String fieldName = this.fieldName.getStringValue(); + String fieldType = this.fieldType.getTypeDescriptor(); + + StringBuffer sb = new StringBuffer(typeDescriptor.length() + fieldName.length() + fieldType.length() + 3); + sb.append(typeDescriptor); + sb.append("->"); + sb.append(fieldName); + sb.append(":"); + sb.append(fieldType); + cachedFieldString = sb.toString(); + } + return cachedFieldString; + } + + String cachedShortFieldString = null; + /** + * @return a "short" string containing just the field name and type, formatted like fieldName:fieldType + */ + public String getShortFieldString() { + if (cachedShortFieldString == null) { + String fieldName = this.fieldName.getStringValue(); + String fieldType = this.fieldType.getTypeDescriptor(); + + StringBuffer sb = new StringBuffer(fieldName.length() + fieldType.length() + 1); + sb.append(fieldName); + sb.append(":"); + sb.append(fieldType); + cachedShortFieldString = sb.toString(); + } + return cachedShortFieldString; + } + + + /** + * calculate and cache the hashcode + */ + private void calcHashCode() { + hashCode = classType.hashCode(); + hashCode = 31 * hashCode + fieldType.hashCode(); + hashCode = 31 * hashCode + fieldName.hashCode(); + } + + @Override + public int hashCode() { + //there's a small possibility that the actual hash code will be 0. If so, we'll + //just end up recalculating it each time + if (hashCode == 0) + calcHashCode(); + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + //This assumes that the referenced items have been interned in both objects. + //This is a valid assumption because all outside code must use the static + //"getInterned..." style methods to make new items, and any item created + //internally is guaranteed to be interned + FieldIdItem other = (FieldIdItem)o; + return (classType == other.classType && + fieldType == other.fieldType && + fieldName == other.fieldName); + } + + public FieldIdItem convert() { + return this; + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java new file mode 100644 index 00000000..e3f9a0f3 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java @@ -0,0 +1,301 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import com.google.common.base.Preconditions; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.Utf8Utils; + +public class HeaderItem extends Item { + /** + * the file format magic number, represented as the + * low-order bytes of a string + */ + public static final byte[][] MAGIC_VALUES = new byte[][] { + new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00}, //"dex\n035" + '\0'; + new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x36, 0x00}}; //"dex\n036" + '\0'; + + + /** size of this section, in bytes */ + private static final int HEADER_SIZE = 0x70; + + /** the endianness constants */ + private static final int LITTLE_ENDIAN = 0x12345678; + private static final int BIG_ENDIAN = 0x78563412; + + /* Which magic value to use when writing out the header item */ + private int magic_index = 0; + + private boolean checksumSignatureSet = false; + private int checksum; + private byte[] signature; + + /** + * Create a new uninitialized HeaderItem + * @param dexFile The DexFile containing this HeaderItem + */ + protected HeaderItem(final DexFile dexFile) { + super(dexFile); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + byte[] readMagic = in.readBytes(8); + + boolean success = false; + for (int i=0; i extends Section { + + /** + * Create a new indexed section + * @param dexFile The DexFile that this section belongs to + * @param itemType The itemType that this section will hold + */ + public IndexedSection(DexFile dexFile, ItemType itemType) { + super(dexFile, itemType); + } + + /** {@inheritDoc} */ + protected void readItems(Input in, ReadContext readContext) { + for (int i = 0; i < items.size(); i++) { + T item = (T)ItemFactory.makeItem(ItemType, DexFile); + items.set(i, item); + item.readFrom(in, i, readContext); + } + } + + /** + * Gets the item at the specified index in this section, or null if the index is -1 + * @param index the index of the item to get + * @return the item at the specified index in this section, or null if the index is -1 + */ + public T getOptionalItemByIndex(int index) { + if (index == -1) { + return null; + } + + return getItemByIndex(index); + } + + /** + * Gets the item at the specified index in this section + * @param index the index of the item to get + * @return the item at the specified index in this section + */ + public T getItemByIndex(int index) { + try { + //if index is out of bounds, just let it throw an exception + return items.get(index); + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, "Error occured while retrieving the " + this.ItemType.TypeName + + " item at index " + index); + } + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Item.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Item.java new file mode 100644 index 00000000..98c23389 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Item.java @@ -0,0 +1,224 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import com.google.common.base.Preconditions; +import org.jf.dexlib.Util.AlignmentUtils; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.ExceptionWithContext; +import org.jf.dexlib.Util.Input; + +public abstract class Item implements Comparable { + /** + * The offset of this item in the dex file, or -1 if not known + */ + protected int offset = -1; + + /** + * The index of this item in the containing section, or -1 if not known + */ + protected int index = -1; + + /** + * The DexFile that this item is associatedr with + */ + protected final DexFile dexFile; + + /** + * The constructor that is used when reading in a DexFile + * @param dexFile the DexFile that this item is associated with + */ + protected Item(DexFile dexFile) { + assert dexFile != null; + + this.dexFile = dexFile; + } + + /** + * Read in the item from the given input stream, and initialize the index + * @param in the Input object to read from + * @param index the index within the containing section of the item being read in + * @param readContext a ReadContext object to hold information that is + * only needed while reading in a file + */ + protected void readFrom(Input in, int index, ReadContext readContext) { + try { + assert AlignmentUtils.isAligned(in.getCursor(), getItemType().ItemAlignment); + + this.offset = in.getCursor(); + this.index = index; + + this.readItem(in, readContext); + } catch (Exception ex) { + throw addExceptionContext(ex); + } + } + + /** + * Place the item at the given offset and index, and return the offset of the byte following this item + * @param offset The offset to place the item at + * @param index The index of the item within the containing section + * @return The offset of the byte following this item + */ + protected int placeAt(int offset, int index) { + try { + assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment); + assert !dexFile.getInplace() || (offset == this.offset && this.index == index); + + this.offset = offset; + this.index = index; + return this.placeItem(offset); + } catch (Exception ex) { + throw addExceptionContext(ex); + } + } + + /** + * Write and annotate this item to the output stream + * @param out The output stream to write and annotate to + */ + protected void writeTo(AnnotatedOutput out) { + try { + assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment); + //ensure that it is being written to the same offset where it was previously placed + assert out.getCursor() == offset; + + if (out.annotates()) { + out.annotate(0, "[" + index + "] " + this.getItemType().TypeName); + } + + out.indent(); + writeItem(out); + out.deindent(); + } catch (Exception ex) { + throw addExceptionContext(ex); + } + } + + /** + * Returns a human readable form of this item + * @return a human readable form of this item + */ + public String toString() { + return getConciseIdentity(); + } + + /** + * The method in the concrete item subclass that actually reads in the data for the item + * + * The logic in this method can assume that the given Input object is valid and is + * aligned as neccessary. + * + * This method is for internal use only + * @param in the Input object to read from + * @param readContext a ReadContext object to hold information that is + * only needed while reading in a file + */ + protected abstract void readItem(Input in, ReadContext readContext); + + /** + * The method should finalize the layout of the item and return the offset of the byte + * immediately following the item. + * + * The implementation of this method can assume that the offset argument has already been + * aligned based on the item's alignment requirements + * + * This method is for internal use only + * @param offset the (pre-aligned) offset to place the item at + * @return the size of the item, in bytes + */ + protected abstract int placeItem(int offset); + + /** + * The method in the concrete item subclass that actually writes and annotates the data + * for the item. + * + * The logic in this method can assume that the given Output object is valid and is + * aligned as neccessary + * + * @param out The AnnotatedOutput object to write/annotate to + */ + protected abstract void writeItem(AnnotatedOutput out); + + /** + * This method is called to add item specific context information to an exception, to identify the "current item" + * when the exception occured. It adds the value returned by getConciseIdentity as context for the + * exception + * @param ex The exception that occured + * @return A RuntimeException with additional details about the item added + */ + protected final RuntimeException addExceptionContext(Exception ex) { + return ExceptionWithContext.withContext(ex, getConciseIdentity()); + } + + /** + * @return An ItemType enum that represents the item type of this item + */ + public abstract ItemType getItemType(); + + /** + * @return A concise (human-readable) string value that conveys the identity of this item + */ + public abstract String getConciseIdentity(); + + + /** + * Note that the item must have been placed before calling this method (See DexFile.place()) + * @return the offset in the dex file where this item is located + */ + public int getOffset() { + Preconditions.checkState(offset != -1, + "The offset is not set until the DexFile containing this item is placed."); + return offset; + } + + /** + * Note that the item must have been placed before calling this method (See DexFile.place()) + * @return the index of this item within the item's containing section. + */ + public int getIndex() { + Preconditions.checkState(index != -1, + "The index is not set until the DexFile containing this item is placed."); + return index; + } + + /** + * @return True if this item has been placed, otherwise False + */ + public boolean isPlaced() { + return offset != -1; + } + + /** + * @return the DexFile that contains this item + */ + public DexFile getDexFile() { + return dexFile; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java new file mode 100644 index 00000000..553d1898 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java @@ -0,0 +1,71 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +class ItemFactory { + protected static Item makeItem(ItemType itemType, DexFile dexFile) { + switch (itemType) { + case TYPE_STRING_ID_ITEM: + return new StringIdItem(dexFile); + case TYPE_TYPE_ID_ITEM: + return new TypeIdItem(dexFile); + case TYPE_PROTO_ID_ITEM: + return new ProtoIdItem(dexFile); + case TYPE_FIELD_ID_ITEM: + return new FieldIdItem(dexFile); + case TYPE_METHOD_ID_ITEM: + return new MethodIdItem(dexFile); + case TYPE_CLASS_DEF_ITEM: + return new ClassDefItem(dexFile); + case TYPE_TYPE_LIST: + return new TypeListItem(dexFile); + case TYPE_ANNOTATION_SET_REF_LIST: + return new AnnotationSetRefList(dexFile); + case TYPE_ANNOTATION_SET_ITEM: + return new AnnotationSetItem(dexFile); + case TYPE_CLASS_DATA_ITEM: + return new ClassDataItem(dexFile); + case TYPE_CODE_ITEM: + return new CodeItem(dexFile); + case TYPE_STRING_DATA_ITEM: + return new StringDataItem(dexFile); + case TYPE_DEBUG_INFO_ITEM: + return new DebugInfoItem(dexFile); + case TYPE_ANNOTATION_ITEM: + return new AnnotationItem(dexFile); + case TYPE_ENCODED_ARRAY_ITEM: + return new EncodedArrayItem(dexFile); + case TYPE_ANNOTATIONS_DIRECTORY_ITEM: + return new AnnotationDirectoryItem(dexFile); + default: + assert false; + } + return null; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemType.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemType.java new file mode 100644 index 00000000..a8c7868f --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemType.java @@ -0,0 +1,123 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import java.util.TreeMap; + +/** + * Enumeration of all the top-level item types. + */ +public enum ItemType { + TYPE_HEADER_ITEM( 0x0000, 17, 4, "header_item"), + TYPE_STRING_ID_ITEM( 0x0001, 0, 4, "string_id_item"), + TYPE_TYPE_ID_ITEM( 0x0002, 1, 4, "type_id_item"), + TYPE_PROTO_ID_ITEM( 0x0003, 2, 4, "proto_id_item"), + TYPE_FIELD_ID_ITEM( 0x0004, 3, 4, "field_id_item"), + TYPE_METHOD_ID_ITEM( 0x0005, 4, 4, "method_id_item"), + TYPE_CLASS_DEF_ITEM( 0x0006, 5, 4, "class_def_item"), + TYPE_MAP_LIST( 0x1000, 16, 4, "map_list"), + TYPE_TYPE_LIST( 0x1001, 6, 4, "type_list"), + TYPE_ANNOTATION_SET_REF_LIST( 0x1002, 7, 4, "annotation_set_ref_list"), + TYPE_ANNOTATION_SET_ITEM( 0x1003, 8, 4, "annotation_set_item"), + TYPE_CLASS_DATA_ITEM( 0x2000, 9, 1, "class_data_item"), + TYPE_CODE_ITEM( 0x2001, 10, 4, "code_item"), + TYPE_STRING_DATA_ITEM( 0x2002, 11, 1, "string_data_item"), + TYPE_DEBUG_INFO_ITEM( 0x2003, 12, 1, "debug_info_item"), + TYPE_ANNOTATION_ITEM( 0x2004, 13, 1, "annotation_item"), + TYPE_ENCODED_ARRAY_ITEM( 0x2005, 14, 1, "encoded_array_item"), + TYPE_ANNOTATIONS_DIRECTORY_ITEM(0x2006, 15, 4, "annotations_directory_item"); + + /** A map to facilitate looking up an ItemType by ordinal */ + private final static TreeMap itemTypeIntegerMap; + + /** builds the itemTypeIntegerMap object */ + static { + itemTypeIntegerMap = new TreeMap(); + + for (ItemType itemType: ItemType.values()) { + itemTypeIntegerMap.put(itemType.MapValue, itemType); + } + } + + + + /** + * value when represented in a MapItem + */ + public final int MapValue; + + /** + * name of the type + */ + public final String TypeName; + + /** + * index for this item's section + */ + public final int SectionIndex; + + /** + * the alignment for this item type + */ + public final int ItemAlignment; + /** + * Constructs an instance. + * + * @param mapValue value when represented in a MapItem + * @param sectionIndex index for this item's section + * @param itemAlignment the byte alignment required by this item + * @param typeName non-null; name of the type + */ + private ItemType(int mapValue, int sectionIndex, int itemAlignment, String typeName) { + this.MapValue = mapValue; + this.SectionIndex = sectionIndex; + this.ItemAlignment = itemAlignment; + this.TypeName = typeName; + } + + /** + * Converts an int value to the corresponding ItemType enum value, + * or null if the value isn't a valid ItemType value + * + * @param itemType the int value to convert to an ItemType + * @return the ItemType enum value corresponding to itemType, or null + * if not a valid ItemType value + */ + public static ItemType fromInt(int itemType) { + return itemTypeIntegerMap.get(itemType); + } + + /** + * Returns true if this is an indexed item, or false if its an offsetted item + * @return true if this is an indexed item, or false if its an offsetted item + */ + public boolean isIndexedItem() { + return MapValue <= 0x1000; + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MapItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MapItem.java new file mode 100644 index 00000000..e69a33a4 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MapItem.java @@ -0,0 +1,138 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +/** + * This item represents a map_list item from the dex specification. It contains a + * SectionInfo instance for every section in the DexFile, with the number of items + * in and offset of that section. + */ +public class MapItem extends Item { + /** + * This item is read in immediately after the HeaderItem, and the section info contained + * by this item is added to the ReadContext object, which is used when reading in the other + * sections in the dex file. + * + * This item should be placed last. It depends on the fact that the other sections + * in the file have been placed. + */ + + /** + * Create a new uninitialized MapItem + * @param dexFile The DexFile that this item belongs to + */ + protected MapItem(final DexFile dexFile) { + super(dexFile); + } + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + Section[] sections = dexFile.getOrderedSections(); + //the list returned by getOrderedSections doesn't contain the header + //or map section, so add 2 to the length + return offset + 4 + (sections.length + 2) * 12; + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + int size = in.readInt(); + + for (int i=0; i 0; + Section[] sections = dexFile.getOrderedSections(); + + out.annotate("map_size: 0x" + Integer.toHexString(sections.length + 2) + " (" + + Integer.toString(sections.length + 2) + ")"); + out.writeInt(sections.length + 2); + + int index = 0; + out.annotate(0, "[" + index++ + "]"); + out.indent(); + writeSectionInfo(out, ItemType.TYPE_HEADER_ITEM, 1, 0); + out.deindent(); + + for (Section section: dexFile.getOrderedSections()) { + out.annotate(0, "[" + index++ + "]"); + out.indent(); + writeSectionInfo(out, section.ItemType, section.getItems().size(), section.getOffset()); + out.deindent(); + } + + out.annotate(0, "[" + index++ + "]"); + out.indent(); + writeSectionInfo(out, ItemType.TYPE_MAP_LIST, 1, dexFile.MapItem.getOffset()); + out.deindent(); + } + + private void writeSectionInfo(AnnotatedOutput out, ItemType itemType, int sectionSize, int sectionOffset) { + if (out.annotates()) { + out.annotate(2, "item_type: " + itemType); + out.annotate(2, "unused"); + out.annotate(4, "section_size: 0x" + Integer.toHexString(sectionSize) + " (" + sectionSize + ")"); + out.annotate(4, "section_off: 0x" + Integer.toHexString(sectionOffset)); + } + + out.writeShort(itemType.MapValue); + out.writeShort(0); + out.writeInt(sectionSize); + out.writeInt(sectionOffset); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_MAP_LIST; + } + + /** {@inheritDoc} */ + public int compareTo(MapItem o) { + return 0; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "map_item"; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java new file mode 100644 index 00000000..c3522c5e --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java @@ -0,0 +1,256 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +public class MethodIdItem extends Item implements Convertible { + private int hashCode = 0; + + private TypeIdItem classType; + private ProtoIdItem methodPrototype; + private StringIdItem methodName; + + /** + * Creates a new uninitialized MethodIdItem + * @param dexFile The DexFile that this item belongs to + */ + protected MethodIdItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new MethodIdItem for the given class, type and name + * @param dexFile The DexFile that this item belongs to + * @param classType the class that the method is a member of + * @param methodPrototype the type of the method + * @param methodName the name of the method + */ + private MethodIdItem(DexFile dexFile, TypeIdItem classType, ProtoIdItem methodPrototype, StringIdItem methodName) { + this(dexFile); + this.classType = classType; + this.methodPrototype = methodPrototype; + this.methodName = methodName; + } + + /** + * Returns a MethodIdItem for the given values, and that has been interned into + * the given DexFile + * @param dexFile The DexFile that this item belongs to + * @param classType the class that the method is a member of + * @param methodPrototype the type of the method + * @param methodName the name of the method + * @return a MethodIdItem for the given values, and that has been interned into + * the given DexFile + */ + public static MethodIdItem internMethodIdItem(DexFile dexFile, TypeIdItem classType, + ProtoIdItem methodPrototype, StringIdItem methodName) { + MethodIdItem methodIdItem = new MethodIdItem(dexFile, classType, methodPrototype, methodName); + return dexFile.MethodIdsSection.intern(methodIdItem); + } + + /** + * Looks up a MethodIdItem from the given DexFile for the given + * values + * @param dexFile The DexFile that this item belongs to + * @param classType the class that the method is a member of + * @param methodPrototype the type of the method + * @param methodName the name of the method + * @return a MethodIdItem from the given DexFile for the given + * values, or null if it doesn't exist + */ + public static MethodIdItem lookupMethodIdItem(DexFile dexFile, TypeIdItem classType, + ProtoIdItem methodPrototype, StringIdItem methodName) { + MethodIdItem methodIdItem = new MethodIdItem(dexFile, classType, methodPrototype, methodName); + return dexFile.MethodIdsSection.getInternedItem(methodIdItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + classType = dexFile.TypeIdsSection.getItemByIndex(in.readShort()); + methodPrototype = dexFile.ProtoIdsSection.getItemByIndex(in.readShort()); + methodName = dexFile.StringIdsSection.getItemByIndex(in.readInt()); + } + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + return offset + 8; + } + + /** {@inheritDoc} */ + protected void writeItem(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate(2, "class_type: " + classType.getTypeDescriptor()); + out.annotate(2, "method_prototype: " + methodPrototype.getPrototypeString()); + out.annotate(4, "method_name: " + methodName.getStringValue()); + } + + int classIndex = classType.getIndex(); + if (classIndex > 0xffff) { + throw new RuntimeException(String.format("Error writing method_id_item for %s. The type index of " + + "defining class %s is too large", getMethodString(), classType.getTypeDescriptor())); + } + out.writeShort(classIndex); + + int prototypeIndex = methodPrototype.getIndex(); + if (prototypeIndex > 0xffff) { + throw new RuntimeException(String.format("Error writing method_id_item for %0. The prototype index of " + + "method prototype %s is too large", getMethodString(), methodPrototype.getPrototypeString())); + } + out.writeShort(prototypeIndex); + + out.writeInt(methodName.getIndex()); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_METHOD_ID_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "method_id_item: " + getMethodString(); + } + + /** {@inheritDoc} */ + public int compareTo(MethodIdItem o) { + int result = classType.compareTo(o.classType); + if (result != 0) { + return result; + } + + result = methodName.compareTo(o.methodName); + if (result != 0) { + return result; + } + + return methodPrototype.compareTo(o.methodPrototype); + } + + private String cachedMethodString = null; + /** + * @return a string formatted like LclassName;->methodName(TTTT..)R + */ + public String getMethodString() { + if (cachedMethodString == null) { + String classType = this.classType.getTypeDescriptor(); + String methodName = this.methodName.getStringValue(); + String prototypeString = methodPrototype.getPrototypeString(); + + StringBuilder sb = new StringBuilder(classType.length() + methodName.length() + prototypeString.length() + + 2); + sb.append(classType); + sb.append("->"); + sb.append(methodName); + sb.append(prototypeString); + cachedMethodString = sb.toString(); + } + return cachedMethodString; + } + + private String cachedShortMethodString = null; + /** + * @return a string formatted like methodName(TTTT..)R + */ + public String getShortMethodString() { + if (cachedShortMethodString == null) { + String methodName = this.methodName.getStringValue(); + String prototypeString = methodPrototype.getPrototypeString(); + + StringBuilder sb = new StringBuilder(methodName.length() + prototypeString.length()); + sb.append(methodName); + sb.append(prototypeString); + cachedShortMethodString = sb.toString(); + } + return cachedShortMethodString; + } + + /** + * @return the method prototype + */ + public ProtoIdItem getPrototype() { + return methodPrototype; + } + + /** + * @return the name of the method + */ + public StringIdItem getMethodName() { + return methodName; + } + + /** + * @return the class this method is a member of + */ + public TypeIdItem getContainingClass() { + return classType; + } + + /** + * calculate and cache the hashcode + */ + private void calcHashCode() { + hashCode = classType.hashCode(); + hashCode = 31 * hashCode + methodPrototype.hashCode(); + hashCode = 31 * hashCode + methodName.hashCode(); + } + + @Override + public int hashCode() { + //there's a small possibility that the actual hash code will be 0. If so, we'll + //just end up recalculating it each time + if (hashCode == 0) + calcHashCode(); + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + //This assumes that the referenced items have been interned in both objects. + //This is a valid assumption because all outside code must use the static + //"getInterned..." style methods to make new items, and any item created + //internally is guaranteed to be interned + MethodIdItem other = (MethodIdItem)o; + return (classType == other.classType && + methodPrototype == other.methodPrototype && + methodName == other.methodName); + } + + public MethodIdItem convert() { + return this; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/OdexDependencies.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/OdexDependencies.java new file mode 100644 index 00000000..581b8acc --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/OdexDependencies.java @@ -0,0 +1,76 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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; + +import org.jf.dexlib.Util.Input; + +import java.io.UnsupportedEncodingException; + +public class OdexDependencies { + public final int modificationTime; + public final int crc; + public final int dalvikBuild; + + private final String[] dependencies; + private final byte[][] dependencyChecksums; + + public OdexDependencies (Input in) { + modificationTime = in.readInt(); + crc = in.readInt(); + dalvikBuild = in.readInt(); + + int dependencyCount = in.readInt(); + + dependencies = new String[dependencyCount]; + dependencyChecksums = new byte[dependencyCount][]; + + for (int i=0; i extends Section { + public OffsettedSection(DexFile dexFile, ItemType itemType) { + super(dexFile, itemType); + } + + public void readItems(Input in, ReadContext readContext) { + + for (int i = 0; i < items.size(); i++) { + assert items.get(i) == null; + + in.alignTo(ItemType.ItemAlignment); + + T item = (T)ItemFactory.makeItem(ItemType, DexFile); + + items.set(i, item); + item.readFrom(in, i, readContext); + } + + readContext.setItemsForSection(ItemType, items); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java new file mode 100644 index 00000000..a7b4a0a1 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java @@ -0,0 +1,231 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +public class ProtoIdItem extends Item { + private int hashCode = 0; + + private StringIdItem shortyDescriptor; + private TypeIdItem returnType; + private TypeListItem parameters; + + /** + * Creates a new uninitialized ProtoIdItem + * @param dexFile The DexFile that this item belongs to + */ + protected ProtoIdItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new ProtoIdItem with the given values + * @param dexFile The DexFile that this item belongs to + * @param returnType the return type + * @param parameters a TypeListItem containing a list of the parameter types + */ + private ProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { + this(dexFile); + + String shortyString = returnType.toShorty(); + if (parameters != null) { + shortyString += parameters.getShortyString(); + } + this.shortyDescriptor = StringIdItem.internStringIdItem(dexFile, shortyString); + this.returnType = returnType; + this.parameters = parameters; + } + + /** + * Returns a ProtoIdItem for the given values, and that has been interned into + * the given DexFile + * @param dexFile The DexFile that this item belongs to + * @param returnType the return type + * @param parameters a TypeListItem containing a list of the parameter types + * @return a ProtoIdItem for the given values, and that has been interned into + * the given DexFile + */ + public static ProtoIdItem internProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { + ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters); + return dexFile.ProtoIdsSection.intern(protoIdItem); + } + + /** + * Looks up the ProtoIdItem from the given DexFile for the given + * values + * @param dexFile the Dexfile to find the type in + * @param returnType the return type + * @param parameters a TypeListItem containing a list of the parameter types + * @return a ProtoIdItem from the given DexFile for the given + * values, or null if it doesn't exist + */ + public static ProtoIdItem lookupProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { + ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters); + return dexFile.ProtoIdsSection.getInternedItem(protoIdItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + shortyDescriptor = dexFile.StringIdsSection.getItemByIndex(in.readInt()); + returnType = dexFile.TypeIdsSection.getItemByIndex(in.readInt()); + parameters = (TypeListItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST, in.readInt()); + } + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + return offset + 12; + } + + /** {@inheritDoc} */ + protected void writeItem(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate(4, "shorty_descriptor: " + shortyDescriptor.getStringValue()); + out.annotate(4, "return_type: " + returnType.getTypeDescriptor()); + + if (parameters == null) { + out.annotate(4, "parameters:"); + } else { + out.annotate(4, "parameters: " + parameters.getTypeListString("")); + } + } + + out.writeInt(shortyDescriptor.getIndex()); + out.writeInt(returnType.getIndex()); + out.writeInt(parameters == null?0:parameters.getOffset()); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_PROTO_ID_ITEM; + } + + /** {@inheritDoc} */ + public int compareTo(ProtoIdItem o) { + int result = returnType.compareTo(o.returnType); + if (result != 0) { + return result; + } + + if (parameters == null) { + if (o.parameters == null) { + return 0; + } + return -1; + } else if (o.parameters == null) { + return 1; + } + + return parameters.compareTo(o.parameters); + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "proto_id_item: " + getPrototypeString(); + } + + private String cachedPrototypeString = null; + /** + * @return a string in the format (TTTT..)R where TTTT.. are the parameter types and R is the return type + */ + public String getPrototypeString() { + if (cachedPrototypeString == null) { + StringBuilder sb = new StringBuilder("("); + if (parameters != null) { + sb.append(parameters.getTypeListString("")); + } + sb.append(")"); + sb.append(returnType.getTypeDescriptor()); + + cachedPrototypeString = sb.toString(); + } + return cachedPrototypeString; + } + + /** + * @return the return type of the method + */ + public TypeIdItem getReturnType() { + return returnType; + } + + /** + * @return a TypeListItem containing the method parameter types + */ + public TypeListItem getParameters() { + return parameters; + } + + /** + * @return the number of registers required for the parameters of this ProtoIdItem + */ + public int getParameterRegisterCount() { + if (parameters == null) { + return 0; + } else { + return parameters.getRegisterCount(); + } + } + + /** + * calculate and cache the hashcode + */ + private void calcHashCode() { + hashCode = returnType.hashCode(); + hashCode = 31 * hashCode + (parameters==null?0:parameters.hashCode()); + } + + @Override + public int hashCode() { + //there's a small possibility that the actual hash code will be 0. If so, we'll + //just end up recalculating it each time + if (hashCode == 0) + calcHashCode(); + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + //This assumes that the referenced items have been interned in both objects. + //This is a valid assumption because all outside code must use the static + //"getInterned..." style methods to make new items, and any item created + //internally is guaranteed to be interned + ProtoIdItem other = (ProtoIdItem)o; + return (returnType == other.returnType && + parameters == other.parameters); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ReadContext.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ReadContext.java new file mode 100644 index 00000000..a25f3a27 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ReadContext.java @@ -0,0 +1,200 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.ExceptionWithContext; +import org.jf.dexlib.Util.SparseArray; + +import java.util.List; + + +/** + * This class stores context information that is only needed when reading in a dex file + * Namely, it handles "pre-creating" items when an item needs to resolve some other item + * that it references, and keeps track of those pre-created items, so the corresponding section + * for the pre-created items uses them, instead of creating new items + */ +public class ReadContext { + private SparseArray typeListItems = new SparseArray(0); + private SparseArray annotationSetRefLists = new SparseArray(0); + private SparseArray annotationSetItems = new SparseArray(0); + private SparseArray classDataItems = new SparseArray(0); + private SparseArray codeItems = new SparseArray(0); + private SparseArray stringDataItems = new SparseArray(0); + private SparseArray debugInfoItems = new SparseArray(0); + private SparseArray annotationItems = new SparseArray(0); + private SparseArray encodedArrayItems = new SparseArray(0); + private SparseArray annotationDirectoryItems = new SparseArray(0); + + private SparseArray[] itemsByType = new SparseArray[] { + null, //string_id_item + null, //type_id_item + null, //proto_id_item + null, //field_id_item + null, //method_id_item + null, //class_def_item + typeListItems, + annotationSetRefLists, + annotationSetItems, + classDataItems, + codeItems, + stringDataItems, + debugInfoItems, + annotationItems, + encodedArrayItems, + annotationDirectoryItems, + null, //map_list + null //header_item + }; + + + /** + * The section sizes that are passed in while reading HeaderItem/MapItem, via the + * addSection method. + */ + private int[] sectionSizes = new int[18]; + + /** + * The section offsets that are passed in while reading MapItem/HeaderItem, via the + * addSection method. + */ + private int[] sectionOffsets = new int[18]; + + /** + * Creates a new ReadContext instance. + */ + public ReadContext() { + for (int i=0; i<18; i++) { + sectionSizes[i] = -1; + sectionOffsets[i] = -1; + } + } + + /** + * Gets the offsetted item of the specified type for the given offset. This method does not support retrieving an + * optional item where a value of 0 indicates "not present". Use getOptionalOffsettedItemByOffset instead. + * + * @param itemType The type of item to get + * @param offset The offset of the item + * @return the offsetted item of the specified type at the specified offset + */ + public Item getOffsettedItemByOffset(ItemType itemType, int offset) { + assert !itemType.isIndexedItem(); + + SparseArray sa = itemsByType[itemType.SectionIndex]; + Item item = sa.get(offset); + if (item == null) { + throw new ExceptionWithContext(String.format("Could not find the %s item at offset %#x", + itemType.TypeName, offset)); + } + return item; + } + + /** + * Gets the optional offsetted item of the specified type for the given offset + * + * @param itemType The type of item to get + * @param offset The offset of the item + * @return the offsetted item of the specified type at the specified offset, or null if the offset is 0 + */ + public Item getOptionalOffsettedItemByOffset(ItemType itemType, int offset) { + assert !itemType.isIndexedItem(); + + assert !itemType.isIndexedItem(); + + SparseArray sa = itemsByType[itemType.SectionIndex]; + Item item = sa.get(offset); + if (item == null && offset != 0) { + throw new ExceptionWithContext(String.format("Could not find the %s item at offset %#x", + itemType.TypeName, offset)); + } + return item; + } + + /** + * Adds the size and offset information for the given offset + * @param itemType the item type of the section + * @param sectionSize the size of the section + * @param sectionOffset the offset of the section + */ + public void addSection(final ItemType itemType, int sectionSize, int sectionOffset) { + int storedSectionSize = sectionSizes[itemType.SectionIndex]; + if (storedSectionSize == -1) { + sectionSizes[itemType.SectionIndex] = sectionSize; + } else { + if (storedSectionSize != sectionSize) { + throw new RuntimeException("The section size in the header and map for item type " + + itemType + " do not match"); + } + } + + int storedSectionOffset = sectionOffsets[itemType.SectionIndex]; + if (storedSectionOffset == -1) { + sectionOffsets[itemType.SectionIndex] = sectionOffset; + } else { + if (storedSectionOffset != sectionOffset) { + throw new RuntimeException("The section offset in the header and map for item type " + + itemType + " do not match"); + } + } + } + + /** + * Sets the items for the specified section. This should be called by an offsetted section + * after it is finished reading in all its items. + * @param itemType the item type of the section. This must be an offsetted item type + * @param items the full list of items in the section, ordered by offset + */ + public void setItemsForSection(ItemType itemType, List items) { + assert !itemType.isIndexedItem(); + + SparseArray sa = itemsByType[itemType.SectionIndex]; + + sa.ensureCapacity(items.size()); + for (Item item: items) { + sa.append(item.getOffset(), item); + } + } + + /** + * @param itemType the item type of the section + * @return the size of the given section as it was read in from the map item + */ + public int getSectionSize(ItemType itemType) { + return sectionSizes[itemType.SectionIndex]; + } + + /** + * @param itemType the item type of the section + * @return the offset of the given section as it was read in from the map item + */ + public int getSectionOffset(ItemType itemType) { + return sectionOffsets[itemType.SectionIndex]; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Section.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Section.java new file mode 100644 index 00000000..0c20a719 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Section.java @@ -0,0 +1,221 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AlignmentUtils; +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +public abstract class Section { + /** + * A list of the items that this section contains. + * If the section has been placed, this list should be in the order that the items + * will written to the dex file + */ + protected final ArrayList items; + + /** + * A HashMap of the items in this section. This is used when interning items, to determine + * if this section already has an item equivalent to the one that is being interned. + * Both the key and the value should be the same object + */ + protected HashMap uniqueItems = null; + + /** + * The offset of this section within the DexFile + */ + protected int offset = 0; + + /** + * The type of item that this section holds + */ + public final ItemType ItemType; + + /** + * The DexFile that this section belongs to + */ + public final DexFile DexFile; + + /** + * Create a new section + * @param dexFile The DexFile that this section belongs to + * @param itemType The itemType that this section will hold + */ + protected Section(DexFile dexFile, ItemType itemType) { + this.DexFile = dexFile; + items = new ArrayList(); + this.ItemType = itemType; + } + + /** + * Finalize the location of all items, and place them starting at the given offset + * @param offset The offset where this section should be placed + * @return the offset of the byte immediate after the last item in this section + */ + protected int placeAt(int offset) { + if (items.size() > 0) { + offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment); + assert !DexFile.getInplace() || offset == this.offset; + this.offset = offset; + + for (int i=0; i < items.size(); i++) { + T item = items.get(i); + assert item != null; + offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment); + offset = item.placeAt(offset, i); + } + } else { + this.offset = 0; + } + + return offset; + } + + /** + * Write the items to the given AnnotatedOutput + * @param out the AnnotatedOutput object to write to + */ + protected void writeTo(AnnotatedOutput out) { + out.annotate(0, " "); + out.annotate(0, "-----------------------------"); + out.annotate(0, this.ItemType.TypeName + " section"); + out.annotate(0, "-----------------------------"); + out.annotate(0, " "); + + for (Item item: items) { + assert item!=null; + out.alignTo(ItemType.ItemAlignment); + item.writeTo(out); + out.annotate(0, " "); + } + } + + /** + * Read the specified number of items from the given Input object + * @param size The number of items to read + * @param in The Input object to read from + * @param readContext a ReadContext object to hold information that is + * only needed while reading in a file + */ + protected void readFrom(int size, Input in, ReadContext readContext) { + //readItems() expects that the list will already be the correct size, so add null items + //until we reach the specified size + items.ensureCapacity(size); + for (int i = items.size(); i < size; i++) { + items.add(null); + } + + in.alignTo(ItemType.ItemAlignment); + offset = in.getCursor(); + + //call the subclass's method that actually reads in the items + readItems(in, readContext); + } + + /** + * This method in the concrete item subclass should read in all the items from the given Input + * object, using any pre-created items as applicable (i.e. items that were created prior to reading in the + * section, by other items requesting items from this section that they reference by index/offset) + * @param in the Input + * @param readContext a ReadContext object to hold information that is + * only needed while reading in a file + */ + protected abstract void readItems(Input in, ReadContext readContext); + + /** + * Gets the offset where the first item in this section is placed + * @return the ofset where the first item in this section is placed + */ + public int getOffset() { + return offset; + } + + /** + * Gets a the items contained in this section as a read-only list + * @return A read-only List object containing the items in this section + */ + public List getItems() { + return Collections.unmodifiableList(items); + } + + /** + * This method checks if an item that is equivalent to the given item has already been added. If found, + * it returns that item. If not found, it adds the given item to this section and returns it. + * @param item the item to intern + * @return An item from this section that is equivalent to the given item. It may or may not be the same + * as the item passed to this method. + */ + protected T intern(T item) { + if (item == null) { + return null; + } + T internedItem = getInternedItem(item); + if (internedItem == null) { + uniqueItems.put(item, item); + items.add(item); + return item; + } + return internedItem; + } + + /** + * Returns the interned item that is equivalent to the given item, or null + * @param item the item to check + * @return the interned item that is equivalent to the given item, or null + */ + protected T getInternedItem(T item) { + if (uniqueItems == null) { + buildInternedItemMap(); + } + return uniqueItems.get(item); + } + + /** + * Builds the interned item map from the items that are in this section + */ + private void buildInternedItemMap() { + uniqueItems = new HashMap(); + for (T item: items) { + assert item != null; + uniqueItems.put(item, item); + } + } + + /** + * Sorts the items in the section + */ + protected void sortSection() { + Collections.sort(items); + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java new file mode 100644 index 00000000..a565d099 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java @@ -0,0 +1,169 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.Leb128Utils; +import org.jf.dexlib.Util.Utf8Utils; + +public class StringDataItem extends Item { + private int hashCode = 0; + + private String stringValue; + + /** + * Creates a new uninitialized StringDataItem + * @param dexFile The DexFile that this item belongs to + */ + protected StringDataItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new StringDataItem for the given string + * @param dexFile The DexFile that this item belongs to + * @param stringValue The string value that this item represents + */ + private StringDataItem(DexFile dexFile, String stringValue) { + super(dexFile); + + this.stringValue = stringValue; + } + + /** + * Returns a StringDataItem for the given values, and that has been interned into + * the given DexFile + * @param dexFile The DexFile that this item belongs to + * @param value The string value that this item represents + * @return a StringDataItem for the given values, and that has been interned into + * the given DexFile + */ + public static StringDataItem internStringDataItem(DexFile dexFile, String value) { + StringDataItem StringDataItem = new StringDataItem(dexFile, value); + return dexFile.StringDataSection.intern(StringDataItem); + } + + /** + * Looks up the StringDataItem from the given DexFile for the given + * string value + * @param dexFile the Dexfile to find the string value in + * @param value The string value to look up + * @return a StringDataItem from the given DexFile for the given + * string value, or null if it doesn't exist + **/ + public static StringDataItem lookupStringDataItem(DexFile dexFile, String value) { + StringDataItem StringDataItem = new StringDataItem(dexFile, value); + return dexFile.StringDataSection.getInternedItem(StringDataItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + in.readUnsignedLeb128(); //string length + stringValue = in.realNullTerminatedUtf8String(); + } + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + return offset + Leb128Utils.unsignedLeb128Size(stringValue.length()) + + Utf8Utils.stringToUtf8Bytes(stringValue).length + 1; + } + + /** {@inheritDoc} */ + protected void writeItem(AnnotatedOutput out) { + byte[] encodedValue = Utf8Utils.stringToUtf8Bytes(stringValue); + if (out.annotates()) { + out.annotate("string_size: 0x" + Integer.toHexString(stringValue.length()) + " (" + stringValue.length() + + ")"); + out.writeUnsignedLeb128(stringValue.length()); + + out.annotate(encodedValue.length + 1, "string_data: \"" + Utf8Utils.escapeString(stringValue) + "\""); + } else { + out.writeUnsignedLeb128(stringValue.length()); + } + out.write(encodedValue); + out.writeByte(0); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_STRING_DATA_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "string_data_item: \"" + Utf8Utils.escapeString(getStringValue()) + "\""; + } + + /** {@inheritDoc} */ + public int compareTo(StringDataItem o) { + return getStringValue().compareTo(o.getStringValue()); + } + + /** + * Get the string value of this item as a String + * @return the string value of this item as a String + */ + public String getStringValue() { + return stringValue; + } + + /** + * calculate and cache the hashcode + */ + private void calcHashCode() { + hashCode = getStringValue().hashCode(); + } + + @Override + public int hashCode() { + //there's a small possibility that the actual hash code will be 0. If so, we'll + //just end up recalculating it each time + if (hashCode == 0) + calcHashCode(); + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + //This assumes that the referenced items have been interned in both objects. + //This is a valid assumption because all outside code must use the static + //"getInterned..." style methods to make new items, and any item created + //internally is guaranteed to be interned + StringDataItem other = (StringDataItem)o; + return getStringValue().equals(other.getStringValue()); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java new file mode 100644 index 00000000..61bb65ef --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java @@ -0,0 +1,177 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.Utf8Utils; + +import javax.annotation.Nullable; + +public class StringIdItem extends Item { + private StringDataItem stringDataItem; + + /** + * Creates a new uninitialized StringIdItem + * @param dexFile The DexFile that this item belongs to + */ + protected StringIdItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new StringIdItem for the given StringDataItem + * @param dexFile The DexFile that this item belongs to + * @param stringDataItem The StringDataItem that this StringIdItem represents + */ + protected StringIdItem(DexFile dexFile, StringDataItem stringDataItem) { + super(dexFile); + this.stringDataItem = stringDataItem; + } + + /** + * Returns a StringIdItem for the given values, and that has been interned into + * the given DexFile + * @param dexFile The DexFile that this item will belong to + * @param stringValue The string value that this item represents + * @return a StringIdItem for the given values, and that has been interned into + * the given DexFile + */ + public static StringIdItem internStringIdItem(DexFile dexFile, String stringValue) { + StringDataItem stringDataItem = StringDataItem.internStringDataItem(dexFile, stringValue); + if (stringDataItem == null) { + return null; + } + StringIdItem stringIdItem = new StringIdItem(dexFile, stringDataItem); + return dexFile.StringIdsSection.intern(stringIdItem); + } + + /** + * Looks up the StringIdItem from the given DexFile for the given + * string value + * @param dexFile the Dexfile to find the string value in + * @param stringValue The string value to look up + * @return a StringIdItem from the given DexFile for the given + * string value, or null if it doesn't exist + */ + public static StringIdItem lookupStringIdItem(DexFile dexFile, String stringValue) { + StringDataItem stringDataItem = StringDataItem.lookupStringDataItem(dexFile, stringValue); + if (stringDataItem == null) { + return null; + } + StringIdItem stringIdItem = new StringIdItem(dexFile, stringDataItem); + return dexFile.StringIdsSection.getInternedItem(stringIdItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + int stringDataOffset = in.readInt(); + + stringDataItem = (StringDataItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_STRING_DATA_ITEM, + stringDataOffset); + } + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + return offset + 4; + } + + /** {@inheritDoc} */ + protected void writeItem(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate(4, stringDataItem.getConciseIdentity()); + } + + out.writeInt(stringDataItem.getOffset()); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_STRING_ID_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "string_id_item: " + Utf8Utils.escapeString(getStringValue()); + } + + /** {@inheritDoc} */ + public int compareTo(StringIdItem o) { + //sort by the string value + return getStringValue().compareTo(o.getStringValue()); + } + + /** + * Get the String value that this StringIdItem represents + * @return the String value that this StringIdItem represents + */ + public String getStringValue() { + return stringDataItem.getStringValue(); + } + + /** + * Get the String value that the given StringIdItem represents + * @param stringIdItem The StringIdItem to get the string value of + * @return the String value that the given StringIdItem represents + */ + @Nullable + public static String getStringValue(@Nullable StringIdItem stringIdItem) { + return stringIdItem==null?null:stringIdItem.getStringValue(); + } + + /** + * Get the StringDataItem that this StringIdItem references + * @return the StringDataItem that this StringIdItem references + */ + public StringDataItem getStringDataItem() { + return stringDataItem; + } + + @Override + public int hashCode() { + return stringDataItem.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + //This assumes that the referenced items have been interned in both objects. + //This is a valid assumption because all outside code must use the static + //"getInterned..." style methods to make new items, and any item created + //internally is guaranteed to be interned + StringIdItem other = (StringIdItem)o; + return stringDataItem == other.stringDataItem; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java new file mode 100644 index 00000000..6cc2b968 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java @@ -0,0 +1,211 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; + +import javax.annotation.Nullable; + +public class TypeIdItem extends Item { + private StringIdItem typeDescriptor; + + /** + * Creates a new uninitialized TypeIdItem + * @param dexFile The DexFile that this item belongs to + */ + protected TypeIdItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new TypeIdItem for the given StringIdItem + * @param dexFile The DexFile that this item will belong to + * @param typeDescriptor The StringIdItem containing the type descriptor that + * this TypeIdItem represents + */ + private TypeIdItem(DexFile dexFile, StringIdItem typeDescriptor) { + super(dexFile); + this.typeDescriptor = typeDescriptor; + } + + /** + * Returns a TypeIdItem for the given values, and that has been interned into + * the given DexFile + * @param dexFile The DexFile that this item will belong to + * @param typeDescriptor The StringIdItem containing the type descriptor that + * this TypeIdItem represents + * @return a TypeIdItem for the given values, and that has been interned into + * the given DexFile + */ + public static TypeIdItem internTypeIdItem(DexFile dexFile, StringIdItem typeDescriptor) { + TypeIdItem typeIdItem = new TypeIdItem(dexFile, typeDescriptor); + return dexFile.TypeIdsSection.intern(typeIdItem); + } + + /** + * Returns a TypeIdItem for the given values, and that has been interned into + * the given DexFile + * @param dexFile The DexFile that this item will belong to + * @param typeDescriptor The string containing the type descriptor that this + * TypeIdItem represents + * @return a TypeIdItem for the given values, and that has been interned into + * the given DexFile + */ + public static TypeIdItem internTypeIdItem(DexFile dexFile, String typeDescriptor) { + StringIdItem stringIdItem = StringIdItem.internStringIdItem(dexFile, typeDescriptor); + if (stringIdItem == null) { + return null; + } + TypeIdItem typeIdItem = new TypeIdItem(dexFile, stringIdItem); + return dexFile.TypeIdsSection.intern(typeIdItem); + } + + /** + * Looks up the TypeIdItem from the given DexFile for the given + * type descriptor + * @param dexFile the Dexfile to find the type in + * @param typeDescriptor The string containing the type descriptor to look up + * @return a TypeIdItem from the given DexFile for the given + * type descriptor, or null if it doesn't exist + */ + public static TypeIdItem lookupTypeIdItem(DexFile dexFile, String typeDescriptor) { + StringIdItem stringIdItem = StringIdItem.lookupStringIdItem(dexFile, typeDescriptor); + if (stringIdItem == null) { + return null; + } + TypeIdItem typeIdItem = new TypeIdItem(dexFile, stringIdItem); + return dexFile.TypeIdsSection.getInternedItem(typeIdItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + int stringIdIndex = in.readInt(); + this.typeDescriptor = dexFile.StringIdsSection.getItemByIndex(stringIdIndex); + } + + /** {@inheritDoc} */ + protected int placeItem(int offset) { + return offset + 4; + } + + /** {@inheritDoc} */ + protected void writeItem(AnnotatedOutput out) { + if (out.annotates()) { + out.annotate(4, typeDescriptor.getConciseIdentity()); + } + + out.writeInt(typeDescriptor.getIndex()); + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_TYPE_ID_ITEM; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "type_id_item: " + getTypeDescriptor(); + } + + /** {@inheritDoc} */ + public int compareTo(TypeIdItem o) { + //sort by the index of the StringIdItem + return typeDescriptor.compareTo(o.typeDescriptor); + } + + /** + * Returns the type descriptor as a String for this type + * @return the type descriptor as a String for this type + */ + public String getTypeDescriptor() { + return typeDescriptor.getStringValue(); + } + + /** + * Returns the type descriptor as a String for the given type + * @param typeIdItem The TypeIdItem to get the type descriptor of + * @return the type descriptor as a String for the gvien type + */ + @Nullable + public static String getTypeDescriptor(@Nullable TypeIdItem typeIdItem) { + return typeIdItem==null?null:typeIdItem.getTypeDescriptor(); + } + + /** + * Returns the "shorty" representation of this type, used to create the shorty prototype string for a method + * @return the "shorty" representation of this type, used to create the shorty prototype string for a method + */ + public String toShorty() { + String type = getTypeDescriptor(); + if (type.length() > 1) { + return "L"; + } else { + return type; + } + } + + /** + * Calculates the number of 2-byte registers that an instance of this type requires + * @return The number of 2-byte registers that an instance of this type requires + */ + public int getRegisterCount() { + String type = this.getTypeDescriptor(); + /** Only the long and double primitive types are 2 words, + * everything else is a single word + */ + if (type.charAt(0) == 'J' || type.charAt(0) == 'D') { + return 2; + } else { + return 1; + } + } + + @Override + public int hashCode() { + return typeDescriptor.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + //This assumes that the referenced items have been interned in both objects. + //This is a valid assumption because all outside code must use the static + //"getInterned..." style methods to make new items, and any item created + //internally is guaranteed to be interned + TypeIdItem other = (TypeIdItem)o; + return typeDescriptor == other.typeDescriptor; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java new file mode 100644 index 00000000..50262ea1 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java @@ -0,0 +1,288 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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; + +import org.jf.dexlib.Util.AnnotatedOutput; +import org.jf.dexlib.Util.Input; +import org.jf.dexlib.Util.ReadOnlyArrayList; + +import java.util.List; + +public class TypeListItem extends Item { + private int hashCode = 0; + + private TypeIdItem[] typeList; + + /** + * Creates a new uninitialized TypeListItem + * @param dexFile The DexFile that this item belongs to + */ + protected TypeListItem(DexFile dexFile) { + super(dexFile); + } + + /** + * Creates a new TypeListItem for the given string + * @param dexFile The DexFile that this item belongs to + * @param typeList A list of the types that this TypeListItem represents + */ + private TypeListItem(DexFile dexFile, TypeIdItem[] typeList) { + super(dexFile); + + this.typeList = typeList; + } + + /** + * Returns a TypeListItem for the given values, and that has been interned into + * the given DexFile + * @param dexFile The DexFile that this item belongs to + * @param typeList A list of the types that this TypeListItem represents + * @return a TypeListItem for the given values, and that has been interned into + * the given DexFile + */ + public static TypeListItem internTypeListItem(DexFile dexFile, List typeList) { + TypeIdItem[] typeArray = new TypeIdItem[typeList.size()]; + typeList.toArray(typeArray); + TypeListItem typeListItem = new TypeListItem(dexFile, typeArray); + return dexFile.TypeListsSection.intern(typeListItem); + } + + /** + * Looks up the TypeListItem from the given DexFile for the given + * list of types + * @param dexFile the Dexfile to find the type in + * @param typeList A list of the types that the TypeListItem represents + * @return a TypeListItem from the given DexFile for the given + * list of types, or null if it doesn't exist + */ + public static TypeListItem lookupTypeListItem(DexFile dexFile, List typeList) { + TypeIdItem[] typeArray = new TypeIdItem[typeList.size()]; + typeList.toArray(typeArray); + TypeListItem typeListItem = new TypeListItem(dexFile, typeArray); + return dexFile.TypeListsSection.getInternedItem(typeListItem); + } + + /** {@inheritDoc} */ + protected void readItem(Input in, ReadContext readContext) { + int size = in.readInt(); + typeList = new TypeIdItem[size]; + for (int i=0; i 0xffff) { + throw new RuntimeException(String.format("Error writing type_list entry. The type index of " + + "type %s is too large", typeIdItem.getTypeDescriptor())); + } + out.writeShort(typeIndex); + } + } + + /** {@inheritDoc} */ + public ItemType getItemType() { + return ItemType.TYPE_TYPE_LIST; + } + + /** {@inheritDoc} */ + public String getConciseIdentity() { + return "type_list: " + getTypeListString(""); + } + + /** {@inheritDoc} */ + public int compareTo(TypeListItem o) { + if (o == null) { + return 1; + } + + int thisSize = typeList.length; + int otherSize = o.typeList.length; + int size = Math.min(thisSize, otherSize); + + for (int i = 0; i < size; i++) { + int result = typeList[i].compareTo(o.typeList[i]); + if (result != 0) { + return result; + } + } + + if (thisSize < otherSize) { + return -1; + } else if (thisSize > otherSize) { + return 1; + } else { + return 0; + } + } + + /** + * @return the number of registers required for this TypeListItem + */ + public int getRegisterCount() { + int wordCount = 0; + for (TypeIdItem typeIdItem: typeList) { + wordCount += typeIdItem.getRegisterCount(); + } + return wordCount; + } + + /** + * @return a string consisting of the type descriptors in this TypeListItem + * that are separated by the given separator + * @param separator the separator between each type + */ + public String getTypeListString(String separator) { + int size = 0; + for (TypeIdItem typeIdItem: typeList) { + size += typeIdItem.getTypeDescriptor().length(); + size += separator.length(); + } + + StringBuilder sb = new StringBuilder(size); + for (TypeIdItem typeIdItem: typeList) { + sb.append(typeIdItem.getTypeDescriptor()); + sb.append(separator); + } + if (typeList.length > 0) { + sb.delete(sb.length() - separator.length(), sb.length()); + } + return sb.toString(); + } + + /** + * @return a string consisting of the shorty form of the type descriptors in this + * TypeListItem that are directly concatenated together + */ + public String getShortyString() { + StringBuilder sb = new StringBuilder(); + for (TypeIdItem typeIdItem: typeList) { + sb.append(typeIdItem.toShorty()); + } + return sb.toString(); + } + + /** + * @param index the index of the TypeIdItem to get + * @return the TypeIdItem at the given index + */ + public TypeIdItem getTypeIdItem(int index) { + return typeList[index]; + } + + /** + * @return the number of types in this TypeListItem + */ + public int getTypeCount() { + return typeList.length; + } + + /** + * @return an array of the TypeIdItems in this TypeListItem + */ + public List getTypes() { + return new ReadOnlyArrayList(typeList); + } + + /** + * Helper method to allow easier "inline" retrieval of of the list of TypeIdItems + * @param typeListItem the typeListItem to return the types of (can be null) + * @return an array of the TypeIdItems in the specified TypeListItem, or null if the + * TypeListItem is null + */ + public static List getTypes(TypeListItem typeListItem) { + return typeListItem==null?null:typeListItem.getTypes(); + } + + /** + * calculate and cache the hashcode + */ + private void calcHashCode() { + int hashCode = 1; + + for (TypeIdItem typeIdItem: typeList) { + hashCode = 31 * hashCode + typeIdItem.hashCode(); + } + this.hashCode = hashCode; + } + + @Override + public int hashCode() { + //there's a small possibility that the actual hash code will be 0. If so, we'll + //just end up recalculating it each time + if (hashCode == 0) + calcHashCode(); + return hashCode; + } + + @Override + public boolean equals(Object o) { + if (this==o) { + return true; + } + if (o==null || !this.getClass().equals(o.getClass())) { + return false; + } + + //This assumes that the referenced items have been interned in both objects. + //This is a valid assumption because all outside code must use the static + //"getInterned..." style methods to make new items, and any item created + //internally is guaranteed to be interned + TypeListItem other = (TypeListItem)o; + if (typeList.length != other.typeList.length) { + return false; + } + + for (int i=0; i accessFlagsByName; + + static { + allFlags = AccessFlags.values(); + + accessFlagsByName = new HashMap(); + for (AccessFlags accessFlag: allFlags) { + accessFlagsByName.put(accessFlag.accessFlagName, accessFlag); + } + } + + private AccessFlags(int value, String accessFlagName, boolean validForClass, boolean validForMethod, + boolean validForField) { + this.value = value; + this.accessFlagName = accessFlagName; + this.validForClass = validForClass; + this.validForMethod = validForMethod; + this.validForField = validForField; + } + + public static AccessFlags[] getAccessFlagsForClass(int accessFlagValue) { + int size = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForClass && (accessFlagValue & accessFlag.value) != 0) { + size++; + } + } + + AccessFlags[] accessFlags = new AccessFlags[size]; + int accessFlagsPosition = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForClass && (accessFlagValue & accessFlag.value) != 0) { + accessFlags[accessFlagsPosition++] = accessFlag; + } + } + return accessFlags; + } + + private static String formatAccessFlags(AccessFlags[] accessFlags) { + int size = 0; + for (AccessFlags accessFlag: accessFlags) { + size += accessFlag.toString().length() + 1; + } + + StringBuilder sb = new StringBuilder(size); + for (AccessFlags accessFlag: accessFlags) { + sb.append(accessFlag.toString()); + sb.append(" "); + } + if (accessFlags.length > 0) { + sb.delete(sb.length() - 1, sb.length()); + } + return sb.toString(); + } + + public static String formatAccessFlagsForClass(int accessFlagValue) { + return formatAccessFlags(getAccessFlagsForClass(accessFlagValue)); + } + + public static AccessFlags[] getAccessFlagsForMethod(int accessFlagValue) { + int size = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForMethod && (accessFlagValue & accessFlag.value) != 0) { + size++; + } + } + + AccessFlags[] accessFlags = new AccessFlags[size]; + int accessFlagsPosition = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForMethod && (accessFlagValue & accessFlag.value) != 0) { + accessFlags[accessFlagsPosition++] = accessFlag; + } + } + return accessFlags; + } + + public static String formatAccessFlagsForMethod(int accessFlagValue) { + return formatAccessFlags(getAccessFlagsForMethod(accessFlagValue)); + } + + public static AccessFlags[] getAccessFlagsForField(int accessFlagValue) { + int size = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForField && (accessFlagValue & accessFlag.value) != 0) { + size++; + } + } + + AccessFlags[] accessFlags = new AccessFlags[size]; + int accessFlagsPosition = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForField && (accessFlagValue & accessFlag.value) != 0) { + accessFlags[accessFlagsPosition++] = accessFlag; + } + } + return accessFlags; + } + + public static String formatAccessFlagsForField(int accessFlagValue) { + return formatAccessFlags(getAccessFlagsForField(accessFlagValue)); + } + + public static AccessFlags getAccessFlag(String accessFlag) { + return accessFlagsByName.get(accessFlag); + } + + public int getValue() { + return value; + } + + public String toString() { + return accessFlagName; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/AlignmentUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/AlignmentUtils.java new file mode 100644 index 00000000..0d9741b2 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/AlignmentUtils.java @@ -0,0 +1,41 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +public abstract class AlignmentUtils { + public static int alignOffset(int offset, int alignment) { + int mask = alignment - 1; + assert (alignment >= 0) && ((mask & alignment) == 0); + return (offset + mask) & ~mask; + } + + public static boolean isAligned(int offset, int alignment) { + return (offset % alignment) == 0; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/AnnotatedOutput.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/AnnotatedOutput.java new file mode 100644 index 00000000..928b2008 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/AnnotatedOutput.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +/** + * Interface for a binary output destination that may be augmented + * with textual annotations. + */ +public interface AnnotatedOutput + extends Output { + /** + * Get whether this instance will actually keep annotations. + * + * @return true iff annotations are being kept + */ + public boolean annotates(); + + /** + * Get whether this instance is intended to keep verbose annotations. + * Annotators may use the result of calling this method to inform their + * annotation activity. + * + * @return true iff annotations are to be verbose + */ + public boolean isVerbose(); + + /** + * Add an annotation for the subsequent output. Any previously + * open annotation will be closed by this call, and the new + * annotation marks all subsequent output until another annotation + * call. + * + * @param msg non-null; the annotation message + */ + public void annotate(String msg); + + /** + * Add an annotation for a specified amount of subsequent + * output. Any previously open annotation will be closed by this + * call. If there is already pending annotation from one or more + * previous calls to this method, the new call "consumes" output + * after all the output covered by the previous calls. + * + * @param amt >= 0; the amount of output for this annotation to + * cover + * @param msg non-null; the annotation message + */ + public void annotate(int amt, String msg); + + /** + * End the most recent annotation. Subsequent output will be unannotated, + * until the next call to {@link #annotate}. + */ + public void endAnnotation(); + + /** + * Get the maximum width of the annotated output. This is advisory: + * Implementations of this interface are encouraged to deal with too-wide + * output, but annotaters are encouraged to attempt to avoid exceeding + * the indicated width. + * + * @return >= 1; the maximum width + */ + public int getAnnotationWidth(); + + public void setIndentAmount(int indentAmount); + public void indent(); + public void deindent(); +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java new file mode 100644 index 00000000..dab08372 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java @@ -0,0 +1,71 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +import java.util.Arrays; +import java.util.Comparator; + +public class ArrayUtils { + /** + * Utility method to sort two related arrays - that is, two arrays where the elements are related to each other + * by their position in the array. i.e. firstArray[0] is related to secondArray[0], firstArray[1] is related to + * secondArray[1], and so on. The first array is sorted based on its implementation of Comparable, and the 2nd + * array is sorted by it's related item in the first array, so that the same elements are still related to each + * other after the sort + * @param firstArray The first array, which contains the values to sort + * @param secondArray The second array, which will be sorted based on the values in the first array + * @param The type of element in the first array + * @param the type of element in the second array + */ + public static , B> void sortTwoArrays(A[] firstArray, B[] secondArray) + { + if (firstArray.length != secondArray.length) { + throw new RuntimeException("Both arrays must be of the same length"); + } + + class element + { + public A first; + public B second; + } + + element[] elements = new element[firstArray.length]; + + Arrays.sort(elements, new Comparator(){ + public int compare(element a, element b) { + return a.first.compareTo(b.first); + } + }); + + for (int i=0; ibyte[], which provides read-only access and + * can "reveal" a partial slice of the underlying array. + * + * Note: Multibyte accessors all use big-endian order. + */ +public final class ByteArray { + /** non-null; underlying array */ + private final byte[] bytes; + + /** >= 0; start index of the slice (inclusive) */ + private final int start; + + /** >= 0, <= bytes.length; size computed as + * end - start (in the constructor) */ + private final int size; + + /** + * Constructs an instance. + * + * @param bytes non-null; the underlying array + * @param start >= 0; start index of the slice (inclusive) + * @param end >= start, <= bytes.length; end index of + * the slice (exclusive) + */ + public ByteArray(byte[] bytes, int start, int end) { + if (bytes == null) { + throw new NullPointerException("bytes == null"); + } + + if (start < 0) { + throw new IllegalArgumentException("start < 0"); + } + + if (end < start) { + throw new IllegalArgumentException("end < start"); + } + + if (end > bytes.length) { + throw new IllegalArgumentException("end > bytes.length"); + } + + this.bytes = bytes; + this.start = start; + this.size = end - start; + } + + /** + * Constructs an instance from an entire byte[]. + * + * @param bytes non-null; the underlying array + */ + public ByteArray(byte[] bytes) { + this(bytes, 0, bytes.length); + } + + /** + * Gets the size of the array, in bytes. + * + * @return >= 0; the size + */ + public int size() { + return size; + } + + /** + * Returns a slice (that is, a sub-array) of this instance. + * + * @param start >= 0; start index of the slice (inclusive) + * @param end >= start, <= size(); end index of + * the slice (exclusive) + * @return non-null; the slice + */ + public ByteArray slice(int start, int end) { + checkOffsets(start, end); + return new ByteArray(bytes, start + this.start, end + this.start); + } + + /** + * Returns the offset into the given array represented by the given + * offset into this instance. + * + * @param offset offset into this instance + * @param bytes non-null; (alleged) underlying array + * @return corresponding offset into bytes + * @throws IllegalArgumentException thrown if bytes is + * not the underlying array of this instance + */ + public int underlyingOffset(int offset, byte[] bytes) { + if (bytes != this.bytes) { + throw new IllegalArgumentException("wrong bytes"); + } + + return start + offset; + } + + /** + * Gets the signed byte value at a particular offset. + * + * @param off >= 0, < size(); offset to fetch + * @return signed byte at that offset + */ + public int getByte(int off) { + checkOffsets(off, off + 1); + return getByte0(off); + } + + /** + * Gets the signed short value at a particular offset. + * + * @param off >= 0, < (size() - 1); offset to fetch + * @return signed short at that offset + */ + public int getShort(int off) { + checkOffsets(off, off + 2); + return (getByte0(off) << 8) | getUnsignedByte0(off + 1); + } + + /** + * Gets the signed int value at a particular offset. + * + * @param off >= 0, < (size() - 3); offset to fetch + * @return signed int at that offset + */ + public int getInt(int off) { + checkOffsets(off, off + 4); + return (getByte0(off) << 24) | + (getUnsignedByte0(off + 1) << 16) | + (getUnsignedByte0(off + 2) << 8) | + getUnsignedByte0(off + 3); + } + + /** + * Gets the signed long value at a particular offset. + * + * @param off >= 0, < (size() - 7); offset to fetch + * @return signed int at that offset + */ + public long getLong(int off) { + checkOffsets(off, off + 8); + int part1 = (getByte0(off) << 24) | + (getUnsignedByte0(off + 1) << 16) | + (getUnsignedByte0(off + 2) << 8) | + getUnsignedByte0(off + 3); + int part2 = (getByte0(off + 4) << 24) | + (getUnsignedByte0(off + 5) << 16) | + (getUnsignedByte0(off + 6) << 8) | + getUnsignedByte0(off + 7); + + return (part2 & 0xffffffffL) | ((long) part1) << 32; + } + + /** + * Gets the unsigned byte value at a particular offset. + * + * @param off >= 0, < size(); offset to fetch + * @return unsigned byte at that offset + */ + public int getUnsignedByte(int off) { + checkOffsets(off, off + 1); + return getUnsignedByte0(off); + } + + /** + * Gets the unsigned short value at a particular offset. + * + * @param off >= 0, < (size() - 1); offset to fetch + * @return unsigned short at that offset + */ + public int getUnsignedShort(int off) { + checkOffsets(off, off + 2); + return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1); + } + + /** + * Copies the contents of this instance into the given raw + * byte[] at the given offset. The given array must be + * large enough. + * + * @param out non-null; array to hold the output + * @param offset non-null; index into out for the first + * byte of output + */ + public void getBytes(byte[] out, int offset) { + if ((out.length - offset) < size) { + throw new IndexOutOfBoundsException("(out.length - offset) < " + + "size()"); + } + + System.arraycopy(bytes, start, out, offset, size); + } + + /** + * Checks a range of offsets for validity, throwing if invalid. + * + * @param s start offset (inclusive) + * @param e end offset (exclusive) + */ + private void checkOffsets(int s, int e) { + if ((s < 0) || (e < s) || (e > size)) { + throw new IllegalArgumentException("bad range: " + s + ".." + e + + "; actual size " + size); + } + } + + /** + * Gets the signed byte value at the given offset, + * without doing any argument checking. + * + * @param off offset to fetch + * @return byte at that offset + */ + private int getByte0(int off) { + return bytes[start + off]; + } + + /** + * Gets the unsigned byte value at the given offset, + * without doing any argument checking. + * + * @param off offset to fetch + * @return byte at that offset + */ + private int getUnsignedByte0(int off) { + return bytes[start + off] & 0xff; + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java new file mode 100644 index 00000000..ae33cb0e --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java @@ -0,0 +1,678 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; + +/** + * Implementation of {@link AnnotatedOutput} which stores the written data + * into a byte[]. + * + *

    Note: As per the {@link Output} interface, multi-byte + * writes all use little-endian order.

    + */ +public final class ByteArrayAnnotatedOutput + implements AnnotatedOutput { + /** default size for stretchy instances */ + private static final int DEFAULT_SIZE = 1000; + + /** + * whether the instance is stretchy, that is, whether its array + * may be resized to increase capacity + */ + private final boolean stretchy; + + /** non-null; the data itself */ + private byte[] data; + + /** >= 0; current output cursor */ + private int cursor; + + /** whether annotations are to be verbose */ + private boolean verbose; + + /** + * null-ok; list of annotations, or null if this instance + * isn't keeping them + */ + private ArrayList annotations; + + /** >= 40 (if used); the desired maximum annotation width */ + private int annotationWidth; + + /** + * >= 8 (if used); the number of bytes of hex output to use + * in annotations + */ + private int hexCols; + + private int currentIndent = 0; + private int indentAmount = 2; + + /** + * Constructs an instance with a fixed maximum size. Note that the + * given array is the only one that will be used to store data. In + * particular, no reallocation will occur in order to expand the + * capacity of the resulting instance. Also, the constructed + * instance does not keep annotations by default. + * + * @param data non-null; data array to use for output + */ + public ByteArrayAnnotatedOutput(byte[] data) { + this(data, false); + } + + /** + * Constructs a "stretchy" instance. The underlying array may be + * reallocated. The constructed instance does not keep annotations + * by default. + */ + public ByteArrayAnnotatedOutput() { + this(new byte[DEFAULT_SIZE], true); + } + + /** + * Internal constructor. + * + * @param data non-null; data array to use for output + * @param stretchy whether the instance is to be stretchy + */ + private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) { + if (data == null) { + throw new NullPointerException("data == null"); + } + + this.stretchy = stretchy; + this.data = data; + this.cursor = 0; + this.verbose = false; + this.annotations = null; + this.annotationWidth = 0; + this.hexCols = 0; + } + + /** + * Gets the underlying byte[] of this instance, which + * may be larger than the number of bytes written + * + * @see #toByteArray + * + * @return non-null; the byte[] + */ + public byte[] getArray() { + return data; + } + + /** + * Constructs and returns a new byte[] that contains + * the written contents exactly (that is, with no extra unwritten + * bytes at the end). + * + * @see #getArray + * + * @return non-null; an appropriately-constructed array + */ + public byte[] toByteArray() { + byte[] result = new byte[cursor]; + System.arraycopy(data, 0, result, 0, cursor); + return result; + } + + /** {@inheritDoc} */ + public int getCursor() { + return cursor; + } + + /** {@inheritDoc} */ + public void assertCursor(int expectedCursor) { + if (cursor != expectedCursor) { + throw new ExceptionWithContext("expected cursor " + + expectedCursor + "; actual value: " + cursor); + } + } + + /** {@inheritDoc} */ + public void writeByte(int value) { + int writeAt = cursor; + int end = writeAt + 1; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + data[writeAt] = (byte) value; + cursor = end; + } + + /** {@inheritDoc} */ + public void writeShort(int value) { + int writeAt = cursor; + int end = writeAt + 2; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + data[writeAt] = (byte) value; + data[writeAt + 1] = (byte) (value >> 8); + cursor = end; + } + + /** {@inheritDoc} */ + public void writeInt(int value) { + int writeAt = cursor; + int end = writeAt + 4; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + data[writeAt] = (byte) value; + data[writeAt + 1] = (byte) (value >> 8); + data[writeAt + 2] = (byte) (value >> 16); + data[writeAt + 3] = (byte) (value >> 24); + cursor = end; + } + + /** {@inheritDoc} */ + public void writeLong(long value) { + int writeAt = cursor; + int end = writeAt + 8; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + int half = (int) value; + data[writeAt] = (byte) half; + data[writeAt + 1] = (byte) (half >> 8); + data[writeAt + 2] = (byte) (half >> 16); + data[writeAt + 3] = (byte) (half >> 24); + + half = (int) (value >> 32); + data[writeAt + 4] = (byte) half; + data[writeAt + 5] = (byte) (half >> 8); + data[writeAt + 6] = (byte) (half >> 16); + data[writeAt + 7] = (byte) (half >> 24); + + cursor = end; + } + + /** {@inheritDoc} */ + public int writeUnsignedLeb128(int value) { + long remaining = (value & 0xFFFFFFFFL) >> 7; + long lValue = value; + int count = 0; + + while (remaining != 0) { + writeByte((int)(lValue & 0x7f) | 0x80); + lValue = remaining; + remaining >>= 7; + count++; + } + + writeByte((int)(lValue & 0x7f)); + return count + 1; + } + + /** {@inheritDoc} */ + public int writeSignedLeb128(int value) { + int remaining = value >> 7; + int count = 0; + boolean hasMore = true; + int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; + + while (hasMore) { + hasMore = (remaining != end) + || ((remaining & 1) != ((value >> 6) & 1)); + + writeByte((value & 0x7f) | (hasMore ? 0x80 : 0)); + value = remaining; + remaining >>= 7; + count++; + } + + return count; + } + + /** {@inheritDoc} */ + public void write(ByteArray bytes) { + int blen = bytes.size(); + int writeAt = cursor; + int end = writeAt + blen; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + bytes.getBytes(data, writeAt); + cursor = end; + } + + /** {@inheritDoc} */ + public void write(byte[] bytes, int offset, int length) { + int writeAt = cursor; + int end = writeAt + length; + int bytesEnd = offset + length; + + // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0) + if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) { + throw new IndexOutOfBoundsException("bytes.length " + + bytes.length + "; " + + offset + "..!" + end); + } + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + System.arraycopy(bytes, offset, data, writeAt, length); + cursor = end; + } + + /** {@inheritDoc} */ + public void write(byte[] bytes) { + write(bytes, 0, bytes.length); + } + + /** {@inheritDoc} */ + public void writeZeroes(int count) { + if (count < 0) { + throw new IllegalArgumentException("count < 0"); + } + + int end = cursor + count; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + /* + * There is no need to actually write zeroes, since the array is + * already preinitialized with zeroes. + */ + + cursor = end; + } + + /** {@inheritDoc} */ + public void alignTo(int alignment) { + int mask = alignment - 1; + + if ((alignment < 0) || ((mask & alignment) != 0)) { + throw new IllegalArgumentException("bogus alignment"); + } + + int end = (cursor + mask) & ~mask; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + /* + * There is no need to actually write zeroes, since the array is + * already preinitialized with zeroes. + */ + + cursor = end; + } + + /** {@inheritDoc} */ + public boolean annotates() { + return (annotations != null); + } + + /** {@inheritDoc} */ + public boolean isVerbose() { + return verbose; + } + + /** {@inheritDoc} */ + public void annotate(String msg) { + if (annotations == null) { + return; + } + + endAnnotation(); + annotations.add(new Annotation(cursor, msg, currentIndent)); + } + + public void indent() { + currentIndent++; + } + + public void deindent() { + currentIndent--; + if (currentIndent < 0) { + currentIndent = 0; + } + } + + public void setIndentAmount(int indentAmount) { + this.indentAmount = indentAmount; + } + + /** {@inheritDoc} */ + public void annotate(int amt, String msg) { + if (annotations == null) { + return; + } + + endAnnotation(); + + int asz = annotations.size(); + int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd(); + int startAt; + + if (lastEnd <= cursor) { + startAt = cursor; + } else { + startAt = lastEnd; + } + + annotations.add(new Annotation(startAt, startAt + amt, msg, currentIndent)); + } + + /** {@inheritDoc} */ + public void endAnnotation() { + if (annotations == null) { + return; + } + + int sz = annotations.size(); + + if (sz != 0) { + annotations.get(sz - 1).setEndIfUnset(cursor); + } + } + + /** {@inheritDoc} */ + public int getAnnotationWidth() { + int leftWidth = 8 + (hexCols * 2) + (hexCols / 2); + + return annotationWidth - leftWidth; + } + + /** + * Indicates that this instance should keep annotations. This method may + * be called only once per instance, and only before any data has been + * written to the it. + * + * @param annotationWidth >= 40; the desired maximum annotation width + * @param verbose whether or not to indicate verbose annotations + */ + public void enableAnnotations(int annotationWidth, boolean verbose) { + if ((annotations != null) || (cursor != 0)) { + throw new RuntimeException("cannot enable annotations"); + } + + if (annotationWidth < 40) { + throw new IllegalArgumentException("annotationWidth < 40"); + } + + int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1; + if (hexCols < 6) { + hexCols = 6; + } else if (hexCols > 10) { + hexCols = 10; + } + + this.annotations = new ArrayList(1000); + this.annotationWidth = annotationWidth; + this.hexCols = hexCols; + this.verbose = verbose; + } + + /** + * Finishes up annotation processing. This closes off any open + * annotations and removes annotations that don't refer to written + * data. + */ + public void finishAnnotating() { + // Close off the final annotation, if any. + endAnnotation(); + + // Remove annotations that refer to unwritten data. + if (annotations != null) { + int asz = annotations.size(); + while (asz > 0) { + Annotation last = annotations.get(asz - 1); + if (last.getStart() > cursor) { + annotations.remove(asz - 1); + asz--; + } else if (last.getEnd() > cursor) { + last.setEnd(cursor); + break; + } else { + break; + } + } + } + } + + /** + * Writes the annotated content of this instance to the given writer. + * + * @param out non-null; where to write to + */ + public void writeAnnotationsTo(Writer out) throws IOException { + int width2 = getAnnotationWidth(); + int width1 = annotationWidth - width2 - 1; + + StringBuilder padding = new StringBuilder(); + for (int i=0; i<1000; i++) { + padding.append(' '); + } + + TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|"); + Writer left = twoc.getLeft(); + Writer right = twoc.getRight(); + int leftAt = 0; // left-hand byte output cursor + int rightAt = 0; // right-hand annotation index + int rightSz = annotations.size(); + + while ((leftAt < cursor) && (rightAt < rightSz)) { + Annotation a = annotations.get(rightAt); + int start = a.getStart(); + int end; + String text; + + if (leftAt < start) { + // This is an area with no annotation. + end = start; + start = leftAt; + text = ""; + } else { + // This is an area with an annotation. + end = a.getEnd(); + text = padding.substring(0, a.getIndent() * this.indentAmount) + a.getText(); + rightAt++; + } + + left.write(Hex.dump(data, start, end - start, start, hexCols, 6)); + right.write(text); + twoc.flush(); + leftAt = end; + } + + if (leftAt < cursor) { + // There is unannotated output at the end. + left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt, + hexCols, 6)); + } + + while (rightAt < rightSz) { + // There are zero-byte annotations at the end. + right.write(annotations.get(rightAt).getText()); + rightAt++; + } + + twoc.flush(); + } + + /** + * Throws the excpetion for when an attempt is made to write past the + * end of the instance. + */ + private static void throwBounds() { + throw new IndexOutOfBoundsException("attempt to write past the end"); + } + + /** + * Reallocates the underlying array if necessary. Calls to this method + * should be guarded by a test of {@link #stretchy}. + * + * @param desiredSize >= 0; the desired minimum total size of the array + */ + private void ensureCapacity(int desiredSize) { + if (data.length < desiredSize) { + byte[] newData = new byte[desiredSize * 2 + 1000]; + System.arraycopy(data, 0, newData, 0, cursor); + data = newData; + } + } + + /** + * Annotation on output. + */ + private static class Annotation { + /** >= 0; start of annotated range (inclusive) */ + private final int start; + + /** + * >= 0; end of annotated range (exclusive); + * Integer.MAX_VALUE if unclosed + */ + private int end; + + /** non-null; annotation text */ + private final String text; + + private int indent; + + /** + * Constructs an instance. + * + * @param start >= 0; start of annotated range + * @param end >= start; end of annotated range (exclusive) or + * Integer.MAX_VALUE if unclosed + * @param text non-null; annotation text + */ + public Annotation(int start, int end, String text, int indent) { + this.start = start; + this.end = end; + this.text = text; + this.indent = indent; + } + + /** + * Constructs an instance. It is initally unclosed. + * + * @param start >= 0; start of annotated range + * @param text non-null; annotation text + */ + public Annotation(int start, String text, int indent) { + this(start, Integer.MAX_VALUE, text, indent); + } + + /** + * Sets the end as given, but only if the instance is unclosed; + * otherwise, do nothing. + * + * @param end >= start; the end + */ + public void setEndIfUnset(int end) { + if (this.end == Integer.MAX_VALUE) { + this.end = end; + } + } + + /** + * Sets the end as given. + * + * @param end >= start; the end + */ + public void setEnd(int end) { + this.end = end; + } + + /** + * Gets the start. + * + * @return the start + */ + public int getStart() { + return start; + } + + /** + * Gets the end. + * + * @return the end + */ + public int getEnd() { + return end; + } + + /** + * Gets the text. + * + * @return non-null; the text + */ + public String getText() { + return text; + } + + public int getIndent() { + return indent; + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java new file mode 100644 index 00000000..e74a62ec --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +/** + * Implementation of {@link Input} which reads the data from a + * byte[] instance. + * + *

    Note: As per the {@link Input } interface, multi-byte + * reads all use little-endian order.

    + */ +public class ByteArrayInput + implements Input { + + /** non-null; the data itself */ + private byte[] data; + + /** >= 0; current read cursor */ + private int cursor; + + /** + * Constructs an instance with the given data + * + * @param data non-null; data array to use for input + */ + public ByteArrayInput(byte[] data) { + if (data == null) { + throw new NullPointerException("data == null"); + } + + this.data = data; + this.cursor = 0; + } + + /** + * Gets the underlying byte[] of this instance + * + * @return non-null; the byte[] + */ + public byte[] getArray() { + return data; + } + + /** {@inheritDoc} */ + public int getCursor() { + return cursor; + } + + /** {@inheritDoc} */ + public void setCursor(int cursor) { + if (cursor < 0 || cursor >= data.length) + throw new IndexOutOfBoundsException("The provided cursor value " + + "is not within the bounds of this instance's data array"); + this.cursor = cursor; + } + + /** {@inheritDoc} */ + public void assertCursor(int expectedCursor) { + if (cursor != expectedCursor) { + throw new ExceptionWithContext("expected cursor " + + expectedCursor + "; actual value: " + cursor); + } + } + + /** {@inheritDoc} */ + public byte readByte() { + return data[cursor++]; + } + + /** {@inheritDoc} */ + public int readShort() { + int readAt = cursor; + int result = ((data[readAt++] & 0xff) + + ((data[readAt++] & 0xff) << 8)); + cursor = readAt; + return result; + } + + /** {@inheritDoc} */ + public int readInt() { + int readAt = cursor; + int result = (data[readAt++] & 0xff) + + ((data[readAt++] & 0xff) << 8) + + ((data[readAt++] & 0xff) << 16) + + ((data[readAt++] & 0xff) << 24); + cursor = readAt; + return result; + } + + /** {@inheritDoc} */ + public long readLong() { + int readAt = cursor; + + long result = (data[readAt++] & 0xffL) | + ((data[readAt++] & 0xffL) << 8) | + ((data[readAt++] & 0xffL) << 16) | + ((data[readAt++] & 0xffL) << 24) | + ((data[readAt++] & 0xffL) << 32) | + ((data[readAt++] & 0xffL) << 40) | + ((data[readAt++] & 0xffL) << 48) | + ((data[readAt++] & 0xffL) << 56); + cursor = readAt; + return result; + } + + + /** {@inheritDoc} */ + public int readUnsignedOrSignedLeb128() { + int end = cursor; + int currentByteValue; + int result; + + result = data[end++] & 0xff; + if (result > 0x7f) { + currentByteValue = data[end++] & 0xff; + result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7); + if (currentByteValue > 0x7f) { + currentByteValue = data[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 14; + if (currentByteValue > 0x7f) { + currentByteValue = data[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 21; + if (currentByteValue > 0x7f) { + currentByteValue = data[end++] & 0xff; + if (currentByteValue > 0x0f) { + throwInvalidLeb(); + } + result |= currentByteValue << 28; + } + } + } + } else { + cursor = end; + return result; + } + + cursor = end; + + //If the last byte is 0, then this was an unsigned value (incorrectly) written in a signed format + //The caller wants to know if this is the case, so we'll return the negated value instead + //If there was only a single byte that had a value of 0, then we would have returned in the above + //"else" + if (data[end-1] == 0) { + return ~result; + } + return result; + } + + + + + /** {@inheritDoc} */ + public int readUnsignedLeb128() { + int end = cursor; + int currentByteValue; + int result; + + result = data[end++] & 0xff; + if (result > 0x7f) { + currentByteValue = data[end++] & 0xff; + result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7); + if (currentByteValue > 0x7f) { + currentByteValue = data[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 14; + if (currentByteValue > 0x7f) { + currentByteValue = data[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 21; + if (currentByteValue > 0x7f) { + currentByteValue = data[end++] & 0xff; + if (currentByteValue > 0x0f) { + throwInvalidLeb(); + } + result |= currentByteValue << 28; + } + } + } + } + + cursor = end; + return result; + } + + /** {@inheritDoc} */ + public int readSignedLeb128() { + int end = cursor; + int currentByteValue; + int result; + + result = data[end++] & 0xff; + if (result <= 0x7f) { + result = (result << 25) >> 25; + } else { + currentByteValue = data[end++] & 0xff; + result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7); + if (currentByteValue <= 0x7f) { + result = (result << 18) >> 18; + } else { + currentByteValue = data[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 14; + if (currentByteValue <= 0x7f) { + result = (result << 11) >> 11; + } else { + currentByteValue = data[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 21; + if (currentByteValue <= 0x7f) { + result = (result << 4) >> 4; + } else { + currentByteValue = data[end++] & 0xff; + if (currentByteValue > 0x0f) { + throwInvalidLeb(); + } + result |= currentByteValue << 28; + } + } + } + } + + cursor = end; + return result; + } + + /** {@inheritDoc} */ + public void read(byte[] bytes, int offset, int length) { + int end = cursor + length; + + if (end > data.length) { + throwBounds(); + } + + System.arraycopy(data, cursor, bytes, offset, length); + cursor = end; + } + + /** {@inheritDoc} */ + public void read(byte[] bytes) { + int length = bytes.length; + int end = cursor + length; + + if (end > data.length) { + throwBounds(); + } + + System.arraycopy(data, cursor, bytes, 0, length); + cursor = end; + } + + /** {@inheritDoc} */ + public byte[] readBytes(int length) { + int end = cursor + length; + + if (end > data.length) { + throwBounds(); + } + + byte[] result = new byte[length]; + System.arraycopy(data, cursor, result, 0, length); + cursor = end; + return result; + } + + /** {@inheritDoc} */ + public String realNullTerminatedUtf8String() { + int startPosition = cursor; + while (data[cursor] != 0) { + cursor++; + } + int byteCount = cursor - startPosition; + + //skip the terminating null + cursor++; + + return Utf8Utils.utf8BytesToString(data, startPosition, byteCount); + } + + /** {@inheritDoc} */ + public void skipBytes(int count) { + cursor += count; + } + + /** {@inheritDoc} */ + public void alignTo(int alignment) { + cursor = AlignmentUtils.alignOffset(cursor, alignment); + } + + /** + * Throws the excpetion for when an attempt is made to read past the + * end of the instance. + */ + private static void throwBounds() { + throw new IndexOutOfBoundsException("attempt to read past the end"); + } + + /** + * Throws the exception for when an invalid LEB128 value is encountered + */ + private static void throwInvalidLeb() { + throw new RuntimeException("invalid LEB128 integer encountered"); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java new file mode 100644 index 00000000..f2a1f709 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +import java.util.ArrayList; + +/** + * Implementation of {@link AnnotatedOutput} which stores the written data + * into a byte[]. + * + *

    Note: As per the {@link Output} interface, multi-byte + * writes all use little-endian order.

    + */ +public final class ByteArrayOutput implements Output +{ + /** default size for stretchy instances */ + private static final int DEFAULT_SIZE = 1000; + + /** + * whether the instance is stretchy, that is, whether its array + * may be resized to increase capacity + */ + private final boolean stretchy; + + /** non-null; the data itself */ + private byte[] data; + + /** >= 0; current output cursor */ + private int cursor; + + /** whether annotations are to be verbose */ + private boolean verbose; + + /** + * null-ok; list of annotations, or null if this instance + * isn't keeping them + */ + private ArrayList annotations; + + /** >= 40 (if used); the desired maximum annotation width */ + private int annotationWidth; + + /** + * >= 8 (if used); the number of bytes of hex output to use + * in annotations + */ + private int hexCols; + + /** + * Constructs an instance with a fixed maximum size. Note that the + * given array is the only one that will be used to store data. In + * particular, no reallocation will occur in order to expand the + * capacity of the resulting instance. Also, the constructed + * instance does not keep annotations by default. + * + * @param data non-null; data array to use for output + */ + public ByteArrayOutput(byte[] data) { + this(data, false); + } + + /** + * Constructs a "stretchy" instance. The underlying array may be + * reallocated. The constructed instance does not keep annotations + * by default. + */ + public ByteArrayOutput() { + this(new byte[DEFAULT_SIZE], true); + } + + /** + * Internal constructor. + * + * @param data non-null; data array to use for output + * @param stretchy whether the instance is to be stretchy + */ + private ByteArrayOutput(byte[] data, boolean stretchy) { + if (data == null) { + throw new NullPointerException("data == null"); + } + + this.stretchy = stretchy; + this.data = data; + this.cursor = 0; + this.verbose = false; + this.annotations = null; + this.annotationWidth = 0; + this.hexCols = 0; + } + + /** + * Gets the underlying byte[] of this instance, which + * may be larger than the number of bytes written + * + * @see #toByteArray + * + * @return non-null; the byte[] + */ + public byte[] getArray() { + return data; + } + + /** + * Constructs and returns a new byte[] that contains + * the written contents exactly (that is, with no extra unwritten + * bytes at the end). + * + * @see #getArray + * + * @return non-null; an appropriately-constructed array + */ + public byte[] toByteArray() { + byte[] result = new byte[cursor]; + System.arraycopy(data, 0, result, 0, cursor); + return result; + } + + /** {@inheritDoc} */ + public int getCursor() { + return cursor; + } + + /** {@inheritDoc} */ + public void assertCursor(int expectedCursor) { + if (cursor != expectedCursor) { + throw new ExceptionWithContext("expected cursor " + + expectedCursor + "; actual value: " + cursor); + } + } + + /** {@inheritDoc} */ + public void writeByte(int value) { + int writeAt = cursor; + int end = writeAt + 1; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + data[writeAt] = (byte) value; + cursor = end; + } + + /** {@inheritDoc} */ + public void writeShort(int value) { + int writeAt = cursor; + int end = writeAt + 2; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + data[writeAt] = (byte) value; + data[writeAt + 1] = (byte) (value >> 8); + cursor = end; + } + + /** {@inheritDoc} */ + public void writeInt(int value) { + int writeAt = cursor; + int end = writeAt + 4; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + data[writeAt] = (byte) value; + data[writeAt + 1] = (byte) (value >> 8); + data[writeAt + 2] = (byte) (value >> 16); + data[writeAt + 3] = (byte) (value >> 24); + cursor = end; + } + + /** {@inheritDoc} */ + public void writeLong(long value) { + int writeAt = cursor; + int end = writeAt + 8; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + int half = (int) value; + data[writeAt] = (byte) half; + data[writeAt + 1] = (byte) (half >> 8); + data[writeAt + 2] = (byte) (half >> 16); + data[writeAt + 3] = (byte) (half >> 24); + + half = (int) (value >> 32); + data[writeAt + 4] = (byte) half; + data[writeAt + 5] = (byte) (half >> 8); + data[writeAt + 6] = (byte) (half >> 16); + data[writeAt + 7] = (byte) (half >> 24); + + cursor = end; + } + + /** {@inheritDoc} */ + public int writeUnsignedLeb128(int value) { + int remaining = value >>> 7; + int count = 0; + + while (remaining != 0) { + writeByte((value & 0x7f) | 0x80); + value = remaining; + remaining >>>= 7; + count++; + } + + writeByte(value & 0x7f); + return count + 1; + } + + /** {@inheritDoc} */ + public int writeSignedLeb128(int value) { + int remaining = value >> 7; + int count = 0; + boolean hasMore = true; + int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; + + while (hasMore) { + hasMore = (remaining != end) + || ((remaining & 1) != ((value >> 6) & 1)); + + writeByte((value & 0x7f) | (hasMore ? 0x80 : 0)); + value = remaining; + remaining >>= 7; + count++; + } + + return count; + } + + /** {@inheritDoc} */ + public void write(ByteArray bytes) { + int blen = bytes.size(); + int writeAt = cursor; + int end = writeAt + blen; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + bytes.getBytes(data, writeAt); + cursor = end; + } + + /** {@inheritDoc} */ + public void write(byte[] bytes, int offset, int length) { + int writeAt = cursor; + int end = writeAt + length; + int bytesEnd = offset + length; + + // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0) + if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) { + throw new IndexOutOfBoundsException("bytes.length " + + bytes.length + "; " + + offset + "..!" + end); + } + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + System.arraycopy(bytes, offset, data, writeAt, length); + cursor = end; + } + + /** {@inheritDoc} */ + public void write(byte[] bytes) { + write(bytes, 0, bytes.length); + } + + /** {@inheritDoc} */ + public void writeZeroes(int count) { + if (count < 0) { + throw new IllegalArgumentException("count < 0"); + } + + int end = cursor + count; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + /* + * There is no need to actually write zeroes, since the array is + * already preinitialized with zeroes. + */ + + cursor = end; + } + + /** {@inheritDoc} */ + public void alignTo(int alignment) { + int end = AlignmentUtils.alignOffset(cursor, alignment); + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + cursor = end; + } + + /** {@inheritDoc} */ + public boolean annotates() { + return (annotations != null); + } + + /** {@inheritDoc} */ + public boolean isVerbose() { + return verbose; + } + + /** {@inheritDoc} */ + public void annotate(String msg) { + if (annotations == null) { + return; + } + + endAnnotation(); + annotations.add(new Annotation(cursor, msg)); + } + + /** {@inheritDoc} */ + public void annotate(int amt, String msg) { + if (annotations == null) { + return; + } + + endAnnotation(); + + int asz = annotations.size(); + int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd(); + int startAt; + + if (lastEnd <= cursor) { + startAt = cursor; + } else { + startAt = lastEnd; + } + + annotations.add(new Annotation(startAt, startAt + amt, msg)); + } + + /** {@inheritDoc} */ + public void endAnnotation() { + if (annotations == null) { + return; + } + + int sz = annotations.size(); + + if (sz != 0) { + annotations.get(sz - 1).setEndIfUnset(cursor); + } + } + + /** {@inheritDoc} */ + public int getAnnotationWidth() { + int leftWidth = 8 + (hexCols * 2) + (hexCols / 2); + + return annotationWidth - leftWidth; + } + + /** + * Indicates that this instance should keep annotations. This method may + * be called only once per instance, and only before any data has been + * written to the it. + * + * @param annotationWidth >= 40; the desired maximum annotation width + * @param verbose whether or not to indicate verbose annotations + */ + public void enableAnnotations(int annotationWidth, boolean verbose) { + if ((annotations != null) || (cursor != 0)) { + throw new RuntimeException("cannot enable annotations"); + } + + if (annotationWidth < 40) { + throw new IllegalArgumentException("annotationWidth < 40"); + } + + int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1; + if (hexCols < 6) { + hexCols = 6; + } else if (hexCols > 10) { + hexCols = 10; + } + + this.annotations = new ArrayList(1000); + this.annotationWidth = annotationWidth; + this.hexCols = hexCols; + this.verbose = verbose; + } + + /** + * Finishes up annotation processing. This closes off any open + * annotations and removes annotations that don't refer to written + * data. + */ + public void finishAnnotating() { + // Close off the final annotation, if any. + endAnnotation(); + + // Remove annotations that refer to unwritten data. + if (annotations != null) { + int asz = annotations.size(); + while (asz > 0) { + Annotation last = annotations.get(asz - 1); + if (last.getStart() > cursor) { + annotations.remove(asz - 1); + asz--; + } else if (last.getEnd() > cursor) { + last.setEnd(cursor); + break; + } else { + break; + } + } + } + } + + /** + * Throws the excpetion for when an attempt is made to write past the + * end of the instance. + */ + private static void throwBounds() { + throw new IndexOutOfBoundsException("attempt to write past the end"); + } + + /** + * Reallocates the underlying array if necessary. Calls to this method + * should be guarded by a test of {@link #stretchy}. + * + * @param desiredSize >= 0; the desired minimum total size of the array + */ + private void ensureCapacity(int desiredSize) { + if (data.length < desiredSize) { + byte[] newData = new byte[desiredSize * 2 + 1000]; + System.arraycopy(data, 0, newData, 0, cursor); + data = newData; + } + } + + /** + * Annotation on output. + */ + private static class Annotation { + /** >= 0; start of annotated range (inclusive) */ + private final int start; + + /** + * >= 0; end of annotated range (exclusive); + * Integer.MAX_VALUE if unclosed + */ + private int end; + + /** non-null; annotation text */ + private final String text; + + /** + * Constructs an instance. + * + * @param start >= 0; start of annotated range + * @param end >= start; end of annotated range (exclusive) or + * Integer.MAX_VALUE if unclosed + * @param text non-null; annotation text + */ + public Annotation(int start, int end, String text) { + this.start = start; + this.end = end; + this.text = text; + } + + /** + * Constructs an instance. It is initally unclosed. + * + * @param start >= 0; start of annotated range + * @param text non-null; annotation text + */ + public Annotation(int start, String text) { + this(start, Integer.MAX_VALUE, text); + } + + /** + * Sets the end as given, but only if the instance is unclosed; + * otherwise, do nothing. + * + * @param end >= start; the end + */ + public void setEndIfUnset(int end) { + if (this.end == Integer.MAX_VALUE) { + this.end = end; + } + } + + /** + * Sets the end as given. + * + * @param end >= start; the end + */ + public void setEnd(int end) { + this.end = end; + } + + /** + * Gets the start. + * + * @return the start + */ + public int getStart() { + return start; + } + + /** + * Gets the end. + * + * @return the end + */ + public int getEnd() { + return end; + } + + /** + * Gets the text. + * + * @return non-null; the text + */ + public String getText() { + return text; + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java new file mode 100644 index 00000000..2bfd8cd6 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java @@ -0,0 +1,451 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +import org.jf.dexlib.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class is intended to provide an easy to use container to build up a method's debug info. You can easily add + * an "event" at a specific address, where an event is something like a line number, start/end local, etc. + * The events must be added such that the code addresses increase monotonically. This matches how a parser would + * generally behave, and is intended to increase performance. + */ +public class DebugInfoBuilder +{ + private static final int LINE_BASE = -4; + private static final int LINE_RANGE = 15; + private static final int FIRST_SPECIAL = 0x0a; + + private int lineStart = 0; + private ArrayList parameterNames = new ArrayList(); + private ArrayList events = new ArrayList(); + private int lastAddress = 0; + + private boolean hasData; + + private int currentAddress; + private int currentLine; + + public DebugInfoBuilder() { + } + + private void checkAddress(int address) { + if (lastAddress > address) { + throw new RuntimeException("Cannot add an event with an address before the address of the prior event"); + } + } + + public void addParameterName(String parameterName) { + if (parameterName != null) { + hasData = true; + } + + parameterNames.add(parameterName); + } + + public void addLine(int address, int line) { + hasData = true; + + checkAddress(address); + + if (lineStart == 0) { + lineStart = line; + } + + events.add(new LineEvent(address, line)); + } + + public void addLocal(int address, int registerNumber, String localName, String localType) { + hasData = true; + + checkAddress(address); + + events.add(new StartLocalEvent(address, registerNumber, localName, localType)); + } + + public void addLocalExtended(int address, int registerNumber, String localName, String localType, + String signature) { + hasData = true; + + checkAddress(address); + + events.add(new StartLocalExtendedEvent(address, registerNumber, localName, localType, signature)); + } + + public void addEndLocal(int address, int registerNumber) { + hasData = true; + + checkAddress(address); + + events.add(new EndLocalEvent(address, registerNumber)); + } + + public void addRestartLocal(int address, int registerNumber) { + hasData = true; + + checkAddress(address); + + events.add(new RestartLocalEvent(address, registerNumber)); + } + + public void addPrologue(int address) { + hasData = true; + + checkAddress(address); + + events.add(new PrologueEvent(address)); + } + + public void addEpilogue(int address) { + hasData = true; + + checkAddress(address); + + events.add(new EpilogueEvent(address)); + } + + public void addSetFile(int address, String fileName) { + hasData = true; + + checkAddress(address); + + events.add(new SetFileEvent(address, fileName)); + } + + public int getParameterNameCount() { + return parameterNames.size(); + } + + public DebugInfoItem encodeDebugInfo(DexFile dexFile) { + if (!hasData) { + return null; + } + + ByteArrayOutput out = new ByteArrayOutput(); + StringIdItem[] parameterNamesArray = new StringIdItem[parameterNames.size()]; + ArrayList referencedItems = new ArrayList(); + + if (lineStart == 0) { + lineStart = 1; + } + + currentLine = lineStart; + + for (Event event: events) { + event.emit(dexFile, out, referencedItems); + } + emitEndSequence(out); + + int index = 0; + for (String parameterName: parameterNames) { + if (parameterName == null) { + parameterNamesArray[index++] = null; + } else { + parameterNamesArray[index++] = StringIdItem.internStringIdItem(dexFile, parameterName); + } + } + + Item[] referencedItemsArray = new Item[referencedItems.size()]; + referencedItems.toArray(referencedItemsArray); + return DebugInfoItem.internDebugInfoItem(dexFile, lineStart, parameterNamesArray, out.toByteArray(), + referencedItemsArray); + } + + public static byte calculateSpecialOpcode(int lineDelta, int addressDelta) { + return (byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE)); + } + + private interface Event + { + int getAddress(); + void emit(DexFile dexFile, Output out, List referencedItems); + } + + private void emitEndSequence(Output out) { + out.writeByte(0); + } + + private void emitAdvancePC(Output out, int address) { + int addressDelta = address-currentAddress; + + if (addressDelta > 0) { + out.writeByte(1); + out.writeUnsignedLeb128(addressDelta); + currentAddress = address; + } + } + + private void emitAdvanceLine(Output out, int lineDelta) { + out.writeByte(2); + out.writeSignedLeb128(lineDelta); + } + + private void emitStartLocal(Output out, int registerNum) { + out.writeByte(3); + out.writeUnsignedLeb128(registerNum); + out.writeByte(1); + out.writeByte(1); + } + + private void emitStartLocalExtended(Output out, int registerNum) { + out.writeByte(4); + out.writeUnsignedLeb128(registerNum); + out.writeByte(1); + out.writeByte(1); + out.writeByte(1); + } + + private void emitEndLocal(Output out, int registerNum) { + out.writeByte(5); + out.writeUnsignedLeb128(registerNum); + } + + private void emitRestartLocal(Output out, int registerNum) { + out.writeByte(6); + out.writeUnsignedLeb128(registerNum); + } + + private void emitSetPrologueEnd(Output out) { + out.writeByte(7); + } + + private void emitSetEpilogueBegin(Output out) { + out.writeByte(8); + } + + private void emitSetFile(Output out) { + out.writeByte(9); + out.writeByte(1); + } + + private void emitSpecialOpcode(Output out, byte opcode) { + out.writeByte(opcode); + } + + private class LineEvent implements Event + { + private final int address; + private final int line; + + public LineEvent(int address, int line) { + this.address = address; + this.line = line; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, Output out, List referencedItems) { + int lineDelta = line - currentLine; + int addressDelta = address - currentAddress; + + if (lineDelta < -4 || lineDelta > 10) { + emitAdvanceLine(out, lineDelta); + lineDelta = 0; + } + if (lineDelta < 2 && addressDelta > 16 || lineDelta > 1 && addressDelta > 15) { + emitAdvancePC(out, address); + addressDelta = 0; + } + + //TODO: need to handle the case when the line delta is larger than a signed int + emitSpecialOpcode(out, calculateSpecialOpcode(lineDelta, addressDelta)); + + currentAddress = address; + currentLine = line; + } + } + + private class StartLocalEvent implements Event + { + private final int address; + private final int registerNum; + private final String localName; + private final String localType; + + public StartLocalEvent(int address, int registerNum, String localName, String localType) { + this.address = address; + this.registerNum = registerNum; + this.localName = localName; + this.localType = localType; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, Output out, List referencedItems) { + emitAdvancePC(out, address); + emitStartLocal(out, registerNum); + referencedItems.add(localName==null?null:StringIdItem.internStringIdItem(dexFile, localName)); + referencedItems.add(localType==null?null:TypeIdItem.internTypeIdItem(dexFile, + StringIdItem.internStringIdItem(dexFile, localType))); + } + } + + private class StartLocalExtendedEvent implements Event + { + private final int address; + private final int registerNum; + private final String localName; + private final String localType; + private final String signature; + + public StartLocalExtendedEvent(int address, int registerNum, String localName, String localType, + String signature) { + this.address = address; + this.registerNum = registerNum; + this.localName = localName; + this.localType = localType; + this.signature = signature; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, Output out, List referencedItems) { + emitAdvancePC(out, address); + emitStartLocalExtended(out, registerNum); + if (localName != null) { + referencedItems.add(StringIdItem.internStringIdItem(dexFile, localName)); + } + if (localType != null) { + referencedItems.add(TypeIdItem.internTypeIdItem(dexFile, + StringIdItem.internStringIdItem(dexFile, localType))); + } + if (signature != null) { + referencedItems.add(StringIdItem.internStringIdItem(dexFile, signature)); + } + } + } + + private class EndLocalEvent implements Event + { + private final int address; + private final int registerNum; + + public EndLocalEvent(int address, int registerNum) { + this.address = address; + this.registerNum = registerNum; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, Output out, List referencedItems) { + emitAdvancePC(out, address); + emitEndLocal(out, registerNum); + } + } + + private class RestartLocalEvent implements Event + { + private final int address; + private final int registerNum; + + public RestartLocalEvent(int address, int registerNum) { + this.address = address; + this.registerNum = registerNum; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, Output out, List referencedItems) { + emitAdvancePC(out, address); + emitRestartLocal(out, registerNum); + } + } + + private class PrologueEvent implements Event + { + private final int address; + + public PrologueEvent(int address) { + this.address = address; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, Output out, List referencedItems) { + emitAdvancePC(out, address); + emitSetPrologueEnd(out); + } + } + + private class EpilogueEvent implements Event + { + private final int address; + + public EpilogueEvent(int address) { + this.address = address; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, Output out, List referencedItems) { + emitAdvancePC(out, address); + emitSetEpilogueBegin(out); + } + } + + private class SetFileEvent implements Event + { + private final int address; + private final String fileName; + + public SetFileEvent(int address, String fileName) { + this.address = address; + this.fileName = fileName; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, Output out, List referencedItems) { + emitAdvancePC(out, address); + emitSetFile(out); + if (fileName != null) { + referencedItems.add(StringIdItem.internStringIdItem(dexFile, fileName)); + } + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java new file mode 100644 index 00000000..91f14072 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java @@ -0,0 +1,143 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +public class EncodedValueUtils { + public static byte getRequiredBytesForSignedIntegralValue(long value) { + /* + * Figure out how many bits are needed to represent the value, + * including a sign bit: The bit count is subtracted from 65 + * and not 64 to account for the sign bit. The xor operation + * has the effect of leaving non-negative values alone and + * unary complementing negative values (so that a leading zero + * count always returns a useful number for our present + * purpose). + */ + int requiredBits = + 65 - Long.numberOfLeadingZeros(value ^ (value >> 63)); + + // Round up the requiredBits to a number of bytes. + return (byte)((requiredBits + 0x07) >> 3); + } + + public static long decodeSignedIntegralValue(byte[] bytes) { + long value = 0; + for (int i = 0; i < bytes.length; i++) { + value |= (((long)(bytes[i] & 0xFF)) << (i * 8)); + } + + int shift = (8 - bytes.length) * 8; + return value << shift >> shift; + } + + public static byte[] encodeSignedIntegralValue(long value) { + int requiredBytes = getRequiredBytesForSignedIntegralValue(value); + + byte[] bytes = new byte[requiredBytes]; + + for (int i = 0; i < requiredBytes; i++) { + bytes[i] = (byte) value; + value >>= 8; + } + return bytes; + } + + + + + + public static byte getRequiredBytesForUnsignedIntegralValue(long value) { + // Figure out how many bits are needed to represent the value. + int requiredBits = 64 - Long.numberOfLeadingZeros(value); + if (requiredBits == 0) { + requiredBits = 1; + } + + // Round up the requiredBits to a number of bytes. + return (byte)((requiredBits + 0x07) >> 3); + } + + public static long decodeUnsignedIntegralValue(byte[] bytes) { + long value = 0; + for (int i = 0; i < bytes.length; i++) { + value |= (((long)(bytes[i] & 0xFF)) << i * 8); + } + return value; + } + + public static byte[] encodeUnsignedIntegralValue(long value) { + int requiredBytes = getRequiredBytesForUnsignedIntegralValue(value); + + byte[] bytes = new byte[requiredBytes]; + + for (int i = 0; i < requiredBytes; i++) { + bytes[i] = (byte) value; + value >>= 8; + } + return bytes; + } + + + + + + public static int getRequiredBytesForRightZeroExtendedValue(long value) { + // Figure out how many bits are needed to represent the value. + int requiredBits = 64 - Long.numberOfTrailingZeros(value); + if (requiredBits == 0) { + requiredBits = 1; + } + + // Round up the requiredBits to a number of bytes. + return (requiredBits + 0x07) >> 3; + } + + public static long decodeRightZeroExtendedValue(byte[] bytes) { + long value = 0; + for (int i = 0; i < bytes.length; i++) { + value |= (((long)(bytes[i] & 0xFF)) << (i * 8)); + } + return value << (8 - bytes.length) * 8; + } + + public static byte[] encodeRightZeroExtendedValue(long value) { + int requiredBytes = getRequiredBytesForRightZeroExtendedValue(value); + + // Scootch the first bits to be written down to the low-order bits. + value >>= 64 - (requiredBytes * 8); + + byte[] bytes = new byte[requiredBytes]; + + for(int i = 0; i < requiredBytes; i++) { + bytes[i] = (byte)value; + value >>= 8; + } + return bytes; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ExceptionWithContext.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ExceptionWithContext.java new file mode 100644 index 00000000..bc10edf1 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ExceptionWithContext.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * Exception which carries around structured context. + */ +public class ExceptionWithContext + extends RuntimeException { + /** non-null; human-oriented context of the exception */ + private StringBuffer context; + + /** + * Augments the given exception with the given context, and return the + * result. The result is either the given exception if it was an + * {@link ExceptionWithContext}, or a newly-constructed exception if it + * was not. + * + * @param ex non-null; the exception to augment + * @param str non-null; context to add + * @return non-null; an appropriate instance + */ + public static ExceptionWithContext withContext(Throwable ex, String str) { + ExceptionWithContext ewc; + + if (ex instanceof ExceptionWithContext) { + ewc = (ExceptionWithContext) ex; + } else { + ewc = new ExceptionWithContext(ex); + } + + ewc.addContext(str); + return ewc; + } + + /** + * Constructs an instance. + * + * @param message human-oriented message + */ + public ExceptionWithContext(String message) { + this(message, null); + } + + /** + * Constructs an instance. + * + * @param cause null-ok; exception that caused this one + */ + public ExceptionWithContext(Throwable cause) { + this(null, cause); + } + + /** + * Constructs an instance. + * + * @param message human-oriented message + * @param cause null-ok; exception that caused this one + */ + public ExceptionWithContext(String message, Throwable cause) { + super((message != null) ? message : + (cause != null) ? cause.getMessage() : null, + cause); + + if (cause instanceof ExceptionWithContext) { + String ctx = ((ExceptionWithContext) cause).context.toString(); + context = new StringBuffer(ctx.length() + 200); + context.append(ctx); + } else { + context = new StringBuffer(200); + } + } + + /** {@inheritDoc} */ + @Override + public void printStackTrace(PrintStream out) { + super.printStackTrace(out); + out.println(context); + } + + /** {@inheritDoc} */ + @Override + public void printStackTrace(PrintWriter out) { + super.printStackTrace(out); + out.println(context); + } + + /** + * Adds a line of context to this instance. + * + * @param str non-null; new context + */ + public void addContext(String str) { + if (str == null) { + throw new NullPointerException("str == null"); + } + + context.append(str); + if (!str.endsWith("\n")) { + context.append('\n'); + } + } + + /** + * Gets the context. + * + * @return non-null; the context + */ + public String getContext() { + return context.toString(); + } + + /** + * Prints the message and context. + * + * @param out non-null; where to print to + */ + public void printContext(PrintStream out) { + out.println(getMessage()); + out.print(context); + } + + /** + * Prints the message and context. + * + * @param out non-null; where to print to + */ + public void printContext(PrintWriter out) { + out.println(getMessage()); + out.print(context); + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java new file mode 100644 index 00000000..d8cd9bc8 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * File I/O utilities. + */ +public final class FileUtils { + /** + * This class is uninstantiable. + */ + private FileUtils() { + // This space intentionally left blank. + } + + /** + * Reads the named file, translating {@link IOException} to a + * {@link RuntimeException} of some sort. + * + * @param fileName non-null; name of the file to read + * @return non-null; contents of the file + */ + public static byte[] readFile(String fileName) + throws IOException { + File file = new File(fileName); + return readFile(file); + } + + /** + * Reads the given file, translating {@link IOException} to a + * {@link RuntimeException} of some sort. + * + * @param file non-null; the file to read + * @return non-null; contents of the file + */ + public static byte[] readFile(File file) + throws IOException { + return readFile(file, 0, -1); + } + + /** + * Reads the specified block from the given file, translating + * {@link IOException} to a {@link RuntimeException} of some sort. + * + * @param file non-null; the file to read + * @param offset the offset to begin reading + * @param length the number of bytes to read, or -1 to read to the + * end of the file + * @return non-null; contents of the file + */ + public static byte[] readFile(File file, int offset, int length) + throws IOException { + if (!file.exists()) { + throw new RuntimeException(file + ": file not found"); + } + + if (!file.isFile()) { + throw new RuntimeException(file + ": not a file"); + } + + if (!file.canRead()) { + throw new RuntimeException(file + ": file not readable"); + } + + long longLength = file.length(); + int fileLength = (int) longLength; + if (fileLength != longLength) { + throw new RuntimeException(file + ": file too long"); + } + + if (length == -1) { + length = fileLength - offset; + } + + if (offset + length > fileLength) { + throw new RuntimeException(file + ": file too short"); + } + + FileInputStream in = new FileInputStream(file); + + int at = offset; + while(at > 0) { + long amt = in.skip(at); + if (amt == -1) { + throw new RuntimeException(file + ": unexpected EOF"); + } + at -= amt; + } + + byte[] result = readStream(in, length); + + in.close(); + + return result; + } + + public static byte[] readStream(InputStream in, int length) + throws IOException { + byte[] result = new byte[length]; + int at=0; + + while (length > 0) { + int amt = in.read(result, at, length); + if (amt == -1) { + throw new RuntimeException("unexpected EOF"); + } + at += amt; + length -= amt; + } + + return result; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Hex.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Hex.java new file mode 100644 index 00000000..a56bf5f2 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Hex.java @@ -0,0 +1,315 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +/** + * Utilities for formatting numbers as hexadecimal. + */ +public final class Hex { + /** + * This class is uninstantiable. + */ + private Hex() { + // This space intentionally left blank. + } + + /** + * Formats a long as an 8-byte unsigned hex value. + * + * @param v value to format + * @return non-null; formatted form + */ + public static String u8(long v) { + char[] result = new char[16]; + for (int i = 0; i < 16; i++) { + result[15 - i] = Character.forDigit((int) v & 0x0f, 16); + v >>= 4; + } + + return new String(result); + } + + /** + * Formats an int as a 4-byte unsigned hex value. + * + * @param v value to format + * @return non-null; formatted form + */ + public static String u4(int v) { + char[] result = new char[8]; + for (int i = 0; i < 8; i++) { + result[7 - i] = Character.forDigit(v & 0x0f, 16); + v >>= 4; + } + + return new String(result); + } + + /** + * Formats an int as a 3-byte unsigned hex value. + * + * @param v value to format + * @return non-null; formatted form + */ + public static String u3(int v) { + char[] result = new char[6]; + for (int i = 0; i < 6; i++) { + result[5 - i] = Character.forDigit(v & 0x0f, 16); + v >>= 4; + } + + return new String(result); + } + + /** + * Formats an int as a 2-byte unsigned hex value. + * + * @param v value to format + * @return non-null; formatted form + */ + public static String u2(int v) { + char[] result = new char[4]; + for (int i = 0; i < 4; i++) { + result[3 - i] = Character.forDigit(v & 0x0f, 16); + v >>= 4; + } + + return new String(result); + } + + /** + * Formats an int as either a 2-byte unsigned hex value + * (if the value is small enough) or a 4-byte unsigned hex value (if + * not). + * + * @param v value to format + * @return non-null; formatted form + */ + public static String u2or4(int v) { + if (v == (char) v) { + return u2(v); + } else { + return u4(v); + } + } + + /** + * Formats an int as a 1-byte unsigned hex value. + * + * @param v value to format + * @return non-null; formatted form + */ + public static String u1(int v) { + char[] result = new char[2]; + for (int i = 0; i < 2; i++) { + result[1 - i] = Character.forDigit(v & 0x0f, 16); + v >>= 4; + } + + return new String(result); + } + + /** + * Formats an int as a 4-bit unsigned hex nibble. + * + * @param v value to format + * @return non-null; formatted form + */ + public static String uNibble(int v) { + char[] result = new char[1]; + + result[0] = Character.forDigit(v & 0x0f, 16); + return new String(result); + } + + /** + * Formats a long as an 8-byte signed hex value. + * + * @param v value to format + * @return non-null; formatted form + */ + public static String s8(long v) { + char[] result = new char[17]; + + if (v < 0) { + result[0] = '-'; + v = -v; + } else { + result[0] = '+'; + } + + for (int i = 0; i < 16; i++) { + result[16 - i] = Character.forDigit((int) v & 0x0f, 16); + v >>= 4; + } + + return new String(result); + } + + /** + * Formats an int as a 4-byte signed hex value. + * + * @param v value to format + * @return non-null; formatted form + */ + public static String s4(int v) { + char[] result = new char[9]; + + if (v < 0) { + result[0] = '-'; + v = -v; + } else { + result[0] = '+'; + } + + for (int i = 0; i < 8; i++) { + result[8 - i] = Character.forDigit(v & 0x0f, 16); + v >>= 4; + } + + return new String(result); + } + + /** + * Formats an int as a 2-byte signed hex value. + * + * @param v value to format + * @return non-null; formatted form + */ + public static String s2(int v) { + char[] result = new char[5]; + + if (v < 0) { + result[0] = '-'; + v = -v; + } else { + result[0] = '+'; + } + + for (int i = 0; i < 4; i++) { + result[4 - i] = Character.forDigit(v & 0x0f, 16); + v >>= 4; + } + + return new String(result); + } + + /** + * Formats an int as a 1-byte signed hex value. + * + * @param v value to format + * @return non-null; formatted form + */ + public static String s1(int v) { + char[] result = new char[3]; + + if (v < 0) { + result[0] = '-'; + v = -v; + } else { + result[0] = '+'; + } + + for (int i = 0; i < 2; i++) { + result[2 - i] = Character.forDigit(v & 0x0f, 16); + v >>= 4; + } + + return new String(result); + } + + /** + * Formats a hex dump of a portion of a byte[]. The result + * is always newline-terminated, unless the passed-in length was zero, + * in which case the result is always the empty string (""). + * + * @param arr non-null; array to format + * @param offset >= 0; offset to the part to dump + * @param length >= 0; number of bytes to dump + * @param outOffset >= 0; first output offset to print + * @param bpl >= 0; number of bytes of output per line + * @param addressLength {2,4,6,8}; number of characters for each address + * header + * @return non-null; a string of the dump + */ + public static String dump(byte[] arr, int offset, int length, + int outOffset, int bpl, int addressLength) { + int end = offset + length; + + // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0) + if (((offset | length | end) < 0) || (end > arr.length)) { + throw new IndexOutOfBoundsException("arr.length " + + arr.length + "; " + + offset + "..!" + end); + } + + if (outOffset < 0) { + throw new IllegalArgumentException("outOffset < 0"); + } + + if (length == 0) { + return ""; + } + + StringBuffer sb = new StringBuffer(length * 4 + 6); + boolean bol = true; + int col = 0; + + while (length > 0) { + if (col == 0) { + String astr; + switch (addressLength) { + case 2: astr = Hex.u1(outOffset); break; + case 4: astr = Hex.u2(outOffset); break; + case 6: astr = Hex.u3(outOffset); break; + default: astr = Hex.u4(outOffset); break; + } + sb.append(astr); + sb.append(": "); + } else if ((col & 1) == 0) { + sb.append(' '); + } + sb.append(Hex.u1(arr[offset])); + outOffset++; + offset++; + col++; + if (col == bpl) { + sb.append('\n'); + col = 0; + } + length--; + } + + if (col != 0) { + sb.append('\n'); + } + + return sb.toString(); + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/IndentingWriter.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/IndentingWriter.java new file mode 100644 index 00000000..e8b85e09 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/IndentingWriter.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +import java.io.FilterWriter; +import java.io.IOException; +import java.io.Writer; + +/** + * Writer that wraps another writer and passes width-limited and + * optionally-prefixed output to its subordinate. When lines are + * wrapped they are automatically indented based on the start of the + * line. + */ +public final class IndentingWriter extends FilterWriter { + /** null-ok; optional prefix for every line */ + private final String prefix; + + /** > 0; the maximum output width */ + private final int width; + + /** > 0; the maximum indent */ + private final int maxIndent; + + /** >= 0; current output column (zero-based) */ + private int column; + + /** whether indent spaces are currently being collected */ + private boolean collectingIndent; + + /** >= 0; current indent amount */ + private int indent; + + /** + * Constructs an instance. + * + * @param out non-null; writer to send final output to + * @param width >= 0; the maximum output width (not including + * prefix), or 0 for no maximum + * @param prefix non-null; the prefix for each line + */ + public IndentingWriter(Writer out, int width, String prefix) { + super(out); + + if (out == null) { + throw new NullPointerException("out == null"); + } + + if (width < 0) { + throw new IllegalArgumentException("width < 0"); + } + + if (prefix == null) { + throw new NullPointerException("prefix == null"); + } + + this.width = (width != 0) ? width : Integer.MAX_VALUE; + this.maxIndent = width >> 1; + this.prefix = (prefix.length() == 0) ? null : prefix; + + bol(); + } + + /** + * Constructs a no-prefix instance. + * + * @param out non-null; writer to send final output to + * @param width >= 0; the maximum output width (not including + * prefix), or 0 for no maximum + */ + public IndentingWriter(Writer out, int width) { + this(out, width, ""); + } + + /** {@inheritDoc} */ + @Override + public void write(int c) throws IOException { + synchronized (lock) { + if (collectingIndent) { + if (c == ' ') { + indent++; + if (indent >= maxIndent) { + indent = maxIndent; + collectingIndent = false; + } + } else { + collectingIndent = false; + } + } + + if ((column == width) && (c != '\n')) { + out.write('\n'); + column = 0; + /* + * Note: No else, so this should fall through to the next + * if statement. + */ + } + + if (column == 0) { + if (prefix != null) { + out.write(prefix); + } + + if (!collectingIndent) { + for (int i = 0; i < indent; i++) { + out.write(' '); + } + column = indent; + } + } + + out.write(c); + + if (c == '\n') { + bol(); + } else { + column++; + } + } + } + + /** {@inheritDoc} */ + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + synchronized (lock) { + while (len > 0) { + write(cbuf[off]); + off++; + len--; + } + } + } + + /** {@inheritDoc} */ + @Override + public void write(String str, int off, int len) throws IOException { + synchronized (lock) { + while (len > 0) { + write(str.charAt(off)); + off++; + len--; + } + } + } + + /** + * Indicates that output is at the beginning of a line. + */ + private void bol() { + column = 0; + collectingIndent = (maxIndent != 0); + indent = 0; + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Input.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Input.java new file mode 100644 index 00000000..2364fabf --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Input.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +/** + * Interface for a source for binary input. This is similar to + * java.util.DataInput, but no IOExceptions + * are declared, and multibyte input is defined to be little-endian. + */ +public interface Input { + /** + * Gets the current cursor position. This is the same as the number of + * bytes read from this instance. + * + * @return >= 0; the cursor position + */ + public int getCursor(); + + /** + * Sets the current cursor position. + * + * @return >= 0; the cursor position + */ + public void setCursor(int cursor); + + /** + * Asserts that the cursor is the given value. + * + * @param expectedCursor the expected cursor value + * @throws RuntimeException thrown if getCursor() != + * expectedCursor + */ + public void assertCursor(int expectedCursor); + + /** + * Reads a byte from this instance. + * + * @return the byte value that was read + */ + public byte readByte(); + + /** + * Reads a short from this instance. + * + * @return the short value that was read, as an int + */ + public int readShort(); + + /** + * Reads an int from this instance. + * + * @return the unsigned int value that was read + */ + public int readInt(); + + /** + * Reads a long from this instance. + * + * @return the long value that was read + */ + public long readLong(); + + + /** + * Reads a DWARFv3-style signed LEB128 integer. For details, + * see the "Dalvik Executable Format" document or DWARF v3 section + * 7.6. + * + * @return the integer value that was read + */ + public int readSignedLeb128(); + + /** + * Reads a DWARFv3-style unsigned LEB128 integer. For details, + * see the "Dalvik Executable Format" document or DWARF v3 section + * 7.6. + * + * @return the integer value that was read + */ + public int readUnsignedLeb128(); + + + /** + * Reads a unsigned value as a DWARFv3-style LEB128 integer. It specifically + * checks for the case when the value was incorrectly formatted as a signed + * LEB128, and returns the appropriate unsigned value, but negated + * @return If the value was formatted as a ULEB128, it returns the actual unsigned + * value. Otherwise, if the value was formatted as a signed LEB128, it negates the + * "correct" unsigned value and returns that + */ + public int readUnsignedOrSignedLeb128(); + + /** + * reads a byte[] from this instance. + * + * @param bytes non-null; the buffer to read the data into + * @param offset >= 0; offset into bytes for the first + * byte to write + * @param length >= 0; number of bytes to read + */ + public void read(byte[] bytes, int offset, int length); + + /** + * reads a byte[] from this instance. This is just + * a convenient shorthand for read(bytes, 0, bytes.length). + * + * @param bytes non-null; the buffer to read the data into + */ + public void read(byte[] bytes); + + + /** + * reads a byte[] from this instance + * + * @param length >= 0; number of bytes to read + * @return a byte array containing length bytes + */ + public byte[] readBytes(int length); + + /** + * reads and decodes a null terminated utf8 string from the current cursor up to but not including + * the next null (0) byte. The terminating null byte is read and discarded, so that after the read, + * the cursor is positioned at the byte immediately after the terminating null + * + * @return a string representing the decoded value + */ + public String realNullTerminatedUtf8String(); + + /** + * Skips the given number of bytes. + * + * @param count >= 0; the number of bytes to skip + */ + public void skipBytes(int count); + + /** + * Skip extra bytes if necessary to force alignment of the output + * cursor as given. + * + * @param alignment > 0; the alignment; must be a power of two + */ + public void alignTo(int alignment); +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java new file mode 100644 index 00000000..a5aafe68 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +/** + * LEB128 (little-endian base 128) utilities. + */ +public final class Leb128Utils { + /** + * This class is uninstantiable. + */ + private Leb128Utils() { + // This space intentionally left blank. + } + + /** + * Gets the number of bytes in the unsigned LEB128 encoding of the + * given value. + * + * @param value the value in question + * @return its write size, in bytes + */ + public static int unsignedLeb128Size(int value) { + // TODO: This could be much cleverer. + + int remaining = value >>> 7; + int count = 0; + + while (remaining != 0) { + value = remaining; + remaining >>>= 7; + count++; + } + + return count + 1; + } + + /** + * Gets the number of bytes in the signed LEB128 encoding of the + * given value. + * + * @param value the value in question + * @return its write size, in bytes + */ + public static int signedLeb128Size(int value) { + // TODO: This could be much cleverer. + + int remaining = value >> 7; + int count = 0; + boolean hasMore = true; + int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; + + while (hasMore) { + hasMore = (remaining != end) + || ((remaining & 1) != ((value >> 6) & 1)); + + value = remaining; + remaining >>= 7; + count++; + } + + return count; + } + + /** + * Writes an unsigned leb128 to the buffer at the specified location + * @param value the value to write as an unsigned leb128 + * @param buffer the buffer to write to + * @param bufferIndex the index to start writing at + */ + public static void writeUnsignedLeb128(int value, byte[] buffer, int bufferIndex) { + int remaining = value >>> 7; + int count = 0; + + while (remaining != 0) { + buffer[bufferIndex] = (byte)((value & 0x7f) | 0x80); + bufferIndex++; + value = remaining; + remaining >>>= 7; + count++; + } + + buffer[bufferIndex] = (byte)(value & 0x7f); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java new file mode 100644 index 00000000..2e97f512 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java @@ -0,0 +1,200 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +public class NumberUtils { + + /** + * Decodes the high signed 4-bit nibble from the given byte + * @param b the byte to decode + * @return the decoded signed nibble + */ + public static byte decodeHighSignedNibble(byte b) { + return (byte)(b >> 4); + } + + /** + * Decodes the low signed 4-bit nibble from the given byte + * @param b the byte to decode + * @return the decoded signed nibble + */ + public static byte decodeLowSignedNibble(byte b) { + return (byte)(((byte)(b << 4)) >> 4); + } + + /** + * Decodes the high unsigned 4-bit nibble from the given byte + * @param b the byte to decode + * @return the decoded unsigned nibble + */ + public static byte decodeHighUnsignedNibble(byte b) { + return (byte)((b & 0xFF) >>> 4); + } + + /** + * Decodes the low unsigned 4-bit nibble from the given byte + * @param b the byte to decode + * @return the decoded unsigned nibble + */ + public static byte decodeLowUnsignedNibble(byte b) { + return (byte)(b & 0x0F); + } + + /** + * Decodes an unsigned byte from a signed byte + * @param b the signed byte to decode + * @return the decoded unsigned byte as a short + */ + public static short decodeUnsignedByte(byte b) { + return (short)(b & 0xFF); + } + + /** + * Decodes a signed short value from 2 individual bytes + * The parameters are in order from least significant byte to most significant byte + * @param lsb the least significant byte + * @param msb the most significant byte + * @return the decoded signed short value + */ + public static short decodeShort(byte lsb, byte msb) { + return (short) + ( (lsb & 0xFF) | + (msb << 8) + ); + } + + /** + * Decodes a signed short value in little endian format from the given byte array at the given index. + * @param bytes the byte array + * @param index the index of the first byte of the signed short value to decode + * @return the decoded signed short value + */ + public static short decodeShort(byte[] bytes, int index) { + return (short) + ( (bytes[index++] & 0xFF) | + (bytes[index] << 8) + ); + } + + /** + * Decodes an unsigned short value from 2 individual bytes + * The parameters are in order from least significant byte to most significant byte + * @param lsb the least significant byte + * @param msb the most significant byte + * @return the decoded unsigned short value as an int + */ + public static int decodeUnsignedShort(byte lsb, byte msb) { + return ( (lsb & 0xFF) | + ((msb & 0xFF) << 8) + ); + } + + /** + * Decodes an unsigned short value in little endian format from the given byte array at the given index. + * @param bytes the byte array + * @param index the index of the first byte of the unsigned short value to decode + * @return the decoded unsigned short value as an int + */ + public static int decodeUnsignedShort(byte[] bytes, int index) { + return ( (bytes[index++] & 0xFF) | + ((bytes[index] & 0xFF) << 8) + ); + } + + /** + * Decodes a signed integer value from 4 individual bytes + * The parameters are in order from least significant byte to most significant byte + * @param lsb the least significant byte + * @param mlsb the middle least significant byte + * @param mmsb the middle most significant byte + * @param msb the most significant byte + * @return the decoded signed integer value + */ + public static int decodeInt(byte lsb, byte mlsb, byte mmsb, byte msb) { + return (lsb & 0xFF) | + ((mlsb & 0xFF) << 8) | + ((mmsb & 0xFF) << 16) | + (msb << 24); + } + + /** + * Decodes a signed integer value in little endian format from the given byte array at the given index. + * @param bytes the byte array + * @param index the index of the first byte of the signed integer value to decode + * @return the decoded signed integer value + */ + public static int decodeInt(byte[] bytes, int index) { + return (bytes[index++] & 0xFF) | + ((bytes[index++] & 0xFF) << 8) | + ((bytes[index++] & 0xFF) << 16) | + (bytes[index] << 24); + } + + /** + * Decodes a signed long value from 8 individual bytes + * The parameters are in order from least significant byte to most significant byte + * @param llsb the lower least significant byte + * @param lmlsb the lower middle least significant byte + * @param lmmsb the lower middle most significant byte + * @param lgsb the lower greater significant byte + * @param glsb the greater least significant byte + * @param gmlsb the greater middle least significant byte + * @param gmmsb the greater middle most significant byte + * @param gmsb the greater most significant byte + * @return the decoded signed long value + */ + public static long decodeLong(byte llsb, byte lmlsb, byte lmmsb, byte lgsb, byte glsb, byte gmlsb, byte gmmsb, + byte gmsb) { + return (llsb & 0xFFL) | + ((lmlsb & 0xFFL) << 8) | + ((lmmsb & 0xFFL) << 16) | + ((lgsb & 0xFFL) << 24) | + ((glsb & 0xFFL) << 32) | + ((gmlsb & 0xFFL) << 40) | + ((gmmsb & 0xFFL) << 48) | + (((long)gmsb) << 56); + } + + /** + * Decodes a signed long value in little endian format from the given byte array at the given index. + * @param bytes the byte array + * @param index the index of the first byte of the signed long value to decode + * @return the decoded signed long value + */ + public static long decodeLong(byte[] bytes, int index) { + return (bytes[index++] & 0xFFL) | + ((bytes[index++] & 0xFFL) << 8) | + ((bytes[index++] & 0xFFL) << 16) | + ((bytes[index++] & 0xFFL) << 24) | + ((bytes[index++] & 0xFFL) << 32) | + ((bytes[index++] & 0xFFL) << 40) | + ((bytes[index++] & 0xFFL) << 48) | + (((long)bytes[index]) << 56); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Output.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Output.java new file mode 100644 index 00000000..49b41336 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Output.java @@ -0,0 +1,141 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +/** + * Interface for a sink for binary output. This is similar to + * java.util.DataOutput, but no IOExceptions + * are declared, and multibyte output is defined to be little-endian. + */ +public interface Output { + /** + * Gets the current cursor position. This is the same as the number of + * bytes written to this instance. + * + * @return >= 0; the cursor position + */ + public int getCursor(); + + /** + * Asserts that the cursor is the given value. + * + * @param expectedCursor the expected cursor value + * @throws RuntimeException thrown if getCursor() != + * expectedCursor + */ + public void assertCursor(int expectedCursor); + + /** + * Writes a byte to this instance. + * + * @param value the value to write; all but the low 8 bits are ignored + */ + public void writeByte(int value); + + /** + * Writes a short to this instance. + * + * @param value the value to write; all but the low 16 bits are ignored + */ + public void writeShort(int value); + + /** + * Writes an int to this instance. + * + * @param value the value to write + */ + public void writeInt(int value); + + /** + * Writes a long to this instance. + * + * @param value the value to write + */ + public void writeLong(long value); + + /** + * Writes a DWARFv3-style unsigned LEB128 integer. For details, + * see the "Dalvik Executable Format" document or DWARF v3 section + * 7.6. + * + * @param value value to write, treated as an unsigned value + * @return 1..5; the number of bytes actually written + */ + public int writeUnsignedLeb128(int value); + + /** + * Writes a DWARFv3-style unsigned LEB128 integer. For details, + * see the "Dalvik Executable Format" document or DWARF v3 section + * 7.6. + * + * @param value value to write + * @return 1..5; the number of bytes actually written + */ + public int writeSignedLeb128(int value); + + /** + * Writes a {@link org.jf.dexlib.Util.ByteArray} to this instance. + * + * @param bytes non-null; the array to write + */ + public void write(ByteArray bytes); + + /** + * Writes a portion of a byte[] to this instance. + * + * @param bytes non-null; the array to write + * @param offset >= 0; offset into bytes for the first + * byte to write + * @param length >= 0; number of bytes to write + */ + public void write(byte[] bytes, int offset, int length); + + /** + * Writes a byte[] to this instance. This is just + * a convenient shorthand for write(bytes, 0, bytes.length). + * + * @param bytes non-null; the array to write + */ + public void write(byte[] bytes); + + /** + * Writes the given number of 0 bytes. + * + * @param count >= 0; the number of zeroes to write + */ + public void writeZeroes(int count); + + /** + * Adds extra bytes if necessary (with value 0) to + * force alignment of the output cursor as given. + * + * @param alignment > 0; the alignment; must be a power of two + */ + public void alignTo(int alignment); +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Pair.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Pair.java new file mode 100644 index 00000000..f246c999 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Pair.java @@ -0,0 +1,40 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +public class Pair { + public final A first; + public final B second; + + public Pair(A first, B second) { + this.first = first; + this.second = second; + } +} + diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ReadOnlyArrayList.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ReadOnlyArrayList.java new file mode 100644 index 00000000..2667979d --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ReadOnlyArrayList.java @@ -0,0 +1,52 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +import java.util.AbstractList; +import java.util.RandomAccess; + +public class ReadOnlyArrayList extends AbstractList implements RandomAccess { + private final T[] arr; + + public ReadOnlyArrayList(T[] arr) { + this.arr = arr; + } + + public int size() { + return arr.length; + } + + public T get(int i) { + return arr[i]; + } + + public static ReadOnlyArrayList of(T... items) { + return new ReadOnlyArrayList(items); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/SparseArray.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/SparseArray.java new file mode 100644 index 00000000..25fb7b46 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/SparseArray.java @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * SparseArrays map integers to Objects. Unlike a normal array of Objects, + * there can be gaps in the indices. It is intended to be more efficient + * than using a HashMap to map Integers to Objects. + */ +public class SparseArray { + private static final Object DELETED = new Object(); + private boolean mGarbage = false; + + /** + * Creates a new SparseArray containing no mappings. + */ + public SparseArray() { + this(10); + } + + /** + * Creates a new SparseArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public SparseArray(int initialCapacity) { + mKeys = new int[initialCapacity]; + mValues = new Object[initialCapacity]; + mSize = 0; + } + + /** + * Gets the Object mapped from the specified key, or null + * if no such mapping has been made. + */ + public E get(int key) { + return get(key, null); + } + + /** + * Gets the Object mapped from the specified key, or the specified Object + * if no such mapping has been made. + */ + public E get(int key, E valueIfKeyNotFound) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0 || mValues[i] == DELETED) { + return valueIfKeyNotFound; + } else { + return (E) mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(int key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + if (mValues[i] != DELETED) { + mValues[i] = DELETED; + mGarbage = true; + } + } + } + + /** + * Alias for {@link #delete(int)}. + */ + public void remove(int key) { + delete(key); + } + + private void gc() { + // Log.e("SparseArray", "gc start with " + mSize); + + int n = mSize; + int o = 0; + int[] keys = mKeys; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + Object val = values[i]; + + if (val != DELETED) { + if (i != o) { + keys[o] = keys[i]; + values[o] = val; + } + + o++; + } + } + + mGarbage = false; + mSize = o; + + // Log.e("SparseArray", "gc end with " + mSize); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, E value) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (i < mSize && mValues[i] == DELETED) { + mKeys[i] = key; + mValues[i] = value; + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + + // Search again because indices may have changed. + i = ~binarySearch(mKeys, 0, mSize, key); + } + + if (mSize >= mKeys.length) { + int n = Math.max(mSize + 1, mKeys.length * 2); + + int[] nkeys = new int[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseArray + * currently stores. + */ + public int size() { + if (mGarbage) { + gc(); + } + + return mSize; + } + + /** + * Given an index in the range 0...size()-1, returns + * the key from the indexth key-value mapping that this + * SparseArray stores. + */ + public int keyAt(int index) { + if (mGarbage) { + gc(); + } + + return mKeys[index]; + } + + /** + * Given an index in the range 0...size()-1, returns + * the value from the indexth key-value mapping that this + * SparseArray stores. + */ + public E valueAt(int index) { + if (mGarbage) { + gc(); + } + + return (E) mValues[index]; + } + + /** + * Given an index in the range 0...size()-1, sets a new + * value for the indexth key-value mapping that this + * SparseArray stores. + */ + public void setValueAt(int index, E value) { + if (mGarbage) { + gc(); + } + + mValues[index] = value; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(int key) { + if (mGarbage) { + gc(); + } + + return binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(E value) { + if (mGarbage) { + gc(); + } + + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseArray. + */ + public void clear() { + int n = mSize; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + values[i] = null; + } + + mSize = 0; + mGarbage = false; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(int key, E value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = Math.max(pos + 1, mKeys.length * 2); + + int[] nkeys = new int[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + /** + * Increases the size of the underlying storage if needed, to ensure that it can + * hold the specified number of items without having to allocate additional memory + * @param capacity the number of items + */ + public void ensureCapacity(int capacity) { + if (mGarbage && mSize >= mKeys.length) { + gc(); + } + + if (mKeys.length < capacity) { + int[] nkeys = new int[capacity]; + Object[] nvalues = new Object[capacity]; + + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + } + + private static int binarySearch(int[] a, int start, int len, int key) { + int high = start + len, low = start - 1, guess; + + while (high - low > 1) { + guess = (high + low) / 2; + + if (a[guess] < key) + low = guess; + else + high = guess; + } + + if (high == start + len) + return ~(start + len); + else if (a[high] == key) + return high; + else + return ~high; + } + + /** + * @return a read-only list of the values in this SparseArray which are in ascending order, based on their + * associated key + */ + public List getValues() { + return Collections.unmodifiableList(Arrays.asList((E[])mValues)); + } + + private int[] mKeys; + private Object[] mValues; + private int mSize; +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java new file mode 100644 index 00000000..4e687f19 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +/** + * SparseIntArrays map integers to integers. Unlike a normal array of integers, + * there can be gaps in the indices. It is intended to be more efficient + * than using a HashMap to map Integers to Integers. + */ +public class SparseIntArray { + /** + * Creates a new SparseIntArray containing no mappings. + */ + public SparseIntArray() { + this(10); + } + + /** + * Creates a new SparseIntArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public SparseIntArray(int initialCapacity) { + mKeys = new int[initialCapacity]; + mValues = new int[initialCapacity]; + mSize = 0; + } + + /** + * Gets the int mapped from the specified key, or 0 + * if no such mapping has been made. + */ + public int get(int key) { + return get(key, 0); + } + + /** + * Gets the int mapped from the specified key, or the specified value + * if no such mapping has been made. + */ + public int get(int key, int valueIfKeyNotFound) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0) { + return valueIfKeyNotFound; + } else { + return mValues[i]; + } + } + + /** + * Gets the int mapped from the specified key, or if not present, the + * closest key that is less than the specified key. + */ + public int getClosestSmaller(int key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0) { + i = ~i; + if (i > 0) { + i--; + } + return mValues[i]; + } else { + return mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(int key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + removeAt(i); + } + } + + /** + * Removes the mapping at the given index. + */ + public void removeAt(int index) { + System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1)); + System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1)); + mSize--; + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(int key, int value) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (mSize >= mKeys.length) { + int n = Math.max(mSize + 1, mKeys.length * 2); + + int[] nkeys = new int[n]; + int[] nvalues = new int[n]; + + // Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseIntArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseIntArray + * currently stores. + */ + public int size() { + return mSize; + } + + /** + * Given an index in the range 0...size()-1, returns + * the key from the indexth key-value mapping that this + * SparseIntArray stores. + */ + public int keyAt(int index) { + return mKeys[index]; + } + + /** + * Given an index in the range 0...size()-1, returns + * the value from the indexth key-value mapping that this + * SparseIntArray stores. + */ + public int valueAt(int index) { + return mValues[index]; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(int key) { + return binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(int value) { + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseIntArray. + */ + public void clear() { + mSize = 0; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(int key, int value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = Math.max(pos + 1, mKeys.length * 2); + + int[] nkeys = new int[n]; + int[] nvalues = new int[n]; + + // Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + private static int binarySearch(int[] a, int start, int len, int key) { + int high = start + len, low = start - 1, guess; + + while (high - low > 1) { + guess = (high + low) / 2; + + if (a[guess] < key) + low = guess; + else + high = guess; + } + + if (high == start + len) + return ~(start + len); + else if (a[high] == key) + return high; + else + return ~high; + } + + private int[] mKeys; + private int[] mValues; + private int mSize; +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TryListBuilder.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TryListBuilder.java new file mode 100644 index 00000000..aadcaa7a --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TryListBuilder.java @@ -0,0 +1,347 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +import org.jf.dexlib.CodeItem; +import org.jf.dexlib.TypeIdItem; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +public class TryListBuilder +{ + /*TODO: add logic to merge adjacent, identical try blocks, and remove superflous handlers + Also provide a "strict" mode, where the above isn't performed, which will be useful to be able to + exactly reproduce the original .dex file (for testing/verification purposes)*/ + + + private TryRange firstTryRange = new TryRange(0,0); + private TryRange lastTryRange = new TryRange(0,0); + + public TryListBuilder() { + firstTryRange.next = lastTryRange; + lastTryRange.previous = firstTryRange; + } + + private class TryRange + { + public TryRange previous = null; + public TryRange next = null; + + public int startAddress; + public int endAddress; + public LinkedList handlers; + + public int catchAllHandlerAddress; + + public TryRange(int startAddress, int endAddress) { + this.startAddress = startAddress; + this.endAddress = endAddress; + this.handlers = new LinkedList(); + this.previous = null; + this.next = null; + catchAllHandlerAddress = -1; + } + + public void append(TryRange tryRange) { + /*we use a dummy last item, so this.next will always + have a value*/ + this.next.previous = tryRange; + tryRange.next = this.next; + + this.next = tryRange; + tryRange.previous = this; + } + + public void prepend(TryRange tryRange){ + /*we use a dummy first item, so this.previous will always + have a value*/ + this.previous.next = tryRange; + tryRange.previous = this.previous; + + this.previous = tryRange; + tryRange.next = this; + } + + /** + * This splits the current range into two ranges at the given + * address. The existing range will be shortened to the first + * half, and a new range will be created and returned for the + * 2nd half. + * @param address The address to split at + * @return The 2nd half of the + */ + public TryRange split(int address) { + //this is a private class, so address is assumed + //to be valid + + TryRange tryRange = new TryRange(address, endAddress); + tryRange.catchAllHandlerAddress = this.catchAllHandlerAddress; + tryRange.handlers.addAll(this.handlers); + append(tryRange); + + this.endAddress = address; + + return tryRange; + } + + public void appendHandler(Handler handler) { + handlers.addLast(handler); + } + + public void prependHandler(Handler handler) { + handlers.addFirst(handler); + } + } + + private class Handler + { + public final TypeIdItem type; + public final int handlerAddress; + + public Handler(TypeIdItem type, int handlerAddress) { + this.type = type; + this.handlerAddress = handlerAddress; + } + } + + public Pair, List> encodeTries() { + if (firstTryRange.next == lastTryRange) { + return new Pair, List>(null, null); + } + + ArrayList tries = new ArrayList(); + ArrayList handlers = new ArrayList(); + + HashMap handlerDict = + new HashMap(); + + TryRange tryRange = firstTryRange.next; + + while (tryRange != lastTryRange) { + CodeItem.EncodedTypeAddrPair[] encodedTypeAddrPairs = + new CodeItem.EncodedTypeAddrPair[tryRange.handlers.size()]; + + int index = 0; + for (Handler handler: tryRange.handlers) { + CodeItem.EncodedTypeAddrPair encodedTypeAddrPair = new CodeItem.EncodedTypeAddrPair( + handler.type, + handler.handlerAddress); + encodedTypeAddrPairs[index++] = encodedTypeAddrPair; + } + + CodeItem.EncodedCatchHandler encodedCatchHandler = new CodeItem.EncodedCatchHandler( + encodedTypeAddrPairs, + tryRange.catchAllHandlerAddress); + CodeItem.EncodedCatchHandler internedEncodedCatchHandler = handlerDict.get(encodedCatchHandler); + if (internedEncodedCatchHandler == null) { + handlerDict.put(encodedCatchHandler, encodedCatchHandler); + handlers.add(encodedCatchHandler); + } else { + encodedCatchHandler = internedEncodedCatchHandler; + } + + CodeItem.TryItem tryItem = new CodeItem.TryItem( + tryRange.startAddress, + tryRange.endAddress - tryRange.startAddress, + encodedCatchHandler); + tries.add(tryItem); + + tryRange = tryRange.next; + } + + return new Pair, List>(tries, handlers); + } + + public void addCatchAllHandler(int startAddress, int endAddress, int handlerAddress) { + TryRange startRange; + TryRange endRange; + + Pair ranges = getBoundingRanges(startAddress, endAddress); + startRange = ranges.first; + endRange = ranges.second; + + int previousEnd = startAddress; + TryRange tryRange = startRange; + + /*Now we have the start and end ranges that exactly match the start and end + of the range being added. We need to iterate over all the ranges from the start + to end range inclusively, and append the handler to the end of each range's handler + list. We also need to create a new range for any "holes" in the existing ranges*/ + do + { + //is there a hole? If so, add a new range to fill the hole + if (tryRange.startAddress > previousEnd) { + TryRange newRange = new TryRange(previousEnd, tryRange.startAddress); + tryRange.prepend(newRange); + tryRange = newRange; + } + + if (tryRange.catchAllHandlerAddress == -1) { + tryRange.catchAllHandlerAddress = handlerAddress; + } + + previousEnd = tryRange.endAddress; + tryRange = tryRange.next; + } while (tryRange.previous != endRange); + } + + public Pair getBoundingRanges(int startAddress, int endAddress) { + TryRange startRange = null; + TryRange endRange = null; + + TryRange tryRange = firstTryRange.next; + while (tryRange != lastTryRange) { + if (startAddress == tryRange.startAddress) { + //|-----| + //^------ + /*Bam. We hit the start of the range right on the head*/ + startRange = tryRange; + break; + } else if (startAddress > tryRange.startAddress && startAddress < tryRange.endAddress) { + //|-----| + // ^---- + /*Almost. The start of the range being added is in the middle + of an existing try range. We need to split the existing range + at the start address of the range being added*/ + startRange = tryRange.split(startAddress); + break; + }else if (startAddress < tryRange.startAddress) { + if (endAddress <= tryRange.startAddress) { + // |-----| + //^--^ + /*Oops, totally too far! The new range doesn't overlap any existing + ones, so we just add it and return*/ + startRange = new TryRange(startAddress, endAddress); + tryRange.prepend(startRange); + return new Pair(startRange, startRange); + } else { + // |-----| + //^--------- + /*Oops, too far! We've passed the start of the range being added, but + the new range does overlap this one. We need to add a new range just + before this one*/ + startRange = new TryRange(startAddress, tryRange.startAddress); + tryRange.prepend(startRange); + break; + } + } + + tryRange = tryRange.next; + } + + //|-----| + // ^----- + /*Either the list of tries is blank, or all the tries in the list + end before the range being added starts. In either case, we just need + to add a new range at the end of the list*/ + if (startRange == null) { + startRange = new TryRange(startAddress, endAddress); + lastTryRange.prepend(startRange); + return new Pair(startRange, startRange); + } + + tryRange = startRange; + while (tryRange != lastTryRange) { + if (tryRange.endAddress == endAddress) { + //|-----| + //------^ + /*Bam! We hit the end right on the head.*/ + endRange = tryRange; + break; + } else if (tryRange.startAddress < endAddress && tryRange.endAddress > endAddress) { + //|-----| + //--^ + /*Almost. The range being added ends in the middle of an + existing range. We need to split the existing range + at the end of the range being added.*/ + tryRange.split(endAddress); + endRange = tryRange; + break; + } else if (tryRange.startAddress >= endAddress) { + //|-----| |-----| + //-----------^ + /*Oops, too far! The current range starts after the range being added + ends. We need to create a new range that starts at the end of the + previous range, and ends at the end of the range being added*/ + endRange = new TryRange(tryRange.previous.endAddress, endAddress); + tryRange.prepend(endRange); + break; + } + tryRange = tryRange.next; + } + + //|-----| + //--------^ + /*The last range in the list ended before the end of the range being added. + We need to add a new range that starts at the end of the last range in the + list, and ends at the end of the range being added.*/ + if (endRange == null) { + endRange = new TryRange(lastTryRange.previous.endAddress, endAddress); + lastTryRange.prepend(endRange); + } + + return new Pair(startRange, endRange); + } + + public void addHandler(TypeIdItem type, int startAddress, int endAddress, int handlerAddress) { + TryRange startRange; + TryRange endRange; + + //TODO: need to check for pre-existing exception types in the handler list? + + Pair ranges = getBoundingRanges(startAddress, endAddress); + startRange = ranges.first; + endRange = ranges.second; + Handler handler = new Handler(type, handlerAddress); + + int previousEnd = startAddress; + TryRange tryRange = startRange; + + /*Now we have the start and end ranges that exactly match the start and end + of the range being added. We need to iterate over all the ranges from the start + to end range inclusively, and append the handler to the end of each range's handler + list. We also need to create a new range for any "holes" in the existing ranges*/ + do + { + //is there a hole? If so, add a new range to fill the hole + if (tryRange.startAddress > previousEnd) { + TryRange newRange = new TryRange(previousEnd, tryRange.startAddress); + tryRange.prepend(newRange); + tryRange = newRange; + } + + tryRange.appendHandler(handler); + previousEnd = tryRange.endAddress; + tryRange = tryRange.next; + } while (tryRange.previous != endRange); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java new file mode 100644 index 00000000..d064a4da --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +import java.io.*; + +/** + * Class that takes a combined output destination and provides two + * output writers, one of which ends up writing to the left column and + * one which goes on the right. + */ +public final class TwoColumnOutput { + /** non-null; underlying writer for final output */ + private final Writer out; + + /** > 0; the left column width */ + private final int leftWidth; + + /** non-null; pending left column output */ + private final StringBuffer leftBuf; + + /** non-null; pending right column output */ + private final StringBuffer rightBuf; + + /** non-null; left column writer */ + private final IndentingWriter leftColumn; + + /** non-null; right column writer */ + private final IndentingWriter rightColumn; + + /** + * Turns the given two strings (with widths) and spacer into a formatted + * two-column string. + * + * @param s1 non-null; first string + * @param width1 > 0; width of the first column + * @param spacer non-null; spacer string + * @param s2 non-null; second string + * @param width2 > 0; width of the second column + * @return non-null; an appropriately-formatted string + */ + public static String toString(String s1, int width1, String spacer, + String s2, int width2) { + int len1 = s1.length(); + int len2 = s2.length(); + + StringWriter sw = new StringWriter((len1 + len2) * 3); + TwoColumnOutput twoOut = + new TwoColumnOutput(sw, width1, width2, spacer); + + try { + twoOut.getLeft().write(s1); + twoOut.getRight().write(s2); + } catch (IOException ex) { + throw new RuntimeException("shouldn't happen", ex); + } + + twoOut.flush(); + return sw.toString(); + } + + /** + * Constructs an instance. + * + * @param out non-null; writer to send final output to + * @param leftWidth > 0; width of the left column, in characters + * @param rightWidth > 0; width of the right column, in characters + * @param spacer non-null; spacer string to sit between the two columns + */ + public TwoColumnOutput(Writer out, int leftWidth, int rightWidth, + String spacer) { + if (out == null) { + throw new NullPointerException("out == null"); + } + + if (leftWidth < 1) { + throw new IllegalArgumentException("leftWidth < 1"); + } + + if (rightWidth < 1) { + throw new IllegalArgumentException("rightWidth < 1"); + } + + if (spacer == null) { + throw new NullPointerException("spacer == null"); + } + + StringWriter leftWriter = new StringWriter(1000); + StringWriter rightWriter = new StringWriter(1000); + + this.out = out; + this.leftWidth = leftWidth; + this.leftBuf = leftWriter.getBuffer(); + this.rightBuf = rightWriter.getBuffer(); + this.leftColumn = new IndentingWriter(leftWriter, leftWidth); + this.rightColumn = + new IndentingWriter(rightWriter, rightWidth, spacer); + } + + /** + * Constructs an instance. + * + * @param out non-null; stream to send final output to + * @param leftWidth >= 1; width of the left column, in characters + * @param rightWidth >= 1; width of the right column, in characters + * @param spacer non-null; spacer string to sit between the two columns + */ + public TwoColumnOutput(OutputStream out, int leftWidth, int rightWidth, + String spacer) { + this(new OutputStreamWriter(out), leftWidth, rightWidth, spacer); + } + + /** + * Gets the writer to use to write to the left column. + * + * @return non-null; the left column writer + */ + public Writer getLeft() { + return leftColumn; + } + + /** + * Gets the writer to use to write to the right column. + * + * @return non-null; the right column writer + */ + public Writer getRight() { + return rightColumn; + } + + /** + * Flushes the output. If there are more lines of pending output in one + * column, then the other column will get filled with blank lines. + */ + public void flush() { + try { + appendNewlineIfNecessary(leftBuf, leftColumn); + appendNewlineIfNecessary(rightBuf, rightColumn); + outputFullLines(); + flushLeft(); + flushRight(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Outputs to the final destination as many full line pairs as + * there are in the pending output, removing those lines from + * their respective buffers. This method terminates when at + * least one of the two column buffers is empty. + */ + private void outputFullLines() throws IOException { + for (;;) { + int leftLen = leftBuf.indexOf("\n"); + if (leftLen < 0) { + return; + } + + int rightLen = rightBuf.indexOf("\n"); + if (rightLen < 0) { + return; + } + + if (leftLen != 0) { + out.write(leftBuf.substring(0, leftLen)); + } + + if (rightLen != 0) { + writeSpaces(out, leftWidth - leftLen); + out.write(rightBuf.substring(0, rightLen)); + } + + out.write('\n'); + + leftBuf.delete(0, leftLen + 1); + rightBuf.delete(0, rightLen + 1); + } + } + + /** + * Flushes the left column buffer, printing it and clearing the buffer. + * If the buffer is already empty, this does nothing. + */ + private void flushLeft() throws IOException { + appendNewlineIfNecessary(leftBuf, leftColumn); + + while (leftBuf.length() != 0) { + rightColumn.write('\n'); + outputFullLines(); + } + } + + /** + * Flushes the right column buffer, printing it and clearing the buffer. + * If the buffer is already empty, this does nothing. + */ + private void flushRight() throws IOException { + appendNewlineIfNecessary(rightBuf, rightColumn); + + while (rightBuf.length() != 0) { + leftColumn.write('\n'); + outputFullLines(); + } + } + + /** + * Appends a newline to the given buffer via the given writer, but + * only if it isn't empty and doesn't already end with one. + * + * @param buf non-null; the buffer in question + * @param out non-null; the writer to use + */ + private static void appendNewlineIfNecessary(StringBuffer buf, + Writer out) + throws IOException { + int len = buf.length(); + + if ((len != 0) && (buf.charAt(len - 1) != '\n')) { + out.write('\n'); + } + } + + /** + * Writes the given number of spaces to the given writer. + * + * @param out non-null; where to write + * @param amt >= 0; the number of spaces to write + */ + private static void writeSpaces(Writer out, int amt) throws IOException { + while (amt > 0) { + out.write(' '); + amt--; + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TypeUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TypeUtils.java new file mode 100644 index 00000000..40cedda0 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TypeUtils.java @@ -0,0 +1,64 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.Util; + +import org.jf.dexlib.EncodedValue.*; +import org.jf.dexlib.TypeIdItem; + +public class TypeUtils +{ + public static EncodedValue makeDefaultValueForType(String type) { + switch (type.charAt(0)) { + case 'Z': + return BooleanEncodedValue.FalseValue; + case 'B': + return new ByteEncodedValue((byte)0); + case 'S': + return new ShortEncodedValue((short)0); + case 'C': + return new CharEncodedValue((char)0); + case 'I': + return new IntEncodedValue(0); + case 'J': + return new LongEncodedValue(0); + case 'F': + return new FloatEncodedValue(0); + case 'D': + return new DoubleEncodedValue(0); + case 'L': + case '[': + return NullEncodedValue.NullValue; + } + return null; + } + + public static EncodedValue makeDefaultValueForType(TypeIdItem type) { + return makeDefaultValueForType(type.getTypeDescriptor()); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Utf8Utils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Utf8Utils.java new file mode 100644 index 00000000..0011bc50 --- /dev/null +++ b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Utf8Utils.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * As per the Apache license requirements, this file has been modified + * from its original state. + * + * Such modifications are Copyright (C) 2010 Ben Gruver, and are released + * under the original license + */ + +package org.jf.dexlib.Util; + +import java.io.IOException; +import java.io.Writer; + +/** + * Constants of type CONSTANT_Utf8_info. + */ +public final class Utf8Utils { + + + /** + * Converts a string into its Java-style UTF-8 form. Java-style UTF-8 + * differs from normal UTF-8 in the handling of character '\0' and + * surrogate pairs. + * + * @param string non-null; the string to convert + * @return non-null; the UTF-8 bytes for it + */ + public static byte[] stringToUtf8Bytes(String string) { + int len = string.length(); + byte[] bytes = new byte[len * 3]; // Avoid having to reallocate. + int outAt = 0; + + for (int i = 0; i < len; i++) { + char c = string.charAt(i); + if ((c != 0) && (c < 0x80)) { + bytes[outAt] = (byte) c; + outAt++; + } else if (c < 0x800) { + bytes[outAt] = (byte) (((c >> 6) & 0x1f) | 0xc0); + bytes[outAt + 1] = (byte) ((c & 0x3f) | 0x80); + outAt += 2; + } else { + bytes[outAt] = (byte) (((c >> 12) & 0x0f) | 0xe0); + bytes[outAt + 1] = (byte) (((c >> 6) & 0x3f) | 0x80); + bytes[outAt + 2] = (byte) ((c & 0x3f) | 0x80); + outAt += 3; + } + } + + byte[] result = new byte[outAt]; + System.arraycopy(bytes, 0, result, 0, outAt); + 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 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; + int outAt = 0; + + for (int at = start; length > 0; /*at*/) { + int v0 = bytes[at] & 0xFF; + char out; + switch (v0 >> 4) { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: { + // 0XXXXXXX -- single-byte encoding + length--; + if (v0 == 0) { + // A single zero byte is illegal. + return throwBadUtf8(v0, at); + } + out = (char) v0; + at++; + break; + } + case 0x0c: case 0x0d: { + // 110XXXXX -- two-byte encoding + length -= 2; + if (length < 0) { + return throwBadUtf8(v0, at); + } + int v1 = bytes[at + 1] & 0xFF; + if ((v1 & 0xc0) != 0x80) { + return throwBadUtf8(v1, at + 1); + } + int value = ((v0 & 0x1f) << 6) | (v1 & 0x3f); + if ((value != 0) && (value < 0x80)) { + /* + * This should have been represented with + * one-byte encoding. + */ + return throwBadUtf8(v1, at + 1); + } + out = (char) value; + at += 2; + break; + } + case 0x0e: { + // 1110XXXX -- three-byte encoding + length -= 3; + if (length < 0) { + return throwBadUtf8(v0, at); + } + int v1 = bytes[at + 1] & 0xFF; + if ((v1 & 0xc0) != 0x80) { + return throwBadUtf8(v1, at + 1); + } + int v2 = bytes[at + 2] & 0xFF; + if ((v2 & 0xc0) != 0x80) { + return throwBadUtf8(v2, at + 2); + } + int value = ((v0 & 0x0f) << 12) | ((v1 & 0x3f) << 6) | + (v2 & 0x3f); + if (value < 0x800) { + /* + * This should have been represented with one- or + * two-byte encoding. + */ + return throwBadUtf8(v2, at + 2); + } + out = (char) value; + at += 3; + break; + } + default: { + // 10XXXXXX, 1111XXXX -- illegal + return throwBadUtf8(v0, at); + } + } + chars[outAt] = out; + outAt++; + } + + return new String(chars, 0, outAt); + } + + /** + * Helper for {@link #utf8BytesToString}, which throws the right + * exception for a bogus utf-8 byte. + * + * @param value the byte value + * @param offset the file offset + * @return never + * @throws IllegalArgumentException always thrown + */ + private static String throwBadUtf8(int value, int offset) { + throw new IllegalArgumentException("bad utf-8 byte " + Hex.u1(value) + + " at offset " + Hex.u4(offset)); + } + + public static void writeEscapedChar(Writer writer, char c) throws IOException { + if ((c >= ' ') && (c < 0x7f)) { + if ((c == '\'') || (c == '\"') || (c == '\\')) { + writer.write('\\'); + } + writer.write(c); + return; + } else if (c <= 0x7f) { + switch (c) { + case '\n': writer.write("\\n"); return; + case '\r': writer.write("\\r"); return; + case '\t': writer.write("\\t"); return; + } + } + + writer.write("\\u"); + writer.write(Character.forDigit(c >> 12, 16)); + writer.write(Character.forDigit((c >> 8) & 0x0f, 16)); + writer.write(Character.forDigit((c >> 4) & 0x0f, 16)); + writer.write(Character.forDigit(c & 0x0f, 16)); + + } + + public static void writeEscapedString(Writer writer, String value) throws IOException { + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + + if ((c >= ' ') && (c < 0x7f)) { + if ((c == '\'') || (c == '\"') || (c == '\\')) { + writer.write('\\'); + } + writer.write(c); + continue; + } else if (c <= 0x7f) { + switch (c) { + case '\n': writer.write("\\n"); continue; + case '\r': writer.write("\\r"); continue; + case '\t': writer.write("\\t"); continue; + } + } + + writer.write("\\u"); + writer.write(Character.forDigit(c >> 12, 16)); + writer.write(Character.forDigit((c >> 8) & 0x0f, 16)); + writer.write(Character.forDigit((c >> 4) & 0x0f, 16)); + writer.write(Character.forDigit(c & 0x0f, 16)); + } + } + + public static String escapeString(String value) { + int len = value.length(); + StringBuilder sb = new StringBuilder(len * 3 / 2); + + for (int i = 0; i < len; i++) { + char c = value.charAt(i); + + if ((c >= ' ') && (c < 0x7f)) { + if ((c == '\'') || (c == '\"') || (c == '\\')) { + sb.append('\\'); + } + sb.append(c); + continue; + } else if (c <= 0x7f) { + switch (c) { + case '\n': sb.append("\\n"); continue; + case '\r': sb.append("\\r"); continue; + case '\t': sb.append("\\t"); continue; + } + } + + sb.append("\\u"); + sb.append(Character.forDigit(c >> 12, 16)); + sb.append(Character.forDigit((c >> 8) & 0x0f, 16)); + sb.append(Character.forDigit((c >> 4) & 0x0f, 16)); + sb.append(Character.forDigit(c & 0x0f, 16)); + } + + return sb.toString(); + } +} diff --git a/brut.apktool.smali/examples/AnnotationTypes/ClassAnnotation.smali b/brut.apktool.smali/examples/AnnotationTypes/ClassAnnotation.smali new file mode 100644 index 00000000..941e7829 --- /dev/null +++ b/brut.apktool.smali/examples/AnnotationTypes/ClassAnnotation.smali @@ -0,0 +1,4 @@ +.class public abstract interface annotation LClassAnnotation; +.super Ljava/lang/Object; +.implements Ljava/lang/annotation/Annotation; + diff --git a/brut.apktool.smali/examples/AnnotationTypes/FieldAnnotation.smali b/brut.apktool.smali/examples/AnnotationTypes/FieldAnnotation.smali new file mode 100644 index 00000000..17846b96 --- /dev/null +++ b/brut.apktool.smali/examples/AnnotationTypes/FieldAnnotation.smali @@ -0,0 +1,4 @@ +.class public abstract interface annotation LFieldAnnotation; +.super Ljava/lang/Object; +.implements Ljava/lang/annotation/Annotation; + diff --git a/brut.apktool.smali/examples/AnnotationTypes/Main.smali b/brut.apktool.smali/examples/AnnotationTypes/Main.smali new file mode 100644 index 00000000..293edc4d --- /dev/null +++ b/brut.apktool.smali/examples/AnnotationTypes/Main.smali @@ -0,0 +1,138 @@ +.class public LMain; +.super Ljava/lang/Object; + + +#expected output: +#@ClassAnnotation() +#@MethodAnnotation() +#@FieldAnnotation() +#@ParameterAnnotation() + + +.method public static main([Ljava/lang/String;)V + .registers 1 + + invoke-static {}, LMain;->testClassAnnotation()V + + invoke-static {}, LMain;->testMethodAnnotation()V + + invoke-static {}, LMain;->testFieldAnnotation()V + + const-string v0, "" + + invoke-static {v0}, LMain;->testParameterAnnotation(Ljava/lang/String;)V + + return-void +.end method + +.annotation runtime LClassAnnotation; +.end annotation + +.method public static testClassAnnotation()V + .registers 3 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-class v1, LMain; + const-class v2, LClassAnnotation; + + invoke-virtual {v1, v2}, Ljava/lang/Class;->getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; + move-result-object v1 + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + return-void +.end method + + + +.method public static testMethodAnnotation()V + .registers 4 + + .annotation runtime LMethodAnnotation; + .end annotation + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-class v1, LMain; + const-string v2, "testMethodAnnotation" + + const/4 v3, 0 + new-array v3, v3, [Ljava/lang/Class; + + invoke-virtual {v1, v2, v3}, Ljava/lang/Class;->getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; + move-result-object v1 + + const-class v2, LMethodAnnotation; + + invoke-virtual {v1, v2}, Ljava/lang/reflect/Method;->getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; + move-result-object v1 + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + return-void +.end method + + +.field public static fieldAnnotationTest:Ljava/lang/Object; + .annotation runtime LFieldAnnotation; + .end annotation +.end field + +.method public static testFieldAnnotation()V + .registers 3 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-class v1, LMain; + const-string v2, "fieldAnnotationTest" + + invoke-virtual {v1, v2}, Ljava/lang/Class;->getField(Ljava/lang/String;)Ljava/lang/reflect/Field; + move-result-object v1 + + const-class v2, LFieldAnnotation; + + invoke-virtual {v1, v2}, Ljava/lang/reflect/Field;->getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; + move-result-object v1 + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + return-void +.end method + + +.method public static testParameterAnnotation(Ljava/lang/String;)V + .registers 6 + + .parameter + .annotation runtime LParameterAnnotation; + .end annotation + .end parameter + + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-class v1, LMain; + const-string v2, "testParameterAnnotation" + + const/4 v3, 1 + new-array v3, v3, [Ljava/lang/Class; + + const-class v4, Ljava/lang/String; + const/4 v5, 0 + aput-object v4, v3, v5 + + invoke-virtual {v1, v2, v3}, Ljava/lang/Class;->getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; + move-result-object v1 + + + invoke-virtual {v1}, Ljava/lang/reflect/Method;->getParameterAnnotations()[[Ljava/lang/annotation/Annotation; + move-result-object v1 + + aget-object v1, v1, v5 + aget-object v1, v1, v5 + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/examples/AnnotationTypes/MethodAnnotation.smali b/brut.apktool.smali/examples/AnnotationTypes/MethodAnnotation.smali new file mode 100644 index 00000000..2e632996 --- /dev/null +++ b/brut.apktool.smali/examples/AnnotationTypes/MethodAnnotation.smali @@ -0,0 +1,4 @@ +.class public abstract interface annotation LMethodAnnotation; +.super Ljava/lang/Object; +.implements Ljava/lang/annotation/Annotation; + diff --git a/brut.apktool.smali/examples/AnnotationTypes/ParameterAnnotation.smali b/brut.apktool.smali/examples/AnnotationTypes/ParameterAnnotation.smali new file mode 100644 index 00000000..d114b2a1 --- /dev/null +++ b/brut.apktool.smali/examples/AnnotationTypes/ParameterAnnotation.smali @@ -0,0 +1,3 @@ +.class public abstract interface annotation LParameterAnnotation; +.super Ljava/lang/Object; +.implements Ljava/lang/annotation/Annotation; \ No newline at end of file diff --git a/brut.apktool.smali/examples/AnnotationValues/10.smali b/brut.apktool.smali/examples/AnnotationValues/10.smali new file mode 100644 index 00000000..4460a019 --- /dev/null +++ b/brut.apktool.smali/examples/AnnotationValues/10.smali @@ -0,0 +1,6 @@ +.class public L10; +.super Ljava/lang/Object; + +.method public static 11()V + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/examples/AnnotationValues/AnnotationWithValues.smali b/brut.apktool.smali/examples/AnnotationValues/AnnotationWithValues.smali new file mode 100644 index 00000000..54465105 --- /dev/null +++ b/brut.apktool.smali/examples/AnnotationValues/AnnotationWithValues.smali @@ -0,0 +1,67 @@ +.class public abstract interface annotation LAnnotationWithValues; +.super Ljava/lang/Object; +.implements Ljava/lang/annotation/Annotation; + +.method public abstract booleanValue()Z +.end method + +.method public abstract byteValue()B +.end method + +.method public abstract charValue()C +.end method + +.method public abstract shortValue()S +.end method + +.method public abstract intValue()I +.end method + +.method public abstract longValue()J +.end method + +.method public abstract floatValue()F +.end method + +.method public abstract doubleValue()D +.end method + +.method public abstract stringValue()Ljava/lang/String; +.end method + +.method public abstract subAnnotationValue()LSubAnnotation; +.end method + +.method public abstract typeValue()Ljava/lang/Class; +.end method + +.method public abstract methodValue()Ljava/lang/reflect/Method; +.end method + +#dalvik doesn't seem to like field values +#.method public abstract fieldValue()Ljava/lang/reflect/Field; +#.end method + +.method public abstract enumValue()LEnum; +.end method + +.annotation system Ldalvik/annotation/AnnotationDefault; + value = .subannotation LAnnotationWithValues; + booleanValue = false + byteValue = 1t + charValue = '2' + shortValue = 3s + intValue = 4 + longValue = 5l + floatValue = 6.0f + doubleValue = 7.0 + stringValue = "8" + subAnnotationValue = .subannotation LSubAnnotation; + stringValue = "9" + .end subannotation + typeValue = L10; + methodValue = L10;->11()V + enumValue = .enum LEnum;->12:LEnum; + .end subannotation +.end annotation + diff --git a/brut.apktool.smali/examples/AnnotationValues/Enum.smali b/brut.apktool.smali/examples/AnnotationValues/Enum.smali new file mode 100644 index 00000000..cbc60a44 --- /dev/null +++ b/brut.apktool.smali/examples/AnnotationValues/Enum.smali @@ -0,0 +1,52 @@ +.class public final enum LEnum; +.super Ljava/lang/Enum; + +.field private static final synthetic $VALUES:[LEnum; + +.field public static final enum 12:LEnum; + +.method static constructor ()V + .registers 4 + + const/4 v3, 1 + const/4 v2, 0 + new-instance v0, LEnum; + const-string v1, "12" + invoke-direct {v0, v1, v2}, LEnum;->(Ljava/lang/String;I)V + sput-object v0, LEnum;->12:LEnum; + + const/4 v0, 1 + new-array v0, v0, [LEnum; + sget-object v1, LEnum;->12:LEnum; + aput-object v1, v0, v2 + + sput-object v0, LEnum;->$VALUES:[LEnum; + return-void +.end method + +.method private constructor (Ljava/lang/String;I)V + .registers 3 + + invoke-direct {p0, p1, p2}, Ljava/lang/Enum;->(Ljava/lang/String;I)V + return-void +.end method + +.method public static valueOf(Ljava/lang/String;)LEnum; + .registers 2 + + const-class v0, LEnum; + invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + move-result-object v1 + check-cast v1, LEnum; + return-object v1 +.end method + +.method public static values()[LEnum; + .registers 1 + + sget-object v0, LEnum;->$VALUES:[LEnum; + invoke-virtual {v0}, [LEnum;->clone()Ljava/lang/Object; + move-result-object v0 + check-cast v0, [LEnum; + return-object v0 +.end method \ No newline at end of file diff --git a/brut.apktool.smali/examples/AnnotationValues/Main.smali b/brut.apktool.smali/examples/AnnotationValues/Main.smali new file mode 100644 index 00000000..5f35c9de --- /dev/null +++ b/brut.apktool.smali/examples/AnnotationValues/Main.smali @@ -0,0 +1,25 @@ +.class public LMain; +.super Ljava/lang/Object; + +#expected output: +#@AnnotationWithValues(booleanValue=false, byteValue=1, charValue=2, doubleValue=7.0, enumValue=12, floatValue=6.0, intValue=4, longValue=5, methodValue=public static void 10.11(), shortValue=3, stringValue=8, subAnnotationValue=@SubAnnotation(stringValue=9), typeValue=class 10) + + +.method public static main([Ljava/lang/String;)V + .registers 3 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-class v1, LMain; + const-class v2, LAnnotationWithValues; + + invoke-virtual {v1, v2}, Ljava/lang/Class;->getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; + move-result-object v1 + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + return-void +.end method + +.annotation runtime LAnnotationWithValues; +.end annotation diff --git a/brut.apktool.smali/examples/AnnotationValues/SubAnnotation.smali b/brut.apktool.smali/examples/AnnotationValues/SubAnnotation.smali new file mode 100644 index 00000000..61ef9fd4 --- /dev/null +++ b/brut.apktool.smali/examples/AnnotationValues/SubAnnotation.smali @@ -0,0 +1,6 @@ +.class public abstract interface annotation LSubAnnotation; +.super Ljava/lang/Object; +.implements Ljava/lang/annotation/Annotation; + +.method public abstract stringValue()Ljava/lang/String; +.end method \ No newline at end of file diff --git a/brut.apktool.smali/examples/Enums/Enum.smali b/brut.apktool.smali/examples/Enums/Enum.smali new file mode 100644 index 00000000..1bae8c5b --- /dev/null +++ b/brut.apktool.smali/examples/Enums/Enum.smali @@ -0,0 +1,73 @@ +.class public final enum LEnum; +.super Ljava/lang/Enum; + +#This class is an example of how to define an enum. You have +#to do all of the work that java normally takes care of + +.field private static final synthetic $VALUES:[LEnum; + +.field public static final enum VALUE1:LEnum; +.field public static final enum VALUE2:LEnum; + +.method static constructor ()V + .registers 4 + + #create an instance of this class for the VALUE1 value + new-instance v0, LEnum; + const-string v1, "VALUE1" + const/4 v2, 0 + invoke-direct {v0, v1, v2}, LEnum;->(Ljava/lang/String;I)V + + #and store it in VALUE1 + sput-object v0, LEnum;->VALUE1:LEnum; + + #create an instance of this class for the VALUE2 value + new-instance v0, LEnum; + const-string v1, "VALUE2" + const/4 v3, 1 + invoke-direct {v0, v1, v3}, LEnum;->(Ljava/lang/String;I)V + + #and store it in VALUE2 + sput-object v0, LEnum;->VALUE2:LEnum; + + #create an array of Enums, for the $VALUES member + const/4 v0, 2 + new-array v0, v0, [LEnum; + + #add VALUE1 to the array + sget-object v1, LEnum;->VALUE1:LEnum; + aput-object v1, v0, v2 + + #add VALUE2 to the array + sget-object v1, LEnum;->VALUE2:LEnum; + aput-object v1, v0, v3 + + #and store the array in $VALUES + sput-object v0, LEnum;->$VALUES:[LEnum; + + return-void +.end method + +.method private constructor (Ljava/lang/String;I)V + .registers 3 + invoke-direct {p0, p1, p2}, Ljava/lang/Enum;->(Ljava/lang/String;I)V + return-void +.end method + +.method public static valueof(Ljava/lang/String;)LEnum; + .registers 2 + const-class v0, LEnum; + invoke-static {v0, p0}, Ljava/lang/Enum;->valueOf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + move-result-object v1 + check-cast v1, LEnum; + return-object v1 +.end method + +.method public static values()[LEnum; + .registers 1 + sget-object v0, LEnum;->$VALUES:[LEnum; + invoke-virtual {v0}, [LEnum;->clone()Ljava/lang/Object; + move-result-object v0 + check-cast v0, [LEnum; + return-object v0 +.end method \ No newline at end of file diff --git a/brut.apktool.smali/examples/Enums/Main.smali b/brut.apktool.smali/examples/Enums/Main.smali new file mode 100644 index 00000000..65354193 --- /dev/null +++ b/brut.apktool.smali/examples/Enums/Main.smali @@ -0,0 +1,15 @@ +.class public LMain; +.super Ljava/lang/Object; + + + +.method public static main([Ljava/lang/String;)V + .registers 2 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + sget-object v1, LEnum;->VALUE1:LEnum; + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/examples/HelloWorld/HelloWorld.smali b/brut.apktool.smali/examples/HelloWorld/HelloWorld.smali new file mode 100644 index 00000000..17b03f84 --- /dev/null +++ b/brut.apktool.smali/examples/HelloWorld/HelloWorld.smali @@ -0,0 +1,27 @@ +.class public LHelloWorld; + +#Ye olde hello world application +#To assemble and run this on a phone or emulator: +# +#java -jar smali.jar -o classes.dex HelloWorld.smali +#zip HelloWorld.zip classes.dex +#adb push HelloWorld.zip /data/local +#adb shell dalvikvm -cp /data/local/HelloWorld.zip HelloWorld +# +#if you get out of memory type errors when running smali.jar, try +#java -Xmx512m -jar smali.jar HelloWorld.smali +#instead + +.super Ljava/lang/Object; + +.method public static main([Ljava/lang/String;)V + .registers 2 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-string v1, "Hello World!" + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + + return-void +.end method \ No newline at end of file diff --git a/brut.apktool.smali/examples/Interface/Interface.smali b/brut.apktool.smali/examples/Interface/Interface.smali new file mode 100644 index 00000000..2c5d1548 --- /dev/null +++ b/brut.apktool.smali/examples/Interface/Interface.smali @@ -0,0 +1,5 @@ +.class public abstract interface LInterface; +.super Ljava/lang/Object; + +.method public abstract interfaceMethod()Ljava/lang/String; +.end method \ No newline at end of file diff --git a/brut.apktool.smali/examples/Interface/Main.smali b/brut.apktool.smali/examples/Interface/Main.smali new file mode 100644 index 00000000..f2f69f87 --- /dev/null +++ b/brut.apktool.smali/examples/Interface/Main.smali @@ -0,0 +1,35 @@ +.class public LMain; +.super Ljava/lang/Object; +.implements LInterface; + +#expected output: +#in interfaceMethod() + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public static main([Ljava/lang/String;)V + .registers 3 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + new-instance v1, LMain; + invoke-direct {v1}, LMain;->()V + invoke-interface {v1}, LInterface;->interfaceMethod()Ljava/lang/String; + move-result-object v1 + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + return-void +.end method + + +.method public interfaceMethod()Ljava/lang/String; + .registers 1 + + const-string v0, "in interfaceMethod()" + return-object v0 +.end method \ No newline at end of file diff --git a/brut.apktool.smali/examples/MethodOverloading/Main.smali b/brut.apktool.smali/examples/MethodOverloading/Main.smali new file mode 100644 index 00000000..a61303ec --- /dev/null +++ b/brut.apktool.smali/examples/MethodOverloading/Main.smali @@ -0,0 +1,50 @@ +.class public LMain; +.super Ljava/lang/Object; + +#expected output: +#returning a string +#42 + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method + +.method public static main([Ljava/lang/String;)V + .registers 4 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + new-instance v1, LMain; + invoke-direct {v1}, LMain;->()V + invoke-virtual {v1}, LMain;->overloadTest()Ljava/lang/String; + move-result-object v2 + + invoke-virtual {v0, v2}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + invoke-virtual {v1}, LMain;->overloadTest()I + move-result v2 + + invoke-static {v2}, Ljava/lang/Integer;->toString(I)Ljava/lang/String; + move-result-object v2 + + invoke-virtual {v0, v2}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + return-void +.end method + + +.method public overloadTest()Ljava/lang/String; + .registers 1 + + const-string v0, "returning a string" + return-object v0 +.end method + +.method public overloadTest()I + .registers 1 + + const v0, 42 + return v0 +.end method \ No newline at end of file diff --git a/brut.apktool.smali/examples/RecursiveAnnotation/Main.smali b/brut.apktool.smali/examples/RecursiveAnnotation/Main.smali new file mode 100644 index 00000000..d44bdca8 --- /dev/null +++ b/brut.apktool.smali/examples/RecursiveAnnotation/Main.smali @@ -0,0 +1,24 @@ +.class public LMain; +.super Ljava/lang/Object; + +#expected output (using the dalvik's default stack size) +#@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=@RecursiveAnnotation(value=java.lang.StackOverflowError)))))))))))))))))))))))))))))) + +.method public static main([Ljava/lang/String;)V + .registers 3 + + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + + const-class v1, LMain; + const-class v2, LRecursiveAnnotation; + + invoke-virtual {v1, v2}, Ljava/lang/Class;->getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; + move-result-object v1 + + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + return-void +.end method + +.annotation runtime LRecursiveAnnotation; +.end annotation diff --git a/brut.apktool.smali/examples/RecursiveAnnotation/RecursiveAnnotation.smali b/brut.apktool.smali/examples/RecursiveAnnotation/RecursiveAnnotation.smali new file mode 100644 index 00000000..0fde4a88 --- /dev/null +++ b/brut.apktool.smali/examples/RecursiveAnnotation/RecursiveAnnotation.smali @@ -0,0 +1,18 @@ +.class public abstract interface annotation LRecursiveAnnotation; +.super Ljava/lang/Object; +.implements Ljava/lang/annotation/Annotation; + +#this is a recursive annotation that has a default value of itself. +#Trying to print .toString() on an instance of this annotation +#will cause a stack overflow + +.method public abstract value()LRecursiveAnnotation; +.end method + +.annotation system Ldalvik/annotation/AnnotationDefault; + value = .subannotation LRecursiveAnnotation; + value = .subannotation LRecursiveAnnotation; + .end subannotation + .end subannotation +.end annotation + diff --git a/brut.apktool.smali/examples/RecursiveExceptionHandler/Main.smali b/brut.apktool.smali/examples/RecursiveExceptionHandler/Main.smali new file mode 100644 index 00000000..ea113650 --- /dev/null +++ b/brut.apktool.smali/examples/RecursiveExceptionHandler/Main.smali @@ -0,0 +1,21 @@ +.class public LMain; +.super Ljava/lang/Object; + +.method public static main([Ljava/lang/String;)V + .registers 3 + + :second_handler + :first_try_start + new-instance v0, Ljava/lang/RuntimeException; + invoke-direct {v0}, Ljava/lang/RuntimeException;->()V + throw v0 + :first_try_end + .catch Ljava/lang/Exception; {:first_try_start .. :first_try_end} :first_handler + :first_handler + :second_try_start + new-instance v0, Ljava/lang/RuntimeException; + invoke-direct {v0}, Ljava/lang/RuntimeException;->()V + throw v0 + :second_try_end + .catch Ljava/lang/Exception; {:second_try_start .. :second_try_end} :second_handler +.end method \ No newline at end of file diff --git a/brut.apktool.smali/scripts/baksmali b/brut.apktool.smali/scripts/baksmali new file mode 100755 index 00000000..bd3f820e --- /dev/null +++ b/brut.apktool.smali/scripts/baksmali @@ -0,0 +1,83 @@ +#!/bin/bash +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# As per the Apache license requirements, this file has been modified +# from its original state. +# +# Such modifications are Copyright (C) 2010 Ben Gruver, and are released +# under the original license + +# This script is a wrapper around baksmali.jar, so you can simply call +# "baksmali", instead of java -jar baksmali.jar. It is heavily based on +# the "dx" script from the Android SDK + +# Set up prog to be the path of this script, including following symlinks, +# and set up progdir to be the fully-qualified pathname of its directory. +prog="$0" +while [ -h "${prog}" ]; do + newProg=`/bin/ls -ld "${prog}"` + echo ${newProg} + + + newProg=`expr "${newProg}" : ".* -> \(.*\)$"` + if expr "x${newProg}" : 'x/' >/dev/null; then + prog="${newProg}" + else + progdir=`dirname "${prog}"` + prog="${progdir}/${newProg}" + fi +done +oldwd=`pwd` +progdir=`dirname "${prog}"` +cd "${progdir}" +progdir=`pwd` +prog="${progdir}"/`basename "${prog}"` +cd "${oldwd}" + + +jarfile=baksmali.jar +libdir="$progdir" +if [ ! -r "$libdir/$jarfile" ] +then + echo `basename "$prog"`": can't find $jarfile" + exit 1 +fi + +javaOpts="" + +# If you want DX to have more memory when executing, uncomment the following +# line and adjust the value accordingly. Use "java -X" for a list of options +# you can pass here. +# +javaOpts="-Xmx256M" + +# Alternatively, this will extract any parameter "-Jxxx" from the command line +# and pass them to Java (instead of to dx). This makes it possible for you to +# add a command-line parameter such as "-JXmx256M" in your ant scripts, for +# example. +while expr "x$1" : 'x-J' >/dev/null; do + opt=`expr "$1" : '-J\(.*\)'` + javaOpts="${javaOpts} -${opt}" + shift +done + +if [ "$OSTYPE" = "cygwin" ] ; then + jarpath=`cygpath -w "$libdir/$jarfile"` +else + jarpath="$libdir/$jarfile" +fi + +exec java $javaOpts -jar "$jarpath" "$@" diff --git a/brut.apktool.smali/scripts/smali b/brut.apktool.smali/scripts/smali new file mode 100755 index 00000000..b01199e1 --- /dev/null +++ b/brut.apktool.smali/scripts/smali @@ -0,0 +1,83 @@ +#!/bin/bash +# +# Copyright (C) 2007 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# As per the Apache license requirements, this file has been modified +# from its original state. +# +# Such modifications are Copyright (C) 2010 Ben Gruver, and are released +# under the original license + +# This script is a wrapper for smali.jar, so you can simply call "smali", +# instead of java -jar smali.jar. It is heavily based on the "dx" script +# from the Android SDK + +# Set up prog to be the path of this script, including following symlinks, +# and set up progdir to be the fully-qualified pathname of its directory. +prog="$0" +while [ -h "${prog}" ]; do + newProg=`/bin/ls -ld "${prog}"` + echo ${newProg} + + + newProg=`expr "${newProg}" : ".* -> \(.*\)$"` + if expr "x${newProg}" : 'x/' >/dev/null; then + prog="${newProg}" + else + progdir=`dirname "${prog}"` + prog="${progdir}/${newProg}" + fi +done +oldwd=`pwd` +progdir=`dirname "${prog}"` +cd "${progdir}" +progdir=`pwd` +prog="${progdir}"/`basename "${prog}"` +cd "${oldwd}" + + +jarfile=smali.jar +libdir="$progdir" +if [ ! -r "$libdir/$jarfile" ] +then + echo `basename "$prog"`": can't find $jarfile" + exit 1 +fi + +javaOpts="" + +# If you want DX to have more memory when executing, uncomment the following +# line and adjust the value accordingly. Use "java -X" for a list of options +# you can pass here. +# +javaOpts="-Xmx256M" + +# Alternatively, this will extract any parameter "-Jxxx" from the command line +# and pass them to Java (instead of to dx). This makes it possible for you to +# add a command-line parameter such as "-JXmx256M" in your ant scripts, for +# example. +while expr "x$1" : 'x-J' >/dev/null; do + opt=`expr "$1" : '-J\(.*\)'` + javaOpts="${javaOpts} -${opt}" + shift +done + +if [ "$OSTYPE" = "cygwin" ] ; then + jarpath=`cygpath -w "$libdir/$jarfile"` +else + jarpath="$libdir/$jarfile" +fi + +exec java $javaOpts -jar "$jarpath" "$@" diff --git a/brut.apktool.smali/smali/build.gradle b/brut.apktool.smali/smali/build.gradle new file mode 100644 index 00000000..14d2a556 --- /dev/null +++ b/brut.apktool.smali/smali/build.gradle @@ -0,0 +1,145 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +configurations { + antlr3 + jflex + proguard +} + +ext.antlrSource = 'src/main/antlr3' +ext.antlrOutput = file(new File(buildDir, '/generated-sources/antlr3')) + +ext.jflexSource = "src/main/jflex" +ext.jflexOutput = file(new File(buildDir, '/generated-sources/jflex')) + +ext.testAntlrSource = 'src/test/antlr3' +ext.testAntlrOutput = file(new File(buildDir, '/generated-test-sources/antlr3')) + +sourceSets.main.java.srcDir antlrOutput +sourceSets.main.java.srcDir jflexOutput + +sourceSets.test.java.srcDir testAntlrOutput + +dependencies { + compile project(':brut.apktool.smali:util') + compile project(':brut.apktool.smali:dexlib') + compile 'org.antlr:antlr-runtime:3.2' + compile 'commons-cli:commons-cli:1.2' + + testCompile 'junit:junit:4.6' + + antlr3 'org.antlr:antlr:3.2' + jflex 'de.jflex:jflex:1.4.3' + proguard 'net.sf.proguard:proguard-base:4.8' +} + +task generateAntlrSource(type: JavaExec) { + inputs.dir file(antlrSource) + outputs.dir file(antlrOutput) + + mkdir(antlrOutput) + def grammars = fileTree(antlrSource).include('**/*.g') + + classpath = files(configurations.antlr3.asPath) + main = 'org.antlr.Tool' + args '-fo', relativePath(new File(antlrOutput, 'org/jf/smali')) + args grammars.files +} + +task generateTestAntlrSource(type: JavaExec) { + inputs.dir file(testAntlrSource) + outputs.dir file(testAntlrOutput) + + mkdir(testAntlrOutput) + def grammars = fileTree(testAntlrSource).include('**/*.g') + + classpath = files(configurations.antlr3.asPath) + main = 'org.antlr.Tool' + args '-fo', relativePath(new File(testAntlrOutput, 'org/jf/smali')) + args grammars.files.join(' ') +} + +task generateJflexSource(type: JavaExec) { + inputs.dir file(jflexSource) + outputs.dir file(jflexOutput) + + mkdir(jflexOutput) + def grammars = fileTree(jflexSource).include('**/*.flex') + + classpath = files(configurations.jflex.asPath) + main = 'JFlex.Main' + args '-q' + args '-d', relativePath(new File(jflexOutput, 'org/jf/smali')) + args grammars.files.join(' ') +} + + +compileJava.dependsOn generateAntlrSource, generateJflexSource +compileTestJava.dependsOn generateTestAntlrSource + +// We have to do this in taskGraph.whenReady, so that we use the correct +// version to resolve the project dependencies +gradle.taskGraph.whenReady { + // build a jar containing all dependencies + jar { + from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } + + manifest { + attributes("Main-Class": "org.jf.smali.main") + } + } + + processResources.inputs.properties('version': version) + processResources.expand('version': version) + + proguard { + def outFile = relativePath(buildDir) + '/libs/' + jar.baseName + '-' + jar.version + '-small' + '.' + jar.extension + + inputs.file jar.archivePath + outputs.file outFile + + args '-injars ' + jar.archivePath + '(!**/TestStringTemplate*.class)' + args '-outjars ' + outFile + } +} + +task proguard(type: JavaExec, dependsOn: jar) { + classpath = files(configurations.proguard.asPath) + main = 'proguard.ProGuard' + args '-libraryjars ' + System.properties['java.home'] + '/lib/rt.jar' + args '-dontobfuscate' + args '-dontoptimize' + args '-keep public class org.jf.smali.main { public static void main(java.lang.String[]); }' + args '-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }' + args '-dontwarn com.google.common.base.**' + args '-dontnote com.google.common.base.**' +} \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/main/antlr3/smaliLexer.g b/brut.apktool.smali/smali/src/main/antlr3/smaliLexer.g new file mode 100644 index 00000000..872e1065 --- /dev/null +++ b/brut.apktool.smali/smali/src/main/antlr3/smaliLexer.g @@ -0,0 +1,791 @@ +/* + * The comment, number, string and character constant lexical rules are + * derived from rules from the Java 1.6 grammar which can be found here: + * http://openjdk.java.net/projects/compiler-grammar/antlrworks/Java.g + * + * Specifically, these rules: + * + * BASE_INTEGER, DECIMAL_EXPONENT, BINARY_EXPONENT, HEX_PREFIX, HEX_DIGIT, + * BASE_FLOAT_OR_ID, BASE_FLOAT, ESCAPE_SEQUENCE, POSITIVE_INTEGER_LITERAL, + * NEGATIVE_INTEGER_LITERAL, LONG_LITERAL, SHORT_LITERAL, BYTE_LITERAL, + * FLOAT_LITERAL_OR_ID, DOUBLE_LITERAL_OR_ID, FLOAT_LITERAL, DOUBLE_LITERAL, + * BOOL_LITERAL, STRING_LITERAL, BASE_STRING_LITERAL, CHAR_LITERAL, + * BASE_CHAR_LITERAL + * + * These rules were originally copyrighted by Terence Parr, and are used here in + * accordance with the following license + * + * [The "BSD licence"] + * Copyright (c) 2007-2008 Terence Parr + * 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. + * + * + * The remainder of this grammar is released by me (Ben Gruver) under the + * following license: + * + * [The "BSD licence"] + * Copyright (c) 2010 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. + */ + +lexer grammar smaliLexer; + +options { + superClass=ANTLRLexerWithErrorInterface; +} + +@lexer::header { + package org.jf.smali; + + import static org.jf.smali.LexerErrorInterface.ANTLRLexerWithErrorInterface; +} + +@lexer::members { + public static final int ERROR_CHANNEL = 100; + public String getErrorHeader(RecognitionException e) { + return getSourceName()+"["+ e.line+","+e.charPositionInLine+"]"; + } +} + +/********************************************************** +* DIRECTIVES +**********************************************************/ + +CLASS_DIRECTIVE + : '.class'; + +SUPER_DIRECTIVE + : '.super'; + +IMPLEMENTS_DIRECTIVE + : '.implements'; + +SOURCE_DIRECTIVE + : '.source'; + +FIELD_DIRECTIVE + : '.field'; + +END_FIELD_DIRECTIVE + : '.end field'; + +SUBANNOTATION_DIRECTIVE + : '.subannotation'; + +END_SUBANNOTATION_DIRECTIVE + : '.end subannotation'; + +ANNOTATION_DIRECTIVE + : '.annotation'; + +END_ANNOTATION_DIRECTIVE + : '.end annotation'; + +ENUM_DIRECTIVE + : '.enum'; + +METHOD_DIRECTIVE + : '.method'; + +END_METHOD_DIRECTIVE + : '.end method'; + +REGISTERS_DIRECTIVE + : '.registers'; + +LOCALS_DIRECTIVE + : '.locals'; + +ARRAY_DATA_DIRECTIVE + : '.array-data'; + +END_ARRAY_DATA_DIRECTIVE + : '.end array-data'; + +PACKED_SWITCH_DIRECTIVE + : '.packed-switch'; + +END_PACKED_SWITCH_DIRECTIVE + : '.end packed-switch'; + +SPARSE_SWITCH_DIRECTIVE + : '.sparse-switch'; + +END_SPARSE_SWITCH_DIRECTIVE + : '.end sparse-switch'; + +CATCH_DIRECTIVE + : '.catch'; + +CATCHALL_DIRECTIVE + : '.catchall'; + +LINE_DIRECTIVE + : '.line'; + +PARAMETER_DIRECTIVE + : '.parameter'; + +END_PARAMETER_DIRECTIVE + : '.end parameter'; + +LOCAL_DIRECTIVE + : '.local'; + +END_LOCAL_DIRECTIVE + : '.end local'; + +RESTART_LOCAL_DIRECTIVE + : '.restart local'; + +PROLOGUE_DIRECTIVE + : '.prologue'; + +EPILOGUE_DIRECTIVE + : '.epilogue'; + +/********************************************************** +* LITERALS +**********************************************************/ +fragment BASE_INTEGER + : '0' + | ('1'..'9') ('0'..'9')* + | '0' ('0'..'7')+ + | HEX_PREFIX HEX_DIGIT+; + +fragment DECIMAL_EXPONENT + : ('e'|'E') '-'? ('0'..'9')+; + +fragment BINARY_EXPONENT + : ('p'|'P') '-'? ('0'..'9')+; + +fragment HEX_PREFIX + : '0x'|'0X'; + +fragment HEX_DIGIT + : ('0'..'9')|('A'..'F')|('a'..'f'); + +fragment HEX_DIGITS + : HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT; + +/*This can either be floating point numbers, or identifier*/ +fragment BASE_FLOAT_OR_ID + : '-'? ('0'..'9')+ DECIMAL_EXPONENT + | HEX_PREFIX HEX_DIGIT+ BINARY_EXPONENT + | '-'? ('i' | 'I') ('n' | 'N') ('f' | 'F') ('i' | 'I') ('n' | 'N') ('i' | 'I') ('t' | 'T') ('y' | 'Y') + | ('n' | 'N') ('a' | 'A') ('n' | 'N'); + +/*These can't be identifiers, due to the decimal point*/ +fragment BASE_FLOAT + : '-'? ('0'..'9')+ '.' ('0'..'9')* DECIMAL_EXPONENT? + | '-'? '.' ('0'..'9')+ DECIMAL_EXPONENT? + | '-'? HEX_PREFIX HEX_DIGIT+ '.' HEX_DIGIT* BINARY_EXPONENT + | '-'? HEX_PREFIX '.' HEX_DIGIT+ BINARY_EXPONENT; + +fragment ESCAPE_SEQUENCE[StringBuilder sb] + : '\\' + ( + 'b' {sb.append("\b");} + | 't' {sb.append("\t");} + | 'n' {sb.append("\n");} + | 'f' {sb.append("\f");} + | 'r' {sb.append("\r");} + | '\"' {sb.append("\"");} + | '\'' {sb.append("'");} + | '\\' {sb.append("\\");} + | 'u' HEX_DIGITS {sb.append((char)Integer.parseInt($HEX_DIGITS.text, 16));} + ); + +POSITIVE_INTEGER_LITERAL + : BASE_INTEGER; + +NEGATIVE_INTEGER_LITERAL + : '-' BASE_INTEGER; + +LONG_LITERAL + : '-'? BASE_INTEGER ('l'|'L'); + +SHORT_LITERAL + : '-'? BASE_INTEGER ('s'|'S'); + +BYTE_LITERAL + : '-'? BASE_INTEGER ('t'|'T'); + +FLOAT_LITERAL_OR_ID + : BASE_FLOAT_OR_ID ('f'|'F') + | '-'? ('0'..'9')+ ('f'|'F'); + +DOUBLE_LITERAL_OR_ID + : BASE_FLOAT_OR_ID ('d'|'D')? + | '-'? ('0'..'9')+ ('d'|'D'); + +FLOAT_LITERAL + : BASE_FLOAT ('f'|'F'); + +DOUBLE_LITERAL + : BASE_FLOAT ('d'|'D')?; + +BOOL_LITERAL + : 'true' + | 'false'; + +NULL_LITERAL + : 'null'; + +STRING_LITERAL + @init {StringBuilder sb = new StringBuilder();} + : BASE_STRING_LITERAL[sb] {setText(sb.toString());}; + +fragment BASE_STRING_LITERAL[StringBuilder sb] + : '"' {sb.append('"');} + ( ESCAPE_SEQUENCE[sb] + | ~( '\\' | '"' | '\r' | '\n' ) {sb.append((char)input.LA(-1));} + )* + '"' {sb.append('"');}; + +CHAR_LITERAL + @init {StringBuilder sb = new StringBuilder();} + : BASE_CHAR_LITERAL[sb] {setText(sb.toString());}; + +fragment BASE_CHAR_LITERAL[StringBuilder sb] + : '\'' {sb.append('\'');} + ( ESCAPE_SEQUENCE[sb] + | ~( '\\' | '\'' | '\r' | '\n' ) {sb.append((char)input.LA(-1));} + ) + '\'' { sb.append('\''); }; + + +/********************************************************** +* MISC +**********************************************************/ +REGISTER + : ('v'|'p') ('0'..'9')+; + +ANNOTATION_VISIBILITY + : 'build' + | 'runtime' + | 'system'; + +ACCESS_SPEC + : 'public' + | 'private' + | 'protected' + | 'static' + | 'final' + | 'synchronized' + | 'bridge' + | 'varargs' + | 'native' + | 'abstract' + | 'strictfp' + | 'synthetic' + | 'constructor' + | 'declared-synchronized' + | 'interface' + | 'enum' + | 'annotation' + | 'volatile' + | 'transient'; + +VERIFICATION_ERROR_TYPE + : 'no-error' + | 'generic-error' + | 'no-such-class' + | 'no-such-field' + | 'no-such-method' + | 'illegal-class-access' + | 'illegal-field-access' + | 'illegal-method-access' + | 'class-change-error' + | 'instantiation-error'; + +INLINE_INDEX + : 'inline@0x' HEX_DIGIT+; + +VTABLE_INDEX + : 'vtable@0x' HEX_DIGIT+; + +FIELD_OFFSET + : 'field@0x' HEX_DIGIT+; + +OFFSET + : '+' BASE_INTEGER; + +LINE_COMMENT + : '#' + ( + ~('\n'|'\r')* ('\r\n' | '\r' | '\n') + | ~('\n'|'\r')* + ) + {$channel = HIDDEN;}; + +/********************************************************** +* Instructions +**********************************************************/ +INSTRUCTION_FORMAT10t + : 'goto'; + +INSTRUCTION_FORMAT10x + : 'return-void' + | 'nop'; + +INSTRUCTION_FORMAT10x_ODEX + : 'return-void-barrier'; + +INSTRUCTION_FORMAT11n + : 'const/4'; + +INSTRUCTION_FORMAT11x + : 'move-result' + | 'move-result-wide' + | 'move-result-object' + | 'move-exception' + | 'return' + | 'return-wide' + | 'return-object' + | 'monitor-enter' + | 'monitor-exit' + | 'throw'; + +INSTRUCTION_FORMAT12x_OR_ID + : 'move' + | 'move-wide' + | 'move-object' + | 'array-length' + | 'neg-int' + | 'not-int' + | 'neg-long' + | 'not-long' + | 'neg-float' + | 'neg-double' + | 'int-to-long' + | 'int-to-float' + | 'int-to-double' + | 'long-to-int' + | 'long-to-float' + | 'long-to-double' + | 'float-to-int' + | 'float-to-long' + | 'float-to-double' + | 'double-to-int' + | 'double-to-long' + | 'double-to-float' + | 'int-to-byte' + | 'int-to-char' + | 'int-to-short'; + +INSTRUCTION_FORMAT12x + : 'add-int/2addr' + | 'sub-int/2addr' + | 'mul-int/2addr' + | 'div-int/2addr' + | 'rem-int/2addr' + | 'and-int/2addr' + | 'or-int/2addr' + | 'xor-int/2addr' + | 'shl-int/2addr' + | 'shr-int/2addr' + | 'ushr-int/2addr' + | 'add-long/2addr' + | 'sub-long/2addr' + | 'mul-long/2addr' + | 'div-long/2addr' + | 'rem-long/2addr' + | 'and-long/2addr' + | 'or-long/2addr' + | 'xor-long/2addr' + | 'shl-long/2addr' + | 'shr-long/2addr' + | 'ushr-long/2addr' + | 'add-float/2addr' + | 'sub-float/2addr' + | 'mul-float/2addr' + | 'div-float/2addr' + | 'rem-float/2addr' + | 'add-double/2addr' + | 'sub-double/2addr' + | 'mul-double/2addr' + | 'div-double/2addr' + | 'rem-double/2addr'; + +INSTRUCTION_FORMAT20bc + : 'throw-verification-error'; + +INSTRUCTION_FORMAT20t + : 'goto/16'; + +INSTRUCTION_FORMAT21c_FIELD + : 'sget' + | 'sget-wide' + | 'sget-object' + | 'sget-boolean' + | 'sget-byte' + | 'sget-char' + | 'sget-short' + | 'sput' + | 'sput-wide' + | 'sput-object' + | 'sput-boolean' + | 'sput-byte' + | 'sput-char' + | 'sput-short'; + +INSTRUCTION_FORMAT21c_FIELD_ODEX + : 'sget-volatile' + | 'sget-wide-volatile' + | 'sget-object-volatile' + | 'sput-volatile' + | 'sput-wide-volatile' + | 'sput-object-volatile'; + +INSTRUCTION_FORMAT21c_STRING + : 'const-string'; + +INSTRUCTION_FORMAT21c_TYPE + : 'check-cast' + | 'new-instance' + | 'const-class'; + +INSTRUCTION_FORMAT21h + : 'const/high16' + | 'const-wide/high16'; + +INSTRUCTION_FORMAT21s + : 'const/16' + | 'const-wide/16'; + +INSTRUCTION_FORMAT21t + : 'if-eqz' + | 'if-nez' + | 'if-ltz' + | 'if-gez' + | 'if-gtz' + | 'if-lez'; + +INSTRUCTION_FORMAT22b + : 'add-int/lit8' + | 'rsub-int/lit8' + | 'mul-int/lit8' + | 'div-int/lit8' + | 'rem-int/lit8' + | 'and-int/lit8' + | 'or-int/lit8' + | 'xor-int/lit8' + | 'shl-int/lit8' + | 'shr-int/lit8' + | 'ushr-int/lit8'; + +INSTRUCTION_FORMAT22c_FIELD + : 'iget' + | 'iget-wide' + | 'iget-object' + | 'iget-boolean' + | 'iget-byte' + | 'iget-char' + | 'iget-short' + | 'iput' + | 'iput-wide' + | 'iput-object' + | 'iput-boolean' + | 'iput-byte' + | 'iput-char' + | 'iput-short'; + +INSTRUCTION_FORMAT22c_FIELD_ODEX + : 'iget-volatile' + | 'iget-wide-volatile' + | 'iget-object-volatile' + | 'iput-volatile' + | 'iput-wide-volatile' + | 'iput-object-volatile'; + +INSTRUCTION_FORMAT22c_TYPE + : 'instance-of' + | 'new-array'; + + +INSTRUCTION_FORMAT22cs_FIELD + : 'iget-quick' + | 'iget-wide-quick' + | 'iget-object-quick' + | 'iput-quick' + | 'iput-wide-quick' + | 'iput-object-quick'; + +INSTRUCTION_FORMAT22s_OR_ID + : 'rsub-int'; + +INSTRUCTION_FORMAT22s + : 'add-int/lit16' + | 'mul-int/lit16' + | 'div-int/lit16' + | 'rem-int/lit16' + | 'and-int/lit16' + | 'or-int/lit16' + | 'xor-int/lit16'; + +INSTRUCTION_FORMAT22t + : 'if-eq' + | 'if-ne' + | 'if-lt' + | 'if-ge' + | 'if-gt' + | 'if-le'; + +INSTRUCTION_FORMAT22x + : 'move/from16' + | 'move-wide/from16' + | 'move-object/from16'; + +INSTRUCTION_FORMAT23x + : 'cmpl-float' + | 'cmpg-float' + | 'cmpl-double' + | 'cmpg-double' + | 'cmp-long' + | 'aget' + | 'aget-wide' + | 'aget-object' + | 'aget-boolean' + | 'aget-byte' + | 'aget-char' + | 'aget-short' + | 'aput' + | 'aput-wide' + | 'aput-object' + | 'aput-boolean' + | 'aput-byte' + | 'aput-char' + | 'aput-short' + | 'add-int' + | 'sub-int' + | 'mul-int' + | 'div-int' + | 'rem-int' + | 'and-int' + | 'or-int' + | 'xor-int' + | 'shl-int' + | 'shr-int' + | 'ushr-int' + | 'add-long' + | 'sub-long' + | 'mul-long' + | 'div-long' + | 'rem-long' + | 'and-long' + | 'or-long' + | 'xor-long' + | 'shl-long' + | 'shr-long' + | 'ushr-long' + | 'add-float' + | 'sub-float' + | 'mul-float' + | 'div-float' + | 'rem-float' + | 'add-double' + | 'sub-double' + | 'mul-double' + | 'div-double' + | 'rem-double'; + +INSTRUCTION_FORMAT30t + : 'goto/32'; + +INSTRUCTION_FORMAT31c + : 'const-string/jumbo'; + +INSTRUCTION_FORMAT31i_OR_ID + : 'const'; + +INSTRUCTION_FORMAT31i + : 'const-wide/32'; + +INSTRUCTION_FORMAT31t + : 'fill-array-data' + | 'packed-switch' + | 'sparse-switch'; + +INSTRUCTION_FORMAT32x + : 'move/16' + | 'move-wide/16' + | 'move-object/16'; + +INSTRUCTION_FORMAT35c_METHOD + : 'invoke-virtual' + | 'invoke-super' + | 'invoke-direct' + | 'invoke-static' + | 'invoke-interface'; + +INSTRUCTION_FORMAT35c_METHOD_ODEX + : 'invoke-direct-empty'; + +INSTRUCTION_FORMAT35c_TYPE + : 'filled-new-array'; + +INSTRUCTION_FORMAT35mi_METHOD + : 'execute-inline'; + +INSTRUCTION_FORMAT35ms_METHOD + : 'invoke-virtual-quick' + | 'invoke-super-quick'; + +INSTRUCTION_FORMAT3rc_METHOD + : 'invoke-virtual/range' + | 'invoke-super/range' + | 'invoke-direct/range' + | 'invoke-static/range' + | 'invoke-interface/range'; + +INSTRUCTION_FORMAT3rc_METHOD_ODEX + : 'invoke-object-init/range'; + +INSTRUCTION_FORMAT3rc_TYPE + : 'filled-new-array/range'; + +INSTRUCTION_FORMAT3rmi_METHOD + : 'execute-inline/range'; + +INSTRUCTION_FORMAT3rms_METHOD + : 'invoke-virtual-quick/range' + | 'invoke-super-quick/range'; + +INSTRUCTION_FORMAT51l + : 'const-wide'; + +/********************************************************** +* Types +**********************************************************/ +fragment BASE_SIMPLE_NAME: + ( 'A'..'Z' + | 'a'..'z' + | '0'..'9' + | '$' + | '-' + | '_' + | '\u00a1'..'\u1fff' + | '\u2010'..'\u2027' + | '\u2030'..'\ud7ff' + | '\ue000'..'\uffef' + )+; + +fragment BASE_PRIMITIVE_TYPE + : 'Z'|'B'|'S'|'C'|'I'|'J'|'F'|'D'; + + +fragment BASE_CLASS_DESCRIPTOR + : 'L' (BASE_SIMPLE_NAME '/')* BASE_SIMPLE_NAME ';'; + +fragment BASE_ARRAY_DESCRIPTOR + : '['+ (BASE_PRIMITIVE_TYPE | BASE_CLASS_DESCRIPTOR); + +fragment BASE_TYPE + : BASE_PRIMITIVE_TYPE + | BASE_CLASS_DESCRIPTOR + | BASE_ARRAY_DESCRIPTOR; + +PRIMITIVE_TYPE + : BASE_PRIMITIVE_TYPE; + +VOID_TYPE + : 'V'; + +CLASS_DESCRIPTOR + : BASE_CLASS_DESCRIPTOR; + +ARRAY_DESCRIPTOR + : BASE_ARRAY_DESCRIPTOR; + +PARAM_LIST_OR_ID + : BASE_PRIMITIVE_TYPE BASE_PRIMITIVE_TYPE+; + +PARAM_LIST + : BASE_TYPE BASE_TYPE+; + +SIMPLE_NAME + : BASE_SIMPLE_NAME; + +METHOD_NAME + : '' + | ''; + + +/********************************************************** +* Symbols +**********************************************************/ + +DOTDOT + : '..'; + +ARROW + : '->'; + +EQUAL + : '='; + +COLON + : ':'; + +COMMA + : ','; + +OPEN_BRACE + : '{'; + +CLOSE_BRACE + : '}'; + +OPEN_PAREN + : '('; + +CLOSE_PAREN + : ')'; + +WHITE_SPACE + : (' '|'\t'|'\n'|'\r')+ {$channel = HIDDEN;}; diff --git a/brut.apktool.smali/smali/src/main/antlr3/smaliParser.g b/brut.apktool.smali/smali/src/main/antlr3/smaliParser.g new file mode 100644 index 00000000..3d272b0e --- /dev/null +++ b/brut.apktool.smali/smali/src/main/antlr3/smaliParser.g @@ -0,0 +1,1061 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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. + */ + +parser grammar smaliParser; + +options { + tokenVocab=smaliLexer; + output=AST; + ASTLabelType=CommonTree; +} + +tokens { + //I_* tokens are imaginary tokens used as parent AST nodes + I_CLASS_DEF; + I_SUPER; + I_IMPLEMENTS; + I_SOURCE; + I_ACCESS_LIST; + I_METHODS; + I_FIELDS; + I_FIELD; + I_FIELD_TYPE; + I_FIELD_INITIAL_VALUE; + I_METHOD; + I_METHOD_PROTOTYPE; + I_METHOD_RETURN_TYPE; + I_REGISTERS; + I_LOCALS; + I_LABELS; + I_LABEL; + I_ANNOTATIONS; + I_ANNOTATION; + I_ANNOTATION_ELEMENT; + I_SUBANNOTATION; + I_ENCODED_FIELD; + I_ENCODED_METHOD; + I_ENCODED_ENUM; + I_ENCODED_ARRAY; + I_ARRAY_ELEMENT_SIZE; + I_ARRAY_ELEMENTS; + I_PACKED_SWITCH_START_KEY; + I_PACKED_SWITCH_TARGET_COUNT; + I_PACKED_SWITCH_TARGETS; + I_PACKED_SWITCH_DECLARATION; + I_PACKED_SWITCH_DECLARATIONS; + I_SPARSE_SWITCH_KEYS; + I_SPARSE_SWITCH_TARGET_COUNT; + I_SPARSE_SWITCH_TARGETS; + I_SPARSE_SWITCH_DECLARATION; + I_SPARSE_SWITCH_DECLARATIONS; + I_ADDRESS; + I_CATCH; + I_CATCHALL; + I_CATCHES; + I_PARAMETER; + I_PARAMETERS; + I_PARAMETER_NOT_SPECIFIED; + I_ORDERED_DEBUG_DIRECTIVES; + I_LINE; + I_LOCAL; + I_END_LOCAL; + I_RESTART_LOCAL; + I_PROLOGUE; + I_EPILOGUE; + I_STATEMENTS; + I_STATEMENT_FORMAT10t; + I_STATEMENT_FORMAT10x; + I_STATEMENT_FORMAT11n; + I_STATEMENT_FORMAT11x; + I_STATEMENT_FORMAT12x; + I_STATEMENT_FORMAT20bc; + I_STATEMENT_FORMAT20t; + I_STATEMENT_FORMAT21c_TYPE; + I_STATEMENT_FORMAT21c_FIELD; + I_STATEMENT_FORMAT21c_STRING; + I_STATEMENT_FORMAT21h; + I_STATEMENT_FORMAT21s; + I_STATEMENT_FORMAT21t; + I_STATEMENT_FORMAT22b; + I_STATEMENT_FORMAT22c_FIELD; + I_STATEMENT_FORMAT22c_TYPE; + I_STATEMENT_FORMAT22s; + I_STATEMENT_FORMAT22t; + I_STATEMENT_FORMAT22x; + I_STATEMENT_FORMAT23x; + I_STATEMENT_FORMAT30t; + I_STATEMENT_FORMAT31c; + I_STATEMENT_FORMAT31i; + I_STATEMENT_FORMAT31t; + I_STATEMENT_FORMAT32x; + I_STATEMENT_FORMAT35c_METHOD; + I_STATEMENT_FORMAT35c_TYPE; + I_STATEMENT_FORMAT3rc_METHOD; + I_STATEMENT_FORMAT3rc_TYPE; + I_STATEMENT_FORMAT51l; + I_STATEMENT_ARRAY_DATA; + I_STATEMENT_PACKED_SWITCH; + I_STATEMENT_SPARSE_SWITCH; + I_REGISTER_RANGE; + I_REGISTER_LIST; + + LABEL; + INTEGER_LITERAL; + INVALID_TOKEN; +} + +@header { +package org.jf.smali; + +import org.jf.dexlib.Code.Format.*; +import org.jf.dexlib.Code.Opcode; +} + + +@members { + private boolean verboseErrors = false; + private boolean allowOdex = false; + private int apiLevel; + + public void setVerboseErrors(boolean verboseErrors) { + this.verboseErrors = verboseErrors; + } + + public void setAllowOdex(boolean allowOdex) { + this.allowOdex = allowOdex; + } + + public void setApiLevel(int apiLevel) { + this.apiLevel = apiLevel; + } + + public String getErrorMessage(RecognitionException e, + String[] tokenNames) { + + if (verboseErrors) { + List stack = getRuleInvocationStack(e, this.getClass().getName()); + String msg = null; + + if (e instanceof NoViableAltException) { + NoViableAltException nvae = (NoViableAltException)e; + msg = " no viable alt; token="+getTokenErrorDisplay(e.token)+ + " (decision="+nvae.decisionNumber+ + " state "+nvae.stateNumber+")"+ + " decision=<<"+nvae.grammarDecisionDescription+">>"; + } else { + msg = super.getErrorMessage(e, tokenNames); + } + + return stack + " " + msg; + } else { + return super.getErrorMessage(e, tokenNames); + } + } + + public String getTokenErrorDisplay(Token t) { + if (!verboseErrors) { + String s = t.getText(); + if ( s==null ) { + if ( t.getType()==Token.EOF ) { + s = ""; + } + else { + s = "<"+tokenNames[t.getType()]+">"; + } + } + s = s.replaceAll("\n","\\\\n"); + s = s.replaceAll("\r","\\\\r"); + s = s.replaceAll("\t","\\\\t"); + return "'"+s+"'"; + } + + CommonToken ct = (CommonToken)t; + + String channelStr = ""; + if (t.getChannel()>0) { + channelStr=",channel="+t.getChannel(); + } + String txt = t.getText(); + if ( txt!=null ) { + txt = txt.replaceAll("\n","\\\\n"); + txt = txt.replaceAll("\r","\\\\r"); + txt = txt.replaceAll("\t","\\\\t"); + } + else { + txt = ""; + } + return "[@"+t.getTokenIndex()+","+ct.getStartIndex()+":"+ct.getStopIndex()+"='"+txt+"',<"+tokenNames[t.getType()]+">"+channelStr+","+t.getLine()+":"+t.getCharPositionInLine()+"]"; + } + + public String getErrorHeader(RecognitionException e) { + return getSourceName()+"["+ e.line+","+e.charPositionInLine+"]"; + } + + private CommonTree buildTree(int type, String text, List children) { + CommonTree root = new CommonTree(new CommonToken(type, text)); + for (CommonTree child: children) { + root.addChild(child); + } + return root; + } + + private CommonToken getParamListSubToken(CommonToken baseToken, String str, int typeStartIndex) { + CommonToken token = new CommonToken(baseToken); + token.setStartIndex(baseToken.getStartIndex() + typeStartIndex); + + switch (str.charAt(typeStartIndex)) { + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + case 'J': + case 'F': + case 'D': + { + token.setType(PRIMITIVE_TYPE); + token.setText(str.substring(typeStartIndex, typeStartIndex+1)); + token.setStopIndex(baseToken.getStartIndex() + typeStartIndex); + break; + } + case 'L': + { + int i = typeStartIndex; + while (str.charAt(++i) != ';'); + + token.setType(CLASS_DESCRIPTOR); + token.setText(str.substring(typeStartIndex, i + 1)); + token.setStopIndex(baseToken.getStartIndex() + i); + break; + } + case '[': + { + int i = typeStartIndex; + while (str.charAt(++i) == '['); + + if (str.charAt(i++) == 'L') { + while (str.charAt(i++) != ';'); + } + + token.setType(ARRAY_DESCRIPTOR); + token.setText(str.substring(typeStartIndex, i)); + token.setStopIndex(baseToken.getStartIndex() + i - 1); + break; + } + default: + throw new RuntimeException(String.format("Invalid character '\%c' in param list \"\%s\" at position \%d", str.charAt(typeStartIndex), str, typeStartIndex)); + } + + return token; + } + + private CommonTree parseParamList(CommonToken paramListToken) { + String paramList = paramListToken.getText(); + CommonTree root = new CommonTree(); + + int startIndex = paramListToken.getStartIndex(); + + int i=0; + while (i classAnnotations; + } + @init + { $smali_file::hasClassSpec = $smali_file::hasSuperSpec = $smali_file::hasSourceSpec = false; + $smali_file::classAnnotations = new ArrayList(); + } + : + ( {!$smali_file::hasClassSpec}?=> class_spec {$smali_file::hasClassSpec = true;} + | {!$smali_file::hasSuperSpec}?=> super_spec {$smali_file::hasSuperSpec = true;} + | implements_spec + | {!$smali_file::hasSourceSpec}?=> source_spec {$smali_file::hasSourceSpec = true;} + | method + | field + | annotation {$smali_file::classAnnotations.add($annotation.tree);} + )+ + EOF + { + if (!$smali_file::hasClassSpec) { + throw new SemanticException(input, "The file must contain a .class directive"); + } + + if (!$smali_file::hasSuperSpec) { + if (!$class_spec.className.equals("Ljava/lang/Object;")) { + throw new SemanticException(input, "The file must contain a .super directive"); + } + } + } + -> ^(I_CLASS_DEF + class_spec + super_spec? + implements_spec* + source_spec? + ^(I_METHODS method*) ^(I_FIELDS field*) {buildTree(I_ANNOTATIONS, "I_ANNOTATIONS", $smali_file::classAnnotations)}); + +class_spec returns[String className] + : CLASS_DIRECTIVE access_list CLASS_DESCRIPTOR {$className = $CLASS_DESCRIPTOR.text;} -> CLASS_DESCRIPTOR access_list; + +super_spec + : SUPER_DIRECTIVE CLASS_DESCRIPTOR -> ^(I_SUPER[$start, "I_SUPER"] CLASS_DESCRIPTOR); + +implements_spec + : IMPLEMENTS_DIRECTIVE CLASS_DESCRIPTOR -> ^(I_IMPLEMENTS[$start, "I_IMPLEMENTS"] CLASS_DESCRIPTOR); + +source_spec + : SOURCE_DIRECTIVE STRING_LITERAL -> ^(I_SOURCE[$start, "I_SOURCE"] STRING_LITERAL); + +access_list + : ACCESS_SPEC* -> ^(I_ACCESS_LIST[$start,"I_ACCESS_LIST"] ACCESS_SPEC*); + + +/*When there are annotations immediately after a field definition, we don't know whether they are field annotations +or class annotations until we determine if there is an .end field directive. In either case, we still "consume" and parse +the annotations. If it turns out that they are field annotations, we include them in the I_FIELD AST. Otherwise, we +add them to the $smali_file::classAnnotations list*/ +field + @init {List annotations = new ArrayList();} + : FIELD_DIRECTIVE access_list simple_name COLON nonvoid_type_descriptor (EQUAL literal)? + ( ({input.LA(1) == ANNOTATION_DIRECTIVE}? annotation {annotations.add($annotation.tree);})* + ( END_FIELD_DIRECTIVE + -> ^(I_FIELD[$start, "I_FIELD"] simple_name access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS annotation*)) + | /*epsilon*/ {$smali_file::classAnnotations.addAll(annotations);} + -> ^(I_FIELD[$start, "I_FIELD"] simple_name access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS)) + ) + ); + +method + scope {int currentAddress;} + : {$method::currentAddress = 0;} + METHOD_DIRECTIVE access_list method_name method_prototype statements_and_directives + END_METHOD_DIRECTIVE + -> ^(I_METHOD[$start, "I_METHOD"] method_name method_prototype access_list statements_and_directives); + +statements_and_directives + scope + { + boolean hasRegistersDirective; + List packedSwitchDeclarations; + List sparseSwitchDeclarations; + List methodAnnotations; + } + : { + $method::currentAddress = 0; + $statements_and_directives::hasRegistersDirective = false; + $statements_and_directives::packedSwitchDeclarations = new ArrayList(); + $statements_and_directives::sparseSwitchDeclarations = new ArrayList(); + $statements_and_directives::methodAnnotations = new ArrayList(); + } + ( instruction {$method::currentAddress += $instruction.size/2;} + | registers_directive + | label + | catch_directive + | catchall_directive + | parameter_directive + | ordered_debug_directive + | annotation {$statements_and_directives::methodAnnotations.add($annotation.tree);} + )* + -> registers_directive? + ^(I_LABELS label*) + {buildTree(I_PACKED_SWITCH_DECLARATIONS, "I_PACKED_SWITCH_DECLARATIONS", $statements_and_directives::packedSwitchDeclarations)} + {buildTree(I_SPARSE_SWITCH_DECLARATIONS, "I_SPARSE_SWITCH_DECLARATIONS", $statements_and_directives::sparseSwitchDeclarations)} + ^(I_STATEMENTS instruction*) + ^(I_CATCHES catch_directive* catchall_directive*) + ^(I_PARAMETERS parameter_directive*) + ^(I_ORDERED_DEBUG_DIRECTIVES ordered_debug_directive*) + {buildTree(I_ANNOTATIONS, "I_ANNOTATIONS", $statements_and_directives::methodAnnotations)}; + +registers_directive + : ( + directive=REGISTERS_DIRECTIVE regCount=integral_literal -> ^(I_REGISTERS[$REGISTERS_DIRECTIVE, "I_REGISTERS"] $regCount) + | directive=LOCALS_DIRECTIVE regCount2=integral_literal -> ^(I_LOCALS[$LOCALS_DIRECTIVE, "I_LOCALS"] $regCount2) + ) + { + if ($statements_and_directives::hasRegistersDirective) { + throw new SemanticException(input, $directive, "There can only be a single .registers or .locals directive in a method"); + } + $statements_and_directives::hasRegistersDirective=true; + }; + +/*identifiers are much more general than most languages. Any of the below can either be +the indicated type OR an identifier, depending on the context*/ +simple_name + : SIMPLE_NAME + | ACCESS_SPEC -> SIMPLE_NAME[$ACCESS_SPEC] + | VERIFICATION_ERROR_TYPE -> SIMPLE_NAME[$VERIFICATION_ERROR_TYPE] + | POSITIVE_INTEGER_LITERAL -> SIMPLE_NAME[$POSITIVE_INTEGER_LITERAL] + | NEGATIVE_INTEGER_LITERAL -> SIMPLE_NAME[$NEGATIVE_INTEGER_LITERAL] + | INTEGER_LITERAL -> SIMPLE_NAME[$INTEGER_LITERAL] + | FLOAT_LITERAL_OR_ID -> SIMPLE_NAME[$FLOAT_LITERAL_OR_ID] + | DOUBLE_LITERAL_OR_ID -> SIMPLE_NAME[$DOUBLE_LITERAL_OR_ID] + | BOOL_LITERAL -> SIMPLE_NAME[$BOOL_LITERAL] + | NULL_LITERAL -> SIMPLE_NAME[$NULL_LITERAL] + | REGISTER -> SIMPLE_NAME[$REGISTER] + | PARAM_LIST_OR_ID -> SIMPLE_NAME[$PARAM_LIST_OR_ID] + | PRIMITIVE_TYPE -> SIMPLE_NAME[$PRIMITIVE_TYPE] + | VOID_TYPE -> SIMPLE_NAME[$VOID_TYPE] + | ANNOTATION_VISIBILITY -> SIMPLE_NAME[$ANNOTATION_VISIBILITY] + | INSTRUCTION_FORMAT10t -> SIMPLE_NAME[$INSTRUCTION_FORMAT10t] + | INSTRUCTION_FORMAT10x -> SIMPLE_NAME[$INSTRUCTION_FORMAT10x] + | INSTRUCTION_FORMAT10x_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT10x_ODEX] + | INSTRUCTION_FORMAT11x -> SIMPLE_NAME[$INSTRUCTION_FORMAT11x] + | INSTRUCTION_FORMAT12x_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT12x_OR_ID] + | INSTRUCTION_FORMAT21c_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_FIELD] + | INSTRUCTION_FORMAT21c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_FIELD_ODEX] + | INSTRUCTION_FORMAT21c_STRING -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_STRING] + | INSTRUCTION_FORMAT21c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT21c_TYPE] + | INSTRUCTION_FORMAT21t -> SIMPLE_NAME[$INSTRUCTION_FORMAT21t] + | INSTRUCTION_FORMAT22c_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD] + | INSTRUCTION_FORMAT22c_FIELD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_FIELD_ODEX] + | INSTRUCTION_FORMAT22c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT22c_TYPE] + | INSTRUCTION_FORMAT22cs_FIELD -> SIMPLE_NAME[$INSTRUCTION_FORMAT22cs_FIELD] + | INSTRUCTION_FORMAT22s_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT22s_OR_ID] + | INSTRUCTION_FORMAT22t -> SIMPLE_NAME[$INSTRUCTION_FORMAT22t] + | INSTRUCTION_FORMAT23x -> SIMPLE_NAME[$INSTRUCTION_FORMAT23x] + | INSTRUCTION_FORMAT31i_OR_ID -> SIMPLE_NAME[$INSTRUCTION_FORMAT31i_OR_ID] + | INSTRUCTION_FORMAT31t -> SIMPLE_NAME[$INSTRUCTION_FORMAT31t] + | INSTRUCTION_FORMAT35c_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_METHOD] + | INSTRUCTION_FORMAT35c_METHOD_ODEX -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_METHOD_ODEX] + | INSTRUCTION_FORMAT35c_TYPE -> SIMPLE_NAME[$INSTRUCTION_FORMAT35c_TYPE] + | INSTRUCTION_FORMAT35mi_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35mi_METHOD] + | INSTRUCTION_FORMAT35ms_METHOD -> SIMPLE_NAME[$INSTRUCTION_FORMAT35ms_METHOD] + | INSTRUCTION_FORMAT51l -> SIMPLE_NAME[$INSTRUCTION_FORMAT51l]; + +method_name + : simple_name + | METHOD_NAME -> SIMPLE_NAME[$METHOD_NAME]; + +method_prototype + : OPEN_PAREN param_list CLOSE_PAREN type_descriptor + -> ^(I_METHOD_PROTOTYPE[$start, "I_METHOD_PROTOTYPE"] ^(I_METHOD_RETURN_TYPE type_descriptor) param_list?); + +param_list + : PARAM_LIST -> { parseParamList((CommonToken)$PARAM_LIST) } + | PARAM_LIST_OR_ID -> { parseParamList((CommonToken)$PARAM_LIST_OR_ID) } + | nonvoid_type_descriptor*; + +type_descriptor + : VOID_TYPE + | PRIMITIVE_TYPE + | CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR; + +nonvoid_type_descriptor + : PRIMITIVE_TYPE + | CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR; + +reference_type_descriptor + : CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR; + +integer_literal + : POSITIVE_INTEGER_LITERAL -> INTEGER_LITERAL[$POSITIVE_INTEGER_LITERAL] + | NEGATIVE_INTEGER_LITERAL -> INTEGER_LITERAL[$NEGATIVE_INTEGER_LITERAL] + | INTEGER_LITERAL; + +float_literal + : FLOAT_LITERAL_OR_ID -> FLOAT_LITERAL[$FLOAT_LITERAL_OR_ID] + | FLOAT_LITERAL; + +double_literal + : DOUBLE_LITERAL_OR_ID -> DOUBLE_LITERAL[$DOUBLE_LITERAL_OR_ID] + | DOUBLE_LITERAL; + +literal + : LONG_LITERAL + | integer_literal + | SHORT_LITERAL + | BYTE_LITERAL + | float_literal + | double_literal + | CHAR_LITERAL + | STRING_LITERAL + | BOOL_LITERAL + | NULL_LITERAL + | array_literal + | subannotation + | type_field_method_literal + | enum_literal; + +integral_literal + : LONG_LITERAL + | integer_literal + | SHORT_LITERAL + | CHAR_LITERAL + | BYTE_LITERAL; + +fixed_32bit_literal + : LONG_LITERAL + | integer_literal + | SHORT_LITERAL + | BYTE_LITERAL + | float_literal + | CHAR_LITERAL + | BOOL_LITERAL; + +fixed_literal returns[int size] + : integer_literal {$size = 4;} + | LONG_LITERAL {$size = 8;} + | SHORT_LITERAL {$size = 2;} + | BYTE_LITERAL {$size = 1;} + | float_literal {$size = 4;} + | double_literal {$size = 8;} + | CHAR_LITERAL {$size = 2;} + | BOOL_LITERAL {$size = 1;}; + +array_literal + : OPEN_BRACE (literal (COMMA literal)* | ) CLOSE_BRACE + -> ^(I_ENCODED_ARRAY[$start, "I_ENCODED_ARRAY"] literal*); + +annotation_element + : simple_name EQUAL literal + -> ^(I_ANNOTATION_ELEMENT[$start, "I_ANNOTATION_ELEMENT"] simple_name literal); + +annotation + : ANNOTATION_DIRECTIVE ANNOTATION_VISIBILITY CLASS_DESCRIPTOR + annotation_element* END_ANNOTATION_DIRECTIVE + -> ^(I_ANNOTATION[$start, "I_ANNOTATION"] ANNOTATION_VISIBILITY ^(I_SUBANNOTATION[$start, "I_SUBANNOTATION"] CLASS_DESCRIPTOR annotation_element*)); + +subannotation + : SUBANNOTATION_DIRECTIVE CLASS_DESCRIPTOR annotation_element* END_SUBANNOTATION_DIRECTIVE + -> ^(I_SUBANNOTATION[$start, "I_SUBANNOTATION"] CLASS_DESCRIPTOR annotation_element*); + +enum_literal + : ENUM_DIRECTIVE reference_type_descriptor ARROW simple_name COLON reference_type_descriptor + -> ^(I_ENCODED_ENUM reference_type_descriptor simple_name reference_type_descriptor); + +type_field_method_literal + : reference_type_descriptor + ( ARROW + ( simple_name COLON nonvoid_type_descriptor -> ^(I_ENCODED_FIELD reference_type_descriptor simple_name nonvoid_type_descriptor) + | method_name method_prototype -> ^(I_ENCODED_METHOD reference_type_descriptor method_name method_prototype) + ) + | -> reference_type_descriptor + ) + | PRIMITIVE_TYPE + | VOID_TYPE; + +fully_qualified_method + : reference_type_descriptor ARROW method_name method_prototype + -> reference_type_descriptor method_name method_prototype; + +fully_qualified_field + : reference_type_descriptor ARROW simple_name COLON nonvoid_type_descriptor + -> reference_type_descriptor simple_name nonvoid_type_descriptor; + +label + : COLON simple_name -> ^(I_LABEL[$COLON, "I_LABEL"] simple_name I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +label_ref_or_offset + : COLON simple_name -> simple_name + | OFFSET + | NEGATIVE_INTEGER_LITERAL -> OFFSET[$NEGATIVE_INTEGER_LITERAL]; + +register_list + : REGISTER (COMMA REGISTER)* -> ^(I_REGISTER_LIST[$start, "I_REGISTER_LIST"] REGISTER*) + | ->^(I_REGISTER_LIST[$start, "I_REGISTER_LIST"]); + +register_range + : (startreg=REGISTER (DOTDOT endreg=REGISTER)?)? -> ^(I_REGISTER_RANGE[$start, "I_REGISTER_RANGE"] $startreg? $endreg?); + +verification_error_reference + : CLASS_DESCRIPTOR | fully_qualified_field | fully_qualified_method; + +catch_directive + : CATCH_DIRECTIVE nonvoid_type_descriptor OPEN_BRACE from=label_ref_or_offset DOTDOT to=label_ref_or_offset CLOSE_BRACE using=label_ref_or_offset + -> ^(I_CATCH[$start, "I_CATCH"] I_ADDRESS[$start, Integer.toString($method::currentAddress)] nonvoid_type_descriptor $from $to $using); + +catchall_directive + : CATCHALL_DIRECTIVE OPEN_BRACE from=label_ref_or_offset DOTDOT to=label_ref_or_offset CLOSE_BRACE using=label_ref_or_offset + -> ^(I_CATCHALL[$start, "I_CATCHALL"] I_ADDRESS[$start, Integer.toString($method::currentAddress)] $from $to $using); + +/*When there are annotations immediately after a parameter definition, we don't know whether they are parameter annotations +or method annotations until we determine if there is an .end parameter directive. In either case, we still "consume" and parse +the annotations. If it turns out that they are parameter annotations, we include them in the I_PARAMETER AST. Otherwise, we +add them to the $statements_and_directives::methodAnnotations list*/ +parameter_directive + @init {List annotations = new ArrayList();} + : PARAMETER_DIRECTIVE + STRING_LITERAL? + ({input.LA(1) == ANNOTATION_DIRECTIVE}? annotation {annotations.add($annotation.tree);})* + + ( END_PARAMETER_DIRECTIVE + -> ^(I_PARAMETER[$start, "I_PARAMETER"] STRING_LITERAL? ^(I_ANNOTATIONS annotation*)) + | /*epsilon*/ {$statements_and_directives::methodAnnotations.addAll(annotations);} + -> ^(I_PARAMETER[$start, "I_PARAMETER"] STRING_LITERAL? ^(I_ANNOTATIONS)) + ); + +ordered_debug_directive + : line_directive + | local_directive + | end_local_directive + | restart_local_directive + | prologue_directive + | epilogue_directive + | source_directive; + +line_directive + : LINE_DIRECTIVE integral_literal + -> ^(I_LINE integral_literal I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +local_directive + : LOCAL_DIRECTIVE REGISTER COMMA simple_name COLON nonvoid_type_descriptor (COMMA STRING_LITERAL)? + -> ^(I_LOCAL[$start, "I_LOCAL"] REGISTER simple_name nonvoid_type_descriptor STRING_LITERAL? I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +end_local_directive + : END_LOCAL_DIRECTIVE REGISTER + -> ^(I_END_LOCAL[$start, "I_END_LOCAL"] REGISTER I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +restart_local_directive + : RESTART_LOCAL_DIRECTIVE REGISTER + -> ^(I_RESTART_LOCAL[$start, "I_RESTART_LOCAL"] REGISTER I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +prologue_directive + : PROLOGUE_DIRECTIVE + -> ^(I_PROLOGUE[$start, "I_PROLOGUE"] I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +epilogue_directive + : EPILOGUE_DIRECTIVE + -> ^(I_EPILOGUE[$start, "I_EPILOGUE"] I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +source_directive + : SOURCE_DIRECTIVE STRING_LITERAL + -> ^(I_SOURCE[$start, "I_SOURCE"] STRING_LITERAL I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +instruction_format12x + : INSTRUCTION_FORMAT12x + | INSTRUCTION_FORMAT12x_OR_ID -> INSTRUCTION_FORMAT12x[$INSTRUCTION_FORMAT12x_OR_ID]; + +instruction_format22s + : INSTRUCTION_FORMAT22s + | INSTRUCTION_FORMAT22s_OR_ID -> INSTRUCTION_FORMAT22s[$INSTRUCTION_FORMAT22s_OR_ID]; + +instruction_format31i + : INSTRUCTION_FORMAT31i + | INSTRUCTION_FORMAT31i_OR_ID -> INSTRUCTION_FORMAT31i[$INSTRUCTION_FORMAT31i_OR_ID]; + + + +instruction returns [int size] + : insn_format10t { $size = $insn_format10t.size; } + | insn_format10x { $size = $insn_format10x.size; } + | insn_format10x_odex { $size = $insn_format10x_odex.size; } + | insn_format11n { $size = $insn_format11n.size; } + | insn_format11x { $size = $insn_format11x.size; } + | insn_format12x { $size = $insn_format12x.size; } + | insn_format20bc { $size = $insn_format20bc.size; } + | insn_format20t { $size = $insn_format20t.size; } + | insn_format21c_field { $size = $insn_format21c_field.size; } + | insn_format21c_field_odex { $size = $insn_format21c_field_odex.size; } + | insn_format21c_string { $size = $insn_format21c_string.size; } + | insn_format21c_type { $size = $insn_format21c_type.size; } + | insn_format21h { $size = $insn_format21h.size; } + | insn_format21s { $size = $insn_format21s.size; } + | insn_format21t { $size = $insn_format21t.size; } + | insn_format22b { $size = $insn_format22b.size; } + | insn_format22c_field { $size = $insn_format22c_field.size; } + | insn_format22c_field_odex { $size = $insn_format22c_field_odex.size; } + | insn_format22c_type { $size = $insn_format22c_type.size; } + | insn_format22cs_field { $size = $insn_format22cs_field.size; } + | insn_format22s { $size = $insn_format22s.size; } + | insn_format22t { $size = $insn_format22t.size; } + | insn_format22x { $size = $insn_format22x.size; } + | insn_format23x { $size = $insn_format23x.size; } + | insn_format30t { $size = $insn_format30t.size; } + | insn_format31c { $size = $insn_format31c.size; } + | insn_format31i { $size = $insn_format31i.size; } + | insn_format31t { $size = $insn_format31t.size; } + | insn_format32x { $size = $insn_format32x.size; } + | insn_format35c_method { $size = $insn_format35c_method.size; } + | insn_format35c_type { $size = $insn_format35c_type.size; } + | insn_format35c_method_odex { $size = $insn_format35c_method_odex.size; } + | insn_format35mi_method { $size = $insn_format35mi_method.size; } + | insn_format35ms_method { $size = $insn_format35ms_method.size; } + | insn_format3rc_method { $size = $insn_format3rc_method.size; } + | insn_format3rc_method_odex { $size = $insn_format3rc_method_odex.size; } + | insn_format3rc_type { $size = $insn_format3rc_type.size; } + | insn_format3rmi_method { $size = $insn_format3rmi_method.size; } + | insn_format3rms_method { $size = $insn_format3rms_method.size; } + | insn_format51l { $size = $insn_format51l.size; } + | insn_array_data_directive { $size = $insn_array_data_directive.size; } + | insn_packed_switch_directive { $size = $insn_packed_switch_directive.size; } + | insn_sparse_switch_directive { $size = $insn_sparse_switch_directive.size; }; + +insn_format10t returns [int size] + : //e.g. goto endloop: + //e.g. goto +3 + INSTRUCTION_FORMAT10t label_ref_or_offset {$size = Format.Format10t.size;} + -> ^(I_STATEMENT_FORMAT10t[$start, "I_STATEMENT_FORMAT10t"] INSTRUCTION_FORMAT10t label_ref_or_offset); + +insn_format10x returns [int size] + : //e.g. return-void + INSTRUCTION_FORMAT10x {$size = Format.Format10x.size;} + -> ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x); + +insn_format10x_odex returns [int size] + : //e.g. return-void-barrier + INSTRUCTION_FORMAT10x_ODEX {$size = Format.Format10x.size;} + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT10x_ODEX.text); + }; + +insn_format11n returns [int size] + : //e.g. const/4 v0, 5 + INSTRUCTION_FORMAT11n REGISTER COMMA integral_literal {$size = Format.Format11n.size;} + -> ^(I_STATEMENT_FORMAT11n[$start, "I_STATEMENT_FORMAT11n"] INSTRUCTION_FORMAT11n REGISTER integral_literal); + +insn_format11x returns [int size] + : //e.g. move-result-object v1 + INSTRUCTION_FORMAT11x REGISTER {$size = Format.Format11x.size;} + -> ^(I_STATEMENT_FORMAT11x[$start, "I_STATEMENT_FORMAT11x"] INSTRUCTION_FORMAT11x REGISTER); + +insn_format12x returns [int size] + : //e.g. move v1 v2 + instruction_format12x REGISTER COMMA REGISTER {$size = Format.Format12x.size;} + -> ^(I_STATEMENT_FORMAT12x[$start, "I_STATEMENT_FORMAT12x"] instruction_format12x REGISTER REGISTER); + +insn_format20bc returns [int size] + : //e.g. throw-verification-error generic-error, Lsome/class; + INSTRUCTION_FORMAT20bc VERIFICATION_ERROR_TYPE COMMA verification_error_reference {$size += Format.Format20bc.size;} + { + if (!allowOdex || Opcode.getOpcodeByName($INSTRUCTION_FORMAT20bc.text) == null || apiLevel >= 14) { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT20bc.text); + } + } + -> ^(I_STATEMENT_FORMAT20bc INSTRUCTION_FORMAT20bc VERIFICATION_ERROR_TYPE verification_error_reference); + //TODO: check if dalvik has a jumbo version of throw-verification-error + +insn_format20t returns [int size] + : //e.g. goto/16 endloop: + INSTRUCTION_FORMAT20t label_ref_or_offset {$size = Format.Format20t.size;} + -> ^(I_STATEMENT_FORMAT20t[$start, "I_STATEMENT_FORMAT20t"] INSTRUCTION_FORMAT20t label_ref_or_offset); + +insn_format21c_field returns [int size] + : //e.g. sget-object v0, java/lang/System/out LJava/io/PrintStream; + INSTRUCTION_FORMAT21c_FIELD REGISTER COMMA fully_qualified_field {$size = Format.Format21c.size;} + -> ^(I_STATEMENT_FORMAT21c_FIELD[$start, "I_STATEMENT_FORMAT21c_FIELD"] INSTRUCTION_FORMAT21c_FIELD REGISTER fully_qualified_field); + +insn_format21c_field_odex returns [int size] + : //e.g. sget-object-volatile v0, java/lang/System/out LJava/io/PrintStream; + INSTRUCTION_FORMAT21c_FIELD_ODEX REGISTER COMMA fully_qualified_field {$size = Format.Format21c.size;} + { + if (!allowOdex || Opcode.getOpcodeByName($INSTRUCTION_FORMAT21c_FIELD_ODEX.text) == null || apiLevel >= 14) { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT21c_FIELD_ODEX.text); + } + } + -> ^(I_STATEMENT_FORMAT21c_FIELD[$start, "I_STATEMENT_FORMAT21c_FIELD"] INSTRUCTION_FORMAT21c_FIELD_ODEX REGISTER fully_qualified_field); + +insn_format21c_string returns [int size] + : //e.g. const-string v1, "Hello World!" + INSTRUCTION_FORMAT21c_STRING REGISTER COMMA STRING_LITERAL {$size = Format.Format21c.size;} + -> ^(I_STATEMENT_FORMAT21c_STRING[$start, "I_STATEMENT_FORMAT21c_STRING"] INSTRUCTION_FORMAT21c_STRING REGISTER STRING_LITERAL); + +insn_format21c_type returns [int size] + : //e.g. const-class v2, Lorg/jf/HelloWorld2/HelloWorld2; + INSTRUCTION_FORMAT21c_TYPE REGISTER COMMA reference_type_descriptor {$size = Format.Format21c.size;} + -> ^(I_STATEMENT_FORMAT21c_TYPE[$start, "I_STATEMENT_FORMAT21c"] INSTRUCTION_FORMAT21c_TYPE REGISTER reference_type_descriptor); + +insn_format21h returns [int size] + : //e.g. const/high16 v1, 1234 + INSTRUCTION_FORMAT21h REGISTER COMMA integral_literal {$size = Format.Format21h.size;} + -> ^(I_STATEMENT_FORMAT21h[$start, "I_STATEMENT_FORMAT21h"] INSTRUCTION_FORMAT21h REGISTER integral_literal); + +insn_format21s returns [int size] + : //e.g. const/16 v1, 1234 + INSTRUCTION_FORMAT21s REGISTER COMMA integral_literal {$size = Format.Format21s.size;} + -> ^(I_STATEMENT_FORMAT21s[$start, "I_STATEMENT_FORMAT21s"] INSTRUCTION_FORMAT21s REGISTER integral_literal); + +insn_format21t returns [int size] + : //e.g. if-eqz v0, endloop: + INSTRUCTION_FORMAT21t REGISTER COMMA (label_ref_or_offset) {$size = Format.Format21t.size;} + -> ^(I_STATEMENT_FORMAT21t[$start, "I_STATEMENT_FORMAT21t"] INSTRUCTION_FORMAT21t REGISTER label_ref_or_offset); + +insn_format22b returns [int size] + : //e.g. add-int v0, v1, 123 + INSTRUCTION_FORMAT22b REGISTER COMMA REGISTER COMMA integral_literal {$size = Format.Format22b.size;} + -> ^(I_STATEMENT_FORMAT22b[$start, "I_STATEMENT_FORMAT22b"] INSTRUCTION_FORMAT22b REGISTER REGISTER integral_literal); + +insn_format22c_field returns [int size] + : //e.g. iput-object v1, v0 org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String; + INSTRUCTION_FORMAT22c_FIELD REGISTER COMMA REGISTER COMMA fully_qualified_field {$size = Format.Format22c.size;} + -> ^(I_STATEMENT_FORMAT22c_FIELD[$start, "I_STATEMENT_FORMAT22c_FIELD"] INSTRUCTION_FORMAT22c_FIELD REGISTER REGISTER fully_qualified_field); + +insn_format22c_field_odex returns [int size] + : //e.g. iput-object-volatile v1, v0 org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String; + INSTRUCTION_FORMAT22c_FIELD_ODEX REGISTER COMMA REGISTER COMMA fully_qualified_field {$size = Format.Format22c.size;} + { + if (!allowOdex || Opcode.getOpcodeByName($INSTRUCTION_FORMAT22c_FIELD_ODEX.text) == null || apiLevel >= 14) { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT22c_FIELD_ODEX.text); + } + } + -> ^(I_STATEMENT_FORMAT22c_FIELD[$start, "I_STATEMENT_FORMAT22c_FIELD"] INSTRUCTION_FORMAT22c_FIELD_ODEX REGISTER REGISTER fully_qualified_field); + +insn_format22c_type returns [int size] + : //e.g. instance-of v0, v1, Ljava/lang/String; + INSTRUCTION_FORMAT22c_TYPE REGISTER COMMA REGISTER COMMA nonvoid_type_descriptor {$size = Format.Format22c.size;} + -> ^(I_STATEMENT_FORMAT22c_TYPE[$start, "I_STATEMENT_FORMAT22c_TYPE"] INSTRUCTION_FORMAT22c_TYPE REGISTER REGISTER nonvoid_type_descriptor); + +insn_format22cs_field returns [int size] + : //e.g. iget-quick v0, v1, field@0xc + INSTRUCTION_FORMAT22cs_FIELD REGISTER COMMA REGISTER COMMA FIELD_OFFSET + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT22cs_FIELD.text); + }; + +insn_format22s returns [int size] + : //e.g. add-int/lit16 v0, v1, 12345 + instruction_format22s REGISTER COMMA REGISTER COMMA integral_literal {$size = Format.Format22s.size;} + -> ^(I_STATEMENT_FORMAT22s[$start, "I_STATEMENT_FORMAT22s"] instruction_format22s REGISTER REGISTER integral_literal); + +insn_format22t returns [int size] + : //e.g. if-eq v0, v1, endloop: + INSTRUCTION_FORMAT22t REGISTER COMMA REGISTER COMMA label_ref_or_offset {$size = Format.Format22t.size;} + -> ^(I_STATEMENT_FORMAT22t[$start, "I_STATEMENT_FFORMAT22t"] INSTRUCTION_FORMAT22t REGISTER REGISTER label_ref_or_offset); + +insn_format22x returns [int size] + : //e.g. move/from16 v1, v1234 + INSTRUCTION_FORMAT22x REGISTER COMMA REGISTER {$size = Format.Format22x.size;} + -> ^(I_STATEMENT_FORMAT22x[$start, "I_STATEMENT_FORMAT22x"] INSTRUCTION_FORMAT22x REGISTER REGISTER); + +insn_format23x returns [int size] + : //e.g. add-int v1, v2, v3 + INSTRUCTION_FORMAT23x REGISTER COMMA REGISTER COMMA REGISTER {$size = Format.Format23x.size;} + -> ^(I_STATEMENT_FORMAT23x[$start, "I_STATEMENT_FORMAT23x"] INSTRUCTION_FORMAT23x REGISTER REGISTER REGISTER); + +insn_format30t returns [int size] + : //e.g. goto/32 endloop: + INSTRUCTION_FORMAT30t label_ref_or_offset {$size = Format.Format30t.size;} + -> ^(I_STATEMENT_FORMAT30t[$start, "I_STATEMENT_FORMAT30t"] INSTRUCTION_FORMAT30t label_ref_or_offset); + +insn_format31c returns [int size] + : //e.g. const-string/jumbo v1 "Hello World!" + INSTRUCTION_FORMAT31c REGISTER COMMA STRING_LITERAL {$size = Format.Format31c.size;} + ->^(I_STATEMENT_FORMAT31c[$start, "I_STATEMENT_FORMAT31c"] INSTRUCTION_FORMAT31c REGISTER STRING_LITERAL); + +insn_format31i returns [int size] + : //e.g. const v0, 123456 + instruction_format31i REGISTER COMMA fixed_32bit_literal {$size = Format.Format31i.size;} + -> ^(I_STATEMENT_FORMAT31i[$start, "I_STATEMENT_FORMAT31i"] instruction_format31i REGISTER fixed_32bit_literal); + +insn_format31t returns [int size] + : //e.g. fill-array-data v0, ArrayData: + INSTRUCTION_FORMAT31t REGISTER COMMA label_ref_or_offset {$size = Format.Format31t.size;} + { + if ($INSTRUCTION_FORMAT31t.text.equals("packed-switch")) { + CommonTree root = new CommonTree(new CommonToken(I_PACKED_SWITCH_DECLARATION, "I_PACKED_SWITCH_DECLARATION")); + CommonTree address = new CommonTree(new CommonToken(I_ADDRESS, Integer.toString($method::currentAddress))); + root.addChild(address); + root.addChild($label_ref_or_offset.tree.dupNode()); + $statements_and_directives::packedSwitchDeclarations.add(root); + } else if ($INSTRUCTION_FORMAT31t.text.equals("sparse-switch")) { + CommonTree root = new CommonTree(new CommonToken(I_SPARSE_SWITCH_DECLARATION, "I_SPARSE_SWITCH_DECLARATION")); + CommonTree address = new CommonTree(new CommonToken(I_ADDRESS, Integer.toString($method::currentAddress))); + root.addChild(address); + root.addChild($label_ref_or_offset.tree.dupNode()); + $statements_and_directives::sparseSwitchDeclarations.add(root); + } + } + -> ^(I_STATEMENT_FORMAT31t[$start, "I_STATEMENT_FORMAT31t"] INSTRUCTION_FORMAT31t REGISTER label_ref_or_offset); + +insn_format32x returns [int size] + : //e.g. move/16 v4567, v1234 + INSTRUCTION_FORMAT32x REGISTER COMMA REGISTER {$size = Format.Format32x.size;} + -> ^(I_STATEMENT_FORMAT32x[$start, "I_STATEMENT_FORMAT32x"] INSTRUCTION_FORMAT32x REGISTER REGISTER); + +insn_format35c_method returns [int size] + : //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V + INSTRUCTION_FORMAT35c_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA fully_qualified_method {$size = Format.Format35c.size;} + -> ^(I_STATEMENT_FORMAT35c_METHOD[$start, "I_STATEMENT_FORMAT35c_METHOD"] INSTRUCTION_FORMAT35c_METHOD register_list fully_qualified_method); + +insn_format35c_type returns [int size] + : //e.g. filled-new-array {v0,v1}, I + INSTRUCTION_FORMAT35c_TYPE OPEN_BRACE register_list CLOSE_BRACE COMMA nonvoid_type_descriptor {$size = Format.Format35c.size;} + -> ^(I_STATEMENT_FORMAT35c_TYPE[$start, "I_STATEMENT_FORMAT35c_TYPE"] INSTRUCTION_FORMAT35c_TYPE register_list nonvoid_type_descriptor); + +insn_format35c_method_odex returns [int size] + : //e.g. invoke-direct {p0}, Ljava/lang/Object;->()V + INSTRUCTION_FORMAT35c_METHOD_ODEX OPEN_BRACE register_list CLOSE_BRACE COMMA fully_qualified_method + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT35c_METHOD_ODEX.text); + }; + +insn_format35mi_method returns [int size] + : //e.g. execute-inline {v0, v1}, inline@0x4 + INSTRUCTION_FORMAT35mi_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA INLINE_INDEX + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT35mi_METHOD.text); + }; + +insn_format35ms_method returns [int size] + : //e.g. invoke-virtual-quick {v0, v1}, vtable@0x4 + INSTRUCTION_FORMAT35ms_METHOD OPEN_BRACE register_list CLOSE_BRACE COMMA VTABLE_INDEX + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT35ms_METHOD.text); + }; + +insn_format3rc_method returns [int size] + : //e.g. invoke-virtual/range {v25..v26}, java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder; + INSTRUCTION_FORMAT3rc_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA fully_qualified_method {$size = Format.Format3rc.size;} + -> ^(I_STATEMENT_FORMAT3rc_METHOD[$start, "I_STATEMENT_FORMAT3rc_METHOD"] INSTRUCTION_FORMAT3rc_METHOD register_range fully_qualified_method); + +insn_format3rc_method_odex returns [int size] + : //e.g. invoke-object-init/range {p0}, Ljava/lang/Object;->()V + INSTRUCTION_FORMAT3rc_METHOD_ODEX OPEN_BRACE register_list CLOSE_BRACE COMMA fully_qualified_method + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rc_METHOD_ODEX.text); + }; + +insn_format3rc_type returns [int size] + : //e.g. filled-new-array/range {v0..v6}, I + INSTRUCTION_FORMAT3rc_TYPE OPEN_BRACE register_range CLOSE_BRACE COMMA nonvoid_type_descriptor {$size = Format.Format3rc.size;} + -> ^(I_STATEMENT_FORMAT3rc_TYPE[$start, "I_STATEMENT_FORMAT3rc_TYPE"] INSTRUCTION_FORMAT3rc_TYPE register_range nonvoid_type_descriptor); + +insn_format3rmi_method returns [int size] + : //e.g. execute-inline/range {v0 .. v10}, inline@0x14 + INSTRUCTION_FORMAT3rmi_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA INLINE_INDEX + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rmi_METHOD.text); + }; + +insn_format3rms_method returns [int size] + : //e.g. invoke-virtual-quick/range {v0 .. v10}, vtable@0x14 + INSTRUCTION_FORMAT3rms_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA VTABLE_INDEX + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rms_METHOD.text); + }; + +insn_format51l returns [int size] + : //e.g. const-wide v0, 5000000000L + INSTRUCTION_FORMAT51l REGISTER COMMA fixed_literal {$size = Format.Format51l.size;} + -> ^(I_STATEMENT_FORMAT51l[$start, "I_STATEMENT_FORMAT51l"] INSTRUCTION_FORMAT51l REGISTER fixed_literal); + +insn_array_data_directive returns [int size] + @init {boolean needsNop = false;} + : ARRAY_DATA_DIRECTIVE + { + if (($method::currentAddress \% 2) != 0) { + needsNop = true; + $size = 2; + } else { + $size = 0; + } + } + + integral_literal (fixed_literal {$size+=$fixed_literal.size;})* END_ARRAY_DATA_DIRECTIVE + {$size = (($size + 1)/2)*2 + 8;} + + /*add a nop statement before this if needed to force the correct alignment*/ + -> {needsNop}? ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x[$start, "nop"]) + ^(I_STATEMENT_ARRAY_DATA ^(I_ARRAY_ELEMENT_SIZE integral_literal) ^(I_ARRAY_ELEMENTS fixed_literal*)) + + -> ^(I_STATEMENT_ARRAY_DATA[$start, "I_STATEMENT_ARRAY_DATA"] ^(I_ARRAY_ELEMENT_SIZE integral_literal) + ^(I_ARRAY_ELEMENTS fixed_literal*)); + +insn_packed_switch_directive returns [int size] + @init {boolean needsNop = false; int targetCount = 0;} + : PACKED_SWITCH_DIRECTIVE + { + targetCount = 0; + if (($method::currentAddress \% 2) != 0) { + needsNop = true; + $size = 2; + } else { + $size = 0; + } + } + + fixed_32bit_literal + + (switch_target += label_ref_or_offset {$size+=4; targetCount++;})* + + END_PACKED_SWITCH_DIRECTIVE {$size = $size + 8;} + + /*add a nop statement before this if needed to force the correct alignment*/ + -> {needsNop}? ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x[$start, "nop"]) + ^(I_STATEMENT_PACKED_SWITCH[$start, "I_STATEMENT_PACKED_SWITCH"] + ^(I_PACKED_SWITCH_START_KEY[$start, "I_PACKED_SWITCH_START_KEY"] fixed_32bit_literal) + ^(I_PACKED_SWITCH_TARGETS[$start, "I_PACKED_SWITCH_TARGETS"] I_PACKED_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] $switch_target*) + ) + + -> ^(I_STATEMENT_PACKED_SWITCH[$start, "I_STATEMENT_PACKED_SWITCH"] + ^(I_PACKED_SWITCH_START_KEY[$start, "I_PACKED_SWITCH_START_KEY"] fixed_32bit_literal) + ^(I_PACKED_SWITCH_TARGETS[$start, "I_PACKED_SWITCH_TARGETS"] I_PACKED_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] $switch_target*) + ); + +insn_sparse_switch_directive returns [int size] + @init {boolean needsNop = false; int targetCount = 0;} + : SPARSE_SWITCH_DIRECTIVE + { + targetCount = 0; + if (($method::currentAddress \% 2) != 0) { + needsNop = true; + $size = 2; + } else { + $size = 0; + } + } + + (fixed_32bit_literal ARROW switch_target += label_ref_or_offset {$size += 8; targetCount++;})* + + END_SPARSE_SWITCH_DIRECTIVE {$size = $size + 4;} + + /*add a nop statement before this if needed to force the correct alignment*/ + -> {needsNop}? ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x[$start, "nop"]) + ^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"] + I_SPARSE_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] + ^(I_SPARSE_SWITCH_KEYS[$start, "I_SPARSE_SWITCH_KEYS"] fixed_32bit_literal*) + ^(I_SPARSE_SWITCH_TARGETS $switch_target*)) + -> ^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"] + I_SPARSE_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] + ^(I_SPARSE_SWITCH_KEYS[$start, "I_SPARSE_SWITCH_KEYS"] fixed_32bit_literal*) + ^(I_SPARSE_SWITCH_TARGETS $switch_target*)); \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/main/antlr3/smaliTreeWalker.g b/brut.apktool.smali/smali/src/main/antlr3/smaliTreeWalker.g new file mode 100644 index 00000000..22338594 --- /dev/null +++ b/brut.apktool.smali/smali/src/main/antlr3/smaliTreeWalker.g @@ -0,0 +1,1592 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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. + */ + +tree grammar smaliTreeWalker; + +options { + tokenVocab=smaliParser; + ASTLabelType=CommonTree; +} + +@header { +package org.jf.smali; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.regex.*; +import java.lang.Float; +import java.lang.Double; + +import org.jf.dexlib.*; +import org.jf.dexlib.EncodedValue.*; +import org.jf.dexlib.Util.*; +import org.jf.dexlib.Code.*; +import org.jf.dexlib.Code.Format.*; +} + +@members { + public DexFile dexFile; + public TypeIdItem classType; + + private byte parseRegister_nibble(String register, int totalMethodRegisters, int methodParameterRegisters) + throws SemanticException { + //register should be in the format "v12" + int val = Byte.parseByte(register.substring(1)); + if (register.charAt(0) == 'p') { + val = totalMethodRegisters - methodParameterRegisters + val; + } + if (val >= 2<<4) { + throw new SemanticException(input, "The maximum allowed register in this context is list of registers is v15"); + } + //the parser wouldn't have accepted a negative register, i.e. v-1, so we don't have to check for val<0; + return (byte)val; + } + + //return a short, because java's byte is signed + private short parseRegister_byte(String register, int totalMethodRegisters, int methodParameterRegisters) + throws SemanticException { + //register should be in the format "v123" + int val = Short.parseShort(register.substring(1)); + if (register.charAt(0) == 'p') { + val = totalMethodRegisters - methodParameterRegisters + val; + } + if (val >= 2<<8) { + throw new SemanticException(input, "The maximum allowed register in this context is v255"); + } + return (short)val; + } + + //return an int because java's short is signed + private int parseRegister_short(String register, int totalMethodRegisters, int methodParameterRegisters) + throws SemanticException { + //register should be in the format "v12345" + int val = Integer.parseInt(register.substring(1)); + if (register.charAt(0) == 'p') { + val = totalMethodRegisters - methodParameterRegisters + val; + } + if (val >= 2<<16) { + throw new SemanticException(input, "The maximum allowed register in this context is v65535"); + } + //the parser wouldn't accept a negative register, i.e. v-1, so we don't have to check for val<0; + return val; + } + + private static Pattern specialFloatRegex = Pattern.compile("((-)?infinityf)|(nanf)", Pattern.CASE_INSENSITIVE); + private float parseFloat(String floatString) { + Matcher m = specialFloatRegex.matcher(floatString); + if (m.matches()) { + //got an infinity + if (m.start(1) != -1) { + if (m.start(2) != -1) { + return Float.NEGATIVE_INFINITY; + } else { + return Float.POSITIVE_INFINITY; + } + } else { + return Float.NaN; + } + } + return Float.parseFloat(floatString); + } + + private static Pattern specialDoubleRegex = Pattern.compile("((-)?infinityd?)|(nand?)", Pattern.CASE_INSENSITIVE); + private double parseDouble(String doubleString) { + Matcher m = specialDoubleRegex.matcher(doubleString); + if (m.matches()) { + //got an infinity + if (m.start(1) != -1) { + if (m.start(2) != -1) { + return Double.NEGATIVE_INFINITY; + } else { + return Double.POSITIVE_INFINITY; + } + } else { + return Double.NaN; + } + } + return Double.parseDouble(doubleString); + } + + public String getErrorMessage(RecognitionException e, String[] tokenNames) { + if ( e instanceof SemanticException ) { + return e.getMessage(); + } else { + return super.getErrorMessage(e, tokenNames); + } + } + + public String getErrorHeader(RecognitionException e) { + return getSourceName()+"["+ e.line+","+e.charPositionInLine+"]"; + } +} + + + +smali_file + : ^(I_CLASS_DEF header methods fields annotations) + { + AnnotationDirectoryItem annotationDirectoryItem = null; + ClassDefItem classDefItem = null; + ClassDataItem classDataItem = null; + + if ( $methods.methodAnnotations != null || + $methods.parameterAnnotations != null || + $fields.fieldAnnotations != null || + $annotations.annotationSetItem != null) { + annotationDirectoryItem = AnnotationDirectoryItem.internAnnotationDirectoryItem( + dexFile, + $annotations.annotationSetItem, + $fields.fieldAnnotations, + $methods.methodAnnotations, + $methods.parameterAnnotations); + } + + if ($fields.staticFields.size() != 0 || $fields.instanceFields.size() != 0 || + $methods.directMethods.size() != 0 || $methods.virtualMethods.size()!= 0) { + classDataItem = ClassDataItem.internClassDataItem(dexFile, $fields.staticFields, $fields.instanceFields, + $methods.directMethods, $methods.virtualMethods); + } + + classDefItem = ClassDefItem.internClassDefItem(dexFile, $header.classType, $header.accessFlags, + $header.superType, $header.implementsList, $header.sourceSpec, annotationDirectoryItem, + classDataItem, $fields.staticFieldInitialValues); + }; + catch [Exception ex] { + reportError(new SemanticException(input, ex)); + } + + + +header returns[TypeIdItem classType, int accessFlags, TypeIdItem superType, TypeListItem implementsList, StringIdItem sourceSpec] +: class_spec super_spec? implements_list source_spec + { + classType = $class_spec.type; + $classType = classType; + $accessFlags = $class_spec.accessFlags; + $superType = $super_spec.type; + $implementsList = $implements_list.implementsList; + $sourceSpec = $source_spec.source; + }; + + +class_spec returns[TypeIdItem type, int accessFlags] + : class_type_descriptor access_list + { + $type = $class_type_descriptor.type; + $accessFlags = $access_list.value; + }; + +super_spec returns[TypeIdItem type] + : ^(I_SUPER class_type_descriptor) + { + $type = $class_type_descriptor.type; + }; + + +implements_spec returns[TypeIdItem type] + : ^(I_IMPLEMENTS class_type_descriptor) + { + $type = $class_type_descriptor.type; + }; + +implements_list returns[TypeListItem implementsList] +@init { List typeList; } + : {typeList = new LinkedList();} + (implements_spec {typeList.add($implements_spec.type);} )* + { + if (typeList.size() > 0) { + $implementsList = TypeListItem.internTypeListItem(dexFile, typeList); + } else { + $implementsList = null; + } + }; + +source_spec returns[StringIdItem source] + : {$source = null;} + ^(I_SOURCE string_literal {$source = StringIdItem.internStringIdItem(dexFile, $string_literal.value);}) + | ; + + + +access_list returns [int value] + @init + { + $value = 0; + } + : ^(I_ACCESS_LIST + ( + ACCESS_SPEC + { + $value |= AccessFlags.getAccessFlag($ACCESS_SPEC.getText()).getValue(); + } + )*); + + +fields returns[List staticFields, List instanceFields, + List staticFieldInitialValues, List fieldAnnotations] + @init + { + $staticFields = new LinkedList(); + $instanceFields = new LinkedList(); + $staticFieldInitialValues = new ArrayList(); + } + : ^(I_FIELDS + (field + { + if ($field.encodedField.isStatic()) { + $staticFields.add($field.encodedField); + $staticFieldInitialValues.add(new ClassDefItem.StaticFieldInitializer( + $field.encodedValue, $field.encodedField)); + } else { + $instanceFields.add($field.encodedField); + } + if ($field.fieldAnnotationSet != null) { + if ($fieldAnnotations == null) { + $fieldAnnotations = new LinkedList(); + } + AnnotationDirectoryItem.FieldAnnotation fieldAnnotation = new AnnotationDirectoryItem.FieldAnnotation( + $field.encodedField.field, $field.fieldAnnotationSet); + $fieldAnnotations.add(fieldAnnotation); + } + })*); + +methods returns[List directMethods, + List virtualMethods, + List methodAnnotations, + List parameterAnnotations] + @init + { + $directMethods = new LinkedList(); + $virtualMethods = new LinkedList(); + } + : ^(I_METHODS + (method + { + if ($method.encodedMethod.isDirect()) { + $directMethods.add($method.encodedMethod); + } else { + $virtualMethods.add($method.encodedMethod); + } + if ($method.methodAnnotationSet != null) { + if ($methodAnnotations == null) { + $methodAnnotations = new LinkedList(); + } + AnnotationDirectoryItem.MethodAnnotation methodAnnotation = + new AnnotationDirectoryItem.MethodAnnotation($method.encodedMethod.method, $method.methodAnnotationSet); + $methodAnnotations.add(methodAnnotation); + } + if ($method.parameterAnnotationSets != null) { + if ($parameterAnnotations == null) { + $parameterAnnotations = new LinkedList(); + } + AnnotationDirectoryItem.ParameterAnnotation parameterAnnotation = + new AnnotationDirectoryItem.ParameterAnnotation($method.encodedMethod.method, + $method.parameterAnnotationSets); + $parameterAnnotations.add(parameterAnnotation); + } + })*); + +field returns [ClassDataItem.EncodedField encodedField, EncodedValue encodedValue, AnnotationSetItem fieldAnnotationSet] + :^(I_FIELD SIMPLE_NAME access_list ^(I_FIELD_TYPE nonvoid_type_descriptor) field_initial_value annotations?) + { + StringIdItem memberName = StringIdItem.internStringIdItem(dexFile, $SIMPLE_NAME.text); + TypeIdItem fieldType = $nonvoid_type_descriptor.type; + + FieldIdItem fieldIdItem = FieldIdItem.internFieldIdItem(dexFile, classType, fieldType, memberName); + $encodedField = new ClassDataItem.EncodedField(fieldIdItem, $access_list.value); + + if ($field_initial_value.encodedValue != null) { + if (!$encodedField.isStatic()) { + throw new SemanticException(input, "Initial field values can only be specified for static fields."); + } + + $encodedValue = $field_initial_value.encodedValue; + } else { + $encodedValue = null; + } + + if ($annotations.annotationSetItem != null) { + $fieldAnnotationSet = $annotations.annotationSetItem; + } + }; + + +field_initial_value returns[EncodedValue encodedValue] + : ^(I_FIELD_INITIAL_VALUE literal) {$encodedValue = $literal.encodedValue;} + | ; + +literal returns[EncodedValue encodedValue] + : integer_literal { $encodedValue = new IntEncodedValue($integer_literal.value); } + | long_literal { $encodedValue = new LongEncodedValue($long_literal.value); } + | short_literal { $encodedValue = new ShortEncodedValue($short_literal.value); } + | byte_literal { $encodedValue = new ByteEncodedValue($byte_literal.value); } + | float_literal { $encodedValue = new FloatEncodedValue($float_literal.value); } + | double_literal { $encodedValue = new DoubleEncodedValue($double_literal.value); } + | char_literal { $encodedValue = new CharEncodedValue($char_literal.value); } + | string_literal { $encodedValue = new StringEncodedValue(StringIdItem.internStringIdItem(dexFile, $string_literal.value)); } + | bool_literal { $encodedValue = $bool_literal.value?BooleanEncodedValue.TrueValue:BooleanEncodedValue.FalseValue; } + | NULL_LITERAL { $encodedValue = NullEncodedValue.NullValue; } + | type_descriptor { $encodedValue = new TypeEncodedValue($type_descriptor.type); } + | array_literal { $encodedValue = new ArrayEncodedValue($array_literal.values); } + | subannotation { $encodedValue = new AnnotationEncodedValue($subannotation.annotationType, $subannotation.elementNames, $subannotation.elementValues); } + | field_literal { $encodedValue = new FieldEncodedValue($field_literal.value); } + | method_literal { $encodedValue = new MethodEncodedValue($method_literal.value); } + | enum_literal { $encodedValue = new EnumEncodedValue($enum_literal.value); }; + + +//everything but string +fixed_size_literal returns[byte[\] value] + : integer_literal { $value = LiteralTools.intToBytes($integer_literal.value); } + | long_literal { $value = LiteralTools.longToBytes($long_literal.value); } + | short_literal { $value = LiteralTools.shortToBytes($short_literal.value); } + | byte_literal { $value = new byte[] { $byte_literal.value }; } + | float_literal { $value = LiteralTools.floatToBytes($float_literal.value); } + | double_literal { $value = LiteralTools.doubleToBytes($double_literal.value); } + | char_literal { $value = LiteralTools.charToBytes($char_literal.value); } + | bool_literal { $value = LiteralTools.boolToBytes($bool_literal.value); }; + +//everything but string +fixed_64bit_literal returns[long value] + : integer_literal { $value = $integer_literal.value; } + | long_literal { $value = $long_literal.value; } + | short_literal { $value = $short_literal.value; } + | byte_literal { $value = $byte_literal.value; } + | float_literal { $value = Float.floatToRawIntBits($float_literal.value); } + | double_literal { $value = Double.doubleToRawLongBits($double_literal.value); } + | char_literal { $value = $char_literal.value; } + | bool_literal { $value = $bool_literal.value?1:0; }; + +//everything but string and double +//long is allowed, but it must fit into an int +fixed_32bit_literal returns[int value] + : integer_literal { $value = $integer_literal.value; } + | long_literal { LiteralTools.checkInt($long_literal.value); $value = (int)$long_literal.value; } + | short_literal { $value = $short_literal.value; } + | byte_literal { $value = $byte_literal.value; } + | float_literal { $value = Float.floatToRawIntBits($float_literal.value); } + | char_literal { $value = $char_literal.value; } + | bool_literal { $value = $bool_literal.value?1:0; }; + +array_elements returns[List values] + : {$values = new ArrayList();} + ^(I_ARRAY_ELEMENTS + (fixed_size_literal + { + $values.add($fixed_size_literal.value); + })*); + +packed_switch_target_count returns[int targetCount] + : I_PACKED_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_PACKED_SWITCH_TARGET_COUNT.text);}; + +packed_switch_targets[int baseAddress] returns[int[\] targets] + : + ^(I_PACKED_SWITCH_TARGETS + packed_switch_target_count + { + int targetCount = $packed_switch_target_count.targetCount; + $targets = new int[targetCount]; + int targetsPosition = 0; + } + + (offset_or_label + { + $targets[targetsPosition++] = ($method::currentAddress + $offset_or_label.offsetValue) - $baseAddress; + })* + ); + +sparse_switch_target_count returns[int targetCount] + : I_SPARSE_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_SPARSE_SWITCH_TARGET_COUNT.text);}; + +sparse_switch_keys[int targetCount] returns[int[\] keys] + : { + $keys = new int[$targetCount]; + int keysPosition = 0; + } + ^(I_SPARSE_SWITCH_KEYS + (fixed_32bit_literal + { + $keys[keysPosition++] = $fixed_32bit_literal.value; + })* + ); + + +sparse_switch_targets[int baseAddress, int targetCount] returns[int[\] targets] + : { + $targets = new int[$targetCount]; + int targetsPosition = 0; + } + ^(I_SPARSE_SWITCH_TARGETS + (offset_or_label + { + $targets[targetsPosition++] = ($method::currentAddress + $offset_or_label.offsetValue) - $baseAddress; + })* + ); + +method returns[ ClassDataItem.EncodedMethod encodedMethod, + AnnotationSetItem methodAnnotationSet, + AnnotationSetRefList parameterAnnotationSets] + scope + { + HashMap labels; + TryListBuilder tryList; + int currentAddress; + DebugInfoBuilder debugInfo; + HashMap packedSwitchDeclarations; + HashMap sparseSwitchDeclarations; + } + @init + { + MethodIdItem methodIdItem = null; + int totalMethodRegisters = 0; + int methodParameterRegisters = 0; + int accessFlags = 0; + boolean isStatic = false; + } + : { + $method::labels = new HashMap(); + $method::tryList = new TryListBuilder(); + $method::currentAddress = 0; + $method::debugInfo = new DebugInfoBuilder(); + $method::packedSwitchDeclarations = new HashMap(); + $method::sparseSwitchDeclarations = new HashMap(); + } + ^( I_METHOD + method_name_and_prototype + access_list + { + methodIdItem = $method_name_and_prototype.methodIdItem; + accessFlags = $access_list.value; + isStatic = (accessFlags & AccessFlags.STATIC.getValue()) != 0; + methodParameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); + if (!isStatic) { + methodParameterRegisters++; + } + } + ( registers_directive + { + if ($registers_directive.isLocalsDirective) { + totalMethodRegisters = $registers_directive.registers + methodParameterRegisters; + } else { + totalMethodRegisters = $registers_directive.registers; + } + } + )? + labels + packed_switch_declarations + sparse_switch_declarations + statements[totalMethodRegisters, methodParameterRegisters] + catches + parameters + ordered_debug_directives[totalMethodRegisters, methodParameterRegisters] + annotations + ) + { + Pair, List> temp = $method::tryList.encodeTries(); + List tries = temp.first; + List handlers = temp.second; + + DebugInfoItem debugInfoItem = $method::debugInfo.encodeDebugInfo(dexFile); + + CodeItem codeItem; + + boolean isAbstract = false; + boolean isNative = false; + + if ((accessFlags & AccessFlags.ABSTRACT.getValue()) != 0) { + isAbstract = true; + } else if ((accessFlags & AccessFlags.NATIVE.getValue()) != 0) { + isNative = true; + } + + if ($statements.instructions.size() == 0) { + if (!isAbstract && !isNative) { + throw new SemanticException(input, $I_METHOD, "A non-abstract/non-native method must have at least 1 instruction"); + } + + String methodType; + if (isAbstract) { + methodType = "an abstract"; + } else { + methodType = "a native"; + } + + if ($registers_directive.start != null) { + if ($registers_directive.isLocalsDirective) { + throw new SemanticException(input, $registers_directive.start, "A .locals directive is not valid in \%s method", methodType); + } else { + throw new SemanticException(input, $registers_directive.start, "A .registers directive is not valid in \%s method", methodType); + } + } + + if ($method::labels.size() > 0) { + throw new SemanticException(input, $I_METHOD, "Labels cannot be present in \%s method", methodType); + } + + if ((tries != null && tries.size() > 0) || (handlers != null && handlers.size() > 0)) { + throw new SemanticException(input, $I_METHOD, "try/catch blocks cannot be present in \%s method", methodType); + } + + if (debugInfoItem != null) { + throw new SemanticException(input, $I_METHOD, "debug directives cannot be present in \%s method", methodType); + } + + codeItem = null; + } else { + if (isAbstract) { + throw new SemanticException(input, $I_METHOD, "An abstract method cannot have any instructions"); + } + if (isNative) { + throw new SemanticException(input, $I_METHOD, "A native method cannot have any instructions"); + } + + if ($registers_directive.start == null) { + throw new SemanticException(input, $I_METHOD, "A .registers or .locals directive must be present for a non-abstract/non-final method"); + } + + if (totalMethodRegisters < methodParameterRegisters) { + throw new SemanticException(input, $registers_directive.start, "This method requires at least " + + Integer.toString(methodParameterRegisters) + + " registers, for the method parameters"); + } + + int methodParameterCount = methodIdItem.getPrototype().getParameterRegisterCount(); + if ($method::debugInfo.getParameterNameCount() > methodParameterCount) { + throw new SemanticException(input, $I_METHOD, "Too many parameter names specified. This method only has " + + Integer.toString(methodParameterCount) + + " parameters."); + } + + codeItem = CodeItem.internCodeItem(dexFile, + totalMethodRegisters, + methodParameterRegisters, + $statements.maxOutRegisters, + debugInfoItem, + $statements.instructions, + tries, + handlers); + } + + $encodedMethod = new ClassDataItem.EncodedMethod(methodIdItem, accessFlags, codeItem); + + if ($annotations.annotationSetItem != null) { + $methodAnnotationSet = $annotations.annotationSetItem; + } + + if ($parameters.parameterAnnotations != null) { + $parameterAnnotationSets = $parameters.parameterAnnotations; + } + }; + +method_prototype returns[ProtoIdItem protoIdItem] + : ^(I_METHOD_PROTOTYPE ^(I_METHOD_RETURN_TYPE type_descriptor) field_type_list) + { + TypeIdItem returnType = $type_descriptor.type; + List parameterTypes = $field_type_list.types; + TypeListItem parameterTypeListItem = null; + if (parameterTypes != null && parameterTypes.size() > 0) { + parameterTypeListItem = TypeListItem.internTypeListItem(dexFile, parameterTypes); + } + + $protoIdItem = ProtoIdItem.internProtoIdItem(dexFile, returnType, parameterTypeListItem); + }; + +method_name_and_prototype returns[MethodIdItem methodIdItem] + : SIMPLE_NAME method_prototype + { + String methodNameString = $SIMPLE_NAME.text; + StringIdItem methodName = StringIdItem.internStringIdItem(dexFile, methodNameString); + ProtoIdItem protoIdItem = $method_prototype.protoIdItem; + + $methodIdItem = MethodIdItem.internMethodIdItem(dexFile, classType, protoIdItem, methodName); + }; + +field_type_list returns[List types] + @init + { + $types = new LinkedList(); + } + : ( + nonvoid_type_descriptor + { + $types.add($nonvoid_type_descriptor.type); + } + )*; + + +fully_qualified_method returns[MethodIdItem methodIdItem] + : reference_type_descriptor SIMPLE_NAME method_prototype + { + TypeIdItem classType = $reference_type_descriptor.type; + StringIdItem methodName = StringIdItem.internStringIdItem(dexFile, $SIMPLE_NAME.text); + ProtoIdItem prototype = $method_prototype.protoIdItem; + $methodIdItem = MethodIdItem.internMethodIdItem(dexFile, classType, prototype, methodName); + }; + +fully_qualified_field returns[FieldIdItem fieldIdItem] + : reference_type_descriptor SIMPLE_NAME nonvoid_type_descriptor + { + TypeIdItem classType = $reference_type_descriptor.type; + StringIdItem fieldName = StringIdItem.internStringIdItem(dexFile, $SIMPLE_NAME.text); + TypeIdItem fieldType = $nonvoid_type_descriptor.type; + $fieldIdItem = FieldIdItem.internFieldIdItem(dexFile, classType, fieldType, fieldName); + }; + +registers_directive returns[boolean isLocalsDirective, int registers] + : {$registers = 0;} + ^( ( I_REGISTERS {$isLocalsDirective = false;} + | I_LOCALS {$isLocalsDirective = true;} + ) + short_integral_literal {$registers = $short_integral_literal.value;} + ); + +labels + : ^(I_LABELS label_def*); + +label_def + : ^(I_LABEL SIMPLE_NAME address) + { + if ($method::labels.containsKey($SIMPLE_NAME.text)) { + throw new SemanticException(input, $I_LABEL, "Label " + $SIMPLE_NAME.text + " has multiple defintions."); + } + + + $method::labels.put($SIMPLE_NAME.text, $address.address); + }; + +packed_switch_declarations + : ^(I_PACKED_SWITCH_DECLARATIONS packed_switch_declaration*); +packed_switch_declaration + : ^(I_PACKED_SWITCH_DECLARATION address offset_or_label_absolute[$address.address]) + { + int switchDataAddress = $offset_or_label_absolute.address; + if ((switchDataAddress \% 2) != 0) { + switchDataAddress++; + } + if (!$method::packedSwitchDeclarations.containsKey(switchDataAddress)) { + $method::packedSwitchDeclarations.put(switchDataAddress, $address.address); + } + }; + +sparse_switch_declarations + : ^(I_SPARSE_SWITCH_DECLARATIONS sparse_switch_declaration*); +sparse_switch_declaration + : ^(I_SPARSE_SWITCH_DECLARATION address offset_or_label_absolute[$address.address]) + { + int switchDataAddress = $offset_or_label_absolute.address; + if ((switchDataAddress \% 2) != 0) { + switchDataAddress++; + } + if (!$method::sparseSwitchDeclarations.containsKey(switchDataAddress)) { + $method::sparseSwitchDeclarations.put(switchDataAddress, $address.address); + } + + }; + +catches : ^(I_CATCHES catch_directive* catchall_directive*); + +catch_directive + : ^(I_CATCH address nonvoid_type_descriptor from=offset_or_label_absolute[$address.address] to=offset_or_label_absolute[$address.address] + using=offset_or_label_absolute[$address.address]) + { + TypeIdItem type = $nonvoid_type_descriptor.type; + int startAddress = $from.address; + int endAddress = $to.address; + int handlerAddress = $using.address; + + $method::tryList.addHandler(type, startAddress, endAddress, handlerAddress); + }; + +catchall_directive + : ^(I_CATCHALL address from=offset_or_label_absolute[$address.address] to=offset_or_label_absolute[$address.address] + using=offset_or_label_absolute[$address.address]) + { + int startAddress = $from.address; + int endAddress = $to.address; + int handlerAddress = $using.address; + + $method::tryList.addCatchAllHandler(startAddress, endAddress, handlerAddress); + }; + +address returns[int address] + : I_ADDRESS + { + $address = Integer.parseInt($I_ADDRESS.text); + }; + +parameters returns[AnnotationSetRefList parameterAnnotations] + @init + { + int parameterCount = 0; + List annotationSetItems = new ArrayList(); + } + : ^(I_PARAMETERS (parameter + { + if ($parameter.parameterAnnotationSet != null) { + while (annotationSetItems.size() < parameterCount) { + annotationSetItems.add(AnnotationSetItem.internAnnotationSetItem(dexFile, null)); + } + annotationSetItems.add($parameter.parameterAnnotationSet); + } + + parameterCount++; + })* + ) + { + if (annotationSetItems.size() > 0) { + while (annotationSetItems.size() < parameterCount) { + annotationSetItems.add(AnnotationSetItem.internAnnotationSetItem(dexFile, null)); + } + $parameterAnnotations = AnnotationSetRefList.internAnnotationSetRefList(dexFile, annotationSetItems); + } + }; + +parameter returns[AnnotationSetItem parameterAnnotationSet] + : ^(I_PARAMETER ( string_literal {$method::debugInfo.addParameterName($string_literal.value);} + | {$method::debugInfo.addParameterName(null);} + ) + annotations {$parameterAnnotationSet = $annotations.annotationSetItem;} + ); + +ordered_debug_directives[int totalMethodRegisters, int methodParameterRegisters] + : ^(I_ORDERED_DEBUG_DIRECTIVES ( line + | local[$totalMethodRegisters, $methodParameterRegisters] + | end_local[$totalMethodRegisters, $methodParameterRegisters] + | restart_local[$totalMethodRegisters, $methodParameterRegisters] + | prologue + | epilogue + | source + )*); + +line + : ^(I_LINE integral_literal address) + { + $method::debugInfo.addLine($address.address, $integral_literal.value); + }; + +local[int totalMethodRegisters, int methodParameterRegisters] + : ^(I_LOCAL REGISTER SIMPLE_NAME nonvoid_type_descriptor string_literal? address) + { + int registerNumber = parseRegister_short($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + if ($string_literal.value != null) { + $method::debugInfo.addLocalExtended($address.address, registerNumber, $SIMPLE_NAME.text, $nonvoid_type_descriptor.type.getTypeDescriptor(), $string_literal.value); + } else { + $method::debugInfo.addLocal($address.address, registerNumber, $SIMPLE_NAME.text, $nonvoid_type_descriptor.type.getTypeDescriptor()); + } + }; + +end_local[int totalMethodRegisters, int methodParameterRegisters] + : ^(I_END_LOCAL REGISTER address) + { + int registerNumber = parseRegister_short($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + $method::debugInfo.addEndLocal($address.address, registerNumber); + }; + +restart_local[int totalMethodRegisters, int methodParameterRegisters] + : ^(I_RESTART_LOCAL REGISTER address) + { + int registerNumber = parseRegister_short($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + $method::debugInfo.addRestartLocal($address.address, registerNumber); + }; + +prologue + : ^(I_PROLOGUE address) + { + $method::debugInfo.addPrologue($address.address); + }; + +epilogue + : ^(I_EPILOGUE address) + { + $method::debugInfo.addEpilogue($address.address); + }; + +source + : ^(I_SOURCE string_literal address) + { + $method::debugInfo.addSetFile($address.address, $string_literal.value); + }; + +statements[int totalMethodRegisters, int methodParameterRegisters] returns[List instructions, int maxOutRegisters] + @init + { + $instructions = new LinkedList(); + $maxOutRegisters = 0; + } + : ^(I_STATEMENTS (instruction[$totalMethodRegisters, $methodParameterRegisters, $instructions] + { + $method::currentAddress += $instructions.get($instructions.size() - 1).getSize($method::currentAddress); + if ($maxOutRegisters < $instruction.outRegisters) { + $maxOutRegisters = $instruction.outRegisters; + } + })*); + +label_ref returns[int labelAddress] + : SIMPLE_NAME + { + Integer labelAdd = $method::labels.get($SIMPLE_NAME.text); + + if (labelAdd == null) { + throw new SemanticException(input, $SIMPLE_NAME, "Label \"" + $SIMPLE_NAME.text + "\" is not defined."); + } + + $labelAddress = labelAdd; + }; + +offset returns[int offsetValue] + : OFFSET + { + String offsetText = $OFFSET.text; + if (offsetText.charAt(0) == '+') { + offsetText = offsetText.substring(1); + } + $offsetValue = LiteralTools.parseInt(offsetText); + }; + +offset_or_label_absolute[int baseAddress] returns[int address] + : offset {$address = $offset.offsetValue + $baseAddress;} + | label_ref {$address = $label_ref.labelAddress;}; + +offset_or_label returns[int offsetValue] + : offset {$offsetValue = $offset.offsetValue;} + | label_ref {$offsetValue = $label_ref.labelAddress-$method::currentAddress;}; + + +register_list[int totalMethodRegisters, int methodParameterRegisters] returns[byte[\] registers, byte registerCount] + @init + { + $registers = new byte[5]; + $registerCount = 0; + } + : ^(I_REGISTER_LIST + (REGISTER + { + if ($registerCount == 5) { + throw new SemanticException(input, $I_REGISTER_LIST, "A list of registers can only have a maximum of 5 registers. Use the /range alternate opcode instead."); + } + $registers[$registerCount++] = parseRegister_nibble($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + })*); + +register_range[int totalMethodRegisters, int methodParameterRegisters] returns[int startRegister, int endRegister] + : ^(I_REGISTER_RANGE (startReg=REGISTER endReg=REGISTER?)?) + { + if ($startReg == null) { + $startRegister = 0; + $endRegister = -1; + } else { + $startRegister = parseRegister_short($startReg.text, $totalMethodRegisters, $methodParameterRegisters); + if ($endReg == null) { + $endRegister = $startRegister; + } else { + $endRegister = parseRegister_short($endReg.text, $totalMethodRegisters, $methodParameterRegisters); + } + + int registerCount = $endRegister-$startRegister+1; + if (registerCount < 1) { + throw new SemanticException(input, $I_REGISTER_RANGE, "A register range must have the lower register listed first"); + } + } + } + ; + +verification_error_reference returns[Item item] + : CLASS_DESCRIPTOR + { + $item = TypeIdItem.internTypeIdItem(dexFile, $start.getText()); + } + | fully_qualified_field + { + $item = $fully_qualified_field.fieldIdItem; + } + | fully_qualified_method + { + $item = $fully_qualified_method.methodIdItem; + }; + +verification_error_type returns[VerificationErrorType verificationErrorType] + : VERIFICATION_ERROR_TYPE + { + $verificationErrorType = VerificationErrorType.fromString($VERIFICATION_ERROR_TYPE.text); + }; + +instruction[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : insn_format10t[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format10t.outRegisters; } + | insn_format10x[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format10x.outRegisters; } + | insn_format11n[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format11n.outRegisters; } + | insn_format11x[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format11x.outRegisters; } + | insn_format12x[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format12x.outRegisters; } + | insn_format20bc[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format20bc.outRegisters; } + | insn_format20t[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format20t.outRegisters; } + | insn_format21c_field[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format21c_field.outRegisters; } + | insn_format21c_string[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format21c_string.outRegisters; } + | insn_format21c_type[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format21c_type.outRegisters; } + | insn_format21h[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format21h.outRegisters; } + | insn_format21s[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format21s.outRegisters; } + | insn_format21t[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format21t.outRegisters; } + | insn_format22b[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format22b.outRegisters; } + | insn_format22c_field[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format22c_field.outRegisters; } + | insn_format22c_type[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format22c_type.outRegisters; } + | insn_format22s[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format22s.outRegisters; } + | insn_format22t[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format22t.outRegisters; } + | insn_format22x[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format22x.outRegisters; } + | insn_format23x[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format23x.outRegisters; } + | insn_format30t[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format30t.outRegisters; } + | insn_format31c[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format31c.outRegisters; } + | insn_format31i[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format31i.outRegisters; } + | insn_format31t[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format31t.outRegisters; } + | insn_format32x[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format32x.outRegisters; } + | insn_format35c_method[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format35c_method.outRegisters; } + | insn_format35c_type[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format35c_type.outRegisters; } + | insn_format3rc_method[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format3rc_method.outRegisters; } + | insn_format3rc_type[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format3rc_type.outRegisters; } + | insn_format51l_type[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_format51l_type.outRegisters; } + | insn_array_data_directive[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_array_data_directive.outRegisters; } + | insn_packed_switch_directive[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_packed_switch_directive.outRegisters; } + | insn_sparse_switch_directive[$totalMethodRegisters, $methodParameterRegisters, $instructions] { $outRegisters = $insn_sparse_switch_directive.outRegisters; }; + catch [Exception ex] { + reportError(new SemanticException(input, ex)); + recover(input, null); + } + + +insn_format10t[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. goto endloop: + {$outRegisters = 0;} + ^(I_STATEMENT_FORMAT10t INSTRUCTION_FORMAT10t offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT10t.text); + + int addressOffset = $offset_or_label.offsetValue; + + $instructions.add(new Instruction10t(opcode, addressOffset)); + }; + +insn_format10x[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. return + ^(I_STATEMENT_FORMAT10x INSTRUCTION_FORMAT10x) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT10x.text); + $instructions.add(new Instruction10x(opcode)); + }; + +insn_format11n[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. const/4 v0, 5 + ^(I_STATEMENT_FORMAT11n INSTRUCTION_FORMAT11n REGISTER short_integral_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT11n.text); + byte regA = parseRegister_nibble($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + short litB = $short_integral_literal.value; + LiteralTools.checkNibble(litB); + + $instructions.add(new Instruction11n(opcode, regA, (byte)litB)); + }; + +insn_format11x[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. move-result-object v1 + ^(I_STATEMENT_FORMAT11x INSTRUCTION_FORMAT11x REGISTER) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT11x.text); + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + $instructions.add(new Instruction11x(opcode, regA)); + }; + +insn_format12x[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. move v1 v2 + ^(I_STATEMENT_FORMAT12x INSTRUCTION_FORMAT12x registerA=REGISTER registerB=REGISTER) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT12x.text); + byte regA = parseRegister_nibble($registerA.text, $totalMethodRegisters, $methodParameterRegisters); + byte regB = parseRegister_nibble($registerB.text, $totalMethodRegisters, $methodParameterRegisters); + + $instructions.add(new Instruction12x(opcode, regA, regB)); + }; + +insn_format20bc[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. throw-verification-error generic-error, Lsome/class; + ^(I_STATEMENT_FORMAT20bc INSTRUCTION_FORMAT20bc verification_error_type verification_error_reference) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT20bc.text); + + VerificationErrorType verificationErrorType = $verification_error_type.verificationErrorType; + Item referencedItem = $verification_error_reference.item; + + $instructions.add(new Instruction20bc(opcode, verificationErrorType, referencedItem)); + }; + +insn_format20t[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. goto/16 endloop: + ^(I_STATEMENT_FORMAT20t INSTRUCTION_FORMAT20t offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT20t.text); + + int addressOffset = $offset_or_label.offsetValue; + + $instructions.add(new Instruction20t(opcode, addressOffset)); + }; + +insn_format21c_field[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. sget_object v0, java/lang/System/out LJava/io/PrintStream; + ^(I_STATEMENT_FORMAT21c_FIELD inst=(INSTRUCTION_FORMAT21c_FIELD | INSTRUCTION_FORMAT21c_FIELD_ODEX) REGISTER fully_qualified_field) + { + Opcode opcode = Opcode.getOpcodeByName($inst.text); + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + FieldIdItem fieldIdItem = $fully_qualified_field.fieldIdItem; + + $instructions.add(new Instruction21c(opcode, regA, fieldIdItem)); + }; + +insn_format21c_string[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. const-string v1, "Hello World!" + ^(I_STATEMENT_FORMAT21c_STRING INSTRUCTION_FORMAT21c_STRING REGISTER string_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21c_STRING.text); + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + StringIdItem stringIdItem = StringIdItem.internStringIdItem(dexFile, $string_literal.value); + + instructions.add(new Instruction21c(opcode, regA, stringIdItem)); + }; + +insn_format21c_type[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. const-class v2, org/jf/HelloWorld2/HelloWorld2 + ^(I_STATEMENT_FORMAT21c_TYPE INSTRUCTION_FORMAT21c_TYPE REGISTER reference_type_descriptor) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21c_TYPE.text); + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + TypeIdItem typeIdItem = $reference_type_descriptor.type; + + $instructions.add(new Instruction21c(opcode, regA, typeIdItem)); + }; + +insn_format21h[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. const/high16 v1, 1234 + ^(I_STATEMENT_FORMAT21h INSTRUCTION_FORMAT21h REGISTER short_integral_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21h.text); + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + short litB = $short_integral_literal.value; + + instructions.add(new Instruction21h(opcode, regA, litB)); + }; + +insn_format21s[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. const/16 v1, 1234 + ^(I_STATEMENT_FORMAT21s INSTRUCTION_FORMAT21s REGISTER short_integral_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21s.text); + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + short litB = $short_integral_literal.value; + + $instructions.add(new Instruction21s(opcode, regA, litB)); + }; + +insn_format21t[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. if-eqz v0, endloop: + ^(I_STATEMENT_FORMAT21t INSTRUCTION_FORMAT21t REGISTER offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21t.text); + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + int addressOffset = $offset_or_label.offsetValue; + + if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) { + throw new SemanticException(input, $offset_or_label.start, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); + } + + $instructions.add(new Instruction21t(opcode, regA, (short)addressOffset)); + }; + +insn_format22b[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. add-int v0, v1, 123 + ^(I_STATEMENT_FORMAT22b INSTRUCTION_FORMAT22b registerA=REGISTER registerB=REGISTER short_integral_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22b.text); + short regA = parseRegister_byte($registerA.text, $totalMethodRegisters, $methodParameterRegisters); + short regB = parseRegister_byte($registerB.text, $totalMethodRegisters, $methodParameterRegisters); + + short litC = $short_integral_literal.value; + LiteralTools.checkByte(litC); + + $instructions.add(new Instruction22b(opcode, regA, regB, (byte)litC)); + }; + +insn_format22c_field[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. iput-object v1, v0, org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String; + ^(I_STATEMENT_FORMAT22c_FIELD inst=(INSTRUCTION_FORMAT22c_FIELD | INSTRUCTION_FORMAT22c_FIELD_ODEX) registerA=REGISTER registerB=REGISTER fully_qualified_field) + { + Opcode opcode = Opcode.getOpcodeByName($inst.text); + byte regA = parseRegister_nibble($registerA.text, $totalMethodRegisters, $methodParameterRegisters); + byte regB = parseRegister_nibble($registerB.text, $totalMethodRegisters, $methodParameterRegisters); + + FieldIdItem fieldIdItem = $fully_qualified_field.fieldIdItem; + + $instructions.add(new Instruction22c(opcode, regA, regB, fieldIdItem)); + }; + +insn_format22c_type[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. instance-of v0, v1, Ljava/lang/String; + ^(I_STATEMENT_FORMAT22c_TYPE INSTRUCTION_FORMAT22c_TYPE registerA=REGISTER registerB=REGISTER nonvoid_type_descriptor) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22c_TYPE.text); + byte regA = parseRegister_nibble($registerA.text, $totalMethodRegisters, $methodParameterRegisters); + byte regB = parseRegister_nibble($registerB.text, $totalMethodRegisters, $methodParameterRegisters); + + TypeIdItem typeIdItem = $nonvoid_type_descriptor.type; + + $instructions.add(new Instruction22c(opcode, regA, regB, typeIdItem)); + }; + +insn_format22s[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. add-int/lit16 v0, v1, 12345 + ^(I_STATEMENT_FORMAT22s INSTRUCTION_FORMAT22s registerA=REGISTER registerB=REGISTER short_integral_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22s.text); + byte regA = parseRegister_nibble($registerA.text, $totalMethodRegisters, $methodParameterRegisters); + byte regB = parseRegister_nibble($registerB.text, $totalMethodRegisters, $methodParameterRegisters); + + short litC = $short_integral_literal.value; + + $instructions.add(new Instruction22s(opcode, regA, regB, litC)); + }; + +insn_format22t[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. if-eq v0, v1, endloop: + ^(I_STATEMENT_FORMAT22t INSTRUCTION_FORMAT22t registerA=REGISTER registerB=REGISTER offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22t.text); + byte regA = parseRegister_nibble($registerA.text, $totalMethodRegisters, $methodParameterRegisters); + byte regB = parseRegister_nibble($registerB.text, $totalMethodRegisters, $methodParameterRegisters); + + int addressOffset = $offset_or_label.offsetValue; + + if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) { + throw new SemanticException(input, $offset_or_label.start, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); + } + + $instructions.add(new Instruction22t(opcode, regA, regB, (short)addressOffset)); + }; + +insn_format22x[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. move/from16 v1, v1234 + ^(I_STATEMENT_FORMAT22x INSTRUCTION_FORMAT22x registerA=REGISTER registerB=REGISTER) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22x.text); + short regA = parseRegister_byte($registerA.text, $totalMethodRegisters, $methodParameterRegisters); + int regB = parseRegister_short($registerB.text, $totalMethodRegisters, $methodParameterRegisters); + + $instructions.add(new Instruction22x(opcode, regA, regB)); + }; + +insn_format23x[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. add-int v1, v2, v3 + ^(I_STATEMENT_FORMAT23x INSTRUCTION_FORMAT23x registerA=REGISTER registerB=REGISTER registerC=REGISTER) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT23x.text); + short regA = parseRegister_byte($registerA.text, $totalMethodRegisters, $methodParameterRegisters); + short regB = parseRegister_byte($registerB.text, $totalMethodRegisters, $methodParameterRegisters); + short regC = parseRegister_byte($registerC.text, $totalMethodRegisters, $methodParameterRegisters); + + $instructions.add(new Instruction23x(opcode, regA, regB, regC)); + }; + +insn_format30t[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. goto/32 endloop: + ^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT30t.text); + + int addressOffset = $offset_or_label.offsetValue; + + $instructions.add(new Instruction30t(opcode, addressOffset)); + }; + +insn_format31c[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. const-string/jumbo v1 "Hello World!" + ^(I_STATEMENT_FORMAT31c INSTRUCTION_FORMAT31c REGISTER string_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT31c.text); + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + StringIdItem stringIdItem = StringIdItem.internStringIdItem(dexFile, $string_literal.value); + + $instructions.add(new Instruction31c(opcode, regA, stringIdItem)); + }; + +insn_format31i[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. const v0, 123456 + ^(I_STATEMENT_FORMAT31i INSTRUCTION_FORMAT31i REGISTER fixed_32bit_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT31i.text); + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + int litB = $fixed_32bit_literal.value; + + $instructions.add(new Instruction31i(opcode, regA, litB)); + }; + +insn_format31t[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. fill-array-data v0, ArrayData: + ^(I_STATEMENT_FORMAT31t INSTRUCTION_FORMAT31t REGISTER offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT31t.text); + + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + int addressOffset = $offset_or_label.offsetValue; + if (($method::currentAddress + addressOffset) \% 2 != 0) { + addressOffset++; + } + + $instructions.add(new Instruction31t(opcode, regA, addressOffset)); + }; + +insn_format32x[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. move/16 v5678, v1234 + ^(I_STATEMENT_FORMAT32x INSTRUCTION_FORMAT32x registerA=REGISTER registerB=REGISTER) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT32x.text); + int regA = parseRegister_short($registerA.text, $totalMethodRegisters, $methodParameterRegisters); + int regB = parseRegister_short($registerB.text, $totalMethodRegisters, $methodParameterRegisters); + + $instructions.add(new Instruction32x(opcode, regA, regB)); + }; + +insn_format35c_method[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V + ^(I_STATEMENT_FORMAT35c_METHOD INSTRUCTION_FORMAT35c_METHOD register_list[$totalMethodRegisters, $methodParameterRegisters] fully_qualified_method) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT35c_METHOD.text); + + //this depends on the fact that register_list returns a byte[5] + byte[] registers = $register_list.registers; + byte registerCount = $register_list.registerCount; + $outRegisters = registerCount; + + MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem; + + $instructions.add(new Instruction35c(opcode, registerCount, registers[0], registers[1], registers[2], registers[3], registers[4], methodIdItem)); + }; + +insn_format35c_type[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. filled-new-array {v0,v1}, I + ^(I_STATEMENT_FORMAT35c_TYPE INSTRUCTION_FORMAT35c_TYPE register_list[$totalMethodRegisters, $methodParameterRegisters] nonvoid_type_descriptor) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT35c_TYPE.text); + + //this depends on the fact that register_list returns a byte[5] + byte[] registers = $register_list.registers; + byte registerCount = $register_list.registerCount; + $outRegisters = registerCount; + + TypeIdItem typeIdItem = $nonvoid_type_descriptor.type; + + $instructions.add(new Instruction35c(opcode, registerCount, registers[0], registers[1], registers[2], registers[3], registers[4], typeIdItem)); + }; + +insn_format3rc_method[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. invoke-virtual/range {v25..v26} java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder; + ^(I_STATEMENT_FORMAT3rc_METHOD INSTRUCTION_FORMAT3rc_METHOD register_range[$totalMethodRegisters, $methodParameterRegisters] fully_qualified_method) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT3rc_METHOD.text); + int startRegister = $register_range.startRegister; + int endRegister = $register_range.endRegister; + + int registerCount = endRegister-startRegister+1; + $outRegisters = registerCount; + + MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem; + + $instructions.add(new Instruction3rc(opcode, (short)registerCount, startRegister, methodIdItem)); + }; + +insn_format3rc_type[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. filled-new-array/range {v0..v6} I + ^(I_STATEMENT_FORMAT3rc_TYPE INSTRUCTION_FORMAT3rc_TYPE register_range[$totalMethodRegisters, $methodParameterRegisters] nonvoid_type_descriptor) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT3rc_TYPE.text); + int startRegister = $register_range.startRegister; + int endRegister = $register_range.endRegister; + + int registerCount = endRegister-startRegister+1; + $outRegisters = registerCount; + + TypeIdItem typeIdItem = $nonvoid_type_descriptor.type; + + $instructions.add(new Instruction3rc(opcode, (short)registerCount, startRegister, typeIdItem)); + }; + +insn_format51l_type[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. const-wide v0, 5000000000L + ^(I_STATEMENT_FORMAT51l INSTRUCTION_FORMAT51l REGISTER fixed_64bit_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT51l.text); + short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters); + + long litB = $fixed_64bit_literal.value; + + $instructions.add(new Instruction51l(opcode, regA, litB)); + }; + +insn_array_data_directive[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : //e.g. .array-data 4 1000000 .end array-data + ^(I_STATEMENT_ARRAY_DATA ^(I_ARRAY_ELEMENT_SIZE short_integral_literal) array_elements) + { + if (($method::currentAddress \% 2) != 0) { + $instructions.add(new Instruction10x(Opcode.NOP)); + $method::currentAddress++; + } + + int elementWidth = $short_integral_literal.value; + List byteValues = $array_elements.values; + + int length = 0; + for (byte[] byteValue: byteValues) { + length+=byteValue.length; + } + + byte[] encodedValues = new byte[length]; + int index = 0; + for (byte[] byteValue: byteValues) { + System.arraycopy(byteValue, 0, encodedValues, index, byteValue.length); + index+=byteValue.length; + } + + $instructions.add(new ArrayDataPseudoInstruction(elementWidth, encodedValues)); + }; + +insn_packed_switch_directive[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : + ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) + { + if (($method::currentAddress \% 2) != 0) { + $instructions.add(new Instruction10x(Opcode.NOP)); + $method::currentAddress++; + } + Integer baseAddress = $method::packedSwitchDeclarations.get($method::currentAddress); + if (baseAddress == null) { + baseAddress = 0; + } + } + packed_switch_targets[baseAddress]) + { + + int startKey = $fixed_32bit_literal.value; + int[] targets = $packed_switch_targets.targets; + + $instructions.add(new PackedSwitchDataPseudoInstruction(startKey, targets)); + }; + +insn_sparse_switch_directive[int totalMethodRegisters, int methodParameterRegisters, List instructions] returns[int outRegisters] + : + ^(I_STATEMENT_SPARSE_SWITCH sparse_switch_target_count sparse_switch_keys[$sparse_switch_target_count.targetCount] + { + if (($method::currentAddress \% 2) != 0) { + $instructions.add(new Instruction10x(Opcode.NOP)); + $method::currentAddress++; + } + Integer baseAddress = $method::sparseSwitchDeclarations.get($method::currentAddress); + if (baseAddress == null) { + baseAddress = 0; + } + } + + sparse_switch_targets[baseAddress, $sparse_switch_target_count.targetCount]) + { + int[] keys = $sparse_switch_keys.keys; + int[] targets = $sparse_switch_targets.targets; + + $instructions.add(new SparseSwitchDataPseudoInstruction(keys, targets)); + }; + +nonvoid_type_descriptor returns [TypeIdItem type] + : (PRIMITIVE_TYPE + | CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR) + { + $type = TypeIdItem.internTypeIdItem(dexFile, $start.getText()); + }; + + +reference_type_descriptor returns [TypeIdItem type] + : (CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR) + { + $type = TypeIdItem.internTypeIdItem(dexFile, $start.getText()); + }; + + + + + + +class_type_descriptor returns [TypeIdItem type] + : CLASS_DESCRIPTOR + { + $type = TypeIdItem.internTypeIdItem(dexFile, $CLASS_DESCRIPTOR.text); + }; + +type_descriptor returns [TypeIdItem type] + : VOID_TYPE {$type = TypeIdItem.internTypeIdItem(dexFile, "V");} + | nonvoid_type_descriptor {$type = $nonvoid_type_descriptor.type;} + ; + +short_integral_literal returns[short value] + : long_literal + { + LiteralTools.checkShort($long_literal.value); + $value = (short)$long_literal.value; + } + | integer_literal + { + LiteralTools.checkShort($integer_literal.value); + $value = (short)$integer_literal.value; + } + | short_literal {$value = $short_literal.value;} + | char_literal {$value = (short)$char_literal.value;} + | byte_literal {$value = $byte_literal.value;}; + +integral_literal returns[int value] + : long_literal + { + LiteralTools.checkInt($long_literal.value); + $value = (int)$long_literal.value; + } + | integer_literal {$value = $integer_literal.value;} + | short_literal {$value = $short_literal.value;} + | byte_literal {$value = $byte_literal.value;}; + + +integer_literal returns[int value] + : INTEGER_LITERAL { $value = LiteralTools.parseInt($INTEGER_LITERAL.text); }; + +long_literal returns[long value] + : LONG_LITERAL { $value = LiteralTools.parseLong($LONG_LITERAL.text); }; + +short_literal returns[short value] + : SHORT_LITERAL { $value = LiteralTools.parseShort($SHORT_LITERAL.text); }; + +byte_literal returns[byte value] + : BYTE_LITERAL { $value = LiteralTools.parseByte($BYTE_LITERAL.text); }; + +float_literal returns[float value] + : FLOAT_LITERAL { $value = parseFloat($FLOAT_LITERAL.text); }; + +double_literal returns[double value] + : DOUBLE_LITERAL { $value = parseDouble($DOUBLE_LITERAL.text); }; + +char_literal returns[char value] + : CHAR_LITERAL { $value = $CHAR_LITERAL.text.charAt(1); }; + +string_literal returns[String value] + : STRING_LITERAL + { + $value = $STRING_LITERAL.text; + $value = $value.substring(1,$value.length()-1); + }; + +bool_literal returns[boolean value] + : BOOL_LITERAL { $value = Boolean.parseBoolean($BOOL_LITERAL.text); }; + +array_literal returns[EncodedValue[\] values] + : {ArrayList valuesList = new ArrayList();} + ^(I_ENCODED_ARRAY (literal {valuesList.add($literal.encodedValue);})*) + { + $values = new EncodedValue[valuesList.size()]; + valuesList.toArray($values); + }; + + +annotations returns[AnnotationSetItem annotationSetItem] + : {ArrayList annotationList = new ArrayList();} + ^(I_ANNOTATIONS (annotation {annotationList.add($annotation.annotationItem);} )*) + { + if (annotationList.size() > 0) { + $annotationSetItem = AnnotationSetItem.internAnnotationSetItem(dexFile, annotationList); + } + }; + + +annotation returns[AnnotationItem annotationItem] + : ^(I_ANNOTATION ANNOTATION_VISIBILITY subannotation) + { + AnnotationVisibility visibility = AnnotationVisibility.valueOf($ANNOTATION_VISIBILITY.text.toUpperCase()); + AnnotationEncodedSubValue encodedAnnotation = new AnnotationEncodedSubValue($subannotation.annotationType, + $subannotation.elementNames, $subannotation.elementValues); + $annotationItem = AnnotationItem.internAnnotationItem(dexFile, visibility, encodedAnnotation); + }; + +annotation_element returns[StringIdItem elementName, EncodedValue elementValue] + : ^(I_ANNOTATION_ELEMENT SIMPLE_NAME literal) + { + $elementName = StringIdItem.internStringIdItem(dexFile, $SIMPLE_NAME.text); + $elementValue = $literal.encodedValue; + }; + +subannotation returns[TypeIdItem annotationType, StringIdItem[\] elementNames, EncodedValue[\] elementValues] + : {ArrayList elementNamesList = new ArrayList(); + ArrayList elementValuesList = new ArrayList();} + ^( I_SUBANNOTATION + class_type_descriptor + (annotation_element + { + elementNamesList.add($annotation_element.elementName); + elementValuesList.add($annotation_element.elementValue); + } )* ) + { + $annotationType = $class_type_descriptor.type; + $elementNames = new StringIdItem[elementNamesList.size()]; + elementNamesList.toArray($elementNames); + $elementValues = new EncodedValue[elementValuesList.size()]; + elementValuesList.toArray($elementValues); + }; + +field_literal returns[FieldIdItem value] + : ^(I_ENCODED_FIELD fully_qualified_field) + { + $value = $fully_qualified_field.fieldIdItem; + }; + +method_literal returns[MethodIdItem value] + : ^(I_ENCODED_METHOD fully_qualified_method) + { + $value = $fully_qualified_method.methodIdItem; + }; + +enum_literal returns[FieldIdItem value] + : ^(I_ENCODED_ENUM fully_qualified_field) + { + $value = $fully_qualified_field.fieldIdItem; + }; diff --git a/brut.apktool.smali/smali/src/main/java/org/jf/smali/InvalidToken.java b/brut.apktool.smali/smali/src/main/java/org/jf/smali/InvalidToken.java new file mode 100644 index 00000000..b7b10781 --- /dev/null +++ b/brut.apktool.smali/smali/src/main/java/org/jf/smali/InvalidToken.java @@ -0,0 +1,52 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.smali; + +import org.antlr.runtime.CharStream; +import org.antlr.runtime.CommonToken; + +public class InvalidToken extends CommonToken { + private final String message; + + public InvalidToken(String message) { + super(smaliParser.INVALID_TOKEN); + this.message = message; + this.channel = smaliLexer.ERROR_CHANNEL; + } + + public InvalidToken(String message, String text) { + super(smaliParser.INVALID_TOKEN, text); + this.message = message; + this.channel = smaliLexer.ERROR_CHANNEL; + } + + public String getMessage() { + return message; + } +} diff --git a/brut.apktool.smali/smali/src/main/java/org/jf/smali/LexerErrorInterface.java b/brut.apktool.smali/smali/src/main/java/org/jf/smali/LexerErrorInterface.java new file mode 100644 index 00000000..001279ec --- /dev/null +++ b/brut.apktool.smali/smali/src/main/java/org/jf/smali/LexerErrorInterface.java @@ -0,0 +1,48 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.smali; + +import org.antlr.runtime.CharStream; +import org.antlr.runtime.Lexer; +import org.antlr.runtime.RecognizerSharedState; + +public interface LexerErrorInterface { + public int getNumberOfSyntaxErrors(); + + //ANTLR doesn't provide any way to add interfaces to the lexer class directly, so this is an intermediate + //class that implements LexerErrorInterface that we can have the ANTLR parser extend + public abstract static class ANTLRLexerWithErrorInterface extends Lexer implements LexerErrorInterface { + public ANTLRLexerWithErrorInterface() { + } + + public ANTLRLexerWithErrorInterface(CharStream input, RecognizerSharedState state) { + super(input, state); + } + } +} diff --git a/brut.apktool.smali/smali/src/main/java/org/jf/smali/LiteralTools.java b/brut.apktool.smali/smali/src/main/java/org/jf/smali/LiteralTools.java new file mode 100644 index 00000000..1c7fb784 --- /dev/null +++ b/brut.apktool.smali/smali/src/main/java/org/jf/smali/LiteralTools.java @@ -0,0 +1,378 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.smali; + +public class LiteralTools +{ + public static byte parseByte(String byteLiteral) + throws NumberFormatException { + if (byteLiteral == null) { + throw new NumberFormatException("string is null"); + } + if (byteLiteral.length() == 0) { + throw new NumberFormatException("string is blank"); + } + + char[] byteChars; + if (byteLiteral.toUpperCase().endsWith("T")) { + byteChars = byteLiteral.substring(0, byteLiteral.length()-1).toCharArray(); + } else { + byteChars = byteLiteral.toCharArray(); + } + + int position = 0; + int radix = 10; + boolean negative = false; + if (byteChars[position] == '-') { + position++; + negative = true; + } + + if (byteChars[position] == '0') { + position++; + if (position == byteChars.length) { + return 0; + } else if (byteChars[position] == 'x' || byteChars[position] == 'X') { + radix = 16; + position++; + } else if (Character.digit(byteChars[position], 8) >= 0) { + radix = 8; + } + } + + byte result = 0; + byte shiftedResult; + int digit; + byte maxValue = (byte)(Byte.MAX_VALUE / (radix / 2)); + + while (position < byteChars.length) { + digit = Character.digit(byteChars[position], radix); + if (digit < 0) { + throw new NumberFormatException("The string contains invalid an digit - '" + byteChars[position] + "'"); + } + shiftedResult = (byte)(result * radix); + if (result > maxValue) { + throw new NumberFormatException(byteLiteral + " cannot fit into a byte"); + } + if (shiftedResult < 0 && shiftedResult >= -digit) { + throw new NumberFormatException(byteLiteral + " cannot fit into a byte"); + } + result = (byte)(shiftedResult + digit); + position++; + } + + if (negative) { + //allow -0x80, which is = 0x80 + if (result == Byte.MIN_VALUE) { + return result; + } else if (result < 0) { + throw new NumberFormatException(byteLiteral + " cannot fit into a byte"); + } + return (byte)(result * -1); + } else { + return result; + } + } + + public static short parseShort(String shortLiteral) + throws NumberFormatException { + if (shortLiteral == null) { + throw new NumberFormatException("string is null"); + } + if (shortLiteral.length() == 0) { + throw new NumberFormatException("string is blank"); + } + + char[] shortChars; + if (shortLiteral.toUpperCase().endsWith("S")) { + shortChars = shortLiteral.substring(0, shortLiteral.length()-1).toCharArray(); + } else { + shortChars = shortLiteral.toCharArray(); + } + + int position = 0; + int radix = 10; + boolean negative = false; + if (shortChars[position] == '-') { + position++; + negative = true; + } + + if (shortChars[position] == '0') { + position++; + if (position == shortChars.length) { + return 0; + } else if (shortChars[position] == 'x' || shortChars[position] == 'X') { + radix = 16; + position++; + } else if (Character.digit(shortChars[position], 8) >= 0) { + radix = 8; + } + } + + short result = 0; + short shiftedResult; + int digit; + short maxValue = (short)(Short.MAX_VALUE / (radix / 2)); + + while (position < shortChars.length) { + digit = Character.digit(shortChars[position], radix); + if (digit < 0) { + throw new NumberFormatException("The string contains invalid an digit - '" + shortChars[position] + "'"); + } + shiftedResult = (short)(result * radix); + if (result > maxValue) { + throw new NumberFormatException(shortLiteral + " cannot fit into a short"); + } + if (shiftedResult < 0 && shiftedResult >= -digit) { + throw new NumberFormatException(shortLiteral + " cannot fit into a short"); + } + result = (short)(shiftedResult + digit); + position++; + } + + if (negative) { + //allow -0x8000, which is = 0x8000 + if (result == Short.MIN_VALUE) { + return result; + } else if (result < 0) { + throw new NumberFormatException(shortLiteral + " cannot fit into a short"); + } + return (short)(result * -1); + } else { + return result; + } + } + + public static int parseInt(String intLiteral) + throws NumberFormatException { + if (intLiteral == null) { + throw new NumberFormatException("string is null"); + } + if (intLiteral.length() == 0) { + throw new NumberFormatException("string is blank"); + } + + char[] intChars = intLiteral.toCharArray(); + int position = 0; + int radix = 10; + boolean negative = false; + if (intChars[position] == '-') { + position++; + negative = true; + } + + if (intChars[position] == '0') { + position++; + if (position == intChars.length) { + return 0; + } else if (intChars[position] == 'x' || intChars[position] == 'X') { + radix = 16; + position++; + } else if (Character.digit(intChars[position], 8) >= 0) { + radix = 8; + } + } + + int result = 0; + int shiftedResult; + int digit; + int maxValue = Integer.MAX_VALUE / (radix / 2); + + while (position < intChars.length) { + digit = Character.digit(intChars[position], radix); + if (digit < 0) { + throw new NumberFormatException("The string contains an invalid digit - '" + intChars[position] + "'"); + } + shiftedResult = result * radix; + if (result > maxValue) { + throw new NumberFormatException(intLiteral + " cannot fit into an int"); + } + if (shiftedResult < 0 && shiftedResult >= -digit) { + throw new NumberFormatException(intLiteral + " cannot fit into an int"); + } + result = shiftedResult + digit; + position++; + } + + if (negative) { + //allow -0x80000000, which is = 0x80000000 + if (result == Integer.MIN_VALUE) { + return result; + } else if (result < 0) { + throw new NumberFormatException(intLiteral + " cannot fit into an int"); + } + return result * -1; + } else { + return result; + } + } + + public static long parseLong(String longLiteral) + throws NumberFormatException { + if (longLiteral == null) { + throw new NumberFormatException("string is null"); + } + if (longLiteral.length() == 0) { + throw new NumberFormatException("string is blank"); + } + + char[] longChars; + if (longLiteral.toUpperCase().endsWith("L")) { + longChars = longLiteral.substring(0, longLiteral.length()-1).toCharArray(); + } else { + longChars = longLiteral.toCharArray(); + } + + int position = 0; + int radix = 10; + boolean negative = false; + if (longChars[position] == '-') { + position++; + negative = true; + } + + if (longChars[position] == '0') { + position++; + if (position == longChars.length) { + return 0; + } else if (longChars[position] == 'x' || longChars[position] == 'X') { + radix = 16; + position++; + } else if (Character.digit(longChars[position], 8) >= 0) { + radix = 8; + } + } + + long result = 0; + long shiftedResult; + int digit; + long maxValue = Long.MAX_VALUE / (radix / 2); + + while (position < longChars.length) { + digit = Character.digit(longChars[position], radix); + if (digit < 0) { + throw new NumberFormatException("The string contains an invalid digit - '" + longChars[position] + "'"); + } + shiftedResult = result * radix; + if (result > maxValue) { + throw new NumberFormatException(longLiteral + " cannot fit into a long"); + } + if (shiftedResult < 0 && shiftedResult >= -digit) { + throw new NumberFormatException(longLiteral + " cannot fit into a long"); + } + result = shiftedResult + digit; + position++; + } + + if (negative) { + //allow -0x8000000000000000, which is = 0x8000000000000000 + if (result == Long.MIN_VALUE) { + return result; + } else if (result < 0) { + throw new NumberFormatException(longLiteral + " cannot fit into a long"); + } + return result * -1; + } else { + return result; + } + } + + public static byte[] longToBytes(long value) { + byte[] bytes = new byte[8]; + + for (int i=0; value != 0; i++) { + bytes[i] = (byte)value; + value = value >>> 8; + } + return bytes; + } + + public static byte[] intToBytes(int value) { + byte[] bytes = new byte[4]; + + for (int i=0; value != 0; i++) { + bytes[i] = (byte)value; + value = value >>> 8; + } + return bytes; + } + + public static byte[] shortToBytes(short value) { + byte[] bytes = new byte[2]; + + bytes[0] = (byte)value; + bytes[1] = (byte)(value >>> 8); + return bytes; + } + + public static byte[] floatToBytes(float value) { + return intToBytes(Float.floatToRawIntBits(value)); + } + + public static byte[] doubleToBytes(double value) { + return longToBytes(Double.doubleToRawLongBits(value)); + } + + public static byte[] charToBytes(char value) { + return shortToBytes((short)value); + } + + public static byte[] boolToBytes(boolean value) { + if (value) { + return new byte[] { 0x01 }; + } else { + return new byte[] { 0x00 }; + } + } + + public static void checkInt(long value) { + if (value > 0xFFFFFFFF || value < -0x80000000) { + throw new NumberFormatException(Long.toString(value) + " cannot fit into an int"); + } + } + + public static void checkShort(long value) { + if (value > 0xFFFF | value < -0x8000) { + throw new NumberFormatException(Long.toString(value) + " cannot fit into a short"); + } + } + + public static void checkByte(long value) { + if (value > 0xFF | value < -0x80) { + throw new NumberFormatException(Long.toString(value) + " cannot fit into a byte"); + } + } + + public static void checkNibble(long value) { + if (value > 0x0F | value < -0x08) { + throw new NumberFormatException(Long.toString(value) + " cannot fit into a nibble"); + } + } +} diff --git a/brut.apktool.smali/smali/src/main/java/org/jf/smali/OdexedInstructionException.java b/brut.apktool.smali/smali/src/main/java/org/jf/smali/OdexedInstructionException.java new file mode 100644 index 00000000..3565c2fb --- /dev/null +++ b/brut.apktool.smali/smali/src/main/java/org/jf/smali/OdexedInstructionException.java @@ -0,0 +1,46 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.smali; + +import org.antlr.runtime.IntStream; +import org.antlr.runtime.RecognitionException; + +public class OdexedInstructionException extends RecognitionException { + private String odexedInstruction; + + OdexedInstructionException(IntStream input, String odexedInstruction) { + super(input); + this.odexedInstruction = odexedInstruction; + } + + public String getMessage() { + return odexedInstruction + " is an odexed instruction. You cannot reassemble a disassembled odex file " + + "unless it has been deodexed."; + } +} diff --git a/brut.apktool.smali/smali/src/main/java/org/jf/smali/SemanticException.java b/brut.apktool.smali/smali/src/main/java/org/jf/smali/SemanticException.java new file mode 100644 index 00000000..73e4c9d8 --- /dev/null +++ b/brut.apktool.smali/smali/src/main/java/org/jf/smali/SemanticException.java @@ -0,0 +1,74 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.smali; + +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.IntStream; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.CommonTree; + +public class SemanticException extends RecognitionException { + private String errorMessage; + + + SemanticException(IntStream input, String errorMessage, Object... messageArguments) { + super(input); + this.errorMessage = String.format(errorMessage, messageArguments); + } + + SemanticException(IntStream input, Exception ex) { + super(input); + this.errorMessage = ex.getMessage(); + } + + SemanticException(IntStream input, CommonTree tree, String errorMessage, Object... messageArguments) { + super(); + this.input = input; + this.token = tree.getToken(); + this.index = tree.getTokenStartIndex(); + this.line = token.getLine(); + this.charPositionInLine = token.getCharPositionInLine(); + this.errorMessage = String.format(errorMessage, messageArguments); + } + + SemanticException(IntStream input, Token token, String errorMessage, Object... messageArguments) { + super(); + this.input = input; + this.token = token; + this.index = ((CommonToken)token).getStartIndex(); + this.line = token.getLine(); + this.charPositionInLine = token.getCharPositionInLine(); + this.errorMessage = String.format(errorMessage, messageArguments); + } + + public String getMessage() { + return errorMessage; + } +} diff --git a/brut.apktool.smali/smali/src/main/java/org/jf/smali/main.java b/brut.apktool.smali/smali/src/main/java/org/jf/smali/main.java new file mode 100644 index 00000000..e38f8dea --- /dev/null +++ b/brut.apktool.smali/smali/src/main/java/org/jf/smali/main.java @@ -0,0 +1,456 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.smali; + +import org.antlr.runtime.*; +import org.antlr.runtime.tree.CommonTree; +import org.antlr.runtime.tree.CommonTreeNodeStream; +import org.apache.commons.cli.*; +import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib.CodeItem; +import org.jf.dexlib.DexFile; +import org.jf.dexlib.Util.ByteArrayAnnotatedOutput; +import org.jf.util.ConsoleUtil; +import org.jf.util.SmaliHelpFormatter; + +import java.io.*; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Properties; +import java.util.Set; + +/** + * Main class for smali. It recognizes enough options to be able to dispatch + * to the right "actual" main. + */ +public class main { + + public static final String VERSION; + + private final static Options basicOptions; + private final static Options debugOptions; + private final static Options options; + + static { + basicOptions = new Options(); + debugOptions = new Options(); + options = new Options(); + buildOptions(); + + InputStream templateStream = main.class.getClassLoader().getResourceAsStream("smali.properties"); + Properties properties = new Properties(); + String version = "(unknown)"; + try { + properties.load(templateStream); + version = properties.getProperty("application.version"); + } catch (IOException ex) { + } + VERSION = version; + } + + + /** + * This class is uninstantiable. + */ + private main() { + } + + /** + * Run! + */ + public static void main(String[] args) { + Locale locale = new Locale("en", "US"); + Locale.setDefault(locale); + + CommandLineParser parser = new PosixParser(); + CommandLine commandLine; + + try { + commandLine = parser.parse(options, args); + } catch (ParseException ex) { + usage(); + return; + } + + boolean allowOdex = false; + boolean sort = false; + boolean fixJumbo = true; + boolean fixGoto = true; + boolean verboseErrors = false; + boolean oldLexer = false; + boolean printTokens = false; + + boolean apiSet = false; + int apiLevel = 14; + + String outputDexFile = "out.dex"; + String dumpFileName = null; + + String[] remainingArgs = commandLine.getArgs(); + + Option[] options = commandLine.getOptions(); + + for (int i=0; i filesToProcess = new LinkedHashSet(); + + for (String arg: remainingArgs) { + File argFile = new File(arg); + + if (!argFile.exists()) { + throw new RuntimeException("Cannot find file or directory \"" + arg + "\""); + } + + if (argFile.isDirectory()) { + getSmaliFilesInDir(argFile, filesToProcess); + } else if (argFile.isFile()) { + filesToProcess.add(argFile); + } + } + + Opcode.updateMapsForApiLevel(apiLevel); + + DexFile dexFile = new DexFile(); + + if (apiSet && apiLevel >= 14) { + dexFile.HeaderItem.setVersion(36); + } + + boolean errors = false; + + for (File file: filesToProcess) { + if (!assembleSmaliFile(file, dexFile, verboseErrors, oldLexer, printTokens, allowOdex, apiLevel)) { + errors = true; + } + } + + if (errors) { + System.exit(1); + } + + + if (sort) { + dexFile.setSortAllItems(true); + } + + if (fixJumbo || fixGoto) { + fixInstructions(dexFile, fixJumbo, fixGoto); + } + + dexFile.place(); + + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); + + if (dumpFileName != null) { + out.enableAnnotations(120, true); + } + + dexFile.writeTo(out); + + byte[] bytes = out.toByteArray(); + + DexFile.calcSignature(bytes); + DexFile.calcChecksum(bytes); + + if (dumpFileName != null) { + out.finishAnnotating(); + + FileWriter fileWriter = new FileWriter(dumpFileName); + out.writeAnnotationsTo(fileWriter); + fileWriter.close(); + } + + FileOutputStream fileOutputStream = new FileOutputStream(outputDexFile); + + fileOutputStream.write(bytes); + fileOutputStream.close(); + } catch (RuntimeException ex) { + System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:"); + ex.printStackTrace(); + System.exit(2); + } catch (Throwable ex) { + System.err.println("\nUNEXPECTED TOP-LEVEL ERROR:"); + ex.printStackTrace(); + System.exit(3); + } + } + + private static void getSmaliFilesInDir(File dir, Set smaliFiles) { + for(File file: dir.listFiles()) { + if (file.isDirectory()) { + getSmaliFilesInDir(file, smaliFiles); + } else if (file.getName().endsWith(".smali")) { + smaliFiles.add(file); + } + } + } + + private static void fixInstructions(DexFile dexFile, boolean fixJumbo, boolean fixGoto) { + dexFile.place(); + + for (CodeItem codeItem: dexFile.CodeItemsSection.getItems()) { + codeItem.fixInstructions(fixJumbo, fixGoto); + } + } + + private static boolean assembleSmaliFile(File smaliFile, DexFile dexFile, boolean verboseErrors, boolean oldLexer, + boolean printTokens, boolean allowOdex, int apiLevel) + throws Exception { + CommonTokenStream tokens; + + + boolean lexerErrors = false; + LexerErrorInterface lexer; + + if (oldLexer) { + ANTLRFileStream input = new ANTLRFileStream(smaliFile.getAbsolutePath(), "UTF-8"); + input.name = smaliFile.getAbsolutePath(); + + lexer = new smaliLexer(input); + tokens = new CommonTokenStream((TokenSource)lexer); + } else { + FileInputStream fis = new FileInputStream(smaliFile.getAbsolutePath()); + InputStreamReader reader = new InputStreamReader(fis, "UTF-8"); + + lexer = new smaliFlexLexer(reader); + ((smaliFlexLexer)lexer).setSourceFile(smaliFile); + tokens = new CommonTokenStream((TokenSource)lexer); + } + + if (printTokens) { + tokens.getTokens(); + + for (int i=0; i 0 || lexer.getNumberOfSyntaxErrors() > 0) { + return false; + } + + CommonTree t = (CommonTree) result.getTree(); + + CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); + treeStream.setTokenStream(tokens); + + smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); + + dexGen.dexFile = dexFile; + dexGen.smali_file(); + + if (dexGen.getNumberOfSyntaxErrors() > 0) { + return false; + } + + return true; + } + + + /** + * Prints the usage message. + */ + private static void usage(boolean printDebugOptions) { + SmaliHelpFormatter formatter = new SmaliHelpFormatter(); + + int consoleWidth = ConsoleUtil.getConsoleWidth(); + if (consoleWidth <= 0) { + consoleWidth = 80; + } + + formatter.setWidth(consoleWidth); + + formatter.printHelp("java -jar smali.jar [options] [--] [|folder]*", + "assembles a set of smali files into a dex file", basicOptions, printDebugOptions?debugOptions:null); + } + + private static void usage() { + usage(false); + } + + /** + * Prints the version message. + */ + private static void version() { + System.out.println("smali " + VERSION + " (http://smali.googlecode.com)"); + System.out.println("Copyright (C) 2010 Ben Gruver (JesusFreke@JesusFreke.com)"); + System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)"); + System.exit(0); + } + + private static void buildOptions() { + 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. Specify twice for debug options") + .create("?"); + + Option outputOption = OptionBuilder.withLongOpt("output") + .withDescription("the name of the dex file that will be written. The default is out.dex") + .hasArg() + .withArgName("FILE") + .create("o"); + + Option allowOdexOption = OptionBuilder.withLongOpt("allow-odex-instructions") + .withDescription("allow odex instructions to be compiled into the dex file. Only a few" + + " instructions are supported - the ones that can exist in a dead code path and not" + + " cause dalvik to reject the class") + .create("x"); + + Option apiLevelOption = OptionBuilder.withLongOpt("api-level") + .withDescription("The numeric api-level of the file to generate, e.g. 14 for ICS. If not " + + "specified, it defaults to 14 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); + + Option dumpOption = OptionBuilder.withLongOpt("dump-to") + .withDescription("additionally writes a dump of written dex file to FILE (.dump by default)") + .hasOptionalArg() + .withArgName("FILE") + .create("D"); + + Option sortOption = OptionBuilder.withLongOpt("sort") + .withDescription("sort the items in the dex file into a canonical order before writing") + .create("S"); + + Option noFixJumboOption = OptionBuilder.withLongOpt("no-fix-jumbo") + .withDescription("Don't automatically instructions with the /jumbo variant where appropriate") + .create("J"); + + Option noFixGotoOption = OptionBuilder.withLongOpt("no-fix-goto") + .withDescription("Don't replace goto type instructions with a larger version where appropriate") + .create("G"); + + Option verboseErrorsOption = OptionBuilder.withLongOpt("verbose-errors") + .withDescription("Generate verbose error messages") + .create("V"); + + Option oldLexerOption = OptionBuilder.withLongOpt("old-lexer") + .withDescription("Use the old lexer") + .create("L"); + + Option printTokensOption = OptionBuilder.withLongOpt("print-tokens") + .withDescription("Print the name and text of each token") + .create("T"); + + basicOptions.addOption(versionOption); + basicOptions.addOption(helpOption); + basicOptions.addOption(outputOption); + basicOptions.addOption(allowOdexOption); + basicOptions.addOption(apiLevelOption); + + debugOptions.addOption(dumpOption); + debugOptions.addOption(sortOption); + debugOptions.addOption(noFixJumboOption); + debugOptions.addOption(noFixGotoOption); + debugOptions.addOption(verboseErrorsOption); + debugOptions.addOption(oldLexerOption); + debugOptions.addOption(printTokensOption); + + for (Object option: basicOptions.getOptions()) { + options.addOption((Option)option); + } + + for (Object option: debugOptions.getOptions()) { + options.addOption((Option)option); + } + } +} \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/main/jflex/smaliLexer.flex b/brut.apktool.smali/smali/src/main/jflex/smaliLexer.flex new file mode 100644 index 00000000..231abefd --- /dev/null +++ b/brut.apktool.smali/smali/src/main/jflex/smaliLexer.flex @@ -0,0 +1,611 @@ +package org.jf.smali; + +import java.io.*; +import org.antlr.runtime.*; +import org.jf.util.*; +import static org.jf.smali.smaliParser.*; + +%% + +%public +%class smaliFlexLexer +%implements TokenSource +%implements LexerErrorInterface +%type Token +%unicode +%line +%column +%char + +%{ + private StringBuffer sb = new StringBuffer(); + private String stringOrCharError = null; + private int stringStartLine; + private int stringStartCol; + private int stringStartChar; + + private int lexerErrors = 0; + + private File sourceFile; + + private boolean suppressErrors; + + public Token nextToken() { + try { + Token token = yylex(); + if (token instanceof InvalidToken) { + InvalidToken invalidToken = (InvalidToken)token; + if (!suppressErrors) { + System.err.println(getErrorHeader(invalidToken) + " Error for input '" + + invalidToken.getText() + "': " + invalidToken.getMessage()); + } + lexerErrors++; + } + return token; + } + catch (java.io.IOException e) { + System.err.println("shouldn't happen: " + e.getMessage()); + return Token.EOF_TOKEN; + } + } + + public void setLine(int line) { + this.yyline = line-1; + } + + public void setColumn(int column) { + this.yycolumn = column; + } + + public int getLine() { + return this.yyline+1; + } + + public int getColumn() { + return this.yycolumn; + } + + public void setSuppressErrors(boolean suppressErrors) { + this.suppressErrors = suppressErrors; + } + + public void setSourceFile(File sourceFile) { + this.sourceFile = sourceFile; + } + + public String getSourceName() { + try { + return PathUtil.getRelativeFile(new File("."), sourceFile).getPath(); + } catch (IOException ex) { + return sourceFile.getAbsolutePath(); + } + } + + public int getNumberOfSyntaxErrors() { + return lexerErrors; + } + + private Token newToken(int type, String text, boolean hidden) { + CommonToken token = new CommonToken(type, text); + if (hidden) { + token.setChannel(Token.HIDDEN_CHANNEL); + } + + token.setStartIndex(yychar); + token.setStopIndex(yychar + yylength() - 1); + token.setLine(getLine()); + token.setCharPositionInLine(getColumn()); + return token; + } + + private Token newToken(int type, String text) { + return newToken(type, text, false); + } + + private Token newToken(int type, boolean hidden) { + return newToken(type, yytext(), hidden); + } + + private Token newToken(int type) { + return newToken(type, yytext(), false); + } + + private Token invalidToken(String message, String text) { + InvalidToken token = new InvalidToken(message, text); + + token.setStartIndex(yychar); + token.setStopIndex(yychar + yylength() - 1); + token.setLine(getLine()); + token.setCharPositionInLine(getColumn()); + + return token; + } + + private Token invalidToken(String message) { + return invalidToken(message, yytext()); + } + + private void beginStringOrChar(int state) { + yybegin(state); + sb.setLength(0); + stringStartLine = getLine(); + stringStartCol = getColumn(); + stringStartChar = yychar; + stringOrCharError = null; + } + + private Token endStringOrChar(int type) { + yybegin(YYINITIAL); + + if (stringOrCharError != null) { + return invalidStringOrChar(stringOrCharError); + } + + CommonToken token = new CommonToken(type, sb.toString()); + token.setStartIndex(stringStartChar); + token.setStopIndex(yychar + yylength() - 1); + token.setLine(stringStartLine); + token.setCharPositionInLine(stringStartCol); + return token; + } + + private void setStringOrCharError(String message) { + if (stringOrCharError == null) { + stringOrCharError = message; + } + } + + private Token invalidStringOrChar(String message) { + yybegin(YYINITIAL); + + InvalidToken token = new InvalidToken(message, sb.toString()); + token.setStartIndex(stringStartChar); + token.setStopIndex(yychar + yylength() - 1); + token.setLine(stringStartLine); + token.setCharPositionInLine(stringStartCol); + return token; + } + + public String getErrorHeader(InvalidToken token) { + return getSourceName()+"["+ token.getLine()+","+token.getCharPositionInLine()+"]"; + } +%} + +HexPrefix = 0 [xX] + +HexDigit = [0-9a-fA-F] +HexDigits = [0-9a-fA-F]{4} +FewerHexDigits = [0-9a-fA-F]{0,3} + +Integer1 = 0 +Integer2 = [1-9] [0-9]* +Integer3 = 0 [0-7]+ +Integer4 = {HexPrefix} {HexDigit}+ +Integer = {Integer1} | {Integer2} | {Integer3} | {Integer4} + +DecimalExponent = [eE] -? [0-9]+ + +BinaryExponent = [pP] -? [0-9]+ + +/*This can either be a floating point number or an identifier*/ +FloatOrID1 = -? [0-9]+ {DecimalExponent} +FloatOrID2 = -? {HexPrefix} {HexDigit}+ {BinaryExponent} +FloatOrID3 = -? [iI][nN][fF][iI][nN][iI][tT][yY] +FloatOrID4 = [nN][aA][nN] +FloatOrID = {FloatOrID1} | {FloatOrID2} | {FloatOrID3} | {FloatOrID4} + + +/*This can only be a float and not an identifier, due to the decimal point*/ +Float1 = -? [0-9]+ "." [0-9]* {DecimalExponent}? +Float2 = -? "." [0-9]+ {DecimalExponent}? +Float3 = -? {HexPrefix} {HexDigit}+ "." {HexDigit}* {BinaryExponent} +Float4 = -? {HexPrefix} "." {HexDigit}+ {BinaryExponent} +Float = {Float1} | {Float2} | {Float3} | {Float4} + +HighSurrogate = [\ud800-\udbff] + +LowSurrogate = [\udc00-\udfff] + +SimpleNameCharacter = ({HighSurrogate} {LowSurrogate}) | [A-Za-z0-9$\-_\u00a1-\u1fff\u2010-\u2027\u2030-\ud7ff\ue000-\uffef] + +SimpleName = {SimpleNameCharacter}+ + +PrimitiveType = [ZBSCIJFD] + +ClassDescriptor = L ({SimpleName} "/")* {SimpleName} ; + +ArrayDescriptor = "[" + ({PrimitiveType} | {ClassDescriptor}) + +Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor} + + +%state STRING +%state CHAR + +%% + +/*Directives*/ + +{ + ".class" { return newToken(CLASS_DIRECTIVE); } + ".super" { return newToken(SUPER_DIRECTIVE); } + ".implements" { return newToken(IMPLEMENTS_DIRECTIVE); } + ".source" { return newToken(SOURCE_DIRECTIVE); } + ".field" { return newToken(FIELD_DIRECTIVE); } + ".end field" { return newToken(END_FIELD_DIRECTIVE); } + ".subannotation" { return newToken(SUBANNOTATION_DIRECTIVE); } + ".end subannotation" { return newToken(END_SUBANNOTATION_DIRECTIVE); } + ".annotation" { return newToken(ANNOTATION_DIRECTIVE); } + ".end annotation" { return newToken(END_ANNOTATION_DIRECTIVE); } + ".enum" { return newToken(ENUM_DIRECTIVE); } + ".method" { return newToken(METHOD_DIRECTIVE); } + ".end method" { return newToken(END_METHOD_DIRECTIVE); } + ".registers" { return newToken(REGISTERS_DIRECTIVE); } + ".locals" { return newToken(LOCALS_DIRECTIVE); } + ".array-data" { return newToken(ARRAY_DATA_DIRECTIVE); } + ".end array-data" { return newToken(END_ARRAY_DATA_DIRECTIVE); } + ".packed-switch" { return newToken(PACKED_SWITCH_DIRECTIVE); } + ".end packed-switch" { return newToken(END_PACKED_SWITCH_DIRECTIVE); } + ".sparse-switch" { return newToken(SPARSE_SWITCH_DIRECTIVE); } + ".end sparse-switch" { return newToken(END_SPARSE_SWITCH_DIRECTIVE); } + ".catch" { return newToken(CATCH_DIRECTIVE); } + ".catchall" { return newToken(CATCHALL_DIRECTIVE); } + ".line" { return newToken(LINE_DIRECTIVE); } + ".parameter" { return newToken(PARAMETER_DIRECTIVE); } + ".end parameter" { return newToken(END_PARAMETER_DIRECTIVE); } + ".local" { return newToken(LOCAL_DIRECTIVE); } + ".end local" { return newToken(END_LOCAL_DIRECTIVE); } + ".restart local" { return newToken(RESTART_LOCAL_DIRECTIVE); } + ".prologue" { return newToken(PROLOGUE_DIRECTIVE); } + ".epilogue" { return newToken(EPILOGUE_DIRECTIVE); } + + ".end" { return invalidToken("Invalid directive"); } + ".end " [a-zA-z0-9\-_]+ { return invalidToken("Invalid directive"); } + ".restart" { return invalidToken("Invalid directive"); } + ".restart " [a-zA-z0-9\-_]+ { return invalidToken("Invalid directive"); } +} + +/*Literals*/ + { + -? {Integer} { return newToken(INTEGER_LITERAL); } + -? {Integer} [lL] { return newToken(LONG_LITERAL); } + -? {Integer} [sS] { return newToken(SHORT_LITERAL); } + -? {Integer} [tT] { return newToken(BYTE_LITERAL); } + + {FloatOrID} [fF] | -? [0-9]+ [fF] { return newToken(FLOAT_LITERAL_OR_ID); } + {FloatOrID} [dD]? | -? [0-9]+ [dD] { return newToken(DOUBLE_LITERAL_OR_ID); } + {Float} [fF] { return newToken(FLOAT_LITERAL); } + {Float} [dD]? { return newToken(DOUBLE_LITERAL); } + + "true"|"false" { return newToken(BOOL_LITERAL); } + "null" { return newToken(NULL_LITERAL); } + + "\"" { beginStringOrChar(STRING); sb.append('"'); } + + ' { beginStringOrChar(CHAR); sb.append('\''); } +} + + { + "\"" { sb.append('"'); return endStringOrChar(STRING_LITERAL); } + + [^\r\n\"\\]+ { sb.append(yytext()); } + "\\b" { sb.append('\b'); } + "\\t" { sb.append('\t'); } + "\\n" { sb.append('\n'); } + "\\f" { sb.append('\f'); } + "\\r" { sb.append('\r'); } + "\\'" { sb.append('\''); } + "\\\"" { sb.append('"'); } + "\\\\" { sb.append('\\'); } + "\\u" {HexDigits} { sb.append((char)Integer.parseInt(yytext().substring(2,6), 16)); } + + "\\u" {FewerHexDigits} { + sb.append(yytext()); + setStringOrCharError("Invalid \\u sequence. \\u must be followed by 4 hex digits"); + } + + "\\" [^btnfr'\"\\u] { + sb.append(yytext()); + setStringOrCharError("Invalid escape sequence " + yytext()); + } + + [\r\n] { return invalidStringOrChar("Unterminated string literal"); } + <> { return invalidStringOrChar("Unterminated string literal"); } +} + + { + ' { + sb.append('\''); + if (sb.length() == 2) { + return invalidStringOrChar("Empty character literal"); + } else if (sb.length() > 3) { + return invalidStringOrChar("Character literal with multiple chars"); + } + + return endStringOrChar(CHAR_LITERAL); + } + + [^\r\n'\\]+ { sb.append(yytext()); } + "\\b" { sb.append('\b'); } + "\\t" { sb.append('\t'); } + "\\n" { sb.append('\n'); } + "\\f" { sb.append('\f'); } + "\\r" { sb.append('\r'); } + "\\'" { sb.append('\''); } + "\\\"" { sb.append('"'); } + "\\\\" { sb.append('\\'); } + "\\u" {HexDigits} { sb.append((char)Integer.parseInt(yytext().substring(2,6), 16)); } + + "\\u" {HexDigit}* { + sb.append(yytext()); + setStringOrCharError("Invalid \\u sequence. \\u must be followed by exactly 4 hex digits"); + } + + "\\" [^btnfr'\"\\u] { + sb.append(yytext()); + setStringOrCharError("Invalid escape sequence " + yytext()); + } + + [\r\n] { return invalidStringOrChar("Unterminated character literal"); } + <> { return invalidStringOrChar("Unterminated character literal"); } +} + +/*Misc*/ + { + [vp] [0-9]+ { return newToken(REGISTER); } + + "build" | "runtime" | "system" { + return newToken(ANNOTATION_VISIBILITY); + } + + "public" | "private" | "protected" | "static" | "final" | "synchronized" | "bridge" | "varargs" | "native" | + "abstract" | "strictfp" | "synthetic" | "constructor" | "declared-synchronized" | "interface" | "enum" | + "annotation" | "volatile" | "transient" { + return newToken(ACCESS_SPEC); + } + + "no-error" | "generic-error" | "no-such-class" | "no-such-field" | "no-such-method" | "illegal-class-access" | + "illegal-field-access" | "illegal-method-access" | "class-change-error" | "instantiation-error" { + return newToken(VERIFICATION_ERROR_TYPE); + } + + "inline@0x" {HexDigit}+ { return newToken(INLINE_INDEX); } + "vtable@0x" {HexDigit}+ { return newToken(VTABLE_INDEX); } + "field@0x" {HexDigit}+ { return newToken(FIELD_OFFSET); } + + "+" {Integer} { return newToken(OFFSET); } + + # [^\r\n]* { return newToken(LINE_COMMENT, true); } +} + +/*Instructions*/ + { + "goto" { + return newToken(INSTRUCTION_FORMAT10t); + } + + "return-void" | "nop" { + return newToken(INSTRUCTION_FORMAT10x); + } + + "return-void-barrier" { + return newToken(INSTRUCTION_FORMAT10x_ODEX); + } + + "const/4" { + return newToken(INSTRUCTION_FORMAT11n); + } + + "move-result" | "move-result-wide" | "move-result-object" | "move-exception" | "return" | "return-wide" | + "return-object" | "monitor-enter" | "monitor-exit" | "throw" { + return newToken(INSTRUCTION_FORMAT11x); + } + + "move" | "move-wide" | "move-object" | "array-length" | "neg-int" | "not-int" | "neg-long" | "not-long" | + "neg-float" | "neg-double" | "int-to-long" | "int-to-float" | "int-to-double" | "long-to-int" | "long-to-float" | + "long-to-double" | "float-to-int" | "float-to-long" | "float-to-double" | "double-to-int" | "double-to-long" | + "double-to-float" | "int-to-byte" | "int-to-char" | "int-to-short" { + return newToken(INSTRUCTION_FORMAT12x_OR_ID); + } + + "add-int/2addr" | "sub-int/2addr" | "mul-int/2addr" | "div-int/2addr" | "rem-int/2addr" | "and-int/2addr" | + "or-int/2addr" | "xor-int/2addr" | "shl-int/2addr" | "shr-int/2addr" | "ushr-int/2addr" | "add-long/2addr" | + "sub-long/2addr" | "mul-long/2addr" | "div-long/2addr" | "rem-long/2addr" | "and-long/2addr" | "or-long/2addr" | + "xor-long/2addr" | "shl-long/2addr" | "shr-long/2addr" | "ushr-long/2addr" | "add-float/2addr" | + "sub-float/2addr" | "mul-float/2addr" | "div-float/2addr" | "rem-float/2addr" | "add-double/2addr" | + "sub-double/2addr" | "mul-double/2addr" | "div-double/2addr" | "rem-double/2addr" { + return newToken(INSTRUCTION_FORMAT12x); + } + + "throw-verification-error" { + return newToken(INSTRUCTION_FORMAT20bc); + } + + "goto/16" { + return newToken(INSTRUCTION_FORMAT20t); + } + + "sget" | "sget-wide" | "sget-object" | "sget-boolean" | "sget-byte" | "sget-char" | "sget-short" | "sput" | + "sput-wide" | "sput-object" | "sput-boolean" | "sput-byte" | "sput-char" | "sput-short" { + return newToken(INSTRUCTION_FORMAT21c_FIELD); + } + + "sget-volatile" | "sget-wide-volatile" | "sget-object-volatile" | "sput-volatile" | "sput-wide-volatile" | + "sput-object-volatile" { + return newToken(INSTRUCTION_FORMAT21c_FIELD_ODEX); + } + + "const-string" { + return newToken(INSTRUCTION_FORMAT21c_STRING); + } + + "check-cast" | "new-instance" | "const-class" { + return newToken(INSTRUCTION_FORMAT21c_TYPE); + } + + "const/high16" | "const-wide/high16" { + return newToken(INSTRUCTION_FORMAT21h); + } + + "const/16" | "const-wide/16" { + return newToken(INSTRUCTION_FORMAT21s); + } + + "if-eqz" | "if-nez" | "if-ltz" | "if-gez" | "if-gtz" | "if-lez" { + return newToken(INSTRUCTION_FORMAT21t); + } + + "add-int/lit8" | "rsub-int/lit8" | "mul-int/lit8" | "div-int/lit8" | "rem-int/lit8" | "and-int/lit8" | + "or-int/lit8" | "xor-int/lit8" | "shl-int/lit8" | "shr-int/lit8" | "ushr-int/lit8" { + return newToken(INSTRUCTION_FORMAT22b); + } + + "iget" | "iget-wide" | "iget-object" | "iget-boolean" | "iget-byte" | "iget-char" | "iget-short" | "iput" | + "iput-wide" | "iput-object" | "iput-boolean" | "iput-byte" | "iput-char" | "iput-short" { + return newToken(INSTRUCTION_FORMAT22c_FIELD); + } + + "iget-volatile" | "iget-wide-volatile" | "iget-object-volatile" | "iput-volatile" | "iput-wide-volatile" | + "iput-object-volatile" { + return newToken(INSTRUCTION_FORMAT22c_FIELD_ODEX); + } + + "instance-of" | "new-array" { + return newToken(INSTRUCTION_FORMAT22c_TYPE); + } + + "iget-quick" | "iget-wide-quick" | "iget-object-quick" | "iput-quick" | "iput-wide-quick" | "iput-object-quick" { + return newToken(INSTRUCTION_FORMAT22cs_FIELD); + } + + "rsub-int" { + return newToken(INSTRUCTION_FORMAT22s_OR_ID); + } + + "add-int/lit16" | "mul-int/lit16" | "div-int/lit16" | "rem-int/lit16" | "and-int/lit16" | "or-int/lit16" | + "xor-int/lit16" { + return newToken(INSTRUCTION_FORMAT22s); + } + + "if-eq" | "if-ne" | "if-lt" | "if-ge" | "if-gt" | "if-le" { + return newToken(INSTRUCTION_FORMAT22t); + } + + "move/from16" | "move-wide/from16" | "move-object/from16" { + return newToken(INSTRUCTION_FORMAT22x); + } + + "cmpl-float" | "cmpg-float" | "cmpl-double" | "cmpg-double" | "cmp-long" | "aget" | "aget-wide" | "aget-object" | + "aget-boolean" | "aget-byte" | "aget-char" | "aget-short" | "aput" | "aput-wide" | "aput-object" | "aput-boolean" | + "aput-byte" | "aput-char" | "aput-short" | "add-int" | "sub-int" | "mul-int" | "div-int" | "rem-int" | "and-int" | + "or-int" | "xor-int" | "shl-int" | "shr-int" | "ushr-int" | "add-long" | "sub-long" | "mul-long" | "div-long" | + "rem-long" | "and-long" | "or-long" | "xor-long" | "shl-long" | "shr-long" | "ushr-long" | "add-float" | + "sub-float" | "mul-float" | "div-float" | "rem-float" | "add-double" | "sub-double" | "mul-double" | "div-double" | + "rem-double" { + return newToken(INSTRUCTION_FORMAT23x); + } + + "goto/32" { + return newToken(INSTRUCTION_FORMAT30t); + } + + "const-string/jumbo" { + return newToken(INSTRUCTION_FORMAT31c); + } + + "const" { + return newToken(INSTRUCTION_FORMAT31i_OR_ID); + } + + "const-wide/32" { + return newToken(INSTRUCTION_FORMAT31i); + } + + "fill-array-data" | "packed-switch" | "sparse-switch" { + return newToken(INSTRUCTION_FORMAT31t); + } + + "move/16" | "move-wide/16" | "move-object/16" { + return newToken(INSTRUCTION_FORMAT32x); + } + + "invoke-virtual" | "invoke-super" | "invoke-direct" | "invoke-static" | "invoke-interface" { + return newToken(INSTRUCTION_FORMAT35c_METHOD); + } + + "invoke-direct-empty" { + return newToken(INSTRUCTION_FORMAT35c_METHOD_ODEX); + } + + "filled-new-array" { + return newToken(INSTRUCTION_FORMAT35c_TYPE); + } + + "execute-inline" { + return newToken(INSTRUCTION_FORMAT35mi_METHOD); + } + + "invoke-virtual-quick" | "invoke-super-quick" { + return newToken(INSTRUCTION_FORMAT35ms_METHOD); + } + + "invoke-virtual/range" | "invoke-super/range" | "invoke-direct/range" | "invoke-static/range" | + "invoke-interface/range" { + return newToken(INSTRUCTION_FORMAT3rc_METHOD); + } + + "invoke-object-init/range" { + return newToken(INSTRUCTION_FORMAT3rc_METHOD_ODEX); + } + + "filled-new-array/range" { + return newToken(INSTRUCTION_FORMAT3rc_TYPE); + } + + "execute-inline/range" { + return newToken(INSTRUCTION_FORMAT3rmi_METHOD); + } + + "invoke-virtual-quick/range" | "invoke-super-quick/range" { + return newToken(INSTRUCTION_FORMAT3rms_METHOD); + } + + "const-wide" { + return newToken(INSTRUCTION_FORMAT51l); + } +} + +/*Types*/ + { + {PrimitiveType} { return newToken(PRIMITIVE_TYPE); } + V { return newToken(VOID_TYPE); } + {ClassDescriptor} { return newToken(CLASS_DESCRIPTOR); } + {ArrayDescriptor} { return newToken(ARRAY_DESCRIPTOR); } + {PrimitiveType} {PrimitiveType}+ { return newToken(PARAM_LIST_OR_ID); } + {Type} {Type}+ { return newToken(PARAM_LIST); } + {SimpleName} { return newToken(SIMPLE_NAME); } + "" | "" { return newToken(METHOD_NAME); } +} + +/*Symbols/Whitespace/EOF*/ + { + ".." { return newToken(DOTDOT); } + "->" { return newToken(ARROW); } + "=" { return newToken(EQUAL); } + ":" { return newToken(COLON); } + "," { return newToken(COMMA); } + "{" { return newToken(OPEN_BRACE); } + "}" { return newToken(CLOSE_BRACE); } + "(" { return newToken(OPEN_PAREN); } + ")" { return newToken(CLOSE_PAREN); } + [\r\n\t ]+ { return newToken(WHITE_SPACE, true); } + <> { return newToken(EOF); } +} + +/*catch all*/ + { + "." { return invalidToken("Invalid directive"); } + "." [a-zA-z\-_] { return invalidToken("Invalid directive"); } + "." [a-zA-z\-_] [a-zA-z0-9\-_]* { return invalidToken("Invalid directive"); } + . { return invalidToken("Invalid text"); } +} diff --git a/brut.apktool.smali/smali/src/main/resources/smali.properties b/brut.apktool.smali/smali/src/main/resources/smali.properties new file mode 100644 index 00000000..73a02f18 --- /dev/null +++ b/brut.apktool.smali/smali/src/main/resources/smali.properties @@ -0,0 +1 @@ +application.version=1.4.0 diff --git a/brut.apktool.smali/smali/src/test/antlr3/org/jf/smali/expectedTokensTestGrammar.g b/brut.apktool.smali/smali/src/test/antlr3/org/jf/smali/expectedTokensTestGrammar.g new file mode 100644 index 00000000..6780b970 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/antlr3/org/jf/smali/expectedTokensTestGrammar.g @@ -0,0 +1,153 @@ +/* + * The string related lexical rules are derived from rules from the + * Java 1.6 grammar which can be found here: + * http://openjdk.java.net/projects/compiler-grammar/antlrworks/Java.g + * + * Specifically, these rules: + * + * HEX_PREFIX, HEX_DIGIT, ESCAPE_SEQUENCE, STRING_LITERAL, BASE_STRING_LITERAL + * + * These rules were originally copyrighted by Terence Parr, and are used here in + * accordance with the following license + * + * [The "BSD licence"] + * Copyright (c) 2007-2008 Terence Parr + * 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. + * + * + * The remainder of this grammar is released by me (Ben Gruver) under the + * following license: + * + * [The "BSD licence"] + * Copyright (c) 2010 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. + */ + +grammar expectedTokensTestGrammar; + +@lexer::header { +package org.jf.smali; +} + +@parser::header { +package org.jf.smali; + +import java.util.Collections; +} + +@parser::members { + public static class ExpectedToken { + public final String tokenName; + public final String tokenText; + + public ExpectedToken(String tokenName, String tokenText) { + this.tokenName = tokenName; + this.tokenText = tokenText; + } + + public ExpectedToken(String tokenName) { + this.tokenName = tokenName; + this.tokenText = null; + } + } + + private final ArrayList expectedTokens = new ArrayList(); + + public List getExpectedTokens() { + return Collections.unmodifiableList(expectedTokens); + } +} + + +fragment HEX_DIGIT + : ('0'..'9')|('A'..'F')|('a'..'f'); + +fragment HEX_DIGITS + : HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT; + +fragment ESCAPE_SEQUENCE[StringBuilder sb] + : '\\' + ( + 'b' {sb.append("\b");} + | 't' {sb.append("\t");} + | 'n' {sb.append("\n");} + | 'f' {sb.append("\f");} + | 'r' {sb.append("\r");} + | '\"' {sb.append("\"");} + | '\'' {sb.append("'");} + | '\\' {sb.append("\\");} + | 'u' HEX_DIGITS {sb.append((char)Integer.parseInt($HEX_DIGITS.text, 16));} + ); + + +STRING_LITERAL + @init {StringBuilder sb = new StringBuilder();} + : BASE_STRING_LITERAL[sb] {setText(sb.toString());}; + +fragment BASE_STRING_LITERAL[StringBuilder sb] + : '"' + ( ESCAPE_SEQUENCE[sb] + | ~( '\\' | '"' | '\r' | '\n' ) {sb.append((char)input.LA(-1));} + )* + '"'; + +TOKEN_NAME + : (('a'..'z')|('A' .. 'Z')|'_'|('0'..'9'))+; + +WHITE_SPACE + : (' '|'\t'|'\n'|'\r')+ {$channel = HIDDEN;}; + +top : token*; + +token : TOKEN_NAME ( '(' STRING_LITERAL ')' ) + { + expectedTokens.add(new ExpectedToken($TOKEN_NAME.getText(), $STRING_LITERAL.getText())); + } | + TOKEN_NAME + { + expectedTokens.add(new ExpectedToken($TOKEN_NAME.getText())); + }; \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/java/ByteLiteralTest.java b/brut.apktool.smali/smali/src/test/java/ByteLiteralTest.java new file mode 100644 index 00000000..2e6511da --- /dev/null +++ b/brut.apktool.smali/smali/src/test/java/ByteLiteralTest.java @@ -0,0 +1,140 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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. + */ + +import org.jf.smali.LiteralTools; +import org.junit.Assert; +import org.junit.Test; + +public class ByteLiteralTest +{ + + @Test + public void SuccessHexTests() { + + Assert.assertTrue(LiteralTools.parseByte("0x0T") == 0x0); + Assert.assertTrue(LiteralTools.parseByte("0x00") == 0x0); + Assert.assertTrue(LiteralTools.parseByte("0x1T") == 0x1); + Assert.assertTrue(LiteralTools.parseByte("0x12") == 0x12); + Assert.assertTrue(LiteralTools.parseByte("0x7fT") == 0x7f); + Assert.assertTrue(LiteralTools.parseByte("0x80t") == Byte.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseByte("0xFFt") == -1); + + Assert.assertTrue(LiteralTools.parseByte("-0x00") == 0); + Assert.assertTrue(LiteralTools.parseByte("-0x01") == -1); + Assert.assertTrue(LiteralTools.parseByte("-0x12") == -0x12); + Assert.assertTrue(LiteralTools.parseByte("-0x80") == Byte.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseByte("-0x1f") == -0x1f); + } + + @Test(expected=NumberFormatException.class) + public void FaileHexTest1() { + LiteralTools.parseByte("-0x81"); + } + + @Test(expected=NumberFormatException.class) + public void FailHexTest2() { + LiteralTools.parseByte("-0xFF"); + } + + @Test(expected=NumberFormatException.class) + public void FailHexTest3() { + LiteralTools.parseByte("0x100"); + } + + + + @Test + public void SuccessDecTests() { + Assert.assertTrue(LiteralTools.parseByte("0") == 0); + Assert.assertTrue(LiteralTools.parseByte("1t") == 1); + Assert.assertTrue(LiteralTools.parseByte("123") == 123); + Assert.assertTrue(LiteralTools.parseByte("127T") == 127); + Assert.assertTrue(LiteralTools.parseByte("128") == Byte.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseByte("255") == -1); + + + Assert.assertTrue(LiteralTools.parseByte("-0") == 0); + Assert.assertTrue(LiteralTools.parseByte("-1") == -1); + Assert.assertTrue(LiteralTools.parseByte("-123") == -123); + Assert.assertTrue(LiteralTools.parseByte("-127") == -127); + Assert.assertTrue(LiteralTools.parseByte("-128") == Byte.MIN_VALUE); + } + + @Test(expected=NumberFormatException.class) + public void FaileDecTest1() { + LiteralTools.parseByte("-129"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest2() { + LiteralTools.parseByte("-255"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest3() { + LiteralTools.parseByte("256"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest4() { + LiteralTools.parseByte("260"); + } + + + @Test + public void SuccessOctTests() { + Assert.assertTrue(LiteralTools.parseByte("00") == 00); + Assert.assertTrue(LiteralTools.parseByte("01") == 01); + Assert.assertTrue(LiteralTools.parseByte("0123t") == 0123); + Assert.assertTrue(LiteralTools.parseByte("0177") == Byte.MAX_VALUE); + Assert.assertTrue(LiteralTools.parseByte("0200T") == Byte.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseByte("0377") == -1); + + + Assert.assertTrue(LiteralTools.parseByte("-00") == 0); + Assert.assertTrue(LiteralTools.parseByte("-01") == -1); + Assert.assertTrue(LiteralTools.parseByte("-0123") == -0123); + Assert.assertTrue(LiteralTools.parseByte("-0177") == -0177); + Assert.assertTrue(LiteralTools.parseByte("-0200") == Byte.MIN_VALUE); + } + + @Test(expected=NumberFormatException.class) + public void FaileOctTest1() { + LiteralTools.parseByte("-0201"); + } + + @Test(expected=NumberFormatException.class) + public void FailOctTest2() { + LiteralTools.parseByte("-0377"); + } + + @Test(expected=NumberFormatException.class) + public void FailOctTest3() { + LiteralTools.parseByte("0400"); + } +} diff --git a/brut.apktool.smali/smali/src/test/java/IntLiteralTest.java b/brut.apktool.smali/smali/src/test/java/IntLiteralTest.java new file mode 100644 index 00000000..f5a8c969 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/java/IntLiteralTest.java @@ -0,0 +1,144 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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. + */ + +import org.jf.smali.LiteralTools; +import org.junit.Assert; +import org.junit.Test; + +public class IntLiteralTest +{ + @Test + public void SuccessHexTests() { + + Assert.assertTrue(LiteralTools.parseInt("0x0") == 0x0); + Assert.assertTrue(LiteralTools.parseInt("0x00") == 0x0); + Assert.assertTrue(LiteralTools.parseInt("0x1") == 0x1); + Assert.assertTrue(LiteralTools.parseInt("0x12345678") == 0x12345678); + Assert.assertTrue(LiteralTools.parseInt("0x7fffffff") == 0x7fffffff); + Assert.assertTrue(LiteralTools.parseInt("0x80000000") == Integer.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseInt("0xFFFFFFFF") == -1); + + Assert.assertTrue(LiteralTools.parseInt("-0x00") == 0); + Assert.assertTrue(LiteralTools.parseInt("-0x01") == -1); + Assert.assertTrue(LiteralTools.parseInt("-0x12345678") == -0x12345678); + Assert.assertTrue(LiteralTools.parseInt("-0x80000000") == Integer.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseInt("-0x1FFFFFFF") == -0x1FFFFFFF); + } + + @Test(expected=NumberFormatException.class) + public void FaileHexTest1() { + LiteralTools.parseInt("-0x80000001"); + } + + @Test(expected=NumberFormatException.class) + public void FailHexTest2() { + LiteralTools.parseInt("-0xFFFFFFFF"); + } + + @Test(expected=NumberFormatException.class) + public void FailHexTest3() { + LiteralTools.parseInt("0x100000000"); + } + + + + @Test + public void SuccessDecTests() { + Assert.assertTrue(LiteralTools.parseInt("0") == 0); + Assert.assertTrue(LiteralTools.parseInt("1") == 1); + Assert.assertTrue(LiteralTools.parseInt("1234567890") == 1234567890); + Assert.assertTrue(LiteralTools.parseInt("2147483647") == 2147483647); + Assert.assertTrue(LiteralTools.parseInt("2147483648") == Integer.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseInt("4294967295") == -1); + + + Assert.assertTrue(LiteralTools.parseInt("-0") == 0); + Assert.assertTrue(LiteralTools.parseInt("-1") == -1); + Assert.assertTrue(LiteralTools.parseInt("-1234567890") == -1234567890); + Assert.assertTrue(LiteralTools.parseInt("-2147483647") == -2147483647); + Assert.assertTrue(LiteralTools.parseInt("-2147483648") == Integer.MIN_VALUE); + } + + @Test(expected=NumberFormatException.class) + public void FaileDecTest1() { + LiteralTools.parseInt("-2147483649"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest2() { + LiteralTools.parseInt("-4294967295"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest3() { + LiteralTools.parseInt("4294967296"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest4() { + LiteralTools.parseInt("4294967300"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest5() { + LiteralTools.parseInt("8589934592"); + } + + + @Test + public void SuccessOctTests() { + Assert.assertTrue(LiteralTools.parseInt("00") == 00); + Assert.assertTrue(LiteralTools.parseInt("01") == 01); + Assert.assertTrue(LiteralTools.parseInt("012345670123") == 012345670123); + Assert.assertTrue(LiteralTools.parseInt("017777777777") == Integer.MAX_VALUE); + Assert.assertTrue(LiteralTools.parseInt("020000000000") == Integer.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseInt("037777777777") == -1); + + + Assert.assertTrue(LiteralTools.parseInt("-00") == 0); + Assert.assertTrue(LiteralTools.parseInt("-01") == -1); + Assert.assertTrue(LiteralTools.parseInt("-012345670123") == -012345670123); + Assert.assertTrue(LiteralTools.parseInt("-017777777777") == -017777777777); + Assert.assertTrue(LiteralTools.parseInt("-020000000000") == Integer.MIN_VALUE); + } + + @Test(expected=NumberFormatException.class) + public void FaileOctTest1() { + LiteralTools.parseInt("-020000000001"); + } + + @Test(expected=NumberFormatException.class) + public void FailOctTest2() { + LiteralTools.parseInt("-037777777777"); + } + + @Test(expected=NumberFormatException.class) + public void FailOctTest3() { + LiteralTools.parseInt("040000000000"); + } +} diff --git a/brut.apktool.smali/smali/src/test/java/LexerTest.java b/brut.apktool.smali/smali/src/test/java/LexerTest.java new file mode 100644 index 00000000..5afc2e2c --- /dev/null +++ b/brut.apktool.smali/smali/src/test/java/LexerTest.java @@ -0,0 +1,212 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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. + */ + +import org.antlr.runtime.ANTLRInputStream; +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.RecognitionException; +import org.jf.dexlib.Util.Utf8Utils; +import org.jf.smali.*; +import static org.jf.smali.expectedTokensTestGrammarParser.ExpectedToken; +import org.junit.Assert; +import org.junit.Test; + +import java.io.*; +import java.util.HashMap; +import java.util.List; + +public class LexerTest { + private static final HashMap tokenTypesByName; + + static { + tokenTypesByName = new HashMap(); + + for (int i=0; i expectedTokens = expectedTokensParser.getExpectedTokens(); + + InputStream smaliStream = LexerTest.class.getClassLoader().getResourceAsStream(smaliFile); + if (smaliStream == null) { + Assert.fail("Could not load " + smaliFile); + } + smaliFlexLexer lexer = new smaliFlexLexer(smaliStream); + lexer.setSourceFile(new File(test + ".smali")); + lexer.setSuppressErrors(true); + + CommonTokenStream tokenStream = new CommonTokenStream(lexer); + List tokens = tokenStream.getTokens(); + + int expectedTokenIndex = 0; + CommonToken token; + for (int i=0; i= expectedTokens.size()) { + Assert.fail("Too many tokens"); + } + + if (token.getType() == smaliParser.INVALID_TOKEN) { + Assert.assertTrue("Encountered an INVALID_TOKEN not on the error channel", + token.getChannel() == smaliLexer.ERROR_CHANNEL); + } + + ExpectedToken expectedToken = expectedTokens.get(expectedTokenIndex++); + if (!tokenTypesByName.containsKey(expectedToken.tokenName)) { + Assert.fail("Unknown token: " + expectedToken.tokenName); + } + int expectedTokenType = tokenTypesByName.get(expectedToken.tokenName); + + if (token.getType() != expectedTokenType) { + Assert.fail(String.format("Invalid token at index %d. Expecting %s, got %s(%s)", + expectedTokenIndex-1, expectedToken.tokenName, getTokenName(token.getType()), token.getText())); + } + + if (expectedToken.tokenText != null) { + if (!expectedToken.tokenText.equals(token.getText())) { + Assert.fail( + String.format("Invalid token text at index %d. Expecting text \"%s\", got \"%s\"", + expectedTokenIndex - 1, expectedToken.tokenText, token.getText())); + } + } + } + + if (expectedTokenIndex < expectedTokens.size()) { + Assert.fail(String.format("Not enough tokens. Expecting %d tokens, but got %d", expectedTokens.size(), + expectedTokenIndex)); + } + } + + + + private static String getTokenName(int tokenType) { + return smaliParser.tokenNames[tokenType]; + } +} diff --git a/brut.apktool.smali/smali/src/test/java/LongLiteralTest.java b/brut.apktool.smali/smali/src/test/java/LongLiteralTest.java new file mode 100644 index 00000000..f68cf5a7 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/java/LongLiteralTest.java @@ -0,0 +1,133 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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. + */ + +import org.jf.smali.LiteralTools; +import org.junit.Assert; +import org.junit.Test; + +public class LongLiteralTest +{ + @Test + public void SuccessHexTests() { + Assert.assertTrue(LiteralTools.parseLong("0x0L") == 0x0); + Assert.assertTrue(LiteralTools.parseLong("0x00L") == 0x0); + Assert.assertTrue(LiteralTools.parseLong("0x1L") == 0x1); + Assert.assertTrue(LiteralTools.parseLong("0x1234567890123456L") == 0x1234567890123456L); + Assert.assertTrue(LiteralTools.parseLong("0x7fffffffffffffffL") == 0x7fffffffffffffffL); + Assert.assertTrue(LiteralTools.parseLong("0x8000000000000000L") == Long.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseLong("0xFFFFFFFFFFFFFFFFL") == -1); + + Assert.assertTrue(LiteralTools.parseLong("-0x00L") == 0); + Assert.assertTrue(LiteralTools.parseLong("-0x01L") == -1); + Assert.assertTrue(LiteralTools.parseLong("-0x1234567890123456L") == -0x1234567890123456L); + Assert.assertTrue(LiteralTools.parseLong("-0x8000000000000000L") == Long.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseLong("-0x1fffffffffffffffL") == -0x1fffffffffffffffL); + } + + @Test(expected=NumberFormatException.class) + public void FaileHexTest1() { + LiteralTools.parseLong("-0x8000000000000001"); + } + + @Test(expected=NumberFormatException.class) + public void FailHexTest2() { + LiteralTools.parseLong("-0xFFFFFFFFFFFFFFFF"); + } + + @Test(expected=NumberFormatException.class) + public void FailHexTest3() { + LiteralTools.parseLong("0x10000000000000000"); + } + + @Test + public void SuccessDecTests() { + Assert.assertTrue(LiteralTools.parseLong("0L") == 0); + Assert.assertTrue(LiteralTools.parseLong("1") == 1); + Assert.assertTrue(LiteralTools.parseLong("1234567890123456789") == 1234567890123456789L); + Assert.assertTrue(LiteralTools.parseLong("9223372036854775807") == 9223372036854775807L); + Assert.assertTrue(LiteralTools.parseLong("9223372036854775808") == Long.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseLong("18446744073709551615L") == -1); + + Assert.assertTrue(LiteralTools.parseLong("-0") == 0); + Assert.assertTrue(LiteralTools.parseLong("-1") == -1); + Assert.assertTrue(LiteralTools.parseLong("-1234567890123456789") == -1234567890123456789L); + Assert.assertTrue(LiteralTools.parseLong("-9223372036854775807") == -9223372036854775807L); + Assert.assertTrue(LiteralTools.parseLong("-9223372036854775808") == Long.MIN_VALUE); + } + + @Test(expected=NumberFormatException.class) + public void FaileDecTest1() { + LiteralTools.parseLong("-9223372036854775809"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest2() { + LiteralTools.parseLong("-18446744073709551616"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest3() { + LiteralTools.parseLong("18446744073709551617"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest4() { + LiteralTools.parseLong("18446744073709551700"); + } + + @Test + public void SuccessOctTests() { + Assert.assertTrue(LiteralTools.parseLong("00") == 00); + Assert.assertTrue(LiteralTools.parseLong("01") == 01); + Assert.assertTrue(LiteralTools.parseLong("0123456701234567012345") == 0123456701234567012345L); + Assert.assertTrue(LiteralTools.parseLong("0777777777777777777777") == Long.MAX_VALUE); + Assert.assertTrue(LiteralTools.parseLong("01000000000000000000000") == Long.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseLong("01777777777777777777777") == -1); + + Assert.assertTrue(LiteralTools.parseLong("-00") == 0); + Assert.assertTrue(LiteralTools.parseLong("-01") == -1); + Assert.assertTrue(LiteralTools.parseLong("-0123456701234567012345") == -0123456701234567012345L); + Assert.assertTrue(LiteralTools.parseLong("-0777777777777777777777") == -0777777777777777777777L); + Assert.assertTrue(LiteralTools.parseLong("-01000000000000000000000") == Long.MIN_VALUE); + } + + @Test(expected=NumberFormatException.class) + public void FaileOctTest1() { + LiteralTools.parseLong("-01000000000000000000001"); + } + + @Test(expected=NumberFormatException.class) + public void FailOctTest2() { + LiteralTools.parseLong("-01777777777777777777777"); + } + + @Test(expected=NumberFormatException.class) + public void FailOctTest3() { + LiteralTools.parseLong("02000000000000000000000"); + } +} diff --git a/brut.apktool.smali/smali/src/test/java/ShortLiteralTest.java b/brut.apktool.smali/smali/src/test/java/ShortLiteralTest.java new file mode 100644 index 00000000..e366be85 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/java/ShortLiteralTest.java @@ -0,0 +1,140 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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. + */ + +import org.jf.smali.LiteralTools; +import org.junit.Assert; +import org.junit.Test; + +public class ShortLiteralTest +{ + + @Test + public void SuccessHexTests() { + + Assert.assertTrue(LiteralTools.parseShort("0x0") == 0x0); + Assert.assertTrue(LiteralTools.parseShort("0x00") == 0x0); + Assert.assertTrue(LiteralTools.parseShort("0x1") == 0x1); + Assert.assertTrue(LiteralTools.parseShort("0x1234") == 0x1234); + Assert.assertTrue(LiteralTools.parseShort("0x7fff") == 0x7fff); + Assert.assertTrue(LiteralTools.parseShort("0x8000") == Short.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseShort("0xFFFF") == -1); + + Assert.assertTrue(LiteralTools.parseShort("-0x00") == 0); + Assert.assertTrue(LiteralTools.parseShort("-0x01") == -1); + Assert.assertTrue(LiteralTools.parseShort("-0x1234") == -0x1234); + Assert.assertTrue(LiteralTools.parseShort("-0x8000") == Short.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseShort("-0x1fff") == -0x1fff); + } + + @Test(expected=NumberFormatException.class) + public void FaileHexTest1() { + LiteralTools.parseShort("-0x8001"); + } + + @Test(expected=NumberFormatException.class) + public void FailHexTest2() { + LiteralTools.parseShort("-0xFFFF"); + } + + @Test(expected=NumberFormatException.class) + public void FailHexTest3() { + LiteralTools.parseShort("0x100000"); + } + + + + @Test + public void SuccessDecTests() { + Assert.assertTrue(LiteralTools.parseShort("0") == 0); + Assert.assertTrue(LiteralTools.parseShort("1") == 1); + Assert.assertTrue(LiteralTools.parseShort("12345") == 12345); + Assert.assertTrue(LiteralTools.parseShort("32767") == 32767); + Assert.assertTrue(LiteralTools.parseShort("32768") == Short.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseShort("65535") == -1); + + + Assert.assertTrue(LiteralTools.parseShort("-0") == 0); + Assert.assertTrue(LiteralTools.parseShort("-1") == -1); + Assert.assertTrue(LiteralTools.parseShort("-12345") == -12345); + Assert.assertTrue(LiteralTools.parseShort("-32767") == -32767); + Assert.assertTrue(LiteralTools.parseShort("-32768") == Short.MIN_VALUE); + } + + @Test(expected=NumberFormatException.class) + public void FaileDecTest1() { + LiteralTools.parseShort("-32769"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest2() { + LiteralTools.parseShort("-65535"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest3() { + LiteralTools.parseShort("65536"); + } + + @Test(expected=NumberFormatException.class) + public void FailDecTest4() { + LiteralTools.parseShort("65600"); + } + + + @Test + public void SuccessOctTests() { + Assert.assertTrue(LiteralTools.parseShort("00") == 00); + Assert.assertTrue(LiteralTools.parseShort("01") == 01); + Assert.assertTrue(LiteralTools.parseShort("012345") == 012345); + Assert.assertTrue(LiteralTools.parseShort("077777") == Short.MAX_VALUE); + Assert.assertTrue(LiteralTools.parseShort("0100000") == Short.MIN_VALUE); + Assert.assertTrue(LiteralTools.parseShort("0177777") == -1); + + + Assert.assertTrue(LiteralTools.parseShort("-00") == 0); + Assert.assertTrue(LiteralTools.parseShort("-01") == -1); + Assert.assertTrue(LiteralTools.parseShort("-012345") == -012345); + Assert.assertTrue(LiteralTools.parseShort("-077777") == -077777); + Assert.assertTrue(LiteralTools.parseShort("-0100000") == Short.MIN_VALUE); + } + + @Test(expected=NumberFormatException.class) + public void FaileOctTest1() { + LiteralTools.parseShort("-0100001"); + } + + @Test(expected=NumberFormatException.class) + public void FailOctTest2() { + LiteralTools.parseShort("-0177777"); + } + + @Test(expected=NumberFormatException.class) + public void FailOctTest3() { + LiteralTools.parseShort("0200000"); + } +} diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/ByteLiteralTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/ByteLiteralTest.smali new file mode 100644 index 00000000..99ad38de --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/ByteLiteralTest.smali @@ -0,0 +1,47 @@ +0x0T +0x00 +0x1T +0x12 +0x7fT +0x80t +0xFFt +-0x00 +-0x01 +-0x12 +-0x80 +-0x1f +-0x1fT +-0x81 +-0xFF +-0x100 +0 +1t +123 +127T +128 +255 +-0 +-1 +-123 +-123T +-127 +-128 +-129 +-255 +256 +260 +00 +01 +0123t +0177 +0200T +0377 +-00 +-01 +-0123 +-0123t +-177 +-0200 +-0201 +-0377 +-0400 \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/ByteLiteralTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/ByteLiteralTest.tokens new file mode 100644 index 00000000..921247f7 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/ByteLiteralTest.tokens @@ -0,0 +1,47 @@ +BYTE_LITERAL("0x0T") +INTEGER_LITERAL("0x00") +BYTE_LITERAL("0x1T") +INTEGER_LITERAL("0x12") +BYTE_LITERAL("0x7fT") +BYTE_LITERAL("0x80t") +BYTE_LITERAL("0xFFt") +INTEGER_LITERAL("-0x00") +INTEGER_LITERAL("-0x01") +INTEGER_LITERAL("-0x12") +INTEGER_LITERAL("-0x80") +INTEGER_LITERAL("-0x1f") +BYTE_LITERAL("-0x1fT") +INTEGER_LITERAL("-0x81") +INTEGER_LITERAL("-0xFF") +INTEGER_LITERAL("-0x100") +INTEGER_LITERAL("0") +BYTE_LITERAL("1t") +INTEGER_LITERAL("123") +BYTE_LITERAL("127T") +INTEGER_LITERAL("128") +INTEGER_LITERAL("255") +INTEGER_LITERAL("-0") +INTEGER_LITERAL("-1") +INTEGER_LITERAL("-123") +BYTE_LITERAL("-123T") +INTEGER_LITERAL("-127") +INTEGER_LITERAL("-128") +INTEGER_LITERAL("-129") +INTEGER_LITERAL("-255") +INTEGER_LITERAL("256") +INTEGER_LITERAL("260") +INTEGER_LITERAL("00") +INTEGER_LITERAL("01") +BYTE_LITERAL("0123t") +INTEGER_LITERAL("0177") +BYTE_LITERAL("0200T") +INTEGER_LITERAL("0377") +INTEGER_LITERAL("-00") +INTEGER_LITERAL("-01") +INTEGER_LITERAL("-0123") +BYTE_LITERAL("-0123t") +INTEGER_LITERAL("-177") +INTEGER_LITERAL("-0200") +INTEGER_LITERAL("-0201") +INTEGER_LITERAL("-0377") +INTEGER_LITERAL("-0400") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/CharLiteralTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/CharLiteralTest.smali new file mode 100644 index 00000000..3f5b01f9 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/CharLiteralTest.smali @@ -0,0 +1,40 @@ +' ' +' ' +'a' +'A' +'z' +'Z' +'a''a' +'1' +'2' +'\n' +'\r' +'\t' +'\b' +'\f' +'\u0030' +'\uABCD' +'\uabcd' +'\\' +'\'' +'\"' +'"' + + +'a'a' +'ab' +'\a' +'' +' +' +' +'\u' +'\u0' +'\ua' +'\uab' +'\u01a' +'\uz' +'\u012z' + +'a' +' \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/CharLiteralTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/CharLiteralTest.tokens new file mode 100644 index 00000000..a8a57ea5 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/CharLiteralTest.tokens @@ -0,0 +1,40 @@ +CHAR_LITERAL("' '") +CHAR_LITERAL("'\t'") +CHAR_LITERAL("'a'") +CHAR_LITERAL("'A'") +CHAR_LITERAL("'z'") +CHAR_LITERAL("'Z'") +CHAR_LITERAL("'a'")CHAR_LITERAL("'a'") +CHAR_LITERAL("'1'") +CHAR_LITERAL("'2'") +CHAR_LITERAL("'\n'") +CHAR_LITERAL("'\r'") +CHAR_LITERAL("'\t'") +CHAR_LITERAL("'\b'") +CHAR_LITERAL("'\f'") +CHAR_LITERAL("'\u0030'") +CHAR_LITERAL("'\uABCD'") +CHAR_LITERAL("'\uabcd'") +CHAR_LITERAL("'\\'") +CHAR_LITERAL("'''") +CHAR_LITERAL("'\"'") +CHAR_LITERAL("'\"'") + + +CHAR_LITERAL("'a'") SIMPLE_NAME("a") INVALID_TOKEN("'") +INVALID_TOKEN("'ab'") +INVALID_TOKEN("'\\a'") +INVALID_TOKEN("''") +INVALID_TOKEN("'") +INVALID_TOKEN("'") +INVALID_TOKEN("'") +INVALID_TOKEN("'\\u'") +INVALID_TOKEN("'\\u0'") +INVALID_TOKEN("'\\ua'") +INVALID_TOKEN("'\\uab'") +INVALID_TOKEN("'\\u01a'") +INVALID_TOKEN("'\\uz'") +INVALID_TOKEN("'\\u012z'") + +CHAR_LITERAL("'a'") +INVALID_TOKEN("'") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/CommentTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/CommentTest.smali new file mode 100644 index 00000000..dd246cbf --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/CommentTest.smali @@ -0,0 +1,6 @@ +# +#abcd +# abcd +#0x1234 +0x1234 #0x1234 +invoke-virtual #invoke-virtual \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/CommentTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/CommentTest.tokens new file mode 100644 index 00000000..5f781ade --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/CommentTest.tokens @@ -0,0 +1,6 @@ +LINE_COMMENT("#") WHITE_SPACE("\n") +LINE_COMMENT("#abcd") WHITE_SPACE("\n") +LINE_COMMENT("# abcd") WHITE_SPACE("\n") +LINE_COMMENT("#0x1234") WHITE_SPACE("\n") +INTEGER_LITERAL("0x1234") WHITE_SPACE(" ") LINE_COMMENT("#0x1234") WHITE_SPACE("\n") +INSTRUCTION_FORMAT35c_METHOD("invoke-virtual") WHITE_SPACE(" ") LINE_COMMENT("#invoke-virtual") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/DirectiveTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/DirectiveTest.smali new file mode 100644 index 00000000..5ec54d8b --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/DirectiveTest.smali @@ -0,0 +1,56 @@ + +.class +.super +.implements +.source +.field +.end field +.subannotation +.end subannotation +.annotation +.end annotation +.enum +.method +.end method +.registers +.locals +.array-data +.end array-data +.packed-switch +.end packed-switch +.sparse-switch +.end sparse-switch +.catch +.catchall +.line +.parameter +.end parameter +.local +.end local +.restart local +.prologue +.epilogue + + +.class.super.implements +.class .super .implements +.class .super .implements +.class .super .implements + .class + .super + .implements + + +.blah1234 +.end blah +.local1234 +.super1234 +.super@ +.super.super +.supeer +.end +. +.1234.1234 +. + + diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/DirectiveTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/DirectiveTest.tokens new file mode 100644 index 00000000..0579ad1b --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/DirectiveTest.tokens @@ -0,0 +1,64 @@ +CLASS_DIRECTIVE(".class") +SUPER_DIRECTIVE(".super") +IMPLEMENTS_DIRECTIVE(".implements") +SOURCE_DIRECTIVE(".source") +FIELD_DIRECTIVE(".field") +END_FIELD_DIRECTIVE(".end field") +SUBANNOTATION_DIRECTIVE(".subannotation") +END_SUBANNOTATION_DIRECTIVE(".end subannotation") +ANNOTATION_DIRECTIVE(".annotation") +END_ANNOTATION_DIRECTIVE(".end annotation") +ENUM_DIRECTIVE(".enum") +METHOD_DIRECTIVE(".method") +END_METHOD_DIRECTIVE(".end method") +REGISTERS_DIRECTIVE(".registers") +LOCALS_DIRECTIVE(".locals") +ARRAY_DATA_DIRECTIVE(".array-data") +END_ARRAY_DATA_DIRECTIVE(".end array-data") +PACKED_SWITCH_DIRECTIVE(".packed-switch") +END_PACKED_SWITCH_DIRECTIVE(".end packed-switch") +SPARSE_SWITCH_DIRECTIVE(".sparse-switch") +END_SPARSE_SWITCH_DIRECTIVE(".end sparse-switch") +CATCH_DIRECTIVE(".catch") +CATCHALL_DIRECTIVE(".catchall") +LINE_DIRECTIVE(".line") +PARAMETER_DIRECTIVE(".parameter") +END_PARAMETER_DIRECTIVE(".end parameter") +LOCAL_DIRECTIVE(".local") +END_LOCAL_DIRECTIVE(".end local") +RESTART_LOCAL_DIRECTIVE(".restart local") +PROLOGUE_DIRECTIVE(".prologue") +EPILOGUE_DIRECTIVE(".epilogue") + + +CLASS_DIRECTIVE(".class") +SUPER_DIRECTIVE(".super") +IMPLEMENTS_DIRECTIVE(".implements") +CLASS_DIRECTIVE(".class") +SUPER_DIRECTIVE(".super") +IMPLEMENTS_DIRECTIVE(".implements") +CLASS_DIRECTIVE(".class") +SUPER_DIRECTIVE(".super") +IMPLEMENTS_DIRECTIVE(".implements") +CLASS_DIRECTIVE(".class") +SUPER_DIRECTIVE(".super") +IMPLEMENTS_DIRECTIVE(".implements") +CLASS_DIRECTIVE(".class") +SUPER_DIRECTIVE(".super") +IMPLEMENTS_DIRECTIVE(".implements") + + +INVALID_TOKEN(".blah1234") +INVALID_TOKEN(".end blah") +INVALID_TOKEN(".local1234") +INVALID_TOKEN(".super1234") +SUPER_DIRECTIVE(".super") +INVALID_TOKEN("@") +SUPER_DIRECTIVE(".super") +SUPER_DIRECTIVE(".super") +INVALID_TOKEN(".supeer") +INVALID_TOKEN(".end") +INVALID_TOKEN(".") +DOUBLE_LITERAL(".1234") +DOUBLE_LITERAL(".1234") +INVALID_TOKEN(".") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/FloatLiteralTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/FloatLiteralTest.smali new file mode 100644 index 00000000..71319ffd --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/FloatLiteralTest.smali @@ -0,0 +1,242 @@ +1234E9 +1234e9 +1234e-9 +-1234e9 +-1234e-9 + +1234E9d +1234e9D +1234e-9d +-1234e9D +-1234e-9d + +1234E9F +1234e9F +1234e-9f +-1234e9F +-1234e-9f + +0x123ABCP1 +0x123ABCp1 +0x123ABCp-1 +-0x123ABCp1 +-0x123ABCp-1 + +0x123ABCP1D +0x123ABCp1D +0x123ABCp-1D +-0x123ABCp1d +-0x123ABCp-1d + +0x123ABCP1f +0x123ABCp1F +0x123ABCp-1f +-0x123ABCp1F +-0x123ABCp-1F + +infinity +-infinity +INFINITY +-INFINITY +InFiNiTy +-InFiNiTy + +infinityd +-infinityD +INFINITYD +-INFINITYD +InFiNiTyd +-InFiNiTyd + +infinityf +-infinityf +INFINITYF +-INFINITYF +InFiNiTyF +-InFiNiTyF + +nan +NAN +NaN +nAn + +nanD +NAND +NaND +nAnd + +nanf +NANf +NaNf +nAnF + +1234. +1234.e10 +1234.2 +1234.2e2 +1234.e-10 +-1234. +-1234.e10 +-1234.2 +-1234.2e2 +-1234.e-10 + +1234.d +1234.e10D +1234.2D +1234.2e2D +1234.e-10D +-1234.D +-1234.e10D +-1234.2d +-1234.2e2d +-1234.e-10D + +1234.F +1234.e10F +1234.2f +1234.2e2f +1234.e-10F +-1234.F +-1234.e10F +-1234.2f +-1234.2e2f +-1234.e-10F + + +.2 +.2e2 +.2e-2 +-.2 +-.2e2 +-.2e-2 + + +.2D +.2e2D +.2e-2D +-.2d +-.2e2d +-.2e-2d + +.2f +.2e2F +.2e-2f +-.2F +-.2e2F +-.2e-2F + +0x12AB.p10 +0x12AB.p-10 +0x12AB.12ABp10 +0x12AB.12ABp-10 +-0x12AB.p10 +-0x12AB.p-10 +-0x12AB.12ABp10 +-0x12AB.12ABp-10 + +0x12AB.p10D +0x12AB.p-10D +0x12AB.12ABp10d +0x12AB.12ABp-10D +-0x12AB.p10D +-0x12AB.p-10d +-0x12AB.12ABp10D +-0x12AB.12ABp-10D + +0x12AB.p10f +0x12AB.p-10f +0x12AB.12ABp10f +0x12AB.12ABp-10f +-0x12AB.p10f +-0x12AB.p-10f +-0x12AB.12ABp10f +-0x12AB.12ABp-10F + +0x.12ABp10 +0x.12ABp-10 +-0x.12ABp10 +-0x.12ABp-10 +0x.1234p10 + +0x.12ABp10d +0x.12ABp-10D +-0x.12ABp10D +-0x.12ABp-10D +0x.1234p10D + +0x.12ABp10F +0x.12ABp-10f +-0x.12ABp10f +-0x.12ABp-10f +0x.1234p10f + +1234F +1234f +-1234F +-1234f + + +1234D +1234d +-1234D +-1234d + + + + +1234e +1234eA +1234eZ +1234e- +1234e-A +1234e-Z +-1234e +-1234eA +-1234eZ +-1234e- +-1234e-A +-1234e-Z + +0x123ABCp +0x123ABCpA +0x123ABCpZ +0x123ABCp- +0x123ABCp-A +0x123ABCp-Z +-0x123ABCp +-0x123ABCpA +-0x123ABCpZ +-0x123ABCp- +-0x123ABCp-A +-0x123ABCp-Z +0x123ABCDE1 + +infinitye +-infinitye +infinityp +-infinityp + +nane +NANp +NaNE +nAnP + +1234.e +1234.1234e +1234.e- +-1234.e +-1234.e- +1234.p +1234.p- +-1234.p +-1234.p- + +.1234e +.e10 +.p10 + +1234abcf +1234abcF +1234abcd +1234abcD \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/FloatLiteralTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/FloatLiteralTest.tokens new file mode 100644 index 00000000..0945971c --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/FloatLiteralTest.tokens @@ -0,0 +1,241 @@ +DOUBLE_LITERAL_OR_ID("1234E9") +DOUBLE_LITERAL_OR_ID("1234e9") +DOUBLE_LITERAL_OR_ID("1234e-9") +DOUBLE_LITERAL_OR_ID("-1234e9") +DOUBLE_LITERAL_OR_ID("-1234e-9") + +DOUBLE_LITERAL_OR_ID("1234E9d") +DOUBLE_LITERAL_OR_ID("1234e9D") +DOUBLE_LITERAL_OR_ID("1234e-9d") +DOUBLE_LITERAL_OR_ID("-1234e9D") +DOUBLE_LITERAL_OR_ID("-1234e-9d") + +FLOAT_LITERAL_OR_ID("1234E9F") +FLOAT_LITERAL_OR_ID("1234e9F") +FLOAT_LITERAL_OR_ID("1234e-9f") +FLOAT_LITERAL_OR_ID("-1234e9F") +FLOAT_LITERAL_OR_ID("-1234e-9f") + +DOUBLE_LITERAL_OR_ID("0x123ABCP1") +DOUBLE_LITERAL_OR_ID("0x123ABCp1") +DOUBLE_LITERAL_OR_ID("0x123ABCp-1") +DOUBLE_LITERAL_OR_ID("-0x123ABCp1") +DOUBLE_LITERAL_OR_ID("-0x123ABCp-1") + +DOUBLE_LITERAL_OR_ID("0x123ABCP1D") +DOUBLE_LITERAL_OR_ID("0x123ABCp1D") +DOUBLE_LITERAL_OR_ID("0x123ABCp-1D") +DOUBLE_LITERAL_OR_ID("-0x123ABCp1d") +DOUBLE_LITERAL_OR_ID("-0x123ABCp-1d") + +FLOAT_LITERAL_OR_ID("0x123ABCP1f") +FLOAT_LITERAL_OR_ID("0x123ABCp1F") +FLOAT_LITERAL_OR_ID("0x123ABCp-1f") +FLOAT_LITERAL_OR_ID("-0x123ABCp1F") +FLOAT_LITERAL_OR_ID("-0x123ABCp-1F") + +DOUBLE_LITERAL_OR_ID("infinity") +DOUBLE_LITERAL_OR_ID("-infinity") +DOUBLE_LITERAL_OR_ID("INFINITY") +DOUBLE_LITERAL_OR_ID("-INFINITY") +DOUBLE_LITERAL_OR_ID("InFiNiTy") +DOUBLE_LITERAL_OR_ID("-InFiNiTy") + +DOUBLE_LITERAL_OR_ID("infinityd") +DOUBLE_LITERAL_OR_ID("-infinityD") +DOUBLE_LITERAL_OR_ID("INFINITYD") +DOUBLE_LITERAL_OR_ID("-INFINITYD") +DOUBLE_LITERAL_OR_ID("InFiNiTyd") +DOUBLE_LITERAL_OR_ID("-InFiNiTyd") + +FLOAT_LITERAL_OR_ID("infinityf") +FLOAT_LITERAL_OR_ID("-infinityf") +FLOAT_LITERAL_OR_ID("INFINITYF") +FLOAT_LITERAL_OR_ID("-INFINITYF") +FLOAT_LITERAL_OR_ID("InFiNiTyF") +FLOAT_LITERAL_OR_ID("-InFiNiTyF") + +DOUBLE_LITERAL_OR_ID("nan") +DOUBLE_LITERAL_OR_ID("NAN") +DOUBLE_LITERAL_OR_ID("NaN") +DOUBLE_LITERAL_OR_ID("nAn") + +DOUBLE_LITERAL_OR_ID("nanD") +DOUBLE_LITERAL_OR_ID("NAND") +DOUBLE_LITERAL_OR_ID("NaND") +DOUBLE_LITERAL_OR_ID("nAnd") + +FLOAT_LITERAL_OR_ID("nanf") +FLOAT_LITERAL_OR_ID("NANf") +FLOAT_LITERAL_OR_ID("NaNf") +FLOAT_LITERAL_OR_ID("nAnF") + +DOUBLE_LITERAL("1234.") +DOUBLE_LITERAL("1234.e10") +DOUBLE_LITERAL("1234.2") +DOUBLE_LITERAL("1234.2e2") +DOUBLE_LITERAL("1234.e-10") +DOUBLE_LITERAL("-1234.") +DOUBLE_LITERAL("-1234.e10") +DOUBLE_LITERAL("-1234.2") +DOUBLE_LITERAL("-1234.2e2") +DOUBLE_LITERAL("-1234.e-10") + +DOUBLE_LITERAL("1234.d") +DOUBLE_LITERAL("1234.e10D") +DOUBLE_LITERAL("1234.2D") +DOUBLE_LITERAL("1234.2e2D") +DOUBLE_LITERAL("1234.e-10D") +DOUBLE_LITERAL("-1234.D") +DOUBLE_LITERAL("-1234.e10D") +DOUBLE_LITERAL("-1234.2d") +DOUBLE_LITERAL("-1234.2e2d") +DOUBLE_LITERAL("-1234.e-10D") + +FLOAT_LITERAL("1234.F") +FLOAT_LITERAL("1234.e10F") +FLOAT_LITERAL("1234.2f") +FLOAT_LITERAL("1234.2e2f") +FLOAT_LITERAL("1234.e-10F") +FLOAT_LITERAL("-1234.F") +FLOAT_LITERAL("-1234.e10F") +FLOAT_LITERAL("-1234.2f") +FLOAT_LITERAL("-1234.2e2f") +FLOAT_LITERAL("-1234.e-10F") + + +DOUBLE_LITERAL(".2") +DOUBLE_LITERAL(".2e2") +DOUBLE_LITERAL(".2e-2") +DOUBLE_LITERAL("-.2") +DOUBLE_LITERAL("-.2e2") +DOUBLE_LITERAL("-.2e-2") + + +DOUBLE_LITERAL(".2D") +DOUBLE_LITERAL(".2e2D") +DOUBLE_LITERAL(".2e-2D") +DOUBLE_LITERAL("-.2d") +DOUBLE_LITERAL("-.2e2d") +DOUBLE_LITERAL("-.2e-2d") + +FLOAT_LITERAL(".2f") +FLOAT_LITERAL(".2e2F") +FLOAT_LITERAL(".2e-2f") +FLOAT_LITERAL("-.2F") +FLOAT_LITERAL("-.2e2F") +FLOAT_LITERAL("-.2e-2F") + +DOUBLE_LITERAL("0x12AB.p10") +DOUBLE_LITERAL("0x12AB.p-10") +DOUBLE_LITERAL("0x12AB.12ABp10") +DOUBLE_LITERAL("0x12AB.12ABp-10") +DOUBLE_LITERAL("-0x12AB.p10") +DOUBLE_LITERAL("-0x12AB.p-10") +DOUBLE_LITERAL("-0x12AB.12ABp10") +DOUBLE_LITERAL("-0x12AB.12ABp-10") + +DOUBLE_LITERAL("0x12AB.p10D") +DOUBLE_LITERAL("0x12AB.p-10D") +DOUBLE_LITERAL("0x12AB.12ABp10d") +DOUBLE_LITERAL("0x12AB.12ABp-10D") +DOUBLE_LITERAL("-0x12AB.p10D") +DOUBLE_LITERAL("-0x12AB.p-10d") +DOUBLE_LITERAL("-0x12AB.12ABp10D") +DOUBLE_LITERAL("-0x12AB.12ABp-10D") + +FLOAT_LITERAL("0x12AB.p10f") +FLOAT_LITERAL("0x12AB.p-10f") +FLOAT_LITERAL("0x12AB.12ABp10f") +FLOAT_LITERAL("0x12AB.12ABp-10f") +FLOAT_LITERAL("-0x12AB.p10f") +FLOAT_LITERAL("-0x12AB.p-10f") +FLOAT_LITERAL("-0x12AB.12ABp10f") +FLOAT_LITERAL("-0x12AB.12ABp-10F") + +DOUBLE_LITERAL("0x.12ABp10") +DOUBLE_LITERAL("0x.12ABp-10") +DOUBLE_LITERAL("-0x.12ABp10") +DOUBLE_LITERAL("-0x.12ABp-10") +DOUBLE_LITERAL("0x.1234p10") + +DOUBLE_LITERAL("0x.12ABp10d") +DOUBLE_LITERAL("0x.12ABp-10D") +DOUBLE_LITERAL("-0x.12ABp10D") +DOUBLE_LITERAL("-0x.12ABp-10D") +DOUBLE_LITERAL("0x.1234p10D") + +FLOAT_LITERAL("0x.12ABp10F") +FLOAT_LITERAL("0x.12ABp-10f") +FLOAT_LITERAL("-0x.12ABp10f") +FLOAT_LITERAL("-0x.12ABp-10f") +FLOAT_LITERAL("0x.1234p10f") + +FLOAT_LITERAL_OR_ID("1234F") +FLOAT_LITERAL_OR_ID("1234f") +FLOAT_LITERAL_OR_ID("-1234F") +FLOAT_LITERAL_OR_ID("-1234f") + +DOUBLE_LITERAL_OR_ID("1234D") +DOUBLE_LITERAL_OR_ID("1234d") +DOUBLE_LITERAL_OR_ID("-1234D") +DOUBLE_LITERAL_OR_ID("-1234d") + + + + +SIMPLE_NAME("1234e") +SIMPLE_NAME("1234eA") +SIMPLE_NAME("1234eZ") +SIMPLE_NAME("1234e-") +SIMPLE_NAME("1234e-A") +SIMPLE_NAME("1234e-Z") +SIMPLE_NAME("-1234e") +SIMPLE_NAME("-1234eA") +SIMPLE_NAME("-1234eZ") +SIMPLE_NAME("-1234e-") +SIMPLE_NAME("-1234e-A") +SIMPLE_NAME("-1234e-Z") + +SIMPLE_NAME("0x123ABCp") +SIMPLE_NAME("0x123ABCpA") +SIMPLE_NAME("0x123ABCpZ") +SIMPLE_NAME("0x123ABCp-") +SIMPLE_NAME("0x123ABCp-A") +SIMPLE_NAME("0x123ABCp-Z") +SIMPLE_NAME("-0x123ABCp") +SIMPLE_NAME("-0x123ABCpA") +SIMPLE_NAME("-0x123ABCpZ") +SIMPLE_NAME("-0x123ABCp-") +SIMPLE_NAME("-0x123ABCp-A") +SIMPLE_NAME("-0x123ABCp-Z") +INTEGER_LITERAL("0x123ABCDE1") + +SIMPLE_NAME("infinitye") +SIMPLE_NAME("-infinitye") +SIMPLE_NAME("infinityp") +SIMPLE_NAME("-infinityp") + +SIMPLE_NAME("nane") +SIMPLE_NAME("NANp") +SIMPLE_NAME("NaNE") +SIMPLE_NAME("nAnP") + +DOUBLE_LITERAL("1234.") SIMPLE_NAME("e") +DOUBLE_LITERAL("1234.1234") SIMPLE_NAME("e") +DOUBLE_LITERAL("1234.") SIMPLE_NAME("e-") +DOUBLE_LITERAL("-1234.") SIMPLE_NAME("e") +DOUBLE_LITERAL("-1234.") SIMPLE_NAME("e-") +DOUBLE_LITERAL("1234.") SIMPLE_NAME("p") +DOUBLE_LITERAL("1234.") SIMPLE_NAME("p-") +DOUBLE_LITERAL("-1234.") SIMPLE_NAME("p") +DOUBLE_LITERAL("-1234.") SIMPLE_NAME("p-") + +DOUBLE_LITERAL(".1234") SIMPLE_NAME("e") +INVALID_TOKEN(".e10") +INVALID_TOKEN(".p10") + +SIMPLE_NAME("1234abcf") +SIMPLE_NAME("1234abcF") +SIMPLE_NAME("1234abcd") +SIMPLE_NAME("1234abcD") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/InstructionTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/InstructionTest.smali new file mode 100644 index 00000000..2247adc0 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/InstructionTest.smali @@ -0,0 +1,228 @@ +goto +return-void +nop +return-void-barrier +const/4 +move-result +move-result-wide +move-result-object +move-exception +return +return-wide +move +move-wide +move-object +array-length +neg-int +not-int +neg-long +not-long +neg-float +neg-double +int-to-long +int-to-float +int-to-double +long-to-int +long-to-float +long-to-double +float-to-int +float-to-long +float-to-double +double-to-int +double-to-long +add-int/2addr +sub-int/2addr +mul-int/2addr +div-int/2addr +rem-int/2addr +and-int/2addr +or-int/2addr +xor-int/2addr +shl-int/2addr +shr-int/2addr +ushr-int/2addr +add-long/2addr +sub-long/2addr +mul-long/2addr +div-long/2addr +rem-long/2addr +and-long/2addr +or-long/2addr +xor-long/2addr +shl-long/2addr +shr-long/2addr +ushr-long/2addr +add-float/2addr +sub-float/2addr +mul-float/2addr +div-float/2addr +rem-float/2addr +add-double/2addr +throw-verification-error +goto/16 +sget +sget-wide +sget-object +sget-boolean +sget-byte +sget-char +sget-short +sput +sput-wide +sput-object +sput-boolean +sput-byte +sput-char +sput-short +sget-volatile +sget-wide-volatile +sget-object-volatile +sput-volatile +sput-wide-volatile +sput-object-volatile +const-string +check-cast +new-instance +const-class +const/high16 +const-wide/high16 +const/16 +const-wide/16 +if-eqz +if-nez +if-ltz +if-gez +if-gtz +if-lez +add-int/lit8 +rsub-int/lit8 +mul-int/lit8 +div-int/lit8 +rem-int/lit8 +and-int/lit8 +iget +iget-wide +iget-object +iget-boolean +iget-byte +iget-char +iget-short +iput +iput-wide +iput-object +iput-boolean +iput-byte +iput-char +iput-short +iget-volatile +iget-wide-volatile +iget-object-volatile +iput-volatile +iput-wide-volatile +iput-object-volatile +instance-of +new-array +iget-quick +iget-wide-quick +iget-object-quick +iput-quick +iput-wide-quick +iput-object-quick +rsub-int +add-int/lit16 +mul-int/lit16 +div-int/lit16 +rem-int/lit16 +and-int/lit16 +or-int/lit16 +if-eq +if-ne +if-lt +if-ge +if-gt +if-le +move/from16 +move-wide/from16 +move-object/from16 +cmpl-float +cmpg-float +cmpl-double +cmpg-double +cmp-long +aget +aget-wide +aget-object +aget-boolean +aget-byte +aget-char +aget-short +aput +aput-wide +aput-object +aput-boolean +aput-byte +aput-char +aput-short +add-int +sub-int +mul-int +div-int +rem-int +and-int +or-int +xor-int +shl-int +shr-int +ushr-int +add-long +sub-long +mul-long +div-long +rem-long +and-long +or-long +xor-long +shl-long +shr-long +ushr-long +add-float +sub-float +mul-float +div-float +rem-float +add-double +sub-double +mul-double +div-double +goto/32 +const-string/jumbo +const +const-wide/32 +fill-array-data +packed-switch +sparse-switch +move/16 +move-wide/16 +move-object/16 +invoke-virtual +invoke-super +invoke-direct +invoke-static +invoke-interface +filled-new-array +invoke-direct-empty +invoke-object-init/range +throw-verification-error +execute-inline +invoke-virtual-quick +invoke-super-quick +invoke-virtual/range +invoke-super/range +invoke-direct/range +invoke-static/range +invoke-interface/range +filled-new-array/range +execute-inline/range +invoke-virtual-quick/range +invoke-super-quick/range +const-wide diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/InstructionTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/InstructionTest.tokens new file mode 100644 index 00000000..2adb364d --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/InstructionTest.tokens @@ -0,0 +1,228 @@ +INSTRUCTION_FORMAT10t("goto") +INSTRUCTION_FORMAT10x("return-void") +INSTRUCTION_FORMAT10x("nop") +INSTRUCTION_FORMAT10x_ODEX("return-void-barrier") +INSTRUCTION_FORMAT11n("const/4") +INSTRUCTION_FORMAT11x("move-result") +INSTRUCTION_FORMAT11x("move-result-wide") +INSTRUCTION_FORMAT11x("move-result-object") +INSTRUCTION_FORMAT11x("move-exception") +INSTRUCTION_FORMAT11x("return") +INSTRUCTION_FORMAT11x("return-wide") +INSTRUCTION_FORMAT12x_OR_ID("move") +INSTRUCTION_FORMAT12x_OR_ID("move-wide") +INSTRUCTION_FORMAT12x_OR_ID("move-object") +INSTRUCTION_FORMAT12x_OR_ID("array-length") +INSTRUCTION_FORMAT12x_OR_ID("neg-int") +INSTRUCTION_FORMAT12x_OR_ID("not-int") +INSTRUCTION_FORMAT12x_OR_ID("neg-long") +INSTRUCTION_FORMAT12x_OR_ID("not-long") +INSTRUCTION_FORMAT12x_OR_ID("neg-float") +INSTRUCTION_FORMAT12x_OR_ID("neg-double") +INSTRUCTION_FORMAT12x_OR_ID("int-to-long") +INSTRUCTION_FORMAT12x_OR_ID("int-to-float") +INSTRUCTION_FORMAT12x_OR_ID("int-to-double") +INSTRUCTION_FORMAT12x_OR_ID("long-to-int") +INSTRUCTION_FORMAT12x_OR_ID("long-to-float") +INSTRUCTION_FORMAT12x_OR_ID("long-to-double") +INSTRUCTION_FORMAT12x_OR_ID("float-to-int") +INSTRUCTION_FORMAT12x_OR_ID("float-to-long") +INSTRUCTION_FORMAT12x_OR_ID("float-to-double") +INSTRUCTION_FORMAT12x_OR_ID("double-to-int") +INSTRUCTION_FORMAT12x_OR_ID("double-to-long") +INSTRUCTION_FORMAT12x("add-int/2addr") +INSTRUCTION_FORMAT12x("sub-int/2addr") +INSTRUCTION_FORMAT12x("mul-int/2addr") +INSTRUCTION_FORMAT12x("div-int/2addr") +INSTRUCTION_FORMAT12x("rem-int/2addr") +INSTRUCTION_FORMAT12x("and-int/2addr") +INSTRUCTION_FORMAT12x("or-int/2addr") +INSTRUCTION_FORMAT12x("xor-int/2addr") +INSTRUCTION_FORMAT12x("shl-int/2addr") +INSTRUCTION_FORMAT12x("shr-int/2addr") +INSTRUCTION_FORMAT12x("ushr-int/2addr") +INSTRUCTION_FORMAT12x("add-long/2addr") +INSTRUCTION_FORMAT12x("sub-long/2addr") +INSTRUCTION_FORMAT12x("mul-long/2addr") +INSTRUCTION_FORMAT12x("div-long/2addr") +INSTRUCTION_FORMAT12x("rem-long/2addr") +INSTRUCTION_FORMAT12x("and-long/2addr") +INSTRUCTION_FORMAT12x("or-long/2addr") +INSTRUCTION_FORMAT12x("xor-long/2addr") +INSTRUCTION_FORMAT12x("shl-long/2addr") +INSTRUCTION_FORMAT12x("shr-long/2addr") +INSTRUCTION_FORMAT12x("ushr-long/2addr") +INSTRUCTION_FORMAT12x("add-float/2addr") +INSTRUCTION_FORMAT12x("sub-float/2addr") +INSTRUCTION_FORMAT12x("mul-float/2addr") +INSTRUCTION_FORMAT12x("div-float/2addr") +INSTRUCTION_FORMAT12x("rem-float/2addr") +INSTRUCTION_FORMAT12x("add-double/2addr") +INSTRUCTION_FORMAT20bc("throw-verification-error") +INSTRUCTION_FORMAT20t("goto/16") +INSTRUCTION_FORMAT21c_FIELD("sget") +INSTRUCTION_FORMAT21c_FIELD("sget-wide") +INSTRUCTION_FORMAT21c_FIELD("sget-object") +INSTRUCTION_FORMAT21c_FIELD("sget-boolean") +INSTRUCTION_FORMAT21c_FIELD("sget-byte") +INSTRUCTION_FORMAT21c_FIELD("sget-char") +INSTRUCTION_FORMAT21c_FIELD("sget-short") +INSTRUCTION_FORMAT21c_FIELD("sput") +INSTRUCTION_FORMAT21c_FIELD("sput-wide") +INSTRUCTION_FORMAT21c_FIELD("sput-object") +INSTRUCTION_FORMAT21c_FIELD("sput-boolean") +INSTRUCTION_FORMAT21c_FIELD("sput-byte") +INSTRUCTION_FORMAT21c_FIELD("sput-char") +INSTRUCTION_FORMAT21c_FIELD("sput-short") +INSTRUCTION_FORMAT21c_FIELD_ODEX("sget-volatile") +INSTRUCTION_FORMAT21c_FIELD_ODEX("sget-wide-volatile") +INSTRUCTION_FORMAT21c_FIELD_ODEX("sget-object-volatile") +INSTRUCTION_FORMAT21c_FIELD_ODEX("sput-volatile") +INSTRUCTION_FORMAT21c_FIELD_ODEX("sput-wide-volatile") +INSTRUCTION_FORMAT21c_FIELD_ODEX("sput-object-volatile") +INSTRUCTION_FORMAT21c_STRING("const-string") +INSTRUCTION_FORMAT21c_TYPE("check-cast") +INSTRUCTION_FORMAT21c_TYPE("new-instance") +INSTRUCTION_FORMAT21c_TYPE("const-class") +INSTRUCTION_FORMAT21h("const/high16") +INSTRUCTION_FORMAT21h("const-wide/high16") +INSTRUCTION_FORMAT21s("const/16") +INSTRUCTION_FORMAT21s("const-wide/16") +INSTRUCTION_FORMAT21t("if-eqz") +INSTRUCTION_FORMAT21t("if-nez") +INSTRUCTION_FORMAT21t("if-ltz") +INSTRUCTION_FORMAT21t("if-gez") +INSTRUCTION_FORMAT21t("if-gtz") +INSTRUCTION_FORMAT21t("if-lez") +INSTRUCTION_FORMAT22b("add-int/lit8") +INSTRUCTION_FORMAT22b("rsub-int/lit8") +INSTRUCTION_FORMAT22b("mul-int/lit8") +INSTRUCTION_FORMAT22b("div-int/lit8") +INSTRUCTION_FORMAT22b("rem-int/lit8") +INSTRUCTION_FORMAT22b("and-int/lit8") +INSTRUCTION_FORMAT22c_FIELD("iget") +INSTRUCTION_FORMAT22c_FIELD("iget-wide") +INSTRUCTION_FORMAT22c_FIELD("iget-object") +INSTRUCTION_FORMAT22c_FIELD("iget-boolean") +INSTRUCTION_FORMAT22c_FIELD("iget-byte") +INSTRUCTION_FORMAT22c_FIELD("iget-char") +INSTRUCTION_FORMAT22c_FIELD("iget-short") +INSTRUCTION_FORMAT22c_FIELD("iput") +INSTRUCTION_FORMAT22c_FIELD("iput-wide") +INSTRUCTION_FORMAT22c_FIELD("iput-object") +INSTRUCTION_FORMAT22c_FIELD("iput-boolean") +INSTRUCTION_FORMAT22c_FIELD("iput-byte") +INSTRUCTION_FORMAT22c_FIELD("iput-char") +INSTRUCTION_FORMAT22c_FIELD("iput-short") +INSTRUCTION_FORMAT22c_FIELD_ODEX("iget-volatile") +INSTRUCTION_FORMAT22c_FIELD_ODEX("iget-wide-volatile") +INSTRUCTION_FORMAT22c_FIELD_ODEX("iget-object-volatile") +INSTRUCTION_FORMAT22c_FIELD_ODEX("iput-volatile") +INSTRUCTION_FORMAT22c_FIELD_ODEX("iput-wide-volatile") +INSTRUCTION_FORMAT22c_FIELD_ODEX("iput-object-volatile") +INSTRUCTION_FORMAT22c_TYPE("instance-of") +INSTRUCTION_FORMAT22c_TYPE("new-array") +INSTRUCTION_FORMAT22cs_FIELD("iget-quick") +INSTRUCTION_FORMAT22cs_FIELD("iget-wide-quick") +INSTRUCTION_FORMAT22cs_FIELD("iget-object-quick") +INSTRUCTION_FORMAT22cs_FIELD("iput-quick") +INSTRUCTION_FORMAT22cs_FIELD("iput-wide-quick") +INSTRUCTION_FORMAT22cs_FIELD("iput-object-quick") +INSTRUCTION_FORMAT22s_OR_ID("rsub-int") +INSTRUCTION_FORMAT22s("add-int/lit16") +INSTRUCTION_FORMAT22s("mul-int/lit16") +INSTRUCTION_FORMAT22s("div-int/lit16") +INSTRUCTION_FORMAT22s("rem-int/lit16") +INSTRUCTION_FORMAT22s("and-int/lit16") +INSTRUCTION_FORMAT22s("or-int/lit16") +INSTRUCTION_FORMAT22t("if-eq") +INSTRUCTION_FORMAT22t("if-ne") +INSTRUCTION_FORMAT22t("if-lt") +INSTRUCTION_FORMAT22t("if-ge") +INSTRUCTION_FORMAT22t("if-gt") +INSTRUCTION_FORMAT22t("if-le") +INSTRUCTION_FORMAT22x("move/from16") +INSTRUCTION_FORMAT22x("move-wide/from16") +INSTRUCTION_FORMAT22x("move-object/from16") +INSTRUCTION_FORMAT23x("cmpl-float") +INSTRUCTION_FORMAT23x("cmpg-float") +INSTRUCTION_FORMAT23x("cmpl-double") +INSTRUCTION_FORMAT23x("cmpg-double") +INSTRUCTION_FORMAT23x("cmp-long") +INSTRUCTION_FORMAT23x("aget") +INSTRUCTION_FORMAT23x("aget-wide") +INSTRUCTION_FORMAT23x("aget-object") +INSTRUCTION_FORMAT23x("aget-boolean") +INSTRUCTION_FORMAT23x("aget-byte") +INSTRUCTION_FORMAT23x("aget-char") +INSTRUCTION_FORMAT23x("aget-short") +INSTRUCTION_FORMAT23x("aput") +INSTRUCTION_FORMAT23x("aput-wide") +INSTRUCTION_FORMAT23x("aput-object") +INSTRUCTION_FORMAT23x("aput-boolean") +INSTRUCTION_FORMAT23x("aput-byte") +INSTRUCTION_FORMAT23x("aput-char") +INSTRUCTION_FORMAT23x("aput-short") +INSTRUCTION_FORMAT23x("add-int") +INSTRUCTION_FORMAT23x("sub-int") +INSTRUCTION_FORMAT23x("mul-int") +INSTRUCTION_FORMAT23x("div-int") +INSTRUCTION_FORMAT23x("rem-int") +INSTRUCTION_FORMAT23x("and-int") +INSTRUCTION_FORMAT23x("or-int") +INSTRUCTION_FORMAT23x("xor-int") +INSTRUCTION_FORMAT23x("shl-int") +INSTRUCTION_FORMAT23x("shr-int") +INSTRUCTION_FORMAT23x("ushr-int") +INSTRUCTION_FORMAT23x("add-long") +INSTRUCTION_FORMAT23x("sub-long") +INSTRUCTION_FORMAT23x("mul-long") +INSTRUCTION_FORMAT23x("div-long") +INSTRUCTION_FORMAT23x("rem-long") +INSTRUCTION_FORMAT23x("and-long") +INSTRUCTION_FORMAT23x("or-long") +INSTRUCTION_FORMAT23x("xor-long") +INSTRUCTION_FORMAT23x("shl-long") +INSTRUCTION_FORMAT23x("shr-long") +INSTRUCTION_FORMAT23x("ushr-long") +INSTRUCTION_FORMAT23x("add-float") +INSTRUCTION_FORMAT23x("sub-float") +INSTRUCTION_FORMAT23x("mul-float") +INSTRUCTION_FORMAT23x("div-float") +INSTRUCTION_FORMAT23x("rem-float") +INSTRUCTION_FORMAT23x("add-double") +INSTRUCTION_FORMAT23x("sub-double") +INSTRUCTION_FORMAT23x("mul-double") +INSTRUCTION_FORMAT23x("div-double") +INSTRUCTION_FORMAT30t("goto/32") +INSTRUCTION_FORMAT31c("const-string/jumbo") +INSTRUCTION_FORMAT31i_OR_ID("const") +INSTRUCTION_FORMAT31i("const-wide/32") +INSTRUCTION_FORMAT31t("fill-array-data") +INSTRUCTION_FORMAT31t("packed-switch") +INSTRUCTION_FORMAT31t("sparse-switch") +INSTRUCTION_FORMAT32x("move/16") +INSTRUCTION_FORMAT32x("move-wide/16") +INSTRUCTION_FORMAT32x("move-object/16") +INSTRUCTION_FORMAT35c_METHOD("invoke-virtual") +INSTRUCTION_FORMAT35c_METHOD("invoke-super") +INSTRUCTION_FORMAT35c_METHOD("invoke-direct") +INSTRUCTION_FORMAT35c_METHOD("invoke-static") +INSTRUCTION_FORMAT35c_METHOD("invoke-interface") +INSTRUCTION_FORMAT35c_TYPE("filled-new-array") +INSTRUCTION_FORMAT35c_METHOD_ODEX("invoke-direct-empty") +INSTRUCTION_FORMAT3rc_METHOD_ODEX("invoke-object-init/range") +INSTRUCTION_FORMAT20bc("throw-verification-error") +INSTRUCTION_FORMAT35mi_METHOD("execute-inline") +INSTRUCTION_FORMAT35ms_METHOD("invoke-virtual-quick") +INSTRUCTION_FORMAT35ms_METHOD("invoke-super-quick") +INSTRUCTION_FORMAT3rc_METHOD("invoke-virtual/range") +INSTRUCTION_FORMAT3rc_METHOD("invoke-super/range") +INSTRUCTION_FORMAT3rc_METHOD("invoke-direct/range") +INSTRUCTION_FORMAT3rc_METHOD("invoke-static/range") +INSTRUCTION_FORMAT3rc_METHOD("invoke-interface/range") +INSTRUCTION_FORMAT3rc_TYPE("filled-new-array/range") +INSTRUCTION_FORMAT3rmi_METHOD("execute-inline/range") +INSTRUCTION_FORMAT3rms_METHOD("invoke-virtual-quick/range") +INSTRUCTION_FORMAT3rms_METHOD("invoke-super-quick/range") +INSTRUCTION_FORMAT51l("const-wide") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/IntegerLiteralTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/IntegerLiteralTest.smali new file mode 100644 index 00000000..68eae042 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/IntegerLiteralTest.smali @@ -0,0 +1,45 @@ +0x0 +0x00 +0x1 +0x12345678 +0x7fffffff +0x80000000 +0xFFFFFFFF +-0x00 +-0x01 +-0x12345678 +-0x80000000 +-0x1FFFFFFF +-0x80000001 +-0xFFFFFFFF +-0x100000000 +0 +1 +1234567890 +2147483647 +2147483648 +4294967295 +-0 +-1 +-1234567890 +-2147483647 +-2147483648 +-2147483649 +-4294967295 +4294967295 +4294967300 +8589934592 +00 +01 +012345670123 +017777777777 +020000000000 +037777777777 +-00 +-01 +-012345670123 +-017777777777 +-020000000000 +-020000000001 +-037777777777 +0400000000000 \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/IntegerLiteralTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/IntegerLiteralTest.tokens new file mode 100644 index 00000000..b829c396 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/IntegerLiteralTest.tokens @@ -0,0 +1,45 @@ +INTEGER_LITERAL("0x0") +INTEGER_LITERAL("0x00") +INTEGER_LITERAL("0x1") +INTEGER_LITERAL("0x12345678") +INTEGER_LITERAL("0x7fffffff") +INTEGER_LITERAL("0x80000000") +INTEGER_LITERAL("0xFFFFFFFF") +INTEGER_LITERAL("-0x00") +INTEGER_LITERAL("-0x01") +INTEGER_LITERAL("-0x12345678") +INTEGER_LITERAL("-0x80000000") +INTEGER_LITERAL("-0x1FFFFFFF") +INTEGER_LITERAL("-0x80000001") +INTEGER_LITERAL("-0xFFFFFFFF") +INTEGER_LITERAL("-0x100000000") +INTEGER_LITERAL("0") +INTEGER_LITERAL("1") +INTEGER_LITERAL("1234567890") +INTEGER_LITERAL("2147483647") +INTEGER_LITERAL("2147483648") +INTEGER_LITERAL("4294967295") +INTEGER_LITERAL("-0") +INTEGER_LITERAL("-1") +INTEGER_LITERAL("-1234567890") +INTEGER_LITERAL("-2147483647") +INTEGER_LITERAL("-2147483648") +INTEGER_LITERAL("-2147483649") +INTEGER_LITERAL("-4294967295") +INTEGER_LITERAL("4294967295") +INTEGER_LITERAL("4294967300") +INTEGER_LITERAL("8589934592") +INTEGER_LITERAL("00") +INTEGER_LITERAL("01") +INTEGER_LITERAL("012345670123") +INTEGER_LITERAL("017777777777") +INTEGER_LITERAL("020000000000") +INTEGER_LITERAL("037777777777") +INTEGER_LITERAL("-00") +INTEGER_LITERAL("-01") +INTEGER_LITERAL("-012345670123") +INTEGER_LITERAL("-017777777777") +INTEGER_LITERAL("-020000000000") +INTEGER_LITERAL("-020000000001") +INTEGER_LITERAL("-037777777777") +INTEGER_LITERAL("0400000000000") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/LongLiteralTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/LongLiteralTest.smali new file mode 100644 index 00000000..0cde8545 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/LongLiteralTest.smali @@ -0,0 +1,48 @@ +0x0L +0x00L +0x1L +0x1234567890123456L +0x7fffffffffffffffL +0x8000000000000000L +0xFFFFFFFFFFFFFFFFL +-0x00L +-0x01L +-0x1234567890123456L +-0x8000000000000000L +-0x1fffffffffffffffL +-0x8000000000000001 +-0xFFFFFFFFFFFFFFFF +0x10000000000000000 +0L +1 +1234567890123456789 +1234567890123456789L +9223372036854775807 +9223372036854775808 +18446744073709551615L +-0 +-1 +-1234567890123456789 +-1234567890123456789L +-9223372036854775807 +-9223372036854775808 +-9223372036854775809 +-18446744073709551616 +18446744073709551617 +18446744073709551700 +00 +01 +0123456701234567012345 +0123456701234567012345L +0777777777777777777777 +0100000000000000000000 +0177777777777777777777 +-00 +-01 +-0123456701234567012345 +-0123456701234567012345L +-0777777777777777777777 +-0100000000000000000000 +-0100000000000000000001 +-0177777777777777777777 +-02000000000000000000000 \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/LongLiteralTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/LongLiteralTest.tokens new file mode 100644 index 00000000..f8ab03e8 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/LongLiteralTest.tokens @@ -0,0 +1,48 @@ +LONG_LITERAL("0x0L") +LONG_LITERAL("0x00L") +LONG_LITERAL("0x1L") +LONG_LITERAL("0x1234567890123456L") +LONG_LITERAL("0x7fffffffffffffffL") +LONG_LITERAL("0x8000000000000000L") +LONG_LITERAL("0xFFFFFFFFFFFFFFFFL") +LONG_LITERAL("-0x00L") +LONG_LITERAL("-0x01L") +LONG_LITERAL("-0x1234567890123456L") +LONG_LITERAL("-0x8000000000000000L") +LONG_LITERAL("-0x1fffffffffffffffL") +INTEGER_LITERAL("-0x8000000000000001") +INTEGER_LITERAL("-0xFFFFFFFFFFFFFFFF") +INTEGER_LITERAL("0x10000000000000000") +LONG_LITERAL("0L") +INTEGER_LITERAL("1") +INTEGER_LITERAL("1234567890123456789") +LONG_LITERAL("1234567890123456789L") +INTEGER_LITERAL("9223372036854775807") +INTEGER_LITERAL("9223372036854775808") +LONG_LITERAL("18446744073709551615L") +INTEGER_LITERAL("-0") +INTEGER_LITERAL("-1") +INTEGER_LITERAL("-1234567890123456789") +LONG_LITERAL("-1234567890123456789L") +INTEGER_LITERAL("-9223372036854775807") +INTEGER_LITERAL("-9223372036854775808") +INTEGER_LITERAL("-9223372036854775809") +INTEGER_LITERAL("-18446744073709551616") +INTEGER_LITERAL("18446744073709551617") +INTEGER_LITERAL("18446744073709551700") +INTEGER_LITERAL("00") +INTEGER_LITERAL("01") +INTEGER_LITERAL("0123456701234567012345") +LONG_LITERAL("0123456701234567012345L") +INTEGER_LITERAL("0777777777777777777777") +INTEGER_LITERAL("0100000000000000000000") +INTEGER_LITERAL("0177777777777777777777") +INTEGER_LITERAL("-00") +INTEGER_LITERAL("-01") +INTEGER_LITERAL("-0123456701234567012345") +LONG_LITERAL("-0123456701234567012345L") +INTEGER_LITERAL("-0777777777777777777777") +INTEGER_LITERAL("-0100000000000000000000") +INTEGER_LITERAL("-0100000000000000000001") +INTEGER_LITERAL("-0177777777777777777777") +INTEGER_LITERAL("-02000000000000000000000") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/MiscTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/MiscTest.smali new file mode 100644 index 00000000..32bbd272 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/MiscTest.smali @@ -0,0 +1,78 @@ +true +false +null +p0 +p90 +pab +v0 +v90 +vab + +truefalse + +build +runtime +system + +public +private +protected +static +final +synchronized +bridge +varargs +native +abstract +strictfp +synthetic +constructor +declared-synchronized +interface +enum +annotation +volatile +transient + +no-error +generic-error +no-such-class +no-such-field +no-such-method +illegal-class-access +illegal-field-access +illegal-method-access +class-change-error +instantiation-error + +inline@0xABCD +inline@0x0123 +inline@0x0123ABCD + +vtable@0xABCD +vtable@0x0123 +vtable@0x0123ABCD + +field@0xABCD +field@0x0123 +field@0x0123ABCD + +inline@ +inline@zzz +inline@abcd +vtable@ +vtable@zzz +vtable@abcd +field@ +field@zzz +field@abcd + ++0 ++10 ++01 ++0777 ++0x1234ABC ++1234 + ++08 ++ \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/MiscTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/MiscTest.tokens new file mode 100644 index 00000000..841c9935 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/MiscTest.tokens @@ -0,0 +1,78 @@ +BOOL_LITERAL("true") +BOOL_LITERAL("false") +NULL_LITERAL("null") +REGISTER("p0") +REGISTER("p90") +SIMPLE_NAME("pab") +REGISTER("v0") +REGISTER("v90") +SIMPLE_NAME("vab") + +SIMPLE_NAME("truefalse") + +ANNOTATION_VISIBILITY("build") +ANNOTATION_VISIBILITY("runtime") +ANNOTATION_VISIBILITY("system") + +ACCESS_SPEC("public") +ACCESS_SPEC("private") +ACCESS_SPEC("protected") +ACCESS_SPEC("static") +ACCESS_SPEC("final") +ACCESS_SPEC("synchronized") +ACCESS_SPEC("bridge") +ACCESS_SPEC("varargs") +ACCESS_SPEC("native") +ACCESS_SPEC("abstract") +ACCESS_SPEC("strictfp") +ACCESS_SPEC("synthetic") +ACCESS_SPEC("constructor") +ACCESS_SPEC("declared-synchronized") +ACCESS_SPEC("interface") +ACCESS_SPEC("enum") +ACCESS_SPEC("annotation") +ACCESS_SPEC("volatile") +ACCESS_SPEC("transient") + +VERIFICATION_ERROR_TYPE("no-error") +VERIFICATION_ERROR_TYPE("generic-error") +VERIFICATION_ERROR_TYPE("no-such-class") +VERIFICATION_ERROR_TYPE("no-such-field") +VERIFICATION_ERROR_TYPE("no-such-method") +VERIFICATION_ERROR_TYPE("illegal-class-access") +VERIFICATION_ERROR_TYPE("illegal-field-access") +VERIFICATION_ERROR_TYPE("illegal-method-access") +VERIFICATION_ERROR_TYPE("class-change-error") +VERIFICATION_ERROR_TYPE("instantiation-error") + +INLINE_INDEX("inline@0xABCD") +INLINE_INDEX("inline@0x0123") +INLINE_INDEX("inline@0x0123ABCD") + +VTABLE_INDEX("vtable@0xABCD") +VTABLE_INDEX("vtable@0x0123") +VTABLE_INDEX("vtable@0x0123ABCD") + +FIELD_OFFSET("field@0xABCD") +FIELD_OFFSET("field@0x0123") +FIELD_OFFSET("field@0x0123ABCD") + +SIMPLE_NAME("inline") INVALID_TOKEN("@") +SIMPLE_NAME("inline") INVALID_TOKEN("@") SIMPLE_NAME("zzz") +SIMPLE_NAME("inline") INVALID_TOKEN("@") SIMPLE_NAME("abcd") +SIMPLE_NAME("vtable") INVALID_TOKEN("@") +SIMPLE_NAME("vtable") INVALID_TOKEN("@") SIMPLE_NAME("zzz") +SIMPLE_NAME("vtable") INVALID_TOKEN("@") SIMPLE_NAME("abcd") +SIMPLE_NAME("field") INVALID_TOKEN("@") +SIMPLE_NAME("field") INVALID_TOKEN("@") SIMPLE_NAME("zzz") +SIMPLE_NAME("field") INVALID_TOKEN("@") SIMPLE_NAME("abcd") + +OFFSET("+0") +OFFSET("+10") +OFFSET("+01") +OFFSET("+0777") +OFFSET("+0x1234ABC") +OFFSET("+1234") + +OFFSET("+0") INTEGER_LITERAL("8") +INVALID_TOKEN("+") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/RealSmaliFileTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/RealSmaliFileTest.smali new file mode 100644 index 00000000..2062f75f --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/RealSmaliFileTest.smali @@ -0,0 +1,294 @@ +.class public final Lcom/android/internal/telephony/cdma/RuimFileHandler; +.super Lcom/android/internal/telephony/IccFileHandler; +.source "RuimFileHandler.java" + + +# static fields +.field static final LOG_TAG:Ljava/lang/String; = "CDMA" + +.field static final CARD_MAX_APPS:I = 0x8 + +.field mWifiOnUid:I + +.field public static mWifiRunning:Z = false +.field public static mWifiRunning2:Z = true + +.field mVideoOnTimer:Lcom/android/internal/os/BatteryStatsImpl$StopwatchTimer; + +.field public static final PI:D = 3.141592653589793 + +.field public static final MAX_VALUE:D = 1.7976931348623157E308 +.field public static final MIN_VALUE:D = 4.9E-324 +.field public static final NEGATIVE_INFINITY:D = -Infinity +.field public static final NaN:D = NaN +.field public static final POSITIVE_INFINITY:D = Infinity + +# annotations +.annotation system Ldalvik/annotation/MemberClasses; + value = { + Lcom/android/internal/util/TypedProperties$TypeException;, + Lcom/android/internal/util/TypedProperties$ParseException; + } +.end annotation + +.annotation system Ldalvik/annotation/Signature; + value = { + "Ljava/util/HashMap", + "<", + "Ljava/lang/String;", + "Ljava/lang/Object;", + ">;" + } +.end annotation + + +.field final mWindowTimers:Ljava/util/ArrayList; + .annotation system Ldalvik/annotation/Signature; + value = { + "Ljava/util/ArrayList", + "<", + "Lcom/android/internal/os/BatteryStatsImpl$StopwatchTimer;", + ">;" + } + .end annotation +.end field + + +# direct methods +.method static constructor ()V + .registers 1 + + .prologue + .line 180 + const/4 v0, 0x0 + + sput v0, Lcom/android/internal/os/BatteryStatsImpl;->sKernelWakelockUpdateVersion:I + + .line 182 + const/4 v0, 0x6 + + new-array v0, v0, [I + + fill-array-data v0, :array_14 + + sput-object v0, Lcom/android/internal/os/BatteryStatsImpl;->PROC_WAKELOCKS_FORMAT:[I + + .line 3495 + new-instance v0, Lcom/android/internal/os/BatteryStatsImpl$1; + + invoke-direct {v0}, Lcom/android/internal/os/BatteryStatsImpl$1;->()V + + sput-object v0, Lcom/android/internal/os/BatteryStatsImpl;->CREATOR:Landroid/os/Parcelable$Creator; + + return-void + + .line 182 + nop + + :array_14 + .array-data 0x4 + 0x9t 0x10t 0x0t 0x0t + 0x9t 0x20t 0x0t 0x0t + 0x9t 0x0t 0x0t 0x0t + 0x9t 0x0t 0x0t 0x0t + 0x9t 0x0t 0x0t 0x0t + 0x9t 0x20t 0x0t 0x0t + .end array-data +.end method + + +# direct methods +.method constructor (Lcom/android/internal/telephony/cdma/CDMAPhone;)V + .registers 2 + .parameter "phone" + + .prologue + .line 42 + invoke-direct {p0, p1}, Lcom/android/internal/telephony/IccFileHandler;->(Lcom/android/internal/telephony/PhoneBase;)V + + .line 43 + return-void +.end method + +.method protected getEFPath(I)Ljava/lang/String; + .registers 3 + .parameter "efid" + + .prologue + .line 71 + sparse-switch p1, :sswitch_data_c + + .line 77 + invoke-virtual {p0, p1}, Lcom/android/internal/telephony/cdma/RuimFileHandler;->getCommonIccEFPath(I)Ljava/lang/String; + + move-result-object v0 + + :goto_7 + return-object v0 + + .line 75 + :sswitch_8 + const-string v0, "3F007F25" + + goto :goto_7 + + .line 71 + nop + + :sswitch_data_c + .sparse-switch + 0x6f32 -> :sswitch_8 + 0x6f3c -> :sswitch_8 + 0x6f41 -> :sswitch_8 + .end sparse-switch +.end method + +.method CardStateFromRILInt(I)Lcom/android/internal/telephony/IccCardStatus$CardState; + .registers 6 + .parameter "state" + + .prologue + .line 59 + packed-switch p1, :pswitch_data_26 + + .line 64 + new-instance v1, Ljava/lang/RuntimeException; + + new-instance v2, Ljava/lang/StringBuilder; + + invoke-direct {v2}, Ljava/lang/StringBuilder;->()V + + const-string v3, "Unrecognized RIL_CardState: " + + invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v2, p1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder; + + move-result-object v2 + + invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object v2 + + invoke-direct {v1, v2}, Ljava/lang/RuntimeException;->(Ljava/lang/String;)V + + throw v1 + + .line 60 + :pswitch_1c + sget-object v0, Lcom/android/internal/telephony/IccCardStatus$CardState;->CARDSTATE_ABSENT:Lcom/android/internal/telephony/IccCardStatus$CardState; + + .line 67 + .local v0, newState:Lcom/android/internal/telephony/IccCardStatus$CardState; + :goto_1e + return-object v0 + + .line 61 + .end local v0 #newState:Lcom/android/internal/telephony/IccCardStatus$CardState; + :pswitch_1f + sget-object v0, Lcom/android/internal/telephony/IccCardStatus$CardState;->CARDSTATE_PRESENT:Lcom/android/internal/telephony/IccCardStatus$CardState; + + .restart local v0 #newState:Lcom/android/internal/telephony/IccCardStatus$CardState; + goto :goto_1e + + .line 62 + .end local v0 #newState:Lcom/android/internal/telephony/IccCardStatus$CardState; + :pswitch_22 + sget-object v0, Lcom/android/internal/telephony/IccCardStatus$CardState;->CARDSTATE_ERROR:Lcom/android/internal/telephony/IccCardStatus$CardState; + + .restart local v0 #newState:Lcom/android/internal/telephony/IccCardStatus$CardState; + goto :goto_1e + + .line 59 + nop + + :pswitch_data_26 + .packed-switch 0x0 + :pswitch_1c + :pswitch_1f + :pswitch_22 + .end packed-switch +.end method + +.method public setCallForwardingOption(IILjava/lang/String;ILandroid/os/Message;)V + .registers 13 + .parameter "commandInterfaceCFAction" + .parameter "commandInterfaceCFReason" + .parameter "dialingNumber" + .parameter "timerSeconds" + .parameter "onComplete" + + .prologue + const/4 v3, 0x1 + + const/4 v4, 0x0 + + .line 981 + invoke-direct {p0, p1}, Lcom/android/internal/telephony/gsm/GSMPhone;->isValidCommandInterfaceCFAction(I)Z + + move-result v0 + + if-eqz v0, :cond_28 + + invoke-direct {p0, p2}, Lcom/android/internal/telephony/gsm/GSMPhone;->isValidCommandInterfaceCFReason(I)Z + + move-result v0 + + if-eqz v0, :cond_28 + + .line 985 + if-nez p2, :cond_2b + + .line 986 + iget-object v0, p0, Lcom/android/internal/telephony/gsm/GSMPhone;->h:Lcom/android/internal/telephony/gsm/GSMPhone$MyHandler; + + const/16 v1, 0xc + + invoke-virtual {p0, p1}, Lcom/android/internal/telephony/gsm/GSMPhone;->isCfEnable(I)Z + + move-result v2 + + if-eqz v2, :cond_29 + + move v2, v3 + + :goto_1b + invoke-virtual {v0, v1, v2, v4, p5}, Lcom/android/internal/telephony/gsm/GSMPhone$MyHandler;->obtainMessage(IIILjava/lang/Object;)Landroid/os/Message; + + move-result-object v6 + + .line 991 + .local v6, resp:Landroid/os/Message; + :goto_1f + iget-object v0, p0, Lcom/android/internal/telephony/gsm/GSMPhone;->mCM:Lcom/android/internal/telephony/CommandsInterface; + + move v1, p1 + + move v2, p2 + + move-object v4, p3 + + move v5, p4 + + invoke-interface/range {v0 .. v6}, Lcom/android/internal/telephony/CommandsInterface;->setCallForward(IIILjava/lang/String;ILandroid/os/Message;)V + + .line 998 + .end local v6 #resp:Landroid/os/Message; + :cond_28 + return-void + + :cond_29 + move v2, v4 + + .line 986 + goto :goto_1b + + .line 989 + :cond_2b + move-object v6, p5 + + .restart local v6 #resp:Landroid/os/Message; + goto :goto_1f +.end method \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/RealSmaliFileTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/RealSmaliFileTest.tokens new file mode 100644 index 00000000..fc63c92c --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/RealSmaliFileTest.tokens @@ -0,0 +1,770 @@ +CLASS_DIRECTIVE(".class") +ACCESS_SPEC("public") +ACCESS_SPEC("final") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/cdma/RuimFileHandler;") +SUPER_DIRECTIVE(".super") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccFileHandler;") +SOURCE_DIRECTIVE(".source") +STRING_LITERAL("\"RuimFileHandler.java\"") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("static") +ACCESS_SPEC("final") +SIMPLE_NAME("LOG_TAG") +COLON(":") +CLASS_DESCRIPTOR("Ljava/lang/String;") +EQUAL("=") +STRING_LITERAL("\"CDMA\"") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("static") +ACCESS_SPEC("final") +SIMPLE_NAME("CARD_MAX_APPS") +COLON(":") +PRIMITIVE_TYPE("I") +EQUAL("=") +INTEGER_LITERAL("0x8") +FIELD_DIRECTIVE(".field") +SIMPLE_NAME("mWifiOnUid") +COLON(":") +PRIMITIVE_TYPE("I") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("public") +ACCESS_SPEC("static") +SIMPLE_NAME("mWifiRunning") +COLON(":") +PRIMITIVE_TYPE("Z") +EQUAL("=") +BOOL_LITERAL("false") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("public") +ACCESS_SPEC("static") +SIMPLE_NAME("mWifiRunning2") +COLON(":") +PRIMITIVE_TYPE("Z") +EQUAL("=") +BOOL_LITERAL("true") +FIELD_DIRECTIVE(".field") +SIMPLE_NAME("mVideoOnTimer") +COLON(":") +CLASS_DESCRIPTOR("Lcom/android/internal/os/BatteryStatsImpl$StopwatchTimer;") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("public") +ACCESS_SPEC("static") +ACCESS_SPEC("final") +SIMPLE_NAME("PI") +COLON(":") +PRIMITIVE_TYPE("D") +EQUAL("=") +DOUBLE_LITERAL("3.141592653589793") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("public") +ACCESS_SPEC("static") +ACCESS_SPEC("final") +SIMPLE_NAME("MAX_VALUE") +COLON(":") +PRIMITIVE_TYPE("D") +EQUAL("=") +DOUBLE_LITERAL("1.7976931348623157E308") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("public") +ACCESS_SPEC("static") +ACCESS_SPEC("final") +SIMPLE_NAME("MIN_VALUE") +COLON(":") +PRIMITIVE_TYPE("D") +EQUAL("=") +DOUBLE_LITERAL("4.9E-324") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("public") +ACCESS_SPEC("static") +ACCESS_SPEC("final") +SIMPLE_NAME("NEGATIVE_INFINITY") +COLON(":") +PRIMITIVE_TYPE("D") +EQUAL("=") +DOUBLE_LITERAL_OR_ID("-Infinity") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("public") +ACCESS_SPEC("static") +ACCESS_SPEC("final") +DOUBLE_LITERAL_OR_ID("NaN") +COLON(":") +PRIMITIVE_TYPE("D") +EQUAL("=") +DOUBLE_LITERAL_OR_ID("NaN") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("public") +ACCESS_SPEC("static") +ACCESS_SPEC("final") +SIMPLE_NAME("POSITIVE_INFINITY") +COLON(":") +PRIMITIVE_TYPE("D") +EQUAL("=") +DOUBLE_LITERAL_OR_ID("Infinity") +ANNOTATION_DIRECTIVE(".annotation") +ANNOTATION_VISIBILITY("system") +CLASS_DESCRIPTOR("Ldalvik/annotation/MemberClasses;") +SIMPLE_NAME("value") +EQUAL("=") +OPEN_BRACE("{") +CLASS_DESCRIPTOR("Lcom/android/internal/util/TypedProperties$TypeException;") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/util/TypedProperties$ParseException;") +CLOSE_BRACE("}") +END_ANNOTATION_DIRECTIVE(".end annotation") +ANNOTATION_DIRECTIVE(".annotation") +ANNOTATION_VISIBILITY("system") +CLASS_DESCRIPTOR("Ldalvik/annotation/Signature;") +SIMPLE_NAME("value") +EQUAL("=") +OPEN_BRACE("{") +STRING_LITERAL("\"Ljava/util/HashMap\"") +COMMA(",") +STRING_LITERAL("\"<\"") +COMMA(",") +STRING_LITERAL("\"Ljava/lang/String;\"") +COMMA(",") +STRING_LITERAL("\"Ljava/lang/Object;\"") +COMMA(",") +STRING_LITERAL("\">;\"") +CLOSE_BRACE("}") +END_ANNOTATION_DIRECTIVE(".end annotation") +FIELD_DIRECTIVE(".field") +ACCESS_SPEC("final") +SIMPLE_NAME("mWindowTimers") +COLON(":") +CLASS_DESCRIPTOR("Ljava/util/ArrayList;") +ANNOTATION_DIRECTIVE(".annotation") +ANNOTATION_VISIBILITY("system") +CLASS_DESCRIPTOR("Ldalvik/annotation/Signature;") +SIMPLE_NAME("value") +EQUAL("=") +OPEN_BRACE("{") +STRING_LITERAL("\"Ljava/util/ArrayList\"") +COMMA(",") +STRING_LITERAL("\"<\"") +COMMA(",") +STRING_LITERAL("\"Lcom/android/internal/os/BatteryStatsImpl$StopwatchTimer;\"") +COMMA(",") +STRING_LITERAL("\">;\"") +CLOSE_BRACE("}") +END_ANNOTATION_DIRECTIVE(".end annotation") +END_FIELD_DIRECTIVE(".end field") +METHOD_DIRECTIVE(".method") +ACCESS_SPEC("static") +ACCESS_SPEC("constructor") +METHOD_NAME("") +OPEN_PAREN("(") +CLOSE_PAREN(")") +VOID_TYPE("V") +REGISTERS_DIRECTIVE(".registers") +INTEGER_LITERAL("1") +PROLOGUE_DIRECTIVE(".prologue") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("180") +INSTRUCTION_FORMAT11n("const/4") +REGISTER("v0") +COMMA(",") +INTEGER_LITERAL("0x0") +INSTRUCTION_FORMAT21c_FIELD("sput") +REGISTER("v0") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/os/BatteryStatsImpl;") +ARROW("->") +SIMPLE_NAME("sKernelWakelockUpdateVersion") +COLON(":") +PRIMITIVE_TYPE("I") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("182") +INSTRUCTION_FORMAT11n("const/4") +REGISTER("v0") +COMMA(",") +INTEGER_LITERAL("0x6") +INSTRUCTION_FORMAT22c_TYPE("new-array") +REGISTER("v0") +COMMA(",") +REGISTER("v0") +COMMA(",") +ARRAY_DESCRIPTOR("[I") +INSTRUCTION_FORMAT31t("fill-array-data") +REGISTER("v0") +COMMA(",") +COLON(":") +SIMPLE_NAME("array_14") +INSTRUCTION_FORMAT21c_FIELD("sput-object") +REGISTER("v0") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/os/BatteryStatsImpl;") +ARROW("->") +SIMPLE_NAME("PROC_WAKELOCKS_FORMAT") +COLON(":") +ARRAY_DESCRIPTOR("[I") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("3495") +INSTRUCTION_FORMAT21c_TYPE("new-instance") +REGISTER("v0") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/os/BatteryStatsImpl$1;") +INSTRUCTION_FORMAT35c_METHOD("invoke-direct") +OPEN_BRACE("{") +REGISTER("v0") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/os/BatteryStatsImpl$1;") +ARROW("->") +METHOD_NAME("") +OPEN_PAREN("(") +CLOSE_PAREN(")") +VOID_TYPE("V") +INSTRUCTION_FORMAT21c_FIELD("sput-object") +REGISTER("v0") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/os/BatteryStatsImpl;") +ARROW("->") +SIMPLE_NAME("CREATOR") +COLON(":") +CLASS_DESCRIPTOR("Landroid/os/Parcelable$Creator;") +INSTRUCTION_FORMAT10x("return-void") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("182") +INSTRUCTION_FORMAT10x("nop") +COLON(":") +SIMPLE_NAME("array_14") +ARRAY_DATA_DIRECTIVE(".array-data") +INTEGER_LITERAL("0x4") +BYTE_LITERAL("0x9t") +BYTE_LITERAL("0x10t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x9t") +BYTE_LITERAL("0x20t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x9t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x9t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x9t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x9t") +BYTE_LITERAL("0x20t") +BYTE_LITERAL("0x0t") +BYTE_LITERAL("0x0t") +END_ARRAY_DATA_DIRECTIVE(".end array-data") +END_METHOD_DIRECTIVE(".end method") +METHOD_DIRECTIVE(".method") +ACCESS_SPEC("constructor") +METHOD_NAME("") +OPEN_PAREN("(") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/cdma/CDMAPhone;") +CLOSE_PAREN(")") +VOID_TYPE("V") +REGISTERS_DIRECTIVE(".registers") +INTEGER_LITERAL("2") +PARAMETER_DIRECTIVE(".parameter") +STRING_LITERAL("\"phone\"") +PROLOGUE_DIRECTIVE(".prologue") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("42") +INSTRUCTION_FORMAT35c_METHOD("invoke-direct") +OPEN_BRACE("{") +REGISTER("p0") +COMMA(",") +REGISTER("p1") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccFileHandler;") +ARROW("->") +METHOD_NAME("") +OPEN_PAREN("(") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/PhoneBase;") +CLOSE_PAREN(")") +VOID_TYPE("V") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("43") +INSTRUCTION_FORMAT10x("return-void") +END_METHOD_DIRECTIVE(".end method") +METHOD_DIRECTIVE(".method") +ACCESS_SPEC("protected") +SIMPLE_NAME("getEFPath") +OPEN_PAREN("(") +PRIMITIVE_TYPE("I") +CLOSE_PAREN(")") +CLASS_DESCRIPTOR("Ljava/lang/String;") +REGISTERS_DIRECTIVE(".registers") +INTEGER_LITERAL("3") +PARAMETER_DIRECTIVE(".parameter") +STRING_LITERAL("\"efid\"") +PROLOGUE_DIRECTIVE(".prologue") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("71") +INSTRUCTION_FORMAT31t("sparse-switch") +REGISTER("p1") +COMMA(",") +COLON(":") +SIMPLE_NAME("sswitch_data_c") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("77") +INSTRUCTION_FORMAT35c_METHOD("invoke-virtual") +OPEN_BRACE("{") +REGISTER("p0") +COMMA(",") +REGISTER("p1") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/cdma/RuimFileHandler;") +ARROW("->") +SIMPLE_NAME("getCommonIccEFPath") +OPEN_PAREN("(") +PRIMITIVE_TYPE("I") +CLOSE_PAREN(")") +CLASS_DESCRIPTOR("Ljava/lang/String;") +INSTRUCTION_FORMAT11x("move-result-object") +REGISTER("v0") +COLON(":") +SIMPLE_NAME("goto_7") +INSTRUCTION_FORMAT11x("return-object") +REGISTER("v0") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("75") +COLON(":") +SIMPLE_NAME("sswitch_8") +INSTRUCTION_FORMAT21c_STRING("const-string") +REGISTER("v0") +COMMA(",") +STRING_LITERAL("\"3F007F25\"") +INSTRUCTION_FORMAT10t("goto") +COLON(":") +SIMPLE_NAME("goto_7") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("71") +INSTRUCTION_FORMAT10x("nop") +COLON(":") +SIMPLE_NAME("sswitch_data_c") +SPARSE_SWITCH_DIRECTIVE(".sparse-switch") +INTEGER_LITERAL("0x6f32") +ARROW("->") +COLON(":") +SIMPLE_NAME("sswitch_8") +INTEGER_LITERAL("0x6f3c") +ARROW("->") +COLON(":") +SIMPLE_NAME("sswitch_8") +INTEGER_LITERAL("0x6f41") +ARROW("->") +COLON(":") +SIMPLE_NAME("sswitch_8") +END_SPARSE_SWITCH_DIRECTIVE(".end sparse-switch") +END_METHOD_DIRECTIVE(".end method") +METHOD_DIRECTIVE(".method") +SIMPLE_NAME("CardStateFromRILInt") +OPEN_PAREN("(") +PRIMITIVE_TYPE("I") +CLOSE_PAREN(")") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;") +REGISTERS_DIRECTIVE(".registers") +INTEGER_LITERAL("6") +PARAMETER_DIRECTIVE(".parameter") +STRING_LITERAL("\"state\"") +PROLOGUE_DIRECTIVE(".prologue") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("59") +INSTRUCTION_FORMAT31t("packed-switch") +REGISTER("p1") +COMMA(",") +COLON(":") +SIMPLE_NAME("pswitch_data_26") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("64") +INSTRUCTION_FORMAT21c_TYPE("new-instance") +REGISTER("v1") +COMMA(",") +CLASS_DESCRIPTOR("Ljava/lang/RuntimeException;") +INSTRUCTION_FORMAT21c_TYPE("new-instance") +REGISTER("v2") +COMMA(",") +CLASS_DESCRIPTOR("Ljava/lang/StringBuilder;") +INSTRUCTION_FORMAT35c_METHOD("invoke-direct") +OPEN_BRACE("{") +REGISTER("v2") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Ljava/lang/StringBuilder;") +ARROW("->") +METHOD_NAME("") +OPEN_PAREN("(") +CLOSE_PAREN(")") +VOID_TYPE("V") +INSTRUCTION_FORMAT21c_STRING("const-string") +REGISTER("v3") +COMMA(",") +STRING_LITERAL("\"Unrecognized RIL_CardState: \"") +INSTRUCTION_FORMAT35c_METHOD("invoke-virtual") +OPEN_BRACE("{") +REGISTER("v2") +COMMA(",") +REGISTER("v3") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Ljava/lang/StringBuilder;") +ARROW("->") +SIMPLE_NAME("append") +OPEN_PAREN("(") +CLASS_DESCRIPTOR("Ljava/lang/String;") +CLOSE_PAREN(")") +CLASS_DESCRIPTOR("Ljava/lang/StringBuilder;") +INSTRUCTION_FORMAT11x("move-result-object") +REGISTER("v2") +INSTRUCTION_FORMAT35c_METHOD("invoke-virtual") +OPEN_BRACE("{") +REGISTER("v2") +COMMA(",") +REGISTER("p1") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Ljava/lang/StringBuilder;") +ARROW("->") +SIMPLE_NAME("append") +OPEN_PAREN("(") +PRIMITIVE_TYPE("I") +CLOSE_PAREN(")") +CLASS_DESCRIPTOR("Ljava/lang/StringBuilder;") +INSTRUCTION_FORMAT11x("move-result-object") +REGISTER("v2") +INSTRUCTION_FORMAT35c_METHOD("invoke-virtual") +OPEN_BRACE("{") +REGISTER("v2") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Ljava/lang/StringBuilder;") +ARROW("->") +SIMPLE_NAME("toString") +OPEN_PAREN("(") +CLOSE_PAREN(")") +CLASS_DESCRIPTOR("Ljava/lang/String;") +INSTRUCTION_FORMAT11x("move-result-object") +REGISTER("v2") +INSTRUCTION_FORMAT35c_METHOD("invoke-direct") +OPEN_BRACE("{") +REGISTER("v1") +COMMA(",") +REGISTER("v2") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Ljava/lang/RuntimeException;") +ARROW("->") +METHOD_NAME("") +OPEN_PAREN("(") +CLASS_DESCRIPTOR("Ljava/lang/String;") +CLOSE_PAREN(")") +VOID_TYPE("V") +INSTRUCTION_FORMAT11x("throw") +REGISTER("v1") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("60") +COLON(":") +SIMPLE_NAME("pswitch_1c") +INSTRUCTION_FORMAT21c_FIELD("sget-object") +REGISTER("v0") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;") +ARROW("->") +SIMPLE_NAME("CARDSTATE_ABSENT") +COLON(":") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("67") +LOCAL_DIRECTIVE(".local") +REGISTER("v0") +COMMA(",") +SIMPLE_NAME("newState") +COLON(":") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;") +COLON(":") +SIMPLE_NAME("goto_1e") +INSTRUCTION_FORMAT11x("return-object") +REGISTER("v0") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("61") +END_LOCAL_DIRECTIVE(".end local") +REGISTER("v0") +COLON(":") +SIMPLE_NAME("pswitch_1f") +INSTRUCTION_FORMAT21c_FIELD("sget-object") +REGISTER("v0") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;") +ARROW("->") +SIMPLE_NAME("CARDSTATE_PRESENT") +COLON(":") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;") +RESTART_LOCAL_DIRECTIVE(".restart local") +REGISTER("v0") +INSTRUCTION_FORMAT10t("goto") +COLON(":") +SIMPLE_NAME("goto_1e") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("62") +END_LOCAL_DIRECTIVE(".end local") +REGISTER("v0") +COLON(":") +SIMPLE_NAME("pswitch_22") +INSTRUCTION_FORMAT21c_FIELD("sget-object") +REGISTER("v0") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;") +ARROW("->") +SIMPLE_NAME("CARDSTATE_ERROR") +COLON(":") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/IccCardStatus$CardState;") +RESTART_LOCAL_DIRECTIVE(".restart local") +REGISTER("v0") +INSTRUCTION_FORMAT10t("goto") +COLON(":") +SIMPLE_NAME("goto_1e") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("59") +INSTRUCTION_FORMAT10x("nop") +COLON(":") +SIMPLE_NAME("pswitch_data_26") +PACKED_SWITCH_DIRECTIVE(".packed-switch") +INTEGER_LITERAL("0x0") +COLON(":") +SIMPLE_NAME("pswitch_1c") +COLON(":") +SIMPLE_NAME("pswitch_1f") +COLON(":") +SIMPLE_NAME("pswitch_22") +END_PACKED_SWITCH_DIRECTIVE(".end packed-switch") +END_METHOD_DIRECTIVE(".end method") +METHOD_DIRECTIVE(".method") +ACCESS_SPEC("public") +SIMPLE_NAME("setCallForwardingOption") +OPEN_PAREN("(") +PARAM_LIST("IILjava/lang/String;ILandroid/os/Message;") +CLOSE_PAREN(")") +VOID_TYPE("V") +REGISTERS_DIRECTIVE(".registers") +INTEGER_LITERAL("13") +PARAMETER_DIRECTIVE(".parameter") +STRING_LITERAL("\"commandInterfaceCFAction\"") +PARAMETER_DIRECTIVE(".parameter") +STRING_LITERAL("\"commandInterfaceCFReason\"") +PARAMETER_DIRECTIVE(".parameter") +STRING_LITERAL("\"dialingNumber\"") +PARAMETER_DIRECTIVE(".parameter") +STRING_LITERAL("\"timerSeconds\"") +PARAMETER_DIRECTIVE(".parameter") +STRING_LITERAL("\"onComplete\"") +PROLOGUE_DIRECTIVE(".prologue") +INSTRUCTION_FORMAT11n("const/4") +REGISTER("v3") +COMMA(",") +INTEGER_LITERAL("0x1") +INSTRUCTION_FORMAT11n("const/4") +REGISTER("v4") +COMMA(",") +INTEGER_LITERAL("0x0") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("981") +INSTRUCTION_FORMAT35c_METHOD("invoke-direct") +OPEN_BRACE("{") +REGISTER("p0") +COMMA(",") +REGISTER("p1") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/gsm/GSMPhone;") +ARROW("->") +SIMPLE_NAME("isValidCommandInterfaceCFAction") +OPEN_PAREN("(") +PRIMITIVE_TYPE("I") +CLOSE_PAREN(")") +PRIMITIVE_TYPE("Z") +INSTRUCTION_FORMAT11x("move-result") +REGISTER("v0") +INSTRUCTION_FORMAT21t("if-eqz") +REGISTER("v0") +COMMA(",") +COLON(":") +SIMPLE_NAME("cond_28") +INSTRUCTION_FORMAT35c_METHOD("invoke-direct") +OPEN_BRACE("{") +REGISTER("p0") +COMMA(",") +REGISTER("p2") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/gsm/GSMPhone;") +ARROW("->") +SIMPLE_NAME("isValidCommandInterfaceCFReason") +OPEN_PAREN("(") +PRIMITIVE_TYPE("I") +CLOSE_PAREN(")") +PRIMITIVE_TYPE("Z") +INSTRUCTION_FORMAT11x("move-result") +REGISTER("v0") +INSTRUCTION_FORMAT21t("if-eqz") +REGISTER("v0") +COMMA(",") +COLON(":") +SIMPLE_NAME("cond_28") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("985") +INSTRUCTION_FORMAT21t("if-nez") +REGISTER("p2") +COMMA(",") +COLON(":") +SIMPLE_NAME("cond_2b") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("986") +INSTRUCTION_FORMAT22c_FIELD("iget-object") +REGISTER("v0") +COMMA(",") +REGISTER("p0") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/gsm/GSMPhone;") +ARROW("->") +SIMPLE_NAME("h") +COLON(":") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/gsm/GSMPhone$MyHandler;") +INSTRUCTION_FORMAT21s("const/16") +REGISTER("v1") +COMMA(",") +INTEGER_LITERAL("0xc") +INSTRUCTION_FORMAT35c_METHOD("invoke-virtual") +OPEN_BRACE("{") +REGISTER("p0") +COMMA(",") +REGISTER("p1") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/gsm/GSMPhone;") +ARROW("->") +SIMPLE_NAME("isCfEnable") +OPEN_PAREN("(") +PRIMITIVE_TYPE("I") +CLOSE_PAREN(")") +PRIMITIVE_TYPE("Z") +INSTRUCTION_FORMAT11x("move-result") +REGISTER("v2") +INSTRUCTION_FORMAT21t("if-eqz") +REGISTER("v2") +COMMA(",") +COLON(":") +SIMPLE_NAME("cond_29") +INSTRUCTION_FORMAT12x_OR_ID("move") +REGISTER("v2") +COMMA(",") +REGISTER("v3") +COLON(":") +SIMPLE_NAME("goto_1b") +INSTRUCTION_FORMAT35c_METHOD("invoke-virtual") +OPEN_BRACE("{") +REGISTER("v0") +COMMA(",") +REGISTER("v1") +COMMA(",") +REGISTER("v2") +COMMA(",") +REGISTER("v4") +COMMA(",") +REGISTER("p5") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/gsm/GSMPhone$MyHandler;") +ARROW("->") +SIMPLE_NAME("obtainMessage") +OPEN_PAREN("(") +PARAM_LIST("IIILjava/lang/Object;") +CLOSE_PAREN(")") +CLASS_DESCRIPTOR("Landroid/os/Message;") +INSTRUCTION_FORMAT11x("move-result-object") +REGISTER("v6") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("991") +LOCAL_DIRECTIVE(".local") +REGISTER("v6") +COMMA(",") +SIMPLE_NAME("resp") +COLON(":") +CLASS_DESCRIPTOR("Landroid/os/Message;") +COLON(":") +SIMPLE_NAME("goto_1f") +INSTRUCTION_FORMAT22c_FIELD("iget-object") +REGISTER("v0") +COMMA(",") +REGISTER("p0") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/gsm/GSMPhone;") +ARROW("->") +SIMPLE_NAME("mCM") +COLON(":") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/CommandsInterface;") +INSTRUCTION_FORMAT12x_OR_ID("move") +REGISTER("v1") +COMMA(",") +REGISTER("p1") +INSTRUCTION_FORMAT12x_OR_ID("move") +REGISTER("v2") +COMMA(",") +REGISTER("p2") +INSTRUCTION_FORMAT12x_OR_ID("move-object") +REGISTER("v4") +COMMA(",") +REGISTER("p3") +INSTRUCTION_FORMAT12x_OR_ID("move") +REGISTER("v5") +COMMA(",") +REGISTER("p4") +INSTRUCTION_FORMAT3rc_METHOD("invoke-interface/range") +OPEN_BRACE("{") +REGISTER("v0") +DOTDOT("..") +REGISTER("v6") +CLOSE_BRACE("}") +COMMA(",") +CLASS_DESCRIPTOR("Lcom/android/internal/telephony/CommandsInterface;") +ARROW("->") +SIMPLE_NAME("setCallForward") +OPEN_PAREN("(") +PARAM_LIST("IIILjava/lang/String;ILandroid/os/Message;") +CLOSE_PAREN(")") +VOID_TYPE("V") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("998") +END_LOCAL_DIRECTIVE(".end local") +REGISTER("v6") +COLON(":") +SIMPLE_NAME("cond_28") +INSTRUCTION_FORMAT10x("return-void") +COLON(":") +SIMPLE_NAME("cond_29") +INSTRUCTION_FORMAT12x_OR_ID("move") +REGISTER("v2") +COMMA(",") +REGISTER("v4") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("986") +INSTRUCTION_FORMAT10t("goto") +COLON(":") +SIMPLE_NAME("goto_1b") +LINE_DIRECTIVE(".line") +INTEGER_LITERAL("989") +COLON(":") +SIMPLE_NAME("cond_2b") +INSTRUCTION_FORMAT12x_OR_ID("move-object") +REGISTER("v6") +COMMA(",") +REGISTER("p5") +RESTART_LOCAL_DIRECTIVE(".restart local") +REGISTER("v6") +INSTRUCTION_FORMAT10t("goto") +COLON(":") +SIMPLE_NAME("goto_1f") +END_METHOD_DIRECTIVE(".end method") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/ShortLiteralTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/ShortLiteralTest.smali new file mode 100644 index 00000000..b5bfbbf1 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/ShortLiteralTest.smali @@ -0,0 +1,49 @@ +0x0S +0x00 +0x1s +0x1234 +0x7fffS +0x8000s +0xFFFF +-0x00 +-0x01 +-01234 +-01234s +-0x8000 +-0x1fff +-0x1fffS +-0x8001 +-0xFFFF +-0x100000 +0 +1 +12345 +12345s +32767 +32678 +65535S +-0 +-1 +-12345S +-32767 +-32768s +-32679s +-65535s +65536 +65600 +00 +01 +012345 +012345s +077777 +0100000 +0177777 +-00 +-01 +-012345 +-012345S +-077777 +-0100000 +-0100001 +-0177777 +0200000 \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/ShortLiteralTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/ShortLiteralTest.tokens new file mode 100644 index 00000000..d0a1fabf --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/ShortLiteralTest.tokens @@ -0,0 +1,49 @@ +SHORT_LITERAL("0x0S") +INTEGER_LITERAL("0x00") +SHORT_LITERAL("0x1s") +INTEGER_LITERAL("0x1234") +SHORT_LITERAL("0x7fffS") +SHORT_LITERAL("0x8000s") +INTEGER_LITERAL("0xFFFF") +INTEGER_LITERAL("-0x00") +INTEGER_LITERAL("-0x01") +INTEGER_LITERAL("-01234") +SHORT_LITERAL("-01234s") +INTEGER_LITERAL("-0x8000") +INTEGER_LITERAL("-0x1fff") +SHORT_LITERAL("-0x1fffS") +INTEGER_LITERAL("-0x8001") +INTEGER_LITERAL("-0xFFFF") +INTEGER_LITERAL("-0x100000") +INTEGER_LITERAL("0") +INTEGER_LITERAL("1") +INTEGER_LITERAL("12345") +SHORT_LITERAL("12345s") +INTEGER_LITERAL("32767") +INTEGER_LITERAL("32678") +SHORT_LITERAL("65535S") +INTEGER_LITERAL("-0") +INTEGER_LITERAL("-1") +SHORT_LITERAL("-12345S") +INTEGER_LITERAL("-32767") +SHORT_LITERAL("-32768s") +SHORT_LITERAL("-32679s") +SHORT_LITERAL("-65535s") +INTEGER_LITERAL("65536") +INTEGER_LITERAL("65600") +INTEGER_LITERAL("00") +INTEGER_LITERAL("01") +INTEGER_LITERAL("012345") +SHORT_LITERAL("012345s") +INTEGER_LITERAL("077777") +INTEGER_LITERAL("0100000") +INTEGER_LITERAL("0177777") +INTEGER_LITERAL("-00") +INTEGER_LITERAL("-01") +INTEGER_LITERAL("-012345") +SHORT_LITERAL("-012345S") +INTEGER_LITERAL("-077777") +INTEGER_LITERAL("-0100000") +INTEGER_LITERAL("-0100001") +INTEGER_LITERAL("-0177777") +INTEGER_LITERAL("0200000") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/StringLiteralTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/StringLiteralTest.smali new file mode 100644 index 00000000..d4eaba5a --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/StringLiteralTest.smali @@ -0,0 +1,43 @@ +"" +" " +" " +" " +"aaaaaaaaaaaaaaaaaaaaaaaaaa" +"\"\"\"\\" +"abcd1234" +"abcd 1234" +"ABCD""ABCD" +"1234" +"\n" +"\r" +"\t" +"\b" +"\f" +"\u0030" +"\uABCD" +"\uabcd" +"\\" +"\'" +"'" +"\"" +"\n\r\t\b\f\u0030\uABCD\"\\\''\"" +"\uABCD01234" +"\uABCDEFGHIJK" +"\u12341234" +"\\\\\\\\\\\\\\\"" + +"a"a" +"\a" +" +" +" +"\u" +"\u0" +"\ua" +"\uab" +"\u01a" +"\uz" +"\u012z\u\u\u\uz\"" + +"abcd" +" \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/StringLiteralTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/StringLiteralTest.tokens new file mode 100644 index 00000000..474668c5 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/StringLiteralTest.tokens @@ -0,0 +1,43 @@ +STRING_LITERAL("\"\"") +STRING_LITERAL("\" \"") +STRING_LITERAL("\" \"") +STRING_LITERAL("\" \"") +STRING_LITERAL("\"aaaaaaaaaaaaaaaaaaaaaaaaaa\"") +STRING_LITERAL("\"\"\"\"\\\"") +STRING_LITERAL("\"abcd1234\"") +STRING_LITERAL("\"abcd 1234\"") +STRING_LITERAL("\"ABCD\"") STRING_LITERAL("\"ABCD\"") +STRING_LITERAL("\"1234\"") +STRING_LITERAL("\"\n\"") +STRING_LITERAL("\"\r\"") +STRING_LITERAL("\"\t\"") +STRING_LITERAL("\"\b\"") +STRING_LITERAL("\"\f\"") +STRING_LITERAL("\"\u0030\"") +STRING_LITERAL("\"\uABCD\"") +STRING_LITERAL("\"\uabcd\"") +STRING_LITERAL("\"\\\"") +STRING_LITERAL("\"\'\"") +STRING_LITERAL("\"'\"") +STRING_LITERAL("\"\"\"") +STRING_LITERAL("\"\n\r\t\b\f\u0030\uABCD\"\\\''\"\"") +STRING_LITERAL("\"\uABCD01234\"") +STRING_LITERAL("\"\uABCDEFGHIJK\"") +STRING_LITERAL("\"\u12341234\"") +STRING_LITERAL("\"\\\\\\\\\\\\\\\"\"") + +STRING_LITERAL("\"a\"") SIMPLE_NAME("a") INVALID_TOKEN("\"") +INVALID_TOKEN("\"\\a\"") +INVALID_TOKEN("\"") +INVALID_TOKEN("\"") +INVALID_TOKEN("\"") +INVALID_TOKEN("\"\\u\"") +INVALID_TOKEN("\"\\u0\"") +INVALID_TOKEN("\"\\ua\"") +INVALID_TOKEN("\"\\uab\"") +INVALID_TOKEN("\"\\u01a\"") +INVALID_TOKEN("\"\\uz\"") +INVALID_TOKEN("\"\\u012z\\u\\u\\u\\uz\"\"") + +STRING_LITERAL("\"abcd\"") +INVALID_TOKEN("\"") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/SymbolTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/SymbolTest.smali new file mode 100644 index 00000000..6b11061b --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/SymbolTest.smali @@ -0,0 +1,19 @@ +.. +-> += +: +, +{}() +{ } ( ) +{ +} +( +) + + + + + + + + \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/SymbolTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/SymbolTest.tokens new file mode 100644 index 00000000..009c9324 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/SymbolTest.tokens @@ -0,0 +1,12 @@ +DOTDOT("..") WHITE_SPACE("\n") +ARROW("->") WHITE_SPACE("\n") +EQUAL("=") WHITE_SPACE("\n") +COLON(":") WHITE_SPACE("\n") +COMMA(",") WHITE_SPACE("\n") +OPEN_BRACE("{") CLOSE_BRACE("}") OPEN_PAREN("(") CLOSE_PAREN(")") WHITE_SPACE("\n") +OPEN_BRACE("{") WHITE_SPACE(" ") CLOSE_BRACE("}") WHITE_SPACE(" ") OPEN_PAREN("(") WHITE_SPACE(" ") CLOSE_PAREN(")") WHITE_SPACE("\n") +OPEN_BRACE("{") WHITE_SPACE("\n") +CLOSE_BRACE("}") WHITE_SPACE("\n") +OPEN_PAREN("(") WHITE_SPACE("\n") +CLOSE_PAREN(")") +WHITE_SPACE("\n \n\t\n\t \n\t \n\t \t\n \t\n \t\n\r\r") \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.smali b/brut.apktool.smali/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.smali new file mode 100644 index 00000000..7fd9b65a --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.smali @@ -0,0 +1,48 @@ +Z +B +S +C +I +J +F +D +V + +Ljava/lang/String; +LI; +LV; +LI/I/I; + +[Z +[B +[S +[C +[I +[J +[F +[D +[Ljava/lang/String; +[LI/I/I; + +IIIII +ZBSCIJFD +ILa;[La;[I +Ljava/lang/String;Ljava/lang/String; +[I[I[I +[I[Z +[I[Ljava/lang/String; + + + + +Ljava/lang/String +L; +LI +L[Ljava/lang/String; + +[ +[V +[java/lang/String; +[; + + \ No newline at end of file diff --git a/brut.apktool.smali/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.tokens b/brut.apktool.smali/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.tokens new file mode 100644 index 00000000..531622e9 --- /dev/null +++ b/brut.apktool.smali/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.tokens @@ -0,0 +1,48 @@ +PRIMITIVE_TYPE("Z") +PRIMITIVE_TYPE("B") +PRIMITIVE_TYPE("S") +PRIMITIVE_TYPE("C") +PRIMITIVE_TYPE("I") +PRIMITIVE_TYPE("J") +PRIMITIVE_TYPE("F") +PRIMITIVE_TYPE("D") +VOID_TYPE("V") + +CLASS_DESCRIPTOR("Ljava/lang/String;") +CLASS_DESCRIPTOR("LI;") +CLASS_DESCRIPTOR("LV;") +CLASS_DESCRIPTOR("LI/I/I;") + +ARRAY_DESCRIPTOR("[Z") +ARRAY_DESCRIPTOR("[B") +ARRAY_DESCRIPTOR("[S") +ARRAY_DESCRIPTOR("[C") +ARRAY_DESCRIPTOR("[I") +ARRAY_DESCRIPTOR("[J") +ARRAY_DESCRIPTOR("[F") +ARRAY_DESCRIPTOR("[D") +ARRAY_DESCRIPTOR("[Ljava/lang/String;") +ARRAY_DESCRIPTOR("[LI/I/I;") + +PARAM_LIST_OR_ID("IIIII") +PARAM_LIST_OR_ID("ZBSCIJFD") +PARAM_LIST("ILa;[La;[I") +PARAM_LIST("Ljava/lang/String;Ljava/lang/String;") +PARAM_LIST("[I[I[I") +PARAM_LIST("[I[Z") +PARAM_LIST("[I[Ljava/lang/String;") + +METHOD_NAME("") +METHOD_NAME("") + +SIMPLE_NAME("Ljava") INVALID_TOKEN("/") SIMPLE_NAME("lang") INVALID_TOKEN("/") SIMPLE_NAME("String") +SIMPLE_NAME("L") INVALID_TOKEN(";") +SIMPLE_NAME("LI") +SIMPLE_NAME("L") ARRAY_DESCRIPTOR("[Ljava/lang/String;") + +INVALID_TOKEN("[") +INVALID_TOKEN("[") VOID_TYPE("V") +INVALID_TOKEN("[") SIMPLE_NAME("java") INVALID_TOKEN("/") SIMPLE_NAME("lang") INVALID_TOKEN("/") SIMPLE_NAME("String") INVALID_TOKEN(";") +INVALID_TOKEN("[") INVALID_TOKEN(";") + +INVALID_TOKEN("<") SIMPLE_NAME("linit") INVALID_TOKEN(">") \ No newline at end of file diff --git a/brut.apktool.smali/util/.gitignore b/brut.apktool.smali/util/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/brut.apktool.smali/util/.gitignore @@ -0,0 +1 @@ +/target diff --git a/brut.apktool.smali/util/src/main/java/ds/tree/DuplicateKeyException.java b/brut.apktool.smali/util/src/main/java/ds/tree/DuplicateKeyException.java new file mode 100644 index 00000000..5f660b39 --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/ds/tree/DuplicateKeyException.java @@ -0,0 +1,41 @@ +/* +The MIT License + +Copyright (c) 2008 Tahseen Ur Rehman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package ds.tree; + +/** + * excepion thrown if a duplicate key is inserted in a {@link RadixTree} + * + * @author Tahseen Ur Rehman + * email: tahseen.ur.rehman {at.spam.me.not} gmail.com + */ +public class DuplicateKeyException extends RuntimeException +{ + private static final long serialVersionUID = 3141795907493885706L; + + public DuplicateKeyException(String msg) + { + super(msg); + } +} diff --git a/brut.apktool.smali/util/src/main/java/ds/tree/RadixTree.java b/brut.apktool.smali/util/src/main/java/ds/tree/RadixTree.java new file mode 100644 index 00000000..bf05e9d9 --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/ds/tree/RadixTree.java @@ -0,0 +1,115 @@ +/* +The MIT License + +Copyright (c) 2008 Tahseen Ur Rehman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package ds.tree; + +import java.util.ArrayList; + +/** + * This interface represent the operation of a radix tree. A radix tree, + * Patricia trie/tree, or crit bit tree is a specialized set data structure + * based on the trie that is used to store a set of strings. In contrast with a + * regular trie, the edges of a Patricia trie are labelled with sequences of + * characters rather than with single characters. These can be strings of + * characters, bit strings such as integers or IP addresses, or generally + * arbitrary sequences of objects in lexicographical order. Sometimes the names + * radix tree and crit bit tree are only applied to trees storing integers and + * Patricia trie is retained for more general inputs, but the structure works + * the same way in all cases. + * + * @author Tahseen Ur Rehman + * email: tahseen.ur.rehman {at.spam.me.not} gmail.com + */ +public interface RadixTree { + /** + * Insert a new string key and its value to the tree. + * + * @param key + * The string key of the object + * @param value + * The value that need to be stored corresponding to the given + * key. + * @throws DuplicateKeyException + */ + public void insert(String key, T value); + + /** + * Delete a key and its associated value from the tree. + * @param key The key of the node that need to be deleted + * @return + */ + public boolean delete(String key); + + /** + * Find a value based on its corresponding key. + * + * @param key The key for which to search the tree. + * @return The value corresponding to the key. null if iot can not find the key + */ + public T find(String key); + + /** + * Find an existing entry and replace it's value. If no existing entry, do nothing + * + * @param key The key for which to search the tree. + * @param value The value to set for the entry + * @return true if an entry was found for the given key, false if not found + */ + public boolean replace(String key, final T value); + + /** + * Check if the tree contains any entry corresponding to the given key. + * + * @param key The key that needto be searched in the tree. + * @return retun true if the key is present in the tree otherwise false + */ + public boolean contains(String key); + + /** + * Search for all the keys that start with given prefix. limiting the results based on the supplied limit. + * + * @param prefix The prefix for which keys need to be search + * @param recordLimit The limit for the results + * @return The list of values those key start with the given prefix + */ + public ArrayList searchPrefix(String prefix, int recordLimit); + + /** + * Return the size of the Radix tree + * @return the size of the tree + */ + public long getSize(); + + /** + * Complete the a prefix to the point where ambiguity starts. + * + * Example: + * If a tree contain "blah1", "blah2" + * complete("b") -> return "blah" + * + * @param prefix The prefix we want to complete + * @return The unambiguous completion of the string. + */ + public String complete(String prefix); +} diff --git a/brut.apktool.smali/util/src/main/java/ds/tree/RadixTreeImpl.java b/brut.apktool.smali/util/src/main/java/ds/tree/RadixTreeImpl.java new file mode 100644 index 00000000..58797d48 --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/ds/tree/RadixTreeImpl.java @@ -0,0 +1,462 @@ +/* +The MIT License + +Copyright (c) 2008 Tahseen Ur Rehman, Javid Jamae + +http://code.google.com/p/radixtree/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package ds.tree; + +import java.util.ArrayList; +import java.util.Formattable; +import java.util.Formatter; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Queue; + +/** + * Implementation for Radix tree {@link RadixTree} + * + * @author Tahseen Ur Rehman (tahseen.ur.rehman {at.spam.me.not} gmail.com) + * @author Javid Jamae + * @author Dennis Heidsiek + */ +public class RadixTreeImpl implements RadixTree, Formattable { + + protected RadixTreeNode root; + + protected long size; + + /** + * Create a Radix Tree with only the default node root. + */ + public RadixTreeImpl() { + root = new RadixTreeNode(); + root.setKey(""); + size = 0; + } + + public T find(String key) { + Visitor visitor = new VisitorImpl() { + + public void visit(String key, RadixTreeNode parent, + RadixTreeNode node) { + if (node.isReal()) + result = node.getValue(); + } + }; + + visit(key, visitor); + + return visitor.getResult(); + } + + public boolean replace(String key, final T value) { + Visitor visitor = new VisitorImpl() { + public void visit(String key, RadixTreeNode parent, RadixTreeNode node) { + if (node.isReal()) { + node.setValue(value); + result = value; + } else { + result = null; + } + } + }; + + visit(key, visitor); + + return visitor.getResult() != null; + } + + public boolean delete(String key) { + Visitor visitor = new VisitorImpl(Boolean.FALSE) { + public void visit(String key, RadixTreeNode parent, + RadixTreeNode node) { + result = node.isReal(); + + // if it is a real node + if (result) { + // If there no children of the node we need to + // delete it from the its parent children list + if (node.getChildern().size() == 0) { + Iterator> it = parent.getChildern() + .iterator(); + while (it.hasNext()) { + if (it.next().getKey().equals(node.getKey())) { + it.remove(); + break; + } + } + + // if parent is not real node and has only one child + // then they need to be merged. + if (parent.getChildern().size() == 1 + && parent.isReal() == false) { + mergeNodes(parent, parent.getChildern().get(0)); + } + } else if (node.getChildern().size() == 1) { + // we need to merge the only child of this node with + // itself + mergeNodes(node, node.getChildern().get(0)); + } else { // we jus need to mark the node as non real. + node.setReal(false); + } + } + } + + /** + * Merge a child into its parent node. Operation only valid if it is + * only child of the parent node and parent node is not a real node. + * + * @param parent + * The parent Node + * @param child + * The child Node + */ + private void mergeNodes(RadixTreeNode parent, + RadixTreeNode child) { + parent.setKey(parent.getKey() + child.getKey()); + parent.setReal(child.isReal()); + parent.setValue(child.getValue()); + parent.setChildern(child.getChildern()); + } + + }; + + visit(key, visitor); + + if(visitor.getResult()) { + size--; + } + return visitor.getResult().booleanValue(); + } + + /* + * (non-Javadoc) + * @see ds.tree.RadixTree#insert(java.lang.String, java.lang.Object) + */ + public void insert(String key, T value) throws DuplicateKeyException { + try { + insert(key, root, value); + } catch (DuplicateKeyException e) { + // re-throw the exception with 'key' in the message + throw new DuplicateKeyException("Duplicate key: '" + key + "'"); + } + size++; + } + + /** + * Recursively insert the key in the radix tree. + * + * @param key The key to be inserted + * @param node The current node + * @param value The value associated with the key + * @throws DuplicateKeyException If the key already exists in the database. + */ + private void insert(String key, RadixTreeNode node, T value) + throws DuplicateKeyException { + + int numberOfMatchingCharacters = node.getNumberOfMatchingCharacters(key); + + // we are either at the root node + // or we need to go down the tree + if (node.getKey().equals("") == true || numberOfMatchingCharacters == 0 || (numberOfMatchingCharacters < key.length() && numberOfMatchingCharacters >= node.getKey().length())) { + boolean flag = false; + String newText = key.substring(numberOfMatchingCharacters, key.length()); + for (RadixTreeNode child : node.getChildern()) { + if (child.getKey().startsWith(newText.charAt(0) + "")) { + flag = true; + insert(newText, child, value); + break; + } + } + + // just add the node as the child of the current node + if (flag == false) { + RadixTreeNode n = new RadixTreeNode(); + n.setKey(newText); + n.setReal(true); + n.setValue(value); + + node.getChildern().add(n); + } + } + // there is a exact match just make the current node as data node + else if (numberOfMatchingCharacters == key.length() && numberOfMatchingCharacters == node.getKey().length()) { + if (node.isReal() == true) { + throw new DuplicateKeyException("Duplicate key"); + } + + node.setReal(true); + node.setValue(value); + } + // This node need to be split as the key to be inserted + // is a prefix of the current node key + else if (numberOfMatchingCharacters > 0 && numberOfMatchingCharacters < node.getKey().length()) { + RadixTreeNode n1 = new RadixTreeNode(); + n1.setKey(node.getKey().substring(numberOfMatchingCharacters, node.getKey().length())); + n1.setReal(node.isReal()); + n1.setValue(node.getValue()); + n1.setChildern(node.getChildern()); + + node.setKey(key.substring(0, numberOfMatchingCharacters)); + node.setReal(false); + node.setChildern(new ArrayList>()); + node.getChildern().add(n1); + + if(numberOfMatchingCharacters < key.length()) { + RadixTreeNode n2 = new RadixTreeNode(); + n2.setKey(key.substring(numberOfMatchingCharacters, key.length())); + n2.setReal(true); + n2.setValue(value); + + node.getChildern().add(n2); + } else { + node.setValue(value); + node.setReal(true); + } + } + // this key need to be added as the child of the current node + else { + RadixTreeNode n = new RadixTreeNode(); + n.setKey(node.getKey().substring(numberOfMatchingCharacters, node.getKey().length())); + n.setChildern(node.getChildern()); + n.setReal(node.isReal()); + n.setValue(node.getValue()); + + node.setKey(key); + node.setReal(true); + node.setValue(value); + + node.getChildern().add(n); + } + } + + public ArrayList searchPrefix(String key, int recordLimit) { + ArrayList keys = new ArrayList(); + + RadixTreeNode node = searchPefix(key, root); + + if (node != null) { + if (node.isReal()) { + keys.add(node.getValue()); + } + getNodes(node, keys, recordLimit); + } + + return keys; + } + + private void getNodes(RadixTreeNode parent, ArrayList keys, int limit) { + Queue> queue = new LinkedList>(); + + queue.addAll(parent.getChildern()); + + while (!queue.isEmpty()) { + RadixTreeNode node = queue.remove(); + if (node.isReal() == true) { + keys.add(node.getValue()); + } + + if (keys.size() == limit) { + break; + } + + queue.addAll(node.getChildern()); + } + } + + private RadixTreeNode searchPefix(String key, RadixTreeNode node) { + RadixTreeNode result = null; + + int numberOfMatchingCharacters = node.getNumberOfMatchingCharacters(key); + + if (numberOfMatchingCharacters == key.length() && numberOfMatchingCharacters <= node.getKey().length()) { + result = node; + } else if (node.getKey().equals("") == true + || (numberOfMatchingCharacters < key.length() && numberOfMatchingCharacters >= node.getKey().length())) { + String newText = key.substring(numberOfMatchingCharacters, key.length()); + for (RadixTreeNode child : node.getChildern()) { + if (child.getKey().startsWith(newText.charAt(0) + "")) { + result = searchPefix(newText, child); + break; + } + } + } + + return result; + } + + public boolean contains(String key) { + Visitor visitor = new VisitorImpl(Boolean.FALSE) { + public void visit(String key, RadixTreeNode parent, + RadixTreeNode node) { + result = node.isReal(); + } + }; + + visit(key, visitor); + + return visitor.getResult().booleanValue(); + } + + /** + * visit the node those key matches the given key + * @param key The key that need to be visited + * @param visitor The visitor object + */ + public void visit(String key, Visitor visitor) { + if (root != null) { + visit(key, visitor, null, root); + } + } + + /** + * recursively visit the tree based on the supplied "key". calls the Visitor + * for the node those key matches the given prefix + * + * @param prefix + * The key o prefix to search in the tree + * @param visitor + * The Visitor that will be called if a node with "key" as its + * key is found + * @param node + * The Node from where onward to search + */ + private void visit(String prefix, Visitor visitor, + RadixTreeNode parent, RadixTreeNode node) { + + int numberOfMatchingCharacters = node.getNumberOfMatchingCharacters(prefix); + + // if the node key and prefix match, we found a match! + if (numberOfMatchingCharacters == prefix.length() && numberOfMatchingCharacters == node.getKey().length()) { + visitor.visit(prefix, parent, node); + } else if (node.getKey().equals("") == true // either we are at the + // root + || (numberOfMatchingCharacters < prefix.length() && numberOfMatchingCharacters >= node.getKey().length())) { // OR we need to + // traverse the childern + String newText = prefix.substring(numberOfMatchingCharacters, prefix.length()); + for (RadixTreeNode child : node.getChildern()) { + // recursively search the child nodes + if (child.getKey().startsWith(newText.charAt(0) + "")) { + visit(newText, visitor, node, child); + break; + } + } + } + } + + public long getSize() { + return size; + } + + /** + * Display the Trie on console. + * + * WARNING! Do not use this for a large Trie, it's for testing purpose only. + * @see formatTo + */ + @Deprecated + public void display() { + formatNodeTo(new Formatter(System.out), 0, root); + } + + @Deprecated + private void display(int level, RadixTreeNode node) { + formatNodeTo(new Formatter(System.out), level, node); + } + + /** + * WARNING! Do not use this for a large Trie, it's for testing purpose only. + */ + private void formatNodeTo(Formatter f, int level, RadixTreeNode node) { + for (int i = 0; i < level; i++) { + f.format(" "); + } + f.format("|"); + for (int i = 0; i < level; i++) { + f.format("-"); + } + + if (node.isReal() == true) + f.format("%s[%s]*%n", node.getKey(), node.getValue()); + else + f.format("%s%n", node.getKey()); + + for (RadixTreeNode child : node.getChildern()) { + formatNodeTo(f, level + 1, child); + } + } + + /** + * Writes a textual representation of this tree to the given formatter. + * + * Currently, all options are simply ignored. + * + * WARNING! Do not use this for a large Trie, it's for testing purpose only. + */ + public void formatTo(Formatter formatter, int flags, int width, int precision) { + formatNodeTo(formatter, 0, root); + } + + /** + * Complete the a prefix to the point where ambiguity starts. + * + * Example: + * If a tree contain "blah1", "blah2" + * complete("b") -> return "blah" + * + * @param prefix The prefix we want to complete + * @return The unambiguous completion of the string. + */ + public String complete(String prefix) { + return complete(prefix, root, ""); + } + + private String complete(String key, RadixTreeNode node, String base) { + int i = 0; + int keylen = key.length(); + int nodelen = node.getKey().length(); + + while (i < keylen && i < nodelen) { + if (key.charAt(i) != node.getKey().charAt(i)) { + break; + } + i++; + } + + if (i == keylen && i <= nodelen) { + return base + node.getKey(); + } + else if (nodelen == 0 || (i < keylen && i >= nodelen)) { + String beginning = key.substring(0, i); + String ending = key.substring(i, keylen); + for (RadixTreeNode child : node.getChildern()) { + if (child.getKey().startsWith(ending.charAt(0) + "")) { + return complete(ending, child, base + beginning); + } + } + } + + return ""; + } +} \ No newline at end of file diff --git a/brut.apktool.smali/util/src/main/java/ds/tree/RadixTreeNode.java b/brut.apktool.smali/util/src/main/java/ds/tree/RadixTreeNode.java new file mode 100644 index 00000000..287e0e98 --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/ds/tree/RadixTreeNode.java @@ -0,0 +1,103 @@ +/* +The MIT License + +Copyright (c) 2008 Tahseen Ur Rehman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package ds.tree; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a node of a Radix tree {@link RadixTreeImpl} + * + * @author Tahseen Ur Rehman + * @email tahseen.ur.rehman {at.spam.me.not} gmail.com + * @param + */ +class RadixTreeNode { + private String key; + + private List> childern; + + private boolean real; + + private T value; + + /** + * intailize the fields with default values to avoid null reference checks + * all over the places + */ + public RadixTreeNode() { + key = ""; + childern = new ArrayList>(); + real = false; + } + + public T getValue() { + return value; + } + + public void setValue(T data) { + this.value = data; + } + + public String getKey() { + return key; + } + + public void setKey(String value) { + this.key = value; + } + + public boolean isReal() { + return real; + } + + public void setReal(boolean datanode) { + this.real = datanode; + } + + public List> getChildern() { + return childern; + } + + public void setChildern(List> childern) { + this.childern = childern; + } + + public int getNumberOfMatchingCharacters(String key) { + int numberOfMatchingCharacters = 0; + while (numberOfMatchingCharacters < key.length() && numberOfMatchingCharacters < this.getKey().length()) { + if (key.charAt(numberOfMatchingCharacters) != this.getKey().charAt(numberOfMatchingCharacters)) { + break; + } + numberOfMatchingCharacters++; + } + return numberOfMatchingCharacters; + } + + @Override + public String toString() { + return key; + } +} diff --git a/brut.apktool.smali/util/src/main/java/ds/tree/Visitor.java b/brut.apktool.smali/util/src/main/java/ds/tree/Visitor.java new file mode 100644 index 00000000..816e08b0 --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/ds/tree/Visitor.java @@ -0,0 +1,56 @@ +/* +The MIT License + +Copyright (c) 2008 Tahseen Ur Rehman, Javid Jamae + +http://code.google.com/p/radixtree/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package ds.tree; + +/** + * The visitor interface that is used by {@link RadixTreeImpl} for perfroming + * task on a searched node. + * + * @author Tahseen Ur Rehman (tahseen.ur.rehman {at.spam.me.not} gmail.com) + * @author Javid Jamae + * @author Dennis Heidsiek + * @param + */ +public interface Visitor { + /** + * This method gets called by {@link RadixTreeImpl#visit(String, Visitor) visit} + * when it finds a node matching the key given to it. + * + * @param key The key that matched the node + * @param parent The parent of the node being visited + * @param node The node that is being visited + */ + public void visit(String key, RadixTreeNode parent, RadixTreeNode node); + + /** + * The visitor can store any type of result object, depending on the context of + * what it is being used for. + * + * @return The result captured by the visitor. + */ + public R getResult(); +} diff --git a/brut.apktool.smali/util/src/main/java/ds/tree/VisitorImpl.java b/brut.apktool.smali/util/src/main/java/ds/tree/VisitorImpl.java new file mode 100644 index 00000000..20409e02 --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/ds/tree/VisitorImpl.java @@ -0,0 +1,27 @@ +package ds.tree; + + +/** + * A simple standard implementation for a {@link visitor}. + * + * @author Dennis Heidsiek + * @param + */ +public abstract class VisitorImpl implements Visitor { + + protected R result; + + public VisitorImpl() { + this.result = null; + } + + public VisitorImpl(R initialValue) { + this.result = initialValue; + } + + public R getResult() { + return result; + } + + abstract public void visit(String key, RadixTreeNode parent, RadixTreeNode node); +} \ No newline at end of file diff --git a/brut.apktool.smali/util/src/main/java/org/jf/util/ClassFileNameHandler.java b/brut.apktool.smali/util/src/main/java/org/jf/util/ClassFileNameHandler.java new file mode 100644 index 00000000..ba42e03a --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/org/jf/util/ClassFileNameHandler.java @@ -0,0 +1,366 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.util; + +import ds.tree.RadixTree; +import ds.tree.RadixTreeImpl; + +import java.io.*; +import java.nio.CharBuffer; +import java.util.regex.Pattern; + +/** + * This class checks for case-insensitive file systems, and generates file names based on a given class name, that are + * guaranteed to be unique. When "colliding" class names are found, it appends a numeric identifier to the end of the + * class name to distinguish it from another class with a name that differes only by case. i.e. a.smali and a_2.smali + */ +public class ClassFileNameHandler { + private PackageNameEntry top; + private String fileExtension; + private boolean modifyWindowsReservedFilenames; + + public ClassFileNameHandler(File path, String fileExtension) { + this.top = new PackageNameEntry(path); + this.fileExtension = fileExtension; + this.modifyWindowsReservedFilenames = testForWindowsReservedFileNames(path); + } + + public File getUniqueFilenameForClass(String className) { + //class names should be passed in the normal dalvik style, with a leading L, a trailing ;, and using + //'/' as a separator. + if (className.charAt(0) != 'L' || className.charAt(className.length()-1) != ';') { + throw new RuntimeException("Not a valid dalvik class name"); + } + + int packageElementCount = 1; + for (int i=1; i= className.length()-1) { + throw new RuntimeException("Not a valid dalvik class name"); + } + + packageElement = className.substring(elementStart, className.length()-1); + if (modifyWindowsReservedFilenames && isReservedFileName(packageElement)) { + packageElement += "#"; + } + + packageElements[elementIndex] = packageElement; + + return top.addUniqueChild(packageElements, 0); + } + + private static boolean testForWindowsReservedFileNames(File path) { + File f = new File(path, "aux.smali"); + if (f.exists()) { + return false; + } + + try { + FileWriter writer = new FileWriter(f); + writer.write("test"); + writer.flush(); + writer.close(); + f.delete(); //doesn't throw IOException + return false; + } catch (IOException ex) { + //if an exception occured, it's likely that we're on a windows system. + return true; + } + } + + private static Pattern reservedFileNameRegex = Pattern.compile("^CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9]$", + Pattern.CASE_INSENSITIVE); + private static boolean isReservedFileName(String className) { + return reservedFileNameRegex.matcher(className).matches(); + } + + private abstract class FileSystemEntry { + public final File file; + + public FileSystemEntry(File file) { + this.file = file; + } + + public abstract File addUniqueChild(String[] pathElements, int pathElementsIndex); + + public FileSystemEntry makeVirtual(File parent) { + return new VirtualGroupEntry(this, parent); + } + } + + private class PackageNameEntry extends FileSystemEntry { + //this contains the FileSystemEntries for all of this package's children + //the associated keys are all lowercase + private RadixTree children = new RadixTreeImpl(); + + public PackageNameEntry(File parent, String name) { + super(new File(parent, name)); + } + + public PackageNameEntry(File path) { + super(path); + } + + @Override + public File addUniqueChild(String[] pathElements, int pathElementsIndex) { + String elementName; + String elementNameLower; + + if (pathElementsIndex == pathElements.length - 1) { + elementName = pathElements[pathElementsIndex]; + elementName += fileExtension; + } else { + elementName = pathElements[pathElementsIndex]; + } + elementNameLower = elementName.toLowerCase(); + + FileSystemEntry existingEntry = children.find(elementNameLower); + if (existingEntry != null) { + FileSystemEntry virtualEntry = existingEntry; + //if there is already another entry with the same name but different case, we need to + //add a virtual group, and then add the existing entry and the new entry to that group + if (!(existingEntry instanceof VirtualGroupEntry)) { + if (existingEntry.file.getName().equals(elementName)) { + if (pathElementsIndex == pathElements.length - 1) { + return existingEntry.file; + } else { + return existingEntry.addUniqueChild(pathElements, pathElementsIndex + 1); + } + } else { + virtualEntry = existingEntry.makeVirtual(file); + children.replace(elementNameLower, virtualEntry); + } + } + + return virtualEntry.addUniqueChild(pathElements, pathElementsIndex); + } + + if (pathElementsIndex == pathElements.length - 1) { + ClassNameEntry classNameEntry = new ClassNameEntry(file, elementName); + children.insert(elementNameLower, classNameEntry); + return classNameEntry.file; + } else { + PackageNameEntry packageNameEntry = new PackageNameEntry(file, elementName); + children.insert(elementNameLower, packageNameEntry); + return packageNameEntry.addUniqueChild(pathElements, pathElementsIndex + 1); + } + } + } + + /** + * A virtual group that groups together file system entries with the same name, differing only in case + */ + private class VirtualGroupEntry extends FileSystemEntry { + //this contains the FileSystemEntries for all of the files/directories in this group + //the key is the unmodified name of the entry, before it is modified to be made unique (if needed). + private RadixTree groupEntries = new RadixTreeImpl(); + + //whether the containing directory is case sensitive or not. + //-1 = unset + //0 = false; + //1 = true; + private int isCaseSensitive = -1; + + public VirtualGroupEntry(FileSystemEntry firstChild, File parent) { + super(parent); + + //use the name of the first child in the group as-is + groupEntries.insert(firstChild.file.getName(), firstChild); + } + + @Override + public File addUniqueChild(String[] pathElements, int pathElementsIndex) { + String elementName = pathElements[pathElementsIndex]; + + if (pathElementsIndex == pathElements.length - 1) { + elementName = elementName + fileExtension; + } + + FileSystemEntry existingEntry = groupEntries.find(elementName); + if (existingEntry != null) { + if (pathElementsIndex == pathElements.length - 1) { + return existingEntry.file; + } else { + return existingEntry.addUniqueChild(pathElements, pathElementsIndex+1); + } + } + + + if (pathElementsIndex == pathElements.length - 1) { + String fileName; + if (!isCaseSensitive()) { + fileName = pathElements[pathElementsIndex] + "." + (groupEntries.getSize()+1) + fileExtension; + } else { + fileName = elementName; + } + + ClassNameEntry classNameEntry = new ClassNameEntry(file, fileName); + groupEntries.insert(elementName, classNameEntry); + return classNameEntry.file; + } else { + String fileName; + if (!isCaseSensitive()) { + fileName = pathElements[pathElementsIndex] + "." + (groupEntries.getSize()+1); + } else { + fileName = elementName; + } + + PackageNameEntry packageNameEntry = new PackageNameEntry(file, fileName); + groupEntries.insert(elementName, packageNameEntry); + return packageNameEntry.addUniqueChild(pathElements, pathElementsIndex + 1); + } + } + + private boolean isCaseSensitive() { + if (isCaseSensitive != -1) { + return isCaseSensitive == 1; + } + + File path = file; + + if (path.exists() && path.isFile()) { + path = path.getParentFile(); + } + + if ((!file.exists() && !file.mkdirs())) { + return false; + } + + try { + boolean result = testCaseSensitivity(path); + isCaseSensitive = result?1:0; + return result; + } catch (IOException ex) { + return false; + } + } + + private boolean testCaseSensitivity(File path) throws IOException { + int num = 1; + File f, f2; + do { + f = new File(path, "test." + num); + f2 = new File(path, "TEST." + num++); + } while(f.exists() || f2.exists()); + + try { + try { + FileWriter writer = new FileWriter(f); + writer.write("test"); + writer.flush(); + writer.close(); + } catch (IOException ex) { + try {f.delete();} catch (Exception ex2) {} + throw ex; + } + + if (f2.exists()) { + return false; + } + + if (f2.createNewFile()) { + return true; + } + + //the above 2 tests should catch almost all cases. But maybe there was a failure while creating f2 + //that isn't related to case sensitivity. Let's see if we can open the file we just created using + //f2 + try { + CharBuffer buf = CharBuffer.allocate(32); + FileReader reader = new FileReader(f2); + + while (reader.read(buf) != -1 && buf.length() < 4); + if (buf.length() == 4 && buf.toString().equals("test")) { + return false; + } else { + //we probably shouldn't get here. If the filesystem was case-sensetive, creating a new + //FileReader should have thrown a FileNotFoundException. Otherwise, we should have opened + //the file and read in the string "test". It's remotely possible that someone else modified + //the file after we created it. Let's be safe and return false here as well + assert(false); + return false; + } + } catch (FileNotFoundException ex) { + return true; + } + } finally { + try { f.delete(); } catch (Exception ex) {} + try { f2.delete(); } catch (Exception ex) {} + } + } + + @Override + public FileSystemEntry makeVirtual(File parent) { + return this; + } + } + + private class ClassNameEntry extends FileSystemEntry { + public ClassNameEntry(File parent, String name) { + super(new File(parent, name)); + } + + @Override + public File addUniqueChild(String[] pathElements, int pathElementsIndex) { + assert false; + return file; + } + } +} diff --git a/brut.apktool.smali/util/src/main/java/org/jf/util/CommentingIndentingWriter.java b/brut.apktool.smali/util/src/main/java/org/jf/util/CommentingIndentingWriter.java new file mode 100644 index 00000000..9b1de4f3 --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/org/jf/util/CommentingIndentingWriter.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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.util; + +import java.io.IOException; +import java.io.Writer; + +public class CommentingIndentingWriter extends IndentingWriter { + private final String commentStr; + + public CommentingIndentingWriter(Writer writer, String commentStr) { + super(writer); + this.commentStr = commentStr; + } + + protected void writeLineStart() throws IOException { + writer.write(commentStr); + } +} diff --git a/brut.apktool.smali/util/src/main/java/org/jf/util/ConsoleUtil.java b/brut.apktool.smali/util/src/main/java/org/jf/util/ConsoleUtil.java new file mode 100644 index 00000000..4d1e030f --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/org/jf/util/ConsoleUtil.java @@ -0,0 +1,110 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.util; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ConsoleUtil { + /** + * Attempt to find the width of the console. If it can't get the width, return a default of 80 + * @return + */ + public static int getConsoleWidth() { + if (System.getProperty("os.name").toLowerCase().contains("windows")) { + try { + return attemptMode(); + } catch (Exception ex) { + } + } else { + try { + return attemptStty(); + } catch (Exception ex) { + } + } + + return 80; + } + + private static int attemptStty() { + String output = attemptCommand(new String[]{"sh", "-c", "stty size < /dev/tty"}); + if (output == null) { + return 80; + } + + String[] vals = output.split(" "); + if (vals.length < 2) { + return 80; + } + return Integer.parseInt(vals[1]); + } + + private static int attemptMode() { + String output = attemptCommand(new String[]{"mode", "con"}); + if (output == null) { + return 80; + } + + Pattern pattern = Pattern.compile("Columns:[ \t]*(\\d+)"); + Matcher m = pattern.matcher(output); + if (!m.find()) { + return 80; + } + + return Integer.parseInt(m.group(1)); + } + + private static String attemptCommand(String[] command) { + StringBuffer buffer = null; + + try { + + Process p = Runtime.getRuntime().exec(command); + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + + String line; + + while ((line = reader.readLine()) != null) { + if (buffer == null) { + buffer = new StringBuffer(); + } + + buffer.append(line); + } + + if (buffer != null) { + return buffer.toString(); + } + return null; + } catch (Exception ex) { + return null; + } + } +} diff --git a/brut.apktool.smali/util/src/main/java/org/jf/util/IndentingWriter.java b/brut.apktool.smali/util/src/main/java/org/jf/util/IndentingWriter.java new file mode 100644 index 00000000..422f4412 --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/org/jf/util/IndentingWriter.java @@ -0,0 +1,198 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.util; + +import java.io.IOException; +import java.io.Writer; + +public class IndentingWriter extends Writer { + protected final Writer writer; + protected final char[] buffer = new char[16]; + protected int indentLevel = 0; + private boolean beginningOfLine = true; + private static final String newLine = System.getProperty("line.separator"); + + public IndentingWriter(Writer writer) { + this.writer = writer; + } + + protected void writeLineStart() throws IOException { + } + + protected void writeIndent() throws IOException { + for (int i=0; i>>= 4; + } while (value != 0); + + while (bufferIndex>0) { + write(buffer[--bufferIndex]); + } + } + + public void printSignedIntAsDec(int value) throws IOException { + int bufferIndex = 0; + + if (value < 0) { + value *= -1; + write('-'); + } + + do { + int digit = value % 10; + buffer[bufferIndex++] = (char)(digit + '0'); + + value = value / 10; + } while (value != 0); + + while (bufferIndex>0) { + write(buffer[--bufferIndex]); + } + } +} diff --git a/brut.apktool.smali/util/src/main/java/org/jf/util/PathUtil.java b/brut.apktool.smali/util/src/main/java/org/jf/util/PathUtil.java new file mode 100644 index 00000000..91eb7584 --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/org/jf/util/PathUtil.java @@ -0,0 +1,125 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.util; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +public class PathUtil { + private PathUtil() { + } + + public static File getRelativeFile(File baseFile, File fileToRelativize) throws IOException { + if (baseFile.isFile()) { + baseFile = baseFile.getParentFile(); + } + + return new File(getRelativeFileInternal(baseFile.getCanonicalFile(), fileToRelativize.getCanonicalFile())); + } + + public static String getRelativePath(String basePath, String pathToRelativize) throws IOException { + File baseFile = new File(basePath); + if (baseFile.isFile()) { + baseFile = baseFile.getParentFile(); + } + + return getRelativeFileInternal(baseFile.getCanonicalFile(), + new File(pathToRelativize).getCanonicalFile()); + } + + static String getRelativeFileInternal(File canonicalBaseFile, File canonicalFileToRelativize) { + ArrayList basePath = getPathComponents(canonicalBaseFile); + ArrayList pathToRelativize = getPathComponents(canonicalFileToRelativize); + + //if the roots aren't the same (i.e. different drives on a windows machine), we can't construct a relative + //path from one to the other, so just return the canonical file + if (!basePath.get(0).equals(pathToRelativize.get(0))) { + return canonicalFileToRelativize.getPath(); + } + + int commonDirs; + StringBuilder sb = new StringBuilder(); + + for (commonDirs=1; commonDirs getPathComponents(File file) { + ArrayList path = new ArrayList(); + + while (file != null) { + File parentFile = file.getParentFile(); + + if (parentFile == null) { + path.add(0, file.getPath()); + } else { + path.add(0, file.getName()); + } + + file = parentFile; + } + + return path; + } +} diff --git a/brut.apktool.smali/util/src/main/java/org/jf/util/SmaliHelpFormatter.java b/brut.apktool.smali/util/src/main/java/org/jf/util/SmaliHelpFormatter.java new file mode 100644 index 00000000..3d0137e4 --- /dev/null +++ b/brut.apktool.smali/util/src/main/java/org/jf/util/SmaliHelpFormatter.java @@ -0,0 +1,47 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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.util; + +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; + +import java.io.PrintWriter; + +public class SmaliHelpFormatter extends HelpFormatter { + public void printHelp(String cmdLineSyntax, String header, Options options, Options debugOptions) { + super.printHelp(cmdLineSyntax, header, options, ""); + if (debugOptions != null) { + System.out.println(); + System.out.println("Debug Options:"); + PrintWriter pw = new PrintWriter(System.out); + super.printOptions(pw, getWidth(), debugOptions, getLeftPadding(), getDescPadding()); + pw.flush(); + } + } +} diff --git a/brut.apktool.smali/util/src/test/java/org/jf/util/PathUtilTest.java b/brut.apktool.smali/util/src/test/java/org/jf/util/PathUtilTest.java new file mode 100644 index 00000000..cafc41f5 --- /dev/null +++ b/brut.apktool.smali/util/src/test/java/org/jf/util/PathUtilTest.java @@ -0,0 +1,269 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2010 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.util; + +import org.junit.*; + +import java.io.File; + +public class PathUtilTest { + + @Test + public void pathUtilTest1() { + File[] roots = File.listRoots(); + + if (roots.length > 1) { + File basePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "test.txt"); + File relativePath = new File(roots[1] + "some" + File.separatorChar + "dir" + File.separatorChar + "test.txt"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, relativePath.getPath()); + } + } + + @Test + public void pathUtilTest2() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "test.txt"); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "test.txt"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + /*the "internal" version of the method in PathUtil doesn't handle the case when the "leaf" of the base path + is a file, this is handled by the two public wrappers. Since we're not calling them, the correct return is + a single dot"*/ + Assert.assertEquals(path, "."); + } + + @Test + public void pathUtilTest3() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "."); + } + + @Test + public void pathUtilTest4() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar + "dir"); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "."); + } + + @Test + public void pathUtilTest5() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar + "dir"); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "."); + } + + @Test + public void pathUtilTest6() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "."); + } + + @Test + public void pathUtilTest7() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some"); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "dir"); + } + + @Test + public void pathUtilTest8() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "dir"); + } + + @Test + public void pathUtilTest9() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some"); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "dir"); + } + + @Test + public void pathUtilTest10() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "dir"); + } + + @Test + public void pathUtilTest11() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some"); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "dir2"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "dir" + File.separatorChar + "dir2"); + } + + @Test + public void pathUtilTest12() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "dir2" + File.separatorChar); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "dir" + File.separatorChar + "dir2"); + } + + @Test + public void pathUtilTest13() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some"); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "dir2" + File.separatorChar); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "dir" + File.separatorChar + "dir2"); + } + + @Test + public void pathUtilTest14() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "dir2"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "dir" + File.separatorChar + "dir2"); + } + + @Test + public void pathUtilTest15() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar + "dir3"); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "dir2"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, ".." + File.separatorChar + "dir" + File.separatorChar + "dir2"); + } + + @Test + public void pathUtilTest16() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some2" + File.separatorChar + "dir3"); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "dir2"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, ".." + File.separatorChar + ".." + File.separatorChar + "some" + File.separatorChar + "dir" + File.separatorChar + "dir2"); + } + + @Test + public void pathUtilTest17() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0].getPath()); + File relativePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "dir2"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, "some" + File.separatorChar + "dir" + File.separatorChar + "dir2"); + } + + @Test + public void pathUtilTest18() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar + "dir"); + File relativePath = new File(roots[0] + "some"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, ".."); + } + + @Test + public void pathUtilTest19() { + File[] roots = File.listRoots(); + + File basePath = new File(roots[0] + "some" + File.separatorChar + "dir" + File.separatorChar + "dir2"); + File relativePath = new File(roots[0] + "some"); + + String path = PathUtil.getRelativeFileInternal(basePath, relativePath); + + Assert.assertEquals(path, ".." + File.separatorChar + ".."); + } +} diff --git a/brut.j.common/.gitignore b/brut.j.common/.gitignore new file mode 100644 index 00000000..fc10688e --- /dev/null +++ b/brut.j.common/.gitignore @@ -0,0 +1,6 @@ +/nbproject +/build +/dist +/build.xml +/target + diff --git a/LICENSE b/brut.j.common/LICENSE similarity index 100% rename from LICENSE rename to brut.j.common/LICENSE diff --git a/brut.j.common/NOTICE b/brut.j.common/NOTICE new file mode 100644 index 00000000..7e90fb19 --- /dev/null +++ b/brut.j.common/NOTICE @@ -0,0 +1,6 @@ +Brut Java commons +Copyright 2010 Ryszard Wiśniewski + +This product includes software developed by: + + * Ryszard Wiśniewski (brut.alll@gmail.com) diff --git a/apktool-lib/src/main/java/brut/androlib/AndrolibException.java b/brut.j.common/src/main/java/brut/common/BrutException.java similarity index 67% rename from apktool-lib/src/main/java/brut/androlib/AndrolibException.java rename to brut.j.common/src/main/java/brut/common/BrutException.java index d1601341..4b960d81 100644 --- a/apktool-lib/src/main/java/brut/androlib/AndrolibException.java +++ b/brut.j.common/src/main/java/brut/common/BrutException.java @@ -1,5 +1,5 @@ /** - * Copyright 2011 Ryszard Wiśniewski + * Copyright 2010 Ryszard Wiśniewski * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,26 +14,24 @@ * limitations under the License. */ -package brut.androlib; - -import brut.common.BrutException; +package brut.common; /** * @author Ryszard Wiśniewski */ -public class AndrolibException extends BrutException { - public AndrolibException() { +public class BrutException extends Exception { + public BrutException(Throwable cause) { + super(cause); } - public AndrolibException(String message) { - super(message); - } - - public AndrolibException(String message, Throwable cause) { + public BrutException(String message, Throwable cause) { super(message, cause); } - public AndrolibException(Throwable cause) { - super(cause); + public BrutException(String message) { + super(message); + } + + public BrutException() { } } diff --git a/src/templates/apache2.0-header.txt b/brut.j.common/src/templates/apache2.0-header.txt similarity index 90% rename from src/templates/apache2.0-header.txt rename to brut.j.common/src/templates/apache2.0-header.txt index 5d349945..52100979 100644 --- a/src/templates/apache2.0-header.txt +++ b/brut.j.common/src/templates/apache2.0-header.txt @@ -1,4 +1,4 @@ - Copyright 2011 Ryszard Wiśniewski + Copyright 2010 Ryszard Wiśniewski Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/brut.j.dir/.gitignore b/brut.j.dir/.gitignore new file mode 100644 index 00000000..fc10688e --- /dev/null +++ b/brut.j.dir/.gitignore @@ -0,0 +1,6 @@ +/nbproject +/build +/dist +/build.xml +/target + diff --git a/brut.j.dir/LICENSE b/brut.j.dir/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/brut.j.dir/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/brut.j.dir/NOTICE b/brut.j.dir/NOTICE new file mode 100644 index 00000000..a2a6c467 --- /dev/null +++ b/brut.j.dir/NOTICE @@ -0,0 +1,7 @@ +Brut Directories +Copyright 2010 Ryszard Wiśniewski + +This product includes software developed by: + + * Ryszard Wiśniewski (brut.alll@gmail.com) + * The Apache Software Foundation (http://www.apache.org/) diff --git a/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java new file mode 100644 index 00000000..bdf10223 --- /dev/null +++ b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java @@ -0,0 +1,279 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package brut.directory; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +public abstract class AbstractDirectory implements Directory { + protected Set mFiles; + protected Map mDirs; + + @Override + public Set getFiles() { + return getFiles(false); + } + + @Override + public Set getFiles(boolean recursive) { + if (mFiles == null) { + loadFiles(); + } + if (!recursive) { + return mFiles; + } + + Set files = new LinkedHashSet(mFiles); + for (Map.Entry dir : getAbstractDirs().entrySet()) { + for (String path : dir.getValue().getFiles(true)) { + files.add(dir.getKey() + separator + path); + } + } + return files; + } + + @Override + public boolean containsFile(String path) { + SubPath subpath; + try { + subpath = getSubPath(path); + } catch (PathNotExist e) { + return false; + } + + if (subpath.dir != null) { + return subpath.dir.containsFile(subpath.path); + } + return getFiles().contains(subpath.path); + } + + @Override + public boolean containsDir(String path) { + SubPath subpath; + try { + subpath = getSubPath(path); + } catch (PathNotExist e) { + return false; + } + + if (subpath.dir != null) { + return subpath.dir.containsDir(subpath.path); + } + return getAbstractDirs().containsKey(subpath.path); + } + + @Override + public Map getDirs() + throws UnsupportedOperationException { + return getDirs(false); + } + + @Override + public Map getDirs(boolean recursive) + throws UnsupportedOperationException { + return new LinkedHashMap(getAbstractDirs(recursive)); + } + + @Override + public InputStream getFileInput(String path) throws DirectoryException { + SubPath subpath = getSubPath(path); + if (subpath.dir != null) { + return subpath.dir.getFileInput(subpath.path); + } + if (! getFiles().contains(subpath.path)) { + throw new PathNotExist(path); + } + return getFileInputLocal(subpath.path); + } + + @Override + public OutputStream getFileOutput(String path) throws DirectoryException { + ParsedPath parsed = parsePath(path); + if (parsed.dir == null) { + getFiles().add(parsed.subpath); + return getFileOutputLocal(parsed.subpath); + } + + Directory dir; + // IMPOSSIBLE_EXCEPTION + try { + dir = createDir(parsed.dir); + } catch (PathAlreadyExists e) { + dir = getAbstractDirs().get(parsed.dir); + } + return dir.getFileOutput(parsed.subpath); + } + + @Override + public Directory getDir(String path) throws PathNotExist { + SubPath subpath = getSubPath(path); + if (subpath.dir != null) { + return subpath.dir.getDir(subpath.path); + } + if (! getAbstractDirs().containsKey(subpath.path)) { + throw new PathNotExist(path); + } + return getAbstractDirs().get(subpath.path); + } + + @Override + public Directory createDir(String path) throws DirectoryException { + ParsedPath parsed = parsePath(path); + AbstractDirectory dir; + if (parsed.dir == null) { + if (getAbstractDirs().containsKey(parsed.subpath)) { + throw new PathAlreadyExists(path); + } + dir = createDirLocal(parsed.subpath); + getAbstractDirs().put(parsed.subpath, dir); + return dir; + } + + if (getAbstractDirs().containsKey(parsed.dir)) { + dir = getAbstractDirs().get(parsed.dir); + } else { + dir = createDirLocal(parsed.dir); + getAbstractDirs().put(parsed.dir, dir); + } + return dir.createDir(parsed.subpath); + } + + @Override + public boolean removeFile(String path) { + SubPath subpath; + try { + subpath = getSubPath(path); + } catch (PathNotExist e) { + return false; + } + + if (subpath.dir != null) { + return subpath.dir.removeFile(subpath.path); + } + if (! getFiles().contains(subpath.path)) { + return false; + } + removeFileLocal(subpath.path); + getFiles().remove(subpath.path); + return true; + } + + public void copyToDir(Directory out) throws DirectoryException { + DirUtil.copyToDir(out, out); + } + + public void copyToDir(Directory out, String[] fileNames) + throws DirectoryException { + DirUtil.copyToDir(out, out, fileNames); + } + + public void copyToDir(Directory out, String fileName) + throws DirectoryException { + DirUtil.copyToDir(out, out, fileName); + } + + public void copyToDir(File out) throws DirectoryException { + DirUtil.copyToDir(this, out); + } + + public void copyToDir(File out, String[] fileNames) + throws DirectoryException { + DirUtil.copyToDir(this, out, fileNames); + } + + public void copyToDir(File out, String fileName) + throws DirectoryException { + DirUtil.copyToDir(this, out, fileName); + } + + protected Map getAbstractDirs() { + return getAbstractDirs(false); + } + + protected Map getAbstractDirs(boolean recursive) { + if (mDirs == null) { + loadDirs(); + } + if (!recursive) { + return mDirs; + } + + Map dirs = new LinkedHashMap(mDirs); + for (Map.Entry dir : getAbstractDirs().entrySet()) { + for (Map.Entry subdir : dir.getValue().getAbstractDirs( + true).entrySet()) { + dirs.put(dir.getKey() + separator + subdir.getKey(), + subdir.getValue()); + } + } + return dirs; + } + + private SubPath getSubPath(String path) throws PathNotExist { + ParsedPath parsed = parsePath(path); + if (parsed.dir == null) { + return new SubPath(null, parsed.subpath); + } + if (! getAbstractDirs().containsKey(parsed.dir)) { + throw new PathNotExist(path); + } + return new SubPath(getAbstractDirs().get(parsed.dir), parsed.subpath); + } + + private ParsedPath parsePath(String path) { + int pos = path.indexOf(separator); + if (pos == -1) { + return new ParsedPath(null, path); + } + return new ParsedPath(path.substring(0, pos), path.substring(pos + 1)); + } + + abstract protected void loadFiles(); + abstract protected void loadDirs(); + abstract protected InputStream getFileInputLocal(String name) + throws DirectoryException; + abstract protected OutputStream getFileOutputLocal(String name) + throws DirectoryException; + abstract protected AbstractDirectory createDirLocal(String name) + throws DirectoryException; + abstract protected void removeFileLocal(String name); + + + private class ParsedPath { + public String dir; + public String subpath; + public ParsedPath(String dir, String subpath) { + this.dir = dir; + this.subpath = subpath; + } + } + + private class SubPath { + public AbstractDirectory dir; + public String path; + + public SubPath(AbstractDirectory dir, String path) { + this.dir = dir; + this.path = path; + } + } +} diff --git a/brut.j.dir/src/main/java/brut/directory/ChangesWrapperDirectory.java b/brut.j.dir/src/main/java/brut/directory/ChangesWrapperDirectory.java new file mode 100644 index 00000000..73fa6f38 --- /dev/null +++ b/brut.j.dir/src/main/java/brut/directory/ChangesWrapperDirectory.java @@ -0,0 +1,127 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//package brut.directory; +// +//import java.io.InputStream; +//import java.io.OutputStream; +//import java.util.LinkedHashSet; +//import java.util.Map; +//import java.util.Set; +// +//public class ChangesWrapperDirectory implements Directory { +// private Directory mOriginal; +// private Directory mChanges; +// private Set mRemoved; +// +// public ChangesWrapperDirectory(Directory original, Directory changes) { +// this(original, changes, new LinkedHashSet()); +// } +// +// public ChangesWrapperDirectory(Directory original, Directory changes, +// Set removed) { +// super(); +// mOriginal = original; +// mChanges = changes; +// mRemoved = removed; +// } +// +// public Directory getOriginal() { +// return mOriginal; +// } +// +// public Directory getChanges() { +// return mChanges; +// } +// +// public Set getRemoved() { +// return mRemoved; +// } +// +// @Override +// public boolean containsDir(String path) { +// return ! getRemoved().contains(path) && (getOriginal().containsDir(path) || getChanges().containsDir(path)); +// } +// +// @Override +// public boolean containsFile(String path) { +// return ! getRemoved().contains(path) && (getOriginal().containsFile(path) || getChanges().containsFile(path)); +// } +// +// @Override +// public Directory createDir(String path) throws PathAlreadyExists, +// DirectoryException { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public Directory getDir(String path) throws PathNotExist { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public Map getDirs() { +// return getDirs(false); +// } +// +// @Override +// public Map getDirs(boolean recursive) { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public InputStream getFileInput(String path) throws PathNotExist, +// DirectoryException { +// if (getRemoved().contains(path)) { +// throw new PathNotExist(path); +// } +// if (getChanges().containsFile(path)) { +// return getChanges().getFileInput(path); +// } +// return getOriginal().getFileInput(path); +// } +// +// @Override +// public OutputStream getFileOutput(String path) throws DirectoryException { +// getRemoved().remove(path); +// return getChanges().getFileOutput(path); +// } +// +// @Override +// public Set getFiles() { +// return getFiles(false); +// } +// +// @Override +// public Set getFiles(boolean recursive) { +// Set files = new LinkedHashSet(getOriginal().getFiles(recursive)); +// files.addAll(getChanges().getFiles(recursive)); +// files.removeAll(getRemoved()); +// return files; +// } +// +// @Override +// public boolean removeFile(String path) { +// if(! containsFile(path)) { +// return false; +// } +// +// getChanges().removeFile(path); +// getRemoved().add(path); +// return true; +// } +// +//} diff --git a/brut.j.dir/src/main/java/brut/directory/DirUtil.java b/brut.j.dir/src/main/java/brut/directory/DirUtil.java new file mode 100644 index 00000000..5b85af8f --- /dev/null +++ b/brut.j.dir/src/main/java/brut/directory/DirUtil.java @@ -0,0 +1,92 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package brut.directory; + +import brut.common.BrutException; +import brut.util.BrutIO; +import brut.util.OS; +import java.io.*; + +/** + * @author Ryszard Wiśniewski + */ +public class DirUtil { + public static void copyToDir(Directory in, Directory out) + throws DirectoryException { + for (String fileName : in.getFiles(true)) { + copyToDir(in, out, fileName); + } + } + + public static void copyToDir(Directory in, Directory out, + String[] fileNames) throws DirectoryException { + for (int i = 0; i < fileNames.length; i++) { + copyToDir(in, out, fileNames[i]); + } + } + + public static void copyToDir(Directory in, Directory out, String fileName) + throws DirectoryException { + try { + if (in.containsDir(fileName)) { + // TODO: remove before copying + in.getDir(fileName).copyToDir(out.createDir(fileName)); + } else { + BrutIO.copyAndClose(in.getFileInput(fileName), + out.getFileOutput(fileName)); + } + } catch (IOException ex) { + throw new DirectoryException( + "Error copying file: " + fileName, ex); + } + } + + public static void copyToDir(Directory in, File out) + throws DirectoryException { + for (String fileName : in.getFiles(true)) { + copyToDir(in, out, fileName); + } + } + + public static void copyToDir(Directory in, File out, String[] fileNames) + throws DirectoryException { + for (int i = 0; i < fileNames.length; i++) { + copyToDir(in, out, fileNames[i]); + } + } + + public static void copyToDir(Directory in, File out, String fileName) + throws DirectoryException { + try { + if (in.containsDir(fileName)) { + OS.rmdir(new File(out, fileName)); + in.getDir(fileName).copyToDir(new File(out, fileName)); + } else { + File outFile = new File(out, fileName); + outFile.getParentFile().mkdirs(); + BrutIO.copyAndClose(in.getFileInput(fileName), + new FileOutputStream(outFile)); + } + } catch (IOException ex) { + throw new DirectoryException( + "Error copying file: " + fileName, ex); + } catch (BrutException ex) { + throw new DirectoryException( + "Error copying file: " + fileName, ex); + } + } +} diff --git a/brut.j.dir/src/main/java/brut/directory/Directory.java b/brut.j.dir/src/main/java/brut/directory/Directory.java new file mode 100644 index 00000000..e6ff5b07 --- /dev/null +++ b/brut.j.dir/src/main/java/brut/directory/Directory.java @@ -0,0 +1,51 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package brut.directory; + +import java.io.*; +import java.util.Map; +import java.util.Set; + +public interface Directory { + public Set getFiles(); + public Set getFiles(boolean recursive); + public Map getDirs(); + public Map getDirs(boolean recursive); + + public boolean containsFile(String path); + public boolean containsDir(String path); + + public InputStream getFileInput(String path) throws DirectoryException; + public OutputStream getFileOutput(String path) throws DirectoryException; + public Directory getDir(String path) throws PathNotExist; + public Directory createDir(String path) throws DirectoryException; + + public boolean removeFile(String path); + + public void copyToDir(Directory out) throws DirectoryException; + public void copyToDir(Directory out, String[] fileNames) + throws DirectoryException; + public void copyToDir(Directory out, String fileName) + throws DirectoryException; + public void copyToDir(File out) throws DirectoryException; + public void copyToDir(File out, String[] fileNames) + throws DirectoryException; + public void copyToDir(File out, String fileName) + throws DirectoryException; + + public final char separator = '/'; +} diff --git a/apktool-lib/src/main/java/brut/androlib/err/InFileNotFoundException.java b/brut.j.dir/src/main/java/brut/directory/DirectoryException.java similarity index 51% rename from apktool-lib/src/main/java/brut/androlib/err/InFileNotFoundException.java rename to brut.j.dir/src/main/java/brut/directory/DirectoryException.java index af8de523..8e486156 100644 --- a/apktool-lib/src/main/java/brut/androlib/err/InFileNotFoundException.java +++ b/brut.j.dir/src/main/java/brut/directory/DirectoryException.java @@ -1,5 +1,5 @@ /** - * Copyright 2011 Ryszard Wiśniewski + * Copyright 2010 Ryszard Wiśniewski * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,27 +14,26 @@ * limitations under the License. */ -package brut.androlib.err; +package brut.directory; -import brut.androlib.AndrolibException; +import brut.common.BrutException; -/** - * @author Ryszard Wiśniewski - */ -public class InFileNotFoundException extends AndrolibException { +public class DirectoryException extends BrutException { + private static final long serialVersionUID = -8871963042836625387L; - public InFileNotFoundException(Throwable cause) { - super(cause); + public DirectoryException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); } - public InFileNotFoundException(String message, Throwable cause) { - super(message, cause); + public DirectoryException(String detailMessage) { + super(detailMessage); } - public InFileNotFoundException(String message) { - super(message); + public DirectoryException(Throwable throwable) { + super(throwable); } - public InFileNotFoundException() { + public DirectoryException() { + super(); } } diff --git a/brut.j.dir/src/main/java/brut/directory/FileDirectory.java b/brut.j.dir/src/main/java/brut/directory/FileDirectory.java new file mode 100644 index 00000000..f7d37dc2 --- /dev/null +++ b/brut.j.dir/src/main/java/brut/directory/FileDirectory.java @@ -0,0 +1,108 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package brut.directory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; + +public class FileDirectory extends AbstractDirectory { + private File mDir; + + public FileDirectory(String dir) throws DirectoryException { + this(new File(dir)); + } + + public FileDirectory(File dir) throws DirectoryException { + super(); + if (! dir.isDirectory()) { + throw new DirectoryException("file must be a directory: " + dir); + } + mDir = dir; + } + + @Override + protected AbstractDirectory createDirLocal(String name) throws DirectoryException { + File dir = new File(generatePath(name)); + dir.mkdir(); + return new FileDirectory(dir); + } + + @Override + protected InputStream getFileInputLocal(String name) throws DirectoryException { + try { + return new FileInputStream(generatePath(name)); + } catch (FileNotFoundException e) { + throw new DirectoryException(e); + } + } + + @Override + protected OutputStream getFileOutputLocal(String name) throws DirectoryException { + try { + return new FileOutputStream(generatePath(name)); + } catch (FileNotFoundException e) { + throw new DirectoryException(e); + } + } + + @Override + protected void loadDirs() { + loadAll(); + } + + @Override + protected void loadFiles() { + loadAll(); + } + + @Override + protected void removeFileLocal(String name) { + new File(generatePath(name)).delete(); + } + + private String generatePath(String name) { + return getDir().getPath() + separator + name; + } + + private void loadAll() { + mFiles = new LinkedHashSet(); + mDirs = new LinkedHashMap(); + + File[] files = getDir().listFiles(); + for (int i = 0; i < files.length; i++) { + File file = files[i]; + if (file.isFile()) { + mFiles.add(file.getName()); + } else { + // IMPOSSIBLE_EXCEPTION + try { + mDirs.put(file.getName(), new FileDirectory(file)); + } catch (DirectoryException e) {} + } + } + } + + private File getDir() { + return mDir; + } +} diff --git a/apktool-lib/src/main/java/brut/androlib/err/CantFind9PatchChunk.java b/brut.j.dir/src/main/java/brut/directory/PathAlreadyExists.java similarity index 53% rename from apktool-lib/src/main/java/brut/androlib/err/CantFind9PatchChunk.java rename to brut.j.dir/src/main/java/brut/directory/PathAlreadyExists.java index 0e80bc66..a29b609c 100644 --- a/apktool-lib/src/main/java/brut/androlib/err/CantFind9PatchChunk.java +++ b/brut.j.dir/src/main/java/brut/directory/PathAlreadyExists.java @@ -1,5 +1,5 @@ /** - * Copyright 2011 Ryszard Wiśniewski + * Copyright 2010 Ryszard Wiśniewski * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,27 +14,23 @@ * limitations under the License. */ -package brut.androlib.err; +package brut.directory; -import brut.androlib.AndrolibException; - -/** - * @author Ryszard Wiśniewski - */ -public class CantFind9PatchChunk extends AndrolibException { - - public CantFind9PatchChunk(Throwable cause) { - super(cause); +public class PathAlreadyExists extends DirectoryException { + public PathAlreadyExists() { } - public CantFind9PatchChunk(String message, Throwable cause) { - super(message, cause); + public PathAlreadyExists(Throwable throwable) { + super(throwable); } - public CantFind9PatchChunk(String message) { - super(message); + public PathAlreadyExists(String detailMessage) { + super(detailMessage); } - public CantFind9PatchChunk() { + public PathAlreadyExists(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); } + + private static final long serialVersionUID = 3776428251424428904L; } diff --git a/apktool-lib/src/main/java/brut/androlib/err/UndefinedResObject.java b/brut.j.dir/src/main/java/brut/directory/PathNotExist.java similarity index 53% rename from apktool-lib/src/main/java/brut/androlib/err/UndefinedResObject.java rename to brut.j.dir/src/main/java/brut/directory/PathNotExist.java index 3bcfa356..46a55002 100644 --- a/apktool-lib/src/main/java/brut/androlib/err/UndefinedResObject.java +++ b/brut.j.dir/src/main/java/brut/directory/PathNotExist.java @@ -1,5 +1,5 @@ /** - * Copyright 2011 Ryszard Wiśniewski + * Copyright 2010 Ryszard Wiśniewski * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,26 +14,24 @@ * limitations under the License. */ -package brut.androlib.err; +package brut.directory; -import brut.androlib.AndrolibException; - -/** - * @author Ryszard Wiśniewski - */ -public class UndefinedResObject extends AndrolibException { - public UndefinedResObject(Throwable cause) { - super(cause); +public class PathNotExist extends DirectoryException { + public PathNotExist() { + super(); } - public UndefinedResObject(String message, Throwable cause) { - super(message, cause); + public PathNotExist(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); } - public UndefinedResObject(String message) { - super(message); + public PathNotExist(String detailMessage) { + super(detailMessage); } - public UndefinedResObject() { + public PathNotExist(Throwable throwable) { + super(throwable); } + + private static final long serialVersionUID = -6949242015506342032L; } diff --git a/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java b/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java new file mode 100644 index 00000000..effc4044 --- /dev/null +++ b/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java @@ -0,0 +1,144 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package brut.directory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class ZipRODirectory extends AbstractDirectory { + private ZipFile mZipFile; + private String mPath; + + public ZipRODirectory(String zipFileName) throws DirectoryException { + this(zipFileName, ""); + } + + public ZipRODirectory(File zipFile) throws DirectoryException { + this(zipFile, ""); + } + + public ZipRODirectory(ZipFile zipFile) { + this(zipFile, ""); + } + + public ZipRODirectory(String zipFileName, String path) + throws DirectoryException { + this(new File(zipFileName), path); + } + + public ZipRODirectory(File zipFile, String path) throws DirectoryException { + super(); + try { + mZipFile = new ZipFile(zipFile); + } catch (IOException e) { + throw new DirectoryException(e); + } + mPath = path; + } + + public ZipRODirectory(ZipFile zipFile, String path) { + super(); + mZipFile = zipFile; + mPath = path; + } + + @Override + protected AbstractDirectory createDirLocal(String name) + throws DirectoryException { + throw new UnsupportedOperationException(); + } + + @Override + protected InputStream getFileInputLocal(String name) + throws DirectoryException { + try { + return getZipFile().getInputStream(new ZipEntry(getPath() + name)); + } catch (IOException e) { + throw new PathNotExist(name, e); + } + } + + @Override + protected OutputStream getFileOutputLocal(String name) + throws DirectoryException { + throw new UnsupportedOperationException(); + } + + @Override + protected void loadDirs() { + loadAll(); + } + + @Override + protected void loadFiles() { + loadAll(); + } + + @Override + protected void removeFileLocal(String name) { + throw new UnsupportedOperationException(); + } + + private void loadAll() { + mFiles = new LinkedHashSet(); + mDirs = new LinkedHashMap(); + + int prefixLen = getPath().length(); + Enumeration entries = getZipFile().entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String name = entry.getName(); + + if (name.equals(getPath()) || ! name.startsWith(getPath())) { + continue; + } + + String subname = name.substring(prefixLen); + + int pos = subname.indexOf(separator); + if (pos == -1) { + if (! entry.isDirectory()) { + mFiles.add(subname); + continue; + } + } else { + subname = subname.substring(0, pos); + } + + if (! mDirs.containsKey(subname)) { + AbstractDirectory dir = new ZipRODirectory(getZipFile(), getPath() + subname + separator); + mDirs.put(subname, dir); + } + } + } + + private String getPath() { + return mPath; + } + + private ZipFile getZipFile() { + return mZipFile; + } + +} diff --git a/brut.j.dir/src/templates/apache2.0-header.txt b/brut.j.dir/src/templates/apache2.0-header.txt new file mode 100644 index 00000000..52100979 --- /dev/null +++ b/brut.j.dir/src/templates/apache2.0-header.txt @@ -0,0 +1,14 @@ + Copyright 2010 Ryszard Wiśniewski + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/brut.j.util/.gitignore b/brut.j.util/.gitignore new file mode 100644 index 00000000..fc10688e --- /dev/null +++ b/brut.j.util/.gitignore @@ -0,0 +1,6 @@ +/nbproject +/build +/dist +/build.xml +/target + diff --git a/brut.j.util/LICENSE b/brut.j.util/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/brut.j.util/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/brut.j.util/NOTICE b/brut.j.util/NOTICE new file mode 100644 index 00000000..ba16cf53 --- /dev/null +++ b/brut.j.util/NOTICE @@ -0,0 +1,7 @@ +Brut Java utils +Copyright 2010 Ryszard Wiśniewski + +This product includes software developed by: + + * Ryszard Wiśniewski (brut.alll@gmail.com) + * The Apache Software Foundation (http://www.apache.org/) diff --git a/brut.j.util/src/main/java/brut/util/BrutIO.java b/brut.j.util/src/main/java/brut/util/BrutIO.java new file mode 100644 index 00000000..a0ec17d7 --- /dev/null +++ b/brut.j.util/src/main/java/brut/util/BrutIO.java @@ -0,0 +1,62 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package brut.util; + +import java.io.*; +import org.apache.commons.io.IOUtils; + +/** + * @author Ryszard Wiśniewski + */ +public class BrutIO { + public static void copyAndClose(InputStream in, OutputStream out) + throws IOException { + try { + IOUtils.copy(in, out); + } finally { + try { + in.close(); + out.close(); + } catch (IOException ex) {} + } + } + + public static long recursiveModifiedTime(File[] files) { + long modified = 0; + for (int i = 0; i < files.length; i++) { + long submodified = recursiveModifiedTime(files[i]); + if (submodified > modified) { + modified = submodified; + } + } + return modified; + } + + public static long recursiveModifiedTime(File file) { + long modified = file.lastModified(); + if (file.isDirectory()) { + File[] subfiles = file.listFiles(); + for (int i = 0; i < subfiles.length; i++) { + long submodified = recursiveModifiedTime(subfiles[i]); + if (submodified > modified) { + modified = submodified; + } + } + } + return modified; + } +} diff --git a/brut.j.util/src/main/java/brut/util/DataInputDelegate.java b/brut.j.util/src/main/java/brut/util/DataInputDelegate.java new file mode 100644 index 00000000..f51e6b59 --- /dev/null +++ b/brut.j.util/src/main/java/brut/util/DataInputDelegate.java @@ -0,0 +1,91 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package brut.util; + +import java.io.DataInput; +import java.io.IOException; + +/** + * @author Ryszard Wiśniewski + */ +abstract public class DataInputDelegate implements DataInput { + protected final DataInput mDelegate; + + public DataInputDelegate(DataInput delegate) { + this.mDelegate = delegate; + } + + public int skipBytes(int n) throws IOException { + return mDelegate.skipBytes(n); + } + + public int readUnsignedShort() throws IOException { + return mDelegate.readUnsignedShort(); + } + + public int readUnsignedByte() throws IOException { + return mDelegate.readUnsignedByte(); + } + + public String readUTF() throws IOException { + return mDelegate.readUTF(); + } + + public short readShort() throws IOException { + return mDelegate.readShort(); + } + + public long readLong() throws IOException { + return mDelegate.readLong(); + } + + public String readLine() throws IOException { + return mDelegate.readLine(); + } + + public int readInt() throws IOException { + return mDelegate.readInt(); + } + + public void readFully(byte[] b, int off, int len) throws IOException { + mDelegate.readFully(b, off, len); + } + + public void readFully(byte[] b) throws IOException { + mDelegate.readFully(b); + } + + public float readFloat() throws IOException { + return mDelegate.readFloat(); + } + + public double readDouble() throws IOException { + return mDelegate.readDouble(); + } + + public char readChar() throws IOException { + return mDelegate.readChar(); + } + + public byte readByte() throws IOException { + return mDelegate.readByte(); + } + + public boolean readBoolean() throws IOException { + return mDelegate.readBoolean(); + } +} diff --git a/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java b/brut.j.util/src/main/java/brut/util/Duo.java similarity index 52% rename from apktool-lib/src/main/java/brut/androlib/res/data/ResID.java rename to brut.j.util/src/main/java/brut/util/Duo.java index 24158030..42d88a86 100644 --- a/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java +++ b/brut.j.util/src/main/java/brut/util/Duo.java @@ -1,5 +1,5 @@ /** - * Copyright 2011 Ryszard Wiśniewski + * Copyright 2010 Ryszard Wiśniewski * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,43 +14,18 @@ * limitations under the License. */ -package brut.androlib.res.data; +package brut.util; /** * @author Ryszard Wiśniewski */ -public class ResID { - public final int package_; - public final int type; - public final int entry; +public class Duo { + public final T1 m1; + public final T2 m2; - public final int id; - - public ResID(int package_, int type, int entry) { - this(package_, type, entry, (package_ << 24) + (type << 16) + entry); - } - - public ResID(int id) { - this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id); - } - - public ResID(int package_, int type, int entry, int id) { - this.package_ = package_; - this.type = type; - this.entry = entry; - this.id = id; - } - - @Override - public String toString() { - return String.format("0x%08x", id); - } - - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + this.id; - return hash; + public Duo(T1 t1, T2 t2) { + this.m1 = t1; + this.m2 = t2; } @Override @@ -61,10 +36,21 @@ public class ResID { if (getClass() != obj.getClass()) { return false; } - final ResID other = (ResID) obj; - if (this.id != other.id) { + final Duo other = (Duo) obj; + if (this.m1 != other.m1 && (this.m1 == null || !this.m1.equals(other.m1))) { + return false; + } + if (this.m2 != other.m2 && (this.m2 == null || !this.m2.equals(other.m2))) { return false; } return true; } + + @Override + public int hashCode() { + int hash = 3; + hash = 71 * hash + (this.m1 != null ? this.m1.hashCode() : 0); + hash = 71 * hash + (this.m2 != null ? this.m2.hashCode() : 0); + return hash; + } } diff --git a/brut.j.util/src/main/java/brut/util/ExtDataInput.java b/brut.j.util/src/main/java/brut/util/ExtDataInput.java new file mode 100644 index 00000000..7ef14951 --- /dev/null +++ b/brut.j.util/src/main/java/brut/util/ExtDataInput.java @@ -0,0 +1,85 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package brut.util; + +import java.io.*; + +/** + * @author Ryszard Wiśniewski + */ +public class ExtDataInput extends DataInputDelegate { + public ExtDataInput(InputStream in) { + this((DataInput) new DataInputStream(in)); + } + + public ExtDataInput(DataInput delegate) { + super(delegate); + } + + public int[] readIntArray(int length) throws IOException { + int[] array = new int[length]; + for(int i = 0; i < length; i++) { + array[i] = readInt(); + } + return array; + } + + public void skipInt() throws IOException { + skipBytes(4); + } + + public void skipCheckInt(int expected) throws IOException { + int got = readInt(); + if (got != expected) { + throw new IOException(String.format( + "Expected: 0x%08x, got: 0x%08x", expected, got)); + } + } + + public void skipCheckShort(short expected) throws IOException { + short got = readShort(); + if (got != expected) { + throw new IOException(String.format( + "Expected: 0x%08x, got: 0x%08x", expected, got)); + } + } + + public void skipCheckByte(byte expected) throws IOException { + byte got = readByte(); + if (got != expected) { + throw new IOException(String.format( + "Expected: 0x%08x, got: 0x%08x", expected, got)); + } + } + + public String readNulEndedString(int length, boolean fixed) + throws IOException { + StringBuilder string = new StringBuilder(16); + while(length-- != 0) { + short ch = readShort(); + if (ch == 0) { + break; + } + string.append((char) ch); + } + if (fixed) { + skipBytes(length * 2); + } + + return string.toString(); + } +} diff --git a/brut.j.util/src/main/java/brut/util/Jar.java b/brut.j.util/src/main/java/brut/util/Jar.java new file mode 100644 index 00000000..773bb83b --- /dev/null +++ b/brut.j.util/src/main/java/brut/util/Jar.java @@ -0,0 +1,82 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package brut.util; + +import brut.common.BrutException; +import java.io.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.apache.commons.io.IOUtils; + +/** + * @author Ryszard Wiśniewski + */ +abstract public class Jar { + private final static Set mLoaded = new HashSet(); + private final static Map mExtracted = + new HashMap(); + + public static File getResourceAsFile(String name) throws BrutException { + File file = mExtracted.get(name); + if (file == null) { + file = extractToTmp(name); + mExtracted.put(name, file); + } + return file; + } + + public static void load(String libPath) { + if (mLoaded.contains(libPath)) { + return; + } + + File libFile; + try { + libFile = getResourceAsFile(libPath); + } catch (BrutException ex) { + throw new UnsatisfiedLinkError(ex.getMessage()); + } + + System.load(libFile.getAbsolutePath()); + } + + public static File extractToTmp(String resourcePath) throws BrutException { + return extractToTmp(resourcePath, "brut_util_Jar_"); + } + + public static File extractToTmp(String resourcePath, String tmpPrefix) + throws BrutException { + try { + InputStream in = Class.class.getResourceAsStream(resourcePath); + if (in == null) { + throw new FileNotFoundException(resourcePath); + } + File fileOut = File.createTempFile(tmpPrefix, null); + fileOut.deleteOnExit(); + OutputStream out = new FileOutputStream(fileOut); + IOUtils.copy(in, out); + in.close(); + out.close(); + return fileOut; + } catch (IOException ex) { + throw new BrutException( + "Could not extract resource: " + resourcePath, ex); + } + } +} diff --git a/brut.j.util/src/main/java/brut/util/OS.java b/brut.j.util/src/main/java/brut/util/OS.java new file mode 100644 index 00000000..15f7992f --- /dev/null +++ b/brut.j.util/src/main/java/brut/util/OS.java @@ -0,0 +1,138 @@ +/** + * Copyright 2010 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package brut.util; + +import brut.common.BrutException; +import java.io.*; +import java.util.Arrays; +import org.apache.commons.io.IOUtils; + +/** + * @author Ryszard Wiśniewski + */ +public class OS { + public static void rmdir(File dir) throws BrutException { + if (! dir.exists()) { + return; + } + File[] files = dir.listFiles(); + for (int i = 0; i < files.length; i++) { + File file = files[i]; + if (file.isDirectory()) { + rmdir(file); + } else { + file.delete(); + } + } + dir.delete(); + } + + public static void rmdir(String dir) throws BrutException { + rmdir(new File(dir)); + } + + public static void cpdir(File src, File dest) throws BrutException { + dest.mkdirs(); + File[] files = src.listFiles(); + for (int i = 0; i < files.length; i++) { + File file = files[i]; + File destFile = new File(dest.getPath() + File.separatorChar + + file.getName()); + if (file.isDirectory()) { + cpdir(file, destFile); + continue; + } + try { + InputStream in = new FileInputStream(file); + OutputStream out = new FileOutputStream(destFile); + IOUtils.copy(in, out); + in.close(); + out.close(); + } catch (IOException ex) { + throw new BrutException("Could not copy file: " + file, ex); + } + } + } + + public static void cpdir(String src, String dest) throws BrutException { + cpdir(new File(src), new File(dest)); + } + + public static void exec(String[] cmd) throws BrutException { + Process ps = null; + try { + ps = Runtime.getRuntime().exec(cmd); + + new StreamForwarder(ps.getInputStream(), System.err).start(); + new StreamForwarder(ps.getErrorStream(), System.err).start(); + if (ps.waitFor() != 0) { + throw new BrutException( + "could not exec command: " + Arrays.toString(cmd)); + } + } catch (IOException ex) { + throw new BrutException( + "could not exec command: " + Arrays.toString(cmd), ex); + } catch (InterruptedException ex) { + throw new BrutException( + "could not exec command: " + Arrays.toString(cmd), ex); + } + } + + public static File createTempDirectory() throws BrutException { + try { + File tmp = File.createTempFile("BRUT", null); + if (!tmp.delete()) { + throw new BrutException("Could not delete tmp file: " + tmp.getAbsolutePath()); + } + if (!tmp.mkdir()) { + throw new BrutException("Could not create tmp dir: " + tmp.getAbsolutePath()); + } + return tmp; + } catch (IOException ex) { + throw new BrutException("Could not create tmp dir", ex); + } + } + + static class StreamForwarder extends Thread { + + public StreamForwarder(InputStream in, OutputStream out) { + mIn = in; + mOut = out; + } + + @Override + public void run() { + try { + BufferedReader in = new BufferedReader( + new InputStreamReader(mIn)); + BufferedWriter out = new BufferedWriter( + new OutputStreamWriter(mOut)); + String line; + while ((line = in.readLine()) != null) { + out.write(line); + out.newLine(); + } + out.flush(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + private final InputStream mIn; + private final OutputStream mOut; + } +} diff --git a/brut.j.util/src/templates/apache2.0-header.txt b/brut.j.util/src/templates/apache2.0-header.txt new file mode 100644 index 00000000..52100979 --- /dev/null +++ b/brut.j.util/src/templates/apache2.0-header.txt @@ -0,0 +1,14 @@ + Copyright 2010 Ryszard Wiśniewski + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..fdaa6d37 --- /dev/null +++ b/build.gradle @@ -0,0 +1,118 @@ +apply plugin: 'java' +apply plugin: 'maven' + +repositories { + mavenCentral() +} + +dependencies { +} + +task wrapper(type: Wrapper) { + gradleVersion = '1.2' +} + +subprojects { + apply plugin: 'java' + apply plugin: 'maven' + apply plugin: 'idea' + + repositories { + mavenCentral() + } + dependencies { + testCompile 'junit:junit:4.8.2' + } +} + project(':brut.j.common') { + dependencies { + testCompile "junit:junit:3.8.1" + } + } + project(':brut.j.util') { + dependencies { + compile project(':brut.j.common'), "commons-io:commons-io:1.4" + testCompile "junit:junit:3.8.1" + } + } + project(':brut.j.dir') { + dependencies { + compile project(':brut.j.common'), project(':brut.j.util') + testCompile "junit:junit:3.8.1" + } + } + project(':brut.apktool.smali:util') { + dependencies { + compile 'commons-cli:commons-cli:1.2' + testCompile 'junit:junit:4.6' + } + } + project(':brut.apktool.smali:dexlib') { + dependencies { + compile 'com.google.code.findbugs:jsr305:1.3.9' + compile 'com.google.collections:google-collections:1.0' + } + } + project(':brut.apktool.smali:baksmali') { + dependencies { + compile project(':brut.apktool.smali:util'), project(':brut.apktool.smali:dexlib') + compile 'commons-cli:commons-cli:1.2' + compile 'com.google.code.findbugs:jsr305:1.3.9' + } + } + project(':brut.apktool:apktool-lib') { + + ext.baseVersion = '1.5.1' + ext.jarVersion = baseVersion + + def versionSuffix + try { + def git = org.eclipse.jgit.api.Git.open(file('.')) + def head = git.getRepository().getRef("HEAD") + versionSuffix = head.getObjectId().abbreviate(8).name() + + if (!git.status().call().clean) { + versionSuffix += '-dirty' + } + } catch (Exception) { + // In case we can't get the commit for some reason, + // just use -dev + versionSuffix = 'SNAPSHOT' + } + + version = baseVersion + '-' + versionSuffix + + // use -dev for the jar name, rather than the + // full commit+dirty string + jarVersion = baseVersion + '-dev' + + jar { + version = jarVersion + } + + dependencies { + compile ("junit:junit:4.10") { + exclude(module: 'hamcrest-core') + } + compile project(':brut.j.dir'), project(':brut.j.util'), project(':brut.j.common'), project(':brut.apktool.smali:util'), + project(':brut.apktool.smali:dexlib'), project(':brut.apktool.smali:baksmali'),project(':brut.apktool.smali:smali'), + "org.yaml:snakeyaml:1.7", "xpp3:xpp3:1.1.4c","xmlunit:xmlunit:1.3", "com.google.guava:guava:12.0", + "org.apache.commons:commons-lang3:3.1", "net.lingala.zip4j:zip4j:1.3.1" + } + } + project(':brut.apktool:apktool-cli') { + gradle.taskGraph.whenReady { + // build a jar containing all dependencies + jar { + from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } + + manifest { + attributes("Main-Class": "brut.apktool.Main") + } + } + } + + dependencies { + compile project(':brut.apktool:apktool-lib') + } + } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..08878cbc --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Sep 19 17:44:17 GMT-06:00 2012 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.2-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..d8809f15 --- /dev/null +++ b/gradlew @@ -0,0 +1,168 @@ +#!/bin/bash + +############################################################################## +## ## +## Gradle wrapper script for UN*X ## +## ## +############################################################################## + +# Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together. +# GRADLE_OPTS="$GRADLE_OPTS -Xmx512m" +# JAVA_OPTS="$JAVA_OPTS -Xmx512m" + +GRADLE_APP_NAME=Gradle + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set JAVA_HOME if it's not already set. +if [ -z "$JAVA_HOME" ] ; then + if $darwin ; then + [ -z "$JAVA_HOME" -a -d "/Library/Java/Home" ] && export JAVA_HOME="/Library/Java/Home" + [ -z "$JAVA_HOME" -a -d "/System/Library/Frameworks/JavaVM.framework/Home" ] && export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home" + else + javaExecutable="`which javac`" + [ -z "$javaExecutable" -o "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ] && die "JAVA_HOME not set and cannot find javac to deduce location, please set JAVA_HOME." + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + [ `expr "$readLink" : '\([^ ]*\)'` = "no" ] && die "JAVA_HOME not set and readlink not available, please set JAVA_HOME." + javaExecutable="`readlink -f \"$javaExecutable\"`" + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + export JAVA_HOME="$javaHome" + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVACMD" ] && JAVACMD=`cygpath --unix "$JAVACMD"` + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +STARTER_MAIN_CLASS=org.gradle.wrapper.GradleWrapperMain +CLASSPATH=`dirname "$0"`/gradle/wrapper/gradle-wrapper.jar +WRAPPER_PROPERTIES=`dirname "$0"`/gradle/wrapper/gradle-wrapper.properties +# Determine the Java command to use to start the JVM. +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="java" + fi +fi +if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi +if [ -z "$JAVA_HOME" ] ; then + warn "JAVA_HOME environment variable is not set" +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add GRADLE_APP_NAME to the JAVA_OPTS as -Xdock:name +if $darwin; then + JAVA_OPTS="$JAVA_OPTS -Xdock:name=$GRADLE_APP_NAME" +# we may also want to set -Xdock:image +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + JAVA_HOME=`cygpath --path --mixed "$JAVA_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +GRADLE_APP_BASE_NAME=`basename "$0"` + +exec "$JAVACMD" $JAVA_OPTS $GRADLE_OPTS \ + -classpath "$CLASSPATH" \ + -Dorg.gradle.appname="$GRADLE_APP_BASE_NAME" \ + -Dorg.gradle.wrapper.properties="$WRAPPER_PROPERTIES" \ + $STARTER_MAIN_CLASS \ + "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..4855abb8 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,82 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem ## +@rem Gradle startup script for Windows ## +@rem ## +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together. +@rem set GRADLE_OPTS=%GRADLE_OPTS% -Xmx512m +@rem set JAVA_OPTS=%JAVA_OPTS% -Xmx512m + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=.\ + +@rem Find java.exe +set JAVA_EXE=java.exe +if not defined JAVA_HOME goto init + +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. +echo. +goto end + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set STARTER_MAIN_CLASS=org.gradle.wrapper.GradleWrapperMain +set CLASSPATH=%DIRNAME%\gradle\wrapper\gradle-wrapper.jar +set WRAPPER_PROPERTIES=%DIRNAME%\gradle\wrapper\gradle-wrapper.properties + +set GRADLE_OPTS=%JAVA_OPTS% %GRADLE_OPTS% -Dorg.gradle.wrapper.properties="%WRAPPER_PROPERTIES%" + +@rem Execute Gradle +"%JAVA_EXE%" %GRADLE_OPTS% -classpath "%CLASSPATH%" %STARTER_MAIN_CLASS% %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +if not "%OS%"=="Windows_NT" echo 1 > nul | choice /n /c:1 + +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit "%ERRORLEVEL%" +exit /b "%ERRORLEVEL%" + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega \ No newline at end of file diff --git a/pom.xml b/pom.xml deleted file mode 100644 index b6d7ce84..00000000 --- a/pom.xml +++ /dev/null @@ -1,96 +0,0 @@ - - 4.0.0 - - brut.apktool - apktool-project - 1.0-SNAPSHOT - pom - - apktool - http://github.com/iBotPeaches/brut.apktool - - - UTF-8 - ${basedir} - - - - apktool-lib - apktool-cli - ../brut.apktool.smali - ../brut.j.dir - ../brut.j.common - ../brut.j.util - - - - - - .. - - LICENSE - NOTICE - NOTICE-smali - - - - - - - com.mycila.maven-license-plugin - maven-license-plugin - -
    ${root.basedir}/src/templates/apache2.0-header.txt
    - true - - .gitignore - LICENSE - NOTICE - NOTICE-smali - -
    - - - - check - - - -
    - - org.apache.maven.plugins - maven-compiler-plugin - - 6 - 6 - - -
    - - - - org.apache.maven.wagon - wagon-webdav - 1.0-beta-2 - - -
    - - - - android-apktool.googlecode.com - http://apktool2.googlecode.com/git/m2-releases - - - - - - android-apktool.googlecode.com - dav:https://apktool2.googlecode.com/git/m2-releases - - - android-apktool.googlecode.com - dav:https://apktool2.googlecode.com/git/m2-snapshots - - -
    \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..42b4d382 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +include 'brut.j.common', + 'brut.j.util', + 'brut.j.dir', + 'brut.apktool.smali:util', + 'brut.apktool.smali:dexlib', + 'brut.apktool.smali:baksmali', + 'brut.apktool.smali:smali', + 'brut.apktool:apktool-lib', + 'brut.apktool:apktool-cli' \ No newline at end of file