fix: prevent collisions in method ids for java-input
This commit is contained in:
@@ -11,7 +11,7 @@ public class InfoStorage {
|
||||
private final Map<FieldInfo, FieldInfo> fields = new HashMap<>();
|
||||
// use only one MethodInfo instance
|
||||
private final Map<MethodInfo, MethodInfo> uniqueMethods = new HashMap<>();
|
||||
// can contain same method with different ids (from different dex files)
|
||||
// can contain same method with different ids (from different files)
|
||||
private final Map<Integer, MethodInfo> methods = new HashMap<>();
|
||||
|
||||
public ClassInfo getCls(ArgType type) {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package jadx.core.dex.info;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -39,9 +43,11 @@ public final class MethodInfo implements Comparable<MethodInfo> {
|
||||
public static MethodInfo fromRef(RootNode root, IMethodRef methodRef) {
|
||||
InfoStorage infoStorage = root.getInfoStorage();
|
||||
int uniqId = methodRef.getUniqId();
|
||||
MethodInfo prevMth = infoStorage.getByUniqId(uniqId);
|
||||
if (prevMth != null) {
|
||||
return prevMth;
|
||||
if (uniqId != 0) {
|
||||
MethodInfo prevMth = infoStorage.getByUniqId(uniqId);
|
||||
if (prevMth != null) {
|
||||
return prevMth;
|
||||
}
|
||||
}
|
||||
methodRef.load();
|
||||
ArgType parentClsType = ArgType.parse(methodRef.getParentClassType());
|
||||
@@ -50,7 +56,9 @@ public final class MethodInfo implements Comparable<MethodInfo> {
|
||||
List<ArgType> args = Utils.collectionMap(methodRef.getArgTypes(), ArgType::parse);
|
||||
MethodInfo newMth = new MethodInfo(parentClass, methodRef.getName(), args, returnType);
|
||||
MethodInfo uniqMth = infoStorage.putMethod(newMth);
|
||||
infoStorage.putByUniqId(uniqId, uniqMth);
|
||||
if (uniqId != 0) {
|
||||
infoStorage.putByUniqId(uniqId, uniqMth);
|
||||
}
|
||||
return uniqMth;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,12 @@ public abstract class BaseExternalTest extends IntegrationTest {
|
||||
protected abstract String getSamplesDir();
|
||||
|
||||
protected JadxArgs prepare(String inputFile) {
|
||||
return prepare(new File(getSamplesDir(), inputFile));
|
||||
}
|
||||
|
||||
protected JadxArgs prepare(File input) {
|
||||
JadxArgs args = new JadxArgs();
|
||||
args.getInputFiles().add(new File(getSamplesDir(), inputFile));
|
||||
args.getInputFiles().add(input);
|
||||
args.setOutDir(new File("../jadx-external-tests-tmp"));
|
||||
return args;
|
||||
}
|
||||
@@ -47,6 +51,7 @@ public abstract class BaseExternalTest extends IntegrationTest {
|
||||
|
||||
protected void decompile(JadxArgs jadxArgs, @Nullable String clsPatternStr, @Nullable String mthPatternStr) {
|
||||
JadxDecompiler jadx = new JadxDecompiler(jadxArgs);
|
||||
jadx.getPluginManager().unload("java-convert");
|
||||
jadx.load();
|
||||
|
||||
if (clsPatternStr == null) {
|
||||
|
||||
+12
-16
@@ -21,22 +21,22 @@ import jadx.api.plugins.utils.ZipSecurity;
|
||||
public class JavaFileLoader {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JavaFileLoader.class);
|
||||
|
||||
public static final int MAX_MAGIC_SIZE = 4;
|
||||
public static final byte[] JAVA_CLASS_FILE_MAGIC = { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE };
|
||||
public static final byte[] ZIP_FILE_MAGIC = { 0x50, 0x4B, 0x03, 0x04 };
|
||||
private static final int MAX_MAGIC_SIZE = 4;
|
||||
private static final byte[] JAVA_CLASS_FILE_MAGIC = { (byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE };
|
||||
private static final byte[] ZIP_FILE_MAGIC = { 0x50, 0x4B, 0x03, 0x04 };
|
||||
|
||||
private static int classUniqId = 1;
|
||||
private int classUniqId = 1;
|
||||
|
||||
public static List<JavaClassReader> collectFiles(List<Path> inputFiles) {
|
||||
public List<JavaClassReader> collectFiles(List<Path> inputFiles) {
|
||||
return inputFiles.stream()
|
||||
.map(Path::toFile)
|
||||
.map(JavaFileLoader::loadFromFile)
|
||||
.map(this::loadFromFile)
|
||||
.filter(list -> !list.isEmpty())
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<JavaClassReader> loadFromFile(File file) {
|
||||
private List<JavaClassReader> loadFromFile(File file) {
|
||||
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
|
||||
return loadReader(file, inputStream, file.getAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
@@ -45,7 +45,7 @@ public class JavaFileLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private static List<JavaClassReader> loadReader(File file, InputStream in, String inputFileName) throws IOException {
|
||||
private List<JavaClassReader> loadReader(File file, InputStream in, String inputFileName) throws IOException {
|
||||
byte[] magic = new byte[MAX_MAGIC_SIZE];
|
||||
if (in.read(magic) != magic.length) {
|
||||
return Collections.emptyList();
|
||||
@@ -61,7 +61,7 @@ public class JavaFileLoader {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static List<JavaClassReader> collectFromZip(File file) {
|
||||
private List<JavaClassReader> collectFromZip(File file) {
|
||||
List<JavaClassReader> result = new ArrayList<>();
|
||||
try {
|
||||
ZipSecurity.readZipEntries(file, (entry, in) -> {
|
||||
@@ -94,7 +94,7 @@ public class JavaFileLoader {
|
||||
int estimateSize = prefix.length + in.available();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(estimateSize);
|
||||
out.write(prefix);
|
||||
byte[] buffer = new byte[0xFFFF];
|
||||
byte[] buffer = new byte[8 * 1024];
|
||||
while (true) {
|
||||
int len = in.read(buffer);
|
||||
if (len == -1) {
|
||||
@@ -105,11 +105,7 @@ public class JavaFileLoader {
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private static int getNextUniqId() {
|
||||
classUniqId++;
|
||||
if (classUniqId >= 0xFFFF) {
|
||||
classUniqId = 1;
|
||||
}
|
||||
return classUniqId;
|
||||
private int getNextUniqId() {
|
||||
return classUniqId++;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ public class JavaInputPlugin implements JadxInputPlugin {
|
||||
|
||||
@Override
|
||||
public ILoadResult loadFiles(List<Path> inputFiles) {
|
||||
List<JavaClassReader> readers = JavaFileLoader.collectFiles(inputFiles);
|
||||
List<JavaClassReader> readers = new JavaFileLoader().collectFiles(inputFiles);
|
||||
if (readers.isEmpty()) {
|
||||
return EmptyLoadResult.INSTANCE;
|
||||
}
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ public class ConstPoolReader {
|
||||
int descIdx = data.readU2();
|
||||
|
||||
JavaMethodRef mthRef = new JavaMethodRef();
|
||||
mthRef.initUniqId(clsReader, clsIdx, nameIdx, descIdx);
|
||||
mthRef.initUniqId(clsReader, idx, true);
|
||||
mthRef.setParentClassType(getClass(clsIdx));
|
||||
mthRef.setName(getUtf8(nameIdx));
|
||||
mthRef.setDescr(getUtf8(descIdx));
|
||||
|
||||
+3
-3
@@ -94,7 +94,7 @@ public class JavaClassData implements IClassData {
|
||||
methodRef.setParentClassType(classType);
|
||||
JavaMethodData method = new JavaMethodData(this, methodRef);
|
||||
for (int i = 0; i < methodsCount; i++) {
|
||||
parseMethod(reader, method, clsIdx);
|
||||
parseMethod(reader, method, i);
|
||||
mthConsumer.accept(method);
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public class JavaClassData implements IClassData {
|
||||
field.setAttributes(attributes);
|
||||
}
|
||||
|
||||
private void parseMethod(DataReader reader, JavaMethodData method, int clsIdx) {
|
||||
private void parseMethod(DataReader reader, JavaMethodData method, int id) {
|
||||
int accessFlags = reader.readU2();
|
||||
int nameIdx = reader.readU2();
|
||||
int descriptorIdx = reader.readU2();
|
||||
@@ -120,7 +120,7 @@ public class JavaClassData implements IClassData {
|
||||
|
||||
JavaMethodRef methodRef = method.getMethodRef();
|
||||
methodRef.reset();
|
||||
methodRef.initUniqId(clsReader, clsIdx, nameIdx, descriptorIdx);
|
||||
methodRef.initUniqId(clsReader, id, false);
|
||||
methodRef.setName(constPoolReader.getUtf8(nameIdx));
|
||||
methodRef.setDescr(constPoolReader.getUtf8(descriptorIdx));
|
||||
|
||||
|
||||
+9
-3
@@ -16,9 +16,15 @@ public class JavaMethodRef extends JavaMethodProto implements IMethodRef {
|
||||
return uniqId;
|
||||
}
|
||||
|
||||
public void initUniqId(JavaClassReader clsReader, int clsIdx, int nameIdx, int descIdx) {
|
||||
// TODO: check for id overlap
|
||||
this.uniqId = (clsReader.getId() & 0xFFF) << 20 | (clsIdx & 0xFF) << 12 | (nameIdx & 0xFF) << 4 | descIdx & 0xF;
|
||||
public void initUniqId(JavaClassReader clsReader, int id, boolean fromConstPool) {
|
||||
int readerId = clsReader.getId();
|
||||
if (readerId > 0xFFFF || id > 0x7FFF) {
|
||||
// loaded more than 65535 classes or more than 32767 methods in this class -> disable caching
|
||||
this.uniqId = 0;
|
||||
} else {
|
||||
int source = fromConstPool ? 0 : 0x8000;
|
||||
this.uniqId = (readerId & 0xFFFF) << 16 | source | id & 0x7FFF;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
@@ -4,6 +4,11 @@ import jadx.api.plugins.input.insns.custom.ICustomPayload;
|
||||
|
||||
public interface IMethodRef extends IMethodProto, ICustomPayload {
|
||||
|
||||
/**
|
||||
* Method unique id (will be used for caching).
|
||||
*
|
||||
* @return 0 if can't calculate good unique identifier (disable caching)
|
||||
*/
|
||||
int getUniqId();
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user