diff --git a/ProtoProxy/Connection.cpp b/ProtoProxy/Connection.cpp new file mode 100644 index 000000000..fe37d5003 --- /dev/null +++ b/ProtoProxy/Connection.cpp @@ -0,0 +1,203 @@ + +// Connection.cpp + +// Interfaces to the cConnection class representing a single pair of connected sockets + +#include "Globals.h" +#include "Connection.h" +#include "Server.h" + + + + + +cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) : + m_Server(a_Server), + m_LogFile(NULL), + m_ClientSocket(a_ClientSocket), + m_ServerSocket(-1), + m_BeginTick(clock()) +{ + AString fnam; + Printf(fnam, "Log_%d.log", (int)time(NULL)); + m_LogFile = fopen(fnam.c_str(), "w"); + Log("Log file created"); +} + + + + + +cConnection::~cConnection() +{ + fclose(m_LogFile); +} + + + + + +void cConnection::Run(void) +{ + if (!ConnectToServer()) + { + Log("Cannot connect to server; aborting"); + return; + } + + while (true) + { + fd_set ReadFDs; + FD_ZERO(&ReadFDs); + FD_SET(m_ServerSocket, &ReadFDs); + FD_SET(m_ClientSocket, &ReadFDs); + int res = select(2, &ReadFDs, NULL, NULL, NULL); + if (res <= 0) + { + printf("select() failed: %d; aborting client", WSAGetLastError()); + return; + } + if (FD_ISSET(m_ServerSocket, &ReadFDs)) + { + if (!RelayFromServer()) + { + return; + } + } + if (FD_ISSET(m_ClientSocket, &ReadFDs)) + { + if (!RelayFromClient()) + { + return; + } + } + } +} + + + + + +void cConnection::Log(const char * a_Format, ...) +{ + va_list args; + va_start(args, a_Format); + AString msg; + AppendVPrintf(msg, a_Format, args); + va_end(args); + AString FullMsg; + Printf(FullMsg, "[%5.3f] %s\n", GetRelativeTime(), msg.c_str()); + + cCSLock Lock(m_CSLog); + fputs(FullMsg.c_str(), m_LogFile); +} + + + + + +void cConnection::DataLog(const void * a_Data, int a_Size, const char * a_Format, ...) +{ + va_list args; + va_start(args, a_Format); + AString msg; + AppendVPrintf(msg, a_Format, args); + va_end(args); + AString FullMsg; + AString Hex; + Printf(FullMsg, "[%5.3f] %s\n%s", GetRelativeTime(), msg.c_str(), CreateHexDump(Hex, a_Data, a_Size, 16).c_str()); + + cCSLock Lock(m_CSLog); + fputs(FullMsg.c_str(), m_LogFile); +} + + + + + +bool cConnection::ConnectToServer(void) +{ + m_ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_ServerSocket == INVALID_SOCKET) + { + return false; + } + sockaddr_in localhost; + localhost.sin_family = AF_INET; + localhost.sin_port = htons(m_Server.GetConnectPort()); + localhost.sin_addr.s_addr = htonl(0x7f000001); // localhost + if (connect(m_ServerSocket, (sockaddr *)&localhost, sizeof(localhost)) != 0) + { + printf("connection to server failed: %d\n", WSAGetLastError()); + return false; + } + return true; +} + + + + + +bool cConnection::RelayFromServer(void) +{ + char Buffer[1024]; + int res = recv(m_ServerSocket, Buffer, sizeof(Buffer), 0); + if (res <= 0) + { + Log("Server closed the socket: %d; %d; aborting connection", res, WSAGetLastError()); + return false; + } + + DataLog(Buffer, res, "Received %d bytes from the server", res); + // TODO: Process the data + + res = send(m_ClientSocket, Buffer, res, 0); + if (res <= 0) + { + Log("Client closed the socket: %d, %d; aborting connection", res, WSAGetLastError()); + return false; + } + + return true; +} + + + + + +bool cConnection::RelayFromClient(void) +{ + char Buffer[1024]; + int res = recv(m_ClientSocket, Buffer, sizeof(Buffer), 0); + if (res <= 0) + { + Log("Client closed the socket: %d; %d; aborting connection", res, WSAGetLastError()); + return false; + } + + DataLog(Buffer, res, "Received %d bytes from the client", res); + // TODO: Process the data + + res = send(m_ServerSocket, Buffer, res, 0); + if (res <= 0) + { + Log("Server closed the socket: %d, %d; aborting connection", res, WSAGetLastError()); + return false; + } + + return true; +} + + + + + +double cConnection::GetRelativeTime(void) +{ + return (double)(clock() - m_BeginTick) / CLOCKS_PER_SEC; + +} + + + + diff --git a/ProtoProxy/Connection.h b/ProtoProxy/Connection.h new file mode 100644 index 000000000..decf42435 --- /dev/null +++ b/ProtoProxy/Connection.h @@ -0,0 +1,59 @@ + +// Connection.h + +// Interfaces to the cConnection class representing a single pair of connected sockets + + + + + +#pragma once + +#include + + + + + +class cServer; + + + + + +class cConnection +{ + cCriticalSection m_CSLog; + FILE * m_LogFile; + + cServer & m_Server; + SOCKET m_ClientSocket; + SOCKET m_ServerSocket; + + clock_t m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime()) + +public: + cConnection(SOCKET a_ClientSocket, cServer & a_Server); + ~cConnection(); + + void Run(void); + + void Log(const char * a_Format, ...); + void DataLog(const void * a_Data, int a_Size, const char * a_Format, ...); + +protected: + bool ConnectToServer(void); + + /// Relays data from server to client; returns false if connection aborted + bool RelayFromServer(void); + + /// Relays data from client to server; returns false if connection aborted + bool RelayFromClient(void); + + /// Returns the time relative to the first call of this function, in the fractional seconds elapsed + double GetRelativeTime(void); +} ; + + + + diff --git a/ProtoProxy/Globals.cpp b/ProtoProxy/Globals.cpp new file mode 100644 index 000000000..2c60fd698 --- /dev/null +++ b/ProtoProxy/Globals.cpp @@ -0,0 +1,10 @@ + +// Globals.cpp + +// This file is used for precompiled header generation in MSVC environments + +#include "Globals.h" + + + + diff --git a/ProtoProxy/Globals.h b/ProtoProxy/Globals.h new file mode 100644 index 000000000..e97d2e561 --- /dev/null +++ b/ProtoProxy/Globals.h @@ -0,0 +1,219 @@ + +// Globals.h + +// This file gets included from every module in the project, so that global symbols may be introduced easily +// Also used for precompiled header generation in MSVC environments + + + + + +// Compiler-dependent stuff: +#if defined(_MSC_VER) + // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether + #pragma warning(disable:4481) + + // Disable some warnings that we don't care about: + #pragma warning(disable:4100) + + #define OBSOLETE __declspec(deprecated) + + // No alignment needed in MSVC + #define ALIGN_8 + #define ALIGN_16 + +#elif defined(__GNUC__) + + // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? + #define abstract + + // TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + #define OBSOLETE __attribute__((deprecated)) + + #define ALIGN_8 __attribute__((aligned(8))) + #define ALIGN_16 __attribute__((aligned(16))) + + // Some portability macros :) + #define stricmp strcasecmp + +#else + + #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" + + /* + // Copy and uncomment this into another #elif section based on your compiler identification + + // Explicitly mark classes as abstract (no instances can be created) + #define abstract + + // Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + // Mark functions as obsolete, so that their usage results in a compile-time warning + #define OBSOLETE + + // Mark types / variables for alignment. Do the platforms need it? + #define ALIGN_8 + #define ALIGN_16 + */ + +#endif + + + + + +// Integral types with predefined sizes: +typedef long long Int64; +typedef int Int32; +typedef short Int16; + + + + + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for any class that shouldn't allow copying itself +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName &); \ + void operator=(const TypeName &) + +// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc +#define UNUSED(X) (void)(X) + + + + +// OS-dependent stuff: +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #include + + // Windows SDK defines min and max macros, messing up with our std::min and std::max usage + #undef min + #undef max + + // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant + #ifdef GetFreeSpace + #undef GetFreeSpace + #endif // GetFreeSpace +#else + #include + #include // for mkdir + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include +#if !defined(ANDROID_NDK) + #include +#endif +#endif + +#if !defined(ANDROID_NDK) + #define USE_SQUIRREL +#endif + +#if defined(ANDROID_NDK) + #define FILE_IO_PREFIX "/sdcard/mcserver/" +#else + #define FILE_IO_PREFIX "" +#endif + + + + + +// CRT stuff: +#include +#include +#include +#include + + + + + +// STL stuff: +#include +#include +#include +#include +#include +#include +#include + + + + + +// Common headers (part 1, without macros): +#include "StringUtils.h" +#include "cCriticalSection.h" + + + + + +// Common definitions: + +/// Evaluates to the number of elements in an array (compile-time!) +#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X))) + +/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)" ) +#define KiB * 1024 + +/// Faster than (int)floorf((float)x / (float)div) +#define FAST_FLOOR_DIV( x, div ) ( (x) < 0 ? (((int)x / div) - 1) : ((int)x / div) ) + +// Own version of assert() that writes failed assertions to the log for review +#ifdef NDEBUG + #define ASSERT(x) ((void)0) +#else + #define ASSERT assert +#endif + +// Pretty much the same as ASSERT() but stays in Release builds +#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) ) + + + + + +/// A generic interface used mainly in ForEach() functions +template class cItemCallback +{ +public: + /// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating + virtual bool Item(Type * a_Type) = 0; +} ; + + + + + +#include "CryptoPP/osrng.h" +#include "CryptoPP/rsa.h" + +using namespace CryptoPP; + + + + +#define LOGERROR printf +#define LOGINFO printf +#define LOGWARNING printf \ No newline at end of file diff --git a/ProtoProxy/ProtoProxy.cpp b/ProtoProxy/ProtoProxy.cpp new file mode 100644 index 000000000..abc4a7606 --- /dev/null +++ b/ProtoProxy/ProtoProxy.cpp @@ -0,0 +1,30 @@ + +// ProtoProxy.cpp + +// Implements the main app entrypoint + +#include "Globals.h" +#include "Server.h" + + + + + +int main(int argc, char ** argv) +{ + cServer Server; + int res = Server.Init(25565, 25564); + if (res != 0) + { + printf("Server initialization failed: %d", res); + return res; + } + + Server.Run(); + + return 0; +} + + + + diff --git a/ProtoProxy/ProtoProxy.sln b/ProtoProxy/ProtoProxy.sln new file mode 100644 index 000000000..62d298905 --- /dev/null +++ b/ProtoProxy/ProtoProxy.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProtoProxy", "ProtoProxy.vcproj", "{EFEC8F76-1397-49A4-885B-314CB4244231}" + ProjectSection(ProjectDependencies) = postProject + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} = {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CryptoPP", "..\VC2008\CryptoPP.vcproj", "{3423EC9A-52E4-4A4D-9753-EDEBC38785EF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EFEC8F76-1397-49A4-885B-314CB4244231}.Debug|Win32.ActiveCfg = Debug|Win32 + {EFEC8F76-1397-49A4-885B-314CB4244231}.Debug|Win32.Build.0 = Debug|Win32 + {EFEC8F76-1397-49A4-885B-314CB4244231}.Release|Win32.ActiveCfg = Release|Win32 + {EFEC8F76-1397-49A4-885B-314CB4244231}.Release|Win32.Build.0 = Release|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.ActiveCfg = Debug|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.Build.0 = Debug|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|Win32.ActiveCfg = Release|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ProtoProxy/ProtoProxy.txt b/ProtoProxy/ProtoProxy.txt new file mode 100644 index 000000000..bbeab490f --- /dev/null +++ b/ProtoProxy/ProtoProxy.txt @@ -0,0 +1,25 @@ + +// ProtoProxy.txt + +// A readme for the project + +/* +ProtoProxy +========== + +This is a project to create a proxy for the MineCraft protocol, allowing anyone to view the data sent over a network connection between a client and a server. This, in fact, performs a kind of Man-In-The-Middle (MITM) attack on the protocol by tapping in between the connection points and providing a decrypter and an encrypter for each. + +In order to catch the encryption parameters, the MC protocol needs to be understood at least a little bit at the beginning, when the cryptography parameters are exchanged. + +This project is currently Windows-only and I don't plan on making it multi-platform, although the effort needed for doing so should be minimal. + +The proxy only works on the localhost connection. It listens on port 25565 and expects the underlying MC server to run on port 25564. + + + +*/ + + + + + diff --git a/ProtoProxy/ProtoProxy.vcproj b/ProtoProxy/ProtoProxy.vcproj new file mode 100644 index 000000000..2d980bed7 --- /dev/null +++ b/ProtoProxy/ProtoProxy.vcproj @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ProtoProxy/Server.cpp b/ProtoProxy/Server.cpp new file mode 100644 index 000000000..e0abcb596 --- /dev/null +++ b/ProtoProxy/Server.cpp @@ -0,0 +1,75 @@ + +// Server.cpp + +// Interfaces to the cServer class encapsulating the entire "server" + +#include "Globals.h" +#include "Server.h" +#include "Connection.h" + + + + + +cServer::cServer(void) +{ +} + + + + + +int cServer::Init(short a_ListenPort, short a_ConnectPort) +{ + m_ConnectPort = a_ConnectPort; + WSAData wsa; + int res = WSAStartup(0x0202, &wsa); + if (res != 0) + { + printf("Cannot initialize WinSock: %d\n", res); + return res; + } + + printf("Generating protocol encryption keypair...\n"); + AutoSeededRandomPool rng; + m_PrivateKey.GenerateRandomWithKeySize(rng, 1024); + RSA::PublicKey pk(m_PrivateKey); + m_PublicKey = pk; + + m_ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + sockaddr_in local; + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_addr.s_addr = htonl(0x7f000001); // localhost + local.sin_port = htons(a_ListenPort); + bind(m_ListenSocket, (sockaddr *)&local, sizeof(local)); + listen(m_ListenSocket, 1); + return 0; +} + + + + + +void cServer::Run(void) +{ + printf("Server running\n"); + while (true) + { + sockaddr_in Addr; + ZeroMemory(&Addr, sizeof(Addr)); + int AddrSize = sizeof(Addr); + SOCKET client = accept(m_ListenSocket, (sockaddr *)&Addr, &AddrSize); + if (client == INVALID_SOCKET) + { + printf("accept returned an error: %d; bailing out", WSAGetLastError()); + return; + } + cConnection Connection(client, *this); + Connection.Run(); + } +} + + + + diff --git a/ProtoProxy/Server.h b/ProtoProxy/Server.h new file mode 100644 index 000000000..da64036b4 --- /dev/null +++ b/ProtoProxy/Server.h @@ -0,0 +1,38 @@ + +// Server.h + +// Interfaces to the cServer class encapsulating the entire "server" + + + + + +#pragma once + + + + + + +class cServer +{ + SOCKET m_ListenSocket; + RSA::PrivateKey m_PrivateKey; + RSA::PublicKey m_PublicKey; + short m_ConnectPort; + +public: + cServer(void); + + int Init(short a_ListenPort, short a_ConnectPort); + void Run(void); + + RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; } + RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; } + + short GetConnectPort(void) const { return m_ConnectPort; } +} ; + + + +