feat: add package node, allow to rename packages
This commit is contained in:
@@ -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)
|
||||
+10
-1
@@ -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)
|
||||
|
||||
+3
@@ -23,6 +23,9 @@ object JadxScriptConfiguration : ScriptCompilationConfiguration({
|
||||
wholeClasspath = true
|
||||
)
|
||||
}
|
||||
ide {
|
||||
acceptedLocations(ScriptAcceptedLocation.Everywhere)
|
||||
}
|
||||
|
||||
baseClass(JadxScriptBaseClass::class)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user