ProtoProxy: Initial import of the protocol proxy project.

Currently it logs all communication, doesn't decode anything, doesn't decrypt.

git-svn-id: http://mc-server.googlecode.com/svn/trunk@822 0a769ca7-a7f5-676a-18bf-c427514a06d6
master
madmaxoft@gmail.com 2012-09-02 15:38:28 +00:00
parent f075bed23c
commit e1c83be32d
10 changed files with 943 additions and 0 deletions

203
ProtoProxy/Connection.cpp Normal file
View File

@ -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;
}

59
ProtoProxy/Connection.h Normal file
View File

@ -0,0 +1,59 @@
// Connection.h
// Interfaces to the cConnection class representing a single pair of connected sockets
#pragma once
#include <time.h>
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);
} ;

10
ProtoProxy/Globals.cpp Normal file
View File

@ -0,0 +1,10 @@
// Globals.cpp
// This file is used for precompiled header generation in MSVC environments
#include "Globals.h"

219
ProtoProxy/Globals.h Normal file
View File

@ -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 <Windows.h>
#include <winsock2.h>
// 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 <sys/types.h>
#include <sys/stat.h> // for mkdir
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <dirent.h>
#include <errno.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
#if !defined(ANDROID_NDK)
#include <tr1/memory>
#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 <assert.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
// STL stuff:
#include <vector>
#include <list>
#include <deque>
#include <string>
#include <map>
#include <algorithm>
#include <memory>
// 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 <typename Type> 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

30
ProtoProxy/ProtoProxy.cpp Normal file
View File

@ -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;
}

29
ProtoProxy/ProtoProxy.sln Normal file
View File

@ -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

25
ProtoProxy/ProtoProxy.txt Normal file
View File

@ -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.
*/

View File

@ -0,0 +1,255 @@
<?xml version="1.0" encoding="windows-1250"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="ProtoProxy"
ProjectGUID="{EFEC8F76-1397-49A4-885B-314CB4244231}"
RootNamespace="ProtoProxy"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..;../source"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="Globals.h"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="..;../source"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="Globals.h"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;h;hpp"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\source\cCriticalSection.cpp"
>
</File>
<File
RelativePath="..\source\cCriticalSection.h"
>
</File>
<File
RelativePath="..\source\cIsThread.cpp"
>
</File>
<File
RelativePath="..\source\cIsThread.h"
>
</File>
<File
RelativePath=".\Connection.cpp"
>
</File>
<File
RelativePath=".\Connection.h"
>
</File>
<File
RelativePath=".\Globals.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\Globals.h"
>
</File>
<File
RelativePath=".\ProtoProxy.cpp"
>
</File>
<File
RelativePath=".\Server.cpp"
>
</File>
<File
RelativePath=".\Server.h"
>
</File>
<File
RelativePath="..\source\StringUtils.cpp"
>
</File>
<File
RelativePath="..\source\StringUtils.h"
>
</File>
</Filter>
<File
RelativePath=".\ProtoProxy.txt"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

75
ProtoProxy/Server.cpp Normal file
View File

@ -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();
}
}

38
ProtoProxy/Server.h Normal file
View File

@ -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; }
} ;