refactor: fix zip security in dex plugin, remove smali deps from jadx-core
This commit is contained in:
@@ -10,12 +10,6 @@ dependencies {
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
|
||||
implementation 'org.smali:baksmali:2.4.0'
|
||||
implementation('org.smali:smali:2.4.0') {
|
||||
exclude group: 'com.google.guava'
|
||||
}
|
||||
implementation 'com.google.guava:guava:29.0-jre'
|
||||
|
||||
testImplementation 'org.apache.commons:commons-lang3:3.10'
|
||||
|
||||
testRuntimeOnly(project(':jadx-plugins:jadx-dex-input'))
|
||||
|
||||
@@ -2,7 +2,7 @@ package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
import jadx.api.plugins.utils.ZipSecurity;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
|
||||
public class ResourceFile {
|
||||
|
||||
@@ -17,12 +17,12 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ResourceFile.ZipRef;
|
||||
import jadx.api.impl.SimpleCodeInfo;
|
||||
import jadx.api.plugins.utils.ZipSecurity;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.android.Res9patchStreamDecoder;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.ResTableParser;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.plugins.utils.ZipSecurity;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
@@ -37,7 +38,6 @@ import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
|
||||
import static jadx.core.utils.Utils.notEmpty;
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public class JsonCodeGen {
|
||||
|
||||
JsonClass jsonCls = new JsonClass();
|
||||
jsonCls.setPkg(classInfo.getAliasPkg());
|
||||
jsonCls.setDex(cls.getInputPath().toString());
|
||||
jsonCls.setDex(cls.getInputFileName());
|
||||
jsonCls.setName(classInfo.getFullName());
|
||||
if (classInfo.hasAlias()) {
|
||||
jsonCls.setAlias(classInfo.getAliasFullName());
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -39,7 +37,6 @@ import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.nodes.parser.SignatureParser;
|
||||
import jadx.core.dex.visitors.ProcessAnonymous;
|
||||
import jadx.core.utils.SmaliUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
@@ -50,10 +47,7 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);
|
||||
|
||||
private final RootNode root;
|
||||
private final IClassData cls;
|
||||
private final int clsDefOffset;
|
||||
@Nullable
|
||||
private final Path inputPath;
|
||||
private final IClassData clsData;
|
||||
|
||||
private final ClassInfo clsInfo;
|
||||
private AccessInfo accessFlags;
|
||||
@@ -86,11 +80,9 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
|
||||
public ClassNode(RootNode root, IClassData cls) {
|
||||
this.root = root;
|
||||
this.inputPath = cls.getInputPath();
|
||||
this.clsDefOffset = cls.getClassDefOffset();
|
||||
this.clsInfo = ClassInfo.fromType(root, ArgType.object(cls.getType()));
|
||||
initialLoad(cls);
|
||||
this.cls = cls.copy(); // TODO: need only for rename feature
|
||||
this.clsData = cls.copy();
|
||||
}
|
||||
|
||||
private void initialLoad(IClassData cls) {
|
||||
@@ -152,9 +144,7 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
// Create empty class
|
||||
private ClassNode(RootNode root, String name, int accessFlags) {
|
||||
this.root = root;
|
||||
this.cls = null;
|
||||
this.inputPath = null;
|
||||
this.clsDefOffset = 0;
|
||||
this.clsData = null;
|
||||
this.clsInfo = ClassInfo.fromName(root, name);
|
||||
this.interfaces = new ArrayList<>();
|
||||
this.methods = new ArrayList<>();
|
||||
@@ -299,13 +289,13 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
}
|
||||
|
||||
public void deepUnload() {
|
||||
if (cls == null) {
|
||||
if (clsData == null) {
|
||||
// manually added class
|
||||
return;
|
||||
}
|
||||
clearAttributes();
|
||||
root().getConstValues().removeForClass(this);
|
||||
initialLoad(cls);
|
||||
initialLoad(clsData);
|
||||
ProcessAnonymous.runForClass(this);
|
||||
|
||||
for (ClassNode innerClass : innerClasses) {
|
||||
@@ -610,29 +600,28 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
|
||||
public String getSmali() {
|
||||
if (smali == null) {
|
||||
StringWriter stringWriter = new StringWriter(4096);
|
||||
getSmali(this, stringWriter);
|
||||
stringWriter.append(System.lineSeparator());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
getSmali(sb);
|
||||
sb.append(System.lineSeparator());
|
||||
Set<ClassNode> allInlinedClasses = new LinkedHashSet<>();
|
||||
getInnerAndInlinedClassesRecursive(allInlinedClasses);
|
||||
for (ClassNode innerClass : allInlinedClasses) {
|
||||
getSmali(innerClass, stringWriter);
|
||||
stringWriter.append(System.lineSeparator());
|
||||
innerClass.getSmali(sb);
|
||||
sb.append(System.lineSeparator());
|
||||
}
|
||||
smali = stringWriter.toString();
|
||||
smali = sb.toString();
|
||||
}
|
||||
return smali;
|
||||
}
|
||||
|
||||
protected static boolean getSmali(ClassNode classNode, StringWriter stringWriter) {
|
||||
Path inputPath = classNode.inputPath;
|
||||
if (inputPath == null) {
|
||||
stringWriter.append(String.format("###### Class %s is created by jadx", classNode.getFullName()));
|
||||
return false;
|
||||
protected void getSmali(StringBuilder sb) {
|
||||
if (this.clsData == null) {
|
||||
sb.append(String.format("###### Class %s is created by jadx", getFullName()));
|
||||
return;
|
||||
}
|
||||
stringWriter.append(String.format("###### Class %s (%s)", classNode.getFullName(), classNode.getRawName()));
|
||||
stringWriter.append(System.lineSeparator());
|
||||
return SmaliUtils.getSmaliCode(inputPath, classNode.clsDefOffset, stringWriter);
|
||||
sb.append(String.format("###### Class %s (%s)", getFullName(), getRawName()));
|
||||
sb.append(System.lineSeparator());
|
||||
sb.append(this.clsData.getDisassembledCode());
|
||||
}
|
||||
|
||||
public ProcessState getState() {
|
||||
@@ -668,8 +657,8 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getInputPath() {
|
||||
return inputPath;
|
||||
public String getInputFileName() {
|
||||
return clsData == null ? "synthetic" : clsData.getInputFileName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -88,8 +87,8 @@ public class FieldNode extends LineAttrNode implements ICodeNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getInputPath() {
|
||||
return parentClass.getInputPath();
|
||||
public String getInputFileName() {
|
||||
return parentClass.getInputFileName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface IDexNode {
|
||||
|
||||
String typeName();
|
||||
|
||||
RootNode root();
|
||||
|
||||
Path getInputPath();
|
||||
String getInputFileName();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -598,8 +597,8 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getInputPath() {
|
||||
return parentClass.getInputPath();
|
||||
public String getInputFileName() {
|
||||
return parentClass.getInputFileName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,11 +8,11 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.plugins.utils.ZipSecurity;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
|
||||
public class SaveCode {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SaveCode.class);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -37,12 +36,7 @@ public class ErrorsCounter {
|
||||
}
|
||||
|
||||
public static String formatMsg(IDexNode node, String msg) {
|
||||
return msg + " in " + node.typeName() + ": " + node + ", file: " + getNodeFile(node);
|
||||
}
|
||||
|
||||
private static String getNodeFile(IDexNode node) {
|
||||
Path inputPath = node.getInputPath();
|
||||
return inputPath == null ? "synthetic" : inputPath.toString();
|
||||
return msg + " in " + node.typeName() + ": " + node + ", file: " + node.getInputFileName();
|
||||
}
|
||||
|
||||
private synchronized <N extends IDexNode & IAttributeNode> String addError(N node, String error, @Nullable Throwable e) {
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||
import org.jf.baksmali.BaksmaliOptions;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.smali.Smali;
|
||||
import org.jf.smali.SmaliOptions;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
// TODO: move smali dependencies out from jadx-core
|
||||
public class SmaliUtils {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SmaliUtils.class);
|
||||
|
||||
public static void assembleDex(String outputDexFile, String inputSmali) {
|
||||
try {
|
||||
SmaliOptions options = new SmaliOptions();
|
||||
options.outputDexFile = outputDexFile;
|
||||
Smali.assemble(options, inputSmali);
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Smali assemble error", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getSmaliCode(Path path, int clsDefOffset, StringWriter stringWriter) {
|
||||
if (clsDefOffset == 0) {
|
||||
return false;
|
||||
}
|
||||
try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(path))) {
|
||||
DexBackedDexFile dexFile = DexBackedDexFile.fromInputStream(null, inputStream);
|
||||
DexBackedClassDef dexBackedClassDef = new DexBackedClassDef(dexFile, clsDefOffset, 0);
|
||||
getSmaliCode(dexBackedClassDef, stringWriter);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error generating smali", e);
|
||||
stringWriter.append("Error generating smali code: ");
|
||||
stringWriter.append(e.getMessage());
|
||||
stringWriter.append(System.lineSeparator());
|
||||
stringWriter.append(Utils.getStackTrace(e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void getSmaliCode(DexBackedClassDef classDef, StringWriter stringWriter) throws IOException {
|
||||
ClassDefinition classDefinition = new ClassDefinition(new BaksmaliOptions(), classDef);
|
||||
classDefinition.writeTo(new IndentingWriter(stringWriter));
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,11 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourcesLoader;
|
||||
import jadx.api.plugins.utils.ZipSecurity;
|
||||
import jadx.core.dex.visitors.SaveCode;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
|
||||
public class ResourcesSaver implements Runnable {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ResourcesSaver.class);
|
||||
|
||||
@@ -100,7 +100,7 @@ public abstract class BaseExternalTest extends IntegrationTest {
|
||||
throw new JadxRuntimeException("Class process failed", e);
|
||||
}
|
||||
LOG.info("----------------------------------------------------------------");
|
||||
LOG.info("Print class: {}, {}", classNode.getFullName(), classNode.getInputPath());
|
||||
LOG.info("Print class: {} from: {}", classNode.getFullName(), classNode.getInputFileName());
|
||||
if (mthPattern != null) {
|
||||
printMethods(classNode, mthPattern);
|
||||
} else {
|
||||
|
||||
@@ -132,26 +132,24 @@ public class RenameDialog extends JDialog {
|
||||
return String.format("%s %s = %s", type, id, renameText);
|
||||
}
|
||||
|
||||
private boolean writeDeobfMapFile(Path deobfMapPath, List<String> deobfMap) throws IOException {
|
||||
private void writeDeobfMapFile(Path deobfMapPath, List<String> deobfMap) throws IOException {
|
||||
if (deobfMapPath == null) {
|
||||
LOG.error("updateDeobfMapFile(): deobfMapPath is null!");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
File tmpFile = File.createTempFile("deobf_tmp_", ".txt");
|
||||
FileOutputStream fileOut = new FileOutputStream(tmpFile);
|
||||
for (String entry : deobfMap) {
|
||||
fileOut.write(entry.getBytes());
|
||||
fileOut.write(System.lineSeparator().getBytes());
|
||||
try (FileOutputStream fileOut = new FileOutputStream(tmpFile)) {
|
||||
for (String entry : deobfMap) {
|
||||
fileOut.write(entry.getBytes());
|
||||
fileOut.write(System.lineSeparator().getBytes());
|
||||
}
|
||||
}
|
||||
fileOut.close();
|
||||
File oldMap = File.createTempFile("deobf_bak_", ".txt");
|
||||
Files.copy(deobfMapPath, oldMap.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
LOG.trace("Copying " + tmpFile.toPath() + " to " + deobfMapPath);
|
||||
Files.copy(tmpFile.toPath(), deobfMapPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.delete(oldMap.toPath());
|
||||
Files.delete(tmpFile.toPath());
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -5,5 +5,9 @@ plugins {
|
||||
dependencies {
|
||||
api(project(":jadx-plugins:jadx-plugins-api"))
|
||||
|
||||
testImplementation('org.smali:smali:2.4.0')
|
||||
// TODO: finish own smali printer
|
||||
implementation 'org.smali:baksmali:2.4.0'
|
||||
implementation 'com.google.guava:guava:29.0-jre' // force latest version for smali
|
||||
|
||||
testImplementation 'org.smali:smali:2.4.0' // compile smali files in tests
|
||||
}
|
||||
|
||||
+46
-28
@@ -1,24 +1,24 @@
|
||||
package jadx.plugins.input.dex;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import jadx.api.plugins.utils.ZipSecurity;
|
||||
import jadx.plugins.input.dex.sections.DexConsts;
|
||||
|
||||
public class DexFileLoader {
|
||||
@@ -26,43 +26,57 @@ public class DexFileLoader {
|
||||
|
||||
public static List<DexReader> collectDexFiles(List<Path> pathsList) {
|
||||
return pathsList.stream()
|
||||
.map(path -> loadDexFromPath(path, 0))
|
||||
.map(Path::toFile)
|
||||
.map(DexFileLoader::loadDexFromFile)
|
||||
.filter(list -> !list.isEmpty())
|
||||
.flatMap(Collection::stream)
|
||||
.peek(dr -> LOG.debug("Loading dex: {}", dr))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<DexReader> loadDexFromPath(Path path, int depth) {
|
||||
try (InputStream inputStream = Files.newInputStream(path, StandardOpenOption.READ)) {
|
||||
private static List<DexReader> loadDexFromFile(File file) {
|
||||
try (InputStream inputStream = new FileInputStream(file)) {
|
||||
return checkFileMagic(file, inputStream, file.getAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
LOG.error("File open error: {}", file.getAbsolutePath(), e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<DexReader> checkFileMagic(File file, InputStream inputStream, String inputFileName) throws IOException {
|
||||
try (InputStream in = inputStream.markSupported() ? inputStream : new BufferedInputStream(inputStream)) {
|
||||
byte[] magic = new byte[DexConsts.MAX_MAGIC_SIZE];
|
||||
if (inputStream.read(magic) != magic.length) {
|
||||
in.mark(magic.length);
|
||||
if (in.read(magic) != magic.length) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (isStartWithBytes(magic, DexConsts.DEX_FILE_MAGIC)) {
|
||||
return Collections.singletonList(new DexReader(path));
|
||||
in.reset();
|
||||
DexReader dexReader = new DexReader(inputFileName, readAllBytes(in));
|
||||
return Collections.singletonList(dexReader);
|
||||
}
|
||||
if (depth == 0 && isStartWithBytes(magic, DexConsts.ZIP_FILE_MAGIC)) {
|
||||
return collectDexFromZip(path, depth);
|
||||
if (file != null && isStartWithBytes(magic, DexConsts.ZIP_FILE_MAGIC)) {
|
||||
return collectDexFromZip(file);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("File open error: {}", path, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static List<DexReader> collectDexFromZip(Path path, int depth) throws IOException {
|
||||
private static List<DexReader> collectDexFromZip(File file) {
|
||||
List<DexReader> result = new ArrayList<>();
|
||||
FileSystem zip = FileSystems.newFileSystem(path, (ClassLoader) null);
|
||||
for (Path rootDir : zip.getRootDirectories()) {
|
||||
Files.walkFileTree(rootDir, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
// TODO: add zip security checks
|
||||
result.addAll(loadDexFromPath(file, depth + 1));
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
try (ZipFile zip = new ZipFile(file)) {
|
||||
zip.stream()
|
||||
.filter(entry -> !entry.isDirectory())
|
||||
.filter(ZipSecurity::isValidZipEntry)
|
||||
.forEach(entry -> {
|
||||
try (InputStream in = zip.getInputStream(entry)) {
|
||||
result.addAll(checkFileMagic(null, in, entry.getName()));
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to read zip entry: {}", entry, e);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to open zip file: {}", file.getAbsolutePath());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -79,4 +93,8 @@ public class DexFileLoader {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static byte[] readAllBytes(InputStream in) throws IOException {
|
||||
return ByteStreams.toByteArray(in);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,7 @@ public class DexLoadResult implements ILoadResult {
|
||||
@Nullable
|
||||
private final Closeable closeable;
|
||||
|
||||
public DexLoadResult(List<DexReader> dexReaders) {
|
||||
this(dexReaders, null);
|
||||
}
|
||||
|
||||
public DexLoadResult(List<DexReader> dexReaders, Closeable closeable) {
|
||||
public DexLoadResult(List<DexReader> dexReaders, @Nullable Closeable closeable) {
|
||||
this.dexReaders = dexReaders;
|
||||
this.closeable = closeable;
|
||||
}
|
||||
@@ -38,9 +34,7 @@ public class DexLoadResult implements ILoadResult {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
for (DexReader dexReader : dexReaders) {
|
||||
dexReader.close();
|
||||
}
|
||||
dexReaders.clear();
|
||||
if (closeable != null) {
|
||||
closeable.close();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
package jadx.plugins.input.dex;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jadx.api.plugins.input.data.IClassData;
|
||||
@@ -14,22 +9,18 @@ import jadx.plugins.input.dex.sections.DexHeader;
|
||||
import jadx.plugins.input.dex.sections.SectionReader;
|
||||
import jadx.plugins.input.dex.sections.annotations.AnnotationsParser;
|
||||
|
||||
public class DexReader implements Closeable {
|
||||
public class DexReader {
|
||||
|
||||
private final Path path;
|
||||
private final String inputFileName;
|
||||
private final ByteBuffer buf;
|
||||
private final DexHeader header;
|
||||
|
||||
public DexReader(Path path) throws IOException {
|
||||
this.path = path;
|
||||
this.buf = ByteBuffer.wrap(Files.readAllBytes(path));
|
||||
public DexReader(String inputFileName, byte[] content) {
|
||||
this.inputFileName = inputFileName;
|
||||
this.buf = ByteBuffer.wrap(content);
|
||||
this.header = new DexHeader(new SectionReader(this, 0));
|
||||
}
|
||||
|
||||
public String getDexVersion() {
|
||||
return this.header.getVersion();
|
||||
}
|
||||
|
||||
public void visitClasses(Consumer<IClassData> consumer) {
|
||||
int count = header.getClassDefsSize();
|
||||
if (count == 0) {
|
||||
@@ -53,26 +44,12 @@ public class DexReader implements Closeable {
|
||||
return header;
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public String getFullPath() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
FileSystem fileSystem = path.getFileSystem();
|
||||
if (fileSystem.getClass().getName().contains("Zip")) {
|
||||
sb.append(fileSystem.toString()).append(':');
|
||||
}
|
||||
sb.append(path.toAbsolutePath());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
public String getInputFileName() {
|
||||
return inputFileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getFullPath();
|
||||
return inputFileName;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -1,6 +1,5 @@
|
||||
package jadx.plugins.input.dex.sections;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -15,6 +14,7 @@ import jadx.api.plugins.input.data.IMethodData;
|
||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
||||
import jadx.plugins.input.dex.sections.annotations.AnnotationsParser;
|
||||
import jadx.plugins.input.dex.utils.SmaliUtils;
|
||||
|
||||
public class DexClassData implements IClassData {
|
||||
public static final int SIZE = 8 * 4;
|
||||
@@ -71,8 +71,8 @@ public class DexClassData implements IClassData {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getInputPath() {
|
||||
return in.getDexReader().getPath();
|
||||
public String getInputFileName() {
|
||||
return in.getDexReader().getInputFileName();
|
||||
}
|
||||
|
||||
public int getAnnotationsOff() {
|
||||
@@ -186,11 +186,16 @@ public class DexClassData implements IClassData {
|
||||
return annotationsParser.readClassAnnotations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getClassDefOffset() {
|
||||
return in.pos(0).getAbsPos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisassembledCode() {
|
||||
byte[] dexBuf = in.getDexReader().getBuf().array();
|
||||
return SmaliUtils.getSmaliCode(dexBuf, getClassDefOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getType();
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package jadx.plugins.input.dex.utils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||
import org.jf.baksmali.BaksmaliOptions;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.util.IndentingWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SmaliUtils {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SmaliUtils.class);
|
||||
|
||||
public static String getSmaliCode(byte[] dexBuf, int clsDefOffset) {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
try {
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(null, dexBuf);
|
||||
DexBackedClassDef dexBackedClassDef = new DexBackedClassDef(dexFile, clsDefOffset, 0);
|
||||
ClassDefinition classDefinition = new ClassDefinition(new BaksmaliOptions(), dexBackedClassDef);
|
||||
classDefinition.writeTo(new IndentingWriter(stringWriter));
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error generating smali", e);
|
||||
stringWriter.append("Error generating smali code: ");
|
||||
stringWriter.append(e.getMessage());
|
||||
stringWriter.append(System.lineSeparator());
|
||||
e.printStackTrace(new PrintWriter(stringWriter, true));
|
||||
}
|
||||
return stringWriter.toString();
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -13,7 +13,7 @@ import jadx.api.plugins.input.data.AccessFlags;
|
||||
import jadx.api.plugins.input.data.AccessFlagsScope;
|
||||
import jadx.api.plugins.input.data.ICodeReader;
|
||||
import jadx.api.plugins.input.data.ILoadResult;
|
||||
import jadx.plugins.input.dex.utils.SmaliUtils;
|
||||
import jadx.plugins.input.dex.utils.SmaliTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -31,7 +31,7 @@ class DexInputPluginTest {
|
||||
|
||||
@Test
|
||||
public void loadTestSmali() throws Exception {
|
||||
processFile(SmaliUtils.compileSmaliFromResource("samples/test.smali"));
|
||||
processFile(SmaliTestUtils.compileSmaliFromResource("samples/test.smali"));
|
||||
}
|
||||
|
||||
private static void processFile(Path sample) throws IOException {
|
||||
@@ -65,6 +65,9 @@ class DexInputPluginTest {
|
||||
System.out.println(mth.disassembleMethod());
|
||||
System.out.println("---");
|
||||
});
|
||||
System.out.println("----");
|
||||
System.out.println(cls.getDisassembledCode());
|
||||
System.out.println("----");
|
||||
});
|
||||
assertThat(count.get()).isGreaterThan(0);
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import java.util.stream.Collectors;
|
||||
import org.jf.smali.Smali;
|
||||
import org.jf.smali.SmaliOptions;
|
||||
|
||||
public class SmaliUtils {
|
||||
public class SmaliTestUtils {
|
||||
|
||||
public static Path compileSmaliFromResource(String res) {
|
||||
try {
|
||||
+2
-4
@@ -1,6 +1,5 @@
|
||||
package jadx.api.plugins.input.data;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -23,7 +22,7 @@ public interface IClassData {
|
||||
|
||||
String getSourceFile();
|
||||
|
||||
Path getInputPath();
|
||||
String getInputFileName();
|
||||
|
||||
void visitFieldsAndMethods(Consumer<IFieldData> fieldsConsumer, Consumer<IMethodData> mthConsumer);
|
||||
|
||||
@@ -31,6 +30,5 @@ public interface IClassData {
|
||||
|
||||
List<IAnnotation> getAnnotations();
|
||||
|
||||
// TODO: make api methods to get dissembled code
|
||||
int getClassDefOffset();
|
||||
String getDisassembledCode();
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package jadx.core.utils.files;
|
||||
package jadx.api.plugins.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
Reference in New Issue
Block a user