Skip to content

Commit

Permalink
Transformer now supports Field primitive sets and gets + cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Rongmario committed Dec 9, 2023
1 parent e88d0d2 commit 91b220f
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 70 deletions.
206 changes: 165 additions & 41 deletions src/main/java/com/cleanroommc/hackery/ReflectionHackery.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.cleanroommc.hackery;

import com.cleanroommc.hackery.enums.EnumHackery;
import jdk.internal.misc.Unsafe;
import net.minecraftforge.common.util.EnumHelper;
import net.minecraftforge.fml.common.FMLLog;

import javax.annotation.Nullable;
import java.lang.reflect.*;
Expand Down Expand Up @@ -58,45 +55,85 @@ public static void stripFieldOfModifier(Field field, int modifierFlag) throws Il
field$modifiers.setInt(field, field.getModifiers() & ~modifierFlag);
}


public static void setField(Field field, Object owner, Object value) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
putHelper(field.getType(), unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value);
} else {
putHelper(field.getType(), owner, unsafe.objectFieldOffset(field), value);
}
}

private static void putHelper(Class<?> clazz, Object owner, long offset, Object value) {
if (clazz.equals(int.class)) {
unsafe.putInt(owner, offset, (int) value);
return;
public static void setBooleanField(Field field, Object owner, boolean value) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
unsafe.putBoolean(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value);
} else {
unsafe.putBoolean(owner, unsafe.objectFieldOffset(field), value);
}
if (clazz.equals(byte.class)) {
unsafe.putByte(owner, offset, (byte) value);
return;
}

public static void setByteField(Field field, Object owner, byte value) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
unsafe.putByte(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value);
} else {
unsafe.putByte(owner, unsafe.objectFieldOffset(field), value);
}
if (clazz.equals(long.class)) {
unsafe.putLong(owner, offset, (long) value);
return;
}

public static void setCharField(Field field, Object owner, char value) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
unsafe.putChar(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value);
} else {
unsafe.putChar(owner, unsafe.objectFieldOffset(field), value);
}
if (clazz.equals(float.class)) {
unsafe.putFloat(owner, offset, (float) value);
return;
}

public static void setShortField(Field field, Object owner, short value) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
unsafe.putShort(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value);
} else {
unsafe.putShort(owner, unsafe.objectFieldOffset(field), value);
}
if (clazz.equals(double.class)) {
unsafe.putDouble(owner, offset, (double) value);
return;
}

public static void setIntField(Field field, Object owner, int value) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
unsafe.putInt(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value);
} else {
unsafe.putInt(owner, unsafe.objectFieldOffset(field), value);
}
if (clazz.equals(boolean.class)) {
unsafe.putBoolean(owner, offset, (boolean) value);
return;
}

public static void setLongField(Field field, Object owner, long value) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
unsafe.putLong(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value);
} else {
unsafe.putLong(owner, unsafe.objectFieldOffset(field), value);
}
if (clazz.equals(char.class)) {
unsafe.putChar(owner, offset, (char) value);
return;
}

public static void setFloatField(Field field, Object owner, float value) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
unsafe.putFloat(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value);
} else {
unsafe.putFloat(owner, unsafe.objectFieldOffset(field), value);
}
}

public static void setDoubleField(Field field, Object owner, double value) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
unsafe.putDouble(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field), value);
} else {
unsafe.putDouble(owner, unsafe.objectFieldOffset(field), value);
}
unsafe.putObject(owner, offset, value);
}

public static Object getField(Field field, Object owner) throws IllegalAccessException {
Expand All @@ -108,29 +145,116 @@ public static Object getField(Field field, Object owner) throws IllegalAccessExc
}
}

public static boolean getBooleanField(Field field, Object owner) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
return unsafe.getBoolean(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));
} else {
return unsafe.getBoolean(owner, unsafe.objectFieldOffset(field));
}
}

public static byte getByteField(Field field, Object owner) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
return unsafe.getByte(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));
} else {
return unsafe.getByte(owner, unsafe.objectFieldOffset(field));
}
}

public static char getCharField(Field field, Object owner) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
return unsafe.getChar(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));
} else {
return unsafe.getChar(owner, unsafe.objectFieldOffset(field));
}
}

public static short getShortField(Field field, Object owner) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
return unsafe.getShort(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));
} else {
return unsafe.getShort(owner, unsafe.objectFieldOffset(field));
}
}

public static int getIntField(Field field, Object owner) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
return unsafe.getInt(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));
} else {
return unsafe.getInt(owner, unsafe.objectFieldOffset(field));
}
}

public static long getLongField(Field field, Object owner) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
return unsafe.getLong(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));
} else {
return unsafe.getLong(owner, unsafe.objectFieldOffset(field));
}
}

public static float getFloatField(Field field, Object owner) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
return unsafe.getFloat(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));
} else {
return unsafe.getFloat(owner, unsafe.objectFieldOffset(field));
}
}

public static double getDoubleField(Field field, Object owner) throws IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) {
unsafe.ensureClassInitialized(field.getDeclaringClass());
return unsafe.getDouble(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));
} else {
return unsafe.getDouble(owner, unsafe.objectFieldOffset(field));
}
}

private static void putHelper(Class<?> clazz, Object owner, long offset, Object value) {
if (clazz == Integer.TYPE) {
unsafe.putInt(owner, offset, (int) value);
} else if (clazz == Byte.TYPE) {
unsafe.putByte(owner, offset, (byte) value);
} else if (clazz == Long.TYPE) {
unsafe.putLong(owner, offset, (long) value);
} else if (clazz == Float.TYPE) {
unsafe.putFloat(owner, offset, (float) value);
} else if (clazz == Double.TYPE) {
unsafe.putDouble(owner, offset, (double) value);
} else if (clazz == Boolean.TYPE) {
unsafe.putBoolean(owner, offset, (boolean) value);
} else if (clazz == Character.TYPE) {
unsafe.putChar(owner, offset, (char) value);
} else {
unsafe.putObject(owner, offset, value);
}
}

private static Object getHelper(Class<?> clazz, Object owner, long offset) {
if (clazz.equals(int.class)) {
if (clazz == Integer.TYPE) {
return unsafe.getInt(owner, offset);
}
if (clazz.equals(byte.class)) {
} else if (clazz == Byte.TYPE) {
return unsafe.getByte(owner, offset);
}
if (clazz.equals(long.class)) {
} else if (clazz == Long.TYPE) {
return unsafe.getLong(owner, offset);
}
if (clazz.equals(float.class)) {
} else if (clazz == Float.TYPE) {
return unsafe.getFloat(owner, offset);
}
if (clazz.equals(double.class)) {
} else if (clazz == Double.TYPE) {
return unsafe.getDouble(owner, offset);
}
if (clazz.equals(boolean.class)) {
} else if (clazz == Boolean.TYPE) {
return unsafe.getBoolean(owner, offset);
}
if (clazz.equals(char.class)) {
} else if (clazz == Character.TYPE) {
return unsafe.getChar(owner, offset);
} else {
return unsafe.getObject(owner, offset);
}
return unsafe.getObject(owner, offset);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,77 @@
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.*;

import java.util.List;
import java.util.stream.Stream;

public class ReflectionFieldTransformer implements IClassTransformer {
private static final List<String> excludeList = Stream.of("com.cleanroommc.hackery", "org.spongepowered", "net.minecraft", "com.google", "com.ibm.icu", "io.netty", "com.sun", "it.unimi.dsi", "oshi", "org.slf4j", "com.mojang", "zone.rong", "kotlin").toList();
public class ReflectionFieldTransformer implements IClassTransformer
{
private static final List<String> EXCLUSIONS = List.of("com.cleanroommc.hackery", "org.spongepowered", "net.minecraft", "com.google", "com.ibm.icu", "io.netty", "com.sun", "it.unimi.dsi", "oshi", "org.slf4j", "com.mojang", "zone.rong", "kotlin");
private static final String OUR_REFLECTION_CLASS = "com/cleanroommc/hackery/ReflectionHackery";

private static String replaceInstruction(InsnList insnList, MethodInsnNode oldNode, String methodName, String methodDescriptor)
{
insnList.set(oldNode, new MethodInsnNode(Opcodes.INVOKESTATIC, OUR_REFLECTION_CLASS, methodName, methodDescriptor));
return oldNode.name + " -> " + methodName;
}

@Override
public byte[] transform(String s, String s1, byte[] bytes) {
if (bytes == null) {
public byte[] transform(String s, String s1, byte[] bytes)
{
if (bytes == null)
{
return null;
}
for (String str: excludeList) {
for (String str: EXCLUSIONS)
{
if (s1.startsWith(str))
{
return bytes;
}
}
//System.out.println(s1);

ClassNode classNode = new ClassNode();
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);

if (classNode.methods != null) {
for (MethodNode methodNode : classNode.methods) {
if (methodNode.instructions != null) {
for (AbstractInsnNode insnNode : methodNode.instructions) {
if (insnNode.getType() == AbstractInsnNode.METHOD_INSN) {
if (insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
if (((MethodInsnNode)insnNode).owner.equals("java/lang/reflect/Field")) {
if (((MethodInsnNode)insnNode).name.equals("set")) {
methodNode.instructions.insert(insnNode, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/cleanroommc/hackery/ReflectionHackery", "setField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;Ljava/lang/Object;)V"));
methodNode.instructions.remove(insnNode);
FMLLog.log.info(s1 + "'s SET transforming");
}

if (((MethodInsnNode)insnNode).name.equals("get")) {
methodNode.instructions.insert(insnNode, new MethodInsnNode(Opcodes.INVOKESTATIC, "com/cleanroommc/hackery/ReflectionHackery", "getField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object;"));
methodNode.instructions.remove(insnNode);
FMLLog.log.info(s1 + "'s GET transforming");
}
if (classNode.methods != null)
{
for (MethodNode methodNode : classNode.methods)
{
InsnList instructions = methodNode.instructions;
if (instructions != null)
{
for (AbstractInsnNode insnNode : instructions)
{
if (insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL && insnNode instanceof MethodInsnNode methodInsnNode)
{
if ("java/lang/reflect/Field".equals(methodInsnNode.owner))
{
String result = switch (methodInsnNode.name)
{
case "set" -> replaceInstruction(instructions, methodInsnNode, "setField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;Ljava/lang/Object;)V");
case "get" -> replaceInstruction(instructions, methodInsnNode, "getField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object;");
case "setBoolean" -> replaceInstruction(instructions, methodInsnNode, "setBooleanField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;Z)V");
case "getBoolean" -> replaceInstruction(instructions, methodInsnNode, "getBooleanField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Z");
case "setByte" -> replaceInstruction(instructions, methodInsnNode, "setByteField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;B)V");
case "getByte" -> replaceInstruction(instructions, methodInsnNode, "getByteField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)B");
case "setChar" -> replaceInstruction(instructions, methodInsnNode, "setCharField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;C)V");
case "getChar" -> replaceInstruction(instructions, methodInsnNode, "getCharField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)C");
case "setShort" -> replaceInstruction(instructions, methodInsnNode, "setShortField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;S)V");
case "getShort" -> replaceInstruction(instructions, methodInsnNode, "getShortField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)S");
case "setInt" -> replaceInstruction(instructions, methodInsnNode, "setIntField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;I)V");
case "getInt" -> replaceInstruction(instructions, methodInsnNode, "getIntField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)I");
case "setLong" -> replaceInstruction(instructions, methodInsnNode, "setLongField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;J)V");
case "getLong" -> replaceInstruction(instructions, methodInsnNode, "getLongField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)J");
case "setFloat" -> replaceInstruction(instructions, methodInsnNode, "setFloatField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;F)V");
case "getFloat" -> replaceInstruction(instructions, methodInsnNode, "getFloatField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)F");
case "setDouble" -> replaceInstruction(instructions, methodInsnNode, "setDoubleField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;D)V");
case "getDouble" -> replaceInstruction(instructions, methodInsnNode, "getDoubleField", "(Ljava/lang/reflect/Field;Ljava/lang/Object;)D");
default -> null;
};
if (result != null) {
FMLLog.log.info("[{}]: Transformed {}", s1, result);
}
}
}
Expand All @@ -63,4 +89,5 @@ public byte[] transform(String s, String s1, byte[] bytes) {
classNode.accept(classWriter);
return classWriter.toByteArray();
}

}

0 comments on commit 91b220f

Please sign in to comment.