Участник
-
Автор темы
- #1
(микротуториал)
все квары и комманды(и прочие плюхи) есть тут(дамп от 08.09.2022)
1) находим сами квары:
Берем интерфейс(CreateInterface)(
tier0.dll:
VEngineCvar007 -> CCvar
в нем тыкаем на все интересное(и внутри того на что тыкали так же тыкаем)
на оффсете 0x40 лежит кучка интересностей
обсматриваем эти интересности
ну думаю очевидно что это конвар
сразу в консоли замечаем что тип квара у нас boolean(true/false)
видим что структура квара имеет размер 80 байт
первый указатель это имя
второй указатель не интересен нам(он указывает на некст квар токо не совсем на него а на ячейку перед ним но не суть. у нас и так все квары в массиве зачем нам некст)
третий и четвертый указатели(если это указатели) нам пока не интересны они везде по нулям
пятая ячейка это забегая вперед скажу - два инта: тип и какаято хуита
дальше идёт инт флаги
дальше идёт указатель на какуюто хуйню
дальше идёт юнион значения.
чтобы определить где там тип где там что, берем квары разных типов и сравниваем что же у них разное
сразу бросается в глаза после описания идёт какойто инт который у обоих различается
и потом еще ниже тоже различается инт(это флаги)
простой способ проверить - тупо МЕНЯЕМ эти неизвестные хрени.
у tv_broadcast_url например семёрку меняем на ноль
до модификации:
после:
ну сразу видим что это оказывается ТИП конвара, и судя по всему 0 это булеана, а 7 это строка(остальные значения этого енума находятся аналогично - меняем тупо от 0 до 1 2 3 4 5 и тд и тп и проверяем в консоли значение(и выставляем его тоже для проверки. например пробуем отрицательное значение поставить, пробуем огромное, пробуем дробное и тд и тп) пока у нас не крашнет xD)
енум сразу скажу вот такой
там к концу идут массивы из флоатов от 2 до 4 элементов размером
ну и тот же самый способ для определения оффсета флагов - ищем какие-нибудь квары с флагами которые отображаются в консольке в find, смотрим структуру этих кваров, меняем подозрительные поля смотрим что происходит с флагами в консольке в find. способ "меняй и смотри что будет"
теперь давайте посчитаем количество кваров в этом списке кваров
возвращаемся к списку, скроллим дохуя в реклассе и находим конец визуально(ну находим точку где начинается мусор)
мы видим что вот это у нас последний валидный квар в списке
просто выделяем от конца до начала
67616 видим размер общий. размер одного узла явно 8+8 байт(указатель на конвар и какойто индексо-подобный шлак), делим 67616 на 16 = 4226
ищем 4226 в нашем CCvar
нашли парочку, перезапускаем доту там чекаем тестим выбираем какую-нибудь смотрим какая переменная не ломается и правильно отображает колво кваров. я например взял ту что на 0x58 мне она красивее кажется
вот вам и контейнер
начало на 0x40, размер в типе uint16 на 0x58,
элемент структурой:
сам квар вот такой
ну собсна вот вам и квары, бегаете по всему списку находите нужный по имени ставите нужное значение в соответствии с типом квара.
таким же способом находятся и конкомманды - тыкаем на все интересное находим чтото подозрительное
структура там простенькая
самих именно коллбеков комманд нет прямо там в структуре, но в структере есть индекс коллбека.
массив коллбеков лежит рядом на 0x118
2) регистрация конкомманд
я например еще люблю регать свои консольные команды это весело(понятно что лучше сделать меню и тд и тп)
хотите зарегать свою команду - смотрите как регаются команды в доте(например cl_ent_find_index возьмём). ищем какую-нибудь команду по строке
видим такую картину
тут вызывается функция которая регает команду
(команды хранятся в виде айдишника, который представляет собой индекс в массиве конкомманд)
можем бпшек наставить, потестить пореверсить че там да как
также можно посмотреть как они анрегаются поискав хрефы на переменную которая содержит айди квара
ну кароче вот так вот команды регаются.
пример кастомной команды:
3) выставление значений кварам с учётом уведомления об изменении
смотрим в квар который просто так не меняется(требует уведомления), например dota_camera_distance
смотрим в реклассе и ищем подозрительные вещи. мне например сразу же в глаза бросилось
ставим хвбп дворд на чтение, меняем конвар из консоли:
сразу видим что наша 84 летит в edi, потом edi летит в eax, потом eax умножается на три(а потом еще на 8, то есть итого на двадцать четыре) потом это дерьмо прибавляется к rsi+80. смотрим что же там на rsi(это CCvar) + 0x80:
очевидно что это таблица коллбеков кваров. окей дальше
берем наш коллбек от dota_camera_distance(ну 0x84 умножаем на 24 и прибавляем к [CCvar + 0x80])
ставим бп чекаем арги(чекнув место вызова функции)
на скрине видно 4 аргумента(rcx, edx, r8, r9) типы чисто судя по размеру регистров void*, int, void*, void*
сразу скажу кароче коллбек выглядит вот так(один из аргументов неизвестен. ну я ноль передаю вроде с св читс и дистанцией камеры работает собственно)
вот например св читс + дистанция камеры:
все квары и комманды(и прочие плюхи) есть тут(дамп от 08.09.2022)
Пожалуйста, авторизуйтесь для просмотра ссылки.
с новой обновой очень сильно реворкнули квары.1) находим сами квары:
Берем интерфейс(CreateInterface)(
Пожалуйста, авторизуйтесь для просмотра ссылки.
)tier0.dll:
VEngineCvar007 -> CCvar
в нем тыкаем на все интересное(и внутри того на что тыкали так же тыкаем)
на оффсете 0x40 лежит кучка интересностей
обсматриваем эти интересности
ну думаю очевидно что это конвар
сразу в консоли замечаем что тип квара у нас boolean(true/false)
видим что структура квара имеет размер 80 байт
первый указатель это имя
второй указатель не интересен нам(он указывает на некст квар токо не совсем на него а на ячейку перед ним но не суть. у нас и так все квары в массиве зачем нам некст)
третий и четвертый указатели(если это указатели) нам пока не интересны они везде по нулям
пятая ячейка это забегая вперед скажу - два инта: тип и какаято хуита
дальше идёт инт флаги
дальше идёт указатель на какуюто хуйню
дальше идёт юнион значения.
чтобы определить где там тип где там что, берем квары разных типов и сравниваем что же у них разное
сразу бросается в глаза после описания идёт какойто инт который у обоих различается
и потом еще ниже тоже различается инт(это флаги)
простой способ проверить - тупо МЕНЯЕМ эти неизвестные хрени.
у tv_broadcast_url например семёрку меняем на ноль
до модификации:
после:
ну сразу видим что это оказывается ТИП конвара, и судя по всему 0 это булеана, а 7 это строка(остальные значения этого енума находятся аналогично - меняем тупо от 0 до 1 2 3 4 5 и тд и тп и проверяем в консоли значение(и выставляем его тоже для проверки. например пробуем отрицательное значение поставить, пробуем огромное, пробуем дробное и тд и тп) пока у нас не крашнет xD)
енум сразу скажу вот такой
C++:
enum class EConvarType : std::uint8_t
{
BOOL = 0,
INT32,
UINT32,
INT64,
UINT64,
FLOAT,
DOUBLE,
STRING,
COLOR_RGBA,
UNK_SOME_TWO_FLOATS,
UNK_SOME_THREE_FLOATS,
UNK_SOME_FOUR_FLOATS,
UNK_SOME_THREE_FLOATS_AGAIN,
};
ну и тот же самый способ для определения оффсета флагов - ищем какие-нибудь квары с флагами которые отображаются в консольке в find, смотрим структуру этих кваров, меняем подозрительные поля смотрим что происходит с флагами в консольке в find. способ "меняй и смотри что будет"
теперь давайте посчитаем количество кваров в этом списке кваров
возвращаемся к списку, скроллим дохуя в реклассе и находим конец визуально(ну находим точку где начинается мусор)
мы видим что вот это у нас последний валидный квар в списке
просто выделяем от конца до начала
67616 видим размер общий. размер одного узла явно 8+8 байт(указатель на конвар и какойто индексо-подобный шлак), делим 67616 на 16 = 4226
ищем 4226 в нашем CCvar
нашли парочку, перезапускаем доту там чекаем тестим выбираем какую-нибудь смотрим какая переменная не ломается и правильно отображает колво кваров. я например взял ту что на 0x58 мне она красивее кажется
вот вам и контейнер
начало на 0x40, размер в типе uint16 на 0x58,
элемент структурой:
C++:
struct CvarNode
{
ConVariable* var{};
int some_leaf_like_index_shit{};
};
C++:
union ConVarValue
{
bool boolean{};
std::uint64_t u64;
std::int64_t i64;
std::uint32_t u32;
std::int32_t i32;
float flt;
double dbl;
const char* str;
std::uint32_t clr_rgba;
std::array<float, 2> two_floats;
std::array<float, 3> three_floats;
std::array<float, 4> four_floats;
};
struct ConVariable
{
const char* name{};
void* next_convar_node_like_shit{};
void* unk1{};
void* unk2{};
const char* help{};
EConvarType type{};
int unk_maybe_number_of_times_changed{};
int flags{};
int unk4{};
int CALLBACK_INDEX{};
int unk5{};
ConVarValue value{};
}
таким же способом находятся и конкомманды - тыкаем на все интересное находим чтото подозрительное
структура там простенькая
C++:
struct ConCmd
{
const char* name{};
const char* help{};
int flags{};
void* accessor{};
void* accessor_related_shit_maybe{};
int shit_unk{};
int CallbackListIndex{};
};
массив коллбеков лежит рядом на 0x118
2) регистрация конкомманд
я например еще люблю регать свои консольные команды это весело(понятно что лучше сделать меню и тд и тп)
хотите зарегать свою команду - смотрите как регаются команды в доте(например cl_ent_find_index возьмём). ищем какую-нибудь команду по строке
видим такую картину
тут вызывается функция которая регает команду
(команды хранятся в виде айдишника, который представляет собой индекс в массиве конкомманд)
можем бпшек наставить, потестить пореверсить че там да как
также можно посмотреть как они анрегаются поискав хрефы на переменную которая содержит айди квара
ну кароче вот так вот команды регаются.
C++:
struct ConCMDID
{
static inline constexpr auto BAD_ID = 0xFFFF;
std::uint64_t impl{};
bool IsGood() const noexcept
{
return impl != BAD_ID;
}
void Invalidate() noexcept
{
impl = BAD_ID;
}
};
struct ConCMDRegistrationInfo
{
const char* cmd_name{};
const char* help_str{};
std::uint64_t flags{};
void* callback{};
void* unk1{};
void* unk2{};
void* unk3{};
void* output_id_holder{};
};
ConCommandSource2(const std::string_view& name, const std::string_view& desc, void(* callback)(void*, const CCommand&))
{
ICvar::ConCMDRegistrationInfo info{};
info.cmd_name = name.data();
info.help_str = desc.data();
info.callback = callback;
info.output_id_holder = this;
Constructor::Invoke(&info);
}
...
//CCvar VVV
private:
//search for xrefs for constructed concmd variables.
static inline constexpr auto UnRegisterCMDVFTable_Index = 38;
public:
auto UnRegisterCMD(const ConCMDID& id)
{
CallVFunc<UnRegisterCMDVFTable_Index>(id);
}
3) выставление значений кварам с учётом уведомления об изменении
смотрим в квар который просто так не меняется(требует уведомления), например dota_camera_distance
смотрим в реклассе и ищем подозрительные вещи. мне например сразу же в глаза бросилось
ставим хвбп дворд на чтение, меняем конвар из консоли:
сразу видим что наша 84 летит в edi, потом edi летит в eax, потом eax умножается на три(а потом еще на 8, то есть итого на двадцать четыре) потом это дерьмо прибавляется к rsi+80. смотрим что же там на rsi(это CCvar) + 0x80:
очевидно что это таблица коллбеков кваров. окей дальше
берем наш коллбек от dota_camera_distance(ну 0x84 умножаем на 24 и прибавляем к [CCvar + 0x80])
ставим бп чекаем арги(чекнув место вызова функции)
на скрине видно 4 аргумента(rcx, edx, r8, r9) типы чисто судя по размеру регистров void*, int, void*, void*
сразу скажу кароче коллбек выглядит вот так(один из аргументов неизвестен. ну я ноль передаю вроде с св читс и дистанцией камеры работает собственно)
C++:
struct ConVarID
{
static inline constexpr auto BAD_ID = 0xFFFFFFFF;
std::uint64_t impl{};
void* var_ptr{};
bool IsGood() const noexcept
{
return impl != BAD_ID;
}
void Invalidate() noexcept
{
impl = BAD_ID;
}
};
using t_CvarCallback = void(*)(const ConVarID& id, int unk1, const ConVarValue* val, const ConVarValue* old_val);
C++:
/*
t_CvarCallback GetCVarCallback(int index)
{
if (index)
{
auto table = Member<void*>(0x80);
if (table)
return *reinterpret_cast<t_CvarCallback*>(reinterpret_cast<std::uintptr_t>(table) + 24 * index);
}
return nullptr;
}
*/
/*
auto CCvar::GetCvarList() const noexcept
{
return std::span<const CvarNode>{ Member<const CvarNode*>(0x40), Member<std::uint16_t>(0x58) };
}
*/
for (const auto& [cvar_node, idx] : ICvar::Create()->GetCvarList() | indexed_range)
{
if (cvar_node.var)
{
if (make_stringview(cvar_node.var->name) == "dota_camera_distance")
{
const auto old_val = cvar_node.var->value;
cvar_node.var->value.flt = 2222.0f;
if (auto cb = ICvar::Create()->GetCVarCallback(cvar_node.var->CALLBACK_INDEX); cb)
cb(ICvar::ConVarID{ .impl = static_cast<std::uint64_t>(idx), .var_ptr = (void*)&cvar_node},
0, &cvar_node.var->value, & old_val);
}
if (make_stringview(cvar_node.var->name) == "sv_cheats")
{
const auto old_val = cvar_node.var->value;
cvar_node.var->value.boolean = true;
if (auto cb = ICvar::Create()->GetCVarCallback(cvar_node.var->CALLBACK_INDEX); cb)
cb(ICvar::ConVarID{ .impl = static_cast<std::uint64_t>(idx), .var_ptr = (void*)&cvar_node },
0, &cvar_node.var->value, &old_val);
}
}
}
Последнее редактирование: