Commit 813a72f57c94031fdba852d9ab4fbcb7dd3d9496
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
Showing
12 changed files
with
519 additions
and
84 deletions
java/com/mumfrey/liteloader/core/Events.java
| ... | ... | @@ -12,6 +12,10 @@ import com.mumfrey.liteloader.Tickable; |
| 12 | 12 | import com.mumfrey.liteloader.core.hooks.HookChat; |
| 13 | 13 | import com.mumfrey.liteloader.core.hooks.HookLogin; |
| 14 | 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 | 19 | import com.mumfrey.liteloader.util.ModUtilities; |
| 16 | 20 | import com.mumfrey.liteloader.util.PrivateFields; |
| 17 | 21 | |
| ... | ... | @@ -37,6 +41,11 @@ public class Events implements IPlayerUsage |
| 37 | 41 | private final PluginChannels pluginChannels; |
| 38 | 42 | |
| 39 | 43 | /** |
| 44 | + * ASM hook proxy | |
| 45 | + */ | |
| 46 | + private final ASMHookProxy asmProxy; | |
| 47 | + | |
| 48 | + /** | |
| 40 | 49 | * Reference to the minecraft timer |
| 41 | 50 | */ |
| 42 | 51 | private Timer minecraftTimer; |
| ... | ... | @@ -144,11 +153,12 @@ public class Events implements IPlayerUsage |
| 144 | 153 | * @param minecraft |
| 145 | 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 | 158 | this.loader = loader; |
| 150 | 159 | this.minecraft = minecraft; |
| 151 | 160 | this.pluginChannels = pluginChannels; |
| 161 | + this.asmProxy = asmProxy; | |
| 152 | 162 | } |
| 153 | 163 | |
| 154 | 164 | /** |
| ... | ... | @@ -239,16 +249,34 @@ public class Events implements IPlayerUsage |
| 239 | 249 | if ((this.chatListeners.size() > 0 || this.chatFilters.size() > 0) && !this.chatHooked) |
| 240 | 250 | { |
| 241 | 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 | 265 | // Login hook |
| 247 | 266 | if ((this.preLoginListeners.size() > 0 || this.loginListeners.size() > 0) && !this.loginHooked) |
| 248 | 267 | { |
| 249 | 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 | 282 | // Tick hook |
| ... | ... | @@ -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 | 647 | * @param chatPacket |
| 620 | 648 | * @return | ... | ... |
java/com/mumfrey/liteloader/core/LiteLoader.java
| ... | ... | @@ -35,6 +35,7 @@ import net.minecraft.src.SimpleReloadableResourceManager; |
| 35 | 35 | import net.minecraft.src.World; |
| 36 | 36 | |
| 37 | 37 | import com.mumfrey.liteloader.*; |
| 38 | +import com.mumfrey.liteloader.core.hooks.asm.ASMHookProxy; | |
| 38 | 39 | import com.mumfrey.liteloader.crashreport.CallableLiteLoaderBrand; |
| 39 | 40 | import com.mumfrey.liteloader.crashreport.CallableLiteLoaderMods; |
| 40 | 41 | import com.mumfrey.liteloader.gui.GuiControlsPaginated; |
| ... | ... | @@ -149,6 +150,11 @@ public final class LiteLoader |
| 149 | 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 | 158 | * Event manager |
| 153 | 159 | */ |
| 154 | 160 | private Events events; |
| ... | ... | @@ -156,7 +162,7 @@ public final class LiteLoader |
| 156 | 162 | /** |
| 157 | 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 | 168 | * Permission Manager |
| ... | ... | @@ -350,7 +356,7 @@ public final class LiteLoader |
| 350 | 356 | this.minecraft = minecraft; |
| 351 | 357 | |
| 352 | 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 | 361 | // Spawn mod instances |
| 356 | 362 | this.loadMods(); | ... | ... |
java/com/mumfrey/liteloader/core/PluginChannels.java
| ... | ... | @@ -12,6 +12,9 @@ import net.minecraft.src.Packet250CustomPayload; |
| 12 | 12 | |
| 13 | 13 | import com.mumfrey.liteloader.PluginChannelListener; |
| 14 | 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 | 18 | import com.mumfrey.liteloader.permissions.PermissionsManagerClient; |
| 16 | 19 | |
| 17 | 20 | /** |
| ... | ... | @@ -39,11 +42,16 @@ public class PluginChannels |
| 39 | 42 | * List of mods which implement PluginChannelListener interface |
| 40 | 43 | */ |
| 41 | 44 | private LinkedList<PluginChannelListener> pluginChannelListeners = new LinkedList<PluginChannelListener>(); |
| 45 | + | |
| 46 | + private ASMHookProxy asmProxy; | |
| 42 | 47 | |
| 43 | 48 | /** |
| 44 | 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 | 61 | // Plugin channels hook |
| 54 | 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 | 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 | 140 | { |
| 141 | 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 | 144 | throw new ProfilerCrossThreadAccessException(Thread.currentThread().getName()); |
| 144 | 145 | } |
| 145 | 146 | |
| ... | ... | @@ -203,6 +204,7 @@ public class HookProfiler extends Profiler |
| 203 | 204 | { |
| 204 | 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 | 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 | 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 | 199 | \ No newline at end of file | ... | ... |
java/com/mumfrey/liteloader/launch/LiteLoaderTransformer.java
| 1 | 1 | package com.mumfrey.liteloader.launch; |
| 2 | 2 | |
| 3 | -import java.util.ListIterator; | |
| 4 | - | |
| 5 | 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 | 5 | public class LiteLoaderTransformer implements IClassTransformer |
| 18 | 6 | { |
| 19 | 7 | private static final String classMappingRenderLightningBolt = "net.minecraft.src.RenderLightningBolt"; |
| 20 | - private static final String classMappingCallableJVMFlags = "net.minecraft.src.CallableJVMFlags"; | |
| 21 | 8 | |
| 22 | 9 | // TODO Obfuscation 1.6.4 |
| 23 | 10 | private static final String classMappingRenderLightningBoltObf = "bha"; |
| 24 | - private static final String classMappingCallableJVMFlagsObf = "h"; | |
| 25 | 11 | |
| 26 | 12 | private static boolean postInit = false; |
| 27 | 13 | |
| ... | ... | @@ -35,58 +21,6 @@ public class LiteLoaderTransformer implements IClassTransformer |
| 35 | 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 | 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 | 61 | |
| 62 | 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 | 72 | @SuppressWarnings("unchecked") |
| 65 | 73 | @Override |
| 66 | 74 | public void acceptOptions(List<String> args, File gameDirectory, File assetsDirectory, String profile) |
| ... | ... | @@ -124,33 +132,45 @@ public class LiteLoaderTweaker implements ITweaker |
| 124 | 132 | if (arg.startsWith("-")) |
| 125 | 133 | { |
| 126 | 134 | if (classifier != null) |
| 127 | - classifier = this.addClassifiedArg(classifier, ""); | |
| 135 | + { | |
| 136 | + this.addClassifiedArg(classifier, ""); | |
| 137 | + classifier = null; | |
| 138 | + } | |
| 128 | 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 | 143 | else |
| 144 | + { | |
| 131 | 145 | classifier = arg; |
| 146 | + } | |
| 132 | 147 | } |
| 133 | 148 | else |
| 134 | 149 | { |
| 135 | 150 | if (classifier != null) |
| 136 | - classifier = this.addClassifiedArg(classifier, arg); | |
| 151 | + { | |
| 152 | + this.addClassifiedArg(classifier, arg); | |
| 153 | + classifier = null; | |
| 154 | + } | |
| 137 | 155 | else |
| 138 | 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 | 163 | this.launchArgs.put(classifiedArg, arg); |
| 146 | - return null; | |
| 147 | 164 | } |
| 148 | 165 | |
| 149 | 166 | @Override |
| 150 | 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 | 175 | for (String transformerClassName : LiteLoaderTweaker.modTransformers) |
| 156 | 176 | { | ... | ... |