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