Ревёрсер среднего звена
-
Автор темы
- #1
Как известно, клиенты сетевых игр общаются с сервером посредством посылки сообщений. В доте этим занимается класс NetChannel, с ним и будем работать. В нём есть методы SendNetMessage & PostReceivedNetMessage, думаю, тут объяснения излишни.
В первую очередь нужно определить, где его взять, тут всё просто, за нас
Однако есть проблема: CreateNetChannel вызывается только при подключении к матчу, если вдруг вы захотите реинжектнуть чит — никакого Нетчана у вас уже не будет. Но выход есть. В конструкторе и деструкторе Нетчана лежит указатель на его же виртуальную таблицу, который засовывается в [rcx+0]. Следовательно, нам нужно просто вытащить из метода этот указатель и сделать байтхук вышеуказанных функций класса. В IDA открываем networksystem.dll, ищем функцию с хрефом "CNetChan::m_StreamVoice"(конструктор нетчана), видим в начале vtable, делаем сигнатуру на функцию. Или используете проверенную годами сигнатуру, которую, как и в целом метод хукинга, нам предоставил Liberalist. Оформляем хук через MinHook:
Тайпдефы для SendNetMessage и PostReceivedNetMessage напрямую пастим с McDota вместе с СNetworkMessages.h и INetChannel.h. Так, стоп, а что это за google::protobuf::Message? Дота использует для сериализации сообщений гугловскую библиотеку Protocol Buffers. Суть проста: пишешь .proto-файл с описанием структуры сообщений, а протокомпилятор за тебя это превращает в рабочие C++-файлы. Её можно сбилдить из сурсов, но мы сделаем это цивилизованно. Ставим себе пакетный менеджер vcpkg по
В vcpkg/installed/x64-windows/bin лежат libprotobuf.dll и libprotobuf-lite.dll. То же самое в vcpkg/installed/x64-windows/debug/bin, но с суффиксом -d. Их надо либо загружать в процесс перед вашим читом, либо закинуть в папку с dota2.exe, чтобы ваш чит от имени игры имел к ним доступ. Но и это не всё, ведь нам нужны вышеуказанные .proto-файлы, чтобы скомпилить классы месседжей. В папке x64-windows/tools/ лежит protoc.exe, добавьте её себе в PATH, чтобы из любого места в консоли этот экзешник вызывать. Сами прото-файлы обновляются в
Чтобы вы не думали и не гадали, какие из них вам нужны, я переписал PowerShell-скрипт Wolf49406 в более компактный вид:
Копируете в .ps1 файл, вписываете в переменные путей что надо и запускаете. Потом в визуалке включаете все скомпиленные файлы к себе в проект(их очень желательно инклюднуть в pch.h, чтобы не сидеть каждый раз по 5 минут, ожидая компиляцию). Заметьте, что если у вас уже есть enumы типа DotaUnitOrder_t, они будут конфликтовать с протобафовскими. Удалите свои и интегрируйте в проект новые, т. к. они по сути самообновляемые!
Всё работает и компилируется? Отлично, вернёмся к нашим виртуальным функциям. Сами индексы обновить несложно:
PostReceivedNetMessage — в дилибе на индексе 88 делают call на метод CTSQueue. В DLLке смотрим содержание методов выше этого индекса, замечаем, что в одном из них в конце делается call с хрефом «CTSQueue corruption». Это и есть наш метод на индексе 86
SendNetMessage — хреф «CNetChan::SendNetMessage» на индексе 69, вот и всё.
Вот теперь можно и спросить, что можно делать с помощью перехвата сообщений. На самом деле много чего, в этом гайде рассмотрим базовый пример.
Сделаем-ка рофл-функцию из лучшего чита во Вселенной, которая заменяет анимацию атаки шейкера.
В первую очередь нужно определить, какое сообщение нам нужно перехватывать. Гейб Ньюэлл любезно сделал для нас консольную команду
Ага, то есть за анимацию юнита отвечает сообщение CDOTAUserMsg_TE_UnitAnimation, ID которого 521, а activity анимации атаки это 1503(так у всех юнитов, у ренжевиков при атаке со второй руки активити 1504). При этом entity это handle на сущность.
Замахиваемся ещё пару раз, видим, что sequence_variant меняется в зависимости от варианта анимации от 0 до 2. Теперь кастуем Enchant Totem и замахиваемся с ним, видим sequence_variant: 3. Значит, чтобы анимация нам показывалась так, как будто он всегда бьёт под тотемом, нужно перед обработкой заменить sequence_variant на 3:
И вот наш шейкер без каких-либо баффов бьёт с приятной анимацией.
За серьёзными реализациями намного более полезных функций загляните
Спасибо Liberalist, Wolf49406 и Гейбу Ньюэллу лично за предоставление информации по теме данного гайда. Если есть какие-то вопросы, пишите здесь, будем дополнять гайд вместе
В первую очередь нужно определить, где его взять, тут всё просто, за нас
Пожалуйста, авторизуйтесь для просмотра ссылки.
уже наревёрсил. В networksystem.dll есть публичный интерфейс NetworkSystem, в нём на индексе 26 есть метод CreateNetChannel, а уже там хукаются методы Нетчана.
NetChannel vtable hook:
// класс VMT пастить c McDota
namespace VMTs {
inline std::unique_ptr<VMT> NetworkSystem{};
}
void hkPostReceivedNetMessage(INetChannel* thisptr, NetMessageHandle_t* messageHandle, google::protobuf::Message* msg, void const* type, int bits) {
VMTs::NetChannel->GetOriginalMethod<decltype(&hkPostReceivedNetMessage)>(86)(thisptr, messageHandle, msg, type, bits);
}
inline bool hkSendNetMessage(INetChannel* thisptr, NetMessageHandle_t* messageHandle, google::protobuf::Message* msg, NetChannelBufType_t type) {
return VMTs::NetChannel->GetOriginalMethod<decltype(&hkSendNetMessage)>(69)(thisptr, messageHandle, msg, type);
}
inline void* CreateNetChannel(void* thisptr, int unk, void* ns_addr, const char* str, unsigned int uUnk, unsigned int uUnk2) {
VMTs::NetChannel.reset();
void* ret = VMTs::NetworkSystem->GetOriginalMethod<decltype(&CreateNetChannel)>(26)(thisptr, unk, ns_addr, str, uUnk, uUnk2);
VMTs::NetChannel = std::unique_ptr<VMT>(new VMT(ret));
VMTs::NetChannel->HookVM(hkSendNetMessage, 69);
VMTs::NetChannel->HookVM(hkPostReceivedNetMessage, 86);
VMTs::NetChannel->ApplyVMT();
return ret;
}
void InitVirtualHooks(){
auto NetworkSystem = GetInterface<void>("networksystem.dll", "NetworkSystemVersion001");
VMTs::NetworkSystem = std::unique_ptr<VMT>(new VMT(Interfaces::NetworkSystem));
VMTs::NetworkSystem->HookVM(CreateNetChannel, 26);
VMTs::NetworkSystem->ApplyVMT();
}
C++:
inline void HookFunc(void* func, void* detour, void* original, const std::string& name) {
if (MH_CreateHook(func, detour,
(LPVOID*)original) != MH_OK ||
MH_EnableHook(func) != MH_OK)
std::cout << "Could not hook" << name << "()!\n";
};
#define HOOKFUNC(func) HookFunc(##func, &hk##func, &o##func, #func)
// работает с IDA-форматом сигнатур, у вас скорее всего как-то по-другому будет
#define SIGSCAN(func, sig, dll) ParseCombo(sig, funcAddr, funcAddrMask); \
func =(decltype(func))PatternScanExModule(ctx.CurProcHandle, ctx.CurProcId, dll, funcAddr, funcAddrMask)
typedef void(__fastcall* PostReceivedNetMessageFn)(INetChannel* thisptr, NetMessageHandle_t* messageHandle, google::protobuf::Message* msg, void const* type, int bits);
inline PostReceivedNetMessageFn oPostReceivedNetMessage{};
inline void hkPostReceivedNetMessage(INetChannel* thisptr, NetMessageHandle_t* messageHandle, google::protobuf::Message* msg, void const* type, int bits) {
oPostReceivedNetMessage(thisptr, messageHandle, msg, type, bits);
}
typedef bool(__fastcall* SendNetMessageFn)(INetChannel* thisptr, NetMessageHandle_t* messageHandle, google::protobuf::Message* msg, NetChannelBufType_t type);
inline SendNetMessageFn oSendNetMessage{};
inline bool hkSendNetMessage(INetChannel* thisptr, NetMessageHandle_t* messageHandle, google::protobuf::Message* msg, NetChannelBufType_t type) {
return oSendNetMessage(thisptr, messageHandle, msg, type);
}
inline void HookNetChan(bool log) {
char funcAddr[60]{}, funcAddrMask[60]{};
uintptr_t addr;
SIGSCAN(addr, "40 53 56 57 41 56 48 83 EC ?? 45 33 F6 48 8D 71", L"networksystem.dll"); // NetChan constructor
uintptr_t** vtable = (uintptr_t**)GetAbsoluteAddress(addr + 0x15, 3, 7); // vtable ptr at 0x15
uintptr_t* PostReceivedNetMessage = vtable[86], *SendNetMessage = vtable[69];
HOOKFUNC(PostReceivedNetMessage);
HOOKFUNC(SendNetMessage);
}
Пожалуйста, авторизуйтесь для просмотра ссылки.
на их же сайте(noad), не забываем про секцию о интеграции с Visual Studio. Потом в папке с vcpkg.exe открываем консоль и пишем vcpkg.exe install protobuf protobuf:x64-windows
. Установится всё в папку installed/x64-windows. Теперь в ваших проектах на визуалке уже будут прилинкованы эти зависимости, но если хочется, то в настройках проекта отключите vcpkg и скопируйте из вышеуказанной папки себе include/, lib/ и debug/lib/.В vcpkg/installed/x64-windows/bin лежат libprotobuf.dll и libprotobuf-lite.dll. То же самое в vcpkg/installed/x64-windows/debug/bin, но с суффиксом -d. Их надо либо загружать в процесс перед вашим читом, либо закинуть в папку с dota2.exe, чтобы ваш чит от имени игры имел к ним доступ. Но и это не всё, ведь нам нужны вышеуказанные .proto-файлы, чтобы скомпилить классы месседжей. В папке x64-windows/tools/ лежит protoc.exe, добавьте её себе в PATH, чтобы из любого места в консоли этот экзешник вызывать. Сами прото-файлы обновляются в
Пожалуйста, авторизуйтесь для просмотра ссылки.
Чтобы вы не думали и не гадали, какие из них вам нужны, я переписал PowerShell-скрипт Wolf49406 в более компактный вид:
Пожалуйста, авторизуйтесь для просмотра ссылки.
Копируете в .ps1 файл, вписываете в переменные путей что надо и запускаете. Потом в визуалке включаете все скомпиленные файлы к себе в проект(их очень желательно инклюднуть в pch.h, чтобы не сидеть каждый раз по 5 минут, ожидая компиляцию). Заметьте, что если у вас уже есть enumы типа DotaUnitOrder_t, они будут конфликтовать с протобафовскими. Удалите свои и интегрируйте в проект новые, т. к. они по сути самообновляемые!
Всё работает и компилируется? Отлично, вернёмся к нашим виртуальным функциям. Сами индексы обновить несложно:
PostReceivedNetMessage — в дилибе на индексе 88 делают call на метод CTSQueue. В DLLке смотрим содержание методов выше этого индекса, замечаем, что в одном из них в конце делается call с хрефом «CTSQueue corruption». Это и есть наш метод на индексе 86
SendNetMessage — хреф «CNetChan::SendNetMessage» на индексе 69, вот и всё.
Вот теперь можно и спросить, что можно делать с помощью перехвата сообщений. На самом деле много чего, в этом гайде рассмотрим базовый пример.
Сделаем-ка рофл-функцию из лучшего чита во Вселенной, которая заменяет анимацию атаки шейкера.
В первую очередь нужно определить, какое сообщение нам нужно перехватывать. Гейб Ньюэлл любезно сделал для нас консольную команду
net_showreliable 1
, которая логирует в консоль все получаемые сообщения. Это будет вашим основным инструментом анализа нетворк-составляющей доты. Итак, загружаемся в демку за шейкера, прописываем команду, спавним неподвижную цель и замахиваемся на неё(но не бьём, чтобы не плодить лишние события). В консоли видим:Ага, то есть за анимацию юнита отвечает сообщение CDOTAUserMsg_TE_UnitAnimation, ID которого 521, а activity анимации атаки это 1503(так у всех юнитов, у ренжевиков при атаке со второй руки активити 1504). При этом entity это handle на сущность.
Замахиваемся ещё пару раз, видим, что sequence_variant меняется в зависимости от варианта анимации от 0 до 2. Теперь кастуем Enchant Totem и замахиваемся с ним, видим sequence_variant: 3. Значит, чтобы анимация нам показывалась так, как будто он всегда бьёт под тотемом, нужно перед обработкой заменить sequence_variant на 3:
C++:
using ENT_HANDLE = uint32_t;
constexpr uint32_t ENT_HANDLE_MASK = 0x7fff;
constexpr uint32_t NET_ENT_HANDLE_MASK = 0x3fff;
#define NH2IDX(H) ((H) & NET_ENT_HANDLE_MASK) // Network messages have a mask that's two times smaller
#define H2IDX(H) ((H) & ENT_HANDLE_MASK) // Entity handle to entity index
// здесь shaker это C_DOTA_BaseNPC_Hero*, т. к. нам нужны сообщения именно об анимациях шейкера
// думаю справитесь с инициализацией этого
inline void hkPostReceivedNetMessage(INetChannel* thisptr, NetMessageHandle_t* messageHandle, google::protobuf::Message* msg, void const* type, int bits) {
if (messageHandle->messageID != 4) // not CNetMsg_Tick [4]
{
if (messageHandle->messageID == 521 && shaker) {
// Конкретный header с классом месседжа вам подскажет визуалка(dota_usermessages.pb.h)
auto animMsg = reinterpret_cast<CDOTAUserMsg_TE_UnitAnimation*>(msg);
if (animMsg->activity() == 1503 &&
NH2IDX(animMsg->entity()) == H2IDX(shaker->GetIdentity().entHandle))
animMsg->set_sequence_variant(3);
}
}
oPostReceivedNetMessage(thisptr, messageHandle, msg, type, bits);
}
За серьёзными реализациями намного более полезных функций загляните
Пожалуйста, авторизуйтесь для просмотра ссылки.
, а конкретно в Dota2Cheat/Hooks/NetChannel.hСпасибо Liberalist, Wolf49406 и Гейбу Ньюэллу лично за предоставление информации по теме данного гайда. Если есть какие-то вопросы, пишите здесь, будем дополнять гайд вместе
Последнее редактирование: