What is an injection client?
An injection client is one that modifies the client at runtime, rather than compile-time (Such as a client exported with Minecraft Coder Pack).
What is an agent and why do I need one?
An agent gives a program instrumentation capabilities which allows it to re-define classes on the fly. In the context of an injection client, it will be used to insert hooks.
What is ASM?
ASM is an all purpose Java bytecode manipulation and analysis framework written by the OW2 consortium.
Why ASM?
ASM is objectively the fastest library when it comes to bytecode modification. It will be used to make bytecode modification with agents simple. In this scenario we will be using ASM's tree API for inserting & changing existing classes.
Pro-tip: Use the source of ASM rather than downloading the binary. They compact their output and remove generics which makes iterating lists cumbersome. The source comes with the download which can be located here.
Where can I learn ASM?(Specifically the Tree API)
The official 4.0 documentation PDF (Page 95)
David Tiller's "Not-So-Secret Java Agents"
The examples provided in the ASM download
Step 0: Setting up your workspace
I will be using a library that I wrote. All of my examples will revolve around it. If you would like to use it I suggest downloading and adding the source to your project.
SkidHijack on Github
Step 1: Setting up an agent
Rather than re-hashing what's been said before I'll provide some good links to other tutorials on agents:
Oracle docs on Instrumentation
Basic agent setup
How to use ASM with Agent's instrumentation
Step 2: How to make Minecraft easy to access
There are multiple ways to approach this. I will only be going over which revolves around what I call "shell" classes. Shell classes are used to access class members regardless of their modifiers (private / final / protected). For example I've made a shell class of PlayerControllerMP.
Normally hitDelay and currentDamageMP aren't public. When we create our mods (Like Fastmine) we will referrer to this shell's fields & methods. Then we will have our agent rewrite all classes in our client package so that references to our shell classes are replaced with the original classes. As for the original classes, we will change all member's access to be public when possible (certain members should not be touched, such as the <clinit> and <init> methods).
Here is how I register classes & packages for bytecode modification:
Here is an example ClassModder implementation (PlayerModder):
Here is how I re-direct references from the shell class to the obfuscated ones: (Source too long for post)
RemappedName
ShellReplacementModder
Here is how I make everything accessible at runtime.
AccessModder
Step 3: Writing the client
This step should be familiar to you if you've written a client using Minecraft Coder Pack (Especially due to the shells). The only "hard" part is injecting events.
Here's an example of a cancellable event. It invokes a method that returns the cancelled state of the event.
Step 4: Compile & Use
Export as a normal jar.
Overwrite META-INF/Manifest.mf - Ensure Manifest's Agent attributes are correct.
Edit Minecraft launch args and play
Launch args: (Noverify is used because we may invalidate existing stack-frames with out changes. Using COMPUTE_FRAMES can remedy this but may result in some problems with reflection since it uses that in computing the new frames)
Credits
-------
GenericSkid
https://hentaiandecchi.ayzhin.com/forums/showthread.php?tid=4540
An injection client is one that modifies the client at runtime, rather than compile-time (Such as a client exported with Minecraft Coder Pack).
What is an agent and why do I need one?
An agent gives a program instrumentation capabilities which allows it to re-define classes on the fly. In the context of an injection client, it will be used to insert hooks.
What is ASM?
ASM is an all purpose Java bytecode manipulation and analysis framework written by the OW2 consortium.
Why ASM?
ASM is objectively the fastest library when it comes to bytecode modification. It will be used to make bytecode modification with agents simple. In this scenario we will be using ASM's tree API for inserting & changing existing classes.
Pro-tip: Use the source of ASM rather than downloading the binary. They compact their output and remove generics which makes iterating lists cumbersome. The source comes with the download which can be located here.
Where can I learn ASM?(Specifically the Tree API)
The official 4.0 documentation PDF (Page 95)
David Tiller's "Not-So-Secret Java Agents"
The examples provided in the ASM download
Getting Started
Step 0: Setting up your workspace
I will be using a library that I wrote. All of my examples will revolve around it. If you would like to use it I suggest downloading and adding the source to your project.
SkidHijack on Github
Step 1: Setting up an agent
Rather than re-hashing what's been said before I'll provide some good links to other tutorials on agents:
Oracle docs on Instrumentation
Basic agent setup
How to use ASM with Agent's instrumentation
Step 2: How to make Minecraft easy to access
There are multiple ways to approach this. I will only be going over which revolves around what I call "shell" classes. Shell classes are used to access class members regardless of their modifiers (private / final / protected). For example I've made a shell class of PlayerControllerMP.
Code:
Code:
@RemappedName(name = "blm")
public class PlayerControllerMP {
@RemappedName(name = "g")
public int hitDelay;
@RemappedName(name = "e")
public float currentDamage;
@RemappedName(name = "a")
public void attack(EntityPlayer attacker, Entity target) {}
}
Here is how I register classes & packages for bytecode modification:
Code:
Code:
// Modding a package
PackageMatcher clientPkgMatcher = new PackageMatcher("me/lpk/client");
clientPkgMatcher.addReceiver(new ShellReplacementModder(clientPkgMatcher, "mcshell"));
Refactorer.register(clientPkgMatcher);
// Modding a specific class
ClassMatcher player = new ClassMatcher("bnn");
player.addReceiver(new PlayerModder(player));
Refactorer.register(player);
Code:
Code:
public class PlayerModder extends ClassModder {
public PlayerModder(AbstractMatcher<?> matcher) {
super(matcher);
}
@Override
public void modify(ClassNode cn) {
for (MethodNode mn : cn.methods) {
if (mn.name.equals("m") && mn.desc.equals("()V")) {
// onUpdate
mn.instructions.insert(new MethodInsnNode(Opcodes.INVOKESTATIC, "Events", "callTick", "()V", false));
} else if (mn.name.equals("a") && mn.desc.equals("(Ljava/lang/String;)V")) {
LabelNode end = new LabelNode();
mn.instructions.insert(new JumpInsnNode(Opcodes.IFNE, end));
mn.instructions.insert(new MethodInsnNode(Opcodes.INVOKESTATIC, "Events", "callChatMessage", "(Ljava/lang/String;)Z", false));
mn.instructions.insert(new VarInsnNode(Opcodes.ALOAD, 1));
mn.instructions.add(end);
}
}
}
}
RemappedName
ShellReplacementModder
Here is how I make everything accessible at runtime.
AccessModder
Step 3: Writing the client
This step should be familiar to you if you've written a client using Minecraft Coder Pack (Especially due to the shells). The only "hard" part is injecting events.
Here's an example of a cancellable event. It invokes a method that returns the cancelled state of the event.
Code:
Code:
// EntityPlayerSP
@Override
public void modify(ClassNode cn) {
for (MethodNode mn : cn.methods) {
if (mn.name.equals("a") && mn.desc.equals("(Ljava/lang/String;)V")) {
// Logic (pseudo-code):
//
// boolean cancelled = Events.callChatMessage(parameter_string)
// if (cencelled) --> Jump to end of method
//
LabelNode end = new LabelNode();
mn.instructions.add(end);
mn.instructions.insert(new JumpInsnNode(Opcodes.IFNE, end));
mn.instructions.insert(new MethodInsnNode(Opcodes.INVOKESTATIC, "me/lpk/client/Events", "callChatMessage", "(Ljava/lang/String;)Z", false));
mn.instructions.insert(new VarInsnNode(Opcodes.ALOAD, 1));
}
}
}
Step 4: Compile & Use
Export as a normal jar.
Overwrite META-INF/Manifest.mf - Ensure Manifest's Agent attributes are correct.
Edit Minecraft launch args and play
Launch args: (Noverify is used because we may invalidate existing stack-frames with out changes. Using COMPUTE_FRAMES can remedy this but may result in some problems with reflection since it uses that in computing the new frames)
Code:
Code:
-javaagent:"path/to/Agent.jar" -noverify
Credits
-------
GenericSkid
https://hentaiandecchi.ayzhin.com/forums/showthread.php?tid=4540
Last edited:
