Гайд Продолжение серии. шема(оффсеты)+локал игрок

Начинающий
Статус
Оффлайн
Регистрация
15 Дек 2018
Сообщения
146
Реакции[?]
9
Поинты[?]
0
хочу понять как эти структуры получились, пытаюсь вызвать функцию FindDeclaredClass, но не получается:
SchemaSystemTypeScope = CreateInterface("schemasystem.dll", " CSchemaSystemTypeScope::Type_DeclaredClass");
ui VMT = *(ui*)SchemaSystemTypeScope;
ui DeclaredClass = *(ui*)(VMT + 2 * 8);
auto MyDeclaredClass = (ui(__fastcall*)(ui _schema, cc _module))DeclaredClass;
ui MyFindDeclaredClass = MyDeclaredClass(SchemaSystemTypeScope , "SchemaSystem.dll");
CMSG("MyFinDeclaredClass %s\n", n2hex(MyFindDeclaredClass));
Оно не будет так работать, у тебя вторым аргументом в функции CreateInterface должно будет указано название интерфейса
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
779
Реакции[?]
331
Поинты[?]
63K
хочу понять как эти структуры получились, пытаюсь вызвать функцию FindDeclaredClass, но не получается:
SchemaSystemTypeScope = CreateInterface("schemasystem.dll", " CSchemaSystemTypeScope::Type_DeclaredClass");
ui VMT = *(ui*)SchemaSystemTypeScope;
ui DeclaredClass = *(ui*)(VMT + 2 * 8);
auto MyDeclaredClass = (ui(__fastcall*)(ui _schema, cc _module))DeclaredClass;
ui MyFindDeclaredClass = MyDeclaredClass(SchemaSystemTypeScope , "SchemaSystem.dll");
CMSG("MyFinDeclaredClass %s\n", n2hex(MyFindDeclaredClass));
а не не, ты кривое имя в CreateInterface передаешь(то есть во-первых такого интерфейса просто не зарегистрировано, а во-вторых они все идут в формате НАЗВАНИЕ_ВЕРСИЯ), так не получится. ты вызываешь CreateInterface schemasystem_001, получаешь CSchemaSystem. у нее из вмт вызываешь GetScope("client.dll"). дальше там у этого CSchemaSystemTypeScope вызываешь FindDeclaredClass("C_BaseEntity").
C++:
    Schema = CreateInterface("schemasystem.dll", "SchemaSystem_001");
    //GetScope - Schema->VirtualCall<13>
    //FindDeclaredClass - Scope->VirtualCall<2>
//все это __thiscall разумеется, то есть this в rcx не забудь передать
    ClassDescription* description = Schema->GetScope("client.dll")->FindDeclaredClass("C_BaseEntity");
 
Начинающий
Статус
Оффлайн
Регистрация
12 Сен 2020
Сообщения
42
Реакции[?]
3
Поинты[?]
0
а не не, ты кривое имя в CreateInterface передаешь(то есть во-первых такого интерфейса просто не зарегистрировано, а во-вторых они все идут в формате НАЗВАНИЕ_ВЕРСИЯ), так не получится. ты вызываешь CreateInterface schemasystem_001, получаешь CSchemaSystem. у нее из вмт вызываешь GetScope("client.dll"). дальше там у этого CSchemaSystemTypeScope вызываешь FindDeclaredClass("C_BaseEntity").
C++:
    Schema = CreateInterface("schemasystem.dll", "SchemaSystem_001");
    //GetScope - Schema->VirtualCall<13>
    //FindDeclaredClass - Scope->VirtualCall<2>
//все это __thiscall разумеется, то есть this в rcx не забудь передать
    ClassDescription* description = Schema->GetScope("client.dll")->FindDeclaredClass("C_BaseEntity");
вроде бы дошло, по крайней мере адрес получил, спасибо! так сложно глупые вопросы не задавать, наверно надо в виртуальные функции углубиться?!), но можешь концептуально описать значение данных структур (типа struct СlassDescription), (не знаю как правильно спросить) чем они полезны в данном случае? ну или помоги советом, что почитать на подобную тематику, чтобы как-то понятнее стало, praydog с первого раза не очень помог, может перечитать надо.
 
Последнее редактирование:
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
779
Реакции[?]
331
Поинты[?]
63K
вроде бы дошло, по крайней мере адрес получил, спасибо! так сложно глупые вопросы не задавать, наверно надо в виртуальные функции углубиться?!), но можешь концептуально описать значение данных структур (типа struct СlassDescription), (не знаю как правильно спросить) чем они полезны в данном случае? ну или помоги советом, что почитать на подобную тематику, чтобы как-то понятнее стало, praydog с первого раза не очень помог, может перечитать надо.
функция FindDeclaredClass возвращает указатель на неопределенную структуру. то есть указатель на кучу какой-то информации. я беру несколько таких указателей и сравниваю и нахожу какието закономерности(ну например понятно что если я запросил класс C_BaseEntity и где-то в этой структуре вижу указатель на строку C_BaseEntity то данный член этой структуры это имя класса. потом потыкал на другие члены, нашел описание самих нетваров класса, посмотрел какого они размера(то есть расстояние от первого нетвара до второго нетвара) блаблабла и т.д., посчитал сколько всего нетваров, допустим их 10 штук, и нашел число 10(или 9 или 11, в общем похожее число) в самой структуре, понятно что этот член означает количество нетваров(либо количество нетваров минус один/плюс один).
эти структуры никакого значения не имеют, это просто коллекция оффсетов которые я глазами нашел. ты можешь спокойно вместо ClassDescription->Size использовать оффсет +28 и считывать оттуда 2 байта(short). или можешь все это сделать в структуру чтобы компилятор сам за тебя все эти оффсеты прописывал(ну естественно тебе самому предварительно нужно будет их высчитать и просто в нужном порядке все это расположить чтобы оффсеты в структуре совпадали с реальными оффсетами).в общем эта структура - отражение того что я увидел в памяти. я бы мог и просто оффсет прибавлять но это не так красиво.

насчет твоего первого вопроса про классы и виртуальные функции, тебе сначала нужно понимать как какой класс получать.нельзя просто так вызывать CreateInterface и думать что тебе выдаст любой класс в доте. вообще получить класс это довольно сложная задача. чтобы ее решить тебе нужно посмотреть как этот класс получает сама программа. есть два типа классов: классы которые ты получаешь напрямую из сиги(то есть находишь какую-то функцию где в регистр суется указатель на класс, и потом просто считываешь с этого указателя и вот тебе класс. то есть ты ищешь хреф на класс, а потом делаешь сигу на этот хреф.), и классы которые обернуты самой программой в этакие массивы(менеджеры,фабрики как угодно можешь их называть), которые хранят в себе и управляют различными объектами. то есть в первом случае ты просто ищешь руками и потом когда нашел делаешь сигу, а во втором ты пытаешься понять как работает эта фабрика(менеджер/что угодно) и потом когда ты понимаешь как она работает можешь получить любой класс из этой фабрики.
например CreateInterface работает очень просто. где-то там в памяти есть массив(кучка указателей) со всеми интерфейсами. он по каждому проходится, смотрит имя, если совпадает возвращает тебе этот указатель, если не находит совпадения то создает новый интерфейс и возвращает его тебе. в этом массиве есть только те интерфейсы которые принадлежат конкретно данной фабрике/менеджеру.
а CSchemaSystem имеет свой внутренний менеджер(ну то есть опять же просто массив/его аналог) где хранятся указатели на экземпляры класса CSchemaSystemTypeScope. у CSchemaSystemTypeScope свой менеджер где хранятся указатели на ClassDescription. и вместо того чтобы все это говно разгребать руками и искать хрен пойми где, ты просто вызываешь CreateInterface("schemasystem.dll", "SchemaSystem_001")->GetScope("client.dll")->FindDeclaredClass("C_BaseEntity")
в итоге все что тебе нужно это просто найти эти все функции(CreateInterface это экспорт, GetScope это виртуальная функция, FindDeclaredClass тоже) и теперь ты просто можешь передавать строки и тебе само там все внутри найдет.
 
Начинающий
Статус
Оффлайн
Регистрация
15 Дек 2018
Сообщения
146
Реакции[?]
9
Поинты[?]
0
не уверен что тебя понял, так что если ответ не удовлетворяет то перефразируй
schemasystem_001 это название интерфейса,а не класса. эта строка имеет значение только для функции CreateInterface и нигде больше. CSchemaSystemTypeScope::FindDeclaredClass принимает имя класса в том виде в каком оно было зарегистрировано в Schema, а зарегистрировано оно было в таком виде в каком оно и есть в сурс файлах вальве(class My_Class), а какое оно в сурс файлах вальве можно узнать через RTTI(IDA->Class informer). так что ты находишь там какой-нибудь класс который тебе нужен(C_BaseEntity), вбиваешь его в шему(FindDeclaredClass("C_BaseEntity")) и если тебе вернуло 0 то значит такой класс не зарегистрирован, а если вернуло не 0 то обрабатываешь.
хотя раз praydog сделал генератор сдк, то очевидно что он смог механически получить все зарегистрированные классы. почитай там сурсы у него. если не ошибаюсь то там вроде в самом CSchemaSystemTypeScope на каком-то оффсете просто массив лежит с зареганными классами. но толку мало от этого Source2Gen потому что в шеме половина описанных классов - мусор, а остальная половина хоть и полезная но ее очень мало. поэтому я просто ручками ввожу ей несколько классов и не парюсь.
А как ты находишь названия классов?
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
779
Реакции[?]
331
Поинты[?]
63K
А как ты находишь названия классов?
через RTTI. в доте он есть.
RTTI это опция компилятора(ну дота написана на MSVC то есть в визуалке в общем, поэтому про нее и будем говорить), которую можно отключить(но вальвы это не сделали), которая идентифицирует классы по их названию/типу/в общем предоставляет информацию о классе на рантайме. типо по дефолту класс это просто структурированная кучка данных и после компиляции она просто превращается в кучу оффсетов. то есть на рантайме(когда ты запустил прогу) такого понятия как класс нету. а вот RTTI как раз таки помогает на рантайме идентифицировать классы. кароче
Пожалуйста, авторизуйтесь для просмотра ссылки.

так вот через этот RTTI можно у большинства(не всех но реально процентов 90% в доте по крайней мере) классов получить инфу(rtti данные хранятся в самом .dll/.exe файле в .data сегменте), в той же IDA плагин Class Informer. инфа включает в себя указатель на вмт название и еще какие-то юзлес данные. также в Reclass можно эту RTTI посмотреть прямо на рантайме(не знаю почему этот функционал в x64dbg не добавили), подключил к доте ввел указатель и сразу видишь название класса если на него есть RTTI.
в общем RTTI это информация о типах которую можно посмотреть на рантайме. точно также как и CSchemaSystem это информация о классах(правда тут не только названия и т.д. но еще и описания членов и их типов, то есть CSchemaSystem это кастомная RTTI от вальвов) которую также можно на рантайме посмотреть.
полезно когда ты нашел какой-то указатель и без понятия что это за указатель, ввел его в реклассе и тебе сразу выдало имя класса(если на него есть RTTI а это в 90% случаях)
 
Начинающий
Статус
Оффлайн
Регистрация
15 Дек 2018
Сообщения
146
Реакции[?]
9
Поинты[?]
0
ui VTable = *(ui*)schema;

ui fs = *(ui*)(VTable + 13 * 8);
auto Scope = (ui(__fastcall*)(ui _Interface, const char* _Module))fs;
ui SCOPE = Scope(schema, "client.dll");

ui dc = *(ui*)(VTable + 2 * 8);
auto Class = (ui(__fastcall*)(ui _Scope, const char* _Class))dc;
ui CLASS = Class(SCOPE, "C_DOTA_BaseNPC_Hero");

CMSG("Scope : %s\n", n2hex(SCOPE));
CMSG("Class : %s\n", n2hex(CLASS));

Вроде все правильно делаю, а DeclaredClass нуль возвращает
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
779
Реакции[?]
331
Поинты[?]
63K
ui VTable = *(ui*)schema;

ui fs = *(ui*)(VTable + 13 * 8);
auto Scope = (ui(__fastcall*)(ui _Interface, const char* _Module))fs;
ui SCOPE = Scope(schema, "client.dll");

ui dc = *(ui*)(VTable + 2 * 8);
auto Class = (ui(__fastcall*)(ui _Scope, const char* _Class))dc;
ui CLASS = Class(SCOPE, "C_DOTA_BaseNPC_Hero");

CMSG("Scope : %s\n", n2hex(SCOPE));
CMSG("Class : %s\n", n2hex(CLASS));

Вроде все правильно делаю, а DeclaredClass нуль возвращает
ui dc = *(ui*)(VTable + 2 * 8);
ты у шемы 2 функцию вызываешь а не у скопа...
 
Начинающий
Статус
Оффлайн
Регистрация
15 Дек 2018
Сообщения
146
Реакции[?]
9
Поинты[?]
0
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
779
Реакции[?]
331
Поинты[?]
63K
Начинающий
Статус
Оффлайн
Регистрация
12 Сен 2020
Сообщения
42
Реакции[?]
3
Поинты[?]
0
разобрался с вопросом, лишний коммент, потом дополню)
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
15 Дек 2018
Сообщения
146
Реакции[?]
9
Поинты[?]
0
А как ты узнаешь у членов класса какие у них типы данных, для чего они, их размер, то есть - pad[6]???
Название класса сразу понятно, оно там подписано, а остальные хз
 

Вложения

Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
779
Реакции[?]
331
Поинты[?]
63K
А как ты узнаешь у членов класса какие у них типы данных, для чего они, их размер, то есть - pad[6]???
Название класса сразу понятно, оно там подписано, а остальные хз
ну я узнаю их по заполненности 8 байтовой ячейки. если там 4 байта нулей и 4 байта каких-то данных то скорее всего это 2 int'а(один из которых 0 а другой xxx) будет а не long long.
также по инструкциям mov dword:[rcx+offset], mov byte:[rcx+offset], mov word:[rcx+offset], mov qword:[rcx+offset]
а также по смыслу(если в классе всего 10 нетваров, то понятно что число 10(или 9 или 11) обозначает количество нетваров(или количество минус один/плюс один). также проверяю это и на другом классе и если там также то моя гипотеза верна. если нет то вывожу новую.).
char pad[6] это как раз таки 6 байт(я обозначил short + char[6], а можно было short + short + int/как душе угодно) которые я не смог определить зачем нужны, поэтому я просто на них забил. u64 idk точно также "i don't know", idk2 точно так же не смог определить.
ну и рекласс использую только для определения больших структур. там просто дофига мусора всякого на экране. а для маленьких структур типо описания нетвара из шемы и обычного окна dump в x64dbg хватает.
если мы говорим про массив то размер структуры это расстояние от одного члена до другого такого же в следующей структуре
 
Начинающий
Статус
Оффлайн
Регистрация
15 Дек 2018
Сообщения
146
Реакции[?]
9
Поинты[?]
0
ну я узнаю их по заполненности 8 байтовой ячейки. если там 4 байта нулей и 4 байта каких-то данных то скорее всего это 2 int'а(один из которых 0 а другой xxx) будет а не long long.
также по инструкциям mov dword:[rcx+offset], mov byte:[rcx+offset], mov word:[rcx+offset], mov qword:[rcx+offset]
а также по смыслу(если в классе всего 10 нетваров, то понятно что число 10(или 9 или 11) обозначает количество нетваров(или количество минус один/плюс один). также проверяю это и на другом классе и если там также то моя гипотеза верна. если нет то вывожу новую.).
char pad[6] это как раз таки 6 байт(я обозначил short + char[6], а можно было short + short + int/как душе угодно) которые я не смог определить зачем нужны, поэтому я просто на них забил. u64 idk точно также "i don't know", idk2 точно так же не смог определить.
ну и рекласс использую только для определения больших структур. там просто дофига мусора всякого на экране. а для маленьких структур типо описания нетвара из шемы и обычного окна dump в x64dbg хватает.
если мы говорим про массив то размер структуры это расстояние от одного члена до другого такого же в следующей структуре
Спасибо!
 
Начинающий
Статус
Оффлайн
Регистрация
12 Сен 2020
Сообщения
42
Реакции[?]
3
Поинты[?]
0
можешь объяснить идею использования enum-ов (откуда их взять и их назначение) и класса CArray, можно общими словами, чтобы потом конкретизировать вопрос, если очень обширный.
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
779
Реакции[?]
331
Поинты[?]
63K
можешь объяснить идею использования enum-ов (откуда их взять и их назначение) и класса CArray.
enumы из LWSS, они я так понял через шему получены. там есть FindDeclaredClass а есть FindDeclaredEnum
enumы нужны чтобы не сойти с ума от циферок. они по дефолту существуют только на компайл-тайме, это как макросы.
#define Super_puper_edinichka 1
чтобы не писать 1 и потом думать " а что за единичка блин че она значит" ты в своем коде пишешь Super_puper_edinichka потом читаешь эту строку и сразу понимаешь что это и зачем нужно. а для компьютера которому насрать что эта единичка обозначает это просто число 1.
точно так же и enumы
enum{
Super_puper_edinichka = 1,
Super_puper_dvoyka,//=2 автоматически
//и т.д.
}
а CArray это просто моя обертка на дефолтный массив, которая подходит моему стилю. лучше напиши свой CArray, ты пишешь по-своему а я по-своему и предпочтения у нас разные,а так если надо то вот:
C++:
template<typename TYPE, u64 size>
    class Array {
    public:
        TYPE elements[size];
        u64 count = 0;

        TYPE* PrepareEmpty() {
            count++;
            return &elements[count - 1];
        }
        void operator=(TYPE s) {
            elements[count] = s;
            count++;
        }
        void RemoveAndShift(u64 INDEX) {
            if (!count) return;
            for (u64 i = INDEX; i < count; i++) {
                elements[i] = elements[i + 1];
            }
            count--;
        }

        TYPE& operator[](u64 i) {
            return elements[i];
        }
        TYPE* begin() { return &elements[0]; }
        TYPE* end() { return &elements[count]; }
    };
 
Начинающий
Статус
Оффлайн
Регистрация
12 Сен 2020
Сообщения
42
Реакции[?]
3
Поинты[?]
0
спасибо большое, буду опять вникать)
ты пишешь по-своему а я по-своему и предпочтения у нас разные,а так если надо то вот:
у тебя очень логичный код, при всем желании лучше бы не написал)
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
779
Реакции[?]
331
Поинты[?]
63K
спасибо большое, буду опять вникать)

у тебя очень логичный код, при всем желании лучше бы не написал)
почитай кстати полезная штука
Пожалуйста, авторизуйтесь для просмотра ссылки.

в двух словах:
у Array должен быть begin() и end();

C++:
Array items;

for(auto& item: items){

item.blablabla();

}

вместо традиционного

for(int i = 0; i < items.count; i++){

items.elements[i].blablabla()

}
 
Сверху Снизу