main.java revision 7bb8ace93bb02750a95d3a68fe99153980abdee8
1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2010 Ben Gruver (JesusFreke) 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29package org.jf.baksmali; 30 31import org.apache.commons.cli.*; 32import org.jf.dexlib.DexFile; 33 34import java.io.File; 35import java.io.InputStream; 36import java.io.IOException; 37import java.util.ArrayList; 38import java.util.List; 39import java.util.Properties; 40 41public class main { 42 43 public static final String VERSION; 44 45 private static final Options basicOptions; 46 private static final Options debugOptions; 47 private static final Options options; 48 49 public static final int ALL = 1; 50 public static final int ALLPRE = 2; 51 public static final int ALLPOST = 4; 52 public static final int ARGS = 8; 53 public static final int DEST = 16; 54 public static final int MERGE = 32; 55 public static final int FULLMERGE = 64; 56 57 static { 58 options = new Options(); 59 basicOptions = new Options(); 60 debugOptions = new Options(); 61 buildOptions(); 62 63 InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties"); 64 Properties properties = new Properties(); 65 String version = "(unknown)"; 66 try { 67 properties.load(templateStream); 68 version = properties.getProperty("application.version"); 69 } catch (IOException ex) { 70 } 71 VERSION = version; 72 } 73 74 /** 75 * This class is uninstantiable. 76 */ 77 private main() { 78 } 79 80 /** 81 * Run! 82 */ 83 public static void main(String[] args) { 84 CommandLineParser parser = new PosixParser(); 85 CommandLine commandLine; 86 87 try { 88 commandLine = parser.parse(options, args); 89 } catch (ParseException ex) { 90 usage(); 91 return; 92 } 93 94 boolean disassemble = true; 95 boolean doDump = false; 96 boolean write = false; 97 boolean sort = false; 98 boolean fixRegisters = false; 99 boolean noParameterRegisters = false; 100 boolean useLocalsDirective = false; 101 boolean useSequentialLabels = false; 102 boolean outputDebugInfo = true; 103 boolean addCodeOffsets = false; 104 boolean deodex = false; 105 boolean verify = false; 106 107 int registerInfo = 0; 108 109 String outputDirectory = "out"; 110 String dumpFileName = null; 111 String outputDexFileName = null; 112 String inputDexFileName = null; 113 String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar"; 114 List<String> bootClassPathDirs = new ArrayList<String>(); 115 bootClassPathDirs.add("."); 116 117 118 String[] remainingArgs = commandLine.getArgs(); 119 120 Option[] options = commandLine.getOptions(); 121 122 for (int i=0; i<options.length; i++) { 123 Option option = options[i]; 124 String opt = option.getOpt(); 125 126 switch (opt.charAt(0)) { 127 case 'v': 128 version(); 129 return; 130 case '?': 131 while (++i < options.length) { 132 if (options[i].getOpt().charAt(0) == '?') { 133 usage(true); 134 return; 135 } 136 } 137 usage(false); 138 return; 139 case 'o': 140 outputDirectory = commandLine.getOptionValue("o"); 141 break; 142 case 'p': 143 noParameterRegisters = true; 144 break; 145 case 'l': 146 useLocalsDirective = true; 147 break; 148 case 's': 149 useSequentialLabels = true; 150 break; 151 case 'b': 152 outputDebugInfo = false; 153 break; 154 case 'd': 155 bootClassPathDirs.add(option.getValue()); 156 break; 157 case 'f': 158 addCodeOffsets = true; 159 break; 160 case 'r': 161 String[] values = commandLine.getOptionValues('r'); 162 163 if (values == null || values.length == 0) { 164 registerInfo = ARGS | DEST; 165 } else { 166 for (String value: values) { 167 if (value.equalsIgnoreCase("ALL")) { 168 registerInfo |= ALL; 169 } else if (value.equalsIgnoreCase("ALLPRE")) { 170 registerInfo |= ALLPRE; 171 } else if (value.equalsIgnoreCase("ALLPOST")) { 172 registerInfo |= ALLPOST; 173 } else if (value.equalsIgnoreCase("ARGS")) { 174 registerInfo |= ARGS; 175 } else if (value.equalsIgnoreCase("DEST")) { 176 registerInfo |= DEST; 177 } else if (value.equalsIgnoreCase("MERGE")) { 178 registerInfo |= MERGE; 179 } else if (value.equalsIgnoreCase("FULLMERGE")) { 180 registerInfo |= FULLMERGE; 181 } else { 182 usage(); 183 return; 184 } 185 } 186 187 if ((registerInfo & FULLMERGE) != 0) { 188 registerInfo &= ~MERGE; 189 } 190 } 191 break; 192 case 'c': 193 String bcp = commandLine.getOptionValue("c"); 194 if (bcp != null && bcp.charAt(0) == ':') { 195 bootClassPath = bootClassPath + bcp; 196 } else { 197 bootClassPath = bcp; 198 } 199 break; 200 case 'x': 201 deodex = true; 202 break; 203 case 'N': 204 disassemble = false; 205 break; 206 case 'D': 207 doDump = true; 208 dumpFileName = commandLine.getOptionValue("D", inputDexFileName + ".dump"); 209 break; 210 case 'W': 211 write = true; 212 outputDexFileName = commandLine.getOptionValue("W"); 213 break; 214 case 'S': 215 sort = true; 216 break; 217 case 'F': 218 fixRegisters = true; 219 break; 220 case 'V': 221 verify = true; 222 break; 223 default: 224 assert false; 225 } 226 } 227 228 if (remainingArgs.length != 1) { 229 usage(); 230 return; 231 } 232 233 inputDexFileName = remainingArgs[0]; 234 235 try { 236 File dexFileFile = new File(inputDexFileName); 237 if (!dexFileFile.exists()) { 238 System.err.println("Can't find the file " + inputDexFileName); 239 System.exit(1); 240 } 241 242 //Read in and parse the dex file 243 DexFile dexFile = new DexFile(dexFileFile, !fixRegisters, false); 244 245 if (dexFile.isOdex()) { 246 if (doDump) { 247 System.err.println("-D cannot be used with on odex file. Ignoring -D"); 248 } 249 if (write) { 250 System.err.println("-W cannot be used with an odex file. Ignoring -W"); 251 } 252 if (!deodex) { 253 System.err.println("Warning: You are disassembling an odex file without deodexing it. You"); 254 System.err.println("won't be able to re-assemble the results unless you deodex it with the -x"); 255 System.err.println("option"); 256 } 257 } else { 258 deodex = false; 259 } 260 261 if (disassemble) { 262 String[] bootClassPathDirsArray = new String[bootClassPathDirs.size()]; 263 for (int i=0; i<bootClassPathDirsArray.length; i++) { 264 bootClassPathDirsArray[i] = bootClassPathDirs.get(i); 265 } 266 267 baksmali.disassembleDexFile(dexFile, deodex, outputDirectory, bootClassPathDirsArray, bootClassPath, 268 noParameterRegisters, useLocalsDirective, useSequentialLabels, outputDebugInfo, addCodeOffsets, 269 registerInfo, verify); 270 } 271 272 if ((doDump || write) && !dexFile.isOdex()) { 273 try 274 { 275 dump.dump(dexFile, dumpFileName, outputDexFileName, sort); 276 }catch (IOException ex) { 277 System.err.println("Error occured while writing dump file"); 278 ex.printStackTrace(); 279 } 280 } 281 } catch (RuntimeException ex) { 282 System.err.println("\n\nUNEXPECTED TOP-LEVEL EXCEPTION:"); 283 ex.printStackTrace(); 284 System.exit(1); 285 } catch (Throwable ex) { 286 System.err.println("\n\nUNEXPECTED TOP-LEVEL ERROR:"); 287 ex.printStackTrace(); 288 System.exit(1); 289 } 290 } 291 292 /** 293 * Prints the usage message. 294 */ 295 private static void usage(boolean printDebugOptions) { 296 baksmaliHelpFormatter formatter = new baksmaliHelpFormatter(); 297 formatter.setWidth(100); 298 299 formatter.printHelp("java -jar baksmali.jar [options] <dex-file>", 300 "disassembles and/or dumps a dex file", basicOptions, ""); 301 302 if (printDebugOptions) { 303 System.out.println(); 304 System.out.println("Debug Options:"); 305 306 StringBuffer sb = new StringBuffer(); 307 formatter.renderOptions(sb, debugOptions); 308 System.out.println(sb.toString()); 309 } 310 } 311 312 private static void usage() { 313 usage(false); 314 } 315 316 /** 317 * Prints the version message. 318 */ 319 protected static void version() { 320 System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)"); 321 System.out.println("Copyright (C) 2010 Ben Gruver (JesusFreke@JesusFreke.com)"); 322 System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)"); 323 System.exit(0); 324 } 325 326 private static void buildOptions() { 327 Option versionOption = OptionBuilder.withLongOpt("version") 328 .withDescription("prints the version then exits") 329 .create("v"); 330 331 Option helpOption = OptionBuilder.withLongOpt("help") 332 .withDescription("prints the help message then exits. Specify twice for debug options") 333 .create("?"); 334 335 Option noDisassemblyOption = OptionBuilder.withLongOpt("no-disassembly") 336 .withDescription("suppresses the output of the disassembly") 337 .create("N"); 338 339 Option dumpOption = OptionBuilder.withLongOpt("dump-to") 340 .withDescription("dumps the given dex file into a single annotated dump file named FILE" + 341 " (<dexfile>.dump by default), along with the normal disassembly.") 342 .hasOptionalArg() 343 .withArgName("FILE") 344 .create("D"); 345 346 Option writeDexOption = OptionBuilder.withLongOpt("write-dex") 347 .withDescription("additionally rewrites the input dex file to FILE") 348 .hasArg() 349 .withArgName("FILE") 350 .create("W"); 351 352 Option outputDirOption = OptionBuilder.withLongOpt("output") 353 .withDescription("the directory where the disassembled files will be placed. The default is out") 354 .hasArg() 355 .withArgName("DIR") 356 .create("o"); 357 358 Option sortOption = OptionBuilder.withLongOpt("sort") 359 .withDescription("sort the items in the dex file into a canonical order before dumping/writing") 360 .create("S"); 361 362 Option fixSignedRegisterOption = OptionBuilder.withLongOpt("fix-signed-registers") 363 .withDescription("when dumping or rewriting, fix any registers in the debug info that are encoded as" + 364 " a signed value") 365 .create("F"); 366 367 Option noParameterRegistersOption = OptionBuilder.withLongOpt("no-parameter-registers") 368 .withDescription("use the v<n> syntax instead of the p<n> syntax for registers mapped to method " + 369 "parameters") 370 .create("p"); 371 372 Option deodexerantOption = OptionBuilder.withLongOpt("deodex") 373 .withDescription("deodex the given odex file. This option is ignored if the input file is not an " + 374 "odex file.") 375 .create("x"); 376 377 Option useLocalsOption = OptionBuilder.withLongOpt("use-locals") 378 .withDescription("output the .locals directive with the number of non-parameter registers, rather" + 379 " than the .register directive with the total number of register") 380 .create("l"); 381 382 Option sequentialLabelsOption = OptionBuilder.withLongOpt("sequential-labels") 383 .withDescription("create label names using a sequential numbering scheme per label type, rather than " + 384 "using the bytecode address") 385 .create("s"); 386 387 Option noDebugInfoOption = OptionBuilder.withLongOpt("no-debug-info") 388 .withDescription("don't write out debug info (.local, .param, .line, etc.)") 389 .create("b"); 390 391 Option registerInfoOption = OptionBuilder.withLongOpt("register-info") 392 .hasOptionalArgs() 393 .withArgName("REGISTER_INFO_TYPES") 394 .withValueSeparator(',') 395 .withDescription("print the specificed type(s) of register information for each instruction. " + 396 "\"ARGS,DEST\" is the default if no types are specified.\nValid values are:\nALL: all " + 397 "pre- and post-instruction registers.\nALLPRE: all pre-instruction registers\nALLPOST: all " + 398 "post-instruction registers\nARGS: any pre-instruction registers used as arguments to the " + 399 "instruction\nDEST: the post-instruction destination register, if any\nMERGE: Any " + 400 "pre-instruction register has been merged from more than 1 different post-instruction " + 401 "register from its predecessors\nFULLMERGE: For each register that would be printed by " + 402 "MERGE, also show the incoming register types that were merged") 403 .create("r"); 404 405 Option classPathOption = OptionBuilder.withLongOpt("bootclasspath") 406 .withDescription("the bootclasspath jars to use, for analysis. Defaults to " + 407 "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar. If the value begins with a " + 408 ":, it will be appended to the default bootclasspath instead of replacing it") 409 .hasOptionalArg() 410 .withArgName("BOOTCLASSPATH") 411 .create("c"); 412 413 Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir") 414 .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + 415 "directory") 416 .hasArg() 417 .withArgName("DIR") 418 .create("d"); 419 420 Option codeOffsetOption = OptionBuilder.withLongOpt("code-offsets") 421 .withDescription("add comments to the disassembly containing the code offset for each address") 422 .create("f"); 423 424 Option verifyDexOption = OptionBuilder.withLongOpt("verify") 425 .withDescription("perform bytecode verification") 426 .create("V"); 427 428 basicOptions.addOption(versionOption); 429 basicOptions.addOption(helpOption); 430 basicOptions.addOption(outputDirOption); 431 basicOptions.addOption(noParameterRegistersOption); 432 basicOptions.addOption(deodexerantOption); 433 basicOptions.addOption(useLocalsOption); 434 basicOptions.addOption(sequentialLabelsOption); 435 basicOptions.addOption(noDebugInfoOption); 436 basicOptions.addOption(registerInfoOption); 437 basicOptions.addOption(classPathOption); 438 basicOptions.addOption(classPathDirOption); 439 basicOptions.addOption(codeOffsetOption); 440 441 debugOptions.addOption(dumpOption); 442 debugOptions.addOption(noDisassemblyOption); 443 debugOptions.addOption(writeDexOption); 444 debugOptions.addOption(sortOption); 445 debugOptions.addOption(fixSignedRegisterOption); 446 debugOptions.addOption(verifyDexOption); 447 448 449 for (Object option: basicOptions.getOptions()) { 450 options.addOption((Option)option); 451 } 452 for (Object option: debugOptions.getOptions()) { 453 options.addOption((Option)option); 454 } 455 } 456}