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,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 | { |