View Full Version : Open Source Updater

11-13-2016, 03:31 PM
Java Class Updater

So, after much deliberation and delays, i'm finally going to release my full updater open source for people to use and learn from (though the latter is quite unlikely). If enough people start using it and contributing, it should help make the updates of the include occur much faster. I kept everything in the updater, so it should be ready to run. As such, once you download and set it up, anyone should be able to run it and submit a pull request to the include to update the hooks.

Java-Class-Updater (https://github.com/KyleHunter/Java-Class-Updater)

I will not be using this thread/tutorial to talk about how to make an updater, or to explain how one works. This thread is simply going to describe how this specific updater is organized and how to best modify it. If you do not already have a good understanding of JVM bytecode or how updater's work, I suggest reading Brandon;'s tutorial located here (https://villavu.com/forum/showthread.php?t=111556). You will most likely be wasting your time if you try and learn much from this thread without having a decent background.

If you find any hooks that are broken, please create a Github issue here (https://github.com/KyleHunter/Java-Class-Updater/issues). If you are interested in contributing to the updater and there are any issues currently, that is an excellent place to start.

General Overview

Much of this updater is written quite neat and organized, while other parts of it are very inefficient and bloated. In my last rewrite of it, I decided to keep the deob bloated, as it works very well and doesn't need updating often (if ever). I spent the majority of my time ensuring that the class and field finders were very easy to modify and use. This updater is strongly based on deobfuscation of the gamepack, much of which is specifically done for an individual pattern. This allows each pattern to be incredible strong, and allows the finding of patterns simple, due to the fact that they rarely change since the deob removes the changes to the bytecode. Many of the patterns used in the updater are your standard regex patterns, i.e ALOAD, GETFIELD, ACONST_NULL. For other patterns, those that can not be easily found with a standard regex pattern, I follow unique conditional statements or opaque predicates. This seems to be a unique approach as compared to the majority of updaters used. It allows the use of very small patterns that don't break, yet are still incredibly unique within the gamepack.

The deob is located within the deob/ package and that is where all the deobfuscation occurs. The DeobFrame.java is simply an abstract class which we overwrite with the specific deob we are using. Deobsfucate.java contains the running portion of the code in which we add all the deob methods to a list, and run each entry. Method.java removes the unused methods within the pack (this was heavily taken from an old jh thread). Multipliers.java standardizes the way in which the multipliers are written in the pack. EqualSwap.java was originally done in order to standardize the way in which variables were assigned in the pack, but is now just a catch all for any type of deob that I add to the updater (very messy and unorganized). RemoveExceptions.java does just that, removes all exceptions that are added to the pack as a means of obfuscation (much of this was done together with Brandon). OpaquePredicates.java standardizes the comparators within all opaque predicates. MethodName.java removes dummy parameters from method names. Note: this is not the "proper" way in which to do this, and it renders the pack completely unable to run. This is because I don't remove the parameter from references to the method. Since I only use the updater to update and not for a bot, it doesn't affect me.

Both the class finders and field finders are located within analysers/package.

Class Analyzers
Within analysers/classes is where all the classes and methods live in which all the class names within the gamepack are located and identified. The classAnalyserFrame.java class is the framework that is extended for all of the individual class analyzers to use specifically. Most of the methods and fields that are contained within are self explanatory and easy to use. Each class finder extends the classAnalyserFrame and implements the abstract method "identify()". This is the actual working part of the code that is used to find the specific classes within the client. All of the classAnalsyers are run from a class mainAnalyser.java which is located within analysers/methods. This class loads all the class finders into a list and runs each one individually. If a class is unable to be found, it will prompt you to enter in the class name (if you have it).

Field Analyzers
All of the field analyzers are located inside analysers/methods. This is where the majority of the time is spent in maintaining an updater, as these are the most apt to break. Similar to the class analyzers, there is a methodAnalyserFrame.java which is extended to every specific class of finders. Also similar to the class analyzers, the method "identify()" is implemented in every class and contains all the code to find all wanted fields within the class. Objects for the classes are created in the analysers/methods.java class. This allows us to access any of the field values from any location in the updater. This makes it easy to find fields that depend on the location of another. These analyzers are also run from the methods/mainAnalyser.java class.

Updater Running
The main class that is run upon execution is the main/eUpdater.java class. There are several useful variables in which you can change depending on what you want:

Revision is the current revision of the client, if it is higher than the current gamepack downloaded, it will download a new one.
simbaPrint if true will save the hooks file to a simba file located at: "C:/Simba/Includes/Reflection/lib/internal/Hooks.simba"
logPrint will print the readable hooks file to the debug
forceDeob/Download are self explanatory
findMultis will find multipliers if true
doRefactor will do a very crappy and basic refactor of the client if set true

11-13-2016, 03:31 PM
Using the Searchers

As much as possible, I tried to keep the bulk of the code used in finding fields done within other methods to allow the writing of each pattern to be as short as possible. I didn't want to do this to such an extreme that it made it difficult to make complex patterns either. As such, the searcher/ package contains a multitude of useful methods in which allow important bytecode determinations, with very little code needed.


As the name implies, the FieldSearcher class contains methods that involve the fields within the gamepack. As such, all of these methods are used on a specific class within the gamepack, never a method (as a method doesn't own a class).
A FieldSearcher object is created like so:

FieldSearcher Fs = new FieldSearcher(cF); //Where cF is a classFrame
Most of the FieldSearcher methods or pretty self explanatory, but i'll write a bit about here anyway:

findDesc(String desc) -> Returns first field found with matching desc
countDesc(String desc) -> Returns the number of fields that have the specified desc
findContainsDesc(String desc) -> Same as findDesc, except not exact
findAccess(int Acc) -> Returns first field found with matching access
findValue(int Acc)-> Returns true if a field is found with the matching value
findDescInstance(String desc, int Instance) -> Same as findDesc, but will return the specified instance. This is useful for looping through fields

The FieldSearcher's are pretty limited and are mainly used for identifying classes, or very basic fields


This class is used to find the multiplier for a given integer field. Feel free to look through it to see how it works, but since it's used at the end on all fields, you really won't ever touch it.


This is the most used class within the updater, and almost every field within the updater is found by using it. It contains the majority of all the finders within the updater.

To be continued..

11-13-2016, 03:32 PM

11-13-2016, 03:35 PM
You're a gentleman and a scholar, sir. This community (and many others, before too long) thanks you profusely.

11-13-2016, 03:38 PM
and many others

I think DreamBot can finally start updating their hooks on time, gg.

EDIT: Also finally, finally I have something to read on for the upcoming weeks :)

11-13-2016, 06:15 PM
Thank you Kyle. Interesting to see where this will go. Looking forward to that tutorial.

11-13-2016, 06:44 PM
Awesome! Thanks for sharing this! Looking forward to reading through it!

11-13-2016, 06:45 PM
Glad I didnt bother you earlier! it seems like there has been a high frequency of updates lately; I was literally scratching my head on how you obtain the values in your hooks file.


wow... this is much more expansive than I could have guessed...

11-16-2016, 08:52 PM

One Kid
11-23-2016, 10:26 PM
I want to personally thank you for contributing to this community, great post.

01-22-2017, 11:33 PM
So the following fields are outdated?
How can i update them?
I post them in the GitHub link.

Broken Fields:
~> SpokenText
~> CombatCycle
~> Health
~> MaxHealth

01-23-2017, 12:59 PM
So the following fields are outdated?
How can i update them?
I post them in the GitHub link.

Broken Fields:
~> SpokenText
~> CombatCycle
~> Health
~> MaxHealth

Yes, there's an issue already for it here (https://github.com/KyleHunter/Java-Class-Updater/issues/2). You can update it, but the entire health method in actor changed, so the include for simba needs to be rewritten for proper health support

01-24-2017, 04:16 PM
looks interesting thanks for sharing.

01-25-2017, 03:10 PM
Looks good :)

02-09-2017, 01:27 PM
I am absolutely stumped.. i managed to get the Update working last hook update. But now it keeps downloading Rev 129

Has this major update broken something in the updater? that Sir. Kyle needs too fix or am i still noob at this?

02-09-2017, 03:33 PM
AFools; Just change the revision to 133 and re-run the updater.

02-09-2017, 04:38 PM
that didn't work. have you both been able to update your hooks successfully?

I delete the previous posts to keep this thread tidy*** but i would love a reply. even by pm

02-09-2017, 05:19 PM
that didn't work. have you both been able to update your hooks successfully?

I delete the previous posts to keep this thread tidy*** but i would love a reply. even by pm

ReflectionRevision = '133';
{Node: db}
Node_Prev: THook = ['cv', 1];
Node_UID: THook = ['cg', 1];
Node_Next: THook = ['cz', 1];

{Cacheable: dh}

{Renderable: cg}
Renderable_ModelHeight: THook = ['cw', -741542225];

{Animable: av}

{Model: cu}

{AnimationSequence: gc}

{NpcDefinition: gl}
NpcDefinition_Actions: THook = ['u', 1];
NpcDefinition_Name: THook = ['j', 1];
NpcDefinition_ID: THook = ['w', -644397589];
NpcDefinition_CombatLevel: THook = ['m', 492855685];

{linkedList: dk}
linkedList_Head: THook = ['k', 1];
linkedList_Current: THook = ['y', 1];

{Actor: aq}
Actor_QueueX: THook = ['ck', 1];
Actor_QueueY: THook = ['cy', 1];
Actor_QueueSize: THook = ['cu', 50188383];
Actor_WorldX: THook = ['aq', -1277733559];
Actor_WorldY: THook = ['aw', 904629673];
Actor_Animation: THook = ['bf', -914774871];
Actor_SpokenText: THook = ['NULL', 1];
Actor_CombatCycle: THook = ['NULL', 1];
Actor_Health: THook = ['NULL', 1];
Actor_MaxHealth: THook = ['NULL', 1];
Actor_InteractingIndex: THook = ['bz', 988218421];

{Npc: am}
Npc_Definition: THook = ['k', 1];

{ObjectDefinition: gb}
ObjectDefinition_Actions: THook = ['aw', 1];
ObjectDefinition_Name: THook = ['g', 1];

{Buffer: fm}

{Widget: ee}
Widget_Children: THook = ['ex', 1];
Widget_ItemID: THook = ['ep', 2019303675];
Widget_ItemAmount: THook = ['ew', -857758291];
Widget_WidgetID: THook = ['i', -1084644105];
Widget_Name: THook = ['cn', 1];
Widget_Text: THook = ['bt', 1];
Widget_IsHidden: THook = ['ai', 1];
Widget_AbsoluteY: THook = ['ap', 443329775];
Widget_AbsoluteX: THook = ['x', -1357818025];
Widget_RelativeY: THook = ['aj', 1042321287];
Widget_RelativeX: THook = ['af', 922112325];
Widget_Width: THook = ['aq', 1692967795];
Widget_Height: THook = ['aw', 1871365705];
Widget_ParentID: THook = ['aa', -119763401];
Widget_ScrollY: THook = ['aj', 1042321287];
Widget_ScrollX: THook = ['af', 922112325];
Widget_InvIDs: THook = ['ei', 1];
Widget_BoundsIndex: THook = ['ea', -1167966953];
Widget_StackSizes: THook = ['et', 1];

{WidgetNode: t}
WidgetNode_Id: THook = ['k', -264141929];

{HashTable: dw}
HashTable_Buckets: THook = ['y', 1];
HashTable_Size: THook = ['k', 1];
HashTable_Index: THook = ['w', 1];

{GameShell: dr}

{Player: e}
Player_Name: THook = ['k', 1];
Player_Definition: THook = ['y', 1];
Player_CombatLevel: THook = ['q', 780783815];

{Client: client}
Client_LoopCycle: THook = ['client.u', -1160915299];
Client_MenuOptions: THook = ['client.iw', 1];
Client_MenuActions: THook = ['client.ik', 1];
Client_IsMenuOpen: THook = ['client.hi', 1];
Client_MenuX: THook = ['fq.hk', -1705421671];
Client_MenuY: THook = ['hp.hw', 1166704265];
Client_MenuWidth: THook = ['ce.hc', 352118349];
Client_MenuHeight: THook = ['f.ht', 1699315237];
Client_MenuCount: THook = ['client.hf', -676073067];
Client_Region: THook = ['be.dl', 1];
Client_Plane: THook = ['az.gu', -202288423];
Client_LocalPlayers: THook = ['client.gd', 1];
Client_DestinationY: THook = ['client.my', -1853127321];
Client_DestinationX: THook = ['client.mz', -2089549517];
Client_LocalPlayer: THook = ['ck.hm', 1];
Client_BaseX: THook = ['ds.dj', 148386865];
Client_BaseY: THook = ['aw.dv', -2113345181];
Client_Widgets: THook = ['ee.k', 1];
Client_GameSettings: THook = ['ea.o', 1];
Client_CurrentLevels: THook = ['client.hj', 1];
Client_RealLevels: THook = ['client.hg', 1];
Client_Experiences: THook = ['client.hd', 1];
Client_Weight: THook = ['client.jb', 896323235];
Client_Energy: THook = ['client.jl', -1201387633];
Client_CurrentWorld: THook = ['client.j', -991946675];
Client_WidgetNodeCache: THook = ['client.it', 1];
Client_TileSettings: THook = ['g.y', 1];
Client_TileHeights: THook = ['g.k', 1];
Client_LocalNpcs: THook = ['client.ch', 1];
Client_NpcIndices: THook = ['client.ct', 1];
Client_CrossHairColor: THook = ['client.gx', 1273935027];
Client_MapOffset: THook = ['client.ed', 1960296863];
Client_MapAngle: THook = ['client.fb', -701834271];
Client_MapScale: THook = ['client.et', 1198943407];
Client_CameraPitch: THook = ['gv.fy', -1960299599];
Client_Sine: THook = ['cy.e', 1];
Client_Cosine: THook = ['cy.l', 1];
Client_CameraYaw: THook = ['r.fm', -1354163607];
Client_CameraZ: THook = ['ai.fe', -641523113];
Client_CameraX: THook = ['gk.fc', 1755627167];
Client_CameraY: THook = ['p.fo', -1666790629];
Client_GroundItems: THook = ['client.hp', 1];
Client_LoginState: THook = ['client.f', -398258759];
Client_PlayerIndex: THook = ['client.gn', 1012417049];
Client_WidgetPositionX: THook = ['client.lz', 1];
Client_WidgetPositionY: THook = ['client.lx', 1];
Client_WidgetWidths: THook = ['client.lb', 1];
Client_WidgetHeights: THook = ['client.le', 1];

{Region: ca}
Region_SceneTiles: THook = ['j', 1];

{BoundaryObject: cz}
BoundaryObject_ID: THook = ['d', 361638875];
BoundaryObject_Flags: THook = ['n', 237331929];
BoundaryObject_LocalX: THook = ['y', -1960985619];
BoundaryObject_LocalY: THook = ['o', -151355893];
BoundaryObject_Plane: THook = ['k', 881373909];
BoundaryObject_Render: THook = ['j', 1];
BoundaryObject_Render2: THook = ['q', 1];
BoundaryObject_Orientation: THook = ['r', -825712711];
BoundaryObject_Height: THook = ['w', 390668661];

{GameObject: cr}
GameObject_ID: THook = ['i', -1720341121];
GameObject_Flags: THook = ['v', -1821318125];
GameObject_Plane: THook = ['k', -1974480323];
GameObject_WorldX: THook = ['o', -86230869];
GameObject_WorldY: THook = ['r', 691719557];
GameObject_Height: THook = ['y', 40754033];
GameObject_Render: THook = ['w', 1];
GameObject_Orientation: THook = ['j', -484814025];
GameObject_LocalX: THook = ['q', -471364253];
GameObject_LocalY: THook = ['n', -627130765];
GameObject_OffsetX: THook = ['d', 1028189815];
GameObject_OffsetY: THook = ['c', 2076095165];

{FloorDecoration: ci}
FloorDecoration_Render: THook = ['r', 1];
FloorDecoration_LocalX: THook = ['y', -1753048791];
FloorDecoration_LocalY: THook = ['o', 775897375];
FloorDecoration_Plane: THook = ['k', 1530199991];
FloorDecoration_ID: THook = ['w', 1076621313];
FloorDecoration_Flags: THook = ['j', 1465975205];

{WallDecoration: cm}
WallDecoration_ID: THook = ['c', 1404301829];
WallDecoration_Flags: THook = ['s', -2031378505];
WallDecoration_LocalX: THook = ['y', 1989015177];
WallDecoration_LocalY: THook = ['o', 1606769689];
WallDecoration_Plane: THook = ['k', 1715294221];
WallDecoration_Renderable: THook = ['d', 1];
WallDecoration_Renderable2: THook = ['n', 1];
WallDecoration_Orientation: THook = ['r', -428581447];
WallDecoration_Height: THook = ['w', 1520110451];
WallDecoration_RelativeX: THook = ['j', 1216538467];
WallDecoration_RelativeY: THook = ['q', 902214613];

{SceneTile: ct}
SceneTile_GameObject: THook = ['g', 1];
SceneTile_BoundaryObject: THook = ['q', 1];
SceneTile_WallDecoration: THook = ['d', 1];
SceneTile_GroundDecoration: THook = ['n', 1];

{Item: au}
Item_ID: THook = ['k', -1628923779];
Item_StackSizes: THook = ['y', 2022815601];

03-26-2017, 04:58 AM
I ran this and it updated my hooks, most reflection stuff seemed OK but all my functions for using inv items dont work :(

then I ran


and it returned -1.

For Item the hooks were:
StackSizes : ao.f * -789244747
ID : ao.i * -166911529

Anyone else getting this? if not can some1 post the item hooks lol

12-11-2017, 10:25 PM
I'm looking at Method.java (redundant/unused method deobber) and wondering two things:

public int Deob() {
int Total = 0;
int Fixed = 10;
while (Fixed != 0) {
Fixed = Run();
Total = Total + Fixed;
System.out.print("Removed " + Total + " Dummy Methods");
return Total;

The while loop here I think means that it is doing multiple passes over the class nodes; why does it take more than one pass to remove all of the unused methods?

for (ClassNode Class : CLASSES.values()) {
List<MethodNode> Methods = Class.methods;
for (MethodNode Method : Methods) {
totalMethods.add(new MethodInfo(Class.name, Method.name, Method.desc));
if (Method.name.length() > 2 || Modifier.isAbstract(Method.access) || isOverridden(Class, Method)) {
MethodInfo mInfo = new MethodInfo(Class.name, Method.name, Method.desc);
Add(mInfo, goodMethods);

Here it is considering any method with a name length > 2 as a used method. Why is that? Is this specific to Jagex's obfuscation or am I missing something obvious here.


12-12-2017, 02:54 AM

There is no method in the entire obfuscated jar that is more than 2 letters UNLESS it is a JDK method.. This also goes for class names and field names (with the exception of "client"). It's the way Jagex's obfuscator works. In my own de-obber, I don't check for name length because I use mine for RS3 as well..

Also, it does multiple passes because if one unused method is calling another method, that other method wouldn't have been registered as "un-used" the first time around.. So once you do the first pass, you will now have new un-used methods that aren't called by any other methods or any other unused methods.. Therefore multiple passes helps in this case. It's easier to do multiple passes rather than storing a stack of possible unused methods.


1st pass (scan for unused methods and remove them):

void foo() { //Foo is unused.. Will be removed at the end of the first pass.
int i = 0;
print(bar(i)); //Bar is being used..

int bar(int i) { //Unused parameter.
return 0;

2nd pass (scan for new unused methods and remove them): foo ()V was removed in the first pass..

int bar(int i) { //Unused parameter. Unused method.
return 0;

3rd pass..: -- bar (I)I removed in the second pass..