Merge branch 'wip-2.0' of git://github.com/brutall/brut.apktool into brutall-wip-2.0

This commit is contained in:
Connor Tumbleson 2013-05-02 12:56:46 -05:00
commit ae5e292b1f
8 changed files with 141 additions and 68 deletions

1
.gitignore vendored
View File

@ -26,3 +26,4 @@ build/
bin/ bin/
*.iml *.iml
.idea/* .idea/*
/out

View File

@ -44,6 +44,8 @@ import static org.jf.dexlib.ClassDataItem.EncodedField;
import static org.jf.dexlib.ClassDataItem.EncodedMethod; import static org.jf.dexlib.ClassDataItem.EncodedMethod;
public class ClassPath { public class ClassPath {
public static boolean dontLoadClassPath = false;
private static ClassPath theClassPath = null; private static ClassPath theClassPath = null;
/** /**
@ -261,6 +263,10 @@ public class ClassPath {
@Nonnull @Nonnull
public static ClassDef getClassDef(String classType, boolean createUnresolvedClassDef) { public static ClassDef getClassDef(String classType, boolean createUnresolvedClassDef) {
if (dontLoadClassPath) {
return null;
}
ClassDef classDef = theClassPath.classDefs.get(classType); ClassDef classDef = theClassPath.classDefs.get(classType);
if (classDef == null) { if (classDef == null) {
//if it's an array class, try to create it //if it's an array class, try to create it

View File

@ -2434,7 +2434,7 @@ public class MethodAnalyzer {
RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
assert arrayRegisterType != null; assert arrayRegisterType != null;
if (arrayRegisterType.category != RegisterType.Category.Null) { if (! ClassPath.dontLoadClassPath && arrayRegisterType.category != RegisterType.Category.Null) {
assert arrayRegisterType.type != null; assert arrayRegisterType.type != null;
if (arrayRegisterType.type.getClassType().charAt(0) != '[') { if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
@ -2503,7 +2503,7 @@ public class MethodAnalyzer {
RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
assert arrayRegisterType != null; assert arrayRegisterType != null;
if (arrayRegisterType.category != RegisterType.Category.Null) { if (! ClassPath.dontLoadClassPath && arrayRegisterType.category != RegisterType.Category.Null) {
assert arrayRegisterType.type != null; assert arrayRegisterType.type != null;
if (arrayRegisterType.type.getClassType().charAt(0) != '[') { if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",

View File

@ -182,10 +182,9 @@ public class Main {
} }
private static void cmdBuild(CommandLine cli) throws BrutException { private static void cmdBuild(CommandLine cli) throws BrutException {
int paraCount = cli.getArgList().size(); String[] args = cli.getArgs();
String apkName = (String) cli.getArgList().get(paraCount - 1); String appDirName = args.length < 2 ? "." : args[1];
String mAaptPath = ""; String mAaptPath = "";
String appDirName = ".";
File outFile = null; File outFile = null;
Androlib instance = new Androlib(); Androlib instance = new Androlib();
@ -223,10 +222,6 @@ public class Main {
outFile = null; outFile = null;
} }
if (apkName != null) {
appDirName = apkName;
}
// try and build apk // try and build apk
instance.build(new File(appDirName), outFile, flags,mAaptPath); instance.build(new File(appDirName), outFile, flags,mAaptPath);
} }

View File

@ -27,6 +27,10 @@ import org.jf.dexlib.Code.Analysis.RegisterType;
*/ */
public class DebugInjector { public class DebugInjector {
private boolean areParamsInjected;
private int currParam;
private int lastParam;
public static void inject(ListIterator<String> it, StringBuilder out) public static void inject(ListIterator<String> it, StringBuilder out)
throws AndrolibException { throws AndrolibException {
new DebugInjector(it, out).inject(); new DebugInjector(it, out).inject();
@ -44,7 +48,7 @@ public class DebugInjector {
nextAndAppend(); nextAndAppend();
return; return;
} }
injectParameters(definition); parseParamsNumber(definition);
boolean end = false; boolean end = false;
while (!end) { while (!end) {
@ -52,24 +56,28 @@ public class DebugInjector {
} }
} }
private void injectParameters(String definition) throws AndrolibException { private void parseParamsNumber(String definition) throws AndrolibException {
int pos = definition.indexOf('('); int pos = definition.indexOf('(');
if (pos == -1) { if (pos == -1) {
throw new AndrolibException(); throw new AndrolibException();
} }
int pos2 = definition.indexOf(')', pos); int pos2 = definition.indexOf(')', pos);
if (pos2 == -1) { if (pos2 == -1) {
throw new AndrolibException(); throw new AndrolibException();
} }
String params = definition.substring(pos + 1, pos2); String params = definition.substring(pos + 1, pos2);
int i = definition.contains(" static ") ? 0 : 1; currParam = definition.contains(" static ") ? 0 : 1;
int argc = TypeName.listFromInternalName(params).size() + i; lastParam = TypeName.listFromInternalName(params).size() + currParam - 1;
while (i < argc) { }
mOut.append(".parameter \"p").append(i).append("\"\n");
i++; private void injectRemainingParams() {
} areParamsInjected = true;
} while(currParam <= lastParam) {
mOut.append(".parameter \"p").append(currParam).append("\"\n");
currParam++;
}
}
private boolean step() { private boolean step() {
String line = next(); String line = next();
@ -86,6 +94,9 @@ public class DebugInjector {
case '.': case '.':
return processDirective(line); return processDirective(line);
default: default:
if (! areParamsInjected) {
injectRemainingParams();
}
return processInstruction(line); return processInstruction(line);
} }
} }
@ -158,11 +169,19 @@ public class DebugInjector {
private boolean processDirective(String line) { private boolean processDirective(String line) {
String line2 = line.substring(1); String line2 = line.substring(1);
if (line2.startsWith("line ") || line2.equals("prologue") if (line2.startsWith("line ") || line2.startsWith("local ") || line2.startsWith("end local ")) {
|| line2.startsWith("parameter") || line2.startsWith("local ") return false;
|| line2.startsWith("end local ")) { }
return false; if (line2.equals("prologue")) {
} if (! areParamsInjected) {
injectRemainingParams();
}
return false;
}
if (line2.equals("parameter")) {
mOut.append(".parameter \"p").append(currParam++).append("\"\n");
return false;
}
append(line); append(line);
if (line2.equals("end method")) { if (line2.equals("end method")) {
@ -193,7 +212,7 @@ public class DebugInjector {
} }
private String next() { private String next() {
return mIt.next().trim(); return mIt.next().split("//", 2)[1].trim();
} }
private String nextAndAppend() { private String nextAndAppend() {

View File

@ -74,19 +74,18 @@ public class SmaliBuilder {
StringBuilder out = new StringBuilder(); StringBuilder out = new StringBuilder();
List<String> lines = IOUtils.readLines(inStream); List<String> lines = IOUtils.readLines(inStream);
if (!mFlags.containsKey("debug")) { if (!mFlags.get("debug")) {
final String[] linesArray = lines.toArray(new String[0]); final String[] linesArray = lines.toArray(new String[0]);
for (int i = 2; i < linesArray.length - 2; i++) { for (int i = 1; i < linesArray.length - 1; i++) {
out.append(linesArray[i]).append('\n'); out.append(linesArray[i].split("//", 2)[1]).append('\n');
} }
} else { } else {
lines.remove(lines.size() - 1); lines.remove(lines.size() - 1);
lines.remove(lines.size() - 1); ListIterator<String> it = lines.listIterator(1);
ListIterator<String> it = lines.listIterator(2);
out.append(".source \"").append(inFile.getName()).append("\"\n"); out.append(".source \"").append(inFile.getName()).append("\"\n");
while (it.hasNext()) { while (it.hasNext()) {
String line = it.next().trim(); String line = it.next().split("//", 2)[1].trim();
if (line.isEmpty() || line.charAt(0) == '#' if (line.isEmpty() || line.charAt(0) == '#'
|| line.startsWith(".source")) { || line.startsWith(".source")) {
continue; continue;

View File

@ -17,10 +17,20 @@
package brut.androlib.src; package brut.androlib.src;
import brut.androlib.AndrolibException; import brut.androlib.AndrolibException;
import org.jf.baksmali.baksmali;
import org.jf.dexlib.Code.Analysis.ClassPath;
import org.jf.dexlib.DexFile;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import org.jf.baksmali.baksmali; import java.nio.charset.Charset;
import org.jf.dexlib.DexFile; 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> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
@ -35,24 +45,62 @@ public class SmaliDecoder {
private SmaliDecoder(File apkFile, File outDir, boolean debug, private SmaliDecoder(File apkFile, File outDir, boolean debug,
boolean bakdeb) { boolean bakdeb) {
mApkFile = apkFile; mApkFile = apkFile;
mOutDir = outDir; mOutDir = outDir.toPath();
mDebug = debug; mDebug = debug;
mBakDeb = bakdeb; mBakDeb = bakdeb;
} }
private void decode() throws AndrolibException { private void decode() throws AndrolibException {
try { try {
ClassPath.dontLoadClassPath = mDebug;
baksmali.disassembleDexFile(mApkFile.getAbsolutePath(), baksmali.disassembleDexFile(mApkFile.getAbsolutePath(),
new DexFile(mApkFile), false, mOutDir.getAbsolutePath(), new DexFile(mApkFile), false, mOutDir.toAbsolutePath().toString(),
null, null, null, false, true, true, mBakDeb, false, false, null, null, null, false, true, true, mBakDeb, false, false,
0, false, false, null, false); mDebug ? org.jf.baksmali.main.DIFFPRE : 0, false, false, null, false);
if (mDebug) {
Files.walkFileTree(mOutDir, new SmaliFileVisitor());
}
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
private final File mApkFile; private final File mApkFile;
private final File mOutDir; private final Path mOutDir;
private final boolean mDebug; private final boolean mDebug;
private final boolean mBakDeb; private final boolean mBakDeb;
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;
while ((line = in.readLine()) != null) {
out.write(";// ");
out.write(line);
out.newLine();
}
out.write("}}");
out.newLine();
}
Files.delete(file);
return FileVisitResult.CONTINUE;
}
}
} }

View File

@ -18,8 +18,12 @@ package brut.androlib.src;
import brut.androlib.AndrolibException; import brut.androlib.AndrolibException;
import brut.util.Duo; import brut.util.Duo;
import com.google.common.base.Joiner;
import java.io.File; import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@ -120,6 +124,27 @@ public class TypeName {
return types; 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) public static Duo<TypeName, Integer> fetchFromInternalName(String internal)
throws AndrolibException { throws AndrolibException {
String origInternal = internal; String origInternal = internal;
@ -139,9 +164,7 @@ public class TypeName {
} while (isArray); } while (isArray);
int length = array + 1; int length = array + 1;
String package_ = null; String type;
String type = null;
String innerType = null;
switch (internal.charAt(0)) { switch (internal.charAt(0)) {
case 'B': case 'B':
type = "byte"; type = "byte";
@ -176,31 +199,13 @@ public class TypeName {
throw new AndrolibException("Invalid internal name: " throw new AndrolibException("Invalid internal name: "
+ origInternal); + origInternal);
} }
length += pos; return new Duo<>(fromNameParts(Arrays.asList(internal.substring(1, pos).split("/")), array), length + pos);
internal = internal.substring(1, pos);
pos = internal.lastIndexOf('/');
if (pos == -1) {
package_ = "";
type = internal;
} else {
package_ = internal.substring(0, pos).replace('/', '.');
type = internal.substring(pos + 1);
}
pos = type.indexOf('$');
if (pos != -1) {
innerType = type.substring(pos + 1);
type = type.substring(0, pos);
}
break;
default: default:
throw new AndrolibException("Invalid internal name: " throw new AndrolibException("Invalid internal name: "
+ origInternal); + origInternal);
} }
return new Duo<TypeName, Integer>(new TypeName(package_, type, return new Duo<>(new TypeName(null, type, null, array), length);
innerType, array), length);
} }
private Boolean mIsFileOwner; private Boolean mIsFileOwner;