main.java revision 1c56c7e7507dc24ae1ed2f693c793d94df814c76
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package org.jf.baksmali; 18 19import org.apache.commons.cli.*; 20import org.jf.baksmali.Deodex.Deodexerant; 21import org.jf.dexlib.DexFile; 22 23import java.io.File; 24import java.io.InputStream; 25import java.io.IOException; 26import java.util.Properties; 27 28public class main { 29 30 public static final String VERSION; 31 32 private static final Options options; 33 34 public static final int ALL = 1; 35 public static final int ALLPRE = 2; 36 public static final int ALLPOST = 4; 37 public static final int ARGS = 8; 38 public static final int DEST = 16; 39 public static final int MERGE = 32; 40 public static final int FULLMERGE = 64; 41 42 static { 43 options = new Options(); 44 buildOptions(); 45 46 InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties"); 47 Properties properties = new Properties(); 48 String version = "(unknown)"; 49 try { 50 properties.load(templateStream); 51 version = properties.getProperty("application.version"); 52 } catch (IOException ex) { 53 } 54 VERSION = version; 55 } 56 57 /** 58 * This class is uninstantiable. 59 */ 60 private main() { 61 } 62 63 /** 64 * Run! 65 */ 66 public static void main(String[] args) { 67 CommandLineParser parser = new PosixParser(); 68 CommandLine commandLine; 69 70 try { 71 commandLine = parser.parse(options, args); 72 } catch (ParseException ex) { 73 usage(); 74 return; 75 } 76 77 boolean disassemble = true; 78 boolean doDump = false; 79 boolean write = false; 80 boolean sort = false; 81 boolean fixRegisters = false; 82 boolean noParameterRegisters = false; 83 boolean useLocalsDirective = false; 84 boolean useSequentialLabels = false; 85 boolean outputDebugInfo = true; 86 87 int registerInfo = 0; 88 89 String outputDirectory = "out"; 90 String dumpFileName = null; 91 String outputDexFileName = null; 92 String inputDexFileName = null; 93 String deodexerantHost = null; 94 String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar"; 95 int deodexerantPort = 0; 96 97 String[] remainingArgs = commandLine.getArgs(); 98 99 if (commandLine.hasOption("v")) { 100 version(); 101 return; 102 } 103 104 if (commandLine.hasOption("?")) { 105 usage(); 106 return; 107 } 108 109 if (remainingArgs.length != 1) { 110 usage(); 111 return; 112 } 113 114 inputDexFileName = remainingArgs[0]; 115 116 if (commandLine.hasOption("n")) { 117 disassemble = false; 118 } 119 120 if (commandLine.hasOption("d")) { 121 doDump = true; 122 dumpFileName = commandLine.getOptionValue("d", inputDexFileName + ".dump"); 123 } 124 125 if (commandLine.hasOption("w")) { 126 write = true; 127 outputDexFileName = commandLine.getOptionValue("w"); 128 } 129 130 if (commandLine.hasOption("o")) { 131 outputDirectory = commandLine.getOptionValue("o"); 132 } 133 134 if (commandLine.hasOption("s")) { 135 sort = true; 136 } 137 138 if (commandLine.hasOption("f")) { 139 fixRegisters = true; 140 } 141 142 if (commandLine.hasOption("p")) { 143 noParameterRegisters = true; 144 } 145 146 if (commandLine.hasOption("l")) { 147 useLocalsDirective = true; 148 } 149 150 if (commandLine.hasOption("i")) { 151 useSequentialLabels = true; 152 } 153 154 if (commandLine.hasOption("b")) { 155 outputDebugInfo = false; 156 } 157 158 if (commandLine.hasOption("r")) { 159 String[] values = commandLine.getOptionValues('r'); 160 161 if (values == null || values.length == 0) { 162 registerInfo = ARGS | DEST | MERGE; 163 } else { 164 for (String value: values) { 165 if (value.equalsIgnoreCase("ALL")) { 166 registerInfo |= ALL; 167 } else if (value.equalsIgnoreCase("ALLPRE")) { 168 registerInfo |= ALLPRE; 169 } else if (value.equalsIgnoreCase("ALLPOST")) { 170 registerInfo |= ALLPOST; 171 } else if (value.equalsIgnoreCase("ARGS")) { 172 registerInfo |= ARGS; 173 } else if (value.equalsIgnoreCase("DEST")) { 174 registerInfo |= DEST; 175 } else if (value.equalsIgnoreCase("MERGE")) { 176 registerInfo |= MERGE; 177 } else if (value.equalsIgnoreCase("FULLMERGE")) { 178 registerInfo |= FULLMERGE; 179 } else { 180 usage(); 181 return; 182 } 183 } 184 185 if ((registerInfo & FULLMERGE) != 0) { 186 registerInfo &= ~MERGE; 187 } 188 } 189 } 190 191 if (commandLine.hasOption("c")) { 192 bootClassPath = commandLine.getOptionValue("c"); 193 } 194 195 if (commandLine.hasOption("x")) { 196 String deodexerantAddress = commandLine.getOptionValue("x"); 197 String[] parts = deodexerantAddress.split(":"); 198 if (parts.length != 2) { 199 System.err.println("Invalid deodexerant address. Expecting :<port> or <host>:<port>"); 200 System.exit(1); 201 } 202 203 deodexerantHost = parts[0]; 204 if (deodexerantHost.length() == 0) { 205 deodexerantHost = "localhost"; 206 } 207 try { 208 deodexerantPort = Integer.parseInt(parts[1]); 209 } catch (NumberFormatException ex) { 210 System.err.println("Invalid port \"" + deodexerantPort + "\" for deodexerant address"); 211 System.exit(1); 212 } 213 } 214 215 try { 216 File dexFileFile = new File(inputDexFileName); 217 if (!dexFileFile.exists()) { 218 System.err.println("Can't find the file " + inputDexFileName); 219 System.exit(1); 220 } 221 222 //Read in and parse the dex file 223 DexFile dexFile = new DexFile(dexFileFile, !fixRegisters, false); 224 225 Deodexerant deodexerant = null; 226 227 228 if (deodexerantHost != null) { 229 if (!dexFile.isOdex()) { 230 System.err.println("-x cannot be used with a normal dex file. Ignoring -x"); 231 } 232 deodexerant = new Deodexerant(dexFile, deodexerantHost, deodexerantPort); 233 } 234 235 if (dexFile.isOdex()) { 236 if (doDump) { 237 System.err.println("-d cannot be used with on odex file. Ignoring -d"); 238 } 239 if (write) { 240 System.err.println("-w cannot be used with an odex file. Ignoring -w"); 241 } 242 if (deodexerant == null) { 243 System.err.println("Warning: You are disassembling an odex file without deodexing it. You"); 244 System.err.println("won't be able to re-assemble the results unless you use deodexerant, and"); 245 System.err.println("the -x option for baksmali"); 246 } 247 } 248 249 if (disassemble) { 250 baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, bootClassPath, noParameterRegisters, 251 useLocalsDirective, useSequentialLabels, outputDebugInfo, registerInfo); 252 } 253 254 if ((doDump || write) && !dexFile.isOdex()) { 255 try 256 { 257 dump.dump(dexFile, dumpFileName, outputDexFileName, sort); 258 }catch (IOException ex) { 259 System.err.println("Error occured while writing dump file"); 260 ex.printStackTrace(); 261 } 262 } 263 } catch (RuntimeException ex) { 264 System.err.println("\n\nUNEXPECTED TOP-LEVEL EXCEPTION:"); 265 ex.printStackTrace(); 266 System.exit(1); 267 } catch (Throwable ex) { 268 System.err.println("\n\nUNEXPECTED TOP-LEVEL ERROR:"); 269 ex.printStackTrace(); 270 System.exit(1); 271 } 272 } 273 274 /** 275 * Prints the usage message. 276 */ 277 private static void usage() { 278 HelpFormatter formatter = new HelpFormatter(); 279 formatter.setWidth(100); 280 formatter.printHelp("java -jar baksmali.jar [options] <dex-file>", 281 "disassembles and/or dumps a dex file", options, ""); 282 } 283 284 /** 285 * Prints the version message. 286 */ 287 private static void version() { 288 System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)"); 289 System.out.println("Copyright (C) 2009 Ben Gruver"); 290 System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)"); 291 System.exit(0); 292 } 293 294 private static void buildOptions() { 295 Option versionOption = OptionBuilder.withLongOpt("version") 296 .withDescription("prints the version then exits") 297 .create("v"); 298 299 Option helpOption = OptionBuilder.withLongOpt("help") 300 .withDescription("prints the help message then exits") 301 .create("?"); 302 303 Option noDisassemblyOption = OptionBuilder.withLongOpt("no-disassembly") 304 .withDescription("suppresses the output of the disassembly") 305 .create("n"); 306 307 Option dumpOption = OptionBuilder.withLongOpt("dump-to") 308 .withDescription("dumps the given dex file into a single annotated dump file named FILE" + 309 " (<dexfile>.dump by default), along with the normal disassembly.") 310 .hasOptionalArg() 311 .withArgName("FILE") 312 .create("d"); 313 314 Option writeDexOption = OptionBuilder.withLongOpt("write-dex") 315 .withDescription("additionally rewrites the input dex file to FILE") 316 .hasArg() 317 .withArgName("FILE") 318 .create("w"); 319 320 Option outputDirOption = OptionBuilder.withLongOpt("output") 321 .withDescription("the directory where the disassembled files will be placed. The default is out") 322 .hasArg() 323 .withArgName("DIR") 324 .create("o"); 325 326 Option sortOption = OptionBuilder.withLongOpt("sort") 327 .withDescription("sort the items in the dex file into a canonical order before dumping/writing") 328 .create("s"); 329 330 Option fixSignedRegisterOption = OptionBuilder.withLongOpt("fix-signed-registers") 331 .withDescription("when dumping or rewriting, fix any registers in the debug info that are encoded as" + 332 " a signed value") 333 .create("f"); 334 335 Option noParameterRegistersOption = OptionBuilder.withLongOpt("no-parameter-registers") 336 .withDescription("use the v<n> syntax instead of the p<n> syntax for registers mapped to method" + 337 " parameters") 338 .create("p"); 339 340 Option deodexerantOption = OptionBuilder.withLongOpt("deodexerant") 341 .withDescription("connect to deodexerant on the specified HOST:PORT, and deodex the input odex" 342 + " file. This option is ignored if the input file is a dex file instead of an odex file") 343 .hasArg() 344 .withArgName("HOST:PORT") 345 .create("x"); 346 347 Option useLocalsOption = OptionBuilder.withLongOpt("use-locals") 348 .withDescription("output the .locals directive with the number of non-parameter registers, rather" + 349 " than the .register directive with the total number of register") 350 .create("l"); 351 352 Option sequentialLabelsOption = OptionBuilder.withLongOpt("sequential-labels") 353 .withDescription("create label names using a sequential numbering scheme per label type, rather than " + 354 "using the bytecode address") 355 .create("q"); 356 357 Option noDebugInfoOption = OptionBuilder.withLongOpt("no-debug-info") 358 .withDescription("don't write out debug info (.local, .param, .line, etc.)") 359 .create("b"); 360 361 Option registerInfoOption = OptionBuilder.withLongOpt("register-info") 362 .hasOptionalArgs() 363 .withArgName("REGISTER_INFO_TYPES") 364 .withValueSeparator(',') 365 .withDescription("print the specificed type(s) of register information for each instruction. " + 366 "\"ARGS,DEST,MERGE\" is the default if no types are specified.\nValid values are:\nALL: all " + 367 "pre- and post-instruction registers.\nALLPRE: all pre-instruction registers\nALLPOST: all " + 368 "post-instruction registers\nARGS: any pre-instruction registers used as arguments to the " + 369 "instruction\nDEST: the post-instruction destination register, if any\nMERGE: Any " + 370 "pre-instruction register has been merged from more than 1 different post-instruction " + 371 "register from its predecessors\nFULLMERGE: For each register that would be printed by " + 372 "MERGE, also show the incoming register types that were merged") 373 .create("r"); 374 375 Option classPathOption = OptionBuilder.withLongOpt("bootclasspath") 376 .withDescription("the bootclasspath jars to use, for analysis") 377 .hasOptionalArg() 378 .withArgName("BOOTCLASSPATH") 379 .create("c"); 380 381 options.addOption(versionOption); 382 options.addOption(helpOption); 383 options.addOption(dumpOption); 384 options.addOption(noDisassemblyOption); 385 options.addOption(writeDexOption); 386 options.addOption(outputDirOption); 387 options.addOption(sortOption); 388 options.addOption(fixSignedRegisterOption); 389 options.addOption(noParameterRegistersOption); 390 options.addOption(deodexerantOption); 391 options.addOption(useLocalsOption); 392 options.addOption(sequentialLabelsOption); 393 options.addOption(noDebugInfoOption); 394 options.addOption(registerInfoOption); 395 options.addOption(classPathOption); 396 } 397}