Register for your free account! | Forgot your password?

Go Back   elitepvpers > Popular Games > Silkroad Online > SRO Coding Corner
You last visited: Today at 17:12

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



[Guide] Client Based Packet Injection in Silkroad

Discussion on [Guide] Client Based Packet Injection in Silkroad within the SRO Coding Corner forum part of the Silkroad Online category.

Reply
 
Old   #1

 
elite*gold: 260
Join Date: Aug 2008
Posts: 560
Received Thanks: 3,778
[Guide] Client Based Packet Injection in Silkroad

This article is a special one! Thanks to clockwork for letting me share some knowledge I learned from him in the past. This information is still relevant and useful for Silkroad, but as I will write about, might be too much work for the benefits.

Client Based Packet Injection in Silkroad

I. Purpose

Welcome to the fourth guide in my series! I would like to take this time to thank clockwork for allowing me to write a guide based on his approach for performing client based packet injections in Silkroad. This is an approach he came up with a number of years ago and it was used ever since in 0x33’s programs. The purpose of this guide is to show how to accomplish a creative yet simple approach to being able to inject packets from the client to the server using the client’s existing functions.

While the goal of this guide is to educate users on how this task was accomplished, I am not writing this guide solely so you can use it in your own programs. The method itself is very creative and works, but it is extremely limited and highly dependent on the client, which can be a good thing and a bad thing. While it certainly is one of the safest ways to go about injecting packets that always have the right security, I think using a proxy server is the better approach to take to complete control over the packet flow into and out of the client. I will cover that technique in a later guide, but for now I am glad to be able to share this method with anyone who is interested in it.

II. Requirements

This guide, just like the last, might be a little advanced for most readers at this stage again. I will try to make things as clear as possible and write towards an intermediate level, but some of the concepts used here just take time and experience to understand over a longer term. Some of the concepts here might also seem like they are not greatly explained either. This might be because this approach is being presented more for the educational value of knowing how it works rather than the benefits of being able to use it in your own programs. I think it would be more worth while to invest into a more powerful solution than can be accomplished easily using this method.

In order to be able to follow along this guide, you will need:
• OllyDbg 1.10 (or equivalent)
• Visual Studio 2008 (or equivalent)

Before you continue this guide, you will also need to have already read and understood the three previous guides! All of the guides build on one another in some shape or form, so it’s most beneficial to have read them all by now.

III. Theory

After we get our own DLL into a process, we might think to ourselves, “How can we send packets to the server?” Naturally, we might think we could make the client do the work for us and rightfully so since that’s the job of the client. However, actually coming up with a solution to accomplish this can be tricky. This is because the client is huge, there is a lot of code to go through and we will not even be able to trace through even a minor percentage of it.

So, how do we stumble upon a solution like the one that is presented in this guide? Experience, intuition, and a whole lot of luck! I’ve noticed personally a lot of the things I’ve found in the client and have made use of were stumbled upon by chance. I was looking for something specific and was off target but instead found something more interesting instead. While I don’t know the original thought process that was used to come up with this method, you can be sure that it is not something that you can just look at code and do; it takes time.

If we think about the game client and how it works, the process might look something like this:
1. User interacts with the game client via mouse/keyboard.
2. Client processes input and determines what data to send the server.
3. Client formats data to send and applies any security packaging and finally sends it.

Naturally, we might want to just go to step 3 and figure out that process so we can inject packets manually. However, this is not always so easy, especially when the client has packet security like Silkroad does. In fact, trying to come up with our own packet injecting solution that does all the work might be infeasible.

As a result, we have to think of other methods of accomplishing the task. What if we were able to fake the step 1? In that case, we could get the client to send data, but it would be a packet based on whatever we were faking. Not entirely useful, but it is definitely a start.

What if we were able to fake step 1, and then modify step 2 a little to use our own data rather than the client data? In that case, the game client would then execute step 3 which it assumes is valid data and would take care of everything for us. That is what this client based injection process does. It simulates step 1 to get the client to send a packet, it then modifies the contents of that packet, and finally it lets the client take care of everything else.

As a result, packet security can be ignored as the client will always create the correct security information for the data. If the security process were to change, then everything would still be fine as the client would have been updated to accommodate that. In theory, it really is a nice idea. However, as mentioned earlier in this section, being able to come up with such a solution is not easy or even trivial.

It really takes a lot of work, so it might not be practical to try on a new game. Trying to call functions in a client out of order is a really tricky process and there is a lot of things you have to take into considerations. Despite all of these obstacles, we still have a method available to us to use with Silkroad, so we can now get into the actual implementation of it.

IV. Implementation

Like the previous guide, the code for this guide will be placed at the end in a complete form so you have a compliable version. The code snippets shown will not be in order and a few things might be left out. This is done to try and make the guide more readable as trying to explain the code section by section can get tricky.

To get started, we will be reusing our WndProc codecave from the previous guide, Integrating AntTweakBar into Silkroad. We will be making a few modifications though. First, we will no longer be using AntTweakBar in the example so we need to clear out that logic. In addiction, I’ve changed a few minor things in how we use the codecave. Rather than placing the specific code in the UserOnInject, we now call a Setup function. Since there is nothing really new here, you can see the final code in the final code listing.

To create our GUI in Visual Studio, we will need to do the following:
1. Right click on the Resource Files folder in the Solution Explorer for the DLL project and choose Add -> Resource.
2. In the new Add Resource Dialog that pops up, highlight Dialog and press New.
3. Now that we have a new dialog, we need to change a few settings. Right click on the dialog in the form editor and choose Properties.
4. In the Properties Bar, change “Border” to “Thin”.
5. Next, change “Minimize Box” to “True”.
6. Finally, change “Visible” to “True”.
7. Save the current changes.

With our GUI made, we can now add a test button to drive our program. Click on the Toolbox window tab in Visual Studio to bring up a bar of components we can add to our dialog. Click on the Button component and click once on the dialog to place it. Right click on the button and choose properties. Change the “Caption” to “Sit” and hit enter in the field to update the value. We have two existing buttons on our dialog that we do not need. Select the “OK” and “Cancel” buttons and press delete to remove them from the form. Save again. Here is a screenshot of how the GUI looks for me.


Switch back to your DLL.cpp file so we can resume working with code. At this point, we need to add in the code for our GUI. If you have never done any Win32 GUI programming before, then I would suggest you Google some simple tutorials to get a feel for it. What will follow next is the GUI code; there is not much that needs to be explained here. There is one section that we have not gone over yet, but we will be getting to that soon. Here is the code:

Code:
namespace Win32Gui
{
	INT_PTR CALLBACK DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		switch(uMsg) 
		{
			case WM_INITDIALOG:
			{
			}
			break;

			case WM_COMMAND:
			{
				int button = LOWORD(wParam);
				switch(button)
				{
					case IDCANCEL:
					{
						PostQuitMessage(0);
					} break;

					case IDC_BUTTON1:
					{
						Packet::TPacket * sitPacket = new Packet::TPacket;
						sitPacket->size = 1;
						sitPacket->opcode = 0x7017;
						sitPacket->data[0] = 0x04;
						Packet::SendPacket(sitPacket);
					} break;
				}
			} break;

			default:
			{
				return FALSE;
			}
		}
		return TRUE;
	}

	DWORD WINAPI GuiThread(LPVOID lpParam)
	{
		HWND hwnd = CreateDialog(gInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
		MSG Msg = {0};
		while(GetMessage(&Msg, NULL, 0, 0) > 0)
		{
			if(!IsDialogMessage(hwnd, &Msg))
			{
				TranslateMessage(&Msg);
				DispatchMessage(&Msg);
			}
		}
		return 0;
	}

	void Setup()
	{
		CreateThread(0, 0, GuiThread, 0, 0, 0);
	}
}
The reason we have our code setup this way is because we want our own GUI to run in a different thread than the main Silkroad thread. The reason for this is because if our GUI stalls or takes a while to process, the game is not affected. If we were to tie our GUI logic into the main Silkroad thread, which is possible, we can adversely affect the game.

Next, I am going to skip slightly out of order and cover the higher level packet sending functionality that we will be using. The reason we need this functionality is because our GUI thread runs in a separate thread from the main client. As a result, if we were to simply inject packets from the second thread we would most likely crash our client or cause corruption and disconnect.

In order to implement a multithreaded safe solution, we must use a critical section to lock our data so threads can access the data in a serial form and not have any issues of overlapping each other and messing things up. Here is the code:

Code:
namespace Packet
{
	struct TPacket
	{
		WORD size;
		WORD opcode;
		BYTE data[4096];
	};

	CRITICAL_SECTION cs = {0};
	TPacket * packetQueue[32] = {0};
	int packetQueueIndex = 0;

	void Setup()
	{
		InitializeCriticalSection(&cs);
	}

	void SendPacket(TPacket * p)
	{
		EnterCriticalSection(&cs);
		packetQueue[packetQueueIndex++] = p;
		if(packetQueueIndex >= 32)
			packetQueueIndex = 0;
		LeaveCriticalSection(&cs);
	}

	TPacket * GetNextPacket()
	{
		TPacket * p = 0;
		EnterCriticalSection(&cs);
		for(int x = 0; x < 32; ++x)
		{
			if(packetQueue[x])
			{
				p = packetQueue[x];
				packetQueue[x] = 0;
				break;
			}
		}
		LeaveCriticalSection(&cs);
		return p;
	}
}
Rather than using a vector, a list, or a queue, I opted to just do it the old fashion way because of how some troubles can arise when using those containers in a DLL that is used the way ours is setup. I noticed my overall code was crashing the Visual Studio linker often, so I’m not sure if I have memory corrupted on my system or the code just breaks the linker.

Finally we can get to the good stuff. What I will cover next is the client based injection approach clockwork came up with and what was used for a while in our programs. I will post the code and then start explaining what the comments don’t. Here it is:

Code:
namespace CC_InjectPacket
{
	// The location of the hook
	// Binary Search pattern for Olly: 8B 11 8B 42 2C 57 
	// MOV EDX,[ECX]
	// MOV EAX,[EDX+2C] <- Patch
	// PUSH EDI
	#define SEND_HOOK 0x7418A2

	// The location of our packet sending code
	// Binary Search pattern for Olly: 00 75 0C 6A 04
	// PUSH 4
	// MOV ECX,00B36D5C <- SEND_ECX
	// CALL 006168B0 <- SEND_CALL
	#define SEND_ECX 0xD3B8E4
	#define SEND_CALL 0x7215E0

	// Holds the final bytes for the codecave
	BYTE toggleOn[6];

	// Holds the original bytes for the codecave
	BYTE toggleOff[6];

	// Data to be sent is stored in a static location so
	// we can access it easily from our codecave  
	char sendbuffer[8192] = {0};

	void Format(LPBYTE _edi)
	{
		// This function is called from our hook in the send procedure
		// and replaces the original packet data with our own

		LPBYTE pdata = _edi;	// packet struct
		pdata += 0x34;	// point to data

		// Read size from our sendbuffer
		WORD size = 0;
		memcpy((LPVOID)&size, sendbuffer, 2);
		size += 6;

		// Overwrite data in packet struct with data from sendbuffer
		memcpy(pdata, (LPVOID)&sendbuffer, size);
	}

	DWORD sendhook_retAddr1 = 0;
	__declspec(naked) void codecave_sendhook()
	{
		__asm 
		{
			pop sendhook_retAddr1
			mov eax,[edx + 0x2C] // original command from client
			pushad
			push edi		// packet struct
			call Format
			add esp, 4
			popad 
			push edi		// packet struct
			call eax		// finish client's send
			push sendhook_retAddr1
			ret
		}
	}

	// Sends a packet to the game server.
	void SendPacketRaw(WORD size, WORD opcode, LPBYTE data)
	{
		size += 6;
		memcpy(sendbuffer + 0, &size, 2);
		memcpy(sendbuffer + 2, &opcode, 2);
		sendbuffer[4] = 0;
		sendbuffer[5] = 0;
		memcpy(sendbuffer + 6, data, size - 6);

		// For debugging purposes
		for(int x = 0; x < size; ++x)
			printf("%.2X ", sendbuffer[x]);
		printf("\n");

		// Write out the codecave
		edx::WriteBytes(SEND_HOOK, toggleOn, 6);

		// Invoke a packet send
		__asm
		{
			push 4
			mov ecx, SEND_ECX
			mov eax, SEND_CALL
			call eax
		}

		// Restore original bytes
		edx::WriteBytes(SEND_HOOK, toggleOff, 6);
	}

	void Setup()
	{
		// Store the original client code
		edx::ReadBytes(SEND_HOOK, toggleOff, 6);

		// Write out the codecave and save the new code
		edx::CreateCodeCave(SEND_HOOK, 6, codecave_sendhook);
		edx::ReadBytes(SEND_HOOK, toggleOn, 6);

		// Restore the old code
		edx::WriteBytes(SEND_HOOK, toggleOff, 6);

		// For debugging purposes
		printf("Toggle On: ");
		for(int x = 0; x < 6; ++x)
			printf("%.2X ", toggleOn[x]);
		printf("\n");

		// For debugging purposes
		printf("Toggle Off: ");
		for(int x = 0; x < 6; ++x)
			printf("%.2X ", toggleOff[x]);
		printf("\n");
	}
}
That’s quite a big chunk of code, so I’ll try to go slow and cover everything. As explained in the theory section, we will want to trigger a packet to be sent by the client. In order to do this, we have to find 2 separate locations. First we need to find the packet sending call itself and the associated data pointer for it and then we have to find the location of the trigger function we are going to be using.

How do we find the trigger function? We can use the binary code snippet shown in OllyDbg to track down the location. How do we know this won’t change anytime soon? Well, that comment is from 2007, so I think we are pretty good for a while. The function that was hooked was the function used during the character command actions, such as when you sit/stand.

Once we have that trigger function and the associated data pointer, we can next find the location of the packet sending code. We can use the second binary snippet to do this. If we look above the code a little we will see the line, 0074182F |. 68 B0F9C400 PUSH sro_clie.00C4F9B0 ; UNICODE "(%03d) Message : 0x%04x". This line is actually a debug message that the client is set to not execute, but we could actually make the client show all of the opcodes it sends if we made the right changes!

Once we have our locations tracked down, we can move into the specific workings of the method. We want to simply perform a Kansas City Shuffle on the client. We make the client think we executed an action command. As a result, it sets up the packet and then goes to send it. However, when it does that, we quickly change the contents of the packet so it’s our own packet rather than the one the client build. As a result, the client performs the necessary security processing on the packet and dispatches it without even knowing we pulled a quick one on it.

The reason we have to toggle our codecave bytes is because we want the client to be able to send regularly, so only when we trigger the packet sending function does we want to hook the send call and change the contents. That’s all there is to it! It is a very creative approach and a lot easier to implement than trying to reconstruct a new packet sending function from scratch.

I am not going to paste the code for our new UserOnWndProc and UserOnInject functions because those will be in the final code listing. Aside from those two functions, we have everything we need to handle packet injection via the client. Here is our final DLL.cpp file:

Code:
#include <windows.h>
#include "../common/common.h"
#include "resource.h"

// Global instance handle to this DLL
HMODULE gInstance = NULL;

// Function prototype
void UserOnInject();

// Main DLL entry point
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReason, LPVOID lpReserved)
{
	UNREFERENCED_PARAMETER(lpReserved);
	if(ulReason == DLL_PROCESS_ATTACH)
	{
		gInstance = hModule;
		// Do not notify this DLL of thread based events
		DisableThreadLibraryCalls(hModule);
	}
	return TRUE;
}

// This is the main function that is called when the DLL is injected into the process
extern "C" __declspec(dllexport) void OnInject(DWORD address, LPDWORD bytes)
{
	// Restore the original bytes at the OEP
	DWORD wrote = 0;
	WriteProcessMemory(GetCurrentProcess(), UlongToPtr(address), bytes, 6, &wrote);

	// Call our user function to keep this function clean
	UserOnInject();
}

//------------------------------------------------------------------------

struct tSilkroadWndProc
{
	HWND hWnd;
	UINT message;
	WPARAM wParam;
	LPARAM lParam;
};

tSilkroadWndProc SilkroadWndProc = {0};

DWORD UserOnWndProc();

namespace CC_WndProc
{
	DWORD codecave_WndProc_ReturnAddress = 0;
	DWORD codecave_WndProc_esp = 0;
	DWORD codecave_WndProc_result = 0;

	void OnWndProc()
	{
		memcpy(&SilkroadWndProc, ((LPBYTE)ULongToPtr(codecave_WndProc_esp)) + 4, 16);
		codecave_WndProc_result = UserOnWndProc();
	}

	__declspec(naked) void codecave_WndProc()
	{
		__asm pop codecave_WndProc_ReturnAddress
		__asm mov codecave_WndProc_esp, esp
		__asm pushad
		OnWndProc();
		__asm cmp codecave_WndProc_result, 0
		__asm jne LABEL_1
		__asm popad
		__asm xor EAX, EAX
		__asm ret 0x10
LABEL_1:
		__asm popad
		__asm CMP EAX, 0x496
		__asm push codecave_WndProc_ReturnAddress
		__asm ret
	}

	void Setup()
	{
		edx::CreateCodeCave(0x7324F4, 5, CC_WndProc::codecave_WndProc);
	}
}

//------------------------------------------------------------------------

namespace CC_InjectPacket
{
	// The location of the hook
	// Binary Search pattern for Olly: 8B 11 8B 42 2C 57 
	// MOV EDX,[ECX]
	// MOV EAX,[EDX+2C] <- Patch
	// PUSH EDI
	#define SEND_HOOK 0x7418A2

	// The location of our packet sending code
	// Binary Search pattern for Olly: 00 75 0C 6A 04
	// PUSH 4
	// MOV ECX,00B36D5C <- SEND_ECX
	// CALL 006168B0 <- SEND_CALL
	#define SEND_ECX 0xD3B8E4
	#define SEND_CALL 0x7215E0

	// Holds the final bytes for the codecave
	BYTE toggleOn[6];

	// Holds the original bytes for the codecave
	BYTE toggleOff[6];

	// Data to be sent is stored in a static location so
	// we can access it easily from our codecave  
	char sendbuffer[8192] = {0};

	void Format(LPBYTE _edi)
	{
		// This function is called from our hook in the send procedure
		// and replaces the original packet data with our own

		LPBYTE pdata = _edi;	// packet struct
		pdata += 0x34;	// point to data

		// Read size from our sendbuffer
		WORD size = 0;
		memcpy((LPVOID)&size, sendbuffer, 2);
		size += 6;

		// Overwrite data in packet struct with data from sendbuffer
		memcpy(pdata, (LPVOID)&sendbuffer, size);
	}

	DWORD sendhook_retAddr1 = 0;
	__declspec(naked) void codecave_sendhook()
	{
		__asm 
		{
			pop sendhook_retAddr1
			mov eax,[edx + 0x2C] // original command from client
			pushad
			push edi		// packet struct
			call Format
			add esp, 4
			popad 
			push edi		// packet struct
			call eax		// finish client's send
			push sendhook_retAddr1
			ret
		}
	}

	// Sends a packet to the game server.
	void SendPacketRaw(WORD size, WORD opcode, LPBYTE data)
	{
		size += 6;
		memcpy(sendbuffer + 0, &size, 2);
		memcpy(sendbuffer + 2, &opcode, 2);
		sendbuffer[4] = 0;
		sendbuffer[5] = 0;
		memcpy(sendbuffer + 6, data, size - 6);

		// For debugging purposes
		for(int x = 0; x < size; ++x)
			printf("%.2X ", sendbuffer[x]);
		printf("\n");

		// Write out the codecave
		edx::WriteBytes(SEND_HOOK, toggleOn, 6);

		// Invoke a packet send
		__asm
		{
			push 4
			mov ecx, SEND_ECX
			mov eax, SEND_CALL
			call eax
		}

		// Restore original bytes
		edx::WriteBytes(SEND_HOOK, toggleOff, 6);
	}

	void Setup()
	{
		// Store the original client code
		edx::ReadBytes(SEND_HOOK, toggleOff, 6);

		// Write out the codecave and save the new code
		edx::CreateCodeCave(SEND_HOOK, 6, codecave_sendhook);
		edx::ReadBytes(SEND_HOOK, toggleOn, 6);

		// Restore the old code
		edx::WriteBytes(SEND_HOOK, toggleOff, 6);

		// For debugging purposes
		printf("Toggle On: ");
		for(int x = 0; x < 6; ++x)
			printf("%.2X ", toggleOn[x]);
		printf("\n");

		// For debugging purposes
		printf("Toggle Off: ");
		for(int x = 0; x < 6; ++x)
			printf("%.2X ", toggleOff[x]);
		printf("\n");
	}
}

//------------------------------------------------------------------------

namespace Packet
{
	struct TPacket
	{
		WORD size;
		WORD opcode;
		BYTE data[4096];
	};

	CRITICAL_SECTION cs = {0};
	TPacket * packetQueue[32] = {0};
	int packetQueueIndex = 0;

	void Setup()
	{
		InitializeCriticalSection(&cs);
	}

	void SendPacket(TPacket * p)
	{
		EnterCriticalSection(&cs);
		packetQueue[packetQueueIndex++] = p;
		if(packetQueueIndex >= 32)
			packetQueueIndex = 0;
		LeaveCriticalSection(&cs);
	}

	TPacket * GetNextPacket()
	{
		TPacket * p = 0;
		EnterCriticalSection(&cs);
		for(int x = 0; x < 32; ++x)
		{
			if(packetQueue[x])
			{
				p = packetQueue[x];
				packetQueue[x] = 0;
				break;
			}
		}
		LeaveCriticalSection(&cs);
		return p;
	}
}

//------------------------------------------------------------------------

namespace Win32Gui
{
	INT_PTR CALLBACK DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		switch(uMsg) 
		{
			case WM_INITDIALOG:
			{
			}
			break;

			case WM_COMMAND:
			{
				int button = LOWORD(wParam);
				switch(button)
				{
					case IDCANCEL:
					{
						PostQuitMessage(0);
					} break;

					case IDC_BUTTON1:
					{
						Packet::TPacket * sitPacket = new Packet::TPacket;
						sitPacket->size = 1;
						sitPacket->opcode = 0x7017;
						sitPacket->data[0] = 0x04;
						Packet::SendPacket(sitPacket);
					} break;
				}
			} break;

			default:
			{
				return FALSE;
			}
		}
		return TRUE;
	}

	DWORD WINAPI GuiThread(LPVOID lpParam)
	{
		HWND hwnd = CreateDialog(gInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
		MSG Msg = {0};
		while(GetMessage(&Msg, NULL, 0, 0) > 0)
		{
			if(!IsDialogMessage(hwnd, &Msg))
			{
				TranslateMessage(&Msg);
				DispatchMessage(&Msg);
			}
		}
		return 0;
	}

	void Setup()
	{
		CreateThread(0, 0, GuiThread, 0, 0, 0);
	}
}

//------------------------------------------------------------------------

// The function where we place all our logic
void UserOnInject()
{
	// Create a debugging console
	edx::CreateConsole("SilkroadFramework Debugging Console");

	// Mutex for the launcher, no patches required to start Silkroad now
	CreateMutexA(0, 0, "Silkroad Online Launcher");
	CreateMutexA(0, 0, "Ready");

	CC_WndProc::Setup();
	CC_InjectPacket::Setup();
	Packet::Setup();
	Win32Gui::Setup();
}

// User function called on the WndProc
DWORD UserOnWndProc()
{
	static int MYTIMERID = 777;

	if(SilkroadWndProc.message == WM_CREATE)
	{
		// Attach a timer to our main window
		SetTimer(SilkroadWndProc.hWnd, MYTIMERID, 250, 0);
	}

	// If a timer triggers
	if(SilkroadWndProc.message == WM_TIMER)
	{
		// Check to see if it is ours
		if(SilkroadWndProc.wParam == MYTIMERID)
		{
			Packet::TPacket * p = Packet::GetNextPacket();
			while(p)
			{
				CC_InjectPacket::SendPacketRaw(p->size, p->opcode, p->data);
				delete p;
				p = Packet::GetNextPacket();
			}
			return 0;
		}
	}
	return 1;
}
And that’s all there is to it! We should be able to compile and run to start Silkroad and once we are in game, be able to click the Sit button to make the character sit and stand. It is nothing specular, but having this ability a few years back when there was no information like there is today made it great!

V. Conclusion

Hopefully by now, you have a good idea of one way you can injects packets from the client to the server while in the client itself. As mentioned before, this approach does work out, but it is not the most ideal method nowadays. It is client dependent, subject to possible stability problems, and you have to take into consideration multithreaded issues if you do not inject packets while inside the main Silkroad thread. With that said though, this guide should show you just the things that are possible with a DLL injected in a process and how you can use a little creativity to do things that might otherwise be really tricky.

That wraps up this article. I will have a future guide that shows an alternative to this method based on my previously released sr33 and edx33 projects. Those articles will take some time to get done, so I will be working on others in the mean time. I hope you found this guide informational and beneficial. Stay tuned for future guides!

Drew “pushedx” Benton
edxLabs
pushedx is offline  
Thanks
46 Users
Old 06/25/2009, 08:26   #2
 
JasonWalker's Avatar
 
elite*gold: 0
Join Date: May 2008
Posts: 162
Received Thanks: 257
Perfect guide! Perfect job! Thanks.
JasonWalker is offline  
Old 06/25/2009, 09:15   #3
 
elite*gold: 0
Join Date: Jun 2008
Posts: 188
Received Thanks: 106
Tried to do this a few times, always failed. I suck at reversing.

Thanks.
maxbot is offline  
Old 06/25/2009, 09:48   #4
 
x_king_x's Avatar
 
elite*gold: 20
Join Date: Nov 2008
Posts: 746
Received Thanks: 147
Thanks Perfect Job
Btw u said that's the 4th thread of ur series ...
so can u put like... links for them in ur signature?
Thanks again
Keep It Up
#Edit
Found The three Other threads:



Search FTW
x_king_x is offline  
Old 06/25/2009, 10:48   #5

 
elite*gold: 260
Join Date: Aug 2008
Posts: 560
Received Thanks: 3,778
Yea, I had plans to adding the articles to my signature soon. I've just been busy writing some more and handling some other things, but I'll do it. Thanks!
pushedx is offline  
Old 06/25/2009, 14:15   #6
 
elite*gold: 0
Join Date: May 2008
Posts: 259
Received Thanks: 94
You are my ***

Im trying to learn some C++. hope i can do it
soadmania is offline  
Old 06/25/2009, 18:56   #7
 
MoSHiRiKaTo's Avatar
 
elite*gold: 0
Join Date: Apr 2007
Posts: 23
Received Thanks: 22
Very well explained, even the binary strings included Great job, I have to test this out! Thanks Drew! <3
MoSHiRiKaTo is offline  
Old 06/27/2009, 12:30   #8
 
strukel's Avatar
 
elite*gold: 20
Join Date: Jul 2007
Posts: 2,215
Received Thanks: 1,360
#stick
strukel is offline  
Old 08/14/2009, 00:07   #9
 
elite*gold: 0
Join Date: Mar 2008
Posts: 29
Received Thanks: 1
I followed this guide to a "T" and when I click "sit" nothing happens ... Am I missing something ? Does this need to be updated for the new version of silkroad ? And if so how would one go about doing that ... Thanks Drew !
D@RK_L!GHT is offline  
Old 08/14/2009, 01:45   #10

 
[Wicked]'s Avatar
 
elite*gold: 702
Join Date: Jul 2008
Posts: 1,291
Received Thanks: 415
Well based on the new client the Opcodes and Packets are changed. That's all that needs to be changed i guess But i don't know and i can't test cause i quit isro ^.^ So try it and see.
[Wicked] is offline  
Old 08/14/2009, 02:32   #11
 
elite*gold: 0
Join Date: Mar 2008
Posts: 29
Received Thanks: 1
Quote:
Originally Posted by Cha0sBG® View Post
Well based on the new client the Opcodes and Packets are changed. That's all that needs to be changed i guess But i don't know and i can't test cause i quit isro ^.^ So try it and see.
Yeah but how would I go about doing this ...
D@RK_L!GHT is offline  
Old 08/14/2009, 04:56   #12

 
elite*gold: 260
Join Date: Aug 2008
Posts: 560
Received Thanks: 3,778
Quote:
Originally Posted by D@RK_L!GHT View Post
Yeah but how would I go about doing this ...
To get the updated opcode, the easiest thing to do is find a pre 1.203 client and find the part in the client that uses the 0x7017 opcode. Then, in the new latest unpacked client, do binary searches based on surrounding code to find the area and you should be able to find the new opcode.

It's a bit harder with that specific opcode because the code is similar in other places, so you'll want to just look at the code that calls the function, and then binary search around that instead. That should get you to the newest opcode.

For anyone that doesn't want to play hide and seek:

I'll be working on combining my guides into one project that's a little easier to maintain after we get Legend 4+. We are going to get a new client for that and since JM said it's coming n August, I've been holding off from trying to reupdate the guides each update.
pushedx is offline  
Old 08/14/2009, 13:51   #13
 
elite*gold: 0
Join Date: Mar 2008
Posts: 29
Received Thanks: 1
Thanks Drew, I get it now =)

How would you go about finding opcodes originally(without knowing what it was before the update)? Sorry for all the questions, Im just trying to understand the process. Thanks for all your help drew, I really appreicate it.

Btw I heard you know my friend bleed...
D@RK_L!GHT is offline  
Old 08/14/2009, 14:15   #14
 
elite*gold: 0
Join Date: Jun 2008
Posts: 188
Received Thanks: 106
Quote:
Originally Posted by pushedx View Post
To get the updated opcode, the easiest thing to do is find a pre 1.203 client and find the part in the client that uses the 0x7017 opcode. Then, in the new latest unpacked client, do binary searches based on surrounding code to find the area and you should be able to find the new opcode.

It's a bit harder with that specific opcode because the code is similar in other places, so you'll want to just look at the code that calls the function, and then binary search around that instead. That should get you to the newest opcode.

For anyone that doesn't want to play hide and seek:

I'll be working on combining my guides into one project that's a little easier to maintain after we get Legend 4+. We are going to get a new client for that and since JM said it's coming n August, I've been holding off from trying to reupdate the guides each update.
Yeah, that's how everyone probably does it for C->S opcodes. For those who don't know how to get them anyway, here are binary strings for some most used C->S opcodes (I guess they haven't changed) :

Code:
745A : 6A 01 8B CE E8 EB 7C FE FF 33 DB 53
72CD : 8B CF E8 63 7A FE FF 84 C0 0F 85 D4 0B 00 00 BB 01 00 00 00 53 8B CF E8 0E 59 FE FF 56
7017 : 33 C4 50 8D 44 24 24 64 A3 00 00 00 00 33 DB 53 - seems to be wrong
7738 : 33 C0 8B 4C 24 48 64 89 0D 00 00 00 00 59 5E 5B 83 C4 48
706D : 80 C3 0D 6A 01 88 5C 24 3F 88 54 24 40 C6 44 24 3C 00
maxbot is offline  
Old 08/14/2009, 18:32   #15

 
elite*gold: 260
Join Date: Aug 2008
Posts: 560
Received Thanks: 3,778
Quote:
Originally Posted by D@RK_L!GHT View Post
How would you go about finding opcodes originally(without knowing what it was before the update)?
There's two ways to go about this:

1. Find the packet handler for Silkroad in Olly and set a log breakpoint on the opcode that's used. You can use my extracting sent packets guide to find that and then set the log breakpoint on the part where the opcode is stolen. Then you can do stuff in game and watch the console output in Olly. This method is not recommended for Server to Client packets.

2. Use a Silkroad proxy such as edx33, nuconnector, or anything that allows you to see complete packets to perform actions in game to get the opcodes that are sent from the client. This is generally the best approach assuming the tools exist to do it (which they do for Silkroad)!

Once you have the opcodes from the game, you can then just do the constant searches in the client again to follow the client's logic for those packets. The only exception in this process are the login server packets which are processed differently so finding the processing for those is a bit more work. For world server packets though, it's pretty simple.
pushedx is offline  
Reply


Similar Threads Similar Threads
[RELEASE] I named my bot LILPROHACKER, based "Disconnect'S packet based BOT"
02/08/2010 - Shaiya Hacks, Bots, Cheats & Exploits - 6 Replies
Hello guys Thats release section right??? http://img686.imageshack.us/img686/8051/fuckthate .jpg Shaiya Packet Bot By lilprohacker - G-R - Your Gaming Community DLL error, but its not my problem i did it just for show people what is dll error. Solution: Put "pipeClient.dll" and "packet.dll" to Shaiya Packet Bot v1023 folder http://img707.imageshack.us/img707/4519/fuckthat. jpg
Packet injection
10/06/2008 - General Coding - 0 Replies
I´ve got a question about packet injection. So is it right that you have to sniff the packets from the game and then you can send it to the server?? E.g I found the packet wich is sent when casting an ability, could i use a packet injection tool to send this packet to the server and then my character would cast this spell?



All times are GMT +1. The time now is 17:18.


Powered by vBulletin®
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2025 elitepvpers All Rights Reserved.