MapleResearch
MapleStory v95 AntiHack Analysis
Install / Use
/learn @Maxcloud/MapleResearchREADME
MapleStory v95 Client Analysis
Hello everyone welcome to our analysis on GMS v95.1
- This document is a work in progress
- This is not a professional document
- The primary focus is on what I d to do to create localhost
- There is too much for me to go into excruciating detail about
- Please contribute if you know anything more !!!
CSecurityClient
Class used to handle anti cheat integration
- Houses HackShield related fields in this version
- In other versions houses NGS and XignCode3 relations fields
- Is a TSingleton<CSecurityClient>
In lots of places in the client usage of CSecurityClient looks like such (pseudo):
if ( TSingleton<CSecurityClient>::IsInstantiated() )
{
TSingleton<CSecurityClient>::GetInstance();
//Usage of class such as
CSecurityClient::InitModule();
}
PatchRetZero here can save you lots of patches you'd have to do in other places otherwise.
Class Pseudo
// write access to const memory has been detected, the output may be wrong!
void __thiscall CSecurityClient::CSecurityClient(CSecurityClient *this)
{
CSecurityClient *v1; // edi
TSecType<int> *v2; // esi
int v3; // eax
char v4; // dl
TSecData<int> *v5; // ecx
int v6; // eax
TSecData<int> *v7; // edx
int v8; // eax
char v9; // cl
v1 = this;
v2 = &this->m_bInitModule;
if ( this == (CSecurityClient *)-4 )
TSingleton<CSecurityClient>::ms_pInstance = 0;
else
TSingleton<CSecurityClient>::ms_pInstance = this;
this->vfptr = (CSecurityClientVtbl *)&CSecurityClient::`vftable';
this->m_bInitModule.m_secdata = (TSecData<int> *)ZAllocEx<ZAllocAnonSelector>::Alloc(
&ZAllocEx<ZAllocAnonSelector>::_s_alloc,
0xCu);
v2->FakePtr1 = (unsigned int)&v2[-1365].FakePtr2 + rand();
v3 = rand();
v4 = v2->FakePtr1;
v5 = v2->m_secdata;
v2->FakePtr2 = (unsigned int)&v2[-1365].FakePtr2 + v3;
v5->FakePtr1 = v4;
v2->m_secdata->FakePtr2 = v2->FakePtr2;
TSecType<int>::SetData(v2, 0);
v1->m_bStartModule.m_secdata = (TSecData<int> *)ZAllocEx<ZAllocAnonSelector>::Alloc(
&ZAllocEx<ZAllocAnonSelector>::_s_alloc,
0xCu);
v1->m_bStartModule.FakePtr1 = (unsigned int)&v1[-52].m_szHShieldPath[rand() + 20];
v6 = rand();
v7 = v1->m_bStartModule.m_secdata;
v1->m_bStartModule.FakePtr2 = (unsigned int)&v1[-52].m_szHShieldPath[v6 + 20];
v7->FakePtr1 = v1->m_bStartModule.FakePtr1;
v1->m_bStartModule.m_secdata->FakePtr2 = v1->m_bStartModule.FakePtr2;
TSecType<int>::SetData(&v1->m_bStartModule, 0);
v1->m_nThreatCode = 0;
v1->m_nThreatParamSize.m_secdata = (TSecData<long> *)ZAllocEx<ZAllocAnonSelector>::Alloc(
&ZAllocEx<ZAllocAnonSelector>::_s_alloc,
0xCu);
v1->m_nThreatParamSize.FakePtr1 = (unsigned int)&v1[-52].m_szHShieldPath[rand() + 36];
v8 = rand();
v9 = v1->m_nThreatParamSize.FakePtr1;
v1->m_nThreatParamSize.FakePtr2 = (unsigned int)&v1[-52].m_szHShieldPath[v8 + 36];
v1->m_nThreatParamSize.m_secdata->FakePtr1 = v9;
v1->m_nThreatParamSize.m_secdata->FakePtr2 = v1->m_nThreatParamSize.FakePtr2;
TSecType<long>::SetData(&v1->m_nThreatParamSize, 0);
v1->m_pThreatParam = 0;
v1->m_hMainWnd = 0;
}
void __thiscall CSecurityClient::InitModule(CSecurityClient *this)
{
CSecurityClient *v1; // esi
unsigned int v2; // eax
int v3; // eax
int (__stdcall **pExceptionObject)(ZXString<char> *); // [esp+4h] [ebp-214h]
unsigned int v5; // [esp+8h] [ebp-210h]
CHAR sModulePath; // [esp+Ch] [ebp-20Ch]
char v7; // [esp+Dh] [ebp-20Bh]
unsigned __int8 sModuleFolderPath; // [esp+110h] [ebp-108h]
char v9; // [esp+111h] [ebp-107h]
v1 = this;
sModuleFolderPath = 0;
memset(&v9, 0, 0x103u);
sModulePath = 0;
memset(&v7, 0, 0x103u);
GetModuleFolderName((char *)&sModuleFolderPath);
_mbsnbcpy((unsigned __int8 *)&sModulePath, &sModuleFolderPath, 0x104u);
_mbsnbcat((unsigned __int8 *)&sModulePath, "\\HShield", 8u);
_mbsnbcpy((unsigned __int8 *)v1->m_szHShieldPath, (const unsigned __int8 *)&sModulePath, 0x104u);
v2 = _AhnHS_HSUpdateA(&sModulePath, 600000u, 20000u);
if ( v2 )
{
v5 = v2;
pExceptionObject = CSecurityUpdateFailed::`vftable';
_CxxThrowException(&pExceptionObject, &_TI2_AVCSecurityUpdateFailed__);
}
_mbsnbcpy((unsigned __int8 *)&sModulePath, &sModuleFolderPath, 0x104u);
_mbsnbcat((unsigned __int8 *)&sModulePath, "\\HShield\\EHSvc.dll", 0x12u);
v3 = _AhnHS_InitializeA(&sModulePath, (int)_AhnHS_Callback, 9947, (int)"B7621D704ED72C489EE54605", 46808511, 1);
if ( v3 )
{
v5 = v3;
pExceptionObject = CSecurityInitFailed::`vftable';
_CxxThrowException(&pExceptionObject, &_TI2_AVCSecurityInitFailed__);
}
TSecType<int>::SetData(&v1->m_bInitModule, 1);
}
void __thiscall CSecurityClient::ClearModule(CSecurityClient *this)
{
TSecType<int> *v1; // esi
signed int v2; // eax
int (__stdcall **pExceptionObject)(ZXString<char> *); // [esp+4h] [ebp-8h]
int v4; // [esp+8h] [ebp-4h]
v1 = &this->m_bInitModule;
if ( TSecType<int>::GetData(&this->m_bInitModule) )
{
v2 = _AhnHS_Uninitialize();
if ( v2 )
{
v4 = v2;
pExceptionObject = CSecurityClearFailed::`vftable';
_CxxThrowException(&pExceptionObject, &_TI2_AVCSecurityClearFailed__);
}
TSecType<int>::SetData(v1, 0);
}
}
void __thiscall CSecurityClient::StartModule(CSecurityClient *this)
{
CSecurityClient *v1; // esi
signed int v2; // eax
int (__stdcall **v3)(ZXString<char> *); // [esp+0h] [ebp-Ch]
int v4; // [esp+4h] [ebp-8h]
v1 = this;
v2 = _AhnHS_StartService();
if ( v2 )
{
v4 = v2;
v3 = CSecurityInitFailed::`vftable';
_CxxThrowException(&v3, &_TI2_AVCSecurityInitFailed__);
}
_AhnHS_CheckHackShieldRunningStatus();
v1->m_dwCallbackTime = GetTickCount();
TSecType<int>::SetData(&v1->m_bStartModule, 1);
}
void __thiscall CSecurityClient::StopModule(CSecurityClient *this)
{
TSecType<int> *v1; // esi
signed int v2; // eax
int (__stdcall **pExceptionObject)(ZXString<char> *); // [esp+4h] [ebp-8h]
int v4; // [esp+8h] [ebp-4h]
v1 = &this->m_bStartModule;
if ( TSecType<int>::GetData(&this->m_bStartModule) )
{
v2 = _AhnHS_StopService();
if ( v2 )
{
v4 = v2;
pExceptionObject = CSecurityClearFailed::`vftable';
_CxxThrowException(&pExceptionObject, &_TI2_AVCSecurityClearFailed__);
}
TSecType<int>::SetData(v1, 0);
}
}
//Just throws an exception if HS error code is set
//Checks CSecurityClient->m_nThreatCode is a bad HS return code and throw ( result > 0x10501 )
signed int __thiscall CSecurityClient__Update(_DWORD *this)
{
signed int result; // eax
bool v2; // zf
bool v3; // sf
unsigned __int8 v4; // of
int (__stdcall **v5)(int); // [esp+0h] [ebp-8h]
int v6; // [esp+4h] [ebp-4h]
result = this[7];
if ( result > 0x10501 )
{
if ( result > 0x10801 )
{
if ( result != 0x10A01 )
return result;
LABEL_18:
v6 = this[7];
v5 = &off_BF643C;
sub_A68B61((int)&v5, &_TI2_AVCSecurityThreatDetected__);
JUMPOUT(*(_DWORD *)algn_A52B42);
}
if ( result == 0x10801 || result == 67073 )
goto LABEL_18;
if ( result <= 0x10700 )
return result;
v4 = __OFSUB__(result, 67333);
v2 = result == 67333;
v3 = result - 67333 < 0;
LABEL_10:
if ( !((unsigned __int8)(v3 ^ v4) | v2) )
return result;
goto LABEL_18;
}
if ( result == 0x10501 )
goto LABEL_18;
if ( result > 0x10303 )
{
if ( result < 0x10306 )
return result;
v4 = __OFSUB__(result, 66312);
v2 = result == 66312;
v3 = result - 66312 < 0;
goto LABEL_10;
}
if ( result >= 0x10301 || result == 0x10102 || result == 0x10104 )
goto LABEL_18;
return result;
}
Other Weird Checks
- Game checks
ws2_32.dlldos header magic to see if its been tampered - Game removes loopback adapters ( TODO: Add the winapi call name here )
GetIpAddrTable GetAdaptersInfocalls used to check adapter stuff ?- Client checks for the HackShield mutex
meteora - Client checks to see if
ehsvc.dllis loaded - Client literally does an IAT count on the
ehsvc.dllto see if its been tampered. I just loaded original - Client checks
WvsClientMutexmutex for multi client
IP Checks
- Game is fucking booby trapped with IP checks
- It's not worth me pointing out where ( will eventually )
- But basically getpeername is called, just return the expected IP
63.251.217.1 - Sad thing is they have heavy API checks on winsock so use the WSP variants like I do
- TODO: Talk more about the
MyGetProcAddressand heavy winapi checks ( xxxx.nst )
CWvsApp Checks
CSecurityClient::Updateis called inCWvsApp::RunCWvsApp->m_tLastServerIPCheckis inCWvsApp::CallUpdateCWvsApp->m_tLastServerIPCheck2is inCWvsApp::Run| Also contains CSecurityClient check belowCWvsApp->m_tLastSecurityCheckis inCWvsApp::Run
CWvsApp->m_tLastSecurityCheck
Everyday I pray and ask god what this done but I am unsure. All i know is i have to spoof it so client doesn't crash.
CSecurityClient Check
Thisis inside m_tLastServerIPCheck2
Checks if some files in the HShield folder exist 3N.mhe, v3warpds.v3d, v3warpns.v3d
Checks _AhnHS_StartSerice ret and expects HS_ERR_ALREADY_SERVICE_RUNNING ( 0x00000201 )
Checks CSecurityClient->m_dwCallbackTime is <= 60000
if ( TSingleton_CSecurityClient__IsInstantiated() )
{
v22 = '\x01';
v15 = '3';
v16 = 'N';
v17 = '.';
v18 = 'm';
v19 = 'h';
v20 = 'e';
v21 = '\0';
v25 = 'v';
v26 = '3';
v27 = 'w';
v28 = 'a';
v29 = 'r';
v30 = 'p'
View on GitHub67/100
Security Score
Audited on Jul 30, 2025
No findings
