forked from openrs2/openrs2
parent
0db3d979c9
commit
b6d96ac4e6
@ -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…
Reference in new issue