Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Гайд CS2 Anti-Aim после обновления AG2 — Разбор логики и базы

Sloppy
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
770
Реакции
20
Здарова, реверсеры.

С выходом апдейта AG2 в CS2 многие старые наработки по анти-аимам благополучно отправились на свалку истории. Source 2 — это вам не костыльный движок CS:GO, здесь манипуляции с углами и десинком требуют куда более глубокого понимания того, как работают сабтик и стейт-машина игрока.

Попал в руки один забавный фрагмент кода, который якобы сгенерировала нейронка. Сразу видно, что это голая база, собранная из кусков старых паблик-сурсов и попыток подстроиться под новую схему. Работать «из коробки» на актуальном билде оно, конечно же, не будет, но для тех, кто только вкатывается в C++ и реверс CS2, это неплохой пример того, на каких оффсетах сейчас строят проверки.

В коде реализованы базовые проверки на состояние игрока (health, movetype) и попытка отсечки AA во время атаки через чтение стейта кнопок. Сама логика крутилок — классический набор из Jitter, Spin и Backwards.

Код:
Expand Collapse Copy
#include "antiaim.h"
#include "sdk/buttons.hpp"
#include <cmath>
 
namespace AntiAim
{
    constexpr std::ptrdiff_t kButtonCurrentStateOffset = 0x8;
 
    static inline float SanitizeViewComponent(float v, float fallback)
    {
        if (!std::isfinite(v)) return fallback;
        return v;
    }
 
    int GetMoveType(uintptr_t pawn)
    {
        if (!pawn) return MOVETYPE_NONE;
        return static_cast<int>(Game::Read<uint32_t>(pawn + SCHEMA_CLIENT("C_BaseEntity", "m_MoveType")));
    }
 
    bool IsAttacking()
    {
        if (!Game::clientBase) return false;
        uintptr_t btnAddr = Game::clientBase + cs2_dumper::buttons::attack;
        uint32_t attackState = Game::Read<uint32_t>(btnAddr + kButtonCurrentStateOffset);
        return (attackState & 1) != 0;
    }
 
    bool Apply(uintptr_t localPawn, float viewPitch, float viewYaw, float& outPitch, float& outYaw)
    {
        state.active = false;
        state.desyncDelta = 0.f;
 
        if (!config.enabled || !localPawn)
            return false;
 
        if (config.pitchType == PITCH_NONE && config.yawType == YAW_NONE)
            return false;
 
        int health = Game::Read<int32_t>(localPawn + SCHEMA_CLIENT("C_BaseEntity", "m_iHealth"));
        if (health <= 0)
            return false;
 
        int moveType = GetMoveType(localPawn);
        if (moveType == MOVETYPE_NONE || moveType == MOVETYPE_LADDER || moveType == MOVETYPE_NOCLIP)
            return false;
 
        if (IsAttacking())
            return false;
 
        viewPitch = SanitizeViewComponent(viewPitch, 0.f);
        viewYaw = SanitizeViewComponent(viewYaw, 0.f);
 
        outPitch = viewPitch;
        outYaw = viewYaw;
 
        switch (config.pitchType)
        {
        case PITCH_NONE:  break;
        case PITCH_DOWN:  outPitch = kPitchMax;  break;
        case PITCH_UP:    outPitch = kPitchMin;  break;
        case PITCH_ZERO:  outPitch = 0.0f;       break;
        default: break;
        }
 
        switch (config.yawType)
        {
        case YAW_NONE:
            break;
        case YAW_BACKWARDS:
            outYaw = NormalizeAngle(outYaw + 180.0f);
            break;
        case YAW_JITTER:
            jitterSide = !jitterSide;
            outYaw = NormalizeAngle(viewYaw +
                (jitterSide ? config.jitterRange : -config.jitterRange));
            break;
        case YAW_SPIN:
            spinAngle = NormalizeAngle(spinAngle + config.spinSpeed);
            outYaw = NormalizeAngle(viewYaw + spinAngle);
            break;
        default:
            break;
        }
 
        outPitch = ClampPitch(outPitch);
        outYaw = NormalizeAngle(outYaw);
 
        state.active = true;
        state.realPitch = viewPitch;
        state.realYaw = viewYaw;
        state.fakePitch = outPitch;
        state.fakeYaw = outYaw;
        float delta = outYaw - viewYaw;
        while (delta > 180.f) delta -= 360.f;
        while (delta < -180.f) delta += 360.f;
        state.desyncDelta = delta;
        state.pitchMode = config.pitchType;
        state.yawMode = config.yawType;
 
        return true;
    }
}

Основная проблема этой пасты — она не учитывает специфику работы с usercmd в актуальных условиях. Сейчас просто переписать углы в памяти недостаточно, нужно вклиниваться в процесс формирования пакетов, чтобы сервер не выкинул вас за пределы нормы или не начал «трясти» камеру.

Если кто-то копал глубже в сторону десинка через манипуляцию тиками в обновленном Source 2, делитесь мыслями — кажется, эра простых анти-аимов окончательно ушла, и теперь всё упирается в чистый реверс серверных проверок.
 
Hello, reversers.

With the release of the AG2 update in CS2, many old anti-aiming techniques were consigned to the dustbin of history. Source 2 isn't the hacky CS:GO engine; manipulating angles and desync here requires a much deeper understanding of how subtick and player state machines work.

I stumbled across a funny code fragment, supposedly generated by a neural network. It's immediately obvious that it's a barebones database, cobbled together from scraps of old public resources and attempts to adapt to the new system. It certainly won't work out of the box on the current build, but for those just getting started with C++ and CS2 reverse engineering, it's a good example of the offsets currently used for checks.

The code implements basic player state checks (health, movetype) and an attempt to cut off AA during an attack by reading the button state. The spinner logic itself is a classic combination of Jitter, Spin, and Backwards.

Код:
Expand Collapse Copy
#include "antiaim.h"
#include "sdk/buttons.hpp"
#include <cmath>
 
namespace AntiAim
{
    constexpr std::ptrdiff_t kButtonCurrentStateOffset = 0x8;
 
    static inline float SanitizeViewComponent(float v, float fallback)
    {
        if (!std::isfinite(v)) return fallback;
        return v;
    }
 
    int GetMoveType(uintptr_t pawn)
    {
        if (!pawn) return MOVETYPE_NONE;
        return static_cast<int>(Game::Read<uint32_t>(pawn + SCHEMA_CLIENT("C_BaseEntity", "m_MoveType")));
    }
 
    bool IsAttacking()
    {
        if (!Game::clientBase) return false;
        uintptr_t btnAddr = Game::clientBase + cs2_dumper::buttons::attack;
        uint32_t attackState = Game::Read<uint32_t>(btnAddr + kButtonCurrentStateOffset);
        return (attackState & 1) != 0;
    }
 
    bool Apply(uintptr_t localPawn, float viewPitch, float viewYaw, float& outPitch, float& outYaw)
    {
        state.active = false;
        state.desyncDelta = 0.f;
 
        if (!config.enabled || !localPawn)
            return false;
 
        if (config.pitchType == PITCH_NONE && config.yawType == YAW_NONE)
            return false;
 
        int health = Game::Read<int32_t>(localPawn + SCHEMA_CLIENT("C_BaseEntity", "m_iHealth"));
        if (health <= 0)
            return false;
 
        int moveType = GetMoveType(localPawn);
        if (moveType == MOVETYPE_NONE || moveType == MOVETYPE_LADDER || moveType == MOVETYPE_NOCLIP)
            return false;
 
        if (IsAttacking())
            return false;
 
        viewPitch = SanitizeViewComponent(viewPitch, 0.f);
        viewYaw = SanitizeViewComponent(viewYaw, 0.f);
 
        outPitch = viewPitch;
        outYaw = viewYaw;
 
        switch (config.pitchType)
        {
        case PITCH_NONE: break;
        case PITCH_DOWN: outPitch = kPitchMax; break;
        case PITCH_UP: outPitch = kPitchMin; break;
        case PITCH_ZERO: outPitch = 0.0f;    break;
        default: break;
        }
 
        switch (config.yawType)
        {
        case YAW_NONE:
            break;
        case YAW_BACKWARDS:
            outYaw = NormalizeAngle(outYaw + 180.0f);
            break;
        case YAW_JITTER:
            jitterSide = !jitterSide;
            outYaw = NormalizeAngle(viewYaw +
                (jitterSide ? config.jitterRange : -config.jitterRange));
            break;
        case YAW_SPIN:
            spinAngle = NormalizeAngle(spinAngle + config.spinSpeed);
            outYaw = NormalizeAngle(viewYaw + spinAngle);
            break;
        default:
            break;
        }
 
        outPitch = ClampPitch(outPitch);
        outYaw = NormalizeAngle(outYaw);
 
        state.active = true;
        state.realPitch = viewPitch;
        state.realYaw = viewYaw;
        state.fakePitch = outPitch;
        state.fakeYaw = outYaw;
        float delta = outYaw - viewYaw;
        while (delta > 180.f) delta -= 360.f;
        while (delta < -180.f) delta += 360.f;
        state.desyncDelta = delta;
        state.pitchMode = config.pitchType;
        state.yawMode = config.yawType;
 
        return true;
    }
}

The main problem with this paste is that it doesn't take into account the specifics of working with usercmd in current conditions. Nowadays, simply rewriting memory corners isn't enough; you need to intervene in the packet generation process to prevent the server from throwing you out of bounds or causing camera shake.

If anyone has delved deeper into the desinking through tick manipulation in the updated Source 2, please share your thoughts—it seems the era of simple anti-aim is finally over, and now everything comes down to pure reverse engineering of server checks.
Просто убей себя уже, бесполезные посты AI-slop
 
Назад
Сверху Снизу