1/* 2 * Copyright 2016, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32package org.jf.baksmali; 33 34import com.beust.jcommander.JCommander; 35import com.beust.jcommander.Parameter; 36import com.google.common.base.Strings; 37import com.google.common.collect.Lists; 38import org.jf.dexlib2.DexFileFactory; 39import org.jf.dexlib2.Opcodes; 40import org.jf.dexlib2.dexbacked.DexBackedDexFile; 41import org.jf.util.jcommander.Command; 42import org.jf.util.jcommander.ExtendedParameter; 43 44import javax.annotation.Nonnull; 45import java.io.File; 46import java.io.IOException; 47import java.util.List; 48 49/** 50 * This class implements common functionality for commands that need to load a dex file based on 51 * command line input 52 */ 53public abstract class DexInputCommand extends Command { 54 55 @Parameter(names = {"-a", "--api"}, 56 description = "The numeric api level of the file being disassembled.") 57 @ExtendedParameter(argumentNames = "api") 58 public int apiLevel = 15; 59 60 @Parameter(description = "A dex/apk/oat/odex file. For apk or oat files that contain multiple dex " + 61 "files, you can specify the specific entry to use as if the apk/oat file was a directory. " + 62 "e.g. \"app.apk/classes2.dex\". For more information, see \"baksmali help input\".") 63 @ExtendedParameter(argumentNames = "file") 64 protected List<String> inputList = Lists.newArrayList(); 65 66 protected File inputFile; 67 protected String inputEntry; 68 protected DexBackedDexFile dexFile; 69 70 public DexInputCommand(@Nonnull List<JCommander> commandAncestors) { 71 super(commandAncestors); 72 } 73 74 /** 75 * Parses a dex file input from the user and loads the given dex file. 76 * 77 * In some cases, the input file can contain multiple dex files. If this is the case, you can refer to a specific 78 * dex file with a slash, followed by the entry name, optionally in quotes. 79 * 80 * If the entry name is enclosed in quotes, then it will strip the first and last quote and look for an entry with 81 * exactly that name. Otherwise, it will perform a partial filename match against the entry to find any candidates. 82 * If there is a single matching candidate, it will be used. Otherwise, an error will be generated. 83 * 84 * For example, to refer to the "/system/framework/framework.jar:classes2.dex" entry within the 85 * "framework/arm/framework.oat" oat file, you could use any of: 86 * 87 * framework/arm/framework.oat/"/system/framework/framework.jar:classes2.dex" 88 * framework/arm/framework.oat/system/framework/framework.jar:classes2.dex 89 * framework/arm/framework.oat/framework/framework.jar:classes2.dex 90 * framework/arm/framework.oat/framework.jar:classes2.dex 91 * framework/arm/framework.oat/classes2.dex 92 * 93 * The last option is the easiest, but only works if the oat file doesn't contain another entry with the 94 * "classes2.dex" name. e.g. "/system/framework/blah.jar:classes2.dex" 95 * 96 * It's technically possible (although unlikely) for an oat file to contain 2 entries like: 97 * /system/framework/framework.jar:classes2.dex 98 * system/framework/framework.jar:classes2.dex 99 * 100 * In this case, the "framework/arm/framework.oat/system/framework/framework.jar:classes2.dex" syntax will generate 101 * an error because both entries match the partial entry name. Instead, you could use the following for the 102 * first and second entry respectively: 103 * 104 * framework/arm/framework.oat/"/system/framework/framework.jar:classes2.dex" 105 * framework/arm/framework.oat/"system/framework/framework.jar:classes2.dex" 106 * 107 * @param input The name of a dex, apk, odex or oat file/entry. 108 */ 109 protected void loadDexFile(@Nonnull String input) { 110 File file = new File(input); 111 112 while (file != null && !file.exists()) { 113 file = file.getParentFile(); 114 } 115 116 if (file == null || !file.exists() || file.isDirectory()) { 117 System.err.println("Can't find file: " + input); 118 System.exit(1); 119 } 120 121 inputFile = file; 122 123 String dexEntry = null; 124 if (file.getPath().length() < input.length()) { 125 dexEntry = input.substring(file.getPath().length() + 1); 126 } 127 128 if (!Strings.isNullOrEmpty(dexEntry)) { 129 boolean exactMatch = false; 130 if (dexEntry.length() > 2 && dexEntry.charAt(0) == '"' && dexEntry.charAt(dexEntry.length() - 1) == '"') { 131 dexEntry = dexEntry.substring(1, dexEntry.length() - 1); 132 exactMatch = true; 133 } 134 135 inputEntry = dexEntry; 136 137 try { 138 dexFile = DexFileFactory.loadDexEntry(file, dexEntry, exactMatch, Opcodes.forApi(apiLevel)); 139 } catch (IOException ex) { 140 throw new RuntimeException(ex); 141 } 142 } else { 143 try { 144 dexFile = DexFileFactory.loadDexFile(file, Opcodes.forApi(apiLevel)); 145 } catch (IOException ex) { 146 throw new RuntimeException(ex); 147 } 148 } 149 } 150} 151