It definitely seems like it's an issue with your IDE as opposed to with your code. If I were you, I'd try out Eclipse and see if it works on that IDE. Just to confirm that it's indeed an issue with the IDE.Consider this, i have "import asm.tree.InsnList" and I try to call the function ".add()" on an InsnList. This is a legitimate function, that actually exists on objects of type "InsnList". HOWEVER, IntelliJ produces an error here, saying " Cannot resolve symbol 'add' "...
View attachment 76
But when I check the module library, I can clearly see the method being in there... So I think IntelliJ is screwing with me, and that there's actually nothing wrong with the code.
View attachment 75
I am using a bit newer versions of the ASM jar files (8.0.1) but it didn't work before when I had 7.2 either, that's why I upgraded them to the newest version.
public Map<String, ClassNode> getClasses() {
return classes;
}
Interesting! If you make any progress, feel free to post it in a thread in the programming forum; I'd love to see. And thank you, I really tried to go above and beyond with this tutorial.Hi there, found this guide looking for a way to create a Runescape Classic injection/reflection bot via Google. It looks like this should be applicable to that version as well so hopefully I can get it working! I'm starting with the ORSC (Open Runescape Classic) local version but eventually would also like to work on RE for a private server, but just for the experience. Thanks for the great write-up, by far the best guide I've come across so far. I used to love writing scripts for the old bots way back when, but this is a whole different beast.
I'm having some difficulty getting the loader working for ORSC - I think it has something to do with the fact that the actual applet is started from a launcher and there may be something I'm missing with the initial settings... Would you rather I post it here (as I'm basically following this guide) or make a new thread since it's about RSC instead of RS2? Edit: Decided to post a new thread since it's probably going to contain a lot of info that isn't directly related to this thread's main purpose.Interesting! If you make any progress, feel free to post it in a thread in the programming forum; I'd love to see. And thank you, I really tried to go above and beyond with this tutorial.![]()
Hey Shenandoah,Hello, and welcome to my tutorial on how to write an Injection Bot for Runescape. The other tutorials that I've seen on other forums are pretty bad in my opinion, mainly because they don't explain how crucial aspects of the bot architecture works, leaving it up to the reader to figure out exactly how and why something works in the first place. This is my attempt to explain everything in detail, from how bytecode works, how to manipulate bytecode with the ASM library, to how the classloaders can be used to make the injection work, and why it works. This tutorial will be very detailed, so if you're looking for a quick explanation, you're probably in the wrong place. This tutorial will follow a bottom-up teaching approach, meaning that I'll start talking about the core basics of what you need to know to write your own bot, and then things will get increasingly more advanced until we've got a working prototype of our very own bot. I will assume that you already know Java, or at least the basics. If you don't know Java, you might as well stop reading now unless you like reading gibberish that doesn't make any sense. Anyways, let's get started!
Table of Contents
1. How do injection bots work?
2. Bytecode Manipulation, what is it?
3. Creating our own Runescape loader
4. Bytecode Basics
5. ASM Basics
6. Accessor methods & interfaces
7. The ClassLoader Hierarchy
8. Hijacking the canvas and drawing our own stuff
9. Creating our own mouse events and keyboard events
10. Conclusion
How do injection bots work?I'm sure you already have some idea of what an injection is, and what it does. And, you're correct! An injection bot is a bot that automates gameplay through injecting accessor and mutator methods, along with additional logic, into various classes, and using these methods at runtime to control the behaviour of the client. Have you ever seen those puppets that are controlled by pieces of string? Well, you can think of the client as the puppet, and the injection process as you attaching your own strings to a puppet that's already being controlled by someone else. Here is what the entire injection process looks like:
As you might have guessed already, the thing we are injecting into the class files is called bytecode.
Bytecode Manipulation, what is it?The Java Virtual Machine, the platform on which Java programs are ran, does not understand Java code. The JVM only understands bytecode. Bytecode for the JVM is like assembly for the processor. Java code must at some point be compiled into bytecode, and only then can it be ran on the JVM. This is what happens when you turn a Java file into a class file. Bytecode manipulation must then be the manipulation of the bytecode in a class file. We accomplish this by either using the Instrumentation API or depending on third-party libraries like ASM, JavaAssist or any other library. For this tutorial, we'll be using the ASM library. Because it is based on the Visitor design pattern and it's quite low-level, it might have a steep learning curve for you. However, if you're already familiar with JVM bytecode and the Visitor design pattern, this will probably be much easier for you. I'll be explaining how everything works nevertheless.
However, before we can get started actually using the ASM library, we need to download the jar file(s) and add them to our project. You can start out by downloading the required jar files.
Links:
Maven Repository: org.ow2.asm » asm » 7.2
mvnrepository.com
Maven Repository: org.ow2.asm » asm-tree » 7.2
mvnrepository.com
When you're done downloading the jar files, open up your IDE of choice and add the jar files to your external libraries folder. If you're using Eclipse, check out this tutorial on how to do it. If you're on Intellij IDEA like me, you can add the jar files to the project by doing the following:
1. Click File -> Project Structure...
2. Click Modules in the sidebar to the left.
3. Click the "Dependencies" tab.
4. Click the plus symbol right beside "Scope", and click "JARs or directories..."
5. Locate the two jar files on your system, and pick both.
Done!
Creating our own Runescape loaderI lied. I said this would be an injection bot, but in truth, it's a hybrid bot. The truth is, we're going to be using the Reflection library to actually load the client inside our own JFrame. Sorry for lying! To make it even worse, I won't be explaining the actual bytecode injection part until we're done loading the client. We absolutely need to do this before we can start changing the bytecode, since we obviously need class files to change in the first place.
Before we can get started with making our loader, we first need to actually download the client. Because I'm a bit paranoid, I'll be using a Runescape Private Server client to demonstrate bytecode injection instead of the real deal. However, the same concepts still apply for the real thing. If you want to try on the real thing, proceed at your own risk!
Click here to download the client.
You might be asking yourself: what is Reflection? Well, we can use bytecode injection and reflection to accomplish the same thing. The only difference is that bytecode injection obviously involves changing the class files, while reflection simply copies them so we can retrieve the data we want. For loading the applet into our own JFrame, reflection is sufficient. But once we start getting into things like overlaying the canvas and stuff, bytecode injection can make our job a whole lot easier. With reflection, you're limited to manipulating the attributes of class files, interfaces, and so on. Bytecode injection is a little faster too.
Are you ready? If so, start by creating a new project in your IDE of choice. Make sure you don't forget about importing the ASM jars to your project. Even though we won't be using them quite yet, it's best to just get it over with.
Alright. Before we start, let me explain just what we're going to be doing exactly. We're going to be creating our own JFrame object, and we'll be adding the applet of our target client into our JFrame object. To illustrate further, let's open up the client in jd-gui, which is a Java decompiler so we can see the Java code of the class files in the Jar file. I always like to do this first because we need a general idea of the architeccture of the program before we can start thinking about ways to manipulate it. This rings true for other programs that aren't java programs.
Okay, so we're greeted with this window.
View attachment 3
If we look in the manifest of the JAR file, we can see that the class called "Main" is the main class. How appropriate! Let's take a look, shall we?
Interesting... As we investigate further, we quickly realize that the Game class is where everything happens. The Game class' superclass is RSApplet. Hmm, I think we're getting close. I wrote earlier about how we wanted to add the client's applet to our own JFrame object. Well, take a look at the RSApplet class code:Code:public final class Main { public static void main(String[] args) { if (args.length > 1) { System.out.println("Running local"); ClientSettings.SERVER_IP = "127.0.0.1"; } try { Game game = new Game(); Game.nodeID = 10; Game.portOff = 0; Game.setHighMem(); Game.isMembers = true; Signlink.storeid = 32; Signlink.startpriv(InetAddress.getLocalHost()); game.createClientFrame(503, 765); } catch (UnknownHostException e) { e.printStackTrace(); } } }
This class extends the Applet class. That means we can basically treat this class like an applet. This is great news! To make things easier for us, however, we're going to be mimicking the Main class. We'll be creating the Game object and configuring the Signlink class, all of this with reflection, so we can set the same values that are present in the Main class file. We're going to do almost exactly the same thing as the Main class, with one exception: the last piece of code. We want to avoid calling the createClientFrame method because this creates its own JFrame. But if you take a look in the RSApplet class (where the createClientFrame code actually resides), you can see another interesting method right beside it. The initClientFrame method which does everything the createClientFrame method does, except for actually creating the JFrame object (RSFrame). This is amazing, this is just what we need. Let's get started with the basics of Reflection, so we can start creating our loader!Code:public class RSApplet extends Applet implements Runnable, MouseListener, MouseMotionListener, KeyListener, FocusListener, WindowListener {
Although I'm impressed that you're taking on such a big project so quickly, I think you might underestimate just how hard it is, especially if you're planning on making a client for the real game. I have personally tested everything in my tutorial, and I've made sure it works. Depending on what client you use however, you'll have to adjust your approach accordingly. I used an RSPS as an example in my tutorial, and the code in the real client is different. Focus on the information in the tutorial instead of the actual code because you will absolutely have to write different code for whatever you're working with anyway.Hey Shenandoah,
Last couple weeks im trying to make a RuneScape bot client. I am playing the game for about 15 years myself. I have some Visual Studio C# knowledge, so thats the place I started. Most examples I found are some simple web forms which only redirect to the web page from RumeScape. Online Youtube video’s use mostly AHK or other script languages. If you don’t want a color bot high chances you move to Java. I tried to find old bots since they are less developed and might be easy to learn from like rsbot, powerbot. Most are not opensource so I moved to github, most RuneScape related Java apps have some problems. They just don’t startup, outdated libaries or not working with real client because jagex obfuscated their .jar file once week.
Basically I want to make a bot client which can read, start, stop a script and can read id’s from items and ingame objects.
Ive tried but I can’t make it past chapter 2. It Is not step by step, I really want to understand how to make your project and make a documentation for myself. The knowledge is a basis for me to implement other github examples into it.
If someone has a completed version I would love to see. So I can write my own comments at the code.
Leave me a message and I get in contact. This is for learning only!![]()
Thanks for your reply.Although I'm impressed that you're taking on such a big project so quickly, I think you might underestimate just how hard it is, especially if you're planning on making a client for the real game. I have personally tested everything in my tutorial, and I've made sure it works. Depending on what client you use however, you'll have to adjust your approach accordingly. I used an RSPS as an example in my tutorial, and the code in the real client is different. Focus on the information in the tutorial instead of the actual code because you will absolutely have to write different code for whatever you're working with anyway.
Unfortunately, I don't have the files anymore for this tutorial. I might go through the tutorial and create a new project eventually, but I simply don't have the time right now. Personally, I've found Parabot to be quite educational. Read through the source code, and you'll see how they approach it, and you might also get some idea as to how you're going to structure your code, what design patterns to use, etc.
I'm sorry I can't be of much more help.
Hey, thanks man! I'm glad you like the tutorial.Hey Shenandoah! I know this is a pretty old post, but I hope you're still willing to answer one of my questions! I just learned a lot about bytecode in Java so I was drawn to this post and I've been wanting to take a stab at reflection to learn! First off I wanna say how amazing this tutorial really is, but my question pertains less to making a bot. It's more about `casting` and `reflection`!
REFERENCE IMAGE:
View attachment 82
(Sorry about my overly wordy comments, I like to comment everything im learning or doing on new projects because it helps stick it in my mind better! Also I changed my comments to reflect what casting actually does, and forgot to retake the screenshot.)
So in the line `Applet applet = (Applet) init.newInstance();` we are using the constructor we got from the instance `clazz` ( a `Class` instance that represents the class `Game`) to make a new instance of `Game` (since casting only changes the type of the reference/pointer variable not the instance on the heap). So this doesnt mess with me all too much since it makes sense, `Game` is a subclass of `RSApplet` which itself is a subclass of `Applet` it being casted to Applet is perfectly fine.
View attachment 83
And since we use the constructor reflected from `Game` the instance `applet` should only have instance variables from the `Game` class like `nodeID` (I want to make sure this is correct thinking!). Which makes sense or else it would not be able to set the instance variable `nodeID` to 10 that only resides in the `Game` class and not the `Applet` class in the line pictured below:
View attachment 84
So my question is, why are we casting the the `Game` instance to an `Applet`, if it does not get any of `Applet`'s instance variables? Is it because we want to use specific methods that need an `Applet` data type and not a `Game` data type later?
I hope I explained it well enough! If you need me to elaborate anymore I will gladly do so!
Sorry for such a long wait on the reply (had a lot of classes ahaha)! Okay, so that makes a lot of sense, and I've been doing some reading on Applets in general! But I do have one question, wouldn't the `Game` class also be a container type (since it extends `RSApplet` which extends `Applet` which extends `Panel` which finally extends `Container`)? So if my thinking is right wouldnt that mean that making `applet` a `Game` instance (object) (not casting it to `Applet`) would still allow it to be used as a parameter to perform the `add()` instance method on the `frame` `JFrame` instance?Hey, thanks man! I'm glad you like the tutorial.
The main reason why we're casting the Game instance to an Applet type is because we want to add the applet to the JFrame via the add method. So, yes, we're casting it to an applet because the add method requires a Container type, which the Applet class is. This is so that we can render the applet inside our JFrame. Does that make sense?
It's nice to see someone who's being thorough. Before I explain, let me just warn you that it's been a while since I wrote this tutorial, so I don't remember everything that's going on in the tutorial. Because it's been such a long time, I'm definitely susceptible to making mistakes.Sorry for such a long wait on the reply (had a lot of classes ahaha)! Okay, so that makes a lot of sense, and I've been doing some reading on Applets in general! But I do have one question, wouldn't the `Game` class also be a container type (since it extends `RSApplet` which extends `Applet` which extends `Panel` which finally extends `Container`)? So if my thinking is right wouldnt that mean that making `applet` a `Game` instance (object) (not casting it to `Applet`) would still allow it to be used as a parameter to perform the `add()` instance method on the `frame` `JFrame` instance?
Below is a picture of me using just a `Game applet` in the `frame.add(applet)` method, (and it working correctly) and then trying other Class types that do not extend `Container` somewhere in their heirarchy (and them throwing errors):
View attachment 85
View attachment 86
Below is the error the add methods threw:
View attachment 87
Finally, below is the printed out array of components currently in the `JFrame frame` after adding the `Game applet` (also side question! does `frame.add(apple)` actually add `applet` as a component in a `Container` and not a `JFrame`?):
View attachment 88
Im sorry if this is a lot to dig through! This is just my first time working with Applets regrettably, and so I didnt know if there were special hidden ideas or hoops to jump through for using Applets with reflected Classes! I should honestly finish the tutorial and see what happens if I go back and not cast `applet` to `Applet`. Again I cannot thank you enough for your help!
Happy easter! Hope you had a good time with your family.QUESTION: So when you say that, the `Game` class cannot be used to cast since it isnt in our repository, do you mean that in the sense that, since we are using reflection it would be counter intuitive? Or maybe its something completely different that I am missing! Basically what I am saying is, you fully answered my last question but now it opened the door to that new question (I promise I wont ask anymore questions on this ahaha)!
I know its been a long time since you made this tutorial so its probably not easy to go back and help especially on little things like this, but I cant thank you enough for doing it!
Thank you for the green to go on experimenting! Gives me a sort of mission besides just completing the tutorial! And you're right, couldnt have said it better myself, I should try and go through everything, and dig into the main bits like I wanted at the start with the bytecode, before getting bogged down! Already learned a lot of new things I never bothered to ever dive into (like up/down casting (narrowing/widening), and how generics are used all throughout reflection). Anyway, thank you again, and if I run into anything else I get stumped on I'll let you know!Happy easter! Hope you had a good time with your family.
I can see that you've added the client jar itself to the referenced libraries. That changes things a little. This is how you're able to reference the Game class, and other classes from that jar file as well. I didn't actually do this in the tutorial, and I would argue that it's a bit unnecessary to do so. The only reason I can think of for adding the client jar to the referenced libraries would be just to be able to cast the instance to a Game instance instead of an Applet instance. It's going to be the same, but there's really no reason not to just cast it to an Applet instead.
I still think it's good that you're deviating from the tutorial a bit, and that you're experimenting. That's how you learn the most, so I think you should continue doing that. There is a lot of content to go through in this tutorial though, so it might be a good idea to get everything working in order first.