324 lines
9.7 KiB
C++
324 lines
9.7 KiB
C++
|
|
#ifdef USE_GAME_GUARD_AUTH_3
|
|
|
|
#include <toolkit/XEnv.h>
|
|
#include <network/IConnection.h>
|
|
#include <toolkit/XStringUtil.h>
|
|
#include <toolkit/XConsole.h>
|
|
#include <logging/FileLog.h>
|
|
|
|
#include "StructPlayer.h"
|
|
#include "SendMessage.h"
|
|
#include "XGameGuard3.h"
|
|
|
|
#ifdef _WIN64
|
|
#pragma comment( lib, "ggsrv30lib_x64_MT.lib" )
|
|
#else
|
|
#pragma comment( lib, "ggsrv30lib_x86_MT.lib" )
|
|
#endif
|
|
|
|
|
|
|
|
// LOG_DEBUG 의 경우는 최대 10240 byte 정도의 큰 로그가 남을 수 있으므로 Buffer Overflow 에
|
|
// 주의하여 구현해 주시기 바랍니다.
|
|
void __stdcall CS3LogCallback( int nLogType, char *szLog )
|
|
{
|
|
// 로그가 발생 시 callback 함수로 로그를 전달합니다.
|
|
// callback 함수가 등록되어 있지 않다면 호출되지 않습니다.
|
|
// 게임가드 프로토콜 버전, 내부버전의업데이트가발생했을때호출됩니다. (호출빈도 낮음)
|
|
if( nLogType == LOG_UPDATE )
|
|
FileLogHandler::GetFileLogHandler()->LogStringEx( NULL, "GameGuardLog", "this is update log : %s", szLog );
|
|
// 초기화상황, 일반적인로그, 에러발생등의로그가발생했을때호출됩니다. (호출빈도 보통)
|
|
else if( nLogType == LOG_NORMAL )
|
|
FileLogHandler::GetFileLogHandler()->LogStringEx( NULL, "GameGuardLog", "this is normal log : %s", szLog );
|
|
/*
|
|
// 모든 유저의 패킷 정보가 남게 됩니다. (호출빈도,양 매우 높음)
|
|
else if( nLogType == LOG_DEBUG )
|
|
FileLogHandler::GetFileLogHandler()->LogStringEx( NULL, "GameGuardLog", "this is debug log : %s\n", szLog );
|
|
*/
|
|
}
|
|
|
|
void __stdcall CS3UpdateInfoCallback( int nUpdateType, int nBefore, int nAfter )
|
|
{
|
|
if( nUpdateType == UPDATE_PROTOCOL )
|
|
FileLogHandler::GetFileLogHandler()->LogStringEx( NULL, "GameGuardLog", "this is protocol update notification : %d -> %d", nBefore, nAfter);
|
|
else if( nUpdateType == UPDATE_GGVERSION )
|
|
FileLogHandler::GetFileLogHandler()->LogStringEx( NULL, "GameGuardLog", "this is ggversion update notification : %d -> %d", nBefore, nAfter);
|
|
}
|
|
|
|
|
|
const DWORD XGameGuard3::Init()
|
|
{
|
|
m_strErrorDescription.clear();
|
|
|
|
if( IsInitialized() )
|
|
{
|
|
m_strErrorDescription = "Already initialized";
|
|
return SS_ERROR_ALREADY_INITIALIZED;
|
|
}
|
|
|
|
std::vector< std::string > vTokens;
|
|
XStringUtil::Split( ENV().GetString( "game.security_solution_init_parameter", "" ).c_str(), vTokens, "|", false );
|
|
|
|
char * pszModulePath = const_cast< char * >( ( vTokens.size() >= 1 ) ? vTokens[ 0 ].c_str() : "GameGuard" );
|
|
/*
|
|
사용안함(구버전 인증)
|
|
UINT32 dwNumActive = ( vTokens.size() >= 2 ) ? atoi( vTokens[ 1 ].c_str() ) : 400;
|
|
int nLogType = ( vTokens.size() >= 3 ) ? atoi( vTokens[ 2 ].c_str() ) : 0;
|
|
int nUpdateTimeLimit = ( vTokens.size() >= 4 ) ? atoi( vTokens[ 3 ].c_str() ) : 30;
|
|
int nUpdateCondition = ( vTokens.size() >= 5 ) ? atoi( vTokens[ 4 ].c_str() ) : 50;
|
|
*/
|
|
|
|
UINT32 nError = InitCSAuth3( pszModulePath );
|
|
if( nError != ERROR_SUCCESS )
|
|
{
|
|
XStringUtil::Format( m_strErrorDescription, "Error on InitCSAuth3(%lu)", nError );
|
|
return SS_ERROR_INIT_FAILED;
|
|
}
|
|
|
|
// Log callback 함수등록
|
|
SetCallbackFunction( CALLBACK_LOG, (PVOID)CS3LogCallback );
|
|
// Update callback 함수등록
|
|
SetCallbackFunction( CALLBACK_UPDATE, (PVOID)CS3UpdateInfoCallback );
|
|
|
|
m_bInit = true;
|
|
return SS_ERROR_SUCCESS;
|
|
}
|
|
|
|
const DWORD XGameGuard3::DeInit()
|
|
{
|
|
m_strErrorDescription.clear();
|
|
|
|
if( !IsInitialized() )
|
|
{
|
|
m_strErrorDescription = "Not initialized";
|
|
return SS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
m_bInit = false;
|
|
|
|
// 서버가 종료되는 경우
|
|
CloseCSAuth3();
|
|
return SS_ERROR_SUCCESS;
|
|
}
|
|
|
|
const DWORD XGameGuard3::InitClientSession( struct IStreamSocketConnection * pConnection )
|
|
{
|
|
m_strErrorDescription.clear();
|
|
|
|
if( !IsInitialized() )
|
|
{
|
|
m_strErrorDescription = "SS not initialized";
|
|
return SS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
_CONNECTION_TAG * pTag = static_cast< _CONNECTION_TAG * >( pConnection->GetTag() );
|
|
if( !pTag )
|
|
{
|
|
m_strErrorDescription = "No connection tag";
|
|
return SS_ERROR_NO_CONNECTION_TAG;
|
|
}
|
|
|
|
if( pTag->pSecuritySolutionBuffer )
|
|
{
|
|
m_strErrorDescription = "Already initialized";
|
|
return SS_ERROR_ALREADY_INITIALIZED;
|
|
}
|
|
|
|
ClientSessionInfo * pSession = new ClientSessionInfo;
|
|
|
|
// 클라이언트 세션 세팅 완료 직후 서버/클라 체크를 1회 진행함
|
|
pTag->pSecuritySolutionBuffer = pSession;
|
|
|
|
DWORD nError = sendAuthQuery( pConnection, pSession );
|
|
if( nError != SS_ERROR_SUCCESS )
|
|
{
|
|
return nError;
|
|
}
|
|
|
|
pSession->m_nNextRequestTime = GetArTime() + GameRule::nPeriodOfSecuritySolutionCheck;
|
|
pSession->m_bIsFirstTimeCheck = false;
|
|
return SS_ERROR_SUCCESS;
|
|
}
|
|
|
|
const bool XGameGuard3::IsInitCompletedClientSession( struct IStreamSocketConnection * pConnection ) const
|
|
{
|
|
m_strErrorDescription.clear();
|
|
|
|
if( !IsInitialized() )
|
|
{
|
|
m_strErrorDescription = "SS not initialized";
|
|
return false;
|
|
}
|
|
|
|
_CONNECTION_TAG * pTag = static_cast< _CONNECTION_TAG * >( pConnection->GetTag() );
|
|
if( !pTag )
|
|
{
|
|
m_strErrorDescription = "No connection tag";
|
|
return false;
|
|
}
|
|
|
|
ClientSessionInfo * pSession = static_cast< ClientSessionInfo * >( pTag->pSecuritySolutionBuffer );
|
|
if( !pSession || !pSession->m_pCCSAuth3 )
|
|
{
|
|
m_strErrorDescription = "CS not initialized";
|
|
return false;
|
|
}
|
|
|
|
// pSession->m_bInitComplete == false 여도 일단 통과시켜서 캐릭터 선택 및 이후 진행은 가능하게 하되
|
|
// ProcValidation에서 m_bInitComplete == false 인 경우에는 별도의 타임아웃 제한을 걸어서 체크함
|
|
|
|
return true;
|
|
}
|
|
|
|
const DWORD XGameGuard3::DeinitClientSession( struct IStreamSocketConnection * pConnection )
|
|
{
|
|
m_strErrorDescription.clear();
|
|
|
|
if( !IsInitialized() )
|
|
{
|
|
m_strErrorDescription = "SS not initialized";
|
|
return SS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
_CONNECTION_TAG * pTag = static_cast< _CONNECTION_TAG * >( pConnection->GetTag() );
|
|
if( !pTag )
|
|
{
|
|
m_strErrorDescription = "No connection tag";
|
|
return SS_ERROR_NO_CONNECTION_TAG;
|
|
}
|
|
|
|
ClientSessionInfo * pSession = static_cast< ClientSessionInfo * >( pTag->pSecuritySolutionBuffer );
|
|
if( !pSession || !pSession->m_pCCSAuth3 )
|
|
{
|
|
m_strErrorDescription = "CS not initialized";
|
|
return SS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
pTag->pSecuritySolutionBuffer = NULL;
|
|
delete pSession;
|
|
|
|
return SS_ERROR_SUCCESS;
|
|
}
|
|
|
|
const DWORD XGameGuard3::ProcValidation( struct IStreamSocketConnection * pConnection )
|
|
{
|
|
m_strErrorDescription.clear();
|
|
|
|
if( !IsInitialized() )
|
|
{
|
|
m_strErrorDescription = "SS not initialized";
|
|
return SS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
_CONNECTION_TAG * pTag = static_cast< _CONNECTION_TAG * >( pConnection->GetTag() );
|
|
if( !pTag )
|
|
{
|
|
m_strErrorDescription = "No connection tag";
|
|
return SS_ERROR_NO_CONNECTION_TAG;
|
|
}
|
|
|
|
ClientSessionInfo * pSession = static_cast< ClientSessionInfo * >( pTag->pSecuritySolutionBuffer );
|
|
if( !pSession || !pSession->m_pCCSAuth3 )
|
|
{
|
|
m_strErrorDescription = "CS not initialized";
|
|
return SS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
AR_TIME tCurrent = GetArTime();
|
|
if( pSession->m_nNextRequestTime <= tCurrent )
|
|
{
|
|
DWORD nError = sendAuthQuery( pConnection, pSession );
|
|
if( nError != SS_ERROR_SUCCESS )
|
|
{
|
|
return nError;
|
|
}
|
|
|
|
pSession->m_nNextRequestTime = GetArTime() + GameRule::nPeriodOfSecuritySolutionCheck;
|
|
}
|
|
|
|
return SS_ERROR_SUCCESS;
|
|
}
|
|
|
|
const DWORD XGameGuard3::OnValidationResponse( struct IStreamSocketConnection * pConnection, const void * pResponseBuffer, unsigned int size )
|
|
{
|
|
m_strErrorDescription.clear();
|
|
|
|
if( !IsInitialized() )
|
|
{
|
|
m_strErrorDescription = "SS not initialized";
|
|
return SS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
_CONNECTION_TAG * pTag = static_cast< _CONNECTION_TAG * >( pConnection->GetTag() );
|
|
if( !pTag )
|
|
{
|
|
m_strErrorDescription = "No connection tag";
|
|
return SS_ERROR_NO_CONNECTION_TAG;
|
|
}
|
|
|
|
ClientSessionInfo * pSession = static_cast< ClientSessionInfo * >( pTag->pSecuritySolutionBuffer );
|
|
if( !pSession || !pSession->m_pCCSAuth3 )
|
|
{
|
|
m_strErrorDescription = "CS not initialized";
|
|
return SS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
if( size > sizeof( pSession->m_pCCSAuth3->packet ) )
|
|
{
|
|
XStringUtil::Format( m_strErrorDescription, "Error on size too big(%lu)", size );
|
|
return SS_ERROR_INVALID_CLIENT_RESPONSE;
|
|
}
|
|
|
|
s_memcpy( &pSession->m_pCCSAuth3->packet, sizeof( pSession->m_pCCSAuth3->packet ), pResponseBuffer, size );
|
|
UINT32 nError = pSession->m_pCCSAuth3->Check( size );
|
|
if( nError >= 3000 )
|
|
{
|
|
XStringUtil::Format( m_strErrorDescription, "Error on Check Auth Answer(%lu)", nError );
|
|
return SS_ERROR_INVALID_CLIENT_RESPONSE;
|
|
}
|
|
|
|
// from gameguard 클라이언트에서 게임서버에 접속 시 서버에서는 첫 번째 인증을 송수신 후에
|
|
// 즉시로 두 번째 인증을 전송한다면 게임가드를 건너 뛰는 핵 툴 방식에 대하여 즉각적인 차단이 가능합니다.
|
|
if( pSession->m_bIsFirstTimeCheck == false )
|
|
{
|
|
pSession->m_bIsFirstTimeCheck = true;
|
|
DWORD nError = sendAuthQuery( pConnection, pSession );
|
|
if( nError != SS_ERROR_SUCCESS )
|
|
{
|
|
return nError;
|
|
}
|
|
|
|
pSession->m_nNextRequestTime = GetArTime() + GameRule::nPeriodOfSecuritySolutionCheck;
|
|
}
|
|
|
|
return SS_ERROR_SUCCESS;
|
|
}
|
|
|
|
const DWORD XGameGuard3::sendAuthQuery( struct IStreamSocketConnection * pConnection, struct XGameGuard3::ClientSessionInfo * pSession )
|
|
{
|
|
const int buffer_size = sizeof( pSession->m_pCCSAuth3->packet ) + sizeof( TS_SC_GAME_GUARD_AUTH_QUERY );
|
|
char auth_msg_buffer[buffer_size] = { 0, };
|
|
|
|
TS_SC_GAME_GUARD_AUTH_QUERY* msg = new (auth_msg_buffer) TS_SC_GAME_GUARD_AUTH_QUERY;
|
|
|
|
UINT32 uReturnedPacketSize = 0;
|
|
UINT32 nError = pSession->m_pCCSAuth3->Get( &uReturnedPacketSize );
|
|
if( nError >= 3000 )
|
|
{
|
|
XStringUtil::Format( m_strErrorDescription, "Error on GetAuthQuery(%lu)", nError );
|
|
return SS_ERROR_CREATING_REQUEST_FAILED;
|
|
}
|
|
|
|
if( uReturnedPacketSize > 0 )
|
|
{
|
|
msg->size += uReturnedPacketSize;
|
|
msg->auth_data_size = MAKELONG( uReturnedPacketSize, 1 );
|
|
s_memcpy( (msg+1), buffer_size-sizeof( *msg ), &pSession->m_pCCSAuth3->packet, uReturnedPacketSize );
|
|
PendMessage( pConnection, msg );
|
|
}
|
|
|
|
return SS_ERROR_SUCCESS;
|
|
}
|
|
|
|
#endif |