The Power of MHS: Generic Packet Sniffer/Editor

Submit Tutorials Related to Memory Hacking Software

Moderators: g3nuin3, SpeedWing, WhiteHat, mezzo

The Power of MHS: Generic Packet Sniffer/Editor

Postby Shynd » Sat Dec 01, 2007 7:08 am

The Power of MHS: Generic Packet Editor

Exceeder posted asking for adding a "packet scanner & editor" to MHS and his request inspired me to show off the power of L. Spiro Script once again. Why 'add' a packet editor to MHS when you can write your own without much effort?

WS2_32.dll:send(), WS2_32.dll:recv(), and WSOCK32.dll:recv() are the three functions I'm going to concentrate on in this tutorial. In the research I've done--and I truly hope that L. Spiro or someone else can give more information on this--nearly all Winsock-based programs use WS2_32.dll:send() to send information, whereas the newer games use WS2_32.dll:recv() to receive information and the older ones use WSOCK32.dll:recv(). That, of course, is just backed by the games I currently have installed on my computer and is by no means conclusive. However, of the games I checked, none of them used BOTH of the receive functions, so breakpointing both functions didn't hurt any.


WS2_32.dll:send()

The send() function is very straight-forward: the 2nd parameter passed to the function is a pointer to an array of bytes that make up the packet of information being sent and the 3rd parameter passed to the function is the length, in bytes, of the packet. All we have to do is breakpoint near the start of the function, parse the packet from the 2nd parameter, change anything we want to change, and pass control back to the executing thread. This is the easiest of the breakpoints to set and easiest to understand.

The only ASM we have to pay attention to in this function is the part that aligns the stack. Attach MHS to your favorite game (if you haven't already) and open up the Disassembler window. On the Helper portion of the Disassembler window, click the Exports tab, then scroll down until you see WS2_32.dll or WSOCK32.dll (either one should do just fine, though if you see WS2_32.dll, choose that one). Click the + button, then scroll down until you see send. Right-click on send and choose Goto Function. Your diassembler window should change and display something like:
Code: Select all
71AB428A | 8BFF    | MOV     EDI, EDI |
71AB428C | 55      | PUSH    EBP      |
71AB428D | 8BEC    | MOV     EBP, ESP | ;moves stack pointer into EBP
71AB428F | 83EC 10 | SUB     ESP, 10  |


We're going to be breakpointing on the fourth line, SUB ESP, 10. Whether we use a Hardware or Software breakpoint, the stack will be aligned by the time the breakpoint is hit and an aligned stack means we'll have no problem getting the parameters off of it. At 71AB428F, the stack looks like this:
EBP+0x00: Old EBP
EBP+0x04: Return Address of Calling Function
EBP+0x08: Parameter 1 -- socket
EBP+0x0C: Parameter 2 -- packet buffer
EBP+0x10: Parameter 3 -- length
EBP+0x14: Parameter 4 -- flags
etc...

So, to grab the 2nd and 3rd parameters, we have to read EBP+0x08 and EBP+0x0C. First, lets set up the function that will handle this breakpoint, then we'll compile and set the breakpoint.

Code: Select all
//handler for our WSOCK32/WS2_32:send breakpoint
void On_BP_1(LPVOID lpvAddress, LPPROC_INFO_MHS lpProcInfo)
{
   //EBP+0x0C == 2nd parameter, or a pointer to the packet
   extern DWORD ptr = {"", lpProcInfo->pcContext->Ebp + 0x0C};
   //EBP+0x10 == 3rd parameter, or length
   extern int len = {"", lpProcInfo->pcContext->Ebp + 0x10};
   
   //buffer into which we'll read the packet, 2kb should be more than enough, even
   //for the longest packet we encounter (hopefully)
   BYTE packet[2048] = {0};
   //string which we use to display the packet
   //needs to be three times as big as the packet itself
   char packetstr[6144] = {0};
   
   //read len bytes from packetptr into packet
   ReadProcessMemory(GetCurProcessHandle(), (void *)ptr, &packet, len, NULL);
   
   //populate the packet string so we can output it to console
   for (int i = 0; i < len; i++)
      SPrintF(&packetstr[(i * 3)], "%02X ", packet[i]);
   
   //output the string to console
   PrintF("SEND | % 3i | %s", len, packetstr);
}


It's rather well commented, but just so we're clear: if you want to compare the packet to something or change the packet around, you'd do so directly after the ReadProcessMemory call. Make any changes to byte array packet as you want, then WriteProcessMemory(GetCurProcessHandle(), (void *)ptr, packet, len, NULL);.

Save the script as whatever you want. Choose File -> Add to Scripts. Either hit [F5] or click the Compile button in the bottom-right.

Now, to set the breakpoint, go back to the Disassembler window and right-click on the line that says SUB ESP, 10. Choose Breakpoints -> Add Breakpoint Here. Put in a name that reminds you that this is the WS2_32:send() breakpoint (the name doesn't actually matter at all). Make sure both On Execute and Hardware are selected/checked. Set the Callback Function to Script Function by choosing it out of the dropdown box; make sure to set the Parm for Callback Function to 1 (note: the function we wrote is called On_BP_1, which is why the Parm for Callback Function needs to be 1 as well).

Now the script is compiled and the breakpoint is set; you are more than welcome to test this out and see how you're doing. Go do something in the game, i.e. logging in or moving or something, that sends information to the server. You should see the packet show up in your Code Editor window console.



WSOCK32.dll:recv()

This function is pretty easy, too, as it calls WSARecv() to read bytes off of the socket stream. We'll just breakpoint directly after that call.
Code: Select all
71AD2E9E   E8 17000000      CALL <JMP.&WS2_32.WSARecv>
71AD2EA3   83F8 FF          CMP EAX,-1


We breakpoint on line 71AD2EA3. Here's the breakpoint handler:
Code: Select all
//handler for our WSOCK32:recv breakpoint
void On_BP_2(LPVOID lpvAddress, LPPROC_INFO_MHS lpProcInfo)
{
   //make sure WSARecv() didn't fail
   if (lpProcInfo->pcContext->Eax == -1)
      return;
      
   //pointer to the packet
   extern DWORD ptr = {"", lpProcInfo->pcContext->Ebp - 0x04};
   //number of bytes read by WSARecv()
   extern int len = {"", lpProcInfo->pcContext->Ebp + 0x10};
   
   //buffer into which we'll read the packet, 2kb should be more than enough, even
   //for the longest packet
   BYTE packet[2048] = {0};
   //string which we use to display the packet
   //needs to be three times as big as the packet itself
   char packetstr[6144] = {0};
   
   //read the received packet from memory
   ReadProcessMemory(GetCurProcessHandle(), (void *)ptr, &packet, len, NULL);
   
   //populate the packet string so we can output it to console
   for (int i = 0; i < len; i++)
      SPrintF(&packetstr[(i * 3)], "%02X ", packet[i]);
   
   //output the string to console
   PrintF("RECV | % 3i | %s", len, packetstr);
}


Set the breakpoint exactly like before, on the CMP EAX, -1 instruction. Be sure to name it something so that you know it's the WSOCK32:recv breakpoint; also make sure the Parm is set to 2.



WS2_32.dll:recv()

This function is slightly more complex. The buffer is not populated with the bytes of the packet of information until right before the function returns, so we can't just breakpoint at the start of the function. If you wanted to do this all yourself, you'd catch the pointer that is passed as the 2nd parameter (buffer into which the bytes will be received), load that up in the Hex Editor, then step through the function until those bytes have been changed. We'll breakpoint on one of the instructions after the bytes have been changed, seeing as I already stepped through the function and found where that is.

Code: Select all
71AB61CD   E8 54C9FFFF      CALL WS2_32.71AB2B26
71AB61D2   3BFB             CMP EDI,EBX
71AB61D4   5F               POP EDI
71AB61D5   75 2E            JNZ SHORT WS2_32.71AB6205
71AB61D7   F645 15 80       TEST BYTE PTR SS:[EBP+15],80
71AB61DB   75 3F            JNZ SHORT WS2_32.71AB621C
71AB61DD   8B45 08          MOV EAX,DWORD PTR SS:[EBP+8]
71AB61E0   5E               POP ESI
71AB61E1   5B               POP EBX
71AB61E2   C9               LEAVE
71AB61E3   C2 1000          RETN 10


At 71AB61CD, the bytes are actually read from the socket stream into memory. At 71AB61DD, the number of bytes that were read from the socket stream is moved into EAX, so this is where we breakpoint.

Now, the breakpoint handler:
Code: Select all
//handler for our WS2_32:recv() breakpoint
void On_BP_3(LPVOID lpvAddress, LPPROC_INFO_MHS lpProcInfo)
{
   //set external variable 'len' as the number of bytes read
   extern DWORD len = {"", lpProcInfo->pcContext->Ebp + 0x08};
   //gather the pointer to the packet of information
   extern DWORD ptr = {"", lpProcInfo->pcContext->Ebp + 0x0C};
   
   //if -1 bytes were returned, or len equals zero, don't go any further
   if ((int)lpProcInfo->pcContext->Eax == -1 || len == 0)
      return;
   
   //byte array into which we'll read the packet
   BYTE packet[2048] = {0};
   //string we use to display the packet
   char packetstr[6144] = {0};
   
   //read the packet from memory
   ReadProcessMemory(GetCurProcessHandle(), (void *)ptr, &packet, len, NULL);
   
   //populate the packet string so we can output it to console
   for (int i = 0; i < len; i++)
      SPrintF(&packetstr[(i * 3)], "%02X ", packet[i]);
   
   //output the string to console
   PrintF("RECV | % 3i | %s", len, packetstr);
}


Set the breakpoint just like the other two times on the MOV EAX, [EBP+8] line. Make sure, again, to name it something so you know it's the WS2_32:recv() breakpoint; again, make sure the Parm is 3.



So, to recap: we have three breakpoints and three breakpoint handling script functions. Highlight all of the breakpoints in the breakpoint tab by shift+clicking them all, then right-click one and save them as wsock_breakpoints.bps or something. Remove them all, compile the script, load the breakpoints back, and test to see if it works. You should get a very nice readout of all the packets sent to and from the current process.


I'm sure I've left a few things out as this is the second time I'm writing this--my computer crashed the last time I got to this point and I lost it all. If you see any discrepancies or have any suggestions or questions, please post.



EDIT: For those of you who like automation and don't want to have to go through these steps, here's an addition to the script that (should) automatically set(s) the breakpoints:
Code: Select all
void On_OpenProcess(HANDLE hProcess, DWORD dwProcessId)
{
   //wsock32_send = GetRemoteFuncAddress("WSOCK32.dll", "send");
   wsock32_recv = GetRemoteFuncAddress("WSOCK32.dll", "recv");
   ws2_32_send = GetRemoteFuncAddress("WS2_32.dll", "send");
   ws2_32_recv = GetRemoteFuncAddress("WS2_32.dll", "recv");
   
   //MessageBox(MBS_OK, "SLDKFJLS", "WSOCK32.dll:recv(): 0x%08X\nWS2_32.dll:send(): 0x%08X\nWS2_32.dll:recv(): 0x%08X", wsock32_recv, ws2_32_send, ws2_32_recv);
   
   if (MessageBox(MBS_YES|MBS_NO, "Enable Packet Editing/Sniffing?", "Would you like to enable the packet editing/sniffing breakpoints for %s?", GetCurProcessName()) == MBS_NO)
      return;
   
   AttachDebugger();
   
   SCRIPT_ADD_BP ws2_send_bp = {0};
   ws2_send_bp.aAddress = ws2_32_send + 0x05;
   ws2_send_bp.iType = SPBT_EXECUTE;
   ws2_send_bp.bHardware = true;
   ws2_send_bp.iCallback = SYS_FUNCS_SCRIPT_FUNC;
   ws2_send_bp.dwNewParms[1] = 1;
   ws2_send_bp.bSet = true;
   AddBreakpoint(&ws2_send_bp, NULL);
   
   SCRIPT_ADD_BP wsock32_recv_bp = {0};
   wsock32_recv_bp.aAddress = wsock32_recv + 0x33;
   wsock32_recv_bp.iType = SPBT_EXECUTE;
   wsock32_recv_bp.bHardware = true;
   wsock32_recv_bp.iCallback = SYS_FUNCS_SCRIPT_FUNC;
   wsock32_recv_bp.dwNewParms[1] = 2;
   wsock32_recv_bp.bSet = true;
   AddBreakpoint(&wsock32_recv_bp, NULL);
   
   SCRIPT_ADD_BP ws2_recv_bp = {0};
   ws2_recv_bp.aAddress = ws2_32_send + 0x83;
   ws2_recv_bp.iType = SPBT_EXECUTE;
   ws2_recv_bp.bHardware = true;
   ws2_recv_bp.iCallback = SYS_FUNCS_SCRIPT_FUNC;
   ws2_recv_bp.dwNewParms[1] = 3;
   ws2_recv_bp.bSet = true;
   AddBreakpoint(&ws2_recv_bp, NULL);
}


-Shynd
Last edited by Shynd on Sat Dec 01, 2007 11:32 pm, edited 4 times in total.
User avatar
Shynd
Acker
 
Posts: 68
Joined: Fri Jan 05, 2007 2:11 am

Postby Exceeder » Sat Dec 01, 2007 8:16 am

sweet
User avatar
Exceeder
I Know My Poop
 
Posts: 414
Joined: Wed Nov 28, 2007 8:48 am

Postby Shynd » Sat Dec 01, 2007 8:34 am

Finished tutorial and edited first post.
User avatar
Shynd
Acker
 
Posts: 68
Joined: Fri Jan 05, 2007 2:11 am

Postby L. Spiro » Sat Dec 01, 2007 9:05 am

Another good one.


Your question regarding how to set breakpoints in the script has been answered so you can add automatic breakpointing to the tutorial if you want.
I need to add functions for saving and loading breakpoint files.

Also, why did your computer crash?
Something related to MHS?


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

Postby Shynd » Sat Dec 01, 2007 10:01 am

Yeah, sorta. I forget exactly what happened but it's happened a few times before. The game I was debugging sometimes throws exceptions when it disconnects from the server and I think I was single-stepping through the code, which made it disconnect, then I hit F9 and my OS basically stopped responding. I could've killed MHS and the game executable, but it would've taken like 15 minutes for Task Manager to pop up and another 10 for it to allow me to select the game/MHS and Kill Process.

On the subject, I've run into another bug that I'm not sure about. Don't know if MHS is the culprit or not. I'll make a post about it in the bugs forum so you can investigate, even though it's not a big deal at all.


ANYWAY, there's an addition to the script at the very bottom of the first post (I edited it) for those of you who want automation.
User avatar
Shynd
Acker
 
Posts: 68
Joined: Fri Jan 05, 2007 2:11 am

Postby L. Spiro » Sat Dec 01, 2007 10:05 am

If the situation is that the operating system becomes very slow, the mouse moves but only once every 20 seconds or so, and every operation takes ages (but eventually does happen), that seems to be a problem with the operating system itself.

I get the same behavior sometimes while debugging in Visual Studio.


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

Postby mezzo » Sat Dec 01, 2007 10:21 am

and yet another very nice one !! You really know your stuff :-)

(a quick way to stop processes that are killing your PC, is to use the 'pskill' tool from sysinternals. I always have a batchfile on my pc with a long list of pskill's with all the processnames that could misbehave. windowskey+r kill.bat and it kills every process that I have 'pre-listed'. The tool is ruthless and has saved me many times from having to reboot. It works on remote PC's too if you are the administrator.. But perhaps you knew of this little gem already)

anyway, that was completely beside the point.

Good tut! keep 'em coming :D
- No thanks, I already have a penguin -
User avatar
mezzo
El Mariachi
 
Posts: 739
Joined: Mon Apr 30, 2007 10:27 pm
Location: Antwerp

Postby Shynd » Sat Dec 01, 2007 10:35 am

L. Spiro wrote:If the situation is that the operating system becomes very slow, the mouse moves but only once every 20 seconds or so, and every operation takes ages (but eventually does happen), that seems to be a problem with the operating system itself.

I get the same behavior sometimes while debugging in Visual Studio.


L. Spiro

The mouse moves just like normal, it's just that the desktop and all windows seem to take forever to do anything. It's not that the CPU overloads, either; Task Manager reads normal. When I mouseover something in the system tray, the tooltip pops up, but if I right-click on it, nothing happens. The alt+tab window works fine, but if I alt-tab to something or click on a window or select one from the taskbar, it shows as if it was not responding. It's really strange and has happened only twice now, but that one time came at a very, very inopportune moment. Since then, I've made posts in notepad and saved them at the end of each sentence, copying and pasting to the browser to check them with the preview button =p
User avatar
Shynd
Acker
 
Posts: 68
Joined: Fri Jan 05, 2007 2:11 am

Postby WhiteHat » Sat Dec 01, 2007 4:58 pm

Excellent ! Thank you very much !.... :)
User avatar
WhiteHat
Elang Djawa
 
Posts: 1059
Joined: Fri Jul 21, 2006 12:49 pm
Location: Away for a while...

Postby mezzo » Tue Dec 04, 2007 12:16 am

finally got around to look at it in a bit more detail..
Can you tell me what you base the following on:

ws2_send_bp.aAddress = ws2_32_send + 0x05;


I actually don't quite understand HOW you got those values.
I get that you don't BP on the call itself, but lower, when the stack is realligned and when the data is in a more appropriate location to be grabbed, but when I check the asm with your code, I never get the same values as you.. I am correct to think that with ws2_32_send + 0x05 you mean the instruction that is 5 bytes deep in the call to the send function, right ?
- No thanks, I already have a penguin -
User avatar
mezzo
El Mariachi
 
Posts: 739
Joined: Mon Apr 30, 2007 10:27 pm
Location: Antwerp

Postby L. Spiro » Tue Dec 04, 2007 1:43 am

Code: Select all
71AB428A | 8BFF | MOV     EDI, EDI |
71AB428C | 55   | PUSH    EBP      |
71AB428D | 8BEC | MOV     EBP, ESP |



8BFF + 55 + 8BEC = 5 total bytes used for these commands.

Skip these commands by adding 5 bytes to the start of the function.


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

Postby mezzo » Tue Dec 04, 2007 1:56 am

okay, I got that one, but what about the +33 on the recv example.. that's the point where I completely lost it :D
- No thanks, I already have a penguin -
User avatar
mezzo
El Mariachi
 
Posts: 739
Joined: Mon Apr 30, 2007 10:27 pm
Location: Antwerp

Postby Shynd » Tue Dec 04, 2007 2:57 am

Look at the ASM at where I say to breakpoint for the recv functions. It's way, way down in the functions, after the bytes have been read off of the socket stream. You're going to have to look at the recv() ASM in full in order to get it:
Code: Select all
71AD2E70 > 8BFF             MOV EDI,EDI
71AD2E72   55               PUSH EBP
71AD2E73   8BEC             MOV EBP,ESP
71AD2E75   51               PUSH ECX
71AD2E76   51               PUSH ECX
71AD2E77   8B45 10          MOV EAX,DWORD PTR SS:[EBP+10]
71AD2E7A   8945 F8          MOV DWORD PTR SS:[EBP-8],EAX
71AD2E7D   8B45 0C          MOV EAX,DWORD PTR SS:[EBP+C]
71AD2E80   8945 FC          MOV DWORD PTR SS:[EBP-4],EAX
71AD2E83   8B45 14          MOV EAX,DWORD PTR SS:[EBP+14]
71AD2E86   6A 00            PUSH 0
71AD2E88   6A 00            PUSH 0
71AD2E8A   8945 0C          MOV DWORD PTR SS:[EBP+C],EAX
71AD2E8D   8D45 0C          LEA EAX,DWORD PTR SS:[EBP+C]
71AD2E90   50               PUSH EAX
71AD2E91   8D45 10          LEA EAX,DWORD PTR SS:[EBP+10]
71AD2E94   50               PUSH EAX
71AD2E95   6A 01            PUSH 1
71AD2E97   8D45 F8          LEA EAX,DWORD PTR SS:[EBP-8]
71AD2E9A   50               PUSH EAX
71AD2E9B   FF75 08          PUSH DWORD PTR SS:[EBP+8]
71AD2E9E   E8 17000000      CALL <JMP.&WS2_32.WSARecv>
71AD2EA3   83F8 FF          CMP EAX,-1
71AD2EA6   74 28            JE SHORT WSOCK32.71AD2ED0
71AD2EA8   F645 0D 80       TEST BYTE PTR SS:[EBP+D],80
71AD2EAC   75 69            JNZ SHORT WSOCK32.71AD2F17
71AD2EAE   8B45 10          MOV EAX,DWORD PTR SS:[EBP+10]
71AD2EB1   C9               LEAVE
71AD2EB2   C2 1000          RETN 10

The line I say to breakpoint is 71AD2EA3 83F8 FF CMP EAX,-1. 71AD2EA3 - 71AD2E70 = what? 0x33.

You can find the ASM of any of these functions by loading up a game that imports WSOCK32.dll and WS2_32.dll and looking at the Exports tab in the disassembler.
User avatar
Shynd
Acker
 
Posts: 68
Joined: Fri Jan 05, 2007 2:11 am

Postby mezzo » Tue Dec 04, 2007 3:07 am

OOOOh okay :D
At 71AB61DD, the number of bytes that were read from the socket stream is moved into EAX, so this is where we breakpoint.

had it confused with this line..

thnx for clearing that up.
- No thanks, I already have a penguin -
User avatar
mezzo
El Mariachi
 
Posts: 739
Joined: Mon Apr 30, 2007 10:27 pm
Location: Antwerp

Postby Shynd » Tue Dec 04, 2007 3:32 am

That's from recv() in WS2_32.dll. WSOCK32 == Winsock 1; WS2_32 == Winsock 2.
User avatar
Shynd
Acker
 
Posts: 68
Joined: Fri Jan 05, 2007 2:11 am

Next

Return to Tutorials

Who is online

Users browsing this forum: No registered users and 0 guests

cron