- 20.06.2022
- 24 317
- 221
- 36
- Награды
- 10
- Пол
- Муж.
Репутация:
- Автор темы
- Администратор
- Модератор
- Команда форума
- #1
Сам софт вот:
Читая для начала:
Для IDA качаем плагины class informer и function string associate
заходим в IDA берем schemasystem.dylib
и параллельно открываем schemasystem.dll. в обоих базах жмякаем Edit->Plugins->Function string associate -> continue (вкладка Edit доступна в нужном нам виде только когда вы сидите в IDA-view или в HEX-view)
ищем в dylibе символ FindTypeScopeForModule
идем по хрефу(наводим на название функции и жмем англ X) видим dq offset жмякаем приходим к вмт.
находим функцию FindTypeScopeForModule в нашей вмт и видим что рядом есть функция с хрефом
наша функция на 3 позиции выше чем эта функция с хрефом.
в .dll базе edit->plugins->class informer->ok
ctrl+f CSchemaSystem
открываем. видим там нашу функцию с хрефом отсчитываем 3 вверх и вот наша функция.
чтобы удостовериться декомплим и чекаем код в обоих базах и видим что код почти одинаковый.
дальше ищем в dylibе символ FindDeclaredClass
хреф и идем к вмт, чекаем код функции, видим что по факту просто вызывается другая виртуальная функция этого же класса.
идем к вмт в .dll базе и ищем там функции где такой же простой код.
ну вот собственно мы нашли эту функцию - FindDeclaredClass.
реализуем на практике:
чтите что шема описывает только классы client/server/и возможно других бесполезных дллок. так что какую-нибудь CNetworkSystem вы шемой не сдампите, да и в client.dll тоже не все классы можно сдампить. но для получения оффсетов у сущностей достаточно.
локалигрок. локальный C_DOTAPlayer
в доте C_DOTAPlayer это не сама сущность(герой на котором вы играете),а что-то вроде ваших глаз которыми вы видите все что происходит на экране, а герои и ваши призывные юниты это все сущности которые принадлежат вашему C_DOTAPlayer(то есть если ent->m_hOwnerEntity == (CHandle)C_DOTAPlayer то это сущность локального игрока)
CHandle это int состоящий из серийника и индекса сущностей(первые 17 и последние 15 байт соответственно), то есть чтобы получить индекс сущности мы берем CHandle и умножаем побитово на & 0x7FFF.
идем в engine2.dylib. ищем символы типа getlocalplayer, находим жмем хреф и идем в вмт, смотрим на какой позиции функция, идем в engine2.dll и ищем рядом с той же позицией в вмт CEngineClient функции, код которых выглядит точно так же и которые возвращают -1(что есть 0xFFFFFFFF) если что-то вдруг не так. класс. нашли(22).
(функа в dylibe)
(функа в длл)
(как видите код почти 1 в 1)
так же ищем IsInGame. тут применим логику. мы знаем что IsInGame идет сразу после GetMaxClients. идем в GetMaxClients чекаем код. ищем в .dll базе функцию точно с таким же кодом а потом просто находим следующую в вмт и это и будет IsInGame
(getmaxclients dylib)
(getmaxclients dll)
следующая за getmaxclients это isingame
typedef ui(__fastcall* fGLocalPlayerentindex)(ui self, ui ptrtoint, ui zero);//принимает указатель на int вторым параметром и засовывает туда результат.
fGLocalPlayerentindex GLocalPlayer;
typedef unsigned char(__fastcall* IsInGame)(ui);//тут параметр на самом деле чисто формальный, он не используется, можете и без него вызывать.
IsInGame InGame;
дальше встает вопрос: а где логику(ну то есть те вещи которые наш чит будет делать каждый тик) чита-то делать? ну вот тут у вас куча вариантов, хукаем директикс/паинттраверс/что угодно, на этом хуке вы будете одновременно и рисовать и выполнять логику чита. лично я выбираю панораму потому что все равно ее придется использовать если мы собрались делать кулдауны абилок врагов(либо же можете с сервера качать иконки спеллов и рисовать их в директиксе или где-нибудь еще), ибо панорама хорошо обращается с системой ресурсов доты.
сделайте себе дамп класса C_DOTAGamerules, поищите в client.dylib симол _g_pGameRules, возьмите какой-нибудь хреф найдите его в .dll и сделайте сигу(есть ниже если что).
GameRules кстати если я не ошибаюсь удаляется по выходу из матча, так что дереференсите при входе в матч а не сразу при инжекте.
так вот, мы в своем ранфрейме будем чекать - если игрок в матче и стадия игры нормальная(ну то есть не стадия пика не стадия загрузки и не какая-нибудь еще другая ненужная нам стадия)(енум увидите ниже в фулл коде) то ищем локал игрока, потом ищем локал тиму и ищем сущности которые принадлежат нашему игроку. ну а потом что-нибудь с ними делаем(в некст туторах).
ну и да забыл сказать не советую в пабе гонять с читами на доту не ломая вак, каким бы говном вак не был, задетектить тот же вмт хук это очень простая задача. если бы вальвы захотели то в ксго и в доте читеров бы просто не было. так что не думайте сражаться с ваком, отрубайте/делайте че угодно я не знаю. иначе скорее всего отлетите. благо способы ломать вак существуют на белом свете так что ищите. а так для тестов создавайте лобби а локацию выбирайте вместо локалхоста стокгольм или где-нибудь еще. в общем на вальвовских серверах. добавляйте ботов и это будет почти 100% как паб
Фулл код:
Актуальная инфа на 2025 год:
дампер шемы и ртти оффлайн:
если вкратце то регистрация в шеме происходит через CSchemaRegistration_blablabla классы в модулях, эти классы имеют у себя виртуальную функцию RegisterAllBindings. эта функция состоит из двух фаз - предварительной и финальной;
биндинги на диске хранят всё кроме типов - типы заполняются из RegisterAllBindings(изза этого нельзя просто с диска(ну т.е. из самого .dll файла без его прогрузки) брать биндинги и дампить их - ну точнее можно(ток их сначала еще идентифицировать тогда придется) но тогда не будет инфы о типах нетваров никакой - только имена оффсеты мета и прочая хуита)
следовательно надо грузить .dll'ки и вызывать у них эти RegisterAllBindings - здесь можно либо частично ммапить без импортов без нихуя(хотя это не на всех версиях доты работает - на некоторых импорты вызываются(следовательно их тоже либо эмулировать либо грузить)) и эмулировать шема систему(ну это хуйня идея слишком много работы и слишком нестабильно между версиями), либо просто нормально полноценно грузить официальную schemasystem.dll и остальные модули тоже нормально грузить, и потом уже доставать либо из шема системы всю инфу(нестабильно между версиями) либо просто абузить тот факт что всё пролетает через стабильные CSchemaSystemTypeScope::InstallSchemaClassBinding(0 index) и CSchemaSystemTypeScope::InstallSchemaEnumBinding(1 index). собственно весь дампер держится на этих двух хуках(вмт по ртти находится)
существует две "мажорные" версии шемы - 2015-2019 и 2019+ (между ними еще есть 2018-2019 кусочек который похож на 2019+ но чуть чуть отличается я его скипнул там ~6 месяцев всего лишь он жил или меньше даже). разница между этими версиями это сами структуры биндингов и того что они содержат(члены, мета, типы и тд) и порядок параметров в InstallSchemaClassBinding/InstallSchemaEnumBinding.
дллки грузятся, хукаются InstallSchemaClassBinding/InstallSchemaEnumBinding в шеме, вызывается экспорт у каждого из модулей InstallSchemaBindings(это грубо говоря просто g_pInterfaceGlobals._g_pSchemaSystem = addr_of_CSchemaSystem и foreach(CSchemaRegistration_blablabla) RegisterAllBindings). все что пролетает через хуки трансформируется в стабильный вид и сохраняется в список, потом после того как всё зарегалось по списку идут фиксы(например инфа(размер и выравнивание) о некоторых типах недоступна изначально(при первичной регистрации) и такие инвалидные типы фиксятся после того как всё зарегалось, за исключением ~11 типов которых в шеме просто нет(~4/5 штук) или нет в конкретно данных версиях но есть в следующих/предыдущих - они остаются как есть с 0 размером и выравниванием, родители фиксятся(родители должны быть в локальном масштабе, а шема может на указывать дубликаты из другого масштаба в качестве родителей(ну это токо для дампа фулл иерархии))) потом запись всей этой хуйни на диск. единственная "нестабильность" это вызов GetSizeAndAlign у которого индекс нестабильный но он не захардкожен а ищется сканом так что все работает вроде нормально на доту(42 версии проверил 2015-2024) и кс2(8 версий 2023-2024)
если кому-то нужны one-liner'ы компиляции то:
MSVC: x64 Native Tools Command Prompt for VS 2022(в пуске есть)
cl /std:c++latest /O2 /EHsc schema_dumper.cpp
Clang-cl:
clang-cl /std:c++latest /O2 /EHsc schema_dumper.cpp
Читая для начала:
Для IDA качаем плагины class informer и function string associate
заходим в IDA берем schemasystem.dylib
и параллельно открываем schemasystem.dll. в обоих базах жмякаем Edit->Plugins->Function string associate -> continue (вкладка Edit доступна в нужном нам виде только когда вы сидите в IDA-view или в HEX-view)
ищем в dylibе символ FindTypeScopeForModule
идем по хрефу(наводим на название функции и жмем англ X) видим dq offset жмякаем приходим к вмт.
находим функцию FindTypeScopeForModule в нашей вмт и видим что рядом есть функция с хрефом
наша функция на 3 позиции выше чем эта функция с хрефом.
в .dll базе edit->plugins->class informer->ok
ctrl+f CSchemaSystem
открываем. видим там нашу функцию с хрефом отсчитываем 3 вверх и вот наша функция.
чтобы удостовериться декомплим и чекаем код в обоих базах и видим что код почти одинаковый.
дальше ищем в dylibе символ FindDeclaredClass
хреф и идем к вмт, чекаем код функции, видим что по факту просто вызывается другая виртуальная функция этого же класса.
идем к вмт в .dll базе и ищем там функции где такой же простой код.
ну вот собственно мы нашли эту функцию - FindDeclaredClass.
реализуем на практике:
C++:
#include <fstream>
//структуры получены путем наблюдения глазиками в реклассе/дебагере той фигни которую вернула функция FindDeclaredClass, если интересно сделайте у себя там просто CMSG с возвратом этой функции и посмотрите в реклассе/дебагере
struct ClassDescription;
struct SchemaParent {
ui idk;
ClassDescription* parent;
};
struct ClassDescription {
ui idk;//0
ui classname;//8
ui modulename;//10
int sizeofclass;//18
short memberstoiterate;//1c
char pad[6];//20
ui MemberInfo;//28
ui idk2;//30
SchemaParent* parent;//38
};
struct SchemaTypeDescription {
ui idk;
ui name;
ui idk2;
};
struct MemberDescription {
ui name;
SchemaTypeDescription* schematypeptr;
int offset;
int idk;
ui idk2;
};
typedef void* (*oCreateInterface)(const char*, int);
oCreateInterface pCreateInterface;
ui CreateInterface(const char* szModule, const char* szInterface) {
pCreateInterface = (oCreateInterface)GetProcAddress(GetModuleHandleA(szModule), "CreateInterface");
return (ui)pCreateInterface(szInterface, 0);
}
ui SchemaSystem = 0;
void SchemaDumpToFileFull(const char* _module, const char* _class) {
ui Scope = ((ui(__fastcall*)(ui schemasys, const char* _mod))
(*(ui*)(*(ui*)(SchemaSystem)+0x68)))(SchemaSystem, _module);
if (!Scope) { CMSG("No such scope!\n"); return; }
CMSG("Scope %s\n", n2hex(Scope));
ui Test1 = ((ui(__fastcall*)(ui scope, const char* _class))
(*(ui*)(*(ui*)(Scope)+0x10)))(Scope, _class);
if (!Test1) { CMSG("No such class!\n"); return; }
std::ofstream myfile;
std::string filepath = "C:\\Users\\Dell\\Desktop\\DOTA_OFFSETS\\";
filepath = filepath + _class + ".txt";
myfile.open(filepath.c_str());
ClassDescription* a = (ClassDescription*)Test1;
label_1: {}
myfile << (cc)a->classname << "\n";
if(!a->memberstoiterate) myfile << "<no members>" << "\n";
for (ui i = 0; i < a->memberstoiterate; i++) {
MemberDescription* z = (MemberDescription*)(a->MemberInfo + i * 0x20);
myfile << "Member " << (const char*)z->name << " type " << (const char*)z->schematypeptr->name << " offset " << (const char*)n2hex(z->offset) << "\n";
//CMSG("Member %s type %s offset %s\n", z->name, z->schematypeptr->name, n2hex(z->offset));
}
if (a->parent) { a = a->parent->parent; goto label_1; }
myfile.close();
}
...
SchemaSystem = CreateInterface("schemasystem.dll", "SchemaSystem_001");
SchemaDumpToFileFull("client.dll", "C_DOTA_BaseNPC_Hero");//дампит C_DOTA_BaseNPC_Hero вместе со всеми его родителями.
...
локалигрок. локальный C_DOTAPlayer
в доте C_DOTAPlayer это не сама сущность(герой на котором вы играете),а что-то вроде ваших глаз которыми вы видите все что происходит на экране, а герои и ваши призывные юниты это все сущности которые принадлежат вашему C_DOTAPlayer(то есть если ent->m_hOwnerEntity == (CHandle)C_DOTAPlayer то это сущность локального игрока)
CHandle это int состоящий из серийника и индекса сущностей(первые 17 и последние 15 байт соответственно), то есть чтобы получить индекс сущности мы берем CHandle и умножаем побитово на & 0x7FFF.
идем в engine2.dylib. ищем символы типа getlocalplayer, находим жмем хреф и идем в вмт, смотрим на какой позиции функция, идем в engine2.dll и ищем рядом с той же позицией в вмт CEngineClient функции, код которых выглядит точно так же и которые возвращают -1(что есть 0xFFFFFFFF) если что-то вдруг не так. класс. нашли(22).
(функа в dylibe)
(функа в длл)
(как видите код почти 1 в 1)
так же ищем IsInGame. тут применим логику. мы знаем что IsInGame идет сразу после GetMaxClients. идем в GetMaxClients чекаем код. ищем в .dll базе функцию точно с таким же кодом а потом просто находим следующую в вмт и это и будет IsInGame
(getmaxclients dylib)
(getmaxclients dll)
следующая за getmaxclients это isingame
typedef ui(__fastcall* fGLocalPlayerentindex)(ui self, ui ptrtoint, ui zero);//принимает указатель на int вторым параметром и засовывает туда результат.
fGLocalPlayerentindex GLocalPlayer;
typedef unsigned char(__fastcall* IsInGame)(ui);//тут параметр на самом деле чисто формальный, он не используется, можете и без него вызывать.
IsInGame InGame;
дальше встает вопрос: а где логику(ну то есть те вещи которые наш чит будет делать каждый тик) чита-то делать? ну вот тут у вас куча вариантов, хукаем директикс/паинттраверс/что угодно, на этом хуке вы будете одновременно и рисовать и выполнять логику чита. лично я выбираю панораму потому что все равно ее придется использовать если мы собрались делать кулдауны абилок врагов(либо же можете с сервера качать иконки спеллов и рисовать их в директиксе или где-нибудь еще), ибо панорама хорошо обращается с системой ресурсов доты.
C++:
#define VT_METHOD(region, index) (*(ui*)(*(ui*)region + index * 8))
CEngineClient = CreateInterface("engine2.dll", "Source2EngineToClient001");
InGame = (IsInGame)VT_METHOD(CEngineClient, 26);
GLocalPlayer = (fGLocalPlayerentindex)VT_METHOD(CEngineClient, 22);
Panorama = CreateInterface("panorama.dll", "PanoramaUIEngine001");
Panorama2 = *(ui*)(Panorama + 0x28);
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((LPCVOID)*(ui*)Panorama2, &mbi, sizeof(mbi));
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);//ставим rwx протект
oRunFrame = VT_METHOD(Panorama2,6);
VT_METHOD(Panorama2, 6) = (ui)&hkRunFrame;//хукаем ранфрейм. это функция которая вызывается каждый кадр и рисует панельки.
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &mbi.Protect);//возвращаем протект
сделайте себе дамп класса C_DOTAGamerules, поищите в client.dylib симол _g_pGameRules, возьмите какой-нибудь хреф найдите его в .dll и сделайте сигу(есть ниже если что).
GameRules кстати если я не ошибаюсь удаляется по выходу из матча, так что дереференсите при входе в матч а не сразу при инжекте.
так вот, мы в своем ранфрейме будем чекать - если игрок в матче и стадия игры нормальная(ну то есть не стадия пика не стадия загрузки и не какая-нибудь еще другая ненужная нам стадия)(енум увидите ниже в фулл коде) то ищем локал игрока, потом ищем локал тиму и ищем сущности которые принадлежат нашему игроку. ну а потом что-нибудь с ними делаем(в некст туторах).
ну и да забыл сказать не советую в пабе гонять с читами на доту не ломая вак, каким бы говном вак не был, задетектить тот же вмт хук это очень простая задача. если бы вальвы захотели то в ксго и в доте читеров бы просто не было. так что не думайте сражаться с ваком, отрубайте/делайте че угодно я не знаю. иначе скорее всего отлетите. благо способы ломать вак существуют на белом свете так что ищите. а так для тестов создавайте лобби а локацию выбирайте вместо локалхоста стокгольм или где-нибудь еще. в общем на вальвовских серверах. добавляйте ботов и это будет почти 100% как паб
Фулл код:
Скрытое содержимое доступно для зарегистрированных пользователей!
Актуальная инфа на 2025 год:
дампер шемы и ртти оффлайн:
Скрытое содержимое доступно для зарегистрированных пользователей!
если вкратце то регистрация в шеме происходит через CSchemaRegistration_blablabla классы в модулях, эти классы имеют у себя виртуальную функцию RegisterAllBindings. эта функция состоит из двух фаз - предварительной и финальной;
биндинги на диске хранят всё кроме типов - типы заполняются из RegisterAllBindings(изза этого нельзя просто с диска(ну т.е. из самого .dll файла без его прогрузки) брать биндинги и дампить их - ну точнее можно(ток их сначала еще идентифицировать тогда придется) но тогда не будет инфы о типах нетваров никакой - только имена оффсеты мета и прочая хуита)
следовательно надо грузить .dll'ки и вызывать у них эти RegisterAllBindings - здесь можно либо частично ммапить без импортов без нихуя(хотя это не на всех версиях доты работает - на некоторых импорты вызываются(следовательно их тоже либо эмулировать либо грузить)) и эмулировать шема систему(ну это хуйня идея слишком много работы и слишком нестабильно между версиями), либо просто нормально полноценно грузить официальную schemasystem.dll и остальные модули тоже нормально грузить, и потом уже доставать либо из шема системы всю инфу(нестабильно между версиями) либо просто абузить тот факт что всё пролетает через стабильные CSchemaSystemTypeScope::InstallSchemaClassBinding(0 index) и CSchemaSystemTypeScope::InstallSchemaEnumBinding(1 index). собственно весь дампер держится на этих двух хуках(вмт по ртти находится)
существует две "мажорные" версии шемы - 2015-2019 и 2019+ (между ними еще есть 2018-2019 кусочек который похож на 2019+ но чуть чуть отличается я его скипнул там ~6 месяцев всего лишь он жил или меньше даже). разница между этими версиями это сами структуры биндингов и того что они содержат(члены, мета, типы и тд) и порядок параметров в InstallSchemaClassBinding/InstallSchemaEnumBinding.
дллки грузятся, хукаются InstallSchemaClassBinding/InstallSchemaEnumBinding в шеме, вызывается экспорт у каждого из модулей InstallSchemaBindings(это грубо говоря просто g_pInterfaceGlobals._g_pSchemaSystem = addr_of_CSchemaSystem и foreach(CSchemaRegistration_blablabla) RegisterAllBindings). все что пролетает через хуки трансформируется в стабильный вид и сохраняется в список, потом после того как всё зарегалось по списку идут фиксы(например инфа(размер и выравнивание) о некоторых типах недоступна изначально(при первичной регистрации) и такие инвалидные типы фиксятся после того как всё зарегалось, за исключением ~11 типов которых в шеме просто нет(~4/5 штук) или нет в конкретно данных версиях но есть в следующих/предыдущих - они остаются как есть с 0 размером и выравниванием, родители фиксятся(родители должны быть в локальном масштабе, а шема может на указывать дубликаты из другого масштаба в качестве родителей(ну это токо для дампа фулл иерархии))) потом запись всей этой хуйни на диск. единственная "нестабильность" это вызов GetSizeAndAlign у которого индекс нестабильный но он не захардкожен а ищется сканом так что все работает вроде нормально на доту(42 версии проверил 2015-2024) и кс2(8 версий 2023-2024)
если кому-то нужны one-liner'ы компиляции то:
MSVC: x64 Native Tools Command Prompt for VS 2022(в пуске есть)
cl /std:c++latest /O2 /EHsc schema_dumper.cpp
Clang-cl:
clang-cl /std:c++latest /O2 /EHsc schema_dumper.cpp
Последние темы в этом разделе:
- Объяснение защиты и гайдик на Aimware crack
- Гайд Lua и GLua в Garry’s Mod
- [Udemy] Paul Chin - Гейм Хакинг: Основы взлома игр с Cheat Engine (2021)
- [Infinite Skills] Реверс-инжиниринг и разработка эксплойтов. Часть 1
- Скрипт Лоадер [Key,UID,Auth - System]
- Скрипт Hwid Checker + анимации + ключ к Hwid + сброс Hwid
- Скрипт Hwid Checker + ключ привязка к Hwid + отправка в телеграм
- Скрипт Hwid Checker
- Приватный Курс по реверс-инжирингу - Курс от colby57
- Курс по реверс-инжирингу - Создание читов для игр