
Análise estática de engenharia reversa das heurísticas de entrada de movimento no motor Source 2, identificando casos extremos estruturais na automação de entrada e nas rotinas de limitação de taxa de salto.
server.dll)Este artigo apresenta uma análise estática de engenharia reversa de duas rotinas de verificação de movimento no lado do cliente implementadas no motor do Counter-Strike 2 (server.dll): o avaliador de automação de entrada / SOCD (sub_1801C6B30, designado CVE-2026-1801C / QUANTUM-SHIFT) e o limitador de taxa de solicitações de pulo (sub_180A7EDB0, designado CVE-2026-180A7 / BAL-JUMP). Através de análise de desmontagem e modelagem matemática formal das máquinas de estado subjacentes, demonstramos casos extremos estruturais em ambas as heurísticas. Especificamente, documentamos como a quantização temporal discreta em sub_1801C6B30 cria uma fronteira de classificação invariante sob deslocamentos de fase fixos de tick único, e como depende de uma métrica de intervalo univariante ($\Delta t$) que permanece invariante sob gatilhos de impulso único com bloqueio de fase.
sub_180A7EDB0sub_1801C6B30)server.dll (Build Retail Win64)0x1C6B30void __fastcall sub_1801C6B30(int *pMovementServices, __int64 pPlayerController, __int64 pUserCmdPB)A rotina avalia eventos de transição de botões de movimento (+moveleft / +moveright) enviados via payloads CBaseUserCmdPB do cliente. A execução segue um pipeline condicional:
sv_auto_cstrafe_attempt_window, padrão 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]);
// Filtragem: Limite mínimo de velocidade
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;
// Atribuição de Símbolo
char symbolCode;
if (roundedTicks == 0)
symbolCode = 1; // Sucesso / 0-tick perfeito
else
symbolCode = bIsOverlap ? 2 : 3; // Overlap ou Underlap
// Buscar ponteiros de configuração ConVar
unsigned int windowCap = *GetConVarUInt(&unk_181DDBD30, &qword_181DDBD38);
if (windowCap - 1 > 999)
return;
sub_1801ECEA0(pMovementServices, windowCap);
// Anexar símbolo ao buffer circular
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;
}
}
// Atualizar histogramas de frequência de ticks discretos
if (bIsOverlap)
pMovementServices[roundedTicks + 7]++;
else
pMovementServices[roundedTicks + 23]++;
int seqLen = *GetConVarInt(&unk_181DDBD40, &qword_181DDBD48);
if ((unsigned int)(seqLen - 1) > 999)
return;
// Avaliar métricas de sequência de janela deslizante
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++;
}
}
// Atualização da janela deslizante
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); // Padrão 20.0%
float upperPct = *GetConVarFloat(&unk_181DDBD60, &qword_181DDBD68); // Padrão 5.0%
dynamicLimit = (lowerPct - upperPct) * successRatio + upperPct;
}
}
float observedOverlapPct = ((float)totalOverlaps / (float)totalValid) * 100.0f;
// Condição de Violação: Limite dinâmico excede a proporção de overlap observada
if (dynamicLimit > observedOverlapPct)
{
sub_1801ECEA0(pMovementServices, 0); // Limpar estado do buffer
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);
// Código de Desconexão 162: NETWORK_DISCONNECT_KICKED_INPUTAUTOMATION
pfnKickClient(pEngine, (unsigned int)(slotIdx - 1), 0, 162);
}
}
}
Seja $\mathbf{S} = {s_1, s_2, \dots, s_N}$ a sequência de símbolos de eventos no buffer de histórico de tamanho $N$. O parâmetro $S_{\text{max}}$ denota o número máximo de símbolos de código $1$ ($s_i = 1$) dentro de qualquer sub-sequência contígua de comprimento $L$:
$$S_{\text{max}} = \max_{0 \le j \le N - L} \sum_{i=j}^{j+L-1} \mathbb{I}(s_i = 1)$$
A função de limite adaptativo $T(S_{\text{max}})$ é condicionada a $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{se } S_{\text{max}} \ge S_{\text{thresh}} \ 0 & \text{se } S_{\text{max}} < S_{\text{thresh}} \end{cases}$$
Onde os valores de configuração padrão são definidos como:
sv_auto_cstrafe_sequence_length)sv_auto_cstrafe_success_threshold)sv_auto_cstrafe_lower_overlap_pct_threshold)sv_auto_cstrafe_upper_overlap_pct_threshold)Uma violação é declarada se e somente se $T(S_{\text{max}}) > P_{\text{overlap}}$, onde $P_{\text{overlap}}$ é a proporção amostral de eventos de código $2$:
$$P_{\text{overlap}} = \frac{100}{N} \sum_{i=1}^{N} \mathbb{I}(s_i = 2)$$
Para qualquer fluxo de entrada onde o deslocamento de fase $\Delta t_{\text{trans}}$ é restrito de modo que $k = \text{round}(\Delta t_{\text{trans}} / \tau_{\text{tick}}) \ge 1$:
pfnKickClient permanece não executado.sub_180A7EDB0)server.dll (Build Retail 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);
// Avaliação do hull de colisão e contato com o solo
(*(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;
// Verificar bit IN_JUMP (0x2) na máscara de botões do comando
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
// Avaliação de intervalo: curTime > lastJumpTime + penaltyInterval
if (curTime > (penaltyInterval + lastJumpTime))
{
if (*pPenaltyActive)
{
sub_180A99DE0(pMovementServices + 16, 0xFFFFFFFFi64, 0xFFFFFFFFi64);
*pPenaltyActive = 0; // Limpar estado de penalidade
}
// Aplicar impulso de pulo
sub_180A7F550(pMovementServices, pMoveData);
if (!*pPenaltyActive)
{
sub_180A99DE0(pPenaltyActive, 0xFFFFFFFFi64, 0xFFFFFFFFi64);
*pPenaltyActive = 1; // Ativar estado de penalidade
}
if (sub_180C28550(*(_QWORD *)(pMovementServices + 8), 2i64))
{
// Armazenar timestamp do pulo: 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)
{
// Aplicar impulso de pulo quando o debug de penalidade está desabilitado
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;
}
Seja $t_n$ o timestamp de execução da $n$-ésima solicitação de pulo, e $\tau_{\text{penalty}}$ o valor de sv_jump_spam_penalty_time (tipicamente $1$ tick, ou $\approx 15.625\text{ ms}$). A transição de estado para o flag de penalidade $P \in {0, 1}$ e a ativação de impulso $I \in {0, 1}$ é definida por:
$$I_n = \begin{cases} 1 & \text{se } P_{n-1} = 0 \text{ ou } t_n - t_{n-1} > \tau_{\text{penalty}} \ 0 & \text{se } P_{n-1} = 1 \text{ e } t_n - t_{n-1} \le \tau_{\text{penalty}} \end{cases}$$
FL_ONGROUND ($P_{\text{ground}} = 1$), a duração entre solicitações corresponde à duração do voo balístico $T_{\text{flight}}$:$$t_n - t_{n-1} = T_{\text{flight}} \ge \frac{2 \cdot v_z}{g} \approx 300\text{--}500\text{ ms}$$
Como $T_{\text{flight}} \gg \tau_{\text{penalty}}$, o predicado $t_n - t_{n-1} > \tau_{\text{penalty}}$ é válido incondicionalmente para todo $n$, impedindo a ativação do caminho de supressão.
| Componente | Símbolo Alvo | Primitiva Atual | Fraqueza Analítica | Remediação Proposta |
|---|---|---|---|---|
| Automação de Entrada | sub_1801C6B30 | Classificação binária ($k=0$ vs $k \ge 1$) + limiarização | Deslocamento de fase fixo ($k=1$) resulta em $S_{\text{max}} = 0$ identicamente | Avaliar a variância amostral $\sigma^2(\Delta t)$ em todos os $k$; sinalizar distribuições de baixa variância independentemente do índice do bin |
| Limitação de Taxa de Pulo | sub_180A7EDB0 | Comparação escalar de delta de tempo ($t_n - t_{n-1} > \tau$) | Invariante sob atrasos de fase em voo ($T_{\text{flight}} \gg \tau$) | Avaliar a continuidade do vetor de velocidade e a duração do contato com o solo ($\Delta t_{\text{contact}}$) |