diff options
Diffstat (limited to 'src')
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; + } + } +} |