InfernoRose Packet - Using Code Injection and Multithreading

Share Your Own Code Samples With the World

Moderators: g3nuin3, SpeedWing, WhiteHat, mezzo

InfernoRose Packet - Using Code Injection and Multithreading

Postby Shynd » Tue Nov 20, 2007 6:22 am

The next three paragraphs are simply prologue. You can skip them if you want.

A friend of mine, who plays InfernoRose (I'm pretty sure it's a private server for ROSE Online, though I don't know because I don't play), came to me with questions on how to write a packet injector in plain AutoIt, just to say it could be done. No injected DLLs, no end-around hacky crap, just AutoIt. A proof-of-concept, of sorts, that he may or may not develop into a full-fledged hack later on.

Now, if you've ever used AutoIt, you know that it's pretty powerful as a Windows automation tool but it has its down-sides. No threading, for example. Still, the problem intrigued me, so we went at it together.

After a little debugging in OllyDbg (and some MHS :)), I'd come up with a decent idea for a codecave that would set a specific address to 0 when a new packet arrives and loop on a Sleep(1) call until that specific address was 1 again. This would allow enough time to modify the packet in memory using a regular memory write in AutoIt and a simple way to resume the thread. After it was all debugged we tried it out and the performance hit was barely noticable unless one sent a long packet, say for a lot of chat or some such. I began to wonder how I could rewrite it in L. Spiro Script (hereafter referred to as LSS) and if that'd do away with the performance hit.


NOTE: This is not useful as-is. It is a proof-of-concept. You have to edit it if you want it to be useful (and I'm told it can be very powerful if done right).

EDIT: I have posted another way of doing this same, exact thing here using breakpoint handling script functions. It's much cleaner.


So, the idea is to hijack the outgoing chat packet when your character says '123' and change the packet so that you REALLY say '321.' Yes, it's useless, but that's not the point. The point is it was incredibly easy to do in Memory Hacking Software's LSS. Here I will attempt to teach some very simple uses for LSS while giving a real-world example of usage.

Code: Select all
0069F458   8BC1             MOV EAX,ECX                  ;\
0069F45A   56               PUSH ESI                   ;> restored operations we overwrote
0069F45B   8B7424 08        MOV ESI,DWORD PTR SS:[ESP+8]   ;/
0069F45F   60               PUSHAD                     ;> store all registers
0069F460   9C               PUSHFD                     ;> and flags so we can restore them
0069F461   8935 58F56900    MOV DWORD PTR DS:[69F558],ESI   ;> store ESI (pointer to packet) in a static location
0069F467   C605 54F56900 00 MOV BYTE PTR DS:[69F554],0      ;> initialize extern resume as 0 (will loop until it's 1)
0069F46E   0FB60D 54F56900  MOVZX ECX,BYTE PTR DS:[69F554]  ;> move value at 0x69F554 into ecx
0069F475   85C9             TEST ECX,ECX               ;> test ecx to see if it's zero or non-zero
0069F477   75 0A            JNZ SHORT TRose.0069F483      ;> if ecx is non-zero, skip over loop
0069F479   6A 01            PUSH 1                     ;> the number of milliseconds to sleep
0069F47B   FF15 34E16200    CALL DWORD PTR DS:[<&KERNEL32.Sleep>]    ; kernel32.Sleep
0069F481  ^EB EB            JMP SHORT TRose.0069F46E      ;> jump back to the top of the loop
0069F483   9D               POPFD                     ;> restore flags
0069F484   61               POPAD                     ;> and registers so we don't get stack overflow exceptions
0069F485  -E9 CD38D6FF      JMP TRose.00402D57            ;> jump back to game routine
That is the code that I injected. If you read the comments, you see that it sets a variable to 0 and then loops until that variable is not zero, allowing us time enough to change the packet. At the line starting with 0069F461, you see that ESI is written to address 0x0069F558. ESI is a pointer to the packet in memory. This allows us to read the pointer and change the packet while the game is looping.

Now then, the script:
Code: Select all
#define CODE_CAVE 0x0069F458
#define JMP_FROM 0x00402D50

//byte array to hold the overwritten functions
//we'll read it from memory at the start and then restore the opcodes on closing the process
BYTE overwritten[7];

//ASM opcodes for a jump to our codecave
BYTE jmp[7] = {0xE9, 0x03, 0xC7, 0x29, 0x00, 0x90, 0x90};

/*
0069F458   8BC1             MOV EAX,ECX                  ;\
0069F45A   56               PUSH ESI                   ;> restored operations we overwrote
0069F45B   8B7424 08        MOV ESI,DWORD PTR SS:[ESP+8]   ;/
0069F45F   60               PUSHAD                     ;> store all registers
0069F460   9C               PUSHFD                     ;> and flags so we can restore them
0069F461   8935 58F56900    MOV DWORD PTR DS:[69F558],ESI   ;> store ESI (pointer to packet) in a static location
0069F467   C605 54F56900 00 MOV BYTE PTR DS:[69F554],0      ;> initialize extern resume as 0 (will loop until it's 1)
0069F46E   0FB60D 54F56900  MOVZX ECX,BYTE PTR DS:[69F554]  ;> move value at 0x69F554 into ecx
0069F475   85C9             TEST ECX,ECX               ;> test ecx to see if it's zero or non-zero
0069F477   75 0A            JNZ SHORT TRose.0069F483      ;> if ecx is non-zero, skip over loop
0069F479   6A 01            PUSH 1                     ;> the number of milliseconds to sleep
0069F47B   FF15 34E16200    CALL DWORD PTR DS:[<&KERNEL32.Sleep>]    ; kernel32.Sleep
0069F481  ^EB EB            JMP SHORT TRose.0069F46E      ;> jump back to the top of the loop
0069F483   9D               POPFD                     ;> restore flags
0069F484   61               POPAD                     ;> and registers so we don't get stack overflow exceptions
0069F485  -E9 CD38D6FF      JMP TRose.00402D57            ;> jump back to game routine
*/
//an array of bytes that we use to 'inject' the above ASM
//if we write this array of bytes to memory as our code cave, it will represent
//the above ASM operations (we have to do it this way until L. Spiro finishes
//the auto assembler ;) )
BYTE codecave[50] = {0x8B, 0xC1, 0x56, 0x8B, 0x74, 0x24, 0x08, 0x60, 0x9C,
                  0x89, 0x35, 0x58, 0xF5, 0x69, 0x00, 0xC6, 0x05, 0x54,
                  0xF5, 0x69, 0x00, 0x00, 0x0F, 0xB6, 0x0D, 0x54, 0xF5,
                  0x69, 0x00, 0x85, 0xC9, 0x75, 0x0A, 0x6A, 0x01, 0xFF,
                  0x15, 0x34, 0xE1, 0x62, 0x00, 0xEB, 0xEB, 0x9D, 0x61,
                  0xE9, 0xCD, 0x38, 0xD6, 0xFF};

//boolean variable used to terminate the thread we create
bool troseopen = false;

//called when TRose.exe is opened in MHS
void On_Open_TROSE_EXE(HANDLE hProcess, DWORD dwProcessId)
{
   //give the user a choice between starting the packet injector or not
   if (MessageBox(MBS_YES | MBS_NO, "Start Packet Injector?", "Do you want to start the packet injector on TRose.exe?\n\nhProcess: 0x%08X | dwProcessId: 0x%08X", (DWORD)hProcess, dwProcessId) == MBS_NO)
      return;

   //read the original game operations into byte array 'overwritten'
   //if only there was a way to restore the overwritten operations
   //before closing the handle to TRose.exe... sadly, at the moment, there
   //is not, as the process handle is closed before On_Close is fired
   //this ReadProcessMemory is fundamentally useless, but good to keep
   //in case the functionality of On_Close is ever changed
   ReadProcessMemory(hProcess, (void *)JMP_FROM, &overwritten, sizeof(overwritten), NULL);
   
   //write the code cave bytes to memory
   WriteProcessMemory(hProcess, (void *)CODE_CAVE, codecave, sizeof(codecave), NULL);
   //overwrite the packet send function with a jump to our code cave
   WriteProcessMemory(hProcess, (void *)JMP_FROM, jmp, sizeof(jmp), NULL);
   
   //set boolean variable 'troseopen' to true because we've now opened the process
   troseopen = true;
   
   //create the thread that will poll for packets
   CreateThread("PreSendPacket", 0);
}

//called when TRose.exe is no longer being hacked by MHS
void On_Close_TROSE_EXE()
{
   //set this to false so our PreSendPacket thread can stop looping upon itself
   troseopen = false;
}

DWORD PreSendPacket(DWORD dwParm)
{
   //pointer to the packet in memory
   extern DWORD ptr = {"", 0x0069F558};
   
   //byte in memory that determines whether game routine is blocked or not
   //if resume is locked at 1, the game will run like normal
   //if resume is 0, game will block operation until resume is set to 1
   extern BYTE resume = {"", 0x0069F554};
   
   //this is the packet for sending chat '123'
   BYTE comparepacket[10] = {0x0A, 0x00, 0x83, 0x07, 0x78, 0x01, 0x31, 0x32, 0x33, 0x00};
   //this is the packet for sending chat '321'
   BYTE injectpacket[10] = {0x0A, 0x00, 0x83, 0x07, 0x78, 0x01, 0x33, 0x32, 0x31, 0x00};
   //our packet buffer
   BYTE newpacket[255] = {0};
   //length of the packet being sent
   extern BYTE len = {"", ptr};
   //determines whether we change the outgoing packet or not
   bool shouldinject = false;
   //string to display the outgoing packet
   char packet[1024] = {0};
   
   //while boolean variable 'troseopen' is true, loop constantly
   while (troseopen)
   {
      //if resume is equal to 0 (new packet available)
      if (resume == 0)
      {
         //initialize shouldinject to true (we'll set it to false at the first sign
         //of the compared packets differing)
         shouldinject = true;
         
         //read the new packet into byte array 'newpacket'
         //if ReadProcessMemory fails, skip the rest (make sure to set resume to 1 first)
         if (!ReadProcessMemory(GetCurProcessHandle(), (void *)ptr, &newpacket, len, NULL))
         {
            resume = 1;
            continue;
         }
         
         //loop over the new packet
         for (int i = 0; i < len; i++)
         {
            //populate the string with the bytes of the packet
            SPrintF(&packet[i*3], "%02X ", newpacket[i]);
            
            //if any single byte differs between the two packets
            //set boolean variable 'shouldinject' to false
            if (comparepacket[i] != newpacket[i]) shouldinject = false;
         }
         
         //print out the packet string
         PrintF("Packet: %s", packet);
         
         //if the two packets were the same
         if (shouldinject)
            //write the changed packet over the current packet
            WriteProcessMemory(GetCurProcessHandle(), (void *)ptr, injectpacket, sizeof(injectpacket), NULL);
         
         //resume game execution
         resume = 1;
      }
      
      //sleep so we don't chew up CPU resources
      Sleep(1);
   }
}
Click here to see it with syntax highlighting on. MUCH easier to read. Thanks to NoMorePasting.com.

It's relatively well commented, but I'll go over the key points in slightly more depth anyway.

First, the injection. Until L. Spiro finishes the Auto-Assembler portion of MHS (though I have no idea if we'll get an Auto-Assembler available in the script or not), code injection has to be done strictly by writing byte arrays to memory. The easiest way to figure out what bytes to write is to open up OllyDbg (or your favorite disassembler), assemble the codecave the way you want it, and then write down the sequence of bytes that you see in the second column. This is currently quite difficult to do with only MHS but, I assume, will be made much easier whenever LS gets around to adding an Auto-Assembler much like Cheat Engine and TSearch have. As a side note, I beg you to be patient with him as writing an ASM parser isn't any small task and it's probably on the backburner as other things are far more important at the moment (such as the ability to handle breakpoints with script functions, etc, which almost makes an Auto-Assembler useless).

Anyway, back to the script. Since the name of the EXE that I wrote this hack for is TRose.exe, my first function is On_Open_TROSE_EXE. This function is called whenever I open TRose.exe for hacking with MHS. This function, as outlined in the documentation, is passed two parameters: HANDLE hProcess and DWORD dwProcessId. We can use these two parameters to a) find out just about any information about the process that was opened or b) read or write information from and to the process. As you can see, this function is where the code is actually injected.

Lastly, however, this function creates a thread on another script function: PreSendPacket(). This gets our packet-interception-and-injection loop up and running.

Most script functions that you write will be triggered via hotkeys. Sometimes, however, you want a script function to monitor the process from the time it's open until the time it's closed without having to hit any keys or tell it when to start. That is where threading comes in. You can start a separate thread on a script function and then have that function loop back upon itself indefinitely (or, at least, until the process is closed), thus allowing MHS to constantly monitor the process for changes and take the appropriate action without any user input. The things bots are made of.

Now, remember how I said a pointer to the packet was going to be placed at address 0x0069F558? Well, thanks to LS's wondrous external variables, with the simple line extern DWORD ptr = {"", 0x0069F558}, we give ourselves a constantly-updating variable that will always hold a pointer to the packet. No need to loop over a ReadProcessMemory call or anything of that nature. This is probably my favorite part of LSS.

We also set up an external variable for 'resume.' When set to 1, this allows the code that I injected to resume sending the packet (hopefully after we've had time to access it and, if necessary, change it). A simple 'resume = 1;' makes this about as easy as ever.

Skip down a few lines to where you see extern BYTE len = {"", ptr};. Notice anything? That's right, I'm using an external variable inside an external variable. Since they're constantly updating, the variable 'len' will always hold the value of the first byte pointed to by 'ptr' or, in other words, len is now always the first byte of the packet. Since the length of the packet to be sent is held in the first byte, I'd say that's rather convenient. If you look at the NoMorePasting.com link of the script, you can see, a few lines down, where I've commented out a ReadProcessMemory call that does exactly the same thing as this external variable. I can't tell you how happy that makes me.

Now then, we initialize shouldinject to true because later, when we're looping through the packet to see if it's the chat packet for '123,' we'll set shouldinject to false at the first sign that it's not the packet we're looking for. Next, we read the packet from the address held in external variable 'ptr' into the byte array 'newpacket.' Finally, it loops through the packet, comparing it to 'comparepacket' and setting 'shouldinject' to false if any differences are found.

Last but not least, if shouldinject is still true, the altered packet is written to memory over the original packet, thus finishing our packet injection. After that, we simply resume game execution with a resume = 1; (which, by the way, saves us a WriteProcessMemory call... I just can't stop pointing out how useful external variables are, I apologize =p).

Voila. Now anytime I try to say '123' in game, it comes out as '321.' I suppose if I had ever played ROSE Online before, I might think of a few other things to do, but, seeing as I don't play, the only use I got out of all of this was learning how powerful and easy LSS is. I hope to add another tutorial some other time but, for now, I hope that you learned a little something from this. Please, by all means, if you have any questions or comments or suggestions, I'd be very happy to hear them.




Let me finish by saying two things:
1) In order to bypass the hack-detection for InfernoRose, you'll have to download ProcessExplorer and use it to suspend or kill all threads in either xGuard.dll or TribalGRD.dll before you can write to the game's memory. If you're having problems figuring out how, either google for the answer or ask and, if I'm feeling charitable enough I'll attempt to answer it here. I don't suppose that many people will be too interested, however, so I'll leave that tutorial out for now.
2) This is in no way a hack that will work on the actual ROSE Online official client. Not only does ROnline have GameGuard protecting it--which I don't know how to nor care to try to bypass--but the address to which you'd inject this code is probably different. Please do not ask for this hack on the official ROSE Online servers as I don't know how to do it and I am really disinclined to try, due to GameGuard being a complete piece of crap.


-Shynd
Last edited by Shynd on Tue Nov 20, 2007 10:49 pm, edited 3 times in total.
User avatar
Shynd
Acker
 
Posts: 68
Joined: Fri Jan 05, 2007 2:11 am

Postby Shynd » Tue Nov 20, 2007 6:23 am

Image
Last edited by Shynd on Tue Nov 20, 2007 10:48 pm, edited 1 time in total.
User avatar
Shynd
Acker
 
Posts: 68
Joined: Fri Jan 05, 2007 2:11 am

Postby L. Spiro » Tue Nov 20, 2007 10:57 am

This is the best tutorial so far.


A note regarding ASM, the Auto-Assembler, and breakpoints handled by script functions:

There is already an Injection Suite in MHS—view the help-file section on the Injection Manager. This compiles ASM code and gives you a string of bytes you can copy at the bottom. It adds the overwritten code there too. This is different from Auto-Assemble in that this could be considered a condensed and effortless way to make standard injections, while Auto-Assemble will require more work on the user’s part (to declare labels and various things) but will have a few extra features allowing the user to use labels and to allocate memory, etc., just as in Cheat Engine and TSearch (I don’t know about TSearch).

Auto-Assemble will be available in the next release (it is already done) along with options in the Disassembler for copying code in Auto-Assemble format and some extras to allow you to preview before injecting and to show you the bytes so you can copy/paste them. This would indeed help people who are injecting via scripts. I also plan to expose the Disassemble() function, Assemble() function, and AutoAssemble() functions to the scripts in the next release.

Finally, you mentioned that Auto-Assemble might take longer to finish because of other features having higher precedence such as breakpoints handlable by scripts. From this I assume you have not yet discovered this feature already existing in the software.
The help file in MHS 4.0.0.6 and above is different, but view this online page for the same idea: Disassembler - Debugger - Breakpoints.
The page in the actual help file is easier to read and explains a new feature in MHS 4.0.0.6.
You can handle breakpoints with scripts via a script function called On_BP_*() where * is a number that matches the Parm value in the breakpoint itself.
You can also use On_BP_TROSE_EXE_*() to associate a number with a process (allowing you to reuse the number). This works exactly the same way as with Hotkey handlers and is fully documented.

Furthermore, you can write DLL functions to handle breakpoint hits, but this is usually not needed since scripts do enough already.


L. Spiro
User avatar
L. Spiro
L. Spiro
 
Posts: 3129
Joined: Mon Jul 17, 2006 10:14 pm
Location: Tokyo, Japan

Postby Shynd » Tue Nov 20, 2007 11:50 am

Holy shit, that makes code injection like 700% easier. I think, for my next tutorial, I shall rewrite this same exact script but in breakpoint handling mode. We shall see. Perhaps something a little more available to all. We shall see.
User avatar
Shynd
Acker
 
Posts: 68
Joined: Fri Jan 05, 2007 2:11 am

nice tut

Postby gotpwnt » Fri Mar 28, 2008 11:40 pm

nice tutorial bro. i just had a time playing with MMH . it is a real nice memory hacker. thanx to L. Spiro for developing this tool. i will help this community after this time. thanx
gotpwnt
I Have A Question
 
Posts: 1
Joined: Fri Mar 28, 2008 11:28 pm

Postby sebxter » Wed Jun 18, 2008 12:52 am

im to stupid-.-
Image
Image
Image
User avatar
sebxter
NULL
 
Posts: 189
Joined: Thu May 15, 2008 4:01 pm


Return to Code Submissions

Who is online

Users browsing this forum: No registered users and 0 guests

cron