sg
-
Автор темы
- #1
Доброго времени суток, господа югеймовцы. Так как раздел в РУ сегменте достаточно пустует решил разбавить его постом о неплохом варианте получения и создвания нетвар менеджера для CS 2. Да и тем более новая кс -- новый уровень пастерства.
Стоит начать с того, как это в основном делается в интернале. ( Данный код для своей базы брал
* Если брать pModuleScope для каждого модуля, то мы будем получать один тот же список постоянно, потому я понял, что в этом нет смысла. Если я что-то упустил, поправьте.
В общем-то, всё невероятно просто, практически как в движке 1-го сурса, лишь с маленькими изменениями и упрощениями.
Как же дела обстоят с созданием такой же системы но для External софта ? Глянем в иду. FindTypeScopeForModule имеет 13 вирутальный индекс в интерфейсе CSchemaSystem.
Что же нам это даёт ?Что всё это сложно и хочется пойти лучше погамать в игрушки. Что наш pScopeModule лежит в массиве массивов по индексу, что мы гетнули из функции CUtlSymbolTable::Find.
Для большей наглядности к последующему коду, я бы хотел приложить вырезку из асм кода, с которым понимание будет полегче. Это label, которые вызывается если наш индекс не равен 0xFFFF, то есть найден.
Итак, с теорией практически покончено. Обращаясь ещё раз к нашей исследуемой функции замечу, что этот самый индекс модуля, мы не знаем, но благо я продебажил данную функцию за вас и выяснил, что индекс равняется 15 ( 0xF ). Так что примем это к сведению и начнём писать код.
С получением элементов не всё так просто, а именно с функцией GetElements, но я это сделал и особо зацикливаться не хочу. Исходники будут, не переживайте :)
Далее идёт уже достаточно простая практика получения самих таблиц с нетварами. Единственное отличие от интернала, что мы будем юзать RPM для таких целей.
Ну и пример финальный пример использования например так:
Ну и самое вкуснямбовое сами сурсы:
Не самые красивые реализации, хоть я и чуть подправил всё же. Для ознакомления пойдёт.
Стоит начать с того, как это в основном делается в интернале. ( Данный код для своей базы брал
Пожалуйста, авторизуйтесь для просмотра ссылки.
)
C++:
auto pModuleScope = g_pInterfaces->m_pSchemaSystem->FindTypeScopeForModule("client.dll");
if (!pModuleScope)
return;
const auto pElements = std::make_unique_for_overwrite< UtlTSHashHandle_t[] >(pModuleScope->GetBindingsTable().Count());
const auto nElements = pModuleScope->GetBindingsTable().GetElements(0, pModuleScope->GetBindingsTable().Count(), pElements.get());
if (!nElements)
return;
for (int nElementIndex = 0; nElementIndex < nElements; nElementIndex++)
{
const UtlTSHashHandle_t hElement = pElements[nElementIndex];
if (!hElement)
continue;
SDK::CSchemaClassBindingBase* const pClassBinding = pModuleScope->GetBindingsTable().Element(hElement);
if (!pClassBinding)
continue;
if (!pClassBinding->GetNumFields())
continue;
std::unordered_map< std::size_t, std::size_t > umapFields;
for (int nFieldIndex = 0; nFieldIndex < pClassBinding->GetNumFields(); nFieldIndex++)
{
SDK::CSchemaField* const pSchemaField = &pClassBinding->GetFields()[nFieldIndex];
if (!pSchemaField)
continue;
umapFields.try_emplace(FNV1A::Hash(pSchemaField->m_pszFieldName), pSchemaField->m_nOffset);
}
m_umapSchemas.try_emplace(FNV1A::Hash(pClassBinding->GetName()), umapFields);
}
В общем-то, всё невероятно просто, практически как в движке 1-го сурса, лишь с маленькими изменениями и упрощениями.
Как же дела обстоят с созданием такой же системы но для External софта ? Глянем в иду. FindTypeScopeForModule имеет 13 вирутальный индекс в интерфейсе CSchemaSystem.
C++:
__int64 __fastcall FindTypeScopeForModule(_QWORD *this_ptr, const char *pszModuleName)
{
unsigned __int16 v4; // [rsp+30h] [rbp+8h] BYREF
// Как я понял, здесь находим индекс нашего названия в таблице.
CUtlSymbolTable::Find(this_ptr + 53, &v4, pszModuleName);
// Если не найден, то вызываем некотурую функцию по 11 вирт. индексу
if ( v4 == 0xFFFF )
return (*(__int64 (__fastcall **)(_QWORD *))(*this_ptr + 88i64))(this_ptr);
// Или же возвращаем как раз таки наш ScopeModule
else
return *(_QWORD *)(this_ptr[51] + 8i64 * v4);
}
Что же нам это даёт ?
Для большей наглядности к последующему коду, я бы хотел приложить вырезку из асм кода, с которым понимание будет полегче. Это label, которые вызывается если наш индекс не равен 0xFFFF, то есть найден.
Код:
loc_18000E246:
movzx ecx, ax ; Сохраняем наш индекс в регистр rcx
mov rax, [rbx+198h] ; RBX - указатель на наш интерфейс, прибавляем 0x198 ( 51 * 8, где 8 размер типа int ).
mov rax, [rax+rcx*8] ; Из полученного элемента массива получаем ещё один, где индекс это наш индекс модуля из списка.
add rsp, 20h ; Выравниваем стек и выходим из функции
pop rbx
retn
Итак, с теорией практически покончено. Обращаясь ещё раз к нашей исследуемой функции замечу, что этот самый индекс модуля, мы не знаем, но благо я продебажил данную функцию за вас и выяснил, что индекс равняется 15 ( 0xF ). Так что примем это к сведению и начнём писать код.
C++:
// Получение базового адреса нашего интерфейса
auto ptrToBaseInterface = g_pMemorySystem->GetModule(HASH("schemasystem.dll")) + schema_offset;
// Получаем наш массив с pScopeModule для каждого модуля
auto ptrToListElement = g_pMemorySystem->RPM<std::uintptr_t>(ptrToBaseInterface + 0x198);
// Получаем уже финальный pScopeModule благодаря той логике что изложил выше ( я кастую именно в поинтер, чтобы облегчить RPM и использоваться указатель this )
auto ptrToScope = g_pMemorySystem->RPM<CSchemaSystemTypeScope*>(ptrToListElement + (0xF * 8));
// Ну и собсна сама таблица
auto pTable = ptrToScope->GetBindingsTable();
auto pElements = std::make_unique_for_overwrite< UtlTSHashHandle_t[] >(pTable.Count());
const auto nElements = pTable.GetElements(0, pTable.Count(), pElements.get());
С получением элементов не всё так просто, а именно с функцией GetElements, но я это сделал и особо зацикливаться не хочу. Исходники будут, не переживайте :)
Далее идёт уже достаточно простая практика получения самих таблиц с нетварами. Единственное отличие от интернала, что мы будем юзать RPM для таких целей.
C++:
// Список содержащий название таблицы и к нему список нетваров
std::unordered_map< std::uint64_t, std::unordered_map< std::uint64_t, std::uint32_t > > umapTableOffsets;
for (int nElementIndex = 0; nElementIndex < nElements; nElementIndex++)
{
const UtlTSHashHandle_t hElement = pElements[nElementIndex];
if (!hElement)
continue;
CSchemaClassBindingBase* const pClassBinding = pTable.Element(hElement);
if (!pClassBinding)
continue;
if (!pClassBinding->GetNumFields())
continue;
printf("%s ( %i ) :\n", pClassBinding->GetName().c_str(), pClassBinding->GetNumFields());
CSchemaField* pSchemaField = &pClassBinding->GetFields()[0];
std::unordered_map< std::uint64_t, std::uint32_t > umapOffsets;
for (int nFieldIndex = 0; nFieldIndex < pClassBinding->GetNumFields(); nFieldIndex++)
{
if (!pSchemaField)
continue;
auto name = pSchemaField->GetName();
umapOffsets.try_emplace(HASH(pSchemaField->GetName()), pSchemaField->GetOffset());
printf("\t%s = 0x%p\n", pSchemaField->GetName().c_str(), pSchemaField->GetOffset());
// Когда я изначально делал, вполне работал вариант с 21 строчки, но сегодня всё по какой-то причине сломалось xD
// Так что просто прибавляю к настоящему адресу 0x20. Это будет адрес след. элемента в списке.
pSchemaField = reinterpret_cast<CSchemaField*>(std::uintptr_t(pSchemaField) + 0x20);
}
umapTableOffsets.try_emplace(HASH(pClassBinding->GetName()), umapOffsets);
}
Ну и пример финальный пример использования например так:
C++:
auto uiHealthOffset = umapTableOffsets[HASH("C_BaseEntity")][HASH("m_iHealth")];
printf("C_BaseEntity->m_iHealth offset is 0x%p\n", uiHealthOffset);
Ну и самое вкуснямбовое сами сурсы:
Пожалуйста, авторизуйтесь для просмотра ссылки.
Не самые красивые реализации, хоть я и чуть подправил всё же. Для ознакомления пойдёт.