Register for your free account! | Forgot your password?

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

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


[Guide] Creating a Simple Loader with Injected DLL for Silkroad

Discussion on [Guide] Creating a Simple Loader with Injected DLL for Silkroad within the SRO Coding Corner forum part of the Silkroad Online category.

Reply
 
Old   #1
 
elite*gold: 260
Join Date: Aug 2008
Posts: 526
Received Thanks: 3,321
[Guide] Creating a Simple Loader with Injected DLL for Silkroad

This next article in my series is a very important one for anyone wanting to get started with client modifications or understanding the client itself. This is the base article that will be used for all my future articles that explain various Silkroad development concepts.

Like my previous article, there is nothing terribly exciting with this one as we have to get through the necessary boring stuff first before we can have any real fun. Unfortunately, this article is very heavy text wise and there are not many relevant images for the concepts being described. Sorry about that, but once we get this out of the way, the sailing will be much smoother!

While I'm writing this article for the Silkroad section, the concepts still apply to any game and you only need to make a couple of changes for it to work in other games. Those changes are the command line for the game and whatever is required to fake the client into thinking the original loader was used.

The last thing I should notice is that I am not attaching any of the files for this article. The reason for that is so users can work out doing these things themselves and never have to worry about not having the skills to do it again in the future. If a user can read through these articles and follow them without much trouble (barring any mistakes on my part), then they are ready for developing with Silkroad.

Creating a Simple Loader with Injected DLL for Silkroad

I. Purpose

The purpose of this guide is to show the necessary framework required for having a simple loader and injected DLL. The methods shown in this guide are going to be built upon in future articles, so this article is a very important foundation to have in place. The code provided is going to be as simple as possible as to not have too many distractions. This guide is also going to make use of existing code I’ve written in the past to help speed it along. I will reference external links for additional reading as well along the way.

II. Requirements

This guide is written as an intermediate guide that most people should be able to follow as I’m not teaching programming per se. Rather I’m using existing code of my own and simplifying it into an easy to use project. It is expected you have some basic knowledge of C++ and some ASM knowledge would not hurt either to understand the code that is not covered.

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

You can use other version of Visual Studio, but be warned that some code does not work the same on older versions. I cannot test all the older versions of Visual Studio anymore since I’ve upgraded to Windows 7 beta and those installs were on my XP drive. I would recommend using at least version 2005 if you cannot get access to 2008. Likewise, I’ve not done any testing with 2010 as it is still in early beta stages.

III. Theory

The essence of any loader is simple. The user runs the loader, the loader performs some routines, and finally the loader launches the game client. Most games nowadays use loaders so they are commonplace. If you were to open up Silkroad.exe, the Silkroad client’s official loader, you might be able to follow some of the logic it does internally. However, that is not important for now. We are concerned with how to make our own loader.

Luckily for us, all we really have to do is create a program that has two abilities. The first ability is being able to create a process in a suspended state. The second ability is being able to inject a DLL into that suspend process. Once we have that done, then all of our real work takes place in the DLL that is going o be injected.

Some loaders do patch the client and make changes when a DLL is not needed, but that is not our scenario here. We need a DLL to be injected into the client in the least obstructive way, which is why our loader is so simple. There are many ways to go about creating a loader. You can even create a loader that uses Window’s workings so you don’t even need to create a loader executable. These are more advanced topics and will not be covered here as we want something simple to get up and running with the client.

That is all there is to a loader really. It is just a simple program that launches another program after performing some internal tasks. The loader we will create will be no different. We now need to look at what the DLL will be doing that we inject into the client.

Once a DLL is injected into a process, it becomes part of the process. That means the DLL can access anything in the executable’s memory as well as the actual code that is going to be executed. This is great for us because we can make patches to the client in real time, add in new functions to change the logic of existing functions, as well as detour certain areas to siphon useful information from the client.

If you look around at a lot of DLL injection tutorials, you will notice how the main bulk of the code is performed in the DLLMain entry function. For the most part, these tutorials are wrong and the suggestions they provide can be hazardous. If you want to create a DLL with a purpose like ours, you will want to be sure to understand the Best Practices for Creating DLLs. There is a lot of useful information to understand in that MSDN resource that most people skip and never make a mention of. As a result, their programs often have troubles when being used on different versions of Windows!

The DLL injection method that I am going to use is an older version of something I came up with a few years back to create a solution that would work with any executable, packed or not. The way it works is by modifying the entry point of the target to immediately jump into the DLL loading function. It will then load the DLL and call a specific function so the DLL can patch the entry point back to its original bytes.

The advantage to the method is that it is very fast, efficient, and provides a correct means to use a DLL in a process that is inline with the best practices suggested by Microsoft. The loader allocates a stub of memory in the targeted host first. It will then write out an assembly based loader procedure that is hand written. Finally, it will overwrite the entry point of the host so execution starts with calling the procedure to inject the DLL. The writing takes place when the process is suspended and the loading and injecting takes places when the application is running, so it al works out perfectly (assuming we need to inject before the process runs, which we do here).

The disadvantage to the method is that it is hard coded and not easy to change or understand if you are new. It is also a lot of code! I do not want to attach any additional files, so I will be embedding the code into the article. I’ve left in the comments to help follow the logic so as a result, the code listing is pretty long. I have worked on a newer much improved method, but it is too complicated to use for now and I’ve not even written about that approach yet.

We should have a basic idea of what our loader and injected DLL are going to be like. It is time to move on to the implementation stage!

IV. Implementation

To begin, we will need to create our Visual Studio workspace. I will spend a little extra time here describing how to go about that because a properly setup workspace can greatly reduce development time and headaches down the line. For this article I will be creating a workspace that has two projects: the loader and the DLL. The loader will also contain code to let the user select the Silkroad client as well as the DLL that is going to be injected. It will then store that information in a convenient location that is writeable on all systems. This means that once you go through the process of setting the client and DLL path, you can simply make changes to your DLL in Visual Studio, run the loader via Visual Studio and be able to test without copying over any files!

To create our workspace, we will first need to go to File -> New -> Project… In the new dialog that pops up (it says New Project), choose Other Project Types and then Visual Studio Solutions. Choose the Blank Solution under the Visual Studio installed templates area and type in a name for your workspace. Finally, hit Ok to create the Solution

Now, under the Solution Explorer on the left pane, right click on your new Solution. Choose Add -> New Project. Under the Visual C++ Project types area, choose Win32 and Win32 Project. We do not want to use a console for the Loader as we want as minimal user distractions so not having a console window is fine. Type in a new name for your Loader project and hit Ok. On the new dialog that pops up, choose Application Settings on the left pane. After the dialog switches, place a check in the Empty project checkbox and hit Finish.

We will repeat the previous step again for the DLL, except under the Application Settings, we will want to change the Application type to DLL and place a check in the Empty project checkbox. We now have our DLL and Loader workspace setup in one nice Visual Studio solution.

There is one more thing we need to do before we get into the specific implementation. When writing code for more than one project, we will most likely wish to share code between the projects without duplicating it. To accomplish this, we will need to create the files once in a location that all projects access. Right click on the Solution again in the Solution Explorer and choose Open Folder in Windows Explorer. In this folder you should see your Loader folder, your DLL folder, and the solution files for the workspace. Create a new folder here named Common. This is what my folder looks like:


Open the newly created Common folder and create two new files: common.h and common.cpp. You can simply create text files that you simply rename, but be careful of extension hiding settings in Windows. If the icons of the files do not change to Visual Studio icons, chances are your files are named common.txt.h and common.txt.cpp (but you only see common.cpp and common.h due to the setting to hide known extension types) and you will need to resave them through Notepad/WordPad. Go to Tools -> Folder Options in any Explorer window if you need to change this. Here is a reference screenshot of how the setting should look:


Once that is done, switch back to Visual Studio. For each of our projects, Loader and DLL, we will need to right click on the Project name and choose Add -> Existing Item. The location of the common files is above the projects directory, so navigate up one level and choose the Common folder and then both common files to add them to the project. When you are done, both projects should have the common.h and common.cpp files added to them. We will be working with only one copy of the common files now; changes made via either project reflect the changers seen in the other.

Just for reference, here is what my workspace looks like now:


Now we can add our main project file for each project. Right click on the DLL project and choose Add -> New Item. Click on the Code entry on the left pane and then choose a C++ File (.cpp) on the right pane. Give your file a name, I chose DLL.cpp and hit Add. Repeat this process for the Loader project as well. I named my file Loader.cpp there.

Now we are finally ready for some code! I am going to do things a little backwards. I’ll start out by showing the loader code, then the DLL code, and finally the code that makes up the Common functionality that is shared between both projects. The idea is to only have the bare minimal functionality needed to create a working loader and injected DLL while not being a pain to work with (this specific point will be address later).

Starting out with the loader code, Loader.cpp:

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	// Let's get rid of any extra warnings we don't need
	UNREFERENCED_PARAMETER(hInstance);
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
	UNREFERENCED_PARAMETER(nCmdShow);

	STARTUPINFOA si = {0};
	PROCESS_INFORMATION pi = {0};

	std::string pathToClient;
	std::string pathToDll;
	edx::ConfigFile cf;
	bool bExists = false;
	std::stringstream ini;

	// Try to load the configuration file
	ini << edx::GetWriteableDirectory("SilkroadFramework") << "SilkroadFramework.ini";
	cf.Open(ini.str(), false, bExists);
	if(bExists)
	{
		pathToClient = cf.Read("client");
		pathToDll = cf.Read("dll");
	}
	if(bExists == false || pathToClient.empty() || pathToDll.empty())
	{
		// Choose the injection DLL if required
		if(pathToDll.empty())
		{
			edx::FileChooser fc;
			fc.AddFilter("Dynamic-link library", "*.dll");
			fc.SetInitialDirectory(edx::GetAbsoluteDirectoryPath().c_str());
			fc.SetDialogTitle("Please choose the DLL to inject...");
			if(fc.ShowChooseFile(true) == false)
			{
				MessageBoxA(0, "The DLL selection process was canceled. The program will now exit.", "Fatal Error", MB_ICONERROR);
				return 0;
			}
			pathToDll = fc.GetSelectedFilePath();
			cf.Write("dll", pathToDll);
		}

		// Choose the game client if required
		if(pathToClient.empty())
		{
			edx::FileChooser fc;
			fc.SetDefaultFileName("sro_client.exe");
			fc.AddFilter("Executable Files", "*.exe");
			fc.SetDialogTitle("Please choose your \"sro_client.exe\" executable...");
			if(fc.ShowChooseFile(true) == false)
			{
				MessageBoxA(0, "The client selection process was canceled. The program will now exit.", "Fatal Error", MB_ICONERROR);
				return 0;
			}
			std::string title = fc.GetSelectedFileTitle();
			if(title.find("sro_client") == std::string::npos)
			{
				std::stringstream ss;
				ss << "You selected the file \"" << fc.GetSelectedFileName() << "\". Are you sure this is your \"sro_client.exe\" file?\n\nIf this file is not your \"sro_client.exe\" file, please choose No to exit the loader and try again. Otherwise please choose Yes to continue.";
				int result = MessageBoxA(0, ss.str().c_str(), "Unexpected file name detected", MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2);
				if(result == IDNO)
				{
					return 0;
				}
			}
			pathToClient = fc.GetSelectedFilePath();
			cf.Write("client", pathToClient);
		}

		// We have to restart the application now since the GetOpenFileName function messes up the working directories.
		// Unfortunately on Vista/Win7, the old method of storing the previous working directory and reseting it
		// doesn't seem to work anymore.
		MessageBoxA(0, "The directory changes have been saved. Please relaunch the program now.", "Application restart required", MB_ICONINFORMATION);
		return 0;
	}

	// Since we want to use a few functions
	WSADATA wsaData = {0};
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	bool bConnected = false;

	// Figure out which login server we should use. This is what Silkroad.exe pretty much does
	// in figuring out which last parameter to send to the client.
	std::stringstream args;
	args << "0 /18 0 ";
	for(int x = 1; x <= 4; ++x)
	{
		std::stringstream ss;
		ss << "gwgt" << x << ".joymax.com";
		if(edx::CanGetHostFromAddress(ss.str()))
		{
			SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
			hostent * host = edx::GetHost(ss.str().c_str());
			if(host)
			{
				sockaddr_in svr = {0};
				svr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
				svr.sin_family = AF_INET;
				svr.sin_port = htons(15779);
				if(connect(s, (struct sockaddr*)&svr, sizeof(svr)) == 0)
				{
					bConnected = true;
					args << (x - 1);
					break;
				}
			}
			closesocket(s);
		}
	}

	if(bConnected == false)
	{
		WSACleanup();
		MessageBoxA(0, "The address to the Silkroad servers could not be resolved. There are two causes for this error:\n\n1. The Silkroad servers are actually down and you will have to wait until they are up again.\n\n2.Your computer cannot obtain the address of the Silkroad servers. Please visit http://www.joymax.com/silkroad first and try launching the Loader again.\n\nIf that fails, try running Silkroad.exe and see if you get the Start button. If you get a Start button, try running the Loader again until it works. If you do not get a Start button, then there is an issue on your system preventing you from obtaining the addresses.", "Fatal Error", MB_ICONERROR);
		return 0;
	}
	WSACleanup();

	// Launch the client in a suspended state so we can patch it
	std::string workingDir = pathToClient.substr(0, 1 + pathToClient.find_last_of("\\/"));
	bool result = edx::CreateSuspendedProcess(pathToClient, args.str(), si, pi);
	if(result == false)
	{
		MessageBoxA(0, "Could not start \"sro_client.exe\".", "Fatal Error", MB_ICONERROR);
		return 0;
	}

	// Inject the DLL so we can have some fun
	result = (FALSE != edx::InjectDLL(pi.hProcess, pathToDll.c_str(), "OnInject", static_cast<DWORD>(edx::GetEntryPoint(pathToClient.c_str())), false));
	if(result == false)
	{
		TerminateThread(pi.hThread, 0);
		MessageBoxA(0, "Could not inject into the Silkroad client process.", "Fatal Error", MB_ICONERROR);
		return 0;
	}

	// Finally resume the client.
	ResumeThread(pi.hThread);
	ResumeThread(pi.hProcess);

	// All done!
	return 0;
}
We start out by getting a path to our writeable directory. This project is a simple SilkroadFramework, so that is the folder that will be used for our configuration files. To access this folder on any PC, simply open a folder with an address of “%appdata%/edxlabs" and you should see the SilkroadFramework folder that is created. Once we get our configuration file, we then check to see if it exists and if there are values to pull from it. If there are we pull the values and continue on.

If there are not any available values, we will create the file (done automatically by Windows when we write to the file) and allow the user to select the DLL to inject as well as the path to your Silkroad client. As mentioned before, we added in this code to make development easier. You just have to set the paths once and then you can continue to develop without having to worry about an annoying loader process. As you might notice in the application, we have to restart the program since functionality has changed a bit on Win7/Vista it seems.

After we have a valid path to the client and DLL to inject, our next task is to generate the command line to launch the client. The code you see simply checks to see if the address is obtainable and if it is, the command line is set to use that server. This is all the Silkroad loader does. We do not want to hard code a command line because when that login server is down, the users will get C9 errors and post like crazy about your program not working, as noticed in the past with older Silkroad utilities.

With the command line string built, we can now simply create our client process in a suspended state and inject our DLL. As discussed earlier, only the entry point of the process is patched in the client to let it load our DLL. All of the new logic and fun stuff comes in the DLL file. If our injection is successful, which it should be unless settings on your PC are preventing it, then we just reuse the client process and exit the loader.

That‘s all there is to our loader! That was pretty painless, right? Now we can look at our DLL, which is even simpler now. Here is the code for our DLL.cpp:

Code:
#include <windows.h>
#include "../common/common.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();
}

// 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");

	printf("Hello world from the SilkroadFramework DLL!\n");
}
If you are saying to yourself “is that all?” then the magic of DLLs and loaders has been finally unveiled. That’s the bare minimal code we need to start the Silkroad client. Of course we don’t have any cool patches to use yet, but that will come later on. For now, we just create the mutexs that the original Silkroad launcher does so the client thinks Silkroad.exe was used. We prefer this approach over patching the check as this is less work and never requires an update.

With the Loader and the DLL code shown, it is time for the Common code that each project uses. This code is pretty much all you should need to do most of your client patching! Starting out with Common.h:

Code:
#pragma once

#include <windows.h>
#include <string>

namespace edx
{
	// Injects a DLL into a process at the specified address
	BOOL InjectDLL(HANDLE hProcess, const char * dllNameToLoad, const char * funcNameToLoad, DWORD injectAddress, bool bDebugAttach);

	// Returns the entry point of an EXE
	ULONGLONG GetEntryPoint(const char * filename);

	// Returns a pointer to a hostent object for the specified address
	hostent * GetHost(const char * address);

	// Returns the absolute directory path of the executable
	std::string GetAbsoluteDirectoryPath();

	// Creates a suspended process
	bool CreateSuspendedProcess(const std::string & filename, const std::string & fileargs, STARTUPINFOA & si, PROCESS_INFORMATION & pi);

	// Returns true if the host is accessible
	bool CanGetHostFromAddress(const std::string & address);

	// Returns the writable directory for this framework.
	// Type in "%appdata%/edxLabs" to access the directory.
	std::string GetWriteableDirectory(std::string baseDir);

	// Creates a codecave
	BOOL CreateCodeCave(DWORD destAddress, BYTE patchSize, VOID (*function)(VOID));

	// Patches bytes in the current process
	BOOL WriteBytes(DWORD destAddress, LPVOID patch, DWORD numBytes);

	// Reads bytes in the current process
	BOOL ReadBytes(DWORD sourceAddress, LPVOID buffer, DWORD numBytes);

	// Reads bytes of a process
	BOOL ReadProcessBytes(HANDLE hProcess, DWORD destAddress, LPVOID buffer, DWORD numBytes);

	// Writes bytes to a process
	BOOL WriteProcessBytes(HANDLE hProcess, DWORD destAddress, LPVOID patch, DWORD numBytes);

	// Creates a console, need to call FreeConsole before exit
	VOID CreateConsole(CONST CHAR * winTitle);

	// Forward declaration for the FileChooser class data so it is not exposed.
	struct tFileChooserData;

	// File chooser class
	class FileChooser
	{
	private:
		tFileChooserData* data;

	public:
		FileChooser();
		~FileChooser();

		// Sets the initial directory the file chooser looks in.
		void SetInitialDirectory(const char * pDir);

		// Sets the default dialog title of the file chooser component.
		void SetDialogTitle(const char * pTitle);

		// Sets the default filename in the file choose dialog.
		void SetDefaultFileName(const char * pFileName);

		// Adds a file browsing filter.
		void AddFilter(const char * pFilterName, const char * pFilterExt);

		// Allow the user to select a file. (open => true for param, save => false for param)
		bool ShowChooseFile(bool open);

		// Returns the file path of the selected file.
		const char * GetSelectedFilePath();

		// Returns the file directory of the selected file.
		const char * GetSelectedFileDirectory();

		// Returns the filename of the selected file.
		const char * GetSelectedFileName();

		// Returns the file title of the selected file.
		const char * GetSelectedFileTitle();

		// Returns the file extension of the selected file.
		const char * GetSelectedFileExtension();
	};

	class ConfigFile
	{
	private:
		std::string mFileName;
		std::string mSection;

	public:
		ConfigFile();
		~ConfigFile();

		// Opens a file to work with
		void Open(std::string filename, bool useCurrentPath, bool & fileExists);

		// Set the section that the 'Write' and 'Read' functions use
		void SetSection(const std::string & section);

		// Get the section that the 'Write' and 'Read' functions use
		std::string GetSection() const;

		// Writes to the current section
		void Write(const std::string & key, const std::string & data);

		// Writes to any section
		void WriteTo(const std::string & section, const std::string & key, const std::string & data);

		// Read from the current section
		std::string Read(const std::string & key);

		// Read from any section
		std::string ReadFrom(const std::string & section, const std::string & key);
	};
}
Now for Common.cpp:

Code:
#define _CRT_SECURE_NO_WARNINGS
#include "common.h"
#include <sstream>
#include <fstream>
#include <shlwapi.h>
#include <shlobj.h>
#include <io.h>
#include <fcntl.h>

#pragma comment(lib, "ws2_32.lib")

namespace edx
{
	// Injects a DLL into a process at the specified address
	BOOL InjectDLL(HANDLE hProcess, const char * dllNameToLoad, const char * funcNameToLoad, DWORD injectAddress, bool bDebugAttach)
	{
		// # of bytes to replace
		DWORD byteCountToReplace = 6;

		// Read in the original bytes that we will restore from the injected dll
		BYTE userPatch[6] = {0};
		memset(userPatch, 0x90, 6);
		DWORD read = 0;
		ReadProcessMemory(hProcess, UlongToPtr(injectAddress), userPatch, 6, &read);

		// We want clear and byteRer to be local here
		{
			BYTE * clear = 0;

			// Allocate a patch of NOPs to write over existing code
			clear = (BYTE*)malloc(byteCountToReplace * sizeof(BYTE));
			memset(clear, 0x90, byteCountToReplace* sizeof(BYTE));

			// Clear out the original memory for a clean injection JMP to codecave
			WriteProcessBytes(hProcess, injectAddress, clear, byteCountToReplace);

			// Free the memory since we do not need it anymore
			free(clear);
		}

		//------------------------------------------//
		// Function variables.						//
		//------------------------------------------//

		// Main DLL we will need to load
		HMODULE kernel32	= NULL;

		// Main functions we will need to import
		FARPROC loadlibrary		= NULL;
		FARPROC getprocaddress	= NULL;
		FARPROC exitprocess		= NULL;

		// The workspace we will build the codecave on locally
		LPBYTE workspace		= NULL;
		DWORD workspaceIndex	= 0;

		// The memory in the process we write to
		LPVOID codecaveAddress	= NULL;
		DWORD dwCodecaveAddress = 0;

		// Strings we have to write into the process
		CHAR injectDllName[MAX_PATH + 1]	= {0};
		CHAR injectFuncName[MAX_PATH + 1]	= {0};
		CHAR injectError0[MAX_PATH + 1]		= {0};
		CHAR injectError1[MAX_PATH + 1]		= {0};
		CHAR injectError2[MAX_PATH + 1]		= {0};
		CHAR user32Name[MAX_PATH + 1]		= {0};
		CHAR msgboxName[MAX_PATH + 1]		= {0};

		// Placeholder addresses to use the strings
		DWORD user32NameAddr	= 0;
		DWORD user32Addr		= 0;
		DWORD msgboxNameAddr	= 0;
		DWORD msgboxAddr		= 0;
		DWORD dllAddr			= 0;
		DWORD dllNameAddr		= 0;
		DWORD funcNameAddr		= 0;
		DWORD error0Addr		= 0;
		DWORD error1Addr		= 0;
		DWORD error2Addr		= 0;

		DWORD offsetOrigBytes = 0;
		DWORD userVar = 0;

		// Temp variables
		DWORD dwTmpSize = 0;

		// Where the codecave execution should begin at
		DWORD codecaveExecAddr = 0;

		//------------------------------------------//
		// Variable initialization.					//
		//------------------------------------------//

		// Get the address of the main DLL
		kernel32	= LoadLibraryA("kernel32.dll");

		// Get our functions
		loadlibrary		= GetProcAddress(kernel32,	"LoadLibraryA");
		getprocaddress	= GetProcAddress(kernel32,	"GetProcAddress");
		exitprocess		= GetProcAddress(kernel32,	"ExitProcess");

		// This section will cause compiler warnings on VS8, 
		// you can upgrade the functions or ignore them

		// Build names
		_snprintf(injectDllName, MAX_PATH, "%s", dllNameToLoad);
		_snprintf(injectFuncName, MAX_PATH, "%s", funcNameToLoad);
		_snprintf(user32Name, MAX_PATH, "user32.dll");
		_snprintf(msgboxName, MAX_PATH, "MessageBoxA");

		// Build error messages
		_snprintf(injectError0, MAX_PATH, "Error");
		_snprintf(injectError1, MAX_PATH, "Could not find the DLL \"%s\"", injectDllName);
		_snprintf(injectError2, MAX_PATH, "Could not load the function \"%s\"", injectFuncName);

		// Create the workspace
		workspace = (LPBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024);

		// Allocate space for the codecave in the process
		codecaveAddress = VirtualAllocEx(hProcess, 0, 1024, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
		dwCodecaveAddress = PtrToUlong(codecaveAddress);

		//------------------------------------------//
		// Data and string writing.					//
		//------------------------------------------//

		// Write out the address for the user32 dll address
		user32Addr = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = 0;
		memcpy(workspace + workspaceIndex, &dwTmpSize, 4);
		workspaceIndex += 4;

		// Write out the address for the MessageBoxA address
		msgboxAddr = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = 0;
		memcpy(workspace + workspaceIndex, &dwTmpSize, 4);
		workspaceIndex += 4;

		// Write out the address for the injected DLL's module
		dllAddr = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = 0;
		memcpy(workspace + workspaceIndex, &dwTmpSize, 4);
		workspaceIndex += 4;

		// Write out the original bytes to be restored
		offsetOrigBytes = workspaceIndex + dwCodecaveAddress;
		memcpy(workspace + workspaceIndex, userPatch, 6);
		workspaceIndex += 6;

		// Write out the address for the injected DLL's module
		userVar = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = 0;
		memcpy(workspace + workspaceIndex, &dwTmpSize, 4);
		workspaceIndex += 4;

		// User32 Dll Name
		user32NameAddr = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = (DWORD)strlen(user32Name) + 1;
		memcpy(workspace + workspaceIndex, user32Name, dwTmpSize);
		workspaceIndex += dwTmpSize;

		// MessageBoxA name
		msgboxNameAddr = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = (DWORD)strlen(msgboxName) + 1;
		memcpy(workspace + workspaceIndex, msgboxName, dwTmpSize);
		workspaceIndex += dwTmpSize;

		// Dll Name
		dllNameAddr = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = (DWORD)strlen(injectDllName) + 1;
		memcpy(workspace + workspaceIndex, injectDllName, dwTmpSize);
		workspaceIndex += dwTmpSize;

		// Function Name
		funcNameAddr = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = (DWORD)strlen(injectFuncName) + 1;
		memcpy(workspace + workspaceIndex, injectFuncName, dwTmpSize);
		workspaceIndex += dwTmpSize;

		// Error Message 1
		error0Addr = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = (DWORD)strlen(injectError0) + 1;
		memcpy(workspace + workspaceIndex, injectError0, dwTmpSize);
		workspaceIndex += dwTmpSize;

		// Error Message 2
		error1Addr = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = (DWORD)strlen(injectError1) + 1;
		memcpy(workspace + workspaceIndex, injectError1, dwTmpSize);
		workspaceIndex += dwTmpSize;

		// Error Message 3
		error2Addr = workspaceIndex + dwCodecaveAddress;
		dwTmpSize = (DWORD)strlen(injectError2) + 1;
		memcpy(workspace + workspaceIndex, injectError2, dwTmpSize);
		workspaceIndex += dwTmpSize;

		// Pad a few INT3s after string data is written for separation
		workspace[workspaceIndex++] = 0xCC;
		workspace[workspaceIndex++] = 0xCC;
		workspace[workspaceIndex++] = 0xCC;

		// Store where the codecave execution should begin
		codecaveExecAddr = workspaceIndex + dwCodecaveAddress;

		if(bDebugAttach)
		{
			// For debugging - infinite loop, attach onto process and step over
			workspace[workspaceIndex++] = 0xEB;
			workspace[workspaceIndex++] = 0xFE;
		}

		// PUSHAD
		workspace[workspaceIndex++] = 0x60;

		//------------------------------------------//
		// User32.dll loading.						//
		//------------------------------------------//

		// User32 DLL Loading
		// PUSH 0x00000000 - Push the address of the DLL name to use in LoadLibraryA
		workspace[workspaceIndex++] = 0x68;
		memcpy(workspace + workspaceIndex, &user32NameAddr, 4);
		workspaceIndex += 4;

		// MOV EAX, ADDRESS - Move the address of LoadLibraryA into EAX
		workspace[workspaceIndex++] = 0xB8;
		memcpy(workspace + workspaceIndex, &loadlibrary, 4);
		workspaceIndex += 4;

		// CALL EAX - Call LoadLibraryA
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0xD0;

		// MessageBoxA Loading
		// PUSH 0x000000 - Push the address of the function name to load
		workspace[workspaceIndex++] = 0x68;
		memcpy(workspace + workspaceIndex, &msgboxNameAddr, 4);
		workspaceIndex += 4;

		// Push EAX, module to use in GetProcAddress
		workspace[workspaceIndex++] = 0x50;

		// MOV EAX, ADDRESS - Move the address of GetProcAddress into EAX
		workspace[workspaceIndex++] = 0xB8;
		memcpy(workspace + workspaceIndex, &getprocaddress, 4);
		workspaceIndex += 4;

		// CALL EAX - Call GetProcAddress
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0xD0;

		// MOV [ADDRESS], EAX - Save the address to our variable
		workspace[workspaceIndex++] = 0xA3;
		memcpy(workspace + workspaceIndex, &msgboxAddr, 4);
		workspaceIndex += 4;

		//------------------------------------------//
		// Injected dll loading.					//
		//------------------------------------------//

		// DLL Loading
		// PUSH 0x00000000 - Push the address of the DLL name to use in LoadLibraryA
		workspace[workspaceIndex++] = 0x68;
		memcpy(workspace + workspaceIndex, &dllNameAddr, 4);
		workspaceIndex += 4;

		// MOV EAX, ADDRESS - Move the address of LoadLibraryA into EAX
		workspace[workspaceIndex++] = 0xB8;
		memcpy(workspace + workspaceIndex, &loadlibrary, 4);
		workspaceIndex += 4;

		// CALL EAX - Call LoadLibraryA
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0xD0;

		// Error Checking
		// CMP EAX, 0
		workspace[workspaceIndex++] = 0x83;
		workspace[workspaceIndex++] = 0xF8;
		workspace[workspaceIndex++] = 0x00;

		// JNZ EIP + 0x24 to skip over error code
		workspace[workspaceIndex++] = 0x75;
		workspace[workspaceIndex++] = 0x24;

		// Error Code 1
		// MessageBox
		// PUSH 0x10 (MB_ICONHAND)
		workspace[workspaceIndex++] = 0x6A;
		workspace[workspaceIndex++] = 0x10;

		// PUSH 0x000000 - Push the address of the MessageBox title
		workspace[workspaceIndex++] = 0x68;
		memcpy(workspace + workspaceIndex, &error0Addr, 4);
		workspaceIndex += 4;

		// PUSH 0x000000 - Push the address of the MessageBox message
		workspace[workspaceIndex++] = 0x68;
		memcpy(workspace + workspaceIndex, &error1Addr, 4);
		workspaceIndex += 4;

		// Push 0
		workspace[workspaceIndex++] = 0x6A;
		workspace[workspaceIndex++] = 0x00;

		// MOV EAX, [ADDRESS] - Move the address of MessageBoxA into EAX
		workspace[workspaceIndex++] = 0xA1;
		memcpy(workspace + workspaceIndex, &msgboxAddr, 4);
		workspaceIndex += 4;

		// CALL EAX - Call MessageBoxA
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0xD0;

		// FreeLibraryAndExitThread
		// Push 0 (exit code)
		workspace[workspaceIndex++] = 0x6A;
		workspace[workspaceIndex++] = 0x00;

		// PUSH [0x000000] - Push the address of the DLL module to unload
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0x35;
		memcpy(workspace + workspaceIndex, &dllAddr, 4);
		workspaceIndex += 4;

		// MOV EAX, ADDRESS - Move the address of ExitProcess into EAX
		workspace[workspaceIndex++] = 0xB8;
		memcpy(workspace + workspaceIndex, &exitprocess, 4);
		workspaceIndex += 4;

		// CALL EAX - Call ExitProcess
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0xD0;

		//	Now we have the address of the injected DLL, so save the handle

		// MOV [ADDRESS], EAX - Save the address to our variable
		workspace[workspaceIndex++] = 0xA3;
		memcpy(workspace + workspaceIndex, &dllAddr, 4);
		workspaceIndex += 4;

		// Load the initialize function from it

		// PUSH 0x000000 - Push the address of the function name to load
		workspace[workspaceIndex++] = 0x68;
		memcpy(workspace + workspaceIndex, &funcNameAddr, 4);
		workspaceIndex += 4;

		// Push EAX, module to use in GetProcAddress
		workspace[workspaceIndex++] = 0x50;

		// MOV EAX, ADDRESS - Move the address of GetProcAddress into EAX
		workspace[workspaceIndex++] = 0xB8;
		memcpy(workspace + workspaceIndex, &getprocaddress, 4);
		workspaceIndex += 4;

		// CALL EAX - Call GetProcAddress
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0xD0;

		// Error Checking
		// CMP EAX, 0
		workspace[workspaceIndex++] = 0x83;
		workspace[workspaceIndex++] = 0xF8;
		workspace[workspaceIndex++] = 0x00;

		// JNZ EIP + 0x24 to skip error code
		workspace[workspaceIndex++] = 0x75;
		workspace[workspaceIndex++] = 0x1E;

		// Error Code 2
		// MessageBox
		// PUSH 0x10 (MB_ICONHAND)
		workspace[workspaceIndex++] = 0x6A;
		workspace[workspaceIndex++] = 0x10;

		// PUSH 0x000000 - Push the address of the MessageBox title
		workspace[workspaceIndex++] = 0x68;
		memcpy(workspace + workspaceIndex, &error0Addr, 4);
		workspaceIndex += 4;

		// PUSH 0x000000 - Push the address of the MessageBox message
		workspace[workspaceIndex++] = 0x68;
		memcpy(workspace + workspaceIndex, &error2Addr, 4);
		workspaceIndex += 4;

		// Push 0
		workspace[workspaceIndex++] = 0x6A;
		workspace[workspaceIndex++] = 0x00;

		// MOV EAX, ADDRESS - Move the address of MessageBoxA into EAX
		workspace[workspaceIndex++] = 0xA1;
		memcpy(workspace + workspaceIndex, &msgboxAddr, 4);
		workspaceIndex += 4;

		// CALL EAX - Call MessageBoxA
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0xD0;

		// ExitProcess
		// Push 0 (exit code)
		workspace[workspaceIndex++] = 0x6A;
		workspace[workspaceIndex++] = 0x00;

		// MOV EAX, ADDRESS - Move the address of ExitProcess into EAX
		workspace[workspaceIndex++] = 0xB8;
		memcpy(workspace + workspaceIndex, &exitprocess, 4);
		workspaceIndex += 4;

		// CALL EAX - Call ExitProcess
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0xD0;

		//	Now that we have the address of the function, we cam call it, 
		// if there was an error, the messagebox would be called as well.

		// Push orig bytes address
		workspace[workspaceIndex++] = 0x68;
		memcpy(workspace + workspaceIndex, &offsetOrigBytes, sizeof(offsetOrigBytes));
		workspaceIndex += 4;

		// Push the address to restore bytes to
		workspace[workspaceIndex++] = 0x68;
		memcpy(workspace + workspaceIndex, &injectAddress, sizeof(injectAddress));
		workspaceIndex += 4;

		// CALL EAX - Call Initialize
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0xD0;

		// Add ESP, 8 2 parameters
		workspace[workspaceIndex++] = 0x83;
		workspace[workspaceIndex++] = 0xC4;
		workspace[workspaceIndex++] = 0x08;
		workspace[workspaceIndex++] = 0x90;

		// Restore registers with POPAD
		workspace[workspaceIndex++] = 0x61;

		// Pad a few NOPS before user code
		workspace[workspaceIndex++] = 0x90;
		workspace[workspaceIndex++] = 0x90;

		// Pop off into local variable
		workspace[workspaceIndex++] = 0x8F;
		workspace[workspaceIndex++] = 0x05;
		memcpy(workspace + workspaceIndex, &userVar, sizeof(userVar));
		workspaceIndex += 4;

		// Subtract 5 from local var
		workspace[workspaceIndex++] = 0x83;
		workspace[workspaceIndex++] = 0x2D;
		memcpy(workspace + workspaceIndex, &userVar, sizeof(userVar));
		workspaceIndex += 4;
		workspace[workspaceIndex++] = 0x05;

		// Pop off into local variable
		workspace[workspaceIndex++] = 0xFF;
		workspace[workspaceIndex++] = 0x35;
		memcpy(workspace + workspaceIndex, &userVar, sizeof(userVar));
		workspaceIndex += 4;

		// Return back to where we should be
		workspace[workspaceIndex++] = 0xC3;

		// Try to write the final codecave patch to the process first.
		// By doing this, worst case event is we add code to the process that is not used
		if(!WriteProcessBytes(hProcess, PtrToUlong(codecaveAddress), workspace, workspaceIndex))
		{
			HeapFree(GetProcessHeap(), 0, workspace);
			return FALSE;
		}

		// Now that the patch is written into the process, we need to make the process call it
		{
			// Make the program patch the starting address to inject the DLL
			BYTE patch2[5] = {0xE8, 0x00, 0x00, 0x00, 0x00};

			// Calculate the JMP offset ( + 5 for the FAR JMP we are adding)
			DWORD toCC = codecaveExecAddr - (injectAddress + 5);

			// Free the workspace memory
			HeapFree(GetProcessHeap(), 0, workspace);

			// Write the offset to the patch
			memcpy(patch2 + 1, &toCC, sizeof(toCC));

			// Make the patch that will JMP to the codecave
			if(!WriteProcessBytes(hProcess, injectAddress, patch2, 5))
			{
				return FALSE;
			}
		}

		// Success!
		return TRUE;
	}

	// Returns the entry point of an EXE
	ULONGLONG GetEntryPoint(const char * filename)
	{
		// Macro for adding pointers/DWORDs together without C arithmetic interfering 
		#define MakePtr( cast, ptr, addValue ) (cast)( (DWORD)(ptr)+(DWORD)(addValue))

		ULONGLONG OEP = 0;
		HANDLE hFile = NULL;
		HANDLE hFileMapping = NULL;
		PIMAGE_DOS_HEADER dosHeader = {0};
		PBYTE g_pMappedFileBase = NULL;
		PIMAGE_FILE_HEADER pImgFileHdr = NULL;

		hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
		if(hFile == INVALID_HANDLE_VALUE)
			return 0;

		hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
		if(hFileMapping == 0)
		{
			CloseHandle(hFile);
			return 0;
		}

		g_pMappedFileBase = (PBYTE)MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
		if(g_pMappedFileBase == 0)
		{
			CloseHandle(hFileMapping);
			CloseHandle(hFile);
			return 0;
		}

		dosHeader = (PIMAGE_DOS_HEADER)g_pMappedFileBase;
		pImgFileHdr = (PIMAGE_FILE_HEADER)g_pMappedFileBase;
		if(dosHeader->e_magic == IMAGE_DOS_SIGNATURE)
		{
			PIMAGE_NT_HEADERS pNTHeader = MakePtr( PIMAGE_NT_HEADERS, dosHeader, dosHeader->e_lfanew);
			PIMAGE_NT_HEADERS64 pNTHeader64 = (PIMAGE_NT_HEADERS64)pNTHeader;

			// First, verify that the e_lfanew field gave us a reasonable pointer, then verify the PE signature.
			if(IsBadReadPtr(pNTHeader, sizeof(pNTHeader->Signature)) || pNTHeader->Signature != IMAGE_NT_SIGNATURE)
			{
				UnmapViewOfFile(g_pMappedFileBase);
				CloseHandle(hFileMapping);
				CloseHandle(hFile);
				return 0;
			}

			if(pNTHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
				OEP = pNTHeader64->OptionalHeader.AddressOfEntryPoint + pNTHeader64->OptionalHeader.ImageBase;
			else
				OEP = pNTHeader->OptionalHeader.AddressOfEntryPoint + pNTHeader->OptionalHeader.ImageBase;
		}
		UnmapViewOfFile(g_pMappedFileBase);
		CloseHandle(hFileMapping);
		CloseHandle(hFile);
		return OEP;
		#undef MakePtr
	}

	// Returns a pointer to a hostent object for the specified address
	hostent * GetHost(const char * address)
	{
		if(inet_addr(address) == INADDR_NONE)
		{
			return gethostbyname(address);
		}
		else
		{
			unsigned long addr = 0;
			addr = inet_addr(address);
			return gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
		}
	}

	// Returns the absolute directory path of the executable
	std::string GetAbsoluteDirectoryPath()
	{
		char tmpDirectory1[MAX_PATH + 1] = {0};
		char tmpDirectory2[MAX_PATH + 1] = {0};
		GetCurrentDirectoryA(MAX_PATH, tmpDirectory1);
		GetFullPathNameA(tmpDirectory1, MAX_PATH, tmpDirectory2, 0);
		return (std::string(tmpDirectory2) + std::string("\\"));
	}

	// Creates a suspended process
	bool CreateSuspendedProcess(const std::string & filename, const std::string & fileargs, STARTUPINFOA & si, PROCESS_INFORMATION & pi)
	{
		si.cb = sizeof(STARTUPINFOA);

		std::stringstream cmdLine;
		cmdLine << "\"" << filename << "\" " << fileargs;

		BOOL result = CreateProcessA(0, (LPSTR)cmdLine.str().c_str(), 0, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
		return (result != 0);
	}

	// Returns true if the host is accessible
	bool CanGetHostFromAddress(const std::string & address)
	{
		return (GetHost(address.c_str()) != NULL);
	}

	// Returns the writable directory for this framework.
	// Type in "%appdata%/edxLabs" to access the directory.
	std::string GetWriteableDirectory(std::string baseDir)
	{
		std::stringstream ss;
		char strPath[MAX_PATH + 1] = {0};
		if(SHGetSpecialFolderPathA(0, strPath, CSIDL_APPDATA, FALSE) == FALSE)
		{
			MessageBoxA(0, "Unable to retrieve the path to the special folder AppData", "Error", MB_ICONERROR);
			throw 0;
		}
		else
		{
			ss << strPath << "\\edxLabs";
			CreateDirectoryA(ss.str().c_str(), 0);
			ss << "\\" << baseDir;
			CreateDirectoryA(ss.str().c_str(), 0);
			ss << "\\";
		}
		return ss.str();
	}

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

	// Private data for the FileChooser class
	struct tFileChooserData
	{
		// File chooser struct
		OPENFILENAMEA fn;

		// Buffers for the file chooser 
		char setDirectory[2048];
		char setDialogTitle[2048];
		char setDefFileName[2048];
		char setFilter[2048];

		// User buffers to store data
		char filepath[2048];
		char filetitle[2048];
		char filename[2048];
		char fileext[2048];
		char filedir[2048];

		// Index in the filter string since we have to manually create it
		int filterIndex;

		// Can we use the ShowChooseFile function?
		bool canShow;
	};

	// Constructor
	FileChooser::FileChooser()
	{
		data = new tFileChooserData;
		memset(data, 0, sizeof(tFileChooserData));

		// Have to set this for the struct
		data->fn.lStructSize = sizeof(OPENFILENAME);

		// We can select a file
		data->canShow = true;
	}

	// Destructor
	FileChooser::~FileChooser()
	{
		delete data;
	}

	// Sets the initial directory the file chooser looks in
	void FileChooser::SetInitialDirectory(const char * pDir)
	{
		_snprintf(data->setDirectory, 2047, "%s", pDir);
	}

	// Sets the default dialog title of the file chooser
	void FileChooser::SetDialogTitle(const char * pTitle)
	{
		_snprintf(data->setDialogTitle, 2047, "%s", pTitle);
	}

	// Sets the default data->filename in the file choose dialog
	void FileChooser::SetDefaultFileName(const char * pFileName)
	{
		_snprintf(data->setDefFileName, 2047, "%s", pFileName);
	}

	// Adds a file browsing filter
	// pFilterName - Name of the extension to display, i.e. "Executable Files"
	// pFilterExt - Extension of the filter, i.e. "*.exe"
	void FileChooser::AddFilter(const char * pFilterName, const char * pFilterExt)
	{
		// First part is the name of the filter
		_snprintf(data->setFilter + data->filterIndex, 2047 - data->filterIndex, "%s", pFilterName);
		data->filterIndex += (int)strlen(pFilterName);

		// Separate with a NULL terminator
		data->setFilter[data->filterIndex++] = '\0';

		// Second part is the extension of the filter, *.EXTENSION
		_snprintf(data->setFilter + data->filterIndex, 2047 - data->filterIndex, "%s", pFilterExt);
		data->filterIndex += (int)strlen(pFilterExt);

		// Separate with a NULL terminator
		data->setFilter[data->filterIndex++] = '\0';
	}

	// Will return a string in this format: "Filename"
	const char * FileChooser::GetSelectedFileTitle()
	{
		return data->filetitle;
	}

	// Will return a string in this format: "Filename.Extension"
	const char * FileChooser::GetSelectedFileName()
	{
		return data->filename;
	}

	// Will return a string in this format: "Drive:\Path\To\File\Filename.Extension"
	const char * FileChooser::GetSelectedFilePath()
	{
		return data->filepath;
	}

	// Will return a string in this format: "Drive:\Path\To\File\"
	const char * FileChooser::GetSelectedFileDirectory()
	{
		return data->filedir;
	}

	// Will return a string in this format: "Extension"
	const char * FileChooser::GetSelectedFileExtension()
	{
		return data->fileext;
	}

	// Allow the user to select a file, returns true on success and false on failure
	bool FileChooser::ShowChooseFile(bool open)
	{
		// If we cannot show the file dialog, return failure
		if(!data->canShow)
			return false;

		// Store the current directory before we change it
		char curDir[256] = {0};
		GetCurrentDirectoryA(255, curDir);

		// Have to set this for the struct
		data->fn.lStructSize = sizeof(OPENFILENAME);

		// Default directory
		data->fn.lpstrInitialDir = data->setDirectory;

		// Finish the file filter with the two NULLS it needs at the end
		data->setFilter[data->filterIndex++] = '\0';
		data->setFilter[data->filterIndex++] = '\0';
		data->fn.lpstrFilter = data->setFilter;

		// Tell the chooser to use our buffer
		data->fn.lpstrFile = data->setDefFileName;

		// Max size of buffer
		data->fn.nMaxFile = 2047;

		// Tell the chooser to use our buffer
		data->fn.lpstrFileTitle = data->filename;

		// Max size of buffer
		data->fn.nMaxFileTitle = 2047;

		// Title we wish to display
		data->fn.lpstrTitle = data->setDialogTitle;

		// Display the chooser!
		if(open)
		{
			// Flags for selecting a file
			data->fn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;

			if(!GetOpenFileNameA(&data->fn))
			{
				// Restore the old directory
				SetCurrentDirectoryA(curDir);

				// Error or the user canceled
				return FALSE;
			}
		}
		else
		{
			if(!GetSaveFileNameA(&data->fn))
			{
				// Restore the old directory
				SetCurrentDirectoryA(curDir);

				// Error or the user canceled
				return FALSE;
			}
		}

		// Restore the old directory
		SetCurrentDirectoryA(curDir);

		// Copy the file path from the struct into the buffer
		_snprintf(data->filepath, 2047, data->fn.lpstrFile);

		// Store the file directory
		_snprintf(data->filedir, 2047, "%s", data->fn.lpstrFile);

		// Loop though the string backwards
		for(int x = (int)strlen(data->filedir) - 1; x >= 0; --x)
		{
			// Stop at the first directory separator
			if(data->filedir[x] == '\\')
			{
				// Remove everything after it by setting a NULL terminator in the string
				data->filedir[x + 1] = 0;
				break;
			}
		}

		// Store the filetitle
		_snprintf(data->filetitle, 2047, "%s", data->filename);

		// Loop though the string forwards
		for(int x = (int)strlen(data->filetitle) - 1; x > 0; --x)
		{
			// Stop at the last extension separator
			if(data->filetitle[x] == '.')
			{
				// Remove it and everything past it
				data->filetitle[x] = 0;
				break;
			}
		}

		// Temp buffer to store the filename with extension
		char temp[2048] = {0};
		_snprintf(temp, 2047, "%s", GetSelectedFileName());
		strcpy(data->fileext, temp + strlen(data->filetitle) + 1);

		// We can no longer use this function again
		data->canShow = false;

		// Success
		return TRUE;
	}

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

	ConfigFile::ConfigFile()
	{
		// Set a default section name
		mSection = "Default";
	}

	ConfigFile::~ConfigFile()
	{
	}

	// Opens a file to work with
	void ConfigFile::Open(std::string filename, bool useCurrentPath, bool & fileExists)
	{
		// If we do not need to get the current file path, simply assign the path
		if(!useCurrentPath)
		{
			mFileName = filename;
		}
		// Otherwise build the path
		else
		{
			// Holds the current directory
			char curDir[MAX_PATH + 1] = {0};
			char fullDir[MAX_PATH + 1] = {0};

			// Store the current directory
			GetCurrentDirectoryA(MAX_PATH, curDir);

			// Store the full path to the current directory
			GetFullPathNameA(curDir, MAX_PATH, fullDir, 0);

			// Build the filename now
			mFileName = fullDir;
			mFileName.append("\\");
			mFileName.append(filename);
		}

		// If there is no filename, the file does not exist
		if(!mFileName.size())
		{
			fileExists = false;
		}
		else
		{
			// File handle
			std::ifstream inFile;

			// Try to open the file
			inFile.open(filename.c_str());

			// Check to see if it is open or not
			fileExists = inFile.is_open();

			// Close the file
			inFile.close();
		}

		// Force the system to read the mapping into shared memory, so that future invocations of the application will see it without the user having to reboot the system 
		WritePrivateProfileStringA(NULL, NULL, NULL, filename.c_str());
	}

	// Set the section that the 'Write' and 'Read' functions use
	void ConfigFile::SetSection(const std::string& section)
	{
		mSection = section;
	}

	// Get the section that the 'Write' and 'Read' functions use
	std::string ConfigFile::GetSection() const
	{
		return mSection;
	}

	// Writes to the current section
	void ConfigFile::Write(const std::string& key, const std::string& data)
	{
		WritePrivateProfileStringA(mSection.c_str(), key.c_str(), data.c_str(), mFileName.c_str());
	}

	// Writes to any section
	void ConfigFile::WriteTo(const std::string& section, const std::string& key, const std::string& data)
	{
		WritePrivateProfileStringA(section.c_str(), key.c_str(), data.c_str(), mFileName.c_str());
	}

	// Read from the current section
	std::string ConfigFile::Read(const std::string& key)
	{
		static char buffer[131072] = {0};
		SecureZeroMemory(buffer, 131072);
		GetPrivateProfileStringA(mSection.c_str(), key.c_str(), NULL, buffer, 131071, mFileName.c_str());
		return std::string(buffer);
	}

	// Read from any section
	std::string ConfigFile::ReadFrom(const std::string& section, const std::string& key)
	{
		static char buffer[131072] = {0};
		SecureZeroMemory(buffer, 131072);
		GetPrivateProfileStringA(section.c_str(), key.c_str(), NULL, buffer, 131071, mFileName.c_str());
		return std::string(buffer);
	}

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

	// Writes bytes to a process
	BOOL WriteProcessBytes(HANDLE hProcess, DWORD destAddress, LPVOID patch, DWORD numBytes)
	{
		DWORD oldProtect = 0;	// Old protection on page we are writing to
		DWORD bytesRet = 0;		// # of bytes written
		BOOL status = TRUE;		// Status of the function

		// Change page protection so we can write executable code
		if(!VirtualProtectEx(hProcess, UlongToPtr(destAddress), numBytes, PAGE_EXECUTE_READWRITE, &oldProtect))
			return FALSE;

		// Write out the data
		if(!WriteProcessMemory(hProcess, UlongToPtr(destAddress), patch, numBytes, &bytesRet))
			status = FALSE;

		// Compare written bytes to the size of the patch
		if(bytesRet != numBytes)
			status = FALSE;

		// Restore the old page protection
		if(!VirtualProtectEx(hProcess, UlongToPtr(destAddress), numBytes, oldProtect, &oldProtect))
			status = FALSE;

		// Make sure changes are made!
		if(!FlushInstructionCache(hProcess, UlongToPtr(destAddress), numBytes))
			status = FALSE;

		// Return the final status, note once we set page protection, we don't want to prematurely return
		return status;
	}

	// Reads bytes of a process
	BOOL ReadProcessBytes(HANDLE hProcess, DWORD destAddress, LPVOID buffer, DWORD numBytes)
	{
		DWORD oldProtect = 0;	// Old protection on page we are writing to
		DWORD bytesRet = 0;		// # of bytes written
		BOOL status = TRUE;		// Status of the function

		// Change page protection so we can read bytes
		if(!VirtualProtectEx(hProcess, UlongToPtr(destAddress), numBytes, PAGE_READONLY, &oldProtect))
			return FALSE;

		// Read in the data
		if(!ReadProcessMemory(hProcess, UlongToPtr(destAddress), buffer, numBytes, &bytesRet))
			status = FALSE;

		// Compare written bytes to the size of the patch
		if(bytesRet != numBytes)
			status = FALSE;

		// Restore the old page protection
		if(!VirtualProtectEx(hProcess, UlongToPtr(destAddress), numBytes, oldProtect, &oldProtect))
			status = FALSE;

		// Return the final status, note once we set page protection, we don't want to prematurely return
		return status;
	}

	// Patches bytes in the current process
	BOOL WriteBytes(DWORD destAddress, LPVOID patch, DWORD numBytes)
	{
		// Store old protection of the memory page
		DWORD oldProtect = 0;

		// Store the source address
		DWORD srcAddress = PtrToUlong(patch);

		// Result of the function
		BOOL result = TRUE;

		// Make sure page is writable
		result = result && VirtualProtect(UlongToPtr(destAddress), numBytes, PAGE_EXECUTE_READWRITE, &oldProtect);

		// Copy over the patch
		memcpy(UlongToPtr(destAddress), patch, numBytes);

		// Restore old page protection
		result = result && VirtualProtect(UlongToPtr(destAddress), numBytes, oldProtect, &oldProtect);

		// Make sure changes are made
		result = result && FlushInstructionCache(GetCurrentProcess(), UlongToPtr(destAddress), numBytes); 

		// Return the result
		return result;
	}

	// Reads bytes in the current process
	BOOL ReadBytes(DWORD sourceAddress, LPVOID buffer, DWORD numBytes)
	{
		// Store old protection of the memory page
		DWORD oldProtect = 0;

		// Store the source address
		DWORD dstAddress = PtrToUlong(buffer);

		// Result of the function
		BOOL result = TRUE;

		// Make sure page is writable
		result = result && VirtualProtect(UlongToPtr(sourceAddress), numBytes, PAGE_EXECUTE_READWRITE, &oldProtect);

		// Copy over the patch
		memcpy(buffer, UlongToPtr(sourceAddress), numBytes);

		// Restore old page protection
		result = result && VirtualProtect(UlongToPtr(sourceAddress), numBytes, oldProtect, &oldProtect);

		// Return the result
		return result;
	}

	// Creates a codecave
	BOOL CreateCodeCave(DWORD destAddress, BYTE patchSize, VOID (*function)(VOID))
	{
		// Offset to make the codecave at
		DWORD offset = 0;

		// Bytes to write
		BYTE patch[5] = {0};

		// Number of extra nops we need
		BYTE nopCount = 0;

		// NOP buffer
		static BYTE nop[0xFF] = {0};

		// Is the buffer filled?
		static BOOL filled = FALSE;

		// Need at least 5 bytes to be patched
		if(patchSize < 5)
			return FALSE;

		// Calculate the code cave
		offset = (PtrToUlong(function) - destAddress) - 5;

		// Construct the patch to the function call
		patch[0] = 0xE8;
		memcpy(patch + 1, &offset, sizeof(DWORD));
		WriteBytes(destAddress, patch, 5);

		// We are done if we do not have NOPs
		nopCount = patchSize - 5;
		if(nopCount == 0)
			return TRUE;

		// Fill in the buffer
		if(filled == FALSE)
		{
			memset(nop, 0x90, 0xFF);
			filled = TRUE;
		}

		// Make the patch now
		WriteBytes(destAddress + 5, nop, nopCount);

		// Success
		return TRUE;
	}

	// Creates a console, need to call FreeConsole before exit
	VOID CreateConsole(CONST CHAR * winTitle)
	{
		// http://www.gamedev.net/community/forums/viewreply.asp?ID=1958358
		INT hConHandle = 0;
		HANDLE lStdHandle = 0;
		FILE *fp = 0 ;

		// Allocate the console
		AllocConsole();

		// Set a title if we need one
		if(winTitle) SetConsoleTitleA(winTitle);

		// redirect unbuffered STDOUT to the console
		lStdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
		hConHandle = _open_osfhandle(PtrToUlong(lStdHandle), _O_TEXT);
		fp = _fdopen(hConHandle, "w");
		*stdout = *fp;
		setvbuf(stdout, NULL, _IONBF, 0);

		// redirect unbuffered STDIN to the console
		lStdHandle = GetStdHandle(STD_INPUT_HANDLE);
		hConHandle = _open_osfhandle(PtrToUlong(lStdHandle), _O_TEXT);
		fp = _fdopen(hConHandle, "r");
		*stdin = *fp;
		setvbuf(stdin, NULL, _IONBF, 0);

		// redirect unbuffered STDERR to the console
		lStdHandle = GetStdHandle(STD_ERROR_HANDLE);
		hConHandle = _open_osfhandle(PtrToUlong(lStdHandle), _O_TEXT);
		fp = _fdopen(hConHandle, "w");
		*stderr = *fp;
		setvbuf(stderr, NULL, _IONBF, 0);
	}
}
I’m not going to cover all of the functions we have, so you can look those over at your leisure. More information on how to use them will come in later tutorials when we make use of them. For now though, I wanted you to have everything you need to get started on your own.

As mentioned before, I said we would be adding some extra code to make life easier. That is what the code for the Config and FileChooser classes represent. Rather than having to create files ourselves and telling end users to do the same, we can make use of this code to efficiently develop and test our works. At any time if we want to choose a different client or DLL, we would need to modify or delete the SilkroadFramework.ini configuration file located in the “%appdata%/edxLabs/SilkroadFramework” folder.

Now, after you copy the code into the files for your project, you should be able to compile and run. Make sure o set the active startup project to the Loader as we can’t run DLLs from Visual Studio. To do this, right click on the Loader project and choose Set as StartUp Project. When the program first runs, you will need to select your DLL file. This will be located in the Debug folder of the Solution, not the actual Project’s Debug folder. Once you have selected the DLL, you will next need to select your Silkroad client, sro_client.exe. Do so and then rerun the loader after the paths are set.

Silkroad should startup and bring you to the login screen just like having launched Silkroad.exe would have. Congratulations! You made it through one of the most perceived hardest tasks in getting started with Silkroad development. Go ahead and close the game unless you plan on logging in or doing other things.

There is one last thing I need to mention. Before you get too far with playing with anything, please checkout my article: The Beginners Guide to Codecaves. I wrote this article a few years back and I highly recommend users read this to get a head start on some of the things we will be doing in the future. The more work you are willing to put into this stuff and learn, the better you will get at it.

V. Conclusion

By this point, you should have a basic understanding of how a Loader and injected DLL work. As you might have noticed, there is nothing really special about the process except for the method that I choose to use to inject the DLL itself, which is a bit advanced compared to traditional means, but a reliable approach that will serve us well.

With the ability to the start the client via your own Loader and inject a custom DLL as well as having access to some basic functions to make patches and codecaves, you know have the basic tools required to have some real fun with the Silkroad client. That fun will come later, but rest assured, it is on the way.

That wraps up this article. We needed to establish a common framework to use in our future endeavors, so that was the task we accomplished here. I hope you found this guide informational and beneficial. Stay tuned for future guides!

Drew “pushedx” Benton
edxLabs



pushedx is offline  
Thanks
92 Users
Old 06/23/2009, 12:19   #2
 
elite*gold: 20
Join Date: Nov 2008
Posts: 746
Received Thanks: 147
Good Job


x_king_x is offline  
Old 06/23/2009, 12:44   #3
 
elite*gold: 20
Join Date: Mar 2008
Posts: 3,940
Received Thanks: 2,119
Nice guide pushedx keep them coming, I'll have a butchers at this later.
theoneofgod is offline  
Old 06/23/2009, 21:15   #4
 
elite*gold: 20
Join Date: Jul 2007
Posts: 2,215
Received Thanks: 1,358
Thx for sharing this.

I recently starting programming, this guide is really helpfull for me.


strukel is offline  
Old 06/24/2009, 23:24   #5
 
elite*gold: 0
Join Date: Mar 2007
Posts: 39
Received Thanks: 4
jw, do u mind if this is posted on other forums? just asking permission 1st just in case

btw excellent guide and its very thorough ^^
Arabian1771 is offline  
Old 06/24/2009, 23:29   #6
 
elite*gold: 260
Join Date: Aug 2008
Posts: 526
Received Thanks: 3,321
Thanks for asking, I don't mind as long as you link back to elitepvpers or tell the guide originally came from here in some way or another.
pushedx is offline  
Thanks
1 User
Old 06/25/2009, 14:50   #7
 
elite*gold: 0
Join Date: May 2008
Posts: 259
Received Thanks: 94
Dude why elitepvpers? I think Stealthex.org is a better place for programmers.
soadmania is offline  
Old 06/25/2009, 16:18   #8
 
elite*gold: 0
Join Date: May 2008
Posts: 162
Received Thanks: 258
Quote:
Originally Posted by soadmania View Post
Dude why elitepvpers? I think Stealthex.org is a better place for programmers.
stealthex's best is epvp's worst. :P

remember:

"Your best is our worst."
JasonWalker is offline  
Old 06/25/2009, 16:34   #9
 
elite*gold: 0
Join Date: Jun 2008
Posts: 188
Received Thanks: 96
Quote:
Originally Posted by soadmania View Post
Dude why elitepvpers? I think Stealthex.org is a better place for programmers.
Partially agree with that. However, epvp has a lot more members (but a lot less programmers).

Actually, I don't care where he posts his guides, they're awesome wherever they're posted ;p
maxbot is offline  
Thanks
3 Users
Old 06/27/2009, 12:32   #10
 
elite*gold: 20
Join Date: Jul 2007
Posts: 2,215
Received Thanks: 1,358
#stick
strukel is offline  
Old 01/08/2010, 10:34   #11
 
elite*gold: 0
Join Date: Dec 2008
Posts: 316
Received Thanks: 44
I want to make it compatible with rsro ip: 212.24.57.34 - 212.24.57.36. But is CanGetHostFromAddress unless?
XchangliiX is offline  
Old 06/04/2010, 12:19   #12
 
elite*gold: 0
Join Date: May 2010
Posts: 1
Received Thanks: 0
How can I add multiclient option to this Loader?
vcb is offline  
Old 07/29/2010, 15:21   #13
 
elite*gold: 0
Join Date: Dec 2007
Posts: 2,400
Received Thanks: 1,514
Typo in IV:

"... If the icons of the files do not change to Visual Studio icons, chances are your files are named common.txt.h and common.txt.cpp (but you only see common.cpp and common.h due to the setting to hide known extension types) ..."

It should be .h.txt and .cpp.txt. Not really important but some people might ask questions about it.
lesderid is offline  
Old 08/04/2010, 20:09   #14
 
elite*gold: 0
Join Date: Aug 2010
Posts: 54
Received Thanks: 7
Nice TuT.
Bl0wFish is offline  
Old 08/13/2010, 03:22   #15
 
elite*gold: 0
Join Date: May 2007
Posts: 304
Received Thanks: 102
i get an error saying incorrect version of isilk.dll


baracoudaking is offline  
Reply


Similar Threads
[Guide] Creating your own ingame Silkroad GUI
04/25/2019 - SRO Guides & Templates - 26 Replies
As I already said here, in the last few weeks (actually months, haven't been working actively on it), I've been creating a generic ingame GUI, which is fully movable/draggable around, features control support and with which you can make your own modifications to the game user interface easily. I changed my mind about releasing the full open-source application which features a totally Silkroad-like interface - because the code is too much complicated and it definetly requires a rewrite (I didn't...
[TUTORIAL] Creating a simple DLL Cheat/Hack
11/06/2018 - Kal Hacks, Bots, Cheats & Exploits - 160 Replies
HOW TO CREATE YOUR OWN DLL HACK Hello guys, In recent days, I recieved many questions about how to use the pointers posted in one specific thread. So here is guide for creating the basic Proxy-DLL skeleton + hack. I will try to explain it to details. Requirements 1] Some C++ and UCE (memory and such stuff) knowladge 2] Some Time
[Guide] A Simple Silkroad Proxy Reference
08/04/2010 - SRO Coding Corner - 17 Replies
This guide is similar to to my Loader/Injected DLL guide. It's a complete project, but by itself, it is not enough to fully utilize at this stage. However, there is so much to this topic that I have to break down everything into different parts first. This guide will be the first in a small part series that shows how a Silkroad proxy is made. Future guides will complete the project by showing how to do the hook for a client, as well as a simple clientless that uses the proxy. Right now, the...
[Intermediate] Creating a strong but simple cipher
08/31/2008 - CO2 Programming - 9 Replies
Basically, here's the idea, we have a 'key' that contains every value a byte supports (0 to 255). When you encrypt a byte for instance 171 (0xAB) it creates an "x" using the first 4 bits of the byte, and "y" using the last for bits of the byte Value = 171 (0xAB) X = 10 (0xA) Y = 11 (0xB) Then in the output of the encrypt routine, it it'll fill that index as Key Here's an illustration to make it simpler; http://img120.imageshack.us/img120/3282/cipheran4.gif



All times are GMT +2. The time now is 03:08.


Powered by vBulletin®
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.

BTC: 3KeUpz52VCbhmLwuwydqxu6U1xsgbT8YT5
ETH: 0xc6ec801B7563A4376751F33b0573308aDa611E05

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