Add server-side anti-cheat evidence scoring
Some checks failed
build / Linux asan (push) Has been cancelled
build / Linux release (push) Has been cancelled
build / FreeBSD build (push) Has been cancelled

This commit is contained in:
server
2026-04-16 11:18:39 +02:00
parent b4ee8aa5d7
commit e8bcfe06f0
5 changed files with 160 additions and 17 deletions

View File

@@ -1539,7 +1539,12 @@ bool CheckComboHack(LPCHARACTER ch, BYTE bArg, DWORD dwTime, bool CheckSpeedHack
{
// 말에 타거나 내렸을 때 1.5초간 공격은 핵으로 간주하지 않되 공격력은 없게 하는 처리
if (get_dword_time() - ch->GetLastMountTime() > 1500)
{
char szDetail[128];
snprintf(szDetail, sizeof(szDetail), "arg=%u interval=%d valid=%d scalar=%d", bArg, ComboInterval, ch->GetValidComboInterval(), HackScalar);
ch->RecordAntiCheatViolation("COMBO", MIN(20, 2 + HackScalar), szDetail, HackScalar >= 5);
ch->IncreaseComboHackCount(1 + HackScalar);
}
ch->SkipComboAttackByTime(ch->GetValidComboInterval());
}
@@ -1556,6 +1561,9 @@ void CInputMain::Move(LPCHARACTER ch, const char * data)
if (pinfo->bFunc >= FUNC_MAX_NUM && !(pinfo->bFunc & 0x80))
{
char szDetail[64];
snprintf(szDetail, sizeof(szDetail), "func=%u arg=%u", pinfo->bFunc, pinfo->bArg);
ch->RecordAntiCheatViolation("MOVE_TYPE", 12, szDetail, true);
sys_err("invalid move type: %s", ch->GetName());
return;
}
@@ -1580,13 +1588,17 @@ void CInputMain::Move(LPCHARACTER ch, const char * data)
if (((false == ch->IsRiding() && fDist > 25) || fDist > 40) && OXEVENT_MAP_INDEX != ch->GetMapIndex())
{
if( false == LC_IsEurope() )
{
const PIXEL_POSITION & warpPos = ch->GetWarpPosition();
if (warpPos.x == 0 && warpPos.y == 0)
LogManager::instance().HackLog("Teleport", ch); // 부정확할 수 있음
}
char szDetail[160];
snprintf(szDetail,
sizeof(szDetail),
"dist=%.1f riding=%d cur=(%ld,%ld) dst=(%ld,%ld)",
fDist,
ch->IsRiding() ? 1 : 0,
static_cast<long>(ch->GetX()),
static_cast<long>(ch->GetY()),
static_cast<long>(pinfo->lX),
static_cast<long>(pinfo->lY));
ch->RecordAntiCheatViolation("MOVE_DISTANCE", 15, szDetail, true);
sys_log(0, "MOVE: %s trying to move too far (dist: %.1fm) Riding(%d)", ch->GetName(), fDist, ch->IsRiding());
@@ -1659,7 +1671,7 @@ void CInputMain::Move(LPCHARACTER ch, const char * data)
char szBuf[256];
snprintf(szBuf, sizeof(szBuf), "SKILL_HACK: name=%s, job=%d, group=%d, motion=%d", name, job, group, motion);
LogManager::instance().HackLog(szBuf, ch->GetDesc()->GetAccountTable().login, ch->GetName(), ch->GetDesc()->GetHostName());
ch->RecordAntiCheatViolation("SKILL_MOTION", 40, szBuf, true);
sys_log(0, "%s", szBuf);
if (test_server)
@@ -1869,6 +1881,9 @@ int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiByt
if( iCount > nCountLimit )
{
//LogManager::instance().HackLog( "SYNC_POSITION_HACK", ch );
char szDetail[64];
snprintf(szDetail, sizeof(szDetail), "count=%d limit=%d", iCount, nCountLimit);
ch->RecordAntiCheatViolation("SYNC_POSITION_COUNT", 3, szDetail);
sys_err( "Too many SyncPosition Count(%d) from Name(%s)", iCount, ch->GetName() );
//ch->GetDesc()->SetPhase(PHASE_CLOSE);
//return -1;
@@ -1918,12 +1933,29 @@ int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiByt
if (ch->GetSyncHackCount() < g_iSyncHackLimitCount)
{
ch->SetSyncHackCount(ch->GetSyncHackCount() + 1);
char szDetail[160];
snprintf(szDetail,
sizeof(szDetail),
"owner_dist=%.1f limit=%.1f victim=%s count=%d",
fDistWithSyncOwner,
fLimitDistWithSyncOwner,
victim->GetName(),
ch->GetSyncHackCount());
ch->RecordAntiCheatViolation("SYNC_OWNER_DISTANCE", 8, szDetail);
continue;
}
else
{
LogManager::instance().HackLog( "SYNC_POSITION_HACK", ch );
char szDetail[192];
snprintf(szDetail,
sizeof(szDetail),
"owner_dist=%.1f limit=%.1f victim=%s sync=(%ld,%ld)",
fDistWithSyncOwner,
fLimitDistWithSyncOwner,
victim->GetName(),
static_cast<long>(e->lX),
static_cast<long>(e->lY));
ch->RecordAntiCheatViolation("SYNC_OWNER_DISTANCE", 35, szDetail, true);
sys_err( "Too far SyncPosition DistanceWithSyncOwner(%f)(%s) from Name(%s) CH(%d,%d) VICTIM(%d,%d) SYNC(%d,%d)",
fDistWithSyncOwner, victim->GetName(), ch->GetName(), ch->GetX(), ch->GetY(), victim->GetX(), victim->GetY(),
e->lX, e->lY );
@@ -1947,12 +1979,29 @@ int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiByt
if (ch->GetSyncHackCount() < g_iSyncHackLimitCount)
{
ch->SetSyncHackCount(ch->GetSyncHackCount() + 1);
char szDetail[160];
snprintf(szDetail,
sizeof(szDetail),
"interval_ms=%ld limit_us=%ld victim=%s count=%d",
tvDiff->tv_sec * 1000 + tvDiff->tv_usec / 1000,
static_cast<long>(g_lValidSyncInterval),
victim->GetName(),
ch->GetSyncHackCount());
ch->RecordAntiCheatViolation("SYNC_INTERVAL", 8, szDetail);
continue;
}
else
{
LogManager::instance().HackLog( "SYNC_POSITION_HACK", ch );
char szDetail[192];
snprintf(szDetail,
sizeof(szDetail),
"interval_ms=%ld limit_us=%ld victim=%s sync=(%ld,%ld)",
tvDiff->tv_sec * 1000 + tvDiff->tv_usec / 1000,
static_cast<long>(g_lValidSyncInterval),
victim->GetName(),
static_cast<long>(e->lX),
static_cast<long>(e->lY));
ch->RecordAntiCheatViolation("SYNC_INTERVAL", 35, szDetail, true);
sys_err( "Too often SyncPosition Interval(%ldms)(%s) from Name(%s) VICTIM(%d,%d) SYNC(%d,%d)",
tvDiff->tv_sec * 1000 + tvDiff->tv_usec / 1000, victim->GetName(), ch->GetName(), victim->GetX(), victim->GetY(),
e->lX, e->lY );
@@ -1964,8 +2013,17 @@ int CInputMain::SyncPosition(LPCHARACTER ch, const char * c_pcData, size_t uiByt
}
else if( fDist > 25.0f )
{
LogManager::instance().HackLog( "SYNC_POSITION_HACK", ch );
char szDetail[192];
snprintf(szDetail,
sizeof(szDetail),
"dist=%.1f victim=%s cur=(%ld,%ld) sync=(%ld,%ld)",
fDist,
victim->GetName(),
static_cast<long>(victim->GetX()),
static_cast<long>(victim->GetY()),
static_cast<long>(e->lX),
static_cast<long>(e->lY));
ch->RecordAntiCheatViolation("SYNC_DISTANCE", 30, szDetail, true);
sys_err( "Too far SyncPosition Distance(%f)(%s) from Name(%s) CH(%d,%d) VICTIM(%d,%d) SYNC(%d,%d)",
fDist, victim->GetName(), ch->GetName(), ch->GetX(), ch->GetY(), victim->GetX(), victim->GetY(),
e->lX, e->lY );
@@ -3492,4 +3550,3 @@ int CInputDead::Analyze(LPDESC d, uint16_t wHeader, const char * c_pData)
return (this->*(it->second.handler))(d, c_pData);
}