Part2 – Networking functions
In this Part2 of the tutorial series we will try to find suitable points to extract the network packets while they are being send or received by the client. I think it is a good step into reversing an unknown program to check the I/O APIs used by it.
The chapters
- Part1 – Preliminaries and anti-hacking
- Part2 – Networking functions
- Part3 – Detecting events (monster/player/user move, attack …)
- Part4 – Initiating events (attack, move)
- Part5 – Inventory / Vendor / …
- Part6 – Pathfinding
In this Chapter
- Locate packet reading functions
- Understanding the packet structure
- Locate packet sending function
Requirements
- Silkroad Online
- A decent debugger: OllyDbg 2.X
- A codecaving and injection framework: mmBBQ 3.X
- An editor of your choice (mine is Vim ;) )
Packet reading:
Normally the functions that will be called if you want to read or send network packets is WSARecv(..) for receiving new packets and WSASend(...). Of course this applies to MS Windows APIs. So we are going to set breakpoints there. To do so we load our modified executable (described in Part1) and run it. Then we log into the game to make sure all dynamic modules are loaded before we proceed. Now we can search for all inter-modul calls: (right click -> Search for -> “All intermodular calls”). This is a huge list, but you can now just type the string youre searching for, in our case “WSAR” should be enough to find the right call:

Now set a normal software breakpoint (INT3) at the desired position (F2) or double click in the hex presentation of the call to “WSARecv”.
The next steps are the secret when reverse engineering. Just ask yourself the following questions:
- What are we searching?
- How does the searched position could look like?
- What should happen before and after this secret location?
- What do we want to do at this location?
- Especially what event could bring us to this position?
Actually we are searching for the packet read function. It should get as parameter the complete packet (maybe separate the packet id) We would like to hook this position to get more information what packet could define which event. The entry point in our search for this function is WSARecv(...) (or any other network receive function from your operating system API).
With this vague description of our searched location we start at the breakpoint of the call to WSARecv(...). So the next packet read will interrupt the program in OllyDbg. Before we set the breakpoint at this position we should think about a good event or moment to receive a usable breakpoint hit (and that way a cool packet LPWSABUF). For this matter I have chosen the moment when you selecte a character. Just before you click start, set the breakpoint. As the breakpoint hits, we see the following stack:

A small look into the Msdn will bring us knowledge about the parameters seen here. The most interesting attribute is
defined here: Msdn (LPWSABUF)
So when we follow the pointer (by left clicking on address -> Right click -> “Follow DWORD in Dump”) to this data structure we will see this:

The second member of this struct contains a pointer to the buffer that will be filled with the received data.
One Step over (F8) later we can see how the packet was filled by WSARecv:

At this point we have several options to go on:
- We set a Memory Read Breakpoint within the received data to see what code reads this information. To set a Memory Breakpoint, mark the memory you want to BP and press Shift+F3 (or Right click -> Breakpoint -> Memory). Set Read access and click ok. Note: Its not wise to set the breakpoint directly in the first bytes of the buffer, because we can assume that the start of a packet will contain mostly the id and the length of it. Good, now you are able to run (F9) again so the process will continue until somebody reads the breakpointed addresses. This will lead us to:
The REP MOVS instruction copies ECX DWORD (32Bit) from ESI to EDI. Command help can always be opend by: Shift-F1. Now step over this rep movs with (F8) and select EDI (right click -> Follow in Dump). We set a memory read breakpoint in this area and run run. Now we reach again the WSARecv call (Step over and eax tells us there was nothing to receive 0xFFFFFFFF == -1). Another run bring us to the next rep mov. Stepping to the end of this function

shows us that the new filled buffer is the return value here (contained in EAX) Another few steps in the calling function ends

As return value we get the received packet:

/* 0x00 */ void** functions;
/* 0x04 */ uint32_t bytes_read;
/* 0x08 */ uint32_t len;
/* 0x0C */ uint32_t unknown1;
/* 0x10 */ unsigned char* buffer;
/* 0x14 */ unsigned char* buffer2;
/* 0x18 */ uint16_t packet_id;
/* 0x1A */ uint16_t unknown2;
}sro_packet_t;
At this point a little bit of reversers intuition is needed, to show the way I set memory breakpoints to anything except the void** functions part. Now play and we reach this function:

If we step a bit forward there is a call to 0xCD9540. The functions first argument is the address of the packet_t struct. We can assume what part of this data can be the packet id. In the screen above the packet id is 0x34A5. So right click in the text section -> Search for -> All constants, enter the value 0x34A5 and press OK:

We follow the second result here and reach a really interesting function :)

This function contains a bunch of calls that create a callback for a specific packet_id. The first MOV is the packet id and the second is the function that will be called if that specific packet arrives. In our case we copy the function address for 0x34A5 (0x943F40) and press Ctrl+g to jump to that address in the code. We set a normal breakpoint at this address and restart the application. The breakpoint then hits after pressing start. And we can follow up the stack until we see a function that matches our needs. In our actual case there are 4 RETURNs before 0xCD9540.
So next step is to hook our new discovered function first thing here is to define the packet_t struct in lua:
typedef struct sro_packet_t{
/* 0x00 */ void** functions;
/* 0x04 */ uint32_t bytes_read;
/* 0x08 */ uint32_t len;
/* 0x0C */ uint32_t unknown1;
/* 0x10 */ unsigned char* buffer;
/* 0x14 */ unsigned char* buffer2;
/* 0x18 */ uint16_t packet_id;
/* 0x1A */ uint16_t unknown2;
}sro_packet_t;
]]
Next we have to define the function that will be called before 0xCD9540 is called:
local packet = cast("sro_packet_t*", context.arg32(1));
local buf = ffi.string(packet.buffer, packet.len);
print("[READ]: "..packet.packet_id);
end
Finally we set a codecave at the top of function 0xCD9540:
When these three blocks of Lua code are inserted i.e. at the end of config.lua, mmBBQ will print out the read packet codes as they pass by. We already added a lot of packetfilter stuff in the file: sro_net.lua. To see the full lua sourcecode of that just download the mmbbq SDK from here.
Packet Sending functions
To finish our packet journey we still need to find a suitable function for packet send. The start will be the same as the read part except that we breakpoint WSASend this time and follow the control flow up. This time its a bit more easy cause of the stack.

We will select the last return addr here 0x8CE578 (right click -> Follow in Disassembler) Now we set a breakpoint at the top of the reached function and run run. If we are fast enough another time the breakpoint hits, otherwise we receive a disconnect and we have to reload the binary again. The breakpoint hits and we follow the return address in the stack to the calling function. If we breakpoint the top of this function again and the breakpoint hits we will see that the first argument is a pointer on the stack. We follow this address in dump and we see a similar
structure we already know from packet_read 0x8CE980 is the function address.
local packet = cast("sro_packet_t*", context.arg32(1));
local buf = ffi.string(packet.buffer, packet.len);
print("[SEND]: "..packet.packet_id);
end
codecave.inject("packet_send", 0x8CE980, hook_packet_send);
I hope my writings are informative and that you can adopt it for your the work. Feel free to contact me on suggestions or questions: #duschkumpane at freenode irc
Greetz,
defragger
[...] Part2 – Networking (Packet read, write) [...]
Any information on when part 3 will be out?
Hi this is a very nice guide. I have a question though, how did you found out at this point the exact structure of the sro_packet_t struct? You already know, at such early point, the variable sizes and the descriptive field names. Any clue?
part 3 part 3 part 3 part 3
thanks for article! waiting for next part
I’m waiting for the part 3 impatiently for a long time ! :)
part3?
Wow im really learning things now haha :D
thank you defragger!
i would appreciate if you keep on doing this ;)
can’t wait for the next chapter!
We are currently a bit busy and distracted, but we will make the third chapter. We work on PathOfExile (PoE) so maybe we switch the tutorials target to that title.
Please start these again, I learned tons from it! Open up a donation link too so I can donate to keep you guys motivated (money is no problem for me, I can donate a decent amount!)
+1
Sounds nice, would be great if the tutorials switched to PathOfExile!
Thats a piece of a good tutorial!
Actually helped me with Metin 2 client ;) My next goal is knight online, its all to learn how it works.
For anyone willing to learn more RE try Lena tutorial :)
Gives very good basics about RE.
Cant wait to see part 3! Any info about the time of release?
You guys are true teachers! can’t wait for part 3
Really hope you are going to do the 3rd part of the series!
Awesome stuff.
Can’t wait for the next chapter. I will donate too! Keep going! You guys are great!
Please put part 3 :)