feat: add package node, allow to rename packages

This commit is contained in:
Skylot
2022-07-25 12:49:46 +01:00
parent 278d7fa3f9
commit bc7300bd01
12 changed files with 353 additions and 7 deletions
@@ -399,7 +399,7 @@ public class ClassGen {
annotationGen.addForField(code, f);
boolean addInfoComments = f.checkCommentsLevel(CommentsLevel.INFO);
if (f.getFieldInfo().isRenamed() && addInfoComments) {
if (f.getFieldInfo().hasAlias() && addInfoComments) {
code.newLine();
CodeGenUtils.addRenamedComment(code, f, f.getName());
}
@@ -72,10 +72,6 @@ public final class FieldInfo {
return declClass.makeRawFullName() + '.' + name + ':' + TypeGen.signature(type);
}
public boolean isRenamed() {
return !name.equals(alias);
}
public boolean equalsNameAndType(FieldInfo other) {
return name.equals(other.name) && type.equals(other.type);
}
@@ -3,6 +3,8 @@ package jadx.core.dex.info;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.instructions.args.ArgType;
public class InfoStorage {
@@ -14,6 +16,8 @@ public class InfoStorage {
// can contain same method with different ids (from different files)
private final Map<Integer, MethodInfo> methods = new HashMap<>();
private final Map<String, PackageInfo> packages = new HashMap<>();
public ClassInfo getCls(ArgType type) {
return classes.get(type);
}
@@ -58,4 +62,12 @@ public class InfoStorage {
return field;
}
}
public @Nullable PackageInfo getPkg(String fullName) {
return packages.get(fullName);
}
public void putPkg(PackageInfo pkg) {
packages.put(pkg.getFullName(), pkg);
}
}
@@ -0,0 +1,90 @@
package jadx.core.dex.info;
import java.util.Objects;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.nodes.RootNode;
public class PackageInfo {
private final @Nullable PackageInfo parentPkg;
private final String fullName;
private final String name;
public static synchronized PackageInfo fromFullPkg(RootNode root, String fullPkg) {
PackageInfo existPkg = root.getInfoStorage().getPkg(fullPkg);
if (existPkg != null) {
return existPkg;
}
PackageInfo newPkg;
int lastDot = fullPkg.lastIndexOf('.');
if (lastDot == -1) {
// unknown root pkg
newPkg = new PackageInfo(fullPkg, null, fullPkg);
} else {
PackageInfo parentPkg = fromFullPkg(root, fullPkg.substring(0, lastDot));
newPkg = new PackageInfo(fullPkg, parentPkg, fullPkg.substring(lastDot + 1));
}
root.getInfoStorage().putPkg(newPkg);
return newPkg;
}
public static synchronized PackageInfo fromShortName(RootNode root, @Nullable PackageInfo parent, String shortName) {
String fullPkg = parent == null ? shortName : parent.getFullName() + '.' + shortName;
PackageInfo existPkg = root.getInfoStorage().getPkg(fullPkg);
if (existPkg != null) {
return existPkg;
}
PackageInfo newPkg = new PackageInfo(fullPkg, parent, shortName);
root.getInfoStorage().putPkg(newPkg);
return newPkg;
}
private PackageInfo(String fullName, @Nullable PackageInfo parentPkg, String name) {
this.fullName = fullName;
this.parentPkg = parentPkg;
this.name = name;
}
public boolean isRoot() {
return parentPkg == null;
}
public boolean isDefaultPkg() {
return fullName.isEmpty();
}
public String getFullName() {
return fullName;
}
public @Nullable PackageInfo getParentPkg() {
return parentPkg;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PackageInfo)) {
return false;
}
return Objects.equals(fullName, ((PackageInfo) o).getFullName());
}
@Override
public int hashCode() {
return fullName.hashCode();
}
@Override
public String toString() {
return fullName;
}
}
@@ -54,11 +54,13 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
import static jadx.core.dex.nodes.ProcessState.LOADED;
import static jadx.core.dex.nodes.ProcessState.NOT_LOADED;
public class ClassNode extends NotificationAttrNode implements IClassNode, ILoadable, ICodeNode, Comparable<ClassNode> {
public class ClassNode extends NotificationAttrNode
implements IClassNode, ILoadable, ICodeNode, IPackageUpdate, Comparable<ClassNode> {
private final RootNode root;
private final IClassData clsData;
private final ClassInfo clsInfo;
private final PackageNode packageNode;
private AccessInfo accessFlags;
private ArgType superClass;
private List<ArgType> interfaces;
@@ -103,6 +105,7 @@ public class ClassNode extends NotificationAttrNode implements IClassNode, ILoad
public ClassNode(RootNode root, IClassData cls) {
this.root = root;
this.clsInfo = ClassInfo.fromType(root, ArgType.object(cls.getType()));
this.packageNode = PackageNode.getForClass(root, clsInfo.getPackage(), this);
this.clsData = cls.copy();
initialLoad(clsData);
}
@@ -236,6 +239,7 @@ public class ClassNode extends NotificationAttrNode implements IClassNode, ILoad
this.fields = new ArrayList<>();
this.accessFlags = new AccessInfo(accessFlags, AFType.CLASS);
this.parentClass = this;
this.packageNode = PackageNode.getForClass(root, clsInfo.getPackage(), this);
}
private void initStaticValues(List<FieldNode> fields) {
@@ -568,6 +572,18 @@ public class ClassNode extends NotificationAttrNode implements IClassNode, ILoad
parentClass = this;
}
@Override
public void onParentPackageUpdate(PackageNode updatedPkg) {
if (isInner()) {
return;
}
getClassInfo().changePkg(packageNode.getAliasPkgInfo().getFullName());
}
public PackageNode getPackageNode() {
return packageNode;
}
public ClassNode getTopParentClass() {
ClassNode parent = getParentClass();
return parent == this ? this : parent.getTopParentClass();
@@ -0,0 +1,6 @@
package jadx.core.dex.nodes;
public interface IPackageUpdate {
void onParentPackageUpdate(PackageNode updatedPkg);
}
@@ -0,0 +1,150 @@
package jadx.core.dex.nodes;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.info.PackageInfo;
public class PackageNode implements IPackageUpdate, IDexNode, Comparable<PackageNode> {
private final RootNode root;
private final PackageInfo pkgInfo;
private final @Nullable PackageNode parentPkg;
private final List<PackageNode> subPackages = new ArrayList<>();
private final List<ClassNode> classes = new ArrayList<>();
private PackageInfo aliasPkgInfo;
public static PackageNode getForClass(RootNode root, String fullPkg, ClassNode cls) {
PackageNode pkg = getOrBuild(root, fullPkg);
pkg.getClasses().add(cls);
return pkg;
}
public static PackageNode getOrBuild(RootNode root, String fullPkg) {
PackageNode existPkg = root.resolvePackage(fullPkg);
if (existPkg != null) {
return existPkg;
}
PackageInfo pgkInfo = PackageInfo.fromFullPkg(root, fullPkg);
PackageNode parentPkg = getParentPkg(root, pgkInfo);
PackageNode pkgNode = new PackageNode(root, parentPkg, pgkInfo);
if (parentPkg != null) {
parentPkg.getSubPackages().add(pkgNode);
}
root.addPackage(pkgNode);
return pkgNode;
}
private static @Nullable PackageNode getParentPkg(RootNode root, PackageInfo pgkInfo) {
return root.resolvePackage(pgkInfo.getParentPkg());
}
private PackageNode(RootNode root, @Nullable PackageNode parentPkg, PackageInfo pkgInfo) {
this.root = root;
this.parentPkg = parentPkg;
this.pkgInfo = pkgInfo;
}
public void rename(String alias) {
rename(alias, true);
}
public void rename(String alias, boolean runUpdates) {
if (pkgInfo.getName().equals(alias)) {
aliasPkgInfo = pkgInfo;
return;
}
aliasPkgInfo = PackageInfo.fromShortName(root, getParentAliasPkgInfo(), alias);
if (runUpdates) {
updatePackages(this);
}
}
@Override
public void onParentPackageUpdate(PackageNode updatedPkg) {
aliasPkgInfo = PackageInfo.fromShortName(root, getParentAliasPkgInfo(), aliasPkgInfo.getName());
updatePackages(updatedPkg);
}
public void updatePackages() {
updatePackages(this);
}
private void updatePackages(PackageNode updatedPkg) {
for (PackageNode subPackage : subPackages) {
subPackage.onParentPackageUpdate(updatedPkg);
}
for (ClassNode cls : classes) {
cls.onParentPackageUpdate(updatedPkg);
}
}
public PackageInfo getPkgInfo() {
return pkgInfo;
}
public PackageInfo getAliasPkgInfo() {
return aliasPkgInfo;
}
public PackageNode getParentPkg() {
return parentPkg;
}
public @Nullable PackageInfo getParentAliasPkgInfo() {
return parentPkg == null ? null : parentPkg.aliasPkgInfo;
}
public List<PackageNode> getSubPackages() {
return subPackages;
}
public List<ClassNode> getClasses() {
return classes;
}
@Override
public String typeName() {
return "package";
}
@Override
public RootNode root() {
return root;
}
@Override
public String getInputFileName() {
return "";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PackageNode)) {
return false;
}
return pkgInfo.equals(((PackageNode) o).pkgInfo);
}
@Override
public int hashCode() {
return pkgInfo.hashCode();
}
@Override
public int compareTo(@NotNull PackageNode other) {
return getPkgInfo().getFullName().compareTo(other.getPkgInfo().getFullName());
}
@Override
public String toString() {
return getPkgInfo().getFullName();
}
}
@@ -1,6 +1,7 @@
package jadx.core.dex.nodes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -37,6 +38,7 @@ import jadx.core.dex.info.ConstStorage;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.InfoStorage;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.info.PackageInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.utils.MethodUtils;
import jadx.core.dex.nodes.utils.TypeUtils;
@@ -78,6 +80,8 @@ public class RootNode implements IRootNode {
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<>();
private List<ClassNode> classes = new ArrayList<>();
private final Map<String, PackageNode> pkgMap = new HashMap<>();
private ClspGraph clsp;
@Nullable
private String appPackage;
@@ -345,6 +349,24 @@ public class RootNode implements IRootNode {
return notInnerClasses;
}
public List<PackageNode> getPackages() {
List<PackageNode> list = new ArrayList<>(pkgMap.values());
Collections.sort(list);
return list;
}
public @Nullable PackageNode resolvePackage(String fullPkg) {
return pkgMap.get(fullPkg);
}
public @Nullable PackageNode resolvePackage(@Nullable PackageInfo pkgInfo) {
return pkgInfo == null ? null : pkgMap.get(pkgInfo.getFullName());
}
public void addPackage(PackageNode pkg) {
pkgMap.put(pkg.getPkgInfo().getFullName(), pkg);
}
@Nullable
public ClassNode resolveClass(ClassInfo clsInfo) {
return clsMap.get(clsInfo);
@@ -9,6 +9,9 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-script-runtime")
implementation("io.github.microutils:kotlin-logging-jvm:3.0.2")
// manual imports ( IDE can't import dependencies by scripts annotations)
implementation("com.github.javafaker:javafaker:1.0.2")
}
sourceSets {
@@ -0,0 +1,39 @@
// animal deobfuscator ^_^
@file:DependsOn("com.github.javafaker:javafaker:1.0.2")
import com.github.javafaker.Faker
import jadx.core.deobf.NameMapper
import java.util.Random
val jadx = getJadxInstance()
jadx.args.isDeobfuscationOn = false
jadx.args.renameFlags = emptySet()
val regex = """[Oo0]+""".toRegex()
val usedNames = mutableSetOf<String>()
val faker = Faker(Random(1))
var dups = 1
jadx.rename.all { name, node ->
when {
name matches regex -> {
val prefix = node.typeName().first()
val alias = faker.name().firstName().cap() + faker.animal().name().cap()
makeUnique(prefix, alias)
}
else -> null
}
}
fun makeUnique(prefix: Char, name: String): String {
while (true) {
val resName = prefix + NameMapper.removeInvalidCharsMiddle(name)
return if (usedNames.add(resName)) resName else "$resName${dups++}"
}
}
jadx.afterLoad {
println("Renames count: ${usedNames.size + dups}, names: ${usedNames.size}, dups: $dups")
}
fun String.cap() = this.replaceFirstChar(Char::uppercaseChar)
@@ -12,9 +12,18 @@ class RenamePass(private val jadx: JadxScriptInstance) {
}
fun all(makeNewName: (String, IDexNode) -> String?) {
jadx.addPass(object : ScriptPreparePass(jadx, "RenameAll") {
jadx.addPass(object : ScriptOrderedPreparePass(
jadx,
"RenameAll",
runBefore = listOf("RenameVisitor")
) {
override fun init(root: IRootNode) {
val rootNode = root as RootNode
for (pkgNode in rootNode.packages) {
makeNewName.invoke(pkgNode.pkgInfo.name, pkgNode)?.let {
pkgNode.rename(it)
}
}
for (cls in rootNode.classes) {
makeNewName.invoke(cls.classInfo.shortName, cls)?.let {
cls.classInfo.changeShortName(it)
@@ -23,6 +23,9 @@ object JadxScriptConfiguration : ScriptCompilationConfiguration({
wholeClasspath = true
)
}
ide {
acceptedLocations(ScriptAcceptedLocation.Everywhere)
}
baseClass(JadxScriptBaseClass::class)