Thread: [NXT] Disabling ISAAC + Reverse-engineering tutorial

Results 1 to 6 of 6
  1. #1 [NXT] Disabling ISAAC + Reverse-engineering tutorial 
    Registered Member

    Join Date
    Nov 2014
    Thanks given
    Thanks received
    Rep Power
    Why am I doing this... How to disable ISAAC in the NXT client
    Or: How to get started with Ghidra, finding your way, patching instructions, exporting the binary. You don't have to actually disable ISAAC, you can follow along. Or you are redirected from future tutorials as a reference as to how to work with Ghidra.

    This is a long tutorial

    As with my [Only registered and activated users can see links. ], this can get technical. However, I am aiming to keep it as simple as I can so that everyone can follow along and learn about Ghidra and NXT.

    In this tutorial, the following will be handled, in order:
    - How to open your binary with Ghidra
    - How to find the jag::Isaac::Init method in the client using memory searching
    - How to find the jag::Isaac::Generate method
    - How to create a structure for our jag::Isaac struct
    - How to refactor our jag::Isaac::Generate method
    - How to patch jag::Isaac::Generate to disable ISAAC
    - How to export our binary

    This tutorial will NOT cover the following:
    - How to generate the hash for the patched binary
    - How to compress the patched binary
    These are things that are already in my patcher. I do expect you to be able to understand the patcher before making a server with NXT. The above two are literally functions in my patcher. If you can't figure that out, this tutorial is not for you, and you should focus on learning more about programming. NXT is complex, and this tutorial does not cover packet identification, which is a way more complex process.

    A heads-up
    So, first of all, I am not sure how smart doing this is. As long as you don't disable RSA your passwords aren't stored plaintext, but it's still not ideal to do this. Unfortunately though, many frameworks don't support ISAAC, and sometimes it's even a huge pain in the butt to add support for ISAAC (Looking at you, Matrix). I highly recommend avoiding this last-resort, as it's technical, difficult, and I am not sure yet if this will break things.

    Basically, what we're going to do is patch some instructions in the jag::Isaac::Generate method. We're going to have to use some assembly tricks in order to preserve instructions, as we don't have much space (The client I tested it on only had 4 bytes at the end of the function).

    Finally, for future reverse engineering, sometimes the compiler inlines functions. This means instead of making a function, the instructions of the functions are added to every place the function would have been called. This also makes reverse engineering harder.

    We're going to need a NXT client and Ghidra. You can get Ghidra at [Only registered and activated users can see links. ]

    Project setup
    Once you have downloaded Ghidra, open it up and make a new project. Give it any name you want. Then, drag the NXT client into the window, importing it. Just click OK if you face any dialogue, the default settings should be good. Please don't forget to extract the client from the LZMA archive. The client should be 7-8MB. If it's around 2MB, extract it, and use the extracted client.

    Your Ghidra window should look something like this, by this point:

    Analyizing the binary
    Now, double-click your binary. In my case, rs2client_raw.exe. This will open up the editor. The first time you open up a binary, you will be faced with a popup box asking if you want to analyze the binary. Once analysis is done, you won't have to analyze the binary anymore the next time you open it. Click yes, and then click Analyze. This will take a while (Up to 40-45 minutes depending on your computer). Click it now and continue reading so you know what's up.

    Allow Ghidra to take its time. Binaries are incredibly complex, unlike Java, the only data remaining in binaries are raw instructions. No class structures, no function start positions, no function end positions, literally only raw instructions. This is what makes reverse engineering so difficult too. If you're going to get serious about NXT, this tutorial will be a good place to start off, as it introduces you into how to get started with Ghidra.

    Once Ghidra is completely done analyzing the binary, you'll get a warning about Ghidra being unable to locate the PDB file. It's impossible to get this file though, unless Jagex leaks it. Click OK, and we can continue with the tutorial.

    Searching for jag::Isaac::Init using memory searching
    As mentioned before, binaries are incredibly difficult to look through. While Ghidra has a decompiler, you can't really look through the binary by looking for code. This restricts our options on how we can find methods. That's also why we don't directly find jag::Isaac::Generate, but find it through jag::Isaac::Init. The same goes for many other functions - you'll have to be smart about it. This is not Java, this is far more complicated.

    Anyway, there's one constant in the jag::Isaac::Init method we can use to find the method, which is used in several methods. We'll have to figure out which function is correct, and which is not. The constant we're looking for has hex value 0x9e3779b9. In Ghidra, press S on your keyboard to open the Search Memory popup. Alternatively, click Search in the top bar, and click Memory. In Search Value enter 9e3779b9. Select Hex format, Loaded Blocks and Search All. Finally, click search:

    We're only interested in MOV operations, so we can sort by Code Unit in the results dialog. This will allow us to search more efficiently. We're looking for a function with several while loops, no nested loops, and a lot of bitshifting. At the end of the function there should be another function call. There's should also be no if statements. The end of the function in my binary looked like this:

    Cool, now that we found the jag::Isaac::Init method, we can name it! Scroll all the way up in the decompiler and select the function name, now press L on your keyboard and enter jag::Isaac::Init

    Now, Init is the function name, in the namespace jag::Isaac. We're naming things similarly as to how we would name things in Java, following a certain hierarchy going from a wide scope to a small scope (jag is a big scope, containing many things, Isaac is a way smaller scope and contains only Isaac related things)

    Finding the jag::Isaac::Generate method
    Nothing much to find, honestly. Remember how there must be a function call at the bottom of our jag::Isaac::Init function? That is our jag::Isaac::Generate method!

    Double-click it to go to the function.

    NOTE: If the function does not look like that, please PM me. It could be I made a mistake and that there's two similar functions that do different things. In which case I need to know so I can update this tutorial

    First things first, let's rename the function so that we know which function we're dealing with again. Select the function name in the decompiler, press L and enter jag::Isaac::Generate. Now we can get back to this method easily through the Symbol Tree

    Creating a struct for our jag::Isaac struct
    We're decompiling the NXT binary as if it were C, so instead of using classes, we're using structs (To keep things simple). Ghidra does have support to create class structures, but I won't cover that in this tutorial.

    In our jag::Isaac::Generate function, scroll to the top of the function and rename param_1 to this. Select param_1 and click L on your keyboard to rename a variable.

    Now, this field probably has a random data type, even though we know it's a struct. So let's create a struct for this data type. Right-click either the parameter type or the parameter name and select Auto Create Structure. Ghidra will now automatically create a struct that most of the time has the right size and some of the fields already identified.

    Our struct is now called astruct, but we want it to have an identifiable name. Right-click astruct in the decompiler and click Edit Data Type. This will open a popup that looks something like this:

    At the bottom there's some fields, we're mainly interested in the Name field. Re-name the struct to jag::Isaac. The size, most of the time, is correct. Now, close the type editor and you'll see our function's prototype is now very readable:

    Refactoring our method and struct
    Our decompiler window now looks slightly different, with a lot of arrows and fields instead of array indices/pointer magic going on:

    Now comes the fun part - refactoring our Isaac struct. We're now going to be cross-referencing with a normal Isaac implementation:
    	private void generateMoreResults() {
    		bb += cc;
    		for (int i=0; i<256; i++) {
    			int x = mm[i];
    			switch (i&3) {
    			case 0:
    				aa = aa^(aa<<13);
    			case 1:
    				aa = aa^(aa>>>6);
    			case 2:
    				aa = aa^(aa<<2);
    			case 3:
    				aa = aa^(aa>>>16);
    			aa = mm[i^128] + aa;
    			int y = mm[i] = mm[(x>>>2) & 0xFF] + aa + bb;
    			randResult[i] = bb = mm[(y>>>10) & 0xFF] + x;
    		valuesUsed = 0;
    (Source: [Only registered and activated users can see links. ])
    As you can see, in my decompiler window, line 12 is incrementing something by one, and is used in line 14 to increment another field. So we already know which field is bb and cc. We can rename these fields through the Data Type Editor, or just through the decompiler. Select the variable names, and press L. Now enter the proper names:

    On line 36 we can see a bitwise XOR operation, which looks a lot like the aa = aa ^ (aa *something*) going on in the switch statement above, so we can rename that field to aa.

    Based on that, we can also figure out which field is mm - in my code that's field_0x404, as on line 39 aa is incremented using mm. So let's rename that to mm.

    Now, that looks quite ugly. Let's open our data editor and see what's going on. We know mm is an array that holds 256 ints. Go to the top of the function, right-click jag::Isaac (The parameter type), click Edit Data Type and in Search enter mm, then press enter:

    As you can see, Ghidra identified field mm to be an uint, but not a uint array! Let's change it. Let's double-click the DataType column for mm and enter uint[256]:

    It won't let us press enter. At the bottom, an error pops up uint[256] doesn't fit within 4 bytes, need 1024 bytes. The field below is identified as uint too, causing this error (In my editor at offset 1032). So let's un-identify that field. Press ESC to cancel setting the data type of mm. Now click the field below (For me, offset 1032). Then press C on your keyboard to Clear the field. This sets the bytes to undefined:

    Now, let's try to set the type of mm to an uint[256]:

    That seems to have done the trick!

    Now, there's still some unresolved fields we have to resolve. Let's get on with that! Around line 44 in my decompiler there's this code:

    Interestingly enough, the variable puVar6 is set to this->mm at the beginning, which is an array. Also, interestingly enough, hex 0x100 is decimal 256, and since it's an array of uints (size = 4 bytes), it's setting data 1024 bytes before puVar6. It also increments the memory address of puVar6 each time by 4 bytes (Once again, +1 = 4 bytes as the data type of the array is 4 bytes). Based on this we can conclude there's another array of 256 values right before this array. The only unidentified array that remains is the results array. So let's open our data editor again and scroll up all the way:

    And as you can see, there's already an uint that Ghidra found. Now, let's change the type of this field to an uint[256] just like we did to mm. I have also renamed the field to results by double-clicking the name column:

    We're almost done completely refactoring the jag::Isaac struct. Only one field remains, which is our remaining field, indicating how many numbers remain before new data must be generated. There's also only enough for one more int, so let's give the field at offset 0 data type uint and let's name it remaining. Now our struct is fully refactored.

    NOTE: Not everything is an int or uint, sometimes fields are char or short. You don't know for sure, but I knew for sure that remaining is of type int. Also the signing on the above fields may be wrong, I just went with what Ghidra picked. The most important thing is having the field names.

    Wew. That was definitely a long section, the refactoring part.

    Patching ISAAC out
    Alright, so now it's time to actually disable ISAAC. For this, we'll be delving into assembly. Remember how the statement here sets a value in the results array?

    If we set that to 0, that means every time we get a value for the ISAAC, we get a 0, which means every opcode is offset by 0, effectively removing ISAAC from the protocol, allowing us to be super lazy and making supporting frameworks like Matrix a lot easier.

    The question is how we do it, though. Let's click the code in the decompiler, and it'll jump to the related assembly code.

    The instruction we're interested in is MOV dword ptr [R9 + -0x400], EDX. This sets the value of dword ptr [R9 + -0x400] to the value of EDX. EDX and R9 are registers (You can google them - assembly x64 registers). Sadly, if we patch this to MOV dword ptr [R9 + -0x400], 0x0, we'll use too many bytes, so that approach is impossible.

    We can try to work out if we can change the value of EDX though. It gets assigned and then added to in the first two instructions of the above screenshot. So while we can't do puVar6[-0x100] = 0x0 directly, we can cheat our way there. Instead of doing ADD EDX,R10D we can do XOR EDX,EDX. This is a very memory-efficient way of setting a field to 0, requiring only 2 bytes!

    Right-click the ADD EDX,R10D code and click Patch Instruction. You will get a popup about assembler rating, just click OK. It will then construct the assembler. After a bit we can type:

    Since we want to set the value in the register EDX to 0, we do XOR in the operator, and as operands, we enter EDX,EDX
    Note: Operator = the field on the left. Operand = the field on the right

    Now press enter to patch the instruction. But now we have a problem. The previous MOV instruction required 3 bytes, but the XOR only requires 2 bytes. So we have a leftover byte! Thankfully, we can just patch it to NOP, meaning it'll just get skipped.

    Right-click the stray byte, Path Instruction and enter NOP in the operator. Leave the operand field empty. Finally, press enter to patch the instruction.

    Finally, as you can see in the decompiler window, we see some changes:

    The results array is now completely set to zeroes! It doesn't really matter that we broke the mm field - we're breaking ISAAC anyway. Now, everything is set to zero and packet opcodes will be offset by 0 all the time, both for incoming and outgoing packets.

    Note: You'll still have to decrypt the XTEA block during login which follows the RSA block, it contains the username. We only patched out the ISAAC stuff, not the XTEA stuff during login

    Exporting the binary
    First, save the project. Press CTRL+S or the save button in the top-left of the screen. Then click File (Top left of screen)->Export Program Or press O on your keyboard.

    Now, set the format to Binary. Then, select an output file. Ghidra will automatically append .bin to the filename, but you can rename it to .exe. Then, click OK and you're good to go!

    You can now re-calculate your CRC and re-generate your hash, compress the client, and test it out. If you crash due to some stack error, you likely messed something small up. Cry it out and start over. I've done that plenty times.

    I am interested in making more tutorials on NXT, so if you have any ideas, please drop them below. Also, I've only tested this tutorial on the 910 client, so things may look slightly different in other clients. If you're really stuck on another version, please let me know and send me your binary. I can take a look for you. I'm interested in helping the scene out as a whole, and it seems like frameworks have a hard time supporting ISAAC, so this is my first push in the back for those servers. ISAAC can cause a lot of weird issues, especially with the NXT client.

    This tutorial also attempts to cover some of the useful parts of reverse engineering, so even if you don't want to patch ISAAC out, it contains a lot of background information that will be required in future tutorials.
    Reply With Quote  

  2. #2  
    JTlr Frost's Avatar
    Join Date
    Oct 2014
    Thanks given
    Thanks received
    Rep Power
    teachdaan, looks good man!

    [Only registered and activated users can see links. ]
    Spoiler for The Hatred Group:

    Spoiler for Dapoosie:

    Reply With Quote  

  3. #3  

    Join Date
    May 2012
    Thanks given
    Thanks received
    Rep Power
    perfect man, good job
    Add my skype for services!
    Check out my services @ [Only registered and activated users can see links. ]
    Also check out my Matrix 3 services thread @ [Only registered and activated users can see links. ]
    Skype : [Only registered and activated users can see links. ].
    Email : [Only registered and activated users can see links. ]
    Reply With Quote  

  4. #4  

    Scythe's Avatar
    Join Date
    Apr 2019
    Thanks given
    Thanks received
    Rep Power
    Helpful, Thanks.
    [Only registered and activated users can see links. ]
    Reply With Quote  

  5. #5  
    Registered Member yenthe's Avatar
    Join Date
    Jul 2011
    Thanks given
    Thanks received
    Rep Power
    Wow, thanks alot for this effort.
    Im very exited to see more of you !
    Reply With Quote  

  6. #6  
    Its Dylan.... Or Is It

    Join Date
    Jul 2016
    Thanks given
    Thanks received
    Rep Power
    Great work mate looking forward to more guides
    [Only registered and activated users can see links. ]
    Reply With Quote  

Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

User Tag List

Similar Threads

  1. Replies: 9
    Last Post: 12-29-2017, 01:15 AM
  2. Replies: 1
    Last Post: 01-06-2015, 07:02 PM
  3. Replies: 9
    Last Post: 01-06-2015, 06:12 PM
  4. Challenge (3 of 3): Reverse engineering
    By Killer 99 in forum Application Development
    Replies: 50
    Last Post: 01-30-2013, 12:01 AM
  5. Deobfuscation/reverse engineering challenge
    By Killer 99 in forum Application Development
    Replies: 10
    Last Post: 08-19-2012, 09:49 AM
Tags for this Thread

View Tag Cloud

Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts