Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
工具/GitHubGitHub/misterdengi/cve-2026-1801c
漏洞分析漏洞利用逆向工程二进制分析论文与研究
GitHubmisterdengi/cve-2026-1801c

CVE-2026-1801C

对 Source 2 引擎中移动输入启发式算法的静态逆向工程分析,识别输入自动化与跳跃频率限制例程中的结构性边界情况。

查看仓库
241个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2026-1801C (QUANTUM-SHIFT) / CVE-2026-180A7 (BAL-JUMP):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 中的离散时间量化如何在固定单 tick 相位偏移下产生不变分类边界,以及 sub_180A7EDB0 如何依赖一个在锁相单脉冲触发下保持不变的单变量区间度量($\Delta t$)。


1. 输入自动化验证(sub_1801C6B30)

1.1 例程规格

  • 目标二进制:server.dll(Win64 正式版)
  • 符号偏移:0x1C6B30
  • 接口:void __fastcall sub_1801C6B30(int *pMovementServices, __int64 pPlayerController, __int64 pUserCmdPB)

1.2 执行流水线与状态表示

该例程评估通过客户端 CBaseUserCmdPB 载荷提交的移动按键转换事件(+moveleft / +moveright)。执行遵循条件流水线:

  1. 速度门控:玩家 Pawn 的二维速度标量平方必须超过静态阈值: $$|v_{xy}|^2 > (0.52 \cdot 260.0)^2 = 18279.04 \text{ unit}^2/\text{s}^2$$
  2. 时间量化:转换相位增量($\Delta t_{\text{trans}}$)被量化为离散帧 tick 单位: $$k = \text{round}\left( \frac{\Delta t_{\text{trans}}}{\tau_{\text{tick}}} \right), \quad \tau_{\text{tick}} \approx 15.625\text{ ms}$$
  3. 符号分类:整数值 $k \in [0, 15]$ 映射到离散事件代码 $c \in {1, 2, 3}$: $$c = \begin{cases} 1 & \text{if } k = 0 \text{(理想 0-tick 转换)} \ 2 & \text{if } k \ge 1 \text{ 且状态为 Overlap} \ 3 & \text{if } k \ge 1 \text{ 且状态为 Underlap} \end{cases}$$
  4. 环形缓冲区存储:分类代码 $c$ 存储在容量为 $N$ 的循环历史数组中(sv_auto_cstrafe_attempt_window,默认 100)。

1.3 反编译逻辑(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);
        }
    }
}

1.4 数学模型与分类分析

设 $\mathbf{S} = {s_1, s_2, \dots, s_N}$ 表示大小为 $N$ 的历史缓冲区中的事件符号序列。参数 $S_{\text{max}}$ 表示长度为 $L$ 的任何连续子序列中代码 $1$ 符号($s_i = 1$)的最大数量:

$$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{if } S_{\text{max}} \ge S_{\text{thresh}} \ 0 & \text{if } S_{\text{max}} < S_{\text{thresh}} \end{cases}$$

其中默认配置值定义如下:

  • $L = 15$(sv_auto_cstrafe_sequence_length)
  • $S_{\text{thresh}} = 10$(sv_auto_cstrafe_success_threshold)
  • $\theta_{\text{lower}} = 20.0$(sv_auto_cstrafe_lower_overlap_pct_threshold)
  • $\theta_{\text{upper}} = 5.0$(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)$$

下载工具