• Я зарабатываю 100 000 RUB / месяц на этом сайте!

    А знаешь как? Я всего-лишь публикую (создаю темы), а админ мне платит. Трачу деньги на мороженое, робуксы и сервера в Minecraft. А ещё на паль из Китая. 

    Хочешь так же? Пиши и узнавай условия: https://t.me/alex_redact
    Реклама: https://t.me/yougame_official

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

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

В первую очередь нужно определить, где его взять, тут всё просто, за нас
Пожалуйста, авторизуйтесь для просмотра ссылки.
уже наревёрсил. В networksystem.dll есть публичный интерфейс NetworkSystem, в нём на индексе 26 есть метод CreateNetChannel, а уже там хукаются методы Нетчана.

NetChannel vtable hook:
Expand Collapse Copy
// класс 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++:
Expand Collapse Copy
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++:
Expand Collapse Copy
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 и Гейбу Ньюэллу лично за предоставление информации по теме данного гайда. Если есть какие-то вопросы, пишите здесь, будем дополнять гайд вместе
 
Последнее редактирование:
Респект особенно за анимацию атаки, не знал что инфа о руке передаётся!

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

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

чтобы слинковаться статически с протобафом - протобаф должен юзать ту же CRT что и длл чита.
Так что нужно правильно выбрать триплет в vcpkg. Если чит юзает /MD то триплет должен быть x64-windows-static-md
 
бля зря я решил пересобирать протобафы...
1678848085344.png

кто знает что делать? в вс все закинул, либы подключил
1678848119148.png
1678848128729.png
причем в релиз компилится а в дебаг не
 
Последнее редактирование:
бля зря я решил пересобирать протобафы...
Посмотреть вложение 241713
кто знает что делать? в вс все закинул, либы подключил
Посмотреть вложение 241714Посмотреть вложение 241715причем в релиз компилится а в дебаг не
Удали steammessages-base, он конфликтует с steammessages
Почему там unresolved symbols при линковке, если честно, не знаю(да ещё и зависящие от конфигурации)
 
бля зря я решил пересобирать протобафы...
Посмотреть вложение 241713
кто знает что делать? в вс все закинул, либы подключил
Посмотреть вложение 241714Посмотреть вложение 241715причем в релиз компилится а в дебаг не
там вроде вот эту хуйню удалить надо
1678906785014.png

+ там на настроечки в визуалке внимательно посмотри(компилятор одинаковой версии должен быть использован при билде либы и при билде твоего проекта, + MT/MD(статик/длл СРТ) настройки должны совпадать у либы и у проекта + на директории инклюдов и прочую хуйню тоже посмотри чтобы в дебаге и релизе нормальные были + либу правильную линкуй(на дебаге дебаг версию, а на релизе релиз версию. в настройках проекта или #ifdef'ом каким-нибудь + #pragma lib(comment, "shit.lib"))) + там папка гуглов должна быть где-нибудь по пути инклюда(google\protobuf)(при этом сами файлики из этой папки не надо в сам проект инклюдить, они просто должны лежать на диске и в путях инклюда прописаны, их .h/.cc собранные файлики юзают) + протобаф файлики должны быть собраны под конкретную/близкую версию либы - ну то есть иногда нельзя старую либу юзать с новыми .h/.cc файликами условно если ты эти файлики собираешь современным protoc
а так ваще нахуя тебе дебаг? в релизе спокойно всё дебажить можно(в х64дбг например. если дебажить через х64дбг например(асм-левел дебаггинг) а не через визуалку(сурс-левел дебаггинг) то это как раз повысит твой скилл дебага реверса асм и тд), символы в релизе тоже генерятся если надо(упрощают задачу. практически сурс-левел дебаггинг) + тебе всё равно возможно рано или поздно придётся дебажить релизную длл, так можешь сейчас научиться это делать чтобы потом не ахуевать
 
Вот это действительно хороший контент.
+rep топик-стартеру.
 
А знает кто-то, как строчки можно в нормальный вид превратить? Я так понял они шифруются чем-то

C++:
Expand Collapse Copy
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++:
Expand Collapse Copy
std::string str;
google::protobuf::TextFormat::PrintToString( *msg, &str );
но крашит просто
 
Может там кодировка UTF-8? Сомневаюсь, что их как-то хэшируют там или что-то

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

Я кароче по-тупому сделал, но работает xd
C++:
Expand Collapse Copy
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
 
Последнее редактирование:
Там вообще странное что-то с кодировкой этой, я разные сообщения отправляю в чат, а в message_text() одни и теже символы(по типу которых я выше кидал), значит скорее всего как то по другому оно там хранится, не прямо в message_text().

Я кароче по-тупому сделал, но работает xd
C++:
Expand Collapse Copy
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
Лично для меня что читать, что перезаписывать чат не имеет практического применения
Но в принципе вариант рабочий
 
+rep
нужно это в кс2 реализовать потеститьь
 
А знает кто-то, как строчки можно в нормальный вид превратить? Я так понял они шифруются чем-то

C++:
Expand Collapse Copy
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++:
Expand Collapse Copy
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++:
Expand Collapse Copy
    std::string CProtobufBinding::ToString(void* msg)
    {
        CUtlString str{};
        std::string result;
        result = CallVFunc<ToString_VFTable_Index/* 0 */, const char*>(msg, &str);
        return result;
    }
 
А для отправки голосовухи в чат( скр ниже ) нужен нетчан как я понял, да?

И еще вопрос, как можно визуально апнуть себе лвл или прогресс героя( ну тут понятно что награда не выдаст ибо это габеновские сервера и т.д. ) ?
VginJ8L.png
 
как можно визуально апнуть себе лвл или прогресс героя
Эй, ты давай это, не наглей
Я сам понятия не имею, как они с рангами это всё делают. Возможно, если посидеть и усиленно продебажить поднятие ранга(ну вот поиграть в игру и затрекать XP каким-нибудь чит энжином), то что-то можно узнать

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