main.java revision 3f7739cd09932129a51badaca9e172aaa47f50f4
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 String bootClassPathDir = "."; 96 int deodexerantPort = 0; 97 98 String[] remainingArgs = commandLine.getArgs(); 99 100 if (commandLine.hasOption("v")) { 101 version(); 102 return; 103 } 104 105 if (commandLine.hasOption("?")) { 106 usage(); 107 return; 108 } 109 110 if (remainingArgs.length != 1) { 111 usage(); 112 return; 113 } 114 115 inputDexFileName = remainingArgs[0]; 116 117 if (commandLine.hasOption("n")) { 118 disassemble = false; 119 } 120 121 if (commandLine.hasOption("d")) { 122 doDump = true; 123 dumpFileName = commandLine.getOptionValue("d", inputDexFileName + ".dump"); 124 } 125 126 if (commandLine.hasOption("w")) { 127 write = true; 128 outputDexFileName = commandLine.getOptionValue("w"); 129 } 130 131 if (commandLine.hasOption("o")) { 132 outputDirectory = commandLine.getOptionValue("o"); 133 } 134 135 if (commandLine.hasOption("s")) { 136 sort = true; 137 } 138 139 if (commandLine.hasOption("f")) { 140 fixRegisters = true; 141 } 142 143 if (commandLine.hasOption("p")) { 144 noParameterRegisters = true; 145 } 146 147 if (commandLine.hasOption("l")) { 148 useLocalsDirective = true; 149 } 150 151 if (commandLine.hasOption("i")) { 152 useSequentialLabels = true; 153 } 154 155 if (commandLine.hasOption("b")) { 156 outputDebugInfo = false; 157 } 158 159 if (commandLine.hasOption("r")) { 160 String[] values = commandLine.getOptionValues('r'); 161 162 if (values == null || values.length == 0) { 163 registerInfo = ARGS | DEST | MERGE; 164 } else { 165 for (String value: values) { 166 if (value.equalsIgnoreCase("ALL")) { 167 registerInfo |= ALL; 168 } else if (value.equalsIgnoreCase("ALLPRE")) { 169 registerInfo |= ALLPRE; 170 } else if (value.equalsIgnoreCase("ALLPOST")) { 171 registerInfo |= ALLPOST; 172 } else if (value.equalsIgnoreCase("ARGS")) { 173 registerInfo |= ARGS; 174 } else if (value.equalsIgnoreCase("DEST")) { 175 registerInfo |= DEST; 176 } else if (value.equalsIgnoreCase("MERGE")) { 177 registerInfo |= MERGE; 178 } else if (value.equalsIgnoreCase("FULLMERGE")) { 179 registerInfo |= FULLMERGE; 180 } else { 181 usage(); 182 return; 183 } 184 } 185 186 if ((registerInfo & FULLMERGE) != 0) { 187 registerInfo &= ~MERGE; 188 } 189 } 190 } 191 192 if (commandLine.hasOption("c")) { 193 bootClassPath = commandLine.getOptionValue("c"); 194 } 195 196 if (commandLine.hasOption("C")) { 197 bootClassPathDir = commandLine.getOptionValue("C"); 198 } 199 200 if (commandLine.hasOption("x")) { 201 String deodexerantAddress = commandLine.getOptionValue("x"); 202 String[] parts = deodexerantAddress.split(":"); 203 if (parts.length != 2) { 204 System.err.println("Invalid deodexerant address. Expecting :<port> or <host>:<port>"); 205 System.exit(1); 206 } 207 208 deodexerantHost = parts[0]; 209 if (deodexerantHost.length() == 0) { 210 deodexerantHost = "localhost"; 211 } 212 try { 213 deodexerantPort = Integer.parseInt(parts[1]); 214 } catch (NumberFormatException ex) { 215 System.err.println("Invalid port \"" + deodexerantPort + "\" for deodexerant address"); 216 System.exit(1); 217 } 218 } 219 220 try { 221 File dexFileFile = new File(inputDexFileName); 222 if (!dexFileFile.exists()) { 223 System.err.println("Can't find the file " + inputDexFileName); 224 System.exit(1); 225 } 226 227 //Read in and parse the dex file 228 DexFile dexFile = new DexFile(dexFileFile, !fixRegisters, false); 229 230 Deodexerant deodexerant = null; 231 232 233 if (deodexerantHost != null) { 234 if (!dexFile.isOdex()) { 235 System.err.println("-x cannot be used with a normal dex file. Ignoring -x"); 236 } 237 deodexerant = new Deodexerant(dexFile, deodexerantHost, deodexerantPort); 238 } 239 240 if (dexFile.isOdex()) { 241 if (doDump) { 242 System.err.println("-d cannot be used with on odex file. Ignoring -d"); 243 } 244 if (write) { 245 System.err.println("-w cannot be used with an odex file. Ignoring -w"); 246 } 247 if (deodexerant == null) { 248 System.err.println("Warning: You are disassembling an odex file without deodexing it. You"); 249 System.err.println("won't be able to re-assemble the results unless you use deodexerant, and"); 250 System.err.println("the -x option for baksmali"); 251 } 252 } 253 254 if (disassemble) { 255 baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, bootClassPathDir, bootClassPath, 256 noParameterRegisters, useLocalsDirective, useSequentialLabels, outputDebugInfo, registerInfo); 257 } 258 259 if ((doDump || write) && !dexFile.isOdex()) { 260 try 261 { 262 dump.dump(dexFile, dumpFileName, outputDexFileName, sort); 263 }catch (IOException ex) { 264 System.err.println("Error occured while writing dump file"); 265 ex.printStackTrace(); 266 } 267 } 268 } catch (RuntimeException ex) { 269 System.err.println("\n\nUNEXPECTED TOP-LEVEL EXCEPTION:"); 270 ex.printStackTrace(); 271 System.exit(1); 272 } catch (Throwable ex) { 273 System.err.println("\n\nUNEXPECTED TOP-LEVEL ERROR:"); 274 ex.printStackTrace(); 275 System.exit(1); 276 } 277 } 278 279 /** 280 * Prints the usage message. 281 */ 282 private static void usage() { 283 HelpFormatter formatter = new HelpFormatter(); 284 formatter.setWidth(100); 285 formatter.printHelp("java -jar baksmali.jar [options] <dex-file>", 286 "disassembles and/or dumps a dex file", options, ""); 287 } 288 289 /** 290 * Prints the version message. 291 */ 292 private static void version() { 293 System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)"); 294 System.out.println("Copyright (C) 2009 Ben Gruver"); 295 System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)"); 296 System.exit(0); 297 } 298 299 private static void buildOptions() { 300 Option versionOption = OptionBuilder.withLongOpt("version") 301 .withDescription("prints the version then exits") 302 .create("v"); 303 304 Option helpOption = OptionBuilder.withLongOpt("help") 305 .withDescription("prints the help message then exits") 306 .create("?"); 307 308 Option noDisassemblyOption = OptionBuilder.withLongOpt("no-disassembly") 309 .withDescription("suppresses the output of the disassembly") 310 .create("n"); 311 312 Option dumpOption = OptionBuilder.withLongOpt("dump-to") 313 .withDescription("dumps the given dex file into a single annotated dump file named FILE" + 314 " (<dexfile>.dump by default), along with the normal disassembly.") 315 .hasOptionalArg() 316 .withArgName("FILE") 317 .create("d"); 318 319 Option writeDexOption = OptionBuilder.withLongOpt("write-dex") 320 .withDescription("additionally rewrites the input dex file to FILE") 321 .hasArg() 322 .withArgName("FILE") 323 .create("w"); 324 325 Option outputDirOption = OptionBuilder.withLongOpt("output") 326 .withDescription("the directory where the disassembled files will be placed. The default is out") 327 .hasArg() 328 .withArgName("DIR") 329 .create("o"); 330 331 Option sortOption = OptionBuilder.withLongOpt("sort") 332 .withDescription("sort the items in the dex file into a canonical order before dumping/writing") 333 .create("s"); 334 335 Option fixSignedRegisterOption = OptionBuilder.withLongOpt("fix-signed-registers") 336 .withDescription("when dumping or rewriting, fix any registers in the debug info that are encoded as" + 337 " a signed value") 338 .create("f"); 339 340 Option noParameterRegistersOption = OptionBuilder.withLongOpt("no-parameter-registers") 341 .withDescription("use the v<n> syntax instead of the p<n> syntax for registers mapped to method" + 342 " parameters") 343 .create("p"); 344 345 Option deodexerantOption = OptionBuilder.withLongOpt("deodexerant") 346 .withDescription("connect to deodexerant on the specified HOST:PORT, and deodex the input odex" 347 + " file. This option is ignored if the input file is a dex file instead of an odex file") 348 .hasArg() 349 .withArgName("HOST:PORT") 350 .create("x"); 351 352 Option useLocalsOption = OptionBuilder.withLongOpt("use-locals") 353 .withDescription("output the .locals directive with the number of non-parameter registers, rather" + 354 " than the .register directive with the total number of register") 355 .create("l"); 356 357 Option sequentialLabelsOption = OptionBuilder.withLongOpt("sequential-labels") 358 .withDescription("create label names using a sequential numbering scheme per label type, rather than " + 359 "using the bytecode address") 360 .create("q"); 361 362 Option noDebugInfoOption = OptionBuilder.withLongOpt("no-debug-info") 363 .withDescription("don't write out debug info (.local, .param, .line, etc.)") 364 .create("b"); 365 366 Option registerInfoOption = OptionBuilder.withLongOpt("register-info") 367 .hasOptionalArgs() 368 .withArgName("REGISTER_INFO_TYPES") 369 .withValueSeparator(',') 370 .withDescription("print the specificed type(s) of register information for each instruction. " + 371 "\"ARGS,DEST,MERGE\" is the default if no types are specified.\nValid values are:\nALL: all " + 372 "pre- and post-instruction registers.\nALLPRE: all pre-instruction registers\nALLPOST: all " + 373 "post-instruction registers\nARGS: any pre-instruction registers used as arguments to the " + 374 "instruction\nDEST: the post-instruction destination register, if any\nMERGE: Any " + 375 "pre-instruction register has been merged from more than 1 different post-instruction " + 376 "register from its predecessors\nFULLMERGE: For each register that would be printed by " + 377 "MERGE, also show the incoming register types that were merged") 378 .create("r"); 379 380 Option classPathOption = OptionBuilder.withLongOpt("bootclasspath") 381 .withDescription("the bootclasspath jars to use, for analysis. Defaults to " + 382 "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar") 383 .hasOptionalArg() 384 .withArgName("BOOTCLASSPATH") 385 .create("c"); 386 387 Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir") 388 .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + 389 "directory") 390 .hasArg() 391 .withArgName("DIR") 392 .create("C"); 393 394 options.addOption(versionOption); 395 options.addOption(helpOption); 396 options.addOption(dumpOption); 397 options.addOption(noDisassemblyOption); 398 options.addOption(writeDexOption); 399 options.addOption(outputDirOption); 400 options.addOption(sortOption); 401 options.addOption(fixSignedRegisterOption); 402 options.addOption(noParameterRegistersOption); 403 options.addOption(deodexerantOption); 404 options.addOption(useLocalsOption); 405 options.addOption(sequentialLabelsOption); 406 options.addOption(noDebugInfoOption); 407 options.addOption(registerInfoOption); 408 options.addOption(classPathOption); 409 options.addOption(classPathDirOption); 410 } 411}