aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc
diff options
context:
space:
mode:
author zaaarf <zaaarf@proton.me>2023-06-11 14:48:24 +0200
committer zaaarf <zaaarf@proton.me>2023-06-11 14:48:24 +0200
commit7c427316a675cbe7e81a04294781c59f2606239d (patch)
tree3a4efc720f44205db6c518ee854f03eebf215e7c /src/main/java/ftbsc
feat: initial implementation, created interface and moved stuff from processor
Diffstat (limited to 'src/main/java/ftbsc')
-rw-r--r--src/main/java/ftbsc/lll/exceptions/AmbiguousMappingException.java27
-rw-r--r--src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java27
-rw-r--r--src/main/java/ftbsc/lll/mapper/IMapper.java56
-rw-r--r--src/main/java/ftbsc/lll/mapper/impl/TSRGMapper.java72
-rw-r--r--src/main/java/ftbsc/lll/mapper/tools/ClassData.java100
-rw-r--r--src/main/java/ftbsc/lll/mapper/tools/MappingUtils.java56
6 files changed, 338 insertions, 0 deletions
diff --git a/src/main/java/ftbsc/lll/exceptions/AmbiguousMappingException.java b/src/main/java/ftbsc/lll/exceptions/AmbiguousMappingException.java
new file mode 100644
index 0000000..794759a
--- /dev/null
+++ b/src/main/java/ftbsc/lll/exceptions/AmbiguousMappingException.java
@@ -0,0 +1,27 @@
+package ftbsc.lll.exceptions;
+
+import ftbsc.lll.mapper.IMapper;
+
+/**
+ * Thrown when a given {@link IMapper} cannot uniquely identify a mapping with
+ * the given data.
+ */
+public class AmbiguousMappingException extends RuntimeException {
+
+ /**
+ * Constructs a new ambiguous mapping definition exception with the specified detail message.
+ * @param message the detail message
+ */
+ public AmbiguousMappingException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new ambiguous definition exception with the specified detail message and cause.
+ * @param message the detail message
+ * @param cause the cause, may be null (indicating nonexistent or unknown cause)
+ */
+ public AmbiguousMappingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java
new file mode 100644
index 0000000..6c286fc
--- /dev/null
+++ b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java
@@ -0,0 +1,27 @@
+package ftbsc.lll.exceptions;
+
+import ftbsc.lll.mapper.IMapper;
+
+/**
+ * Thrown upon failure to find the requested mapping within a loaded {@link IMapper}.
+ */
+public class MappingNotFoundException extends RuntimeException {
+
+ /**
+ * Constructs a new mapping not found exception for the specified mapping.
+ * @param mapping the relevant mapping
+ */
+ public MappingNotFoundException(String mapping) {
+ super(String.format("Could not find mapping for %s!", mapping));
+ }
+
+ /**
+ * Constructs a new mapping not found exception for the specified mapping
+ * with the specified reason.
+ * @param mapping the relevant mapping
+ * @param reason the reason message
+ */
+ public MappingNotFoundException(String mapping, String reason) {
+ this(mapping + ": " + reason);
+ }
+}
diff --git a/src/main/java/ftbsc/lll/mapper/IMapper.java b/src/main/java/ftbsc/lll/mapper/IMapper.java
new file mode 100644
index 0000000..8adf860
--- /dev/null
+++ b/src/main/java/ftbsc/lll/mapper/IMapper.java
@@ -0,0 +1,56 @@
+package ftbsc.lll.mapper;
+
+import ftbsc.lll.exceptions.MappingNotFoundException;
+
+import java.util.HashSet;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+/**
+ * A generic obfuscation mapper.
+ */
+public interface IMapper {
+
+ /**
+ * Reads the given lines of text and attempts to interpret them as
+ * mappings of the given type.
+ * @param lines the lines to read
+ */
+ void populate(Iterable<String> lines);
+
+ /**
+ * Gets the obfuscated name of the class.
+ * @param name the unobfuscated internal name of the desired class
+ * @return the obfuscated name of the class
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ String obfuscateClass(String name);
+
+ /**
+ * Gets the obfuscated name of a class member (field or method).
+ * @param parentName the unobfuscated internal name of the parent class
+ * @param memberName the field name or method signature
+ * @param methodDescriptor the descriptor of the member
+ * @return the obfuscated name of the given member
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ String obfuscateMember(String parentName, String memberName, String methodDescriptor);
+
+ /**
+ * Loads all valid parsers available in the classpath (via the Java Service API),
+ * attempts to parse the given lines into mappings, and returns all built mappers
+ * that succeeded without throwing errors or ftbsc.lll.exceptions.
+ * @param lines the lines of the mapping file
+ * @return a {@link Set} of mappers that could interpret the given input
+ */
+ static Set<IMapper> getMappers(Iterable<String> lines) {
+ Set<IMapper> parsed = new HashSet<>();
+ for(IMapper mapper: ServiceLoader.load(IMapper.class)) {
+ try {
+ mapper.populate(lines);
+ parsed.add(mapper);
+ } catch(Throwable ignored) {}
+ }
+ return parsed;
+ }
+}
diff --git a/src/main/java/ftbsc/lll/mapper/impl/TSRGMapper.java b/src/main/java/ftbsc/lll/mapper/impl/TSRGMapper.java
new file mode 100644
index 0000000..0fc6489
--- /dev/null
+++ b/src/main/java/ftbsc/lll/mapper/impl/TSRGMapper.java
@@ -0,0 +1,72 @@
+package ftbsc.lll.mapper.impl;
+
+import ftbsc.lll.exceptions.MappingNotFoundException;
+import ftbsc.lll.mapper.IMapper;
+import ftbsc.lll.mapper.tools.ClassData;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Parses a .tsrg file into a mapper capable of converting from
+ * plain names to obfuscated ones and vice versa.
+ */
+public class TSRGMapper implements IMapper {
+
+ /**
+ * A Map containing the deobfuscated names as keys and information about
+ * each class as values.
+ */
+ private final Map<String, ClassData> mappings = new HashMap<>();
+
+ /**
+ * Reads the given lines of text and attempts to interpret them as
+ * mappings of the given type.
+ * @param lines the lines to read
+ */
+ @Override
+ public void populate(Iterable<String> lines) {
+ String currentClass = "";
+ for(String l : lines) {
+ if(l == null) continue;
+ if(l.startsWith("\t"))
+ mappings.get(currentClass).addMember(l);
+ else {
+ String[] sp = l.split(" ");
+ ClassData s = new ClassData(sp[0], sp[1]);
+ currentClass = s.unobf;
+ mappings.put(s.unobf, s);
+ }
+ }
+ }
+
+ /**
+ * Gets the obfuscated name of the class.
+ * @param name the unobfuscated internal name of the desired class
+ * @return the obfuscated name of the class
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ @Override
+ public String obfuscateClass(String name) {
+ ClassData data = mappings.get(name.replace('.', '/'));
+ if(data == null)
+ throw new MappingNotFoundException(name);
+ else return data.obf;
+ }
+
+ /**
+ * Gets the obfuscated name of a class member (field or method).
+ * @param parentName the unobfuscated internal name of the parent class
+ * @param memberName the field name or method signature
+ * @param methodDescriptor the optional descriptor of the member, may be null or partial
+ * @return the obfuscated name of the given member
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ @Override
+ public String obfuscateMember(String parentName, String memberName, String methodDescriptor) {
+ ClassData data = mappings.get(parentName.replace('.', '/'));
+ if(data == null)
+ throw new MappingNotFoundException(parentName + "::" + memberName);
+ return data.get(memberName, methodDescriptor);
+ }
+}
diff --git a/src/main/java/ftbsc/lll/mapper/tools/ClassData.java b/src/main/java/ftbsc/lll/mapper/tools/ClassData.java
new file mode 100644
index 0000000..a9fbcec
--- /dev/null
+++ b/src/main/java/ftbsc/lll/mapper/tools/ClassData.java
@@ -0,0 +1,100 @@
+package ftbsc.lll.mapper.tools;
+
+import ftbsc.lll.exceptions.AmbiguousMappingException;
+import ftbsc.lll.exceptions.MappingNotFoundException;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Container class used to store information about classes.
+ */
+public class ClassData {
+ /**
+ * The unobfuscated name (FQN with '/' instad of '.') of the class.
+ */
+ public final String unobf;
+
+ /**
+ * The obfuscated internal name (FQN with '/' instad of '.') of the class.
+ */
+ public final String obf;
+
+ /**
+ * A {@link Map} tying each member's name or signature to its
+ * obfuscated counterpart.
+ */
+ public final Map<String, String> members;
+
+ /**
+ * The constructor. It takes in the names (obfuscated and non-obfuscated)
+ * of a class.
+ * @param unobf the unobfuscated name
+ * @param obf the obfuscated name
+ */
+ public ClassData(String unobf, String obf) {
+ this.unobf = unobf;
+ this.obf = obf;
+ this.members = new HashMap<>();
+ }
+
+ /**
+ * Adds a member to the target class.
+ * For fields only the names are required; for methods,
+ * this takes in the full signature ({@code name + " " + space}).
+ * @param s the String representing the declaration line
+ */
+ public void addMember(String s) {
+ String[] split = s.trim().split(" ");
+ if(split.length == 2) //field
+ members.put(split[0], split[1]);
+ else if (split.length == 3) //method
+ members.put(split[0] + " " + split[1], split[2]);
+ }
+
+ /**
+ * Gets an obfuscated member given the method name and a method descriptor,
+ * which may be partial (i.e. not include return type) or null if the member
+ * is not a method.
+ * @param memberName member name
+ * @param methodDescriptor the method descriptor, or null if it's not a method
+ * @return the requested obfuscated name, or null if nothing was found
+ * @throws AmbiguousMappingException if not enough data was given to uniquely identify a mapping
+ */
+ public String get(String memberName, String methodDescriptor) {
+
+ //find all keys that start with the name
+ List<String> candidates = members.keySet().stream().filter(
+ m -> m.split(" ")[0].equals(memberName)
+ ).collect(Collectors.toList());
+
+ if(methodDescriptor != null) {
+ String signature = String.format("%s %s", memberName, methodDescriptor);
+ candidates = candidates.stream().filter(
+ m -> m.equals(signature)
+ ).collect(Collectors.toList());
+ }
+
+ switch(candidates.size()) {
+ case 0:
+ throw new MappingNotFoundException(String.format(
+ "%s.%s%s",
+ this.unobf,
+ memberName,
+ methodDescriptor == null ? "" : "()"
+ ));
+ case 1:
+ return members.get(candidates.get(0));
+ default:
+ throw new AmbiguousMappingException(String.format(
+ "Mapper could not uniquely identify member %s.%s%s, found %d!",
+ this.unobf,
+ memberName,
+ methodDescriptor == null ? "" : "()",
+ candidates.size()
+ ));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/ftbsc/lll/mapper/tools/MappingUtils.java b/src/main/java/ftbsc/lll/mapper/tools/MappingUtils.java
new file mode 100644
index 0000000..918052a
--- /dev/null
+++ b/src/main/java/ftbsc/lll/mapper/tools/MappingUtils.java
@@ -0,0 +1,56 @@
+package ftbsc.lll.mapper.tools;
+
+import ftbsc.lll.exceptions.MappingNotFoundException;
+import ftbsc.lll.mapper.IMapper;
+import ftbsc.lll.tools.DescriptorBuilder;
+import org.objectweb.asm.Type;
+
+public class MappingUtils {
+ /**
+ * Obfuscates a method descriptor, replacing its class references
+ * with their obfuscated counterparts.
+ * @param descriptor a {@link String} containing the descriptor
+ * @return the obfuscated descriptor
+ */
+ public static String obfuscateMethodDescriptor(String descriptor, IMapper mapper) {
+ Type method = Type.getMethodType(descriptor);
+ Type[] arguments = method.getArgumentTypes();
+ Type returnType = method.getReturnType();
+
+ Type[] obfArguments = new Type[arguments.length];
+ for(int i = 0; i < obfArguments.length; i++)
+ obfArguments[i] = obfuscateType(arguments[i], mapper);
+
+ return Type.getMethodDescriptor(obfuscateType(returnType, mapper), obfArguments);
+ }
+
+ /**
+ * Given a {@link Type} and a valid {@link IMapper} it returns its obfuscated
+ * counterpart.
+ * @param type the type in question
+ * @return the obfuscated type
+ */
+ public static Type obfuscateType(Type type, IMapper mapper) {
+ //unwrap arrays
+ Type unwrapped = type;
+ int arrayLevel = 0;
+ while(unwrapped.getSort() == Type.ARRAY) {
+ unwrapped = unwrapped.getElementType();
+ arrayLevel++;
+ }
+
+ //if it's a primitive no operation is needed
+ if(type.getSort() < Type.ARRAY)
+ return type;
+
+ String internalName = type.getInternalName();
+
+ String internalNameObf;
+ try {
+ internalNameObf = mapper.obfuscateClass(internalName);
+ return Type.getType(DescriptorBuilder.nameToDescriptor(internalNameObf, arrayLevel));
+ } catch(MappingNotFoundException e) {
+ return type;
+ }
+ }
+}