fix: store classes access flags in class set

This commit is contained in:
Skylot
2024-02-01 21:12:19 +00:00
parent edf6ce273c
commit a3a4fabd5a
5 changed files with 99 additions and 42 deletions
@@ -23,8 +23,9 @@ public class ConvertToClsSet {
private static final Logger LOG = LoggerFactory.getLogger(ConvertToClsSet.class);
public static void usage() {
LOG.info("<output .jcst file> <several input dex or jar files> ");
LOG.info("<android API level (number)> <output .jcst file> <several input dex or jar files> ");
LOG.info("Arguments to update core.jcst: "
+ "<android API level (number)> "
+ "<jadx root>/jadx-core/src/main/resources/clst/core.jcst "
+ "<sdk_root>/platforms/android-<api level>/android.jar"
+ "<sdk_root>/platforms/android-<api level>/optional/android.car.jar "
@@ -32,11 +33,12 @@ public class ConvertToClsSet {
}
public static void main(String[] args) {
if (args.length < 2) {
if (args.length != 5) {
usage();
System.exit(1);
}
List<Path> inputPaths = Stream.of(args).map(Paths::get).collect(Collectors.toList());
int androidApiLevel = Integer.parseInt(args[0]);
List<Path> inputPaths = Stream.of(args).skip(1).map(Paths::get).collect(Collectors.toList());
Path output = inputPaths.remove(0);
JadxArgs jadxArgs = new JadxArgs();
@@ -57,6 +59,7 @@ public class ConvertToClsSet {
decompiler.load();
RootNode root = decompiler.getRoot();
ClsSet set = new ClsSet(root);
set.setAndroidApiLevel(androidApiLevel);
set.loadFrom(root);
set.save(output);
@@ -44,14 +44,17 @@ public class ClsSet {
private static final String CLST_PATH = "/clst/" + CLST_FILENAME;
private static final String JADX_CLS_SET_HEADER = "jadx-cst";
private static final int VERSION = 4;
private static final int VERSION = 5;
private static final String STRING_CHARSET = "US-ASCII";
private static final ArgType[] EMPTY_ARGTYPE_ARRAY = new ArgType[0];
private static final ArgType[] OBJECT_ARGTYPE_ARRAY = new ArgType[] { ArgType.OBJECT };
private final RootNode root;
private int androidApiLevel;
public ClsSet(RootNode root) {
this.root = root;
}
@@ -79,7 +82,8 @@ public class ClsSet {
if (LOG.isDebugEnabled()) {
long time = System.currentTimeMillis() - startTime;
int methodsCount = Stream.of(classes).mapToInt(clspClass -> clspClass.getMethodsMap().size()).sum();
LOG.debug("Clst file loaded in {}ms, classes: {}, methods: {}", time, classes.length, methodsCount);
LOG.debug("Clst file loaded in {}ms, android api: {}, classes: {}, methods: {}",
time, androidApiLevel, classes.length, methodsCount);
}
}
@@ -93,7 +97,7 @@ public class ClsSet {
cls.load();
ClspClassSource source = getClspClassSource(cls);
ClspClass nClass = new ClspClass(clsType, k, source);
ClspClass nClass = new ClspClass(clsType, k, cls.getAccessFlags().rawValue(), source);
if (names.put(clsRawName, nClass) != null) {
throw new JadxRuntimeException("Duplicate class: " + clsRawName);
}
@@ -151,7 +155,11 @@ public class ClsSet {
// cls is java.lang.Object
return EMPTY_ARGTYPE_ARRAY;
}
ArgType[] parents = new ArgType[1 + cls.getInterfaces().size()];
int interfacesCount = cls.getInterfaces().size();
if (interfacesCount == 0 && superClass == ArgType.OBJECT) {
return OBJECT_ARGTYPE_ARRAY;
}
ArgType[] parents = new ArgType[1 + interfacesCount];
parents[0] = superClass;
int k = 1;
for (ArgType iface : cls.getInterfaces()) {
@@ -193,10 +201,12 @@ public class ClsSet {
DataOutputStream out = new DataOutputStream(output);
out.writeBytes(JADX_CLS_SET_HEADER);
out.writeByte(VERSION);
out.writeInt(androidApiLevel);
Map<String, ClspClass> names = new HashMap<>(classes.length);
out.writeInt(classes.length);
for (ClspClass cls : classes) {
out.writeInt(cls.getAccFlags());
writeUnsignedByte(out, cls.getSource().ordinal());
String clsName = cls.getName();
writeString(out, clsName);
@@ -243,6 +253,10 @@ public class ClsSet {
out.writeByte(-1);
return;
}
if (arr == OBJECT_ARGTYPE_ARRAY) {
out.writeByte(-2);
return;
}
int size = arr.length;
out.writeByte(size);
if (size != 0) {
@@ -294,22 +308,22 @@ public class ClsSet {
try (DataInputStream in = new DataInputStream(new BufferedInputStream(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) {
|| !JADX_CLS_SET_HEADER.equals(new String(header, STRING_CHARSET))) {
throw new DecodeException("Wrong jadx class set header");
}
int version = in.readByte();
if (version != VERSION) {
throw new DecodeException("Wrong jadx class set version, got: " + version + ", expect: " + VERSION);
}
androidApiLevel = in.readInt();
int clsCount = in.readInt();
classes = new ClspClass[clsCount];
ClspClassSource[] clspClassSources = ClspClassSource.values();
for (int i = 0; i < clsCount; i++) {
int source = readUnsignedByte(in);
if (source < 0 || source > clspClassSources.length) {
throw new DecodeException("Wrong jadx source identifier");
}
int accFlags = in.readInt();
ClspClassSource clsSource = readClsSource(in);
String name = readString(in);
classes[i] = new ClspClass(ArgType.object(name), i, clspClassSources[source]);
classes[i] = new ClspClass(ArgType.object(name), i, accFlags, clsSource);
}
for (int i = 0; i < clsCount; i++) {
ClspClass nClass = classes[i];
@@ -321,6 +335,15 @@ public class ClsSet {
}
}
private static ClspClassSource readClsSource(DataInputStream in) throws IOException, DecodeException {
int source = readUnsignedByte(in);
ClspClassSource[] clspClassSources = ClspClassSource.values();
if (source < 0 || source > clspClassSources.length) {
throw new DecodeException("Wrong jadx source identifier: " + source);
}
return clspClassSources[source];
}
private List<ClspMethod> readClsMethods(DataInputStream in, ClassInfo clsInfo) throws IOException {
int mCount = in.readShort();
List<ClspMethod> methods = new ArrayList<>(mCount);
@@ -366,17 +389,20 @@ public class ClsSet {
@Nullable
private ArgType[] readArgTypesArray(DataInputStream in) throws IOException {
int count = in.readByte();
if (count == -1) {
return null;
switch (count) {
case -1:
return null;
case -2:
return OBJECT_ARGTYPE_ARRAY;
case 0:
return EMPTY_ARGTYPE_ARRAY;
default:
ArgType[] arr = new ArgType[count];
for (int i = 0; i < count; i++) {
arr[i] = readArgType(in);
}
return arr;
}
if (count == 0) {
return EMPTY_ARGTYPE_ARRAY;
}
ArgType[] arr = new ArgType[count];
for (int i = 0; i < count; i++) {
arr[i] = readArgType(in);
}
return arr;
}
private ArgType readArgType(DataInputStream in) throws IOException {
@@ -384,9 +410,6 @@ public class ClsSet {
if (ordinal == -1) {
return null;
}
if (ordinal >= TypeEnum.values().length) {
throw new JadxRuntimeException("Incorrect ordinal for type enum: " + ordinal);
}
switch (TypeEnum.values()[ordinal]) {
case WILDCARD:
ArgType.WildcardBound bound = ArgType.WildcardBound.getByNum(in.readByte());
@@ -414,7 +437,7 @@ public class ClsSet {
return classes[in.readInt()].getClsType();
case ARRAY:
return ArgType.array(readArgType(in));
return ArgType.array(Objects.requireNonNull(readArgType(in)));
case PRIMITIVE:
char shortName = (char) in.readByte();
@@ -474,4 +497,12 @@ public class ClsSet {
nameMap.put(cls.getName(), cls);
}
}
public int getAndroidApiLevel() {
return androidApiLevel;
}
public void setAndroidApiLevel(int androidApiLevel) {
this.androidApiLevel = androidApiLevel;
}
}
@@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import jadx.api.plugins.input.data.AccessFlags;
import jadx.core.dex.instructions.args.ArgType;
/**
@@ -16,21 +17,17 @@ public class ClspClass {
private final ArgType clsType;
private final int id;
private final int accFlags;
private ArgType[] parents;
private Map<String, ClspMethod> methodsMap = Collections.emptyMap();
private List<ArgType> typeParameters = Collections.emptyList();
private ClspClassSource source;
public ClspClass(ArgType clsType, int id) {
this.clsType = clsType;
this.id = id;
this.source = ClspClassSource.APP;
}
public ClspClass(ArgType clsType, int id, ClspClassSource source) {
public ClspClass(ArgType clsType, int id, int accFlags, ClspClassSource source) {
this.clsType = clsType;
this.id = id;
this.accFlags = accFlags;
this.source = source;
}
@@ -46,6 +43,14 @@ public class ClspClass {
return id;
}
public int getAccFlags() {
return accFlags;
}
public boolean isInterface() {
return AccessFlags.hasFlag(accFlags, AccessFlags.INTERFACE);
}
public ArgType[] getParents() {
return parents;
}
@@ -13,6 +13,7 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.Consts;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.ClassNode;
@@ -106,7 +107,7 @@ public class ClspGraph {
private void addClass(ClassNode cls) {
ArgType clsType = cls.getClassInfo().getType();
String rawName = clsType.getObject();
ClspClass clspClass = new ClspClass(clsType, -1);
ClspClass clspClass = new ClspClass(clsType, -1, cls.getAccessFlags().rawValue(), ClspClassSource.APP);
clspClass.setParents(ClsSet.makeParentsArray(cls));
nameMap.put(rawName, clspClass);
}
@@ -174,6 +175,8 @@ public class ClspGraph {
return result == null ? Collections.emptySet() : result;
}
private static final Set<String> OBJECT_SINGLE_SET = Collections.singleton(Consts.CLASS_OBJECT);
private void fillSuperTypesCache() {
Map<String, Set<String>> map = new HashMap<>(nameMap.size());
Set<String> tmpSet = new HashSet<>();
@@ -182,10 +185,25 @@ public class ClspGraph {
tmpSet.clear();
addSuperTypes(cls, tmpSet);
Set<String> result;
if (tmpSet.isEmpty()) {
result = Collections.emptySet();
} else {
result = new HashSet<>(tmpSet);
int size = tmpSet.size();
switch (size) {
case 0: {
result = Collections.emptySet();
break;
}
case 1: {
String supCls = tmpSet.iterator().next();
if (supCls.equals(Consts.CLASS_OBJECT)) {
result = OBJECT_SINGLE_SET;
} else {
result = Collections.singleton(supCls);
}
break;
}
default: {
result = new HashSet<>(tmpSet);
break;
}
}
map.put(cls.getName(), result);
}
Binary file not shown.