Commit 813a72f57c94031fdba852d9ab4fbcb7dd3d9496

Authored by Mumfrey
1 parent 239a6eb0

LiteLoader 1.6.4_02 - experimental - hook packet classes using ASM instead of re…

…flection, fall back to reflection if transformation fails
java/com/mumfrey/liteloader/core/Events.java
@@ -12,6 +12,10 @@ import com.mumfrey.liteloader.Tickable; @@ -12,6 +12,10 @@ import com.mumfrey.liteloader.Tickable;
12 import com.mumfrey.liteloader.core.hooks.HookChat; 12 import com.mumfrey.liteloader.core.hooks.HookChat;
13 import com.mumfrey.liteloader.core.hooks.HookLogin; 13 import com.mumfrey.liteloader.core.hooks.HookLogin;
14 import com.mumfrey.liteloader.core.hooks.HookProfiler; 14 import com.mumfrey.liteloader.core.hooks.HookProfiler;
  15 +import com.mumfrey.liteloader.core.hooks.asm.ASMHookProxy;
  16 +import com.mumfrey.liteloader.core.hooks.asm.ChatPacketTransformer;
  17 +import com.mumfrey.liteloader.core.hooks.asm.LoginPacketTransformer;
  18 +import com.mumfrey.liteloader.core.hooks.asm.PacketTransformer;
15 import com.mumfrey.liteloader.util.ModUtilities; 19 import com.mumfrey.liteloader.util.ModUtilities;
16 import com.mumfrey.liteloader.util.PrivateFields; 20 import com.mumfrey.liteloader.util.PrivateFields;
17 21
@@ -37,6 +41,11 @@ public class Events implements IPlayerUsage @@ -37,6 +41,11 @@ public class Events implements IPlayerUsage
37 private final PluginChannels pluginChannels; 41 private final PluginChannels pluginChannels;
38 42
39 /** 43 /**
  44 + * ASM hook proxy
  45 + */
  46 + private final ASMHookProxy asmProxy;
  47 +
  48 + /**
40 * Reference to the minecraft timer 49 * Reference to the minecraft timer
41 */ 50 */
42 private Timer minecraftTimer; 51 private Timer minecraftTimer;
@@ -144,11 +153,12 @@ public class Events implements IPlayerUsage @@ -144,11 +153,12 @@ public class Events implements IPlayerUsage
144 * @param minecraft 153 * @param minecraft
145 * @param pluginChannels 154 * @param pluginChannels
146 */ 155 */
147 - Events(LiteLoader loader, Minecraft minecraft, PluginChannels pluginChannels) 156 + Events(LiteLoader loader, Minecraft minecraft, PluginChannels pluginChannels, ASMHookProxy asmProxy)
148 { 157 {
149 this.loader = loader; 158 this.loader = loader;
150 this.minecraft = minecraft; 159 this.minecraft = minecraft;
151 this.pluginChannels = pluginChannels; 160 this.pluginChannels = pluginChannels;
  161 + this.asmProxy = asmProxy;
152 } 162 }
153 163
154 /** 164 /**
@@ -239,16 +249,34 @@ public class Events implements IPlayerUsage @@ -239,16 +249,34 @@ public class Events implements IPlayerUsage
239 if ((this.chatListeners.size() > 0 || this.chatFilters.size() > 0) && !this.chatHooked) 249 if ((this.chatListeners.size() > 0 || this.chatFilters.size() > 0) && !this.chatHooked)
240 { 250 {
241 this.chatHooked = true; 251 this.chatHooked = true;
242 - HookChat.register();  
243 - HookChat.registerPacketHandler(this); 252 +
  253 + if (ChatPacketTransformer.isInjected())
  254 + {
  255 + PacketTransformer.registerProxy(Packet3Chat.class, this.asmProxy);
  256 + }
  257 + else
  258 + {
  259 + LiteLoader.getLogger().info("Callback injection failed for chat packet, injecting reflection hook");
  260 + HookChat.register();
  261 + HookChat.registerPacketHandler(this);
  262 + }
244 } 263 }
245 264
246 // Login hook 265 // Login hook
247 if ((this.preLoginListeners.size() > 0 || this.loginListeners.size() > 0) && !this.loginHooked) 266 if ((this.preLoginListeners.size() > 0 || this.loginListeners.size() > 0) && !this.loginHooked)
248 { 267 {
249 this.loginHooked = true; 268 this.loginHooked = true;
250 - ModUtilities.registerPacketOverride(1, HookLogin.class);  
251 - HookLogin.events = this; 269 +
  270 + if (LoginPacketTransformer.isInjected())
  271 + {
  272 + PacketTransformer.registerProxy(Packet1Login.class, this.asmProxy);
  273 + }
  274 + else
  275 + {
  276 + LiteLoader.getLogger().info("Callback injection failed for login packet, injecting reflection hook");
  277 + ModUtilities.registerPacketOverride(1, HookLogin.class);
  278 + HookLogin.events = this;
  279 + }
252 } 280 }
253 281
254 // Tick hook 282 // Tick hook
@@ -614,7 +642,7 @@ public class Events implements IPlayerUsage @@ -614,7 +642,7 @@ public class Events implements IPlayerUsage
614 } 642 }
615 643
616 /** 644 /**
617 - * Callback from the chat hook 645 + * Callback from the reflective chat hook
618 * 646 *
619 * @param chatPacket 647 * @param chatPacket
620 * @return 648 * @return
java/com/mumfrey/liteloader/core/LiteLoader.java
@@ -35,6 +35,7 @@ import net.minecraft.src.SimpleReloadableResourceManager; @@ -35,6 +35,7 @@ import net.minecraft.src.SimpleReloadableResourceManager;
35 import net.minecraft.src.World; 35 import net.minecraft.src.World;
36 36
37 import com.mumfrey.liteloader.*; 37 import com.mumfrey.liteloader.*;
  38 +import com.mumfrey.liteloader.core.hooks.asm.ASMHookProxy;
38 import com.mumfrey.liteloader.crashreport.CallableLiteLoaderBrand; 39 import com.mumfrey.liteloader.crashreport.CallableLiteLoaderBrand;
39 import com.mumfrey.liteloader.crashreport.CallableLiteLoaderMods; 40 import com.mumfrey.liteloader.crashreport.CallableLiteLoaderMods;
40 import com.mumfrey.liteloader.gui.GuiControlsPaginated; 41 import com.mumfrey.liteloader.gui.GuiControlsPaginated;
@@ -149,6 +150,11 @@ public final class LiteLoader @@ -149,6 +150,11 @@ public final class LiteLoader
149 private final LinkedList<ModFile> disabledMods = new LinkedList<ModFile>(); 150 private final LinkedList<ModFile> disabledMods = new LinkedList<ModFile>();
150 151
151 /** 152 /**
  153 + * ASM hook proxy
  154 + */
  155 + private final ASMHookProxy asmProxy = new ASMHookProxy();
  156 +
  157 + /**
152 * Event manager 158 * Event manager
153 */ 159 */
154 private Events events; 160 private Events events;
@@ -156,7 +162,7 @@ public final class LiteLoader @@ -156,7 +162,7 @@ public final class LiteLoader
156 /** 162 /**
157 * Plugin channel manager 163 * Plugin channel manager
158 */ 164 */
159 - private final PluginChannels pluginChannels = new PluginChannels(); 165 + private final PluginChannels pluginChannels = new PluginChannels(this.asmProxy);
160 166
161 /** 167 /**
162 * Permission Manager 168 * Permission Manager
@@ -350,7 +356,7 @@ public final class LiteLoader @@ -350,7 +356,7 @@ public final class LiteLoader
350 this.minecraft = minecraft; 356 this.minecraft = minecraft;
351 357
352 // Create the event broker 358 // Create the event broker
353 - this.events = new Events(this, this.minecraft, this.pluginChannels); 359 + this.events = new Events(this, this.minecraft, this.pluginChannels, this.asmProxy);
354 360
355 // Spawn mod instances 361 // Spawn mod instances
356 this.loadMods(); 362 this.loadMods();
java/com/mumfrey/liteloader/core/PluginChannels.java
@@ -12,6 +12,9 @@ import net.minecraft.src.Packet250CustomPayload; @@ -12,6 +12,9 @@ import net.minecraft.src.Packet250CustomPayload;
12 12
13 import com.mumfrey.liteloader.PluginChannelListener; 13 import com.mumfrey.liteloader.PluginChannelListener;
14 import com.mumfrey.liteloader.core.hooks.HookPluginChannels; 14 import com.mumfrey.liteloader.core.hooks.HookPluginChannels;
  15 +import com.mumfrey.liteloader.core.hooks.asm.ASMHookProxy;
  16 +import com.mumfrey.liteloader.core.hooks.asm.CustomPayloadPacketTransformer;
  17 +import com.mumfrey.liteloader.core.hooks.asm.PacketTransformer;
15 import com.mumfrey.liteloader.permissions.PermissionsManagerClient; 18 import com.mumfrey.liteloader.permissions.PermissionsManagerClient;
16 19
17 /** 20 /**
@@ -39,11 +42,16 @@ public class PluginChannels @@ -39,11 +42,16 @@ public class PluginChannels
39 * List of mods which implement PluginChannelListener interface 42 * List of mods which implement PluginChannelListener interface
40 */ 43 */
41 private LinkedList<PluginChannelListener> pluginChannelListeners = new LinkedList<PluginChannelListener>(); 44 private LinkedList<PluginChannelListener> pluginChannelListeners = new LinkedList<PluginChannelListener>();
  45 +
  46 + private ASMHookProxy asmProxy;
42 47
43 /** 48 /**
44 * Package private 49 * Package private
45 */ 50 */
46 - PluginChannels() {} 51 + PluginChannels(ASMHookProxy proxy)
  52 + {
  53 + this.asmProxy = proxy;
  54 + }
47 55
48 /** 56 /**
49 * 57 *
@@ -53,8 +61,17 @@ public class PluginChannels @@ -53,8 +61,17 @@ public class PluginChannels
53 // Plugin channels hook 61 // Plugin channels hook
54 if (this.pluginChannelListeners.size() > 0 && !this.hookInitDone) 62 if (this.pluginChannelListeners.size() > 0 && !this.hookInitDone)
55 { 63 {
56 - HookPluginChannels.register();  
57 - HookPluginChannels.registerPacketHandler(this); 64 + if (CustomPayloadPacketTransformer.isInjected())
  65 + {
  66 + PacketTransformer.registerProxy(Packet250CustomPayload.class, this.asmProxy);
  67 + }
  68 + else
  69 + {
  70 + LiteLoader.getLogger().info("Callback injection failed for custom payload packet, injecting reflection hook");
  71 + HookPluginChannels.register();
  72 + HookPluginChannels.registerPacketHandler(this);
  73 + }
  74 +
58 this.hookInitDone = true; 75 this.hookInitDone = true;
59 } 76 }
60 } 77 }
java/com/mumfrey/liteloader/core/hooks/HookProfiler.java
@@ -140,6 +140,7 @@ public class HookProfiler extends Profiler @@ -140,6 +140,7 @@ public class HookProfiler extends Profiler
140 { 140 {
141 if (Thread.currentThread() != this.minecraftThread) 141 if (Thread.currentThread() != this.minecraftThread)
142 { 142 {
  143 + this.logger.severe("Profiler cross thread access detected, this indicates an error with one of your mods.");
143 throw new ProfilerCrossThreadAccessException(Thread.currentThread().getName()); 144 throw new ProfilerCrossThreadAccessException(Thread.currentThread().getName());
144 } 145 }
145 146
@@ -203,6 +204,7 @@ public class HookProfiler extends Profiler @@ -203,6 +204,7 @@ public class HookProfiler extends Profiler
203 { 204 {
204 if (Thread.currentThread() != this.minecraftThread) 205 if (Thread.currentThread() != this.minecraftThread)
205 { 206 {
  207 + this.logger.severe("Profiler cross thread access detected, this indicates an error with one of your mods.");
206 throw new ProfilerCrossThreadAccessException(Thread.currentThread().getName()); 208 throw new ProfilerCrossThreadAccessException(Thread.currentThread().getName());
207 } 209 }
208 210
java/com/mumfrey/liteloader/core/hooks/asm/ASMHookProxy.java 0 → 100644
  1 +package com.mumfrey.liteloader.core.hooks.asm;
  2 +
  3 +import com.mumfrey.liteloader.core.Events;
  4 +import com.mumfrey.liteloader.core.LiteLoader;
  5 +import com.mumfrey.liteloader.core.PluginChannels;
  6 +
  7 +import net.minecraft.src.NetHandler;
  8 +import net.minecraft.src.Packet1Login;
  9 +import net.minecraft.src.Packet250CustomPayload;
  10 +import net.minecraft.src.Packet3Chat;
  11 +
  12 +/**
  13 + * Proxy class which handles the redirected calls from the injected packet hooks and routes them to the
  14 + * relevant liteloader handler classes. We do this rather than patching a bunch of bytecode into the packet
  15 + * classes themselves because this is easier to maintain.
  16 + *
  17 + * @author Adam Mummery-Smith
  18 + */
  19 +public class ASMHookProxy
  20 +{
  21 + /**
  22 + * Packet3Chat::processPacket()
  23 + *
  24 + * @param netHandler
  25 + * @param packet
  26 + */
  27 + public void handleChatPacket(NetHandler netHandler, Packet3Chat packet)
  28 + {
  29 + Events events = LiteLoader.getEvents();
  30 + if (events.onChat(packet))
  31 + {
  32 + netHandler.handleChat(packet);
  33 + }
  34 + }
  35 +
  36 + /**
  37 + * Packet3Chat::processPacket()
  38 + *
  39 + * @param netHandler
  40 + * @param packet
  41 + */
  42 + public void handleLoginPacket(NetHandler netHandler, Packet1Login packet)
  43 + {
  44 + Events events = LiteLoader.getEvents();
  45 + if (events.onPreLogin(netHandler, packet))
  46 + {
  47 + netHandler.handleLogin(packet);
  48 + events.onConnectToServer(netHandler, packet);
  49 + }
  50 + }
  51 +
  52 + /**
  53 + * Packet3Chat::processPacket()
  54 + *
  55 + * @param netHandler
  56 + * @param packet
  57 + */
  58 + public void handleCustomPayloadPacket(NetHandler netHandler, Packet250CustomPayload packet)
  59 + {
  60 + netHandler.handleCustomPayload(packet);
  61 +
  62 + PluginChannels pluginChannels = LiteLoader.getPluginChannels();
  63 + pluginChannels.onPluginChannelMessage(packet);
  64 + }
  65 +}
java/com/mumfrey/liteloader/core/hooks/asm/ChatPacketTransformer.java 0 → 100644
  1 +package com.mumfrey.liteloader.core.hooks.asm;
  2 +
  3 +/**
  4 + * Transformer for Packet 3 (chat)
  5 + *
  6 + * @author Adam Mummery-Smith
  7 + */
  8 +public class ChatPacketTransformer extends PacketTransformer
  9 +{
  10 + private static boolean injected = false;
  11 +
  12 + public ChatPacketTransformer()
  13 + {
  14 + // TODO Obfuscation 1.6.4
  15 + super("net.minecraft.src.Packet3Chat", "dm", "handleChatPacket");
  16 + }
  17 +
  18 + @Override
  19 + protected void notifyInjected()
  20 + {
  21 + ChatPacketTransformer.injected = true;
  22 + }
  23 +
  24 + public static boolean isInjected()
  25 + {
  26 + return ChatPacketTransformer.injected;
  27 + }
  28 +}
java/com/mumfrey/liteloader/core/hooks/asm/CrashReportTransformer.java 0 → 100644
  1 +package com.mumfrey.liteloader.core.hooks.asm;
  2 +
  3 +import java.util.ListIterator;
  4 +
  5 +import net.minecraft.launchwrapper.IClassTransformer;
  6 +
  7 +import org.objectweb.asm.ClassReader;
  8 +import org.objectweb.asm.ClassWriter;
  9 +import org.objectweb.asm.Opcodes;
  10 +import org.objectweb.asm.tree.AbstractInsnNode;
  11 +import org.objectweb.asm.tree.ClassNode;
  12 +import org.objectweb.asm.tree.InsnList;
  13 +import org.objectweb.asm.tree.MethodInsnNode;
  14 +import org.objectweb.asm.tree.MethodNode;
  15 +import org.objectweb.asm.tree.VarInsnNode;
  16 +
  17 +public class CrashReportTransformer implements IClassTransformer
  18 +{
  19 + private static final String classMappingCallableJVMFlags = "net.minecraft.src.CallableJVMFlags";
  20 +
  21 + // TODO Obfuscation 1.6.4
  22 + private static final String classMappingCallableJVMFlagsObf = "h";
  23 +
  24 + @Override
  25 + public byte[] transform(String name, String transformedName, byte[] basicClass)
  26 + {
  27 + if (classMappingCallableJVMFlags.equals(name) || classMappingCallableJVMFlagsObf.equals(name))
  28 + {
  29 + try
  30 + {
  31 + return this.transformCallableJVMFlags(basicClass);
  32 + }
  33 + catch (Exception ex) {}
  34 + }
  35 +
  36 + return basicClass;
  37 + }
  38 +
  39 + /**
  40 + * Inject the additional callback for populating the crash report into the CallableJVMFlags class
  41 + *
  42 + * @param basicClass basic class
  43 + * @return transformed class
  44 + */
  45 + private byte[] transformCallableJVMFlags(byte[] basicClass)
  46 + {
  47 + ClassReader classReader = new ClassReader(basicClass);
  48 + ClassNode classNode = new ClassNode();
  49 + classReader.accept(classNode, ClassReader.EXPAND_FRAMES);
  50 +
  51 + for (MethodNode method : classNode.methods)
  52 + {
  53 + if ("<init>".equals(method.name))
  54 + {
  55 + this.transformCallableJVMFlagsConstructor(method);
  56 + }
  57 + }
  58 +
  59 + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
  60 + classNode.accept(writer);
  61 + return writer.toByteArray();
  62 + }
  63 +
  64 + /**
  65 + * @param ctor
  66 + */
  67 + public void transformCallableJVMFlagsConstructor(MethodNode ctor)
  68 + {
  69 + InsnList code = new InsnList();
  70 + code.add(new VarInsnNode(Opcodes.ALOAD, 1));
  71 + code.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/mumfrey/liteloader/core/LiteLoader", "populateCrashReport", "(Ljava/lang/Object;)V"));
  72 +
  73 + ListIterator<AbstractInsnNode> insns = ctor.instructions.iterator();
  74 + while (insns.hasNext())
  75 + {
  76 + AbstractInsnNode insnNode = insns.next();
  77 + if (insnNode.getOpcode() == Opcodes.RETURN)
  78 + ctor.instructions.insertBefore(insnNode, code);
  79 + }
  80 + }
  81 +}
java/com/mumfrey/liteloader/core/hooks/asm/CustomPayloadPacketTransformer.java 0 → 100644
  1 +package com.mumfrey.liteloader.core.hooks.asm;
  2 +
  3 +/**
  4 + * Transformer for Packet 250 (custom paylor)
  5 + *
  6 + * @author Adam Mummery-Smith
  7 + */
  8 +public class CustomPayloadPacketTransformer extends PacketTransformer
  9 +{
  10 + private static boolean injected = false;
  11 +
  12 + public CustomPayloadPacketTransformer()
  13 + {
  14 + // TODO Obfuscation 1.6.4
  15 + super("net.minecraft.src.Packet250CustomPayload", "ea", "handleCustomPayloadPacket");
  16 + }
  17 +
  18 + @Override
  19 + protected void notifyInjected()
  20 + {
  21 + CustomPayloadPacketTransformer.injected = true;
  22 + }
  23 +
  24 + public static boolean isInjected()
  25 + {
  26 + return CustomPayloadPacketTransformer.injected;
  27 + }
  28 +}
java/com/mumfrey/liteloader/core/hooks/asm/LoginPacketTransformer.java 0 → 100644
  1 +package com.mumfrey.liteloader.core.hooks.asm;
  2 +
  3 +/**
  4 + * Transformer for Packet 1 (login)
  5 + *
  6 + * @author Adam Mummery-Smith
  7 + */
  8 +public class LoginPacketTransformer extends PacketTransformer
  9 +{
  10 + private static boolean injected = false;
  11 +
  12 + public LoginPacketTransformer()
  13 + {
  14 + // TODO Obfuscation 1.6.4
  15 + super("net.minecraft.src.Packet1Login", "ep", "handleLoginPacket");
  16 + }
  17 +
  18 + @Override
  19 + protected void notifyInjected()
  20 + {
  21 + LoginPacketTransformer.injected = true;
  22 + }
  23 +
  24 + public static boolean isInjected()
  25 + {
  26 + return LoginPacketTransformer.injected;
  27 + }
  28 +}
0 \ No newline at end of file 29 \ No newline at end of file
java/com/mumfrey/liteloader/core/hooks/asm/PacketTransformer.java 0 → 100644
  1 +package com.mumfrey.liteloader.core.hooks.asm;
  2 +
  3 +import java.lang.reflect.Field;
  4 +import java.util.List;
  5 +
  6 +import org.objectweb.asm.ClassReader;
  7 +import org.objectweb.asm.ClassWriter;
  8 +import org.objectweb.asm.Opcodes;
  9 +import org.objectweb.asm.tree.ClassNode;
  10 +import org.objectweb.asm.tree.FieldInsnNode;
  11 +import org.objectweb.asm.tree.FieldNode;
  12 +import org.objectweb.asm.tree.InsnNode;
  13 +import org.objectweb.asm.tree.MethodInsnNode;
  14 +import org.objectweb.asm.tree.MethodNode;
  15 +import org.objectweb.asm.tree.VarInsnNode;
  16 +
  17 +import net.minecraft.launchwrapper.IClassTransformer;
  18 +
  19 +/**
  20 + * Class transformer which transforms a Packet class and alters the "processPacket" function to call the specified
  21 + * callback method in ASMHookProxy instead of the usual behaviour of calling handleXXXPacket in NetClientHandler.
  22 + *
  23 + * @author Adam Mummery-Smith
  24 + */
  25 +public abstract class PacketTransformer implements IClassTransformer
  26 +{
  27 + private static final String netHandlerClass = "net/minecraft/src/NetHandler";
  28 + private static final String processPacketMethod = "processPacket";
  29 +
  30 + // TODO Obfuscation 1.6.4
  31 + private static final String netHandlerClassObf = "ez";
  32 + private static final String processPacketMethodObf = "a";
  33 +
  34 + private final String packetClass;
  35 + private final String packetClassObf;
  36 +
  37 + private final String handlerMethodName;
  38 +
  39 + /**
  40 + * ctor
  41 + * @param packetClass Packet class name we want to override (FQ)
  42 + * @param packetClassObf Obfuscated packet class name
  43 + * @param handlerMethodName Method name to map to in handlerClass (must have signature (NetHandler, PacketClass)Void)
  44 + */
  45 + protected PacketTransformer(String packetClass, String packetClassObf, String handlerMethodName)
  46 + {
  47 + this.packetClass = packetClass;
  48 + this.packetClassObf = packetClassObf;
  49 + this.handlerMethodName = handlerMethodName;
  50 + }
  51 +
  52 + /* (non-Javadoc)
  53 + * @see net.minecraft.launchwrapper.IClassTransformer#transform(java.lang.String, java.lang.String, byte[])
  54 + */
  55 + @Override
  56 + public byte[] transform(String name, String transformedName, byte[] basicClass)
  57 + {
  58 + if (this.packetClass.equals(name) || this.packetClassObf.equals(name))
  59 + {
  60 + try
  61 + {
  62 + byte[] transformedClass = this.transformClass(name, basicClass);
  63 + this.notifyInjected();
  64 + return transformedClass;
  65 + }
  66 + catch (Exception ex) {}
  67 + }
  68 +
  69 + return basicClass;
  70 + }
  71 +
  72 + /**
  73 + * Found the packet class we want to transform, attempt to transform it
  74 + *
  75 + * @param className
  76 + * @param basicClass
  77 + * @return
  78 + */
  79 + private byte[] transformClass(String className, byte[] basicClass)
  80 + {
  81 + boolean transformed = true;
  82 +
  83 + ClassReader classReader = new ClassReader(basicClass);
  84 + ClassNode classNode = new ClassNode();
  85 + classReader.accept(classNode, ClassReader.EXPAND_FRAMES);
  86 +
  87 + // Try and transform obfuscated first
  88 + if (!this.tryTransformMethod(className, classNode, PacketTransformer.processPacketMethodObf, PacketTransformer.netHandlerClassObf))
  89 + {
  90 + // Try to transform non-obf for use in dev env
  91 + if (!this.tryTransformMethod(className, classNode, PacketTransformer.processPacketMethod, PacketTransformer.netHandlerClass))
  92 + {
  93 + transformed = false;
  94 + }
  95 + }
  96 +
  97 + // If we successfully transformed the method, transform the class and add a private static field "proxy" which will hold the handler
  98 + if (transformed)
  99 + {
  100 + classNode.fields.add(new FieldNode(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, "proxy", "Lcom/mumfrey/liteloader/core/hooks/asm/ASMHookProxy;", null, null));
  101 + }
  102 +
  103 + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
  104 + classNode.accept(writer);
  105 + return writer.toByteArray();
  106 + }
  107 +
  108 + /**
  109 + * @param className
  110 + * @param classNode
  111 + * @param functionName
  112 + * @param netHandlerClassName
  113 + */
  114 + private boolean tryTransformMethod(String className, ClassNode classNode, String functionName, String netHandlerClassName)
  115 + {
  116 + MethodNode method = this.findMethodByNameAndSignature(classNode.methods, functionName, "(L" + netHandlerClassName + ";)V");
  117 +
  118 + if (method != null)
  119 + {
  120 + String targetMethodSig = "(L" + netHandlerClassName + ";L" + className.replace('.', '/') + ";)V";
  121 + this.transformMethod(className, method, targetMethodSig);
  122 + return true;
  123 + }
  124 +
  125 + return false;
  126 + }
  127 +
  128 + /**
  129 + * Clear the old method contents and replace with the call to our handler function
  130 + *
  131 + * @param method
  132 + * @param targetMethodSig
  133 + */
  134 + private void transformMethod(String className, MethodNode method, String targetMethodSig)
  135 + {
  136 + // Dump the old method content, we don't want it any more
  137 + method.instructions.clear();
  138 +
  139 + // Get the value of the "proxy" field from the object on the stack (which is "this")
  140 + method.instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, className.replace('.', '/'), "proxy", "Lcom/mumfrey/liteloader/core/hooks/asm/ASMHookProxy;"));
  141 +
  142 + // Push method argument 1 (NetHandler instance) onto the stack
  143 + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
  144 +
  145 + // Push "this" onto the stack
  146 + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
  147 +
  148 + // Invoke the handler function with the args we just pushed onto the stack
  149 + method.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "com/mumfrey/liteloader/core/hooks/asm/ASMHookProxy", this.handlerMethodName, targetMethodSig));
  150 +
  151 + // Return
  152 + method.instructions.add(new InsnNode(Opcodes.RETURN));
  153 + }
  154 +
  155 + /**
  156 + * For subclasses, to set a local flag indicating the code was successfully injected
  157 + */
  158 + protected abstract void notifyInjected();
  159 +
  160 + /**
  161 + * @param classNode
  162 + * @param funcName
  163 + * @param funcNameObf
  164 + * @param funcSig
  165 + * @param funcSigObf
  166 + * @return
  167 + */
  168 + private MethodNode findMethodByNameAndSignature(List<MethodNode> methods, String funcName, String funcSig)
  169 + {
  170 + for (MethodNode method : methods)
  171 + {
  172 + if (funcName.equals(method.name) && funcSig.equals(method.desc))
  173 + return method;
  174 + }
  175 +
  176 + return null;
  177 + }
  178 +
  179 + /**
  180 + * Register the proxy (handler) for a packet
  181 + *
  182 + * @param packetClass
  183 + * @param proxy
  184 + */
  185 + public static void registerProxy(Class<?> packetClass, ASMHookProxy proxy)
  186 + {
  187 + try
  188 + {
  189 + Field fProxy = packetClass.getDeclaredField("proxy");
  190 + fProxy.setAccessible(true);
  191 + fProxy.set(null, proxy);
  192 + }
  193 + catch (Exception ex)
  194 + {
  195 + ex.printStackTrace();
  196 + }
  197 + }
  198 +}
0 \ No newline at end of file 199 \ No newline at end of file
java/com/mumfrey/liteloader/launch/LiteLoaderTransformer.java
1 package com.mumfrey.liteloader.launch; 1 package com.mumfrey.liteloader.launch;
2 2
3 -import java.util.ListIterator;  
4 -  
5 import net.minecraft.launchwrapper.IClassTransformer; 3 import net.minecraft.launchwrapper.IClassTransformer;
6 4
7 -import org.objectweb.asm.ClassReader;  
8 -import org.objectweb.asm.ClassWriter;  
9 -import org.objectweb.asm.Opcodes;  
10 -import org.objectweb.asm.tree.AbstractInsnNode;  
11 -import org.objectweb.asm.tree.ClassNode;  
12 -import org.objectweb.asm.tree.InsnList;  
13 -import org.objectweb.asm.tree.MethodInsnNode;  
14 -import org.objectweb.asm.tree.MethodNode;  
15 -import org.objectweb.asm.tree.VarInsnNode;  
16 -  
17 public class LiteLoaderTransformer implements IClassTransformer 5 public class LiteLoaderTransformer implements IClassTransformer
18 { 6 {
19 private static final String classMappingRenderLightningBolt = "net.minecraft.src.RenderLightningBolt"; 7 private static final String classMappingRenderLightningBolt = "net.minecraft.src.RenderLightningBolt";
20 - private static final String classMappingCallableJVMFlags = "net.minecraft.src.CallableJVMFlags";  
21 8
22 // TODO Obfuscation 1.6.4 9 // TODO Obfuscation 1.6.4
23 private static final String classMappingRenderLightningBoltObf = "bha"; 10 private static final String classMappingRenderLightningBoltObf = "bha";
24 - private static final String classMappingCallableJVMFlagsObf = "h";  
25 11
26 private static boolean postInit = false; 12 private static boolean postInit = false;
27 13
@@ -35,58 +21,6 @@ public class LiteLoaderTransformer implements IClassTransformer @@ -35,58 +21,6 @@ public class LiteLoaderTransformer implements IClassTransformer
35 LiteLoaderTweaker.postInit(); 21 LiteLoaderTweaker.postInit();
36 } 22 }
37 23
38 - if (classMappingCallableJVMFlags.equals(name) || classMappingCallableJVMFlagsObf.equals(name))  
39 - {  
40 - try  
41 - {  
42 - return this.transformCallableJVMFlags(basicClass);  
43 - }  
44 - catch (Exception ex) {}  
45 - }  
46 -  
47 return basicClass; 24 return basicClass;
48 } 25 }
49 -  
50 - /**  
51 - * Inject the additional callback for populating the crash report into the CallableJVMFlags class  
52 - *  
53 - * @param basicClass basic class  
54 - * @return transformed class  
55 - */  
56 - private byte[] transformCallableJVMFlags(byte[] basicClass)  
57 - {  
58 - ClassReader classReader = new ClassReader(basicClass);  
59 - ClassNode classNode = new ClassNode();  
60 - classReader.accept(classNode, ClassReader.EXPAND_FRAMES);  
61 -  
62 - for (MethodNode method : classNode.methods)  
63 - {  
64 - if ("<init>".equals(method.name))  
65 - {  
66 - this.transformCallableJVMFlagsConstructor(method);  
67 - }  
68 - }  
69 -  
70 - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);  
71 - classNode.accept(writer);  
72 - return writer.toByteArray();  
73 - }  
74 -  
75 - /**  
76 - * @param ctor  
77 - */  
78 - public void transformCallableJVMFlagsConstructor(MethodNode ctor)  
79 - {  
80 - InsnList code = new InsnList();  
81 - code.add(new VarInsnNode(Opcodes.ALOAD, 1));  
82 - code.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "com/mumfrey/liteloader/core/LiteLoader", "populateCrashReport", "(Ljava/lang/Object;)V"));  
83 -  
84 - ListIterator<AbstractInsnNode> insns = ctor.instructions.iterator();  
85 - while (insns.hasNext())  
86 - {  
87 - AbstractInsnNode insnNode = insns.next();  
88 - if (insnNode.getOpcode() == Opcodes.RETURN)  
89 - ctor.instructions.insertBefore(insnNode, code);  
90 - }  
91 - }  
92 } 26 }
java/com/mumfrey/liteloader/launch/LiteLoaderTweaker.java
@@ -61,6 +61,14 @@ public class LiteLoaderTweaker implements ITweaker @@ -61,6 +61,14 @@ public class LiteLoaderTweaker implements ITweaker
61 61
62 private List<String> passThroughArgs; 62 private List<String> passThroughArgs;
63 63
  64 + private static final String[] requiredTransformers = {
  65 + "com.mumfrey.liteloader.launch.LiteLoaderTransformer",
  66 + "com.mumfrey.liteloader.core.hooks.asm.CrashReportTransformer",
  67 + "com.mumfrey.liteloader.core.hooks.asm.ChatPacketTransformer",
  68 + "com.mumfrey.liteloader.core.hooks.asm.LoginPacketTransformer",
  69 + "com.mumfrey.liteloader.core.hooks.asm.CustomPayloadPacketTransformer"
  70 + };
  71 +
64 @SuppressWarnings("unchecked") 72 @SuppressWarnings("unchecked")
65 @Override 73 @Override
66 public void acceptOptions(List<String> args, File gameDirectory, File assetsDirectory, String profile) 74 public void acceptOptions(List<String> args, File gameDirectory, File assetsDirectory, String profile)
@@ -124,33 +132,45 @@ public class LiteLoaderTweaker implements ITweaker @@ -124,33 +132,45 @@ public class LiteLoaderTweaker implements ITweaker
124 if (arg.startsWith("-")) 132 if (arg.startsWith("-"))
125 { 133 {
126 if (classifier != null) 134 if (classifier != null)
127 - classifier = this.addClassifiedArg(classifier, ""); 135 + {
  136 + this.addClassifiedArg(classifier, "");
  137 + classifier = null;
  138 + }
128 else if (arg.contains("=")) 139 else if (arg.contains("="))
129 - classifier = this.addClassifiedArg(arg.substring(0, arg.indexOf('=')), arg.substring(arg.indexOf('=') + 1)); 140 + {
  141 + this.addClassifiedArg(arg.substring(0, arg.indexOf('=')), arg.substring(arg.indexOf('=') + 1));
  142 + }
130 else 143 else
  144 + {
131 classifier = arg; 145 classifier = arg;
  146 + }
132 } 147 }
133 else 148 else
134 { 149 {
135 if (classifier != null) 150 if (classifier != null)
136 - classifier = this.addClassifiedArg(classifier, arg); 151 + {
  152 + this.addClassifiedArg(classifier, arg);
  153 + classifier = null;
  154 + }
137 else 155 else
138 this.singularLaunchArgs.add(arg); 156 this.singularLaunchArgs.add(arg);
139 } 157 }
140 } 158 }
141 } 159 }
142 160
143 - private String addClassifiedArg(String classifiedArg, String arg) 161 + private void addClassifiedArg(String classifiedArg, String arg)
144 { 162 {
145 this.launchArgs.put(classifiedArg, arg); 163 this.launchArgs.put(classifiedArg, arg);
146 - return null;  
147 } 164 }
148 165
149 @Override 166 @Override
150 public void injectIntoClassLoader(LaunchClassLoader classLoader) 167 public void injectIntoClassLoader(LaunchClassLoader classLoader)
151 { 168 {
152 - LiteLoaderTweaker.logger.info("Injecting LiteLoader class transformer");  
153 - classLoader.registerTransformer(LiteLoaderTransformer.class.getName()); 169 + for (String requiredTransformerClassName : LiteLoaderTweaker.requiredTransformers)
  170 + {
  171 + LiteLoaderTweaker.logger.info(String.format("Injecting required class transformer '%s'", requiredTransformerClassName));
  172 + classLoader.registerTransformer(requiredTransformerClassName);
  173 + }
154 174
155 for (String transformerClassName : LiteLoaderTweaker.modTransformers) 175 for (String transformerClassName : LiteLoaderTweaker.modTransformers)
156 { 176 {