
تحليل هندسة عكسية ثابتة لاستدلالات إدخال الحركة في محرك Source 2، مع تحديد الحالات الحدودية البنيوية في أتمتة الإدخال وروتينات الحد من معدل القفز.
server.dll)تقدّم هذه الورقة تحليلاً هندسيًا عكسيًا ثابتًا لروتينين للتحقق من الحركة من جانب العميل مطبّقين في محرك Counter-Strike 2 (server.dll): مُقيّم أتمتة الإدخال / SOCD (sub_1801C6B30، المعيّن CVE-2026-1801C / QUANTUM-SHIFT) ومُحدِّد معدل طلبات القفز (sub_180A7EDB0، المعيّن CVE-2026-180A7 / BAL-JUMP). من خلال تحليل التفكيك والنمذجة الرياضية الرسمية لآلات الحالة الأساسية، نُظهر حالات حافة بنيوية في كلا الخوارزميتين. على وجه التحديد، نوثّق كيف يُنشئ التكميم الزمني المنفصل في sub_1801C6B30 حدًا تصنيفيًا ثابتًا تحت إزاحات طورية ثابتة بإطار واحد، وكيف يعتمد sub_180A7EDB0 على مقياس فاصل زمني أحادي المتغير ($\Delta t$) يبقى ثابتًا تحت محفزات نبضية مفردة مقفلة الطور.
sub_1801C6B30)server.dll (إصدار Win64 Retail)0x1C6B30void __fastcall sub_1801C6B30(int *pMovementServices, __int64 pPlayerController, __int64 pUserCmdPB)يقيّم الروتين أحداث انتقال أزرار الحركة (+moveleft / +moveright) المُرسلة عبر حمولات CBaseUserCmdPB من العميل. يتبع التنفيذ خط أنابيب شرطيًا:
sv_auto_cstrafe_attempt_window، الافتراضي 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]);
// Gating: Minimum velocity threshold
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;
// Symbol Assignment
char symbolCode;
if (roundedTicks == 0)
symbolCode = 1; // Success / Perfect 0-tick
else
symbolCode = bIsOverlap ? 2 : 3; // Overlap or Underlap
// Fetch ConVar configuration pointers
unsigned int windowCap = *GetConVarUInt(&unk_181DDBD30, &qword_181DDBD38);
if (windowCap - 1 > 999)
return;
sub_1801ECEA0(pMovementServices, windowCap);
// Append symbol to circular buffer
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;
}
}
// Update discrete tick frequency histograms
if (bIsOverlap)
pMovementServices[roundedTicks + 7]++;
else
pMovementServices[roundedTicks + 23]++;
int seqLen = *GetConVarInt(&unk_181DDBD40, &qword_181DDBD48);
if ((unsigned int)(seqLen - 1) > 999)
return;
// Evaluate sliding window sequence metrics
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++;
}
}
// Sliding window update
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); // Default 20.0%
float upperPct = *GetConVarFloat(&unk_181DDBD60, &qword_181DDBD68); // Default 5.0%
dynamicLimit = (lowerPct - upperPct) * successRatio + upperPct;
}
}
float observedOverlapPct = ((float)totalOverlaps / (float)totalValid) * 100.0f;
// Violation Condition: Dynamic limit exceeds observed overlap ratio
if (dynamicLimit > observedOverlapPct)
{
sub_1801ECEA0(pMovementServices, 0); // Flush buffer state
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);
// Disconnect Code 162: NETWORK_DISCONNECT_KICKED_INPUTAUTOMATION
pfnKickClient(pEngine, (unsigned int)(slotIdx - 1), 0, 162);
}
}
}
لنفترض أن $\mathbf{S} = {s_1, s_2, \dots, s_N}$ يمثل تسلسل رموز الأحداث في مخزن التاريخ بحجم $N$. تشير المعلمة $S_{\text{max}}$ إلى الحد الأقصى لعدد رموز الكود $1$ ($s_i = 1$) داخل أي تسلسل فرعي متصل بطول $L$:
$$S_{\text{max}} = \max_{0 \le j \le N - L} \sum_{i=j}^{j+L-1} \mathbb{I}(s_i = 1)$$
دالة العتبة التكيفية $T(S_{\text{max}})$ مشروطة بـ $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{إذا كان } S_{\text{max}} \ge S_{\text{thresh}} \ 0 & \text{إذا كان } S_{\text{max}} < S_{\text{thresh}} \end{cases}$$
حيث تُعرَّف قيم التكوين الافتراضية على النحو التالي:
sv_auto_cstrafe_sequence_length)sv_auto_cstrafe_success_threshold)sv_auto_cstrafe_lower_overlap_pct_threshold)sv_auto_cstrafe_upper_overlap_pct_threshold)يُعلن عن انتهاك إذا وفقط إذا كان $T(S_{\text{max}}) > P_{\text{overlap}}$، حيث $P_{\text{overlap}}$ هي نسبة العينة لأحداث الكود $2$:
$$P_{\text{overlap}} = \frac{100}{N} \sum_{i=1}^{N} \mathbb{I}(s_i = 2)$$
لأي تدفق إدخال حيث يكون إزاحة الطور $\Delta t_{\text{trans}}$ مقيدة بحيث $k = \text{round}(\Delta t_{\text{trans}} / \tau_{\text{tick}}) \ge 1$:
pfnKickClient غير منفَّذ.sub_180A7EDB0)server.dll (إصدار Win64 Retail)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);
// Collision hull & ground contact evaluation
(*(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;
// Check IN_JUMP bit (0x2) in command button mask
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
// Interval evaluation: curTime > lastJumpTime + penaltyInterval
if (curTime > (penaltyInterval + lastJumpTime))
{
if (*pPenaltyActive)
{
sub_180A99DE0(pMovementServices + 16, 0xFFFFFFFFi64, 0xFFFFFFFFi64);
*pPenaltyActive = 0; // Clear penalty state
}
// Apply jump impulse
sub_180A7F550(pMovementServices, pMoveData);
if (!*pPenaltyActive)
{
sub_180A99DE0(pPenaltyActive, 0xFFFFFFFFi64, 0xFFFFFFFFi64);
*pPenaltyActive = 1; // Engage penalty state
}
if (sub_180C28550(*(_QWORD *)(pMovementServices + 8), 2i64))
{
// Store jump timestamp: 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)
{
// Apply jump impulse when penalty debug is disabled
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;
}
لنفترض أن $t_n$ يشير إلى الطابع الزمني لتنفيذ طلب القفز رقم $n$، وأن $\tau_{\text{penalty}}$ يشير إلى قيمة sv_jump_spam_penalty_time (عادةً إطار واحد، أو $\approx 15.625\text{ مللي ثانية}$). يُعرَّف انتقال الحالة لعلامة العقوبة $P \in {0, 1}$ وتفعيل الدفعة $I \in {0, 1}$ على النحو التالي:
$$I_n = \begin{cases} 1 & \text{إذا كان } P_{n-1} = 0 \text{ أو } t_n - t_{n-1} > \tau_{\text{penalty}} \ 0 & \text{إذا كان } P_{n-1} = 1 \text{ و } t_n - t_{n-1} \le \tau_{\text{penalty}} \end{cases}$$
FL_ONGROUND ($P_{\text{ground}} = 1$)، فإن مدة الفاصل بين الطلبات تتوافق مع مدة الطيران الباليستي $T_{\text{flight}}$:$$t_n - t_{n-1} = T_{\text{flight}} \ge \frac{2 \cdot v_z}{g} \approx 300\text{--}500\text{ مللي ثانية}$$
بما أن $T_{\text{flight}} \gg \tau_{\text{penalty}}$، فإن المسند $t_n - t_{n-1} > \tau_{\text{penalty}}$ يتحقق بشكل غير مشروط لجميع $n$، مما يمنع تفعيل مسار القمع.
| المكوّن | الرمز المستهدف | البدائية الحالية | الضعف التحليلي | المعالجة المقترحة |
|---|---|---|---|---|
| أتمتة الإدخال | sub_1801C6B30 | تصنيف ثنائي ($k=0$ مقابل $k \ge 1$) + تحديد عتبة | إزاحة الطور الثابتة ($k=1$) تنتج $S_{\text{max}} = 0$ بشكل مطابق | تقييم تباين العينة $\sigma^2(\Delta t)$ عبر جميع قيم $k$؛ وضع علامة على التوزيعات منخفضة التباين بغض النظر عن فهرس الحاوية |
| تحديد معدل القفز | sub_180A7EDB0 | مقارنة فرق زمني قياسي ($t_n - t_{n-1} > \tau$) | ثابت تحت تأخيرات الطور الجوي ($T_{\text{flight}} \gg \tau$) | تقييم استمرارية متجه السرعة ومدة ملامسة الأرض ($\Delta t_{\text{contact}}$) |