Загрузка...

Script
APC Injection + Process Hollowing in C++: Bypassing VAC from an amateur

Thread in C/C++ created by invisibilitybro Jan 11, 2026. 290 views

  1. invisibilitybro
    APC Injection + Process Hollowing в C++: Обход VAC от дилетанта

    DLL-injection — VAC сжирает. Комбо APC injection + Process Hollowing прячет код в легитимном процессе, маскируя его под системный. Пишем на Visual Studio 2022, C++20, x64. Нужно: VS, Process Hacker, любая VAC-защищённая игра (например, CS:GO или CS2 для тестов). Цель — внедрить шеллкод без сигнатур.

    Process Hollowing: Вырезаем тело легитимного процесса

    Процесс hollowing позволяет заменить код выполняющегося процесса на ваш собственный. Это делается путем загрузки процесса в приостановленном состоянии, выгрузки его старого кода и замены на свой. Это дает возможность скрыть изменения, так как код работает в контексте нормального процесса.

    1. Стартуем процесс в suspended состоянии:

    Первым шагом создаем процесс в suspended (приостановленном) состоянии. Это нужно для того, чтобы можно было контролировать его выполнение до того, как он начнёт работать. Важно, чтобы процесс не начал выполняться сразу, иначе система защиты может его заблокировать.

    Code

    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    if (!CreateProcess(L"notepad.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
    std::cerr << "Failed to create process" << std::endl;
    return 1;
    }
    2. Выгружаем старое тело процесса (исходный код):

    Для того чтобы вставить свой код, нужно выгрузить старые сегменты процесса. Мы используем функцию NtUnmapViewOfSection для удаления этих сегментов.

    Code

    HMODULE hModule = GetModuleHandle(NULL);
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDosHeader->e_lfanew);
    SIZE_T imageSize = pNtHeaders->OptionalHeader.SizeOfImage;
    pNtUnmapViewOfSection(pi.hProcess, hModule);
    3. Вставляем свой код:

    Теперь, когда старая память освобождена, мы можем выделить память для своего кода в целевом процессе. Для этого используем VirtualAllocEx, чтобы выделить память в адресном пространстве процесса, и WriteProcessMemory, чтобы записать туда наш код.

    Code

    LPVOID lpBaseAddress = VirtualAllocEx(pi.hProcess, hModule, imageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpBaseAddress) {
    std::cerr << "Failed to allocate memory" << std::endl;
    return 1;
    }

    SIZE_T bytesWritten;
    if (!WriteProcessMemory(pi.hProcess, lpBaseAddress, (LPVOID)hModule, imageSize, &bytesWritten)) {
    std::cerr << "Failed to write memory" << std::endl;
    return 1;
    }
    4. Обновляем PEB и продолжаем процесс:

    После того как код заменен, нужно обновить PEB (Process Environment Block) и продолжить выполнение процесса с новым кодом. Для этого используем ResumeThread.

    Code

    peb->ImageBaseAddress = lpBaseAddress;
    ctx.Rcx = (DWORD64)lpBaseAddress + 0x1000; // entry point
    SetThreadContext(pi.hThread, &ctx);
    ResumeThread(pi.hThread);
    Теперь процесс будет работать с вашим кодом внутри, и VAC не сможет это обнаружить.

    APC Injection: Бесшумный **** в чужой поток

    APC injection позволяет внедрять код в поток другого процесса через вызов асинхронных процедур. Это выполняется при попадании потока в alertable state (например, при вызове SleepEx или WaitForSingleObjectEx).

    1. Открываем целевой процесс:

    Прежде чем внедрять код в процесс, нужно получить к нему доступ. Для этого используем OpenProcess.

    Code

    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPid);
    2. Вставляем код через APC:

    Далее необходимо выделить память в процессе и записать в нее код, который будет выполнен в контексте потока. Используем QueueUserAPC, чтобы вставить код в очередь APC целевого потока.

    Code

    LPVOID lpMem = VirtualAllocEx(hProcess, NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(hProcess, lpMem, shellcode, 0x1000, NULL);
    QueueUserAPC((PAPCFUNC)lpMem, hThread, 0);
    3. Продолжаем выполнение потока:

    После того как код вставлен в очередь APC, поток продолжит выполнение, и ваш код будет выполнен.

    Пример кода: Полный ****

    Пример кода

    Code

    #include <windows.h>
    #include <winternl.h>
    #pragma comment(lib, "ntdll.lib")

    typedef NTSTATUS(NTAPI* pNtUnmapViewOfSection)(HANDLE, PVOID);
    PVOID shellcode = /* ваш xor-encrypted payload тут, 0x1000 байт */;

    void hollow_and_apc() {
    STARTUPINFO si = {}; PROCESS_INFORMATION pi = {};
    CreateProcess(L"C:\\windows\\notepad.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);

    CONTEXT ctx; ctx.ContextFlags = CONTEXT_FULL; GetThreadContext(pi.hThread, &ctx);
    PEB* peb = (PEB*)ctx.Peb; PPEB_LDR_DATA ldr = peb->Ldr;

    pNtUnmapViewOfSection NtUnmap = (pNtUnmapViewOfSection)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtUnmapViewOfSection");
    NtUnmap(pi.hProcess, peb->ImageBaseAddress);

    // map shellcode
    LPVOID base = VirtualAllocEx(pi.hProcess, peb->ImageBaseAddress, 0x10000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(pi.hProcess, base, shellcode, 0x1000, NULL);
    peb->ImageBaseAddress = base; ctx.Rcx = (DWORD64)base + 0x1000; // entry
    SetThreadContext(pi.hThread, &ctx); ResumeThread(pi.hThread);

    // apc в csgo
    HANDLE csgo = OpenProcess(PROCESS_ALL_ACCESS, FALSE, /*csgo_pid*/);
    LPVOID mem = VirtualAllocEx(csgo, NULL, 0x1000, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(csgo, mem, shellcode, 0x1000, NULL);
    HANDLE thread = OpenThread(THREAD_SET_CONTEXT, FALSE, /*target_thread_id*/);
    QueueUserAPC((PAPCFUNC)mem, thread, 0);
    }
    VAC Bypass: Почему это работает

    1. Hollowing меняет entropy процесса без добавления новых файлов, что затрудняет обнаружение.
    2. APC избегает стандартных хуков на CreateRemoteThread, что помогает избежать обнаружения.
    3. Нет использования dll-load, что также снижает шансы на детектирование.
    4. VAC 2026 видит процессы с suspend/resume как нормальное поведение.
    5. Рандомизация APC-очередей через NtQueueApcThreadEx позволяет уменьшить вероятность детектирования.
     
    1. valeraxt
      :ok_shy: :ok_shy: :ok_shy: :ok_shy: ничивошеньки нипон :ok_shy: :ok_shy:
    2. Solidneyka
    3. invisibilitybro Topic starter
      avatarSolidneyka, ну я им не вдохновлялся. он по другому обходит вак
Loading...