提交 15602848 编写于 作者: S Skylot

refactor: fix zip security in dex plugin, remove smali deps from jadx-core

上级 558a8673
......@@ -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());
......
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
......
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
......
package jadx.core.dex.nodes;
import java.nio.file.Path;
public interface IDexNode {
String typeName();
RootNode root();
Path getInputPath();
String getInputFileName();
}
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);
......
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) {
......
......@@ -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
}
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();
}
......
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;
}
}
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();
......
package jadx.core.utils;
package jadx.plugins.input.dex.utils;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
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) {
public static String getSmaliCode(byte[] dexBuf, int clsDefOffset) {
StringWriter stringWriter = new StringWriter();
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);
DexBackedDexFile dexFile = new DexBackedDexFile(null, dexBuf);
DexBackedClassDef dexBackedClassDef = new DexBackedClassDef(dexFile, clsDefOffset, 0);
getSmaliCode(dexBackedClassDef, stringWriter);
return true;
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());
stringWriter.append(Utils.getStackTrace(e));
return false;
e.printStackTrace(new PrintWriter(stringWriter, true));
}
}
private static void getSmaliCode(DexBackedClassDef classDef, StringWriter stringWriter) throws IOException {
ClassDefinition classDefinition = new ClassDefinition(new BaksmaliOptions(), classDef);
classDefinition.writeTo(new IndentingWriter(stringWriter));
return stringWriter.toString();
}
}
......@@ -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);
}
......
......@@ -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 {
......
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();
}
package jadx.core.utils.files;
package jadx.api.plugins.utils;
import java.io.File;
import java.io.IOException;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册