Skip to content

Commit

Permalink
fix: store classes access flags in class set
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Feb 7, 2024
1 parent edf6ce2 commit a3a4fab
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 42 deletions.
9 changes: 6 additions & 3 deletions jadx-cli/src/main/java/jadx/cli/clst/ConvertToClsSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,22 @@ 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 "
+ "<sdk_root>/platforms/android-<api level>/optional/org.apache.http.legacy.jar");
}

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();
Expand All @@ -57,6 +59,7 @@ public static void main(String[] args) {
decompiler.load();
RootNode root = decompiler.getRoot();
ClsSet set = new ClsSet(root);
set.setAndroidApiLevel(androidApiLevel);
set.loadFrom(root);
set.save(output);

Expand Down
85 changes: 58 additions & 27 deletions jadx-core/src/main/java/jadx/core/clsp/ClsSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -79,7 +82,8 @@ public void loadFromClstFile() throws IOException, DecodeException {
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);
}
}

Expand All @@ -93,7 +97,7 @@ public void loadFrom(RootNode root) {
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);
}
Expand Down Expand Up @@ -151,7 +155,11 @@ public static ArgType[] makeParentsArray(ClassNode cls) {
// 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()) {
Expand Down Expand Up @@ -193,10 +201,12 @@ private void save(OutputStream output) throws IOException {
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);
Expand Down Expand Up @@ -243,6 +253,10 @@ private static void writeArgTypesArray(DataOutputStream out, @Nullable ArgType[]
out.writeByte(-1);
return;
}
if (arr == OBJECT_ARGTYPE_ARRAY) {
out.writeByte(-2);
return;
}
int size = arr.length;
out.writeByte(size);
if (size != 0) {
Expand Down Expand Up @@ -294,22 +308,22 @@ private void load(InputStream input) throws IOException, DecodeException {
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];
Expand All @@ -321,6 +335,15 @@ private void load(InputStream input) throws IOException, DecodeException {
}
}

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);
Expand Down Expand Up @@ -366,27 +389,27 @@ private List<ArgType> readArgTypesList(DataInputStream in) throws IOException {
@Nullable
private ArgType[] readArgTypesArray(DataInputStream in) throws IOException {
int count = in.readByte();
if (count == -1) {
return null;
}
if (count == 0) {
return EMPTY_ARGTYPE_ARRAY;
}
ArgType[] arr = new ArgType[count];
for (int i = 0; i < count; i++) {
arr[i] = readArgType(in);
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;
}
return arr;
}

private ArgType readArgType(DataInputStream in) throws IOException {
int ordinal = in.readByte();
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());
Expand Down Expand Up @@ -414,7 +437,7 @@ private ArgType readArgType(DataInputStream in) throws IOException {
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();
Expand Down Expand Up @@ -474,4 +497,12 @@ public void addToMap(Map<String, ClspClass> nameMap) {
nameMap.put(cls.getName(), cls);
}
}

public int getAndroidApiLevel() {
return androidApiLevel;
}

public void setAndroidApiLevel(int androidApiLevel) {
this.androidApiLevel = androidApiLevel;
}
}
19 changes: 12 additions & 7 deletions jadx-core/src/main/java/jadx/core/clsp/ClspClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.Map;
import java.util.Objects;

import jadx.api.plugins.input.data.AccessFlags;
import jadx.core.dex.instructions.args.ArgType;

/**
Expand All @@ -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;
}

Expand All @@ -46,6 +43,14 @@ public int getId() {
return id;
}

public int getAccFlags() {
return accFlags;
}

public boolean isInterface() {
return AccessFlags.hasFlag(accFlags, AccessFlags.INTERFACE);
}

public ArgType[] getParents() {
return parents;
}
Expand Down
28 changes: 23 additions & 5 deletions jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
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;
Expand Down Expand Up @@ -106,7 +107,7 @@ private ClspMethod getMethodFromClass(ClspClass cls, MethodInfo methodInfo) {
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);
}
Expand Down Expand Up @@ -174,6 +175,8 @@ public Set<String> getSuperTypes(String clsName) {
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<>();
Expand All @@ -182,10 +185,25 @@ private void fillSuperTypesCache() {
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);
}
Expand Down
Binary file modified jadx-core/src/main/resources/clst/core.jcst
Binary file not shown.

0 comments on commit a3a4fab

Please sign in to comment.