- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 697
- Реакции
- 18
Для тех, кто до сих пор ковыряет мамонта 1.6 или просто хочет понять базу GoldSrc, выкладываю краткий мануал по поиску Entity List. Несмотря на возраст игры, структура движка — отличный полигон для обучения реверсу.
Методика в IDA Pro
Реверсим hw.dll и ищем строку:
Переходим по Xref (перекрестным ссылкам). Если подняться чуть выше по коду, можно легко выцепить указатели на Entity List и Max Clients. Обычно они лежат рядом.
Сигнатуры под актуальные билды
Если лень искать руками, вот готовые сигнатуры:
Техническая часть и оффсеты
Найденный оффсет указывает на указатель (поинтер) структуры edict_t. Сразу через 4 байта после начала поинтера лежит значение max clients (по дефолту там 32).
Чтобы вытащить данные конкретного противника (его базу, HP и прочее), работаем через pvPrivateData:
Для тех, кто пишет свой internal или external софт, пригодится правильная структура clientdata_t для понимания лейаута памяти:
Метод старый как мир, но в 1.6 до сих пор работает безотказно. В GoldSrc всё вертится вокруг этих структур, так что для написания легит-бота или простого ВХ этого набора данных хватит за глаза.
Интересно, юзает ли сейчас кто-то более изощренные способы обхода в старой контре кроме стандартного хука функций движка.
Методика в IDA Pro
Реверсим hw.dll и ищем строку:
Код:
"%s" %s %s %d %d %d %d\n
Переходим по Xref (перекрестным ссылкам). Если подняться чуть выше по коду, можно легко выцепить указатели на Entity List и Max Clients. Обычно они лежат рядом.
Сигнатуры под актуальные билды
Если лень искать руками, вот готовые сигнатуры:
- A36B4:
Код:
A1 ? ? ? ? 53 8B 1D ? ? ? ? 56 85 C0 - A36BA:
Код:
8B 1D ? ? ? ? 56 85 C0
Техническая часть и оффсеты
Найденный оффсет указывает на указатель (поинтер) структуры edict_t. Сразу через 4 байта после начала поинтера лежит значение max clients (по дефолту там 32).
Чтобы вытащить данные конкретного противника (его базу, HP и прочее), работаем через pvPrivateData:
Код:
// Через edict_t получаем указатель на CBasePlayer
uintptr_t pCBasePlayer = *(uintptr_t*)(pEdict + 0x4B9C); // pvPrivateData
// Читаем здоровье (health) по смещению 0x1e0
int health = *(int*)(pCBasePlayer + 0x1e0);
Для тех, кто пишет свой internal или external софт, пригодится правильная структура clientdata_t для понимания лейаута памяти:
Код:
struct clientdata_t
{
std::uint8_t pad[0x88];
vec3_t origin;
vec3_t velocity;
int viewmodel;
vec3_t punchangle;
int flags;
int waterlevel;
int watertype;
vec3_t view_ofs;
float health;
int b_in_duck;
int weapons;
int fl_time_step_sound;
int fl_duck_time;
int fl_swim_time;
int waterjumptime;
float maxspeed;
float fov;
int weaponanim;
int m_i_id;
int ammo_shells;
int ammo_nails;
int ammo_cells;
int ammo_rockets;
float m_fl_next_attack;
int tfstate;
int pushmsec;
int deadflag;
char physinfo[64];
int iuser1;
int iuser2;
int iuser3;
int iuser4;
float fuser1;
float fuser2;
float fuser3;
float fuser4;
vec3_t vuser1;
vec3_t vuser2;
vec3_t vuser3;
vec3_t vuser4;
};
Метод старый как мир, но в 1.6 до сих пор работает безотказно. В GoldSrc всё вертится вокруг этих структур, так что для написания легит-бота или простого ВХ этого набора данных хватит за глаза.
Интересно, юзает ли сейчас кто-то более изощренные способы обхода в старой контре кроме стандартного хука функций движка.