Jump to content

Primary: Sky Slate Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate Marble
Secondary: Sky Slate Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate Marble
Pattern: Blank Waves Squares Notes Sharp Wood Rockface Leather Honey Vertical Triangles
Welcome to RS-Hacking
Register now to gain access to all of our features. Once registered and logged in, you will be able to create topics, post replies to existing threads, give reputation to your fellow members, get your own private messenger, post status updates, manage your profile and so much more. This message will be removed once you have signed in.
Login to Account Create an Account

Most Liked Content

#11170 Introduction and Comprehensive Cache Format

Posted by Vulcanacht on 28 November 2015 - 01:57 AM

Instead of a standard introduction that you will never read more than once, I've opted to go for something more useful. My name is Dmitry. I am from Russia, but immigrated to the US when I was an infant. I've been writing code since I was 14, currently in my early 20s. Below is a comprehensive post on the cache system used in Old School RuneScape. The newer version of the game is almost the same, but block sizes are larger and has different indices and groups. This post is aimed at those with some, but not a complete knowledge of the cache format and contains helpful values which will help you in refactoring or creating an updater.
1. Basic Information
The cache starts with the directory structure in your local file system. Located in your user home directory, there is a folder called "jagexcache".  The home directory has the cache locator file which points to the absolute location of the specific game and release cycle that is requesting the information. There are older directories, but the client automatically migrates the data to the new directory. This directory houses all of the game files for each of Jagex's java-based games. In each subdirectory of this directory, you will see the individual game caches. They are housed in another subdirectory based on the software release cycle state they are in. The ones you will see mainly are LIVE and BETA. Once you get to the release stage folder, you are introduced to the binary assets. For Old School, these are comprised of 16 indices, a master index, and a data file. If you have played the game, a preference file will be here as well that stores your TOTP session ID and a few other preferences. Each of the 16 indices point to specific data contained in the data file. Each index contains a catalog of groups, represented by 6 bytes of data The first three tell the size of the overall file read and the next three represent the starting block in the data file. The master index file contains information about each group in the child indices such as file sizes, checksums, versions, etc. Finally, the data file consists of sectors of 520 bytes. The first 8 bytes of the sector represent metadata about the file, index, and next sector to load. The remaining 512 bytes are the payload of the sector. 
The hierarchy of the cache is basically:

  • Index
    • Group
      • Files

2. Indices and the Master Index
As mentioned above, the indices are catalogs for each group. They simply contain the size of the group in bytes and where the first starting block occurs. Each index houses a relevant group of data. During the packaging process before we are transmitted the data, the names are removed, but through some reverse engineering and refactoring, it is easy enough to identify what they are.  Here is the list and what they represent:

  • Index 0 - Skeletons - Data used for rigging animations
  • Index 1 - Meshes - Data which covers the surface of the skeleton
  • Index 2 - Configurations - The various types of data accessed in game such as NPCs, objects,locations, varbits, varps, etc.
  • Index 3 - Scaleform/Widgets - The components you interact with on screen
  • Index 4 - Sound Effects - Noises for spells, skills, etc
  • Index 5 - Landscapes - The map data for places you visit in-game. These are encrypted using XTEA
  • Index 6 - MIDI Track 1 - The first set of MIDI files in Jagex's custom format
  • Index 7 - Models - 3D models for characters, items, npcs, etc.
  • Index 8 - Sprites - Graphics for your interface and for some objects in the game
  • Index 9 - Textures - Graphics for roofs, floors, some items, etc.
  • Index 10 - Binary - Huffman codec and Title.jpg are the only two entries
  • Index 11 - MIDI Track 2 - More MIDI tracks
  • Index 12 - ClientScripts - Compiled scripts which control how certain interfaces and other actions work.
  • Index 13 - FontMetrics - Information about the game's fonts
  • Index 14 - Vorbis - Codecs for decoding the MIDI tracks used in game
  • Index 15 - MIDI Instruments - The various instruments from Jagex's soundbank         

To determine the amount of groups allocated for each index, simply divide the size of the file by 6. For example, since index 10 is 12 bytes of data, there are two groups cataloged. We know these to be the Huffman codecs and the title image. Just because a group is allocated, it does not mean it is in use and contains data.
Lets take a look at the Configuration index. Within the index, there are 32 possible groups. Here are 10 known groups:

  • Group 1 = Meshes
  • Group 3 = IdentityKit
  • Group 4 = Floors
  • Group 6 - Locations
  • Group 8 = EnumType
  • Group 9 = VarNpc
  • Group 10 = ObjType
  • Group 12 = SpotAnim
  • Group 14 = VarBit
  • Group 16 = VarPlayer         

Some groups are not known or are left out because they are specific to the game server. Within the Transformers Universe Beta client, there are configuration groups called "DBTableType" and "DBRowType". These are obviously references to a database structure and thus would not be included with the client. Another example is GameLogEvent. This is most likely actions players do and chat messages we send which can later be pulled up for handling reports. This is probably the system they use to manage player profiles instead of just a standard database.
Master Index

The master index is different. The file contains metadata about each of the groups in each index. Each sector may be compressed. The layout is as follows:

  • Byte 0 = Compression method
    • if the compression method is 0, that means there is no compression. Compression 1 indicates it is a BZIP2 compressed sector and compression 2 indicates it has been compressed using the GZIP compressor.
  • Byte 1 through 4 - Initial payload size
    • If there is no compression, the remainder of the payload is copied into a byte array and returned
  • Byte 5 through 9 - Decompressed payload size
    • When decompressed, the size of the payload should be this.

Later builds of the game use additional methods for compression such as LZMA and thus will need to be handled accordingly. Once the index catalog is unpacked, it must then be read to get information about each group. The structure is as follows:

  • The protocol number (byte)
    • For OSRS, this should be 5 through 7.
    • If the protocol is 6, four bytes which represent the version of the index are skipped for some reason
  • Flag to use name tables to look up groups by hashed names (byte)
  • File entry count (2 or 4 bytes based on protocol)
    • After this, the largest file id in the group is determined
  • If the client should use identity tables for hashes, an integer is read for each hash and a table is created for each group.
  • The cycle redundancy values are loaded
  • The versions are loaded
  • The number of children in each group is loaded
  • The wrapped buffers of the children are loaded. These are either a bytebuffer or byte array

Here are the two refactored functions used for the above sections.


Hashing Algorithm
When looking for a value using a string, that string is hashed using a modified Djb2 algorithm. The modification you will notice is the character is encoded with the CP1252 algorithm before being applied to the hash. The code looks like:

public static int jag_djb2(final String str) {
    final int length = str.length();
    int hashcode = 0;
    for (int c = 0; c < length; c++) {
        hashcode = (((hashcode << 5) - hashcode) + CP1252.encode(str.charAt(c)));
    return hashcode;

Here is the mostly refactored CP1252 encoding class from the mid 80's build of the game refactor I am working with. I haven't really seen anyone actually include this in any other refactors or name it correctly.
Reading an index
Reading group metadata from an index is quite simple. The steps are as follows

  • Seek to group_id * 6 since 6 is the size of each catalog in the index file
  • Read all six bytes into a buffer
    • The total group payload size is comprised of the first three bytes - ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
    • The starting block in the data file is comprised of the second three bytes - ((data[3] & 0xFF) << 16) | ((data[4] & 0xFF) << 8) | (data[5] & 0xFF); 

Name Hash Table
As previously mentioned in the master index loading section, there may be identity tables which map hashed names to groups/files in the cache.  Here is how it is used in the client:

public int getgroupid(String name)
    name = name.toLowerCase();
    return this.groupTable.lookup(jag_djb2(name));

public int getfileid(final int group, String name)
    name = name.toLowerCase();
    return this.childrenTable[group].lookup(jag_djb2(name));

It uses the name hash as mentioned previously and finds a value on a integer array that matches it and returns the index which is stored after the loaded hash value. Here is the refactored version of that class.
3. The Cache Data File
The cache data file holds all of the actual data for the game. As mentioned previously, the cache data file is split into sectors of 520 bytes of data. The first 8 bytes contain information needed to read the group data and the last 512 bytes is the actual payload of the sector. I believe the newer version of the game has a 10 byte header instead of 8 to handle the additional files. The structure of the sector header is as follows

  • Bytes 0 to 1 - The group id the block belongs to. This should match the group id that we are reading
  • Bytes 2 to 3 - The sequential block index. Each time a block is read in the loop, a counter is incremented. This counter and the value from this byte range should always match
  • Bytes 4 to 6 - The next block offset in terms of blocks
  • Byte 7          - The ID of the index the group should be a part of. This should always match the worker that is is linked to the index
  • Bytes 8 to 520 - The payload.

Reading the data blocks is also quite simple. The steps to read a complete file are as follows:

  • Get the starting block and file size from the index (done in same method in the client)
  • Declare variables to store the current sequential block index, the total data written, and a byte array to house all of the data.
  • Initialize the byte array to the total size of the group data
  • In a loop, do the following:
    • Determine how much data is needed to be read. This is the total file size subtracted by the amount of data already written to the output buffer. If it is over 512 bytes, reduce it to 512 bytes
    • Seek to current block offset * 520, which is the total sector size
    • Read the next 520 bytes of data from the data file
    • Store the first two bytes of data as the expected file id
    • Store the next two bytes of data as the expected block sequence
    • Store the next three bytes as the next sector/block index
    • Store the last byte as the index id
    • Perform checks to ensure the block values are valid. If it is 0, then we are done reading. If it is above the limit of the data file, then something is broke
    • Perform checks to ensure the file ID, sequential block value, and index values are correct
    • Copy the next 512 bytes to the output buffer
    • Increment the block sequence and set the block offset to the next block offset from the sector header.
  • Once the loop is complete all of the data should be in the buffer. 

Here is a refactored version of the class which handles this task. I didn't really touch the data insertion methods as I have no plans on adding resources to the cache. You should be able to
understand the packaging process though.
Js5 is the name of the abstraction layer in which Jagex use to verify and handle files. It handles updating and checksum verification. I assume it means Jagex store version 5 as they use fs or file_store_32 in the client a few times in the past.  I use a stripped down version for reading files and resource management, but it is pretty self explanatory.  Here is an example of reading data for a whip object using my own layer.


4. A project I am working on
Thank for you for making it this far. I know it was a long and boring read. To thank you on your patience, I would like to mention a project I am working on in my spare time. I am creating a web-based suite to read and extra cache files by simply dragging and dropping the cache files onto your browser. It will start off supporting Old School, but be adapted to support later versions of the game. Using the FileReader API and data transfer through dragging files to your browser, it will make it very simple to export models as well as various configurations in various formats (binary, xml, json, etc). I also want to embed a WebGL-based model and sprite viewer so when you click on an object, you can read the various configuration information, get the inventory sprite, and interact with it in 3D, sort of like the NXT-based character viewer Jagex added awhile back. This will not be completed until probably the summer when all of my university courses are completed and I am not working 50-60 hours per week. I can already load group information and file information. I just need to work on a nice interface and extraction mechanism.

Thanks and I look forward to contributing in the future!

  • HyperactiveLion, frazboyz, Static and 14 others like this

#1208 ASM Updater Tutorial - Part 3

Posted by Cov on 12 February 2013 - 11:03 AM

ASM Updater Tutorial – Part 3
Hello and welcome back to another exciting episode of ASM Updater Tutorial. It’s back after its mid-season break and by the end of this episode you should:
· Have an idea on how to structurally hook a class.
· Store the hooks you have found so you can print them out or export them.

The “Node” Class
The node class is always one of the first classes people hook, this is because it is very easy to hook and it is one of the most important classes in the RS client, if you look in the Updater Log section you can see how many types of Node are present in the client, they hold everything from NPC data to Menu data, and this is their base class. So to start with we need to create a new analyser and call it something like NodeAnalyser and make it extend our AbstractAnalyser class.

Posted Image

Awesome, now I know (from experience) that the Node class can be very easily structurally identified, it has 2 fields that are type itself and a long field. The class can have more fields of course but these 3 are always present and always non-static. So let’s make our canRun method using this knowledge:

Posted Image

Should look something similar to this ^. Now we’ll add it to our analyser list and run it and see how amazi…. shit there are two class identified:

Posted Image

Hmmm time to bust out CJBE and take a look at these classes. Straight away we can see there is a difference between the two classes, class “aew” has a super class of “oy” and “oy’s” class is “java/lang/Object” from this I can see that “oy” is most likely the node class.

Posted Image

So let’s add in a super class check to filter out the unwanted Node sub class (if anyone is interested this is usually called NodeSub by novices, however a more proper name is CacheableNode). So our new canRun method should look similar to this:

Posted Image

Lo and behold it identifies the Node class successfully. Now onto the fields, we know there are three fields in the Node class, two of type node (these are generally called next and previous) and a long (generally regarded as the UID of the Node). So let’s hook the easy UID field first then. We’ll do this be iterating of the FieldNodes in the ClassNode and checking the field type and whether or not it is static, the UID will always be non-static. Our code should look something like this:

Posted Image

We can check this as well using CJBE to make sure you have the correct field. Now we move onto the previous and next fields. They are exactly the same type I hear you cry how the hell do we tell which one is which, well this is where field usage can play a massive part in helping us to identify what field is what. Looking back through old deobs, I have a 578 done by Method and it’s one of the best deobs you can get your hands on. Taking a look at the Node class in the 578 we can see there is a boolean method called isLinked which checks to see if the previous node is not null.

Posted Image

So we can take a look and see if this method still exists in the current client, using CJBE I’ll take a quick look through the methods to see if we have a boolean method similar to the one in refactored client, and surprisingly the first method in the class is a boolean method with no args, just like our refactor, let’s take a look at the bytecode of it:

Posted Image

By breaking down the bytecode we can see what the method does. The first instruction pushes a null reference onto the stack; the next two instructions aload_0 getfield tells us it loads the field value oy/t onto the stack. This is looking even better! The next instruction tells us the is the right one and can assume this is the isLinked method if_acmpne instruction compares whether the 2 values are not equal so we can pretty much assume the method is structure “return null != this.t;” just like our isLinked method so we now know the local field oy/t is the previous field. We’ll add this to out analyser to show we have identified this field.

Posted Image

Ok so now we have the name of the “previous” field we can get our “next” field. We’ll iterate over the FieldNodes again and compare the field type and field name to get our field. The code we’ve added to the analyse method should look something like this:

Posted Image

We’ll run this up and we can see that it finds all our fields correctly:


Final thoughts
This tutorial ended up being larger than I first intended. But we have successfully fully identified the “Node” class. In terms of hooking, most people see them as looking for patterns in the client; I’ll use this bit to say how utterly crap the use of patterns can be for identifying fields. Using regex patterns to search through bytecode can be easily broken and is very inconsistent, as much as you can avoid this.

Whether I continue another tutorial remains to be seen I can always run through with you how to store the hooks and use them but you’ll have to grab me on skype when I’m not working.

Also, FU image limit :P
  • trDna, Interop, VelvetRevolver and 13 others like this

#846 [TUT] ASM Updater Part 1.

Posted by Cov on 14 January 2013 - 04:58 PM

ASM Updater Tutorial – Part 1
I decided to start writing this little series when Inf3cti0us was asking how to use transformers/analysers in the irc. This is going to be more of a hold your hand walkthrough of how to create your first updater. However, you won’t be able to directly copy and paste any code from this document; it will all be pictures muhahahaha, forcing you to actually type the code yourself so you might actually learn something.
By the end of this tutorial series you should be able to do the following:
· Be able to load a jar file and parse its contents.
· Understand how tranformers/analysers work and develop your own.
· Be able to inject into the class files and dump the jar.
· Have a usable base to be able to take forward and use again and again, as well as modify.

Okay so enough chatter and on with the show…

What you will need.
· An IDE, I’d recommend intellij idea, you don’t even need the professional edition, and chances are you will not ever use any of the extra functionality anyway.
· ASM library can be downloaded from http://asm.ow2.org/.
· A bytecode viewer, I’d recommend CJBE which is and updated version of JBE, available here https://github.com/Contra/CJBE.
· A decompiler, really I’d recommend fernflower if you can get your hands on the jar file for it, otherwise JD-GUI is fine, available from http://java.decompil...ree.fr/?q=jdgui.
· Finally the runescape client. This tutorial does not and will not cover how to get this, there’s plenty of stuff about on how to get this yourself.

Also getting a bytecode plugin for your IDE both intellij and eclipse have an ASM one, this is good for visualising your own code once it has been compiled.

Parsing the Jar.
Congratulations you’ve made it through 293 words of guff so far but now we’ll get to some actual code. To parse a jar using ASM is very easy. First we to create a new JarFile instance and pass that into this method. Read the comments carefully as they explain what each line of the method is doing.

Posted Image

Now I’m sure you have noticed that this method returns a HashMap<String, ClassNode>, a bit of explaining around this. A ClassNode is the ASM instance representing a class file. It gives us access to all the fields, methods, interfaces and superclass of the class. The reason we use a HashMap is because it make it easier later on for us to grab classes as and when we need them without having to iterate over the entire ClassNode collection, we use the class name as the String key. What we are then going to do is store the HashMap that is returned from this method call into a public static variable to we can access it no matter what transformer/analyser we are in. So the code we have so far should be as follows:

Posted Image

Identifying your first class.
So far so good, we have all the ClassNode’s we could ever want, but I hear your cry “What do I do with these amazing ClassNode’s”. Well we start identifying them of course. As an example I will demonstrate how to identify the TranslatableString class, this class is very easy to find due to its constructor. Since the last time I checked it is the only class in the client that constructor takes 4 Strings. So we’ll iterate over the values in our HashMap and check each class’s constructor to see if it takes 4 Strings. Again read the comments to follow what is going on in each part of the code.

Posted Image

If done properly your Updaters constructor should look something like this. And your output should look similar to:

Cov's really quick and crappy updater

1090 Classes parsed

TranslatableString class == wj
Give yourself a pat on the back for finding it!

Well here ends part 1 of our journey deep into the depths of developing an updater. So far we have only scratched the surface. In the next tutorial I will cover the abstract transform/analyser and show how we can convert what we have done so far into a basic updater base.

Please leave any comments or improvements that you think could be done or added.
  • trDna, VelvetRevolver, Merphz and 12 others like this

#12712 Gamepack Version

Posted by YungKingDog on 24 March 2017 - 10:03 AM

Hey sphiinx, in the "client.init()V" method you can find a method call where the params are (765, 503, rev) or similar, you can extract the rev from there =)




also hi why the fuck is everyone liking this

  • YungKingDog, BoneCode, tls and 10 others like this

#9681 Is mouse movement data tracked / sent to server?

Posted by BoneCode on 29 May 2015 - 07:17 PM

The question is in the title. Does the oldschool client send data about your mouse movement to the server? Tribot put a lot of hype into its human mouse project, but does that actually affect detection at all? If mouse data is sent, what classes are responsible for it?

To add onto its like osbot's mirror client. It's useless but looks great to idiots. Would you rather buy the newest sneakers that look dope and sick or the white budget income family sneakers. Thats right the dope sick ones, however surprise the indians that made those ones are actually all useless so the shoe falls apart in a month whereas the muslims who made the budget shoes are crazy sick and it lasts forever. 

  • Swipe, Cov, YungKingDog and 7 others like this

#951 [TUT] ASM Updater Part 2.

Posted by Cov on 25 January 2013 - 10:45 AM

ASM Updater Tutorial – Part 2
Hello again and sorry for the delay between the last tutorial and this one, I’ll be honest I totally forgot I was doing these :/ but anyway on with the show.
By the end of this tutorial you will have:
· Refactored the updater so we have a nice base.
· Implemented another analyser.

The AbstractAnalyser
If you know nothing about abstract classes or implementing them then I’m afraid you should stop here for the time being, tbh if you don’t know what they are you shouldn’t even be making an updater….. Now that me being an arsehole is out the way we can move on with the show.
The AbstractAnalyser is where all our searching and identifying takes place. It has 3 main methods in it, 2 abstract and one normal one.

Posted Image

The run method is the main driving force behind the updater and is the only method that is used outside our analysers instance, I have made it final because I don’t want me or other people overriding it later; the canRun method is rather self-explanatory, it checks whether this is the correct class to look in; finally the analyse method, this is where all class analysis takes place.

Refactoring the Updater class
Next on our list is tidying up the rest of the updater. If you remember we had our parseJar method, which extracted all the classes from the JarFile we pass in. We are going to extract this method and put it in its own class, called something like JarUtils, and make it static. Initially this sounds a bit stupid, giving it its own class, however, late on we will add some more functionality to the JarUtils class. So now we have something like this:

Posted Image

Awesome, now our updater is a little cleaner.

Time to play with our AbstractAnalyser
Now we can extract our TranslatableString analysis, I will rename it though to MultiLanguageString, or MLString, which imo is a better for it. This is where our AbstractAnalyser comes in to action. Make a new class called MLStringAnalyser and make it extend our AbstractAnalyser. We end up with something similar to this:

Posted Image

Now take the identifying logic from our Updater class and insert it into the canRun method so we can identify the class:

Posted Image

Congratulations you have your first analyser.

How do I use this fantastic Analyser?
I hear you cry; well further refactoring of the Updater class is needed. First we need to create an ArrayList<AbstractAnalyser> in order to hold all our analysers. Then we need a method that will add all the AbstractAnalyser’s to the ArrayList. Once we have done that we create another method that will iterate over every ClassNode and run every Analyser over it. Your class should resemble something similar to this now:

Posted Image

Final thoughts for part 2
There we go another tutorial complete and more functionality added to your updater. Now you have a good base to work off that will keep your code tidy and easier to maintain. When you look back at some of the very early updaters which pretty much consisted of one class with a method each to find the correct class and one field you can see how beneficial this setup is. One thing to think about is the packaging structure you use I generally structure mine like this:

Posted Image

I find that sort of structure is very easy to maintain.

Next tutorial I will introduce you to the process of hooking fields, by showing you how to hook the Node class. I will also run over a nice way to store your hooks for injecting later on and being able to dump the classes back into a Jar. Injecting will have an entire tutorial on its own, I hope, as it is a very broad subject and I will try to cover the majority of different methods you will need.

Please leave any comments or improvements that you think could be done or added.
  • trDna, Dark, Kaden and 7 others like this

#11853 Wtf is bytecode [Part 3]

Posted by HobbitChild on 08 June 2016 - 02:59 PM


on schedule as always

  • Swipe, YungKingDog, Static and 6 others like this

#10108 BotWatch

Posted by Brainfree on 28 July 2015 - 07:31 PM

Here is a list of data-sets observed by the server that is most likely used for the BotWatch algo:

  • Meta-Hardware: When you login, they upload misc hardware information such as: The number of cores, total RAM, java version, java vendor, OS, OS version, OS architecture.
  • Client instances: They keep a dedicated file which keeps track of any clients running on the machine, and is uploaded when you login. Usually people just set it to read-only, or delete it. 
  • Focus: They upload every time your client gains and loses focus. So every time you minimize your bot, they know. In which should cause a insta-ban, since you're magically performing actions when the client is not even visible.
  • Pitch/Yaw/Screen-Dimensions: Every time you adjust your pitch, yaw, or screen size (for resizable mode) they upload the exact value. Not just that its been adjusted, but precise values.
  • Mouse-Motion: Every 50 ms, they take a snapshot of your mouses current location, and when the queue is full, or you click your mouse, these snapshots are uploaded. With the constant time-span they can compute the kinematics of your mouse (which they can do all sorts of fancy things). The position monitored is updated every cycle, so if the engine was lagging it'll seem your mouse is jumping around, so I'm not to sure how much they weigh this (since they don't monitor frame-rate/frequency).
  • Mouse-Click: Every time you left or right click your mouse, the exact point is uploaded, along with any mouse-motion data leading to that point. They also upload the time (in engine ticks -50ms-) since you last clicked.
  • Key-Model: Every time you press a key, they upload what key it was, and the time difference since you last pressed a key.  This is probably how they detect auto-typers.
  • Remote-Agent: The client will perform a aruged operation with reflection on anonymous static members upon a remote request by the server. They result(s) are then upload, along with any errors that occurred. The server provides a members address and the operation they want to perform. They currently can get/set int fields, invoke and return the values of methods. The only upload numeric or string type return values. Void and 'others' (but not the value of) are also noted as a return. The modifiers of the targeted member(s) is also a possible operation which can be requested. I've thought of three possible usages for this: 1) To counter source bots in which run on a custom client source, and don't use the latest client version. 2) They are using it to upload hidden fields that would have been obviously found if a packet was dedicated for it. 3) To debug the client, but I've never heard of such an application. 
  • The server is notified when the view-port position of a adjustable widget when you scroll. When you scroll a widget up/down/left/right they are notified of the current inset value.
  • GC-Profilnig: http://rs-hacking.co...014-gc-profile/

There is probably more hidden modeling they do, that's yet to be found. I've personally looked at any and all outgoing packets the client would or could send over to the server, including raw packets (login,update,handshake) in revision 84). What I listed is stuff that is pretty obvious to be abstract and useless to the server-end and could (through some imagination) be used to differentiate between a human and automated input. Together, the sum of these models suggests all possible human-input is delegated to the server. The main point of this thread is to discuses model 4. I'm personally paranoid that they are using this information to simulate the view-port in order to determine whatever information they can yield. Though some may argue such a thing would take way to many resources, I beg to differ. Jagex only has to compute a single frame. An average computer computes 50 in a second, with the obvious throttle the engine applies. So the actually computing power required to simulate a frame, given environmental, and local (camera) information, would be simple enough. What information they can extract from knowing what you can see at any given time-frame could be whom evers guess. But personally, I remain hopefull that whatever algorithm they are using these datasets for is not mature. But the fact that they are approaching this in such a matter is alarming, since I assume none of of are specialized to counter such a attack, if it is to evolve. 


I posted this thread to discuses other possible cases in which they'll need to acquire such data. Also people mentioned that all of this modeling data is Legacy code, but the fact that they added a notifier for when you adjust your screen dimensions proves otherwise. Also, wasting bandwidth on a useless scare tactic is silly.


Maybe this is for a ease-of-use model? 

  • HyperactiveLion, 0xAA4A, hexagon and 6 others like this

#1969 The Reverser's Handbook

Posted by trDna on 05 March 2013 - 12:13 AM

This is a collection of tutorials, helpful links, and terminology that will make your life easier when it comes to learning how to reverse engineer Java applications. Only tutorials that are complete and accurate will be shown here.


Bytecode Mnemomics - By Bitwise

Learning About Control Flow Graphs and Analysis

  • Opcode - Unsigned byte value identified by a mnemonic symbol.
  • Bytecode Instruction Arguments - Static values that define the behavior of an instruction (ex. ILOAD_X, where X is the argument).
  • Bytecode Instruction Operands - Argument values are statically known and are stored in the compiled code. Operand values come from the operand stack and are only known at runtime.
  • Multiplier - Multipliers are used to help obfuscate a software's code. It is what it seems to be. It's used so that the intended values of variables are 'masked', and to 'unmask' it, you will need to multiply using a multiplier with a corresponding field or method. For example, if an integer method called getHealthPercentage returns 100, after obfuscation, at anytime getHealthPercentage is called, getHealthPercentage may be altered in a way that it returns 10 instead of 100, and you need to multiply that by 10 to get the actual value of it.
Useful Information - From Eric Bruneton's ASM 4 Guide

Compiled Class Hierarchy (Simplified):
Posted Image

Internal Names
Posted Image

Method Descriptors
Posted Image

Bytecode Appendix
Posted Image
Posted Image
Posted Image
Posted Image

ASM Performances

Posted Image

[Moving along]

Posted Image
  • Cov, Fox, Static and 5 others like this

#11774 [SRC] My Oldschool Updater Source

Posted by Marneus901 on 26 May 2016 - 11:02 PM

I'm done with Runescape for the most part, i'll be working on my own FPS or MMO rather than dabbling with crap.
Its not perfect with some hooks, but for the most part you can have a stable client with little work.
Uses combination of bytecode signitures and my own control flow analysis (DynaNode, much more helpful when it comes to OSRS), and does archive the gamepack and a refactor based on the identification (duplicate names I never fixed, so same name hooks overwrite themselves). Had only just started hooking methods in this updater last time I worked on it.
Its safe to say however that it is outdated as I have not touched it in months (newer updater and my 'updater focus' is on RS3).
You can still hmu for any questions and whatnot as I have studied the RS client like a job for many years, and the updater does not reflect nearly what I know in the client).
caches folder contains .xml files for each revision mapping the hooks for my reflection cache loader system, the modscripts folder is my output used for my injection loaders.
Doesn't matter which you want to use if you do, but iirc I had a problem with the caches for injection (maybe multipliers or methods, i forget).
Download link : https://dl.dropboxus...OSRSUpdater.rar
Heres a current log...
[ - Parameter Parser - ]
Parsing parameters...
Base Link : http://oldschool4.runescape.com/
Succesfully parsed parameters in : 614ms
[ - Client Downloader - ]
JAR Location : http://oldschool4.runescape.com/gamepack_5168748.jar
Downloading runescape client...
Succesfully downloaded client in : 23610ms
Current Runescape Build : 114
Loaded 231 classes from 114!

Removing redundant methods...
Removed 2115/4555 redundant methods!
Finished removing redundant methods! (level 1) (7752ms)

Building ASM DynaNode pool...
Parsing client classes...
Done parsing 231 classes!
Parsing client fields...
Done parsing 3234 fields!
Parsing client methods...
Done parsing 2440 methods!
Done building node pool with 5674 node links made! (23ms)

Building reference tree...
Finished building reference tree with 55335 total references! (467ms)
Field References : 43953 Invokes : 10832 Checkcasts : 550
Refreshing ASM DynaNode pool...
Refreshed node pool; 5098 total nodes refreshed! (3ms)

Loading updater identifiers...
Loaded 62 identifiers! (72ms)
Beginning the identification process...

^ hd identified as Node
-^& hd.iq()Z identified as isParent
-^& hd.ia()V identified as remove
-^* hd.ea identified as getNext
-^* hd.ed identified as getUID
-^* hd.eu identified as getPrevious

^ gr identified as HashTable
-^& gr.s(J)Lhd; identified as get
-^& gr.j(Lhd;J)V identified as put
-^& gr.p()V identified as clear
-^& gr.x()Lhd; identified as resetIndex
-^& gr.d()Lhd; identified as next
-^* gr.p identified as getHead
-^* gr.j identified as getBuckets
-^* gr.x identified as getTail
-^* gr.s identified as getSize
-^* gr.d identified as getIndex

^ gv identified as CacheableNode
-^& gv.fw()V identified as unlink
-^* gv.ct identified as getNext
-^* gv.ce identified as getPrevious

^ ca identified as RenderableNode
-^& ca.v(IIIIIIIII)V identified as renderAt
-^* ca.cg identified as getHeight

^ ge identified as Queue
-^& ge.d()V identified as clear
-^& ge.s(Lgv;)V identified as insertBack
-^& ge.p()Lgv; identified as popFront
-^* ge.s identified as getHead

^ gi identified as Cache
-^& gi.p(Lgv;J)V identified as put
-^& gi.s(J)Lgv; identified as get
-^& gi.x()V identified as clear
-^& gi.j(J)V identified as remove
-^* gi.s identified as getCacheableNode
-^* gi.j identified as getSize
-^* gi.x identified as getHashTable
-^* gi.p identified as getRemaining
-^* gi.d identified as getQueue

^ ds identified as ByteBuffer
-^& ds.ax(Ljava/math/BigInteger;Ljava/math/BigInteger;I)V identified as applyRSA
-^& ds.a(B)B identified as get
-^* ds.j identified as getOffset
-^* ds.s identified as getBytes

^ dd identified as ISAACCipher
-^& dd.j(S)V identified as decrypt
-^& dd.s(I)I identified as next
-^& dd.p(B)V identified as initializeKeySet
-^* dd.d identified as getResults
-^* dd.k identified as getLast
-^* dd.x identified as getCount
-^* dd.u identified as getMemory
-^* dd.o identified as getAccumulator
-^* dd.b identified as getCounter

^ di identified as Packet
-^* di.l identified as getBitOffset
-^* di.k identified as getCipher

^ fa identified as ReferenceTable

^ cm identified as Rasterizer

^ cs identified as Rasterizer2D

^ cf identified as Rasterizer3D

^ v identified as World
-^* v.l identified as getWorld
-^* v.t identified as getMask
-^* v.i identified as getPlayerCount
-^* v.r identified as getDomain
-^* v.e identified as getServerLocation
-^* v.m identified as getActivity

^ ct identified as IdentityKit
-^* ct.u identified as getBodyID
-^* ct.l identified as isVisible

^ dh identified as Model
-^& dh.z(III)V identified as translateVertices
-^* dh.d identified as getVerticeLength
-^* dh.au identified as getShadowIntensity
-^* dh.g identified as getTexturedVerticeLength
-^* dh.k identified as getIndiceLength
-^* dh.ab identified as isFitSingleTile
-^* dh.u identified as getVerticesX
-^* dh.o identified as getVerticesY
-^* dh.b identified as getVerticesZ
-^* dh.i identified as getTexturedIndicesX
-^* dh.y identified as getTexturedIndicesY
-^* dh.q identified as getTexturedIndicesZ
-^* dh.l identified as getIndicesY
-^* dh.c identified as getIndicesX
-^* dh.y identified as getTexturedVerticesX
-^* dh.t identified as getIndicesZ
-^* dh.f identified as getTexturedVerticesY
-^* dh.q identified as getTexturedVerticesZ

^ ar identified as AnimationSequence
-^* ar.r identified as getAnimationID

^ cq identified as AnimatedModel
-^* cq.s identified as getVerticeLength

^ az identified as ItemDefinition
-^* az.bj identified as getNoteID
-^* az.t identified as getID
-^* az.i identified as getModelIndex
-^* az.an identified as getActions
-^* az.r identified as getName
-^* az.ab identified as isMembers
-^* az.ad identified as getGroundActions
-^* az.aj identified as getStackIDs
-^* az.ar identified as getStackSizes
-^* az.aw identified as getUnnotedID
-^* az.at identified as getNoteID
-^* az.az identified as isMembers
-^* az.bq identified as getUnnotedID
-^* az.f identified as getModelOffset
-^* az.q identified as getModelSine
-^* az.a identified as getModelZoom

^ ax identified as Character
-^* ax.ad identified as getLocalX
-^* ax.au identified as getLocalY
-^* ax.co identified as getQueueX
-^* ax.cb identified as getQueueY
-^* ax.bh identified as getAnimation
-^* ax.ac identified as getHitsplatTypes
-^* ax.bo identified as getHeight
-^* ax.ay identified as getOverheadMessage
-^* ax.ak identified as getHitsplatCycles
-^* ax.bd identified as getMaxHealth
-^* ax.bt identified as getInteractingIndex
-^* ax.az identified as getHealth
-^* ax.ar identified as isAnimating
Failed to identify Character.getOrientation!

^ aq identified as NPCDefinition
-^* aq.d identified as getID
-^* aq.b identified as getModelIDs
-^* aq.k identified as getHeadIcons
-^* aq.a identified as getModifiedColors
-^* aq.f identified as getLevel
-^* aq.w identified as getOriginalColors
-^* aq.g identified as getActions
-^* aq.an identified as getHeadIcon
-^* aq.ao identified as isVisible
-^* aq.u identified as getName
-^* aq.ap identified as getChildrenIDs

^ ay identified as ObjectDefinition
-^& ay.l(I)Lay; identified as getChildDefinition
-^* ay.k identified as getID
-^* ay.t identified as getName
-^* ay.y identified as getClipType
-^* ay.am identified as isRotated
-^* ay.n identified as getAnimationID
-^* ay.an identified as getActions
-^* ay.w identified as isWalkable
-^* ay.ar identified as getChildrenIDs
-^* ay.ae identified as isCastingShadow
-^* ay.ah identified as getModelSizeX
-^* ay.ax identified as getModelSizeY
-^* ay.aa identified as getModelSizeZ

^ fq identified as PlayerDefinition
-^* fq.p identified as isFemale
-^* fq.d identified as getAnimatedModelID
-^* fq.s identified as getAppearance
-^* fq.j identified as getBodyColors
-^* fq.u identified as getBaseModelID

^ cj identified as IndexedImage
-^* cj.j identified as getPalette
-^* cj.s identified as getPixels

^ hz identified as Font
-^* hz.s identified as getPixels

^ as identified as NPC
-^* as.s identified as getDefinition

^ p identified as Player
-^* p.j identified as getDefinition
-^* p.o identified as getCombatLevel
-^* p.w identified as isVisible
-^* p.g identified as getTeam
-^* p.m identified as getModel
-^* p.s identified as getName

^ an identified as Item
-^* an.j identified as getStackSize
-^* an.s identified as getID

^ i identified as AnimableNode
-^* dd.k identified as getX
-^* al.t identified as getY
-^* i.j identified as getType
-^* i.p identified as getOrientation
-^* i.x identified as getPlane
-^* i.o identified as getAnimationSequence
-^* i.s identified as getID

^ o identified as Projectile
-^* o.k identified as getSlope
-^* o.d identified as getHeight
-^* o.b identified as getCycle
-^* o.c identified as getTargetDistance
-^* o.p identified as getStartX
-^* o.l identified as getTargetID
-^* o.i identified as getX
-^* o.a identified as getSpeedZ
-^* o.e identified as getSpeedX
-^* o.h identified as getSpeedY
-^* o.z identified as getScalar
-^* o.g identified as getRotationX
-^* o.y identified as getRotationY
-^* o.f identified as getAnimationSequence
-^* o.t identified as isMoving
Failed to identify Projectile.getY!
Failed to identify Projectile.getStartY!
Failed to identify Projectile.getHeightOffset!

^ ag identified as Varpbit
-^* ag.p identified as getConfigID
-^* ag.x identified as getEndbit
-^* ag.d identified as getStartbit

^ hq identified as ClassInfo
-^* hq.d identified as getFields
-^* hq.o identified as getMethods
-^* hq.b identified as getBytes

^ bn identified as IntegerNode
-^* bn.u identified as getValue

^ gs identified as LinkedList
-^& gs.s()V identified as clear
-^* gs.j identified as getNext
-^* gs.s identified as getTail

^ r identified as MouseTracker
-^* r.d identified as getTrackingX
-^* r.j identified as getLock
-^* r.s identified as isTracking
-^* r.x identified as getTrackingY
-^* r.p identified as getLength

^ cv identified as ComplexTile
-^* cv.l identified as isFlat

^ ce identified as GenericTile

^ b identified as IgnoredPlayer
-^* b.s identified as getName
-^* b.j identified as getPreviousName

^ z identified as Friend
-^* z.s identified as getName
-^* z.j identified as getPreviousName
-^* z.p identified as getWorld

^ br identified as AudioEnvelope

^ bs identified as SoundFilter

^ bu identified as AudioInstrument

^ bi identified as AudioTrack
-^* bi.j identified as getInstruments

^ ha identified as AccessFile
-^* ha.p identified as getLength
-^* ha.s identified as getFile
-^* ha.j identified as getPosition

^ a identified as URLDataLoader

^ ew identified as Socket
-^* ew.o identified as getBuffer
-^* ew.s identified as getInputStream
-^* ew.j identified as getOutputStream
-^* ew.p identified as getSocket

^ dc identified as CollisionMap
-^* dc.ax identified as getFlags

^ ex identified as DevelopmentStage
-^* ex.d identified as getStage
-^* ex.u identified as getID

Failed to identify Producer!

^ ch identified as InteractableObject
-^* ch.i identified as getUID
-^* ch.j identified as getHeight
-^* ch.o identified as getX
-^* ch.x identified as getWorldY
-^* ch.d identified as getModel
-^* ch.u identified as getOrientation
-^* ch.b identified as getOffsetX
-^* ch.s identified as getPlane
-^* ch.r identified as getFlags
-^* ch.c identified as getOffsetY
-^* ch.p identified as getWorldX
-^* ch.k identified as getY

^ cy identified as ItemLayer
-^* cy.s identified as getPlane
-^* cy.j identified as getX
-^* cy.p identified as getY
-^* cy.x identified as getItem1
-^* cy.d identified as getItem2
-^* cy.o identified as getID
-^* cy.b identified as getHeight
-^* cy.u identified as getItem3

^ cg identified as BoundaryObject
-^* cg.j identified as getX
-^* cg.p identified as getY
-^* cg.d identified as getOrientation
-^* cg.k identified as getFlags
-^* cg.b identified as getID
-^* cg.s identified as getPlane
-^* cg.x identified as getBackupOrientation
-^* cg.u identified as getModel
-^* cg.o identified as getBackupModel

^ cp identified as FloorDecoration
-^* cp.p identified as getY
-^* cp.j identified as getX
-^* cp.u identified as getFlags
-^* cp.x identified as getModel
-^* cp.d identified as getID
-^* cp.s identified as getPlane

^ cz identified as WallDecoration
-^* cz.u identified as getOrientation
-^* cz.j identified as getX
-^* cz.b identified as getModel
-^* cz.c identified as getID
-^* cz.s identified as getPlane
-^* cz.k identified as getBackupModel
-^* cz.l identified as getFlags
-^* cz.p identified as getY

^ cd identified as Tile
-^* cd.j identified as getX
-^* cd.s identified as getPlane
-^* cd.u identified as getComplexTile
-^* cd.o identified as getBoundaryObject
-^* cd.b identified as getWallDecoration
-^* cd.k identified as getFloorDecoration
-^* cd.c identified as getItemLayer
-^* cd.t identified as getObjects
-^* cd.p identified as getY
-^* cd.f identified as getTile
-^* cd.d identified as getGenericTile

^ ck identified as Region
-^& ck.v(III)Lcp; identified as getFloorDecorationAt
-^& ck.q(III)Lcz; identified as getWallDecorationAt
-^& ck.n(III)Lch; identified as getInteractableObjectAt
-^& ck.aq(Lcd;Z)V identified as renderTile
-^& ck.f(III)Lcg; identified as getBoundaryObjectAt
-^* ck.u identified as getTiles

^ fu identified as Widget
-^* fu.ca identified as getItemName
-^* fu.ck identified as getWidgetActions
-^* fu.bk identified as getWidgetText
-^* fu.am identified as getParentID
-^* fu.r identified as getWidgetIndex
-^* fu.ey identified as getChildren
-^* fu.ef identified as getItemID
-^* fu.eb identified as getItemCount
-^* fu.cg identified as getParentWidget
-^* fu.i identified as getID
-^* fu.ae identified as isHidden
-^* fu.ei identified as getSlotContentIDs
-^* fu.ez identified as getSlotStackSizes
-^* fu.ao identified as getScrollX
-^* fu.as identified as getScrollY
-^* fu.ep identified as getVisibleTime
-^* fu.ew identified as getBoundsIndex
-^* fu.er identified as getDisplayTime
-^* fu.az identified as getBorderThickness
-^* fu.v identified as getRelativeX
-^* fu.ab identified as getRelativeY
-^* fu.an identified as getHeight
-^* fu.ad identified as getWidth

^ x identified as WidgetNode
-^* x.s identified as getID

^ ek identified as MouseListener

^ ec identified as KeyboardListener

^ er identified as GameShell
-^* er.pw identified as isDead

^ client identified as Client
-^& cy.s(IB)Laq; identified as getNPCDefinition
-^& cm.dm(IIII)V identified as setViewportBounds1
-^& client.t(I)V identified as connect
-^& bk.ap(Lax;IS)V identified as updateCharacterOrientation
-^& client.l(I)V identified as connect
-^& cm.cx(IIII)V identified as setViewportBounds2
-^& au.cw([Lfu;IIIIIIIIB)V identified as renderWidget
-^& c.s(II)Lay; identified as getObjectDefinition
-^& c.s(II)Laz; identified as getItemDefinition
-^& bd.du(II)V identified as settingChanged
-^& er.qe(IIII)V identified as initializeApplet
-^* ek.s identified as getMouseListener
-^* ec.s identified as getKeyboardListener
-^* ck.ap identified as getInteractableObjects
-^* hk.s identified as getWidgets
-^* dz.hl identified as getLocalPlayer
-^* client.cm identified as getAllNPCs
-^* client.ds identified as getRegion
-^* client.hu identified as getMenuItemCount
-^* client.gf identified as getAllPlayers
-^* client.ef identified as getMapScale
-^* client.fm identified as getMapAngle
-^* client.ev identified as getMapOffset
-^* client.hr identified as getGroundItemDeque
-^* y.gp identified as getPlane
-^* client.ir identified as getMenuActions
-^* client.io identified as getMenuOptions
-^* ck.q identified as getRegionOffsetX
-^* ck.n identified as getRegionOffsetY
-^* ck.v identified as getRegionOffsetZ
-^* client.hf identified as getCurrentLevels
-^* client.hz identified as getAbsoluteLevels
-^* client.hs identified as getExperiences
-^* u.s identified as getTileHeights
-^* client.g identified as getGameCycle
-^* client.lm identified as getWidgetPositionsX
-^* client.lb identified as getWidgetPositionsY
-^* client.ls identified as getWidgetWidths
-^* client.ln identified as getWidgetHeights
-^* client.mb identified as getDestinationX
-^* client.mw identified as getDestinationY
-^* client.hm identified as isMenuOpen
-^* aq.ha identified as getMenuX
-^* b.hw identified as getMenuY
-^* fj.hj identified as getMenuWidth
-^* s.hp identified as getMenuHeight
-^* e.s identified as getInventoryItems
-^* f.dm identified as getBaseX
-^* db.dh identified as getBaseY
-^* ar.x identified as getAnimationSequenceCache
-^* client.iu identified as getWidgetNodeCache
-^* client.w identified as getCollisionMaps
-^* bh.fl identified as getCameraPitch
-^* ae.fv identified as getCameraYaw
-^* bo.fn identified as getCameraX
-^* bk.fp identified as getCameraY
-^* b.fd identified as getCameraZ
-^* fx.u identified as getGroundItemTable
-^* ay.o identified as getModelCache
-^* client.gl identified as getMouseCrosshairState
-^* client.ig identified as getSpellName
-^* client.it identified as getSelectedItemName
-^* hl.ix identified as getSelectionState
-^* hl.ix identified as getSelectedItemIndex
-^* v.iz identified as getSelectedItemID
-^* ay.x identified as getObjectDefinitionCache
-^* fq.t identified as getPlayerModelCache
-^* u.j identified as getGroundByteArray
-^* az.k identified as getItemModelCache
-^* cf.l identified as getAbsoluteScreenX
-^* cf.t identified as getAbsoluteScreenY
-^* dh.bv identified as getCosineTable
-^* dh.bx identified as getSineTable
-^* cm.a identified as getViewportRight
-^* cm.z identified as getViewportLeft
-^* cm.h identified as getViewportBottom
-^* cm.e identified as getViewportTop
Failed to identify Client.getConnectionState!

Identified 61 classes!
Identified 319 fields!
Identified 39 methods!

Generating injection modscript...

Generating reflection cache...
Done caching reflection model... (578ms)

Beginning refactor dump...
Renaming classes...
Finished renaming classes. (0ms)
Refactoring class superclass names...
Finished refactoring class superclass names. (0ms)
Refactoring field type names...
Finished refactoring field type names. (1493ms)
Refactoring all Type instructions...
Finished refactoring type instructions. (5195ms)
Renaming all fields...
Finished renaming fields. (2ms)
Refactoring all field instructions...
Finished refactoring field instructions. (737ms)
Renaming methods, refactoring parameters and return types...
Finished renaming and refactoring method parameters and return types. (18ms)
Refactoring all invoke instructions...
Finished refactoring all invoke instructions. (322ms)
Finished dumping refactored client! (7767ms)
Dumped 231 classes to refactors\114.jar
Updater ran for : 27081ms

Attached Files

  • hexagon, Septron, Polish Civil and 5 others like this

#8200 Tutorial on how to get rs3 loaded classes(inner pack)

Posted by Doug on 29 October 2014 - 02:39 PM

After 3 hours of shitty searching I found  that jagex are storing the loaded class of the inner pack in two HashMap fields of type <string,byte[]>. The string is the loaded class name  and  the byte array is the loaded class bytes. From there you could create your own class nodes from the class bytes (used in the updater) and even  load the class with the byte array  too ( used for your client). There no need to decrypt , inject , intercept shit now because of this.

You could easily use reflection and get the loaded class from there.


link of the method where the maps are created : https://gist.github....tfile1-java-L26


Result after getting the values with reflection :


first map: https://gist.github....46b99c1f8127397

second map :https://gist.github....f8ae9f17c82d35e


Here how you would get them : 

 public HashMap<String, byte[]> getInnerPackClasses() {
		Map<String, byte[]> innerPackClasses = new HashMap<String, byte[]>();
		final Field field= mainClassInstance.getClass().getDeclaredField("p");
	        final Object clzz = field.get(mainClassInstance);
		final Field tab1 = loader.loadClass("h").getDeclaredField("h");
		final Field tab2 = loader.loadClass("g").getDeclaredField("l");
		innerPackClasses.putAll((HashMap<String, byte[]>) tab1.get(clzz));
		innerPackClasses.putAll((HashMap<String, byte[]>) tab2.get(clzz));
		return innerPackClasses;
1 - the mainClassInstance is the instance of the RS2Applet class ( that you create)
2 - the p field contain  an instance of the class where the HashMaps are stored in .You need to use this instance because the HashMaps are not static.
Here s what you need to make it work :
1-RS2applet instance that you create
2-The object instance of where the hashtables are stored (located in RS2applet)
3- The two HashMaps field names.
NOTE  : I did not start my RS3 updater yet so I  just wrote the class and field names manually for this snippet .So please do not just copy it , it wont work if you have a different gamepack . Also you should identify the needed hooks  in your updater and then use them on this snippet so you can avoid  breaks when runescape updates.

Leave what you think about it and good luck too !

  • Cov, P3aches, YungKingDog and 4 others like this

#12128 Best public client?

Posted by dan on 22 July 2016 - 05:24 PM

I dont know much about this bot but do you expect people to take you seriously when your ui looks like this?


  • YungKingDog, Doug, Odell and 4 others like this

#11857 Basic Runescape Loader

Posted by Cov on 10 June 2016 - 03:57 PM

After over 3 years since I last posted a tutorial I thought I'd dig up one that I started a while ago and never finished..... and finish it. I'm going to throw the code into a github repo as well to preserve it. So without further ado I give you how to build a runescape loader.
When I first started with playing around with runescape most loader tutorials were simply "this is a loader, copy paste and there you go". This is an attempt to rectify that and try to explain why loaders are structured how they are. I might discuss loading an Applet from a local jar file some other time.
What is Runescape?
Bit of an odd start but to put together a loader you need to understand what Runescape is at it's heart, and that is.................. a Java Applet. So what is an Applet? From the documentation an Applet is "An applet is a small program that is intended not to be run on its own, but rather to be embedded inside another application." Now usually Applets are embedded in a browser application, however, in this situation we are going to be embedding it in our own application, and like many others that will eventually be what we know as a bot.
Now an Applet needs something extra to run, that is an AppletStub, taken from the javadocs an AppletStub "serves as the interface between the applet and the browser environment or applet viewer environment in which the application is running." Basically it allows the environment in which the Applet is running to pass parameters, much like the array of string in the main method of a normal java application, to the Applet.
There is another thing that is used, but not required, for the Applet to run, that is the AppletContext. Again, as put forth by the holy book that is the javadocs the AppletContext "corresponds to an applet's environment: the document containing the applet and the other applets in the same document."
In layman's terms the AppletStub is the middle man between Runescape and the Browser, and the AppletContext tells Runescape everything that is around it.
The Parameters
First we will start off with nothing we have discussed so far, just to confuse you! Well we have touched on them. Parameters! Parameters in the wonderful world of Applet's are the equivalent of program arguments. If you view the source of a page with an Applet in you can seem the in the "param" tags. If you've seen some of the older loader examples, like way back when you will probably come across some nasty regex to parse these from the html, but there is a much nicer way of doing it. The parameters Runescape uses are generated and parsed from a separate place on jagex's site http://oldschool.run...m/jav_config.ws go on, click it, you know you want to. If you did give in to temptation you'll see an unformated version of the parameters that we can parse without some awful regex. So on to some code, finally!


So we will start of and make a class called ConfigReader with a constructor that takes a String (the config url), called so because it is reading the old scool config file. You might not be able to see it in the browser but each parameter is on a new line which makes life easy in parsing. So we will make a method called readConfig, that will do what it says read the config from the Url:

private String[] readConfig() {
        // Create the stream and reader so we can dispose of it nicely in the finally
        // Yes I could have used the new try with resources but #yolo
        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        // Create what we will return, the lines of the config to make it easier to parse
        List<String> lines = new ArrayList<>();
        try {
            // Create the URL instance to read the file, open the stream and init the reader
            URL configUrl = new URL(url);
            inputStream = configUrl.openStream();
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

            // Read the config line by line and throw it into what we are going to return
            String line;
            while ((line = bufferedReader.readLine()) != null)

        } catch (IOException e) {
        } finally {
            try {
                // Close everything up nicely
                if (inputStream != null)
                if (bufferedReader != null)
            } catch (IOException e) {
        // Convert to array and return it
        return lines.toArray(new String[lines.size()]);

Next we actually need to use this method, so we will create another method called read which is public so we can use it out side of the ConfigReader instance. This is going to take the lines and parse the parameter name and value and add it into a map we can use later:

public Map<String, String> read() {
        // Read the config file
        String[] page = readConfig();
        // Create the parameter map we want to return
        HashMap<String, String> map = new HashMap<>();
        for (String parameter : page) {
            // Cleanse the string as we don't need "param=" or "msg="
            parameter = parameter.replace("param=", "").replace("msg=", "");
            // Split the string on the "=" sign and limit the split to 2 in case some of the parameters use the "=" sign 
            String[] splitParameter = parameter.split("=", 2);
            // Check if the value is empty and add an empty parameter with the name
            if (splitParameter.length == 1)
                map.put(splitParameter[0], "");
            // Check there is a value and add the parameter with the value
            if (splitParameter.length == 2)
                map.put(splitParameter[0], splitParameter[1]);
        // return our parameters
        return map;

Voila, we have a config reader.


Faking the browser environment


Now we need to fake up the browser environment so we can pass the parameters correctly to the Applet. As we are making a loader for Runescape we will make two classes called RSAppletContext and RSAppletStub. RSAppletContext should implement the AppletContext interface and RSAppletStub should implement the AppletStub interface. We will start with the RSAppletContext, we will do a very basic implementation of this as it is all that is needed:

public class RSAppletContext implements AppletContext {

    private final Map<String, InputStream> streams = new HashMap<>();
    private Applet applet;

    public AudioClip getAudioClip(URL url) {
        // Use the Java Applet implementation of getting an AudioClip
        return Applet.newAudioClip(url);

    public Image getImage(URL url) {
        try {
            // Pretty standard stuff, as long as we return an Image we are good
            return ImageIO.read(url);
        } catch (IOException e) {
            throw new RuntimeException(e);

    public Applet getApplet(String name) {
        // Return the Runescape Applet
        return applet;

    public Enumeration<Applet> getApplets() {
        // Create a Vector of Applets and add the Runescape one to it
        // We use a Vector because it's the easiest way to get an Enumeration<Applet>
        Vector<Applet> applets = new Vector<>();
        return applets.elements();

    public void showDocument(URL url) {
        // Make Java open up the requested url if it's supported
        if (Desktop.isDesktopSupported()) {
            try {
            } catch (IOException | URISyntaxException e) {
                throw new RuntimeException("Unable to open document " + url.getPath());

    public void showDocument(URL url, String target) {
        // Make Java open up the requested url if it's supported
        if (Desktop.isDesktopSupported()) {
            try {
            } catch (IOException | URISyntaxException e) {
                throw new RuntimeException("Unable to open document " + url.getPath());

    public void showStatus(String status) {
        // We don't really need to do anything with the status

    public void setStream(String key, InputStream stream) throws IOException {
        // Basic implementation of a stream map that the AppletContext requires
        if (streams.containsKey(key)) {
        streams.put(key, stream);

    public InputStream getStream(String key) {
        // Basic implementation of a stream map that the AppletContext requires
        return streams.get(key);

    public Iterator<String> getStreamKeys() {
        // Return the keys to the stream map
        return streams.keySet().iterator();

    public void setApplet(Applet applet) {
        // Our one and only method we are going to add to this. It sets the Applet so we can return it and the Enumeration of Applets
        this.applet = applet;

Take particular notice of the setApplet method, this is a setter that we have made to allow us to set the Applet of the AppletContext. This is to help emulate a page and return the single Applet (Runescape) that is one the page.


Next we will look at the RSAppletStub, which is needed to make allow the Applet to actually run. First thing we need to do is create a constructor that takes a Map<String, String> (our parameters and store them so we can use them late. In the constructor we will also make a instance of the RSAppletContext so we can return this in the getAppletContext method:

public class RSAppletStub implements AppletStub {

    private final Map<String, String> parameters;
    private final RSAppletContext appletContext;
    private boolean active = false;

    public RSAppletStub(Map<String, String> parameters) {
        this.parameters = parameters;
        appletContext = new RSAppletContext();

    public boolean isActive() {
        // Lets everything know that it is alive
        return active;

    public void setActive(boolean active) {
        // A setter method for us so we can say we've started the Applet
        this.active = active;

    public URL getDocumentBase() {
        // Return the codebase parameter from our parameter map, the document base is exactly the same as the codebase
        try {
            return new URL(parameters.get("codebase"));
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("Invalid Document Base URL");

    public URL getCodeBase() {
        // Return the codebase parameter from our parameter map
        try {
            return new URL(parameters.get("codebase"));
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("Invalid Code Base URL");

    public String getParameter(String name) {
        // Get the requested parameter from the map
        if (parameters.containsKey(name))
            return parameters.get(name);
        return null;

    public RSAppletContext getAppletContext() {
        // Return our instance of RSAppletContext so we can fake the environment
        return appletContext;

    public void appletResize(int width, int height) {
        // So the environment can set the applet size
        Applet applet = getAppletContext().getApplet("main");
        if (applet != null)
            applet.resize(width, height);

The comments should be enough to allow you to understand what is going on. Please not the setActive method, we have added this in so we can manually set that the Applet is alive.


Put it together and what have you got.....?


We have got most of the dross out of the way now and on to the actual loading of the Applet. So lets make a class to hold our main method. To load the Applet we need to do the following steps:

  1. Create an instance of out ConfigReader and parse the parameters
  2. Use the parameters to build the jar location we are loading
  3. Create an instance of a URLClassLoader to load the jar from a URL
  4. Use the parameters to get the correct class, usually always "client"
  5. Create a new instance of the class and cast it to an Applet so we can use it
  6. Create our stub so we can set the AppletStub of the Applet and pass in the parsed parameters
  7. Use our setter to set the Applet in the AppletContext
  8. Set the AppletStub of the Applet of the Applet
  9. Turn the key and start the Applet up
  10. Set the size, this can also be done by reading the parameters
  11. Using our setter, make it so everything knows the Applet is active
  12. Create a JFrame and add the Applet to it

Run this, wait a few seconds for the Jar to be loaded, and you should see a window pop up that looks like this:


And because I'm nice here's the code with comments that match up to the steps  ;)

public static void main(String[] args) {
        // Create an instance of our config reader and parse the parameters
        ConfigReader configReader = new ConfigReader();
        Map<String, String> map = configReader.read();
        // Use the parameters to build the jar location we are loading
        String jarLocation = map.get("codebase") + map.get("initial_jar");
        try {
            // Use a URLClassLoader because we are loading classes from a jar at a URL
            URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL(jarLocation)});
            // Use the parameters to get the correct class, usually always "client"
            Class<?> clientClass = classLoader.loadClass(map.get("initial_class").replace(".class", ""));
            // Create a new instance of the class and cast it to an Applet so we can use it
            Applet applet = (Applet) clientClass.newInstance();
            // Create our stub so we can set the AppletStub of the Applet and pass in the parsed parameters
            RSAppletStub appletStub = new RSAppletStub(map);
            // Use our setter to set the Applet in the AppletContext
            // Set the AppletStub of the Applet
            // Turn the key and start the Applet up
            // Set the size, this can also be done by reading the parameters, but I was too lazy to parse the Int's
            applet.setSize(765, 503);
            // Using our setter, make it so everything knows the Applet is active

            // Create a JFrame and add the Applet to it
            JFrame frame = new JFrame("Runescape");
            frame.setSize(800, 600);
            JPanel panel = new JPanel();
        } catch (MalformedURLException | InstantiationException | IllegalAccessException | ClassNotFoundException e) {

Final Words


There we have it a basic loader. This is how pretty much every bot loads the game, yes even those crappy ones that think they are cool using the Jagex loader jar. You can extend this to become a Reflection viewer or even a bot itself. Those, however, I will not go into at this time (I waffle a lot so it'd get very boring). 


As a special present to those who managed to read the entire thing the repo which contains the fully working loader, fully commented.


If anyone notices any spelling mistakes, formatting or other poor English let me know and I'll fix it, too lazy to proof read it.

  • Swipe, Marneus901, Shatterhand and 4 others like this

#12700 Tracking player IP's

Posted by dan on 14 March 2017 - 02:06 PM

yes there is actually a secret method to get other player's ip. im not going to post it on the forums for obvious reasons. i am assuming you are interested in it from your post. I'd be willing to help you out but it would come at a cost. I'm open to different types of compensation ($/RSGP) but ultimately i think you will have to suck on my fat cock u ddosing faggot


alternatively you could ask kyle. i heard he is good at leaking rs3 ironmen player ips.


Thanks In Advance,

Dan :)

  • YungKingDog, 0xAA4A, Lotto and 3 others like this

#12678 Swapping widgets

Posted by dan on 06 March 2017 - 10:50 PM

lol do u think he was talking about bot scripts? gimme some of dat good shit u smokin fam

just bout to light up this fatty
  • Fox, YungKingDog, BoneCode and 3 others like this

#11232 Introduction and Comprehensive Cache Format

Posted by packers2016 on 30 November 2015 - 04:30 AM

But we had 0 information about it (this comprehensive) here at RS-H. We are not rune-server.


that attitude really explains an awful lot about this site :)

  • YungKingDog, 0xAA4A, Dex and 3 others like this

#10680 How many types of de-obfuscation methods do you need?

Posted by HobbitChild on 02 October 2015 - 05:01 PM

7 types

  • YungKingDog, 0xAA4A, Dex and 3 others like this

#10515 [Reflection] How to replace Canvas without xbooting/injection

Posted by HobbitChild on 01 September 2015 - 10:48 PM

please explain to everyone here how

will end up with the same bytecode instructions? They wont.




now please stop wasting my fucking time

  • Cov, YungKingDog, Doug and 3 others like this

#12612 processAction

Posted by YungKingDog on 18 February 2017 - 05:19 AM

Does the current doAction still evade bot detection?

yes, i would suggest testing it on your main to find out

  • Fox, Doug, 0xAA4A and 4 others like this