Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Гайд [Сурс] Discord Overlay Hijacking — отрисовка через Shared Section

Sloppy
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
429
Реакции
10
Народ, кто искал способ рисовать оверлей без создания своих окон и палевных хуков на Present/SwapBuffers — ловите базу. Суть метода заключается в использовании общей секции памяти (shared memory) самого Дискорда.

Дискорд маппит свой фреймбуфер в память, и если найти этот участок, в него можно писать напрямую. Это фактически traceless overlay, так как мы не создаем новых объектов, которые мог бы спалить античит, а просто «паразитируем» на легитимном модуле.

Техническая часть

Для работы нам нужна структура хедера, который Дискорд использует для управления фреймбуфером:

Код:
Expand Collapse Copy
typedef struct _Header
{
    UINT Magic;
    UINT FrameCount;
    UINT NoClue;
    UINT Width;
    UINT Height;
    BYTE Buffer[1];
} Header;

Как это работает:
  1. Находим базу модуля DiscordHook64.dll.
  2. Ищем указатель на замапленный файл. В актуальных билдах зацепка идет через смещение (в примере ниже это 0x130E58, но оффсеты имеют свойство тухнуть, так что реверсните свой билд).
  3. Пишем данные кадра (формат BGRA) прямо в буфер.
  4. Инкрементируем FrameCount. Это критично — внутренний модуль Дискорда чекает это значение и, если оно изменилось, копирует фреймбуфер на экран.

Пример реализации записи кадра:

Код:
Expand Collapse Copy
typedef struct _Header
{
    UINT Magic;
    UINT FrameCount;
    UINT NoClue;
    UINT Width;
    UINT Height;
    BYTE Buffer[1];
} Header;
 
inline bool ConnectToProcess(ConnectedProcessInfo& processInfo)
        {
            std::string mappedFilename = "DiscordOverlay_Framebuffer_Memory_" + std::to_string(processInfo.ProcessId);
            processInfo.File = OpenFileMappingA(FILE_MAP_ALL_ACCESS, false, mappedFilename.c_str());
            if (!processInfo.File || processInfo.File == INVALID_HANDLE_VALUE)
                return false;
 
            processInfo.MappedAddress = static_cast<Header*>(MapViewOfFile(processInfo.File, FILE_MAP_ALL_ACCESS, 0, 0, 0));
            return processInfo.MappedAddress;
        }
 
inline void SendFrame(ConnectedProcessInfo& processInfo, UINT width, UINT height, void* frame, UINT size)
        {
            // frame is in B8G8R8A8 format
            // size can be nearly anything since it will get resized
            // for the screen appropriately, although the maximum size is
            // game window width * height * 4 (BGRA)
            processInfo.MappedAddress->Width = width;
            processInfo.MappedAddress->Height = height;
 
            memcpy(processInfo.MappedAddress->Buffer, frame, size);
 
            processInfo.MappedAddress->FrameCount++; // this will cause the internal module to copy over the framebuffer
        }

Если работаете через драйвер и используете свою функцию трансляции VA (Virtual Address), имейте в виду, что при работе с 2MB страницами может возникнуть проблема с проверкой Read/Write битов. Если у вас рендерится только первая страница (4KB), а дальше всё падает — копайте в сторону логики вашей трансляции страниц.

Метод хорош тем, что DiscordOverlay маппит файл с именем вида DiscordOverlay_Framebuffer_Memory_<PID>. Можно подключаться через OpenFileMappingA из юзермода или лезть в секции из ядра.

Кто уже пробовал этот способ под EAC/BE, как обстоят дела с детектом самой записи в shared section?
 
Назад
Сверху Снизу