
Analyse statique de rétro-ingénierie des heuristiques d'entrée de mouvement dans le moteur Source 2, identifiant les cas limites structurels dans l'automatisation des entrées et les routines de limitation du taux de saut.
server.dll)Ce document présente une analyse statique de rétro-ingénierie de deux routines de vérification de mouvement côté client implémentées dans le moteur Counter-Strike 2 (server.dll) : l'évaluateur d'automatisation d'entrée / SOCD (sub_1801C6B30, désigné CVE-2026-1801C / QUANTUM-SHIFT) et le limiteur de débit des requêtes de saut (sub_180A7EDB0, désigné CVE-2026-180A7 / BAL-JUMP). Grâce à l'analyse de désassemblage et à la modélisation mathématique formelle des machines à états sous-jacentes, nous démontrons des cas limites structurels dans les deux heuristiques. Plus précisément, nous documentons comment la quantification temporelle discrète dans sub_1801C6B30 crée une frontière de classification invariante sous des décalages de phase fixes à tick unique, et comment repose sur une métrique d'intervalle univariée ($\Delta t$) qui reste invariante sous des déclencheurs à impulsion unique verrouillés en phase.
sub_180A7EDB0sub_1801C6B30)server.dll (build commercial Win64)0x1C6B30void __fastcall sub_1801C6B30(int *pMovementServices, __int64 pPlayerController, __int64 pUserCmdPB)La routine évalue les événements de transition des boutons de mouvement (+moveleft / +moveright) soumis via les charges utiles client CBaseUserCmdPB. L'exécution suit un pipeline conditionnel :
sv_auto_cstrafe_attempt_window, valeur par défaut 100).sub_1801C6B30)void __fastcall sub_1801C6B30(int *pMovementServices, __int64 pPlayerController, __int64 pUserCmdPB)
{
if (!pPlayerController || (*(_BYTE *)(pUserCmdPB + 16) & 1) == 0)
return;
__int64 pPawn = sub_180B137A0(pPlayerController);
if (!pPawn || !(*(unsigned __int8 (__fastcall **)(__int64))(*(_QWORD *)pPawn + 3216i64))(pPawn))
return;
float *pVel = (float *)sub_1803CBD90(pPawn, &szVelocityBuf);
float vSq = (pVel[0] * pVel[0]) + (pVel[1] * pVel[1]) + (pVel[2] * pVel[2]);
// Filtrage : Seuil de vélocité minimale
if (vSq <= 18279.04f)
return;
int rawTicks = *(_DWORD *)(pUserCmdPB + 24);
bool bIsOverlap = (rawTicks >= 0);
int absTicks = bIsOverlap ? rawTicks : ~rawTicks;
double curTime = sub_1801F46D0(off_181C9F350);
if (curTime * 1e-9 * (double)absTicks >= 15.5)
return;
int roundedTicks = (int)V_roundd();
if ((unsigned int)roundedTicks > 15)
return;
// Attribution de symbole
char symbolCode;
if (roundedTicks == 0)
symbolCode = 1; // Succès / 0-tick parfait
else
symbolCode = bIsOverlap ? 2 : 3; // Chevauchement ou Sous-chevauchement
// Récupération des pointeurs de configuration ConVar
unsigned int windowCap = *GetConVarUInt(&unk_181DDBD30, &qword_181DDBD38);
if (windowCap - 1 > 999)
return;
sub_1801ECEA0(pMovementServices, windowCap);
// Ajout du symbole au tampon circulaire
int bufCap = pMovementServices[0];
if (bufCap > 0)
{
int writeIdx = pMovementServices[6];
if (writeIdx >= 0 && writeIdx < bufCap)
{
*(_BYTE *)(writeIdx + *((_QWORD *)pMovementServices + 1)) = symbolCode;
if (++pMovementServices[6] == bufCap)
pMovementServices[6] = 0;
}
}
// Mise à jour des histogrammes de fréquence de ticks discrets
if (bIsOverlap)
pMovementServices[roundedTicks + 7]++;
else
pMovementServices[roundedTicks + 23]++;
int seqLen = *GetConVarInt(&unk_181DDBD40, &qword_181DDBD48);
if ((unsigned int)(seqLen - 1) > 999)
return;
// Évaluation des métriques de séquence en fenêtre glissante
int totalValid = 0;
int totalOverlaps = 0;
int curSequenceSuccesses = 0;
int maxSequenceSuccesses = 0;
for (int i = 0; i < (int)windowCap; ++i)
{
if (i < bufCap)
{
int readIdx = i + pMovementServices[6] - bufCap;
if (i + pMovementServices[6] < bufCap)
readIdx = i + pMovementServices[6];
char sym = *(_BYTE *)(readIdx + *((_QWORD *)pMovementServices + 1));
if (sym != 0)
{
totalValid++;
if (sym == 2) totalOverlaps++;
else if (sym == 1) curSequenceSuccesses++;
}
}
// Mise à jour de la fenêtre glissante
if (i >= seqLen && (i - seqLen) >= 0)
{
if ((i - seqLen) < bufCap)
{
int popIdx = i + pMovementServices[6] - seqLen - bufCap;
if (i + pMovementServices[6] - seqLen < bufCap)
popIdx = i + pMovementServices[6] - seqLen;
if (*(_BYTE *)(popIdx + *((_QWORD *)pMovementServices + 1)) == 1)
curSequenceSuccesses--;
}
}
if (maxSequenceSuccesses < curSequenceSuccesses)
maxSequenceSuccesses = curSequenceSuccesses;
}
int minAttempts = *GetConVarInt(&unk_181DDBD20, &qword_181DDBD28);
float dynamicLimit = 0.0f;
if (totalValid >= minAttempts)
{
int minSuccessThreshold = *GetConVarInt(&unk_181DDBD50, &qword_181DDBD58);
if (maxSequenceSuccesses >= minSuccessThreshold)
{
float successRatio = 0.0f;
if (seqLen > minSuccessThreshold)
{
successRatio = (float)(maxSequenceSuccesses - minSuccessThreshold) /
(float)(seqLen - minSuccessThreshold);
}
float lowerPct = *GetConVarFloat(&unk_181DDBD70, &qword_181DDBD78); // Défaut 20.0%
float upperPct = *GetConVarFloat(&unk_181DDBD60, &qword_181DDBD68); // Défaut 5.0%
dynamicLimit = (lowerPct - upperPct) * successRatio + upperPct;
}
}
float observedOverlapPct = ((float)totalOverlaps / (float)totalValid) * 100.0f;
// Condition de violation : La limite dynamique dépasse le ratio de chevauchement observé
if (dynamicLimit > observedOverlapPct)
{
sub_1801ECEA0(pMovementServices, 0); // Vidage de l'état du tampon
bool bEnableKick = *GetConVarByte(&unk_181DDBD90, &qword_181DDBD98);
if (bEnableKick)
{
__int64 pEngine = qword_182012050;
void (__fastcall *pfnKickClient)(__int64, unsigned int, _QWORD, __int64) =
*(void (__fastcall **)(__int64, unsigned int, _QWORD, __int64))(*(_QWORD *)pEngine + 768i64);
int slotIdx = -1;
sub_181265470(pPlayerController, &slotIdx);
// Code de déconnexion 162 : NETWORK_DISCONNECT_KICKED_INPUTAUTOMATION
pfnKickClient(pEngine, (unsigned int)(slotIdx - 1), 0, 162);
}
}
}
Soit $\mathbf{S} = {s_1, s_2, \dots, s_N}$ la séquence de symboles d'événements dans le tampon d'historique de taille $N$. Le paramètre $S_{\text{max}}$ désigne le nombre maximal de symboles de code $1$ ($s_i = 1$) dans toute sous-séquence contiguë de longueur $L$ :
$$S_{\text{max}} = \max_{0 \le j \le N - L} \sum_{i=j}^{j+L-1} \mathbb{I}(s_i = 1)$$
La fonction de seuil adaptatif $T(S_{\text{max}})$ est conditionnée par $S_{\text{max}} \ge S_{\text{thresh}}$ :
$$T(S_{\text{max}}) = \begin{cases} \theta_{\text{upper}} + (\theta_{\text{lower}} - \theta_{\text{upper}}) \cdot \frac{S_{\text{max}} - S_{\text{thresh}}}{L - S_{\text{thresh}}} & \text{si } S_{\text{max}} \ge S_{\text{thresh}} \ 0 & \text{si } S_{\text{max}} < S_{\text{thresh}} \end{cases}$$
Où les valeurs de configuration par défaut sont définies comme suit :
sv_auto_cstrafe_sequence_length)sv_auto_cstrafe_success_threshold)sv_auto_cstrafe_lower_overlap_pct_threshold)sv_auto_cstrafe_upper_overlap_pct_threshold)Une violation est déclarée si et seulement si $T(S_{\text{max}}) > P_{\text{overlap}}$, où $P_{\text{overlap}}$ est le ratio d'échantillonnage des événements de code $2$ :
$$P_{\text{overlap}} = \frac{100}{N} \sum_{i=1}^{N} \mathbb{I}(s_i = 2)$$
Pour tout flux d'entrée où le décalage de phase $\Delta t_{\text{trans}}$ est contraint tel que $k = \text{round}(\Delta t_{\text{trans}} / \tau_{\text{tick}}) \ge 1$ :
pfnKickClient reste non exécuté.sub_180A7EDB0)server.dll (build commercial Win64)0x1A7EDB0void* __fastcall sub_180A7EDB0(__int64 pMovementServices, __int64 pMoveData)sub_180A7EDB0)void *__fastcall sub_180A7EDB0(__int64 pMovementServices, __int64 pMoveData)
{
__int64 pPawn = *(_QWORD *)(pMovementServices + 8);
sub_180C28550(pPawn, 2i64);
if (!*(_QWORD *)(pPawn + 56))
nullsub_908(pPawn);
// Évaluation de la coque de collision et du contact au sol
(*(void (__fastcall **)(_QWORD))(**(_QWORD **)(pPawn + 56) + 1624i64))(*(_QWORD *)(pPawn + 56));
sub_1803CBD30(*(_QWORD *)(pPawn + 56));
sub_1803CD4C0(*(_QWORD *)(pPawn + 56), v14);
if (dword_181FDB358)
--dword_181FDB358;
// Vérification du bit IN_JUMP (0x2) dans le masque de boutons de commande
if ((*(_BYTE *)(pPawn + 88) & 2) != 0 || sub_180C28550(pPawn, 2i64))
{
_BYTE *pPenaltyActive = (_BYTE *)(pMovementServices + 16);
if (*pPenaltyActive)
{
float curTime = *((float *)off_181C9F350 + 12);
float lastJumpTime = *(float *)(pMovementServices + 20);
float penaltyInterval = sub_18029CE70(&unk_181FDB4D8, 0xFFFFFFFFi64); // sv_jump_spam_penalty_time
// Évaluation d'intervalle : curTime > lastJumpTime + penaltyInterval
if (curTime > (penaltyInterval + lastJumpTime))
{
if (*pPenaltyActive)
{
sub_180A99DE0(pMovementServices + 16, 0xFFFFFFFFi64, 0xFFFFFFFFi64);
*pPenaltyActive = 0; // Effacement de l'état de pénalité
}
// Application de l'impulsion de saut
sub_180A7F550(pMovementServices, pMoveData);
if (!*pPenaltyActive)
{
sub_180A99DE0(pPenaltyActive, 0xFFFFFFFFi64, 0xFFFFFFFFi64);
*pPenaltyActive = 1; // Activation de l'état de pénalité
}
if (sub_180C28550(*(_QWORD *)(pMovementServices + 8), 2i64))
{
// Stockage de l'horodatage du saut : lastJumpTime = curTime - frameTime
*(float *)(pMovementServices + 20) = *((float *)off_181C9F350 + 12) -
*((float *)off_181C9F350 + 13);
}
return pMovementServices;
}
}
bool bDebugSpam = *GetConVarByte(&unk_181FDB640, &qword_181FDB648);
if (!bDebugSpam)
{
// Application de l'impulsion de saut lorsque le débogage de pénalité est désactivé
sub_180A7F550(pMovementServices, pMoveData);
if (!*pPenaltyActive)
{
sub_180A99DE0(pPenaltyActive, 0xFFFFFFFFi64, 0xFFFFFFFFi64);
*pPenaltyActive = 1;
}
*(float *)(pMovementServices + 20) = *((float *)off_181C9F350 + 12) -
*((float *)off_181C9F350 + 13);
}
}
return pMovementServices;
}
Soit $t_n$ l'horodatage d'exécution de la $n$-ième requête de saut, et $\tau_{\text{penalty}}$ la valeur de sv_jump_spam_penalty_time (typiquement $1$ tick, soit $\approx 15.625\text{ ms}$). La transition d'état pour le drapeau de pénalité $P \in {0, 1}$ et l'activation d'impulsion $I \in {0, 1}$ est définie par :
$$I_n = \begin{cases} 1 & \text{si } P_{n-1} = 0 \text{ ou } t_n - t_{n-1} > \tau_{\text{penalty}} \ 0 & \text{si } P_{n-1} = 1 \text{ et } t_n - t_{n-1} \le \tau_{\text{penalty}} \end{cases}$$
FL_ONGROUND ($P_{\text{ground}} = 1$), la durée entre requêtes correspond à la durée de vol balistique $T_{\text{flight}}$ :$$t_n - t_{n-1} = T_{\text{flight}} \ge \frac{2 \cdot v_z}{g} \approx 300\text{--}500\text{ ms}$$
Puisque $T_{\text{flight}} \gg \tau_{\text{penalty}}$, le prédicat $t_n - t_{n-1} > \tau_{\text{penalty}}$ est vérifié inconditionnellement pour tout $n$, empêchant l'activation du chemin de suppression.
| Composant | Symbole cible | Primitive actuelle | Faiblesse analytique | Remédiation proposée |
|---|---|---|---|---|
| Automatisation d'entrée | sub_1801C6B30 | Classification binaire ($k=0$ vs $k \ge 1$) + seuillage | Le décalage de phase fixe ($k=1$) donne $S_{\text{max}} = 0$ de manière identique | Évaluer la variance d'échantillonnage $\sigma^2(\Delta t)$ sur tous les $k$ ; signaler les distributions à faible variance quel que soit l'index de bac |
| Limitation du débit de saut | sub_180A7EDB0 | Comparaison scalaire de delta temporel ($t_n - t_{n-1} > \tau$) | Invariant sous les délais de phase en vol ($T_{\text{flight}} \gg \tau$) | Évaluer la continuité du vecteur de vélocité et la durée de contact au sol ($\Delta t_{\text{contact}}$) |