RCE Endeavors 😅

July 5, 2022

Creating an Aimbot: Building the Aimbot (3/4)

Filed under: Game Hacking,Programming,Reverse Engineering — admin @ 9:29 PM

Table of Contents:

The previous post covered reverse engineering the Half-Life 2 executable and finding addresses of functions that would allow us to get entity eye positions and eye angles. This was done by means of initially getting a pointer to a global CServerTools interface and tracing through its member functions. This interface will also be useful as it allows for iteration of entities in the game’s entity list. In order to build the aimbot, we need to do the following:

  • Iterate through the entities
    • If the entity is an enemy, find the distance between the entity and the player
    • Keep track of the closest entity
  • Calculate the eye-to-eye vector between the player and the closest enemy
  • Adjust the player’s eye angle to follow this vector

From the previous post, we know that we can call GetPlayerPosition to get the player’s position. To loop through the entity list, we can call FirstEntity and NextEntity, which return a pointer to a CBaseEntity instance. To see whether an entity is an enemy, we can compare the entity name against a set of hostile NPC entity names. If we have an enemy entity, then we calculate the distance between the player and it, and save the position of the entity if it is the closest that we’ve seen so far.

After iterating through the entire entity list, we take the closest enemy, calculate the eye-to-eye vector, and adjust the player’s eye angles with the aid of the VectorAngles function.

Putting it into code, we get the following

auto* serverEntity{ reinterpret_cast<IServerEntity*>(
    GetServerTools()->FirstEntity()) };

if (serverEntity != nullptr) {
    do {
        if (serverEntity == GetServerTools()->FirstEntity()) {

            SetPlayerEyeAnglesToPosition(closestEnemyVector);
            closestEnemyDistance = std::numeric_limits<float>::max();
            closestEnemyVector = GetFurthestVector();
        }

        auto* modelName{ serverEntity->GetModelName().ToCStr() };
        if (modelName != nullptr) {
            auto entityName{ std::string{GetEntityName(serverEntity)} };

            if (IsEntityEnemy(entityName)) {
                Vector eyePosition{};
                QAngle eyeAngles{};

                GetServerTools()->GetPlayerPosition(eyePosition, eyeAngles);

                auto enemyEyePosition{ GetEyePosition(serverEntity) };

                auto distance{ VectorDistance(enemyEyePosition, eyePosition) };
                if (distance <= closestEnemyDistance) {
                    closestEnemyDistance = distance;
                    closestEnemyVector = enemyEyePosition;
                }
            }
        }

        serverEntity = reinterpret_cast<IServerEntity*>(
            GetServerTools()->NextEntity(serverEntity));

    } while (serverEntity != nullptr);
}

There are a few helper functions here that were not covered earlier: the GetFurthestVector function returns a vector with max float values in the x, y, and z fields; GetEntityName returns the entity name as a string by getting the m_iName member of the CBaseEntity instance; and IsEntityEnemy just checks the entity name against a set of enemy NPCs.

The vector math and new view angle calculation happens in SetPlayerEyeAnglesToPosition, which is shown below:

void SetPlayerEyeAnglesToPosition(const Vector& enemyEyePosition) {

    Vector eyePosition{};
    QAngle eyeAngles{};
    GetServerTools()->GetPlayerPosition(eyePosition, eyeAngles);

    Vector forwardVector{ enemyEyePosition.x - eyePosition.x,
        enemyEyePosition.y - eyePosition.y,
        enemyEyePosition.z - eyePosition.z
    };

    VectorNormalize(forwardVector);

    QAngle newEyeAngles{};
    VectorAngles(forwardVector, newEyeAngles);

    SnapEyeAngles(GetLocalPlayer(), newEyeAngles);
}

This function calculates the eye-to-eye vector by subtracting the enemies’ eye position vector with the player’s eye position vector. This new vector is then normalized and passed to the VectorAngles function to calculate the new view angles. Lastly, the player’s eye angles get adjusted to these new angles, which should have the effect of tracking to the entity.

What does this look like in action?

You can see the (very faint) crosshair follow the NPCs head as it walks across the room. When the NPC walks far enough away, the code snaps to a closer NPC. It works!

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

 

Powered by WordPress