mirror of
https://github.com/skylot/jadx.git
synced 2024-11-24 05:09:55 +00:00
fix issues reported by Coverity
This commit is contained in:
parent
343bddc6ad
commit
42eb319751
@ -1,8 +1,9 @@
|
||||
## JADX
|
||||
## JADX
|
||||
|
||||
[![Build Status](https://travis-ci.org/skylot/jadx.png?branch=master)](https://travis-ci.org/skylot/jadx)
|
||||
[![Build Status](https://drone.io/github.com/skylot/jadx/status.png)](https://drone.io/github.com/skylot/jadx/latest)
|
||||
[![Coverage Status](https://coveralls.io/repos/skylot/jadx/badge.png)](https://coveralls.io/r/skylot/jadx)
|
||||
[![Coverity Scan Build Status](https://scan.coverity.com/projects/2166/badge.svg)](https://scan.coverity.com/projects/2166)
|
||||
|
||||
**jadx** - Dex to Java decompiler
|
||||
|
||||
@ -12,14 +13,14 @@ Command line and GUI tools for produce Java source code from Android Dex and Apk
|
||||
### Downloads
|
||||
- [unstable](https://drone.io/github.com/skylot/jadx/files)
|
||||
- from [github](https://github.com/skylot/jadx/releases)
|
||||
- from [sourceforge](http://sourceforge.net/projects/jadx/files/)
|
||||
- from [sourceforge](http://sourceforge.net/projects/jadx/files/)
|
||||
|
||||
|
||||
### Building from source
|
||||
### Building from source
|
||||
git clone https://github.com/skylot/jadx.git
|
||||
cd jadx
|
||||
./gradlew dist
|
||||
|
||||
|
||||
(on Windows, use `gradlew.bat` instead of `./gradlew`)
|
||||
|
||||
Scripts for run jadx will be placed in `build/jadx/bin`
|
||||
|
@ -16,7 +16,7 @@ subprojects {
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
tasks.withType(Compile) {
|
||||
if (!"${it}".contains(':jadx-samples:')) {
|
||||
if (!"$it".contains(':jadx-samples:')) {
|
||||
options.compilerArgs << '-Xlint' << '-Xlint:unchecked' << '-Xlint:deprecation'
|
||||
}
|
||||
}
|
||||
|
@ -14,37 +14,41 @@ public class JadxCLI {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
JadxCLIArgs jadxArgs = new JadxCLIArgs(args);
|
||||
checkArgs(jadxArgs);
|
||||
processAndSave(jadxArgs);
|
||||
} catch (Exception e) {
|
||||
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||
if (processArgs(jadxArgs, args)) {
|
||||
processAndSave(jadxArgs);
|
||||
}
|
||||
} catch (JadxException e) {
|
||||
LOG.error(e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processAndSave(JadxCLIArgs jadxArgs) {
|
||||
static void processAndSave(JadxCLIArgs jadxArgs) throws JadxException {
|
||||
try {
|
||||
Decompiler jadx = new Decompiler(jadxArgs);
|
||||
jadx.loadFiles(jadxArgs.getInput());
|
||||
jadx.setOutputDir(jadxArgs.getOutDir());
|
||||
jadx.save();
|
||||
LOG.info("done");
|
||||
} catch (Throwable e) {
|
||||
LOG.error("jadx error:", e);
|
||||
throw new JadxException("jadx error: " + e.getMessage(), e);
|
||||
}
|
||||
int errorsCount = ErrorsCounter.getErrorCount();
|
||||
if (errorsCount != 0) {
|
||||
if (ErrorsCounter.getErrorCount() != 0) {
|
||||
ErrorsCounter.printReport();
|
||||
throw new JadxException("finished with errors");
|
||||
}
|
||||
System.exit(errorsCount);
|
||||
LOG.info("done");
|
||||
|
||||
}
|
||||
|
||||
private static void checkArgs(JadxCLIArgs jadxArgs) throws JadxException {
|
||||
static boolean processArgs(JadxCLIArgs jadxArgs, String[] args) throws JadxException {
|
||||
if (!jadxArgs.processArgs(args)) {
|
||||
return false;
|
||||
}
|
||||
if (jadxArgs.getInput().isEmpty()) {
|
||||
LOG.error("Please specify input file");
|
||||
jadxArgs.printUsage();
|
||||
System.exit(1);
|
||||
return false;
|
||||
}
|
||||
File outputDir = jadxArgs.getOutDir();
|
||||
if (outputDir == null) {
|
||||
@ -64,5 +68,6 @@ public class JadxCLI {
|
||||
if (outputDir.exists() && !outputDir.isDirectory()) {
|
||||
throw new JadxException("Output directory exists as file " + outputDir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -47,25 +47,25 @@ public final class JadxCLIArgs implements IJadxArgs {
|
||||
private final List<File> input = new ArrayList<File>(1);
|
||||
private File outputDir;
|
||||
|
||||
public JadxCLIArgs(String[] args) {
|
||||
parse(args);
|
||||
processArgs();
|
||||
public boolean processArgs(String[] args) {
|
||||
return parse(args) && process();
|
||||
}
|
||||
|
||||
private void parse(String[] args) {
|
||||
private boolean parse(String[] args) {
|
||||
try {
|
||||
new JCommander(this, args);
|
||||
return true;
|
||||
} catch (ParameterException e) {
|
||||
System.err.println("Arguments parse error: " + e.getMessage());
|
||||
printUsage();
|
||||
System.exit(1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void processArgs() {
|
||||
private boolean process() {
|
||||
if (isPrintHelp()) {
|
||||
printUsage();
|
||||
System.exit(0);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (threadsCount <= 0) {
|
||||
@ -95,8 +95,9 @@ public final class JadxCLIArgs implements IJadxArgs {
|
||||
} catch (JadxException e) {
|
||||
System.err.println("ERROR: " + e.getMessage());
|
||||
printUsage();
|
||||
System.exit(1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void printUsage() {
|
||||
|
@ -25,7 +25,6 @@ import jadx.core.dex.visitors.typeinference.TypeInference;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
@ -95,15 +94,18 @@ public class Jadx {
|
||||
|
||||
public static String getVersion() {
|
||||
try {
|
||||
Enumeration<URL> resources = Utils.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
|
||||
while (resources.hasMoreElements()) {
|
||||
Manifest manifest = new Manifest(resources.nextElement().openStream());
|
||||
String ver = manifest.getMainAttributes().getValue("jadx-version");
|
||||
if (ver != null) {
|
||||
return ver;
|
||||
ClassLoader classLoader = Utils.class.getClassLoader();
|
||||
if (classLoader != null) {
|
||||
Enumeration<URL> resources = classLoader.getResources("META-INF/MANIFEST.MF");
|
||||
while (resources.hasMoreElements()) {
|
||||
Manifest manifest = new Manifest(resources.nextElement().openStream());
|
||||
String ver = manifest.getMainAttributes().getValue("jadx-version");
|
||||
if (ver != null) {
|
||||
return ver;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
LOG.error("Can't get manifest file", e);
|
||||
}
|
||||
return "dev";
|
||||
|
@ -65,6 +65,9 @@ public class ClsSet {
|
||||
for (ClassNode cls : list) {
|
||||
if (cls.getAccessFlags().isPublic()) {
|
||||
NClass nClass = getCls(cls.getRawName(), names);
|
||||
if (nClass == null) {
|
||||
throw new JadxRuntimeException("Missing class: " + cls);
|
||||
}
|
||||
nClass.setParents(makeParentsArray(cls, names));
|
||||
classes[k] = nClass;
|
||||
k++;
|
||||
@ -102,40 +105,47 @@ public class ClsSet {
|
||||
Utils.makeDirsForFile(output);
|
||||
|
||||
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output));
|
||||
String outputName = output.getName();
|
||||
if (outputName.endsWith(CLST_EXTENSION)) {
|
||||
save(outputStream);
|
||||
} else if (outputName.endsWith(".jar")) {
|
||||
ZipOutputStream out = new ZipOutputStream(outputStream);
|
||||
try {
|
||||
out.putNextEntry(new ZipEntry(CLST_PKG_PATH + "/" + CLST_FILENAME));
|
||||
save(out);
|
||||
out.closeEntry();
|
||||
} finally {
|
||||
out.close();
|
||||
outputStream.close();
|
||||
try {
|
||||
String outputName = output.getName();
|
||||
if (outputName.endsWith(CLST_EXTENSION)) {
|
||||
save(outputStream);
|
||||
} else if (outputName.endsWith(".jar")) {
|
||||
ZipOutputStream out = new ZipOutputStream(outputStream);
|
||||
try {
|
||||
out.putNextEntry(new ZipEntry(CLST_PKG_PATH + "/" + CLST_FILENAME));
|
||||
save(out);
|
||||
out.closeEntry();
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown file format: " + outputName);
|
||||
}
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown file format: " + outputName);
|
||||
} finally {
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void save(OutputStream output) throws IOException {
|
||||
DataOutputStream out = new DataOutputStream(output);
|
||||
out.writeBytes(JADX_CLS_SET_HEADER);
|
||||
out.writeByte(VERSION);
|
||||
try {
|
||||
out.writeBytes(JADX_CLS_SET_HEADER);
|
||||
out.writeByte(VERSION);
|
||||
|
||||
LOG.info("Classes count: " + classes.length);
|
||||
out.writeInt(classes.length);
|
||||
for (NClass cls : classes) {
|
||||
writeString(out, cls.getName());
|
||||
}
|
||||
for (NClass cls : classes) {
|
||||
NClass[] parents = cls.getParents();
|
||||
out.writeByte(parents.length);
|
||||
for (NClass parent : parents) {
|
||||
out.writeInt(parent.getId());
|
||||
LOG.info("Classes count: " + classes.length);
|
||||
out.writeInt(classes.length);
|
||||
for (NClass cls : classes) {
|
||||
writeString(out, cls.getName());
|
||||
}
|
||||
for (NClass cls : classes) {
|
||||
NClass[] parents = cls.getParents();
|
||||
out.writeByte(parents.length);
|
||||
for (NClass parent : parents) {
|
||||
out.writeInt(parent.getId());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,55 +154,67 @@ public class ClsSet {
|
||||
if (input == null) {
|
||||
throw new JadxRuntimeException("Can't load classpath file: " + CLST_FILENAME);
|
||||
}
|
||||
load(input);
|
||||
try {
|
||||
load(input);
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void load(File input) throws IOException, DecodeException {
|
||||
String name = input.getName();
|
||||
InputStream inputStream = new FileInputStream(input);
|
||||
if (name.endsWith(CLST_EXTENSION)) {
|
||||
load(inputStream);
|
||||
} else if (name.endsWith(".jar")) {
|
||||
ZipInputStream in = new ZipInputStream(inputStream);
|
||||
try {
|
||||
ZipEntry entry = in.getNextEntry();
|
||||
while (entry != null) {
|
||||
if (entry.getName().endsWith(CLST_EXTENSION)) {
|
||||
load(in);
|
||||
try {
|
||||
if (name.endsWith(CLST_EXTENSION)) {
|
||||
load(inputStream);
|
||||
} else if (name.endsWith(".jar")) {
|
||||
ZipInputStream in = new ZipInputStream(inputStream);
|
||||
try {
|
||||
ZipEntry entry = in.getNextEntry();
|
||||
while (entry != null) {
|
||||
if (entry.getName().endsWith(CLST_EXTENSION)) {
|
||||
load(in);
|
||||
}
|
||||
entry = in.getNextEntry();
|
||||
}
|
||||
entry = in.getNextEntry();
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown file format: " + name);
|
||||
}
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown file format: " + name);
|
||||
} finally {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void load(InputStream input) throws IOException, DecodeException {
|
||||
DataInputStream in = new DataInputStream(input);
|
||||
byte[] header = new byte[JADX_CLS_SET_HEADER.length()];
|
||||
int readHeaderLength = in.read(header);
|
||||
int version = in.readByte();
|
||||
if (readHeaderLength != JADX_CLS_SET_HEADER.length()
|
||||
|| !JADX_CLS_SET_HEADER.equals(new String(header, STRING_CHARSET))
|
||||
|| version != VERSION) {
|
||||
throw new DecodeException("Wrong jadx class set header");
|
||||
}
|
||||
int count = in.readInt();
|
||||
classes = new NClass[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
String name = readString(in);
|
||||
classes[i] = new NClass(name, i);
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
int pCount = in.readByte();
|
||||
NClass[] parents = new NClass[pCount];
|
||||
for (int j = 0; j < pCount; j++) {
|
||||
parents[j] = classes[in.readInt()];
|
||||
try {
|
||||
byte[] header = new byte[JADX_CLS_SET_HEADER.length()];
|
||||
int readHeaderLength = in.read(header);
|
||||
int version = in.readByte();
|
||||
if (readHeaderLength != JADX_CLS_SET_HEADER.length()
|
||||
|| !JADX_CLS_SET_HEADER.equals(new String(header, STRING_CHARSET))
|
||||
|| version != VERSION) {
|
||||
throw new DecodeException("Wrong jadx class set header");
|
||||
}
|
||||
classes[i].setParents(parents);
|
||||
int count = in.readInt();
|
||||
classes = new NClass[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
String name = readString(in);
|
||||
classes[i] = new NClass(name, i);
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
int pCount = in.readByte();
|
||||
NClass[] parents = new NClass[pCount];
|
||||
for (int j = 0; j < pCount; j++) {
|
||||
parents[j] = classes[in.readInt()];
|
||||
}
|
||||
classes[i].setParents(parents);
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,9 @@ public class ConvertToClsSet {
|
||||
|
||||
private static void addFilesFromDirectory(File dir, List<InputFile> inputFiles) throws IOException, DecodeException {
|
||||
File[] files = dir.listFiles();
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
addFilesFromDirectory(file, inputFiles);
|
||||
|
@ -555,10 +555,12 @@ public class InsnGen {
|
||||
}
|
||||
cls.getAttributes().add(AttributeFlag.DONT_GENERATE);
|
||||
MethodNode defCtr = cls.getDefaultConstructor();
|
||||
if (RegionUtils.notEmpty(defCtr.getRegion())) {
|
||||
defCtr.getAttributes().add(AttributeFlag.ANONYMOUS_CONSTRUCTOR);
|
||||
} else {
|
||||
defCtr.getAttributes().add(AttributeFlag.DONT_GENERATE);
|
||||
if (defCtr != null) {
|
||||
if (RegionUtils.notEmpty(defCtr.getRegion())) {
|
||||
defCtr.getAttributes().add(AttributeFlag.ANONYMOUS_CONSTRUCTOR);
|
||||
} else {
|
||||
defCtr.getAttributes().add(AttributeFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
code.add("new ").add(parent == null ? "Object" : useClass(parent)).add("() ");
|
||||
new ClassGen(cls, mgen.getClassGen().getParentGen(), fallback).addClassBody(code);
|
||||
|
@ -312,6 +312,7 @@ public class MethodGen {
|
||||
}
|
||||
}
|
||||
} catch (CodegenException e) {
|
||||
LOG.debug("Error generate fallback instruction: ", e.getCause());
|
||||
code.startLine("// error: " + insn);
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,23 @@ public class ArithNode extends InsnNode {
|
||||
return op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ArithNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
ArithNode that = (ArithNode) obj;
|
||||
return op == that.op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + op.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return InsnUtils.formatOffset(offset) + ": "
|
||||
|
@ -3,7 +3,7 @@ package jadx.core.dex.instructions;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
public class ConstClassNode extends InsnNode {
|
||||
public final class ConstClassNode extends InsnNode {
|
||||
|
||||
private final ArgType clsType;
|
||||
|
||||
@ -16,6 +16,23 @@ public class ConstClassNode extends InsnNode {
|
||||
return clsType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ConstClassNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
ConstClassNode that = (ConstClassNode) obj;
|
||||
return clsType.equals(that.clsType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + clsType.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " " + clsType;
|
||||
|
@ -2,7 +2,7 @@ package jadx.core.dex.instructions;
|
||||
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
public class ConstStringNode extends InsnNode {
|
||||
public final class ConstStringNode extends InsnNode {
|
||||
|
||||
private final String str;
|
||||
|
||||
@ -15,6 +15,23 @@ public class ConstStringNode extends InsnNode {
|
||||
return str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ConstStringNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
ConstStringNode that = (ConstStringNode) obj;
|
||||
return str.equals(that.str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + str.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " \"" + str + "\"";
|
||||
|
@ -8,7 +8,7 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import com.android.dx.io.instructions.FillArrayDataPayloadDecodedInstruction;
|
||||
|
||||
public class FillArrayNode extends InsnNode {
|
||||
public final class FillArrayNode extends InsnNode {
|
||||
|
||||
private final Object data;
|
||||
private ArgType elemType;
|
||||
@ -53,4 +53,21 @@ public class FillArrayNode extends InsnNode {
|
||||
elemType = r;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof FillArrayNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
FillArrayNode that = (FillArrayNode) obj;
|
||||
return elemType.equals(that.elemType) && data == that.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + elemType.hashCode() + data.hashCode();
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,23 @@ public class GotoNode extends InsnNode {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof GotoNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
GotoNode gotoNode = (GotoNode) obj;
|
||||
return target == gotoNode.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-> " + InsnUtils.formatOffset(target);
|
||||
|
@ -70,6 +70,23 @@ public class IfNode extends GotoNode {
|
||||
return elseBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof IfNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
IfNode ifNode = (IfNode) obj;
|
||||
return op == ifNode.op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + op.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return InsnUtils.formatOffset(offset) + ": "
|
||||
|
@ -16,6 +16,23 @@ public class IndexInsnNode extends InsnNode {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof IndexInsnNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
IndexInsnNode that = (IndexInsnNode) obj;
|
||||
return index == null ? that.index == null : index.equals(that.index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + (index != null ? index.hashCode() : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " " + InsnUtils.indexToString(index);
|
||||
|
@ -44,6 +44,26 @@ public class InvokeNode extends InsnNode {
|
||||
return mth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof InvokeNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
InvokeNode that = (InvokeNode) obj;
|
||||
return type == that.type && mth.equals(that.mth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + type.hashCode();
|
||||
result = 31 * result + mth.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return InsnUtils.formatOffset(offset) + ": "
|
||||
|
@ -36,6 +36,29 @@ public class SwitchNode extends InsnNode {
|
||||
return def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof SwitchNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
SwitchNode that = (SwitchNode) obj;
|
||||
return def == that.def
|
||||
&& Arrays.equals(keys, that.keys)
|
||||
&& Arrays.equals(targets, that.targets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(keys);
|
||||
result = 31 * result + Arrays.hashCode(targets);
|
||||
result = 31 * result + def;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder targ = new StringBuilder();
|
||||
|
@ -42,6 +42,32 @@ public final class FieldArg extends RegisterArg {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof FieldArg) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
FieldArg fieldArg = (FieldArg) obj;
|
||||
if (!field.equals(fieldArg.field)) {
|
||||
return false;
|
||||
}
|
||||
if (regArg != null ? !regArg.equals(fieldArg.regArg) : fieldArg.regArg != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + field.hashCode();
|
||||
result = 31 * result + (regArg != null ? regArg.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + field + ")";
|
||||
|
@ -126,6 +126,9 @@ public class RegisterArg extends InsnArg implements Named {
|
||||
}
|
||||
|
||||
public InsnNode getAssignInsn() {
|
||||
if (sVar == null) {
|
||||
return null;
|
||||
}
|
||||
RegisterArg assign = sVar.getAssign();
|
||||
if (assign != null) {
|
||||
return assign.getParentInsn();
|
||||
|
@ -75,6 +75,29 @@ public class ConstructorInsn extends InsnNode {
|
||||
return callType == CallType.SELF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof ConstructorInsn) || !super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
ConstructorInsn that = (ConstructorInsn) o;
|
||||
return callMth.equals(that.callMth)
|
||||
&& callType == that.callType
|
||||
&& instanceArg.equals(that.instanceArg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + callMth.hashCode();
|
||||
result = 31 * result + callType.hashCode();
|
||||
result = 31 * result + instanceArg.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " " + callMth + " " + callType;
|
||||
|
@ -33,6 +33,23 @@ public class TernaryInsn extends InsnNode {
|
||||
return condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof TernaryInsn) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
TernaryInsn that = (TernaryInsn) obj;
|
||||
return condition.equals(that.condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + condition.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return InsnUtils.formatOffset(offset) + ": TERNARY"
|
||||
|
@ -61,9 +61,17 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
}
|
||||
|
||||
public void lock() {
|
||||
cleanSuccessors = Collections.unmodifiableList(cleanSuccessors);
|
||||
successors = Collections.unmodifiableList(successors);
|
||||
predecessors = Collections.unmodifiableList(predecessors);
|
||||
cleanSuccessors = lockList(cleanSuccessors);
|
||||
successors = lockList(successors);
|
||||
predecessors = lockList(predecessors);
|
||||
dominatesOn = lockList(dominatesOn);
|
||||
}
|
||||
|
||||
List<BlockNode> lockList(List<BlockNode> list) {
|
||||
if (list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,6 +85,16 @@ final class LocalVar extends RegisterArg {
|
||||
return endAddr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " " + (isEnd
|
||||
|
@ -13,7 +13,12 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SignatureParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SignatureParser.class);
|
||||
private static final char STOP_CHAR = 0;
|
||||
|
||||
private final String sign;
|
||||
@ -217,6 +222,10 @@ public class SignatureParser {
|
||||
break;
|
||||
}
|
||||
String id = consumeUntil(':');
|
||||
if (id == null) {
|
||||
LOG.error("Can't parse generic map: {}", sign);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
tryConsume(':');
|
||||
List<ArgType> types = consumeExtendsTypesList();
|
||||
map.put(ArgType.genericType(id), types);
|
||||
|
@ -73,7 +73,7 @@ public class ClassModifier extends AbstractVisitor {
|
||||
}
|
||||
|
||||
private static boolean removeFieldUsageFromConstructor(MethodNode mth, FieldNode field, ClassNode fieldsCls) {
|
||||
if (!mth.getAccessFlags().isConstructor()) {
|
||||
if (mth.isNoCode() || !mth.getAccessFlags().isConstructor()) {
|
||||
return false;
|
||||
}
|
||||
List<RegisterArg> args = mth.getArguments(false);
|
||||
@ -114,6 +114,9 @@ public class ClassModifier extends AbstractVisitor {
|
||||
|
||||
private static void removeSyntheticMethods(ClassNode cls) {
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
if (mth.isNoCode()) {
|
||||
continue;
|
||||
}
|
||||
AccessInfo af = mth.getAccessFlags();
|
||||
// remove bridge methods
|
||||
if (af.isBridge() && af.isSynthetic() && !isMethodUniq(cls, mth)) {
|
||||
|
@ -97,7 +97,7 @@ public class Utils {
|
||||
|
||||
public static void makeDirsForFile(File file) {
|
||||
File dir = file.getParentFile();
|
||||
if (!dir.exists()) {
|
||||
if (dir != null && !dir.exists()) {
|
||||
// if directory already created in other thread mkdirs will return false,
|
||||
// so check dir existence again
|
||||
if (!dir.mkdirs() && !dir.exists()) {
|
||||
|
@ -2,6 +2,7 @@ package jadx.core.utils.files;
|
||||
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@ -56,17 +57,23 @@ public class InputFile {
|
||||
private byte[] openDexFromApk(File file) throws IOException {
|
||||
ZipFile zf = new ZipFile(file);
|
||||
ZipEntry dex = zf.getEntry("classes.dex");
|
||||
if (dex == null) {
|
||||
zf.close();
|
||||
throw new JadxRuntimeException("File 'classes.dex' not found in apk file: " + file);
|
||||
}
|
||||
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
|
||||
InputStream in = null;
|
||||
try {
|
||||
InputStream in = zf.getInputStream(dex);
|
||||
|
||||
in = zf.getInputStream(dex);
|
||||
byte[] buffer = new byte[8192];
|
||||
int count;
|
||||
while ((count = in.read(buffer)) != -1) {
|
||||
bytesOut.write(buffer, 0, count);
|
||||
}
|
||||
in.close();
|
||||
} finally {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
zf.close();
|
||||
}
|
||||
return bytesOut.toByteArray();
|
||||
|
@ -4,6 +4,7 @@ import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import com.android.dx.command.DxConsole;
|
||||
import com.android.dx.command.dexer.Main;
|
||||
@ -11,6 +12,8 @@ import com.android.dx.command.dexer.Main.Arguments;
|
||||
|
||||
public class JavaToDex {
|
||||
|
||||
private static final String CHARSET_NAME = "UTF-8";
|
||||
|
||||
public static class DxArgs extends Arguments {
|
||||
public DxArgs(String dexFile, String[] input) {
|
||||
outName = dexFile;
|
||||
@ -27,12 +30,15 @@ public class JavaToDex {
|
||||
|
||||
public byte[] convert(String javaFile) throws JadxException {
|
||||
ByteArrayOutputStream errOut = new ByteArrayOutputStream();
|
||||
DxConsole.err = new PrintStream(errOut);
|
||||
|
||||
try {
|
||||
DxConsole.err = new PrintStream(errOut, true, CHARSET_NAME);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new JadxException(e.getMessage(), e);
|
||||
}
|
||||
PrintStream oldOut = System.out;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
System.setOut(new PrintStream(baos));
|
||||
System.setOut(new PrintStream(baos, true, CHARSET_NAME));
|
||||
DxArgs args = new DxArgs("-", new String[]{javaFile});
|
||||
Main.run(args);
|
||||
baos.close();
|
||||
@ -41,8 +47,12 @@ public class JavaToDex {
|
||||
} finally {
|
||||
System.setOut(oldOut);
|
||||
}
|
||||
// errOut also contains warnings
|
||||
dxErrors = errOut.toString();
|
||||
try {
|
||||
// errOut also contains warnings
|
||||
dxErrors = errOut.toString(CHARSET_NAME);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new JadxException("Can't save error output", e);
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
package jadx.tests
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType
|
||||
import jadx.core.dex.nodes.parser.SignatureParser
|
||||
import spock.lang.Specification
|
||||
@ -84,4 +83,12 @@ class TestSignatureParser extends Specification {
|
||||
argTypes.size() == 1
|
||||
argTypes.get(0) == generic("Ljava/util/List;", wildcard())
|
||||
}
|
||||
|
||||
def "generic map: bad signature"() {
|
||||
when:
|
||||
def map = new SignatureParser("<A:Ljava/lang/Object;B").consumeGenericMap()
|
||||
then:
|
||||
notThrown(NullPointerException)
|
||||
map.isEmpty()
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,10 @@ public class JadxGUI {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
final JadxCLIArgs jadxArgs = new JadxCLIArgs(args);
|
||||
final JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||
if (!jadxArgs.processArgs(args)) {
|
||||
return;
|
||||
}
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
@ -25,7 +28,7 @@ public class JadxGUI {
|
||||
window.setLocationAndPosition();
|
||||
window.setVisible(true);
|
||||
window.setLocationRelativeTo(null);
|
||||
window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
|
||||
if (jadxArgs.getInput().isEmpty()) {
|
||||
window.openFile();
|
||||
|
@ -135,7 +135,10 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
|
||||
private void toggleSearch() {
|
||||
tabbedPane.getSelectedCodePanel().getSearchBar().toggle();
|
||||
CodePanel codePanel = tabbedPane.getSelectedCodePanel();
|
||||
if (codePanel != null) {
|
||||
codePanel.getSearchBar().toggle();
|
||||
}
|
||||
}
|
||||
|
||||
private void initMenuAndToolbar() {
|
||||
@ -148,7 +151,7 @@ public class MainWindow extends JFrame {
|
||||
exit.setMnemonic(KeyEvent.VK_E);
|
||||
exit.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
System.exit(0);
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -145,8 +145,10 @@ class SearchBar extends JToolBar {
|
||||
}
|
||||
|
||||
private void search(int direction) {
|
||||
String text = searchField.getText();
|
||||
if (text.length() == 0) {
|
||||
String searchText = searchField.getText();
|
||||
if (searchText == null
|
||||
|| searchText.length() == 0
|
||||
|| rTextArea.getText() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -156,20 +158,20 @@ class SearchBar extends JToolBar {
|
||||
boolean wholeWord = wholeWordCB.isSelected();
|
||||
|
||||
if (markAllCB.isSelected()) {
|
||||
rTextArea.markAll(text, matchCase, wholeWord, regex);
|
||||
rTextArea.markAll(searchText, matchCase, wholeWord, regex);
|
||||
} else {
|
||||
rTextArea.clearMarkAllHighlights();
|
||||
}
|
||||
|
||||
SearchContext context = new SearchContext();
|
||||
context.setSearchFor(text);
|
||||
context.setSearchFor(searchText);
|
||||
context.setMatchCase(matchCase);
|
||||
context.setRegularExpression(regex);
|
||||
context.setSearchForward(forward);
|
||||
context.setWholeWord(wholeWord);
|
||||
|
||||
// TODO hack: move cursor before previous search for not jump to next occurrence
|
||||
if (direction == 0 && !searchField.getBackground().equals(COLOR_BG_ERROR)) {
|
||||
if (direction == 0 && !COLOR_BG_ERROR.equals(searchField.getBackground())) {
|
||||
try {
|
||||
int caretPos = rTextArea.getCaretPosition();
|
||||
int lineNum = rTextArea.getLineOfOffset(caretPos) - 1;
|
||||
@ -183,7 +185,7 @@ class SearchBar extends JToolBar {
|
||||
|
||||
boolean found = SearchEngine.find(rTextArea, context);
|
||||
if (!found) {
|
||||
int pos = SearchEngine.getNextMatchPos(text, rTextArea.getText(), forward, matchCase, wholeWord);
|
||||
int pos = SearchEngine.getNextMatchPos(searchText, rTextArea.getText(), forward, matchCase, wholeWord);
|
||||
if (pos != -1) {
|
||||
rTextArea.setCaretPosition(forward ? 0 : rTextArea.getDocument().getLength() - 1);
|
||||
search(direction);
|
||||
|
Loading…
Reference in New Issue
Block a user