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