Гайд Продолжение. Сетевые сообщения и NetChannel

Ревёрсер среднего звена
Пользователь
Статус
Оффлайн
Регистрация
24 Ноя 2022
Сообщения
303
Реакции[?]
108
Поинты[?]
57K
Как известно, клиенты сетевых игр общаются с сервером посредством посылки сообщений. В доте этим занимается класс NetChannel, с ним и будем работать. В нём есть методы SendNetMessage & PostReceivedNetMessage, думаю, тут объяснения излишни.

В первую очередь нужно определить, где его взять, тут всё просто, за нас
Пожалуйста, авторизуйтесь для просмотра ссылки.
уже наревёрсил. В 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();
}
Однако есть проблема: CreateNetChannel вызывается только при подключении к матчу, если вдруг вы захотите реинжектнуть чит — никакого Нетчана у вас уже не будет. Но выход есть. В конструкторе и деструкторе Нетчана лежит указатель на его же виртуальную таблицу, который засовывается в [rcx+0]. Следовательно, нам нужно просто вытащить из метода этот указатель и сделать байтхук вышеуказанных функций класса. В IDA открываем networksystem.dll, ищем функцию с хрефом "CNetChan::m_StreamVoice"(конструктор нетчана), видим в начале vtable, делаем сигнатуру на функцию. Или используете проверенную годами сигнатуру, которую, как и в целом метод хукинга, нам предоставил Liberalist. Оформляем хук через MinHook:

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);
}
Тайпдефы для SendNetMessage и PostReceivedNetMessage напрямую пастим с McDota вместе с СNetworkMessages.h и INetChannel.h. Так, стоп, а что это за google::protobuf::Message? Дота использует для сериализации сообщений гугловскую библиотеку Protocol Buffers. Суть проста: пишешь .proto-файл с описанием структуры сообщений, а протокомпилятор за тебя это превращает в рабочие C++-файлы. Её можно сбилдить из сурсов, но мы сделаем это цивилизованно. Ставим себе пакетный менеджер vcpkg по
Пожалуйста, авторизуйтесь для просмотра ссылки.
на их же сайте(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

PostReceivedNetMessage.png

SendNetMessage — хреф «CNetChan::SendNetMessage» на индексе 69, вот и всё.

Вот теперь можно и спросить, что можно делать с помощью перехвата сообщений. На самом деле много чего, в этом гайде рассмотрим базовый пример.

Сделаем-ка рофл-функцию из лучшего чита во Вселенной, которая заменяет анимацию атаки шейкера.
В первую очередь нужно определить, какое сообщение нам нужно перехватывать. Гейб Ньюэлл любезно сделал для нас консольную команду net_showreliable 1, которая логирует в консоль все получаемые сообщения. Это будет вашим основным инструментом анализа нетворк-составляющей доты. Итак, загружаемся в демку за шейкера, прописываем команду, спавним неподвижную цель и замахиваемся на неё(но не бьём, чтобы не плодить лишние события). В консоли видим:
Screenshot_497.png
Ага, то есть за анимацию юнита отвечает сообщение 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);
}
И вот наш шейкер без каких-либо баффов бьёт с приятной анимацией.
Screenshot_499.png

За серьёзными реализациями намного более полезных функций загляните
Пожалуйста, авторизуйтесь для просмотра ссылки.
, а конкретно в Dota2Cheat/Hooks/NetChannel.h

Спасибо Liberalist, Wolf49406 и Гейбу Ньюэллу лично за предоставление информации по теме данного гайда. Если есть какие-то вопросы, пишите здесь, будем дополнять гайд вместе
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
12 Ноя 2022
Сообщения
63
Реакции[?]
23
Поинты[?]
3K
Респект особенно за анимацию атаки, не знал что инфа о руке передаётся!

Кстати кажется понял почему у тебя была проблема с протобафом и почему она решилась.
Ты изначально линковался статически, а статическая либа vcpkg по дефолту /MT
а в чите у тебя /MD
Дллка протобафа использует /MD и чит /MD потому всё норм

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

чтобы слинковаться статически с протобафом - протобаф должен юзать ту же CRT что и длл чита.
Так что нужно правильно выбрать триплет в vcpkg. Если чит юзает /MD то триплет должен быть x64-windows-static-md
 
Пользователь
Статус
Оффлайн
Регистрация
8 Апр 2022
Сообщения
673
Реакции[?]
106
Поинты[?]
69K
бля зря я решил пересобирать протобафы...
1678848085344.png
кто знает что делать? в вс все закинул, либы подключил
1678848119148.png1678848128729.pngпричем в релиз компилится а в дебаг не
 
Последнее редактирование:
Ревёрсер среднего звена
Пользователь
Статус
Оффлайн
Регистрация
24 Ноя 2022
Сообщения
303
Реакции[?]
108
Поинты[?]
57K
бля зря я решил пересобирать протобафы...
Посмотреть вложение 241713
кто знает что делать? в вс все закинул, либы подключил
Посмотреть вложение 241714Посмотреть вложение 241715причем в релиз компилится а в дебаг не
Удали steammessages-base, он конфликтует с steammessages
Почему там unresolved symbols при линковке, если честно, не знаю(да ещё и зависящие от конфигурации)
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
бля зря я решил пересобирать протобафы...
Посмотреть вложение 241713
кто знает что делать? в вс все закинул, либы подключил
Посмотреть вложение 241714Посмотреть вложение 241715причем в релиз компилится а в дебаг не
там вроде вот эту хуйню удалить надо
1678906785014.png
+ там на настроечки в визуалке внимательно посмотри(компилятор одинаковой версии должен быть использован при билде либы и при билде твоего проекта, + MT/MD(статик/длл СРТ) настройки должны совпадать у либы и у проекта + на директории инклюдов и прочую хуйню тоже посмотри чтобы в дебаге и релизе нормальные были + либу правильную линкуй(на дебаге дебаг версию, а на релизе релиз версию. в настройках проекта или #ifdef'ом каким-нибудь + #pragma lib(comment, "shit.lib"))) + там папка гуглов должна быть где-нибудь по пути инклюда(google\protobuf)(при этом сами файлики из этой папки не надо в сам проект инклюдить, они просто должны лежать на диске и в путях инклюда прописаны, их .h/.cc собранные файлики юзают) + протобаф файлики должны быть собраны под конкретную/близкую версию либы - ну то есть иногда нельзя старую либу юзать с новыми .h/.cc файликами условно если ты эти файлики собираешь современным protoc
а так ваще нахуя тебе дебаг? в релизе спокойно всё дебажить можно(в х64дбг например. если дебажить через х64дбг например(асм-левел дебаггинг) а не через визуалку(сурс-левел дебаггинг) то это как раз повысит твой скилл дебага реверса асм и тд), символы в релизе тоже генерятся если надо(упрощают задачу. практически сурс-левел дебаггинг) + тебе всё равно возможно рано или поздно придётся дебажить релизную длл, так можешь сейчас научиться это делать чтобы потом не ахуевать
 
Администратор
Администратор
Статус
Оффлайн
Регистрация
20 Янв 2014
Сообщения
6,564
Реакции[?]
8,445
Поинты[?]
289K
Вот это действительно хороший контент.
+rep топик-стартеру.
 
Пользователь
Статус
Оффлайн
Регистрация
8 Апр 2022
Сообщения
673
Реакции[?]
106
Поинты[?]
69K
А знает кто-то, как строчки можно в нормальный вид превратить? Я так понял они шифруются чем-то

C++:
auto chat_message = reinterpret_cast<CDOTAClientMsg_ChatMessage*>( msg );
const auto& message_text = chat_message->message_text( );
std::cout << message_text << std::endl;
123 пишу в чат, выводит что-то типо "☺ ☺♦ tOl‼Ц"

Еще на стековерфлоу нашел что-то подобное:
C++:
std::string str;
google::protobuf::TextFormat::PrintToString( *msg, &str );
но крашит просто
 
Ревёрсер среднего звена
Пользователь
Статус
Оффлайн
Регистрация
24 Ноя 2022
Сообщения
303
Реакции[?]
108
Поинты[?]
57K
Пользователь
Статус
Оффлайн
Регистрация
8 Апр 2022
Сообщения
673
Реакции[?]
106
Поинты[?]
69K
Может там кодировка UTF-8? Сомневаюсь, что их как-то хэшируют там или что-то

У меня все протобаф-функции подобного рода крашат, поэтому хз
Там вообще странное что-то с кодировкой этой, я разные сообщения отправляю в чат, а в message_text() одни и теже символы(по типу которых я выше кидал), значит скорее всего как то по другому оно там хранится, не прямо в message_text().

Я кароче по-тупому сделал, но работает xd
C++:
char b[256]{ '\0' };
const std::string message_ = rdx->protobufBinding->ToString( msg, &b );

if ( hnd_id == 394 ) {
    const auto s_pos = message_.find( "message_text: \"" ) + 15, e_pos = message_.find( "\"", s_pos );
    std::cout << "chat_message: " << message_.substr( s_pos, e_pos - s_pos ) << std::endl;

}
Ток минус есть один - так только прочитать можно сообщение, а перезаписать нет(

1680271643046.png
 
Последнее редактирование:
Ревёрсер среднего звена
Пользователь
Статус
Оффлайн
Регистрация
24 Ноя 2022
Сообщения
303
Реакции[?]
108
Поинты[?]
57K
Там вообще странное что-то с кодировкой этой, я разные сообщения отправляю в чат, а в message_text() одни и теже символы(по типу которых я выше кидал), значит скорее всего как то по другому оно там хранится, не прямо в message_text().

Я кароче по-тупому сделал, но работает xd
C++:
char b[256]{ '\0' };
const std::string message_ = rdx->protobufBinding->ToString( msg, &b );

if ( hnd_id == 394 ) {
    const auto s_pos = message_.find( "message_text: \"" ) + 15, e_pos = message_.find( "\"", s_pos );
    std::cout << "chat_message: " << message_.substr( s_pos, e_pos - s_pos ) << std::endl;

}
Ток минус есть один - так только прочитать можно сообщение, а перезаписать нет(

Посмотреть вложение 243413
Лично для меня что читать, что перезаписывать чат не имеет практического применения
Но в принципе вариант рабочий
 
i hate p2cs
Участник
Статус
Оффлайн
Регистрация
18 Окт 2022
Сообщения
617
Реакции[?]
218
Поинты[?]
147K
+rep
нужно это в кс2 реализовать потеститьь
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
А знает кто-то, как строчки можно в нормальный вид превратить? Я так понял они шифруются чем-то

C++:
auto chat_message = reinterpret_cast<CDOTAClientMsg_ChatMessage*>( msg );
const auto& message_text = chat_message->message_text( );
std::cout << message_text << std::endl;
123 пишу в чат, выводит что-то типо "☺ ☺♦ tOl‼Ц"

Еще на стековерфлоу нашел что-то подобное:
C++:
std::string str;
google::protobuf::TextFormat::PrintToString( *msg, &str );
но крашит просто
так сохрани в файл строку(по байтам) и посмотри на неё во всяких хексовых редакторах/других инструментах
+ в реклассе посмотри, мб оффсет не тот там или еще что-нибудь
у меня всё нормально(я конеш не юзал message_text, онли в реклассе смотрел), написал русские буковки(123сука) мне их в ютф8 закодировало
1680277174421.png
ToString разве что эскейпит в 8ричную систему байты нон-ascii(0o321 это 209(0xD1), 0o201 это 129(0x81))
1680277319657.png
ToString это
C++:
    std::string CProtobufBinding::ToString(void* msg)
    {
        CUtlString str{};
        std::string result;
        result = CallVFunc<ToString_VFTable_Index/* 0 */, const char*>(msg, &str);
        return result;
    }
 
Начинающий
Статус
Оффлайн
Регистрация
30 Мар 2020
Сообщения
326
Реакции[?]
24
Поинты[?]
12K
А для отправки голосовухи в чат( скр ниже ) нужен нетчан как я понял, да?

И еще вопрос, как можно визуально апнуть себе лвл или прогресс героя( ну тут понятно что награда не выдаст ибо это габеновские сервера и т.д. ) ?
 
Ревёрсер среднего звена
Пользователь
Статус
Оффлайн
Регистрация
24 Ноя 2022
Сообщения
303
Реакции[?]
108
Поинты[?]
57K
как можно визуально апнуть себе лвл или прогресс героя
Эй, ты давай это, не наглей
Я сам понятия не имею, как они с рангами это всё делают. Возможно, если посидеть и усиленно продебажить поднятие ранга(ну вот поиграть в игру и затрекать XP каким-нибудь чит энжином), то что-то можно узнать

нужен нетчан как я понял
Скорее всего. Кстати, спасибо за идею, я кажись почти понял прикол замены текста на свой
 
Сверху Снизу