提交 cd7e5bf0 编写于 作者: S Skylot

Merge branch 'master' into rename

name: "Validate Gradle Wrapper"
on: [push]
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: gradle/wrapper-validation-action@v1
...@@ -7,6 +7,7 @@ import jadx.api.JadxArgs; ...@@ -7,6 +7,7 @@ import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler; import jadx.api.JadxDecompiler;
import jadx.api.impl.NoOpCodeCache; import jadx.api.impl.NoOpCodeCache;
import jadx.core.utils.exceptions.JadxArgsValidateException; import jadx.core.utils.exceptions.JadxArgsValidateException;
import jadx.core.utils.files.FileUtils;
public class JadxCLI { public class JadxCLI {
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class); private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
...@@ -22,6 +23,7 @@ public class JadxCLI { ...@@ -22,6 +23,7 @@ public class JadxCLI {
LOG.error("jadx error: {}", e.getMessage(), e); LOG.error("jadx error: {}", e.getMessage(), e);
result = 1; result = 1;
} finally { } finally {
FileUtils.deleteTempRootDir();
System.exit(result); System.exit(result);
} }
} }
......
...@@ -52,6 +52,8 @@ public class ClassGen { ...@@ -52,6 +52,8 @@ public class ClassGen {
private final Set<ClassInfo> imports = new HashSet<>(); private final Set<ClassInfo> imports = new HashSet<>();
private int clsDeclLine; private int clsDeclLine;
private boolean bodyGenStarted;
public ClassGen(ClassNode cls, JadxArgs jadxArgs) { public ClassGen(ClassNode cls, JadxArgs jadxArgs) {
this(cls, null, jadxArgs.isUseImports(), jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode()); this(cls, null, jadxArgs.isUseImports(), jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode());
} }
...@@ -222,6 +224,7 @@ public class ClassGen { ...@@ -222,6 +224,7 @@ public class ClassGen {
public void addClassBody(CodeWriter clsCode) throws CodegenException { public void addClassBody(CodeWriter clsCode) throws CodegenException {
clsCode.add('{'); clsCode.add('{');
setBodyGenStarted(true);
clsDeclLine = clsCode.getLine(); clsDeclLine = clsCode.getLine();
clsCode.incIndent(); clsCode.incIndent();
addFields(clsCode); addFields(clsCode);
...@@ -656,4 +659,12 @@ public class ClassGen { ...@@ -656,4 +659,12 @@ public class ClassGen {
public boolean isFallbackMode() { public boolean isFallbackMode() {
return fallback; return fallback;
} }
public boolean isBodyGenStarted() {
return bodyGenStarted;
}
public void setBodyGenStarted(boolean bodyGenStarted) {
this.bodyGenStarted = bodyGenStarted;
}
} }
...@@ -183,7 +183,7 @@ public class InsnGen { ...@@ -183,7 +183,7 @@ public class InsnGen {
ClassInfo declClass = field.getDeclClass(); ClassInfo declClass = field.getDeclClass();
// TODO // TODO
boolean fieldFromThisClass = clsGen.getClassNode().getClassInfo().equals(declClass); boolean fieldFromThisClass = clsGen.getClassNode().getClassInfo().equals(declClass);
if (!fieldFromThisClass) { if (!fieldFromThisClass || !clsGen.isBodyGenStarted()) {
// Android specific resources class handler // Android specific resources class handler
if (!handleAppResField(code, clsGen, declClass)) { if (!handleAppResField(code, clsGen, declClass)) {
clsGen.useClass(code, declClass); clsGen.useClass(code, declClass);
......
...@@ -28,6 +28,7 @@ import jadx.core.dex.nodes.InsnNode; ...@@ -28,6 +28,7 @@ import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.parser.FieldInitAttr; import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.dex.regions.Region; import jadx.core.dex.regions.Region;
import jadx.core.dex.regions.SwitchRegion; import jadx.core.dex.regions.SwitchRegion;
import jadx.core.dex.regions.SwitchRegion.CaseInfo;
import jadx.core.dex.regions.SynchronizedRegion; import jadx.core.dex.regions.SynchronizedRegion;
import jadx.core.dex.regions.TryCatchRegion; import jadx.core.dex.regions.TryCatchRegion;
import jadx.core.dex.regions.conditions.IfCondition; import jadx.core.dex.regions.conditions.IfCondition;
...@@ -270,10 +271,9 @@ public class RegionGen extends InsnGen { ...@@ -270,10 +271,9 @@ public class RegionGen extends InsnGen {
code.add(") {"); code.add(") {");
code.incIndent(); code.incIndent();
int size = sw.getKeys().size(); for (CaseInfo caseInfo : sw.getCases()) {
for (int i = 0; i < size; i++) { List<Object> keys = caseInfo.getKeys();
List<Object> keys = sw.getKeys().get(i); IContainer c = caseInfo.getContainer();
IContainer c = sw.getCases().get(i);
for (Object k : keys) { for (Object k : keys) {
code.startLine("case "); code.startLine("case ");
if (k instanceof FieldNode) { if (k instanceof FieldNode) {
......
...@@ -118,6 +118,7 @@ public abstract class AttrNode implements IAttributeNode { ...@@ -118,6 +118,7 @@ public abstract class AttrNode implements IAttributeNode {
return storage.toString(); return storage.toString();
} }
@Override
public boolean isAttrStorageEmpty() { public boolean isAttrStorageEmpty() {
return storage.isEmpty(); return storage.isEmpty();
} }
......
...@@ -35,4 +35,6 @@ public interface IAttributeNode { ...@@ -35,4 +35,6 @@ public interface IAttributeNode {
List<String> getAttributesStringsList(); List<String> getAttributesStringsList();
String getAttributesString(); String getAttributesString();
boolean isAttrStorageEmpty();
} }
...@@ -110,12 +110,7 @@ public class ConstStorage { ...@@ -110,12 +110,7 @@ public class ConstStorage {
} }
private ValueStorage getClsValues(ClassNode cls) { private ValueStorage getClsValues(ClassNode cls) {
ValueStorage classValues = classes.get(cls); return classes.computeIfAbsent(cls, c -> new ValueStorage());
if (classValues == null) {
classValues = new ValueStorage();
classes.put(cls, classValues);
}
return classValues;
} }
@Nullable @Nullable
......
...@@ -3,6 +3,7 @@ package jadx.core.dex.instructions; ...@@ -3,6 +3,7 @@ package jadx.core.dex.instructions;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
...@@ -110,15 +111,13 @@ public class SwitchNode extends TargetInsnNode { ...@@ -110,15 +111,13 @@ public class SwitchNode extends TargetInsnNode {
@Override @Override
public String toString() { public String toString() {
StringBuilder targ = new StringBuilder(); StringBuilder sb = new StringBuilder();
targ.append('['); sb.append(super.toString());
for (int i = 0; i < targets.length; i++) { for (int i = 0; i < targets.length; i++) {
targ.append(InsnUtils.formatOffset(targets[i])); sb.append(" case ").append(keys[i])
if (i < targets.length - 1) { .append(": goto ").append(InsnUtils.formatOffset(targets[i]));
targ.append(", "); sb.append(CodeWriter.NL);
}
} }
targ.append(']'); return sb.toString();
return super.toString() + " k:" + Arrays.toString(keys) + " t:" + targ;
} }
} }
package jadx.core.dex.nodes; package jadx.core.dex.nodes;
import java.io.StringWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
...@@ -555,11 +556,24 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { ...@@ -555,11 +556,24 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
public String getSmali() { public String getSmali() {
if (smali == null) { if (smali == null) {
smali = SmaliUtils.getSmaliCode(dex, clsDefOffset); StringWriter stringWriter = new StringWriter(4096);
getSmali(this, stringWriter);
stringWriter.append(System.lineSeparator());
for (ClassNode innerClass : innerClasses) {
getSmali(innerClass, stringWriter);
stringWriter.append(System.lineSeparator());
}
smali = stringWriter.toString();
} }
return smali; return smali;
} }
protected static boolean getSmali(ClassNode classNode, StringWriter stringWriter) {
stringWriter.append(String.format("###### Class %s (%s)", classNode.getFullName(), classNode.getRawName()));
stringWriter.append(System.lineSeparator());
return SmaliUtils.getSmaliCode(classNode.dex, classNode.clsDefOffset, stringWriter);
}
public ProcessState getState() { public ProcessState getState() {
return state; return state;
} }
......
...@@ -4,33 +4,50 @@ import java.util.ArrayList; ...@@ -4,33 +4,50 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IBranchRegion; import jadx.core.dex.nodes.IBranchRegion;
import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IContainer;
import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.IRegion;
import jadx.core.utils.Utils;
public final class SwitchRegion extends AbstractRegion implements IBranchRegion { public final class SwitchRegion extends AbstractRegion implements IBranchRegion {
private final BlockNode header; private final BlockNode header;
private final List<List<Object>> keys; private final List<CaseInfo> cases;
private final List<IContainer> cases;
private IContainer defCase; private IContainer defCase;
public SwitchRegion(IRegion parent, BlockNode header) { public SwitchRegion(IRegion parent, BlockNode header) {
super(parent); super(parent);
this.header = header; this.header = header;
this.keys = new ArrayList<>();
this.cases = new ArrayList<>(); this.cases = new ArrayList<>();
} }
public static final class CaseInfo {
private final List<Object> keys;
private final IContainer container;
public CaseInfo(List<Object> keys, IContainer container) {
this.keys = keys;
this.container = container;
}
public List<Object> getKeys() {
return keys;
}
public IContainer getContainer() {
return container;
}
}
public BlockNode getHeader() { public BlockNode getHeader() {
return header; return header;
} }
public void addCase(List<Object> keysList, IContainer c) { public void addCase(List<Object> keysList, IContainer c) {
keys.add(keysList); cases.add(new CaseInfo(keysList, c));
cases.add(c);
} }
public void setDefaultCase(IContainer block) { public void setDefaultCase(IContainer block) {
...@@ -41,19 +58,19 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion ...@@ -41,19 +58,19 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
return defCase; return defCase;
} }
public List<List<Object>> getKeys() { public List<CaseInfo> getCases() {
return keys; return cases;
} }
public List<IContainer> getCases() { public List<IContainer> getCaseContainers() {
return cases; return Utils.collectionMap(cases, caseInfo -> caseInfo.container);
} }
@Override @Override
public List<IContainer> getSubBlocks() { public List<IContainer> getSubBlocks() {
List<IContainer> all = new ArrayList<>(cases.size() + 2); List<IContainer> all = new ArrayList<>(cases.size() + 2);
all.add(header); all.add(header);
all.addAll(cases); all.addAll(getCaseContainers());
if (defCase != null) { if (defCase != null) {
all.add(defCase); all.add(defCase);
} }
...@@ -63,7 +80,7 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion ...@@ -63,7 +80,7 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
@Override @Override
public List<IContainer> getBranches() { public List<IContainer> getBranches() {
List<IContainer> branches = new ArrayList<>(cases.size() + 1); List<IContainer> branches = new ArrayList<>(cases.size() + 1);
branches.addAll(cases); branches.addAll(getCaseContainers());
if (defCase != null) { if (defCase != null) {
branches.add(defCase); branches.add(defCase);
} }
...@@ -77,6 +94,16 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion ...@@ -77,6 +94,16 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion
@Override @Override
public String toString() { public String toString() {
return "Switch: " + cases.size() + ", default: " + defCase; StringBuilder sb = new StringBuilder();
sb.append("Switch: ").append(cases.size());
for (CaseInfo caseInfo : cases) {
sb.append(CodeWriter.NL).append(" case ")
.append(Utils.listToString(caseInfo.getKeys()))
.append(" -> ").append(caseInfo.getContainer());
}
if (defCase != null) {
sb.append(CodeWriter.NL).append(" default -> ").append(defCase);
}
return sb.toString();
} }
} }
...@@ -11,6 +11,9 @@ import org.slf4j.LoggerFactory; ...@@ -11,6 +11,9 @@ import org.slf4j.LoggerFactory;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode;
import jadx.core.dex.attributes.annotations.Annotation;
import jadx.core.dex.attributes.annotations.AnnotationsList;
import jadx.core.dex.attributes.nodes.FieldReplaceAttr; import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.MethodInfo; import jadx.core.dex.info.MethodInfo;
...@@ -46,6 +49,7 @@ import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; ...@@ -46,6 +49,7 @@ import jadx.core.dex.visitors.shrink.CodeShrinkVisitor;
import jadx.core.utils.ErrorsCounter; import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.InsnRemover; import jadx.core.utils.InsnRemover;
import jadx.core.utils.InsnUtils; import jadx.core.utils.InsnUtils;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
import static jadx.core.utils.BlockUtils.replaceInsn; import static jadx.core.utils.BlockUtils.replaceInsn;
...@@ -68,6 +72,12 @@ public class ModVisitor extends AbstractVisitor { ...@@ -68,6 +72,12 @@ public class ModVisitor extends AbstractVisitor {
private static final long DOUBLE_TO_BITS = Double.doubleToLongBits(1); private static final long DOUBLE_TO_BITS = Double.doubleToLongBits(1);
private static final long FLOAT_TO_BITS = Float.floatToIntBits(1); private static final long FLOAT_TO_BITS = Float.floatToIntBits(1);
@Override
public boolean visit(ClassNode cls) throws JadxException {
replaceConstInAnnotations(cls);
return true;
}
@Override @Override
public void visit(MethodNode mth) { public void visit(MethodNode mth) {
if (mth.isNoCode()) { if (mth.isNoCode()) {
...@@ -171,6 +181,33 @@ public class ModVisitor extends AbstractVisitor { ...@@ -171,6 +181,33 @@ public class ModVisitor extends AbstractVisitor {
} }
} }
private void replaceConstInAnnotations(ClassNode cls) {
if (cls.root().getArgs().isReplaceConsts()) {
replaceConstsInAnnotationForAttrNode(cls, cls);
cls.getFields().forEach(f -> replaceConstsInAnnotationForAttrNode(cls, f));
cls.getMethods().forEach(m -> replaceConstsInAnnotationForAttrNode(cls, m));
}
}
private void replaceConstsInAnnotationForAttrNode(ClassNode parentCls, AttrNode attrNode) {
AnnotationsList annotationsList = attrNode.get(AType.ANNOTATION_LIST);
if (annotationsList == null) {
return;
}
for (Annotation annotation : annotationsList.getAll()) {
if (annotation.getVisibility() == Annotation.Visibility.SYSTEM) {
continue;
}
for (Map.Entry<String, Object> entry : annotation.getValues().entrySet()) {
Object value = entry.getValue();
FieldNode constField = parentCls.getConstField(value);
if (constField != null) {
entry.setValue(constField.getFieldInfo());
}
}
}
}
private static void replaceConst(MethodNode mth, ClassNode parentClass, BlockNode block, int i, InsnNode insn) { private static void replaceConst(MethodNode mth, ClassNode parentClass, BlockNode block, int i, InsnNode insn) {
FieldNode f; FieldNode f;
if (insn.getType() == InsnType.CONST_STR) { if (insn.getType() == InsnType.CONST_STR) {
......
...@@ -16,8 +16,6 @@ import org.jetbrains.annotations.Nullable; ...@@ -16,8 +16,6 @@ import org.jetbrains.annotations.Nullable;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
import static jadx.core.utils.files.FileUtils.close;
/** /**
* Simple template engine * Simple template engine
* Syntax for replace variable with value: '{{variable}}' * Syntax for replace variable with value: '{{variable}}'
...@@ -56,21 +54,15 @@ public class TemplateFile { ...@@ -56,21 +54,15 @@ public class TemplateFile {
} }
public String build() throws IOException { public String build() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
try {
process(out); process(out);
} finally { return out.toString();
close(out);
} }
return out.toString();
} }
public void save(File outFile) throws IOException { public void save(File outFile) throws IOException {
OutputStream out = new FileOutputStream(outFile); try (OutputStream out = new FileOutputStream(outFile)) {
try {
process(out); process(out);
} finally {
close(out);
} }
} }
......
package jadx.core.utils; package jadx.core.utils;
import java.io.File; import java.io.File;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import org.jetbrains.annotations.TestOnly; import org.jetbrains.annotations.TestOnly;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -12,6 +15,7 @@ import org.slf4j.LoggerFactory; ...@@ -12,6 +15,7 @@ import org.slf4j.LoggerFactory;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.codegen.InsnGen; import jadx.core.codegen.InsnGen;
import jadx.core.codegen.MethodGen; import jadx.core.codegen.MethodGen;
import jadx.core.dex.attributes.IAttributeNode;
import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IBlock;
import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IContainer;
...@@ -85,51 +89,74 @@ public class DebugUtils { ...@@ -85,51 +89,74 @@ public class DebugUtils {
printRegions(mth, false); printRegions(mth, false);
} }
public static void printRegion(MethodNode mth, IRegion region, boolean printInsn) { public static void printRegions(MethodNode mth, boolean printInsns) {
printRegion(mth, region, "", printInsn); printRegion(mth, mth.getRegion(), printInsns);
} }
public static void printRegions(MethodNode mth, boolean printInsns) { public static void printRegion(MethodNode mth, IRegion region, boolean printInsns) {
LOG.debug("|{}", mth); CodeWriter cw = new CodeWriter();
printRegion(mth, mth.getRegion(), "| ", printInsns); cw.startLine('|').add(mth.toString());
printRegion(mth, region, cw, "| ", printInsns);
LOG.debug("\n{}", cw.finish().getCodeStr());
} }
private static void printRegion(MethodNode mth, IRegion region, String indent, boolean printInsns) { private static void printRegion(MethodNode mth, IRegion region, CodeWriter cw, String indent, boolean printInsns) {
LOG.debug("{}{} {}", indent, region, region.getAttributesString()); printWithAttributes(cw, indent, region.toString(), region);
indent += "| "; indent += "| ";
for (IContainer container : region.getSubBlocks()) { for (IContainer container : region.getSubBlocks()) {
if (container instanceof IRegion) { if (container instanceof IRegion) {
printRegion(mth, (IRegion) container, indent, printInsns); printRegion(mth, (IRegion) container, cw, indent, printInsns);
} else { } else {
LOG.debug("{}{} {}", indent, container, container.getAttributesString()); printWithAttributes(cw, indent, container.toString(), container);
if (printInsns && container instanceof IBlock) { if (printInsns && container instanceof IBlock) {
IBlock block = (IBlock) container; IBlock block = (IBlock) container;
printInsns(mth, indent, block); printInsns(mth, cw, indent, block);
} }
} }
} }
} }
private static void printInsns(MethodNode mth, String indent, IBlock block) { private static void printInsns(MethodNode mth, CodeWriter cw, String indent, IBlock block) {
for (InsnNode insn : block.getInstructions()) { for (InsnNode insn : block.getInstructions()) {
try { try {
MethodGen mg = MethodGen.getFallbackMethodGen(mth); MethodGen mg = MethodGen.getFallbackMethodGen(mth);
InsnGen ig = new InsnGen(mg, true); InsnGen ig = new InsnGen(mg, true);
CodeWriter code = new CodeWriter(); CodeWriter code = new CodeWriter();
ig.makeInsn(insn, code); ig.makeInsn(insn, code);
String insnStr = code.toString().substring(CodeWriter.NL.length()); String codeStr = code.finish().getCodeStr();
String attrStr = insn.isAttrStorageEmpty() ? "" : '\t' + insn.getAttributesString();
LOG.debug("{}|> {}{}", indent, insnStr, attrStr); List<String> insnStrings = Arrays.stream(codeStr.split(CodeWriter.NL))
.filter(StringUtils::notBlank)
.map(s -> "|> " + s)
.collect(Collectors.toList());
Iterator<String> it = insnStrings.iterator();
while (true) {
String insnStr = it.next();
if (it.hasNext()) {
cw.startLine(indent).add(insnStr);
} else {
printWithAttributes(cw, indent, insnStr, insn);
break;
}
}
} catch (CodegenException e) { } catch (CodegenException e) {
LOG.debug("{}|>!! {}", indent, insn); cw.startLine(indent).add(">!! ").add(insn.toString());
} }
} }
} }
public static void printMap(String desc, Map<?, ?> map) { private static void printWithAttributes(CodeWriter cw, String indent, String codeStr, IAttributeNode attrNode) {
LOG.debug("Map of {}, size: {}", desc, map.size()); String str = attrNode.isAttrStorageEmpty() ? codeStr : codeStr + ' ' + attrNode.getAttributesString();
for (Map.Entry<?, ?> entry : map.entrySet()) { List<String> attrStrings = Arrays.stream(str.split(CodeWriter.NL))
LOG.debug(" {} : {}", entry.getKey(), entry.getValue()); .filter(StringUtils::notBlank)
.collect(Collectors.toList());
Iterator<String> it = attrStrings.iterator();
if (!it.hasNext()) {
return;
}
cw.startLine(indent).add(it.next());
while (it.hasNext()) {
cw.startLine(indent).add("|+ ").add(it.next());
} }
} }
} }
...@@ -4,7 +4,6 @@ import java.io.IOException; ...@@ -4,7 +4,6 @@ import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.nio.file.Path; import java.nio.file.Path;
import org.jetbrains.annotations.NotNull;
import org.jf.baksmali.Adaptors.ClassDefinition; import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.baksmali.BaksmaliOptions; import org.jf.baksmali.BaksmaliOptions;
import org.jf.dexlib2.DexFileFactory; import org.jf.dexlib2.DexFileFactory;
...@@ -34,24 +33,25 @@ public class SmaliUtils { ...@@ -34,24 +33,25 @@ public class SmaliUtils {
} }
} }
@NotNull public static boolean getSmaliCode(DexNode dex, int clsDefOffset, StringWriter stringWriter) {
public static String getSmaliCode(DexNode dex, int clsDefOffset) {
try { try {
Path path = dex.getDexFile().getPath(); Path path = dex.getDexFile().getPath();
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(path.toFile(), null); DexBackedDexFile dexFile = DexFileFactory.loadDexFile(path.toFile(), null);
DexBackedClassDef dexBackedClassDef = new DexBackedClassDef(dexFile, clsDefOffset); DexBackedClassDef dexBackedClassDef = new DexBackedClassDef(dexFile, clsDefOffset);
return getSmaliCode(dexBackedClassDef); getSmaliCode(dexBackedClassDef, stringWriter);
return true;
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error generating smali", e); LOG.error("Error generating smali", e);
return "Error generating smali code: " + e.getMessage() stringWriter.append("Error generating smali code: ");
+ '\n' + Utils.getStackTrace(e); stringWriter.append(e.getMessage());
stringWriter.append(System.lineSeparator());
stringWriter.append(Utils.getStackTrace(e));
return false;
} }
} }
private static String getSmaliCode(DexBackedClassDef classDef) throws IOException { private static void getSmaliCode(DexBackedClassDef classDef, StringWriter stringWriter) throws IOException {
ClassDefinition classDefinition = new ClassDefinition(new BaksmaliOptions(), classDef); ClassDefinition classDefinition = new ClassDefinition(new BaksmaliOptions(), classDef);
StringWriter sw = new StringWriter(); classDefinition.writeTo(new IndentingWriter(stringWriter));
classDefinition.writeTo(new IndentingWriter(sw));
return sw.toString();
} }
} }
...@@ -211,6 +211,10 @@ public class StringUtils { ...@@ -211,6 +211,10 @@ public class StringUtils {
return str == null || str.isEmpty(); return str == null || str.isEmpty();
} }
public static boolean notBlank(String str) {
return notEmpty(str) && !str.trim().isEmpty();
}
public static int countMatches(String str, String subStr) { public static int countMatches(String str, String subStr) {
if (str == null || str.isEmpty() || subStr == null || subStr.isEmpty()) { if (str == null || str.isEmpty() || subStr == null || subStr.isEmpty()) {
return 0; return 0;
......
...@@ -10,12 +10,15 @@ import java.io.InputStream; ...@@ -10,12 +10,15 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream;
import java.util.stream.Stream;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
...@@ -32,6 +35,9 @@ public class FileUtils { ...@@ -32,6 +35,9 @@ public class FileUtils {
public static final int READ_BUFFER_SIZE = 8 * 1024; public static final int READ_BUFFER_SIZE = 8 * 1024;
private static final int MAX_FILENAME_LENGTH = 128; private static final int MAX_FILENAME_LENGTH = 128;
public static final String JADX_TMP_INSTANCE_PREFIX = "jadx-instance-";
public static final String JADX_TMP_PREFIX = "jadx-tmp-";
private FileUtils() { private FileUtils() {
} }
...@@ -70,6 +76,12 @@ public class FileUtils { ...@@ -70,6 +76,12 @@ public class FileUtils {
} }
} }
public static void makeDirs(@Nullable Path dir) {
if (dir != null) {
makeDirs(dir.toFile());
}
}
public static boolean deleteDir(File dir) { public static boolean deleteDir(File dir) {
File[] content = dir.listFiles(); File[] content = dir.listFiles();
if (content != null) { if (content != null) {
...@@ -80,11 +92,27 @@ public class FileUtils { ...@@ -80,11 +92,27 @@ public class FileUtils {
return dir.delete(); return dir.delete();
} }
public static void deleteDir(Path dir) {
try (Stream<Path> pathStream = Files.walk(dir)) {
pathStream.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (Exception e) {
throw new JadxRuntimeException("Failed to delete directory " + dir, e);
}
}
private static final Path TEMP_ROOT_DIR = createTempRootDir(); private static final Path TEMP_ROOT_DIR = createTempRootDir();
private static Path createTempRootDir() { private static Path createTempRootDir() {
try { try {
Path dir = Files.createTempDirectory("jadx-instance-"); String jadxTmpDir = System.getenv("JADX_TMP_DIR");
Path dir;
if (jadxTmpDir != null) {
dir = Files.createTempDirectory(Paths.get(jadxTmpDir), "jadx-instance-");
} else {
dir = Files.createTempDirectory(JADX_TMP_INSTANCE_PREFIX);
}
dir.toFile().deleteOnExit(); dir.toFile().deleteOnExit();
return dir; return dir;
} catch (Exception e) { } catch (Exception e) {
...@@ -92,6 +120,15 @@ public class FileUtils { ...@@ -92,6 +120,15 @@ public class FileUtils {
} }
} }
public static void deleteTempRootDir() {
deleteDir(TEMP_ROOT_DIR);
}
public static void clearTempRootDir() {
deleteDir(TEMP_ROOT_DIR);
makeDirs(TEMP_ROOT_DIR);
}
public static Path createTempDir(String prefix) { public static Path createTempDir(String prefix) {
try { try {
Path dir = Files.createTempDirectory(TEMP_ROOT_DIR, prefix); Path dir = Files.createTempDirectory(TEMP_ROOT_DIR, prefix);
...@@ -104,7 +141,7 @@ public class FileUtils { ...@@ -104,7 +141,7 @@ public class FileUtils {
public static Path createTempFile(String suffix) { public static Path createTempFile(String suffix) {
try { try {
Path path = Files.createTempFile(TEMP_ROOT_DIR, "jadx-tmp-", suffix); Path path = Files.createTempFile(TEMP_ROOT_DIR, JADX_TMP_PREFIX, suffix);
path.toFile().deleteOnExit(); path.toFile().deleteOnExit();
return path; return path;
} catch (Exception e) { } catch (Exception e) {
...@@ -112,6 +149,14 @@ public class FileUtils { ...@@ -112,6 +149,14 @@ public class FileUtils {
} }
} }
public static Path createTempFileNoDelete(String suffix) {
try {
return Files.createTempFile(TEMP_ROOT_DIR, JADX_TMP_PREFIX, suffix);
} catch (Exception e) {
throw new JadxRuntimeException("Failed to create temp file with suffix: " + suffix, e);
}
}
public static void copyStream(InputStream input, OutputStream output) throws IOException { public static void copyStream(InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[READ_BUFFER_SIZE]; byte[] buffer = new byte[READ_BUFFER_SIZE];
while (true) { while (true) {
......
...@@ -6,7 +6,6 @@ import java.io.IOException; ...@@ -6,7 +6,6 @@ import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
...@@ -21,6 +20,7 @@ import java.util.concurrent.TimeUnit; ...@@ -21,6 +20,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import jadx.api.ICodeInfo; import jadx.api.ICodeInfo;
...@@ -119,6 +119,11 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -119,6 +119,11 @@ public abstract class IntegrationTest extends TestUtils {
args.setFsCaseSensitive(false); // use same value on all systems args.setFsCaseSensitive(false); // use same value on all systems
} }
@AfterEach
public void after() {
FileUtils.clearTempRootDir();
}
public String getTestName() { public String getTestName() {
return this.getClass().getSimpleName(); return this.getClass().getSimpleName();
} }
...@@ -414,7 +419,7 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -414,7 +419,7 @@ public abstract class IntegrationTest extends TestUtils {
temp = FileUtils.createTempFile(suffix); temp = FileUtils.createTempFile(suffix);
} else { } else {
// don't delete on exit // don't delete on exit
temp = Files.createTempFile("jadx", suffix); temp = FileUtils.createTempFileNoDelete(suffix);
System.out.println("Temporary file saved: " + temp.toAbsolutePath()); System.out.println("Temporary file saved: " + temp.toAbsolutePath());
} }
return temp.toFile(); return temp.toFile();
......
package jadx.tests.integration.inner;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestRFieldRestore3 extends IntegrationTest {
public static class TestCls {
@T(2131230730)
public static class A {
@F(2131230730)
private int f;
@M(bind = 2137373737)
private void mth() {
}
@T(2137373737)
private class D {
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface T {
int value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
@interface F {
int value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
@interface M {
int bind();
}
public static class R {
}
}
@Test
public void test() {
Map<Integer, String> map = new HashMap<>();
map.put(2131230730, "id.Button");
map.put(2137373737, "id.MyId");
setResMap(map);
assertThat(getClassNode(TestCls.class))
.code()
.containsOnlyOnce("@T(R.id.Button)")
.containsOnlyOnce("@T(R.id.MyId)")
.containsOnlyOnce("@F(R.id.Button)")
.containsOnlyOnce("@M(bind = R.id.MyId)");
}
}
package jadx.tests.integration.inner;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Test;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestReplaceConstsInAnnotations extends IntegrationTest {
public static class TestCls {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface A {
int i();
float f();
}
@A(i = -1, f = C.FLOAT_CONST)
public static class C {
public static final float FLOAT_CONST = 3.14f;
}
}
@Test
public void test() {
assertThat(getClassNode(TestCls.class))
.code()
.containsOnlyOnce("f = C.FLOAT_CONST");
}
}
package jadx.tests.integration.switches;
import org.junit.jupiter.api.Test;
import jadx.NotYetImplemented;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestSwitchFallThrough extends IntegrationTest {
public static class TestCls {
public int r;
public void test(int a) {
int i = 10;
switch (a) {
case 1:
i = 1000;
// fallthrough
case 2:
r = i;
break;
default:
r = -1;
break;
}
r *= 2;
System.out.println("in: " + a + ", out: " + r);
}
public int testWrap(int a) {
r = 0;
test(a);
return r;
}
public void check() {
assertThat(testWrap(1)).isEqualTo(2000);
assertThat(testWrap(2)).isEqualTo(20);
assertThat(testWrap(0)).isEqualTo(-2);
}
}
@NotYetImplemented("switch fallthrough")
@Test
public void test() {
assertThat(getClassNode(TestCls.class))
.code()
.containsOnlyOnce("switch");
// code correctness checks done in 'check' method
}
}
...@@ -77,6 +77,7 @@ import jadx.api.JadxArgs; ...@@ -77,6 +77,7 @@ import jadx.api.JadxArgs;
import jadx.api.JavaClass; import jadx.api.JavaClass;
import jadx.api.JavaNode; import jadx.api.JavaNode;
import jadx.api.ResourceFile; import jadx.api.ResourceFile;
import jadx.core.utils.files.FileUtils;
import jadx.gui.JadxWrapper; import jadx.gui.JadxWrapper;
import jadx.gui.jobs.BackgroundExecutor; import jadx.gui.jobs.BackgroundExecutor;
import jadx.gui.jobs.BackgroundWorker; import jadx.gui.jobs.BackgroundWorker;
...@@ -1109,6 +1110,8 @@ public class MainWindow extends JFrame { ...@@ -1109,6 +1110,8 @@ public class MainWindow extends JFrame {
settings.setMainWindowExtendedState(getExtendedState()); settings.setMainWindowExtendedState(getExtendedState());
cancelBackgroundJobs(); cancelBackgroundJobs();
dispose(); dispose();
FileUtils.deleteTempRootDir();
System.exit(0); System.exit(0);
} }
......
...@@ -40,7 +40,6 @@ class SearchBar extends JToolBar { ...@@ -40,7 +40,6 @@ class SearchBar extends JToolBar {
private final JCheckBox wholeWordCB; private final JCheckBox wholeWordCB;
private final JCheckBox matchCaseCB; private final JCheckBox matchCaseCB;
private ActionListener forwardListener = e -> search(0);
public SearchBar(RSyntaxTextArea textArea) { public SearchBar(RSyntaxTextArea textArea) {
rTextArea = textArea; rTextArea = textArea;
...@@ -81,6 +80,8 @@ class SearchBar extends JToolBar { ...@@ -81,6 +80,8 @@ class SearchBar extends JToolBar {
nextButton.setBorderPainted(false); nextButton.setBorderPainted(false);
add(nextButton); add(nextButton);
ActionListener forwardListener = e -> search(0);
markAllCB = new JCheckBox(NLS.str("search.mark_all")); markAllCB = new JCheckBox(NLS.str("search.mark_all"));
markAllCB.addActionListener(forwardListener); markAllCB.addActionListener(forwardListener);
add(markAllCB); add(markAllCB);
......
...@@ -17,4 +17,7 @@ public class SystemInfo { ...@@ -17,4 +17,7 @@ public class SystemInfo {
public static final boolean IS_WINDOWS = LOWER_OS_NAME.startsWith("windows"); public static final boolean IS_WINDOWS = LOWER_OS_NAME.startsWith("windows");
public static final boolean IS_MAC = LOWER_OS_NAME.startsWith("mac"); public static final boolean IS_MAC = LOWER_OS_NAME.startsWith("mac");
public static final boolean IS_LINUX = LOWER_OS_NAME.startsWith("linux"); public static final boolean IS_LINUX = LOWER_OS_NAME.startsWith("linux");
private SystemInfo() {
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册