Convert TypedRemapper to Kotlin

pull/48/head
Graham 4 years ago
parent 0db3d979c9
commit b6d96ac4e6
  1. 241
      deob/src/main/java/dev/openrs2/deob/remap/TypedRemapper.java
  2. 229
      deob/src/main/java/dev/openrs2/deob/remap/TypedRemapper.kt

@ -1,241 +0,0 @@
package dev.openrs2.deob.remap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import dev.openrs2.asm.MemberDesc;
import dev.openrs2.asm.MemberRef;
import dev.openrs2.asm.classpath.ClassMetadata;
import dev.openrs2.asm.classpath.ClassPath;
import dev.openrs2.common.StringUtilsKt;
import dev.openrs2.common.collect.DisjointSet;
import kotlin.text.StringsKt;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Remapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class TypedRemapper extends Remapper {
private static final Logger logger = LoggerFactory.getLogger(TypedRemapper.class);
public static final ImmutableSet<String> EXCLUDED_CLASSES = ImmutableSet.of(
"client",
"jagex3/jagmisc/jagmisc",
"loader",
"unpack",
"unpackclass"
);
private static final ImmutableSet<String> EXCLUDED_METHODS = ImmutableSet.of(
"<clinit>",
"<init>",
"main",
"providesignlink",
"quit"
);
private static final ImmutableSet<String> EXCLUDED_FIELDS = ImmutableSet.of(
"cache"
);
private static final int MAX_OBFUSCATED_NAME_LEN = 2;
public static TypedRemapper create(ClassPath classPath) {
var inheritedFieldSets = classPath.createInheritedFieldSets();
var inheritedMethodSets = classPath.createInheritedMethodSets();
var classes = createClassMapping(classPath.getLibraryClasses());
var fields = createFieldMapping(classPath, inheritedFieldSets, classes);
var methods = createMethodMapping(classPath, inheritedMethodSets);
verifyMapping(classes);
verifyMemberMapping(fields);
verifyMemberMapping(methods);
return new TypedRemapper(classes, fields, methods);
}
private static void verifyMapping(Map<String, String> mapping) {
for (var entry : mapping.entrySet()) {
verifyMapping(entry.getKey(), entry.getValue());
}
}
private static void verifyMemberMapping(Map<MemberRef, String> mapping) {
for (var entry : mapping.entrySet()) {
verifyMapping(entry.getKey().getName(), entry.getValue());
}
}
private static void verifyMapping(String name, String mappedName) {
name = name.replaceAll("^(?:loader|unpacker)_", "");
if (name.length() > MAX_OBFUSCATED_NAME_LEN && !name.equals(mappedName)) {
logger.warn("Remapping probably unobfuscated name {} to {}", name, mappedName);
}
}
private static String generateName(Map<String, Integer> prefixes, String prefix) {
return prefix + prefixes.merge(prefix, 1, Integer::sum);
}
private static Map<String, String> createClassMapping(List<ClassMetadata> classes) {
var mapping = new HashMap<String, String>();
var prefixes = new HashMap<String, Integer>();
for (var clazz : classes) {
populateClassMapping(mapping, prefixes, clazz);
}
return mapping;
}
private static String populateClassMapping(Map<String, String> mapping, Map<String, Integer> prefixes, ClassMetadata clazz) {
var name = clazz.getName();
if (mapping.containsKey(name) || EXCLUDED_CLASSES.contains(name) || clazz.getDependency()) {
return mapping.getOrDefault(name, name);
}
var mappedName = name.substring(0, name.lastIndexOf('/') + 1);
var superClass = clazz.getSuperClass();
if (superClass != null && !superClass.getName().equals("java/lang/Object")) {
var superName = populateClassMapping(mapping, prefixes, superClass);
superName = superName.substring(superName.lastIndexOf('/') + 1);
mappedName += generateName(prefixes, superName + "_Sub");
} else if (clazz.getInterface()) {
mappedName += generateName(prefixes, "Interface");
} else {
mappedName += generateName(prefixes, "Class");
}
mapping.put(name, mappedName);
return mappedName;
}
private static Map<MemberRef, String> createFieldMapping(ClassPath classPath, DisjointSet<MemberRef> disjointSet, Map<String, String> classMapping) {
var mapping = new HashMap<MemberRef, String>();
var prefixes = new HashMap<String, Integer>();
for (var partition : disjointSet) {
boolean skip = false;
for (var field : partition) {
var clazz = classPath.get(field.getOwner());
if (EXCLUDED_FIELDS.contains(field.getName())) {
skip = true;
break;
}
if (clazz.getDependency()) {
skip = true;
break;
}
}
if (skip) {
continue;
}
var prefix = "";
var type = Type.getType(partition.iterator().next().getDesc());
if (type.getSort() == Type.ARRAY) {
prefix = Strings.repeat("Array", type.getDimensions());
type = type.getElementType();
}
switch (type.getSort()) {
case Type.BOOLEAN:
case Type.BYTE:
case Type.CHAR:
case Type.SHORT:
case Type.INT:
case Type.LONG:
case Type.FLOAT:
case Type.DOUBLE:
prefix = type.getClassName() + prefix;
break;
case Type.OBJECT:
var className = classMapping.getOrDefault(type.getInternalName(), type.getInternalName());
className = className.substring(className.lastIndexOf('/') + 1);
prefix = className + prefix;
break;
default:
throw new IllegalArgumentException("Unknown field type " + type);
}
prefix = StringUtilsKt.indefiniteArticle(prefix) + StringsKt.capitalize(prefix);
var mappedName = generateName(prefixes, prefix);
for (var field : partition) {
mapping.put(field, mappedName);
}
}
return mapping;
}
public static boolean isMethodImmutable(ClassPath classPath, DisjointSet.Partition<MemberRef> partition) {
for (var method : partition) {
var clazz = classPath.get(method.getOwner());
if (EXCLUDED_METHODS.contains(method.getName())) {
return true;
}
if (clazz.getDependency()) {
return true;
}
if (clazz.isNative(new MemberDesc(method))) {
return true;
}
}
return false;
}
private static Map<MemberRef, String> createMethodMapping(ClassPath classPath, DisjointSet<MemberRef> disjointSet) {
var mapping = new HashMap<MemberRef, String>();
var id = 0;
for (var partition : disjointSet) {
if (isMethodImmutable(classPath, partition)) {
continue;
}
var mappedName = "method" + (++id);
for (var method : partition) {
mapping.put(method, mappedName);
}
}
return mapping;
}
private final Map<String, String> classes;
private final Map<MemberRef, String> fields, methods;
private TypedRemapper(Map<String, String> classes, Map<MemberRef, String> fields, Map<MemberRef, String> methods) {
this.classes = classes;
this.fields = fields;
this.methods = methods;
}
@Override
public String map(String internalName) {
return classes.getOrDefault(internalName, internalName);
}
@Override
public String mapFieldName(String owner, String name, String descriptor) {
return fields.getOrDefault(new MemberRef(owner, name, descriptor), name);
}
@Override
public String mapMethodName(String owner, String name, String descriptor) {
return methods.getOrDefault(new MemberRef(owner, name, descriptor), name);
}
}

@ -0,0 +1,229 @@
package dev.openrs2.deob.remap
import com.github.michaelbull.logging.InlineLogger
import com.google.common.base.Strings
import dev.openrs2.asm.MemberDesc
import dev.openrs2.asm.MemberRef
import dev.openrs2.asm.classpath.ClassMetadata
import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.common.collect.DisjointSet
import dev.openrs2.common.indefiniteArticle
import org.objectweb.asm.Type
import org.objectweb.asm.commons.Remapper
class TypedRemapper private constructor(
private val classes: Map<String, String>,
private val fields: Map<MemberRef, String>,
private val methods: Map<MemberRef, String>
) : Remapper() {
override fun map(internalName: String): String {
return classes.getOrDefault(internalName, internalName)
}
override fun mapFieldName(owner: String, name: String, descriptor: String): String {
return fields.getOrDefault(MemberRef(owner, name, descriptor), name)
}
override fun mapMethodName(owner: String, name: String, descriptor: String): String {
return methods.getOrDefault(MemberRef(owner, name, descriptor), name)
}
companion object {
private val logger = InlineLogger()
val EXCLUDED_CLASSES = setOf(
"client",
"jagex3/jagmisc/jagmisc",
"loader",
"unpack",
"unpackclass"
)
private val EXCLUDED_METHODS = setOf(
"<clinit>",
"<init>",
"main",
"providesignlink",
"quit"
)
private val EXCLUDED_FIELDS = setOf(
"cache"
)
private const val MAX_OBFUSCATED_NAME_LEN = 2
fun create(classPath: ClassPath): TypedRemapper {
val inheritedFieldSets = classPath.createInheritedFieldSets()
val inheritedMethodSets = classPath.createInheritedMethodSets()
val classes = createClassMapping(classPath.libraryClasses)
val fields = createFieldMapping(classPath, inheritedFieldSets, classes)
val methods = createMethodMapping(classPath, inheritedMethodSets)
verifyMapping(classes)
verifyMemberMapping(fields)
verifyMemberMapping(methods)
return TypedRemapper(classes, fields, methods)
}
private fun verifyMapping(mapping: Map<String, String>) {
for ((key, value) in mapping) {
verifyMapping(key, value)
}
}
private fun verifyMemberMapping(mapping: Map<MemberRef, String>) {
for ((key, value) in mapping) {
verifyMapping(key.name, value)
}
}
private fun verifyMapping(name: String, mappedName: String) {
val originalName = name.replace("^(?:loader|unpacker)_".toRegex(), "")
if (originalName.length > MAX_OBFUSCATED_NAME_LEN && originalName != mappedName) {
logger.warn { "Remapping probably unobfuscated name $originalName to $mappedName" }
}
}
private fun generateName(prefixes: MutableMap<String, Int>, prefix: String): String {
return prefix + prefixes.merge(prefix, 1, Integer::sum)
}
private fun createClassMapping(classes: List<ClassMetadata>): Map<String, String> {
val mapping = mutableMapOf<String, String>()
val prefixes = mutableMapOf<String, Int>()
for (clazz in classes) {
populateClassMapping(mapping, prefixes, clazz)
}
return mapping
}
private fun populateClassMapping(
mapping: MutableMap<String, String>,
prefixes: MutableMap<String, Int>,
clazz: ClassMetadata
): String {
val name = clazz.name
if (mapping.containsKey(name) || EXCLUDED_CLASSES.contains(name) || clazz.dependency) {
return mapping.getOrDefault(name, name)
}
var mappedName = name.substring(0, name.lastIndexOf('/') + 1)
val superClass = clazz.superClass
if (superClass != null && superClass.name != "java/lang/Object") {
var superName = populateClassMapping(mapping, prefixes, superClass)
superName = superName.substring(superName.lastIndexOf('/') + 1)
mappedName += generateName(prefixes, superName + "_Sub")
} else if (clazz.`interface`) {
mappedName += generateName(prefixes, "Interface")
} else {
mappedName += generateName(prefixes, "Class")
}
mapping[name] = mappedName
return mappedName
}
private fun createFieldMapping(
classPath: ClassPath,
disjointSet: DisjointSet<MemberRef>,
classMapping: Map<String, String>
): Map<MemberRef, String> {
val mapping = mutableMapOf<MemberRef, String>()
val prefixes = mutableMapOf<String, Int>()
for (partition in disjointSet) {
var skip = false
for ((owner, name) in partition) {
val clazz = classPath[owner]
if (EXCLUDED_FIELDS.contains(name)) {
skip = true
break
}
if (clazz.dependency) {
skip = true
break
}
}
if (skip) {
continue
}
var prefix = ""
var type = Type.getType(partition.iterator().next().desc)
if (type.sort == Type.ARRAY) {
prefix = Strings.repeat("Array", type.dimensions)
type = type.elementType
}
when (type.sort) {
Type.BOOLEAN, Type.BYTE, Type.CHAR, Type.SHORT, Type.INT, Type.LONG, Type.FLOAT, Type.DOUBLE -> {
prefix = type.className + prefix
}
Type.OBJECT -> {
var className = classMapping.getOrDefault(type.internalName, type.internalName)
className = className.substring(className.lastIndexOf('/') + 1)
prefix = className + prefix
}
else -> throw IllegalArgumentException("Unknown field type $type")
}
prefix = prefix.indefiniteArticle() + prefix.capitalize()
val mappedName = generateName(prefixes, prefix)
for (field in partition) {
mapping[field] = mappedName
}
}
return mapping
}
fun isMethodImmutable(classPath: ClassPath, partition: DisjointSet.Partition<MemberRef>): Boolean {
for (method in partition) {
val clazz = classPath[method.owner]
if (EXCLUDED_METHODS.contains(method.name)) {
return true
}
if (clazz.dependency) {
return true
}
if (clazz.isNative(MemberDesc(method))) {
return true
}
}
return false
}
private fun createMethodMapping(
classPath: ClassPath,
disjointSet: DisjointSet<MemberRef>
): Map<MemberRef, String> {
val mapping = mutableMapOf<MemberRef, String>()
var id = 0
for (partition in disjointSet) {
if (isMethodImmutable(classPath, partition)) {
continue
}
val mappedName = "method" + ++id
for (method in partition) {
mapping[method] = mappedName
}
}
return mapping
}
}
}
Loading…
Cancel
Save