Начинающий
-
Автор темы
- #1
Если ты такой же бедный, как я, и хочешь экипнуть шмоточки на свою любимую без встречи с банкоматом(дяди Габена), то этот гайд будет полезным для тебя.
В конечном итоге у тебя должно быть что-то такое:
(MENU)
(IN GAME)
Мой гайд складывается с двух важных штук, а именно:
1- Инвентори Ченжер - добавление шмоток в инвентарь.
2- Скин Ченжер - экипировка шмоток на героев, показывание шмоток в игре.
1 Ищем функцию, которая позволит нам создать объект вещи ( объект класса CEconItem)
Помочь в этом деле может дайлибсы, ссылку на которые вы можете найти в соседних темах. Вам нужен libclient.dylib.
Смотрите где она вызывается и берёте оттуда хрефы и идёте в client.dll и ищите что-то похожее на то, что в либе.
Могут быть разные способы вызова функции, я лично просто делаю сигу(signature) по байтам и просто юзаю популярный метод FindPattern.
Путём вызова вышеупомянотой функции, вы создадите пустую шмотку. Теперь нам нужно её заполнить информацией.
Если посмотреть на сурсы (
Заходим в IDA ->Class Informer-> ждём пока загрузит все вмтки(в случае,если ещё не пользовались этим плагином) -> вписываем CEconItem и ищём там где будет написано вот это(нам нужно то что имеет 9 функций)
Берём любую из фунок и ставим БП в чём угодно: Cheat Engine, x64dbg, etc.
Одеваем/Снимаем шмотку в инвентаре(Желательно та которая касается героя а не курьера т.д.)
Копируем значение из регистра RCX и идём в ReClass.Описываем наш класс.
Как видим размер структуры 0x50.(Айдишку аккаунта я заменил числами из вмт, поэтому не пытайтесь найти и не кидайте;-) )
Есть ещё полезная вмт на 6 индексе(0,1,2....6) которая сдампит вам поля. Благодаря этому можно правильно описать структуру тоже, но это ваш выбор. Я лично ручонками описовал а потом как-то решил проверить, правильно ли я отреверсил класс.
Думаю вам было достаточно 1-го часа , чтоб отреверсить нужное и понять где лежит поле [uint32_t m_ItemDefIndex].
Что нужно туда вписать? Как говорит само название переменной Item Definition Index ( индекс дефиниции айтема). Где нам взять этот индекс дефиниции? Well, если вы пробовали хоть раз делать скинченжер олдовским методом VPK, то думаю вы на память помните название файла items_game.txt.
Качаем и устанавливаем GCFScape.
Открываем и ищем(и открываем)
X:\SteamLibrary\steamapps\common\dota 2 beta\game\dota\pak01_dir.vpk
идём по пути \scripts\items\items_game.txt
Делаем магию экстракта и запускаем в формате текстового документа(рекомен. Notepad++)
Как называется ваша любимая шмотка в доте на английском языке? Моя аркана на секси фантомку, поэтому ищем CTRL+F -> Manifold Paradox
Мы видим на скрине дефиницию под индексом 7247,которая описывает нашу аркану(назваание,префаб,картинка,редкость, стили и т.д.)
Если покрутить выше-ниже то мы увидим что есть какая очерёдность, типа сверху будет 7246,а ниже 7248. В общем это и есть индекс дефиниции( в нашем случае ItemDefIndex арканы на фантомку будет 7247)
Следущее поле после дефиниции индекса которое нам понадобится это АккаунтАйди(а также SOID_t
структуру, которая нам понадобиться при добавлении шмотки в кэш). Его можна "вытащить" из разных мест, но лично я беру его из объекта CDOTAPlayerInventory (
Ты возможно хочешь сказать, ладно я не тупой я понял, как получить инвентарь игрока, он лежит в менеджере, но как же получить менджер, так вот....сам открываешь либу (client.dll) и ищешь что-либо связанное с InventoryManager, смотришь хрефы(если вдруг не понял что такое хрефы,то это строки ) либо
- поможет хреф "welcome_equip_seasonal_terrain" в IDA (client.dll)
EconItemSystem или EconItemSchema получаешь через хреф "scripts/items/items_game.txt" , смотришь кто вызывает(ну логично что в параметр rcx будет this(EconItemSystem))
Нам нужно получить объект CDOTAItemDefinition, чтобы из него брать всякие штуки позже, если нужно будет.
Делаем так само как мы делали в CEconItem( берём любую функу из вмт и ставим БП, я бы порекомендовал взять ту которая голубым цветом подсвечивается). Не хотел заново загружать дбшку новую, поэтому халявщики ловите немножко отревершеную вмт(мб wrong )
А, забыл сказать,скачайте себе FunctionStringAssociate на IDA. Полезная хуйня для реверсинга! Это чтоб видеть какие хрефы внутри функции(как видите выше на скрине)
По сути нам ток надо поле uint32_t m_itemDefIndex (оно лежит где-то между 0x8 - 0x18 поищите, пореверсите)
Есть функа CDOTAItemSchema::GetItemDefinitionByIndex,которую найдёте по хрефу
(Если лень делать сигу на функу то можна и самому ребилднуть функцию)
Окей, здесь давайте остановимся и подсчитаем, чего мы достигли к этому моменту.
- Создавать класс CEconItem и его объект (CreateSharedObject).
- Заполнять CEconItem инфой( AccountID, DefIndex, m_NumInventory )
- Получить CDOTAInventoryManager + CDOTAPlayerInventory
- Получить объект класса CDOTAItemDefinition
Осталось 2 важных шага, чтобы всё сработало.
- Добавить нашу новосозданную и заполненою инфой шмотку в кэш (SharedObjectCache)
-Оповестить наш инвентарь о том что у нас есть шмотка.
Внутри CDOTAGCClientSystem лежит указатель на SharedObjectCache (посмотрите реклассом)
CDOTAGCClientSystem получаем через хреф "cache_%u_%u.soc", там есть функа которая ретёрнит указатель( подсказка, где-то перед концом v6 = )
Теперь добавляем в наш кэш нашу шмотку при помощи вмтшной функции(если что индекс в client.dll будет либо -1 либо +1, помните это!)
И finally оповещаем инвентарь о наших действиях при помощи
Не забываем что если видим две точки то обычно это означает что в регистре RCX лежит указатель this
CDOTAPlayerInventory->SOCReated(soid*,CEconItem*,ESOCacheEvent::Incremental);
Кстате, рекомендую вызывать все функи в основном трэде. В ваших хуках фрейма и т.д. и т.п..
Лично я вот так добавляю:
Я знаю, что там возможно много лишнего наделал, но ничего страшного не случилось. Знаете как лучше сделать? Делайте, как знаете.Я всеголишь делюсь знаниями как Хермеус Мора ака Святой Деоген.
2. Мы добавили шмотки в инвентарь, но мы не можем их экипнуть(одеть, надеть, опробовать, и т.д.) Когда мы одеваем что-либо мы делаем запрос на сервер Валве с просьбой, чтоб можна одеть шмотку либо шмотки). Наша задача состоит в том, чтобы перехватить эту отправку запроса и ничего не высылать, а сделать ответ(give myself green light)
Делаем вмт хуки на
EGCResults ISteamGameCoordinator::SendMessage(uint32_t unMsgType, const void* pubData, uint32_t cubData) = 0;
bool ISteamGameCoordinator::IsMessageAvailable(uint32_t* pcubMsgSize) = 0;
EGCResults ISteamGameCoordinator::RetrieveMessage(uint32_t* punMsgType, void* pubDest, uint32_t cubDest, uint32_t* pcubMsgSize) = 0;
m_pSteamGameCoordinator можна получить из CGCClient, который может быть получен при помощи CreateInterface("DOTA_CLIENT_GCCLIENT");
Нам также нужно получить коллбек на OnGCMessageAvailable(); ( xref in client.dll "Recv msg %u (%s), %u bytes\n" )
чтоб заставить доту проверить наше сообщение(ответ).
Подключаем протобафы к нашему проекту( https://yougame.biz/threads/245652/ )
И обрабатываем наши хуки:
Ну и наконец осталось токо одно сделать, заставить доту проверить наше сообщение. Путём вызова Callback'a OnGCMessageAvailable(). Я его вызываю в хуке фрейма.
На данном этапе мы теперь можем экипнуть ЛЮБУЮ шмотку на героя.
Важная инфа. После выхода из игры, удаляйте файлики .soc из SteamLibrary\steamapps\common\dota 2 beta\game\dota.
Иначе шмотки будут дюпатся :D. Там есть проблема с стандартными вещами, типа если вы экипнете стандарт шмот который последний в списке то будет краш, экипайте стандарт шмот который первый, который будет сверху. Поймёте короч.
Но есть одна проблема с которой я столкнулся. В демке всё работает нормально, а в лобби(на вальвовском сервере) будет стандарт шмотка.
Пофиксить было не легко.
Некоторые челы рекомендовали ловить ивент в котором он заспавнится и делать все свои действия там, но я решил не ебать себе мозг и просто захерачить как работяга
В наших действиях должно быть такое:
- Очищаем m_hOldWearables нашей сущности (фантомки в данном случае) // credits to ExistedDim4
не сам указатель обнуляем, а значения которые он в себе хранит. Я надеюсь вы смотрели в гайд по дампу нашей легенды https://yougame.biz/threads/139802/
)
[class C_DOTA_BaseNPC ->>>>CUtlVector< CHandle< C_EconWearable > >] [m_hOldWearables] Offset 0xREVERSITE_SAMI_HAHAHAHAHHAHA
- Получаем каждую сущность внутри m_hMyWearables
[class C_BaseCombatCharacter ->>>> C_NetworkUtlVectorBase< CHandle< C_EconWearable > >] [m_hMyWearables] Offset 0x0xREVERSITE_SAMI_HAHAHAHAHHAHA.
Далее получаем AttributeContainer данной сущности
[class C_EconEntity ->>>> CAttributeContainer] [m_AttributeManager] Offset 0x960
Дампим шемой CAttributeContainer и смотрим на каком оффсете лежит C_EconItemView, это класс который вы тоже ДОЛЖНЫ задампить
Далее получаем DOTAItemDefinition по индексу внутри EconItemView(который как я сказал лежит в AttributeContainer)
, сравниваем слот и класс с тем что лежит в объектах лежащих в нашем контейнере vecItemEquipped.
Если Conditions are met,то мы инитим эту же EconItemView из нашего фейкового CEconItem* который лежит в кеше, КОТОРЫЙ мы должны получить при помощи GetSharedObjectByID. Есть тысячи способ это сделать ну такш быть я пальну вам немного.
Я уверен, что даже существует скорее всего какая-то вмтшка, но я просто решил замутить по своему. А что, нельзя?
И так, инитим нашу C_EconItemView путём eponymous функции C_EconItemView::Init/ //// credits to ExistedDim4
Last but not least, надо починить пару нетваров
а также PreDataUpdateнуть
и ещё нужно вызвать SetModel(WearableItem) которая пофиксит некоторые баги и с партиклями и с моделью. Она лежит внутри C_EconWearable::Spawn
хреф "CEntitySpawner<class CAdditionalWearable>::Spawn"
строка 163(после HexRay Decompilerинга ) , на ней функа C_EconWearable::Spawn внутри которой вам нужно взять какую-то из тех функций и вызвать.
И самый последний шаг это PostUpdate
На этом всё дамы и господа. Поздравляю вас. Теперь вы первый человек (мб второй хз) в мире, который имеет все шмотки в своём инвентаре,и который может понтоватся перед своими однокласниками гыгыгыгыгы. Будет норм если найдется здесь хоть 1 samaritan который реверснит стильки. Я делал когда-то но уже забыл, и впадлу. но дам подсказку что это нужно иметь дело с протобафами, в том же хуке SendMessage + RetrieveMessage :-)
Credits go to:
McDota
Legendary Liberalist
og
ExistedDim4
Morphling
Гарантирую, что простой копипастинг не спасёт человечество от Апокалипсиса aka VAC. Делаете всё на свой страх и риск.
Вродебы пересмотрел и ничего не забыл, но если что-то упустил, реверсите ёпта. А если ничего не выходит, ну тогда уж и быть оставьте сообщение ниже. Если у вас проблема с компиляцией протобафов, пж не пишите здесь а создайте отдельную тему. Я хочу чтобы в данном теме были сообщение только по отношению к ченжера.
В конечном итоге у тебя должно быть что-то такое:
(MENU)
(IN GAME)
Мой гайд складывается с двух важных штук, а именно:
1- Инвентори Ченжер - добавление шмоток в инвентарь.
2- Скин Ченжер - экипировка шмоток на героев, показывание шмоток в игре.
1 Ищем функцию, которая позволит нам создать объект вещи ( объект класса CEconItem)
Помочь в этом деле может дайлибсы, ссылку на которые вы можете найти в соседних темах. Вам нужен libclient.dylib.
Смотрите где она вызывается и берёте оттуда хрефы и идёте в client.dll и ищите что-то похожее на то, что в либе.
Могут быть разные способы вызова функции, я лично просто делаю сигу(signature) по байтам и просто юзаю популярный метод FindPattern.
Путём вызова вышеупомянотой функции, вы создадите пустую шмотку. Теперь нам нужно её заполнить информацией.
Если посмотреть на сурсы (
Пожалуйста, авторизуйтесь для просмотра ссылки.
), то увидим такие поля, как : m_ItemID, m_AccountID, m_unInventory, m_ItemDefIndex(важно) и т.д. Нам понадобится на самом деле только те которые я упомянул + некоторые которые нам нужно отреверсить самим.Заходим в IDA ->Class Informer-> ждём пока загрузит все вмтки(в случае,если ещё не пользовались этим плагином) -> вписываем CEconItem и ищём там где будет написано вот это(нам нужно то что имеет 9 функций)
Берём любую из фунок и ставим БП в чём угодно: Cheat Engine, x64dbg, etc.
Одеваем/Снимаем шмотку в инвентаре(Желательно та которая касается героя а не курьера т.д.)
Копируем значение из регистра RCX и идём в ReClass.Описываем наш класс.
Как видим размер структуры 0x50.(Айдишку аккаунта я заменил числами из вмт, поэтому не пытайтесь найти и не кидайте;-) )
Есть ещё полезная вмт на 6 индексе(0,1,2....6) которая сдампит вам поля. Благодаря этому можно правильно описать структуру тоже, но это ваш выбор. Я лично ручонками описовал а потом как-то решил проверить, правильно ли я отреверсил класс.
Думаю вам было достаточно 1-го часа , чтоб отреверсить нужное и понять где лежит поле [uint32_t m_ItemDefIndex].
Что нужно туда вписать? Как говорит само название переменной Item Definition Index ( индекс дефиниции айтема). Где нам взять этот индекс дефиниции? Well, если вы пробовали хоть раз делать скинченжер олдовским методом VPK, то думаю вы на память помните название файла items_game.txt.
Качаем и устанавливаем GCFScape.
Открываем и ищем(и открываем)
X:\SteamLibrary\steamapps\common\dota 2 beta\game\dota\pak01_dir.vpk
идём по пути \scripts\items\items_game.txt
Делаем магию экстракта и запускаем в формате текстового документа(рекомен. Notepad++)
Как называется ваша любимая шмотка в доте на английском языке? Моя аркана на секси фантомку, поэтому ищем CTRL+F -> Manifold Paradox
Мы видим на скрине дефиницию под индексом 7247,которая описывает нашу аркану(назваание,префаб,картинка,редкость, стили и т.д.)
Если покрутить выше-ниже то мы увидим что есть какая очерёдность, типа сверху будет 7246,а ниже 7248. В общем это и есть индекс дефиниции( в нашем случае ItemDefIndex арканы на фантомку будет 7247)
Следущее поле после дефиниции индекса которое нам понадобится это АккаунтАйди(а также SOID_t
C++:
struct SOID_t
{
UINT64 SteamID;
UINT32 Type;
};
Пожалуйста, авторизуйтесь для просмотра ссылки.
). Как нам получить CDOTAPlayerInventory? Просто, берёшь и получаешь либо вручную, либо через вмт(не помню индекса))
C++:
class CDOTAPlayerInventory;
class CDOTAInventoryManager
{
public:
CDOTAPlayerInventory*
{
return (CDOTAPlayerInventory*)((uintptr_t)this + OFFSET_ИЩЕМ_С_ПОМОЩЬЮ_РЕКЛАССА) // C Style но похуй, не умею я пользоватся reinterpret_cast-ом
}// не дереференсим иначе получим VMT-шку.
- поможет хреф "welcome_equip_seasonal_terrain" в IDA (client.dll)
EconItemSystem или EconItemSchema получаешь через хреф "scripts/items/items_game.txt" , смотришь кто вызывает(ну логично что в параметр rcx будет this(EconItemSystem))
Нам нужно получить объект CDOTAItemDefinition, чтобы из него брать всякие штуки позже, если нужно будет.
Делаем так само как мы делали в CEconItem( берём любую функу из вмт и ставим БП, я бы порекомендовал взять ту которая голубым цветом подсвечивается). Не хотел заново загружать дбшку новую, поэтому халявщики ловите немножко отревершеную вмт(мб wrong )
А, забыл сказать,скачайте себе FunctionStringAssociate на IDA. Полезная хуйня для реверсинга! Это чтоб видеть какие хрефы внутри функции(как видите выше на скрине)
По сути нам ток надо поле uint32_t m_itemDefIndex (оно лежит где-то между 0x8 - 0x18 поищите, пореверсите)
Есть функа CDOTAItemSchema::GetItemDefinitionByIndex,которую найдёте по хрефу
(Если лень делать сигу на функу то можна и самому ребилднуть функцию)
C++:
class ItemDefList
{
public:
uint32_t ItemDefIndex; //0x0000
uint32_t unk; //0x0004
CDOTAItemDefinition* ItemDefinition; //0x0008
int unk0; //0x0010
uint32_t unk1; //0x0014
}; //Size: 0x0018
class CEconItemSchema
{
public:
void* VMT; //0x0000
void* N00000344; //0x0008
void* N00000345; //0x0010
void* N00000346; //0x0018
void* N00000347; //0x0020
void* N00000348; //0x0028
void* N00000349; //0x0030
void* N0000034A; //0x0038
void* N0000034B; //0x0040
uint64_t ItemDefsa; //0x0048
ItemDefList* ItemDefs; //0x0050
uint64_t ItemDefsCapacity; //0x0058
int unk;
uint32_t ItemDefsCount;
int GetItemsCount()
{
return ItemDefsa;
}
CDOTAItemDefinition* GetItemDefinitionByIndex(int Index)
{
ItemDefList* first = ItemDefs;
ItemDefList* iter = first;
for (int i = 0; i < ItemDefsCount; ++i)
{
if (!iter)
{
continue;
}
if (!iter->ItemDefinition)
{
continue;
}
if (!*(uintptr_t*)iter->ItemDefinition)
{
continue;
}
if (iter->ItemDefIndex == Index)
{
return iter->ItemDefinition;
}
iter = (ItemDefList*)((uintptr_t)first + i * 0x18);
}
return nullptr;
}
}; // size 0x60
Не бейте палками за такую бюджетную итерацию.
Окей, здесь давайте остановимся и подсчитаем, чего мы достигли к этому моменту.
- Создавать класс CEconItem и его объект (CreateSharedObject).
- Заполнять CEconItem инфой( AccountID, DefIndex, m_NumInventory )
- Получить CDOTAInventoryManager + CDOTAPlayerInventory
- Получить объект класса CDOTAItemDefinition
Осталось 2 важных шага, чтобы всё сработало.
- Добавить нашу новосозданную и заполненою инфой шмотку в кэш (SharedObjectCache)
-Оповестить наш инвентарь о том что у нас есть шмотка.
Внутри CDOTAGCClientSystem лежит указатель на SharedObjectCache (посмотрите реклассом)
CDOTAGCClientSystem получаем через хреф "cache_%u_%u.soc", там есть функа которая ретёрнит указатель( подсказка, где-то перед концом v6 = )
Теперь добавляем в наш кэш нашу шмотку при помощи вмтшной функции(если что индекс в client.dll будет либо -1 либо +1, помните это!)
И finally оповещаем инвентарь о наших действиях при помощи
Не забываем что если видим две точки то обычно это означает что в регистре RCX лежит указатель this
CDOTAPlayerInventory->SOCReated(soid*,CEconItem*,ESOCacheEvent::Incremental);
C++:
enum ESOCacheEvent
{
/// Dummy sentinel value
eSOCacheEvent_None = 0,
/// We received a our first update from the GC and are subscribed
eSOCacheEvent_Subscribed = 1,
/// We lost connection to GC or GC notified us that we are no longer subscribed.
/// Objects stay in the cache, but we no longer receive updates
eSOCacheEvent_Unsubscribed = 2,
/// We received a full update from the GC on a cache for which we were already subscribed.
/// This can happen if connectivity is lost, and then restored before we realized it was lost.
eSOCacheEvent_Resubscribed = 3,
/// We received an incremental update from the GC about specific object(s) being
/// added, updated, or removed from the cache
eSOCacheEvent_Incremental = 4,
/// A lister was added to the cache
/// @see CGCClientSharedObjectCache::AddListener
eSOCacheEvent_ListenerAdded = 5,
/// A lister was removed from the cache
/// @see CGCClientSharedObjectCache::RemoveListener
eSOCacheEvent_ListenerRemoved = 6,
};
Кстате, рекомендую вызывать все функи в основном трэде. В ваших хуках фрейма и т.д. и т.п..
Лично я вот так добавляю:
C++:
for (int i = 0; i < g_CDOTAItemSchema->GetItemsCount(); ++i)
{
auto itemdef = g_CDOTAItemSchema->GetItemDefinitionByIndex(i);
if (!itemdef)
{
continue;
}
auto Object = CreateSharedObjectEconItem();
Object->m_ItemID = i + 100;
Object->m_numInventory = i + 1;
Object->m_DefIndex = itemdef->DefinitionIndex;
Object->m_unStyle = 0;
Object->m_AccountID = g_InventoryManager->GetPlayerInventory()->soid.SteamID;
g_DOTAGCClientSystem->SOCache->AddObject(Object);
g_InventoryManager->GetPlayerInventory()->SOCreated(&g_InventoryManager->GetPlayerInventory()->soid, Object, ESOCacheEvent::eSOCacheEvent_Incremental);
}
2. Мы добавили шмотки в инвентарь, но мы не можем их экипнуть(одеть, надеть, опробовать, и т.д.) Когда мы одеваем что-либо мы делаем запрос на сервер Валве с просьбой, чтоб можна одеть шмотку либо шмотки). Наша задача состоит в том, чтобы перехватить эту отправку запроса и ничего не высылать, а сделать ответ(give myself green light)
Делаем вмт хуки на
EGCResults ISteamGameCoordinator::SendMessage(uint32_t unMsgType, const void* pubData, uint32_t cubData) = 0;
bool ISteamGameCoordinator::IsMessageAvailable(uint32_t* pcubMsgSize) = 0;
EGCResults ISteamGameCoordinator::RetrieveMessage(uint32_t* punMsgType, void* pubDest, uint32_t cubDest, uint32_t* pcubMsgSize) = 0;
m_pSteamGameCoordinator можна получить из CGCClient, который может быть получен при помощи CreateInterface("DOTA_CLIENT_GCCLIENT");
Нам также нужно получить коллбек на OnGCMessageAvailable(); ( xref in client.dll "Recv msg %u (%s), %u bytes\n" )
чтоб заставить доту проверить наше сообщение(ответ).
Подключаем протобафы к нашему проекту( https://yougame.biz/threads/245652/ )
И обрабатываем наши хуки:
C++:
class ItemEquipped
{
public:
uint16_t HeroClass;
uint16_t HeroSlot;
uint32_t ItemDefIndex;
};
std::vector<ItemEquipped*> vecItemEquipped;
EGCResults SendMessagehk(ISteamGameCoordinator* thisptr, uint32_t unMsgType, ProtoBufMsgHeader_t* pubData, uint32_t cubData)
{
if (!g_GCClient || !g_GCClient->m_pSteamGameCoordinator)
{
return EGCResults::k_EGCResultNotLoggedOn;
}
int MessageType = unMsgType & 0x7FFFFFFF;
if (MessageType == k_EMsgClientToGCEquipItems) //EGCItemMsg::k_EMsgClientToGCEquipItems
{
CMsgClientToGCEquipItems message;
int Headersize = pubData->m_cubProtoBufExtHdr + sizeof(ProtoBufMsgHeader_t);
if (message.ParsePartialFromArray((const void*)((uintptr_t)pubData + Headersize), cubData - Headersize))
{
auto _class = message.equips().at(0).new_class();
auto _slot = message.equips().at(0).new_slot();
auto item = (C_EconItem*)g_InventoryManager->GetPlayerInventory()->GetItem(message.equips().at(0).item_id());
if (item)
{
int sizeofclass = sizeof(C_EconItem);
item->Equip(_class, _slot);
ItemEquipped* ie = (ItemEquipped*)g_pMemAlloc->Alloc(sizeof(ItemEquipped));//new ItemEquipped();
ie->HeroClass = _class;
ie->HeroSlot = _slot;
ie->HeroSlot = g_InventoryManager->GetSlotForHero(_class,_slot);
ie->ItemDefIndex = item->m_DefIndex;
auto slots = g_InventoryManager->GetItemSlotsForHero(ie->HeroClass);
vecItemEquipped.push_back(ie);
g_InventoryManager->GetPlayerInventory()->SOUpdated(&g_InventoryManager->GetPlayerInventory()->soid, (CSharedObject*)item, eSOCacheEvent_Incremental);
CMsgClientToGCEquipItemsResponse newmessage;
newmessage.set_so_cache_version_id(1);
auto size = newmessage.ByteSizeLong();
CMsgProtoBufHeader origheader; // original
CMsgProtoBufHeader header; // fakeheader
if (pubData->m_cubProtoBufExtHdr > 0)
{
if (origheader.ParsePartialFromArray((const void*)((ui)pubData + sizeof(ProtoBufMsgHeader_t)), pubData->m_cubProtoBufExtHdr))
{
if (origheader.has_job_id_source())
header.set_job_id_target(origheader.job_id_source());
}
else
{
ConColorMsg(&ColorABGR.Red, "Error in Parsing Message\n");
}
}
else
{
ConColorMsg(&ColorABGR.Red, "Error in SendMessage\n");
}
auto headerSize = header.ByteSizeLong(); // size of the header
ProtoBufMsgHeader_t meta_header{ k_EMsgClientToGCEquipItemsResponse | 0x80000000,(UINT32)headerSize };
std::vector<BYTE>MessageBytes;
MessageBytes.resize(sizeof(meta_header) + newmessage.ByteSizeLong() + headerSize);
*(ProtoBufMsgHeader_t*)MessageBytes.data() = meta_header;
header.SerializeToArray(MessageBytes.data() + sizeof(ProtoBufMsgHeader_t), header.ByteSizeLong());
newmessage.SerializeToArray(MessageBytes.data() + sizeof(ProtoBufMsgHeader_t) + headerSize, newmessage.ByteSizeLong());
Messages.push(MessageBytes);
bHasToCheckMessages = true;
}
return EGCResults::k_EGCResultOK;
}
}
else if (MessageType == k_EMsgClientToGCSetItemStyle)
{
/// Пока что не интересовался стилями, кто захочет тот сделает, думаю.
}
return oSendMessage(thisptr, unMsgType, pubData, cubData);
}
bool hkIsMessageAvailable(ISteamGameCoordinator* thisptr, uint32_t* pcubMsgSize)
{
if (!Messages.empty())
{
*pcubMsgSize = Messages.front().size();
return true;
}
return oIsMessageAvailable(thisptr, pcubMsgSize);
}
EGCResults RetrieveMessage(ISteamGameCoordinator* thisptr, uint32_t* punMsgType, void* pubDest, uint32_t cubDest, uint32_t* pcubMsgSize)
{
//int MessageType = *punMsgType & 0x7FFFFFFF;
if (!Messages.empty())
{
auto message = Messages.front();
if (cubDest < message.size())
{
ConMsg("small\n");
return EGCResults::k_EGCResultBufferTooSmall;
}
//auto MessageHeader = reinterpret_cast<const ProtoBufMsgHeader_t*>(Message.data());
ProtoBufMsgHeader_t* header = (ProtoBufMsgHeader_t*)(Messages.front().data());
//header->FlagMsg();
*punMsgType = header->m_EMsgFlagged;
std::memcpy((void*)pubDest, (const void*)header, Messages.front().size());
Messages.pop();
ConMsg("dota has eaten our message\n");
return EGCResults::k_EGCResultOK;
}
return oReceiveMessage(thisptr, punMsgType, pubDest, cubDest, pcubMsgSize);
}
Ну и наконец осталось токо одно сделать, заставить доту проверить наше сообщение. Путём вызова Callback'a OnGCMessageAvailable(). Я его вызываю в хуке фрейма.
C++:
if (bHasToCheckMessages)
{
ConMsg("Message is available\n");
bHasToCheckMessages = false;
oOnMessageAvailable(g_GCClient);
}
На данном этапе мы теперь можем экипнуть ЛЮБУЮ шмотку на героя.
Важная инфа. После выхода из игры, удаляйте файлики .soc из SteamLibrary\steamapps\common\dota 2 beta\game\dota.
Иначе шмотки будут дюпатся :D. Там есть проблема с стандартными вещами, типа если вы экипнете стандарт шмот который последний в списке то будет краш, экипайте стандарт шмот который первый, который будет сверху. Поймёте короч.
Но есть одна проблема с которой я столкнулся. В демке всё работает нормально, а в лобби(на вальвовском сервере) будет стандарт шмотка.
Пофиксить было не легко.
Некоторые челы рекомендовали ловить ивент в котором он заспавнится и делать все свои действия там, но я решил не ебать себе мозг и просто захерачить как работяга
C++:
GetAsyncKeyState(VK_F1) & 1
{
Действия
}
- Очищаем m_hOldWearables нашей сущности (фантомки в данном случае) // credits to ExistedDim4
не сам указатель обнуляем, а значения которые он в себе хранит. Я надеюсь вы смотрели в гайд по дампу нашей легенды https://yougame.biz/threads/139802/
)
[class C_DOTA_BaseNPC ->>>>CUtlVector< CHandle< C_EconWearable > >] [m_hOldWearables] Offset 0xREVERSITE_SAMI_HAHAHAHAHHAHA
- Получаем каждую сущность внутри m_hMyWearables
[class C_BaseCombatCharacter ->>>> C_NetworkUtlVectorBase< CHandle< C_EconWearable > >] [m_hMyWearables] Offset 0x0xREVERSITE_SAMI_HAHAHAHAHHAHA.
Далее получаем AttributeContainer данной сущности
[class C_EconEntity ->>>> CAttributeContainer] [m_AttributeManager] Offset 0x960
Дампим шемой CAttributeContainer и смотрим на каком оффсете лежит C_EconItemView, это класс который вы тоже ДОЛЖНЫ задампить
Далее получаем DOTAItemDefinition по индексу внутри EconItemView(который как я сказал лежит в AttributeContainer)
, сравниваем слот и класс с тем что лежит в объектах лежащих в нашем контейнере vecItemEquipped.
C++:
for (const auto& obj : vecItemEquipped)
{
if (obj->HeroSlot == definition->GetItemHeroSlot()) листайте выше, я на скрине вам раскрыл карты в вмтшке DOTAItemDefinition
{
if (obj->HeroClass == definition->GetItemHeroClass()) /// листайте выше, я на скрине вам раскрыл карты в вмтшке DOTAItemDefinition
{
C++:
C_EconItem* CDOTAGCClientSystem::GetObjectByItemIndex(UINT32 Index)
{
for (int i = 0; i < SOCache->CGCClientSharedObjectTypeCaches->Items->Count; i++)
{
auto obj = *(uintptr_t*)((uintptr_t)SOCache->CGCClientSharedObjectTypeCaches->Items->Message + i * 0x8);
if (obj)
{
C_EconItem* ptr = (C_EconItem*)obj;
if (ptr->m_DefIndex == Index)
{
return ptr;
}
}
}
return nullptr;
}
И так, инитим нашу C_EconItemView путём eponymous функции C_EconItemView::Init/ //// credits to ExistedDim4
C++:
InitEconItemView(ptr, fakeitem, 0); // EconItemView*,EconItem*,0
C++:
WearableItem->SetVarBool(m_bHiddenByEquipmentSlot,false)/// Если кто вдруг уже забыл, WearableItem это сущность из LocalEntity-> m_hMyWearables[ ] в котором лежат хендлы наших шмоток, чтоб получить сущность юзайте McDota's EntitySystem
WearableItem->SetVarBool(m_bOwnerModelChanged,false)
WearableItem->SetVarBool(m_bIsGeneratingEconItem,false)
WearableItem->SetVarBool(m_bHiddenByCombiner,false)
WearableItem->SetVarBool(m_bIsItemVisibleOnGeneratedEntity,true)
C++:
enum DataUpdateType_t
{
DATA_UPDATE_CREATED = 0, // indicates it was created +and+ entered the pvs
// DATA_UPDATE_ENTERED_PVS,
DATA_UPDATE_DATATABLE_CHANGED,
// DATA_UPDATE_LEFT_PVS,
// DATA_UPDATE_DESTROYED, // FIXME: Could enable this, but it's a little worrying
// since it changes a bunch of existing code
};
C++:
LocalEntity->PreDataUpdate(DataUpdateType_t::DATA_UPDATE_CREATED); /// после фикса нетваров
C++:
Если работает, то индексы правильные скорее всего.
class C_BaseEntity
{
public:
virtual void func0();//0x0
virtual void func1();//0x8
virtual void func2();//0x10
virtual void func3();//0x18
virtual void func4();//0x20
virtual void func5();//0x28
virtual void func6();//0x30
virtual bool Spawn(void* ptr);//0x38
virtual void PostDataUpdate(DataUpdateType_t);//0x40
virtual void func9();//0x48
virtual void func10();//0x50
virtual void func11();//0x58
virtual void func12();//0x60
virtual void func13();//0x68
virtual void PreDataUpdate(DataUpdateType_t);//0x70
}
хреф "CEntitySpawner<class CAdditionalWearable>::Spawn"
строка 163(после HexRay Decompilerинга ) , на ней функа C_EconWearable::Spawn внутри которой вам нужно взять какую-то из тех функций и вызвать.
И самый последний шаг это PostUpdate
C++:
LocalEntity->PostDataUpdate(DataUpdateType_t::DATA_UPDATE_DATATABLE_CHANGED);
На этом всё дамы и господа. Поздравляю вас. Теперь вы первый человек (мб второй хз) в мире, который имеет все шмотки в своём инвентаре,и который может понтоватся перед своими однокласниками гыгыгыгыгы. Будет норм если найдется здесь хоть 1 samaritan который реверснит стильки. Я делал когда-то но уже забыл, и впадлу. но дам подсказку что это нужно иметь дело с протобафами, в том же хуке SendMessage + RetrieveMessage :-)
Credits go to:
McDota
Legendary Liberalist
og
ExistedDim4
Morphling
Гарантирую, что простой копипастинг не спасёт человечество от Апокалипсиса aka VAC. Делаете всё на свой страх и риск.
Вродебы пересмотрел и ничего не забыл, но если что-то упустил, реверсите ёпта. А если ничего не выходит, ну тогда уж и быть оставьте сообщение ниже. Если у вас проблема с компиляцией протобафов, пж не пишите здесь а создайте отдельную тему. Я хочу чтобы в данном теме были сообщение только по отношению к ченжера.
Последнее редактирование: