mirror of
https://github.com/revanced/Apktool.git
synced 2025-06-12 05:07:41 +02:00
@ -36,10 +36,8 @@ dependencies {
|
||||
compile project(':brut.j.dir'),
|
||||
project(':brut.j.util'),
|
||||
project(':brut.j.common'),
|
||||
project(':brut.apktool.smali:util'),
|
||||
project(':brut.apktool.smali:dexlib2'),
|
||||
project(':brut.apktool.smali:baksmali'),
|
||||
project(':brut.apktool.smali:smali'),
|
||||
depends.baksmali,
|
||||
depends.smali,
|
||||
depends.snakeyaml,
|
||||
depends.xmlpull,
|
||||
depends.guava,
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package brut.androlib;
|
||||
|
||||
import brut.androlib.java.AndrolibJava;
|
||||
import brut.androlib.meta.MetaInfo;
|
||||
import brut.androlib.meta.UsesFramework;
|
||||
import brut.androlib.res.AndrolibResources;
|
||||
@ -80,8 +79,8 @@ public class Androlib {
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeSourcesSmali(File apkFile, File outDir, String filename, boolean debug, String debugLinePrefix,
|
||||
boolean bakdeb, int api) throws AndrolibException {
|
||||
public void decodeSourcesSmali(File apkFile, File outDir, String filename, boolean bakdeb, int api)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
File smaliDir;
|
||||
if (filename.equalsIgnoreCase("classes.dex")) {
|
||||
@ -92,18 +91,12 @@ public class Androlib {
|
||||
OS.rmdir(smaliDir);
|
||||
smaliDir.mkdirs();
|
||||
LOGGER.info("Baksmaling " + filename + "...");
|
||||
SmaliDecoder.decode(apkFile, smaliDir, filename, debug, debugLinePrefix, bakdeb, api);
|
||||
SmaliDecoder.decode(apkFile, smaliDir, filename, bakdeb, api);
|
||||
} catch (BrutException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeSourcesJava(ExtFile apkFile, File outDir, boolean debug)
|
||||
throws AndrolibException {
|
||||
LOGGER.info("Decoding Java sources...");
|
||||
new AndrolibJava().decode(apkFile, outDir);
|
||||
}
|
||||
|
||||
public void decodeManifestRaw(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
@ -299,9 +292,7 @@ public class Androlib {
|
||||
|
||||
public void buildSources(File appDir)
|
||||
throws AndrolibException {
|
||||
if (!buildSourcesRaw(appDir, "classes.dex")
|
||||
&& !buildSourcesSmali(appDir, "smali", "classes.dex")
|
||||
&& !buildSourcesJava(appDir)) {
|
||||
if (!buildSourcesRaw(appDir, "classes.dex") && !buildSourcesSmali(appDir, "smali", "classes.dex")) {
|
||||
LOGGER.warning("Could not find sources");
|
||||
}
|
||||
}
|
||||
@ -316,9 +307,7 @@ public class Androlib {
|
||||
if (name.startsWith("smali_")) {
|
||||
String filename = name.substring(name.indexOf("_") + 1) + ".dex";
|
||||
|
||||
if (!buildSourcesRaw(appDir, filename)
|
||||
&& !buildSourcesSmali(appDir, name, filename)
|
||||
&& !buildSourcesJava(appDir)) {
|
||||
if (!buildSourcesRaw(appDir, filename) && !buildSourcesSmali(appDir, name, filename)) {
|
||||
LOGGER.warning("Could not find sources");
|
||||
}
|
||||
}
|
||||
@ -372,25 +361,7 @@ public class Androlib {
|
||||
if (apkOptions.forceBuildAll || isModified(smaliDir, dex)) {
|
||||
LOGGER.info("Smaling " + folder + " folder into " + filename +"...");
|
||||
dex.delete();
|
||||
SmaliBuilder.build(smaliDir, dex, apkOptions.debugMode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean buildSourcesJava(File appDir)
|
||||
throws AndrolibException {
|
||||
File javaDir = new File(appDir, "src");
|
||||
if (!javaDir.exists()) {
|
||||
return false;
|
||||
}
|
||||
File dex = new File(appDir, APK_DIRNAME + "/classes.dex");
|
||||
if (! apkOptions.forceBuildAll) {
|
||||
LOGGER.info("Checking whether sources has changed...");
|
||||
}
|
||||
if (apkOptions.forceBuildAll || isModified(javaDir, dex)) {
|
||||
LOGGER.info("Building java sources...");
|
||||
dex.delete();
|
||||
new AndrolibJava().build(javaDir, dex);
|
||||
SmaliBuilder.build(smaliDir, dex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -487,10 +458,6 @@ public class Androlib {
|
||||
|
||||
File apkDir = new File(appDir, APK_DIRNAME);
|
||||
|
||||
if (apkOptions.debugMode) {
|
||||
ResXmlPatcher.removeApplicationDebugTag(new File(apkDir,"AndroidManifest.xml"));
|
||||
}
|
||||
|
||||
if (apkOptions.forceBuildAll || isModified(newFiles(APK_MANIFEST_FILENAMES, appDir),
|
||||
newFiles(APK_MANIFEST_FILENAMES, apkDir))) {
|
||||
LOGGER.info("Building AndroidManifest.xml...");
|
||||
|
@ -128,10 +128,7 @@ public class ApkDecoder {
|
||||
mAndrolib.decodeSourcesRaw(mApkFile, outDir, "classes.dex");
|
||||
break;
|
||||
case DECODE_SOURCES_SMALI:
|
||||
mAndrolib.decodeSourcesSmali(mApkFile, outDir, "classes.dex", mDebug, mDebugLinePrefix, mBakDeb, mApi);
|
||||
break;
|
||||
case DECODE_SOURCES_JAVA:
|
||||
mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug);
|
||||
mAndrolib.decodeSourcesSmali(mApkFile, outDir, "classes.dex", mBakDeb, mApi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -147,10 +144,7 @@ public class ApkDecoder {
|
||||
mAndrolib.decodeSourcesRaw(mApkFile, outDir, file);
|
||||
break;
|
||||
case DECODE_SOURCES_SMALI:
|
||||
mAndrolib.decodeSourcesSmali(mApkFile, outDir, file, mDebug, mDebugLinePrefix, mBakDeb, mApi);
|
||||
break;
|
||||
case DECODE_SOURCES_JAVA:
|
||||
mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug);
|
||||
mAndrolib.decodeSourcesSmali(mApkFile, outDir, file, mBakDeb, mApi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -167,7 +161,7 @@ public class ApkDecoder {
|
||||
}
|
||||
|
||||
public void setDecodeSources(short mode) throws AndrolibException {
|
||||
if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI && mode != DECODE_SOURCES_JAVA) {
|
||||
if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI) {
|
||||
throw new AndrolibException("Invalid decode sources mode: " + mode);
|
||||
}
|
||||
mDecodeSources = mode;
|
||||
@ -180,11 +174,6 @@ public class ApkDecoder {
|
||||
mDecodeResources = mode;
|
||||
}
|
||||
|
||||
public void setDebugMode(boolean debug) {
|
||||
LOGGER.warning("SmaliDebugging has been deprecated. It will be removed in Apktool 2.1 - https://github.com/iBotPeaches/Apktool/issues/1061");
|
||||
mDebug = debug;
|
||||
}
|
||||
|
||||
public void setAnalysisMode(boolean mode, boolean pass) throws AndrolibException{
|
||||
mAnalysisMode = mode;
|
||||
|
||||
@ -208,10 +197,6 @@ public class ApkDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
public void setDebugLinePrefix(String debugLinePrefix) {
|
||||
mDebugLinePrefix = debugLinePrefix;
|
||||
}
|
||||
|
||||
public void setBaksmaliDebugMode(boolean bakdeb) {
|
||||
mBakDeb = bakdeb;
|
||||
}
|
||||
@ -288,7 +273,6 @@ public class ApkDecoder {
|
||||
|
||||
public final static short DECODE_SOURCES_NONE = 0x0000;
|
||||
public final static short DECODE_SOURCES_SMALI = 0x0001;
|
||||
public final static short DECODE_SOURCES_JAVA = 0x0002;
|
||||
|
||||
public final static short DECODE_RESOURCES_NONE = 0x0100;
|
||||
public final static short DECODE_RESOURCES_FULL = 0x0101;
|
||||
@ -396,8 +380,6 @@ public class ApkDecoder {
|
||||
private ResTable mResTable;
|
||||
private short mDecodeSources = DECODE_SOURCES_SMALI;
|
||||
private short mDecodeResources = DECODE_RESOURCES_FULL;
|
||||
private String mDebugLinePrefix = "a=0;// ";
|
||||
private boolean mDebug = false;
|
||||
private boolean mForceDelete = false;
|
||||
private boolean mKeepBrokenResources = false;
|
||||
private boolean mBakDeb = true;
|
||||
|
@ -19,7 +19,6 @@ import java.util.Collection;
|
||||
|
||||
public class ApkOptions {
|
||||
public boolean forceBuildAll = false;
|
||||
public boolean debugMode = false;
|
||||
public boolean verbose = false;
|
||||
public boolean copyOriginalFiles = false;
|
||||
public boolean updateFiles = false;
|
||||
|
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package brut.androlib.java;
|
||||
|
||||
import brut.androlib.res.util.ExtFile;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
public class AndrolibJava {
|
||||
|
||||
public void decode(ExtFile apkFile, File outDir) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
public void build(File javaDir, File dex) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
}
|
@ -343,10 +343,6 @@ final public class AndrolibResources {
|
||||
if (apkOptions.updateFiles) {
|
||||
cmd.add("-u");
|
||||
}
|
||||
if (apkOptions.debugMode) { // inject debuggable="true" into manifest
|
||||
cmd.add("--debug-mode");
|
||||
}
|
||||
|
||||
// force package id so that some frameworks build with correct id
|
||||
// disable if user adds own aapt (can't know if they have this feature)
|
||||
if (mPackageId != null && ! customAapt && ! mSharedLibrary) {
|
||||
|
@ -40,35 +40,6 @@ import java.io.IOException;
|
||||
* @author Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||
*/
|
||||
public final class ResXmlPatcher {
|
||||
|
||||
/**
|
||||
* Removes "debug" tag from file
|
||||
*
|
||||
* @param file AndroidManifest file
|
||||
* @throws AndrolibException
|
||||
*/
|
||||
public static void removeApplicationDebugTag(File file) throws AndrolibException {
|
||||
if (file.exists()) {
|
||||
try {
|
||||
Document doc = loadDocument(file);
|
||||
Node application = doc.getElementById("application");
|
||||
|
||||
// load attr
|
||||
NamedNodeMap attr = application.getAttributes();
|
||||
Node debugAttr = attr.getNamedItem("debug");
|
||||
|
||||
// remove application:debug
|
||||
if (debugAttr != null) {
|
||||
attr.removeNamedItem("debug");
|
||||
}
|
||||
|
||||
saveDocument(file, doc);
|
||||
|
||||
} catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Any @string reference in a <provider> value in AndroidManifest.xml will break on
|
||||
* build, thus preventing the application from installing. This is from a bug/error
|
||||
|
@ -1,244 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package brut.androlib.src;
|
||||
|
||||
import brut.androlib.AndrolibException;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
public class DebugInjector {
|
||||
|
||||
private boolean areParamsInjected;
|
||||
private int currParam;
|
||||
private int lastParam;
|
||||
|
||||
public static void inject(ListIterator<String> it, StringBuilder out)
|
||||
throws AndrolibException {
|
||||
new DebugInjector(it, out).inject();
|
||||
}
|
||||
|
||||
private DebugInjector(ListIterator<String> it, StringBuilder out) {
|
||||
mIt = it;
|
||||
mOut = out;
|
||||
}
|
||||
|
||||
private void inject() throws AndrolibException {
|
||||
String definition = nextAndAppend();
|
||||
if (definition.contains(" abstract ")
|
||||
|| definition.contains(" native ")) {
|
||||
nextAndAppend();
|
||||
return;
|
||||
}
|
||||
parseParamsNumber(definition);
|
||||
|
||||
boolean end = false;
|
||||
while (!end) {
|
||||
end = step();
|
||||
}
|
||||
}
|
||||
|
||||
private void parseParamsNumber(String definition) throws AndrolibException {
|
||||
int pos = definition.indexOf('(');
|
||||
if (pos == -1) {
|
||||
throw new AndrolibException();
|
||||
}
|
||||
int pos2 = definition.indexOf(')', pos);
|
||||
if (pos2 == -1) {
|
||||
throw new AndrolibException();
|
||||
}
|
||||
String params = definition.substring(pos + 1, pos2);
|
||||
|
||||
currParam = definition.contains(" static ") ? 0 : 1;
|
||||
lastParam = TypeName.listFromInternalName(params).size() + currParam - 1;
|
||||
}
|
||||
|
||||
private void injectRemainingParams() {
|
||||
areParamsInjected = true;
|
||||
while(currParam <= lastParam) {
|
||||
//mOut.append(".param \"p").append(currParam).append("\"\n");
|
||||
currParam++;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean step() {
|
||||
String line = next();
|
||||
if (line.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (line.charAt(0)) {
|
||||
case '#':
|
||||
return processComment(line);
|
||||
case ':':
|
||||
append(line);
|
||||
return false;
|
||||
case '.':
|
||||
return processDirective(line);
|
||||
default:
|
||||
if (! areParamsInjected) {
|
||||
injectRemainingParams();
|
||||
}
|
||||
return processInstruction(line);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean processComment(String line) {
|
||||
if (mFirstInstruction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Matcher m = REGISTER_INFO_PATTERN.matcher(line);
|
||||
|
||||
while (m.find()) {
|
||||
String localName = m.group(1);
|
||||
String localType = null;
|
||||
switch (m.group(2)) {
|
||||
case "Reference":
|
||||
case "UninitRef":
|
||||
case "REFERENCE":
|
||||
case "Null":
|
||||
case "UninitThis":
|
||||
localType = "Ljava/lang/Object;";
|
||||
break;
|
||||
case "Boolean":
|
||||
localType = "Z";
|
||||
break;
|
||||
case "Integer":
|
||||
case "One":
|
||||
case "Unknown":
|
||||
localType = "I";
|
||||
break;
|
||||
case "Uninit":
|
||||
case "Conflicted":
|
||||
if (mInitializedRegisters.remove(localName)) {
|
||||
mOut.append(".end local ").append(localName).append('\n');
|
||||
}
|
||||
continue;
|
||||
case "Short":
|
||||
case "PosShort":
|
||||
localType = "S";
|
||||
break;
|
||||
case "Byte":
|
||||
case "PosByte":
|
||||
localType = "B";
|
||||
break;
|
||||
case "Char":
|
||||
localType = "C";
|
||||
break;
|
||||
case "Float":
|
||||
localType = "F";
|
||||
break;
|
||||
case "LongHi":
|
||||
case "LongLo":
|
||||
localType = "J";
|
||||
break;
|
||||
case "DoubleHi":
|
||||
case "DoubleLo":
|
||||
localType = "D";
|
||||
break;
|
||||
default:
|
||||
System.err.println(line);
|
||||
System.err.println(m.group(2));
|
||||
System.err.println(m.group(3));
|
||||
assert false;
|
||||
}
|
||||
|
||||
mInitializedRegisters.add(localName);
|
||||
mOut.append(".local ").append(localName).append(", ").append('"')
|
||||
.append(localName).append('"').append(':').append(localType)
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processDirective(String line) {
|
||||
String line2 = line.substring(1);
|
||||
if (line2.startsWith("line ") || line2.startsWith("local ") || line2.startsWith("end local ")) {
|
||||
return false;
|
||||
}
|
||||
if (line2.equals("prologue")) {
|
||||
if (! areParamsInjected) {
|
||||
injectRemainingParams();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (line2.equals("param")) {
|
||||
mOut.append(".param \"p").append(currParam++).append("\"\n");
|
||||
return false;
|
||||
}
|
||||
if (line2.startsWith("param")) {
|
||||
mOut.append(line).append("\n");
|
||||
currParam++;
|
||||
return false;
|
||||
}
|
||||
|
||||
append(line);
|
||||
if (line2.equals("end method")) {
|
||||
return true;
|
||||
}
|
||||
if (line2.startsWith("annotation ") || line2.equals("sparse-switch")
|
||||
|| line2.startsWith("packed-switch ")
|
||||
|| line2.startsWith("array-data ")) {
|
||||
while (true) {
|
||||
line2 = nextAndAppend();
|
||||
if (line2.startsWith(".end ")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processInstruction(String line) {
|
||||
if (mFirstInstruction) {
|
||||
mOut.append(".prologue\n");
|
||||
mFirstInstruction = false;
|
||||
}
|
||||
mOut.append(".line ").append(mIt.nextIndex()).append('\n').append(line)
|
||||
.append('\n');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String next() {
|
||||
return mIt.next().split("//", 2)[1].trim();
|
||||
}
|
||||
|
||||
private String nextAndAppend() {
|
||||
String line = next();
|
||||
append(line);
|
||||
return line;
|
||||
}
|
||||
|
||||
private void append(String append) {
|
||||
mOut.append(append).append('\n');
|
||||
}
|
||||
|
||||
private final ListIterator<String> mIt;
|
||||
private final StringBuilder mOut;
|
||||
|
||||
private boolean mFirstInstruction = true;
|
||||
private final Set<String> mInitializedRegisters = new HashSet<String>();
|
||||
|
||||
private static final Pattern REGISTER_INFO_PATTERN = Pattern
|
||||
.compile("((?:p|v)\\d+)=\\(([^,)]+)([^)]*)\\);");
|
||||
}
|
@ -21,9 +21,7 @@ import brut.androlib.mod.SmaliMod;
|
||||
import brut.androlib.res.util.ExtFile;
|
||||
import brut.directory.DirectoryException;
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
@ -36,15 +34,13 @@ import org.jf.dexlib2.writer.io.FileDataStore;
|
||||
*/
|
||||
public class SmaliBuilder {
|
||||
|
||||
public static void build(ExtFile smaliDir, File dexFile, boolean debug)
|
||||
throws AndrolibException {
|
||||
new SmaliBuilder(smaliDir, dexFile, debug).build();
|
||||
public static void build(ExtFile smaliDir, File dexFile) throws AndrolibException {
|
||||
new SmaliBuilder(smaliDir, dexFile).build();
|
||||
}
|
||||
|
||||
private SmaliBuilder(ExtFile smaliDir, File dexFile, boolean debug) {
|
||||
private SmaliBuilder(ExtFile smaliDir, File dexFile) {
|
||||
mSmaliDir = smaliDir;
|
||||
mDexFile = dexFile;
|
||||
mDebug = debug;
|
||||
}
|
||||
|
||||
private void build() throws AndrolibException {
|
||||
@ -84,29 +80,9 @@ public class SmaliBuilder {
|
||||
List<String> lines = IOUtils.readLines(inStream);
|
||||
inStream.close();
|
||||
|
||||
if (! mDebug) {
|
||||
final String[] linesArray = lines.toArray(new String[0]);
|
||||
for (int i = 1; i < linesArray.length - 1; i++) {
|
||||
out.append(linesArray[i].split("//", 2)[1]).append('\n');
|
||||
}
|
||||
} else {
|
||||
lines.remove(lines.size() - 1);
|
||||
ListIterator<String> it = lines.listIterator(1);
|
||||
|
||||
out.append(".source \"").append(inFile.getName()).append("\"\n");
|
||||
while (it.hasNext()) {
|
||||
String line = it.next().split("//", 2)[1].trim();
|
||||
if (line.isEmpty() || line.charAt(0) == '#' || line.startsWith(".source")) {
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith(".method ")) {
|
||||
it.previous();
|
||||
DebugInjector.inject(it, out);
|
||||
continue;
|
||||
}
|
||||
|
||||
out.append(line).append('\n');
|
||||
}
|
||||
final String[] linesArray = lines.toArray(new String[0]);
|
||||
for (int i = 1; i < linesArray.length - 1; i++) {
|
||||
out.append(linesArray[i].split("//", 2)[1]).append('\n');
|
||||
}
|
||||
try {
|
||||
if (!SmaliMod.assembleSmaliFile(out.toString(),dexBuilder, false, false, inFile)) {
|
||||
@ -119,7 +95,6 @@ public class SmaliBuilder {
|
||||
|
||||
private final ExtFile mSmaliDir;
|
||||
private final File mDexFile;
|
||||
private final boolean mDebug;
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class.getName());
|
||||
}
|
||||
|
@ -20,52 +20,38 @@ import brut.androlib.AndrolibException;
|
||||
import org.jf.baksmali.baksmali;
|
||||
import org.jf.baksmali.baksmaliOptions;
|
||||
import org.jf.dexlib2.DexFileFactory;
|
||||
import org.jf.dexlib2.analysis.ClassPath;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
|
||||
import org.jf.dexlib2.analysis.InlineMethodResolver;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
public class SmaliDecoder {
|
||||
|
||||
public static void decode(File apkFile, File outDir, String dexName, boolean debug, String debugLinePrefix,
|
||||
boolean bakdeb, int api) throws AndrolibException {
|
||||
new SmaliDecoder(apkFile, outDir, dexName, debug, debugLinePrefix, bakdeb, api).decode();
|
||||
public static void decode(File apkFile, File outDir, String dexName, boolean bakdeb, int api)
|
||||
throws AndrolibException {
|
||||
new SmaliDecoder(apkFile, outDir, dexName, bakdeb, api).decode();
|
||||
}
|
||||
|
||||
private SmaliDecoder(File apkFile, File outDir, String dexName, boolean debug, String debugLinePrefix,
|
||||
boolean bakdeb, int api) {
|
||||
private SmaliDecoder(File apkFile, File outDir, String dexName, boolean bakdeb, int api) {
|
||||
mApkFile = apkFile;
|
||||
mOutDir = outDir.toPath();
|
||||
mOutDir = outDir;
|
||||
mDexFile = dexName;
|
||||
mDebug = debug;
|
||||
mDebugLinePrefix = debugLinePrefix;
|
||||
mBakDeb = bakdeb;
|
||||
mApi = api;
|
||||
mBakDeb = bakdeb;
|
||||
mApi = api;
|
||||
}
|
||||
|
||||
private void decode() throws AndrolibException {
|
||||
try {
|
||||
ClassPath.dontLoadClassPath = mDebug;
|
||||
|
||||
baksmaliOptions options = new baksmaliOptions();
|
||||
|
||||
// options
|
||||
options.deodex = false;
|
||||
options.outputDirectory = mOutDir.toAbsolutePath().toString();
|
||||
options.outputDirectory = mOutDir.toString();
|
||||
options.noParameterRegisters = false;
|
||||
options.useLocalsDirective = true;
|
||||
options.useSequentialLabels = true;
|
||||
@ -73,21 +59,15 @@ public class SmaliDecoder {
|
||||
options.addCodeOffsets = false;
|
||||
options.jobs = -1;
|
||||
options.noAccessorComments = false;
|
||||
options.registerInfo = (mDebug ? baksmaliOptions.DIFFPRE : 0);
|
||||
options.registerInfo = 0;
|
||||
options.ignoreErrors = false;
|
||||
options.inlineResolver = null;
|
||||
options.checkPackagePrivateAccess = false;
|
||||
|
||||
// set jobs automatically
|
||||
if (options.jobs <= 0) {
|
||||
if (mDebug) {
|
||||
options.jobs = 1;
|
||||
} else {
|
||||
options.jobs = Runtime.getRuntime().availableProcessors();
|
||||
if (options.jobs > 6) {
|
||||
options.jobs = 6;
|
||||
}
|
||||
}
|
||||
options.jobs = Runtime.getRuntime().availableProcessors();
|
||||
if (options.jobs > 6) {
|
||||
options.jobs = 6;
|
||||
}
|
||||
|
||||
// create the dex
|
||||
@ -102,54 +82,15 @@ public class SmaliDecoder {
|
||||
InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile)dexFile).getOdexVersion());
|
||||
}
|
||||
|
||||
baksmali.disassembleDexFile(dexFile,options);
|
||||
|
||||
if (mDebug) {
|
||||
Files.walkFileTree(mOutDir, new SmaliFileVisitor());
|
||||
}
|
||||
baksmali.disassembleDexFile(dexFile, options);
|
||||
} catch (IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private final File mApkFile;
|
||||
private final Path mOutDir;
|
||||
private final boolean mDebug;
|
||||
private final String mDebugLinePrefix;
|
||||
private final File mOutDir;
|
||||
private final String mDexFile;
|
||||
private final boolean mBakDeb;
|
||||
private final int mApi;
|
||||
|
||||
private class SmaliFileVisitor extends SimpleFileVisitor<Path> {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
String fileName = file.getFileName().toString();
|
||||
if (! fileName.endsWith(".smali")) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
fileName = fileName.substring(0, fileName.length() - 6);
|
||||
try (
|
||||
BufferedReader in = Files.newBufferedReader(file, Charset.defaultCharset());
|
||||
BufferedWriter out = Files.newBufferedWriter(
|
||||
file.resolveSibling(fileName + ".java"), Charset.defaultCharset())
|
||||
) {
|
||||
TypeName type = TypeName.fromPath(mOutDir.relativize(file.resolveSibling(fileName)));
|
||||
out.write("package " + type.package_ + "; class " + type.getName(true, true) + " { void a() { int a;");
|
||||
out.newLine();
|
||||
|
||||
String line;
|
||||
final String debugLinePrefix = mDebugLinePrefix;
|
||||
while ((line = in.readLine()) != null) {
|
||||
out.write(debugLinePrefix);
|
||||
out.write(line);
|
||||
out.newLine();
|
||||
}
|
||||
|
||||
out.write("}}");
|
||||
out.newLine();
|
||||
}
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,212 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package brut.androlib.src;
|
||||
|
||||
import brut.androlib.AndrolibException;
|
||||
import brut.util.Duo;
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
public class TypeName {
|
||||
public final String package_;
|
||||
public final String type;
|
||||
public final String innerType;
|
||||
public final int array;
|
||||
|
||||
public TypeName(String type, int array) {
|
||||
this(null, type, null, array);
|
||||
}
|
||||
|
||||
public TypeName(String package_, String type, String innerType, int array) {
|
||||
this.package_ = package_;
|
||||
this.type = type;
|
||||
this.innerType = innerType;
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public String getShortenedName() {
|
||||
return getName("java.lang".equals(package_), isFileOwner());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return getName(false, false);
|
||||
}
|
||||
|
||||
public String getName(boolean excludePackage, boolean separateInner) {
|
||||
String name = (package_ == null || excludePackage ? "" : package_ + '.')
|
||||
+ type
|
||||
+ (innerType != null ? (separateInner ? '$' : '.') + innerType
|
||||
: "");
|
||||
for (int i = 0; i < array; i++) {
|
||||
name += "[]";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getJavaFilePath() {
|
||||
return getFilePath(isFileOwner()) + ".java";
|
||||
}
|
||||
|
||||
public String getSmaliFilePath() {
|
||||
return getFilePath(true) + ".smali";
|
||||
}
|
||||
|
||||
public String getFilePath(boolean separateInner) {
|
||||
return package_.replace('.', File.separatorChar) + File.separatorChar
|
||||
+ type + (separateInner && isInner() ? "$" + innerType : "");
|
||||
}
|
||||
|
||||
public boolean isInner() {
|
||||
return innerType != null;
|
||||
}
|
||||
|
||||
public boolean isArray() {
|
||||
return array != 0;
|
||||
}
|
||||
|
||||
public boolean isFileOwner() {
|
||||
if (mIsFileOwner == null) {
|
||||
mIsFileOwner = true;
|
||||
if (isInner()) {
|
||||
char c = innerType.charAt(0);
|
||||
if (c < '0' || c > '9') {
|
||||
mIsFileOwner = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mIsFileOwner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
public static TypeName fromInternalName(String internal)
|
||||
throws AndrolibException {
|
||||
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
|
||||
if (duo.m2 != internal.length()) {
|
||||
throw new AndrolibException("Invalid internal name: " + internal);
|
||||
}
|
||||
return duo.m1;
|
||||
}
|
||||
|
||||
public static List<TypeName> listFromInternalName(String internal)
|
||||
throws AndrolibException {
|
||||
List<TypeName> types = new ArrayList<TypeName>();
|
||||
while (!internal.isEmpty()) {
|
||||
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
|
||||
types.add(duo.m1);
|
||||
internal = internal.substring(duo.m2);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
public static TypeName fromPath(Path path) {
|
||||
List<String> parts = new ArrayList<>(path.getNameCount());
|
||||
for (Path p : path) {
|
||||
parts.add(p.toString());
|
||||
}
|
||||
return fromNameParts(parts, 0);
|
||||
}
|
||||
|
||||
public static TypeName fromNameParts(List<String> parts, int array) {
|
||||
String type = parts.get(parts.size() - 1);
|
||||
parts = parts.subList(0, parts.size() - 1);
|
||||
String innerType = null;
|
||||
|
||||
int pos = type.indexOf('$');
|
||||
if (pos != -1) {
|
||||
innerType = type.substring(pos + 1);
|
||||
type = type.substring(0, pos);
|
||||
}
|
||||
return new TypeName(Joiner.on('.').join(parts), type, innerType, array);
|
||||
}
|
||||
|
||||
public static Duo<TypeName, Integer> fetchFromInternalName(String internal)
|
||||
throws AndrolibException {
|
||||
String origInternal = internal;
|
||||
int array = 0;
|
||||
|
||||
boolean isArray = false;
|
||||
do {
|
||||
if (internal.isEmpty()) {
|
||||
throw new AndrolibException("Invalid internal name: "
|
||||
+ origInternal);
|
||||
}
|
||||
isArray = internal.charAt(0) == '[';
|
||||
if (isArray) {
|
||||
array++;
|
||||
internal = internal.substring(1);
|
||||
}
|
||||
} while (isArray);
|
||||
|
||||
int length = array + 1;
|
||||
String type;
|
||||
switch (internal.charAt(0)) {
|
||||
case 'B':
|
||||
type = "byte";
|
||||
break;
|
||||
case 'C':
|
||||
type = "char";
|
||||
break;
|
||||
case 'D':
|
||||
type = "double";
|
||||
break;
|
||||
case 'F':
|
||||
type = "float";
|
||||
break;
|
||||
case 'I':
|
||||
type = "int";
|
||||
break;
|
||||
case 'J':
|
||||
type = "long";
|
||||
break;
|
||||
case 'S':
|
||||
type = "short";
|
||||
break;
|
||||
case 'Z':
|
||||
type = "boolean";
|
||||
break;
|
||||
case 'V':
|
||||
type = "void";
|
||||
break;
|
||||
case 'L':
|
||||
int pos = internal.indexOf(';');
|
||||
if (pos == -1) {
|
||||
throw new AndrolibException("Invalid internal name: "
|
||||
+ origInternal);
|
||||
}
|
||||
return new Duo<>(fromNameParts(Arrays.asList(internal.substring(1, pos).split("/")), array), length + pos);
|
||||
default:
|
||||
throw new AndrolibException("Invalid internal name: "
|
||||
+ origInternal);
|
||||
}
|
||||
|
||||
return new Duo<>(new TypeName(null, type, null, array), length);
|
||||
}
|
||||
|
||||
private Boolean mIsFileOwner;
|
||||
}
|
Reference in New Issue
Block a user