chore: moved obfuscation stuff to mapping library

This commit is contained in:
zaaarf 2023-06-11 15:32:07 +02:00
parent d528d472a1
commit ed70355a86
No known key found for this signature in database
GPG key ID: 82240E075E31FA4C
7 changed files with 16 additions and 265 deletions

View file

@ -21,7 +21,8 @@ repositories {
dependencies { dependencies {
implementation 'com.squareup:javapoet:1.13.0' implementation 'com.squareup:javapoet:1.13.0'
implementation 'org.ow2.asm:asm-commons:9.5' implementation 'org.ow2.asm:asm-commons:9.5'
implementation 'ftbsc:lll:0.4.1' implementation 'ftbsc:lll:0.4.2'
implementation 'ftbsc.lll:mapper:0.0.2'
} }
jar { jar {

View file

@ -1,27 +0,0 @@
package ftbsc.lll.exceptions;
import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper;
/**
* Thrown upon failure to find the requested mapping within a loaded {@link ObfuscationMapper}.
*/
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);
}
}

View file

@ -6,7 +6,7 @@ import ftbsc.lll.exceptions.NotAProxyException;
import ftbsc.lll.exceptions.TargetNotFoundException; import ftbsc.lll.exceptions.TargetNotFoundException;
import ftbsc.lll.processor.annotations.Target; import ftbsc.lll.processor.annotations.Target;
import ftbsc.lll.processor.tools.containers.ClassContainer; import ftbsc.lll.processor.tools.containers.ClassContainer;
import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; import ftbsc.lll.mapper.IMapper;
import ftbsc.lll.proxies.ProxyType; import ftbsc.lll.proxies.ProxyType;
import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.ProcessingEnvironment;
@ -229,11 +229,11 @@ public class ASTUtils {
* @param parentFQN the unobfuscated FQN of the parent class * @param parentFQN the unobfuscated FQN of the parent class
* @param memberName the name of the member * @param memberName the name of the member
* @param methodDescriptor the descriptor of the method, may be null * @param methodDescriptor the descriptor of the method, may be null
* @param mapper the {@link ObfuscationMapper} to use, may be null * @param mapper the {@link IMapper} to use, may be null
* @return the internal class name * @return the internal class name
* @since 0.3.0 * @since 0.3.0
*/ */
public static String findMemberName(String parentFQN, String memberName, String methodDescriptor, ObfuscationMapper mapper) { public static String findMemberName(String parentFQN, String memberName, String methodDescriptor, IMapper mapper) {
try { try {
return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName, methodDescriptor); return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName, methodDescriptor);
} catch(MappingNotFoundException e) { } catch(MappingNotFoundException e) {

View file

@ -2,7 +2,7 @@ package ftbsc.lll.processor.tools;
import ftbsc.lll.IInjector; import ftbsc.lll.IInjector;
import ftbsc.lll.exceptions.InvalidResourceException; import ftbsc.lll.exceptions.InvalidResourceException;
import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper; import ftbsc.lll.mapper.IMapper;
import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.ProcessingEnvironment;
import java.io.*; import java.io.*;
@ -12,6 +12,7 @@ import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
/** /**
* Class in charge of containing, parsing and processing all processor options, * Class in charge of containing, parsing and processing all processor options,
@ -33,10 +34,10 @@ public class ProcessorOptions {
public final ProcessingEnvironment env; public final ProcessingEnvironment env;
/** /**
* The {@link ObfuscationMapper} used to convert classes and variables * The {@link IMapper} used to convert classes and variables
* to their obfuscated equivalent. Will be null when no mapper is in use. * to their obfuscated equivalent. Will be null when no mapper is in use.
*/ */
public final ObfuscationMapper mapper; public final IMapper mapper;
/** /**
* Whether the processor should issue warnings when compiling code anonymous * Whether the processor should issue warnings when compiling code anonymous
@ -80,10 +81,9 @@ public class ProcessorOptions {
throw new InvalidResourceException(location); throw new InvalidResourceException(location);
} }
} }
//assuming its tsrg file this.mapper = IMapper.getMappers(new BufferedReader(
//todo: replace crappy homebaked parser with actual library new InputStreamReader(targetStream, StandardCharsets.UTF_8)).lines().collect(Collectors.toList())
this.mapper = new ObfuscationMapper(new BufferedReader(new InputStreamReader(targetStream, ).iterator().next(); //TODO: add logic for choosing a specific one
StandardCharsets.UTF_8)).lines());
} }
this.anonymousClassWarning = parseBooleanArg(env.getOptions().get("anonymousClassWarning"), true); this.anonymousClassWarning = parseBooleanArg(env.getOptions().get("anonymousClassWarning"), true);
this.obfuscateInjectorMetadata = parseBooleanArg(env.getOptions().get("obfuscateInjectorMetadata"), true); this.obfuscateInjectorMetadata = parseBooleanArg(env.getOptions().get("obfuscateInjectorMetadata"), true);

View file

@ -1,6 +1,7 @@
package ftbsc.lll.processor.tools.containers; package ftbsc.lll.processor.tools.containers;
import ftbsc.lll.exceptions.AmbiguousDefinitionException; import ftbsc.lll.exceptions.AmbiguousDefinitionException;
import ftbsc.lll.mapper.tools.MappingUtils;
import ftbsc.lll.processor.annotations.Find; import ftbsc.lll.processor.annotations.Find;
import ftbsc.lll.processor.annotations.Patch; import ftbsc.lll.processor.annotations.Patch;
import ftbsc.lll.processor.tools.ProcessorOptions; import ftbsc.lll.processor.tools.ProcessorOptions;
@ -73,7 +74,7 @@ public class FieldContainer {
this.name = this.elem.getSimpleName().toString(); this.name = this.elem.getSimpleName().toString();
this.descriptor = descriptorFromType(this.elem.asType(), options.env); this.descriptor = descriptorFromType(this.elem.asType(), options.env);
} }
this.descriptorObf = options.mapper == null ? this.descriptor : options.mapper.obfuscateType(Type.getType(this.descriptor)).getDescriptor(); this.descriptorObf = options.mapper == null ? this.descriptor : MappingUtils.obfuscateType(Type.getType(this.descriptor), options.mapper).getDescriptor();
this.nameObf = findMemberName(parent.fqn, this.name, null, options.mapper); this.nameObf = findMemberName(parent.fqn, this.name, null, options.mapper);
} }

View file

@ -2,6 +2,7 @@ package ftbsc.lll.processor.tools.containers;
import ftbsc.lll.exceptions.AmbiguousDefinitionException; import ftbsc.lll.exceptions.AmbiguousDefinitionException;
import ftbsc.lll.exceptions.TargetNotFoundException; import ftbsc.lll.exceptions.TargetNotFoundException;
import ftbsc.lll.mapper.tools.MappingUtils;
import ftbsc.lll.processor.annotations.Find; import ftbsc.lll.processor.annotations.Find;
import ftbsc.lll.processor.annotations.Patch; import ftbsc.lll.processor.annotations.Patch;
import ftbsc.lll.processor.annotations.Target; import ftbsc.lll.processor.annotations.Target;
@ -78,7 +79,7 @@ public class MethodContainer {
this.name = this.elem.getSimpleName().toString(); this.name = this.elem.getSimpleName().toString();
this.descriptor = descriptorFromExecutableElement(this.elem, options.env); this.descriptor = descriptorFromExecutableElement(this.elem, options.env);
} }
this.descriptorObf = options.mapper == null ? this.descriptor : options.mapper.obfuscateMethodDescriptor(this.descriptor); this.descriptorObf = options.mapper == null ? this.descriptor : MappingUtils.obfuscateMethodDescriptor(this.descriptor, options.mapper);
this.nameObf = findMemberName(parent.fqn, this.name, this.descriptor, options.mapper); this.nameObf = findMemberName(parent.fqn, this.name, this.descriptor, options.mapper);
} }

View file

@ -1,225 +0,0 @@
package ftbsc.lll.processor.tools.obfuscation;
import ftbsc.lll.exceptions.AmbiguousDefinitionException;
import ftbsc.lll.exceptions.MappingNotFoundException;
import ftbsc.lll.tools.DescriptorBuilder;
import org.objectweb.asm.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Parses a .tsrg file into a mapper capable of converting from
* deobfuscated names to obfuscated ones.
* Obviously, it may only be used at runtime if the .tsrg file is
* included in the resources. However, in that case, I'd recommend
* using the built-in Forge one and refrain from including an extra
* resource for no good reason.
* TODO: CSV format
* @since 0.2.0
*/
public class ObfuscationMapper {
/**
* A Map using the deobfuscated names as keys,
* holding information for that class as value.
*/
private final Map<String, ObfuscationData> mapper = new HashMap<>();
/**
* The public constructor.
* Should be passed a {@link Stream} of Strings, one representing each line.
* Whether they contain line endings or not is irrelevant.
* @param str a {@link Stream} of strings
*/
public ObfuscationMapper(Stream<String> str) {
AtomicReference<String> currentClass = new AtomicReference<>("");
str.forEach(l -> {
if(l == null) return;
if(l.startsWith("\t"))
mapper.get(currentClass.get()).addMember(l);
else {
String[] sp = l.split(" ");
ObfuscationData s = new ObfuscationData(sp[0], sp[1]);
currentClass.set(s.unobf);
mapper.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
*/
public String obfuscateClass(String name) {
ObfuscationData data = mapper.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).
* The method signature must be in this format: "methodName methodDescriptor",
* with a space, because that's how it is in .tsrg files.
* @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
*/
public String obfuscateMember(String parentName, String memberName, String methodDescriptor) {
ObfuscationData data = mapper.get(parentName.replace('.', '/'));
if(data == null)
throw new MappingNotFoundException(parentName + "::" + memberName);
return data.get(memberName, methodDescriptor);
}
/**
* Obfuscates a method descriptor, replacing its class references
* with their obfuscated counterparts.
* @param descriptor a {@link String} containing the descriptor
* @return the obfuscated descriptor
* @since 0.5.1
*/
public String obfuscateMethodDescriptor(String descriptor) {
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] = this.obfuscateType(arguments[i]);
return Type.getMethodDescriptor(this.obfuscateType(returnType), obfArguments);
}
/**
* Given a {@link Type} it returns its obfuscated counterpart.
* @param type the type in question
* @return the obfuscated type
* @since 0.5.1
*/
public Type obfuscateType(Type type) {
//unwrap arrays
Type unwrapped = type;
int arrayLevel = 0;
while(unwrapped.getSort() == org.objectweb.asm.Type.ARRAY) {
unwrapped = unwrapped.getElementType();
arrayLevel++;
}
//if it's a primitive no operation is needed
if(type.getSort() < org.objectweb.asm.Type.ARRAY)
return type;
String internalName = type.getInternalName();
String internalNameObf;
try {
internalNameObf = this.obfuscateClass(internalName);
return Type.getType(DescriptorBuilder.nameToDescriptor(internalNameObf, arrayLevel));
} catch(MappingNotFoundException e) {
return type;
}
}
/**
* Private class used internally for storing information about each
* class. It's private because there is no good reason anyone would
* want to access this outside of this class.
*/
private static class ObfuscationData {
/**
* The unobfuscated name (FQN with '/' instad of '.') of the class.
*/
private final String unobf;
/**
* The obfuscated internal name (FQN with '/' instad of '.') of the class.
*/
private final String obf;
/**
* A {@link Map} tying each member's name or signature to its
* obfuscated counterpart.
*/
private 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
*/
private ObfuscationData(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 AmbiguousDefinitionException 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 AmbiguousDefinitionException(String.format(
"Mapper could not uniquely identify member %s.%s%s, found %d!",
this.unobf,
memberName,
methodDescriptor == null ? "" : "()",
candidates.size()
));
}
}
}
}